diff --git a/.gitignore b/.gitignore index c9ffe48..e6ce5a6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ t t.c *.o +.depen* diff --git a/bin/kill/Makefile b/bin/kill/Makefile new file mode 100644 index 0000000..7349674 --- /dev/null +++ b/bin/kill/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD: releng/11.1/bin/kill/Makefile 298107 2016-04-16 07:45:30Z gjb $ + +PACKAGE=runtime +PROG= kill + +.include diff --git a/bin/kill/Makefile.depend b/bin/kill/Makefile.depend new file mode 100644 index 0000000..55aac63 --- /dev/null +++ b/bin/kill/Makefile.depend @@ -0,0 +1,18 @@ +# $FreeBSD: releng/11.1/bin/kill/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/kill/kill.1 b/bin/kill/kill.1 new file mode 100644 index 0000000..ec1ce8f --- /dev/null +++ b/bin/kill/kill.1 @@ -0,0 +1,157 @@ +.\"- +.\" 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: releng/11.1/bin/kill/kill.1 313143 2017-02-03 11:17:44Z sevan $ +.\" +.Dd October 3, 2016 +.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" +.Pp +Terminate the process group with PGID 117: +.Pp +.Dl "kill -- -117" +.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 +in section 8 of the manual. +.Sh BUGS +A replacement for the command +.Dq Li kill 0 +for +.Xr csh 1 +users should be provided. diff --git a/bin/kill/kill.c b/bin/kill/kill.c new file mode 100644 index 0000000..de925c7 --- /dev/null +++ b/bin/kill/kill.c @@ -0,0 +1,213 @@ +/*- + * 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 +__FBSDID("$FreeBSD: releng/11.1/bin/kill/kill.c 315721 2017-03-22 17:46:08Z bdrewery $"); + +#include +#include +#include +#include +#include +#include +#include + +#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); + +int +main(int argc, char *argv[]) +{ + long pidl; + pid_t pid; + int errors, numsig, 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); + printf("%s\n", sys_signame[numsig]); + 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 + { + pidl = strtol(*argv, &ep, 10); + /* Check for overflow of pid_t. */ + pid = (pid_t)pidl; + if (!**argv || *ep || pid != pidl) + errx(2, "illegal process id: %s", *argv); + ret = kill(pid, numsig); + } + if (ret == -1) { + warn("%s", *argv); + errors = 1; + } + } + + return (errors); +} + +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++) { + (void)fprintf(fp, "%s", sys_signame[n]); + if (n == (sys_nsig / 2) || n == (sys_nsig - 1)) + (void)fprintf(fp, "\n"); + else + (void)fprintf(fp, " "); + } +} + +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/bin/printf/Makefile b/bin/printf/Makefile new file mode 100644 index 0000000..4473ff7 --- /dev/null +++ b/bin/printf/Makefile @@ -0,0 +1,12 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD: releng/11.1/usr.bin/printf/Makefile 265420 2014-05-06 04:22:01Z imp $ + +.include + +PROG= printf + +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + +.include diff --git a/bin/printf/Makefile.depend b/bin/printf/Makefile.depend new file mode 100644 index 0000000..5f58d4c --- /dev/null +++ b/bin/printf/Makefile.depend @@ -0,0 +1,18 @@ +# $FreeBSD: releng/11.1/usr.bin/printf/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/printf/printf.1 b/bin/printf/printf.1 new file mode 100644 index 0000000..ac799a0 --- /dev/null +++ b/bin/printf/printf.1 @@ -0,0 +1,382 @@ +.\" 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. +.\" 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. +.\" +.\" @(#)printf.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: releng/11.1/usr.bin/printf/printf.1 264743 2014-04-21 22:47:18Z pfg $ +.\" +.Dd April 21, 2014 +.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 character. +.It Cm \eb +Write a character. +.It Cm \ec +Ignore remaining characters in this string. +.It Cm \ef +Write a character. +.It Cm \en +Write a character. +.It Cm \er +Write a character. +.It Cm \et +Write a character. +.It Cm \ev +Write a character. +.It Cm \e\' +Write a 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 . +.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 COMPATIBILITY +The traditional +.Bx +behavior of converting arguments of numeric formats not beginning +with a digit to the +.Tn 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 +.Tn 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 +.Tn 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/bin/printf/printf.c b/bin/printf/printf.c new file mode 100644 index 0000000..9638471 --- /dev/null +++ b/bin/printf/printf.c @@ -0,0 +1,686 @@ +/*- + * Copyright 2014 Garrett D'Amore + * 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. + * 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 /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: releng/11.1/usr.bin/printf/printf.c 279503 2015-03-01 21:46:55Z jilles $"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SHELL +#define main printfcmd +#include "bltin/bltin.h" +#include "options.h" +#endif + +#define PF(f, func) do { \ + char *b = NULL; \ + if (havewidth) \ + if (haveprec) \ + (void)asprintf(&b, f, fieldwidth, precision, func); \ + else \ + (void)asprintf(&b, f, fieldwidth, func); \ + else if (haveprec) \ + (void)asprintf(&b, f, precision, func); \ + else \ + (void)asprintf(&b, f, func); \ + if (b) { \ + (void)fputs(b, stdout); \ + free(b); \ + } \ +} 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; + + p = strdup(getstr()); + if (p == NULL) { + warnx("%s", strerror(ENOMEM)); + return (NULL); + } + getout = escape(p, 0, &len); + fputs(p, stdout); + free(p); + if (getout) + return (end_fmt); + break; + } + case 'c': { + char p; + + p = getchr(); + 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/bin/printf/tests/Makefile b/bin/printf/tests/Makefile new file mode 100644 index 0000000..96d3c9e --- /dev/null +++ b/bin/printf/tests/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD: releng/11.1/usr.bin/printf/tests/Makefile 299094 2016-05-04 23:20:53Z ngie $ + +PACKAGE= tests + +TAP_TESTS_SH= legacy_test + +${PACKAGE}FILES+= regress.b.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 diff --git a/bin/printf/tests/Makefile.depend b/bin/printf/tests/Makefile.depend new file mode 100644 index 0000000..bb5235a --- /dev/null +++ b/bin/printf/tests/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/usr.bin/printf/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/printf/tests/legacy_test.sh b/bin/printf/tests/legacy_test.sh new file mode 100644 index 0000000..2b3ac91 --- /dev/null +++ b/bin/printf/tests/legacy_test.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# $FreeBSD: releng/11.1/usr.bin/printf/tests/legacy_test.sh 263227 2014-03-16 08:04:06Z jmmv $ + +SRCDIR="$(dirname "${0}")"; export SRCDIR + +m4 "${SRCDIR}/../regress.m4" "${SRCDIR}/regress.sh" | sh diff --git a/bin/printf/tests/regress.b.out b/bin/printf/tests/regress.b.out new file mode 100644 index 0000000..0373d93 --- /dev/null +++ b/bin/printf/tests/regress.b.out @@ -0,0 +1 @@ +abcdef diff --git a/bin/printf/tests/regress.d.out b/bin/printf/tests/regress.d.out new file mode 100644 index 0000000..da83c08 --- /dev/null +++ b/bin/printf/tests/regress.d.out @@ -0,0 +1 @@ +123, 123,00123,00123,00123 diff --git a/bin/printf/tests/regress.f.out b/bin/printf/tests/regress.f.out new file mode 100644 index 0000000..127cb8a --- /dev/null +++ b/bin/printf/tests/regress.f.out @@ -0,0 +1 @@ +42.250000,-42.250 ,inf,nan diff --git a/bin/printf/tests/regress.l1.out b/bin/printf/tests/regress.l1.out new file mode 100644 index 0000000..9be0dc9 --- /dev/null +++ b/bin/printf/tests/regress.l1.out @@ -0,0 +1 @@ +228 diff --git a/bin/printf/tests/regress.l2.out b/bin/printf/tests/regress.l2.out new file mode 100644 index 0000000..9be0dc9 --- /dev/null +++ b/bin/printf/tests/regress.l2.out @@ -0,0 +1 @@ +228 diff --git a/bin/printf/tests/regress.m1.out b/bin/printf/tests/regress.m1.out new file mode 100644 index 0000000..a9e063e --- /dev/null +++ b/bin/printf/tests/regress.m1.out Binary files differ diff --git a/bin/printf/tests/regress.m2.out b/bin/printf/tests/regress.m2.out new file mode 100644 index 0000000..cf14915 --- /dev/null +++ b/bin/printf/tests/regress.m2.out @@ -0,0 +1,2 @@ +abc +cdef \ No newline at end of file diff --git a/bin/printf/tests/regress.m3.out b/bin/printf/tests/regress.m3.out new file mode 100644 index 0000000..bbe4e70 --- /dev/null +++ b/bin/printf/tests/regress.m3.out @@ -0,0 +1,4 @@ +%abc +%def +%ghi +%jkl diff --git a/bin/printf/tests/regress.m4.out b/bin/printf/tests/regress.m4.out new file mode 100644 index 0000000..5e0b5d4 --- /dev/null +++ b/bin/printf/tests/regress.m4.out @@ -0,0 +1 @@ +0,0.000000,, diff --git a/bin/printf/tests/regress.m5.out b/bin/printf/tests/regress.m5.out new file mode 100644 index 0000000..2838468 --- /dev/null +++ b/bin/printf/tests/regress.m5.out @@ -0,0 +1 @@ +-d diff --git a/bin/printf/tests/regress.missingpos1.out b/bin/printf/tests/regress.missingpos1.out new file mode 100644 index 0000000..3b04f03 --- /dev/null +++ b/bin/printf/tests/regress.missingpos1.out @@ -0,0 +1 @@ +printf: incomplete use of n$ diff --git a/bin/printf/tests/regress.s.out b/bin/printf/tests/regress.s.out new file mode 100644 index 0000000..3d572b9 --- /dev/null +++ b/bin/printf/tests/regress.s.out @@ -0,0 +1 @@ +abc,abc diff --git a/bin/printf/tests/regress.sh b/bin/printf/tests/regress.sh new file mode 100644 index 0000000..3f3949c --- /dev/null +++ b/bin/printf/tests/regress.sh @@ -0,0 +1,31 @@ +# $FreeBSD: releng/11.1/usr.bin/printf/tests/regress.sh 266854 2014-05-29 19:48:18Z pfg $ + +REGRESSION_START($1) + +echo '1..23' + +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_END() diff --git a/bin/printf/tests/regress.zero.out b/bin/printf/tests/regress.zero.out new file mode 100644 index 0000000..fa8f08c --- /dev/null +++ b/bin/printf/tests/regress.zero.out @@ -0,0 +1 @@ +150 diff --git a/bin/sh/Makefile b/bin/sh/Makefile index 262aa61..daac731 100644 --- a/bin/sh/Makefile +++ b/bin/sh/Makefile @@ -1,49 +1,71 @@ -# $Id: Makefile 115 2016-01-14 02:55:19Z reddawg $ -# Application Makefile (C) 2002-2004 The UbixOS Project +# @(#)Makefile 8.4 (Berkeley) 5/5/95 +# $FreeBSD: releng/11.1/bin/sh/Makefile 319184 2017-05-30 04:15:40Z ngie $ -# Include Global 'Source' Options -Makefile.incl -Makefile.incl +.include -#Linker -LD = ld +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} -#Binary File Name -BINARY = sh +# 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/. -#Delete Program -REMOVE = rm -f +LIBADD=edit -#Objects -OBJS = sh1.o +CFLAGS+=-DSHELL -I. -I${.CURDIR} +# for debug: +# DEBUG_FLAGS+= -g -DDEBUG=2 -fno-inline +WARNS?= 2 +WFORMAT=0 -INCLUDES = -I../../include.new +.PATH: ${.CURDIR}/bltin \ + ${.CURDIR:H}/kill \ + ${.CURDIR:H}/test \ + ${.CURDIR:H}/printf -LIBRARIES = ../../lib/libc/libc.so +CLEANFILES+= mknodes mknodes.o \ + mksyntax mksyntax.o +CLEANFILES+= ${GENSRCS} ${GENHDRS} -STARTUP = ../../lib/csu/*.o +build-tools: mknodes mksyntax -# Link The Binary -$(BINARY) : $(OBJS) - $(CC) $(CFLAGS) -o ${BUILD_DIR}/bin/$@ $(STARTUP) $(LIBRARIES) $(OBJS) - #strip $(BINARY) +.ORDER: builtins.c builtins.h +builtins.h: .NOMETA +builtins.c builtins.h: mkbuiltins builtins.def + sh ${.CURDIR}/mkbuiltins ${.CURDIR} -# Compile the source files -.cc.o: - $(CXX) -Wall -O $(CFLAGS) $(INCLUDES) -c -o $@ $< +# 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} -.cc.s: - $(CXX) -Wall -O $(CFLAGS) $(INCLUDES) -S -o $@ $< - -.c.o: - $(CC) -Wall -O $(CFLAGS) $(INCLUDES) -c -o $@ $< - -.c.s: - $(CC) -Wall -O $(CFLAGS) $(INCLUDES) -S -o $@ $< - -.S.o: - $(CC) -Wall $(CLFAGS) $(INCLUDES) -c -o $@ $< - -# Clean Up The junk -clean: - $(REMOVE) $(OBJS) ${BUILD_DIR}/bin/$(BINARY) +.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 + +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + +.include diff --git a/bin/sh/Makefile.depend b/bin/sh/Makefile.depend new file mode 100644 index 0000000..c4b869e --- /dev/null +++ b/bin/sh/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/bin/sh/Makefile.depend 295989 2016-02-24 17:20:11Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + lib/libedit \ + lib/libc \ + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/TOUR b/bin/sh/TOUR new file mode 100644 index 0000000..9748680 --- /dev/null +++ b/bin/sh/TOUR @@ -0,0 +1,288 @@ +# @(#)TOUR 8.1 (Berkeley) 5/31/93 +# $FreeBSD: releng/11.1/bin/sh/TOUR 253650 2013-07-25 15:08:41Z 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 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 Variable substitution + CTLENDVAR End of variable substitution + CTLBACKQ Command substitution + CTLBACKQ|CTLQUOTE Command substitution inside double quotes + 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} + +In addition, the type field will have the VSQUOTE flag set if the +variable is enclosed in double quotes. The name of the variable +comes next, terminated by an equals sign. If the type is not +VSNORMAL, then the text field in the substitution follows, ter- +minated by a CTLENDVAR byte. + +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. + +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 splitting and file name +generation, have the CTLESC characters removed during the vari- +able and command substitution phase. Words which are subject to +splitting and file name generation have the CTLESC characters re- +moved 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: Arguments are processed in three passes. The first +(performed by the routine argstr) performs variable and command +substitution. The second (ifsbreakup) performs word splitting +and the third (expandmeta) performs file name generation. + +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, which is processed by the mkbuilt- +ins 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 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 makefile in this directory compiles these programs +in the normal fashion (so that they can be run regardless of +whether the invoker is ash), but also creates a library named +bltinlib.a which can be linked with ash. 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 pro- +gram is to be compiled stand-alone. + +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 it's 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/bin/sh/alias.c b/bin/sh/alias.c new file mode 100644 index 0000000..d3b3cd1 --- /dev/null +++ b/bin/sh/alias.c @@ -0,0 +1,257 @@ +/*- + * 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. + * 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[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/alias.c 295868 2016-02-21 20:58:24Z jilles $"); + +#include +#include "shell.h" +#include "output.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "options.h" /* XXX for argptr (should remove?) */ +#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; + + app = hashalias(name); + for (ap = *app; ap; ap = ap->next) { + if (equal(name, ap->name)) { + INTOFF; + ckfree(ap->val); + ap->val = savestr(val); + INTON; + return; + } + } + /* not found */ + 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 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; + ckfree(ap->name); + ckfree(ap->val); + ckfree(ap); + INTON; + } + aliases--; + return (0); + } + } + + return (1); +} + +static void +rmaliases(void) +{ + struct alias *ap, *tmp; + int i; + + INTOFF; + for (i = 0; i < ATABSIZE; i++) { + ap = atab[i]; + atab[i] = NULL; + while (ap) { + ckfree(ap->name); + ckfree(ap->val); + tmp = ap; + ap = ap->next; + ckfree(tmp); + } + } + 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/bin/sh/alias.h b/bin/sh/alias.h new file mode 100644 index 0000000..8ec9549 --- /dev/null +++ b/bin/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. + * 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. + * + * @(#)alias.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/alias.h 223060 2011-06-13 21:03:27Z jilles $ + */ + +#define ALIASINUSE 1 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +struct alias *lookupalias(const char *, int); diff --git a/bin/sh/arith.h b/bin/sh/arith.h new file mode 100644 index 0000000..9b2b828 --- /dev/null +++ b/bin/sh/arith.h @@ -0,0 +1,38 @@ +/*- + * 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. + * 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. + * + * @(#)arith.h 1.1 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/arith.h 223060 2011-06-13 21:03:27Z jilles $ + */ + +#include "shell.h" + +#define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3) + +arith_t arith(const char *); +void arith_lex_reset(void); diff --git a/bin/sh/arith_yacc.c b/bin/sh/arith_yacc.c new file mode 100644 index 0000000..1f74c52 --- /dev/null +++ b/bin/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 . 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 +__FBSDID("$FreeBSD: releng/11.1/bin/sh/arith_yacc.c 270246 2014-08-20 20:15:43Z jilles $"); + +#include +#include +#include +#include +#include +#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/bin/sh/arith_yacc.h b/bin/sh/arith_yacc.h new file mode 100644 index 0000000..8bf8bbe --- /dev/null +++ b/bin/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 . 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: releng/11.1/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/bin/sh/arith_yylex.c b/bin/sh/arith_yylex.c new file mode 100644 index 0000000..254f2b8 --- /dev/null +++ b/bin/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 +__FBSDID("$FreeBSD: releng/11.1/bin/sh/arith_yylex.c 279503 2015-03-01 21:46:55Z jilles $"); + +#include +#include +#include +#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/bin/sh/bltin/bltin.h b/bin/sh/bltin/bltin.h new file mode 100644 index 0000000..4763aaf --- /dev/null +++ b/bin/sh/bltin/bltin.h @@ -0,0 +1,79 @@ +/*- + * 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. + * 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. + * + * @(#)bltin.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/bltin/bltin.h 279503 2015-03-01 21:46:55Z jilles $ + */ + +/* + * 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 +#undef main +#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else +#endif + +#include + +pointer stalloc(int); +int killjob(const char *, int); + +extern char *commandname; diff --git a/bin/sh/bltin/echo.c b/bin/sh/bltin/echo.c new file mode 100644 index 0000000..98814d2 --- /dev/null +++ b/bin/sh/bltin/echo.c @@ -0,0 +1,107 @@ +/*- + * 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. + * 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.c 8.2 (Berkeley) 5/4/95 + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/bltin/echo.c 127958 2004-04-06 20:06:54Z markm $"); + +/* + * 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/bin/sh/builtins.def b/bin/sh/builtins.def new file mode 100644 index 0000000..d276302 --- /dev/null +++ b/bin/sh/builtins.def @@ -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. +# 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. +# +# @(#)builtins.def 8.4 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/builtins.def 288430 2015-09-30 21:32:29Z jilles $ + +# +# 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 -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 builtin +aliascmd alias +bgcmd -j bg +bindcmd bind +breakcmd -s break -s continue +cdcmd cd chdir +commandcmd command +dotcmd -s . +echocmd echo +evalcmd -s eval +execcmd -s exec +exitcmd -s exit +letcmd let +exportcmd -s export -s readonly +#exprcmd expr +falsecmd false +fgcmd -j fg +freebsd_wordexpcmd freebsd_wordexp +getoptscmd getopts +hashcmd hash +histcmd -h fc +jobidcmd jobid +jobscmd jobs +killcmd kill +localcmd local +printfcmd printf +pwdcmd pwd +readcmd read +returncmd -s return +setcmd -s set +setvarcmd setvar +shiftcmd -s shift +testcmd test [ +timescmd -s times +trapcmd -s trap +truecmd -s : true +typecmd type +ulimitcmd ulimit +umaskcmd umask +unaliascmd unalias +unsetcmd -s unset +waitcmd wait +wordexpcmd wordexp diff --git a/bin/sh/cd.c b/bin/sh/cd.c new file mode 100644 index 0000000..23e15f4 --- /dev/null +++ b/bin/sh/cd.c @@ -0,0 +1,421 @@ +/*- + * 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. + * 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[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/cd.c 294667 2016-01-24 17:01:34Z jilles $"); + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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); + + 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/bin/sh/cd.h b/bin/sh/cd.h new file mode 100644 index 0000000..8711198 --- /dev/null +++ b/bin/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. + * 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: releng/11.1/bin/sh/cd.h 223060 2011-06-13 21:03:27Z jilles $ + */ + +void pwd_init(int); diff --git a/bin/sh/error.c b/bin/sh/error.c new file mode 100644 index 0000000..cae15b2 --- /dev/null +++ b/bin/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. + * 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[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/error.c 279569 2015-03-03 21:21:43Z jilles $"); + +/* + * 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 +#include +#include +#include + + +/* + * 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/bin/sh/error.h b/bin/sh/error.h new file mode 100644 index 0000000..efad819 --- /dev/null +++ b/bin/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. + * 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. + * + * @(#)error.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/error.h 276038 2014-12-21 23:09:59Z 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 +#include + +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) suppressint = (s) +#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/bin/sh/eval.c b/bin/sh/eval.c new file mode 100644 index 0000000..8c8f003 --- /dev/null +++ b/bin/sh/eval.c @@ -0,0 +1,1382 @@ +/*- + * 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. + * 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[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/eval.c 293635 2016-01-10 16:31:28Z jilles $"); + +#include +#include +#include +#include +#include +#include /* For WIFSIGNALED(status) */ +#include + +/* + * 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; + 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) +{ + if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD || + idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD || + idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD || + idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD || + idx == TYPECMD) + 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 realstatus; + 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.nleft = 0; + memout.nextc = memout.buf; + memout.bufsize = 64; + 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.nextc - memout.buf; + memout.buf = NULL; + } + 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, &realstatus); + INTON; + if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { + 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 redirection + * 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/bin/sh/eval.h b/bin/sh/eval.h new file mode 100644 index 0000000..16050df --- /dev/null +++ b/bin/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. + * 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. + * + * @(#)eval.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/eval.h 289159 2015-10-11 21:33:00Z jilles $ + */ + +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/bin/sh/exec.c b/bin/sh/exec.c new file mode 100644 index 0000000..d3eaba5 --- /dev/null +++ b/bin/sh/exec.c @@ -0,0 +1,778 @@ +/*- + * 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. + * 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[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/exec.c 296813 2016-03-13 22:54:14Z jilles $"); + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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" + + +#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]; +out2fmt_flush("%s:%i", __FILE__, __LINE__); + execve(cmd, argv, envp); +out2fmt_flush("%s:%i", __FILE__, __LINE__); + 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, and not invalidated by cd, 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 command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +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/bin/sh/exec.h b/bin/sh/exec.h new file mode 100644 index 0000000..4c65bf6 --- /dev/null +++ b/bin/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. + * 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. + * + * @(#)exec.h 8.3 (Berkeley) 6/8/95 + * $FreeBSD: releng/11.1/bin/sh/exec.h 238468 2012-07-15 10:19:43Z jilles $ + */ + +/* 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/bin/sh/expand.c b/bin/sh/expand.c new file mode 100644 index 0000000..d42d5cb --- /dev/null +++ b/bin/sh/expand.c @@ -0,0 +1,1549 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu . All rights reserved. + * Copyright (c) 2010-2015 + * Jilles Tjoelker . 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. + * 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[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/expand.c 315486 2017-03-18 16:07:28Z jilles $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 struct nodelist *argbackq; /* list of back quote expressions */ + +static const char *argstr(const char *, int, struct worddest *); +static const char *exptilde(const char *, int); +static const char *expari(const char *, int, struct worddest *); +static void expbackq(union node *, int, int, struct worddest *); +static void subevalvar_trim(const char *, int, int, int); +static int subevalvar_misc(const char *, const char *, int, int, int); +static const char *evalvar(const char *, 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) +{ + static wchar_t s1[2], s2[2]; + + s1[0] = c1; + s2[0] = c2; + 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. + * + * Caution: this function uses global state and is not reentrant. + * However, a new invocation after an interrupted invocation is safe + * and will reset the global state for the new call. + */ +void +expandarg(union node *arg, struct arglist *arglist, int flag) +{ + struct worddest exparg; + + if (fflag) + flag &= ~EXP_GLOB; + argbackq = arg->narg.backquote; + exparg.list = arglist; + exparg.state = WORD_IDLE; + STARTSTACKSTR(expdest); + argstr(arg->narg.text, 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, 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, flag, dst); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, flag, dst); + argbackq = argbackq->next; + break; + case CTLARI: + p = expari(p, 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, 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, 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 (!quoted) + reprocess(expdest - adj - stackblock(), flag, VSNORMAL, 0, 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; + struct nodelist *saveargbackq; + char lastc; + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + int quotes = flag & (EXP_GLOB | EXP_CASE); + size_t nnl; + const char *ifs; + + INTOFF; + saveargbackq = argbackq; + p = grabstackstr(dest); + evalbackcmd(cmd, &in); + ungrabstackstr(p, dest); + + p = in.buf; + nnl = 0; + if (!quoted && flag & EXP_SPLIT) + ifs = ifsset() ? ifsval() : " \t\n"; + else + ifs = ""; + /* Don't copy 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 (lastc == '\n') { + nnl++; + } else { + if (nnl > 0) { + if (strchr(ifs, '\n') != NULL) { + NEXTWORD('\n', flag, dest, dst); + nnl = 0; + } else { + CHECKSTRSPACE(nnl + 2, dest); + while (nnl > 0) { + nnl--; + USTPUTC('\n', dest); + } + } + } + if (strchr(ifs, lastc) != NULL) + NEXTWORD(lastc, flag, dest, dst); + else { + CHECKSTRSPACE(2, dest); + if (quotes && syntax[(int)lastc] == CCTL) + USTPUTC(CTLESC, dest); + USTPUTC(lastc, 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: size=%td: \"%.*s\"\n", + ((dest - stackblock()) - startloc), + (int)((dest - stackblock()) - startloc), + stackblock() + startloc)); + argbackq = saveargbackq; + 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 void +subevalvar_trim(const char *p, int strloc, int subtype, int startloc) +{ + char *startp; + char *loc = NULL; + char *str; + int c = 0; + struct nodelist *saveargbackq = argbackq; + int amount; + + argstr(p, EXP_CASE | EXP_TILDE, NULL); + STACKSTRNUL(expdest); + argbackq = saveargbackq; + 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; + } + *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; + } + *loc = c; + loc--; + } + break; + + case VSTRIMRIGHT: + for (loc = str - 1; loc >= startp;) { + if (patmatch(str, loc)) { + amount = loc - expdest; + STADJUST(amount, expdest); + return; + } + loc--; + } + break; + + case VSTRIMRIGHTMAX: + for (loc = startp; loc < str - 1; loc++) { + if (patmatch(str, loc)) { + amount = loc - expdest; + STADJUST(amount, expdest); + return; + } + } + break; + + + default: + abort(); + } + amount = (expdest - stackblock() - strloc) + 1; + STADJUST(-amount, expdest); +} + + +static int +subevalvar_misc(const char *p, const char *var, int subtype, int startloc, + int varflags) +{ + char *startp; + struct nodelist *saveargbackq = argbackq; + int amount; + + argstr(p, EXP_TILDE, NULL); + STACKSTRNUL(expdest); + argbackq = saveargbackq; + startp = stackblock() + startloc; + + switch (subtype) { + case VSASSIGN: + setvar(var, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + return 1; + + 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 " : ""); + return 0; + + default: + abort(); + } +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +static const char * +evalvar(const char *p, 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; +again: /* jump here after setting a variable with ${var=text} */ + 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: + break; + + case VSPLUS: + case VSMINUS: + if (!set) { + argstr(p, flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) | + (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0), dst); + break; + } + break; + + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + if (!set) { + set = 1; + break; + } + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - stackblock(); + subevalvar_trim(p, patloc, subtype, startloc); + reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst); + if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE) + dst->state = WORD_QUOTEMARK; + break; + + case VSASSIGN: + case VSQUESTION: + if (!set) { + if (subevalvar_misc(p, var, subtype, startloc, + varflags)) { + varflags &= ~VSNUL; + goto again; + } + break; + } + break; + + case VSERROR: + c = p - var - 1; + error("${%.*s%s}: Bad substitution", c, var, + (c > 0 && *p != CTLENDVAR) ? "..." : ""); + + default: + abort(); + } + + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) + 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 specialized variable 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 specialized variable 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; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + argstr(pattern->narg.text, 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/bin/sh/expand.h b/bin/sh/expand.h new file mode 100644 index 0000000..7b58274 --- /dev/null +++ b/bin/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. + * 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. + * + * @(#)expand.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/expand.h 292994 2015-12-31 18:47:54Z jilles $ + */ + +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/bin/sh/funcs/cmv b/bin/sh/funcs/cmv new file mode 100644 index 0000000..b997806 --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)cmv 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/funcs/cmv 139969 2005-01-10 08:39:26Z 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/bin/sh/funcs/dirs b/bin/sh/funcs/dirs new file mode 100644 index 0000000..9c83f4a --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)dirs 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/funcs/dirs 139969 2005-01-10 08:39:26Z 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/bin/sh/funcs/login b/bin/sh/funcs/login new file mode 100644 index 0000000..274b94c --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)login 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/funcs/login 139969 2005-01-10 08:39:26Z imp $ + +# replaces the login builtin in the BSD shell +login () exec login "$@" diff --git a/bin/sh/funcs/newgrp b/bin/sh/funcs/newgrp new file mode 100644 index 0000000..e1e3bca --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)newgrp 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/funcs/newgrp 139969 2005-01-10 08:39:26Z imp $ + +newgrp() exec newgrp "$@" diff --git a/bin/sh/funcs/popd b/bin/sh/funcs/popd new file mode 100644 index 0000000..329e497 --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)popd 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/funcs/popd 139969 2005-01-10 08:39:26Z 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/bin/sh/funcs/pushd b/bin/sh/funcs/pushd new file mode 100644 index 0000000..4e494d7 --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)pushd 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/funcs/pushd 139969 2005-01-10 08:39:26Z 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/bin/sh/funcs/suspend b/bin/sh/funcs/suspend new file mode 100644 index 0000000..21896e7 --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)suspend 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/funcs/suspend 215268 2010-11-13 22:20:46Z jilles $ + +suspend() { + local - + set +m + kill -TSTP 0 +} diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c new file mode 100644 index 0000000..6e3255e --- /dev/null +++ b/bin/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. + * 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[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/histedit.c 296429 2016-03-06 17:24:02Z jilles $"); + +#include +#include +#include +#include +#include +#include +/* + * 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); + editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); + sprintf(editcmd, "%s %s", editor, editfile); + evalstring(editcmd, 0); /* XXX - should use no JC command */ + INTON; + 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/bin/sh/input.c b/bin/sh/input.c new file mode 100644 index 0000000..91b1508 --- /dev/null +++ b/bin/sh/input.c @@ -0,0 +1,521 @@ +/*- + * 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. + * 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[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/input.c 295937 2016-02-23 22:44:01Z jilles $"); + +#include /* defines BUFSIZ */ +#include +#include +#include +#include +#include + +/* + * 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 */ + +EditLine *el; /* cookie for editline package */ + +static void pushfile(void); +static int preadfd(void); +static void popstring(void); + +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); +out2fmt_flush("nr: %i", nr); + + 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) { +out2fmt_flush("%s:%i", __FILE__, __LINE__); + if ((parselleft = preadfd()) == -1) { + parselleft = parsenleft = EOF_NLEFT; + return PEOF; + } +out2fmt_flush("%s:%i", __FILE__, __LINE__); + } + + 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/bin/sh/input.h b/bin/sh/input.h new file mode 100644 index 0000000..8b6dffa --- /dev/null +++ b/bin/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. + * 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. + * + * @(#)input.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/input.h 271593 2014-09-14 16:46:30Z jilles $ + */ + +/* 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/bin/sh/jobs.c b/bin/sh/jobs.c new file mode 100644 index 0000000..b2353c7 --- /dev/null +++ b/bin/sh/jobs.c @@ -0,0 +1,1520 @@ +/*- + * 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. + * 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 + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/jobs.c 296326 2016-03-02 21:24:46Z jilles $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shell.h" +#if JOBS +#include +#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" + + +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; + 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 = ""; + ps = jp->ps + jp->nprocs - 1; + if (jp->state == 0) { + statestr = "Running"; +#if JOBS + } else if (jp->state == JOBSTOPPED) { + 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(ps->status)) { + if (WEXITSTATUS(ps->status) == 0) + statestr = "Done"; + else { + fmtstr(statebuf, sizeof(statebuf), "Done(%d)", + WEXITSTATUS(ps->status)); + statestr = statebuf; + } + } else { + i = WTERMSIG(ps->status); + statestr = strsignal(i); + if (statestr == NULL) + statestr = "Unknown signal"; + if (WCOREDUMP(ps->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(); +out2fmt_flush("pid: %i", pid); + 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; +out2fmt_flush("%s:%i", __FILE__, __LINE__); + + 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)) { +out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + _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 *origstatus) +{ +#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 (origstatus != NULL) + *origstatus = 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/bin/sh/jobs.h b/bin/sh/jobs.h new file mode 100644 index 0000000..451ff4e --- /dev/null +++ b/bin/sh/jobs.h @@ -0,0 +1,100 @@ +/*- + * 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. + * 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. + * + * @(#)jobs.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/jobs.h 255157 2013-09-02 21:57:46Z 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 /* for sig_atomic_t */ + +/* + * 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 +}; + +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/bin/sh/mail.c b/bin/sh/mail.c new file mode 100644 index 0000000..3bfe57d --- /dev/null +++ b/bin/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. + * 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[] = "@(#)mail.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/mail.c 278820 2015-02-15 21:47:43Z jilles $"); + +/* + * 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 +#include +#include + + +#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/bin/sh/mail.h b/bin/sh/mail.h new file mode 100644 index 0000000..88d85a7 --- /dev/null +++ b/bin/sh/mail.h @@ -0,0 +1,36 @@ +/*- + * 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. + * 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. + * + * @(#)mail.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/mail.h 127958 2004-04-06 20:06:54Z markm $ + */ + +void chkmail(int); diff --git a/bin/sh/main.c b/bin/sh/main.c new file mode 100644 index 0000000..f71a9ee --- /dev/null +++ b/bin/sh/main.c @@ -0,0 +1,361 @@ +/*- + * 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. + * 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) 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 +__FBSDID("$FreeBSD: releng/11.1/bin/sh/main.c 265772 2014-05-09 13:27:30Z jilles $"); + +#include +#include +#include +#include +#include +#include +#include + +#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 (;;) { + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + if (pendingsig) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1, SHOWJOBS_DEFAULT); + chkmail(0); + flushout(&output); + } + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + n = parsecmd(inter); + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + /* showtree(n); DEBUG */ + if (n == NEOF) { + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + if (!top || numeof >= 50) { + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + break; +} + if (!stoppedjobs()) { + if (!Iflag) { + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + 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); + } + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + popstackmark(&smark); + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + setstackmark(&smark); + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + if (evalskip != 0) { + if (evalskip == SKIPRETURN) + evalskip = 0; + break; + } + out2fmt_flush("PSIG: %i:%i]", pendingsig, __LINE__); + } + 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/bin/sh/main.h b/bin/sh/main.h new file mode 100644 index 0000000..5cf60ff --- /dev/null +++ b/bin/sh/main.h @@ -0,0 +1,40 @@ +/*- + * 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. + * 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. + * + * @(#)main.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/main.h 229220 2012-01-01 22:17:12Z jilles $ + */ + +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/bin/sh/memalloc.c b/bin/sh/memalloc.c new file mode 100644 index 0000000..6cf0755 --- /dev/null +++ b/bin/sh/memalloc.c @@ -0,0 +1,342 @@ +/*- + * 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. + * 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[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/memalloc.c 278818 2015-02-15 21:41:29Z jilles $"); + +#include +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "expand.h" +#include +#include + +/* + * 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/bin/sh/memalloc.h b/bin/sh/memalloc.h new file mode 100644 index 0000000..e0bfa0f --- /dev/null +++ b/bin/sh/memalloc.h @@ -0,0 +1,86 @@ +/*- + * 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. + * 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. + * + * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/memalloc.h 278818 2015-02-15 21:41:29Z jilles $ + */ + +#include + +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/bin/sh/miscbltin.c b/bin/sh/miscbltin.c new file mode 100644 index 0000000..3a26df9 --- /dev/null +++ b/bin/sh/miscbltin.c @@ -0,0 +1,534 @@ +/*- + * 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. + * 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[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/miscbltin.c 301452 2016-06-05 16:09:31Z jilles $"); + +/* + * Miscellaneous builtins. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + +out2fmt_flush("nread: %i", nread); + 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; + int 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 (val < 0 || (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/bin/sh/mkbuiltins b/bin/sh/mkbuiltins new file mode 100755 index 0000000..eab2db3 --- /dev/null +++ b/bin/sh/mkbuiltins @@ -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. +# 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. +# +# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/mkbuiltins 293118 2016-01-03 21:30:22Z jilles $ + +temp=`/usr/bin/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 +#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 { + 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 +! +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 +rm -f $temp diff --git a/bin/sh/mknodes.c b/bin/sh/mknodes.c new file mode 100644 index 0000000..74391cb --- /dev/null +++ b/bin/sh/mknodes.c @@ -0,0 +1,462 @@ +/*- + * 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. + * 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\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 + +__FBSDID("$FreeBSD: releng/11.1/bin/sh/mknodes.c 292517 2015-12-20 16:40:36Z jilles $"); + +/* + * This program reads the nodetypes file and nodes.c.pat file. It generates + * the files nodes.h and nodes.c. + */ + +#include +#include +#include +#include +#include + +#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]); +//out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + 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); + +//out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + 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/bin/sh/mksyntax.c b/bin/sh/mksyntax.c new file mode 100644 index 0000000..2374167 --- /dev/null +++ b/bin/sh/mksyntax.c @@ -0,0 +1,332 @@ +/*- + * 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. + * 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\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 +__FBSDID("$FreeBSD: releng/11.1/bin/sh/mksyntax.c 279569 2015-03-03 21:21:43Z jilles $"); + +/* + * This program creates syntax.h and syntax.c. + */ + +#include +#include +#include +#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"); +//out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + exit(2); + } + if ((hfile = fopen("syntax.h", "w")) == NULL) { + perror("syntax.h"); +//out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + exit(2); + } + fputs(writer, hfile); + fputs(writer, cfile); + + fputs("#include \n", hfile); + fputs("#include \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(); + +//out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + 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/bin/sh/mktokens b/bin/sh/mktokens new file mode 100644 index 0000000..4a1b97d --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)mktokens 8.1 (Berkeley) 5/31/93 +# $FreeBSD: releng/11.1/bin/sh/mktokens 279503 2015-03-01 21:46:55Z jilles $ + +# 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=`/usr/bin/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/bin/sh/myhistedit.h b/bin/sh/myhistedit.h new file mode 100644 index 0000000..940a47f --- /dev/null +++ b/bin/sh/myhistedit.h @@ -0,0 +1,42 @@ +/*- + * 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. + * + * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/myhistedit.h 229220 2012-01-01 22:17:12Z jilles $ + */ + +#include + +extern History *hist; +extern EditLine *el; +extern int displayhist; + +void histedit(void); +void sethistsize(const char *); +void setterm(const char *); + diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c new file mode 100644 index 0000000..2ca13c4 --- /dev/null +++ b/bin/sh/mystring.c @@ -0,0 +1,98 @@ +/*- + * 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. + * 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[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/mystring.c 270102 2014-08-17 16:40:29Z jilles $"); + +/* + * 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 +#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/bin/sh/mystring.h b/bin/sh/mystring.h new file mode 100644 index 0000000..5bc6116 --- /dev/null +++ b/bin/sh/mystring.h @@ -0,0 +1,41 @@ +/*- + * 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. + * 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. + * + * @(#)mystring.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/mystring.h 268920 2014-07-20 12:06:52Z jilles $ + */ + +#include + +int number(const char *); +int is_number(const char *); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) diff --git a/bin/sh/nodes.c.pat b/bin/sh/nodes.c.pat new file mode 100644 index 0000000..b151f98 --- /dev/null +++ b/bin/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. + * 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. + * + * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/nodes.c.pat 291267 2015-11-24 22:47:19Z jilles $ + */ + +#include +#include +#include +/* + * 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/bin/sh/nodetypes b/bin/sh/nodetypes new file mode 100644 index 0000000..8d86e36 --- /dev/null +++ b/bin/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. +# 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. +# +# @(#)nodetypes 8.2 (Berkeley) 5/4/95 +# $FreeBSD: releng/11.1/bin/sh/nodetypes 278820 2015-02-15 21:47:43Z jilles $ + +# 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< +__FBSDID("$FreeBSD: releng/11.1/bin/sh/options.c 296577 2016-03-09 21:00:57Z jilles $"); + +#include +#include +#include + +#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; +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); + privileged = (getuid() != geteuid() || getgid() != getegid()); +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); + options(1); +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); +out2fmt_flush("*argsptr: 0x%X, minusc: 0x%X, sflag: %i", (uint32_t)*argptr, (uint32_t)minusc, sflag); + if (*argptr != NULL) + out2fmt_flush("argptr: %s", *argptr); + if (*argptr == NULL && minusc == NULL) + sflag = 1; + if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) { + iflag = 1; + if (Eflag == 2) + Eflag = 1; + } +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); + if (mflag == 2) + mflag = iflag; + for (i = 0; i < NOPTS; i++) + if (optval[i] == 2) + optval[i] = 0; + arg0 = argv[0]; +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); + if (sflag == 0 && minusc == NULL) { + scriptname = *argptr++; + setinputfile(scriptname, 0); + commandname = arg0 = scriptname; + } +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); + /* 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); */ +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); +out2fmt_flush("H>%s:%i", __FILE__, __LINE__); +out2fmt_flush("*argsptr: 0x%X, minusc: 0x%X, sflag: %i", (uint32_t)*argptr, (uint32_t)minusc, sflag); +} + + +void +optschanged(void) +{ + setinteractive(iflag); +#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. + */ + +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; +#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ + if (*p == '\0') +#endif + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; +#ifdef NOHACK + break; +#endif + } 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 command builtin. + */ + +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 return the character, 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/bin/sh/options.h b/bin/sh/options.h new file mode 100644 index 0000000..4294484 --- /dev/null +++ b/bin/sh/options.h @@ -0,0 +1,113 @@ +/*- + * 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. + * 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. + * + * @(#)options.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/options.h 294348 2016-01-19 22:41:26Z jilles $ + */ + +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/bin/sh/output.c b/bin/sh/output.c new file mode 100644 index 0000000..36fc176 --- /dev/null +++ b/bin/sh/output.c @@ -0,0 +1,375 @@ +/*- + * 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. + * 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[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/output.c 275766 2014-12-14 16:26:19Z jilles $"); + +/* + * 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 /* defines BUFSIZ */ +#include +#include +#include +#include +#include +#include +#include + +#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, 0, NULL, OUTBUFSIZ, 1, 0}; +struct output errout = {NULL, 0, NULL, 256, 2, 0}; +struct output memout = {NULL, 0, NULL, 0, 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; + + if (dest->buf == NULL) { + INTOFF; + dest->buf = ckmalloc(dest->bufsize); + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; + INTON; + } else if (dest->fd == MEM_OUT) { + offset = dest->bufsize; + INTOFF; + dest->bufsize <<= 1; + dest->buf = ckrealloc(dest->buf, dest->bufsize); + dest->nleft = dest->bufsize - offset; + dest->nextc = dest->buf + offset; + INTON; + } else { + flushout(dest); + } + dest->nleft--; +} + + +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; + dest->nleft = dest->bufsize; +} + + +void +freestdout(void) +{ + INTOFF; + if (output.buf) { + ckfree(output.buf); + output.buf = NULL; + output.nleft = 0; + } + INTON; +} + + +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/bin/sh/output.h b/bin/sh/output.h new file mode 100644 index 0000000..ef9d478 --- /dev/null +++ b/bin/sh/output.h @@ -0,0 +1,83 @@ +/*- + * 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. + * 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. + * + * @(#)output.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/output.h 244162 2012-12-12 22:01:10Z jilles $ + */ + +#ifndef OUTPUT_INCL + +#include +#include + +struct output { + char *nextc; + int nleft; + 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)->nleft < 0? (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/bin/sh/parser.c b/bin/sh/parser.c new file mode 100644 index 0000000..c56b357 --- /dev/null +++ b/bin/sh/parser.c @@ -0,0 +1,2119 @@ +/*- + * 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. + * 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[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/parser.c 301139 2016-06-01 16:56:29Z truckman $"); + +#include +#include +#include + +#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(); +out2fmt_flush("xx: (%c)", c); + 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/bin/sh/parser.h b/bin/sh/parser.h new file mode 100644 index 0000000..39646a7 --- /dev/null +++ b/bin/sh/parser.h @@ -0,0 +1,85 @@ +/*- + * 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. + * 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. + * + * @(#)parser.h 8.3 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/parser.h 288430 2015-09-30 21:32:29Z jilles $ + */ + +/* 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/bin/sh/redir.c b/bin/sh/redir.c new file mode 100644 index 0000000..6ef85ae --- /dev/null +++ b/bin/sh/redir.c @@ -0,0 +1,366 @@ +/*- + * 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. + * 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 + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/redir.c 295085 2016-01-30 21:21:25Z jilles $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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); +out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + _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/bin/sh/redir.h b/bin/sh/redir.h new file mode 100644 index 0000000..0b67d55 --- /dev/null +++ b/bin/sh/redir.h @@ -0,0 +1,45 @@ +/*- + * 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. + * 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. + * + * @(#)redir.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/redir.h 254426 2013-08-16 20:24:41Z jilles $ + */ + +/* 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/bin/sh/sh.1 b/bin/sh/sh.1 new file mode 100644 index 0000000..f8b0861 --- /dev/null +++ b/bin/sh/sh.1 @@ -0,0 +1,2874 @@ +.\"- +.\" 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. +.\" 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: @(#)sh.1 8.6 (Berkeley) 5/4/95 +.\" $FreeBSD: releng/11.1/bin/sh/sh.1 313868 2017-02-17 11:20:55Z bapt $ +.\" +.Dd May 30, 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 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 Ns ) 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. +The default value is +.Aq space , +.Aq tab , +and +.Aq newline +in that order. +This default 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. +.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 Commandline 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/bin/sh/shell.h b/bin/sh/shell.h new file mode 100644 index 0000000..09c2c1e --- /dev/null +++ b/bin/sh/shell.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. + * 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. + * + * @(#)shell.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/shell.h 253658 2013-07-25 19:48:15Z jilles $ + */ + +#ifndef SHELL_H_ +#define SHELL_H_ + +#include + +/* + * 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 + +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/bin/sh/show.c b/bin/sh/show.c new file mode 100644 index 0000000..3dede99 --- /dev/null +++ b/bin/sh/show.c @@ -0,0 +1,408 @@ +/*- + * 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. + * 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[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/show.c 258776 2013-11-30 21:27:11Z jilles $"); + +#include +#include +#include +#include +#include + +#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, "", 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("\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); + } + 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/bin/sh/show.h b/bin/sh/show.h new file mode 100644 index 0000000..0081b9c --- /dev/null +++ b/bin/sh/show.h @@ -0,0 +1,40 @@ +/*- + * 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. + * 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. + * + * @(#)show.h 1.1 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/show.h 200956 2009-12-24 18:41:14Z jilles $ + */ + +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/bin/sh/tests/Makefile b/bin/sh/tests/Makefile new file mode 100644 index 0000000..c51a4a6 --- /dev/null +++ b/bin/sh/tests/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/Makefile 299094 2016-05-04 23:20:53Z ngie $ + +.include + +TESTS_SUBDIRS+= builtins +TESTS_SUBDIRS+= errors +TESTS_SUBDIRS+= execution +TESTS_SUBDIRS+= expansion +TESTS_SUBDIRS+= parameters +TESTS_SUBDIRS+= parser +TESTS_SUBDIRS+= set-e + +.include diff --git a/bin/sh/tests/Makefile.depend b/bin/sh/tests/Makefile.depend new file mode 100644 index 0000000..888b1c9 --- /dev/null +++ b/bin/sh/tests/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/builtins/Makefile b/bin/sh/tests/builtins/Makefile new file mode 100644 index 0000000..61e4090 --- /dev/null +++ b/bin/sh/tests/builtins/Makefile @@ -0,0 +1,179 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/Makefile 318279 2017-05-14 20:17:50Z jilles $ + +PACKAGE= tests + +.include + +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+= 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+= 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+= 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 diff --git a/bin/sh/tests/builtins/Makefile.depend b/bin/sh/tests/builtins/Makefile.depend new file mode 100644 index 0000000..549a228 --- /dev/null +++ b/bin/sh/tests/builtins/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/builtins/alias.0 b/bin/sh/tests/builtins/alias.0 new file mode 100644 index 0000000..2dffb48 --- /dev/null +++ b/bin/sh/tests/builtins/alias.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/alias.0.stdout b/bin/sh/tests/builtins/alias.0.stdout new file mode 100644 index 0000000..52efaf0 --- /dev/null +++ b/bin/sh/tests/builtins/alias.0.stdout @@ -0,0 +1,4 @@ +bar='' +foo=bar +quux='1 2 3' +foo=bar diff --git a/bin/sh/tests/builtins/alias.1 b/bin/sh/tests/builtins/alias.1 new file mode 100644 index 0000000..43339b5 --- /dev/null +++ b/bin/sh/tests/builtins/alias.1 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/alias.1 149781 2005-09-04 11:59:59Z stefanf $ +unalias -a +alias foo diff --git a/bin/sh/tests/builtins/alias.1.stderr b/bin/sh/tests/builtins/alias.1.stderr new file mode 100644 index 0000000..c9f4011 --- /dev/null +++ b/bin/sh/tests/builtins/alias.1.stderr @@ -0,0 +1 @@ +alias: foo: not found diff --git a/bin/sh/tests/builtins/alias3.0 b/bin/sh/tests/builtins/alias3.0 new file mode 100644 index 0000000..00c0c98 --- /dev/null +++ b/bin/sh/tests/builtins/alias3.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/alias3.0.stdout b/bin/sh/tests/builtins/alias3.0.stdout new file mode 100644 index 0000000..52efaf0 --- /dev/null +++ b/bin/sh/tests/builtins/alias3.0.stdout @@ -0,0 +1,4 @@ +bar='' +foo=bar +quux='1 2 3' +foo=bar diff --git a/bin/sh/tests/builtins/alias4.0 b/bin/sh/tests/builtins/alias4.0 new file mode 100644 index 0000000..59a789b --- /dev/null +++ b/bin/sh/tests/builtins/alias4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/alias4.0 254849 2013-08-25 11:42:53Z jilles $ + +unalias -a +alias -- diff --git a/bin/sh/tests/builtins/break1.0 b/bin/sh/tests/builtins/break1.0 new file mode 100644 index 0000000..7342416 --- /dev/null +++ b/bin/sh/tests/builtins/break1.0 @@ -0,0 +1,16 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/break2.0 b/bin/sh/tests/builtins/break2.0 new file mode 100644 index 0000000..d39169f --- /dev/null +++ b/bin/sh/tests/builtins/break2.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/break2.0.stdout b/bin/sh/tests/builtins/break2.0.stdout new file mode 100644 index 0000000..12799cc --- /dev/null +++ b/bin/sh/tests/builtins/break2.0.stdout @@ -0,0 +1 @@ +good diff --git a/bin/sh/tests/builtins/break3.0 b/bin/sh/tests/builtins/break3.0 new file mode 100644 index 0000000..30c88d7 --- /dev/null +++ b/bin/sh/tests/builtins/break3.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/break4.4 b/bin/sh/tests/builtins/break4.4 new file mode 100644 index 0000000..ea126cc --- /dev/null +++ b/bin/sh/tests/builtins/break4.4 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/break5.4 b/bin/sh/tests/builtins/break5.4 new file mode 100644 index 0000000..1ce5971 --- /dev/null +++ b/bin/sh/tests/builtins/break5.4 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/break6.0 b/bin/sh/tests/builtins/break6.0 new file mode 100644 index 0000000..43ddc43 --- /dev/null +++ b/bin/sh/tests/builtins/break6.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/builtin1.0 b/bin/sh/tests/builtins/builtin1.0 new file mode 100644 index 0000000..03a1730 --- /dev/null +++ b/bin/sh/tests/builtins/builtin1.0 @@ -0,0 +1,31 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case1.0 b/bin/sh/tests/builtins/case1.0 new file mode 100644 index 0000000..aa9c0c4 --- /dev/null +++ b/bin/sh/tests/builtins/case1.0 @@ -0,0 +1,13 @@ +#$FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case10.0 b/bin/sh/tests/builtins/case10.0 new file mode 100644 index 0000000..ef8664b --- /dev/null +++ b/bin/sh/tests/builtins/case10.0 @@ -0,0 +1,16 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case11.0 b/bin/sh/tests/builtins/case11.0 new file mode 100644 index 0000000..d126c3e --- /dev/null +++ b/bin/sh/tests/builtins/case11.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/case11.0 228007 2011-11-26 22:28:25Z jilles $ + +false +case x in +*) +esac diff --git a/bin/sh/tests/builtins/case12.0 b/bin/sh/tests/builtins/case12.0 new file mode 100644 index 0000000..6893da3 --- /dev/null +++ b/bin/sh/tests/builtins/case12.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/case12.0 228007 2011-11-26 22:28:25Z jilles $ + +false +case x in +y) +esac diff --git a/bin/sh/tests/builtins/case13.0 b/bin/sh/tests/builtins/case13.0 new file mode 100644 index 0000000..e6074dc --- /dev/null +++ b/bin/sh/tests/builtins/case13.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case14.0 b/bin/sh/tests/builtins/case14.0 new file mode 100644 index 0000000..70b1f20 --- /dev/null +++ b/bin/sh/tests/builtins/case14.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/case14.0 230154 2012-01-15 20:04:05Z jilles $ + +case `false` in +no) exit 3 ;; +esac diff --git a/bin/sh/tests/builtins/case15.0 b/bin/sh/tests/builtins/case15.0 new file mode 100644 index 0000000..ca25cc6 --- /dev/null +++ b/bin/sh/tests/builtins/case15.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/case15.0 230154 2012-01-15 20:04:05Z jilles $ + +case x in +`false`) exit 3 ;; +esac diff --git a/bin/sh/tests/builtins/case16.0 b/bin/sh/tests/builtins/case16.0 new file mode 100644 index 0000000..54e11d1 --- /dev/null +++ b/bin/sh/tests/builtins/case16.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case17.0 b/bin/sh/tests/builtins/case17.0 new file mode 100644 index 0000000..22857a9 --- /dev/null +++ b/bin/sh/tests/builtins/case17.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/case17.0 230161 2012-01-15 21:39:38Z jilles $ + +! case x in x) false ;& y) esac diff --git a/bin/sh/tests/builtins/case18.0 b/bin/sh/tests/builtins/case18.0 new file mode 100644 index 0000000..ed73e1e --- /dev/null +++ b/bin/sh/tests/builtins/case18.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case19.0 b/bin/sh/tests/builtins/case19.0 new file mode 100644 index 0000000..48bae1e --- /dev/null +++ b/bin/sh/tests/builtins/case19.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case2.0 b/bin/sh/tests/builtins/case2.0 new file mode 100644 index 0000000..1d1261e --- /dev/null +++ b/bin/sh/tests/builtins/case2.0 @@ -0,0 +1,106 @@ +# Generated by ./test-fnmatch -s 1, do not edit. +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case20.0 b/bin/sh/tests/builtins/case20.0 new file mode 100644 index 0000000..ede1cd9 --- /dev/null +++ b/bin/sh/tests/builtins/case20.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case3.0 b/bin/sh/tests/builtins/case3.0 new file mode 100644 index 0000000..495a1c6 --- /dev/null +++ b/bin/sh/tests/builtins/case3.0 @@ -0,0 +1,95 @@ +# Generated by ./test-fnmatch -s 2, do not edit. +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case4.0 b/bin/sh/tests/builtins/case4.0 new file mode 100644 index 0000000..f56424b --- /dev/null +++ b/bin/sh/tests/builtins/case4.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case5.0 b/bin/sh/tests/builtins/case5.0 new file mode 100644 index 0000000..ce13a7f --- /dev/null +++ b/bin/sh/tests/builtins/case5.0 @@ -0,0 +1,57 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case6.0 b/bin/sh/tests/builtins/case6.0 new file mode 100644 index 0000000..e38ed09 --- /dev/null +++ b/bin/sh/tests/builtins/case6.0 @@ -0,0 +1,52 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case7.0 b/bin/sh/tests/builtins/case7.0 new file mode 100644 index 0000000..8108705 --- /dev/null +++ b/bin/sh/tests/builtins/case7.0 @@ -0,0 +1,24 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/case7.0 223011 2011-06-12 12:55:46Z jilles $ + +# 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/bin/sh/tests/builtins/case8.0 b/bin/sh/tests/builtins/case8.0 new file mode 100644 index 0000000..1837c9a --- /dev/null +++ b/bin/sh/tests/builtins/case8.0 @@ -0,0 +1,32 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/case9.0 b/bin/sh/tests/builtins/case9.0 new file mode 100644 index 0000000..6184396 --- /dev/null +++ b/bin/sh/tests/builtins/case9.0 @@ -0,0 +1,39 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd1.0 b/bin/sh/tests/builtins/cd1.0 new file mode 100644 index 0000000..db7904c --- /dev/null +++ b/bin/sh/tests/builtins/cd1.0 @@ -0,0 +1,30 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd2.0 b/bin/sh/tests/builtins/cd2.0 new file mode 100644 index 0000000..fd3b61d --- /dev/null +++ b/bin/sh/tests/builtins/cd2.0 @@ -0,0 +1,16 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd3.0 b/bin/sh/tests/builtins/cd3.0 new file mode 100644 index 0000000..557fe59 --- /dev/null +++ b/bin/sh/tests/builtins/cd3.0 @@ -0,0 +1,21 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd4.0 b/bin/sh/tests/builtins/cd4.0 new file mode 100644 index 0000000..91e7b2e --- /dev/null +++ b/bin/sh/tests/builtins/cd4.0 @@ -0,0 +1,38 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd5.0 b/bin/sh/tests/builtins/cd5.0 new file mode 100644 index 0000000..c5f67d5 --- /dev/null +++ b/bin/sh/tests/builtins/cd5.0 @@ -0,0 +1,23 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd6.0 b/bin/sh/tests/builtins/cd6.0 new file mode 100644 index 0000000..110775d --- /dev/null +++ b/bin/sh/tests/builtins/cd6.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd7.0 b/bin/sh/tests/builtins/cd7.0 new file mode 100644 index 0000000..6677a6f --- /dev/null +++ b/bin/sh/tests/builtins/cd7.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd8.0 b/bin/sh/tests/builtins/cd8.0 new file mode 100644 index 0000000..ee0a1ae --- /dev/null +++ b/bin/sh/tests/builtins/cd8.0 @@ -0,0 +1,26 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd9.0 b/bin/sh/tests/builtins/cd9.0 new file mode 100644 index 0000000..e2c55fc --- /dev/null +++ b/bin/sh/tests/builtins/cd9.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/cd9.0.stdout b/bin/sh/tests/builtins/cd9.0.stdout new file mode 100644 index 0000000..dac16a7 --- /dev/null +++ b/bin/sh/tests/builtins/cd9.0.stdout @@ -0,0 +1,2 @@ +/dev +/bin diff --git a/bin/sh/tests/builtins/command1.0 b/bin/sh/tests/builtins/command1.0 new file mode 100644 index 0000000..4977a32 --- /dev/null +++ b/bin/sh/tests/builtins/command1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/command1.0 151797 2005-10-28 14:02:42Z stefanf $ +true() { + false +} +command true diff --git a/bin/sh/tests/builtins/command10.0 b/bin/sh/tests/builtins/command10.0 new file mode 100644 index 0000000..9b18c0d --- /dev/null +++ b/bin/sh/tests/builtins/command10.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command11.0 b/bin/sh/tests/builtins/command11.0 new file mode 100644 index 0000000..21ee477 --- /dev/null +++ b/bin/sh/tests/builtins/command11.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command12.0 b/bin/sh/tests/builtins/command12.0 new file mode 100644 index 0000000..48ad8f3 --- /dev/null +++ b/bin/sh/tests/builtins/command12.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command2.0 b/bin/sh/tests/builtins/command2.0 new file mode 100644 index 0000000..be7d04a --- /dev/null +++ b/bin/sh/tests/builtins/command2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/command2.0 151797 2005-10-28 14:02:42Z stefanf $ +PATH= +command -p cat < /dev/null diff --git a/bin/sh/tests/builtins/command3.0 b/bin/sh/tests/builtins/command3.0 new file mode 100644 index 0000000..7f032fd --- /dev/null +++ b/bin/sh/tests/builtins/command3.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command3.0.stdout b/bin/sh/tests/builtins/command3.0.stdout new file mode 100644 index 0000000..67b8691 --- /dev/null +++ b/bin/sh/tests/builtins/command3.0.stdout @@ -0,0 +1,7 @@ +/bin/ls +true +/bin/ls +fun +break +if +alias foo=bar diff --git a/bin/sh/tests/builtins/command4.0 b/bin/sh/tests/builtins/command4.0 new file mode 100644 index 0000000..1fb7784 --- /dev/null +++ b/bin/sh/tests/builtins/command4.0 @@ -0,0 +1,2 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/command4.0 211973 2010-08-29 20:53:24Z jilles $ +! command -v nonexisting diff --git a/bin/sh/tests/builtins/command5.0 b/bin/sh/tests/builtins/command5.0 new file mode 100644 index 0000000..8438643 --- /dev/null +++ b/bin/sh/tests/builtins/command5.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command5.0.stdout b/bin/sh/tests/builtins/command5.0.stdout new file mode 100644 index 0000000..523f7b2 --- /dev/null +++ b/bin/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/bin/sh/tests/builtins/command6.0 b/bin/sh/tests/builtins/command6.0 new file mode 100644 index 0000000..c11258e --- /dev/null +++ b/bin/sh/tests/builtins/command6.0 @@ -0,0 +1,22 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command6.0.stdout b/bin/sh/tests/builtins/command6.0.stdout new file mode 100644 index 0000000..3180207 --- /dev/null +++ b/bin/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/bin/sh/tests/builtins/command7.0 b/bin/sh/tests/builtins/command7.0 new file mode 100644 index 0000000..1a50d67 --- /dev/null +++ b/bin/sh/tests/builtins/command7.0 @@ -0,0 +1,34 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command8.0 b/bin/sh/tests/builtins/command8.0 new file mode 100644 index 0000000..96a2f3e --- /dev/null +++ b/bin/sh/tests/builtins/command8.0 @@ -0,0 +1,45 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/command9.0 b/bin/sh/tests/builtins/command9.0 new file mode 100644 index 0000000..0848c6c --- /dev/null +++ b/bin/sh/tests/builtins/command9.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/dot1.0 b/bin/sh/tests/builtins/dot1.0 new file mode 100644 index 0000000..29bc67d --- /dev/null +++ b/bin/sh/tests/builtins/dot1.0 @@ -0,0 +1,21 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/dot2.0 b/bin/sh/tests/builtins/dot2.0 new file mode 100644 index 0000000..e0ec0e8 --- /dev/null +++ b/bin/sh/tests/builtins/dot2.0 @@ -0,0 +1,21 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/dot3.0 b/bin/sh/tests/builtins/dot3.0 new file mode 100644 index 0000000..bf3e30c --- /dev/null +++ b/bin/sh/tests/builtins/dot3.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/dot4.0 b/bin/sh/tests/builtins/dot4.0 new file mode 100644 index 0000000..0eb2b3d --- /dev/null +++ b/bin/sh/tests/builtins/dot4.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/eval1.0 b/bin/sh/tests/builtins/eval1.0 new file mode 100644 index 0000000..2dde4e9 --- /dev/null +++ b/bin/sh/tests/builtins/eval1.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/eval2.0 b/bin/sh/tests/builtins/eval2.0 new file mode 100644 index 0000000..e7853ba --- /dev/null +++ b/bin/sh/tests/builtins/eval2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/eval2.0 194897 2009-06-24 20:22:54Z jilles $ + +eval ' +false + +' && exit 1 +exit 0 diff --git a/bin/sh/tests/builtins/eval3.0 b/bin/sh/tests/builtins/eval3.0 new file mode 100644 index 0000000..63efeb1 --- /dev/null +++ b/bin/sh/tests/builtins/eval3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/eval4.0 b/bin/sh/tests/builtins/eval4.0 new file mode 100644 index 0000000..f9d1495 --- /dev/null +++ b/bin/sh/tests/builtins/eval4.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/eval5.0 b/bin/sh/tests/builtins/eval5.0 new file mode 100644 index 0000000..9b19f7a --- /dev/null +++ b/bin/sh/tests/builtins/eval5.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/eval6.0 b/bin/sh/tests/builtins/eval6.0 new file mode 100644 index 0000000..cc3d302 --- /dev/null +++ b/bin/sh/tests/builtins/eval6.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/eval7.0 b/bin/sh/tests/builtins/eval7.0 new file mode 100644 index 0000000..d45914b --- /dev/null +++ b/bin/sh/tests/builtins/eval7.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/eval8.7 b/bin/sh/tests/builtins/eval8.7 new file mode 100644 index 0000000..f2acd94 --- /dev/null +++ b/bin/sh/tests/builtins/eval8.7 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/eval8.7 272983 2014-10-12 13:12:06Z jilles $ + +f() { + eval "return 7 +echo bad2" +} +f diff --git a/bin/sh/tests/builtins/exec1.0 b/bin/sh/tests/builtins/exec1.0 new file mode 100644 index 0000000..894dde9 --- /dev/null +++ b/bin/sh/tests/builtins/exec1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/exec2.0 b/bin/sh/tests/builtins/exec2.0 new file mode 100644 index 0000000..31333dc --- /dev/null +++ b/bin/sh/tests/builtins/exec2.0 @@ -0,0 +1,25 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/exit1.0 b/bin/sh/tests/builtins/exit1.0 new file mode 100644 index 0000000..952de2c --- /dev/null +++ b/bin/sh/tests/builtins/exit1.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/exit2.8 b/bin/sh/tests/builtins/exit2.8 new file mode 100644 index 0000000..b5cf0b2 --- /dev/null +++ b/bin/sh/tests/builtins/exit2.8 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/exit3.0 b/bin/sh/tests/builtins/exit3.0 new file mode 100644 index 0000000..aefc8f9 --- /dev/null +++ b/bin/sh/tests/builtins/exit3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/export1.0 b/bin/sh/tests/builtins/export1.0 new file mode 100644 index 0000000..a1ed6b0 --- /dev/null +++ b/bin/sh/tests/builtins/export1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/fc1.0 b/bin/sh/tests/builtins/fc1.0 new file mode 100644 index 0000000..c7a934c --- /dev/null +++ b/bin/sh/tests/builtins/fc1.0 @@ -0,0 +1,27 @@ +# $FreeBSD: releng/11.1/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 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 4output 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&- + +rm input output error +rmdir ${P}/${T} +exit ${rc:-3} diff --git a/bin/sh/tests/builtins/for1.0 b/bin/sh/tests/builtins/for1.0 new file mode 100644 index 0000000..aae8e32 --- /dev/null +++ b/bin/sh/tests/builtins/for1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/for2.0 b/bin/sh/tests/builtins/for2.0 new file mode 100644 index 0000000..585f730 --- /dev/null +++ b/bin/sh/tests/builtins/for2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/for3.0 b/bin/sh/tests/builtins/for3.0 new file mode 100644 index 0000000..bd42e6b --- /dev/null +++ b/bin/sh/tests/builtins/for3.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts1.0 b/bin/sh/tests/builtins/getopts1.0 new file mode 100644 index 0000000..94e506e --- /dev/null +++ b/bin/sh/tests/builtins/getopts1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts1.0.stdout b/bin/sh/tests/builtins/getopts1.0.stdout new file mode 100644 index 0000000..a0a347e --- /dev/null +++ b/bin/sh/tests/builtins/getopts1.0.stdout @@ -0,0 +1,8 @@ +-1- +a +-2- +a +No arg for -b option +? +-3- +? diff --git a/bin/sh/tests/builtins/getopts10.0 b/bin/sh/tests/builtins/getopts10.0 new file mode 100644 index 0000000..9b4e877 --- /dev/null +++ b/bin/sh/tests/builtins/getopts10.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts2.0 b/bin/sh/tests/builtins/getopts2.0 new file mode 100644 index 0000000..338f570 --- /dev/null +++ b/bin/sh/tests/builtins/getopts2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts2.0.stdout b/bin/sh/tests/builtins/getopts2.0.stdout new file mode 100644 index 0000000..587be6b --- /dev/null +++ b/bin/sh/tests/builtins/getopts2.0.stdout @@ -0,0 +1 @@ +x diff --git a/bin/sh/tests/builtins/getopts3.0 b/bin/sh/tests/builtins/getopts3.0 new file mode 100644 index 0000000..0b6fecc --- /dev/null +++ b/bin/sh/tests/builtins/getopts3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts4.0 b/bin/sh/tests/builtins/getopts4.0 new file mode 100644 index 0000000..2b17123 --- /dev/null +++ b/bin/sh/tests/builtins/getopts4.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts5.0 b/bin/sh/tests/builtins/getopts5.0 new file mode 100644 index 0000000..16129d2 --- /dev/null +++ b/bin/sh/tests/builtins/getopts5.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts6.0 b/bin/sh/tests/builtins/getopts6.0 new file mode 100644 index 0000000..ccce079 --- /dev/null +++ b/bin/sh/tests/builtins/getopts6.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts7.0 b/bin/sh/tests/builtins/getopts7.0 new file mode 100644 index 0000000..4b355bb --- /dev/null +++ b/bin/sh/tests/builtins/getopts7.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts8.0 b/bin/sh/tests/builtins/getopts8.0 new file mode 100644 index 0000000..6d23fee --- /dev/null +++ b/bin/sh/tests/builtins/getopts8.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts8.0.stdout b/bin/sh/tests/builtins/getopts8.0.stdout new file mode 100644 index 0000000..f10cefc --- /dev/null +++ b/bin/sh/tests/builtins/getopts8.0.stdout @@ -0,0 +1,5 @@ +?:y +?:z +?:w +x:unset +OPTIND=3 diff --git a/bin/sh/tests/builtins/getopts9.0 b/bin/sh/tests/builtins/getopts9.0 new file mode 100644 index 0000000..3753554 --- /dev/null +++ b/bin/sh/tests/builtins/getopts9.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/getopts9.0.stdout b/bin/sh/tests/builtins/getopts9.0.stdout new file mode 100644 index 0000000..4d32063 --- /dev/null +++ b/bin/sh/tests/builtins/getopts9.0.stdout @@ -0,0 +1,3 @@ +0:a: +0:b: +1:?: diff --git a/bin/sh/tests/builtins/hash1.0 b/bin/sh/tests/builtins/hash1.0 new file mode 100644 index 0000000..9d74dcb --- /dev/null +++ b/bin/sh/tests/builtins/hash1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/hash1.0 149791 2005-09-05 09:42:10Z stefanf $ +cat /dev/null +hash +hash -r +hash diff --git a/bin/sh/tests/builtins/hash1.0.stdout b/bin/sh/tests/builtins/hash1.0.stdout new file mode 100644 index 0000000..3afc3e7 --- /dev/null +++ b/bin/sh/tests/builtins/hash1.0.stdout @@ -0,0 +1 @@ +/bin/cat diff --git a/bin/sh/tests/builtins/hash2.0 b/bin/sh/tests/builtins/hash2.0 new file mode 100644 index 0000000..1a7cf90 --- /dev/null +++ b/bin/sh/tests/builtins/hash2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/hash2.0 149791 2005-09-05 09:42:10Z stefanf $ +hash +hash cat +hash diff --git a/bin/sh/tests/builtins/hash2.0.stdout b/bin/sh/tests/builtins/hash2.0.stdout new file mode 100644 index 0000000..3afc3e7 --- /dev/null +++ b/bin/sh/tests/builtins/hash2.0.stdout @@ -0,0 +1 @@ +/bin/cat diff --git a/bin/sh/tests/builtins/hash3.0 b/bin/sh/tests/builtins/hash3.0 new file mode 100644 index 0000000..a742111 --- /dev/null +++ b/bin/sh/tests/builtins/hash3.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/hash3.0 149791 2005-09-05 09:42:10Z stefanf $ +hash -v cat +hash diff --git a/bin/sh/tests/builtins/hash3.0.stdout b/bin/sh/tests/builtins/hash3.0.stdout new file mode 100644 index 0000000..a34864c --- /dev/null +++ b/bin/sh/tests/builtins/hash3.0.stdout @@ -0,0 +1,2 @@ +/bin/cat +/bin/cat diff --git a/bin/sh/tests/builtins/hash4.0 b/bin/sh/tests/builtins/hash4.0 new file mode 100644 index 0000000..98764b3 --- /dev/null +++ b/bin/sh/tests/builtins/hash4.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/jobid1.0 b/bin/sh/tests/builtins/jobid1.0 new file mode 100644 index 0000000..0d65d3a --- /dev/null +++ b/bin/sh/tests/builtins/jobid1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/jobid2.0 b/bin/sh/tests/builtins/jobid2.0 new file mode 100644 index 0000000..6cbc5bc --- /dev/null +++ b/bin/sh/tests/builtins/jobid2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/kill1.0 b/bin/sh/tests/builtins/kill1.0 new file mode 100644 index 0000000..db5cf19 --- /dev/null +++ b/bin/sh/tests/builtins/kill1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/kill1.0 262931 2014-03-08 19:44:34Z jilles $ + +: & +p1=$! +: & +p2=$! +wait $p2 +kill %1 diff --git a/bin/sh/tests/builtins/kill2.0 b/bin/sh/tests/builtins/kill2.0 new file mode 100644 index 0000000..78ffcf2 --- /dev/null +++ b/bin/sh/tests/builtins/kill2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/lineno.0 b/bin/sh/tests/builtins/lineno.0 new file mode 100644 index 0000000..497fa14 --- /dev/null +++ b/bin/sh/tests/builtins/lineno.0 @@ -0,0 +1,16 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/lineno.0.stdout b/bin/sh/tests/builtins/lineno.0.stdout new file mode 100644 index 0000000..82583a9 --- /dev/null +++ b/bin/sh/tests/builtins/lineno.0.stdout @@ -0,0 +1,9 @@ +2 +3 +2 +3 +12 +13 +foo +foo +2 diff --git a/bin/sh/tests/builtins/lineno2.0 b/bin/sh/tests/builtins/lineno2.0 new file mode 100644 index 0000000..d2230a4 --- /dev/null +++ b/bin/sh/tests/builtins/lineno2.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/lineno3.0 b/bin/sh/tests/builtins/lineno3.0 new file mode 100644 index 0000000..fab8541 --- /dev/null +++ b/bin/sh/tests/builtins/lineno3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/lineno3.0.stdout b/bin/sh/tests/builtins/lineno3.0.stdout new file mode 100644 index 0000000..6e0e4ac --- /dev/null +++ b/bin/sh/tests/builtins/lineno3.0.stdout @@ -0,0 +1,2 @@ +before: 3 +after: 6 diff --git a/bin/sh/tests/builtins/local1.0 b/bin/sh/tests/builtins/local1.0 new file mode 100644 index 0000000..182bc3d --- /dev/null +++ b/bin/sh/tests/builtins/local1.0 @@ -0,0 +1,13 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/local2.0 b/bin/sh/tests/builtins/local2.0 new file mode 100644 index 0000000..f0997f8 --- /dev/null +++ b/bin/sh/tests/builtins/local2.0 @@ -0,0 +1,17 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/local3.0 b/bin/sh/tests/builtins/local3.0 new file mode 100644 index 0000000..1088a17 --- /dev/null +++ b/bin/sh/tests/builtins/local3.0 @@ -0,0 +1,26 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/local4.0 b/bin/sh/tests/builtins/local4.0 new file mode 100644 index 0000000..fce4a15 --- /dev/null +++ b/bin/sh/tests/builtins/local4.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/local5.0 b/bin/sh/tests/builtins/local5.0 new file mode 100644 index 0000000..38d33a6 --- /dev/null +++ b/bin/sh/tests/builtins/local5.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/local6.0 b/bin/sh/tests/builtins/local6.0 new file mode 100644 index 0000000..44850d0 --- /dev/null +++ b/bin/sh/tests/builtins/local6.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/local7.0 b/bin/sh/tests/builtins/local7.0 new file mode 100644 index 0000000..1455f27 --- /dev/null +++ b/bin/sh/tests/builtins/local7.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/locale1.0 b/bin/sh/tests/builtins/locale1.0 new file mode 100644 index 0000000..4b2ed61 --- /dev/null +++ b/bin/sh/tests/builtins/locale1.0 @@ -0,0 +1,134 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/locale2.0 b/bin/sh/tests/builtins/locale2.0 new file mode 100644 index 0000000..33c83ed --- /dev/null +++ b/bin/sh/tests/builtins/locale2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/locale2.0 318279 2017-05-14 20:17:50Z jilles $ + +$SH -c 'LC_ALL=C true; kill -INT $$; echo continued' +r=$? +[ "$r" -gt 128 ] && [ "$(kill -l "$r")" = INT ] diff --git a/bin/sh/tests/builtins/printf1.0 b/bin/sh/tests/builtins/printf1.0 new file mode 100644 index 0000000..3e3afc2 --- /dev/null +++ b/bin/sh/tests/builtins/printf1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/printf2.0 b/bin/sh/tests/builtins/printf2.0 new file mode 100644 index 0000000..af78b57 --- /dev/null +++ b/bin/sh/tests/builtins/printf2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/printf3.0 b/bin/sh/tests/builtins/printf3.0 new file mode 100644 index 0000000..73a1d7b --- /dev/null +++ b/bin/sh/tests/builtins/printf3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/printf4.0 b/bin/sh/tests/builtins/printf4.0 new file mode 100644 index 0000000..6131968 --- /dev/null +++ b/bin/sh/tests/builtins/printf4.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read1.0 b/bin/sh/tests/builtins/read1.0 new file mode 100644 index 0000000..3639344 --- /dev/null +++ b/bin/sh/tests/builtins/read1.0 @@ -0,0 +1,26 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read1.0.stdout b/bin/sh/tests/builtins/read1.0.stdout new file mode 100644 index 0000000..dbcb1af --- /dev/null +++ b/bin/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/bin/sh/tests/builtins/read2.0 b/bin/sh/tests/builtins/read2.0 new file mode 100644 index 0000000..2ce3227 --- /dev/null +++ b/bin/sh/tests/builtins/read2.0 @@ -0,0 +1,31 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read3.0 b/bin/sh/tests/builtins/read3.0 new file mode 100644 index 0000000..8f03b58 --- /dev/null +++ b/bin/sh/tests/builtins/read3.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read3.0.stdout b/bin/sh/tests/builtins/read3.0.stdout new file mode 100644 index 0000000..8ed98ca --- /dev/null +++ b/bin/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/bin/sh/tests/builtins/read4.0 b/bin/sh/tests/builtins/read4.0 new file mode 100644 index 0000000..4ef0907 --- /dev/null +++ b/bin/sh/tests/builtins/read4.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read4.0.stdout b/bin/sh/tests/builtins/read4.0.stdout new file mode 100644 index 0000000..a8747a4 --- /dev/null +++ b/bin/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/bin/sh/tests/builtins/read5.0 b/bin/sh/tests/builtins/read5.0 new file mode 100644 index 0000000..90057b2 --- /dev/null +++ b/bin/sh/tests/builtins/read5.0 @@ -0,0 +1,32 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read6.0 b/bin/sh/tests/builtins/read6.0 new file mode 100644 index 0000000..0627d7e --- /dev/null +++ b/bin/sh/tests/builtins/read6.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/read6.0 247190 2013-02-23 15:15:41Z jilles $ + +: | read x +r=$? +[ "$r" = 1 ] diff --git a/bin/sh/tests/builtins/read7.0 b/bin/sh/tests/builtins/read7.0 new file mode 100644 index 0000000..b865d5e --- /dev/null +++ b/bin/sh/tests/builtins/read7.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read8.0 b/bin/sh/tests/builtins/read8.0 new file mode 100644 index 0000000..2657294 --- /dev/null +++ b/bin/sh/tests/builtins/read8.0 @@ -0,0 +1,17 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/read9.0 b/bin/sh/tests/builtins/read9.0 new file mode 100644 index 0000000..57c2d76 --- /dev/null +++ b/bin/sh/tests/builtins/read9.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/read9.0 287308 2015-08-30 17:24:22Z jilles $ + +empty='' +read a b c <&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/bin/sh/tests/builtins/return5.0 b/bin/sh/tests/builtins/return5.0 new file mode 100644 index 0000000..4630e3b --- /dev/null +++ b/bin/sh/tests/builtins/return5.0 @@ -0,0 +1,17 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/return6.4 b/bin/sh/tests/builtins/return6.4 new file mode 100644 index 0000000..1e278c4 --- /dev/null +++ b/bin/sh/tests/builtins/return6.4 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/return6.4 212475 2010-09-11 15:07:40Z jilles $ + +while return 4; do exit 3; done diff --git a/bin/sh/tests/builtins/return7.4 b/bin/sh/tests/builtins/return7.4 new file mode 100644 index 0000000..34f37fd --- /dev/null +++ b/bin/sh/tests/builtins/return7.4 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/return8.0 b/bin/sh/tests/builtins/return8.0 new file mode 100644 index 0000000..6db22d0 --- /dev/null +++ b/bin/sh/tests/builtins/return8.0 @@ -0,0 +1,13 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/set1.0 b/bin/sh/tests/builtins/set1.0 new file mode 100644 index 0000000..fc39fad --- /dev/null +++ b/bin/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/bin/sh/tests/builtins/set2.0 b/bin/sh/tests/builtins/set2.0 new file mode 100644 index 0000000..27c8846 --- /dev/null +++ b/bin/sh/tests/builtins/set2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/set3.0 b/bin/sh/tests/builtins/set3.0 new file mode 100644 index 0000000..176c4c6 --- /dev/null +++ b/bin/sh/tests/builtins/set3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap1.0 b/bin/sh/tests/builtins/trap1.0 new file mode 100644 index 0000000..8c364af --- /dev/null +++ b/bin/sh/tests/builtins/trap1.0 @@ -0,0 +1,22 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap10.0 b/bin/sh/tests/builtins/trap10.0 new file mode 100644 index 0000000..5c6f1d6 --- /dev/null +++ b/bin/sh/tests/builtins/trap10.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap11.0 b/bin/sh/tests/builtins/trap11.0 new file mode 100644 index 0000000..002377f --- /dev/null +++ b/bin/sh/tests/builtins/trap11.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap12.0 b/bin/sh/tests/builtins/trap12.0 new file mode 100644 index 0000000..cee101d --- /dev/null +++ b/bin/sh/tests/builtins/trap12.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap13.0 b/bin/sh/tests/builtins/trap13.0 new file mode 100644 index 0000000..c07fca7 --- /dev/null +++ b/bin/sh/tests/builtins/trap13.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap14.0 b/bin/sh/tests/builtins/trap14.0 new file mode 100644 index 0000000..a5447a6 --- /dev/null +++ b/bin/sh/tests/builtins/trap14.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap15.0 b/bin/sh/tests/builtins/trap15.0 new file mode 100644 index 0000000..479e91f --- /dev/null +++ b/bin/sh/tests/builtins/trap15.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap16.0 b/bin/sh/tests/builtins/trap16.0 new file mode 100644 index 0000000..6713206 --- /dev/null +++ b/bin/sh/tests/builtins/trap16.0 @@ -0,0 +1,20 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap17.0 b/bin/sh/tests/builtins/trap17.0 new file mode 100644 index 0000000..5c77f11 --- /dev/null +++ b/bin/sh/tests/builtins/trap17.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap2.0 b/bin/sh/tests/builtins/trap2.0 new file mode 100644 index 0000000..d65d317 --- /dev/null +++ b/bin/sh/tests/builtins/trap2.0 @@ -0,0 +1,52 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap3.0 b/bin/sh/tests/builtins/trap3.0 new file mode 100644 index 0000000..23bf4f0 --- /dev/null +++ b/bin/sh/tests/builtins/trap3.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap4.0 b/bin/sh/tests/builtins/trap4.0 new file mode 100644 index 0000000..755331e --- /dev/null +++ b/bin/sh/tests/builtins/trap4.0 @@ -0,0 +1,17 @@ +# $FreeBSD: releng/11.1/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 + : &3 2>/dev/null' PIPE + echo x 2>/dev/null + } >fifo1 +) +test "$v" = trapped diff --git a/bin/sh/tests/builtins/trap5.0 b/bin/sh/tests/builtins/trap5.0 new file mode 100644 index 0000000..32244b3 --- /dev/null +++ b/bin/sh/tests/builtins/trap5.0 @@ -0,0 +1,19 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap6.0 b/bin/sh/tests/builtins/trap6.0 new file mode 100644 index 0000000..792f812 --- /dev/null +++ b/bin/sh/tests/builtins/trap6.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap7.0 b/bin/sh/tests/builtins/trap7.0 new file mode 100644 index 0000000..0f3e34c --- /dev/null +++ b/bin/sh/tests/builtins/trap7.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/trap7.0 217996 2011-01-27 23:08:20Z jilles $ + +[ "$(trap 'echo trapped' EXIT)" = trapped ] diff --git a/bin/sh/tests/builtins/trap8.0 b/bin/sh/tests/builtins/trap8.0 new file mode 100644 index 0000000..7a20974 --- /dev/null +++ b/bin/sh/tests/builtins/trap8.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/trap9.0 b/bin/sh/tests/builtins/trap9.0 new file mode 100644 index 0000000..8cdd091 --- /dev/null +++ b/bin/sh/tests/builtins/trap9.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/type1.0 b/bin/sh/tests/builtins/type1.0 new file mode 100644 index 0000000..b231c0b --- /dev/null +++ b/bin/sh/tests/builtins/type1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/type1.0.stderr b/bin/sh/tests/builtins/type1.0.stderr new file mode 100644 index 0000000..7853418 --- /dev/null +++ b/bin/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/bin/sh/tests/builtins/type2.0 b/bin/sh/tests/builtins/type2.0 new file mode 100644 index 0000000..80fece1 --- /dev/null +++ b/bin/sh/tests/builtins/type2.0 @@ -0,0 +1,26 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/type3.0 b/bin/sh/tests/builtins/type3.0 new file mode 100644 index 0000000..ab1947f --- /dev/null +++ b/bin/sh/tests/builtins/type3.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/type3.0 255072 2013-08-30 12:09:59Z jilles $ + +[ "$(type type)" = "$(type -- type)" ] diff --git a/bin/sh/tests/builtins/unalias.0 b/bin/sh/tests/builtins/unalias.0 new file mode 100644 index 0000000..4c6765a --- /dev/null +++ b/bin/sh/tests/builtins/unalias.0 @@ -0,0 +1,21 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/var-assign.0 b/bin/sh/tests/builtins/var-assign.0 new file mode 100644 index 0000000..9be3519 --- /dev/null +++ b/bin/sh/tests/builtins/var-assign.0 @@ -0,0 +1,55 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/builtins/var-assign.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 + +# 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 utilites 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/bin/sh/tests/builtins/var-assign2.0 b/bin/sh/tests/builtins/var-assign2.0 new file mode 100644 index 0000000..5a6086a --- /dev/null +++ b/bin/sh/tests/builtins/var-assign2.0 @@ -0,0 +1,55 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/wait1.0 b/bin/sh/tests/builtins/wait1.0 new file mode 100644 index 0000000..631bf78 --- /dev/null +++ b/bin/sh/tests/builtins/wait1.0 @@ -0,0 +1,23 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/wait10.0 b/bin/sh/tests/builtins/wait10.0 new file mode 100644 index 0000000..2c43b73 --- /dev/null +++ b/bin/sh/tests/builtins/wait10.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/wait2.0 b/bin/sh/tests/builtins/wait2.0 new file mode 100644 index 0000000..0669b6b --- /dev/null +++ b/bin/sh/tests/builtins/wait2.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/builtins/wait3.0 b/bin/sh/tests/builtins/wait3.0 new file mode 100644 index 0000000..7132548 --- /dev/null +++ b/bin/sh/tests/builtins/wait3.0 @@ -0,0 +1,21 @@ +# $FreeBSD: releng/11.1/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 4fifo1 +wait || failure $LINENO +(${SH} -c echo >&3) 2>/dev/null && failure $LINENO +wait || failure $LINENO + +test -z "$failures" diff --git a/bin/sh/tests/builtins/wait4.0 b/bin/sh/tests/builtins/wait4.0 new file mode 100644 index 0000000..4a60b98 --- /dev/null +++ b/bin/sh/tests/builtins/wait4.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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 & +wait diff --git a/bin/sh/tests/errors/Makefile.depend b/bin/sh/tests/errors/Makefile.depend new file mode 100644 index 0000000..945fe19 --- /dev/null +++ b/bin/sh/tests/errors/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/errors/assignment-error1.0 b/bin/sh/tests/errors/assignment-error1.0 new file mode 100644 index 0000000..bad4ea9 --- /dev/null +++ b/bin/sh/tests/errors/assignment-error1.0 @@ -0,0 +1,30 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/assignment-error2.0 b/bin/sh/tests/errors/assignment-error2.0 new file mode 100644 index 0000000..08f0bab --- /dev/null +++ b/bin/sh/tests/errors/assignment-error2.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/backquote-error1.0 b/bin/sh/tests/errors/backquote-error1.0 new file mode 100644 index 0000000..ca037a3 --- /dev/null +++ b/bin/sh/tests/errors/backquote-error1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/backquote-error2.0 b/bin/sh/tests/errors/backquote-error2.0 new file mode 100644 index 0000000..0ca8213 --- /dev/null +++ b/bin/sh/tests/errors/backquote-error2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/bad-binary1.126 b/bin/sh/tests/errors/bad-binary1.126 new file mode 100644 index 0000000..201ca0c --- /dev/null +++ b/bin/sh/tests/errors/bad-binary1.126 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/bad-keyword1.0 b/bin/sh/tests/errors/bad-keyword1.0 new file mode 100644 index 0000000..a684eb5 --- /dev/null +++ b/bin/sh/tests/errors/bad-keyword1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/bad-parm-exp1.0 b/bin/sh/tests/errors/bad-parm-exp1.0 new file mode 100644 index 0000000..1623c88 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/bad-parm-exp1.0 164004 2006-11-05 18:41:23Z stefanf $ +false && { + ${} + ${foo/} + ${foo@bar} +} +: diff --git a/bin/sh/tests/errors/bad-parm-exp2.2 b/bin/sh/tests/errors/bad-parm-exp2.2 new file mode 100644 index 0000000..3d45e45 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp2.2 @@ -0,0 +1,2 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/bad-parm-exp2.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${}' diff --git a/bin/sh/tests/errors/bad-parm-exp2.2.stderr b/bin/sh/tests/errors/bad-parm-exp2.2.stderr new file mode 100644 index 0000000..51ea69c --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp2.2.stderr @@ -0,0 +1 @@ +eval: ${}: Bad substitution diff --git a/bin/sh/tests/errors/bad-parm-exp3.2 b/bin/sh/tests/errors/bad-parm-exp3.2 new file mode 100644 index 0000000..c9c2927 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp3.2 @@ -0,0 +1,2 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/bad-parm-exp3.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${foo/}' diff --git a/bin/sh/tests/errors/bad-parm-exp3.2.stderr b/bin/sh/tests/errors/bad-parm-exp3.2.stderr new file mode 100644 index 0000000..70473f9 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp3.2.stderr @@ -0,0 +1 @@ +eval: ${foo/}: Bad substitution diff --git a/bin/sh/tests/errors/bad-parm-exp4.2 b/bin/sh/tests/errors/bad-parm-exp4.2 new file mode 100644 index 0000000..b4923cf --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp4.2 @@ -0,0 +1,2 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/bad-parm-exp4.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${foo:@abc}' diff --git a/bin/sh/tests/errors/bad-parm-exp4.2.stderr b/bin/sh/tests/errors/bad-parm-exp4.2.stderr new file mode 100644 index 0000000..3363f51 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp4.2.stderr @@ -0,0 +1 @@ +eval: ${foo:@...}: Bad substitution diff --git a/bin/sh/tests/errors/bad-parm-exp5.2 b/bin/sh/tests/errors/bad-parm-exp5.2 new file mode 100644 index 0000000..960fe12 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp5.2 @@ -0,0 +1,2 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/bad-parm-exp5.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${/}' diff --git a/bin/sh/tests/errors/bad-parm-exp5.2.stderr b/bin/sh/tests/errors/bad-parm-exp5.2.stderr new file mode 100644 index 0000000..13763f8 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp5.2.stderr @@ -0,0 +1 @@ +eval: ${/}: Bad substitution diff --git a/bin/sh/tests/errors/bad-parm-exp6.2 b/bin/sh/tests/errors/bad-parm-exp6.2 new file mode 100644 index 0000000..278146e --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp6.2 @@ -0,0 +1,2 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/bad-parm-exp6.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${#foo^}' diff --git a/bin/sh/tests/errors/bad-parm-exp6.2.stderr b/bin/sh/tests/errors/bad-parm-exp6.2.stderr new file mode 100644 index 0000000..cc56f65 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp6.2.stderr @@ -0,0 +1 @@ +eval: ${foo...}: Bad substitution diff --git a/bin/sh/tests/errors/bad-parm-exp7.0 b/bin/sh/tests/errors/bad-parm-exp7.0 new file mode 100644 index 0000000..7734d94 --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp7.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/bad-parm-exp8.0 b/bin/sh/tests/errors/bad-parm-exp8.0 new file mode 100644 index 0000000..9757a5e --- /dev/null +++ b/bin/sh/tests/errors/bad-parm-exp8.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/option-error.0 b/bin/sh/tests/errors/option-error.0 new file mode 100644 index 0000000..3fe1073 --- /dev/null +++ b/bin/sh/tests/errors/option-error.0 @@ -0,0 +1,46 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/redirection-error.0 b/bin/sh/tests/errors/redirection-error.0 new file mode 100644 index 0000000..5c98003 --- /dev/null +++ b/bin/sh/tests/errors/redirection-error.0 @@ -0,0 +1,53 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/redirection-error2.2 b/bin/sh/tests/errors/redirection-error2.2 new file mode 100644 index 0000000..d4265a6 --- /dev/null +++ b/bin/sh/tests/errors/redirection-error2.2 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/redirection-error3.0 b/bin/sh/tests/errors/redirection-error3.0 new file mode 100644 index 0000000..91f122f --- /dev/null +++ b/bin/sh/tests/errors/redirection-error3.0 @@ -0,0 +1,54 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/errors/redirection-error4.0 b/bin/sh/tests/errors/redirection-error4.0 new file mode 100644 index 0000000..10e1268 --- /dev/null +++ b/bin/sh/tests/errors/redirection-error4.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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 +/dev/null +( echo bad ) /dev/null +{ echo bad; } &1 >&2 2>&3 + ulimit -n 9 + exec 9<. +) && [ -n "$dummy" ] diff --git a/bin/sh/tests/errors/write-error1.0 b/bin/sh/tests/errors/write-error1.0 new file mode 100644 index 0000000..627129a --- /dev/null +++ b/bin/sh/tests/errors/write-error1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/errors/write-error1.0 244924 2013-01-01 12:48:24Z jilles $ + +! echo >&- 2>/dev/null diff --git a/bin/sh/tests/execution/Makefile b/bin/sh/tests/execution/Makefile new file mode 100644 index 0000000..dec8370 --- /dev/null +++ b/bin/sh/tests/execution/Makefile @@ -0,0 +1,56 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/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+= 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-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 diff --git a/bin/sh/tests/execution/Makefile.depend b/bin/sh/tests/execution/Makefile.depend new file mode 100644 index 0000000..3653147 --- /dev/null +++ b/bin/sh/tests/execution/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/execution/bg1.0 b/bin/sh/tests/execution/bg1.0 new file mode 100644 index 0000000..8bfb7d6 --- /dev/null +++ b/bin/sh/tests/execution/bg1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/bg1.0 221027 2011-04-25 20:54:12Z jilles $ + +: `false` & diff --git a/bin/sh/tests/execution/bg10.0 b/bin/sh/tests/execution/bg10.0 new file mode 100644 index 0000000..57363c3 --- /dev/null +++ b/bin/sh/tests/execution/bg10.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/bg10.0 258535 2013-11-24 23:12:13Z jilles $ +# The redirection overrides the /dev/null; { cat & wait; }' diff --git a/bin/sh/tests/execution/bg9.0 b/bin/sh/tests/execution/bg9.0 new file mode 100644 index 0000000..1f1b1be --- /dev/null +++ b/bin/sh/tests/execution/bg9.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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; { cat & wait; }' diff --git a/bin/sh/tests/execution/fork1.0 b/bin/sh/tests/execution/fork1.0 new file mode 100644 index 0000000..44e4093 --- /dev/null +++ b/bin/sh/tests/execution/fork1.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/fork2.0 b/bin/sh/tests/execution/fork2.0 new file mode 100644 index 0000000..e1dc854 --- /dev/null +++ b/bin/sh/tests/execution/fork2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/fork3.0 b/bin/sh/tests/execution/fork3.0 new file mode 100644 index 0000000..9b39f8d --- /dev/null +++ b/bin/sh/tests/execution/fork3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/func1.0 b/bin/sh/tests/execution/func1.0 new file mode 100644 index 0000000..1fe4bf2 --- /dev/null +++ b/bin/sh/tests/execution/func1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/func2.0 b/bin/sh/tests/execution/func2.0 new file mode 100644 index 0000000..fe8e572 --- /dev/null +++ b/bin/sh/tests/execution/func2.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/func3.0 b/bin/sh/tests/execution/func3.0 new file mode 100644 index 0000000..9098215 --- /dev/null +++ b/bin/sh/tests/execution/func3.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/hash1.0 b/bin/sh/tests/execution/hash1.0 new file mode 100644 index 0000000..8d91f94 --- /dev/null +++ b/bin/sh/tests/execution/hash1.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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 <"$T/ls" +: +EOF +chmod 755 "$T/ls" +PATH=$PATH +ls -ld . diff --git a/bin/sh/tests/execution/int-cmd1.0 b/bin/sh/tests/execution/int-cmd1.0 new file mode 100644 index 0000000..84004ab --- /dev/null +++ b/bin/sh/tests/execution/int-cmd1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/killed1.0 b/bin/sh/tests/execution/killed1.0 new file mode 100644 index 0000000..52ed3e9 --- /dev/null +++ b/bin/sh/tests/execution/killed1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/killed2.0 b/bin/sh/tests/execution/killed2.0 new file mode 100644 index 0000000..5f5e3be --- /dev/null +++ b/bin/sh/tests/execution/killed2.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/not1.0 b/bin/sh/tests/execution/not1.0 new file mode 100644 index 0000000..d0c0f5a --- /dev/null +++ b/bin/sh/tests/execution/not1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/not1.0 249407 2013-04-12 15:19:35Z jilles $ + +f() { ! return $1; } +f 0 && ! f 1 diff --git a/bin/sh/tests/execution/not2.0 b/bin/sh/tests/execution/not2.0 new file mode 100644 index 0000000..62dd852 --- /dev/null +++ b/bin/sh/tests/execution/not2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/not2.0 249407 2013-04-12 15:19:35Z jilles $ + +while :; do + ! break + exit 3 +done diff --git a/bin/sh/tests/execution/path1.0 b/bin/sh/tests/execution/path1.0 new file mode 100644 index 0000000..2e04b43 --- /dev/null +++ b/bin/sh/tests/execution/path1.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/redir1.0 b/bin/sh/tests/execution/redir1.0 new file mode 100644 index 0000000..797d2e2 --- /dev/null +++ b/bin/sh/tests/execution/redir1.0 @@ -0,0 +1,27 @@ +# $FreeBSD: releng/11.1/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 fifo2 +} 3fifo1 +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/bin/sh/tests/execution/redir2.0 b/bin/sh/tests/execution/redir2.0 new file mode 100644 index 0000000..96157e2 --- /dev/null +++ b/bin/sh/tests/execution/redir2.0 @@ -0,0 +1,29 @@ +# $FreeBSD: releng/11.1/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 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/bin/sh/tests/execution/redir3.0 b/bin/sh/tests/execution/redir3.0 new file mode 100644 index 0000000..f923d78 --- /dev/null +++ b/bin/sh/tests/execution/redir3.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/redir3.0 214289 2010-10-24 19:56:34Z jilles $ + +3>&- 3>&- diff --git a/bin/sh/tests/execution/redir4.0 b/bin/sh/tests/execution/redir4.0 new file mode 100644 index 0000000..7712f70 --- /dev/null +++ b/bin/sh/tests/execution/redir4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/redir5.0 b/bin/sh/tests/execution/redir5.0 new file mode 100644 index 0000000..45b3a60 --- /dev/null +++ b/bin/sh/tests/execution/redir5.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/redir5.0 218325 2011-02-05 15:02:19Z jilles $ + +{ (echo bad) >/dev/null; } &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/bin/sh/tests/execution/redir7.0 b/bin/sh/tests/execution/redir7.0 new file mode 100644 index 0000000..0b0e10f --- /dev/null +++ b/bin/sh/tests/execution/redir7.0 @@ -0,0 +1,21 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/set-n1.0 b/bin/sh/tests/execution/set-n1.0 new file mode 100644 index 0000000..4396ef3 --- /dev/null +++ b/bin/sh/tests/execution/set-n1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/set-n2.0 b/bin/sh/tests/execution/set-n2.0 new file mode 100644 index 0000000..9865277 --- /dev/null +++ b/bin/sh/tests/execution/set-n2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/set-n2.0 222661 2011-06-03 21:17:42Z jilles $ + +$SH -n <<'EOF' +echo bad +EOF diff --git a/bin/sh/tests/execution/set-n3.0 b/bin/sh/tests/execution/set-n3.0 new file mode 100644 index 0000000..235a360 --- /dev/null +++ b/bin/sh/tests/execution/set-n3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/set-n4.0 b/bin/sh/tests/execution/set-n4.0 new file mode 100644 index 0000000..caee262 --- /dev/null +++ b/bin/sh/tests/execution/set-n4.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/set-n4.0 222676 2011-06-04 11:28:42Z jilles $ + +$SH -nc 'echo bad' diff --git a/bin/sh/tests/execution/set-x1.0 b/bin/sh/tests/execution/set-x1.0 new file mode 100644 index 0000000..23e68ef --- /dev/null +++ b/bin/sh/tests/execution/set-x1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/set-x2.0 b/bin/sh/tests/execution/set-x2.0 new file mode 100644 index 0000000..9a42b90 --- /dev/null +++ b/bin/sh/tests/execution/set-x2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/set-x3.0 b/bin/sh/tests/execution/set-x3.0 new file mode 100644 index 0000000..3ab82c9 --- /dev/null +++ b/bin/sh/tests/execution/set-x3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/set-x4.0 b/bin/sh/tests/execution/set-x4.0 new file mode 100644 index 0000000..ec8db9e --- /dev/null +++ b/bin/sh/tests/execution/set-x4.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/shellproc1.0 b/bin/sh/tests/execution/shellproc1.0 new file mode 100644 index 0000000..ebd0dec --- /dev/null +++ b/bin/sh/tests/execution/shellproc1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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 <"$T/testshellproc" +printf 'this ' +echo is a test +EOF +chmod 755 "$T/testshellproc" +PATH=$T:$PATH +[ "`testshellproc`" = "this is a test" ] diff --git a/bin/sh/tests/execution/subshell1.0 b/bin/sh/tests/execution/subshell1.0 new file mode 100644 index 0000000..7047982 --- /dev/null +++ b/bin/sh/tests/execution/subshell1.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/subshell1.0.stdout b/bin/sh/tests/execution/subshell1.0.stdout new file mode 100644 index 0000000..8c71af3 --- /dev/null +++ b/bin/sh/tests/execution/subshell1.0.stdout @@ -0,0 +1,2 @@ +100000 +end diff --git a/bin/sh/tests/execution/subshell2.0 b/bin/sh/tests/execution/subshell2.0 new file mode 100644 index 0000000..584c3be --- /dev/null +++ b/bin/sh/tests/execution/subshell2.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/subshell3.0 b/bin/sh/tests/execution/subshell3.0 new file mode 100644 index 0000000..df84768 --- /dev/null +++ b/bin/sh/tests/execution/subshell3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/subshell3.0 245383 2013-01-13 19:39:13Z jilles $ + +(false; exit) && exit 3 +exit 0 diff --git a/bin/sh/tests/execution/subshell4.0 b/bin/sh/tests/execution/subshell4.0 new file mode 100644 index 0000000..25ef59a --- /dev/null +++ b/bin/sh/tests/execution/subshell4.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/subshell4.0 245383 2013-01-13 19:39:13Z jilles $ + +(eval "set v=1"; false) && echo bad; : diff --git a/bin/sh/tests/execution/unknown1.0 b/bin/sh/tests/execution/unknown1.0 new file mode 100644 index 0000000..e5cc409 --- /dev/null +++ b/bin/sh/tests/execution/unknown1.0 @@ -0,0 +1,29 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/execution/var-assign1.0 b/bin/sh/tests/execution/var-assign1.0 new file mode 100644 index 0000000..0c46d3d --- /dev/null +++ b/bin/sh/tests/execution/var-assign1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/execution/var-assign1.0 212467 2010-09-11 14:15:50Z jilles $ + +[ "$(HOME=/etc HOME=/ cd && pwd)" = / ] diff --git a/bin/sh/tests/expansion/Makefile b/bin/sh/tests/expansion/Makefile new file mode 100644 index 0000000..e306632 --- /dev/null +++ b/bin/sh/tests/expansion/Makefile @@ -0,0 +1,102 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/Makefile 315486 2017-03-18 16:07:28Z 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+= 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 diff --git a/bin/sh/tests/expansion/Makefile.depend b/bin/sh/tests/expansion/Makefile.depend new file mode 100644 index 0000000..8b37c7c --- /dev/null +++ b/bin/sh/tests/expansion/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/expansion/arith1.0 b/bin/sh/tests/expansion/arith1.0 new file mode 100644 index 0000000..3f7f413 --- /dev/null +++ b/bin/sh/tests/expansion/arith1.0 @@ -0,0 +1,30 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith10.0 b/bin/sh/tests/expansion/arith10.0 new file mode 100644 index 0000000..283569c --- /dev/null +++ b/bin/sh/tests/expansion/arith10.0 @@ -0,0 +1,35 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith11.0 b/bin/sh/tests/expansion/arith11.0 new file mode 100644 index 0000000..77eaf03 --- /dev/null +++ b/bin/sh/tests/expansion/arith11.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith12.0 b/bin/sh/tests/expansion/arith12.0 new file mode 100644 index 0000000..782f44a --- /dev/null +++ b/bin/sh/tests/expansion/arith12.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith13.0 b/bin/sh/tests/expansion/arith13.0 new file mode 100644 index 0000000..ad054cd --- /dev/null +++ b/bin/sh/tests/expansion/arith13.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith14.0 b/bin/sh/tests/expansion/arith14.0 new file mode 100644 index 0000000..5d4bffa --- /dev/null +++ b/bin/sh/tests/expansion/arith14.0 @@ -0,0 +1,40 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith2.0 b/bin/sh/tests/expansion/arith2.0 new file mode 100644 index 0000000..dc198a7 --- /dev/null +++ b/bin/sh/tests/expansion/arith2.0 @@ -0,0 +1,77 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith3.0 b/bin/sh/tests/expansion/arith3.0 new file mode 100644 index 0000000..16ec017 --- /dev/null +++ b/bin/sh/tests/expansion/arith3.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith4.0 b/bin/sh/tests/expansion/arith4.0 new file mode 100644 index 0000000..6732792 --- /dev/null +++ b/bin/sh/tests/expansion/arith4.0 @@ -0,0 +1,20 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith5.0 b/bin/sh/tests/expansion/arith5.0 new file mode 100644 index 0000000..4f824f2 --- /dev/null +++ b/bin/sh/tests/expansion/arith5.0 @@ -0,0 +1,17 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith6.0 b/bin/sh/tests/expansion/arith6.0 new file mode 100644 index 0000000..3bd024f --- /dev/null +++ b/bin/sh/tests/expansion/arith6.0 @@ -0,0 +1,16 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/arith7.0 b/bin/sh/tests/expansion/arith7.0 new file mode 100644 index 0000000..a5ef692 --- /dev/null +++ b/bin/sh/tests/expansion/arith7.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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 <&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/bin/sh/tests/expansion/arith9.0 b/bin/sh/tests/expansion/arith9.0 new file mode 100644 index 0000000..c8b9735 --- /dev/null +++ b/bin/sh/tests/expansion/arith9.0 @@ -0,0 +1,20 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/assign1.0 b/bin/sh/tests/expansion/assign1.0 new file mode 100644 index 0000000..d91244c --- /dev/null +++ b/bin/sh/tests/expansion/assign1.0 @@ -0,0 +1,37 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst1.0 b/bin/sh/tests/expansion/cmdsubst1.0 new file mode 100644 index 0000000..f10c283 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst1.0 @@ -0,0 +1,48 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst10.0 b/bin/sh/tests/expansion/cmdsubst10.0 new file mode 100644 index 0000000..15b6a87 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst10.0 @@ -0,0 +1,51 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst11.0 b/bin/sh/tests/expansion/cmdsubst11.0 new file mode 100644 index 0000000..1e1ecf0 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst11.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst12.0 b/bin/sh/tests/expansion/cmdsubst12.0 new file mode 100644 index 0000000..ff89d75 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst12.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst13.0 b/bin/sh/tests/expansion/cmdsubst13.0 new file mode 100644 index 0000000..fb5d02d --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst13.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst14.0 b/bin/sh/tests/expansion/cmdsubst14.0 new file mode 100644 index 0000000..2c74641 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst14.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst14.0 245381 2013-01-13 19:19:40Z jilles $ + +! v=`false + +` diff --git a/bin/sh/tests/expansion/cmdsubst15.0 b/bin/sh/tests/expansion/cmdsubst15.0 new file mode 100644 index 0000000..7dbe120 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst15.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst15.0 245381 2013-01-13 19:19:40Z jilles $ + +! v=`false; + +` diff --git a/bin/sh/tests/expansion/cmdsubst16.0 b/bin/sh/tests/expansion/cmdsubst16.0 new file mode 100644 index 0000000..b8dc03e --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst16.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst16.0 245392 2013-01-13 22:35:51Z jilles $ + +f() { return 3; } +f +[ `echo $?` = 3 ] diff --git a/bin/sh/tests/expansion/cmdsubst17.0 b/bin/sh/tests/expansion/cmdsubst17.0 new file mode 100644 index 0000000..3f7d57d --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst17.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst17.0 245422 2013-01-14 12:20:55Z jilles $ + +f() { return 3; } +f +[ `echo $?; :` = 3 ] diff --git a/bin/sh/tests/expansion/cmdsubst18.0 b/bin/sh/tests/expansion/cmdsubst18.0 new file mode 100644 index 0000000..ca3c341 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst18.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst18.0 315168 2017-03-12 18:24:45Z 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/bin/sh/tests/expansion/cmdsubst19.0 b/bin/sh/tests/expansion/cmdsubst19.0 new file mode 100644 index 0000000..fb18b27 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst19.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst19.0 315168 2017-03-12 18:24:45Z 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/bin/sh/tests/expansion/cmdsubst2.0 b/bin/sh/tests/expansion/cmdsubst2.0 new file mode 100644 index 0000000..502bca2 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst2.0 @@ -0,0 +1,43 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst20.0 b/bin/sh/tests/expansion/cmdsubst20.0 new file mode 100644 index 0000000..4e0132f --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst20.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst20.0 315168 2017-03-12 18:24:45Z jilles $ + +set -T +trapped='' +trap "trapped=x$trapped" USR1 +[ "x$(kill -USR1 $$)y" = xy ] && [ "$trapped" = x ] diff --git a/bin/sh/tests/expansion/cmdsubst21.0 b/bin/sh/tests/expansion/cmdsubst21.0 new file mode 100644 index 0000000..196c7cc --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst21.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst21.0 315169 2017-03-12 18:38:03Z jilles $ + +set -T +trapped='' +trap "trapped=x$trapped" TERM +[ "x$($SH -c "kill $$")y" = xy ] && [ "$trapped" = x ] diff --git a/bin/sh/tests/expansion/cmdsubst22.0 b/bin/sh/tests/expansion/cmdsubst22.0 new file mode 100644 index 0000000..7046195 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst22.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst22.0 315169 2017-03-12 18:38:03Z jilles $ + +set -T +trapped='' +trap "trapped=x$trapped" TERM +[ "x$(:; kill $$)y" = xy ] && [ "$trapped" = x ] diff --git a/bin/sh/tests/expansion/cmdsubst23.0 b/bin/sh/tests/expansion/cmdsubst23.0 new file mode 100644 index 0000000..3c4b51c --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst23.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst23.0 315486 2017-03-18 16:07:28Z jilles $ + +unset n +x=abcd +[ "X${n#$(echo a)}X${x#$(echo ab)}X$(echo abc)X" = XXcdXabcX ] diff --git a/bin/sh/tests/expansion/cmdsubst3.0 b/bin/sh/tests/expansion/cmdsubst3.0 new file mode 100644 index 0000000..b421210 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst3.0 @@ -0,0 +1,23 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst4.0 b/bin/sh/tests/expansion/cmdsubst4.0 new file mode 100644 index 0000000..72ef489 --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/cmdsubst4.0 216747 2010-12-27 23:56:03Z jilles $ + +exec 2>/dev/null +! y=$(: /dev/null +! y=$(: ${v?}) diff --git a/bin/sh/tests/expansion/cmdsubst6.0 b/bin/sh/tests/expansion/cmdsubst6.0 new file mode 100644 index 0000000..43e341f --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst6.0 @@ -0,0 +1,53 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst7.0 b/bin/sh/tests/expansion/cmdsubst7.0 new file mode 100644 index 0000000..0a4655c --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst7.0 @@ -0,0 +1,31 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst8.0 b/bin/sh/tests/expansion/cmdsubst8.0 new file mode 100644 index 0000000..0aea2bb --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst8.0 @@ -0,0 +1,17 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/cmdsubst9.0 b/bin/sh/tests/expansion/cmdsubst9.0 new file mode 100644 index 0000000..cc595af --- /dev/null +++ b/bin/sh/tests/expansion/cmdsubst9.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/export1.0 b/bin/sh/tests/expansion/export1.0 new file mode 100644 index 0000000..0fbd979 --- /dev/null +++ b/bin/sh/tests/expansion/export1.0 @@ -0,0 +1,13 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/export2.0 b/bin/sh/tests/expansion/export2.0 new file mode 100644 index 0000000..db160cf --- /dev/null +++ b/bin/sh/tests/expansion/export2.0 @@ -0,0 +1,24 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/export3.0 b/bin/sh/tests/expansion/export3.0 new file mode 100644 index 0000000..77a2e06 --- /dev/null +++ b/bin/sh/tests/expansion/export3.0 @@ -0,0 +1,30 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/heredoc1.0 b/bin/sh/tests/expansion/heredoc1.0 new file mode 100644 index 0000000..dad0f9e --- /dev/null +++ b/bin/sh/tests/expansion/heredoc1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/heredoc1.0 222715 2011-06-05 12:46:26Z jilles $ + +f() { return $1; } + +[ `f 42; { cat; } <&2 + : $((failures += 1)) + fi + i=$((i+1)) +done +exit $((failures > 0)) diff --git a/bin/sh/tests/expansion/ifs3.0 b/bin/sh/tests/expansion/ifs3.0 new file mode 100644 index 0000000..6f11d8b --- /dev/null +++ b/bin/sh/tests/expansion/ifs3.0 @@ -0,0 +1,21 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/ifs4.0 b/bin/sh/tests/expansion/ifs4.0 new file mode 100644 index 0000000..7ecccd2 --- /dev/null +++ b/bin/sh/tests/expansion/ifs4.0 @@ -0,0 +1,39 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/ifs5.0 b/bin/sh/tests/expansion/ifs5.0 new file mode 100644 index 0000000..2dd3f1a --- /dev/null +++ b/bin/sh/tests/expansion/ifs5.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/ifs5.0 278806 2015-02-15 19:48:29Z jilles $ + +set -- $(echo a b c d) +[ "$#" = 4 ] diff --git a/bin/sh/tests/expansion/ifs6.0 b/bin/sh/tests/expansion/ifs6.0 new file mode 100644 index 0000000..89b35cc --- /dev/null +++ b/bin/sh/tests/expansion/ifs6.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/ifs7.0 b/bin/sh/tests/expansion/ifs7.0 new file mode 100644 index 0000000..3b90953 --- /dev/null +++ b/bin/sh/tests/expansion/ifs7.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/length1.0 b/bin/sh/tests/expansion/length1.0 new file mode 100644 index 0000000..b2bcd63 --- /dev/null +++ b/bin/sh/tests/expansion/length1.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/length2.0 b/bin/sh/tests/expansion/length2.0 new file mode 100644 index 0000000..25727d7 --- /dev/null +++ b/bin/sh/tests/expansion/length2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/length2.0 219611 2011-03-13 16:20:38Z jilles $ + +v=$- +[ "${#-}" = "${#v}" ] || echo '${#-} wrong' diff --git a/bin/sh/tests/expansion/length3.0 b/bin/sh/tests/expansion/length3.0 new file mode 100644 index 0000000..970073c --- /dev/null +++ b/bin/sh/tests/expansion/length3.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/length4.0 b/bin/sh/tests/expansion/length4.0 new file mode 100644 index 0000000..fb1ccaf --- /dev/null +++ b/bin/sh/tests/expansion/length4.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/length5.0 b/bin/sh/tests/expansion/length5.0 new file mode 100644 index 0000000..caec998 --- /dev/null +++ b/bin/sh/tests/expansion/length5.0 @@ -0,0 +1,27 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/length6.0 b/bin/sh/tests/expansion/length6.0 new file mode 100644 index 0000000..8a36a2f --- /dev/null +++ b/bin/sh/tests/expansion/length6.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/length7.0 b/bin/sh/tests/expansion/length7.0 new file mode 100644 index 0000000..d3d09a7 --- /dev/null +++ b/bin/sh/tests/expansion/length7.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/length8.0 b/bin/sh/tests/expansion/length8.0 new file mode 100644 index 0000000..da2ffdd --- /dev/null +++ b/bin/sh/tests/expansion/length8.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/local1.0 b/bin/sh/tests/expansion/local1.0 new file mode 100644 index 0000000..bce2523 --- /dev/null +++ b/bin/sh/tests/expansion/local1.0 @@ -0,0 +1,28 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/local2.0 b/bin/sh/tests/expansion/local2.0 new file mode 100644 index 0000000..0e725b1 --- /dev/null +++ b/bin/sh/tests/expansion/local2.0 @@ -0,0 +1,34 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/pathname1.0 b/bin/sh/tests/expansion/pathname1.0 new file mode 100644 index 0000000..485fbc9 --- /dev/null +++ b/bin/sh/tests/expansion/pathname1.0 @@ -0,0 +1,65 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/pathname1.0 304339 2016-08-18 07:43: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/bin/sh/tests/expansion/pathname2.0 b/bin/sh/tests/expansion/pathname2.0 new file mode 100644 index 0000000..3514ac6 --- /dev/null +++ b/bin/sh/tests/expansion/pathname2.0 @@ -0,0 +1,35 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/pathname2.0 304339 2016-08-18 07:43: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/bin/sh/tests/expansion/pathname3.0 b/bin/sh/tests/expansion/pathname3.0 new file mode 100644 index 0000000..4586526 --- /dev/null +++ b/bin/sh/tests/expansion/pathname3.0 @@ -0,0 +1,29 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/pathname4.0 b/bin/sh/tests/expansion/pathname4.0 new file mode 100644 index 0000000..6f6aa0a --- /dev/null +++ b/bin/sh/tests/expansion/pathname4.0 @@ -0,0 +1,28 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/pathname5.0 b/bin/sh/tests/expansion/pathname5.0 new file mode 100644 index 0000000..4165943 --- /dev/null +++ b/bin/sh/tests/expansion/pathname5.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/pathname5.0 278806 2015-02-15 19:48:29Z jilles $ + +[ `echo '/[e]tc'` = /etc ] diff --git a/bin/sh/tests/expansion/pathname6.0 b/bin/sh/tests/expansion/pathname6.0 new file mode 100644 index 0000000..5431fcc --- /dev/null +++ b/bin/sh/tests/expansion/pathname6.0 @@ -0,0 +1,29 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/pathname6.0 304339 2016-08-18 07:43: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/bin/sh/tests/expansion/plus-minus1.0 b/bin/sh/tests/expansion/plus-minus1.0 new file mode 100644 index 0000000..872b40b --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus1.0 @@ -0,0 +1,76 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/plus-minus2.0 b/bin/sh/tests/expansion/plus-minus2.0 new file mode 100644 index 0000000..e99de9e --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/expansion/plus-minus2.0 206145 2010-04-03 20:55:56Z jilles $ + +e= +test "${e:-\}}" = '}' diff --git a/bin/sh/tests/expansion/plus-minus3.0 b/bin/sh/tests/expansion/plus-minus3.0 new file mode 100644 index 0000000..2099a05 --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus3.0 @@ -0,0 +1,44 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/plus-minus4.0 b/bin/sh/tests/expansion/plus-minus4.0 new file mode 100644 index 0000000..2838f9c --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus4.0 @@ -0,0 +1,38 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/plus-minus5.0 b/bin/sh/tests/expansion/plus-minus5.0 new file mode 100644 index 0000000..1a51c5c --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus5.0 @@ -0,0 +1,31 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/plus-minus6.0 b/bin/sh/tests/expansion/plus-minus6.0 new file mode 100644 index 0000000..acd4d77 --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus6.0 @@ -0,0 +1,34 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/plus-minus7.0 b/bin/sh/tests/expansion/plus-minus7.0 new file mode 100644 index 0000000..636f133 --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus7.0 @@ -0,0 +1,26 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/plus-minus8.0 b/bin/sh/tests/expansion/plus-minus8.0 new file mode 100644 index 0000000..218feef --- /dev/null +++ b/bin/sh/tests/expansion/plus-minus8.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/question1.0 b/bin/sh/tests/expansion/question1.0 new file mode 100644 index 0000000..dfbf5bf --- /dev/null +++ b/bin/sh/tests/expansion/question1.0 @@ -0,0 +1,22 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/readonly1.0 b/bin/sh/tests/expansion/readonly1.0 new file mode 100644 index 0000000..51cbc45 --- /dev/null +++ b/bin/sh/tests/expansion/readonly1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/redir1.0 b/bin/sh/tests/expansion/redir1.0 new file mode 100644 index 0000000..5b54394 --- /dev/null +++ b/bin/sh/tests/expansion/redir1.0 @@ -0,0 +1,26 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/set-u1.0 b/bin/sh/tests/expansion/set-u1.0 new file mode 100644 index 0000000..c9f1017 --- /dev/null +++ b/bin/sh/tests/expansion/set-u1.0 @@ -0,0 +1,29 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/set-u2.0 b/bin/sh/tests/expansion/set-u2.0 new file mode 100644 index 0000000..ce91458 --- /dev/null +++ b/bin/sh/tests/expansion/set-u2.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/set-u3.0 b/bin/sh/tests/expansion/set-u3.0 new file mode 100644 index 0000000..6a904f0 --- /dev/null +++ b/bin/sh/tests/expansion/set-u3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/expansion/tilde1.0 b/bin/sh/tests/expansion/tilde1.0 new file mode 100644 index 0000000..4bf1942 --- /dev/null +++ b/bin/sh/tests/expansion/tilde1.0 @@ -0,0 +1,56 @@ +# $FreeBSD: releng/11.1/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 < diff --git a/bin/sh/tests/parameters/Makefile.depend b/bin/sh/tests/parameters/Makefile.depend new file mode 100644 index 0000000..5aee3de --- /dev/null +++ b/bin/sh/tests/parameters/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parameters/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/parameters/env1.0 b/bin/sh/tests/parameters/env1.0 new file mode 100644 index 0000000..7f900bf --- /dev/null +++ b/bin/sh/tests/parameters/env1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/exitstatus1.0 b/bin/sh/tests/parameters/exitstatus1.0 new file mode 100644 index 0000000..3659874 --- /dev/null +++ b/bin/sh/tests/parameters/exitstatus1.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/mail1.0 b/bin/sh/tests/parameters/mail1.0 new file mode 100644 index 0000000..13a7586 --- /dev/null +++ b/bin/sh/tests/parameters/mail1.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/mail2.0 b/bin/sh/tests/parameters/mail2.0 new file mode 100644 index 0000000..a3ba866 --- /dev/null +++ b/bin/sh/tests/parameters/mail2.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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 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/bin/sh/tests/parameters/optind1.0 b/bin/sh/tests/parameters/optind1.0 new file mode 100644 index 0000000..c118bff --- /dev/null +++ b/bin/sh/tests/parameters/optind1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parameters/optind1.0 227773 2011-11-20 21:48:50Z jilles $ + +unset OPTIND && [ -z "$OPTIND" ] diff --git a/bin/sh/tests/parameters/optind2.0 b/bin/sh/tests/parameters/optind2.0 new file mode 100644 index 0000000..c54dae0 --- /dev/null +++ b/bin/sh/tests/parameters/optind2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional1.0 b/bin/sh/tests/parameters/positional1.0 new file mode 100644 index 0000000..287369c --- /dev/null +++ b/bin/sh/tests/parameters/positional1.0 @@ -0,0 +1,13 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional2.0 b/bin/sh/tests/parameters/positional2.0 new file mode 100644 index 0000000..b972c2a --- /dev/null +++ b/bin/sh/tests/parameters/positional2.0 @@ -0,0 +1,65 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional3.0 b/bin/sh/tests/parameters/positional3.0 new file mode 100644 index 0000000..d1e6699 --- /dev/null +++ b/bin/sh/tests/parameters/positional3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional4.0 b/bin/sh/tests/parameters/positional4.0 new file mode 100644 index 0000000..e5db418 --- /dev/null +++ b/bin/sh/tests/parameters/positional4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional5.0 b/bin/sh/tests/parameters/positional5.0 new file mode 100644 index 0000000..2c27775 --- /dev/null +++ b/bin/sh/tests/parameters/positional5.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional6.0 b/bin/sh/tests/parameters/positional6.0 new file mode 100644 index 0000000..e687439 --- /dev/null +++ b/bin/sh/tests/parameters/positional6.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional7.0 b/bin/sh/tests/parameters/positional7.0 new file mode 100644 index 0000000..c778da1 --- /dev/null +++ b/bin/sh/tests/parameters/positional7.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parameters/positional7.0 273802 2014-10-28 22:14:31Z jilles $ + +set -- / '' +IFS=* +set -- "$*" +IFS=: +args="$*" +[ "$#:$args" = "1:/*" ] diff --git a/bin/sh/tests/parameters/positional8.0 b/bin/sh/tests/parameters/positional8.0 new file mode 100644 index 0000000..2dac415 --- /dev/null +++ b/bin/sh/tests/parameters/positional8.0 @@ -0,0 +1,31 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/positional9.0 b/bin/sh/tests/parameters/positional9.0 new file mode 100644 index 0000000..74e6b29 --- /dev/null +++ b/bin/sh/tests/parameters/positional9.0 @@ -0,0 +1,18 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/pwd1.0 b/bin/sh/tests/parameters/pwd1.0 new file mode 100644 index 0000000..bf3b94c --- /dev/null +++ b/bin/sh/tests/parameters/pwd1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parameters/pwd2.0 b/bin/sh/tests/parameters/pwd2.0 new file mode 100644 index 0000000..9f0c935 --- /dev/null +++ b/bin/sh/tests/parameters/pwd2.0 @@ -0,0 +1,24 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/Makefile b/bin/sh/tests/parser/Makefile new file mode 100644 index 0000000..d6847f5 --- /dev/null +++ b/bin/sh/tests/parser/Makefile @@ -0,0 +1,85 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/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+= 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+= 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 diff --git a/bin/sh/tests/parser/Makefile.depend b/bin/sh/tests/parser/Makefile.depend new file mode 100644 index 0000000..6d3359d --- /dev/null +++ b/bin/sh/tests/parser/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/parser/alias1.0 b/bin/sh/tests/parser/alias1.0 new file mode 100644 index 0000000..1095186 --- /dev/null +++ b/bin/sh/tests/parser/alias1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias10.0 b/bin/sh/tests/parser/alias10.0 new file mode 100644 index 0000000..d688343 --- /dev/null +++ b/bin/sh/tests/parser/alias10.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias11.0 b/bin/sh/tests/parser/alias11.0 new file mode 100644 index 0000000..b5a65a2 --- /dev/null +++ b/bin/sh/tests/parser/alias11.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias12.0 b/bin/sh/tests/parser/alias12.0 new file mode 100644 index 0000000..7b54881 --- /dev/null +++ b/bin/sh/tests/parser/alias12.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias13.0 b/bin/sh/tests/parser/alias13.0 new file mode 100644 index 0000000..8d9260e --- /dev/null +++ b/bin/sh/tests/parser/alias13.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias14.0 b/bin/sh/tests/parser/alias14.0 new file mode 100644 index 0000000..b664da1 --- /dev/null +++ b/bin/sh/tests/parser/alias14.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias15.0 b/bin/sh/tests/parser/alias15.0 new file mode 100644 index 0000000..4f47297 --- /dev/null +++ b/bin/sh/tests/parser/alias15.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias15.0.stdout b/bin/sh/tests/parser/alias15.0.stdout new file mode 100644 index 0000000..6dd179c --- /dev/null +++ b/bin/sh/tests/parser/alias15.0.stdout @@ -0,0 +1,4 @@ +echo test1 +test1 +echo test2 +test2 diff --git a/bin/sh/tests/parser/alias2.0 b/bin/sh/tests/parser/alias2.0 new file mode 100644 index 0000000..a1fb214 --- /dev/null +++ b/bin/sh/tests/parser/alias2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias3.0 b/bin/sh/tests/parser/alias3.0 new file mode 100644 index 0000000..23f63af --- /dev/null +++ b/bin/sh/tests/parser/alias3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias4.0 b/bin/sh/tests/parser/alias4.0 new file mode 100644 index 0000000..99c0b06 --- /dev/null +++ b/bin/sh/tests/parser/alias4.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/alias5.0 b/bin/sh/tests/parser/alias5.0 new file mode 100644 index 0000000..24216c3 --- /dev/null +++ b/bin/sh/tests/parser/alias5.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/alias5.0 222165 2011-05-21 22:03:06Z jilles $ + +alias alias0=exit +eval '&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/bin/sh/tests/parser/comment1.0 b/bin/sh/tests/parser/comment1.0 new file mode 100644 index 0000000..3421c75 --- /dev/null +++ b/bin/sh/tests/parser/comment1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/comment1.0 295818 2016-02-19 16:56:07Z jilles $ + +${SH} -c '#' diff --git a/bin/sh/tests/parser/comment2.42 b/bin/sh/tests/parser/comment2.42 new file mode 100644 index 0000000..bdef9f2 --- /dev/null +++ b/bin/sh/tests/parser/comment2.42 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/comment2.42 295818 2016-02-19 16:56:07Z jilles $ + +${SH} -c '# +exit 42' diff --git a/bin/sh/tests/parser/dollar-quote1.0 b/bin/sh/tests/parser/dollar-quote1.0 new file mode 100644 index 0000000..1206141 --- /dev/null +++ b/bin/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/bin/sh/tests/parser/dollar-quote10.0 b/bin/sh/tests/parser/dollar-quote10.0 new file mode 100644 index 0000000..c19a6a2 --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote10.0 @@ -0,0 +1,10 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/dollar-quote11.0 b/bin/sh/tests/parser/dollar-quote11.0 new file mode 100644 index 0000000..7acdd4c --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote11.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/dollar-quote12.0 b/bin/sh/tests/parser/dollar-quote12.0 new file mode 100644 index 0000000..8236b0a --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote12.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/dollar-quote13.0 b/bin/sh/tests/parser/dollar-quote13.0 new file mode 100644 index 0000000..4119121 --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote13.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/dollar-quote2.0 b/bin/sh/tests/parser/dollar-quote2.0 new file mode 100644 index 0000000..4617ed8 --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote2.0 @@ -0,0 +1,5 @@ +# $FreeBSD$ + +# This depends on the ASCII character set. + +[ $'\e' = "$(printf "\033")" ] diff --git a/bin/sh/tests/parser/dollar-quote3.0 b/bin/sh/tests/parser/dollar-quote3.0 new file mode 100644 index 0000000..a7e6852 --- /dev/null +++ b/bin/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/bin/sh/tests/parser/dollar-quote4.0 b/bin/sh/tests/parser/dollar-quote4.0 new file mode 100644 index 0000000..f620af5 --- /dev/null +++ b/bin/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/bin/sh/tests/parser/dollar-quote5.0 b/bin/sh/tests/parser/dollar-quote5.0 new file mode 100644 index 0000000..c2c44ca --- /dev/null +++ b/bin/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/bin/sh/tests/parser/dollar-quote6.0 b/bin/sh/tests/parser/dollar-quote6.0 new file mode 100644 index 0000000..a4b1e3f --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote6.0 @@ -0,0 +1,5 @@ +# $FreeBSD$ + +# This depends on the ASCII character set. + +[ $'\c\\' = $'\034' ] diff --git a/bin/sh/tests/parser/dollar-quote7.0 b/bin/sh/tests/parser/dollar-quote7.0 new file mode 100644 index 0000000..c866b1a --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote7.0 @@ -0,0 +1,6 @@ +# $FreeBSD$ + +set -e + +[ $'\u0024\u0040\u0060' = '$@`' ] +[ $'\U00000024\U00000040\U00000060' = '$@`' ] diff --git a/bin/sh/tests/parser/dollar-quote8.0 b/bin/sh/tests/parser/dollar-quote8.0 new file mode 100644 index 0000000..8f0b41a --- /dev/null +++ b/bin/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/bin/sh/tests/parser/dollar-quote9.0 b/bin/sh/tests/parser/dollar-quote9.0 new file mode 100644 index 0000000..df64b7d --- /dev/null +++ b/bin/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/bin/sh/tests/parser/empty-braces1.0 b/bin/sh/tests/parser/empty-braces1.0 new file mode 100644 index 0000000..c48ef81 --- /dev/null +++ b/bin/sh/tests/parser/empty-braces1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/empty-cmd1.0 b/bin/sh/tests/parser/empty-cmd1.0 new file mode 100644 index 0000000..4bb2ea9 --- /dev/null +++ b/bin/sh/tests/parser/empty-cmd1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/empty-cmd1.0 254843 2013-08-25 10:57:48Z jilles $ + +! (eval ': || f()') 2>/dev/null diff --git a/bin/sh/tests/parser/for1.0 b/bin/sh/tests/parser/for1.0 new file mode 100644 index 0000000..ecf72a7 --- /dev/null +++ b/bin/sh/tests/parser/for1.0 @@ -0,0 +1,29 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/for2.0 b/bin/sh/tests/parser/for2.0 new file mode 100644 index 0000000..6bb766d --- /dev/null +++ b/bin/sh/tests/parser/for2.0 @@ -0,0 +1,15 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/func1.0 b/bin/sh/tests/parser/func1.0 new file mode 100644 index 0000000..9be423f --- /dev/null +++ b/bin/sh/tests/parser/func1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/func2.0 b/bin/sh/tests/parser/func2.0 new file mode 100644 index 0000000..8395a1f --- /dev/null +++ b/bin/sh/tests/parser/func2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/func3.0 b/bin/sh/tests/parser/func3.0 new file mode 100644 index 0000000..fe65a4b --- /dev/null +++ b/bin/sh/tests/parser/func3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/heredoc1.0 b/bin/sh/tests/parser/heredoc1.0 new file mode 100644 index 0000000..02a08e4 --- /dev/null +++ b/bin/sh/tests/parser/heredoc1.0 @@ -0,0 +1,85 @@ +# $FreeBSD: releng/11.1/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 </dev/null; command eval : hi \${r:=0} +exit ${r:-3} diff --git a/bin/sh/tests/parser/heredoc7.0 b/bin/sh/tests/parser/heredoc7.0 new file mode 100644 index 0000000..ce74e64 --- /dev/null +++ b/bin/sh/tests/parser/heredoc7.0 @@ -0,0 +1,19 @@ +# $FreeBSD: releng/11.1/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 ': <"$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/bin/sh/tests/parser/line-cont2.0 b/bin/sh/tests/parser/line-cont2.0 new file mode 100644 index 0000000..74c88de --- /dev/null +++ b/bin/sh/tests/parser/line-cont2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/line-cont2.0 273243 2014-10-17 21:52:57Z jilles $ + +[ "a\ +b" = ab ] diff --git a/bin/sh/tests/parser/line-cont3.0 b/bin/sh/tests/parser/line-cont3.0 new file mode 100644 index 0000000..6a4bdef --- /dev/null +++ b/bin/sh/tests/parser/line-cont3.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/line-cont4.0 b/bin/sh/tests/parser/line-cont4.0 new file mode 100644 index 0000000..fa3c3db --- /dev/null +++ b/bin/sh/tests/parser/line-cont4.0 @@ -0,0 +1,8 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/line-cont5.0 b/bin/sh/tests/parser/line-cont5.0 new file mode 100644 index 0000000..78f5808 --- /dev/null +++ b/bin/sh/tests/parser/line-cont5.0 @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/line-cont6.0 b/bin/sh/tests/parser/line-cont6.0 new file mode 100644 index 0000000..6849518 --- /dev/null +++ b/bin/sh/tests/parser/line-cont6.0 @@ -0,0 +1,23 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/line-cont7.0 b/bin/sh/tests/parser/line-cont7.0 new file mode 100644 index 0000000..0c0d83b --- /dev/null +++ b/bin/sh/tests/parser/line-cont7.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/line-cont7.0 273276 2014-10-19 11:59:15Z jilles $ + +[ "$(\ +( +1\ ++ 1)\ +)" = 2 ] diff --git a/bin/sh/tests/parser/line-cont8.0 b/bin/sh/tests/parser/line-cont8.0 new file mode 100644 index 0000000..b0872cc --- /dev/null +++ b/bin/sh/tests/parser/line-cont8.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/line-cont9.0 b/bin/sh/tests/parser/line-cont9.0 new file mode 100644 index 0000000..ce5c354 --- /dev/null +++ b/bin/sh/tests/parser/line-cont9.0 @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/line-cont9.0 273276 2014-10-19 11:59:15Z jilles $ + +[ "${$\ +:\ ++\ +xyz}" = xyz ] diff --git a/bin/sh/tests/parser/no-space1.0 b/bin/sh/tests/parser/no-space1.0 new file mode 100644 index 0000000..f361172 --- /dev/null +++ b/bin/sh/tests/parser/no-space1.0 @@ -0,0 +1,18 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/no-space2.0 b/bin/sh/tests/parser/no-space2.0 new file mode 100644 index 0000000..fd03be7 --- /dev/null +++ b/bin/sh/tests/parser/no-space2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/nul1.0 b/bin/sh/tests/parser/nul1.0 new file mode 100644 index 0000000..454e74e --- /dev/null +++ b/bin/sh/tests/parser/nul1.0 @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/parser/only-redir1.0 b/bin/sh/tests/parser/only-redir1.0 new file mode 100644 index 0000000..a3fa861 --- /dev/null +++ b/bin/sh/tests/parser/only-redir1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/parser/only-redir1.0 210221 2010-07-18 12:45:31Z jilles $ +&2 +set -v +echo two >&2 +echo three >&2 +EOF diff --git a/bin/sh/tests/parser/set-v1.0.stderr b/bin/sh/tests/parser/set-v1.0.stderr new file mode 100644 index 0000000..d904fa5 --- /dev/null +++ b/bin/sh/tests/parser/set-v1.0.stderr @@ -0,0 +1,5 @@ +one +echo two >&2 +two +echo three >&2 +three diff --git a/bin/sh/tests/parser/var-assign1.0 b/bin/sh/tests/parser/var-assign1.0 new file mode 100644 index 0000000..16bc4e2 --- /dev/null +++ b/bin/sh/tests/parser/var-assign1.0 @@ -0,0 +1,19 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/Makefile b/bin/sh/tests/set-e/Makefile new file mode 100644 index 0000000..c819145 --- /dev/null +++ b/bin/sh/tests/set-e/Makefile @@ -0,0 +1,46 @@ +# $FreeBSD: releng/11.1/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 diff --git a/bin/sh/tests/set-e/Makefile.depend b/bin/sh/tests/set-e/Makefile.depend new file mode 100644 index 0000000..243a366 --- /dev/null +++ b/bin/sh/tests/set-e/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/sh/tests/set-e/and1.0 b/bin/sh/tests/set-e/and1.0 new file mode 100644 index 0000000..6d62319 --- /dev/null +++ b/bin/sh/tests/set-e/and1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/and1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true && true diff --git a/bin/sh/tests/set-e/and2.1 b/bin/sh/tests/set-e/and2.1 new file mode 100644 index 0000000..07ce143 --- /dev/null +++ b/bin/sh/tests/set-e/and2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/and2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true && false +exit 0 diff --git a/bin/sh/tests/set-e/and3.0 b/bin/sh/tests/set-e/and3.0 new file mode 100644 index 0000000..115f891 --- /dev/null +++ b/bin/sh/tests/set-e/and3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/and3.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false && true +exit 0 diff --git a/bin/sh/tests/set-e/and4.0 b/bin/sh/tests/set-e/and4.0 new file mode 100644 index 0000000..27e9024 --- /dev/null +++ b/bin/sh/tests/set-e/and4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/and4.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false && false +exit 0 diff --git a/bin/sh/tests/set-e/background1.0 b/bin/sh/tests/set-e/background1.0 new file mode 100644 index 0000000..214d983 --- /dev/null +++ b/bin/sh/tests/set-e/background1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/background1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false & diff --git a/bin/sh/tests/set-e/cmd1.0 b/bin/sh/tests/set-e/cmd1.0 new file mode 100644 index 0000000..b92faba --- /dev/null +++ b/bin/sh/tests/set-e/cmd1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/cmd1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true diff --git a/bin/sh/tests/set-e/cmd2.1 b/bin/sh/tests/set-e/cmd2.1 new file mode 100644 index 0000000..f0bf224 --- /dev/null +++ b/bin/sh/tests/set-e/cmd2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/cmd2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false +exit 0 diff --git a/bin/sh/tests/set-e/elif1.0 b/bin/sh/tests/set-e/elif1.0 new file mode 100644 index 0000000..334c304 --- /dev/null +++ b/bin/sh/tests/set-e/elif1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/elif2.0 b/bin/sh/tests/set-e/elif2.0 new file mode 100644 index 0000000..d5b0168 --- /dev/null +++ b/bin/sh/tests/set-e/elif2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/eval1.0 b/bin/sh/tests/set-e/eval1.0 new file mode 100644 index 0000000..1fad124 --- /dev/null +++ b/bin/sh/tests/set-e/eval1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/eval1.0 193178 2009-05-31 17:23:27Z stefanf $ +set -e +eval false || true diff --git a/bin/sh/tests/set-e/eval2.1 b/bin/sh/tests/set-e/eval2.1 new file mode 100644 index 0000000..d37e8e0 --- /dev/null +++ b/bin/sh/tests/set-e/eval2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/eval2.1 193178 2009-05-31 17:23:27Z stefanf $ +set -e +eval false +exit 0 diff --git a/bin/sh/tests/set-e/for1.0 b/bin/sh/tests/set-e/for1.0 new file mode 100644 index 0000000..8226853 --- /dev/null +++ b/bin/sh/tests/set-e/for1.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/func1.0 b/bin/sh/tests/set-e/func1.0 new file mode 100644 index 0000000..6bc842a --- /dev/null +++ b/bin/sh/tests/set-e/func1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/func2.1 b/bin/sh/tests/set-e/func2.1 new file mode 100644 index 0000000..c07f648 --- /dev/null +++ b/bin/sh/tests/set-e/func2.1 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/if1.0 b/bin/sh/tests/set-e/if1.0 new file mode 100644 index 0000000..f7e7e96 --- /dev/null +++ b/bin/sh/tests/set-e/if1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/if1.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +if false; then + : +fi diff --git a/bin/sh/tests/set-e/if2.0 b/bin/sh/tests/set-e/if2.0 new file mode 100644 index 0000000..63368bc --- /dev/null +++ b/bin/sh/tests/set-e/if2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/if3.0 b/bin/sh/tests/set-e/if3.0 new file mode 100644 index 0000000..2e8fe34 --- /dev/null +++ b/bin/sh/tests/set-e/if3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/not1.0 b/bin/sh/tests/set-e/not1.0 new file mode 100644 index 0000000..c11f843 --- /dev/null +++ b/bin/sh/tests/set-e/not1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/not1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +! true +exit 0 diff --git a/bin/sh/tests/set-e/not2.0 b/bin/sh/tests/set-e/not2.0 new file mode 100644 index 0000000..dc15237 --- /dev/null +++ b/bin/sh/tests/set-e/not2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/not2.0 193178 2009-05-31 17:23:27Z stefanf $ +set -e +! false +! eval false diff --git a/bin/sh/tests/set-e/or1.0 b/bin/sh/tests/set-e/or1.0 new file mode 100644 index 0000000..766e5c5 --- /dev/null +++ b/bin/sh/tests/set-e/or1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/or1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true || false diff --git a/bin/sh/tests/set-e/or2.0 b/bin/sh/tests/set-e/or2.0 new file mode 100644 index 0000000..552fca8 --- /dev/null +++ b/bin/sh/tests/set-e/or2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/or2.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false || true diff --git a/bin/sh/tests/set-e/or3.1 b/bin/sh/tests/set-e/or3.1 new file mode 100644 index 0000000..68f2d76 --- /dev/null +++ b/bin/sh/tests/set-e/or3.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/or3.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false || false +exit 0 diff --git a/bin/sh/tests/set-e/pipe1.1 b/bin/sh/tests/set-e/pipe1.1 new file mode 100644 index 0000000..9a580b8 --- /dev/null +++ b/bin/sh/tests/set-e/pipe1.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/pipe1.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true | false +exit 0 diff --git a/bin/sh/tests/set-e/pipe2.0 b/bin/sh/tests/set-e/pipe2.0 new file mode 100644 index 0000000..964e0df --- /dev/null +++ b/bin/sh/tests/set-e/pipe2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/pipe2.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false | true diff --git a/bin/sh/tests/set-e/return1.0 b/bin/sh/tests/set-e/return1.0 new file mode 100644 index 0000000..4408198 --- /dev/null +++ b/bin/sh/tests/set-e/return1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/semi1.1 b/bin/sh/tests/set-e/semi1.1 new file mode 100644 index 0000000..6e3a4e2 --- /dev/null +++ b/bin/sh/tests/set-e/semi1.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/semi1.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false; true +exit 0 diff --git a/bin/sh/tests/set-e/semi2.1 b/bin/sh/tests/set-e/semi2.1 new file mode 100644 index 0000000..42c97df --- /dev/null +++ b/bin/sh/tests/set-e/semi2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/semi2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true; false +exit 0 diff --git a/bin/sh/tests/set-e/subshell1.0 b/bin/sh/tests/set-e/subshell1.0 new file mode 100644 index 0000000..8a78870 --- /dev/null +++ b/bin/sh/tests/set-e/subshell1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/subshell1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +(true) diff --git a/bin/sh/tests/set-e/subshell2.1 b/bin/sh/tests/set-e/subshell2.1 new file mode 100644 index 0000000..47d62b3 --- /dev/null +++ b/bin/sh/tests/set-e/subshell2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/subshell2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +(false) +exit 0 diff --git a/bin/sh/tests/set-e/until1.0 b/bin/sh/tests/set-e/until1.0 new file mode 100644 index 0000000..138421f --- /dev/null +++ b/bin/sh/tests/set-e/until1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/until2.0 b/bin/sh/tests/set-e/until2.0 new file mode 100644 index 0000000..f520166 --- /dev/null +++ b/bin/sh/tests/set-e/until2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/until3.0 b/bin/sh/tests/set-e/until3.0 new file mode 100644 index 0000000..d35f118 --- /dev/null +++ b/bin/sh/tests/set-e/until3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/while1.0 b/bin/sh/tests/set-e/while1.0 new file mode 100644 index 0000000..0e90a16 --- /dev/null +++ b/bin/sh/tests/set-e/while1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/bin/sh/tests/set-e/while1.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +while false; do + : +done diff --git a/bin/sh/tests/set-e/while2.0 b/bin/sh/tests/set-e/while2.0 new file mode 100644 index 0000000..ba178e7 --- /dev/null +++ b/bin/sh/tests/set-e/while2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/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/bin/sh/tests/set-e/while3.0 b/bin/sh/tests/set-e/while3.0 new file mode 100644 index 0000000..5230796 --- /dev/null +++ b/bin/sh/tests/set-e/while3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/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/bin/sh/trap.c b/bin/sh/trap.c new file mode 100644 index 0000000..3de3823 --- /dev/null +++ b/bin/sh/trap.c @@ -0,0 +1,555 @@ +/*- + * 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. + * 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[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/trap.c 297360 2016-03-28 18:58:40Z jilles $"); + +#include +#include +#include + +#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" + + +/* + * 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. + */ +void +setinteractive(int on) +{ + static int is_interactive = -1; + + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + is_interactive = on; +} + + +/* + * 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; + evalstring(p, 0); + } + } + if (!setjmp(loc2.loc)) { + handler = &loc2; /* probably unnecessary */ + 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(). */ + } + out2fmt_flush("H-%s:%i", __FILE__,__LINE__); + _exit(exiting_exitstatus); +} diff --git a/bin/sh/trap.h b/bin/sh/trap.h new file mode 100644 index 0000000..6b70272 --- /dev/null +++ b/bin/sh/trap.h @@ -0,0 +1,48 @@ +/*- + * 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. + * 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. + * + * @(#)trap.h 8.3 (Berkeley) 6/5/95 + * $FreeBSD: releng/11.1/bin/sh/trap.h 276038 2014-12-21 23:09:59Z jilles $ + */ + +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(int); +void exitshell(int) __dead2; +void exitshell_savedstatus(void) __dead2; diff --git a/bin/sh/var.c b/bin/sh/var.c new file mode 100644 index 0000000..7aaee36 --- /dev/null +++ b/bin/sh/var.c @@ -0,0 +1,966 @@ +/*- + * 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. + * 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[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: releng/11.1/bin/sh/var.c 318279 2017-05-14 20:17:50Z jilles $"); + +#include +#include +#include + +/* + * Shell variables. + */ + +#include +#include + +#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 + + +#define VTABSIZE 39 + + +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); +} + +/* + * 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/bin/sh/var.h b/bin/sh/var.h new file mode 100644 index 0000000..0dd1367 --- /dev/null +++ b/bin/sh/var.h @@ -0,0 +1,130 @@ +/*- + * 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. + * 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. + * + * @(#)var.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: releng/11.1/bin/sh/var.h 289159 2015-10-11 21:33:00Z jilles $ + */ + +/* + * 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/bin/test/Makefile b/bin/test/Makefile new file mode 100644 index 0000000..571a05f --- /dev/null +++ b/bin/test/Makefile @@ -0,0 +1,15 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD: releng/11.1/bin/test/Makefile 298107 2016-04-16 07:45:30Z gjb $ + +.include + +PACKAGE=runtime +PROG= test +LINKS= ${BINDIR}/test ${BINDIR}/[ +MLINKS= test.1 [.1 + +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + +.include diff --git a/bin/test/Makefile.depend b/bin/test/Makefile.depend new file mode 100644 index 0000000..f9fb94a --- /dev/null +++ b/bin/test/Makefile.depend @@ -0,0 +1,18 @@ +# $FreeBSD: releng/11.1/bin/test/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/test/test.1 b/bin/test/test.1 new file mode 100644 index 0000000..b596edd --- /dev/null +++ b/bin/test/test.1 @@ -0,0 +1,395 @@ +.\"- +.\" 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: releng/11.1/bin/test/test.1 307424 2016-10-16 23:19:14Z sevan $ +.\" +.Dd October 5, 2016 +.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 HISTORY +A +.Nm +utility appeared in +.At v7 . +.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/bin/test/test.c b/bin/test/test.c new file mode 100644 index 0000000..5712015 --- /dev/null +++ b/bin/test/test.c @@ -0,0 +1,629 @@ +/* $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 +__FBSDID("$FreeBSD: releng/11.1/bin/test/test.c 298232 2016-04-19 00:38:07Z araujo $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SHELL +#define main testcmd +#include "bltin/bltin.h" +#else +#include + +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 ::= +*/ + +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 const struct t_op { + char op_text[2]; + short op_num; +} ops1[] = { + {"=", STREQ}, + {"<", STRLT}, + {">", STRGT}, + {"!", UNOT}, + {"(", LPAREN}, + {")", RPAREN}, +}, opsm1[] = { + {"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}, + {"a", BAND}, + {"o", BOR}, +}, ops2[] = { + {"==", STREQ}, + {"!=", STRNE}, +}, opsm2[] = { + {"eq", INTEQ}, + {"ne", INTNE}, + {"ge", INTGE}, + {"gt", INTGT}, + {"le", INTLE}, + {"lt", INTLT}, + {"nt", FILNT}, + {"ot", FILOT}, + {"ef", FILEQ}, +}; + +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; + + 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 int +find_op_1char(const struct t_op *op, const struct t_op *end, const char *s) +{ + char c; + + c = s[0]; + while (op != end) { + if (c == *op->op_text) + return op->op_num; + op++; + } + return OPERAND; +} + +static int +find_op_2char(const struct t_op *op, const struct t_op *end, const char *s) +{ + while (op != end) { + if (s[0] == op->op_text[0] && s[1] == op->op_text[1]) + return op->op_num; + op++; + } + return OPERAND; +} + +static int +find_op(const char *s) +{ + if (s[0] == '\0') + return OPERAND; + else if (s[1] == '\0') + return find_op_1char(ops1, (&ops1)[1], s); + else if (s[2] == '\0') + return s[0] == '-' ? find_op_1char(opsm1, (&opsm1)[1], s + 1) : + find_op_2char(ops2, (&ops2)[1], s); + else if (s[3] == '\0') + return s[0] == '-' ? find_op_2char(opsm2, (&opsm2)[1], s + 1) : + OPERAND; + else + return OPERAND; +} + +static enum token +t_lex(char *s) +{ + int num; + + if (s == NULL) { + return EOI; + } + num = find_op(s); + if (((TOKEN_TYPE(num) == UNOP || TOKEN_TYPE(num) == BUNOP) + && isunopoperand()) || + (num == LPAREN && islparenoperand()) || + (num == RPAREN && isrparenoperand())) + return OPERAND; + return num; +} + +static int +isunopoperand(void) +{ + char *s; + char *t; + int num; + + if (nargc == 1) + return 1; + s = *(t_wp + 1); + if (nargc == 2) + return parenlevel == 1 && strcmp(s, ")") == 0; + t = *(t_wp + 2); + num = find_op(s); + return TOKEN_TYPE(num) == BINOP && + (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); +} + +static int +islparenoperand(void) +{ + char *s; + int num; + + if (nargc == 1) + return 1; + s = *(t_wp + 1); + if (nargc == 2) + return parenlevel == 1 && strcmp(s, ")") == 0; + if (nargc != 3) + return 0; + num = find_op(s); + return TOKEN_TYPE(num) == BINOP; +} + +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/bin/test/tests/Makefile b/bin/test/tests/Makefile new file mode 100644 index 0000000..76e5287 --- /dev/null +++ b/bin/test/tests/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD: releng/11.1/bin/test/tests/Makefile 299094 2016-05-04 23:20:53Z ngie $ + +.include + +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 diff --git a/bin/test/tests/Makefile.depend b/bin/test/tests/Makefile.depend new file mode 100644 index 0000000..552e641 --- /dev/null +++ b/bin/test/tests/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/bin/test/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/test/tests/legacy_test.sh b/bin/test/tests/legacy_test.sh new file mode 100644 index 0000000..bde8bbb --- /dev/null +++ b/bin/test/tests/legacy_test.sh @@ -0,0 +1,196 @@ +#!/bin/sh + +#- +# Copyright (c) June 1996 Wolfram Schneider . 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: releng/11.1/bin/test/tests/legacy_test.sh 289488 2015-10-18 05:51:44Z ngie $ + +# 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)) + # 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" + elif [ "$ret" != "$1" ]; then + printf "not ok %s - (got $ret, expected $1)\n" "$count $2" + else + printf "ok %s\n" "$count $2" + fi +} + +count=0 +echo "1..130" + +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/lib/Makefile.inc b/lib/Makefile.inc new file mode 100644 index 0000000..2d4fd67 --- /dev/null +++ b/lib/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD: releng/11.1/lib/Makefile.inc 201381 2010-01-02 09:58:07Z ed $ +# Default version for system libs (override in /Makefile if necessary) +SHLIB_MAJOR?= 5 + +WARNS?= 6 diff --git a/lib/csu/Makefile b/lib/csu/Makefile index 762732f..8c3a31f 100644 --- a/lib/csu/Makefile +++ b/lib/csu/Makefile @@ -1,36 +1,9 @@ -# $Id: Makefile 141 2016-01-17 02:05:18Z reddawg $ -# The System Makefile (C) 2002 The UbixOS Project +# $FreeBSD: releng/11.1/lib/csu/Makefile 283125 2015-05-19 21:00:53Z emaste $ -# Include Global 'Source' Options -include ../../Makefile.incl -include ../Makefile.incl +.if exists(${.CURDIR}/${MACHINE_ARCH}) +SUBDIR+= ${MACHINE_ARCH} +.else +SUBDIR+= ${MACHINE_CPUARCH} +.endif -CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -Wno-uninitialized -O2 -fno-strict-aliasing -pipe -Wsystem-headers -Werror -Wall -Wno-format-y2k -W -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wcast-align -Wunused-parameter -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls #-DGCRT - -INCLUDES+= -I../libc_old/include - -#Objects -OBJS = crtn.o crti.o crt1.o crtbrand.o - -all: $(OBJS) - -# Compile the source files -.cc.o: - $(CXX) $(CFLAGS) -Wall -nostdlib -O $(INCLUDES) -c -o $@ $< - -.cc.s: - $(CXX) $(CFLAGS) -Wall -nostdlib -O $(INCLUDES) -S -o $@ $< - -.c.o: - $(CC) $(CFLAGS) -Wall -nostdlib -O $(INCLUDES) -c $< - -.c.s: - $(CC) $(CFLAGS) -Wall -nostdlib -O $(INCLUDES) -S -o $@ $< - -.S.o: - $(CC) $(CFLAGS) -Wall -nostdlib -c -o $@ $< - -# Clean up the junk -clean: - $(REMOVE) $(OBJS) - +.include diff --git a/lib/csu/Makefile.inc b/lib/csu/Makefile.inc new file mode 100644 index 0000000..bf7ffbb --- /dev/null +++ b/lib/csu/Makefile.inc @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/lib/csu/Makefile.inc 288074 2015-09-21 18:39:13Z bdrewery $ + +SSP_CFLAGS= + +SED_FIX_NOTE = -i "" -e '/\.note\.tag/s/progbits/note/' + +NO_WMISSING_VARIABLE_DECLARATIONS= + +.include "../Makefile.inc" diff --git a/lib/csu/aarch64/Makefile b/lib/csu/aarch64/Makefile new file mode 100644 index 0000000..9a3de28 --- /dev/null +++ b/lib/csu/aarch64/Makefile @@ -0,0 +1,46 @@ +# $FreeBSD: releng/11.1/lib/csu/aarch64/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} +CLEANFILES+= crt1.s gcrt1.s Scrt1.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1.c is not +# directly compiled to .o files. + +crt1.s: crt1.c + ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1.o: crt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1.s + +gcrt1.s: crt1.c + ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1.o: gcrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1.s + +Scrt1.s: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1.o: Scrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1.s + +.include diff --git a/lib/csu/aarch64/crt1.c b/lib/csu/aarch64/crt1.c new file mode 100644 index 0000000..efe790a --- /dev/null +++ b/lib/csu/aarch64/crt1.c @@ -0,0 +1,85 @@ +/* LINTLIBRARY */ +/*- + * Copyright 1996-1998 John D. Polstra. + * Copyright 2014 Andrew Turner. + * Copyright 2014-2015 The FreeBSD Foundation. + * All rights reserved. + * + * Portions of this software were developed by Andrew Turner + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/aarch64/crt1.c 300406 2016-05-22 08:20:30Z andrew $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +extern long * _end; + +void __start(int, char **, char **, void (*)(void)); + +/* The entry function. */ +__asm(" .text \n" +" .align 0 \n" +" .globl _start \n" +" _start: \n" +" mov x3, x2 \n" /* cleanup */ +" add x1, x0, #8 \n" /* load argv */ +" ldr x0, [x0] \n" /* load argc */ +" add x2, x1, x0, lsl #3 \n" /* env is after argv */ +" add x2, x2, #8 \n" /* argv is null terminated */ +" b __start "); + + +/* The entry function. */ +void +__start(int argc, char *argv[], char *env[], void (*cleanup)(void)) +{ + + handle_argv(argc, argv, env); + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); + +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +__asm__("eprol:"); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} diff --git a/lib/csu/aarch64/crti.S b/lib/csu/aarch64/crti.S new file mode 100644 index 0000000..7c1253b --- /dev/null +++ b/lib/csu/aarch64/crti.S @@ -0,0 +1,48 @@ +/*- + * Copyright 2001 David E. O'Brien + * Copyright 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Andrew Turner + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/aarch64/crti.S 280259 2015-03-19 13:53:47Z andrew $"); + + .section .init,"ax",@progbits + .align 4 + .globl _init + .type _init,@function +_init: + sub sp, sp, #16 + str lr, [sp] + + .section .fini,"ax",@progbits + .align 4 + .globl _fini + .type _fini,@function +_fini: + sub sp, sp, #16 + str lr, [sp] + diff --git a/lib/csu/aarch64/crtn.S b/lib/csu/aarch64/crtn.S new file mode 100644 index 0000000..62c4851 --- /dev/null +++ b/lib/csu/aarch64/crtn.S @@ -0,0 +1,44 @@ +/*- + * Copyright 2001 David E. O'Brien + * Copyright 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Andrew Turner + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/aarch64/crtn.S 280259 2015-03-19 13:53:47Z andrew $"); + + .section .init,"ax",@progbits + ldr lr, [sp] + add sp, sp, #16 + ret + + + .section .fini,"ax",@progbits + ldr lr, [sp] + add sp, sp, #16 + ret + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/amd64/Makefile b/lib/csu/amd64/Makefile new file mode 100644 index 0000000..43dbf56 --- /dev/null +++ b/lib/csu/amd64/Makefile @@ -0,0 +1,47 @@ +# $FreeBSD: releng/11.1/lib/csu/amd64/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include +CFLAGS+= -fno-omit-frame-pointer + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} +CLEANFILES+= crt1.s gcrt1.s Scrt1.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1.c is not +# directly compiled to .o files. + +crt1.s: crt1.c + ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1.o: crt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1.s + +gcrt1.s: crt1.c + ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1.o: gcrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1.s + +Scrt1.s: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1.o: Scrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1.s + +.include diff --git a/lib/csu/amd64/Makefile.depend b/lib/csu/amd64/Makefile.depend new file mode 100644 index 0000000..85f3253 --- /dev/null +++ b/lib/csu/amd64/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/lib/csu/amd64/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/csu/amd64/crt1.c b/lib/csu/amd64/crt1.c new file mode 100644 index 0000000..611a11e --- /dev/null +++ b/lib/csu/amd64/crt1.c @@ -0,0 +1,73 @@ +/* LINTLIBRARY */ +/*- + * Copyright 1996-1998 John D. Polstra. + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/amd64/crt1.c 292000 2015-12-08 19:32:58Z emaste $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +typedef void (*fptr)(void); + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +void _start(char **, void (*)(void)); + +/* The entry function. */ +void +_start(char **ap, void (*cleanup)(void)) +{ + int argc; + char **argv; + char **env; + + argc = *(long *)(void *)ap; + argv = ap + 1; + env = ap + 2 + argc; + handle_argv(argc, argv, env); + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); + +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +__asm__("eprol:"); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} diff --git a/lib/csu/amd64/crti.S b/lib/csu/amd64/crti.S new file mode 100644 index 0000000..a66927b --- /dev/null +++ b/lib/csu/amd64/crti.S @@ -0,0 +1,43 @@ +/*- + * Copyright 1996, 1997, 1998, 2000 John D. Polstra. + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/amd64/crti.S 217105 2011-01-07 16:07:51Z kib $"); + + .section .init,"ax",@progbits + .align 4 + .globl _init + .type _init,@function +_init: + subq $8,%rsp + + .section .fini,"ax",@progbits + .align 4 + .globl _fini + .type _fini,@function +_fini: + subq $8,%rsp + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/amd64/crtn.S b/lib/csu/amd64/crtn.S new file mode 100644 index 0000000..838ea32 --- /dev/null +++ b/lib/csu/amd64/crtn.S @@ -0,0 +1,37 @@ +/*- + * Copyright 1996, 1997, 1998, 2000 John D. Polstra. + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/amd64/crtn.S 217105 2011-01-07 16:07:51Z kib $"); + + .section .init,"ax",@progbits + addq $8,%rsp + ret + + .section .fini,"ax",@progbits + addq $8,%rsp + ret + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/arm/Makefile b/lib/csu/arm/Makefile new file mode 100644 index 0000000..8b10663 --- /dev/null +++ b/lib/csu/arm/Makefile @@ -0,0 +1,47 @@ +# $FreeBSD: releng/11.1/lib/csu/arm/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include +STATIC_CFLAGS+= -mlong-calls + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} +CLEANFILES+= crt1.s gcrt1.s Scrt1.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1.c is not +# directly compiled to .o files. + +crt1.s: crt1.c + ${CC} ${CFLAGS} ${STATIC_CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1.o: crt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1.s + +gcrt1.s: crt1.c + ${CC} ${CFLAGS} ${STATIC_CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1.o: gcrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1.s + +Scrt1.s: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1.o: Scrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1.s + +.include diff --git a/lib/csu/arm/Makefile.depend b/lib/csu/arm/Makefile.depend new file mode 100644 index 0000000..6274e19 --- /dev/null +++ b/lib/csu/arm/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/lib/csu/arm/Makefile.depend 288228 2015-09-25 19:44:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/csu/arm/crt1.c b/lib/csu/arm/crt1.c new file mode 100644 index 0000000..caf28a0 --- /dev/null +++ b/lib/csu/arm/crt1.c @@ -0,0 +1,130 @@ +/* LINTLIBRARY */ +/*- + * Copyright 2001 David E. O'Brien. + * All rights reserved. + * Copyright 1996-1998 John D. Polstra. + * All rights reserved. + * Copyright (c) 1997 Jason R. Thorpe. + * Copyright (c) 1995 Christopher G. Demetriou + * 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 for the + * FreeBSD Project. See http://www.freebsd.org/ for + * information about FreeBSD. + * This product includes software developed for the + * NetBSD Project. See http://www.netbsd.org/ for + * information about NetBSD. + * 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/arm/crt1.c 292000 2015-12-08 19:32:58Z emaste $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +struct Struct_Obj_Entry; +struct ps_strings; + +extern void _start(int, char **, char **, const struct Struct_Obj_Entry *, + void (*)(void), struct ps_strings *); + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +struct ps_strings *__ps_strings; + +void __start(int, char **, char **, struct ps_strings *, + const struct Struct_Obj_Entry *, void (*)(void)); + +/* The entry function. */ +__asm(" .text \n" +" .align 0 \n" +" .globl _start \n" +" _start: \n" +" mov r5, r2 /* cleanup */ \n" +" mov r4, r1 /* obj_main */ \n" +" mov r3, r0 /* ps_strings */ \n" +" /* Get argc, argv, and envp from stack */ \n" +" ldr r0, [sp, #0x0000] \n" +" add r1, sp, #0x0004 \n" +" add r2, r1, r0, lsl #2 \n" +" add r2, r2, #0x0004 \n" +" /* Ensure the stack is properly aligned before calling C code. */\n" +" bic sp, sp, #7 \n" +" sub sp, sp, #8 \n" +" str r5, [sp, #4] \n" +" str r4, [sp, #0] \n" +"\n" +" b __start "); +/* ARGSUSED */ +void +__start(int argc, char **argv, char **env, struct ps_strings *ps_strings, + const struct Struct_Obj_Entry *obj __unused, void (*cleanup)(void)) +{ + + handle_argv(argc, argv, env); + + if (ps_strings != (struct ps_strings *)0) + __ps_strings = ps_strings; + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +#endif + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} + +static const struct { + int32_t namesz; + int32_t descsz; + int32_t type; + char name[sizeof(NOTE_FREEBSD_VENDOR)]; + char desc[sizeof(MACHINE_ARCH)]; +} archtag __attribute__ ((section (NOTE_SECTION), aligned(4))) __used = { + .namesz = sizeof(NOTE_FREEBSD_VENDOR), + .descsz = sizeof(MACHINE_ARCH), + .type = ARCH_NOTETYPE, + .name = NOTE_FREEBSD_VENDOR, + .desc = MACHINE_ARCH +}; + +#ifdef GCRT +__asm__(".text"); +__asm__("eprol:"); +__asm__(".previous"); +#endif diff --git a/lib/csu/arm/crti.S b/lib/csu/arm/crti.S new file mode 100644 index 0000000..faaa18b --- /dev/null +++ b/lib/csu/arm/crti.S @@ -0,0 +1,22 @@ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/arm/crti.S 288373 2015-09-29 16:09:58Z kib $"); + + .section .init,"ax",%progbits + .align 4 + .globl _init + .type _init,%function +_init: + mov ip, sp + stmdb sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + + .section .fini,"ax",%progbits + .align 4 + .globl _fini + .type _fini,%function +_fini: + mov ip, sp + stmdb sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/arm/crtn.S b/lib/csu/arm/crtn.S new file mode 100644 index 0000000..7426a06 --- /dev/null +++ b/lib/csu/arm/crtn.S @@ -0,0 +1,12 @@ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/arm/crtn.S 288373 2015-09-29 16:09:58Z kib $"); + + .section .init,"ax",%progbits + ldmea fp, {fp, sp, pc} + mov pc, lr + + .section .fini,"ax",%progbits + ldmea fp, {fp, sp, pc} + mov pc, lr + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/common/crtbrand.c b/lib/csu/common/crtbrand.c new file mode 100644 index 0000000..df7af67 --- /dev/null +++ b/lib/csu/common/crtbrand.c @@ -0,0 +1,66 @@ +/*- + * Copyright 2000 David E. O'Brien, John D. Polstra. + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/common/crtbrand.c 321309 2017-07-20 23:39:50Z gjb $"); + +#include +#include "notes.h" + +/* + * Special ".note" entry specifying the ABI version. See + * http://www.netbsd.org/Documentation/kernel/elf-notes.html + * for more information. + * + * For all arches except sparc, gcc emits the section directive for the + * following struct with a PROGBITS type. However, newer versions of binutils + * (after 2.16.90) require the section to be of NOTE type, to guarantee that the + * .note.ABI-tag section correctly ends up in the first page of the final + * executable. + * + * Unfortunately, there is no clean way to tell gcc to use another section type, + * so this C file (or the C file that includes it) must be compiled in multiple + * steps: + * + * - Compile the .c file to a .s file. + * - Edit the .s file to change the 'progbits' type to 'note', for the section + * directive that defines the .note.ABI-tag section. + * - Compile the .s file to an object file. + * + * These steps are done in the invididual Makefiles for each applicable arch. + */ +static const struct { + int32_t namesz; + int32_t descsz; + int32_t type; + char name[sizeof(NOTE_FREEBSD_VENDOR)]; + int32_t desc; +} abitag __attribute__ ((section (NOTE_SECTION), aligned(4))) __used = { + .namesz = sizeof(NOTE_FREEBSD_VENDOR), + .descsz = sizeof(int32_t), + .type = ABI_NOTETYPE, + .name = NOTE_FREEBSD_VENDOR, + .desc = 1101001 +}; diff --git a/lib/csu/common/ignore_init.c b/lib/csu/common/ignore_init.c new file mode 100644 index 0000000..85a2726 --- /dev/null +++ b/lib/csu/common/ignore_init.c @@ -0,0 +1,118 @@ +/*- + * Copyright 2012 Konstantin Belousov + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/common/ignore_init.c 245133 2013-01-07 17:58:27Z kib $"); + +#include "notes.h" + +extern int main(int, char **, char **); + +extern void (*__preinit_array_start[])(int, char **, char **) __hidden; +extern void (*__preinit_array_end[])(int, char **, char **) __hidden; +extern void (*__init_array_start[])(int, char **, char **) __hidden; +extern void (*__init_array_end[])(int, char **, char **) __hidden; +extern void (*__fini_array_start[])(void) __hidden; +extern void (*__fini_array_end[])(void) __hidden; +extern void _fini(void) __hidden; +extern void _init(void) __hidden; + +extern int _DYNAMIC; +#pragma weak _DYNAMIC + +char **environ; +const char *__progname = ""; + +static void +finalizer(void) +{ + void (*fn)(void); + size_t array_size, n; + + array_size = __fini_array_end - __fini_array_start; + for (n = array_size; n > 0; n--) { + fn = __fini_array_start[n - 1]; + if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) + (fn)(); + } + _fini(); +} + +static inline void +handle_static_init(int argc, char **argv, char **env) +{ + void (*fn)(int, char **, char **); + size_t array_size, n; + + if (&_DYNAMIC != NULL) + return; + + atexit(finalizer); + + array_size = __preinit_array_end - __preinit_array_start; + for (n = 0; n < array_size; n++) { + fn = __preinit_array_start[n]; + if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) + fn(argc, argv, env); + } + _init(); + array_size = __init_array_end - __init_array_start; + for (n = 0; n < array_size; n++) { + fn = __init_array_start[n]; + if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) + fn(argc, argv, env); + } +} + +static inline void +handle_argv(int argc, char *argv[], char **env) +{ + const char *s; + + if (environ == NULL) + environ = env; + if (argc > 0 && argv[0] != NULL) { + __progname = argv[0]; + for (s = __progname; *s != '\0'; s++) { + if (*s == '/') + __progname = s + 1; + } + } +} + +static const struct { + int32_t namesz; + int32_t descsz; + int32_t type; + char name[sizeof(NOTE_FREEBSD_VENDOR)]; + uint32_t desc; +} crt_noinit_tag __attribute__ ((section (NOTE_SECTION), + aligned(4))) __used = { + .namesz = sizeof(NOTE_FREEBSD_VENDOR), + .descsz = sizeof(uint32_t), + .type = CRT_NOINIT_NOTETYPE, + .name = NOTE_FREEBSD_VENDOR, + .desc = 0 +}; diff --git a/lib/csu/common/notes.h b/lib/csu/common/notes.h new file mode 100644 index 0000000..72c176c --- /dev/null +++ b/lib/csu/common/notes.h @@ -0,0 +1,39 @@ +/*- + * Copyright 2012 Konstantin Belousov + * 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 ``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: releng/11.1/lib/csu/common/notes.h 255874 2013-09-26 07:53:18Z andrew $ + */ + +#ifndef CSU_COMMON_NOTES_H +#define CSU_COMMON_NOTES_H + +#define NOTE_FREEBSD_VENDOR "FreeBSD" + +#define NOTE_SECTION ".note.tag" + +#define ABI_NOTETYPE 1 +#define CRT_NOINIT_NOTETYPE 2 +#define ARCH_NOTETYPE 3 + +#endif diff --git a/lib/csu/crt1.c b/lib/csu/crt1.c deleted file mode 100644 index c412255..0000000 --- a/lib/csu/crt1.c +++ /dev/null @@ -1,120 +0,0 @@ -/* LINTLIBRARY */ -/*- - * Copyright 1996-1998 John D. Polstra. - * 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 ``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. - */ - -#ifndef lint -#ifndef __GNUC__ -#error "GCC is needed to compile this file" -#endif -#endif /* lint */ - -#include - -#include "libc_private.h" -#include "crtbrand.c" - -extern int _DYNAMIC; -#pragma weak _DYNAMIC - -typedef void (*fptr)(void); - -extern void _fini(void); -extern void _init(void); -extern int main(int, char **, char **); -extern void _start(unsigned int *, ...); - -#ifdef GCRT -extern void _mcleanup(void); -extern void monstartup(void *, void *); -extern int eprol; -extern int etext; -#endif - -char **environ; -const char *__progname = ""; - -static __inline fptr -get_rtld_cleanup(void) -{ - fptr retval; - -#ifdef __GNUC__ - __asm__("movl %%edx,%0" : "=rm"(retval)); -#else - retval = (fptr)0; /* XXXX Fix this for other compilers */ -#endif - return(retval); -} - -/* The entry function. */ -void -_start(unsigned int *ap, ...) -{ - fptr cleanup; - int argc; - char **argv; - char **env; - const char *s; - -#ifdef __GNUC__ - __asm__("and $0xfffffff0,%esp"); -#endif - cleanup = get_rtld_cleanup(); - //argv = ≈ - argc = *ap++;// *(long *)(void *)(argv - 1); - argv = (char **)ap; - env = argv + argc + 1; - environ = env; - - if (argc > 0 && argv[0] != NULL) { - __progname = argv[0]; - for (s = __progname; *s != '\0'; s++) - if (*s == '/') - __progname = s + 1; - } - - if (&_DYNAMIC != NULL) - atexit(cleanup); - else - _init_tls(); - -#ifdef GCRT - atexit(_mcleanup); -#endif - atexit(_fini); -#ifdef GCRT - monstartup(&eprol, &etext); -#endif - _init(); - exit( main(argc, argv, env) ); -} - -#ifdef GCRT -__asm__(".text"); -__asm__("eprol:"); -__asm__(".previous"); -#endif - -__asm__(".ident\t\"$FreeBSD: src/lib/csu/i386-elf/crt1.c,v 1.14 2005/05/19 07:36:07 dfr Exp $\""); diff --git a/lib/csu/crtbrand.c b/lib/csu/crtbrand.c deleted file mode 100644 index f17a450..0000000 --- a/lib/csu/crtbrand.c +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * Copyright 2000 David E. O'Brien, John D. Polstra. - * 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 ``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 - -#include - -#define ABI_VENDOR "UbixOS" -#define ABI_SECTION ".note.ABI-tag" -#define ABI_NOTETYPE 1 - -/* - * Special ".note" entry specifying the ABI version. See - * http://www.netbsd.org/Documentation/kernel/elf-notes.html - * for more information. - */ -static const struct { - int32_t namesz; - int32_t descsz; - int32_t type; - char name[sizeof ABI_VENDOR]; - int32_t desc; -} abitag __attribute__ ((section (ABI_SECTION), aligned(4))) __unused = { - sizeof ABI_VENDOR, - sizeof(int32_t), - ABI_NOTETYPE, - ABI_VENDOR, - __UbixOS_version -}; diff --git a/lib/csu/crti.S b/lib/csu/crti.S deleted file mode 100644 index ff895cb..0000000 --- a/lib/csu/crti.S +++ /dev/null @@ -1,41 +0,0 @@ -/*- - * Copyright 1996, 1997, 1998, 2000 John D. Polstra. - * 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 ``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. - */ - - .section .init,"ax",@progbits - .align 4 - .globl _init - .type _init,@function -_init: - sub $12,%esp /* re-align stack pointer */ - - .section .fini,"ax",@progbits - .align 4 - .globl _fini - .type _fini,@function -_fini: - sub $12,%esp /* re-align stack pointer */ - - .section .rodata -.ascii "$FreeBSD: src/lib/csu/i386-elf/crti.S,v 1.7 2005/05/19 07:31:06 dfr Exp $\0" diff --git a/lib/csu/crtn.S b/lib/csu/crtn.S deleted file mode 100644 index 8e820e3..0000000 --- a/lib/csu/crtn.S +++ /dev/null @@ -1,35 +0,0 @@ -/*- - * Copyright 1996, 1997, 1998, 2000 John D. Polstra. - * 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 ``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. - */ - - .section .init,"ax",@progbits - add $12,%esp - ret - - .section .fini,"ax",@progbits - add $12,%esp - ret - - .section .rodata -.ascii "$FreeBSD: src/lib/csu/i386-elf/crtn.S,v 1.6 2005/05/19 07:31:06 dfr Exp $\0" diff --git a/lib/csu/i386/Makefile b/lib/csu/i386/Makefile new file mode 100644 index 0000000..b9b5313 --- /dev/null +++ b/lib/csu/i386/Makefile @@ -0,0 +1,57 @@ +# $FreeBSD: releng/11.1/lib/csu/i386/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= gcrt1.o crt1.o Scrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} crt1_c.o crt1_s.o gcrt1_c.o Scrt1_c.o +CLEANFILES+= crt1_c.s gcrt1_c.s Scrt1_c.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1_c.c is not +# directly compiled to .o files. + +gcrt1_c.s: crt1_c.c + ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1_c.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1_c.o: gcrt1_c.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1_c.s + +gcrt1.o: gcrt1_c.o crt1_s.o + ${LD} ${_LDFLAGS} -o gcrt1.o -r crt1_s.o gcrt1_c.o + +crt1_c.s: crt1_c.c + ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1_c.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1_c.o: crt1_c.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1_c.s + +crt1.o: crt1_c.o crt1_s.o + ${LD} ${_LDFLAGS} -o crt1.o -r crt1_s.o crt1_c.o + ${OBJCOPY} --localize-symbol _start1 crt1.o + +Scrt1_c.s: crt1_c.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1_c.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1_c.o: Scrt1_c.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1_c.s + +Scrt1.o: Scrt1_c.o crt1_s.o + ${LD} ${_LDFLAGS} -o Scrt1.o -r crt1_s.o Scrt1_c.o + ${OBJCOPY} --localize-symbol _start1 Scrt1.o + +.include diff --git a/lib/csu/i386/Makefile.depend b/lib/csu/i386/Makefile.depend new file mode 100644 index 0000000..62680bb --- /dev/null +++ b/lib/csu/i386/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/lib/csu/i386/Makefile.depend 288226 2015-09-25 19:26:08Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/csu/i386/crt1_c.c b/lib/csu/i386/crt1_c.c new file mode 100644 index 0000000..73dd67f --- /dev/null +++ b/lib/csu/i386/crt1_c.c @@ -0,0 +1,74 @@ +/* LINTLIBRARY */ +/*- + * Copyright 1996-1998 John D. Polstra. + * 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 ``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: releng/11.1/lib/csu/i386/crt1_c.c 292000 2015-12-08 19:32:58Z emaste $ + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/i386/crt1_c.c 292000 2015-12-08 19:32:58Z emaste $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +typedef void (*fptr)(void); + +extern void _start(char *, ...); + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +void _start1(fptr, int, char *[]) __dead2; + +/* The entry function, C part. */ +void +_start1(fptr cleanup, int argc, char *argv[]) +{ + char **env; + + env = argv + argc + 1; + handle_argv(argc, argv, env); + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); + +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +__asm__("eprol:"); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} + +__asm(".hidden _start1"); diff --git a/lib/csu/i386/crt1_s.S b/lib/csu/i386/crt1_s.S new file mode 100644 index 0000000..c680e49 --- /dev/null +++ b/lib/csu/i386/crt1_s.S @@ -0,0 +1,54 @@ +/*- + * Copyright 2009 Konstantin Belousov. + * 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 ``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: releng/11.1/lib/csu/i386/crt1_s.S 217383 2011-01-13 23:00:22Z kib $ + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/i386/crt1_s.S 217383 2011-01-13 23:00:22Z kib $"); + + .text + .align 4 + .globl _start + .type _start, @function +_start: + .cfi_startproc + xorl %ebp,%ebp + pushl %ebp + .cfi_def_cfa_offset 4 + movl %esp,%ebp + .cfi_offset %ebp,-8 + .cfi_def_cfa_register %ebp + andl $0xfffffff0,%esp # align stack + leal 8(%ebp),%eax + subl $4,%esp + pushl %eax # argv + pushl 4(%ebp) # argc + pushl %edx # rtld cleanup + call _start1 + int3 + .cfi_endproc + .size _start, . - _start + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/i386/crti.S b/lib/csu/i386/crti.S new file mode 100644 index 0000000..7d29c75 --- /dev/null +++ b/lib/csu/i386/crti.S @@ -0,0 +1,43 @@ +/*- + * Copyright 1996, 1997, 1998, 2000 John D. Polstra. + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/i386/crti.S 217105 2011-01-07 16:07:51Z kib $"); + + .section .init,"ax",@progbits + .align 4 + .globl _init + .type _init,@function +_init: + sub $12,%esp /* re-align stack pointer */ + + .section .fini,"ax",@progbits + .align 4 + .globl _fini + .type _fini,@function +_fini: + sub $12,%esp /* re-align stack pointer */ + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/i386/crtn.S b/lib/csu/i386/crtn.S new file mode 100644 index 0000000..b9c7d2d --- /dev/null +++ b/lib/csu/i386/crtn.S @@ -0,0 +1,37 @@ +/*- + * Copyright 1996, 1997, 1998, 2000 John D. Polstra. + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/i386/crtn.S 217105 2011-01-07 16:07:51Z kib $"); + + .section .init,"ax",@progbits + add $12,%esp + ret + + .section .fini,"ax",@progbits + add $12,%esp + ret + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/mips/Makefile b/lib/csu/mips/Makefile new file mode 100644 index 0000000..18d1e51 --- /dev/null +++ b/lib/csu/mips/Makefile @@ -0,0 +1,46 @@ +# $FreeBSD: releng/11.1/lib/csu/mips/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} +CLEANFILES+= crt1.s gcrt1.s Scrt1.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1.c is not +# directly compiled to .o files. + +crt1.s: crt1.c + ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1.o: crt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1.s + +gcrt1.s: crt1.c + ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1.o: gcrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1.s + +Scrt1.s: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1.o: Scrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1.s + +.include diff --git a/lib/csu/mips/Makefile.depend b/lib/csu/mips/Makefile.depend new file mode 100644 index 0000000..9bf2062 --- /dev/null +++ b/lib/csu/mips/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/lib/csu/mips/Makefile.depend 288228 2015-09-25 19:44:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/csu/mips/crt1.c b/lib/csu/mips/crt1.c new file mode 100644 index 0000000..330b7e0 --- /dev/null +++ b/lib/csu/mips/crt1.c @@ -0,0 +1,90 @@ +/*- + * Copyright 1996-1998 John D. Polstra. + * All rights reserved. + * Copyright (c) 1995 Christopher G. Demetriou + * 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 Christopher G. Demetriou + * for the NetBSD Project. + * 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: releng/11.1/lib/csu/mips/crt1.c 292000 2015-12-08 19:32:58Z emaste $ + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/mips/crt1.c 292000 2015-12-08 19:32:58Z emaste $"); + +#include +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +struct Struct_Obj_Entry; +struct ps_strings; + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +void __start(char **, void (*)(void), struct Struct_Obj_Entry *, struct ps_strings *); + +/* The entry function. */ +void +__start(char **ap, + void (*cleanup)(void), /* from shared loader */ + struct Struct_Obj_Entry *obj __unused, /* from shared loader */ + struct ps_strings *ps_strings __unused) +{ + int argc; + char **argv; + char **env; + + argc = * (long *) ap; + argv = ap + 1; + env = ap + 2 + argc; + handle_argv(argc, argv, env); + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); + +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} + +#ifdef GCRT +__asm__(".text"); +__asm__("eprol:"); +__asm__(".previous"); +#endif diff --git a/lib/csu/mips/crti.S b/lib/csu/mips/crti.S new file mode 100644 index 0000000..8df4311 --- /dev/null +++ b/lib/csu/mips/crti.S @@ -0,0 +1,50 @@ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/mips/crti.S 205112 2010-03-13 02:10:20Z jmallett $"); + + .section .init,"ax",%progbits + .align 4 + .globl _init + .type _init,%function +_init: + .set noreorder +#if defined(__ABICALLS__) && (defined(__mips_o32) || defined(__mips_o64)) + SETUP_GP +#endif + PTR_ADDU sp, sp, -CALLFRAME_SIZ + REG_S ra, CALLFRAME_RA(sp) +#if defined(__ABICALLS__) +#if defined(__mips_o32) || defined(__mips_o64) + SAVE_GP(CALLFRAME_GP) +#else + SETUP_GP64(CALLFRAME_GP, _init) +#endif +#else /* __ABICALLS__ */ +#if defined(__mips_n32) || defined(__mips_n64) + REG_S gp, CALLFRAME_GP(sp) +#endif +#endif + .set reorder + + .section .fini,"ax",%progbits + .align 4 + .globl _fini + .type _fini,%function +_fini: + .set noreorder +#if defined(__ABICALLS__) && (defined(__mips_o32) || defined(__mips_o64)) + SETUP_GP +#endif + PTR_ADDU sp, sp, -CALLFRAME_SIZ + REG_S ra, CALLFRAME_RA(sp) +#if defined(__ABICALLS__) +#if defined(__mips_o32) || defined(__mips_o64) + SAVE_GP(CALLFRAME_GP) +#else + SETUP_GP64(CALLFRAME_GP, _fini) +#endif +#else /* __ABICALLS__ */ +#if defined(__mips_n32) || defined(__mips_n64) + REG_S gp, CALLFRAME_GP(sp) +#endif +#endif + .set reorder diff --git a/lib/csu/mips/crtn.S b/lib/csu/mips/crtn.S new file mode 100644 index 0000000..e058862 --- /dev/null +++ b/lib/csu/mips/crtn.S @@ -0,0 +1,24 @@ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/mips/crtn.S 204674 2010-03-03 21:59:23Z imp $"); + + .section .init,"ax",%progbits + .align 4 + .set noreorder +#if defined(__ABICALLS__) && (defined(__mips_n32) || defined(__mips_n64)) + REG_L gp, CALLFRAME_GP(sp) +#endif + REG_L ra, CALLFRAME_RA(sp) + jr ra + PTR_ADDU sp, sp, CALLFRAME_SIZ + .set reorder + + .section .fini,"ax",%progbits + .align 4 + .set noreorder +#if defined(__ABICALLS__) && (defined(__mips_n32) || defined(__mips_n64)) + REG_L gp, CALLFRAME_GP(sp) +#endif + REG_L ra, CALLFRAME_RA(sp) + jr ra + PTR_ADDU sp, sp, CALLFRAME_SIZ + .set reorder diff --git a/lib/csu/powerpc/Makefile b/lib/csu/powerpc/Makefile new file mode 100644 index 0000000..07a8d24 --- /dev/null +++ b/lib/csu/powerpc/Makefile @@ -0,0 +1,46 @@ +# $FreeBSD: releng/11.1/lib/csu/powerpc/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} +CLEANFILES+= crt1.s gcrt1.s Scrt1.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1.c is not +# directly compiled to .o files. + +crt1.s: crt1.c + ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1.o: crt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1.s + +gcrt1.s: crt1.c + ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1.o: gcrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1.s + +Scrt1.s: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1.o: Scrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1.s + +.include diff --git a/lib/csu/powerpc/Makefile.depend b/lib/csu/powerpc/Makefile.depend new file mode 100644 index 0000000..c73e7e9 --- /dev/null +++ b/lib/csu/powerpc/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/lib/csu/powerpc/Makefile.depend 288228 2015-09-25 19:44:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/csu/powerpc/crt1.c b/lib/csu/powerpc/crt1.c new file mode 100644 index 0000000..4cfb11d --- /dev/null +++ b/lib/csu/powerpc/crt1.c @@ -0,0 +1,102 @@ +/* LINTLIBRARY */ +/*- + * Copyright 2001 David E. O'Brien. + * All rights reserved. + * Copyright 1996-1998 John D. Polstra. + * All rights reserved. + * Copyright (c) 1997 Jason R. Thorpe. + * Copyright (c) 1995 Christopher G. Demetriou + * 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 for the + * FreeBSD Project. See http://www.freebsd.org/ for + * information about FreeBSD. + * This product includes software developed for the + * NetBSD Project. See http://www.netbsd.org/ for + * information about NetBSD. + * 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/powerpc/crt1.c 292000 2015-12-08 19:32:58Z emaste $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +struct Struct_Obj_Entry; +struct ps_strings; + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +struct ps_strings *__ps_strings; + +void _start(int, char **, char **, const struct Struct_Obj_Entry *, + void (*)(void), struct ps_strings *); + +/* The entry function. */ +/* + * First 5 arguments are specified by the PowerPC SVR4 ABI. + * The last argument, ps_strings, is a BSD extension. + */ +/* ARGSUSED */ +void +_start(int argc, char **argv, char **env, + const struct Struct_Obj_Entry *obj __unused, void (*cleanup)(void), + struct ps_strings *ps_strings) +{ + + + handle_argv(argc, argv, env); + + if (ps_strings != (struct ps_strings *)0) + __ps_strings = ps_strings; + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); + +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} + +#ifdef GCRT +__asm__(".text"); +__asm__("eprol:"); +__asm__(".previous"); +#endif diff --git a/lib/csu/powerpc/crti.S b/lib/csu/powerpc/crti.S new file mode 100644 index 0000000..034e1fb --- /dev/null +++ b/lib/csu/powerpc/crti.S @@ -0,0 +1,51 @@ +/*- + * Copyright 2001 David E. O'Brien + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/powerpc/crti.S 217399 2011-01-14 11:34:58Z kib $"); + + .section .init,"ax",@progbits + .align 2 + .globl _init + .type _init,@function +_init: + stwu 1,-16(1) + mflr 0 + stw 31,12(1) + stw 0,20(1) + mr 31,1 + + + .section .fini,"ax",@progbits + .align 2 + .globl _fini +_fini: + stwu 1,-16(1) + mflr 0 + stw 31,12(1) + stw 0,20(1) + mr 31,1 + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/powerpc/crtn.S b/lib/csu/powerpc/crtn.S new file mode 100644 index 0000000..3478ec5 --- /dev/null +++ b/lib/csu/powerpc/crtn.S @@ -0,0 +1,46 @@ +/*- + * Copyright 2001 David E. O'Brien + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/powerpc/crtn.S 217399 2011-01-14 11:34:58Z kib $"); + + .section .init,"ax",@progbits + lwz 11,0(1) + lwz 0,4(11) + mtlr 0 + lwz 31,-4(11) + mr 1,11 + blr + + + .section .fini,"ax",@progbits + lwz 11,0(1) + lwz 0,4(11) + mtlr 0 + lwz 31,-4(11) + mr 1,11 + blr + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/powerpc64/Makefile b/lib/csu/powerpc64/Makefile new file mode 100644 index 0000000..e272396 --- /dev/null +++ b/lib/csu/powerpc64/Makefile @@ -0,0 +1,47 @@ +# $FreeBSD: releng/11.1/lib/csu/powerpc64/Makefile 316369 2017-04-01 19:27:06Z jhibbits $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include \ + -mlongcall + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} +CLEANFILES+= crt1.s gcrt1.s Scrt1.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1.c is not +# directly compiled to .o files. + +crt1.s: crt1.c + ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1.o: crt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1.s + +gcrt1.s: crt1.c + ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1.o: gcrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1.s + +Scrt1.s: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1.o: Scrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1.s + +.include diff --git a/lib/csu/powerpc64/Makefile.depend b/lib/csu/powerpc64/Makefile.depend new file mode 100644 index 0000000..e2dde8d --- /dev/null +++ b/lib/csu/powerpc64/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/lib/csu/powerpc64/Makefile.depend 288228 2015-09-25 19:44:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/csu/powerpc64/crt1.c b/lib/csu/powerpc64/crt1.c new file mode 100644 index 0000000..62ff083 --- /dev/null +++ b/lib/csu/powerpc64/crt1.c @@ -0,0 +1,101 @@ +/* LINTLIBRARY */ +/*- + * Copyright 2001 David E. O'Brien. + * All rights reserved. + * Copyright 1996-1998 John D. Polstra. + * All rights reserved. + * Copyright (c) 1997 Jason R. Thorpe. + * Copyright (c) 1995 Christopher G. Demetriou + * 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 for the + * FreeBSD Project. See http://www.freebsd.org/ for + * information about FreeBSD. + * This product includes software developed for the + * NetBSD Project. See http://www.netbsd.org/ for + * information about NetBSD. + * 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/lib/csu/powerpc64/crt1.c 292000 2015-12-08 19:32:58Z emaste $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +struct Struct_Obj_Entry; +struct ps_strings; + +extern void _start(int, char **, char **, const struct Struct_Obj_Entry *, + void (*)(void), struct ps_strings *); + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +struct ps_strings *__ps_strings; + +/* The entry function. */ +/* + * First 5 arguments are specified by the PowerPC SVR4 ABI. + * The last argument, ps_strings, is a BSD extension. + */ +/* ARGSUSED */ +void +_start(int argc, char **argv, char **env, + const struct Struct_Obj_Entry *obj __unused, void (*cleanup)(void), + struct ps_strings *ps_strings) +{ + + handle_argv(argc, argv, env); + + if (ps_strings != (struct ps_strings *)0) + __ps_strings = ps_strings; + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); + +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} + +#ifdef GCRT +__asm__(".text"); +__asm__("eprol:"); +__asm__(".previous"); +#endif diff --git a/lib/csu/powerpc64/crti.S b/lib/csu/powerpc64/crti.S new file mode 100644 index 0000000..dc52ca7 --- /dev/null +++ b/lib/csu/powerpc64/crti.S @@ -0,0 +1,86 @@ +/*- + * Copyright 2001 David E. O'Brien + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/powerpc64/crti.S 291668 2015-12-03 00:10:57Z nwhitehorn $"); + +#ifdef _CALL_ELF +.abiversion _CALL_ELF +#endif + + .section .init,"ax",@progbits + .p2align 2 + .globl _init +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + .section ".opd","aw" + .p2align 3 +_init: + .quad .L._init,.TOC.@tocbase,0 + .previous + .type _init,@function + + .p2align 4 +.L._init: +#else + .p2align 4 + .globl _init + .type _init,@function +_init: + addis %r2, %r12, (.TOC.-_init)@ha + addi %r2, %r2, (.TOC.-_init)@l + .localentry _init, .-_init +#endif + stdu 1,-48(1) + mflr 0 + std 0,64(1) + +/* Fini */ + .section .fini,"ax",@progbits + .p2align 2 + .globl _fini +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + .section ".opd","aw" + .p2align 3 +_fini: + .quad .L._fini,.TOC.@tocbase,0 + .previous + .type _fini,@function + + .p2align 4 +.L._fini: +#else + .p2align 4 + .globl _fini + .type _fini,@function +_fini: + addis %r2, %r12, (.TOC.-_fini)@ha + addi %r2, %r2, (.TOC.-_fini)@l + .localentry _fini, .-_fini +#endif + stdu 1,-48(1) + mflr 0 + std 0,64(1) + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/powerpc64/crtn.S b/lib/csu/powerpc64/crtn.S new file mode 100644 index 0000000..168d9e7 --- /dev/null +++ b/lib/csu/powerpc64/crtn.S @@ -0,0 +1,42 @@ +/*- + * Copyright 2001 David E. O'Brien + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/powerpc64/crtn.S 217399 2011-01-14 11:34:58Z kib $"); + + .section .init,"ax",@progbits + ld %r1,0(%r1) + ld 0,16(%r1) + mtlr 0 + blr + + + .section .fini,"ax",@progbits + ld %r1,0(%r1) + ld 0,16(%r1) + mtlr 0 + blr + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/riscv/Makefile b/lib/csu/riscv/Makefile new file mode 100644 index 0000000..dd66619 --- /dev/null +++ b/lib/csu/riscv/Makefile @@ -0,0 +1,46 @@ +# $FreeBSD: releng/11.1/lib/csu/riscv/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} +CLEANFILES+= crt1.s gcrt1.s Scrt1.s + +# See the comment in lib/csu/common/crtbrand.c for the reason crt1.c is not +# directly compiled to .o files. + +crt1.s: crt1.c + ${CC} ${CFLAGS} -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +crt1.o: crt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} crt1.s + +gcrt1.s: crt1.c + ${CC} ${CFLAGS} -DGCRT -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +gcrt1.o: gcrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} gcrt1.s + +Scrt1.s: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -S -o ${.TARGET} ${.CURDIR}/crt1.c + sed ${SED_FIX_NOTE} ${.TARGET} + +Scrt1.o: Scrt1.s + ${CC} ${ACFLAGS} -c -o ${.TARGET} Scrt1.s + +.include diff --git a/lib/csu/riscv/crt1.c b/lib/csu/riscv/crt1.c new file mode 100644 index 0000000..88b5565 --- /dev/null +++ b/lib/csu/riscv/crt1.c @@ -0,0 +1,89 @@ +/* LINTLIBRARY */ +/*- + * Copyright 1996-1998 John D. Polstra. + * Copyright (c) 2015 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/riscv/crt1.c 292124 2015-12-11 22:55:23Z br $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +typedef void (*fptr)(void); + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +void __start(int argc, char **argv, char **env, void (*cleanup)(void)); + +/* The entry function. */ +__asm(" .text \n" +" .align 0 \n" +" .globl _start \n" +" _start: \n" +" mv a3, a2 \n" /* cleanup */ +" addi a1, a0, 8 \n" /* get argv */ +" ld a0, 0(a0) \n" /* load argc */ +" slli t0, a0, 3 \n" /* mult by arg size */ +" add a2, a1, t0 \n" /* env is after argv */ +" addi a2, a2, 8 \n" /* argv is null terminated */ +" lla gp, _gp \n" /* load global pointer */ +" call __start"); + +void +__start(int argc, char **argv, char **env, void (*cleanup)(void)) +{ + + handle_argv(argc, argv, env); + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else + _init_tls(); + +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +__asm__("eprol:"); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} diff --git a/lib/csu/riscv/crti.S b/lib/csu/riscv/crti.S new file mode 100644 index 0000000..216de98 --- /dev/null +++ b/lib/csu/riscv/crti.S @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 2015 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * 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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/riscv/crti.S 292124 2015-12-11 22:55:23Z br $"); + +# this puts _gp into .dynsym, so symlook_obj can now find that (see reloc.c) + .weak _gp +_gp: + + .section .init,"ax",@progbits + .align 2 + .globl _init + .type _init,@function +_init: + addi sp, sp, -16 + sd ra, 0(sp) + + .section .fini,"ax",@progbits + .align 2 + .globl _fini + .type _fini,@function +_fini: + addi sp, sp, -16 + sd ra, 0(sp) + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/riscv/crtn.S b/lib/csu/riscv/crtn.S new file mode 100644 index 0000000..f6105c3 --- /dev/null +++ b/lib/csu/riscv/crtn.S @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2015 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * 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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/riscv/crtn.S 292124 2015-12-11 22:55:23Z br $"); + + .section .init,"ax",@progbits + ld ra, 0(sp) + addi sp, sp, 16 + ret + + .section .fini,"ax",@progbits + ld ra, 0(sp) + addi sp, sp, 16 + ret + + .section .note.GNU-stack,"",%progbits diff --git a/lib/csu/sparc64/Makefile b/lib/csu/sparc64/Makefile new file mode 100644 index 0000000..b10b47b --- /dev/null +++ b/lib/csu/sparc64/Makefile @@ -0,0 +1,27 @@ +# $FreeBSD: releng/11.1/lib/csu/sparc64/Makefile 313538 2017-02-10 07:32:40Z ngie $ + +.PATH: ${.CURDIR:H}/common + +SRCS= crt1.c crti.S crtn.S +OBJS= ${SRCS:N*.h:R:S/$/.o/g} +OBJS+= Scrt1.o gcrt1.o +CFLAGS+= -I${.CURDIR:H}/common \ + -I${SRCTOP}/lib/libc/include + +FILES= ${OBJS} +FILESMODE= ${LIBMODE} +FILESOWN= ${LIBOWN} +FILESGRP= ${LIBGRP} +FILESDIR= ${LIBDIR} +# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY. +.undef LIBRARIES_ONLY + +CLEANFILES= ${OBJS} + +gcrt1.o: crt1.c + ${CC} ${CFLAGS} -DGCRT -c -o gcrt1.o ${.ALLSRC} + +Scrt1.o: crt1.c + ${CC} ${CFLAGS} -fPIC -DPIC -c -o Scrt1.o ${.ALLSRC} + +.include diff --git a/lib/csu/sparc64/Makefile.depend b/lib/csu/sparc64/Makefile.depend new file mode 100644 index 0000000..cb18ea7 --- /dev/null +++ b/lib/csu/sparc64/Makefile.depend @@ -0,0 +1,12 @@ +# $FreeBSD: releng/11.1/lib/csu/sparc64/Makefile.depend 288228 2015-09-25 19:44:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/csu/sparc64/crt1.c b/lib/csu/sparc64/crt1.c new file mode 100644 index 0000000..d82a246 --- /dev/null +++ b/lib/csu/sparc64/crt1.c @@ -0,0 +1,103 @@ +/* LINTLIBRARY */ +/*- + * Copyright 2001 David E. O'Brien. + * All rights reserved. + * Copyright (c) 1995, 1998 Berkeley Software Design, Inc. + * All rights reserved. + * Copyright 1996-1998 John D. Polstra. + * 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 authors 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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/sparc64/crt1.c 292000 2015-12-08 19:32:58Z emaste $"); + +#include + +#include "libc_private.h" +#include "crtbrand.c" +#include "ignore_init.c" + +struct Struct_Obj_Entry; +struct ps_strings; + +extern void __sparc_utrap_setup(void); + +#ifdef GCRT +extern void _mcleanup(void); +extern void monstartup(void *, void *); +extern int eprol; +extern int etext; +#endif + +void _start(char **, void (*)(void), struct Struct_Obj_Entry *, + struct ps_strings *); + +/* The entry function. */ +/* + * %o0 holds ps_strings pointer. + * + * Note: kernel may (is not set in stone yet) pass ELF aux vector in %o1, + * but for now we do not use it here. + * + * The SPARC compliance definitions specifies that the kernel pass the + * address of a function to be executed on exit in %g1. We do not make + * use of it as it is quite broken, because gcc can use this register + * as a temporary, so it is not safe from C code. Its even more broken + * for dynamic executables since rtld runs first. + */ +/* ARGSUSED */ +void +_start(char **ap, void (*cleanup)(void), struct Struct_Obj_Entry *obj __unused, + struct ps_strings *ps_strings __unused) +{ + int argc; + char **argv; + char **env; + + argc = *(long *)(void *)ap; + argv = ap + 1; + env = ap + 2 + argc; + handle_argv(argc, argv, env); + + if (&_DYNAMIC != NULL) + atexit(cleanup); + else { + __sparc_utrap_setup(); + _init_tls(); + } +#ifdef GCRT + atexit(_mcleanup); + monstartup(&eprol, &etext); +#endif + + handle_static_init(argc, argv, env); + exit(main(argc, argv, env)); +} + +#ifdef GCRT +__asm__(".text"); +__asm__("eprol:"); +__asm__(".previous"); +#endif diff --git a/lib/csu/sparc64/crti.S b/lib/csu/sparc64/crti.S new file mode 100644 index 0000000..5a3b6d6 --- /dev/null +++ b/lib/csu/sparc64/crti.S @@ -0,0 +1,57 @@ +/*- + * Copyright 2001 David E. O'Brien + * 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 author nor the names of additional 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/sparc64/crti.S 216338 2010-12-09 21:31:21Z dim $"); + + .file "crti.S" + + /* The minimum stack frame size (bytes) is: + * 16 extended words for saving the current register window, + * 1 extended word for "hidden parameter", + * 6 extended words in wihch a callee can store its arguments + * ("The SPARC Architecure Manual" by Weaver & Germond) + * This gives 184 bytes. However we must round up to an extended + * word boundary, thus 192 bytes. + * (if we weren't v9, it would be 96 bytes rather than 192) + */ + + .section .init,"ax",@progbits + .align 4 + .globl _init + .type _init,#function +_init: + save %sp,-192,%sp + + + .section .fini,"ax",@progbits + .globl _fini + .type _fini,#function + .align 4 +_fini: + save %sp,-192,%sp diff --git a/lib/csu/sparc64/crtn.S b/lib/csu/sparc64/crtn.S new file mode 100644 index 0000000..6be6a82 --- /dev/null +++ b/lib/csu/sparc64/crtn.S @@ -0,0 +1,42 @@ +/*- + * Copyright 2001 David E. O'Brien + * 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 author nor the names of additional 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 ``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 +__FBSDID("$FreeBSD: releng/11.1/lib/csu/sparc64/crtn.S 216338 2010-12-09 21:31:21Z dim $"); + + .file "crtn.S" + + .section .init,"ax",@progbits + .align 4 + ret + restore + + .section .fini,"ax",@progbits + .align 4 + ret + restore diff --git a/lib/libedit/Makefile b/lib/libedit/Makefile index 2fb3355..228cc3d 100644 --- a/lib/libedit/Makefile +++ b/lib/libedit/Makefile @@ -1,41 +1,86 @@ -# $Id: Makefile 89 2016-01-12 00:20:40Z reddawg $ -# The System Makefile (C) 2002 The UbixOS Project +# $NetBSD: Makefile,v 1.56 2016/03/02 19:24:20 christos Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $FreeBSD: releng/11.1/lib/libedit/Makefile 313981 2017-02-20 03:33:59Z pfg $ -# Include Global 'Source' Options -Makefile.incl -Makefile.incl +PACKAGE=clibs +LIB= edit +SHLIB_MAJOR= 7 +SHLIBDIR?= /lib -INCLUDES = -I. -I../../include +OSRCS= chared.c common.c el.c eln.c emacs.c fcns.c filecomplete.c help.c \ + hist.c keymacro.c map.c chartype.c \ + parse.c prompt.c read.c refresh.c search.c sig.c terminal.c tty.c vi.c -#Objects -OBJS = read.o el.o +LIBADD= ncursesw -#Sub Sections -SUBS = +MAN= editline.3 editrc.5 -#Output -OUTPUT = libedit.so +MLINKS= editline.3 el_deletestr.3 editline.3 el_end.3 editline.3 el_init.3 \ + editline.3 el_get.3 editline.3 el_getc.3 editline.3 el_gets.3 \ + editline.3 history.3 editline.3 history_end.3 \ + editline.3 history_init.3 editline.3 el_insertstr.3 \ + editline.3 el_line.3 editline.3 el_parse.3 editline.3 el_push.3 \ + editline.3 el_reset.3 editline.3 el_resize.3 editline.3 el_set.3 \ + editline.3 el_source.3 \ + editline.3 tok_init.3 editline.3 tok_end.3 editline.3 tok_reset.3 \ + editline.3 tok_line.3 editline.3 tok_str.3 -lib.so: $(OBJS) - $(CC) -nostdlib -shared -Wl,-soname,libedit.so -o $(OUTPUT) $(OBJS) $(SUBS) +# For speed and debugging +#SRCS= ${OSRCS} +# For protection +SRCS= editline.c +SRCS+= tokenizer.c history.c readline.c +SRCS+= common.h emacs.h fcns.h help.h vi.h +CLEANFILES+= common.h editline.c emacs.h fcns.c fcns.h help.c help.h vi.h -# Compile the source files -.cc.o: - $(CXX) -Wall -nostdinc -O -I./include -c -o $@ $< - -.cc.s: - $(CXX) -Wall -nostdinc -O -I./include -S -o $@ $< - -.c.o: - $(CC) -Wall -nostdinc -O $(INCLUDES) -c $< +INCS= histedit.h -.c.s: - $(CC) -Wall -nostdinc -O -I./include -S -o $@ $< - -.S.o: - $(CC) -Wall -nostdinc -c -o $@ $< - -# Clean up the junk -clean: - $(REMOVE) $(OBJS) $(OUTPUT) - (cd test;make clean) +SRCS+= tokenizern.c historyn.c +CLEANFILES+= tokenizern.c historyn.c +CFLAGS+= -I. -I${.CURDIR} -I${.CURDIR}/edit -DWIDECHAR +#CFLAGS+= -DDEBUG_TTY -DDEBUG_KEY -DDEBUG_READ -DDEBUG -DDEBUG_REFRESH +#CFLAGS+= -DDEBUG_PASTE -DDEBUG_EDIT + +WARNS?= 1 + +SUBDIR= edit/readline + +AHDR= vi.h emacs.h common.h +ASRC= ${.CURDIR}/vi.c ${.CURDIR}/emacs.c ${.CURDIR}/common.c + +.for hdr in vi emacs common +${hdr}.h: ${hdr}.c makelist + sh ${.CURDIR}/makelist -h ${.CURDIR}/${hdr}.c > ${.TARGET} +.endfor + +fcns.h: ${AHDR} makelist + sh ${.CURDIR}/makelist -fh ${AHDR} > ${.TARGET} + +fcns.c: ${AHDR} fcns.h makelist + sh ${.CURDIR}/makelist -fc ${AHDR} > ${.TARGET} + +help.c: ${ASRC} makelist + sh ${.CURDIR}/makelist -bc ${ASRC} > ${.TARGET} + +help.h: ${ASRC} makelist + sh ${.CURDIR}/makelist -bh ${ASRC} > ${.TARGET} + +editline.c: ${OSRCS} + sh ${.CURDIR}/makelist -e ${.ALLSRC:T} > ${.TARGET} + +tokenizern.c: makelist Makefile + sh ${.CURDIR}/makelist -n tokenizer.c > ${.TARGET} + +historyn.c: makelist Makefile + sh ${.CURDIR}/makelist -n history.c > ${.TARGET} + +# minimal dependency to make "make depend" optional +editline.o editline.po editline.pico editline.ln: \ + common.h emacs.h fcns.c fcns.h help.c help.h vi.h + +tc1.o: ${.CURDIR}/TEST/tc1.c + +test: tc1.o libedit.a ${DPADD} ${LIBTERMCAP} + ${CC} ${CFLAGS} ${.ALLSRC} -o ${.TARGET} libedit.a ${LDADD} + +.include diff --git a/lib/libedit/Makefile.depend b/lib/libedit/Makefile.depend new file mode 100644 index 0000000..616d978 --- /dev/null +++ b/lib/libedit/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD: releng/11.1/lib/libedit/Makefile.depend 295989 2016-02-24 17:20:11Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/ncurses/ncursesw \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/libedit/TEST/rl1.c b/lib/libedit/TEST/rl1.c new file mode 100644 index 0000000..6f50b34 --- /dev/null +++ b/lib/libedit/TEST/rl1.c @@ -0,0 +1,53 @@ +/* $NetBSD: rl1.c,v 1.2 2016/02/29 00:54:19 christos Exp $ */ + +/*- + * Copyright (c) 2010 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 +#if !defined(lint) +__RCSID("$NetBSD: rl1.c,v 1.2 2016/02/29 00:54:19 christos Exp $"); +#endif /* not lint */ +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/TEST/rl1.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * test.c: A little test program + */ +#include +#include + +int +main(int argc, char *argv[]) +{ + char *p; + while ((p = readline("hi$")) != NULL) { + add_history(p); + printf("%d %s\n", history_length, p); + } + return 0; +} diff --git a/lib/libedit/TEST/tc1.c b/lib/libedit/TEST/tc1.c new file mode 100644 index 0000000..8605b72 --- /dev/null +++ b/lib/libedit/TEST/tc1.c @@ -0,0 +1,305 @@ +/* $NetBSD: tc1.c,v 1.7 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)test.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: tc1.c,v 1.7 2016/02/17 19:47:49 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/TEST/tc1.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * test.c: A little test program + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "histedit.h" + +static int continuation = 0; +volatile sig_atomic_t gotsig = 0; + +static unsigned char complete(EditLine *, int); + int main(int, char **); +static char *prompt(EditLine *); +static void sig(int); + +static char * +prompt(EditLine *el) +{ + static char a[] = "\1\033[7m\1Edit$\1\033[0m\1 "; + static char b[] = "Edit> "; + + return (continuation ? b : a); +} + +static void +sig(int i) +{ + gotsig = i; +} + +static unsigned char +complete(EditLine *el, int ch) +{ + DIR *dd = opendir("."); + struct dirent *dp; + const char* ptr; + const LineInfo *lf = el_line(el); + int len; + int res = CC_ERROR; + + /* + * Find the last word + */ + for (ptr = lf->cursor - 1; + !isspace((unsigned char)*ptr) && ptr > lf->buffer; ptr--) + continue; + len = lf->cursor - ++ptr; + + for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { + if (len > strlen(dp->d_name)) + continue; + if (strncmp(dp->d_name, ptr, len) == 0) { + if (el_insertstr(el, &dp->d_name[len]) == -1) + res = CC_ERROR; + else + res = CC_REFRESH; + break; + } + } + + closedir(dd); + return res; +} + +int +main(int argc, char *argv[]) +{ + EditLine *el = NULL; + int num; + const char *buf; + Tokenizer *tok; +#if 0 + int lastevent = 0; +#endif + int ncontinuation; + History *hist; + HistEvent ev; + + (void) setlocale(LC_CTYPE, ""); + (void) signal(SIGINT, sig); + (void) signal(SIGQUIT, sig); + (void) signal(SIGHUP, sig); + (void) signal(SIGTERM, sig); + + hist = history_init(); /* Init the builtin history */ + /* Remember 100 events */ + history(hist, &ev, H_SETSIZE, 100); + + tok = tok_init(NULL); /* Initialize the tokenizer */ + + /* Initialize editline */ + el = el_init(*argv, stdin, stdout, stderr); + + el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */ + el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */ + el_set(el, EL_PROMPT_ESC, prompt, '\1');/* Set the prompt function */ + + /* Tell editline to use this history interface */ + el_set(el, EL_HIST, history, hist); + + /* Add a user-defined function */ + el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete); + + /* Bind tab to it */ + el_set(el, EL_BIND, "^I", "ed-complete", NULL); + + /* + * Bind j, k in vi command mode to previous and next line, instead + * of previous and next history. + */ + el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL); + el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL); + + /* + * Source the user's defaults file. + */ + el_source(el, NULL); + + while ((buf = el_gets(el, &num)) != NULL && num != 0) { + int ac, cc, co; +#ifdef DEBUG + int i; +#endif + const char **av; + const LineInfo *li; + li = el_line(el); +#ifdef DEBUG + (void) fprintf(stderr, "==> got %d %s", num, buf); + (void) fprintf(stderr, " > li `%.*s_%.*s'\n", + (li->cursor - li->buffer), li->buffer, + (li->lastchar - 1 - li->cursor), + (li->cursor >= li->lastchar) ? "" : li->cursor); + +#endif + if (gotsig) { + (void) fprintf(stderr, "Got signal %d.\n", (int)gotsig); + gotsig = 0; + el_reset(el); + } + + if (!continuation && num == 1) + continue; + + ac = cc = co = 0; + ncontinuation = tok_line(tok, li, &ac, &av, &cc, &co); + if (ncontinuation < 0) { + (void) fprintf(stderr, "Internal error\n"); + continuation = 0; + continue; + } +#ifdef DEBUG + (void) fprintf(stderr, " > nc %d ac %d cc %d co %d\n", + ncontinuation, ac, cc, co); +#endif +#if 0 + if (continuation) { + /* + * Append to the right event in case the user + * moved around in history. + */ + if (history(hist, &ev, H_SET, lastevent) == -1) + err(1, "%d: %s", lastevent, ev.str); + history(hist, &ev, H_ADD , buf); + } else { + history(hist, &ev, H_ENTER, buf); + lastevent = ev.num; + } +#else + /* Simpler */ + history(hist, &ev, continuation ? H_APPEND : H_ENTER, buf); +#endif + + continuation = ncontinuation; + ncontinuation = 0; + if (continuation) + continue; +#ifdef DEBUG + for (i = 0; i < ac; i++) { + (void) fprintf(stderr, " > arg# %2d ", i); + if (i != cc) + (void) fprintf(stderr, "`%s'\n", av[i]); + else + (void) fprintf(stderr, "`%.*s_%s'\n", + co, av[i], av[i] + co); + } +#endif + + if (strcmp(av[0], "history") == 0) { + int rv; + + switch (ac) { + case 1: + for (rv = history(hist, &ev, H_LAST); rv != -1; + rv = history(hist, &ev, H_PREV)) + (void) fprintf(stdout, "%4d %s", + ev.num, ev.str); + break; + + case 2: + if (strcmp(av[1], "clear") == 0) + history(hist, &ev, H_CLEAR); + else + goto badhist; + break; + + case 3: + if (strcmp(av[1], "load") == 0) + history(hist, &ev, H_LOAD, av[2]); + else if (strcmp(av[1], "save") == 0) + history(hist, &ev, H_SAVE, av[2]); + break; + + badhist: + default: + (void) fprintf(stderr, + "Bad history arguments\n"); + break; + } + } else if (el_parse(el, ac, av) == -1) { + switch (fork()) { + case 0: + execvp(av[0], __DECONST(char **, av)); + perror(av[0]); + _exit(1); + /*NOTREACHED*/ + break; + + case -1: + perror("fork"); + break; + + default: + if (wait(&num) == -1) + perror("wait"); + (void) fprintf(stderr, "Exit %x\n", num); + break; + } + } + + tok_reset(tok); + } + + el_end(el); + tok_end(tok); + history_end(hist); + + return (0); +} diff --git a/lib/libedit/TEST/wtc1.c b/lib/libedit/TEST/wtc1.c new file mode 100644 index 0000000..ccb6f4c --- /dev/null +++ b/lib/libedit/TEST/wtc1.c @@ -0,0 +1,284 @@ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/TEST/wtc1.c 313981 2017-02-20 03:33:59Z pfg $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../histedit.h" + + +static int continuation; +volatile sig_atomic_t gotsig; +static const char hfile[] = ".whistory"; + +static wchar_t * +prompt(EditLine *el) +{ + static wchar_t a[] = L"\1\033[7m\1Edit$\1\033[0m\1 "; + static wchar_t b[] = L"Edit> "; + + return continuation ? b : a; +} + + +static void +sig(int i) +{ + gotsig = i; +} + +const char * +my_wcstombs(const wchar_t *wstr) +{ + static struct { + char *str; + int len; + } buf; + + int needed = wcstombs(0, wstr, 0) + 1; + if (needed > buf.len) { + buf.str = malloc(needed); + buf.len = needed; + } + wcstombs(buf.str, wstr, needed); + buf.str[needed - 1] = 0; + + return buf.str; +} + + +static unsigned char +complete(EditLine *el, int ch) +{ + DIR *dd = opendir("."); + struct dirent *dp; + const wchar_t *ptr; + char *buf, *bptr; + const LineInfoW *lf = el_wline(el); + int len, mblen, i; + unsigned char res = 0; + wchar_t dir[1024]; + + /* Find the last word */ + for (ptr = lf->cursor -1; !iswspace(*ptr) && ptr > lf->buffer; --ptr) + continue; + len = lf->cursor - ++ptr; + + /* Convert last word to multibyte encoding, so we can compare to it */ + wctomb(NULL, 0); /* Reset shift state */ + mblen = MB_LEN_MAX * len + 1; + buf = bptr = malloc(mblen); + if (buf == NULL) + err(1, "malloc"); + for (i = 0; i < len; ++i) { + /* Note: really should test for -1 return from wctomb */ + bptr += wctomb(bptr, ptr[i]); + } + *bptr = 0; /* Terminate multibyte string */ + mblen = bptr - buf; + + /* Scan directory for matching name */ + for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { + if (mblen > strlen(dp->d_name)) + continue; + if (strncmp(dp->d_name, buf, mblen) == 0) { + mbstowcs(dir, &dp->d_name[mblen], + sizeof(dir) / sizeof(*dir)); + if (el_winsertstr(el, dir) == -1) + res = CC_ERROR; + else + res = CC_REFRESH; + break; + } + } + + closedir(dd); + free(buf); + return res; +} + + +int +main(int argc, char *argv[]) +{ + EditLine *el = NULL; + int numc, ncontinuation; + const wchar_t *line; + TokenizerW *tok; + HistoryW *hist; + HistEventW ev; +#ifdef DEBUG + int i; +#endif + + setlocale(LC_ALL, ""); + + (void)signal(SIGINT, sig); + (void)signal(SIGQUIT, sig); + (void)signal(SIGHUP, sig); + (void)signal(SIGTERM, sig); + + hist = history_winit(); /* Init built-in history */ + history_w(hist, &ev, H_SETSIZE, 100); /* Remember 100 events */ + history_w(hist, &ev, H_LOAD, hfile); + + tok = tok_winit(NULL); /* Init the tokenizer */ + + el = el_init(argv[0], stdin, stdout, stderr); + + el_wset(el, EL_EDITOR, L"vi"); /* Default editor is vi */ + el_wset(el, EL_SIGNAL, 1); /* Handle signals gracefully */ + el_wset(el, EL_PROMPT_ESC, prompt, '\1'); /* Set the prompt function */ + + el_wset(el, EL_HIST, history_w, hist); /* FIXME - history_w? */ + + /* Add a user-defined function */ + el_wset(el, EL_ADDFN, L"ed-complete", L"Complete argument", complete); + + /* Bind to it */ + el_wset(el, EL_BIND, L"^I", L"ed-complete", NULL); + + /* + * Bind j, k in vi command mode to previous and next line, instead + * of previous and next history. + */ + el_wset(el, EL_BIND, L"-a", L"k", L"ed-prev-line", NULL); + el_wset(el, EL_BIND, L"-a", L"j", L"ed-next-line", NULL); + + /* Source the user's defaults file. */ + el_source(el, NULL); + + while((line = el_wgets(el, &numc)) != NULL && numc != 0) { + int ac, cc, co, rc; + const wchar_t **av; + + const LineInfoW *li; + li = el_wline(el); + +#ifdef DEBUG + (void)fwprintf(stderr, L"==> got %d %ls", numc, line); + (void)fwprintf(stderr, L" > li `%.*ls_%.*ls'\n", + (li->cursor - li->buffer), li->buffer, + (li->lastchar - 1 - li->cursor), + (li->cursor >= li->lastchar) ? L"" : li->cursor); +#endif + + if (gotsig) { + (void)fprintf(stderr, "Got signal %d.\n", (int)gotsig); + gotsig = 0; + el_reset(el); + } + + if(!continuation && numc == 1) + continue; /* Only got a linefeed */ + + ac = cc = co = 0; + ncontinuation = tok_wline(tok, li, &ac, &av, &cc, &co); + if (ncontinuation < 0) { + (void) fprintf(stderr, "Internal error\n"); + continuation = 0; + continue; + } + +#ifdef DEBUG + (void)fprintf(stderr, " > nc %d ac %d cc %d co %d\n", + ncontinuation, ac, cc, co); +#endif + history_w(hist, &ev, continuation ? H_APPEND : H_ENTER, line); + + continuation = ncontinuation; + ncontinuation = 0; + if(continuation) + continue; + +#ifdef DEBUG + for (i = 0; i < ac; ++i) { + (void)fwprintf(stderr, L" > arg# %2d ", i); + if (i != cc) + (void)fwprintf(stderr, L"`%ls'\n", av[i]); + else + (void)fwprintf(stderr, L"`%.*ls_%ls'\n", + co, av[i], av[i] + co); + } +#endif + + if (wcscmp (av[0], L"history") == 0) { + switch(ac) { + case 1: + for(rc = history_w(hist, &ev, H_LAST); + rc != -1; + rc = history_w(hist, &ev, H_PREV)) + (void)fwprintf(stdout, L"%4d %ls", + ev.num, ev.str); + break; + case 2: + if (wcscmp(av[1], L"clear") == 0) + history_w(hist, &ev, H_CLEAR); + else + goto badhist; + break; + case 3: + if (wcscmp(av[1], L"load") == 0) + history_w(hist, &ev, H_LOAD, + my_wcstombs(av[2])); + else if (wcscmp(av[1], L"save") == 0) + history_w(hist, &ev, H_SAVE, + my_wcstombs(av[2])); + else + goto badhist; + break; + badhist: + default: + (void)fprintf(stderr, + "Bad history arguments\n"); + break; + } + } else if (el_wparse(el, ac, av) == -1) { + switch (fork()) { + case 0: { + Tokenizer *ntok = tok_init(NULL); + int nargc; + const char **nav; + tok_str(ntok, my_wcstombs(line), &nargc, &nav); + execvp(nav[0],(char **)nav); + perror(nav[0]); + _exit(1); + /* NOTREACHED */ + break; + } + case -1: + perror("fork"); + break; + default: + if (wait(&rc) == -1) + perror("wait"); + (void)fprintf(stderr, "Exit %x\n", rc); + break; + } + } + + tok_wreset(tok); + } + + el_end(el); + tok_wend(tok); + history_w(hist, &ev, H_SAVE, hfile); + history_wend(hist); + + fprintf(stdout, "\n"); + return 0; +} + + diff --git a/lib/libedit/chared.c b/lib/libedit/chared.c new file mode 100644 index 0000000..aee75a1 --- /dev/null +++ b/lib/libedit/chared.c @@ -0,0 +1,777 @@ +/* $NetBSD: chared.c,v 1.49 2016/02/24 14:29:21 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: chared.c,v 1.49 2016/02/24 14:29:21 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/chared.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * chared.c: Character editor utilities + */ +#include +#include +#include + +#include "el.h" +#include "common.h" + +private void ch__clearmacro (EditLine *); + +/* value to leave unused in line buffer */ +#define EL_LEAVE 2 + +/* cv_undo(): + * Handle state for the vi undo command + */ +protected void +cv_undo(EditLine *el) +{ + c_undo_t *vu = &el->el_chared.c_undo; + c_redo_t *r = &el->el_chared.c_redo; + size_t size; + + /* Save entire line for undo */ + size = (size_t)(el->el_line.lastchar - el->el_line.buffer); + vu->len = (ssize_t)size; + vu->cursor = (int)(el->el_line.cursor - el->el_line.buffer); + (void)memcpy(vu->buf, el->el_line.buffer, size * sizeof(*vu->buf)); + + /* save command info for redo */ + r->count = el->el_state.doingarg ? el->el_state.argument : 0; + r->action = el->el_chared.c_vcmd.action; + r->pos = r->buf; + r->cmd = el->el_state.thiscmd; + r->ch = el->el_state.thisch; +} + +/* cv_yank(): + * Save yank/delete data for paste + */ +protected void +cv_yank(EditLine *el, const Char *ptr, int size) +{ + c_kill_t *k = &el->el_chared.c_kill; + + (void)memcpy(k->buf, ptr, (size_t)size * sizeof(*k->buf)); + k->last = k->buf + size; +} + + +/* c_insert(): + * Insert num characters + */ +protected void +c_insert(EditLine *el, int num) +{ + Char *cp; + + if (el->el_line.lastchar + num >= el->el_line.limit) { + if (!ch_enlargebufs(el, (size_t)num)) + return; /* can't go past end of buffer */ + } + + if (el->el_line.cursor < el->el_line.lastchar) { + /* if I must move chars */ + for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--) + cp[num] = *cp; + } + el->el_line.lastchar += num; +} + + +/* c_delafter(): + * Delete num characters after the cursor + */ +protected void +c_delafter(EditLine *el, int num) +{ + + if (el->el_line.cursor + num > el->el_line.lastchar) + num = (int)(el->el_line.lastchar - el->el_line.cursor); + + if (el->el_map.current != el->el_map.emacs) { + cv_undo(el); + cv_yank(el, el->el_line.cursor, num); + } + + if (num > 0) { + Char *cp; + + for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) + *cp = cp[num]; + + el->el_line.lastchar -= num; + } +} + + +/* c_delafter1(): + * Delete the character after the cursor, do not yank + */ +protected void +c_delafter1(EditLine *el) +{ + Char *cp; + + for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) + *cp = cp[1]; + + el->el_line.lastchar--; +} + + +/* c_delbefore(): + * Delete num characters before the cursor + */ +protected void +c_delbefore(EditLine *el, int num) +{ + + if (el->el_line.cursor - num < el->el_line.buffer) + num = (int)(el->el_line.cursor - el->el_line.buffer); + + if (el->el_map.current != el->el_map.emacs) { + cv_undo(el); + cv_yank(el, el->el_line.cursor - num, num); + } + + if (num > 0) { + Char *cp; + + for (cp = el->el_line.cursor - num; + cp <= el->el_line.lastchar; + cp++) + *cp = cp[num]; + + el->el_line.lastchar -= num; + } +} + + +/* c_delbefore1(): + * Delete the character before the cursor, do not yank + */ +protected void +c_delbefore1(EditLine *el) +{ + Char *cp; + + for (cp = el->el_line.cursor - 1; cp <= el->el_line.lastchar; cp++) + *cp = cp[1]; + + el->el_line.lastchar--; +} + + +/* ce__isword(): + * Return if p is part of a word according to emacs + */ +protected int +ce__isword(wint_t p) +{ + return Isalnum(p) || Strchr(STR("*?_-.[]~="), p) != NULL; +} + + +/* cv__isword(): + * Return if p is part of a word according to vi + */ +protected int +cv__isword(wint_t p) +{ + if (Isalnum(p) || p == '_') + return 1; + if (Isgraph(p)) + return 2; + return 0; +} + + +/* cv__isWord(): + * Return if p is part of a big word according to vi + */ +protected int +cv__isWord(wint_t p) +{ + return !Isspace(p); +} + + +/* c__prev_word(): + * Find the previous word + */ +protected Char * +c__prev_word(Char *p, Char *low, int n, int (*wtest)(wint_t)) +{ + p--; + + while (n--) { + while ((p >= low) && !(*wtest)(*p)) + p--; + while ((p >= low) && (*wtest)(*p)) + p--; + } + + /* cp now points to one character before the word */ + p++; + if (p < low) + p = low; + /* cp now points where we want it */ + return p; +} + + +/* c__next_word(): + * Find the next word + */ +protected Char * +c__next_word(Char *p, Char *high, int n, int (*wtest)(wint_t)) +{ + while (n--) { + while ((p < high) && !(*wtest)(*p)) + p++; + while ((p < high) && (*wtest)(*p)) + p++; + } + if (p > high) + p = high; + /* p now points where we want it */ + return p; +} + +/* cv_next_word(): + * Find the next word vi style + */ +protected Char * +cv_next_word(EditLine *el, Char *p, Char *high, int n, int (*wtest)(wint_t)) +{ + int test; + + while (n--) { + test = (*wtest)(*p); + while ((p < high) && (*wtest)(*p) == test) + p++; + /* + * vi historically deletes with cw only the word preserving the + * trailing whitespace! This is not what 'w' does.. + */ + if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT)) + while ((p < high) && Isspace(*p)) + p++; + } + + /* p now points where we want it */ + if (p > high) + return high; + else + return p; +} + + +/* cv_prev_word(): + * Find the previous word vi style + */ +protected Char * +cv_prev_word(Char *p, Char *low, int n, int (*wtest)(wint_t)) +{ + int test; + + p--; + while (n--) { + while ((p > low) && Isspace(*p)) + p--; + test = (*wtest)(*p); + while ((p >= low) && (*wtest)(*p) == test) + p--; + } + p++; + + /* p now points where we want it */ + if (p < low) + return low; + else + return p; +} + + +/* cv_delfini(): + * Finish vi delete action + */ +protected void +cv_delfini(EditLine *el) +{ + int size; + int action = el->el_chared.c_vcmd.action; + + if (action & INSERT) + el->el_map.current = el->el_map.key; + + if (el->el_chared.c_vcmd.pos == 0) + /* sanity */ + return; + + size = (int)(el->el_line.cursor - el->el_chared.c_vcmd.pos); + if (size == 0) + size = 1; + el->el_line.cursor = el->el_chared.c_vcmd.pos; + if (action & YANK) { + if (size > 0) + cv_yank(el, el->el_line.cursor, size); + else + cv_yank(el, el->el_line.cursor + size, -size); + } else { + if (size > 0) { + c_delafter(el, size); + re_refresh_cursor(el); + } else { + c_delbefore(el, -size); + el->el_line.cursor += size; + } + } + el->el_chared.c_vcmd.action = NOP; +} + + +/* cv__endword(): + * Go to the end of this word according to vi + */ +protected Char * +cv__endword(Char *p, Char *high, int n, int (*wtest)(wint_t)) +{ + int test; + + p++; + + while (n--) { + while ((p < high) && Isspace(*p)) + p++; + + test = (*wtest)(*p); + while ((p < high) && (*wtest)(*p) == test) + p++; + } + p--; + return p; +} + +/* ch_init(): + * Initialize the character editor + */ +protected int +ch_init(EditLine *el) +{ + c_macro_t *ma = &el->el_chared.c_macro; + + el->el_line.buffer = el_malloc(EL_BUFSIZ * + sizeof(*el->el_line.buffer)); + if (el->el_line.buffer == NULL) + return -1; + + (void) memset(el->el_line.buffer, 0, EL_BUFSIZ * + sizeof(*el->el_line.buffer)); + el->el_line.cursor = el->el_line.buffer; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE]; + + el->el_chared.c_undo.buf = el_malloc(EL_BUFSIZ * + sizeof(*el->el_chared.c_undo.buf)); + if (el->el_chared.c_undo.buf == NULL) + return -1; + (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ * + sizeof(*el->el_chared.c_undo.buf)); + el->el_chared.c_undo.len = -1; + el->el_chared.c_undo.cursor = 0; + el->el_chared.c_redo.buf = el_malloc(EL_BUFSIZ * + sizeof(*el->el_chared.c_redo.buf)); + if (el->el_chared.c_redo.buf == NULL) + return -1; + el->el_chared.c_redo.pos = el->el_chared.c_redo.buf; + el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ; + el->el_chared.c_redo.cmd = ED_UNASSIGNED; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = el->el_line.buffer; + + el->el_chared.c_kill.buf = el_malloc(EL_BUFSIZ * + sizeof(*el->el_chared.c_kill.buf)); + if (el->el_chared.c_kill.buf == NULL) + return -1; + (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ * + sizeof(*el->el_chared.c_kill.buf)); + el->el_chared.c_kill.mark = el->el_line.buffer; + el->el_chared.c_kill.last = el->el_chared.c_kill.buf; + el->el_chared.c_resizefun = NULL; + el->el_chared.c_resizearg = NULL; + el->el_chared.c_aliasfun = NULL; + el->el_chared.c_aliasarg = NULL; + + el->el_map.current = el->el_map.key; + + el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ + el->el_state.doingarg = 0; + el->el_state.metanext = 0; + el->el_state.argument = 1; + el->el_state.lastcmd = ED_UNASSIGNED; + + ma->level = -1; + ma->offset = 0; + ma->macro = el_malloc(EL_MAXMACRO * sizeof(*ma->macro)); + if (ma->macro == NULL) + return -1; + return 0; +} + +/* ch_reset(): + * Reset the character editor + */ +protected void +ch_reset(EditLine *el, int mclear) +{ + el->el_line.cursor = el->el_line.buffer; + el->el_line.lastchar = el->el_line.buffer; + + el->el_chared.c_undo.len = -1; + el->el_chared.c_undo.cursor = 0; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = el->el_line.buffer; + + el->el_chared.c_kill.mark = el->el_line.buffer; + + el->el_map.current = el->el_map.key; + + el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ + el->el_state.doingarg = 0; + el->el_state.metanext = 0; + el->el_state.argument = 1; + el->el_state.lastcmd = ED_UNASSIGNED; + + el->el_history.eventno = 0; + + if (mclear) + ch__clearmacro(el); +} + +private void +ch__clearmacro(EditLine *el) +{ + c_macro_t *ma = &el->el_chared.c_macro; + while (ma->level >= 0) + el_free(ma->macro[ma->level--]); +} + +/* ch_enlargebufs(): + * Enlarge line buffer to be able to hold twice as much characters. + * Returns 1 if successful, 0 if not. + */ +protected int +ch_enlargebufs(EditLine *el, size_t addlen) +{ + size_t sz, newsz; + Char *newbuffer, *oldbuf, *oldkbuf; + + sz = (size_t)(el->el_line.limit - el->el_line.buffer + EL_LEAVE); + newsz = sz * 2; + /* + * If newly required length is longer than current buffer, we need + * to make the buffer big enough to hold both old and new stuff. + */ + if (addlen > sz) { + while(newsz - sz < addlen) + newsz *= 2; + } + + /* + * Reallocate line buffer. + */ + newbuffer = el_realloc(el->el_line.buffer, newsz * sizeof(*newbuffer)); + if (!newbuffer) + return 0; + + /* zero the newly added memory, leave old data in */ + (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); + + oldbuf = el->el_line.buffer; + + el->el_line.buffer = newbuffer; + el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf); + el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf); + /* don't set new size until all buffers are enlarged */ + el->el_line.limit = &newbuffer[sz - EL_LEAVE]; + + /* + * Reallocate kill buffer. + */ + newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz * + sizeof(*newbuffer)); + if (!newbuffer) + return 0; + + /* zero the newly added memory, leave old data in */ + (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); + + oldkbuf = el->el_chared.c_kill.buf; + + el->el_chared.c_kill.buf = newbuffer; + el->el_chared.c_kill.last = newbuffer + + (el->el_chared.c_kill.last - oldkbuf); + el->el_chared.c_kill.mark = el->el_line.buffer + + (el->el_chared.c_kill.mark - oldbuf); + + /* + * Reallocate undo buffer. + */ + newbuffer = el_realloc(el->el_chared.c_undo.buf, + newsz * sizeof(*newbuffer)); + if (!newbuffer) + return 0; + + /* zero the newly added memory, leave old data in */ + (void) memset(&newbuffer[sz], 0, (newsz - sz) * sizeof(*newbuffer)); + el->el_chared.c_undo.buf = newbuffer; + + newbuffer = el_realloc(el->el_chared.c_redo.buf, + newsz * sizeof(*newbuffer)); + if (!newbuffer) + return 0; + el->el_chared.c_redo.pos = newbuffer + + (el->el_chared.c_redo.pos - el->el_chared.c_redo.buf); + el->el_chared.c_redo.lim = newbuffer + + (el->el_chared.c_redo.lim - el->el_chared.c_redo.buf); + el->el_chared.c_redo.buf = newbuffer; + + if (!hist_enlargebuf(el, sz, newsz)) + return 0; + + /* Safe to set enlarged buffer size */ + el->el_line.limit = &el->el_line.buffer[newsz - EL_LEAVE]; + if (el->el_chared.c_resizefun) + (*el->el_chared.c_resizefun)(el, el->el_chared.c_resizearg); + return 1; +} + +/* ch_end(): + * Free the data structures used by the editor + */ +protected void +ch_end(EditLine *el) +{ + el_free(el->el_line.buffer); + el->el_line.buffer = NULL; + el->el_line.limit = NULL; + el_free(el->el_chared.c_undo.buf); + el->el_chared.c_undo.buf = NULL; + el_free(el->el_chared.c_redo.buf); + el->el_chared.c_redo.buf = NULL; + el->el_chared.c_redo.pos = NULL; + el->el_chared.c_redo.lim = NULL; + el->el_chared.c_redo.cmd = ED_UNASSIGNED; + el_free(el->el_chared.c_kill.buf); + el->el_chared.c_kill.buf = NULL; + ch_reset(el, 1); + el_free(el->el_chared.c_macro.macro); + el->el_chared.c_macro.macro = NULL; +} + + +/* el_insertstr(): + * Insert string at cursorI + */ +public int +FUN(el,insertstr)(EditLine *el, const Char *s) +{ + size_t len; + + if (s == NULL || (len = Strlen(s)) == 0) + return -1; + if (el->el_line.lastchar + len >= el->el_line.limit) { + if (!ch_enlargebufs(el, len)) + return -1; + } + + c_insert(el, (int)len); + while (*s) + *el->el_line.cursor++ = *s++; + return 0; +} + + +/* el_deletestr(): + * Delete num characters before the cursor + */ +public void +el_deletestr(EditLine *el, int n) +{ + if (n <= 0) + return; + + if (el->el_line.cursor < &el->el_line.buffer[n]) + return; + + c_delbefore(el, n); /* delete before dot */ + el->el_line.cursor -= n; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; +} + +/* el_cursor(): + * Move the cursor to the left or the right of the current position + */ +public int +el_cursor(EditLine *el, int n) +{ + if (n == 0) + goto out; + + el->el_line.cursor += n; + + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; +out: + return (int)(el->el_line.cursor - el->el_line.buffer); +} + +/* c_gets(): + * Get a string + */ +protected int +c_gets(EditLine *el, Char *buf, const Char *prompt) +{ + wchar_t wch; + ssize_t len; + Char *cp = el->el_line.buffer, ch; + + if (prompt) { + len = (ssize_t)Strlen(prompt); + (void)memcpy(cp, prompt, (size_t)len * sizeof(*cp)); + cp += len; + } + len = 0; + + for (;;) { + el->el_line.cursor = cp; + *cp = ' '; + el->el_line.lastchar = cp + 1; + re_refresh(el); + + if (el_wgetc(el, &wch) != 1) { + ed_end_of_file(el, 0); + len = -1; + break; + } + ch = (Char)wch; + + switch (ch) { + + case L'\b': /* Delete and backspace */ + case 0177: + if (len == 0) { + len = -1; + break; + } + len--; + cp--; + continue; + + case 0033: /* ESC */ + case L'\r': /* Newline */ + case L'\n': + buf[len] = ch; + break; + + default: + if (len >= (ssize_t)(EL_BUFSIZ - 16)) + terminal_beep(el); + else { + buf[len++] = ch; + *cp++ = ch; + } + continue; + } + break; + } + + el->el_line.buffer[0] = '\0'; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + return (int)len; +} + + +/* c_hpos(): + * Return the current horizontal position of the cursor + */ +protected int +c_hpos(EditLine *el) +{ + Char *ptr; + + /* + * Find how many characters till the beginning of this line. + */ + if (el->el_line.cursor == el->el_line.buffer) + return 0; + else { + for (ptr = el->el_line.cursor - 1; + ptr >= el->el_line.buffer && *ptr != '\n'; + ptr--) + continue; + return (int)(el->el_line.cursor - ptr - 1); + } +} + +protected int +ch_resizefun(EditLine *el, el_zfunc_t f, void *a) +{ + el->el_chared.c_resizefun = f; + el->el_chared.c_resizearg = a; + return 0; +} + +protected int +ch_aliasfun(EditLine *el, el_afunc_t f, void *a) +{ + el->el_chared.c_aliasfun = f; + el->el_chared.c_aliasarg = a; + return 0; +} diff --git a/lib/libedit/chared.h b/lib/libedit/chared.h index c94e66d..92a101e 100644 --- a/lib/libedit/chared.h +++ b/lib/libedit/chared.h @@ -1,3 +1,5 @@ +/* $NetBSD: chared.h,v 1.27 2016/02/16 22:53:14 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)chared.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: chared.h,v 1.16 2005/08/08 14:05:37 christos Exp $ - * $FreeBSD: src/lib/libedit/chared.h,v 1.6.12.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/chared.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -40,11 +41,6 @@ #ifndef _h_el_chared #define _h_el_chared -#include -#include - -#include "histedit.h" - #define EL_MAXMACRO 10 /* @@ -63,25 +59,25 @@ typedef struct c_macro_t { int level; int offset; - char **macro; + Char **macro; } c_macro_t; /* * Undo information for vi - no undo in emacs (yet) */ typedef struct c_undo_t { - int len; /* length of saved line */ + ssize_t len; /* length of saved line */ int cursor; /* position of saved cursor */ - char *buf; /* full saved text */ + Char *buf; /* full saved text */ } c_undo_t; /* redo for vi */ typedef struct c_redo_t { - char *buf; /* redo insert key sequence */ - char *pos; - char *lim; + Char *buf; /* redo insert key sequence */ + Char *pos; + Char *lim; el_action_t cmd; /* command to redo */ - char ch; /* char that invoked it */ + Char ch; /* char that invoked it */ int count; int action; /* from cv_action() */ } c_redo_t; @@ -91,18 +87,21 @@ */ typedef struct c_vcmd_t { int action; - char *pos; + Char *pos; } c_vcmd_t; /* * Kill buffer for emacs */ typedef struct c_kill_t { - char *buf; - char *last; - char *mark; + Char *buf; + Char *last; + Char *mark; } c_kill_t; +typedef void (*el_zfunc_t)(EditLine *, void *); +typedef const char *(*el_afunc_t)(void *, const char *); + /* * Note that we use both data structures because the user can bind * commands from both editors! @@ -113,14 +112,16 @@ c_redo_t c_redo; c_vcmd_t c_vcmd; c_macro_t c_macro; + el_zfunc_t c_resizefun; + el_afunc_t c_aliasfun; + void * c_resizearg; + void * c_aliasarg; } el_chared_t; -#define STReof "^D\b\b" #define STRQQ "\"\"" #define isglob(a) (strchr("*[]?", (a)) != NULL) -#define isword(a) (isprint(a)) #define NOP 0x00 #define DELETE 0x01 @@ -134,34 +135,30 @@ #define MODE_REPLACE 1 #define MODE_REPLACE_1 2 -#include "common.h" -#include "vi.h" -#include "emacs.h" -#include "search.h" -#include "fcns.h" - -protected int cv__isword(int); -protected int cv__isWord(int); +protected int cv__isword(wint_t); +protected int cv__isWord(wint_t); protected void cv_delfini(EditLine *); -protected char *cv__endword(char *, char *, int, int (*)(int)); -protected int ce__isword(int); +protected Char *cv__endword(Char *, Char *, int, int (*)(wint_t)); +protected int ce__isword(wint_t); protected void cv_undo(EditLine *); -protected void cv_yank(EditLine *, const char *, int); -protected char *cv_next_word(EditLine*, char *, char *, int, int (*)(int)); -protected char *cv_prev_word(char *, char *, int, int (*)(int)); -protected char *c__next_word(char *, char *, int, int (*)(int)); -protected char *c__prev_word(char *, char *, int, int (*)(int)); +protected void cv_yank(EditLine *, const Char *, int); +protected Char *cv_next_word(EditLine*, Char *, Char *, int, int (*)(wint_t)); +protected Char *cv_prev_word(Char *, Char *, int, int (*)(wint_t)); +protected Char *c__next_word(Char *, Char *, int, int (*)(wint_t)); +protected Char *c__prev_word(Char *, Char *, int, int (*)(wint_t)); protected void c_insert(EditLine *, int); protected void c_delbefore(EditLine *, int); protected void c_delbefore1(EditLine *); protected void c_delafter(EditLine *, int); protected void c_delafter1(EditLine *); -protected int c_gets(EditLine *, char *, const char *); +protected int c_gets(EditLine *, Char *, const Char *); protected int c_hpos(EditLine *); protected int ch_init(EditLine *); protected void ch_reset(EditLine *, int); +protected int ch_resizefun(EditLine *, el_zfunc_t, void *); +protected int ch_aliasfun(EditLine *, el_afunc_t, void *); protected int ch_enlargebufs(EditLine *, size_t); protected void ch_end(EditLine *); diff --git a/lib/libedit/chartype.c b/lib/libedit/chartype.c new file mode 100644 index 0000000..016e28c --- /dev/null +++ b/lib/libedit/chartype.c @@ -0,0 +1,385 @@ +/* $NetBSD: chartype.c,v 1.23 2016/02/28 23:02:24 christos Exp $ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, 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. + * + * 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. + */ + +/* + * chartype.c: character classification and meta information + */ +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +__RCSID("$NetBSD: chartype.c,v 1.23 2016/02/28 23:02:24 christos Exp $"); +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/chartype.c 313981 2017-02-20 03:33:59Z pfg $"); + +#include +#include +#include + +#include "el.h" + +#define CT_BUFSIZ ((size_t)1024) + +#ifdef WIDECHAR +protected int +ct_conv_cbuff_resize(ct_buffer_t *conv, size_t csize) +{ + void *p; + + if (csize <= conv->csize) + return 0; + + conv->csize = csize; + + p = el_realloc(conv->cbuff, conv->csize * sizeof(*conv->cbuff)); + if (p == NULL) { + conv->csize = 0; + el_free(conv->cbuff); + conv->cbuff = NULL; + return -1; + } + conv->cbuff = p; + return 0; +} + +protected int +ct_conv_wbuff_resize(ct_buffer_t *conv, size_t wsize) +{ + void *p; + + if (wsize <= conv->wsize) + return 0; + + conv->wsize = wsize; + + p = el_realloc(conv->wbuff, conv->wsize * sizeof(*conv->wbuff)); + if (p == NULL) { + conv->wsize = 0; + el_free(conv->wbuff); + conv->wbuff = NULL; + return -1; + } + conv->wbuff = p; + return 0; +} + + +public char * +ct_encode_string(const Char *s, ct_buffer_t *conv) +{ + char *dst; + ssize_t used; + + if (!s) + return NULL; + + dst = conv->cbuff; + for (;;) { + used = (ssize_t)(dst - conv->cbuff); + if ((conv->csize - (size_t)used) < 5) { + if (ct_conv_cbuff_resize(conv, + conv->csize + CT_BUFSIZ) == -1) + return NULL; + dst = conv->cbuff + used; + } + if (!*s) + break; + used = ct_encode_char(dst, (size_t)5, *s); + if (used == -1) /* failed to encode, need more buffer space */ + abort(); + ++s; + dst += used; + } + *dst = '\0'; + return conv->cbuff; +} + +public Char * +ct_decode_string(const char *s, ct_buffer_t *conv) +{ + size_t len; + + if (!s) + return NULL; + + len = ct_mbstowcs(NULL, s, (size_t)0); + if (len == (size_t)-1) + return NULL; + + if (conv->wsize < ++len) + if (ct_conv_wbuff_resize(conv, len + CT_BUFSIZ) == -1) + return NULL; + + ct_mbstowcs(conv->wbuff, s, conv->wsize); + return conv->wbuff; +} + + +protected Char ** +ct_decode_argv(int argc, const char *argv[], ct_buffer_t *conv) +{ + size_t bufspace; + int i; + Char *p; + Char **wargv; + ssize_t bytes; + + /* Make sure we have enough space in the conversion buffer to store all + * the argv strings. */ + for (i = 0, bufspace = 0; i < argc; ++i) + bufspace += argv[i] ? strlen(argv[i]) + 1 : 0; + if (conv->wsize < ++bufspace) + if (ct_conv_wbuff_resize(conv, bufspace + CT_BUFSIZ) == -1) + return NULL; + + wargv = el_malloc((size_t)argc * sizeof(*wargv)); + + for (i = 0, p = conv->wbuff; i < argc; ++i) { + if (!argv[i]) { /* don't pass null pointers to mbstowcs */ + wargv[i] = NULL; + continue; + } else { + wargv[i] = p; + bytes = (ssize_t)mbstowcs(p, argv[i], bufspace); + } + if (bytes == -1) { + el_free(wargv); + return NULL; + } else + bytes++; /* include '\0' in the count */ + bufspace -= (size_t)bytes; + p += bytes; + } + + return wargv; +} + + +protected size_t +ct_enc_width(Char c) +{ + /* UTF-8 encoding specific values */ + if (c < 0x80) + return 1; + else if (c < 0x0800) + return 2; + else if (c < 0x10000) + return 3; + else if (c < 0x110000) + return 4; + else + return 0; /* not a valid codepoint */ +} + +protected ssize_t +ct_encode_char(char *dst, size_t len, Char c) +{ + ssize_t l = 0; + if (len < ct_enc_width(c)) + return -1; + l = ct_wctomb(dst, c); + + if (l < 0) { + ct_wctomb_reset; + l = 0; + } + return l; +} + +size_t +ct_mbrtowc(wchar_t *wc, const char *s, size_t n) +{ + mbstate_t mbs; + /* This only works because UTF-8 is stateless */ + memset(&mbs, 0, sizeof(mbs)); + return mbrtowc(wc, s, n, &mbs); +} + +#else + +size_t +ct_mbrtowc(wchar_t *wc, const char *s, size_t n) + if (s == NULL) + return 0; + if (n == 0) + return (size_t)-2; + if (wc != NULL) + *wc = *s; + return *s != '\0'; +} +#endif + +protected const Char * +ct_visual_string(const Char *s) +{ + static Char *buff = NULL; + static size_t buffsize = 0; + void *p; + Char *dst; + ssize_t used = 0; + + if (!s) + return NULL; + if (!buff) { + buffsize = CT_BUFSIZ; + buff = el_malloc(buffsize * sizeof(*buff)); + } + dst = buff; + while (*s) { + used = ct_visual_char(dst, buffsize - (size_t)(dst - buff), *s); + if (used == -1) { /* failed to encode, need more buffer space */ + used = dst - buff; + buffsize += CT_BUFSIZ; + p = el_realloc(buff, buffsize * sizeof(*buff)); + if (p == NULL) + goto out; + buff = p; + dst = buff + used; + /* don't increment s here - we want to retry it! */ + } + else + ++s; + dst += used; + } + if (dst >= (buff + buffsize)) { /* sigh */ + buffsize += 1; + p = el_realloc(buff, buffsize * sizeof(*buff)); + if (p == NULL) + goto out; + buff = p; + dst = buff + buffsize - 1; + } + *dst = 0; + return buff; +out: + el_free(buff); + buffsize = 0; + return NULL; +} + + + +protected int +ct_visual_width(Char c) +{ + int t = ct_chr_class(c); + switch (t) { + case CHTYPE_ASCIICTL: + return 2; /* ^@ ^? etc. */ + case CHTYPE_TAB: + return 1; /* Hmm, this really need to be handled outside! */ + case CHTYPE_NL: + return 0; /* Should this be 1 instead? */ +#ifdef WIDECHAR + case CHTYPE_PRINT: + return wcwidth(c); + case CHTYPE_NONPRINT: + if (c > 0xffff) /* prefer standard 4-byte display over 5-byte */ + return 8; /* \U+12345 */ + else + return 7; /* \U+1234 */ +#else + case CHTYPE_PRINT: + return 1; + case CHTYPE_NONPRINT: + return 4; /* \123 */ +#endif + default: + return 0; /* should not happen */ + } +} + + +protected ssize_t +ct_visual_char(Char *dst, size_t len, Char c) +{ + int t = ct_chr_class(c); + switch (t) { + case CHTYPE_TAB: + case CHTYPE_NL: + case CHTYPE_ASCIICTL: + if (len < 2) + return -1; /* insufficient space */ + *dst++ = '^'; + if (c == '\177') + *dst = '?'; /* DEL -> ^? */ + else + *dst = c | 0100; /* uncontrolify it */ + return 2; + case CHTYPE_PRINT: + if (len < 1) + return -1; /* insufficient space */ + *dst = c; + return 1; + case CHTYPE_NONPRINT: + /* we only use single-width glyphs for display, + * so this is right */ + if ((ssize_t)len < ct_visual_width(c)) + return -1; /* insufficient space */ +#ifdef WIDECHAR + *dst++ = '\\'; + *dst++ = 'U'; + *dst++ = '+'; +#define tohexdigit(v) "0123456789ABCDEF"[v] + if (c > 0xffff) /* prefer standard 4-byte display over 5-byte */ + *dst++ = tohexdigit(((unsigned int) c >> 16) & 0xf); + *dst++ = tohexdigit(((unsigned int) c >> 12) & 0xf); + *dst++ = tohexdigit(((unsigned int) c >> 8) & 0xf); + *dst++ = tohexdigit(((unsigned int) c >> 4) & 0xf); + *dst = tohexdigit(((unsigned int) c ) & 0xf); + return c > 0xffff ? 8 : 7; +#else + *dst++ = '\\'; +#define tooctaldigit(v) (Char)((v) + '0') + *dst++ = tooctaldigit(((unsigned int) c >> 6) & 0x7); + *dst++ = tooctaldigit(((unsigned int) c >> 3) & 0x7); + *dst++ = tooctaldigit(((unsigned int) c ) & 0x7); +#endif + /*FALLTHROUGH*/ + /* these two should be handled outside this function */ + default: /* we should never hit the default */ + return 0; + } +} + + + + +protected int +ct_chr_class(Char c) +{ + if (c == '\t') + return CHTYPE_TAB; + else if (c == '\n') + return CHTYPE_NL; + else if (IsASCII(c) && Iscntrl(c)) + return CHTYPE_ASCIICTL; + else if (Isprint(c)) + return CHTYPE_PRINT; + else + return CHTYPE_NONPRINT; +} diff --git a/lib/libedit/chartype.h b/lib/libedit/chartype.h new file mode 100644 index 0000000..eb16997 --- /dev/null +++ b/lib/libedit/chartype.h @@ -0,0 +1,246 @@ +/* $NetBSD: chartype.h,v 1.25 2016/03/07 00:05:20 christos Exp $ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, 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. + * + * 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: releng/11.1/lib/libedit/chartype.h 313981 2017-02-20 03:33:59Z pfg $ + */ + +#ifndef _h_chartype_f +#define _h_chartype_f + + +#ifdef WIDECHAR + +/* Ideally we should also test the value of the define to see if it + * supports non-BMP code points without requiring UTF-16, but nothing + * seems to actually advertise this properly, despite Unicode 3.1 having + * been around since 2001... */ +#if !defined(__NetBSD__) && !defined(__sun) && !(defined(__APPLE__) && defined(__MACH__)) && !defined(__OpenBSD__) && !defined(__FreeBSD__) +#ifndef __STDC_ISO_10646__ +/* In many places it is assumed that the first 127 code points are ASCII + * compatible, so ensure wchar_t indeed does ISO 10646 and not some other + * funky encoding that could break us in weird and wonderful ways. */ + #error wchar_t must store ISO 10646 characters +#endif +#endif + +/* Oh for a with char32_t and __STDC_UTF_32__ in it... + * ref: ISO/IEC DTR 19769 + */ +#if WCHAR_MAX < INT32_MAX +#warning Build environment does not support non-BMP characters +#endif + +#define ct_wctob wctob +#define ct_wctomb wctomb +#define ct_wctomb_reset wctomb(0,0) +#define ct_wcstombs wcstombs +#define ct_mbstowcs mbstowcs + +#define Char wchar_t +#define FUN(prefix,rest) prefix ## _w ## rest +#define FUNW(type) type ## _w +#define TYPE(type) type ## W +#define FSTR "%ls" +#define FSTARSTR "%.*ls" +#define STR(x) L ## x +#define UC(c) c +#define Isalpha(x) iswalpha(x) +#define Isalnum(x) iswalnum(x) +#define Isgraph(x) iswgraph(x) +#define Isspace(x) iswspace(x) +#define Isdigit(x) iswdigit(x) +#define Iscntrl(x) iswcntrl(x) +#define Isprint(x) iswprint(x) + +#define Isupper(x) iswupper(x) +#define Islower(x) iswlower(x) +#define Toupper(x) towupper(x) +#define Tolower(x) towlower(x) + +#define IsASCII(x) (x < 0x100) + +#define Strlen(x) wcslen(x) +#define Strchr(s,c) wcschr(s,c) +#define Strrchr(s,c) wcsrchr(s,c) +#define Strstr(s,v) wcsstr(s,v) +#define Strdup(x) wcsdup(x) +#define Strcpy(d,s) wcscpy(d,s) +#define Strncpy(d,s,n) wcsncpy(d,s,n) +#define Strncat(d,s,n) wcsncat(d,s,n) + +#define Strcmp(s,v) wcscmp(s,v) +#define Strncmp(s,v,n) wcsncmp(s,v,n) +#define Strcspn(s,r) wcscspn(s,r) + +#define Strtol(p,e,b) wcstol(p,e,b) + +static inline int +Width(wchar_t c) +{ + int w = wcwidth(c); + return w < 0 ? 0 : w; +} + +#else /* NARROW */ + +#define ct_wctob(w) ((int)(w)) +#define ct_wctomb error +#define ct_wctomb_reset +#define ct_wcstombs(a, b, c) (strncpy(a, b, c), strlen(a)) +#define ct_mbstowcs(a, b, c) (strncpy(a, b, c), strlen(a)) + +#define Char char +#define FUN(prefix,rest) prefix ## _ ## rest +#define FUNW(type) type +#define TYPE(type) type +#define FSTR "%s" +#define FSTARSTR "%.*s" +#define STR(x) x +#define UC(c) (unsigned char)(c) + +#define Isalpha(x) isalpha((unsigned char)x) +#define Isalnum(x) isalnum((unsigned char)x) +#define Isgraph(x) isgraph((unsigned char)x) +#define Isspace(x) isspace((unsigned char)x) +#define Isdigit(x) isdigit((unsigned char)x) +#define Iscntrl(x) iscntrl((unsigned char)x) +#define Isprint(x) isprint((unsigned char)x) + +#define Isupper(x) isupper((unsigned char)x) +#define Islower(x) islower((unsigned char)x) +#define Toupper(x) toupper((unsigned char)x) +#define Tolower(x) tolower((unsigned char)x) + +#define IsASCII(x) isascii((unsigned char)x) + +#define Strlen(x) strlen(x) +#define Strchr(s,c) strchr(s,c) +#define Strrchr(s,c) strrchr(s,c) +#define Strstr(s,v) strstr(s,v) +#define Strdup(x) strdup(x) +#define Strcpy(d,s) strcpy(d,s) +#define Strncpy(d,s,n) strncpy(d,s,n) +#define Strncat(d,s,n) strncat(d,s,n) + +#define Strcmp(s,v) strcmp(s,v) +#define Strncmp(s,v,n) strncmp(s,v,n) +#define Strcspn(s,r) strcspn(s,r) + +#define Strtol(p,e,b) strtol(p,e,b) + +#define Width(c) 1 + +#endif + + +#ifdef WIDECHAR +/* + * Conversion buffer + */ +typedef struct ct_buffer_t { + char *cbuff; + size_t csize; + Char *wbuff; + size_t wsize; +} ct_buffer_t; + +#define ct_encode_string __ct_encode_string +/* Encode a wide-character string and return the UTF-8 encoded result. */ +public char *ct_encode_string(const Char *, ct_buffer_t *); + +#define ct_decode_string __ct_decode_string +/* Decode a (multi)?byte string and return the wide-character string result. */ +public Char *ct_decode_string(const char *, ct_buffer_t *); + +/* Decode a (multi)?byte argv string array. + * The pointer returned must be free()d when done. */ +protected Char **ct_decode_argv(int, const char *[], ct_buffer_t *); + +/* Resizes the conversion buffer(s) if needed. */ +protected int ct_conv_cbuff_resize(ct_buffer_t *, size_t); +protected int ct_conv_wbuff_resize(ct_buffer_t *, size_t); +protected ssize_t ct_encode_char(char *, size_t, Char); +protected size_t ct_enc_width(Char); + +#define ct_free_argv(s) el_free(s) + +#else +#define ct_encode_string(s, b) (s) +#define ct_decode_string(s, b) (s) +#define ct_decode_argv(l, s, b) (s) +#define ct_conv_cbuff_resize(b, s) ((s) == (0)) +#define ct_conv_wbuff_resize(b, s) ((s) == (0)) +#define ct_encode_char(d, l, s) (*d = s, 1) +#define ct_free_argv(s) +#endif + +#ifndef NARROWCHAR +/* Encode a characted into the destination buffer, provided there is sufficient + * buffer space available. Returns the number of bytes used up (zero if the + * character cannot be encoded, -1 if there was not enough space available). */ + +/* The maximum buffer size to hold the most unwieldy visual representation, + * in this case \U+nnnnn. */ +#define VISUAL_WIDTH_MAX ((size_t)8) + +/* The terminal is thought of in terms of X columns by Y lines. In the cases + * where a wide character takes up more than one column, the adjacent + * occupied column entries will contain this faux character. */ +#define MB_FILL_CHAR ((Char)-1) + +/* Visual width of character c, taking into account ^? , \0177 and \U+nnnnn + * style visual expansions. */ +protected int ct_visual_width(Char); + +/* Turn the given character into the appropriate visual format, matching + * the width given by ct_visual_width(). Returns the number of characters used + * up, or -1 if insufficient space. Buffer length is in count of Char's. */ +protected ssize_t ct_visual_char(Char *, size_t, Char); + +/* Convert the given string into visual format, using the ct_visual_char() + * function. Uses a static buffer, so not threadsafe. */ +protected const Char *ct_visual_string(const Char *); + + +/* printable character, use ct_visual_width() to find out display width */ +#define CHTYPE_PRINT ( 0) +/* control character found inside the ASCII portion of the charset */ +#define CHTYPE_ASCIICTL (-1) +/* a \t */ +#define CHTYPE_TAB (-2) +/* a \n */ +#define CHTYPE_NL (-3) +/* non-printable character */ +#define CHTYPE_NONPRINT (-4) +/* classification of character c, as one of the above defines */ +protected int ct_chr_class(Char c); +#endif + +size_t ct_mbrtowc(wchar_t *, const char *, size_t); + + +#endif /* _chartype_f */ diff --git a/lib/libedit/common.c b/lib/libedit/common.c new file mode 100644 index 0000000..7f00592 --- /dev/null +++ b/lib/libedit/common.c @@ -0,0 +1,925 @@ +/* $NetBSD: common.c,v 1.40 2016/03/02 19:24:20 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)common.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: common.c,v 1.40 2016/03/02 19:24:20 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/common.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * common.c: Common Editor functions + */ +#include +#include + +#include "el.h" +#include "common.h" +#include "parse.h" +#include "vi.h" + +/* ed_end_of_file(): + * Indicate end of file + * [^D] + */ +protected el_action_t +/*ARGSUSED*/ +ed_end_of_file(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + re_goto_bottom(el); + *el->el_line.lastchar = '\0'; + return CC_EOF; +} + + +/* ed_insert(): + * Add character to the line + * Insert a character [bound to all insert keys] + */ +protected el_action_t +ed_insert(EditLine *el, wint_t c) +{ + int count = el->el_state.argument; + + if (c == '\0') + return CC_ERROR; + + if (el->el_line.lastchar + el->el_state.argument >= + el->el_line.limit) { + /* end of buffer space, try to allocate more */ + if (!ch_enlargebufs(el, (size_t) count)) + return CC_ERROR; /* error allocating more */ + } + + if (count == 1) { + if (el->el_state.inputmode == MODE_INSERT + || el->el_line.cursor >= el->el_line.lastchar) + c_insert(el, 1); + + *el->el_line.cursor++ = (Char)c; + re_fastaddc(el); /* fast refresh for one char. */ + } else { + if (el->el_state.inputmode != MODE_REPLACE_1) + c_insert(el, el->el_state.argument); + + while (count-- && el->el_line.cursor < el->el_line.lastchar) + *el->el_line.cursor++ = (Char)c; + re_refresh(el); + } + + if (el->el_state.inputmode == MODE_REPLACE_1) + return vi_command_mode(el, 0); + + return CC_NORM; +} + + +/* ed_delete_prev_word(): + * Delete from beginning of current word to cursor + * [M-^?] [^W] + */ +protected el_action_t +/*ARGSUSED*/ +ed_delete_prev_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *cp, *p, *kp; + + if (el->el_line.cursor == el->el_line.buffer) + return CC_ERROR; + + cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, + el->el_state.argument, ce__isword); + + for (p = cp, kp = el->el_chared.c_kill.buf; p < el->el_line.cursor; p++) + *kp++ = *p; + el->el_chared.c_kill.last = kp; + + c_delbefore(el, (int)(el->el_line.cursor - cp));/* delete before dot */ + el->el_line.cursor = cp; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; /* bounds check */ + return CC_REFRESH; +} + + +/* ed_delete_next_char(): + * Delete character under cursor + * [^D] [x] + */ +protected el_action_t +/*ARGSUSED*/ +ed_delete_next_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ +#ifdef DEBUG_EDIT +#define EL el->el_line + (void) fprintf(el->el_errfile, + "\nD(b: %p(" FSTR ") c: %p(" FSTR ") last: %p(" FSTR + ") limit: %p(" FSTR ")\n", + EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar, + EL.lastchar, EL.limit, EL.limit); +#endif + if (el->el_line.cursor == el->el_line.lastchar) { + /* if I'm at the end */ + if (el->el_map.type == MAP_VI) { + if (el->el_line.cursor == el->el_line.buffer) { + /* if I'm also at the beginning */ +#ifdef KSHVI + return CC_ERROR; +#else + /* then do an EOF */ + terminal_writec(el, c); + return CC_EOF; +#endif + } else { +#ifdef KSHVI + el->el_line.cursor--; +#else + return CC_ERROR; +#endif + } + } else + return CC_ERROR; + } + c_delafter(el, el->el_state.argument); /* delete after dot */ + if (el->el_map.type == MAP_VI && + el->el_line.cursor >= el->el_line.lastchar && + el->el_line.cursor > el->el_line.buffer) + /* bounds check */ + el->el_line.cursor = el->el_line.lastchar - 1; + return CC_REFRESH; +} + + +/* ed_kill_line(): + * Cut to the end of line + * [^K] [^K] + */ +protected el_action_t +/*ARGSUSED*/ +ed_kill_line(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *kp, *cp; + + cp = el->el_line.cursor; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.lastchar) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + /* zap! -- delete to end */ + el->el_line.lastchar = el->el_line.cursor; + return CC_REFRESH; +} + + +/* ed_move_to_end(): + * Move cursor to the end of line + * [^E] [^E] + */ +protected el_action_t +/*ARGSUSED*/ +ed_move_to_end(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_line.cursor = el->el_line.lastchar; + if (el->el_map.type == MAP_VI) { + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } +#ifdef VI_MOVE + el->el_line.cursor--; +#endif + } + return CC_CURSOR; +} + + +/* ed_move_to_beg(): + * Move cursor to the beginning of line + * [^A] [^A] + */ +protected el_action_t +/*ARGSUSED*/ +ed_move_to_beg(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_line.cursor = el->el_line.buffer; + + if (el->el_map.type == MAP_VI) { + /* We want FIRST non space character */ + while (Isspace(*el->el_line.cursor)) + el->el_line.cursor++; + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + } + return CC_CURSOR; +} + + +/* ed_transpose_chars(): + * Exchange the character to the left of the cursor with the one under it + * [^T] [^T] + */ +protected el_action_t +ed_transpose_chars(EditLine *el, wint_t c) +{ + + if (el->el_line.cursor < el->el_line.lastchar) { + if (el->el_line.lastchar <= &el->el_line.buffer[1]) + return CC_ERROR; + else + el->el_line.cursor++; + } + if (el->el_line.cursor > &el->el_line.buffer[1]) { + /* must have at least two chars entered */ + c = el->el_line.cursor[-2]; + el->el_line.cursor[-2] = el->el_line.cursor[-1]; + el->el_line.cursor[-1] = (Char)c; + return CC_REFRESH; + } else + return CC_ERROR; +} + + +/* ed_next_char(): + * Move to the right one character + * [^F] [^F] + */ +protected el_action_t +/*ARGSUSED*/ +ed_next_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *lim = el->el_line.lastchar; + + if (el->el_line.cursor >= lim || + (el->el_line.cursor == lim - 1 && + el->el_map.type == MAP_VI && + el->el_chared.c_vcmd.action == NOP)) + return CC_ERROR; + + el->el_line.cursor += el->el_state.argument; + if (el->el_line.cursor > lim) + el->el_line.cursor = lim; + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* ed_prev_word(): + * Move to the beginning of the current word + * [M-b] [b] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor == el->el_line.buffer) + return CC_ERROR; + + el->el_line.cursor = c__prev_word(el->el_line.cursor, + el->el_line.buffer, + el->el_state.argument, + ce__isword); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* ed_prev_char(): + * Move to the left one character + * [^B] [^B] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor > el->el_line.buffer) { + el->el_line.cursor -= el->el_state.argument; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; + } else + return CC_ERROR; +} + + +/* ed_quoted_insert(): + * Add the next character typed verbatim + * [^V] [^V] + */ +protected el_action_t +ed_quoted_insert(EditLine *el, wint_t c) +{ + int num; + + tty_quotemode(el); + num = el_wgetc(el, &c); + tty_noquotemode(el); + if (num == 1) + return ed_insert(el, c); + else + return ed_end_of_file(el, 0); +} + + +/* ed_digit(): + * Adds to argument or enters a digit + */ +protected el_action_t +ed_digit(EditLine *el, wint_t c) +{ + + if (!Isdigit(c)) + return CC_ERROR; + + if (el->el_state.doingarg) { + /* if doing an arg, add this in... */ + if (el->el_state.lastcmd == EM_UNIVERSAL_ARGUMENT) + el->el_state.argument = c - '0'; + else { + if (el->el_state.argument > 1000000) + return CC_ERROR; + el->el_state.argument = + (el->el_state.argument * 10) + (c - '0'); + } + return CC_ARGHACK; + } + + return ed_insert(el, c); +} + + +/* ed_argument_digit(): + * Digit that starts argument + * For ESC-n + */ +protected el_action_t +ed_argument_digit(EditLine *el, wint_t c) +{ + + if (!Isdigit(c)) + return CC_ERROR; + + if (el->el_state.doingarg) { + if (el->el_state.argument > 1000000) + return CC_ERROR; + el->el_state.argument = (el->el_state.argument * 10) + + (c - '0'); + } else { /* else starting an argument */ + el->el_state.argument = c - '0'; + el->el_state.doingarg = 1; + } + return CC_ARGHACK; +} + + +/* ed_unassigned(): + * Indicates unbound character + * Bound to keys that are not assigned + */ +protected el_action_t +/*ARGSUSED*/ +ed_unassigned(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_ERROR; +} + + +/** + ** TTY key handling. + **/ + +/* ed_tty_sigint(): + * Tty interrupt character + * [^C] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_sigint(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_tty_dsusp(): + * Tty delayed suspend character + * [^Y] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_dsusp(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_tty_flush_output(): + * Tty flush output characters + * [^O] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_flush_output(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_tty_sigquit(): + * Tty quit character + * [^\] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_sigquit(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_tty_sigtstp(): + * Tty suspend character + * [^Z] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_sigtstp(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_tty_stop_output(): + * Tty disallow output characters + * [^S] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_stop_output(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_tty_start_output(): + * Tty allow output characters + * [^Q] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_start_output(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_newline(): + * Execute command + * [^J] + */ +protected el_action_t +/*ARGSUSED*/ +ed_newline(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + re_goto_bottom(el); + *el->el_line.lastchar++ = '\n'; + *el->el_line.lastchar = '\0'; + return CC_NEWLINE; +} + + +/* ed_delete_prev_char(): + * Delete the character to the left of the cursor + * [^?] + */ +protected el_action_t +/*ARGSUSED*/ +ed_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor <= el->el_line.buffer) + return CC_ERROR; + + c_delbefore(el, el->el_state.argument); + el->el_line.cursor -= el->el_state.argument; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; + return CC_REFRESH; +} + + +/* ed_clear_screen(): + * Clear screen leaving current line at the top + * [^L] + */ +protected el_action_t +/*ARGSUSED*/ +ed_clear_screen(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + terminal_clear_screen(el); /* clear the whole real screen */ + re_clear_display(el); /* reset everything */ + return CC_REFRESH; +} + + +/* ed_redisplay(): + * Redisplay everything + * ^R + */ +protected el_action_t +/*ARGSUSED*/ +ed_redisplay(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_REDISPLAY; +} + + +/* ed_start_over(): + * Erase current line and start from scratch + * [^G] + */ +protected el_action_t +/*ARGSUSED*/ +ed_start_over(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + ch_reset(el, 0); + return CC_REFRESH; +} + + +/* ed_sequence_lead_in(): + * First character in a bound sequence + * Placeholder for external keys + */ +protected el_action_t +/*ARGSUSED*/ +ed_sequence_lead_in(EditLine *el __attribute__((__unused__)), + wint_t c __attribute__((__unused__))) +{ + + return CC_NORM; +} + + +/* ed_prev_history(): + * Move to the previous history line + * [^P] [k] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_history(EditLine *el, wint_t c __attribute__((__unused__))) +{ + char beep = 0; + int sv_event = el->el_history.eventno; + + el->el_chared.c_undo.len = -1; + *el->el_line.lastchar = '\0'; /* just in case */ + + if (el->el_history.eventno == 0) { /* save the current buffer + * away */ + (void) Strncpy(el->el_history.buf, el->el_line.buffer, + EL_BUFSIZ); + el->el_history.last = el->el_history.buf + + (el->el_line.lastchar - el->el_line.buffer); + } + el->el_history.eventno += el->el_state.argument; + + if (hist_get(el) == CC_ERROR) { + if (el->el_map.type == MAP_VI) { + el->el_history.eventno = sv_event; + } + beep = 1; + /* el->el_history.eventno was fixed by first call */ + (void) hist_get(el); + } + if (beep) + return CC_REFRESH_BEEP; + return CC_REFRESH; +} + + +/* ed_next_history(): + * Move to the next history line + * [^N] [j] + */ +protected el_action_t +/*ARGSUSED*/ +ed_next_history(EditLine *el, wint_t c __attribute__((__unused__))) +{ + el_action_t beep = CC_REFRESH, rval; + + el->el_chared.c_undo.len = -1; + *el->el_line.lastchar = '\0'; /* just in case */ + + el->el_history.eventno -= el->el_state.argument; + + if (el->el_history.eventno < 0) { + el->el_history.eventno = 0; + beep = CC_REFRESH_BEEP; + } + rval = hist_get(el); + if (rval == CC_REFRESH) + return beep; + return rval; + +} + + +/* ed_search_prev_history(): + * Search previous in history for a line matching the current + * next search history [M-P] [K] + */ +protected el_action_t +/*ARGSUSED*/ +ed_search_prev_history(EditLine *el, wint_t c __attribute__((__unused__))) +{ + const Char *hp; + int h; + int found = 0; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_undo.len = -1; + *el->el_line.lastchar = '\0'; /* just in case */ + if (el->el_history.eventno < 0) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, + "e_prev_search_hist(): eventno < 0;\n"); +#endif + el->el_history.eventno = 0; + return CC_ERROR; + } + if (el->el_history.eventno == 0) { + (void) Strncpy(el->el_history.buf, el->el_line.buffer, + EL_BUFSIZ); + el->el_history.last = el->el_history.buf + + (el->el_line.lastchar - el->el_line.buffer); + } + if (el->el_history.ref == NULL) + return CC_ERROR; + + hp = HIST_FIRST(el); + if (hp == NULL) + return CC_ERROR; + + c_setpat(el); /* Set search pattern !! */ + + for (h = 1; h <= el->el_history.eventno; h++) + hp = HIST_NEXT(el); + + while (hp != NULL) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp); +#endif + if ((Strncmp(hp, el->el_line.buffer, (size_t) + (el->el_line.lastchar - el->el_line.buffer)) || + hp[el->el_line.lastchar - el->el_line.buffer]) && + c_hmatch(el, hp)) { + found = 1; + break; + } + h++; + hp = HIST_NEXT(el); + } + + if (!found) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "not found\n"); +#endif + return CC_ERROR; + } + el->el_history.eventno = h; + + return hist_get(el); +} + + +/* ed_search_next_history(): + * Search next in history for a line matching the current + * [M-N] [J] + */ +protected el_action_t +/*ARGSUSED*/ +ed_search_next_history(EditLine *el, wint_t c __attribute__((__unused__))) +{ + const Char *hp; + int h; + int found = 0; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_undo.len = -1; + *el->el_line.lastchar = '\0'; /* just in case */ + + if (el->el_history.eventno == 0) + return CC_ERROR; + + if (el->el_history.ref == NULL) + return CC_ERROR; + + hp = HIST_FIRST(el); + if (hp == NULL) + return CC_ERROR; + + c_setpat(el); /* Set search pattern !! */ + + for (h = 1; h < el->el_history.eventno && hp; h++) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp); +#endif + if ((Strncmp(hp, el->el_line.buffer, (size_t) + (el->el_line.lastchar - el->el_line.buffer)) || + hp[el->el_line.lastchar - el->el_line.buffer]) && + c_hmatch(el, hp)) + found = h; + hp = HIST_NEXT(el); + } + + if (!found) { /* is it the current history number? */ + if (!c_hmatch(el, el->el_history.buf)) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "not found\n"); +#endif + return CC_ERROR; + } + } + el->el_history.eventno = found; + + return hist_get(el); +} + + +/* ed_prev_line(): + * Move up one line + * Could be [k] [^p] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_line(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *ptr; + int nchars = c_hpos(el); + + /* + * Move to the line requested + */ + if (*(ptr = el->el_line.cursor) == '\n') + ptr--; + + for (; ptr >= el->el_line.buffer; ptr--) + if (*ptr == '\n' && --el->el_state.argument <= 0) + break; + + if (el->el_state.argument > 0) + return CC_ERROR; + + /* + * Move to the beginning of the line + */ + for (ptr--; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--) + continue; + + /* + * Move to the character requested + */ + for (ptr++; + nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; + ptr++) + continue; + + el->el_line.cursor = ptr; + return CC_CURSOR; +} + + +/* ed_next_line(): + * Move down one line + * Could be [j] [^n] + */ +protected el_action_t +/*ARGSUSED*/ +ed_next_line(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *ptr; + int nchars = c_hpos(el); + + /* + * Move to the line requested + */ + for (ptr = el->el_line.cursor; ptr < el->el_line.lastchar; ptr++) + if (*ptr == '\n' && --el->el_state.argument <= 0) + break; + + if (el->el_state.argument > 0) + return CC_ERROR; + + /* + * Move to the character requested + */ + for (ptr++; + nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; + ptr++) + continue; + + el->el_line.cursor = ptr; + return CC_CURSOR; +} + + +/* ed_command(): + * Editline extended command + * [M-X] [:] + */ +protected el_action_t +/*ARGSUSED*/ +ed_command(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char tmpbuf[EL_BUFSIZ]; + int tmplen; + + tmplen = c_gets(el, tmpbuf, STR("\n: ")); + terminal__putc(el, '\n'); + + if (tmplen < 0 || (tmpbuf[tmplen] = 0, parse_line(el, tmpbuf)) == -1) + terminal_beep(el); + + el->el_map.current = el->el_map.key; + re_clear_display(el); + return CC_REFRESH; +} diff --git a/lib/libedit/common.h b/lib/libedit/common.h deleted file mode 100644 index 35c61eb..0000000 --- a/lib/libedit/common.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Automatically generated file, do not edit */ -#ifndef _h_common_c -#define _h_common_c -protected el_action_t ed_end_of_file (EditLine *, int); -protected el_action_t ed_insert (EditLine *, int); -protected el_action_t ed_delete_prev_word (EditLine *, int); -protected el_action_t ed_delete_next_char (EditLine *, int); -protected el_action_t ed_kill_line (EditLine *, int); -protected el_action_t ed_move_to_end (EditLine *, int); -protected el_action_t ed_move_to_beg (EditLine *, int); -protected el_action_t ed_transpose_chars (EditLine *, int); -protected el_action_t ed_next_char (EditLine *, int); -protected el_action_t ed_prev_word (EditLine *, int); -protected el_action_t ed_prev_char (EditLine *, int); -protected el_action_t ed_quoted_insert (EditLine *, int); -protected el_action_t ed_digit (EditLine *, int); -protected el_action_t ed_argument_digit (EditLine *, int); -protected el_action_t ed_unassigned (EditLine *, int); -protected el_action_t ed_tty_sigint (EditLine *, int); -protected el_action_t ed_tty_dsusp (EditLine *, int); -protected el_action_t ed_tty_flush_output (EditLine *, int); -protected el_action_t ed_tty_sigquit (EditLine *, int); -protected el_action_t ed_tty_sigtstp (EditLine *, int); -protected el_action_t ed_tty_stop_output (EditLine *, int); -protected el_action_t ed_tty_start_output (EditLine *, int); -protected el_action_t ed_newline (EditLine *, int); -protected el_action_t ed_delete_prev_char (EditLine *, int); -protected el_action_t ed_clear_screen (EditLine *, int); -protected el_action_t ed_redisplay (EditLine *, int); -protected el_action_t ed_start_over (EditLine *, int); -protected el_action_t ed_sequence_lead_in (EditLine *, int); -protected el_action_t ed_prev_history (EditLine *, int); -protected el_action_t ed_next_history (EditLine *, int); -protected el_action_t ed_search_prev_history (EditLine *, int); -protected el_action_t ed_search_next_history (EditLine *, int); -protected el_action_t ed_prev_line (EditLine *, int); -protected el_action_t ed_next_line (EditLine *, int); -protected el_action_t ed_command (EditLine *, int); -#endif /* _h_common_c */ diff --git a/lib/libedit/config.h b/lib/libedit/config.h new file mode 100644 index 0000000..49f085d --- /dev/null +++ b/lib/libedit/config.h @@ -0,0 +1,290 @@ +/* $FreeBSD: releng/11.1/lib/libedit/config.h 313981 2017-02-20 03:33:59Z pfg $ */ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if the `closedir' function returns void instead of `int'. */ +/* #undef CLOSEDIR_VOID */ + +/* Define to 1 if you have the header file. */ +#define HAVE_CURSES_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `endpwent' function. */ +#define HAVE_ENDPWENT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getline' function. */ +#define HAVE_GETLINE 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have getpwnam_r and getpwuid_r that are draft POSIX.1 + versions. */ +/* #undef HAVE_GETPW_R_DRAFT */ + +/* Define to 1 if you have getpwnam_r and getpwuid_r that are POSIX.1 + compatible. */ +#define HAVE_GETPW_R_POSIX 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `isascii' function. */ +#define HAVE_ISASCII 1 + +/* Define to 1 if you have the `issetugid' function. */ +#define HAVE_ISSETUGID 1 + +/* Define to 1 if you have the `curses' library (-lcurses). */ +/* #undef HAVE_LIBCURSES */ + +/* Define to 1 if you have the `ncurses' library (-lncurses). */ +#define HAVE_LIBNCURSES 1 + +/* Define to 1 if you have the `termcap' library (-ltermcap). */ +/* #undef HAVE_LIBTERMCAP */ + +/* Define to 1 if you have the `terminfo' library (-lterminfo). */ +/* #undef HAVE_LIBTERMINFO */ + +/* Define to 1 if you have the `termlib' library (-ltermlib). */ +/* #undef HAVE_LIBTERMLIB */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the `memchr' function. */ +#define HAVE_MEMCHR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NCURSES_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the `regcomp' function. */ +#define HAVE_REGCOMP 1 + +/* Define to 1 if you have the `re_comp' function. */ +/* #undef HAVE_RE_COMP */ + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcat' function. */ +#define HAVE_STRLCAT 1 + +/* Define to 1 if you have the `strlcpy' function. */ +#define HAVE_STRLCPY 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if struct dirent has member d_namlen */ +#define HAVE_STRUCT_DIRENT_D_NAMLEN 1 + +/* Define to 1 if you have the `strunvis' function. */ +#define HAVE_STRUNVIS 1 + +/* Define to 1 if you have the `strvis' function. */ +#define HAVE_STRVIS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERMCAP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the type `u_int32_t'. */ +#define HAVE_U_INT32_T 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if you have the `vis' function. */ +#define HAVE_VIS 1 + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libedit-20110729" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libedit" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libedit 3.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libedit-20110729" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "3.0" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "3.0" + +/* Define to 1 if the system provides the SIZE_MAX constant */ +#define HAVE_SIZE_MAX 1 + +/* Define to 1 if you want wide-character code */ +/* #undef WIDECHAR */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define as `fork' if `vfork' does not work. */ +/* #undef vfork */ + + +#include "sys.h" +#undef SCCSID +#undef LIBC_SCCS +#undef lint +#undef SIZE_T_MAX diff --git a/lib/libedit/edit/readline/Makefile b/lib/libedit/edit/readline/Makefile new file mode 100644 index 0000000..ecdb9bd --- /dev/null +++ b/lib/libedit/edit/readline/Makefile @@ -0,0 +1,8 @@ +# Copyright (c) 2011 David E O'Brien +# $FreeBSD: releng/11.1/lib/libedit/edit/readline/Makefile 234690 2012-04-26 00:51:43Z obrien $ + +INCS= readline.h history.h tilde.h + +INCSDIR= ${INCLUDEDIR}/edit/readline + +.include diff --git a/lib/libedit/edit/readline/Makefile.depend b/lib/libedit/edit/readline/Makefile.depend new file mode 100644 index 0000000..06533ba --- /dev/null +++ b/lib/libedit/edit/readline/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: releng/11.1/lib/libedit/edit/readline/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/libedit/edit/readline/history.h b/lib/libedit/edit/readline/history.h new file mode 100644 index 0000000..2bcaf92 --- /dev/null +++ b/lib/libedit/edit/readline/history.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2011 David E. O'Brien + * 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 author nor the names of 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: releng/11.1/lib/libedit/edit/readline/history.h 220370 2011-04-05 18:41:01Z obrien $ + */ + +#include diff --git a/lib/libedit/edit/readline/readline.h b/lib/libedit/edit/readline/readline.h new file mode 100644 index 0000000..2735e36 --- /dev/null +++ b/lib/libedit/edit/readline/readline.h @@ -0,0 +1,227 @@ +/* $NetBSD: readline.h,v 1.39 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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: releng/11.1/lib/libedit/edit/readline/readline.h 313981 2017-02-20 03:33:59Z pfg $ + */ +#ifndef _READLINE_H_ +#define _READLINE_H_ + +#include +#include + +/* list of readline stuff supported by editline library's readline wrapper */ + +/* typedefs */ +typedef int Function(const char *, int); +typedef void VFunction(void); +typedef void rl_vcpfunc_t(char *); +typedef char **rl_completion_func_t(const char *, int, int); +typedef char *rl_compentry_func_t(const char *, int); +typedef int rl_command_func_t(int, int); + +/* only supports length */ +typedef struct { + int length; +} HISTORY_STATE; + +typedef void *histdata_t; + +typedef struct _hist_entry { + const char *line; + histdata_t data; +} HIST_ENTRY; + +typedef struct _keymap_entry { + char type; +#define ISFUNC 0 +#define ISKMAP 1 +#define ISMACR 2 + Function *function; +} KEYMAP_ENTRY; + +#define KEYMAP_SIZE 256 + +typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[KEYMAP_SIZE]; +typedef KEYMAP_ENTRY *Keymap; + +#define control_character_threshold 0x20 +#define control_character_bit 0x40 + +#ifndef CTRL +#include +#if !defined(__sun) && !defined(__hpux) && !defined(_AIX) +#include +#endif +#ifndef CTRL +#define CTRL(c) ((c) & 037) +#endif +#endif +#ifndef UNCTRL +#define UNCTRL(c) (((c) - 'a' + 'A')|control_character_bit) +#endif + +#define RUBOUT 0x7f +#define ABORT_CHAR CTRL('G') +#define RL_READLINE_VERSION 0x0402 +#define RL_PROMPT_START_IGNORE '\1' +#define RL_PROMPT_END_IGNORE '\2' + +/* global variables used by readline enabled applications */ +#ifdef __cplusplus +extern "C" { +#endif +extern const char *rl_library_version; +extern int rl_readline_version; +extern char *rl_readline_name; +extern FILE *rl_instream; +extern FILE *rl_outstream; +extern char *rl_line_buffer; +extern int rl_point, rl_end; +extern int history_base, history_length; +extern int max_input_history; +extern char *rl_basic_word_break_characters; +extern char *rl_completer_word_break_characters; +extern char *rl_completer_quote_characters; +extern rl_compentry_func_t *rl_completion_entry_function; +extern char *(*rl_completion_word_break_hook)(void); +extern rl_completion_func_t *rl_attempted_completion_function; +extern int rl_attempted_completion_over; +extern int rl_completion_type; +extern int rl_completion_query_items; +extern char *rl_special_prefixes; +extern int rl_completion_append_character; +extern int rl_inhibit_completion; +extern Function *rl_pre_input_hook; +extern Function *rl_startup_hook; +extern char *rl_terminal_name; +extern int rl_already_prompted; +extern char *rl_prompt; +/* + * The following is not implemented + */ +extern int rl_catch_signals; +extern int rl_catch_sigwinch; +extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, + emacs_meta_keymap, + emacs_ctlx_keymap; +extern int rl_filename_completion_desired; +extern int rl_ignore_completion_duplicates; +extern int (*rl_getc_function)(FILE *); +extern VFunction *rl_redisplay_function; +extern VFunction *rl_completion_display_matches_hook; +extern VFunction *rl_prep_term_function; +extern VFunction *rl_deprep_term_function; +extern int readline_echoing_p; +extern int _rl_print_completions_horizontally; + +/* supported functions */ +char *readline(const char *); +int rl_initialize(void); + +void using_history(void); +int add_history(const char *); +void clear_history(void); +void stifle_history(int); +int unstifle_history(void); +int history_is_stifled(void); +int where_history(void); +HIST_ENTRY *current_history(void); +HIST_ENTRY *history_get(int); +HIST_ENTRY *remove_history(int); +HIST_ENTRY *replace_history_entry(int, const char *, histdata_t); +int history_total_bytes(void); +int history_set_pos(int); +HIST_ENTRY *previous_history(void); +HIST_ENTRY *next_history(void); +int history_search(const char *, int); +int history_search_prefix(const char *, int); +int history_search_pos(const char *, int, int); +int read_history(const char *); +int write_history(const char *); +int history_truncate_file (const char *, int); +int history_expand(char *, char **); +char **history_tokenize(const char *); +const char *get_history_event(const char *, int *, int); +char *history_arg_extract(int, int, const char *); + +char *tilde_expand(char *); +char *filename_completion_function(const char *, int); +char *username_completion_function(const char *, int); +int rl_complete(int, int); +int rl_read_key(void); +char **completion_matches(const char *, rl_compentry_func_t *); +void rl_display_match_list(char **, int, int); + +int rl_insert(int, int); +int rl_insert_text(const char *); +void rl_reset_terminal(const char *); +int rl_bind_key(int, rl_command_func_t *); +int rl_newline(int, int); +void rl_callback_read_char(void); +void rl_callback_handler_install(const char *, rl_vcpfunc_t *); +void rl_callback_handler_remove(void); +void rl_redisplay(void); +int rl_get_previous_history(int, int); +void rl_prep_terminal(int); +void rl_deprep_terminal(void); +int rl_read_init_file(const char *); +int rl_parse_and_bind(const char *); +int rl_variable_bind(const char *, const char *); +void rl_stuff_char(int); +int rl_add_defun(const char *, rl_command_func_t *, int); +HISTORY_STATE *history_get_history_state(void); +void rl_get_screen_size(int *, int *); +void rl_set_screen_size(int, int); +char *rl_filename_completion_function (const char *, int); +int _rl_abort_internal(void); +int _rl_qsort_string_compare(char **, char **); +char **rl_completion_matches(const char *, rl_compentry_func_t *); +void rl_forced_update_display(void); +int rl_set_prompt(const char *); +int rl_on_new_line(void); + +/* + * The following are not implemented + */ +int rl_kill_text(int, int); +Keymap rl_get_keymap(void); +void rl_set_keymap(Keymap); +Keymap rl_make_bare_keymap(void); +int rl_generic_bind(int, const char *, const char *, Keymap); +int rl_bind_key_in_map(int, rl_command_func_t *, Keymap); +void rl_cleanup_after_signal(void); +void rl_free_line_state(void); +int rl_set_keyboard_input_timeout(int); + +#ifdef __cplusplus +} +#endif + +#endif /* _READLINE_H_ */ diff --git a/lib/libedit/edit/readline/tilde.h b/lib/libedit/edit/readline/tilde.h new file mode 100644 index 0000000..effef4c --- /dev/null +++ b/lib/libedit/edit/readline/tilde.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2011 David E. O'Brien + * 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 author nor the names of 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: releng/11.1/lib/libedit/edit/readline/tilde.h 234690 2012-04-26 00:51:43Z obrien $ + */ + +#include diff --git a/lib/libedit/editline.3 b/lib/libedit/editline.3 new file mode 100644 index 0000000..d5ffd50 --- /dev/null +++ b/lib/libedit/editline.3 @@ -0,0 +1,977 @@ +.\" $NetBSD: editline.3,v 1.88 2016/02/25 14:59:22 wiz Exp $ +.\" +.\" Copyright (c) 1997-2014 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This file was contributed to The NetBSD Foundation by Luke Mewburn. +.\" +.\" 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: releng/11.1/lib/libedit/editline.3 313981 2017-02-20 03:33:59Z pfg $ +.\" +.Dd February 24, 2016 +.Dt EDITLINE 3 +.Os +.Sh NAME +.Nm editline , +.Nm el_init , +.Nm el_init_fd , +.Nm el_end , +.Nm el_reset , +.Nm el_gets , +.Nm el_wgets , +.Nm el_getc , +.Nm el_wgetc , +.Nm el_push , +.Nm el_wpush , +.Nm el_parse , +.Nm el_wparse , +.Nm el_set , +.Nm el_wset , +.Nm el_get , +.Nm el_wget , +.Nm el_source , +.Nm el_resize , +.Nm el_cursor , +.Nm el_line , +.Nm el_wline , +.Nm el_insertstr , +.Nm el_winsertstr , +.Nm el_deletestr , +.Nm el_wdeletestr , +.Nm history_init , +.Nm history_winit , +.Nm history_end , +.Nm history_wend , +.Nm history , +.Nm history_w , +.Nm tok_init , +.Nm tok_winit , +.Nm tok_end , +.Nm tok_wend , +.Nm tok_reset , +.Nm tok_wreset , +.Nm tok_line , +.Nm tok_wline , +.Nm tok_str , +.Nm tok_wstr +.Nd line editor, history and tokenization functions +.Sh LIBRARY +.Lb libedit +.Sh SYNOPSIS +.In histedit.h +.Ft EditLine * +.Fn el_init "const char *prog" "FILE *fin" "FILE *fout" "FILE *ferr" +.Ft EditLine * +.Fn el_init_fd "const char *prog" "FILE *fin" "FILE *fout" "FILE *ferr" "int fdin" "int fdout" "int fderr" +.Ft void +.Fn el_end "EditLine *e" +.Ft void +.Fn el_reset "EditLine *e" +.Ft const char * +.Fn el_gets "EditLine *e" "int *count" +.Ft const wchar_t * +.Fn el_wgets "EditLine *e" "int *count" +.Ft int +.Fn el_getc "EditLine *e" "char *ch" +.Ft int +.Fn el_wgetc "EditLine *e" "wchar_t *ch" +.Ft void +.Fn el_push "EditLine *e" "const char *str" +.Ft void +.Fn el_wpush "EditLine *e" "const wchar_t *str" +.Ft int +.Fn el_parse "EditLine *e" "int argc" "const char *argv[]" +.Ft int +.Fn el_wparse "EditLine *e" "int argc" "const wchar_t *argv[]" +.Ft int +.Fn el_set "EditLine *e" "int op" "..." +.Ft int +.Fn el_wset "EditLine *e" "int op" "..." +.Ft int +.Fn el_get "EditLine *e" "int op" "..." +.Ft int +.Fn el_wget "EditLine *e" "int op" "..." +.Ft int +.Fn el_source "EditLine *e" "const char *file" +.Ft void +.Fn el_resize "EditLine *e" +.Fn int +.Fn el_cursor "EditLine *e" "int count" +.Ft const LineInfo * +.Fn el_line "EditLine *e" +.Ft const LineInfoW * +.Fn el_wline "EditLine *e" +.Ft int +.Fn el_insertstr "EditLine *e" "const char *str" +.Ft int +.Fn el_winsertstr "EditLine *e" "const wchar_t *str" +.Ft void +.Fn el_deletestr "EditLine *e" "int count" +.Ft void +.Fn el_wdeletestr "EditLine *e" "int count" +.Ft History * +.Fn history_init void +.Ft HistoryW * +.Fn history_winit void +.Ft void +.Fn history_end "History *h" +.Ft void +.Fn history_wend "HistoryW *h" +.Ft int +.Fn history "History *h" "HistEvent *ev" "int op" "..." +.Ft int +.Fn history_w "HistoryW *h" "HistEventW *ev" "int op" "..." +.Ft Tokenizer * +.Fn tok_init "const char *IFS" +.Ft TokenizerW * +.Fn tok_winit "const wchar_t *IFS" +.Ft void +.Fn tok_end "Tokenizer *t" +.Ft void +.Fn tok_wend "TokenizerW *t" +.Ft void +.Fn tok_reset "Tokenizer *t" +.Ft void +.Fn tok_wreset "TokenizerW *t" +.Ft int +.Fn tok_line "Tokenizer *t" "const LineInfo *li" "int *argc" "const char **argv[]" "int *cursorc" "int *cursoro" +.Ft int +.Fn tok_wline "TokenizerW *t" "const LineInfoW *li" "int *argc" "const wchar_t **argv[]" "int *cursorc" "int *cursoro" +.Ft int +.Fn tok_str "Tokenizer *t" "const char *str" "int *argc" "const char **argv[]" +.Ft int +.Fn tok_wstr "TokenizerW *t" "const wchar_t *str" "int *argc" "const wchar_t **argv[]" +.Sh DESCRIPTION +The +.Nm +library provides generic line editing, history and tokenization functions, +similar to those found in +.Xr sh 1 . +.Pp +These functions are available in the +.Nm libedit +library (which needs the +.Nm libtermcap +library). +Programs should be linked with +.Fl ledit ltermcap . +.Pp +The +.Nm +library respects the +.Ev LC_CTYPE +locale set by the application program and never uses +.Xr setlocale 3 +to change the locale. +The only locales supported are UTF-8 and the default C or POSIX locale. +If any other locale is set, behaviour is undefined. +.Sh LINE EDITING FUNCTIONS +The line editing functions use a common data structure, +.Fa EditLine , +which is created by +.Fn el_init +or +.Fn el_init_fd +and freed by +.Fn el_end . +.Pp +The wide-character functions behave the same way as their narrow +counterparts. +.Pp +The following functions are available: +.Bl -tag -width 4n +.It Fn el_init +Initialize the line editor, and return a data structure +to be used by all other line editing functions, or +.Dv NULL +on failure. +.Fa prog +is the name of the invoking program, used when reading the +.Xr editrc 5 +file to determine which settings to use. +.Fa fin , +.Fa fout +and +.Fa ferr +are the input, output, and error streams (respectively) to use. +In this documentation, references to +.Dq the tty +are actually to this input/output stream combination. +.It Fn el_init_fd +Like +.Fn el_init +but allows specifying file descriptors for the +.Xr stdio 3 +corresponding streams, in case those were created with +.Xr funopen 3 . +.It Fn el_end +Clean up and finish with +.Fa e , +assumed to have been created with +.Fn el_init +or +.Fn el_init_fd . +.It Fn el_reset +Reset the tty and the parser. +This should be called after an error which may have upset the tty's +state. +.It Fn el_gets +Read a line from the tty. +.Fa count +is modified to contain the number of characters read. +Returns the line read if successful, or +.Dv NULL +if no characters were read or if an error occurred. +If an error occurred, +.Fa count +is set to \-1 and +.Dv errno +contains the error code that caused it. +The return value may not remain valid across calls to +.Fn el_gets +and must be copied if the data is to be retained. +.It Fn el_wgetc +Read a wide character from the tty, respecting the current locale, +or from the input stream written by +.Fn el_wpush +and +.Fn el_push +if that is not empty, and store it in +.Fa ch . +If an invalid or incomplete character is found, it is discarded, +.Va errno +is set to +.Er EILSEQ , +and the next character is read and stored in +.Fa ch . +Returns 1 if a valid character was read, 0 on end of file, or \-1 on +.Xr read 2 +failure. +In the latter case, +.Va errno +is set to indicate the error. +.It Fn el_getc +Read a wide character as described for +.Fn el_wgetc +and return 0 on end of file or \-1 on failure. +If the wide character can be represented as a single-byte character, +convert it with +.Xr wctob 3 , +store the result in +.Fa ch , +and return 1; otherwise, set +.Va errno +to +.Er ERANGE +and return \-1. +In the C or POSIX locale, this simply reads a byte, but for any other +locale, including UTF-8, this is rarely useful. +.It Fn el_push +Pushes +.Fa str +back onto the input stream. +This is used by the macro expansion mechanism. +Refer to the description of +.Ic bind +.Fl s +in +.Xr editrc 5 +for more information. +.It Fn el_parse +Parses the +.Fa argv +array (which is +.Fa argc +elements in size) +to execute builtin +.Nm +commands. +If the command is prefixed with +.Dq prog : +then +.Fn el_parse +will only execute the command if +.Dq prog +matches the +.Fa prog +argument supplied to +.Fn el_init . +The return value is +\-1 if the command is unknown, +0 if there was no error or +.Dq prog +didn't match, or +1 if the command returned an error. +Refer to +.Xr editrc 5 +for more information. +.It Fn el_set +Set +.Nm +parameters. +.Fa op +determines which parameter to set, and each operation has its +own parameter list. +Returns 0 on success, \-1 on failure. +.Pp +The following values for +.Fa op +are supported, along with the required argument list: +.Bl -tag -width 4n +.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)" +Define prompt printing function as +.Fa f , +which is to return a string that contains the prompt. +.It Dv EL_PROMPT_ESC , Fa "char *(*f)(EditLine *)" , Fa "char c" +Same as +.Dv EL_PROMPT , +but the +.Fa c +argument indicates the start/stop literal prompt character. +.Pp +If a start/stop literal character is found in the prompt, the +character itself +is not printed, but characters after it are printed directly to the +terminal without affecting the state of the current line. +A subsequent second start/stop literal character ends this behavior. +This is typically used to embed literal escape sequences that change the +color/style of the terminal in the prompt. +.Dv 0 +unsets it. +.It Dv EL_REFRESH +Re-display the current line on the next terminal line. +.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)" +Define right side prompt printing function as +.Fa f , +which is to return a string that contains the prompt. +.It Dv EL_RPROMPT_ESC , Fa "char *(*f)(EditLine *)" , Fa "char c" +Define the right prompt printing function but with a literal escape character. +.It Dv EL_TERMINAL , Fa "const char *type" +Define terminal type of the tty to be +.Fa type , +or to +.Ev TERM +if +.Fa type +is +.Dv NULL . +.It Dv EL_EDITOR , Fa "const char *mode" +Set editing mode to +.Fa mode , +which must be one of +.Dq emacs +or +.Dq vi . +.It Dv EL_SIGNAL , Fa "int flag" +If +.Fa flag +is non-zero, +.Nm +will install its own signal handler for the following signals when +reading command input: +.Dv SIGCONT , +.Dv SIGHUP , +.Dv SIGINT , +.Dv SIGQUIT , +.Dv SIGSTOP , +.Dv SIGTERM , +.Dv SIGTSTP , +and +.Dv SIGWINCH . +Otherwise, the current signal handlers will be used. +.It Dv EL_BIND , Fa "const char *" , Fa "..." , Dv NULL +Perform the +.Ic bind +builtin command. +Refer to +.Xr editrc 5 +for more information. +.It Dv EL_ECHOTC , Fa "const char *" , Fa "..." , Dv NULL +Perform the +.Ic echotc +builtin command. +Refer to +.Xr editrc 5 +for more information. +.It Dv EL_SETTC , Fa "const char *" , Fa "..." , Dv NULL +Perform the +.Ic settc +builtin command. +Refer to +.Xr editrc 5 +for more information. +.It Dv EL_SETTY , Fa "const char *" , Fa "..." , Dv NULL +Perform the +.Ic setty +builtin command. +Refer to +.Xr editrc 5 +for more information. +.It Dv EL_TELLTC , Fa "const char *" , Fa "..." , Dv NULL +Perform the +.Ic telltc +builtin command. +Refer to +.Xr editrc 5 +for more information. +.It Dv EL_ADDFN , Fa "const char *name" , Fa "const char *help" , \ +Fa "unsigned char (*func)(EditLine *e, int ch)" +Add a user defined function, +.Fn func , +referred to as +.Fa name +which is invoked when a key which is bound to +.Fa name +is entered. +.Fa help +is a description of +.Fa name . +At invocation time, +.Fa ch +is the key which caused the invocation. +The return value of +.Fn func +should be one of: +.Bl -tag -width "CC_REDISPLAY" +.It Dv CC_NORM +Add a normal character. +.It Dv CC_NEWLINE +End of line was entered. +.It Dv CC_EOF +EOF was entered. +.It Dv CC_ARGHACK +Expecting further command input as arguments, do nothing visually. +.It Dv CC_REFRESH +Refresh display. +.It Dv CC_REFRESH_BEEP +Refresh display, and beep. +.It Dv CC_CURSOR +Cursor moved, so update and perform +.Dv CC_REFRESH . +.It Dv CC_REDISPLAY +Redisplay entire input line. +This is useful if a key binding outputs extra information. +.It Dv CC_ERROR +An error occurred. +Beep, and flush tty. +.It Dv CC_FATAL +Fatal error, reset tty to known state. +.El +.It Dv EL_HIST , Fa "History *(*func)(History *, int op, ...)" , \ +Fa "const char *ptr" +Defines which history function to use, which is usually +.Fn history . +.Fa ptr +should be the value returned by +.Fn history_init . +.It Dv EL_EDITMODE , Fa "int flag" +If +.Fa flag +is non-zero, +editing is enabled (the default). +Note that this is only an indication, and does not +affect the operation of +.Nm . +At this time, it is the caller's responsibility to +check this +(using +.Fn el_get ) +to determine if editing should be enabled or not. +.It Dv EL_UNBUFFERED , Fa "int flag" +If +.Fa flag +is zero, +unbuffered mode is disabled (the default). +In unbuffered mode, +.Fn el_gets +will return immediately after processing a single character. +.It Dv EL_GETCFN , Fa "int (*f)(EditLine *, char *c)" +Define the character reading function as +.Fa f , +which is to return the number of characters read and store them in +.Fa c . +This function is called internally by +.Fn el_gets +and +.Fn el_getc . +The builtin function can be set or restored with the special function +name +.Dq Dv EL_BUILTIN_GETCFN . +.It Dv EL_CLIENTDATA , Fa "void *data" +Register +.Fa data +to be associated with this EditLine structure. +It can be retrieved with the corresponding +.Fn el_get +call. +.It Dv EL_SETFP , Fa "int fd" , Fa "FILE *fp" +Set the current +.Nm editline +file pointer for +.Dq input +.Fa fd += +.Dv 0 , +.Dq output +.Fa fd += +.Dv 1 , +or +.Dq error +.Fa fd += +.Dv 2 +from +.Fa fp . +.El +.It Fn el_get +Get +.Nm +parameters. +.Fa op +determines which parameter to retrieve into +.Fa result . +Returns 0 if successful, \-1 otherwise. +.Pp +The following values for +.Fa op +are supported, along with actual type of +.Fa result : +.Bl -tag -width 4n +.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)" , Fa "char *c" +Set +.Fa f . +to a pointer to the function that displays the prompt. +If +.Fa c +is not +.Dv NULL , +set it to the start/stop literal prompt character. +.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)" , Fa "char *c" +Set +.Fa f . +to a pointer to the function that displays the prompt. +If +.Fa c +is not +.Dv NULL , +set it to the start/stop literal prompt character. +.It Dv EL_EDITOR , Fa "const char **n" +Set the name of the editor in +.Fa n , +which will be one of +.Dq emacs +or +.Dq vi . +.It Dv EL_GETTC , Fa "const char *name" , Fa "void *value" +If +.Fa name +is a valid +.Xr termcap 5 +capability set +.Fa value +to the current value of that capability. +.It Dv EL_SIGNAL , Fa "int *s" +Set +.Fa s +to non zero if +.Nm +has installed private signal handlers (see +.Fn el_get +above). +.It Dv EL_EDITMODE , Fa "int *c" +Set +.Fa c +to non-zero if editing is enabled. +.It Dv EL_GETCFN , Fa "int (**f)(EditLine *, char *)" +Return a pointer to the function that read characters, which is equal to +.Dq Dv EL_BUILTIN_GETCFN +in the case of the default builtin function. +.It Dv EL_CLIENTDATA , Fa "void **data" +Set +.Fa data +to the previously registered client data set by an +.Fn el_set +call. +.It Dv EL_UNBUFFERED , Fa "int *c" +Set +.Fa c +to non-zero if unbuffered mode is enabled. +.It Dv EL_GETFP , Fa "int fd", Fa "FILE **fp" +Set +.Fa fp +to the current +.Nm editline +file pointer for +.Dq input +.Fa fd += +.Dv 0 , +.Dq output +.Fa fd += +.Dv 1 , +or +.Dq error +.Fa fd += +.Dv 2 . +.El +.It Fn el_source +Initialize +.Nm +by reading the contents of +.Fa file . +.Fn el_parse +is called for each line in +.Fa file . +If +.Fa file +is +.Dv NULL , +try +.Pa $HOME/.editrc . +Refer to +.Xr editrc 5 +for details on the format of +.Fa file . +.Fn el_source +returns 0 on success and \-1 on error. +.It Fn el_resize +Must be called if the terminal size changes. +If +.Dv EL_SIGNAL +has been set with +.Fn el_set , +then this is done automatically. +Otherwise, it's the responsibility of the application to call +.Fn el_resize +on the appropriate occasions. +.It Fn el_cursor +Move the cursor to the right (if positive) or to the left (if negative) +.Fa count +characters. +Returns the resulting offset of the cursor from the beginning of the line. +.It Fn el_line +Return the editing information for the current line in a +.Fa LineInfo +structure, which is defined as follows: +.Bd -literal +typedef struct lineinfo { + const char *buffer; /* address of buffer */ + const char *cursor; /* address of cursor */ + const char *lastchar; /* address of last character */ +} LineInfo; +.Ed +.Pp +.Fa buffer +is not NUL terminated. +This function may be called after +.Fn el_gets +to obtain the +.Fa LineInfo +structure pertaining to line returned by that function, +and from within user defined functions added with +.Dv EL_ADDFN . +.It Fn el_insertstr +Insert +.Fa str +into the line at the cursor. +Returns \-1 if +.Fa str +is empty or won't fit, and 0 otherwise. +.It Fn el_deletestr +Delete +.Fa count +characters before the cursor. +.El +.Sh HISTORY LIST FUNCTIONS +The history functions use a common data structure, +.Fa History , +which is created by +.Fn history_init +and freed by +.Fn history_end . +.Pp +The following functions are available: +.Bl -tag -width 4n +.It Fn history_init +Initialize the history list, and return a data structure +to be used by all other history list functions, or +.Dv NULL +on failure. +.It Fn history_end +Clean up and finish with +.Fa h , +assumed to have been created with +.Fn history_init . +.It Fn history +Perform operation +.Fa op +on the history list, with optional arguments as needed by the +operation. +.Fa ev +is changed accordingly to operation. +The following values for +.Fa op +are supported, along with the required argument list: +.Bl -tag -width 4n +.It Dv H_SETSIZE , Fa "int size" +Set size of history to +.Fa size +elements. +.It Dv H_GETSIZE +Get number of events currently in history. +.It Dv H_END +Cleans up and finishes with +.Fa h , +assumed to be created with +.Fn history_init . +.It Dv H_CLEAR +Clear the history. +.It Dv H_FUNC , Fa "void *ptr" , Fa "history_gfun_t first" , \ +Fa "history_gfun_t next" , Fa "history_gfun_t last" , \ +Fa "history_gfun_t prev" , Fa "history_gfun_t curr" , \ +Fa "history_sfun_t set" , Fa "history_vfun_t clear" , \ +Fa "history_efun_t enter" , Fa "history_efun_t add" +Define functions to perform various history operations. +.Fa ptr +is the argument given to a function when it's invoked. +.It Dv H_FIRST +Return the first element in the history. +.It Dv H_LAST +Return the last element in the history. +.It Dv H_PREV +Return the previous element in the history. +.It Dv H_NEXT +Return the next element in the history. +.It Dv H_CURR +Return the current element in the history. +.It Dv H_SET +Set the cursor to point to the requested element. +.It Dv H_ADD , Fa "const char *str" +Append +.Fa str +to the current element of the history, or perform the +.Dv H_ENTER +operation with argument +.Fa str +if there is no current element. +.It Dv H_APPEND , Fa "const char *str" +Append +.Fa str +to the last new element of the history. +.It Dv H_ENTER , Fa "const char *str" +Add +.Fa str +as a new element to the history and, if necessary, +removing the oldest entry to keep the list to the created size. +If +.Dv H_SETUNIQUE +has been called with a non-zero argument, the element +will not be entered into the history if its contents match +the ones of the current history element. +If the element is entered +.Fn history +returns 1; if it is ignored as a duplicate returns 0. +Finally +.Fn history +returns \-1 if an error occurred. +.It Dv H_PREV_STR , Fa "const char *str" +Return the closest previous event that starts with +.Fa str . +.It Dv H_NEXT_STR , Fa "const char *str" +Return the closest next event that starts with +.Fa str . +.It Dv H_PREV_EVENT , Fa "int e" +Return the previous event numbered +.Fa e . +.It Dv H_NEXT_EVENT , Fa "int e" +Return the next event numbered +.Fa e . +.It Dv H_LOAD , Fa "const char *file" +Load the history list stored in +.Fa file . +.It Dv H_SAVE , Fa "const char *file" +Save the history list to +.Fa file . +.It Dv H_SAVE_FP , Fa "FILE *fp" +Save the history list to the opened +.Ft FILE +pointer +.Fa fp . +.It Dv H_SETUNIQUE , Fa "int unique" +Set flag that adjacent identical event strings should not be entered +into the history. +.It Dv H_GETUNIQUE +Retrieve the current setting if adjacent identical elements should +be entered into the history. +.It Dv H_DEL , Fa "int e" +Delete the event numbered +.Fa e . +This function is only provided for +.Xr readline 3 +compatibility. +The caller is responsible for free'ing the string in the returned +.Fa HistEvent . +.El +.Pp +.Fn history +returns \*[Gt]= 0 if the operation +.Fa op +succeeds. +Otherwise, \-1 is returned and +.Fa ev +is updated to contain more details about the error. +.El +.Sh TOKENIZATION FUNCTIONS +The tokenization functions use a common data structure, +.Fa Tokenizer , +which is created by +.Fn tok_init +and freed by +.Fn tok_end . +.Pp +The following functions are available: +.Bl -tag -width 4n +.It Fn tok_init +Initialize the tokenizer, and return a data structure +to be used by all other tokenizer functions. +.Fa IFS +contains the Input Field Separators, which defaults to +.Aq space , +.Aq tab , +and +.Aq newline +if +.Dv NULL . +.It Fn tok_end +Clean up and finish with +.Fa t , +assumed to have been created with +.Fn tok_init . +.It Fn tok_reset +Reset the tokenizer state. +Use after a line has been successfully tokenized +by +.Fn tok_line +or +.Fn tok_str +and before a new line is to be tokenized. +.It Fn tok_line +Tokenize +.Fa li , +If successful, modify: +.Fa argv +to contain the words, +.Fa argc +to contain the number of words, +.Fa cursorc +(if not +.Dv NULL ) +to contain the index of the word containing the cursor, +and +.Fa cursoro +(if not +.Dv NULL ) +to contain the offset within +.Fa argv[cursorc] +of the cursor. +.Pp +Returns +0 if successful, +\-1 for an internal error, +1 for an unmatched single quote, +2 for an unmatched double quote, +and +3 for a backslash quoted +.Aq newline . +A positive exit code indicates that another line should be read +and tokenization attempted again. +. +.It Fn tok_str +A simpler form of +.Fn tok_line ; +.Fa str +is a NUL terminated string to tokenize. +.El +. +.\"XXX.Sh EXAMPLES +.\"XXX: provide some examples +.Sh SEE ALSO +.Xr sh 1 , +.Xr signal 3 , +.Xr termcap 3 , +.Xr editrc 5 , +.Xr termcap 5 +.Sh HISTORY +The +.Nm +library first appeared in +.Bx 4.4 . +.Dv CC_REDISPLAY +appeared in +.Nx 1.3 . +.Dv CC_REFRESH_BEEP , +.Dv EL_EDITMODE +and the readline emulation appeared in +.Nx 1.4 . +.Dv EL_RPROMPT +appeared in +.Nx 1.5 . +.Sh AUTHORS +.An -nosplit +The +.Nm +library was written by +.An Christos Zoulas . +.An Luke Mewburn +wrote this manual and implemented +.Dv CC_REDISPLAY , +.Dv CC_REFRESH_BEEP , +.Dv EL_EDITMODE , +and +.Dv EL_RPROMPT . +.An Jaromir Dolecek +implemented the readline emulation. +.An Johny Mattsson +implemented wide-character support. +.Sh BUGS +At this time, it is the responsibility of the caller to +check the result of the +.Dv EL_EDITMODE +operation of +.Fn el_get +(after an +.Fn el_source +or +.Fn el_parse ) +to determine if +.Nm +should be used for further input. +I.e., +.Dv EL_EDITMODE +is purely an indication of the result of the most recent +.Xr editrc 5 +.Ic edit +command. diff --git a/lib/libedit/editrc.5 b/lib/libedit/editrc.5 new file mode 100644 index 0000000..241dbed --- /dev/null +++ b/lib/libedit/editrc.5 @@ -0,0 +1,526 @@ +.\" $NetBSD: editrc.5,v 1.29 2014/12/25 13:39:41 wiz Exp $ +.\" +.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This file was contributed to The NetBSD Foundation by Luke Mewburn. +.\" +.\" 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: releng/11.1/lib/libedit/editrc.5 276881 2015-01-09 07:40:56Z bapt $ +.\" +.Dd December 25, 2014 +.Dt EDITRC 5 +.Os +.Sh NAME +.Nm editrc +.Nd configuration file for editline library +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +file defines various settings to be used by the +.Xr editline 3 +library. +.Pp +The format of each line is: +.Pp +.Dl [prog:]command [arg ...] +.Pp +.Ar command +is one of the +.Xr editline 3 +builtin commands. +Refer to +.Sx BUILTIN COMMANDS +for more information. +.Pp +.Ar prog +is the program name string that a program defines when it calls +.Xr el_init 3 +to set up +.Xr editline 3 , +which is usually +.Va argv[0] . +.Ar command +will be executed for any program which matches +.Ar prog . +.Pp +.Ar prog +may also be a +.Xr regex 3 +style +regular expression, in which case +.Ar command +will be executed for any program that matches the regular expression. +.Pp +If +.Ar prog +is absent, +.Ar command +is executed for all programs. +.Sh BUILTIN COMMANDS +The +.Nm editline +library has some builtin commands, which affect the way +that the line editing and history functions operate. +These are based on similar named builtins present in the +.Xr tcsh 1 +shell. +.Pp +The following builtin commands are available: +.Bl -tag -width 4n +.It Ic bind Oo Fl a Oc Oo Fl e Oc Oo Fl k Oc Oo Fl l Oc Oo Fl r Oc \ +Oo Fl s Oc Oo Fl v Oc Oo Ar key Oo Ar command Oc Oc +Without options, list all bound keys, and the editor command to which +each is bound. +If +.Ar key +is supplied, show the bindings for +.Ar key . +If +.Ar key command +is supplied, bind +.Ar command +to +.Ar key . +Options include: +.Bl -tag -width 4n +.It Fl a +List or change key bindings in the +.Xr vi 1 +mode alternate (command mode) key map. +.It Fl e +Bind all keys to the standard GNU Emacs-like bindings. +.It Fl k +.Ar key +is interpreted as a symbolic arrow key name, which may be one of +.Sq up , +.Sq down , +.Sq left +or +.Sq right . +.It Fl l +List all editor commands and a short description of each. +.It Fl r +Remove a key's binding. +.It Fl s +.Ar command +is taken as a literal string and treated as terminal input when +.Ar key +is typed. +Bound keys in +.Ar command +are themselves reinterpreted, and this continues for ten levels of +interpretation. +.It Fl v +Bind all keys to the standard +.Xr vi 1 Ns -like +bindings. +.El +.Pp +.Ar command +may be one of the commands documented in +.Sx "EDITOR COMMANDS" +below, or another key. +.Pp +.Ar key +and +.Ar command +can contain control characters of the form +.Sm off +.Sq No ^ Ar character +.Sm on +.Po +e.g.\& +.Sq ^A +.Pc , +and the following backslashed escape sequences: +.Pp +.Bl -tag -compact -offset indent -width 4n +.It Ic \ea +Bell +.It Ic \eb +Backspace +.It Ic \ee +Escape +.It Ic \ef +Formfeed +.It Ic \en +Newline +.It Ic \er +Carriage return +.It Ic \et +Horizontal tab +.It Ic \ev +Vertical tab +.Sm off +.It Sy \e Ar nnn +.Sm on +The ASCII character corresponding to the octal number +.Ar nnn . +.El +.Pp +.Sq \e +nullifies the special meaning of the following character, +if it has any, notably +.Sq \e +and +.Sq ^ . +.It Ic echotc Oo Fl sv Oc Ar arg Ar ... +Exercise terminal capabilities given in +.Ar arg ... . +If +.Ar arg +is +.Sq baud , +.Sq cols , +.Sq lines , +.Sq rows , +.Sq meta , +or +.Sq tabs , +the value of that capability is printed, with +.Dq yes +or +.Dq no +indicating that the terminal does or does not have that capability. +.Pp +.Fl s +returns an empty string for non-existent capabilities, rather than +causing an error. +.Fl v +causes messages to be verbose. +.It Ic edit Op Li on | Li off +Enable or disable the +.Nm editline +functionality in a program. +.It Ic history Ar list | Ar size Dv n | Ar unique Dv n +The +.Ar list +command lists all entries in the history. +The +.Ar size +command sets the history size to +.Dv n +entries. +The +.Ar unique +command controls if history should keep duplicate entries. +If +.Dv n +is non zero, only keep unique history entries. +If +.Dv n +is zero, then keep all entries (the default). +.It Ic settc Ar cap Ar val +Set the terminal capability +.Ar cap +to +.Ar val , +as defined in +.Xr termcap 5 . +No sanity checking is done. +.It Ic setty Oo Fl a Oc Oo Fl d Oc Oo Fl q Oc Oo Fl x Oc Oo Ar +mode Oc \ +Oo Ar -mode Oc Oo Ar mode Oc Oo Ar char=c Oc +Control which tty modes that +.Nm +will not allow the user to change. +.Fl d , +.Fl q +or +.Fl x +tells +.Ic setty +to act on the +.Sq edit , +.Sq quote +or +.Sq execute +set of tty modes respectively; defaulting to +.Fl x . +.Pp +Without other arguments, +.Ic setty +lists the modes in the chosen set which are fixed on +.Po +.Sq +mode +.Pc +or off +.Po +.Sq -mode +.Pc . +.Fl a +lists all tty modes in the chosen set regardless of the setting. +With +.Ar +mode , +.Ar -mode +or +.Ar mode , +fixes +.Ar mode +on or off or removes control of +.Ar mode +in the chosen set. +.Pp +.Ic Setty +can also be used to set tty characters to particular values using +.Ar char=value . +If +.Ar value +is empty +then the character is set to +.Dv _POSIX_VDISABLE . +.It Ic telltc +List the values of all the terminal capabilities (see +.Xr termcap 5 ) . +.El +.Sh EDITOR COMMANDS +The following editor commands are available for use in key bindings: +.\" Section automatically generated with makelist +.Bl -tag -width 4n +.It Ic vi-paste-next +Vi paste previous deletion to the right of the cursor. +.It Ic vi-paste-prev +Vi paste previous deletion to the left of the cursor. +.It Ic vi-prev-big-word +Vi move to the previous space delimited word. +.It Ic vi-prev-word +Vi move to the previous word. +.It Ic vi-next-big-word +Vi move to the next space delimited word. +.It Ic vi-next-word +Vi move to the next word. +.It Ic vi-change-case +Vi change case of character under the cursor and advance one character. +.It Ic vi-change-meta +Vi change prefix command. +.It Ic vi-insert-at-bol +Vi enter insert mode at the beginning of line. +.It Ic vi-replace-char +Vi replace character under the cursor with the next character typed. +.It Ic vi-replace-mode +Vi enter replace mode. +.It Ic vi-substitute-char +Vi replace character under the cursor and enter insert mode. +.It Ic vi-substitute-line +Vi substitute entire line. +.It Ic vi-change-to-eol +Vi change to end of line. +.It Ic vi-insert +Vi enter insert mode. +.It Ic vi-add +Vi enter insert mode after the cursor. +.It Ic vi-add-at-eol +Vi enter insert mode at end of line. +.It Ic vi-delete-meta +Vi delete prefix command. +.It Ic vi-end-big-word +Vi move to the end of the current space delimited word. +.It Ic vi-end-word +Vi move to the end of the current word. +.It Ic vi-undo +Vi undo last change. +.It Ic vi-command-mode +Vi enter command mode (use alternative key bindings). +.It Ic vi-zero +Vi move to the beginning of line. +.It Ic vi-delete-prev-char +Vi move to previous character (backspace). +.It Ic vi-list-or-eof +Vi list choices for completion or indicate end of file if empty line. +.It Ic vi-kill-line-prev +Vi cut from beginning of line to cursor. +.It Ic vi-search-prev +Vi search history previous. +.It Ic vi-search-next +Vi search history next. +.It Ic vi-repeat-search-next +Vi repeat current search in the same search direction. +.It Ic vi-repeat-search-prev +Vi repeat current search in the opposite search direction. +.It Ic vi-next-char +Vi move to the character specified next. +.It Ic vi-prev-char +Vi move to the character specified previous. +.It Ic vi-to-next-char +Vi move up to the character specified next. +.It Ic vi-to-prev-char +Vi move up to the character specified previous. +.It Ic vi-repeat-next-char +Vi repeat current character search in the same search direction. +.It Ic vi-repeat-prev-char +Vi repeat current character search in the opposite search direction. +.It Ic vi-match +Vi go to matching () {} or []. +.It Ic vi-undo-line +Vi undo all changes to line. +.It Ic vi-to-column +Vi go to specified column. +.It Ic vi-yank-end +Vi yank to end of line. +.It Ic vi-yank +Vi yank. +.It Ic vi-comment-out +Vi comment out current command. +.It Ic vi-alias +Vi include shell alias. +.It Ic vi-to-history-line +Vi go to specified history file line.. +.It Ic vi-histedit +Vi edit history line with vi. +.It Ic vi-history-word +Vi append word from previous input line. +.It Ic vi-redo +Vi redo last non-motion command. +.It Ic em-delete-or-list +Delete character under cursor or list completions if at end of line. +.It Ic em-delete-next-word +Cut from cursor to end of current word. +.It Ic em-yank +Paste cut buffer at cursor position. +.It Ic em-kill-line +Cut the entire line and save in cut buffer. +.It Ic em-kill-region +Cut area between mark and cursor and save in cut buffer. +.It Ic em-copy-region +Copy area between mark and cursor to cut buffer. +.It Ic em-gosmacs-transpose +Exchange the two characters before the cursor. +.It Ic em-next-word +Move next to end of current word. +.It Ic em-upper-case +Uppercase the characters from cursor to end of current word. +.It Ic em-capitol-case +Capitalize the characters from cursor to end of current word. +.It Ic em-lower-case +Lowercase the characters from cursor to end of current word. +.It Ic em-set-mark +Set the mark at cursor. +.It Ic em-exchange-mark +Exchange the cursor and mark. +.It Ic em-universal-argument +Universal argument (argument times 4). +.It Ic em-meta-next +Add 8th bit to next character typed. +.It Ic em-toggle-overwrite +Switch from insert to overwrite mode or vice versa. +.It Ic em-copy-prev-word +Copy current word to cursor. +.It Ic em-inc-search-next +Emacs incremental next search. +.It Ic em-inc-search-prev +Emacs incremental reverse search. +.It Ic ed-end-of-file +Indicate end of file. +.It Ic ed-insert +Add character to the line. +.It Ic ed-delete-prev-word +Delete from beginning of current word to cursor. +.It Ic ed-delete-next-char +Delete character under cursor. +.It Ic ed-kill-line +Cut to the end of line. +.It Ic ed-move-to-end +Move cursor to the end of line. +.It Ic ed-move-to-beg +Move cursor to the beginning of line. +.It Ic ed-transpose-chars +Exchange the character to the left of the cursor with the one under it. +.It Ic ed-next-char +Move to the right one character. +.It Ic ed-prev-word +Move to the beginning of the current word. +.It Ic ed-prev-char +Move to the left one character. +.It Ic ed-quoted-insert +Add the next character typed verbatim. +.It Ic ed-digit +Adds to argument or enters a digit. +.It Ic ed-argument-digit +Digit that starts argument. +.It Ic ed-unassigned +Indicates unbound character. +.It Ic ed-tty-sigint +Tty interrupt character. +.It Ic ed-tty-dsusp +Tty delayed suspend character. +.It Ic ed-tty-flush-output +Tty flush output characters. +.It Ic ed-tty-sigquit +Tty quit character. +.It Ic ed-tty-sigtstp +Tty suspend character. +.It Ic ed-tty-stop-output +Tty disallow output characters. +.It Ic ed-tty-start-output +Tty allow output characters. +.It Ic ed-newline +Execute command. +.It Ic ed-delete-prev-char +Delete the character to the left of the cursor. +.It Ic ed-clear-screen +Clear screen leaving current line at the top. +.It Ic ed-redisplay +Redisplay everything. +.It Ic ed-start-over +Erase current line and start from scratch. +.It Ic ed-sequence-lead-in +First character in a bound sequence. +.It Ic ed-prev-history +Move to the previous history line. +.It Ic ed-next-history +Move to the next history line. +.It Ic ed-search-prev-history +Search previous in history for a line matching the current. +.It Ic ed-search-next-history +Search next in history for a line matching the current. +.It Ic ed-prev-line +Move up one line. +.It Ic ed-next-line +Move down one line. +.It Ic ed-command +Editline extended command. +.El +.\" End of section automatically generated with makelist +.Sh FILES +.Bl -tag -width "~/.editrcXXX" +.It Pa ~/.editrc +User configuration file for the +.Xr editline 3 +library. +.El +.Sh SEE ALSO +.Xr editline 3 , +.Xr regex 3 , +.Xr termcap 5 +.Sh AUTHORS +.An -nosplit +The +.Nm editline +library was written by +.An Christos Zoulas , +and this manual was written by +.An Luke Mewburn , +with some sections inspired by +.Xr tcsh 1 . diff --git a/lib/libedit/el.c b/lib/libedit/el.c index dd0f7d7..4da0a2c 100644 --- a/lib/libedit/el.c +++ b/lib/libedit/el.c @@ -1,3 +1,5 @@ +/* $NetBSD: el.c,v 1.83 2016/02/24 17:13:22 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -28,29 +30,35 @@ * 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. - * - * $NetBSD: el.c,v 1.41 2005/08/19 04:21:47 christos Exp $ */ +#include "config.h" #if !defined(lint) && !defined(SCCSID) +#if 0 static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94"; +#else +__RCSID("$NetBSD: el.c,v 1.83 2016/02/24 17:13:22 christos Exp $"); +#endif #endif /* not lint && not SCCSID */ #include -__FBSDID("$FreeBSD: src/lib/libedit/el.c,v 1.11.14.1 2005/10/09 03:44:01 delphij Exp $"); +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/el.c 313981 2017-02-20 03:33:59Z pfg $"); /* * el.c: EditLine interface functions */ -#include "sys.h" - #include #include -#include -#include +#include #include -#include "el.h" +#include +#include +#ifdef WIDECHAR +#include +#include +#endif -#define HAVE_ISSETUGID +#include "el.h" +#include "parse.h" /* el_init(): * Initialize editline and set default parameters. @@ -58,18 +66,31 @@ public EditLine * el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) { + return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout), + fileno(ferr)); +} - EditLine *el = (EditLine *) el_malloc(sizeof(EditLine)); +public EditLine * +el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr, + int fdin, int fdout, int fderr) +{ + EditLine *el = el_malloc(sizeof(*el)); if (el == NULL) - return (NULL); + return NULL; memset(el, 0, sizeof(EditLine)); - el->el_infd = fileno(fin); + el->el_infile = fin; el->el_outfile = fout; el->el_errfile = ferr; - if ((el->el_prog = el_strdup(prog)) == NULL) { + + el->el_infd = fdin; + el->el_outfd = fdout; + el->el_errfd = fderr; + + el->el_prog = Strdup(ct_decode_string(prog, &el->el_scratch)); + if (el->el_prog == NULL) { el_free(el); return NULL; } @@ -78,13 +99,17 @@ * Initialize all the modules. Order is important!!! */ el->el_flags = 0; + if (setlocale(LC_CTYPE, NULL) != NULL){ + if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) + el->el_flags |= CHARSET_IS_UTF8; + } - if (term_init(el) == -1) { + if (terminal_init(el) == -1) { el_free(el->el_prog); el_free(el); return NULL; } - (void) key_init(el); + (void) keymacro_init(el); (void) map_init(el); if (tty_init(el) == -1) el->el_flags |= NO_TTY; @@ -95,7 +120,7 @@ (void) sig_init(el); (void) read_init(el); - return (el); + return el; } @@ -111,18 +136,25 @@ el_reset(el); - term_end(el); - key_end(el); + terminal_end(el); + keymacro_end(el); map_end(el); - tty_end(el); + if (!(el->el_flags & NO_TTY)) + tty_end(el); ch_end(el); search_end(el); hist_end(el); prompt_end(el); sig_end(el); - el_free((ptr_t) el->el_prog); - el_free((ptr_t) el); + el_free(el->el_prog); +#ifdef WIDECHAR + el_free(el->el_scratch.cbuff); + el_free(el->el_scratch.wbuff); + el_free(el->el_lgcyconv.cbuff); + el_free(el->el_lgcyconv.wbuff); +#endif + el_free(el); } @@ -142,31 +174,57 @@ * set the editline parameters */ public int -el_set(EditLine *el, int op, ...) +FUN(el,set)(EditLine *el, int op, ...) { - vaList va; + va_list ap; int rv = 0; if (el == NULL) - return (-1); - vaStart(va, op); + return -1; + va_start(ap, op); switch (op) { case EL_PROMPT: - case EL_RPROMPT: - rv = prompt_set(el, vaArg(va, el_pfunc_t), op); + case EL_RPROMPT: { + el_pfunc_t p = va_arg(ap, el_pfunc_t); + + rv = prompt_set(el, p, 0, op, 1); break; + } + + case EL_RESIZE: { + el_zfunc_t p = va_arg(ap, el_zfunc_t); + void *arg = va_arg(ap, void *); + rv = ch_resizefun(el, p, arg); + break; + } + + case EL_ALIAS_TEXT: { + el_afunc_t p = va_arg(ap, el_afunc_t); + void *arg = va_arg(ap, void *); + rv = ch_aliasfun(el, p, arg); + break; + } + + case EL_PROMPT_ESC: + case EL_RPROMPT_ESC: { + el_pfunc_t p = va_arg(ap, el_pfunc_t); + int c = va_arg(ap, int); + + rv = prompt_set(el, p, (Char)c, op, 1); + break; + } case EL_TERMINAL: - rv = term_set(el, vaArg(va, char *)); + rv = terminal_set(el, va_arg(ap, char *)); break; case EL_EDITOR: - rv = map_set_editor(el, vaArg(va, char *)); + rv = map_set_editor(el, va_arg(ap, Char *)); break; case EL_SIGNAL: - if (vaArg(va, int)) + if (va_arg(ap, int)) el->el_flags |= HANDLE_SIGNALS; else el->el_flags &= ~HANDLE_SIGNALS; @@ -178,36 +236,36 @@ case EL_ECHOTC: case EL_SETTY: { - const char *argv[20]; + const Char *argv[20]; int i; - for (i = 1; i < 20; i++) - if ((argv[i] = vaArg(va, char *)) == NULL) + for (i = 1; i < (int)__arraycount(argv); i++) + if ((argv[i] = va_arg(ap, Char *)) == NULL) break; switch (op) { case EL_BIND: - argv[0] = "bind"; + argv[0] = STR("bind"); rv = map_bind(el, i, argv); break; case EL_TELLTC: - argv[0] = "telltc"; - rv = term_telltc(el, i, argv); + argv[0] = STR("telltc"); + rv = terminal_telltc(el, i, argv); break; case EL_SETTC: - argv[0] = "settc"; - rv = term_settc(el, i, argv); + argv[0] = STR("settc"); + rv = terminal_settc(el, i, argv); break; case EL_ECHOTC: - argv[0] = "echotc"; - rv = term_echotc(el, i, argv); + argv[0] = STR("echotc"); + rv = terminal_echotc(el, i, argv); break; case EL_SETTY: - argv[0] = "setty"; + argv[0] = STR("setty"); rv = tty_stty(el, i, argv); break; @@ -221,9 +279,9 @@ case EL_ADDFN: { - char *name = vaArg(va, char *); - char *help = vaArg(va, char *); - el_func_t func = vaArg(va, el_func_t); + Char *name = va_arg(ap, Char *); + Char *help = va_arg(ap, Char *); + el_func_t func = va_arg(ap, el_func_t); rv = map_addfunc(el, name, help, func); break; @@ -231,15 +289,17 @@ case EL_HIST: { - hist_fun_t func = vaArg(va, hist_fun_t); - ptr_t ptr = vaArg(va, char *); + hist_fun_t func = va_arg(ap, hist_fun_t); + void *ptr = va_arg(ap, void *); rv = hist_set(el, func, ptr); + if (!(el->el_flags & CHARSET_IS_UTF8)) + el->el_flags &= ~NARROW_HISTORY; break; } case EL_EDITMODE: - if (vaArg(va, int)) + if (va_arg(ap, int)) el->el_flags &= ~EDIT_DISABLED; else el->el_flags |= EDIT_DISABLED; @@ -248,17 +308,17 @@ case EL_GETCFN: { - el_rfunc_t rc = vaArg(va, el_rfunc_t); + el_rfunc_t rc = va_arg(ap, el_rfunc_t); rv = el_read_setfn(el, rc); break; } case EL_CLIENTDATA: - el->el_data = vaArg(va, void *); + el->el_data = va_arg(ap, void *); break; case EL_UNBUFFERED: - rv = vaArg(va, int); + rv = va_arg(ap, int); if (rv && !(el->el_flags & UNBUFFERED)) { el->el_flags |= UNBUFFERED; read_prepare(el); @@ -270,7 +330,7 @@ break; case EL_PREP_TERM: - rv = vaArg(va, int); + rv = va_arg(ap, int); if (rv) (void) tty_rawmode(el); else @@ -278,13 +338,48 @@ rv = 0; break; + case EL_SETFP: + { + FILE *fp; + int what; + + what = va_arg(ap, int); + fp = va_arg(ap, FILE *); + + rv = 0; + switch (what) { + case 0: + el->el_infile = fp; + el->el_infd = fileno(fp); + break; + case 1: + el->el_outfile = fp; + el->el_outfd = fileno(fp); + break; + case 2: + el->el_errfile = fp; + el->el_errfd = fileno(fp); + break; + default: + rv = -1; + break; + } + break; + } + + case EL_REFRESH: + re_clear_display(el); + re_refresh(el); + terminal__flush(el); + break; + default: rv = -1; break; } - vaEnd(va); - return (rv); + va_end(ap); + return rv; } @@ -292,159 +387,123 @@ * retrieve the editline parameters */ public int -el_get(EditLine *el, int op, void *ret) +FUN(el,get)(EditLine *el, int op, ...) { + va_list ap; int rv; - if (el == NULL || ret == NULL) - return (-1); + if (el == NULL) + return -1; + + va_start(ap, op); + switch (op) { case EL_PROMPT: - case EL_RPROMPT: - rv = prompt_get(el, (el_pfunc_t *) ret, op); + case EL_RPROMPT: { + el_pfunc_t *p = va_arg(ap, el_pfunc_t *); + rv = prompt_get(el, p, 0, op); break; + } + case EL_PROMPT_ESC: + case EL_RPROMPT_ESC: { + el_pfunc_t *p = va_arg(ap, el_pfunc_t *); + Char *c = va_arg(ap, Char *); + + rv = prompt_get(el, p, c, op); + break; + } case EL_EDITOR: - rv = map_get_editor(el, (const char **)ret); + rv = map_get_editor(el, va_arg(ap, const Char **)); break; case EL_SIGNAL: - *((int *) ret) = (el->el_flags & HANDLE_SIGNALS); + *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); rv = 0; break; case EL_EDITMODE: - *((int *) ret) = (!(el->el_flags & EDIT_DISABLED)); + *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); rv = 0; break; case EL_TERMINAL: - term_get(el, (const char **)ret); + terminal_get(el, va_arg(ap, const char **)); rv = 0; break; -#if 0 /* XXX */ - case EL_BIND: - case EL_TELLTC: - case EL_SETTC: - case EL_ECHOTC: - case EL_SETTY: + case EL_GETTC: { - const char *argv[20]; + static char name[] = "gettc"; + char *argv[20]; int i; - for (i = 1; i < sizeof(argv) / sizeof(argv[0]); i++) - if ((argv[i] = vaArg(va, char *)) == NULL) + for (i = 1; i < (int)__arraycount(argv); i++) + if ((argv[i] = va_arg(ap, char *)) == NULL) break; - switch (op) { - case EL_BIND: - argv[0] = "bind"; - rv = map_bind(el, i, argv); - break; - - case EL_TELLTC: - argv[0] = "telltc"; - rv = term_telltc(el, i, argv); - break; - - case EL_SETTC: - argv[0] = "settc"; - rv = term_settc(el, i, argv); - break; - - case EL_ECHOTC: - argv[0] = "echotc"; - rv = term_echotc(el, i, argv); - break; - - case EL_SETTY: - argv[0] = "setty"; - rv = tty_stty(el, i, argv); - break; - - default: - rv = -1; - EL_ABORT((el->errfile, "Bad op %d\n", op)); - break; - } + argv[0] = name; + rv = terminal_gettc(el, i, argv); break; } - case EL_ADDFN: - { - char *name = vaArg(va, char *); - char *help = vaArg(va, char *); - el_func_t func = vaArg(va, el_func_t); - - rv = map_addfunc(el, name, help, func); - break; - } - - case EL_HIST: - { - hist_fun_t func = vaArg(va, hist_fun_t); - ptr_t ptr = vaArg(va, char *); - rv = hist_set(el, func, ptr); - } - break; -#endif /* XXX */ - case EL_GETCFN: - *((el_rfunc_t *)ret) = el_read_getfn(el); + *va_arg(ap, el_rfunc_t *) = el_read_getfn(el); rv = 0; break; case EL_CLIENTDATA: - *((void **)ret) = el->el_data; + *va_arg(ap, void **) = el->el_data; rv = 0; break; case EL_UNBUFFERED: - *((int *) ret) = (!(el->el_flags & UNBUFFERED)); + *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; rv = 0; break; + case EL_GETFP: + { + int what; + FILE **fpp; + + what = va_arg(ap, int); + fpp = va_arg(ap, FILE **); + rv = 0; + switch (what) { + case 0: + *fpp = el->el_infile; + break; + case 1: + *fpp = el->el_outfile; + break; + case 2: + *fpp = el->el_errfile; + break; + default: + rv = -1; + break; + } + break; + } default: rv = -1; + break; } + va_end(ap); - return (rv); + return rv; } -/* el_data_get(): - * Set user private data. - */ -public void -el_data_set (el, data) - EditLine *el; - void *data; -{ - el->el_data = data; - - return; -} - -/* el_data_get(): - * Return user private data. - */ -public void * -el_data_get (el) - EditLine *el; -{ - if (el->el_data) - return (el->el_data); - return (NULL); -} /* el_line(): * Return editing info */ -public const LineInfo * -el_line(EditLine *el) +public const TYPE(LineInfo) * +FUN(el,line)(EditLine *el) { - return (const LineInfo *) (void *) &el->el_line; + return (const TYPE(LineInfo) *)(void *)&el->el_line; } @@ -456,22 +515,26 @@ { FILE *fp; size_t len; + ssize_t slen; char *ptr; + char *path = NULL; + const Char *dptr; + int error = 0; fp = NULL; if (fname == NULL) { #ifdef HAVE_ISSETUGID static const char elpath[] = "/.editrc"; - char path[MAXPATHLEN]; + size_t plen = sizeof(elpath); if (issetugid()) - return (-1); + return -1; if ((ptr = getenv("HOME")) == NULL) - return (-1); - if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) - return (-1); - if (strlcat(path, elpath, sizeof(path)) >= sizeof(path)) - return (-1); + return -1; + plen += strlen(ptr); + if ((path = el_malloc(plen * sizeof(*path))) == NULL) + return -1; + (void)snprintf(path, plen, "%s%s", ptr, elpath); fname = path; #else /* @@ -479,26 +542,40 @@ * to keep from inadvertently opening up the user to a security * hole. */ - return (-1); + return -1; #endif } if (fp == NULL) fp = fopen(fname, "r"); - if (fp == NULL) - return (-1); - - while ((ptr = fgetln(fp, &len)) != NULL) { - if (len > 0 && ptr[len - 1] == '\n') - --len; - ptr[len] = '\0'; - if (parse_line(el, ptr) == -1) { - (void) fclose(fp); - return (-1); - } + if (fp == NULL) { + el_free(path); + return -1; } + ptr = NULL; + len = 0; + while ((slen = getline(&ptr, &len, fp)) != -1) { + if (*ptr == '\n') + continue; /* Empty line. */ + if (slen > 0 && ptr[--slen] == '\n') + ptr[slen] = '\0'; + + dptr = ct_decode_string(ptr, &el->el_scratch); + if (!dptr) + continue; + /* loop until first non-space char or EOL */ + while (*dptr != '\0' && Isspace(*dptr)) + dptr++; + if (*dptr == '#') + continue; /* ignore, this is a comment line */ + if ((error = parse_line(el, dptr)) == -1) + break; + } + free(ptr); + + el_free(path); (void) fclose(fp); - return (0); + return error; } @@ -516,8 +593,8 @@ (void) sigprocmask(SIG_BLOCK, &nset, &oset); /* get the correct window size */ - if (term_get_size(el, &lins, &cols)) - term_change_size(el, lins, cols); + if (terminal_get_size(el, &lins, &cols)) + terminal_change_size(el, lins, cols); (void) sigprocmask(SIG_SETMASK, &oset, NULL); } @@ -530,7 +607,7 @@ el_beep(EditLine *el) { - term_beep(el); + terminal_beep(el); } @@ -539,24 +616,25 @@ */ protected int /*ARGSUSED*/ -el_editmode(EditLine *el, int argc, const char **argv) +el_editmode(EditLine *el, int argc, const Char **argv) { - const char *how; + const Char *how; if (argv == NULL || argc != 2 || argv[1] == NULL) - return (-1); + return -1; how = argv[1]; - if (strcmp(how, "on") == 0) { + if (Strcmp(how, STR("on")) == 0) { el->el_flags &= ~EDIT_DISABLED; tty_rawmode(el); - } else if (strcmp(how, "off") == 0) { + } else if (Strcmp(how, STR("off")) == 0) { tty_cookedmode(el); el->el_flags |= EDIT_DISABLED; } else { - (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how); - return (-1); + (void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n", + how); + return -1; } - return (0); + return 0; } diff --git a/lib/libedit/el.h b/lib/libedit/el.h index eee97df..619b0ce 100644 --- a/lib/libedit/el.h +++ b/lib/libedit/el.h @@ -1,3 +1,5 @@ +/* $NetBSD: el.h,v 1.34 2016/02/24 17:13:22 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)el.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: el.h,v 1.16 2003/10/18 23:48:42 christos Exp $ - * $FreeBSD: src/lib/libedit/el.h,v 1.4.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/el.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -46,17 +47,17 @@ #define VIDEFAULT #define ANCHOR -#include -#include +#include "histedit.h" +#include "chartype.h" -#define EL_BUFSIZ 1024 /* Maximum line size */ +#define EL_BUFSIZ ((size_t)1024) /* Maximum line size */ #define HANDLE_SIGNALS 0x01 #define NO_TTY 0x02 #define EDIT_DISABLED 0x04 #define UNBUFFERED 0x08 - -typedef int bool_t; /* True or not */ +#define CHARSET_IS_UTF8 0x10 +#define NARROW_HISTORY 0x40 typedef unsigned char el_action_t; /* Index to command array */ @@ -66,10 +67,10 @@ } coord_t; typedef struct el_line_t { - char *buffer; /* Input line */ - char *cursor; /* Cursor position */ - char *lastchar; /* Last character */ - const char *limit; /* Max position */ + Char *buffer; /* Input line */ + Char *cursor; /* Cursor position */ + Char *lastchar; /* Last character */ + const Char *limit; /* Max position */ } el_line_t; /* @@ -81,60 +82,66 @@ int argument; /* Numeric argument */ int metanext; /* Is the next char a meta char */ el_action_t lastcmd; /* Previous command */ - el_action_t thiscmd; /* this command */ - char thisch; /* char that generated it */ + el_action_t thiscmd; /* this command */ + Char thisch; /* char that generated it */ } el_state_t; /* * Until we come up with something better... */ -#define el_strdup(a) strdup(a) #define el_malloc(a) malloc(a) #define el_realloc(a,b) realloc(a, b) #define el_free(a) free(a) #include "tty.h" #include "prompt.h" -#include "key.h" -#include "term.h" +#include "keymacro.h" +#include "terminal.h" #include "refresh.h" #include "chared.h" -#include "common.h" #include "search.h" #include "hist.h" +#include "fcns.h" /* el_func_t is needed for map.h */ #include "map.h" -#include "parse.h" #include "sig.h" -#include "help.h" #include "read.h" struct editline { - char *el_prog; /* the program name */ + Char *el_prog; /* the program name */ + FILE *el_infile; /* Stdio stuff */ FILE *el_outfile; /* Stdio stuff */ FILE *el_errfile; /* Stdio stuff */ int el_infd; /* Input file descriptor */ + int el_outfd; /* Output file descriptor */ + int el_errfd; /* Error file descriptor */ int el_flags; /* Various flags. */ + int el_errno; /* Local copy of errno */ coord_t el_cursor; /* Cursor location */ - char **el_display; /* Real screen image = what is there */ - char **el_vdisplay; /* Virtual screen image = what we see */ + Char **el_display; /* Real screen image = what is there */ + Char **el_vdisplay; /* Virtual screen image = what we see */ void *el_data; /* Client data */ el_line_t el_line; /* The current line information */ el_state_t el_state; /* Current editor state */ - el_term_t el_term; /* Terminal dependent stuff */ + el_terminal_t el_terminal; /* Terminal dependent stuff */ el_tty_t el_tty; /* Tty dependent stuff */ el_refresh_t el_refresh; /* Refresh stuff */ el_prompt_t el_prompt; /* Prompt stuff */ el_prompt_t el_rprompt; /* Prompt stuff */ el_chared_t el_chared; /* Characted editor stuff */ el_map_t el_map; /* Key mapping stuff */ - el_key_t el_key; /* Key binding stuff */ + el_keymacro_t el_keymacro; /* Key binding stuff */ el_history_t el_history; /* History stuff */ el_search_t el_search; /* Search stuff */ el_signal_t el_signal; /* Signal handling stuff */ el_read_t el_read; /* Character reading stuff */ +#ifdef WIDECHAR + ct_buffer_t el_scratch; /* Scratch conversion buffer */ + ct_buffer_t el_lgcyconv; /* Buffer for legacy wrappers */ + LineInfo el_lgcylinfo; /* Legacy LineInfo buffer */ +#endif }; -protected int el_editmode(EditLine *, int, const char **); +protected int el_editmode(EditLine *, int, const Char **); #ifdef DEBUG #define EL_ABORT(a) do { \ diff --git a/lib/libedit/eln.c b/lib/libedit/eln.c new file mode 100644 index 0000000..d21db7b --- /dev/null +++ b/lib/libedit/eln.c @@ -0,0 +1,394 @@ +/* $NetBSD: eln.c,v 1.28 2016/02/28 23:02:24 christos Exp $ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, 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. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +__RCSID("$NetBSD: eln.c,v 1.28 2016/02/28 23:02:24 christos Exp $"); +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/eln.c 313981 2017-02-20 03:33:59Z pfg $"); + +#include +#include +#include +#include + +#include "el.h" + +public int +el_getc(EditLine *el, char *cp) +{ + int num_read; + wchar_t wc = 0; + + num_read = el_wgetc(el, &wc); + *cp = '\0'; + if (num_read <= 0) + return num_read; + num_read = ct_wctob(wc); + if (num_read == EOF) { + errno = ERANGE; + return -1; + } else { + *cp = (char)num_read; + return 1; + } +} + + +#ifdef WIDECHAR +public void +el_push(EditLine *el, const char *str) +{ + /* Using multibyte->wide string decoding works fine under single-byte + * character sets too, and Does The Right Thing. */ + el_wpush(el, ct_decode_string(str, &el->el_lgcyconv)); +} + + +public const char * +el_gets(EditLine *el, int *nread) +{ + const wchar_t *tmp; + + tmp = el_wgets(el, nread); + if (tmp != NULL) { + int i; + size_t nwread = 0; + + for (i = 0; i < *nread; i++) + nwread += ct_enc_width(tmp[i]); + *nread = (int)nwread; + } + return ct_encode_string(tmp, &el->el_lgcyconv); +} + + +public int +el_parse(EditLine *el, int argc, const char *argv[]) +{ + int ret; + const wchar_t **wargv; + + wargv = (const wchar_t **) + ct_decode_argv(argc, argv, &el->el_lgcyconv); + if (!wargv) + return -1; + ret = el_wparse(el, argc, wargv); + ct_free_argv(wargv); + + return ret; +} + + +public int +el_set(EditLine *el, int op, ...) +{ + va_list ap; + int ret; + + if (!el) + return -1; + va_start(ap, op); + + switch (op) { + case EL_PROMPT: /* el_pfunc_t */ + case EL_RPROMPT: { + el_pfunc_t p = va_arg(ap, el_pfunc_t); + ret = prompt_set(el, p, 0, op, 0); + break; + } + + case EL_RESIZE: { + el_zfunc_t p = va_arg(ap, el_zfunc_t); + void *arg = va_arg(ap, void *); + ret = ch_resizefun(el, p, arg); + break; + } + + case EL_ALIAS_TEXT: { + el_afunc_t p = va_arg(ap, el_afunc_t); + void *arg = va_arg(ap, void *); + ret = ch_aliasfun(el, p, arg); + break; + } + + case EL_PROMPT_ESC: + case EL_RPROMPT_ESC: { + el_pfunc_t p = va_arg(ap, el_pfunc_t); + int c = va_arg(ap, int); + + ret = prompt_set(el, p, c, op, 0); + break; + } + + case EL_TERMINAL: /* const char * */ + ret = el_wset(el, op, va_arg(ap, char *)); + break; + + case EL_EDITOR: /* const wchar_t * */ + ret = el_wset(el, op, ct_decode_string(va_arg(ap, char *), + &el->el_lgcyconv)); + break; + + case EL_SIGNAL: /* int */ + case EL_EDITMODE: + case EL_UNBUFFERED: + case EL_PREP_TERM: + ret = el_wset(el, op, va_arg(ap, int)); + break; + + case EL_BIND: /* const char * list -> const wchar_t * list */ + case EL_TELLTC: + case EL_SETTC: + case EL_ECHOTC: + case EL_SETTY: { + const char *argv[20]; + int i; + const wchar_t **wargv; + for (i = 1; i < (int)__arraycount(argv) - 1; ++i) + if ((argv[i] = va_arg(ap, const char *)) == NULL) + break; + argv[0] = argv[i] = NULL; + wargv = (const wchar_t **) + ct_decode_argv(i + 1, argv, &el->el_lgcyconv); + if (!wargv) { + ret = -1; + goto out; + } + /* + * AFAIK we can't portably pass through our new wargv to + * el_wset(), so we have to reimplement the body of + * el_wset() for these ops. + */ + switch (op) { + case EL_BIND: + wargv[0] = STR("bind"); + ret = map_bind(el, i, wargv); + break; + case EL_TELLTC: + wargv[0] = STR("telltc"); + ret = terminal_telltc(el, i, wargv); + break; + case EL_SETTC: + wargv[0] = STR("settc"); + ret = terminal_settc(el, i, wargv); + break; + case EL_ECHOTC: + wargv[0] = STR("echotc"); + ret = terminal_echotc(el, i, wargv); + break; + case EL_SETTY: + wargv[0] = STR("setty"); + ret = tty_stty(el, i, wargv); + break; + default: + ret = -1; + } + ct_free_argv(wargv); + break; + } + + /* XXX: do we need to change el_func_t too? */ + case EL_ADDFN: { /* const char *, const char *, el_func_t */ + const char *args[2]; + el_func_t func; + wchar_t **wargv; + + args[0] = va_arg(ap, const char *); + args[1] = va_arg(ap, const char *); + func = va_arg(ap, el_func_t); + + wargv = ct_decode_argv(2, args, &el->el_lgcyconv); + if (!wargv) { + ret = -1; + goto out; + } + /* XXX: The two strdup's leak */ + ret = map_addfunc(el, Strdup(wargv[0]), Strdup(wargv[1]), + func); + ct_free_argv(wargv); + break; + } + case EL_HIST: { /* hist_fun_t, const char * */ + hist_fun_t fun = va_arg(ap, hist_fun_t); + void *ptr = va_arg(ap, void *); + ret = hist_set(el, fun, ptr); + el->el_flags |= NARROW_HISTORY; + break; + } + + case EL_GETCFN: /* el_rfunc_t */ + ret = el_wset(el, op, va_arg(ap, el_rfunc_t)); + break; + + case EL_CLIENTDATA: /* void * */ + ret = el_wset(el, op, va_arg(ap, void *)); + break; + + case EL_SETFP: { /* int, FILE * */ + int what = va_arg(ap, int); + FILE *fp = va_arg(ap, FILE *); + ret = el_wset(el, op, what, fp); + break; + } + + case EL_REFRESH: + re_clear_display(el); + re_refresh(el); + terminal__flush(el); + ret = 0; + break; + + default: + ret = -1; + break; + } + +out: + va_end(ap); + return ret; +} + + +public int +el_get(EditLine *el, int op, ...) +{ + va_list ap; + int ret; + + if (!el) + return -1; + + va_start(ap, op); + + switch (op) { + case EL_PROMPT: /* el_pfunc_t * */ + case EL_RPROMPT: { + el_pfunc_t *p = va_arg(ap, el_pfunc_t *); + ret = prompt_get(el, p, 0, op); + break; + } + + case EL_PROMPT_ESC: /* el_pfunc_t *, char **/ + case EL_RPROMPT_ESC: { + el_pfunc_t *p = va_arg(ap, el_pfunc_t *); + char *c = va_arg(ap, char *); + wchar_t wc = 0; + ret = prompt_get(el, p, &wc, op); + *c = (char)wc; + break; + } + + case EL_EDITOR: { + const char **p = va_arg(ap, const char **); + const wchar_t *pw; + ret = el_wget(el, op, &pw); + *p = ct_encode_string(pw, &el->el_lgcyconv); + if (!el->el_lgcyconv.csize) + ret = -1; + break; + } + + case EL_TERMINAL: /* const char ** */ + ret = el_wget(el, op, va_arg(ap, const char **)); + break; + + case EL_SIGNAL: /* int * */ + case EL_EDITMODE: + case EL_UNBUFFERED: + case EL_PREP_TERM: + ret = el_wget(el, op, va_arg(ap, int *)); + break; + + case EL_GETTC: { + char *argv[20]; + static char gettc[] = "gettc"; + int i; + for (i = 1; i < (int)__arraycount(argv); ++i) + if ((argv[i] = va_arg(ap, char *)) == NULL) + break; + argv[0] = gettc; + ret = terminal_gettc(el, i, argv); + break; + } + + case EL_GETCFN: /* el_rfunc_t */ + ret = el_wget(el, op, va_arg(ap, el_rfunc_t *)); + break; + + case EL_CLIENTDATA: /* void ** */ + ret = el_wget(el, op, va_arg(ap, void **)); + break; + + case EL_GETFP: { /* int, FILE ** */ + int what = va_arg(ap, int); + FILE **fpp = va_arg(ap, FILE **); + ret = el_wget(el, op, what, fpp); + break; + } + + default: + ret = -1; + break; + } + + va_end(ap); + return ret; +} + + +const LineInfo * +el_line(EditLine *el) +{ + const LineInfoW *winfo = el_wline(el); + LineInfo *info = &el->el_lgcylinfo; + size_t offset; + const Char *p; + + info->buffer = ct_encode_string(winfo->buffer, &el->el_lgcyconv); + + offset = 0; + for (p = winfo->buffer; p < winfo->cursor; p++) + offset += ct_enc_width(*p); + info->cursor = info->buffer + offset; + + offset = 0; + for (p = winfo->buffer; p < winfo->lastchar; p++) + offset += ct_enc_width(*p); + info->lastchar = info->buffer + offset; + + return info; +} + + +int +el_insertstr(EditLine *el, const char *str) +{ + return el_winsertstr(el, ct_decode_string(str, &el->el_lgcyconv)); +} +#endif /* WIDECHAR */ diff --git a/lib/libedit/emacs.c b/lib/libedit/emacs.c new file mode 100644 index 0000000..6dfdc61 --- /dev/null +++ b/lib/libedit/emacs.c @@ -0,0 +1,513 @@ +/* $NetBSD: emacs.c,v 1.32 2016/02/16 22:53:14 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: emacs.c,v 1.32 2016/02/16 22:53:14 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/emacs.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * emacs.c: Emacs functions + */ +#include + +#include "el.h" +#include "emacs.h" + +/* em_delete_or_list(): + * Delete character under cursor or list completions if at end of line + * [^D] + */ +protected el_action_t +/*ARGSUSED*/ +em_delete_or_list(EditLine *el, wint_t c) +{ + + if (el->el_line.cursor == el->el_line.lastchar) { + /* if I'm at the end */ + if (el->el_line.cursor == el->el_line.buffer) { + /* and the beginning */ + terminal_writec(el, c); /* then do an EOF */ + return CC_EOF; + } else { + /* + * Here we could list completions, but it is an + * error right now + */ + terminal_beep(el); + return CC_ERROR; + } + } else { + if (el->el_state.doingarg) + c_delafter(el, el->el_state.argument); + else + c_delafter1(el); + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + /* bounds check */ + return CC_REFRESH; + } +} + + +/* em_delete_next_word(): + * Cut from cursor to end of current word + * [M-d] + */ +protected el_action_t +/*ARGSUSED*/ +em_delete_next_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *cp, *p, *kp; + + if (el->el_line.cursor == el->el_line.lastchar) + return CC_ERROR; + + cp = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++) + /* save the text */ + *kp++ = *p; + el->el_chared.c_kill.last = kp; + + c_delafter(el, (int)(cp - el->el_line.cursor)); /* delete after dot */ + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + /* bounds check */ + return CC_REFRESH; +} + + +/* em_yank(): + * Paste cut buffer at cursor position + * [^Y] + */ +protected el_action_t +/*ARGSUSED*/ +em_yank(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *kp, *cp; + + if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) + return CC_NORM; + + if (el->el_line.lastchar + + (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >= + el->el_line.limit) + return CC_ERROR; + + el->el_chared.c_kill.mark = el->el_line.cursor; + cp = el->el_line.cursor; + + /* open the space, */ + c_insert(el, + (int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf)); + /* copy the chars */ + for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++) + *cp++ = *kp; + + /* if an arg, cursor at beginning else cursor at end */ + if (el->el_state.argument == 1) + el->el_line.cursor = cp; + + return CC_REFRESH; +} + + +/* em_kill_line(): + * Cut the entire line and save in cut buffer + * [^U] + */ +protected el_action_t +/*ARGSUSED*/ +em_kill_line(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *kp, *cp; + + cp = el->el_line.buffer; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.lastchar) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + /* zap! -- delete all of it */ + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + return CC_REFRESH; +} + + +/* em_kill_region(): + * Cut area between mark and cursor and save in cut buffer + * [^W] + */ +protected el_action_t +/*ARGSUSED*/ +em_kill_region(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *kp, *cp; + + if (!el->el_chared.c_kill.mark) + return CC_ERROR; + + if (el->el_chared.c_kill.mark > el->el_line.cursor) { + cp = el->el_line.cursor; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_chared.c_kill.mark) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + c_delafter(el, (int)(cp - el->el_line.cursor)); + } else { /* mark is before cursor */ + cp = el->el_chared.c_kill.mark; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.cursor) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark)); + el->el_line.cursor = el->el_chared.c_kill.mark; + } + return CC_REFRESH; +} + + +/* em_copy_region(): + * Copy area between mark and cursor to cut buffer + * [M-W] + */ +protected el_action_t +/*ARGSUSED*/ +em_copy_region(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *kp, *cp; + + if (!el->el_chared.c_kill.mark) + return CC_ERROR; + + if (el->el_chared.c_kill.mark > el->el_line.cursor) { + cp = el->el_line.cursor; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_chared.c_kill.mark) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + } else { + cp = el->el_chared.c_kill.mark; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.cursor) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + } + return CC_NORM; +} + + +/* em_gosmacs_transpose(): + * Exchange the two characters before the cursor + * Gosling emacs transpose chars [^T] + */ +protected el_action_t +em_gosmacs_transpose(EditLine *el, wint_t c) +{ + + if (el->el_line.cursor > &el->el_line.buffer[1]) { + /* must have at least two chars entered */ + c = el->el_line.cursor[-2]; + el->el_line.cursor[-2] = el->el_line.cursor[-1]; + el->el_line.cursor[-1] = (Char)c; + return CC_REFRESH; + } else + return CC_ERROR; +} + + +/* em_next_word(): + * Move next to end of current word + * [M-f] + */ +protected el_action_t +/*ARGSUSED*/ +em_next_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + if (el->el_line.cursor == el->el_line.lastchar) + return CC_ERROR; + + el->el_line.cursor = c__next_word(el->el_line.cursor, + el->el_line.lastchar, + el->el_state.argument, + ce__isword); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* em_upper_case(): + * Uppercase the characters from cursor to end of current word + * [M-u] + */ +protected el_action_t +/*ARGSUSED*/ +em_upper_case(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *cp, *ep; + + ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (cp = el->el_line.cursor; cp < ep; cp++) + if (Islower(*cp)) + *cp = Toupper(*cp); + + el->el_line.cursor = ep; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + return CC_REFRESH; +} + + +/* em_capitol_case(): + * Capitalize the characters from cursor to end of current word + * [M-c] + */ +protected el_action_t +/*ARGSUSED*/ +em_capitol_case(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *cp, *ep; + + ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (cp = el->el_line.cursor; cp < ep; cp++) { + if (Isalpha(*cp)) { + if (Islower(*cp)) + *cp = Toupper(*cp); + cp++; + break; + } + } + for (; cp < ep; cp++) + if (Isupper(*cp)) + *cp = Tolower(*cp); + + el->el_line.cursor = ep; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + return CC_REFRESH; +} + + +/* em_lower_case(): + * Lowercase the characters from cursor to end of current word + * [M-l] + */ +protected el_action_t +/*ARGSUSED*/ +em_lower_case(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *cp, *ep; + + ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (cp = el->el_line.cursor; cp < ep; cp++) + if (Isupper(*cp)) + *cp = Tolower(*cp); + + el->el_line.cursor = ep; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + return CC_REFRESH; +} + + +/* em_set_mark(): + * Set the mark at cursor + * [^@] + */ +protected el_action_t +/*ARGSUSED*/ +em_set_mark(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_chared.c_kill.mark = el->el_line.cursor; + return CC_NORM; +} + + +/* em_exchange_mark(): + * Exchange the cursor and mark + * [^X^X] + */ +protected el_action_t +/*ARGSUSED*/ +em_exchange_mark(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *cp; + + cp = el->el_line.cursor; + el->el_line.cursor = el->el_chared.c_kill.mark; + el->el_chared.c_kill.mark = cp; + return CC_CURSOR; +} + + +/* em_universal_argument(): + * Universal argument (argument times 4) + * [^U] + */ +protected el_action_t +/*ARGSUSED*/ +em_universal_argument(EditLine *el, wint_t c __attribute__((__unused__))) +{ /* multiply current argument by 4 */ + + if (el->el_state.argument > 1000000) + return CC_ERROR; + el->el_state.doingarg = 1; + el->el_state.argument *= 4; + return CC_ARGHACK; +} + + +/* em_meta_next(): + * Add 8th bit to next character typed + * [] + */ +protected el_action_t +/*ARGSUSED*/ +em_meta_next(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_state.metanext = 1; + return CC_ARGHACK; +} + + +/* em_toggle_overwrite(): + * Switch from insert to overwrite mode or vice versa + */ +protected el_action_t +/*ARGSUSED*/ +em_toggle_overwrite(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ? + MODE_REPLACE : MODE_INSERT; + return CC_NORM; +} + + +/* em_copy_prev_word(): + * Copy current word to cursor + */ +protected el_action_t +/*ARGSUSED*/ +em_copy_prev_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *cp, *oldc, *dp; + + if (el->el_line.cursor == el->el_line.buffer) + return CC_ERROR; + + oldc = el->el_line.cursor; + /* does a bounds check */ + cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, + el->el_state.argument, ce__isword); + + c_insert(el, (int)(oldc - cp)); + for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++) + *dp++ = *cp; + + el->el_line.cursor = dp;/* put cursor at end */ + + return CC_REFRESH; +} + + +/* em_inc_search_next(): + * Emacs incremental next search + */ +protected el_action_t +/*ARGSUSED*/ +em_inc_search_next(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_search.patlen = 0; + return ce_inc_search(el, ED_SEARCH_NEXT_HISTORY); +} + + +/* em_inc_search_prev(): + * Emacs incremental reverse search + */ +protected el_action_t +/*ARGSUSED*/ +em_inc_search_prev(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_search.patlen = 0; + return ce_inc_search(el, ED_SEARCH_PREV_HISTORY); +} + + +/* em_delete_prev_char(): + * Delete the character to the left of the cursor + * [^?] + */ +protected el_action_t +/*ARGSUSED*/ +em_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor <= el->el_line.buffer) + return CC_ERROR; + + if (el->el_state.doingarg) + c_delbefore(el, el->el_state.argument); + else + c_delbefore1(el); + el->el_line.cursor -= el->el_state.argument; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; + return CC_REFRESH; +} diff --git a/lib/libedit/emacs.h b/lib/libedit/emacs.h deleted file mode 100644 index 60a5e9e..0000000 --- a/lib/libedit/emacs.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Automatically generated file, do not edit */ -#ifndef _h_emacs_c -#define _h_emacs_c -protected el_action_t em_delete_or_list (EditLine *, int); -protected el_action_t em_delete_next_word (EditLine *, int); -protected el_action_t em_yank (EditLine *, int); -protected el_action_t em_kill_line (EditLine *, int); -protected el_action_t em_kill_region (EditLine *, int); -protected el_action_t em_copy_region (EditLine *, int); -protected el_action_t em_gosmacs_transpose (EditLine *, int); -protected el_action_t em_next_word (EditLine *, int); -protected el_action_t em_upper_case (EditLine *, int); -protected el_action_t em_capitol_case (EditLine *, int); -protected el_action_t em_lower_case (EditLine *, int); -protected el_action_t em_set_mark (EditLine *, int); -protected el_action_t em_exchange_mark (EditLine *, int); -protected el_action_t em_universal_argument (EditLine *, int); -protected el_action_t em_meta_next (EditLine *, int); -protected el_action_t em_toggle_overwrite (EditLine *, int); -protected el_action_t em_copy_prev_word (EditLine *, int); -protected el_action_t em_inc_search_next (EditLine *, int); -protected el_action_t em_inc_search_prev (EditLine *, int); -protected el_action_t em_delete_prev_char (EditLine *, int); -#endif /* _h_emacs_c */ diff --git a/lib/libedit/fcns.h b/lib/libedit/fcns.h deleted file mode 100644 index adf9c48..0000000 --- a/lib/libedit/fcns.h +++ /dev/null @@ -1,109 +0,0 @@ -/* Automatically generated file, do not edit */ -#ifndef _h_fcns_c -#define _h_fcns_c -#define ED_ARGUMENT_DIGIT 0 -#define ED_CLEAR_SCREEN 1 -#define ED_COMMAND 2 -#define ED_DELETE_NEXT_CHAR 3 -#define ED_DELETE_PREV_CHAR 4 -#define ED_DELETE_PREV_WORD 5 -#define ED_DIGIT 6 -#define ED_END_OF_FILE 7 -#define ED_INSERT 8 -#define ED_KILL_LINE 9 -#define ED_MOVE_TO_BEG 10 -#define ED_MOVE_TO_END 11 -#define ED_NEWLINE 12 -#define ED_NEXT_CHAR 13 -#define ED_NEXT_HISTORY 14 -#define ED_NEXT_LINE 15 -#define ED_PREV_CHAR 16 -#define ED_PREV_HISTORY 17 -#define ED_PREV_LINE 18 -#define ED_PREV_WORD 19 -#define ED_QUOTED_INSERT 20 -#define ED_REDISPLAY 21 -#define ED_SEARCH_NEXT_HISTORY 22 -#define ED_SEARCH_PREV_HISTORY 23 -#define ED_SEQUENCE_LEAD_IN 24 -#define ED_START_OVER 25 -#define ED_TRANSPOSE_CHARS 26 -#define ED_TTY_DSUSP 27 -#define ED_TTY_FLUSH_OUTPUT 28 -#define ED_TTY_SIGINT 29 -#define ED_TTY_SIGQUIT 30 -#define ED_TTY_SIGTSTP 31 -#define ED_TTY_START_OUTPUT 32 -#define ED_TTY_STOP_OUTPUT 33 -#define ED_UNASSIGNED 34 -#define EM_CAPITOL_CASE 35 -#define EM_COPY_PREV_WORD 36 -#define EM_COPY_REGION 37 -#define EM_DELETE_NEXT_WORD 38 -#define EM_DELETE_OR_LIST 39 -#define EM_DELETE_PREV_CHAR 40 -#define EM_EXCHANGE_MARK 41 -#define EM_GOSMACS_TRANSPOSE 42 -#define EM_INC_SEARCH_NEXT 43 -#define EM_INC_SEARCH_PREV 44 -#define EM_KILL_LINE 45 -#define EM_KILL_REGION 46 -#define EM_LOWER_CASE 47 -#define EM_META_NEXT 48 -#define EM_NEXT_WORD 49 -#define EM_SET_MARK 50 -#define EM_TOGGLE_OVERWRITE 51 -#define EM_UNIVERSAL_ARGUMENT 52 -#define EM_UPPER_CASE 53 -#define EM_YANK 54 -#define VI_ADD 55 -#define VI_ADD_AT_EOL 56 -#define VI_ALIAS 57 -#define VI_CHANGE_CASE 58 -#define VI_CHANGE_META 59 -#define VI_CHANGE_TO_EOL 60 -#define VI_COMMAND_MODE 61 -#define VI_COMMENT_OUT 62 -#define VI_DELETE_META 63 -#define VI_DELETE_PREV_CHAR 64 -#define VI_END_BIG_WORD 65 -#define VI_END_WORD 66 -#define VI_HISTEDIT 67 -#define VI_HISTORY_WORD 68 -#define VI_INSERT 69 -#define VI_INSERT_AT_BOL 70 -#define VI_KILL_LINE_PREV 71 -#define VI_LIST_OR_EOF 72 -#define VI_MATCH 73 -#define VI_NEXT_BIG_WORD 74 -#define VI_NEXT_CHAR 75 -#define VI_NEXT_WORD 76 -#define VI_PASTE_NEXT 77 -#define VI_PASTE_PREV 78 -#define VI_PREV_BIG_WORD 79 -#define VI_PREV_CHAR 80 -#define VI_PREV_WORD 81 -#define VI_REDO 82 -#define VI_REPEAT_NEXT_CHAR 83 -#define VI_REPEAT_PREV_CHAR 84 -#define VI_REPEAT_SEARCH_NEXT 85 -#define VI_REPEAT_SEARCH_PREV 86 -#define VI_REPLACE_CHAR 87 -#define VI_REPLACE_MODE 88 -#define VI_SEARCH_NEXT 89 -#define VI_SEARCH_PREV 90 -#define VI_SUBSTITUTE_CHAR 91 -#define VI_SUBSTITUTE_LINE 92 -#define VI_TO_COLUMN 93 -#define VI_TO_HISTORY_LINE 94 -#define VI_TO_NEXT_CHAR 95 -#define VI_TO_PREV_CHAR 96 -#define VI_UNDO 97 -#define VI_UNDO_LINE 98 -#define VI_YANK 99 -#define VI_YANK_END 100 -#define VI_ZERO 101 -#define EL_NUM_FCNS 102 -typedef el_action_t (*el_func_t)(EditLine *, int); -protected const el_func_t* func__get(void); -#endif /* _h_fcns_c */ diff --git a/lib/libedit/filecomplete.c b/lib/libedit/filecomplete.c new file mode 100644 index 0000000..7a7699d --- /dev/null +++ b/lib/libedit/filecomplete.c @@ -0,0 +1,697 @@ +/* $NetBSD: filecomplete.c,v 1.40 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +__RCSID("$NetBSD: filecomplete.c,v 1.40 2016/02/17 19:47:49 christos Exp $"); +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/filecomplete.c 313981 2017-02-20 03:33:59Z pfg $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "el.h" +#include "filecomplete.h" + +static const Char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', + '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; +/* Tilde is deliberately omitted here, we treat it specially. */ +static const Char extra_quote_chars[] = { ')', '}', '*', '?', '[', '$', '\0' }; + + +/********************************/ +/* completion functions */ + +/* + * does tilde expansion of strings of type ``~user/foo'' + * if ``user'' isn't valid user name or ``txt'' doesn't start + * w/ '~', returns pointer to strdup()ed copy of ``txt'' + * + * it's the caller's responsibility to free() the returned string + */ +char * +fn_tilde_expand(const char *txt) +{ +#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) + struct passwd pwres; + char pwbuf[1024]; +#endif + struct passwd *pass; + char *temp; + size_t len = 0; + + if (txt[0] != '~') + return strdup(txt); + + temp = strchr(txt + 1, '/'); + if (temp == NULL) { + temp = strdup(txt + 1); + if (temp == NULL) + return NULL; + } else { + /* text until string after slash */ + len = (size_t)(temp - txt + 1); + temp = el_malloc(len * sizeof(*temp)); + if (temp == NULL) + return NULL; + (void)strncpy(temp, txt + 1, len - 2); + temp[len - 2] = '\0'; + } + if (temp[0] == 0) { +#ifdef HAVE_GETPW_R_POSIX + if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), + &pass) != 0) + pass = NULL; +#elif HAVE_GETPW_R_DRAFT + pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf)); +#else + pass = getpwuid(getuid()); +#endif + } else { +#ifdef HAVE_GETPW_R_POSIX + if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) + pass = NULL; +#elif HAVE_GETPW_R_DRAFT + pass = getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf)); +#else + pass = getpwnam(temp); +#endif + } + el_free(temp); /* value no more needed */ + if (pass == NULL) + return strdup(txt); + + /* update pointer txt to point at string immedially following */ + /* first slash */ + txt += len; + + len = strlen(pass->pw_dir) + 1 + strlen(txt) + 1; + temp = el_malloc(len * sizeof(*temp)); + if (temp == NULL) + return NULL; + (void)snprintf(temp, len, "%s/%s", pass->pw_dir, txt); + + return temp; +} + + +/* + * return first found file name starting by the ``text'' or NULL if no + * such file can be found + * value of ``state'' is ignored + * + * it's the caller's responsibility to free the returned string + */ +char * +fn_filename_completion_function(const char *text, int state) +{ + static DIR *dir = NULL; + static char *filename = NULL, *dirname = NULL, *dirpath = NULL; + static size_t filename_len = 0; + struct dirent *entry; + char *temp; + size_t len; + + if (state == 0 || dir == NULL) { + temp = strrchr(text, '/'); + if (temp) { + char *nptr; + temp++; + nptr = el_realloc(filename, (strlen(temp) + 1) * + sizeof(*nptr)); + if (nptr == NULL) { + el_free(filename); + filename = NULL; + return NULL; + } + filename = nptr; + (void)strcpy(filename, temp); + len = (size_t)(temp - text); /* including last slash */ + + nptr = el_realloc(dirname, (len + 1) * + sizeof(*nptr)); + if (nptr == NULL) { + el_free(dirname); + dirname = NULL; + return NULL; + } + dirname = nptr; + (void)strncpy(dirname, text, len); + dirname[len] = '\0'; + } else { + el_free(filename); + if (*text == 0) + filename = NULL; + else { + filename = strdup(text); + if (filename == NULL) + return NULL; + } + el_free(dirname); + dirname = NULL; + } + + if (dir != NULL) { + (void)closedir(dir); + dir = NULL; + } + + /* support for ``~user'' syntax */ + + el_free(dirpath); + dirpath = NULL; + if (dirname == NULL) { + if ((dirname = strdup("")) == NULL) + return NULL; + dirpath = strdup("./"); + } else if (*dirname == '~') + dirpath = fn_tilde_expand(dirname); + else + dirpath = strdup(dirname); + + if (dirpath == NULL) + return NULL; + + dir = opendir(dirpath); + if (!dir) + return NULL; /* cannot open the directory */ + + /* will be used in cycle */ + filename_len = filename ? strlen(filename) : 0; + } + + /* find the match */ + while ((entry = readdir(dir)) != NULL) { + /* skip . and .. */ + if (entry->d_name[0] == '.' && (!entry->d_name[1] + || (entry->d_name[1] == '.' && !entry->d_name[2]))) + continue; + if (filename_len == 0) + break; + /* otherwise, get first entry where first */ + /* filename_len characters are equal */ + if (entry->d_name[0] == filename[0] +#if HAVE_STRUCT_DIRENT_D_NAMLEN + && entry->d_namlen >= filename_len +#else + && strlen(entry->d_name) >= filename_len +#endif + && strncmp(entry->d_name, filename, + filename_len) == 0) + break; + } + + if (entry) { /* match found */ + +#if HAVE_STRUCT_DIRENT_D_NAMLEN + len = entry->d_namlen; +#else + len = strlen(entry->d_name); +#endif + + len = strlen(dirname) + len + 1; + temp = el_malloc(len * sizeof(*temp)); + if (temp == NULL) + return NULL; + (void)snprintf(temp, len, "%s%s", dirname, entry->d_name); + } else { + (void)closedir(dir); + dir = NULL; + temp = NULL; + } + + return temp; +} + + +static const char * +append_char_function(const char *name) +{ + struct stat stbuf; + char *expname = *name == '~' ? fn_tilde_expand(name) : NULL; + const char *rs = " "; + + if (stat(expname ? expname : name, &stbuf) == -1) + goto out; + if (S_ISDIR(stbuf.st_mode)) + rs = "/"; +out: + if (expname) + el_free(expname); + return rs; +} +/* + * returns list of completions for text given + * non-static for readline. + */ +char ** completion_matches(const char *, char *(*)(const char *, int)); +char ** +completion_matches(const char *text, char *(*genfunc)(const char *, int)) +{ + char **match_list = NULL, *retstr, *prevstr; + size_t match_list_len, max_equal, which, i; + size_t matches; + + matches = 0; + match_list_len = 1; + while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { + /* allow for list terminator here */ + if (matches + 3 >= match_list_len) { + char **nmatch_list; + while (matches + 3 >= match_list_len) + match_list_len <<= 1; + nmatch_list = el_realloc(match_list, + match_list_len * sizeof(*nmatch_list)); + if (nmatch_list == NULL) { + el_free(match_list); + return NULL; + } + match_list = nmatch_list; + + } + match_list[++matches] = retstr; + } + + if (!match_list) + return NULL; /* nothing found */ + + /* find least denominator and insert it to match_list[0] */ + which = 2; + prevstr = match_list[1]; + max_equal = strlen(prevstr); + for (; which <= matches; which++) { + for (i = 0; i < max_equal && + prevstr[i] == match_list[which][i]; i++) + continue; + max_equal = i; + } + + retstr = el_malloc((max_equal + 1) * sizeof(*retstr)); + if (retstr == NULL) { + el_free(match_list); + return NULL; + } + (void)strncpy(retstr, match_list[1], max_equal); + retstr[max_equal] = '\0'; + match_list[0] = retstr; + + /* add NULL as last pointer to the array */ + match_list[matches + 1] = NULL; + + return match_list; +} + +/* + * Sort function for qsort(). Just wrapper around strcasecmp(). + */ +static int +_fn_qsort_string_compare(const void *i1, const void *i2) +{ + const char *s1 = ((const char * const *)i1)[0]; + const char *s2 = ((const char * const *)i2)[0]; + + return strcasecmp(s1, s2); +} + +/* + * Display list of strings in columnar format on readline's output stream. + * 'matches' is list of strings, 'num' is number of strings in 'matches', + * 'width' is maximum length of string in 'matches'. + * + * matches[0] is not one of the match strings, but it is counted in + * num, so the strings are matches[1] *through* matches[num-1]. + */ +void +fn_display_match_list (EditLine *el, char **matches, size_t num, size_t width) +{ + size_t line, lines, col, cols, thisguy; + int screenwidth = el->el_terminal.t_size.h; + + /* Ignore matches[0]. Avoid 1-based array logic below. */ + matches++; + num--; + + /* + * Find out how many entries can be put on one line; count + * with one space between strings the same way it's printed. + */ + cols = (size_t)screenwidth / (width + 1); + if (cols == 0) + cols = 1; + + /* how many lines of output, rounded up */ + lines = (num + cols - 1) / cols; + + /* Sort the items. */ + qsort(matches, num, sizeof(char *), _fn_qsort_string_compare); + + /* + * On the ith line print elements i, i+lines, i+lines*2, etc. + */ + for (line = 0; line < lines; line++) { + for (col = 0; col < cols; col++) { + thisguy = line + col * lines; + if (thisguy >= num) + break; + (void)fprintf(el->el_outfile, "%s%-*s", + col == 0 ? "" : " ", (int)width, matches[thisguy]); + } + (void)fprintf(el->el_outfile, "\n"); + } +} + +/* + * Complete the word at or before point, + * 'what_to_do' says what to do with the completion. + * \t means do standard completion. + * `?' means list the possible completions. + * `*' means insert all of the possible completions. + * `!' means to do standard completion, and list all possible completions if + * there is more than one. + * + * Note: '*' support is not implemented + * '!' could never be invoked + */ +int +fn_complete(EditLine *el, + char *(*complet_func)(const char *, int), + char **(*attempted_completion_function)(const char *, int, int), + const Char *word_break, const Char *special_prefixes, + const char *(*app_func)(const char *), size_t query_items, + int *completion_type, int *over, int *point, int *end, + const Char *(*find_word_start_func)(const Char *, const Char *), + Char *(*dequoting_func)(const Char *), + char *(*quoting_func)(const char *)) +{ + const TYPE(LineInfo) *li; + Char *temp; + Char *dequoted_temp; + char **matches; + const Char *ctemp; + size_t len; + int what_to_do = '\t'; + int retval = CC_NORM; + + if (el->el_state.lastcmd == el->el_state.thiscmd) + what_to_do = '?'; + + /* readline's rl_complete() has to be told what we did... */ + if (completion_type != NULL) + *completion_type = what_to_do; + + if (!complet_func) + complet_func = fn_filename_completion_function; + if (!app_func) + app_func = append_char_function; + + /* We now look backwards for the start of a filename/variable word */ + li = FUN(el,line)(el); + if (find_word_start_func) + ctemp = find_word_start_func(li->buffer, li->cursor); + else { + ctemp = li->cursor; + while (ctemp > li->buffer + && !Strchr(word_break, ctemp[-1]) + && (!special_prefixes || !Strchr(special_prefixes, ctemp[-1]) ) ) + ctemp--; + } + + len = (size_t)(li->cursor - ctemp); + temp = el_malloc((len + 1) * sizeof(*temp)); + (void)Strncpy(temp, ctemp, len); + temp[len] = '\0'; + + if (dequoting_func) { + dequoted_temp = dequoting_func(temp); + if (dequoted_temp == NULL) + return retval; + } else + dequoted_temp = NULL; + + /* these can be used by function called in completion_matches() */ + /* or (*attempted_completion_function)() */ + if (point != NULL) + *point = (int)(li->cursor - li->buffer); + if (end != NULL) + *end = (int)(li->lastchar - li->buffer); + + if (attempted_completion_function) { + int cur_off = (int)(li->cursor - li->buffer); + matches = (*attempted_completion_function)( + ct_encode_string(dequoted_temp ? dequoted_temp : temp, + &el->el_scratch), + cur_off - (int)len, cur_off); + } else + matches = NULL; + if (!attempted_completion_function || + (over != NULL && !*over && !matches)) + matches = completion_matches( + ct_encode_string(dequoted_temp ? dequoted_temp : temp, + &el->el_scratch), complet_func); + + if (over != NULL) + *over = 0; + + if (matches) { + int i; + size_t matches_num, maxlen, match_len, match_display=1; + + retval = CC_REFRESH; + /* + * Only replace the completed string with common part of + * possible matches if there is possible completion. + */ + if (matches[0][0] != '\0') { + char *quoted_match; + if (quoting_func) { + quoted_match = quoting_func(matches[0]); + if (quoted_match == NULL) + goto free_matches; + } else + quoted_match = NULL; + el_deletestr(el, (int) len); + FUN(el,insertstr)(el, + ct_decode_string(quoted_match ? quoted_match : + matches[0] , &el->el_scratch)); + } + + if (what_to_do == '?') + goto display_matches; + + if (matches[2] == NULL && + (matches[1] == NULL || strcmp(matches[0], matches[1]) == 0)) { + /* + * We found exact match. Add a space after + * it, unless we do filename completion and the + * object is a directory. + */ + FUN(el,insertstr)(el, + ct_decode_string((*app_func)(matches[0]), + &el->el_scratch)); + } else if (what_to_do == '!') { + display_matches: + /* + * More than one match and requested to list possible + * matches. + */ + + for(i = 1, maxlen = 0; matches[i]; i++) { + match_len = strlen(matches[i]); + if (match_len > maxlen) + maxlen = match_len; + } + /* matches[1] through matches[i-1] are available */ + matches_num = (size_t)(i - 1); + + /* newline to get on next line from command line */ + (void)fprintf(el->el_outfile, "\n"); + + /* + * If there are too many items, ask user for display + * confirmation. + */ + if (matches_num > query_items) { + (void)fprintf(el->el_outfile, + "Display all %zu possibilities? (y or n) ", + matches_num); + (void)fflush(el->el_outfile); + if (getc(stdin) != 'y') + match_display = 0; + (void)fprintf(el->el_outfile, "\n"); + } + + if (match_display) { + /* + * Interface of this function requires the + * strings be matches[1..num-1] for compat. + * We have matches_num strings not counting + * the prefix in matches[0], so we need to + * add 1 to matches_num for the call. + */ + fn_display_match_list(el, matches, + matches_num+1, maxlen); + } + retval = CC_REDISPLAY; + } else if (matches[0][0]) { + /* + * There was some common match, but the name was + * not complete enough. Next tab will print possible + * completions. + */ + el_beep(el); + } else { + /* lcd is not a valid object - further specification */ + /* is needed */ + el_beep(el); + retval = CC_NORM; + } + +free_matches: + /* free elements of array and the array itself */ + for (i = 0; matches[i]; i++) + el_free(matches[i]); + el_free(matches); + matches = NULL; + } + free(dequoted_temp); + el_free(temp); + return retval; +} + +/* + * el-compatible wrapper around rl_complete; needed for key binding + */ +/* ARGSUSED */ +unsigned char +_el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) +{ + return (unsigned char)fn_complete(el, NULL, NULL, + break_chars, NULL, NULL, (size_t)100, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL); +} + +static const Char * +sh_find_word_start(const Char *buffer, const Char *cursor) +{ + const Char *word_start = buffer; + + while (buffer < cursor) { + if (*buffer == '\\') + buffer++; + else if (Strchr(break_chars, *buffer)) + word_start = buffer + 1; + buffer++; + } + return word_start; +} + +static char * +sh_quote(const char *str) +{ + const char *src; + int extra_len = 0; + char *quoted_str, *dst; + + for (src = str; *src != '\0'; src++) + if (Strchr(break_chars, *src) || + Strchr(extra_quote_chars, *src)) + extra_len++; + + quoted_str = malloc(sizeof(*quoted_str) * + (strlen(str) + extra_len + 1)); + if (quoted_str == NULL) + return NULL; + + dst = quoted_str; + for (src = str; *src != '\0'; src++) { + if (Strchr(break_chars, *src) || + Strchr(extra_quote_chars, *src)) + *dst++ = '\\'; + *dst++ = *src; + } + *dst = '\0'; + + return quoted_str; +} + +static Char * +sh_dequote(const Char *str) +{ + Char *dequoted_str, *dst; + + /* save extra space to replace \~ with ./~ */ + dequoted_str = malloc(sizeof(*dequoted_str) * (Strlen(str) + 1 + 1)); + if (dequoted_str == NULL) + return NULL; + + dst = dequoted_str; + + /* dequote \~ at start as ./~ */ + if (*str == '\\' && str[1] == '~') { + str++; + *dst++ = '.'; + *dst++ = '/'; + } + + while (*str) { + if (*str == '\\') + str++; + if (*str) + *dst++ = *str++; + } + *dst = '\0'; + + return dequoted_str; +} + +/* + * completion function using sh quoting rules; for key binding + */ +/* ARGSUSED */ +unsigned char +_el_fn_sh_complete(EditLine *el, int ch __attribute__((__unused__))) +{ + return (unsigned char)fn_complete(el, NULL, NULL, + break_chars, NULL, NULL, 100, + NULL, NULL, NULL, NULL, + sh_find_word_start, sh_dequote, sh_quote); +} diff --git a/lib/libedit/filecomplete.h b/lib/libedit/filecomplete.h new file mode 100644 index 0000000..26e7dee --- /dev/null +++ b/lib/libedit/filecomplete.h @@ -0,0 +1,49 @@ +/* $NetBSD: filecomplete.h,v 1.9 2009/12/30 22:37:40 christos Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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: releng/11.1/lib/libedit/filecomplete.h 276881 2015-01-09 07:40:56Z bapt $ + */ +#ifndef _FILECOMPLETE_H_ +#define _FILECOMPLETE_H_ + +int fn_complete(EditLine *, + char *(*)(const char *, int), + char **(*)(const char *, int, int), + const Char *, const Char *, const char *(*)(const char *), size_t, + int *, int *, int *, int *, + const Char *(*)(const Char *, const Char *), + Char *(*)(const Char *), + char *(*)(const char *)); + +void fn_display_match_list(EditLine *, char **, size_t, size_t); +char *fn_tilde_expand(const char *); +char *fn_filename_completion_function(const char *, int); + +#endif diff --git a/lib/libedit/help.h b/lib/libedit/help.h deleted file mode 100644 index a7e89a1..0000000 --- a/lib/libedit/help.h +++ /dev/null @@ -1,5 +0,0 @@ -/* Automatically generated file, do not edit */ -#ifndef _h_help_c -#define _h_help_c -protected const el_bindings_t *help__get(void); -#endif /* _h_help_c */ diff --git a/lib/libedit/hist.c b/lib/libedit/hist.c new file mode 100644 index 0000000..cd78990 --- /dev/null +++ b/lib/libedit/hist.c @@ -0,0 +1,227 @@ +/* $NetBSD: hist.c,v 1.24 2016/02/16 22:53:14 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)hist.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: hist.c,v 1.24 2016/02/16 22:53:14 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/hist.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * hist.c: History access functions + */ +#include +#include + +#include "el.h" + +/* hist_init(): + * Initialization function. + */ +protected int +hist_init(EditLine *el) +{ + + el->el_history.fun = NULL; + el->el_history.ref = NULL; + el->el_history.buf = el_malloc(EL_BUFSIZ * sizeof(*el->el_history.buf)); + el->el_history.sz = EL_BUFSIZ; + if (el->el_history.buf == NULL) + return -1; + el->el_history.last = el->el_history.buf; + return 0; +} + + +/* hist_end(): + * clean up history; + */ +protected void +hist_end(EditLine *el) +{ + + el_free(el->el_history.buf); + el->el_history.buf = NULL; +} + + +/* hist_set(): + * Set new history interface + */ +protected int +hist_set(EditLine *el, hist_fun_t fun, void *ptr) +{ + + el->el_history.ref = ptr; + el->el_history.fun = fun; + return 0; +} + + +/* hist_get(): + * Get a history line and update it in the buffer. + * eventno tells us the event to get. + */ +protected el_action_t +hist_get(EditLine *el) +{ + const Char *hp; + int h; + + if (el->el_history.eventno == 0) { /* if really the current line */ + (void) Strncpy(el->el_line.buffer, el->el_history.buf, + el->el_history.sz); + el->el_line.lastchar = el->el_line.buffer + + (el->el_history.last - el->el_history.buf); + +#ifdef KSHVI + if (el->el_map.type == MAP_VI) + el->el_line.cursor = el->el_line.buffer; + else +#endif /* KSHVI */ + el->el_line.cursor = el->el_line.lastchar; + + return CC_REFRESH; + } + if (el->el_history.ref == NULL) + return CC_ERROR; + + hp = HIST_FIRST(el); + + if (hp == NULL) + return CC_ERROR; + + for (h = 1; h < el->el_history.eventno; h++) + if ((hp = HIST_NEXT(el)) == NULL) { + el->el_history.eventno = h; + return CC_ERROR; + } + (void) Strncpy(el->el_line.buffer, hp, + (size_t)(el->el_line.limit - el->el_line.buffer)); + el->el_line.buffer[el->el_line.limit - el->el_line.buffer - 1] = '\0'; + el->el_line.lastchar = el->el_line.buffer + Strlen(el->el_line.buffer); + + if (el->el_line.lastchar > el->el_line.buffer + && el->el_line.lastchar[-1] == '\n') + el->el_line.lastchar--; + if (el->el_line.lastchar > el->el_line.buffer + && el->el_line.lastchar[-1] == ' ') + el->el_line.lastchar--; +#ifdef KSHVI + if (el->el_map.type == MAP_VI) + el->el_line.cursor = el->el_line.buffer; + else +#endif /* KSHVI */ + el->el_line.cursor = el->el_line.lastchar; + + return CC_REFRESH; +} + + +/* hist_command() + * process a history command + */ +protected int +hist_command(EditLine *el, int argc, const Char **argv) +{ + const Char *str; + int num; + TYPE(HistEvent) ev; + + if (el->el_history.ref == NULL) + return -1; + + if (argc == 1 || Strcmp(argv[1], STR("list")) == 0) { + /* List history entries */ + + for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el)) + (void) fprintf(el->el_outfile, "%d %s", + el->el_history.ev.num, ct_encode_string(str, &el->el_scratch)); + return 0; + } + + if (argc != 3) + return -1; + + num = (int)Strtol(argv[2], NULL, 0); + + if (Strcmp(argv[1], STR("size")) == 0) + return FUNW(history)(el->el_history.ref, &ev, H_SETSIZE, num); + + if (Strcmp(argv[1], STR("unique")) == 0) + return FUNW(history)(el->el_history.ref, &ev, H_SETUNIQUE, num); + + return -1; +} + +/* hist_enlargebuf() + * Enlarge history buffer to specified value. Called from el_enlargebufs(). + * Return 0 for failure, 1 for success. + */ +protected int +/*ARGSUSED*/ +hist_enlargebuf(EditLine *el, size_t oldsz, size_t newsz) +{ + Char *newbuf; + + newbuf = el_realloc(el->el_history.buf, newsz * sizeof(*newbuf)); + if (!newbuf) + return 0; + + (void) memset(&newbuf[oldsz], '\0', (newsz - oldsz) * sizeof(*newbuf)); + + el->el_history.last = newbuf + + (el->el_history.last - el->el_history.buf); + el->el_history.buf = newbuf; + el->el_history.sz = newsz; + + return 1; +} + +#ifdef WIDECHAR +protected wchar_t * +hist_convert(EditLine *el, int fn, void *arg) +{ + HistEventW ev; + if ((*(el)->el_history.fun)((el)->el_history.ref, &ev, fn, arg) == -1) + return NULL; + return ct_decode_string((const char *)(const void *)ev.str, + &el->el_scratch); +} +#endif diff --git a/lib/libedit/hist.h b/lib/libedit/hist.h index 149e286..b8cb2df 100644 --- a/lib/libedit/hist.h +++ b/lib/libedit/hist.h @@ -1,3 +1,5 @@ +/* $NetBSD: hist.h,v 1.18 2016/02/17 19:47:49 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)hist.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: hist.h,v 1.10 2003/08/07 16:44:31 agc Exp $ - * $FreeBSD: src/lib/libedit/hist.h,v 1.3.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/hist.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -40,23 +41,29 @@ #ifndef _h_el_hist #define _h_el_hist -#include "histedit.h" - -typedef int (*hist_fun_t)(ptr_t, HistEvent *, int, ...); +typedef int (*hist_fun_t)(void *, TYPE(HistEvent) *, int, ...); typedef struct el_history_t { - char *buf; /* The history buffer */ - size_t sz; /* Size of history buffer */ - char *last; /* The last character */ + Char *buf; /* The history buffer */ + size_t sz; /* Size of history buffer */ + Char *last; /* The last character */ int eventno; /* Event we are looking for */ - ptr_t ref; /* Argument for history fcns */ + void *ref; /* Argument for history fcns */ hist_fun_t fun; /* Event access */ - HistEvent ev; /* Event cookie */ + TYPE(HistEvent) ev; /* Event cookie */ } el_history_t; -#define HIST_FUN(el, fn, arg) \ +#define HIST_FUN_INTERNAL(el, fn, arg) \ ((((*(el)->el_history.fun) ((el)->el_history.ref, &(el)->el_history.ev, \ fn, arg)) == -1) ? NULL : (el)->el_history.ev.str) +#ifdef WIDECHAR +#define HIST_FUN(el, fn, arg) \ + (((el)->el_flags & NARROW_HISTORY) ? hist_convert(el, fn, arg) : \ + HIST_FUN_INTERNAL(el, fn, arg)) +#else +#define HIST_FUN(el, fn, arg) HIST_FUN_INTERNAL(el, fn, arg) +#endif + #define HIST_NEXT(el) HIST_FUN(el, H_NEXT, NULL) #define HIST_FIRST(el) HIST_FUN(el, H_FIRST, NULL) @@ -65,12 +72,16 @@ #define HIST_SET(el, num) HIST_FUN(el, H_SET, num) #define HIST_LOAD(el, fname) HIST_FUN(el, H_LOAD fname) #define HIST_SAVE(el, fname) HIST_FUN(el, H_SAVE fname) +#define HIST_SAVE_FP(el, fp) HIST_FUN(el, H_SAVE_FP fp) protected int hist_init(EditLine *); protected void hist_end(EditLine *); protected el_action_t hist_get(EditLine *); -protected int hist_set(EditLine *, hist_fun_t, ptr_t); -protected int hist_command(EditLine *, int, const char **); +protected int hist_set(EditLine *, hist_fun_t, void *); +protected int hist_command(EditLine *, int, const Char **); protected int hist_enlargebuf(EditLine *, size_t, size_t); +#ifdef WIDECHAR +protected wchar_t *hist_convert(EditLine *, int, void *); +#endif #endif /* _h_el_hist */ diff --git a/lib/libedit/histedit.h b/lib/libedit/histedit.h new file mode 100644 index 0000000..bb8d1ac --- /dev/null +++ b/lib/libedit/histedit.h @@ -0,0 +1,313 @@ +/* $NetBSD: histedit.h,v 1.55 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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. + * + * @(#)histedit.h 8.2 (Berkeley) 1/3/94 + * $FreeBSD: releng/11.1/lib/libedit/histedit.h 313981 2017-02-20 03:33:59Z pfg $ + */ + +/* + * histedit.h: Line editor and history interface. + */ +#ifndef _HISTEDIT_H_ +#define _HISTEDIT_H_ + +#define LIBEDIT_MAJOR 2 +#define LIBEDIT_MINOR 11 + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ==== Editing ==== + */ + +typedef struct editline EditLine; + +/* + * For user-defined function interface + */ +typedef struct lineinfo { + const char *buffer; + const char *cursor; + const char *lastchar; +} LineInfo; + +/* + * EditLine editor function return codes. + * For user-defined function interface + */ +#define CC_NORM 0 +#define CC_NEWLINE 1 +#define CC_EOF 2 +#define CC_ARGHACK 3 +#define CC_REFRESH 4 +#define CC_CURSOR 5 +#define CC_ERROR 6 +#define CC_FATAL 7 +#define CC_REDISPLAY 8 +#define CC_REFRESH_BEEP 9 + +/* + * Initialization, cleanup, and resetting + */ +EditLine *el_init(const char *, FILE *, FILE *, FILE *); +EditLine *el_init_fd(const char *, FILE *, FILE *, FILE *, + int, int, int); +void el_end(EditLine *); +void el_reset(EditLine *); + +/* + * Get a line, a character or push a string back in the input queue + */ +const char *el_gets(EditLine *, int *); +int el_getc(EditLine *, char *); +void el_push(EditLine *, const char *); + +/* + * Beep! + */ +void el_beep(EditLine *); + +/* + * High level function internals control + * Parses argc, argv array and executes builtin editline commands + */ +int el_parse(EditLine *, int, const char **); + +/* + * Low level editline access functions + */ +int el_set(EditLine *, int, ...); +int el_get(EditLine *, int, ...); +unsigned char _el_fn_complete(EditLine *, int); +unsigned char _el_fn_sh_complete(EditLine *, int); + +/* + * el_set/el_get parameters + * + * When using el_wset/el_wget (as opposed to el_set/el_get): + * Char is wchar_t, otherwise it is char. + * prompt_func is el_wpfunc_t, otherwise it is el_pfunc_t . + + * Prompt function prototypes are: + * typedef char *(*el_pfunct_t) (EditLine *); + * typedef wchar_t *(*el_wpfunct_t) (EditLine *); + * + * For operations that support set or set/get, the argument types listed are for + * the "set" operation. For "get", each listed type must be a pointer. + * E.g. EL_EDITMODE takes an int when set, but an int* when get. + * + * Operations that only support "get" have the correct argument types listed. + */ +#define EL_PROMPT 0 /* , prompt_func); set/get */ +#define EL_TERMINAL 1 /* , const char *); set/get */ +#define EL_EDITOR 2 /* , const Char *); set/get */ +#define EL_SIGNAL 3 /* , int); set/get */ +#define EL_BIND 4 /* , const Char *, ..., NULL); set */ +#define EL_TELLTC 5 /* , const Char *, ..., NULL); set */ +#define EL_SETTC 6 /* , const Char *, ..., NULL); set */ +#define EL_ECHOTC 7 /* , const Char *, ..., NULL); set */ +#define EL_SETTY 8 /* , const Char *, ..., NULL); set */ +#define EL_ADDFN 9 /* , const Char *, const Char, set */ + /* el_func_t); */ +#define EL_HIST 10 /* , hist_fun_t, const void *); set */ +#define EL_EDITMODE 11 /* , int); set/get */ +#define EL_RPROMPT 12 /* , prompt_func); set/get */ +#define EL_GETCFN 13 /* , el_rfunc_t); set/get */ +#define EL_CLIENTDATA 14 /* , void *); set/get */ +#define EL_UNBUFFERED 15 /* , int); set/get */ +#define EL_PREP_TERM 16 /* , int); set */ +#define EL_GETTC 17 /* , const Char *, ..., NULL); get */ +#define EL_GETFP 18 /* , int, FILE **); get */ +#define EL_SETFP 19 /* , int, FILE *); set */ +#define EL_REFRESH 20 /* , void); set */ +#define EL_PROMPT_ESC 21 /* , prompt_func, Char); set/get */ +#define EL_RPROMPT_ESC 22 /* , prompt_func, Char); set/get */ +#define EL_RESIZE 23 /* , el_zfunc_t, void *); set */ +#define EL_ALIAS_TEXT 24 /* , el_afunc_t, void *); set */ + +#define EL_BUILTIN_GETCFN (NULL) + +/* + * Source named file or $PWD/.editrc or $HOME/.editrc + */ +int el_source(EditLine *, const char *); + +/* + * Must be called when the terminal changes size; If EL_SIGNAL + * is set this is done automatically otherwise it is the responsibility + * of the application + */ +void el_resize(EditLine *); + +/* + * User-defined function interface. + */ +const LineInfo *el_line(EditLine *); +int el_insertstr(EditLine *, const char *); +void el_deletestr(EditLine *, int); + + +/* + * ==== History ==== + */ + +typedef struct history History; + +typedef struct HistEvent { + int num; + const char *str; +} HistEvent; + +/* + * History access functions. + */ +History * history_init(void); +void history_end(History *); + +int history(History *, HistEvent *, int, ...); + +#define H_FUNC 0 /* , UTSL */ +#define H_SETSIZE 1 /* , const int); */ +#define H_GETSIZE 2 /* , void); */ +#define H_FIRST 3 /* , void); */ +#define H_LAST 4 /* , void); */ +#define H_PREV 5 /* , void); */ +#define H_NEXT 6 /* , void); */ +#define H_CURR 8 /* , const int); */ +#define H_SET 7 /* , int); */ +#define H_ADD 9 /* , const wchar_t *); */ +#define H_ENTER 10 /* , const wchar_t *); */ +#define H_APPEND 11 /* , const wchar_t *); */ +#define H_END 12 /* , void); */ +#define H_NEXT_STR 13 /* , const wchar_t *); */ +#define H_PREV_STR 14 /* , const wchar_t *); */ +#define H_NEXT_EVENT 15 /* , const int); */ +#define H_PREV_EVENT 16 /* , const int); */ +#define H_LOAD 17 /* , const char *); */ +#define H_SAVE 18 /* , const char *); */ +#define H_CLEAR 19 /* , void); */ +#define H_SETUNIQUE 20 /* , int); */ +#define H_GETUNIQUE 21 /* , void); */ +#define H_DEL 22 /* , int); */ +#define H_NEXT_EVDATA 23 /* , const int, histdata_t *); */ +#define H_DELDATA 24 /* , int, histdata_t *);*/ +#define H_REPLACE 25 /* , const char *, histdata_t); */ +#define H_SAVE_FP 26 /* , FILE *); */ + + + +/* + * ==== Tokenization ==== + */ + +typedef struct tokenizer Tokenizer; + +/* + * String tokenization functions, using simplified sh(1) quoting rules + */ +Tokenizer *tok_init(const char *); +void tok_end(Tokenizer *); +void tok_reset(Tokenizer *); +int tok_line(Tokenizer *, const LineInfo *, + int *, const char ***, int *, int *); +int tok_str(Tokenizer *, const char *, + int *, const char ***); + +/* + * Begin Wide Character Support + */ +#include +#include + +/* + * ==== Editing ==== + */ +typedef struct lineinfow { + const wchar_t *buffer; + const wchar_t *cursor; + const wchar_t *lastchar; +} LineInfoW; + +const wchar_t *el_wgets(EditLine *, int *); +int el_wgetc(EditLine *, wchar_t *); +void el_wpush(EditLine *, const wchar_t *); + +int el_wparse(EditLine *, int, const wchar_t **); + +int el_wset(EditLine *, int, ...); +int el_wget(EditLine *, int, ...); + +int el_cursor(EditLine *, int); +const LineInfoW *el_wline(EditLine *); +int el_winsertstr(EditLine *, const wchar_t *); +#define el_wdeletestr el_deletestr + +/* + * ==== History ==== + */ +typedef struct histeventW { + int num; + const wchar_t *str; +} HistEventW; + +typedef struct historyW HistoryW; + +HistoryW * history_winit(void); +void history_wend(HistoryW *); + +int history_w(HistoryW *, HistEventW *, int, ...); + +/* + * ==== Tokenization ==== + */ +typedef struct tokenizerW TokenizerW; + +/* Wide character tokenizer support */ +TokenizerW *tok_winit(const wchar_t *); +void tok_wend(TokenizerW *); +void tok_wreset(TokenizerW *); +int tok_wline(TokenizerW *, const LineInfoW *, + int *, const wchar_t ***, int *, int *); +int tok_wstr(TokenizerW *, const wchar_t *, + int *, const wchar_t ***); + +#ifdef __cplusplus +} +#endif + +#endif /* _HISTEDIT_H_ */ diff --git a/lib/libedit/history.c b/lib/libedit/history.c new file mode 100644 index 0000000..6ff6f54 --- /dev/null +++ b/lib/libedit/history.c @@ -0,0 +1,1122 @@ +/* $NetBSD: history.c,v 1.52 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: history.c,v 1.52 2016/02/17 19:47:49 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/history.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * hist.c: TYPE(History) access functions + */ +#include +#include +#include +#include +#include + +static const char hist_cookie[] = "_HiStOrY_V2_\n"; + +#include "histedit.h" +#include "chartype.h" + +typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *); +typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *); +typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *); +typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int); + +struct TYPE(history) { + void *h_ref; /* Argument for history fcns */ + int h_ent; /* Last entry point for history */ + history_gfun_t h_first; /* Get the first element */ + history_gfun_t h_next; /* Get the next element */ + history_gfun_t h_last; /* Get the last element */ + history_gfun_t h_prev; /* Get the previous element */ + history_gfun_t h_curr; /* Get the current element */ + history_sfun_t h_set; /* Set the current element */ + history_sfun_t h_del; /* Set the given element */ + history_vfun_t h_clear; /* Clear the history list */ + history_efun_t h_enter; /* Add an element */ + history_efun_t h_add; /* Append to an element */ +}; + +#define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev) +#define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev) +#define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev) +#define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev) +#define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev) +#define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n) +#define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev) +#define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str) +#define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str) +#define HDEL(h, ev, n) (*(h)->h_del)((h)->h_ref, ev, n) + +#define h_strdup(a) Strdup(a) +#define h_malloc(a) malloc(a) +#define h_realloc(a, b) realloc((a), (b)) +#define h_free(a) free(a) + +typedef struct { + int num; + Char *str; +} HistEventPrivate; + + + +private int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int); +private int history_getsize(TYPE(History) *, TYPE(HistEvent) *); +private int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int); +private int history_getunique(TYPE(History) *, TYPE(HistEvent) *); +private int history_set_fun(TYPE(History) *, TYPE(History) *); +private int history_load(TYPE(History) *, const char *); +private int history_save(TYPE(History) *, const char *); +private int history_save_fp(TYPE(History) *, FILE *); +private int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int); +private int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int); +private int history_next_string(TYPE(History) *, TYPE(HistEvent) *, const Char *); +private int history_prev_string(TYPE(History) *, TYPE(HistEvent) *, const Char *); + + +/***********************************************************************/ + +/* + * Builtin- history implementation + */ +typedef struct hentry_t { + TYPE(HistEvent) ev; /* What we return */ + void *data; /* data */ + struct hentry_t *next; /* Next entry */ + struct hentry_t *prev; /* Previous entry */ +} hentry_t; + +typedef struct history_t { + hentry_t list; /* Fake list header element */ + hentry_t *cursor; /* Current element in the list */ + int max; /* Maximum number of events */ + int cur; /* Current number of events */ + int eventid; /* For generation of unique event id */ + int flags; /* TYPE(History) flags */ +#define H_UNIQUE 1 /* Store only unique elements */ +} history_t; + +private int history_def_next(void *, TYPE(HistEvent) *); +private int history_def_first(void *, TYPE(HistEvent) *); +private int history_def_prev(void *, TYPE(HistEvent) *); +private int history_def_last(void *, TYPE(HistEvent) *); +private int history_def_curr(void *, TYPE(HistEvent) *); +private int history_def_set(void *, TYPE(HistEvent) *, const int); +private void history_def_clear(void *, TYPE(HistEvent) *); +private int history_def_enter(void *, TYPE(HistEvent) *, const Char *); +private int history_def_add(void *, TYPE(HistEvent) *, const Char *); +private int history_def_del(void *, TYPE(HistEvent) *, const int); + +private int history_def_init(void **, TYPE(HistEvent) *, int); +private int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *); +private void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *); + +private int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **); +private int history_set_nth(void *, TYPE(HistEvent) *, int); + +#define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num)) +#define history_def_getsize(p) (((history_t *)p)->cur) +#define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0) +#define history_def_setunique(p, uni) \ + if (uni) \ + (((history_t *)p)->flags) |= H_UNIQUE; \ + else \ + (((history_t *)p)->flags) &= ~H_UNIQUE + +#define he_strerror(code) he_errlist[code] +#define he_seterrev(evp, code) {\ + evp->num = code;\ + evp->str = he_strerror(code);\ + } + +/* error messages */ +static const Char *const he_errlist[] = { + STR("OK"), + STR("unknown error"), + STR("malloc() failed"), + STR("first event not found"), + STR("last event not found"), + STR("empty list"), + STR("no next event"), + STR("no previous event"), + STR("current event is invalid"), + STR("event not found"), + STR("can't read history from file"), + STR("can't write history"), + STR("required parameter(s) not supplied"), + STR("history size negative"), + STR("function not allowed with other history-functions-set the default"), + STR("bad parameters") +}; +/* error codes */ +#define _HE_OK 0 +#define _HE_UNKNOWN 1 +#define _HE_MALLOC_FAILED 2 +#define _HE_FIRST_NOTFOUND 3 +#define _HE_LAST_NOTFOUND 4 +#define _HE_EMPTY_LIST 5 +#define _HE_END_REACHED 6 +#define _HE_START_REACHED 7 +#define _HE_CURR_INVALID 8 +#define _HE_NOT_FOUND 9 +#define _HE_HIST_READ 10 +#define _HE_HIST_WRITE 11 +#define _HE_PARAM_MISSING 12 +#define _HE_SIZE_NEGATIVE 13 +#define _HE_NOT_ALLOWED 14 +#define _HE_BAD_PARAM 15 + +/* history_def_first(): + * Default function to return the first event in the history. + */ +private int +history_def_first(void *p, TYPE(HistEvent) *ev) +{ + history_t *h = (history_t *) p; + + h->cursor = h->list.next; + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, _HE_FIRST_NOTFOUND); + return -1; + } + + return 0; +} + + +/* history_def_last(): + * Default function to return the last event in the history. + */ +private int +history_def_last(void *p, TYPE(HistEvent) *ev) +{ + history_t *h = (history_t *) p; + + h->cursor = h->list.prev; + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, _HE_LAST_NOTFOUND); + return -1; + } + + return 0; +} + + +/* history_def_next(): + * Default function to return the next event in the history. + */ +private int +history_def_next(void *p, TYPE(HistEvent) *ev) +{ + history_t *h = (history_t *) p; + + if (h->cursor == &h->list) { + he_seterrev(ev, _HE_EMPTY_LIST); + return -1; + } + + if (h->cursor->next == &h->list) { + he_seterrev(ev, _HE_END_REACHED); + return -1; + } + + h->cursor = h->cursor->next; + *ev = h->cursor->ev; + + return 0; +} + + +/* history_def_prev(): + * Default function to return the previous event in the history. + */ +private int +history_def_prev(void *p, TYPE(HistEvent) *ev) +{ + history_t *h = (history_t *) p; + + if (h->cursor == &h->list) { + he_seterrev(ev, + (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST); + return -1; + } + + if (h->cursor->prev == &h->list) { + he_seterrev(ev, _HE_START_REACHED); + return -1; + } + + h->cursor = h->cursor->prev; + *ev = h->cursor->ev; + + return 0; +} + + +/* history_def_curr(): + * Default function to return the current event in the history. + */ +private int +history_def_curr(void *p, TYPE(HistEvent) *ev) +{ + history_t *h = (history_t *) p; + + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, + (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST); + return -1; + } + + return 0; +} + + +/* history_def_set(): + * Default function to set the current event in the history to the + * given one. + */ +private int +history_def_set(void *p, TYPE(HistEvent) *ev, const int n) +{ + history_t *h = (history_t *) p; + + if (h->cur == 0) { + he_seterrev(ev, _HE_EMPTY_LIST); + return -1; + } + if (h->cursor == &h->list || h->cursor->ev.num != n) { + for (h->cursor = h->list.next; h->cursor != &h->list; + h->cursor = h->cursor->next) + if (h->cursor->ev.num == n) + break; + } + if (h->cursor == &h->list) { + he_seterrev(ev, _HE_NOT_FOUND); + return -1; + } + return 0; +} + + +/* history_set_nth(): + * Default function to set the current event in the history to the + * n-th one. + */ +private int +history_set_nth(void *p, TYPE(HistEvent) *ev, int n) +{ + history_t *h = (history_t *) p; + + if (h->cur == 0) { + he_seterrev(ev, _HE_EMPTY_LIST); + return -1; + } + for (h->cursor = h->list.prev; h->cursor != &h->list; + h->cursor = h->cursor->prev) + if (n-- <= 0) + break; + if (h->cursor == &h->list) { + he_seterrev(ev, _HE_NOT_FOUND); + return -1; + } + return 0; +} + + +/* history_def_add(): + * Append string to element + */ +private int +history_def_add(void *p, TYPE(HistEvent) *ev, const Char *str) +{ + history_t *h = (history_t *) p; + size_t len; + Char *s; + HistEventPrivate *evp = (void *)&h->cursor->ev; + + if (h->cursor == &h->list) + return history_def_enter(p, ev, str); + len = Strlen(evp->str) + Strlen(str) + 1; + s = h_malloc(len * sizeof(*s)); + if (s == NULL) { + he_seterrev(ev, _HE_MALLOC_FAILED); + return -1; + } + (void) Strncpy(s, h->cursor->ev.str, len); + s[len - 1] = '\0'; + (void) Strncat(s, str, len - Strlen(s) - 1); + h_free(evp->str); + evp->str = s; + *ev = h->cursor->ev; + return 0; +} + + +private int +history_deldata_nth(history_t *h, TYPE(HistEvent) *ev, + int num, void **data) +{ + if (history_set_nth(h, ev, num) != 0) + return -1; + /* magic value to skip delete (just set to n-th history) */ + if (data == (void **)-1) + return 0; + ev->str = Strdup(h->cursor->ev.str); + ev->num = h->cursor->ev.num; + if (data) + *data = h->cursor->data; + history_def_delete(h, ev, h->cursor); + return 0; +} + + +/* history_def_del(): + * Delete element hp of the h list + */ +/* ARGSUSED */ +private int +history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)), + const int num) +{ + history_t *h = (history_t *) p; + if (history_def_set(h, ev, num) != 0) + return -1; + ev->str = Strdup(h->cursor->ev.str); + ev->num = h->cursor->ev.num; + history_def_delete(h, ev, h->cursor); + return 0; +} + + +/* history_def_delete(): + * Delete element hp of the h list + */ +/* ARGSUSED */ +private void +history_def_delete(history_t *h, + TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp) +{ + HistEventPrivate *evp = (void *)&hp->ev; + if (hp == &h->list) + abort(); + if (h->cursor == hp) { + h->cursor = hp->prev; + if (h->cursor == &h->list) + h->cursor = hp->next; + } + hp->prev->next = hp->next; + hp->next->prev = hp->prev; + h_free(evp->str); + h_free(hp); + h->cur--; +} + + +/* history_def_insert(): + * Insert element with string str in the h list + */ +private int +history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str) +{ + hentry_t *c; + + c = h_malloc(sizeof(*c)); + if (c == NULL) + goto oomem; + if ((c->ev.str = h_strdup(str)) == NULL) { + h_free(c); + goto oomem; + } + c->data = NULL; + c->ev.num = ++h->eventid; + c->next = h->list.next; + c->prev = &h->list; + h->list.next->prev = c; + h->list.next = c; + h->cur++; + h->cursor = c; + + *ev = c->ev; + return 0; +oomem: + he_seterrev(ev, _HE_MALLOC_FAILED); + return -1; +} + + +/* history_def_enter(): + * Default function to enter an item in the history + */ +private int +history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str) +{ + history_t *h = (history_t *) p; + + if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list && + Strcmp(h->list.next->ev.str, str) == 0) + return 0; + + if (history_def_insert(h, ev, str) == -1) + return -1; /* error, keep error message */ + + /* + * Always keep at least one entry. + * This way we don't have to check for the empty list. + */ + while (h->cur > h->max && h->cur > 0) + history_def_delete(h, ev, h->list.prev); + + return 1; +} + + +/* history_def_init(): + * Default history initialization function + */ +/* ARGSUSED */ +private int +history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n) +{ + history_t *h = (history_t *) h_malloc(sizeof(*h)); + if (h == NULL) + return -1; + + if (n <= 0) + n = 0; + h->eventid = 0; + h->cur = 0; + h->max = n; + h->list.next = h->list.prev = &h->list; + h->list.ev.str = NULL; + h->list.ev.num = 0; + h->cursor = &h->list; + h->flags = 0; + *p = h; + return 0; +} + + +/* history_def_clear(): + * Default history cleanup function + */ +private void +history_def_clear(void *p, TYPE(HistEvent) *ev) +{ + history_t *h = (history_t *) p; + + while (h->list.prev != &h->list) + history_def_delete(h, ev, h->list.prev); + h->cursor = &h->list; + h->eventid = 0; + h->cur = 0; +} + + + + +/************************************************************************/ + +/* history_init(): + * Initialization function. + */ +public TYPE(History) * +FUN(history,init)(void) +{ + TYPE(HistEvent) ev; + TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h)); + if (h == NULL) + return NULL; + + if (history_def_init(&h->h_ref, &ev, 0) == -1) { + h_free(h); + return NULL; + } + h->h_ent = -1; + h->h_next = history_def_next; + h->h_first = history_def_first; + h->h_last = history_def_last; + h->h_prev = history_def_prev; + h->h_curr = history_def_curr; + h->h_set = history_def_set; + h->h_clear = history_def_clear; + h->h_enter = history_def_enter; + h->h_add = history_def_add; + h->h_del = history_def_del; + + return h; +} + + +/* history_end(): + * clean up history; + */ +public void +FUN(history,end)(TYPE(History) *h) +{ + TYPE(HistEvent) ev; + + if (h->h_next == history_def_next) + history_def_clear(h->h_ref, &ev); + h_free(h->h_ref); + h_free(h); +} + + + +/* history_setsize(): + * Set history number of events + */ +private int +history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num) +{ + + if (h->h_next != history_def_next) { + he_seterrev(ev, _HE_NOT_ALLOWED); + return -1; + } + if (num < 0) { + he_seterrev(ev, _HE_BAD_PARAM); + return -1; + } + history_def_setsize(h->h_ref, num); + return 0; +} + + +/* history_getsize(): + * Get number of events currently in history + */ +private int +history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev) +{ + if (h->h_next != history_def_next) { + he_seterrev(ev, _HE_NOT_ALLOWED); + return -1; + } + ev->num = history_def_getsize(h->h_ref); + if (ev->num < -1) { + he_seterrev(ev, _HE_SIZE_NEGATIVE); + return -1; + } + return 0; +} + + +/* history_setunique(): + * Set if adjacent equal events should not be entered in history. + */ +private int +history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni) +{ + + if (h->h_next != history_def_next) { + he_seterrev(ev, _HE_NOT_ALLOWED); + return -1; + } + history_def_setunique(h->h_ref, uni); + return 0; +} + + +/* history_getunique(): + * Get if adjacent equal events should not be entered in history. + */ +private int +history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev) +{ + if (h->h_next != history_def_next) { + he_seterrev(ev, _HE_NOT_ALLOWED); + return -1; + } + ev->num = history_def_getunique(h->h_ref); + return 0; +} + + +/* history_set_fun(): + * Set history functions + */ +private int +history_set_fun(TYPE(History) *h, TYPE(History) *nh) +{ + TYPE(HistEvent) ev; + + if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL || + nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL || + nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL || + nh->h_del == NULL || nh->h_ref == NULL) { + if (h->h_next != history_def_next) { + if (history_def_init(&h->h_ref, &ev, 0) == -1) + return -1; + h->h_first = history_def_first; + h->h_next = history_def_next; + h->h_last = history_def_last; + h->h_prev = history_def_prev; + h->h_curr = history_def_curr; + h->h_set = history_def_set; + h->h_clear = history_def_clear; + h->h_enter = history_def_enter; + h->h_add = history_def_add; + h->h_del = history_def_del; + } + return -1; + } + if (h->h_next == history_def_next) + history_def_clear(h->h_ref, &ev); + + h->h_ent = -1; + h->h_first = nh->h_first; + h->h_next = nh->h_next; + h->h_last = nh->h_last; + h->h_prev = nh->h_prev; + h->h_curr = nh->h_curr; + h->h_set = nh->h_set; + h->h_clear = nh->h_clear; + h->h_enter = nh->h_enter; + h->h_add = nh->h_add; + h->h_del = nh->h_del; + + return 0; +} + + +/* history_load(): + * TYPE(History) load function + */ +private int +history_load(TYPE(History) *h, const char *fname) +{ + FILE *fp; + char *line; + size_t llen; + ssize_t sz; + size_t max_size; + char *ptr; + int i = -1; + TYPE(HistEvent) ev; +#ifdef WIDECHAR + static ct_buffer_t conv; +#endif + + if ((fp = fopen(fname, "r")) == NULL) + return i; + + line = NULL; + llen = 0; + if ((sz = getline(&line, &llen, fp)) == -1) + goto done; + + if (strncmp(line, hist_cookie, (size_t)sz) != 0) + goto done; + + ptr = h_malloc((max_size = 1024) * sizeof(*ptr)); + if (ptr == NULL) + goto done; + for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) { + if (sz > 0 && line[sz - 1] == '\n') + line[--sz] = '\0'; + if (max_size < (size_t)sz) { + char *nptr; + max_size = ((size_t)sz + 1024) & (size_t)~1023; + nptr = h_realloc(ptr, max_size * sizeof(*ptr)); + if (nptr == NULL) { + i = -1; + goto oomem; + } + ptr = nptr; + } + (void) strunvis(ptr, line); + if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) { + i = -1; + goto oomem; + } + } +oomem: + h_free(ptr); +done: + free(line); + (void) fclose(fp); + return i; +} + + +/* history_save_fp(): + * TYPE(History) save function + */ +private int +history_save_fp(TYPE(History) *h, FILE *fp) +{ + TYPE(HistEvent) ev; + int i = -1, retval; + size_t len, max_size; + char *ptr; + const char *str; +#ifdef WIDECHAR + static ct_buffer_t conv; +#endif + + if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) + goto done; + if (fputs(hist_cookie, fp) == EOF) + goto done; + ptr = h_malloc((max_size = 1024) * sizeof(*ptr)); + if (ptr == NULL) + goto done; + for (i = 0, retval = HLAST(h, &ev); + retval != -1; + retval = HPREV(h, &ev), i++) { + str = ct_encode_string(ev.str, &conv); + len = strlen(str) * 4 + 1; + if (len > max_size) { + char *nptr; + max_size = (len + 1024) & (size_t)~1023; + nptr = h_realloc(ptr, max_size * sizeof(*ptr)); + if (nptr == NULL) { + i = -1; + goto oomem; + } + ptr = nptr; + } + (void) strvis(ptr, str, VIS_WHITE); + (void) fprintf(fp, "%s\n", ptr); + } +oomem: + h_free(ptr); +done: + return i; +} + + +/* history_save(): + * History save function + */ +private int +history_save(TYPE(History) *h, const char *fname) +{ + FILE *fp; + int i; + + if ((fp = fopen(fname, "w")) == NULL) + return -1; + + i = history_save_fp(h, fp); + + (void) fclose(fp); + return i; +} + + +/* history_prev_event(): + * Find the previous event, with number given + */ +private int +history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num) +{ + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) + if (ev->num == num) + return 0; + + he_seterrev(ev, _HE_NOT_FOUND); + return -1; +} + + +private int +history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d) +{ + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) + if (ev->num == num) { + if (d) + *d = ((history_t *)h->h_ref)->cursor->data; + return 0; + } + + he_seterrev(ev, _HE_NOT_FOUND); + return -1; +} + + +/* history_next_event(): + * Find the next event, with number given + */ +private int +history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num) +{ + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) + if (ev->num == num) + return 0; + + he_seterrev(ev, _HE_NOT_FOUND); + return -1; +} + + +/* history_prev_string(): + * Find the previous event beginning with string + */ +private int +history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str) +{ + size_t len = Strlen(str); + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) + if (Strncmp(str, ev->str, len) == 0) + return 0; + + he_seterrev(ev, _HE_NOT_FOUND); + return -1; +} + + +/* history_next_string(): + * Find the next event beginning with string + */ +private int +history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str) +{ + size_t len = Strlen(str); + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) + if (Strncmp(str, ev->str, len) == 0) + return 0; + + he_seterrev(ev, _HE_NOT_FOUND); + return -1; +} + + +/* history(): + * User interface to history functions. + */ +int +FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...) +{ + va_list va; + const Char *str; + int retval; + + va_start(va, fun); + + he_seterrev(ev, _HE_OK); + + switch (fun) { + case H_GETSIZE: + retval = history_getsize(h, ev); + break; + + case H_SETSIZE: + retval = history_setsize(h, ev, va_arg(va, int)); + break; + + case H_GETUNIQUE: + retval = history_getunique(h, ev); + break; + + case H_SETUNIQUE: + retval = history_setunique(h, ev, va_arg(va, int)); + break; + + case H_ADD: + str = va_arg(va, const Char *); + retval = HADD(h, ev, str); + break; + + case H_DEL: + retval = HDEL(h, ev, va_arg(va, const int)); + break; + + case H_ENTER: + str = va_arg(va, const Char *); + if ((retval = HENTER(h, ev, str)) != -1) + h->h_ent = ev->num; + break; + + case H_APPEND: + str = va_arg(va, const Char *); + if ((retval = HSET(h, ev, h->h_ent)) != -1) + retval = HADD(h, ev, str); + break; + + case H_FIRST: + retval = HFIRST(h, ev); + break; + + case H_NEXT: + retval = HNEXT(h, ev); + break; + + case H_LAST: + retval = HLAST(h, ev); + break; + + case H_PREV: + retval = HPREV(h, ev); + break; + + case H_CURR: + retval = HCURR(h, ev); + break; + + case H_SET: + retval = HSET(h, ev, va_arg(va, const int)); + break; + + case H_CLEAR: + HCLEAR(h, ev); + retval = 0; + break; + + case H_LOAD: + retval = history_load(h, va_arg(va, const char *)); + if (retval == -1) + he_seterrev(ev, _HE_HIST_READ); + break; + + case H_SAVE: + retval = history_save(h, va_arg(va, const char *)); + if (retval == -1) + he_seterrev(ev, _HE_HIST_WRITE); + break; + + case H_SAVE_FP: + retval = history_save_fp(h, va_arg(va, FILE *)); + if (retval == -1) + he_seterrev(ev, _HE_HIST_WRITE); + break; + + case H_PREV_EVENT: + retval = history_prev_event(h, ev, va_arg(va, int)); + break; + + case H_NEXT_EVENT: + retval = history_next_event(h, ev, va_arg(va, int)); + break; + + case H_PREV_STR: + retval = history_prev_string(h, ev, va_arg(va, const Char *)); + break; + + case H_NEXT_STR: + retval = history_next_string(h, ev, va_arg(va, const Char *)); + break; + + case H_FUNC: + { + TYPE(History) hf; + + hf.h_ref = va_arg(va, void *); + h->h_ent = -1; + hf.h_first = va_arg(va, history_gfun_t); + hf.h_next = va_arg(va, history_gfun_t); + hf.h_last = va_arg(va, history_gfun_t); + hf.h_prev = va_arg(va, history_gfun_t); + hf.h_curr = va_arg(va, history_gfun_t); + hf.h_set = va_arg(va, history_sfun_t); + hf.h_clear = va_arg(va, history_vfun_t); + hf.h_enter = va_arg(va, history_efun_t); + hf.h_add = va_arg(va, history_efun_t); + hf.h_del = va_arg(va, history_sfun_t); + + if ((retval = history_set_fun(h, &hf)) == -1) + he_seterrev(ev, _HE_PARAM_MISSING); + break; + } + + case H_END: + FUN(history,end)(h); + retval = 0; + break; + + case H_NEXT_EVDATA: + { + int num = va_arg(va, int); + void **d = va_arg(va, void **); + retval = history_next_evdata(h, ev, num, d); + break; + } + + case H_DELDATA: + { + int num = va_arg(va, int); + void **d = va_arg(va, void **); + retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d); + break; + } + + case H_REPLACE: /* only use after H_NEXT_EVDATA */ + { + const Char *line = va_arg(va, const Char *); + void *d = va_arg(va, void *); + const Char *s; + if(!line || !(s = Strdup(line))) { + retval = -1; + break; + } + ((history_t *)h->h_ref)->cursor->ev.str = s; + ((history_t *)h->h_ref)->cursor->data = d; + retval = 0; + break; + } + + default: + retval = -1; + he_seterrev(ev, _HE_UNKNOWN); + break; + } + va_end(va); + return retval; +} diff --git a/lib/libedit/key.h b/lib/libedit/key.h deleted file mode 100644 index 848553e..0000000 --- a/lib/libedit/key.h +++ /dev/null @@ -1,81 +0,0 @@ -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Christos Zoulas of Cornell University. - * - * 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. - * - * @(#)key.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: key.h,v 1.8 2003/08/07 16:44:32 agc Exp $ - * $FreeBSD: src/lib/libedit/key.h,v 1.3.14.1 2005/10/09 03:44:01 delphij Exp $ - */ - -/* - * el.key.h: Key macro header - */ -#ifndef _h_el_key -#define _h_el_key - -typedef union key_value_t { - el_action_t cmd; /* If it is a command the # */ - char *str; /* If it is a string... */ -} key_value_t; - -typedef struct key_node_t key_node_t; - -typedef struct el_key_t { - char *buf; /* Key print buffer */ - key_node_t *map; /* Key map */ - key_value_t val; /* Local conversion buffer */ -} el_key_t; - -#define XK_CMD 0 -#define XK_STR 1 -#define XK_NOD 2 -#define XK_EXE 3 - -#include - -#undef key_end -#undef key_clear -#undef key_print - -protected int key_init(EditLine *); -protected void key_end(EditLine *); -protected key_value_t *key_map_cmd(EditLine *, int); -protected key_value_t *key_map_str(EditLine *, char *); -protected void key_reset(EditLine *); -protected int key_get(EditLine *, char *, key_value_t *); -protected void key_add(EditLine *, const char *, key_value_t *, int); -protected void key_clear(EditLine *, el_action_t *, const char *); -protected int key_delete(EditLine *, const char *); -protected void key_print(EditLine *, const char *); -protected void key_kprint(EditLine *, const char *, key_value_t *, - int); -protected char *key__decode_str(const char *, char *, const char *); - -#endif /* _h_el_key */ diff --git a/lib/libedit/keymacro.c b/lib/libedit/keymacro.c new file mode 100644 index 0000000..0516425 --- /dev/null +++ b/lib/libedit/keymacro.c @@ -0,0 +1,675 @@ +/* $NetBSD: keymacro.c,v 1.14 2016/02/24 14:25:38 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: keymacro.c,v 1.14 2016/02/24 14:25:38 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/keymacro.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * keymacro.c: This module contains the procedures for maintaining + * the extended-key map. + * + * An extended-key (key) is a sequence of keystrokes introduced + * with a sequence introducer and consisting of an arbitrary + * number of characters. This module maintains a map (the + * el->el_keymacro.map) + * to convert these extended-key sequences into input strs + * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE). + * + * Warning: + * If key is a substr of some other keys, then the longer + * keys are lost!! That is, if the keys "abcd" and "abcef" + * are in el->el_keymacro.map, adding the key "abc" will cause + * the first two definitions to be lost. + * + * Restrictions: + * ------------- + * 1) It is not possible to have one key that is a + * substr of another. + */ +#include +#include + +#include "el.h" + +/* + * The Nodes of the el->el_keymacro.map. The el->el_keymacro.map is a + * linked list of these node elements + */ +struct keymacro_node_t { + Char ch; /* single character of key */ + int type; /* node type */ + keymacro_value_t val; /* command code or pointer to str, */ + /* if this is a leaf */ + struct keymacro_node_t *next; /* ptr to next char of this key */ + struct keymacro_node_t *sibling;/* ptr to another key with same prefix*/ +}; + +private int node_trav(EditLine *, keymacro_node_t *, Char *, + keymacro_value_t *); +private int node__try(EditLine *, keymacro_node_t *, const Char *, + keymacro_value_t *, int); +private keymacro_node_t *node__get(wint_t); +private void node__free(keymacro_node_t *); +private void node__put(EditLine *, keymacro_node_t *); +private int node__delete(EditLine *, keymacro_node_t **, + const Char *); +private int node_lookup(EditLine *, const Char *, + keymacro_node_t *, size_t); +private int node_enum(EditLine *, keymacro_node_t *, size_t); + +#define KEY_BUFSIZ EL_BUFSIZ + + +/* keymacro_init(): + * Initialize the key maps + */ +protected int +keymacro_init(EditLine *el) +{ + + el->el_keymacro.buf = el_malloc(KEY_BUFSIZ * + sizeof(*el->el_keymacro.buf)); + if (el->el_keymacro.buf == NULL) + return -1; + el->el_keymacro.map = NULL; + keymacro_reset(el); + return 0; +} + +/* keymacro_end(): + * Free the key maps + */ +protected void +keymacro_end(EditLine *el) +{ + + el_free(el->el_keymacro.buf); + el->el_keymacro.buf = NULL; + node__free(el->el_keymacro.map); +} + + +/* keymacro_map_cmd(): + * Associate cmd with a key value + */ +protected keymacro_value_t * +keymacro_map_cmd(EditLine *el, int cmd) +{ + + el->el_keymacro.val.cmd = (el_action_t) cmd; + return &el->el_keymacro.val; +} + + +/* keymacro_map_str(): + * Associate str with a key value + */ +protected keymacro_value_t * +keymacro_map_str(EditLine *el, Char *str) +{ + + el->el_keymacro.val.str = str; + return &el->el_keymacro.val; +} + + +/* keymacro_reset(): + * Takes all nodes on el->el_keymacro.map and puts them on free list. + * Then initializes el->el_keymacro.map with arrow keys + * [Always bind the ansi arrow keys?] + */ +protected void +keymacro_reset(EditLine *el) +{ + + node__put(el, el->el_keymacro.map); + el->el_keymacro.map = NULL; + return; +} + + +/* keymacro_get(): + * Calls the recursive function with entry point el->el_keymacro.map + * Looks up *ch in map and then reads characters until a + * complete match is found or a mismatch occurs. Returns the + * type of the match found (XK_STR, XK_CMD, or XK_EXE). + * Returns NULL in val.str and XK_STR for no match. + * The last character read is returned in *ch. + */ +protected int +keymacro_get(EditLine *el, Char *ch, keymacro_value_t *val) +{ + + return node_trav(el, el->el_keymacro.map, ch, val); +} + + +/* keymacro_add(): + * Adds key to the el->el_keymacro.map and associates the value in + * val with it. If key is already is in el->el_keymacro.map, the new + * code is applied to the existing key. Ntype specifies if code is a + * command, an out str or a unix command. + */ +protected void +keymacro_add(EditLine *el, const Char *key, keymacro_value_t *val, int ntype) +{ + + if (key[0] == '\0') { + (void) fprintf(el->el_errfile, + "keymacro_add: Null extended-key not allowed.\n"); + return; + } + if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) { + (void) fprintf(el->el_errfile, + "keymacro_add: sequence-lead-in command not allowed\n"); + return; + } + if (el->el_keymacro.map == NULL) + /* tree is initially empty. Set up new node to match key[0] */ + el->el_keymacro.map = node__get(key[0]); + /* it is properly initialized */ + + /* Now recurse through el->el_keymacro.map */ + (void) node__try(el, el->el_keymacro.map, key, val, ntype); + return; +} + + +/* keymacro_clear(): + * + */ +protected void +keymacro_clear(EditLine *el, el_action_t *map, const Char *in) +{ +#ifdef WIDECHAR + if (*in > N_KEYS) /* can't be in the map */ + return; +#endif + if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) && + ((map == el->el_map.key && + el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) || + (map == el->el_map.alt && + el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN))) + (void) keymacro_delete(el, in); +} + + +/* keymacro_delete(): + * Delete the key and all longer keys staring with key, if + * they exists. + */ +protected int +keymacro_delete(EditLine *el, const Char *key) +{ + + if (key[0] == '\0') { + (void) fprintf(el->el_errfile, + "keymacro_delete: Null extended-key not allowed.\n"); + return -1; + } + if (el->el_keymacro.map == NULL) + return 0; + + (void) node__delete(el, &el->el_keymacro.map, key); + return 0; +} + + +/* keymacro_print(): + * Print the binding associated with key key. + * Print entire el->el_keymacro.map if null + */ +protected void +keymacro_print(EditLine *el, const Char *key) +{ + + /* do nothing if el->el_keymacro.map is empty and null key specified */ + if (el->el_keymacro.map == NULL && *key == 0) + return; + + el->el_keymacro.buf[0] = '"'; + if (node_lookup(el, key, el->el_keymacro.map, (size_t)1) <= -1) + /* key is not bound */ + (void) fprintf(el->el_errfile, "Unbound extended key \"" FSTR + "\"\n", key); + return; +} + + +/* node_trav(): + * recursively traverses node in tree until match or mismatch is + * found. May read in more characters. + */ +private int +node_trav(EditLine *el, keymacro_node_t *ptr, Char *ch, keymacro_value_t *val) +{ + wchar_t wc; + + if (ptr->ch == *ch) { + /* match found */ + if (ptr->next) { + /* key not complete so get next char */ + if (el_wgetc(el, &wc) != 1) {/* if EOF or error */ + val->cmd = ED_END_OF_FILE; + return XK_CMD; + /* PWP: Pretend we just read an end-of-file */ + } + *ch = (Char)wc; + return node_trav(el, ptr->next, ch, val); + } else { + *val = ptr->val; + if (ptr->type != XK_CMD) + *ch = '\0'; + return ptr->type; + } + } else { + /* no match found here */ + if (ptr->sibling) { + /* try next sibling */ + return node_trav(el, ptr->sibling, ch, val); + } else { + /* no next sibling -- mismatch */ + val->str = NULL; + return XK_STR; + } + } +} + + +/* node__try(): + * Find a node that matches *str or allocate a new one + */ +private int +node__try(EditLine *el, keymacro_node_t *ptr, const Char *str, + keymacro_value_t *val, int ntype) +{ + + if (ptr->ch != *str) { + keymacro_node_t *xm; + + for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) + if (xm->sibling->ch == *str) + break; + if (xm->sibling == NULL) + xm->sibling = node__get(*str); /* setup new node */ + ptr = xm->sibling; + } + if (*++str == '\0') { + /* we're there */ + if (ptr->next != NULL) { + node__put(el, ptr->next); + /* lose longer keys with this prefix */ + ptr->next = NULL; + } + switch (ptr->type) { + case XK_CMD: + case XK_NOD: + break; + case XK_STR: + case XK_EXE: + if (ptr->val.str) + el_free(ptr->val.str); + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", + ptr->type)); + break; + } + + switch (ptr->type = ntype) { + case XK_CMD: + ptr->val = *val; + break; + case XK_STR: + case XK_EXE: + if ((ptr->val.str = Strdup(val->str)) == NULL) + return -1; + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); + break; + } + } else { + /* still more chars to go */ + if (ptr->next == NULL) + ptr->next = node__get(*str); /* setup new node */ + (void) node__try(el, ptr->next, str, val, ntype); + } + return 0; +} + + +/* node__delete(): + * Delete node that matches str + */ +private int +node__delete(EditLine *el, keymacro_node_t **inptr, const Char *str) +{ + keymacro_node_t *ptr; + keymacro_node_t *prev_ptr = NULL; + + ptr = *inptr; + + if (ptr->ch != *str) { + keymacro_node_t *xm; + + for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) + if (xm->sibling->ch == *str) + break; + if (xm->sibling == NULL) + return 0; + prev_ptr = xm; + ptr = xm->sibling; + } + if (*++str == '\0') { + /* we're there */ + if (prev_ptr == NULL) + *inptr = ptr->sibling; + else + prev_ptr->sibling = ptr->sibling; + ptr->sibling = NULL; + node__put(el, ptr); + return 1; + } else if (ptr->next != NULL && + node__delete(el, &ptr->next, str) == 1) { + if (ptr->next != NULL) + return 0; + if (prev_ptr == NULL) + *inptr = ptr->sibling; + else + prev_ptr->sibling = ptr->sibling; + ptr->sibling = NULL; + node__put(el, ptr); + return 1; + } else { + return 0; + } +} + + +/* node__put(): + * Puts a tree of nodes onto free list using free(3). + */ +private void +node__put(EditLine *el, keymacro_node_t *ptr) +{ + if (ptr == NULL) + return; + + if (ptr->next != NULL) { + node__put(el, ptr->next); + ptr->next = NULL; + } + node__put(el, ptr->sibling); + + switch (ptr->type) { + case XK_CMD: + case XK_NOD: + break; + case XK_EXE: + case XK_STR: + if (ptr->val.str != NULL) + el_free(ptr->val.str); + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type)); + break; + } + el_free(ptr); +} + + +/* node__get(): + * Returns pointer to a keymacro_node_t for ch. + */ +private keymacro_node_t * +node__get(wint_t ch) +{ + keymacro_node_t *ptr; + + ptr = el_malloc(sizeof(*ptr)); + if (ptr == NULL) + return NULL; + ptr->ch = (Char)ch; + ptr->type = XK_NOD; + ptr->val.str = NULL; + ptr->next = NULL; + ptr->sibling = NULL; + return ptr; +} + +private void +node__free(keymacro_node_t *k) +{ + if (k == NULL) + return; + node__free(k->sibling); + node__free(k->next); + el_free(k); +} + +/* node_lookup(): + * look for the str starting at node ptr. + * Print if last node + */ +private int +node_lookup(EditLine *el, const Char *str, keymacro_node_t *ptr, size_t cnt) +{ + ssize_t used; + + if (ptr == NULL) + return -1; /* cannot have null ptr */ + + if (!str || *str == 0) { + /* no more chars in str. node_enum from here. */ + (void) node_enum(el, ptr, cnt); + return 0; + } else { + /* If match put this char into el->el_keymacro.buf. Recurse */ + if (ptr->ch == *str) { + /* match found */ + used = ct_visual_char(el->el_keymacro.buf + cnt, + KEY_BUFSIZ - cnt, ptr->ch); + if (used == -1) + return -1; /* ran out of buffer space */ + if (ptr->next != NULL) + /* not yet at leaf */ + return (node_lookup(el, str + 1, ptr->next, + (size_t)used + cnt)); + else { + /* next node is null so key should be complete */ + if (str[1] == 0) { + size_t px = cnt + (size_t)used; + el->el_keymacro.buf[px] = '"'; + el->el_keymacro.buf[px + 1] = '\0'; + keymacro_kprint(el, el->el_keymacro.buf, + &ptr->val, ptr->type); + return 0; + } else + return -1; + /* mismatch -- str still has chars */ + } + } else { + /* no match found try sibling */ + if (ptr->sibling) + return (node_lookup(el, str, ptr->sibling, + cnt)); + else + return -1; + } + } +} + + +/* node_enum(): + * Traverse the node printing the characters it is bound in buffer + */ +private int +node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt) +{ + ssize_t used; + + if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */ + el->el_keymacro.buf[++cnt] = '"'; + el->el_keymacro.buf[++cnt] = '\0'; + (void) fprintf(el->el_errfile, + "Some extended keys too long for internal print buffer"); + (void) fprintf(el->el_errfile, " \"" FSTR "...\"\n", + el->el_keymacro.buf); + return 0; + } + if (ptr == NULL) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, + "node_enum: BUG!! Null ptr passed\n!"); +#endif + return -1; + } + /* put this char at end of str */ + used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt, + ptr->ch); + if (ptr->next == NULL) { + /* print this key and function */ + el->el_keymacro.buf[cnt + (size_t)used ] = '"'; + el->el_keymacro.buf[cnt + (size_t)used + 1] = '\0'; + keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type); + } else + (void) node_enum(el, ptr->next, cnt + (size_t)used); + + /* go to sibling if there is one */ + if (ptr->sibling) + (void) node_enum(el, ptr->sibling, cnt); + return 0; +} + + +/* keymacro_kprint(): + * Print the specified key and its associated + * function specified by val + */ +protected void +keymacro_kprint(EditLine *el, const Char *key, keymacro_value_t *val, int ntype) +{ + el_bindings_t *fp; + char unparsbuf[EL_BUFSIZ]; + static const char fmt[] = "%-15s-> %s\n"; + + if (val != NULL) + switch (ntype) { + case XK_STR: + case XK_EXE: + (void) keymacro__decode_str(val->str, unparsbuf, + sizeof(unparsbuf), + ntype == XK_STR ? "\"\"" : "[]"); + (void) fprintf(el->el_outfile, fmt, + ct_encode_string(key, &el->el_scratch), unparsbuf); + break; + case XK_CMD: + for (fp = el->el_map.help; fp->name; fp++) + if (val->cmd == fp->func) { + ct_wcstombs(unparsbuf, fp->name, sizeof(unparsbuf)); + unparsbuf[sizeof(unparsbuf) -1] = '\0'; + (void) fprintf(el->el_outfile, fmt, + ct_encode_string(key, &el->el_scratch), unparsbuf); + break; + } +#ifdef DEBUG_KEY + if (fp->name == NULL) + (void) fprintf(el->el_outfile, + "BUG! Command not found.\n"); +#endif + + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); + break; + } + else + (void) fprintf(el->el_outfile, fmt, ct_encode_string(key, + &el->el_scratch), "no input"); +} + + +#define ADDC(c) \ + if (b < eb) \ + *b++ = c; \ + else \ + b++ +/* keymacro__decode_str(): + * Make a printable version of the ey + */ +protected size_t +keymacro__decode_str(const Char *str, char *buf, size_t len, const char *sep) +{ + char *b = buf, *eb = b + len; + const Char *p; + + b = buf; + if (sep[0] != '\0') { + ADDC(sep[0]); + } + if (*str == '\0') { + ADDC('^'); + ADDC('@'); + goto add_endsep; + } + for (p = str; *p != 0; p++) { + Char dbuf[VISUAL_WIDTH_MAX]; + Char *p2 = dbuf; + ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p); + while (l-- > 0) { + ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++); + if (n == -1) /* ran out of space */ + goto add_endsep; + else + b += n; + } + } +add_endsep: + if (sep[0] != '\0' && sep[1] != '\0') { + ADDC(sep[1]); + } + ADDC('\0'); + if ((size_t)(b - buf) >= len) + buf[len - 1] = '\0'; + return (size_t)(b - buf); +} diff --git a/lib/libedit/keymacro.h b/lib/libedit/keymacro.h new file mode 100644 index 0000000..3f569d3 --- /dev/null +++ b/lib/libedit/keymacro.h @@ -0,0 +1,77 @@ +/* $NetBSD: keymacro.h,v 1.3 2016/01/29 19:59:11 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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. + * + * @(#)key.h 8.1 (Berkeley) 6/4/93 + * $FreeBSD: releng/11.1/lib/libedit/keymacro.h 295369 2016-02-07 15:26:21Z pfg $ + */ + +/* + * el.keymacro.h: Key macro header + */ +#ifndef _h_el_keymacro +#define _h_el_keymacro + +typedef union keymacro_value_t { + el_action_t cmd; /* If it is a command the # */ + Char *str; /* If it is a string... */ +} keymacro_value_t; + +typedef struct keymacro_node_t keymacro_node_t; + +typedef struct el_keymacro_t { + Char *buf; /* Key print buffer */ + keymacro_node_t *map; /* Key map */ + keymacro_value_t val; /* Local conversion buffer */ +} el_keymacro_t; + +#define XK_CMD 0 +#define XK_STR 1 +#define XK_NOD 2 +#define XK_EXE 3 + +protected int keymacro_init(EditLine *); +protected void keymacro_end(EditLine *); +protected keymacro_value_t *keymacro_map_cmd(EditLine *, int); +protected keymacro_value_t *keymacro_map_str(EditLine *, Char *); +protected void keymacro_reset(EditLine *); +protected int keymacro_get(EditLine *, Char *, keymacro_value_t *); +protected void keymacro_add(EditLine *, const Char *, keymacro_value_t *, int); +protected void keymacro_clear(EditLine *, el_action_t *, const Char *); +protected int keymacro_delete(EditLine *, const Char *); +protected void keymacro_print(EditLine *, const Char *); +protected void keymacro_kprint(EditLine *, const Char *, keymacro_value_t *, + int); +protected size_t keymacro__decode_str(const Char *, char *, size_t, + const char *); + +#endif /* _h_el_keymacro */ diff --git a/lib/libedit/makelist b/lib/libedit/makelist new file mode 100644 index 0000000..a645b32 --- /dev/null +++ b/lib/libedit/makelist @@ -0,0 +1,264 @@ +#!/bin/sh - +# $FreeBSD: releng/11.1/lib/libedit/makelist 313981 2017-02-20 03:33:59Z pfg $ +# $NetBSD: makelist,v 1.18 2012/03/21 05:34:54 matt Exp $ +# +# Copyright (c) 1992, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Christos Zoulas of Cornell University. +# +# 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. +# +# @(#)makelist 5.3 (Berkeley) 6/4/93 + +# makelist.sh: Automatically generate header files... + +AWK=awk +USAGE="Usage: $0 -n|-h|-e|-fc|-fh|-bc|-bh|-m " + +if [ "x$1" = "x" ] +then + echo $USAGE 1>&2 + exit 1 +fi + +FLAG="$1" +shift + +FILES="$@" + +case $FLAG in + +# generate foo.h file from foo.c +# +-n) + cat << _EOF +#include "config.h" +#undef WIDECHAR +#define NARROWCHAR +#include "${FILES}" +_EOF + ;; + +-h) + set - `echo $FILES | sed -e 's/\\./_/g'` + hdr="_h_`basename $1`" + cat $FILES | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#ifndef %s\n#define %s\n", "'$hdr'", "'$hdr'"); + } + /\(\):/ { + pr = substr($2, 1, 2); + if (pr == "vi" || pr == "em" || pr == "ed") { + name = substr($2, 1, length($2) - 3); +# +# XXX: need a space between name and prototype so that -fc and -fh +# parsing is much easier +# + printf("protected el_action_t\t%s (EditLine *, wint_t);\n", + name); + } + } + END { + printf("#endif /* %s */\n", "'$hdr'"); + }' + ;; + +# generate help.c from various .c files +# +-bc) + cat $FILES | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#include \"config.h\"\n#include \"el.h\"\n"); + printf("#include \"help.h\"\n"); + printf("private const struct el_bindings_t el_func_help[] = {\n"); + low = "abcdefghijklmnopqrstuvwxyz_"; + high = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + for (i = 1; i <= length(low); i++) + tr[substr(low, i, 1)] = substr(high, i, 1); + } + /\(\):/ { + pr = substr($2, 1, 2); + if (pr == "vi" || pr == "em" || pr == "ed") { + name = substr($2, 1, length($2) - 3); + uname = ""; + fname = ""; + for (i = 1; i <= length(name); i++) { + s = substr(name, i, 1); + uname = uname tr[s]; + if (s == "_") + s = "-"; + fname = fname s; + } + + printf(" { %-30.30s %-30.30s\n","STR(\"" fname "\"),", uname ","); + ok = 1; + } + } + /^ \*/ { + if (ok) { + printf(" STR(\""); + for (i = 2; i < NF; i++) + printf("%s ", $i); + printf("%s\") },\n", $i); + ok = 0; + } + } + END { + printf("};\n"); + printf("\nprotected const el_bindings_t* help__get(void)"); + printf("{ return el_func_help; }\n"); + }' + ;; + +# generate help.h from various .c files +# +-bh) + $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#ifndef _h_help_c\n#define _h_help_c\n"); + printf("protected const el_bindings_t *help__get(void);\n"); + printf("#endif /* _h_help_c */\n"); + }' /dev/null + ;; + +# generate fcns.h from various .h files +# +-fh) + cat $FILES | $AWK '/el_action_t/ { print $3 }' | \ + sort | tr '[:lower:]' '[:upper:]' | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#ifndef _h_fcns_c\n#define _h_fcns_c\n"); + count = 0; + } + { + printf("#define\t%-30.30s\t%3d\n", $1, count++); + } + END { + printf("#define\t%-30.30s\t%3d\n", "EL_NUM_FCNS", count); + + printf("typedef el_action_t (*el_func_t)(EditLine *, wint_t);"); + printf("\nprotected const el_func_t* func__get(void);\n"); + printf("#endif /* _h_fcns_c */\n"); + }' + ;; + +# generate fcns.c from various .h files +# +-fc) + cat $FILES | $AWK '/el_action_t/ { print $3 }' | sort | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#include \"config.h\"\n#include \"el.h\"\n"); + printf("#include \"common.h\"\n"); + printf("#include \"emacs.h\"\n"); + printf("#include \"vi.h\"\n"); + printf("private const el_func_t el_func[] = {"); + maxlen = 80; + needn = 1; + len = 0; + } + { + clen = 25 + 2; + len += clen; + if (len >= maxlen) + needn = 1; + if (needn) { + printf("\n "); + needn = 0; + len = 4 + clen; + } + s = $1 ","; + printf("%-26.26s ", s); + } + END { + printf("\n};\n"); + printf("\nprotected const el_func_t* func__get(void) { return el_func; }\n"); + }' + ;; + +# generate editline.c from various .c files +# +-e) + echo "$FILES" | tr ' ' '\012' | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#define protected static\n"); + printf("#define SCCSID\n"); + } + { + printf("#include \"%s\"\n", $1); + }' + ;; + +# generate man page fragment from various .c files +# +-m) + cat $FILES | $AWK ' + BEGIN { + printf(".\\\" Section automatically generated with makelist\n"); + printf(".Bl -tag -width 4n\n"); + } + /\(\):/ { + pr = substr($2, 1, 2); + if (pr == "vi" || pr == "em" || pr == "ed") { + name = substr($2, 1, length($2) - 3); + fname = ""; + for (i = 1; i <= length(name); i++) { + s = substr(name, i, 1); + if (s == "_") + s = "-"; + fname = fname s; + } + + printf(".It Ic %s\n", fname); + ok = 1; + } + } + /^ \*/ { + if (ok) { + for (i = 2; i < NF; i++) + printf("%s ", $i); + printf("%s.\n", $i); + ok = 0; + } + } + END { + printf(".El\n"); + printf(".\\\" End of section automatically generated with makelist\n"); + }' + ;; + +*) + echo $USAGE 1>&2 + exit 1 + ;; + +esac diff --git a/lib/libedit/map.c b/lib/libedit/map.c new file mode 100644 index 0000000..366b65b --- /dev/null +++ b/lib/libedit/map.c @@ -0,0 +1,1429 @@ +/* $NetBSD: map.c,v 1.43 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: map.c,v 1.43 2016/02/17 19:47:49 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/map.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * map.c: Editor function definitions + */ +#include +#include +#include + +#include "el.h" +#include "help.h" +#include "parse.h" + +private void map_print_key(EditLine *, el_action_t *, const Char *); +private void map_print_some_keys(EditLine *, el_action_t *, wint_t, wint_t); +private void map_print_all_keys(EditLine *); +private void map_init_nls(EditLine *); +private void map_init_meta(EditLine *); + +/* keymap tables ; should be N_KEYS*sizeof(KEYCMD) bytes long */ + + +private const el_action_t el_map_emacs[] = { + /* 0 */ EM_SET_MARK, /* ^@ */ + /* 1 */ ED_MOVE_TO_BEG, /* ^A */ + /* 2 */ ED_PREV_CHAR, /* ^B */ + /* 3 */ ED_TTY_SIGINT, /* ^C */ + /* 4 */ EM_DELETE_OR_LIST, /* ^D */ + /* 5 */ ED_MOVE_TO_END, /* ^E */ + /* 6 */ ED_NEXT_CHAR, /* ^F */ + /* 7 */ ED_UNASSIGNED, /* ^G */ + /* 8 */ EM_DELETE_PREV_CHAR, /* ^H */ + /* 9 */ ED_UNASSIGNED, /* ^I */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_KILL_LINE, /* ^K */ + /* 12 */ ED_CLEAR_SCREEN, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_NEXT_HISTORY, /* ^N */ + /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ + /* 16 */ ED_PREV_HISTORY, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_REDISPLAY, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ + /* 21 */ EM_KILL_LINE, /* ^U */ + /* 22 */ ED_QUOTED_INSERT, /* ^V */ + /* 23 */ EM_KILL_REGION, /* ^W */ + /* 24 */ ED_SEQUENCE_LEAD_IN, /* ^X */ + /* 25 */ EM_YANK, /* ^Y */ + /* 26 */ ED_TTY_SIGTSTP, /* ^Z */ + /* 27 */ EM_META_NEXT, /* ^[ */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_TTY_DSUSP, /* ^] */ + /* 30 */ ED_UNASSIGNED, /* ^^ */ + /* 31 */ ED_UNASSIGNED, /* ^_ */ + /* 32 */ ED_INSERT, /* SPACE */ + /* 33 */ ED_INSERT, /* ! */ + /* 34 */ ED_INSERT, /* " */ + /* 35 */ ED_INSERT, /* # */ + /* 36 */ ED_INSERT, /* $ */ + /* 37 */ ED_INSERT, /* % */ + /* 38 */ ED_INSERT, /* & */ + /* 39 */ ED_INSERT, /* ' */ + /* 40 */ ED_INSERT, /* ( */ + /* 41 */ ED_INSERT, /* ) */ + /* 42 */ ED_INSERT, /* * */ + /* 43 */ ED_INSERT, /* + */ + /* 44 */ ED_INSERT, /* , */ + /* 45 */ ED_INSERT, /* - */ + /* 46 */ ED_INSERT, /* . */ + /* 47 */ ED_INSERT, /* / */ + /* 48 */ ED_DIGIT, /* 0 */ + /* 49 */ ED_DIGIT, /* 1 */ + /* 50 */ ED_DIGIT, /* 2 */ + /* 51 */ ED_DIGIT, /* 3 */ + /* 52 */ ED_DIGIT, /* 4 */ + /* 53 */ ED_DIGIT, /* 5 */ + /* 54 */ ED_DIGIT, /* 6 */ + /* 55 */ ED_DIGIT, /* 7 */ + /* 56 */ ED_DIGIT, /* 8 */ + /* 57 */ ED_DIGIT, /* 9 */ + /* 58 */ ED_INSERT, /* : */ + /* 59 */ ED_INSERT, /* ; */ + /* 60 */ ED_INSERT, /* < */ + /* 61 */ ED_INSERT, /* = */ + /* 62 */ ED_INSERT, /* > */ + /* 63 */ ED_INSERT, /* ? */ + /* 64 */ ED_INSERT, /* @ */ + /* 65 */ ED_INSERT, /* A */ + /* 66 */ ED_INSERT, /* B */ + /* 67 */ ED_INSERT, /* C */ + /* 68 */ ED_INSERT, /* D */ + /* 69 */ ED_INSERT, /* E */ + /* 70 */ ED_INSERT, /* F */ + /* 71 */ ED_INSERT, /* G */ + /* 72 */ ED_INSERT, /* H */ + /* 73 */ ED_INSERT, /* I */ + /* 74 */ ED_INSERT, /* J */ + /* 75 */ ED_INSERT, /* K */ + /* 76 */ ED_INSERT, /* L */ + /* 77 */ ED_INSERT, /* M */ + /* 78 */ ED_INSERT, /* N */ + /* 79 */ ED_INSERT, /* O */ + /* 80 */ ED_INSERT, /* P */ + /* 81 */ ED_INSERT, /* Q */ + /* 82 */ ED_INSERT, /* R */ + /* 83 */ ED_INSERT, /* S */ + /* 84 */ ED_INSERT, /* T */ + /* 85 */ ED_INSERT, /* U */ + /* 86 */ ED_INSERT, /* V */ + /* 87 */ ED_INSERT, /* W */ + /* 88 */ ED_INSERT, /* X */ + /* 89 */ ED_INSERT, /* Y */ + /* 90 */ ED_INSERT, /* Z */ + /* 91 */ ED_INSERT, /* [ */ + /* 92 */ ED_INSERT, /* \ */ + /* 93 */ ED_INSERT, /* ] */ + /* 94 */ ED_INSERT, /* ^ */ + /* 95 */ ED_INSERT, /* _ */ + /* 96 */ ED_INSERT, /* ` */ + /* 97 */ ED_INSERT, /* a */ + /* 98 */ ED_INSERT, /* b */ + /* 99 */ ED_INSERT, /* c */ + /* 100 */ ED_INSERT, /* d */ + /* 101 */ ED_INSERT, /* e */ + /* 102 */ ED_INSERT, /* f */ + /* 103 */ ED_INSERT, /* g */ + /* 104 */ ED_INSERT, /* h */ + /* 105 */ ED_INSERT, /* i */ + /* 106 */ ED_INSERT, /* j */ + /* 107 */ ED_INSERT, /* k */ + /* 108 */ ED_INSERT, /* l */ + /* 109 */ ED_INSERT, /* m */ + /* 110 */ ED_INSERT, /* n */ + /* 111 */ ED_INSERT, /* o */ + /* 112 */ ED_INSERT, /* p */ + /* 113 */ ED_INSERT, /* q */ + /* 114 */ ED_INSERT, /* r */ + /* 115 */ ED_INSERT, /* s */ + /* 116 */ ED_INSERT, /* t */ + /* 117 */ ED_INSERT, /* u */ + /* 118 */ ED_INSERT, /* v */ + /* 119 */ ED_INSERT, /* w */ + /* 120 */ ED_INSERT, /* x */ + /* 121 */ ED_INSERT, /* y */ + /* 122 */ ED_INSERT, /* z */ + /* 123 */ ED_INSERT, /* { */ + /* 124 */ ED_INSERT, /* | */ + /* 125 */ ED_INSERT, /* } */ + /* 126 */ ED_INSERT, /* ~ */ + /* 127 */ EM_DELETE_PREV_CHAR, /* ^? */ + /* 128 */ ED_UNASSIGNED, /* M-^@ */ + /* 129 */ ED_UNASSIGNED, /* M-^A */ + /* 130 */ ED_UNASSIGNED, /* M-^B */ + /* 131 */ ED_UNASSIGNED, /* M-^C */ + /* 132 */ ED_UNASSIGNED, /* M-^D */ + /* 133 */ ED_UNASSIGNED, /* M-^E */ + /* 134 */ ED_UNASSIGNED, /* M-^F */ + /* 135 */ ED_UNASSIGNED, /* M-^G */ + /* 136 */ ED_DELETE_PREV_WORD, /* M-^H */ + /* 137 */ ED_UNASSIGNED, /* M-^I */ + /* 138 */ ED_UNASSIGNED, /* M-^J */ + /* 139 */ ED_UNASSIGNED, /* M-^K */ + /* 140 */ ED_CLEAR_SCREEN, /* M-^L */ + /* 141 */ ED_UNASSIGNED, /* M-^M */ + /* 142 */ ED_UNASSIGNED, /* M-^N */ + /* 143 */ ED_UNASSIGNED, /* M-^O */ + /* 144 */ ED_UNASSIGNED, /* M-^P */ + /* 145 */ ED_UNASSIGNED, /* M-^Q */ + /* 146 */ ED_UNASSIGNED, /* M-^R */ + /* 147 */ ED_UNASSIGNED, /* M-^S */ + /* 148 */ ED_UNASSIGNED, /* M-^T */ + /* 149 */ ED_UNASSIGNED, /* M-^U */ + /* 150 */ ED_UNASSIGNED, /* M-^V */ + /* 151 */ ED_UNASSIGNED, /* M-^W */ + /* 152 */ ED_UNASSIGNED, /* M-^X */ + /* 153 */ ED_UNASSIGNED, /* M-^Y */ + /* 154 */ ED_UNASSIGNED, /* M-^Z */ + /* 155 */ ED_UNASSIGNED, /* M-^[ */ + /* 156 */ ED_UNASSIGNED, /* M-^\ */ + /* 157 */ ED_UNASSIGNED, /* M-^] */ + /* 158 */ ED_UNASSIGNED, /* M-^^ */ + /* 159 */ EM_COPY_PREV_WORD, /* M-^_ */ + /* 160 */ ED_UNASSIGNED, /* M-SPACE */ + /* 161 */ ED_UNASSIGNED, /* M-! */ + /* 162 */ ED_UNASSIGNED, /* M-" */ + /* 163 */ ED_UNASSIGNED, /* M-# */ + /* 164 */ ED_UNASSIGNED, /* M-$ */ + /* 165 */ ED_UNASSIGNED, /* M-% */ + /* 166 */ ED_UNASSIGNED, /* M-& */ + /* 167 */ ED_UNASSIGNED, /* M-' */ + /* 168 */ ED_UNASSIGNED, /* M-( */ + /* 169 */ ED_UNASSIGNED, /* M-) */ + /* 170 */ ED_UNASSIGNED, /* M-* */ + /* 171 */ ED_UNASSIGNED, /* M-+ */ + /* 172 */ ED_UNASSIGNED, /* M-, */ + /* 173 */ ED_UNASSIGNED, /* M-- */ + /* 174 */ ED_UNASSIGNED, /* M-. */ + /* 175 */ ED_UNASSIGNED, /* M-/ */ + /* 176 */ ED_ARGUMENT_DIGIT, /* M-0 */ + /* 177 */ ED_ARGUMENT_DIGIT, /* M-1 */ + /* 178 */ ED_ARGUMENT_DIGIT, /* M-2 */ + /* 179 */ ED_ARGUMENT_DIGIT, /* M-3 */ + /* 180 */ ED_ARGUMENT_DIGIT, /* M-4 */ + /* 181 */ ED_ARGUMENT_DIGIT, /* M-5 */ + /* 182 */ ED_ARGUMENT_DIGIT, /* M-6 */ + /* 183 */ ED_ARGUMENT_DIGIT, /* M-7 */ + /* 184 */ ED_ARGUMENT_DIGIT, /* M-8 */ + /* 185 */ ED_ARGUMENT_DIGIT, /* M-9 */ + /* 186 */ ED_UNASSIGNED, /* M-: */ + /* 187 */ ED_UNASSIGNED, /* M-; */ + /* 188 */ ED_UNASSIGNED, /* M-< */ + /* 189 */ ED_UNASSIGNED, /* M-= */ + /* 190 */ ED_UNASSIGNED, /* M-> */ + /* 191 */ ED_UNASSIGNED, /* M-? */ + /* 192 */ ED_UNASSIGNED, /* M-@ */ + /* 193 */ ED_UNASSIGNED, /* M-A */ + /* 194 */ ED_PREV_WORD, /* M-B */ + /* 195 */ EM_CAPITOL_CASE, /* M-C */ + /* 196 */ EM_DELETE_NEXT_WORD, /* M-D */ + /* 197 */ ED_UNASSIGNED, /* M-E */ + /* 198 */ EM_NEXT_WORD, /* M-F */ + /* 199 */ ED_UNASSIGNED, /* M-G */ + /* 200 */ ED_UNASSIGNED, /* M-H */ + /* 201 */ ED_UNASSIGNED, /* M-I */ + /* 202 */ ED_UNASSIGNED, /* M-J */ + /* 203 */ ED_UNASSIGNED, /* M-K */ + /* 204 */ EM_LOWER_CASE, /* M-L */ + /* 205 */ ED_UNASSIGNED, /* M-M */ + /* 206 */ ED_SEARCH_NEXT_HISTORY, /* M-N */ + /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ + /* 208 */ ED_SEARCH_PREV_HISTORY, /* M-P */ + /* 209 */ ED_UNASSIGNED, /* M-Q */ + /* 210 */ ED_UNASSIGNED, /* M-R */ + /* 211 */ ED_UNASSIGNED, /* M-S */ + /* 212 */ ED_UNASSIGNED, /* M-T */ + /* 213 */ EM_UPPER_CASE, /* M-U */ + /* 214 */ ED_UNASSIGNED, /* M-V */ + /* 215 */ EM_COPY_REGION, /* M-W */ + /* 216 */ ED_COMMAND, /* M-X */ + /* 217 */ ED_UNASSIGNED, /* M-Y */ + /* 218 */ ED_UNASSIGNED, /* M-Z */ + /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ + /* 220 */ ED_UNASSIGNED, /* M-\ */ + /* 221 */ ED_UNASSIGNED, /* M-] */ + /* 222 */ ED_UNASSIGNED, /* M-^ */ + /* 223 */ ED_UNASSIGNED, /* M-_ */ + /* 223 */ ED_UNASSIGNED, /* M-` */ + /* 224 */ ED_UNASSIGNED, /* M-a */ + /* 225 */ ED_PREV_WORD, /* M-b */ + /* 226 */ EM_CAPITOL_CASE, /* M-c */ + /* 227 */ EM_DELETE_NEXT_WORD, /* M-d */ + /* 228 */ ED_UNASSIGNED, /* M-e */ + /* 229 */ EM_NEXT_WORD, /* M-f */ + /* 230 */ ED_UNASSIGNED, /* M-g */ + /* 231 */ ED_UNASSIGNED, /* M-h */ + /* 232 */ ED_UNASSIGNED, /* M-i */ + /* 233 */ ED_UNASSIGNED, /* M-j */ + /* 234 */ ED_UNASSIGNED, /* M-k */ + /* 235 */ EM_LOWER_CASE, /* M-l */ + /* 236 */ ED_UNASSIGNED, /* M-m */ + /* 237 */ ED_SEARCH_NEXT_HISTORY, /* M-n */ + /* 238 */ ED_UNASSIGNED, /* M-o */ + /* 239 */ ED_SEARCH_PREV_HISTORY, /* M-p */ + /* 240 */ ED_UNASSIGNED, /* M-q */ + /* 241 */ ED_UNASSIGNED, /* M-r */ + /* 242 */ ED_UNASSIGNED, /* M-s */ + /* 243 */ ED_UNASSIGNED, /* M-t */ + /* 244 */ EM_UPPER_CASE, /* M-u */ + /* 245 */ ED_UNASSIGNED, /* M-v */ + /* 246 */ EM_COPY_REGION, /* M-w */ + /* 247 */ ED_COMMAND, /* M-x */ + /* 248 */ ED_UNASSIGNED, /* M-y */ + /* 249 */ ED_UNASSIGNED, /* M-z */ + /* 250 */ ED_UNASSIGNED, /* M-{ */ + /* 251 */ ED_UNASSIGNED, /* M-| */ + /* 252 */ ED_UNASSIGNED, /* M-} */ + /* 253 */ ED_UNASSIGNED, /* M-~ */ + /* 254 */ ED_DELETE_PREV_WORD /* M-^? */ + /* 255 */ +}; + + +/* + * keymap table for vi. Each index into above tbl; should be + * N_KEYS entries long. Vi mode uses a sticky-extend to do command mode: + * insert mode characters are in the normal keymap, and command mode + * in the extended keymap. + */ +private const el_action_t el_map_vi_insert[] = { +#ifdef KSHVI + /* 0 */ ED_UNASSIGNED, /* ^@ */ + /* 1 */ ED_INSERT, /* ^A */ + /* 2 */ ED_INSERT, /* ^B */ + /* 3 */ ED_INSERT, /* ^C */ + /* 4 */ VI_LIST_OR_EOF, /* ^D */ + /* 5 */ ED_INSERT, /* ^E */ + /* 6 */ ED_INSERT, /* ^F */ + /* 7 */ ED_INSERT, /* ^G */ + /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ + /* 9 */ ED_INSERT, /* ^I */ /* Tab Key */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_INSERT, /* ^K */ + /* 12 */ ED_INSERT, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_INSERT, /* ^N */ + /* 15 */ ED_INSERT, /* ^O */ + /* 16 */ ED_INSERT, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_INSERT, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_INSERT, /* ^T */ + /* 21 */ VI_KILL_LINE_PREV, /* ^U */ + /* 22 */ ED_QUOTED_INSERT, /* ^V */ + /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ + /* ED_DELETE_PREV_WORD: Only until strt edit pos */ + /* 24 */ ED_INSERT, /* ^X */ + /* 25 */ ED_INSERT, /* ^Y */ + /* 26 */ ED_INSERT, /* ^Z */ + /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* [ Esc ] key */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_INSERT, /* ^] */ + /* 30 */ ED_INSERT, /* ^^ */ + /* 31 */ ED_INSERT, /* ^_ */ +#else /* !KSHVI */ + /* + * NOTE: These mappings do NOT Correspond well + * to the KSH VI editing assignments. + * On the other and they are convenient and + * many people have have gotten used to them. + */ + /* 0 */ ED_UNASSIGNED, /* ^@ */ + /* 1 */ ED_MOVE_TO_BEG, /* ^A */ + /* 2 */ ED_PREV_CHAR, /* ^B */ + /* 3 */ ED_TTY_SIGINT, /* ^C */ + /* 4 */ VI_LIST_OR_EOF, /* ^D */ + /* 5 */ ED_MOVE_TO_END, /* ^E */ + /* 6 */ ED_NEXT_CHAR, /* ^F */ + /* 7 */ ED_UNASSIGNED, /* ^G */ + /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ + /* 9 */ ED_UNASSIGNED, /* ^I */ /* Tab Key */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_KILL_LINE, /* ^K */ + /* 12 */ ED_CLEAR_SCREEN, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_NEXT_HISTORY, /* ^N */ + /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ + /* 16 */ ED_PREV_HISTORY, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_REDISPLAY, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ + /* 21 */ VI_KILL_LINE_PREV, /* ^U */ + /* 22 */ ED_QUOTED_INSERT, /* ^V */ + /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ + /* 24 */ ED_UNASSIGNED, /* ^X */ + /* 25 */ ED_TTY_DSUSP, /* ^Y */ + /* 26 */ ED_TTY_SIGTSTP, /* ^Z */ + /* 27 */ VI_COMMAND_MODE, /* ^[ */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_UNASSIGNED, /* ^] */ + /* 30 */ ED_UNASSIGNED, /* ^^ */ + /* 31 */ ED_UNASSIGNED, /* ^_ */ +#endif /* KSHVI */ + /* 32 */ ED_INSERT, /* SPACE */ + /* 33 */ ED_INSERT, /* ! */ + /* 34 */ ED_INSERT, /* " */ + /* 35 */ ED_INSERT, /* # */ + /* 36 */ ED_INSERT, /* $ */ + /* 37 */ ED_INSERT, /* % */ + /* 38 */ ED_INSERT, /* & */ + /* 39 */ ED_INSERT, /* ' */ + /* 40 */ ED_INSERT, /* ( */ + /* 41 */ ED_INSERT, /* ) */ + /* 42 */ ED_INSERT, /* * */ + /* 43 */ ED_INSERT, /* + */ + /* 44 */ ED_INSERT, /* , */ + /* 45 */ ED_INSERT, /* - */ + /* 46 */ ED_INSERT, /* . */ + /* 47 */ ED_INSERT, /* / */ + /* 48 */ ED_INSERT, /* 0 */ + /* 49 */ ED_INSERT, /* 1 */ + /* 50 */ ED_INSERT, /* 2 */ + /* 51 */ ED_INSERT, /* 3 */ + /* 52 */ ED_INSERT, /* 4 */ + /* 53 */ ED_INSERT, /* 5 */ + /* 54 */ ED_INSERT, /* 6 */ + /* 55 */ ED_INSERT, /* 7 */ + /* 56 */ ED_INSERT, /* 8 */ + /* 57 */ ED_INSERT, /* 9 */ + /* 58 */ ED_INSERT, /* : */ + /* 59 */ ED_INSERT, /* ; */ + /* 60 */ ED_INSERT, /* < */ + /* 61 */ ED_INSERT, /* = */ + /* 62 */ ED_INSERT, /* > */ + /* 63 */ ED_INSERT, /* ? */ + /* 64 */ ED_INSERT, /* @ */ + /* 65 */ ED_INSERT, /* A */ + /* 66 */ ED_INSERT, /* B */ + /* 67 */ ED_INSERT, /* C */ + /* 68 */ ED_INSERT, /* D */ + /* 69 */ ED_INSERT, /* E */ + /* 70 */ ED_INSERT, /* F */ + /* 71 */ ED_INSERT, /* G */ + /* 72 */ ED_INSERT, /* H */ + /* 73 */ ED_INSERT, /* I */ + /* 74 */ ED_INSERT, /* J */ + /* 75 */ ED_INSERT, /* K */ + /* 76 */ ED_INSERT, /* L */ + /* 77 */ ED_INSERT, /* M */ + /* 78 */ ED_INSERT, /* N */ + /* 79 */ ED_INSERT, /* O */ + /* 80 */ ED_INSERT, /* P */ + /* 81 */ ED_INSERT, /* Q */ + /* 82 */ ED_INSERT, /* R */ + /* 83 */ ED_INSERT, /* S */ + /* 84 */ ED_INSERT, /* T */ + /* 85 */ ED_INSERT, /* U */ + /* 86 */ ED_INSERT, /* V */ + /* 87 */ ED_INSERT, /* W */ + /* 88 */ ED_INSERT, /* X */ + /* 89 */ ED_INSERT, /* Y */ + /* 90 */ ED_INSERT, /* Z */ + /* 91 */ ED_INSERT, /* [ */ + /* 92 */ ED_INSERT, /* \ */ + /* 93 */ ED_INSERT, /* ] */ + /* 94 */ ED_INSERT, /* ^ */ + /* 95 */ ED_INSERT, /* _ */ + /* 96 */ ED_INSERT, /* ` */ + /* 97 */ ED_INSERT, /* a */ + /* 98 */ ED_INSERT, /* b */ + /* 99 */ ED_INSERT, /* c */ + /* 100 */ ED_INSERT, /* d */ + /* 101 */ ED_INSERT, /* e */ + /* 102 */ ED_INSERT, /* f */ + /* 103 */ ED_INSERT, /* g */ + /* 104 */ ED_INSERT, /* h */ + /* 105 */ ED_INSERT, /* i */ + /* 106 */ ED_INSERT, /* j */ + /* 107 */ ED_INSERT, /* k */ + /* 108 */ ED_INSERT, /* l */ + /* 109 */ ED_INSERT, /* m */ + /* 110 */ ED_INSERT, /* n */ + /* 111 */ ED_INSERT, /* o */ + /* 112 */ ED_INSERT, /* p */ + /* 113 */ ED_INSERT, /* q */ + /* 114 */ ED_INSERT, /* r */ + /* 115 */ ED_INSERT, /* s */ + /* 116 */ ED_INSERT, /* t */ + /* 117 */ ED_INSERT, /* u */ + /* 118 */ ED_INSERT, /* v */ + /* 119 */ ED_INSERT, /* w */ + /* 120 */ ED_INSERT, /* x */ + /* 121 */ ED_INSERT, /* y */ + /* 122 */ ED_INSERT, /* z */ + /* 123 */ ED_INSERT, /* { */ + /* 124 */ ED_INSERT, /* | */ + /* 125 */ ED_INSERT, /* } */ + /* 126 */ ED_INSERT, /* ~ */ + /* 127 */ VI_DELETE_PREV_CHAR, /* ^? */ + /* 128 */ ED_INSERT, /* M-^@ */ + /* 129 */ ED_INSERT, /* M-^A */ + /* 130 */ ED_INSERT, /* M-^B */ + /* 131 */ ED_INSERT, /* M-^C */ + /* 132 */ ED_INSERT, /* M-^D */ + /* 133 */ ED_INSERT, /* M-^E */ + /* 134 */ ED_INSERT, /* M-^F */ + /* 135 */ ED_INSERT, /* M-^G */ + /* 136 */ ED_INSERT, /* M-^H */ + /* 137 */ ED_INSERT, /* M-^I */ + /* 138 */ ED_INSERT, /* M-^J */ + /* 139 */ ED_INSERT, /* M-^K */ + /* 140 */ ED_INSERT, /* M-^L */ + /* 141 */ ED_INSERT, /* M-^M */ + /* 142 */ ED_INSERT, /* M-^N */ + /* 143 */ ED_INSERT, /* M-^O */ + /* 144 */ ED_INSERT, /* M-^P */ + /* 145 */ ED_INSERT, /* M-^Q */ + /* 146 */ ED_INSERT, /* M-^R */ + /* 147 */ ED_INSERT, /* M-^S */ + /* 148 */ ED_INSERT, /* M-^T */ + /* 149 */ ED_INSERT, /* M-^U */ + /* 150 */ ED_INSERT, /* M-^V */ + /* 151 */ ED_INSERT, /* M-^W */ + /* 152 */ ED_INSERT, /* M-^X */ + /* 153 */ ED_INSERT, /* M-^Y */ + /* 154 */ ED_INSERT, /* M-^Z */ + /* 155 */ ED_INSERT, /* M-^[ */ + /* 156 */ ED_INSERT, /* M-^\ */ + /* 157 */ ED_INSERT, /* M-^] */ + /* 158 */ ED_INSERT, /* M-^^ */ + /* 159 */ ED_INSERT, /* M-^_ */ + /* 160 */ ED_INSERT, /* M-SPACE */ + /* 161 */ ED_INSERT, /* M-! */ + /* 162 */ ED_INSERT, /* M-" */ + /* 163 */ ED_INSERT, /* M-# */ + /* 164 */ ED_INSERT, /* M-$ */ + /* 165 */ ED_INSERT, /* M-% */ + /* 166 */ ED_INSERT, /* M-& */ + /* 167 */ ED_INSERT, /* M-' */ + /* 168 */ ED_INSERT, /* M-( */ + /* 169 */ ED_INSERT, /* M-) */ + /* 170 */ ED_INSERT, /* M-* */ + /* 171 */ ED_INSERT, /* M-+ */ + /* 172 */ ED_INSERT, /* M-, */ + /* 173 */ ED_INSERT, /* M-- */ + /* 174 */ ED_INSERT, /* M-. */ + /* 175 */ ED_INSERT, /* M-/ */ + /* 176 */ ED_INSERT, /* M-0 */ + /* 177 */ ED_INSERT, /* M-1 */ + /* 178 */ ED_INSERT, /* M-2 */ + /* 179 */ ED_INSERT, /* M-3 */ + /* 180 */ ED_INSERT, /* M-4 */ + /* 181 */ ED_INSERT, /* M-5 */ + /* 182 */ ED_INSERT, /* M-6 */ + /* 183 */ ED_INSERT, /* M-7 */ + /* 184 */ ED_INSERT, /* M-8 */ + /* 185 */ ED_INSERT, /* M-9 */ + /* 186 */ ED_INSERT, /* M-: */ + /* 187 */ ED_INSERT, /* M-; */ + /* 188 */ ED_INSERT, /* M-< */ + /* 189 */ ED_INSERT, /* M-= */ + /* 190 */ ED_INSERT, /* M-> */ + /* 191 */ ED_INSERT, /* M-? */ + /* 192 */ ED_INSERT, /* M-@ */ + /* 193 */ ED_INSERT, /* M-A */ + /* 194 */ ED_INSERT, /* M-B */ + /* 195 */ ED_INSERT, /* M-C */ + /* 196 */ ED_INSERT, /* M-D */ + /* 197 */ ED_INSERT, /* M-E */ + /* 198 */ ED_INSERT, /* M-F */ + /* 199 */ ED_INSERT, /* M-G */ + /* 200 */ ED_INSERT, /* M-H */ + /* 201 */ ED_INSERT, /* M-I */ + /* 202 */ ED_INSERT, /* M-J */ + /* 203 */ ED_INSERT, /* M-K */ + /* 204 */ ED_INSERT, /* M-L */ + /* 205 */ ED_INSERT, /* M-M */ + /* 206 */ ED_INSERT, /* M-N */ + /* 207 */ ED_INSERT, /* M-O */ + /* 208 */ ED_INSERT, /* M-P */ + /* 209 */ ED_INSERT, /* M-Q */ + /* 210 */ ED_INSERT, /* M-R */ + /* 211 */ ED_INSERT, /* M-S */ + /* 212 */ ED_INSERT, /* M-T */ + /* 213 */ ED_INSERT, /* M-U */ + /* 214 */ ED_INSERT, /* M-V */ + /* 215 */ ED_INSERT, /* M-W */ + /* 216 */ ED_INSERT, /* M-X */ + /* 217 */ ED_INSERT, /* M-Y */ + /* 218 */ ED_INSERT, /* M-Z */ + /* 219 */ ED_INSERT, /* M-[ */ + /* 220 */ ED_INSERT, /* M-\ */ + /* 221 */ ED_INSERT, /* M-] */ + /* 222 */ ED_INSERT, /* M-^ */ + /* 223 */ ED_INSERT, /* M-_ */ + /* 224 */ ED_INSERT, /* M-` */ + /* 225 */ ED_INSERT, /* M-a */ + /* 226 */ ED_INSERT, /* M-b */ + /* 227 */ ED_INSERT, /* M-c */ + /* 228 */ ED_INSERT, /* M-d */ + /* 229 */ ED_INSERT, /* M-e */ + /* 230 */ ED_INSERT, /* M-f */ + /* 231 */ ED_INSERT, /* M-g */ + /* 232 */ ED_INSERT, /* M-h */ + /* 233 */ ED_INSERT, /* M-i */ + /* 234 */ ED_INSERT, /* M-j */ + /* 235 */ ED_INSERT, /* M-k */ + /* 236 */ ED_INSERT, /* M-l */ + /* 237 */ ED_INSERT, /* M-m */ + /* 238 */ ED_INSERT, /* M-n */ + /* 239 */ ED_INSERT, /* M-o */ + /* 240 */ ED_INSERT, /* M-p */ + /* 241 */ ED_INSERT, /* M-q */ + /* 242 */ ED_INSERT, /* M-r */ + /* 243 */ ED_INSERT, /* M-s */ + /* 244 */ ED_INSERT, /* M-t */ + /* 245 */ ED_INSERT, /* M-u */ + /* 246 */ ED_INSERT, /* M-v */ + /* 247 */ ED_INSERT, /* M-w */ + /* 248 */ ED_INSERT, /* M-x */ + /* 249 */ ED_INSERT, /* M-y */ + /* 250 */ ED_INSERT, /* M-z */ + /* 251 */ ED_INSERT, /* M-{ */ + /* 252 */ ED_INSERT, /* M-| */ + /* 253 */ ED_INSERT, /* M-} */ + /* 254 */ ED_INSERT, /* M-~ */ + /* 255 */ ED_INSERT /* M-^? */ +}; + +private const el_action_t el_map_vi_command[] = { + /* 0 */ ED_UNASSIGNED, /* ^@ */ + /* 1 */ ED_MOVE_TO_BEG, /* ^A */ + /* 2 */ ED_UNASSIGNED, /* ^B */ + /* 3 */ ED_TTY_SIGINT, /* ^C */ + /* 4 */ ED_UNASSIGNED, /* ^D */ + /* 5 */ ED_MOVE_TO_END, /* ^E */ + /* 6 */ ED_UNASSIGNED, /* ^F */ + /* 7 */ ED_UNASSIGNED, /* ^G */ + /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */ + /* 9 */ ED_UNASSIGNED, /* ^I */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_KILL_LINE, /* ^K */ + /* 12 */ ED_CLEAR_SCREEN, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_NEXT_HISTORY, /* ^N */ + /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ + /* 16 */ ED_PREV_HISTORY, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_REDISPLAY, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_UNASSIGNED, /* ^T */ + /* 21 */ VI_KILL_LINE_PREV, /* ^U */ + /* 22 */ ED_UNASSIGNED, /* ^V */ + /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ + /* 24 */ ED_UNASSIGNED, /* ^X */ + /* 25 */ ED_UNASSIGNED, /* ^Y */ + /* 26 */ ED_UNASSIGNED, /* ^Z */ + /* 27 */ EM_META_NEXT, /* ^[ */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_UNASSIGNED, /* ^] */ + /* 30 */ ED_UNASSIGNED, /* ^^ */ + /* 31 */ ED_UNASSIGNED, /* ^_ */ + /* 32 */ ED_NEXT_CHAR, /* SPACE */ + /* 33 */ ED_UNASSIGNED, /* ! */ + /* 34 */ ED_UNASSIGNED, /* " */ + /* 35 */ VI_COMMENT_OUT, /* # */ + /* 36 */ ED_MOVE_TO_END, /* $ */ + /* 37 */ VI_MATCH, /* % */ + /* 38 */ ED_UNASSIGNED, /* & */ + /* 39 */ ED_UNASSIGNED, /* ' */ + /* 40 */ ED_UNASSIGNED, /* ( */ + /* 41 */ ED_UNASSIGNED, /* ) */ + /* 42 */ ED_UNASSIGNED, /* * */ + /* 43 */ ED_NEXT_HISTORY, /* + */ + /* 44 */ VI_REPEAT_PREV_CHAR, /* , */ + /* 45 */ ED_PREV_HISTORY, /* - */ + /* 46 */ VI_REDO, /* . */ + /* 47 */ VI_SEARCH_PREV, /* / */ + /* 48 */ VI_ZERO, /* 0 */ + /* 49 */ ED_ARGUMENT_DIGIT, /* 1 */ + /* 50 */ ED_ARGUMENT_DIGIT, /* 2 */ + /* 51 */ ED_ARGUMENT_DIGIT, /* 3 */ + /* 52 */ ED_ARGUMENT_DIGIT, /* 4 */ + /* 53 */ ED_ARGUMENT_DIGIT, /* 5 */ + /* 54 */ ED_ARGUMENT_DIGIT, /* 6 */ + /* 55 */ ED_ARGUMENT_DIGIT, /* 7 */ + /* 56 */ ED_ARGUMENT_DIGIT, /* 8 */ + /* 57 */ ED_ARGUMENT_DIGIT, /* 9 */ + /* 58 */ ED_COMMAND, /* : */ + /* 59 */ VI_REPEAT_NEXT_CHAR, /* ; */ + /* 60 */ ED_UNASSIGNED, /* < */ + /* 61 */ ED_UNASSIGNED, /* = */ + /* 62 */ ED_UNASSIGNED, /* > */ + /* 63 */ VI_SEARCH_NEXT, /* ? */ + /* 64 */ VI_ALIAS, /* @ */ + /* 65 */ VI_ADD_AT_EOL, /* A */ + /* 66 */ VI_PREV_BIG_WORD, /* B */ + /* 67 */ VI_CHANGE_TO_EOL, /* C */ + /* 68 */ ED_KILL_LINE, /* D */ + /* 69 */ VI_END_BIG_WORD, /* E */ + /* 70 */ VI_PREV_CHAR, /* F */ + /* 71 */ VI_TO_HISTORY_LINE, /* G */ + /* 72 */ ED_UNASSIGNED, /* H */ + /* 73 */ VI_INSERT_AT_BOL, /* I */ + /* 74 */ ED_SEARCH_NEXT_HISTORY, /* J */ + /* 75 */ ED_SEARCH_PREV_HISTORY, /* K */ + /* 76 */ ED_UNASSIGNED, /* L */ + /* 77 */ ED_UNASSIGNED, /* M */ + /* 78 */ VI_REPEAT_SEARCH_PREV, /* N */ + /* 79 */ ED_SEQUENCE_LEAD_IN, /* O */ + /* 80 */ VI_PASTE_PREV, /* P */ + /* 81 */ ED_UNASSIGNED, /* Q */ + /* 82 */ VI_REPLACE_MODE, /* R */ + /* 83 */ VI_SUBSTITUTE_LINE, /* S */ + /* 84 */ VI_TO_PREV_CHAR, /* T */ + /* 85 */ VI_UNDO_LINE, /* U */ + /* 86 */ ED_UNASSIGNED, /* V */ + /* 87 */ VI_NEXT_BIG_WORD, /* W */ + /* 88 */ ED_DELETE_PREV_CHAR, /* X */ + /* 89 */ VI_YANK_END, /* Y */ + /* 90 */ ED_UNASSIGNED, /* Z */ + /* 91 */ ED_SEQUENCE_LEAD_IN, /* [ */ + /* 92 */ ED_UNASSIGNED, /* \ */ + /* 93 */ ED_UNASSIGNED, /* ] */ + /* 94 */ ED_MOVE_TO_BEG, /* ^ */ + /* 95 */ VI_HISTORY_WORD, /* _ */ + /* 96 */ ED_UNASSIGNED, /* ` */ + /* 97 */ VI_ADD, /* a */ + /* 98 */ VI_PREV_WORD, /* b */ + /* 99 */ VI_CHANGE_META, /* c */ + /* 100 */ VI_DELETE_META, /* d */ + /* 101 */ VI_END_WORD, /* e */ + /* 102 */ VI_NEXT_CHAR, /* f */ + /* 103 */ ED_UNASSIGNED, /* g */ + /* 104 */ ED_PREV_CHAR, /* h */ + /* 105 */ VI_INSERT, /* i */ + /* 106 */ ED_NEXT_HISTORY, /* j */ + /* 107 */ ED_PREV_HISTORY, /* k */ + /* 108 */ ED_NEXT_CHAR, /* l */ + /* 109 */ ED_UNASSIGNED, /* m */ + /* 110 */ VI_REPEAT_SEARCH_NEXT, /* n */ + /* 111 */ ED_UNASSIGNED, /* o */ + /* 112 */ VI_PASTE_NEXT, /* p */ + /* 113 */ ED_UNASSIGNED, /* q */ + /* 114 */ VI_REPLACE_CHAR, /* r */ + /* 115 */ VI_SUBSTITUTE_CHAR, /* s */ + /* 116 */ VI_TO_NEXT_CHAR, /* t */ + /* 117 */ VI_UNDO, /* u */ + /* 118 */ VI_HISTEDIT, /* v */ + /* 119 */ VI_NEXT_WORD, /* w */ + /* 120 */ ED_DELETE_NEXT_CHAR, /* x */ + /* 121 */ VI_YANK, /* y */ + /* 122 */ ED_UNASSIGNED, /* z */ + /* 123 */ ED_UNASSIGNED, /* { */ + /* 124 */ VI_TO_COLUMN, /* | */ + /* 125 */ ED_UNASSIGNED, /* } */ + /* 126 */ VI_CHANGE_CASE, /* ~ */ + /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */ + /* 128 */ ED_UNASSIGNED, /* M-^@ */ + /* 129 */ ED_UNASSIGNED, /* M-^A */ + /* 130 */ ED_UNASSIGNED, /* M-^B */ + /* 131 */ ED_UNASSIGNED, /* M-^C */ + /* 132 */ ED_UNASSIGNED, /* M-^D */ + /* 133 */ ED_UNASSIGNED, /* M-^E */ + /* 134 */ ED_UNASSIGNED, /* M-^F */ + /* 135 */ ED_UNASSIGNED, /* M-^G */ + /* 136 */ ED_UNASSIGNED, /* M-^H */ + /* 137 */ ED_UNASSIGNED, /* M-^I */ + /* 138 */ ED_UNASSIGNED, /* M-^J */ + /* 139 */ ED_UNASSIGNED, /* M-^K */ + /* 140 */ ED_UNASSIGNED, /* M-^L */ + /* 141 */ ED_UNASSIGNED, /* M-^M */ + /* 142 */ ED_UNASSIGNED, /* M-^N */ + /* 143 */ ED_UNASSIGNED, /* M-^O */ + /* 144 */ ED_UNASSIGNED, /* M-^P */ + /* 145 */ ED_UNASSIGNED, /* M-^Q */ + /* 146 */ ED_UNASSIGNED, /* M-^R */ + /* 147 */ ED_UNASSIGNED, /* M-^S */ + /* 148 */ ED_UNASSIGNED, /* M-^T */ + /* 149 */ ED_UNASSIGNED, /* M-^U */ + /* 150 */ ED_UNASSIGNED, /* M-^V */ + /* 151 */ ED_UNASSIGNED, /* M-^W */ + /* 152 */ ED_UNASSIGNED, /* M-^X */ + /* 153 */ ED_UNASSIGNED, /* M-^Y */ + /* 154 */ ED_UNASSIGNED, /* M-^Z */ + /* 155 */ ED_UNASSIGNED, /* M-^[ */ + /* 156 */ ED_UNASSIGNED, /* M-^\ */ + /* 157 */ ED_UNASSIGNED, /* M-^] */ + /* 158 */ ED_UNASSIGNED, /* M-^^ */ + /* 159 */ ED_UNASSIGNED, /* M-^_ */ + /* 160 */ ED_UNASSIGNED, /* M-SPACE */ + /* 161 */ ED_UNASSIGNED, /* M-! */ + /* 162 */ ED_UNASSIGNED, /* M-" */ + /* 163 */ ED_UNASSIGNED, /* M-# */ + /* 164 */ ED_UNASSIGNED, /* M-$ */ + /* 165 */ ED_UNASSIGNED, /* M-% */ + /* 166 */ ED_UNASSIGNED, /* M-& */ + /* 167 */ ED_UNASSIGNED, /* M-' */ + /* 168 */ ED_UNASSIGNED, /* M-( */ + /* 169 */ ED_UNASSIGNED, /* M-) */ + /* 170 */ ED_UNASSIGNED, /* M-* */ + /* 171 */ ED_UNASSIGNED, /* M-+ */ + /* 172 */ ED_UNASSIGNED, /* M-, */ + /* 173 */ ED_UNASSIGNED, /* M-- */ + /* 174 */ ED_UNASSIGNED, /* M-. */ + /* 175 */ ED_UNASSIGNED, /* M-/ */ + /* 176 */ ED_UNASSIGNED, /* M-0 */ + /* 177 */ ED_UNASSIGNED, /* M-1 */ + /* 178 */ ED_UNASSIGNED, /* M-2 */ + /* 179 */ ED_UNASSIGNED, /* M-3 */ + /* 180 */ ED_UNASSIGNED, /* M-4 */ + /* 181 */ ED_UNASSIGNED, /* M-5 */ + /* 182 */ ED_UNASSIGNED, /* M-6 */ + /* 183 */ ED_UNASSIGNED, /* M-7 */ + /* 184 */ ED_UNASSIGNED, /* M-8 */ + /* 185 */ ED_UNASSIGNED, /* M-9 */ + /* 186 */ ED_UNASSIGNED, /* M-: */ + /* 187 */ ED_UNASSIGNED, /* M-; */ + /* 188 */ ED_UNASSIGNED, /* M-< */ + /* 189 */ ED_UNASSIGNED, /* M-= */ + /* 190 */ ED_UNASSIGNED, /* M-> */ + /* 191 */ ED_UNASSIGNED, /* M-? */ + /* 192 */ ED_UNASSIGNED, /* M-@ */ + /* 193 */ ED_UNASSIGNED, /* M-A */ + /* 194 */ ED_UNASSIGNED, /* M-B */ + /* 195 */ ED_UNASSIGNED, /* M-C */ + /* 196 */ ED_UNASSIGNED, /* M-D */ + /* 197 */ ED_UNASSIGNED, /* M-E */ + /* 198 */ ED_UNASSIGNED, /* M-F */ + /* 199 */ ED_UNASSIGNED, /* M-G */ + /* 200 */ ED_UNASSIGNED, /* M-H */ + /* 201 */ ED_UNASSIGNED, /* M-I */ + /* 202 */ ED_UNASSIGNED, /* M-J */ + /* 203 */ ED_UNASSIGNED, /* M-K */ + /* 204 */ ED_UNASSIGNED, /* M-L */ + /* 205 */ ED_UNASSIGNED, /* M-M */ + /* 206 */ ED_UNASSIGNED, /* M-N */ + /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ + /* 208 */ ED_UNASSIGNED, /* M-P */ + /* 209 */ ED_UNASSIGNED, /* M-Q */ + /* 210 */ ED_UNASSIGNED, /* M-R */ + /* 211 */ ED_UNASSIGNED, /* M-S */ + /* 212 */ ED_UNASSIGNED, /* M-T */ + /* 213 */ ED_UNASSIGNED, /* M-U */ + /* 214 */ ED_UNASSIGNED, /* M-V */ + /* 215 */ ED_UNASSIGNED, /* M-W */ + /* 216 */ ED_UNASSIGNED, /* M-X */ + /* 217 */ ED_UNASSIGNED, /* M-Y */ + /* 218 */ ED_UNASSIGNED, /* M-Z */ + /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ + /* 220 */ ED_UNASSIGNED, /* M-\ */ + /* 221 */ ED_UNASSIGNED, /* M-] */ + /* 222 */ ED_UNASSIGNED, /* M-^ */ + /* 223 */ ED_UNASSIGNED, /* M-_ */ + /* 224 */ ED_UNASSIGNED, /* M-` */ + /* 225 */ ED_UNASSIGNED, /* M-a */ + /* 226 */ ED_UNASSIGNED, /* M-b */ + /* 227 */ ED_UNASSIGNED, /* M-c */ + /* 228 */ ED_UNASSIGNED, /* M-d */ + /* 229 */ ED_UNASSIGNED, /* M-e */ + /* 230 */ ED_UNASSIGNED, /* M-f */ + /* 231 */ ED_UNASSIGNED, /* M-g */ + /* 232 */ ED_UNASSIGNED, /* M-h */ + /* 233 */ ED_UNASSIGNED, /* M-i */ + /* 234 */ ED_UNASSIGNED, /* M-j */ + /* 235 */ ED_UNASSIGNED, /* M-k */ + /* 236 */ ED_UNASSIGNED, /* M-l */ + /* 237 */ ED_UNASSIGNED, /* M-m */ + /* 238 */ ED_UNASSIGNED, /* M-n */ + /* 239 */ ED_UNASSIGNED, /* M-o */ + /* 240 */ ED_UNASSIGNED, /* M-p */ + /* 241 */ ED_UNASSIGNED, /* M-q */ + /* 242 */ ED_UNASSIGNED, /* M-r */ + /* 243 */ ED_UNASSIGNED, /* M-s */ + /* 244 */ ED_UNASSIGNED, /* M-t */ + /* 245 */ ED_UNASSIGNED, /* M-u */ + /* 246 */ ED_UNASSIGNED, /* M-v */ + /* 247 */ ED_UNASSIGNED, /* M-w */ + /* 248 */ ED_UNASSIGNED, /* M-x */ + /* 249 */ ED_UNASSIGNED, /* M-y */ + /* 250 */ ED_UNASSIGNED, /* M-z */ + /* 251 */ ED_UNASSIGNED, /* M-{ */ + /* 252 */ ED_UNASSIGNED, /* M-| */ + /* 253 */ ED_UNASSIGNED, /* M-} */ + /* 254 */ ED_UNASSIGNED, /* M-~ */ + /* 255 */ ED_UNASSIGNED /* M-^? */ +}; + + +/* map_init(): + * Initialize and allocate the maps + */ +protected int +map_init(EditLine *el) +{ + + /* + * Make sure those are correct before starting. + */ +#ifdef MAP_DEBUG + if (sizeof(el_map_emacs) != N_KEYS * sizeof(el_action_t)) + EL_ABORT((el->errfile, "Emacs map incorrect\n")); + if (sizeof(el_map_vi_command) != N_KEYS * sizeof(el_action_t)) + EL_ABORT((el->errfile, "Vi command map incorrect\n")); + if (sizeof(el_map_vi_insert) != N_KEYS * sizeof(el_action_t)) + EL_ABORT((el->errfile, "Vi insert map incorrect\n")); +#endif + + el->el_map.alt = el_malloc(sizeof(*el->el_map.alt) * N_KEYS); + if (el->el_map.alt == NULL) + return -1; + el->el_map.key = el_malloc(sizeof(*el->el_map.key) * N_KEYS); + if (el->el_map.key == NULL) + return -1; + el->el_map.emacs = el_map_emacs; + el->el_map.vic = el_map_vi_command; + el->el_map.vii = el_map_vi_insert; + el->el_map.help = el_malloc(sizeof(*el->el_map.help) * EL_NUM_FCNS); + if (el->el_map.help == NULL) + return -1; + (void) memcpy(el->el_map.help, help__get(), + sizeof(*el->el_map.help) * EL_NUM_FCNS); + el->el_map.func = el_malloc(sizeof(*el->el_map.func) * EL_NUM_FCNS); + if (el->el_map.func == NULL) + return -1; + memcpy(el->el_map.func, func__get(), sizeof(*el->el_map.func) + * EL_NUM_FCNS); + el->el_map.nfunc = EL_NUM_FCNS; + +#ifdef VIDEFAULT + map_init_vi(el); +#else + map_init_emacs(el); +#endif /* VIDEFAULT */ + return 0; +} + + +/* map_end(): + * Free the space taken by the editor maps + */ +protected void +map_end(EditLine *el) +{ + + el_free(el->el_map.alt); + el->el_map.alt = NULL; + el_free(el->el_map.key); + el->el_map.key = NULL; + el->el_map.emacs = NULL; + el->el_map.vic = NULL; + el->el_map.vii = NULL; + el_free(el->el_map.help); + el->el_map.help = NULL; + el_free(el->el_map.func); + el->el_map.func = NULL; +} + + +/* map_init_nls(): + * Find all the printable keys and bind them to self insert + */ +private void +map_init_nls(EditLine *el) +{ + int i; + + el_action_t *map = el->el_map.key; + + for (i = 0200; i <= 0377; i++) + if (Isprint(i)) + map[i] = ED_INSERT; +} + + +/* map_init_meta(): + * Bind all the meta keys to the appropriate ESC- sequence + */ +private void +map_init_meta(EditLine *el) +{ + Char buf[3]; + int i; + el_action_t *map = el->el_map.key; + el_action_t *alt = el->el_map.alt; + + for (i = 0; i <= 0377 && map[i] != EM_META_NEXT; i++) + continue; + + if (i > 0377) { + for (i = 0; i <= 0377 && alt[i] != EM_META_NEXT; i++) + continue; + if (i > 0377) { + i = 033; + if (el->el_map.type == MAP_VI) + map = alt; + } else + map = alt; + } + buf[0] = (Char) i; + buf[2] = 0; + for (i = 0200; i <= 0377; i++) + switch (map[i]) { + case ED_INSERT: + case ED_UNASSIGNED: + case ED_SEQUENCE_LEAD_IN: + break; + default: + buf[1] = i & 0177; + keymacro_add(el, buf, keymacro_map_cmd(el, (int) map[i]), XK_CMD); + break; + } + map[(int) buf[0]] = ED_SEQUENCE_LEAD_IN; +} + + +/* map_init_vi(): + * Initialize the vi bindings + */ +protected void +map_init_vi(EditLine *el) +{ + int i; + el_action_t *key = el->el_map.key; + el_action_t *alt = el->el_map.alt; + const el_action_t *vii = el->el_map.vii; + const el_action_t *vic = el->el_map.vic; + + el->el_map.type = MAP_VI; + el->el_map.current = el->el_map.key; + + keymacro_reset(el); + + for (i = 0; i < N_KEYS; i++) { + key[i] = vii[i]; + alt[i] = vic[i]; + } + + map_init_meta(el); + map_init_nls(el); + + tty_bind_char(el, 1); + terminal_bind_arrow(el); +} + + +/* map_init_emacs(): + * Initialize the emacs bindings + */ +protected void +map_init_emacs(EditLine *el) +{ + int i; + Char buf[3]; + el_action_t *key = el->el_map.key; + el_action_t *alt = el->el_map.alt; + const el_action_t *emacs = el->el_map.emacs; + + el->el_map.type = MAP_EMACS; + el->el_map.current = el->el_map.key; + keymacro_reset(el); + + for (i = 0; i < N_KEYS; i++) { + key[i] = emacs[i]; + alt[i] = ED_UNASSIGNED; + } + + map_init_meta(el); + map_init_nls(el); + + buf[0] = CONTROL('X'); + buf[1] = CONTROL('X'); + buf[2] = 0; + keymacro_add(el, buf, keymacro_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD); + + tty_bind_char(el, 1); + terminal_bind_arrow(el); +} + + +/* map_set_editor(): + * Set the editor + */ +protected int +map_set_editor(EditLine *el, Char *editor) +{ + + if (Strcmp(editor, STR("emacs")) == 0) { + map_init_emacs(el); + return 0; + } + if (Strcmp(editor, STR("vi")) == 0) { + map_init_vi(el); + return 0; + } + return -1; +} + + +/* map_get_editor(): + * Retrieve the editor + */ +protected int +map_get_editor(EditLine *el, const Char **editor) +{ + + if (editor == NULL) + return -1; + switch (el->el_map.type) { + case MAP_EMACS: + *editor = STR("emacs"); + return 0; + case MAP_VI: + *editor = STR("vi"); + return 0; + } + return -1; +} + + +/* map_print_key(): + * Print the function description for 1 key + */ +private void +map_print_key(EditLine *el, el_action_t *map, const Char *in) +{ + char outbuf[EL_BUFSIZ]; + el_bindings_t *bp, *ep; + + if (in[0] == '\0' || in[1] == '\0') { + (void) keymacro__decode_str(in, outbuf, sizeof(outbuf), ""); + ep = &el->el_map.help[el->el_map.nfunc]; + for (bp = el->el_map.help; bp < ep; bp++) + if (bp->func == map[(unsigned char) *in]) { + (void) fprintf(el->el_outfile, + "%s\t->\t" FSTR "\n", outbuf, bp->name); + return; + } + } else + keymacro_print(el, in); +} + + +/* map_print_some_keys(): + * Print keys from first to last + */ +private void +map_print_some_keys(EditLine *el, el_action_t *map, wint_t first, wint_t last) +{ + el_bindings_t *bp, *ep; + Char firstbuf[2], lastbuf[2]; + char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ]; + + firstbuf[0] = (Char)first; + firstbuf[1] = 0; + lastbuf[0] = (Char)last; + lastbuf[1] = 0; + if (map[first] == ED_UNASSIGNED) { + if (first == last) { + (void) keymacro__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); + (void) fprintf(el->el_outfile, + "%-15s-> is undefined\n", unparsbuf); + } + return; + } + ep = &el->el_map.help[el->el_map.nfunc]; + for (bp = el->el_map.help; bp < ep; bp++) { + if (bp->func == map[first]) { + if (first == last) { + (void) keymacro__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); + (void) fprintf(el->el_outfile, "%-15s-> " FSTR "\n", + unparsbuf, bp->name); + } else { + (void) keymacro__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); + (void) keymacro__decode_str(lastbuf, extrabuf, + sizeof(extrabuf), STRQQ); + (void) fprintf(el->el_outfile, + "%-4s to %-7s-> " FSTR "\n", + unparsbuf, extrabuf, bp->name); + } + return; + } + } +#ifdef MAP_DEBUG + if (map == el->el_map.key) { + (void) keymacro__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); + (void) fprintf(el->el_outfile, + "BUG!!! %s isn't bound to anything.\n", unparsbuf); + (void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n", + first, el->el_map.key[first]); + } else { + (void) keymacro__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); + (void) fprintf(el->el_outfile, + "BUG!!! %s isn't bound to anything.\n", unparsbuf); + (void) fprintf(el->el_outfile, "el->el_map.alt[%d] == %d\n", + first, el->el_map.alt[first]); + } +#endif + EL_ABORT((el->el_errfile, "Error printing keys\n")); +} + + +/* map_print_all_keys(): + * Print the function description for all keys. + */ +private void +map_print_all_keys(EditLine *el) +{ + int prev, i; + + (void) fprintf(el->el_outfile, "Standard key bindings\n"); + prev = 0; + for (i = 0; i < N_KEYS; i++) { + if (el->el_map.key[prev] == el->el_map.key[i]) + continue; + map_print_some_keys(el, el->el_map.key, prev, i - 1); + prev = i; + } + map_print_some_keys(el, el->el_map.key, prev, i - 1); + + (void) fprintf(el->el_outfile, "Alternative key bindings\n"); + prev = 0; + for (i = 0; i < N_KEYS; i++) { + if (el->el_map.alt[prev] == el->el_map.alt[i]) + continue; + map_print_some_keys(el, el->el_map.alt, prev, i - 1); + prev = i; + } + map_print_some_keys(el, el->el_map.alt, prev, i - 1); + + (void) fprintf(el->el_outfile, "Multi-character bindings\n"); + keymacro_print(el, STR("")); + (void) fprintf(el->el_outfile, "Arrow key bindings\n"); + terminal_print_arrow(el, STR("")); +} + + +/* map_bind(): + * Add/remove/change bindings + */ +protected int +map_bind(EditLine *el, int argc, const Char **argv) +{ + el_action_t *map; + int ntype, rem; + const Char *p; + Char inbuf[EL_BUFSIZ]; + Char outbuf[EL_BUFSIZ]; + const Char *in = NULL; + Char *out; + el_bindings_t *bp, *ep; + int cmd; + int key; + + if (argv == NULL) + return -1; + + map = el->el_map.key; + ntype = XK_CMD; + key = rem = 0; + for (argc = 1; (p = argv[argc]) != NULL; argc++) + if (p[0] == '-') + switch (p[1]) { + case 'a': + map = el->el_map.alt; + break; + + case 's': + ntype = XK_STR; + break; +#ifdef notyet + case 'c': + ntype = XK_EXE; + break; +#endif + case 'k': + key = 1; + break; + + case 'r': + rem = 1; + break; + + case 'v': + map_init_vi(el); + return 0; + + case 'e': + map_init_emacs(el); + return 0; + + case 'l': + ep = &el->el_map.help[el->el_map.nfunc]; + for (bp = el->el_map.help; bp < ep; bp++) + (void) fprintf(el->el_outfile, + "" FSTR "\n\t" FSTR "\n", + bp->name, bp->description); + return 0; + default: + (void) fprintf(el->el_errfile, + "" FSTR ": Invalid switch `%lc'.\n", + argv[0], (wint_t)p[1]); + } + else + break; + + if (argv[argc] == NULL) { + map_print_all_keys(el); + return 0; + } + if (key) + in = argv[argc++]; + else if ((in = parse__string(inbuf, argv[argc++])) == NULL) { + (void) fprintf(el->el_errfile, + "" FSTR ": Invalid \\ or ^ in instring.\n", + argv[0]); + return -1; + } + if (rem) { + if (key) { + (void) terminal_clear_arrow(el, in); + return -1; + } + if (in[1]) + (void) keymacro_delete(el, in); + else if (map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN) + (void) keymacro_delete(el, in); + else + map[(unsigned char) *in] = ED_UNASSIGNED; + return 0; + } + if (argv[argc] == NULL) { + if (key) + terminal_print_arrow(el, in); + else + map_print_key(el, map, in); + return 0; + } +#ifdef notyet + if (argv[argc + 1] != NULL) { + bindkeymacro_usage(); + return -1; + } +#endif + + switch (ntype) { + case XK_STR: + case XK_EXE: + if ((out = parse__string(outbuf, argv[argc])) == NULL) { + (void) fprintf(el->el_errfile, + "" FSTR ": Invalid \\ or ^ in outstring.\n", argv[0]); + return -1; + } + if (key) + terminal_set_arrow(el, in, keymacro_map_str(el, out), ntype); + else + keymacro_add(el, in, keymacro_map_str(el, out), ntype); + map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; + break; + + case XK_CMD: + if ((cmd = parse_cmd(el, argv[argc])) == -1) { + (void) fprintf(el->el_errfile, + "" FSTR ": Invalid command `" FSTR "'.\n", + argv[0], argv[argc]); + return -1; + } + if (key) + terminal_set_arrow(el, in, keymacro_map_cmd(el, cmd), ntype); + else { + if (in[1]) { + keymacro_add(el, in, keymacro_map_cmd(el, cmd), ntype); + map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; + } else { + keymacro_clear(el, map, in); + map[(unsigned char) *in] = (el_action_t)cmd; + } + } + break; + + /* coverity[dead_error_begin] */ + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); + break; + } + return 0; +} + + +/* map_addfunc(): + * add a user defined function + */ +protected int +map_addfunc(EditLine *el, const Char *name, const Char *help, el_func_t func) +{ + void *p; + size_t nf = el->el_map.nfunc + 1; + + if (name == NULL || help == NULL || func == NULL) + return -1; + + if ((p = el_realloc(el->el_map.func, nf * + sizeof(*el->el_map.func))) == NULL) + return -1; + el->el_map.func = p; + if ((p = el_realloc(el->el_map.help, nf * sizeof(*el->el_map.help))) + == NULL) + return -1; + el->el_map.help = p; + + nf = (size_t)el->el_map.nfunc; + el->el_map.func[nf] = func; + + el->el_map.help[nf].name = name; + el->el_map.help[nf].func = (int)nf; + el->el_map.help[nf].description = help; + el->el_map.nfunc++; + + return 0; +} diff --git a/lib/libedit/map.h b/lib/libedit/map.h index e271259..ef8a5f5 100644 --- a/lib/libedit/map.h +++ b/lib/libedit/map.h @@ -1,3 +1,5 @@ +/* $NetBSD: map.h,v 1.10 2014/07/06 18:15:34 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)map.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: map.h,v 1.8 2003/08/07 16:44:32 agc Exp $ - * $FreeBSD: src/lib/libedit/map.h,v 1.3.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/map.h 276881 2015-01-09 07:40:56Z bapt $ */ /* @@ -41,9 +42,9 @@ #define _h_el_map typedef struct el_bindings_t { /* for the "bind" shell command */ - const char *name; /* function name for bind command */ + const Char *name; /* function name for bind command */ int func; /* function numeric value */ - const char *description; /* description of function */ + const Char *description; /* description of function */ } el_bindings_t; @@ -57,19 +58,21 @@ int type; /* Emacs or vi */ el_bindings_t *help; /* The help for the editor functions */ el_func_t *func; /* List of available functions */ - int nfunc; /* The number of functions/help items */ + size_t nfunc; /* The number of functions/help items */ } el_map_t; #define MAP_EMACS 0 #define MAP_VI 1 -protected int map_bind(EditLine *, int, const char **); +#define N_KEYS 256 + +protected int map_bind(EditLine *, int, const Char **); protected int map_init(EditLine *); protected void map_end(EditLine *); protected void map_init_vi(EditLine *); protected void map_init_emacs(EditLine *); -protected int map_set_editor(EditLine *, char *); -protected int map_get_editor(EditLine *, const char **); -protected int map_addfunc(EditLine *, const char *, const char *, el_func_t); +protected int map_set_editor(EditLine *, Char *); +protected int map_get_editor(EditLine *, const Char **); +protected int map_addfunc(EditLine *, const Char *, const Char *, el_func_t); #endif /* _h_el_map */ diff --git a/lib/libedit/parse.c b/lib/libedit/parse.c new file mode 100644 index 0000000..6520efa --- /dev/null +++ b/lib/libedit/parse.c @@ -0,0 +1,291 @@ +/* $NetBSD: parse.c,v 1.35 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: parse.c,v 1.35 2016/02/17 19:47:49 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/parse.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * parse.c: parse an editline extended command + * + * commands are: + * + * bind + * echotc + * edit + * gettc + * history + * settc + * setty + */ +#include +#include + +#include "el.h" +#include "parse.h" + +private const struct { + const Char *name; + int (*func)(EditLine *, int, const Char **); +} cmds[] = { + { STR("bind"), map_bind }, + { STR("echotc"), terminal_echotc }, + { STR("edit"), el_editmode }, + { STR("history"), hist_command }, + { STR("telltc"), terminal_telltc }, + { STR("settc"), terminal_settc }, + { STR("setty"), tty_stty }, + { NULL, NULL } +}; + + +/* parse_line(): + * Parse a line and dispatch it + */ +protected int +parse_line(EditLine *el, const Char *line) +{ + const Char **argv; + int argc; + TYPE(Tokenizer) *tok; + + tok = FUN(tok,init)(NULL); + FUN(tok,str)(tok, line, &argc, &argv); + argc = FUN(el,parse)(el, argc, argv); + FUN(tok,end)(tok); + return argc; +} + + +/* el_parse(): + * Command dispatcher + */ +public int +FUN(el,parse)(EditLine *el, int argc, const Char *argv[]) +{ + const Char *ptr; + int i; + + if (argc < 1) + return -1; + ptr = Strchr(argv[0], ':'); + if (ptr != NULL) { + Char *tprog; + size_t l; + + if (ptr == argv[0]) + return 0; + l = (size_t)(ptr - argv[0] - 1); + tprog = el_malloc((l + 1) * sizeof(*tprog)); + if (tprog == NULL) + return 0; + (void) Strncpy(tprog, argv[0], l); + tprog[l] = '\0'; + ptr++; + l = (size_t)el_match(el->el_prog, tprog); + el_free(tprog); + if (!l) + return 0; + } else + ptr = argv[0]; + + for (i = 0; cmds[i].name != NULL; i++) + if (Strcmp(cmds[i].name, ptr) == 0) { + i = (*cmds[i].func) (el, argc, argv); + return -i; + } + return -1; +} + + +/* parse__escape(): + * Parse a string of the form ^ \ \ \U+xxxx and return + * the appropriate character or -1 if the escape is not valid + */ +protected int +parse__escape(const Char **ptr) +{ + const Char *p; + wint_t c; + + p = *ptr; + + if (p[1] == 0) + return -1; + + if (*p == '\\') { + p++; + switch (*p) { + case 'a': + c = '\007'; /* Bell */ + break; + case 'b': + c = '\010'; /* Backspace */ + break; + case 't': + c = '\011'; /* Horizontal Tab */ + break; + case 'n': + c = '\012'; /* New Line */ + break; + case 'v': + c = '\013'; /* Vertical Tab */ + break; + case 'f': + c = '\014'; /* Form Feed */ + break; + case 'r': + c = '\015'; /* Carriage Return */ + break; + case 'e': + c = '\033'; /* Escape */ + break; + case 'U': /* Unicode \U+xxxx or \U+xxxxx format */ + { + int i; + const Char hex[] = STR("0123456789ABCDEF"); + const Char *h; + ++p; + if (*p++ != '+') + return -1; + c = 0; + for (i = 0; i < 5; ++i) { + h = Strchr(hex, *p++); + if (!h && i < 4) + return -1; + else if (h) + c = (c << 4) | ((int)(h - hex)); + else + --p; + } + if (c > 0x10FFFF) /* outside valid character range */ + return -1; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int cnt, ch; + + for (cnt = 0, c = 0; cnt < 3; cnt++) { + ch = *p++; + if (ch < '0' || ch > '7') { + p--; + break; + } + c = (c << 3) | (ch - '0'); + } + if ((c & (wint_t)0xffffff00) != (wint_t)0) + return -1; + --p; + break; + } + default: + c = *p; + break; + } + } else if (*p == '^') { + p++; + c = (*p == '?') ? '\177' : (*p & 0237); + } else + c = *p; + *ptr = ++p; + return c; +} + +/* parse__string(): + * Parse the escapes from in and put the raw string out + */ +protected Char * +parse__string(Char *out, const Char *in) +{ + Char *rv = out; + int n; + + for (;;) + switch (*in) { + case '\0': + *out = '\0'; + return rv; + + case '\\': + case '^': + if ((n = parse__escape(&in)) == -1) + return NULL; + *out++ = (Char)n; + break; + + case 'M': + if (in[1] == '-' && in[2] != '\0') { + *out++ = '\033'; + in += 2; + break; + } + /*FALLTHROUGH*/ + + default: + *out++ = *in++; + break; + } +} + + +/* parse_cmd(): + * Return the command number for the command string given + * or -1 if one is not found + */ +protected int +parse_cmd(EditLine *el, const Char *cmd) +{ + el_bindings_t *b = el->el_map.help; + size_t i; + + for (i = 0; i < el->el_map.nfunc; i++) + if (Strcmp(b[i].name, cmd) == 0) + return b[i].func; + return -1; +} diff --git a/lib/libedit/parse.h b/lib/libedit/parse.h index 0fdfd79..6013862 100644 --- a/lib/libedit/parse.h +++ b/lib/libedit/parse.h @@ -1,3 +1,5 @@ +/* $NetBSD: parse.h,v 1.7 2009/12/30 22:37:40 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)parse.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: parse.h,v 1.6 2005/05/29 04:58:15 lukem Exp $ - * $FreeBSD: src/lib/libedit/parse.h,v 1.3.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/parse.h 276881 2015-01-09 07:40:56Z bapt $ */ /* @@ -40,9 +41,9 @@ #ifndef _h_el_parse #define _h_el_parse -protected int parse_line(EditLine *, const char *); -protected int parse__escape(const char **); -protected char *parse__string(char *, const char *); -protected int parse_cmd(EditLine *, const char *); +protected int parse_line(EditLine *, const Char *); +protected int parse__escape(const Char **); +protected Char *parse__string(Char *, const Char *); +protected int parse_cmd(EditLine *, const Char *); #endif /* _h_el_parse */ diff --git a/lib/libedit/prompt.c b/lib/libedit/prompt.c new file mode 100644 index 0000000..8bc14cf --- /dev/null +++ b/lib/libedit/prompt.c @@ -0,0 +1,201 @@ +/* $NetBSD: prompt.c,v 1.23 2016/02/16 15:53:48 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)prompt.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: prompt.c,v 1.23 2016/02/16 15:53:48 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/prompt.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * prompt.c: Prompt printing functions + */ +#include +#include "el.h" + +private Char *prompt_default(EditLine *); +private Char *prompt_default_r(EditLine *); + +/* prompt_default(): + * Just a default prompt, in case the user did not provide one + */ +private Char * +/*ARGSUSED*/ +prompt_default(EditLine *el __attribute__((__unused__))) +{ + static Char a[3] = {'?', ' ', '\0'}; + + return a; +} + + +/* prompt_default_r(): + * Just a default rprompt, in case the user did not provide one + */ +private Char * +/*ARGSUSED*/ +prompt_default_r(EditLine *el __attribute__((__unused__))) +{ + static Char a[1] = {'\0'}; + + return a; +} + + +/* prompt_print(): + * Print the prompt and update the prompt position. + */ +protected void +prompt_print(EditLine *el, int op) +{ + el_prompt_t *elp; + Char *p; + int ignore = 0; + + if (op == EL_PROMPT) + elp = &el->el_prompt; + else + elp = &el->el_rprompt; + + if (elp->p_wide) + p = (*elp->p_func)(el); + else + p = ct_decode_string((char *)(void *)(*elp->p_func)(el), + &el->el_scratch); + + for (; *p; p++) { + if (elp->p_ignore == *p) { + ignore = !ignore; + continue; + } + if (ignore) + terminal__putc(el, *p); + else + re_putc(el, *p, 1); + } + + elp->p_pos.v = el->el_refresh.r_cursor.v; + elp->p_pos.h = el->el_refresh.r_cursor.h; +} + + +/* prompt_init(): + * Initialize the prompt stuff + */ +protected int +prompt_init(EditLine *el) +{ + + el->el_prompt.p_func = prompt_default; + el->el_prompt.p_pos.v = 0; + el->el_prompt.p_pos.h = 0; + el->el_prompt.p_ignore = '\0'; + el->el_rprompt.p_func = prompt_default_r; + el->el_rprompt.p_pos.v = 0; + el->el_rprompt.p_pos.h = 0; + el->el_rprompt.p_ignore = '\0'; + return 0; +} + + +/* prompt_end(): + * Clean up the prompt stuff + */ +protected void +/*ARGSUSED*/ +prompt_end(EditLine *el __attribute__((__unused__))) +{ +} + + +/* prompt_set(): + * Install a prompt printing function + */ +protected int +prompt_set(EditLine *el, el_pfunc_t prf, Char c, int op, int wide) +{ + el_prompt_t *p; + + if (op == EL_PROMPT || op == EL_PROMPT_ESC) + p = &el->el_prompt; + else + p = &el->el_rprompt; + + if (prf == NULL) { + if (op == EL_PROMPT || op == EL_PROMPT_ESC) + p->p_func = prompt_default; + else + p->p_func = prompt_default_r; + } else { + p->p_func = prf; + } + + p->p_ignore = c; + + p->p_pos.v = 0; + p->p_pos.h = 0; + p->p_wide = wide; + + return 0; +} + + +/* prompt_get(): + * Retrieve the prompt printing function + */ +protected int +prompt_get(EditLine *el, el_pfunc_t *prf, Char *c, int op) +{ + el_prompt_t *p; + + if (prf == NULL) + return -1; + + if (op == EL_PROMPT) + p = &el->el_prompt; + else + p = &el->el_rprompt; + + if (prf) + *prf = p->p_func; + if (c) + *c = p->p_ignore; + + return 0; +} diff --git a/lib/libedit/prompt.h b/lib/libedit/prompt.h index 7d50318..95597ff 100644 --- a/lib/libedit/prompt.h +++ b/lib/libedit/prompt.h @@ -1,3 +1,5 @@ +/* $NetBSD: prompt.h,v 1.13 2016/02/17 19:47:49 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)prompt.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: prompt.h,v 1.6 2003/08/07 16:44:32 agc Exp $ - * $FreeBSD: src/lib/libedit/prompt.h,v 1.2.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/prompt.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -40,18 +41,18 @@ #ifndef _h_el_prompt #define _h_el_prompt -#include "histedit.h" - -typedef char * (*el_pfunc_t)(EditLine*); +typedef Char *(*el_pfunc_t)(EditLine *); typedef struct el_prompt_t { - el_pfunc_t p_func; /* Function to return the prompt */ - coord_t p_pos; /* position in the line after prompt */ + el_pfunc_t p_func; /* Function to return the prompt */ + coord_t p_pos; /* position in the line after prompt */ + Char p_ignore; /* character to start/end literal */ + int p_wide; } el_prompt_t; protected void prompt_print(EditLine *, int); -protected int prompt_set(EditLine *, el_pfunc_t, int); -protected int prompt_get(EditLine *, el_pfunc_t *, int); +protected int prompt_set(EditLine *, el_pfunc_t, Char, int, int); +protected int prompt_get(EditLine *, el_pfunc_t *, Char *, int); protected int prompt_init(EditLine *); protected void prompt_end(EditLine *); diff --git a/lib/libedit/read.c b/lib/libedit/read.c index 6817b1b..d7ed83e 100644 --- a/lib/libedit/read.c +++ b/lib/libedit/read.c @@ -1,3 +1,5 @@ +/* $NetBSD: read.c,v 1.86 2016/03/02 19:24:20 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -28,33 +30,40 @@ * 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. - * - * $NetBSD: read.c,v 1.39 2005/08/02 12:11:14 christos Exp $ */ +#include "config.h" #if !defined(lint) && !defined(SCCSID) +#if 0 static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: read.c,v 1.86 2016/03/02 19:24:20 christos Exp $"); +#endif #endif /* not lint && not SCCSID */ #include -__FBSDID("$FreeBSD: src/lib/libedit/read.c,v 1.11.14.1 2005/10/09 03:44:01 delphij Exp $"); +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/read.c 313981 2017-02-20 03:33:59Z pfg $"); /* * read.c: Clean this junk up! This is horrible code. * Terminal read functions */ -#include "sys.h" +#include #include #include -#include +#include #include +#include +#include + #include "el.h" -#define OKCMD -1 +#define OKCMD -1 /* must be -1! */ private int read__fixio(int, int); private int read_preread(EditLine *); -private int read_char(EditLine *, char *); -private int read_getcmd(EditLine *, el_action_t *, char *); +private int read_char(EditLine *, wchar_t *); +private int read_getcmd(EditLine *, el_action_t *, Char *); +private void read_pop(c_macro_t *); /* read_init(): * Initialize the read stuff @@ -87,7 +96,7 @@ protected el_rfunc_t el_read_getfn(EditLine *el) { - return (el->el_read.read_char == read_char) ? + return el->el_read.read_char == read_char ? EL_BUILTIN_GETCFN : el->el_read.read_char; } @@ -120,7 +129,7 @@ */ /* ARGSUSED */ private int -read__fixio(int fd __unused, int e) +read__fixio(int fd __attribute__((__unused__)), int e) { switch (e) { @@ -129,7 +138,7 @@ #ifdef EWOULDBLOCK case EWOULDBLOCK: #ifndef TRY_AGAIN -#define TRY_AGAIN +#define TRY_AGAIN #endif #endif /* EWOULDBLOCK */ @@ -137,7 +146,7 @@ #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EAGAIN: #ifndef TRY_AGAIN -#define TRY_AGAIN +#define TRY_AGAIN #endif #endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */ #endif /* POSIX && EAGAIN */ @@ -146,10 +155,10 @@ #ifdef TRY_AGAIN #if defined(F_SETFL) && defined(O_NDELAY) if ((e = fcntl(fd, F_GETFL, 0)) == -1) - return (-1); + return -1; if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1) - return (-1); + return -1; else e = 1; #endif /* F_SETFL && O_NDELAY */ @@ -158,21 +167,21 @@ { int zero = 0; - if (ioctl(fd, FIONBIO, (ioctl_t) & zero) == -1) - return (-1); + if (ioctl(fd, FIONBIO, &zero) == -1) + return -1; else e = 1; } #endif /* FIONBIO */ #endif /* TRY_AGAIN */ - return (e ? 0 : -1); + return e ? 0 : -1; case EINTR: - return (0); + return 0; default: - return (-1); + return -1; } } @@ -186,10 +195,13 @@ int chrs = 0; if (el->el_tty.t_mode == ED_IO) - return (0); + return 0; +#ifndef WIDECHAR +/* FIONREAD attempts to buffer up multiple bytes, and to make that work + * properly with partial wide/UTF-8 characters would need some careful work. */ #ifdef FIONREAD - (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs); + (void) ioctl(el->el_infd, FIONREAD, &chrs); if (chrs > 0) { char buf[EL_BUFSIZ]; @@ -201,8 +213,8 @@ } } #endif /* FIONREAD */ - - return (chrs > 0); +#endif + return chrs > 0; } @@ -210,36 +222,43 @@ * Push a macro */ public void -el_push(EditLine *el, char *str) +FUN(el,push)(EditLine *el, const Char *str) { c_macro_t *ma = &el->el_chared.c_macro; if (str != NULL && ma->level + 1 < EL_MAXMACRO) { ma->level++; - if ((ma->macro[ma->level] = el_strdup(str)) != NULL) + if ((ma->macro[ma->level] = Strdup(str)) != NULL) return; ma->level--; } - term_beep(el); - term__flush(); + terminal_beep(el); + terminal__flush(el); } /* read_getcmd(): - * Return next command from the input stream. + * Get next command from the input stream, return OKCMD on success. + * Character values > 255 are not looked up in the map, but inserted. */ private int -read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch) +read_getcmd(EditLine *el, el_action_t *cmdnum, Char *ch) { + static const Char meta = (Char)0x80; el_action_t cmd; + wchar_t wc; int num; + el->el_errno = 0; do { - if ((num = el_getc(el, ch)) != 1) /* if EOF or error */ - return (num); + if ((num = el_wgetc(el, &wc)) != 1) {/* if EOF or error */ + el->el_errno = num == 0 ? 0 : errno; + return 0; /* not OKCMD */ + } + *ch = (Char)wc; #ifdef KANJI - if ((*ch & 0200)) { + if ((*ch & meta)) { el->el_state.metanext = 0; cmd = CcViMap[' ']; break; @@ -248,17 +267,22 @@ if (el->el_state.metanext) { el->el_state.metanext = 0; - *ch |= 0200; + *ch |= meta; } - cmd = el->el_map.current[(unsigned char) *ch]; +#ifdef WIDECHAR + if (*ch >= N_KEYS) + cmd = ED_INSERT; + else +#endif + cmd = el->el_map.current[(unsigned char) *ch]; if (cmd == ED_SEQUENCE_LEAD_IN) { - key_value_t val; - switch (key_get(el, ch, &val)) { + keymacro_value_t val; + switch (keymacro_get(el, ch, &val)) { case XK_CMD: cmd = val.cmd; break; case XK_STR: - el_push(el, val.str); + FUN(el,push)(el, val.str); break; #ifdef notyet case XK_EXE: @@ -275,77 +299,155 @@ el->el_map.current = el->el_map.key; } while (cmd == ED_SEQUENCE_LEAD_IN); *cmdnum = cmd; - return (OKCMD); + return OKCMD; } - /* read_char(): * Read a character from the tty. */ private int -read_char(EditLine *el, char *cp) +read_char(EditLine *el, wchar_t *cp) { - int num_read; + ssize_t num_read; int tried = 0; + char cbuf[MB_LEN_MAX]; + size_t cbp = 0; + int save_errno = errno; - while ((num_read = read(el->el_infd, cp, 1)) == -1) - if (!tried && read__fixio(el->el_infd, errno) == 0) - tried = 1; - else { - *cp = '\0'; - return (-1); + again: + el->el_signal->sig_no = 0; + while ((num_read = read(el->el_infd, cbuf + cbp, (size_t)1)) == -1) { + int e = errno; + switch (el->el_signal->sig_no) { + case SIGCONT: + FUN(el,set)(el, EL_REFRESH); + /*FALLTHROUGH*/ + case SIGWINCH: + sig_set(el); + goto again; + default: + break; } + if (!tried && read__fixio(el->el_infd, e) == 0) { + errno = save_errno; + tried = 1; + } else { + errno = e; + *cp = L'\0'; + return -1; + } + } - return (num_read); + /* Test for EOF */ + if (num_read == 0) { + *cp = L'\0'; + return 0; + } + + for (;;) { + + ++cbp; + switch (ct_mbrtowc(cp, cbuf, cbp)) { + case (size_t)-1: + if (cbp > 1) { + /* + * Invalid sequence, discard all bytes + * except the last one. + */ + cbuf[0] = cbuf[cbp - 1]; + cbp = 0; + break; + } else { + /* Invalid byte, discard it. */ + cbp = 0; + goto again; + } + case (size_t)-2: + /* + * We don't support other multibyte charsets. + * The second condition shouldn't happen + * and is here merely for additional safety. + */ + if ((el->el_flags & CHARSET_IS_UTF8) == 0 || + cbp >= MB_LEN_MAX) { + errno = EILSEQ; + *cp = L'\0'; + return -1; + } + /* Incomplete sequence, read another byte. */ + goto again; + default: + /* Valid character, process it. */ + return 1; + } + } } +/* read_pop(): + * Pop a macro from the stack + */ +private void +read_pop(c_macro_t *ma) +{ + int i; -/* el_getc(): - * Read a character + el_free(ma->macro[0]); + for (i = 0; i < ma->level; i++) + ma->macro[i] = ma->macro[i + 1]; + ma->level--; + ma->offset = 0; +} + +/* el_wgetc(): + * Read a wide character */ public int -el_getc(EditLine *el, char *cp) +el_wgetc(EditLine *el, wchar_t *cp) { int num_read; c_macro_t *ma = &el->el_chared.c_macro; - term__flush(); + terminal__flush(el); for (;;) { if (ma->level < 0) { if (!read_preread(el)) break; } + if (ma->level < 0) break; - if (ma->macro[ma->level][ma->offset] == '\0') { - el_free(ma->macro[ma->level--]); - ma->offset = 0; + if (ma->macro[0][ma->offset] == '\0') { + read_pop(ma); continue; } - *cp = ma->macro[ma->level][ma->offset++] & 0377; - if (ma->macro[ma->level][ma->offset] == '\0') { + + *cp = ma->macro[0][ma->offset++]; + + if (ma->macro[0][ma->offset] == '\0') { /* Needed for QuoteMode On */ - el_free(ma->macro[ma->level--]); - ma->offset = 0; + read_pop(ma); } - return (1); + + return 1; } #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Turning raw mode on\n"); #endif /* DEBUG_READ */ if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */ - return (0); + return 0; #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Reading a character\n"); #endif /* DEBUG_READ */ num_read = (*el->el_read.read_char)(el, cp); + if (num_read < 0) + el->el_errno = errno; #ifdef DEBUG_READ - (void) fprintf(el->el_errfile, "Got it %c\n", *cp); + (void) fprintf(el->el_errfile, "Got it %lc\n", *cp); #endif /* DEBUG_READ */ - return (num_read); + return num_read; } protected void @@ -366,7 +468,7 @@ re_refresh(el); /* print the prompt */ if (el->el_flags & UNBUFFERED) - term__flush(); + terminal__flush(el); } protected void @@ -378,27 +480,34 @@ sig_clr(el); } -public const char * -el_gets(EditLine *el, int *nread) +public const Char * +FUN(el,gets)(EditLine *el, int *nread) { int retval; el_action_t cmdnum = 0; int num; /* how many chars we have read at NL */ - char ch; + wchar_t wc; + Char ch, *cp; int crlf = 0; + int nrb; #ifdef FIONREAD c_macro_t *ma = &el->el_chared.c_macro; #endif /* FIONREAD */ + if (nread == NULL) + nread = &nrb; + *nread = 0; + if (el->el_flags & NO_TTY) { - char *cp = el->el_line.buffer; size_t idx; - while ((*el->el_read.read_char)(el, cp) == 1) { + cp = el->el_line.buffer; + while ((num = (*el->el_read.read_char)(el, &wc)) == 1) { + *cp = (Char)wc; /* make sure there is space for next character */ if (cp + 1 >= el->el_line.limit) { - idx = (cp - el->el_line.buffer); - if (!ch_enlargebufs(el, 2)) + idx = (size_t)(cp - el->el_line.buffer); + if (!ch_enlargebufs(el, (size_t)2)) break; cp = &el->el_line.buffer[idx]; } @@ -408,12 +517,13 @@ if (cp[-1] == '\r' || cp[-1] == '\n') break; } + if (num == -1) { + if (errno == EINTR) + cp = el->el_line.buffer; + el->el_errno = errno; + } - el->el_line.cursor = el->el_line.lastchar = cp; - *cp = '\0'; - if (nread) - *nread = el->el_line.cursor - el->el_line.buffer; - return (el->el_line.buffer); + goto noedit; } @@ -421,12 +531,12 @@ if (el->el_tty.t_mode == EX_IO && ma->level < 0) { long chrs = 0; - (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs); + (void) ioctl(el->el_infd, FIONREAD, &chrs); if (chrs == 0) { if (tty_rawmode(el) < 0) { - if (nread) - *nread = 0; - return (NULL); + errno = 0; + *nread = 0; + return NULL; } } } @@ -436,25 +546,24 @@ read_prepare(el); if (el->el_flags & EDIT_DISABLED) { - char *cp; size_t idx; + if ((el->el_flags & UNBUFFERED) == 0) cp = el->el_line.buffer; else cp = el->el_line.lastchar; - term__flush(); + terminal__flush(el); - while ((*el->el_read.read_char)(el, cp) == 1) { + while ((num = (*el->el_read.read_char)(el, &wc)) == 1) { + *cp = (Char)wc; /* make sure there is space next character */ if (cp + 1 >= el->el_line.limit) { - idx = (cp - el->el_line.buffer); - if (!ch_enlargebufs(el, 2)) + idx = (size_t)(cp - el->el_line.buffer); + if (!ch_enlargebufs(el, (size_t)2)) break; cp = &el->el_line.buffer[idx]; } - if (*cp == 4) /* ought to be stty eof */ - break; cp++; crlf = cp[-1] == '\r' || cp[-1] == '\n'; if (el->el_flags & UNBUFFERED) @@ -463,11 +572,13 @@ break; } - el->el_line.cursor = el->el_line.lastchar = cp; - *cp = '\0'; - if (nread) - *nread = el->el_line.cursor - el->el_line.buffer; - return (el->el_line.buffer); + if (num == -1) { + if (errno == EINTR) + cp = el->el_line.buffer; + el->el_errno = errno; + } + + goto noedit; } for (num = OKCMD; num == OKCMD;) { /* while still editing this @@ -477,13 +588,20 @@ #endif /* DEBUG_EDIT */ /* if EOF or error */ if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) { + num = -1; #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Returning from el_gets %d\n", num); #endif /* DEBUG_READ */ break; } - if ((unsigned int)cmdnum >= el->el_map.nfunc) { /* BUG CHECK command */ + if (el->el_errno == EINTR) { + el->el_line.buffer[0] = '\0'; + el->el_line.lastchar = + el->el_line.cursor = el->el_line.buffer; + break; + } + if ((size_t)cmdnum >= el->el_map.nfunc) { /* BUG CHECK command */ #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "ERROR: illegal command from key 0%o\r\n", ch); @@ -499,7 +617,7 @@ break; if (b->name) (void) fprintf(el->el_errfile, - "Executing %s\n", b->name); + "Executing " FSTR "\n", b->name); else (void) fprintf(el->el_errfile, "Error command = %d\n", cmdnum); @@ -513,7 +631,7 @@ el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) { if (cmdnum == VI_DELETE_PREV_CHAR && el->el_chared.c_redo.pos != el->el_chared.c_redo.buf - && isprint((unsigned char)el->el_chared.c_redo.pos[-1])) + && Isprint(el->el_chared.c_redo.pos[-1])) el->el_chared.c_redo.pos--; else *el->el_chared.c_redo.pos++ = ch; @@ -544,7 +662,7 @@ case CC_REFRESH_BEEP: re_refresh(el); - term_beep(el); + terminal_beep(el); break; case CC_NORM: /* normal char */ @@ -565,7 +683,7 @@ break; case CC_NEWLINE: /* normal end of line */ - num = el->el_line.lastchar - el->el_line.buffer; + num = (int)(el->el_line.lastchar - el->el_line.buffer); break; case CC_FATAL: /* fatal error, reset to known state */ @@ -576,7 +694,7 @@ /* put (real) cursor in a known place */ re_clear_display(el); /* reset the display stuff */ ch_reset(el, 1); /* reset the input pointers */ - re_refresh(el); /* print the prompt again */ + re_refresh(el); /* print the prompt again */ break; case CC_ERROR: @@ -585,8 +703,8 @@ (void) fprintf(el->el_errfile, "*** editor ERROR ***\r\n\n"); #endif /* DEBUG_READ */ - term_beep(el); - term__flush(); + terminal_beep(el); + terminal__flush(el); break; } el->el_state.argument = 1; @@ -596,15 +714,26 @@ break; } - term__flush(); /* flush any buffered output */ + terminal__flush(el); /* flush any buffered output */ /* make sure the tty is set up correctly */ if ((el->el_flags & UNBUFFERED) == 0) { read_finish(el); - if (nread) - *nread = num; + *nread = num != -1 ? num : 0; } else { - if (nread) - *nread = el->el_line.lastchar - el->el_line.buffer; + *nread = (int)(el->el_line.lastchar - el->el_line.buffer); } - return (num ? el->el_line.buffer : NULL); + goto done; +noedit: + el->el_line.cursor = el->el_line.lastchar = cp; + *cp = '\0'; + *nread = (int)(el->el_line.cursor - el->el_line.buffer); +done: + if (*nread == 0) { + if (num == -1) { + *nread = -1; + errno = el->el_errno; + } + return NULL; + } else + return el->el_line.buffer; } diff --git a/lib/libedit/read.h b/lib/libedit/read.h index 9bb5eac..d59a8a1 100644 --- a/lib/libedit/read.h +++ b/lib/libedit/read.h @@ -1,3 +1,5 @@ +/* $NetBSD: read.h,v 1.9 2016/02/24 17:13:22 christos Exp $ */ + /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. @@ -13,13 +15,6 @@ * 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 NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -33,8 +28,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $NetBSD: read.h,v 1.4 2004/02/27 14:52:18 christos Exp $ - * $FreeBSD: src/lib/libedit/read.h,v 1.1.2.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/read.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -43,12 +37,12 @@ #ifndef _h_el_read #define _h_el_read -typedef int (*el_rfunc_t)(EditLine *, char *); +typedef int (*el_rfunc_t)(EditLine *, wchar_t *); typedef struct el_read_t { el_rfunc_t read_char; /* Function to read a character */ } el_read_t; - + protected int read_init(EditLine *); protected void read_prepare(EditLine *); protected void read_finish(EditLine *); diff --git a/lib/libedit/readline.c b/lib/libedit/readline.c new file mode 100644 index 0000000..80f18e3 --- /dev/null +++ b/lib/libedit/readline.c @@ -0,0 +1,2336 @@ +/* $NetBSD: readline.c,v 1.126 2016/02/24 17:13:22 christos Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +__RCSID("$NetBSD: readline.c,v 1.126 2016/02/24 17:13:22 christos Exp $"); +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/readline.c 313981 2017-02-20 03:33:59Z pfg $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "readline/readline.h" +#include "el.h" +#include "filecomplete.h" + +void rl_prep_terminal(int); +void rl_deprep_terminal(void); + +/* for rl_complete() */ +#define TAB '\r' + +/* see comment at the #ifdef for sense of this */ +/* #define GDB_411_HACK */ + +/* readline compatibility stuff - look at readline sources/documentation */ +/* to see what these variables mean */ +const char *rl_library_version = "EditLine wrapper"; +int rl_readline_version = RL_READLINE_VERSION; +static char empty[] = { '\0' }; +static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' }; +static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', + '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; +char *rl_readline_name = empty; +FILE *rl_instream = NULL; +FILE *rl_outstream = NULL; +int rl_point = 0; +int rl_end = 0; +char *rl_line_buffer = NULL; +rl_vcpfunc_t *rl_linefunc = NULL; +int rl_done = 0; +VFunction *rl_event_hook = NULL; +KEYMAP_ENTRY_ARRAY emacs_standard_keymap, + emacs_meta_keymap, + emacs_ctlx_keymap; +/* + * The following is not implemented; we always catch signals in the + * libedit fashion: set handlers on entry to el_gets() and clear them + * on the way out. This simplistic approach works for most cases; if + * it does not work for your application, please let us know. + */ +int rl_catch_signals = 1; +int rl_catch_sigwinch = 1; + +int history_base = 1; /* probably never subject to change */ +int history_length = 0; +int max_input_history = 0; +char history_expansion_char = '!'; +char history_subst_char = '^'; +char *history_no_expand_chars = expand_chars; +Function *history_inhibit_expansion_function = NULL; +char *history_arg_extract(int start, int end, const char *str); + +int rl_inhibit_completion = 0; +int rl_attempted_completion_over = 0; +char *rl_basic_word_break_characters = break_chars; +char *rl_completer_word_break_characters = NULL; +char *rl_completer_quote_characters = NULL; +rl_compentry_func_t *rl_completion_entry_function = NULL; +char *(*rl_completion_word_break_hook)(void) = NULL; +rl_completion_func_t *rl_attempted_completion_function = NULL; +Function *rl_pre_input_hook = NULL; +Function *rl_startup1_hook = NULL; +int (*rl_getc_function)(FILE *) = NULL; +char *rl_terminal_name = NULL; +int rl_already_prompted = 0; +int rl_filename_completion_desired = 0; +int rl_ignore_completion_duplicates = 0; +int readline_echoing_p = 1; +int _rl_print_completions_horizontally = 0; +VFunction *rl_redisplay_function = NULL; +Function *rl_startup_hook = NULL; +VFunction *rl_completion_display_matches_hook = NULL; +VFunction *rl_prep_term_function = (VFunction *)rl_prep_terminal; +VFunction *rl_deprep_term_function = (VFunction *)rl_deprep_terminal; +KEYMAP_ENTRY_ARRAY emacs_meta_keymap; + +/* + * The current prompt string. + */ +char *rl_prompt = NULL; +/* + * This is set to character indicating type of completion being done by + * rl_complete_internal(); this is available for application completion + * functions. + */ +int rl_completion_type = 0; + +/* + * If more than this number of items results from query for possible + * completions, we ask user if they are sure to really display the list. + */ +int rl_completion_query_items = 100; + +/* + * List of characters which are word break characters, but should be left + * in the parsed text when it is passed to the completion function. + * Shell uses this to help determine what kind of completing to do. + */ +char *rl_special_prefixes = NULL; + +/* + * This is the character appended to the completed words if at the end of + * the line. Default is ' ' (a space). + */ +int rl_completion_append_character = ' '; + +/* stuff below is used internally by libedit for readline emulation */ + +static History *h = NULL; +static EditLine *e = NULL; +static rl_command_func_t *map[256]; +static jmp_buf topbuf; + +/* internal functions */ +static unsigned char _el_rl_complete(EditLine *, int); +static unsigned char _el_rl_tstp(EditLine *, int); +static char *_get_prompt(EditLine *); +static int _getc_function(EditLine *, wchar_t *); +static HIST_ENTRY *_move_history(int); +static int _history_expand_command(const char *, size_t, size_t, + char **); +static char *_rl_compat_sub(const char *, const char *, + const char *, int); +static int _rl_event_read_char(EditLine *, wchar_t *); +static void _rl_update_pos(void); + + +/* ARGSUSED */ +static char * +_get_prompt(EditLine *el __attribute__((__unused__))) +{ + rl_already_prompted = 1; + return rl_prompt; +} + + +/* + * generic function for moving around history + */ +static HIST_ENTRY * +_move_history(int op) +{ + HistEvent ev; + static HIST_ENTRY rl_he; + + if (history(h, &ev, op) != 0) + return NULL; + + rl_he.line = ev.str; + rl_he.data = NULL; + + return &rl_he; +} + + +/* + * read one key from user defined input function + */ +static int +/*ARGSUSED*/ +_getc_function(EditLine *el __attribute__((__unused__)), wchar_t *c) +{ + int i; + + i = (*rl_getc_function)(NULL); + if (i == -1) + return 0; + *c = (wchar_t)i; + return 1; +} + +static void +_resize_fun(EditLine *el, void *a) +{ + const LineInfo *li; + char **ap = a; + + li = el_line(el); + /* a cheesy way to get rid of const cast. */ + *ap = memchr(li->buffer, *li->buffer, (size_t)1); +} + +static const char * +_default_history_file(void) +{ + struct passwd *p; + static char *path; + size_t len; + + if (path) + return path; + + if ((p = getpwuid(getuid())) == NULL) + return NULL; + + len = strlen(p->pw_dir) + sizeof("/.history"); + if ((path = malloc(len)) == NULL) + return NULL; + + (void)snprintf(path, len, "%s/.history", p->pw_dir); + return path; +} + +/* + * READLINE compatibility stuff + */ + +/* + * Set the prompt + */ +int +rl_set_prompt(const char *prompt) +{ + char *p; + + if (!prompt) + prompt = ""; + if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) + return 0; + if (rl_prompt) + el_free(rl_prompt); + rl_prompt = strdup(prompt); + if (rl_prompt == NULL) + return -1; + + while ((p = strchr(rl_prompt, RL_PROMPT_END_IGNORE)) != NULL) + *p = RL_PROMPT_START_IGNORE; + + return 0; +} + +/* + * initialize rl compat stuff + */ +int +rl_initialize(void) +{ + HistEvent ev; + int editmode = 1; + struct termios t; + + if (e != NULL) + el_end(e); + if (h != NULL) + history_end(h); + + if (!rl_instream) + rl_instream = stdin; + if (!rl_outstream) + rl_outstream = stdout; + + /* + * See if we don't really want to run the editor + */ + if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0) + editmode = 0; + + e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr); + + if (!editmode) + el_set(e, EL_EDITMODE, 0); + + h = history_init(); + if (!e || !h) + return -1; + + history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */ + history_length = 0; + max_input_history = INT_MAX; + el_set(e, EL_HIST, history, h); + + /* Setup resize function */ + el_set(e, EL_RESIZE, _resize_fun, &rl_line_buffer); + + /* setup getc function if valid */ + if (rl_getc_function) + el_set(e, EL_GETCFN, _getc_function); + + /* for proper prompt printing in readline() */ + if (rl_set_prompt("") == -1) { + history_end(h); + el_end(e); + return -1; + } + el_set(e, EL_PROMPT, _get_prompt, RL_PROMPT_START_IGNORE); + el_set(e, EL_SIGNAL, rl_catch_signals); + + /* set default mode to "emacs"-style and read setting afterwards */ + /* so this can be overridden */ + el_set(e, EL_EDITOR, "emacs"); + if (rl_terminal_name != NULL) + el_set(e, EL_TERMINAL, rl_terminal_name); + else + el_get(e, EL_TERMINAL, &rl_terminal_name); + + /* + * Word completion - this has to go AFTER rebinding keys + * to emacs-style. + */ + el_set(e, EL_ADDFN, "rl_complete", + "ReadLine compatible completion function", + _el_rl_complete); + el_set(e, EL_BIND, "^I", "rl_complete", NULL); + + /* + * Send TSTP when ^Z is pressed. + */ + el_set(e, EL_ADDFN, "rl_tstp", + "ReadLine compatible suspend function", + _el_rl_tstp); + el_set(e, EL_BIND, "^Z", "rl_tstp", NULL); + + /* + * Set some readline compatible key-bindings. + */ + el_set(e, EL_BIND, "^R", "em-inc-search-prev", NULL); + + /* + * Allow the use of Home/End keys. + */ + el_set(e, EL_BIND, "\\e[1~", "ed-move-to-beg", NULL); + el_set(e, EL_BIND, "\\e[4~", "ed-move-to-end", NULL); + el_set(e, EL_BIND, "\\e[7~", "ed-move-to-beg", NULL); + el_set(e, EL_BIND, "\\e[8~", "ed-move-to-end", NULL); + el_set(e, EL_BIND, "\\e[H", "ed-move-to-beg", NULL); + el_set(e, EL_BIND, "\\e[F", "ed-move-to-end", NULL); + + /* + * Allow the use of the Delete/Insert keys. + */ + el_set(e, EL_BIND, "\\e[3~", "ed-delete-next-char", NULL); + el_set(e, EL_BIND, "\\e[2~", "ed-quoted-insert", NULL); + + /* + * Ctrl-left-arrow and Ctrl-right-arrow for word moving. + */ + el_set(e, EL_BIND, "\\e[1;5C", "em-next-word", NULL); + el_set(e, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL); + el_set(e, EL_BIND, "\\e[5C", "em-next-word", NULL); + el_set(e, EL_BIND, "\\e[5D", "ed-prev-word", NULL); + el_set(e, EL_BIND, "\\e\\e[C", "em-next-word", NULL); + el_set(e, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL); + + /* read settings from configuration file */ + el_source(e, NULL); + + /* + * Unfortunately, some applications really do use rl_point + * and rl_line_buffer directly. + */ + _resize_fun(e, &rl_line_buffer); + _rl_update_pos(); + + if (rl_startup_hook) + (*rl_startup_hook)(NULL, 0); + + return 0; +} + + +/* + * read one line from input stream and return it, chomping + * trailing newline (if there is any) + */ +char * +readline(const char *p) +{ + HistEvent ev; + const char * volatile prompt = p; + int count; + const char *ret; + char *buf; + static int used_event_hook; + + if (e == NULL || h == NULL) + rl_initialize(); + + rl_done = 0; + + (void)setjmp(topbuf); + + /* update prompt accordingly to what has been passed */ + if (rl_set_prompt(prompt) == -1) + return NULL; + + if (rl_pre_input_hook) + (*rl_pre_input_hook)(NULL, 0); + + if (rl_event_hook && !(e->el_flags&NO_TTY)) { + el_set(e, EL_GETCFN, _rl_event_read_char); + used_event_hook = 1; + } + + if (!rl_event_hook && used_event_hook) { + el_set(e, EL_GETCFN, EL_BUILTIN_GETCFN); + used_event_hook = 0; + } + + rl_already_prompted = 0; + + /* get one line from input stream */ + ret = el_gets(e, &count); + + if (ret && count > 0) { + int lastidx; + + buf = strdup(ret); + if (buf == NULL) + return NULL; + lastidx = count - 1; + if (buf[lastidx] == '\n') + buf[lastidx] = '\0'; + } else + buf = NULL; + + history(h, &ev, H_GETSIZE); + history_length = ev.num; + + return buf; +} + +/* + * history functions + */ + +/* + * is normally called before application starts to use + * history expansion functions + */ +void +using_history(void) +{ + if (h == NULL || e == NULL) + rl_initialize(); +} + + +/* + * substitute ``what'' with ``with'', returning resulting string; if + * globally == 1, substitutes all occurrences of what, otherwise only the + * first one + */ +static char * +_rl_compat_sub(const char *str, const char *what, const char *with, + int globally) +{ + const char *s; + char *r, *result; + size_t len, with_len, what_len; + + len = strlen(str); + with_len = strlen(with); + what_len = strlen(what); + + /* calculate length we need for result */ + s = str; + while (*s) { + if (*s == *what && !strncmp(s, what, what_len)) { + len += with_len - what_len; + if (!globally) + break; + s += what_len; + } else + s++; + } + r = result = el_malloc((len + 1) * sizeof(*r)); + if (result == NULL) + return NULL; + s = str; + while (*s) { + if (*s == *what && !strncmp(s, what, what_len)) { + (void)strncpy(r, with, with_len); + r += with_len; + s += what_len; + if (!globally) { + (void)strcpy(r, s); + return result; + } + } else + *r++ = *s++; + } + *r = '\0'; + return result; +} + +static char *last_search_pat; /* last !?pat[?] search pattern */ +static char *last_search_match; /* last !?pat[?] that matched */ + +const char * +get_history_event(const char *cmd, int *cindex, int qchar) +{ + int idx, sign, sub, num, begin, ret; + size_t len; + char *pat; + const char *rptr; + HistEvent ev; + + idx = *cindex; + if (cmd[idx++] != history_expansion_char) + return NULL; + + /* find out which event to take */ + if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') { + if (history(h, &ev, H_FIRST) != 0) + return NULL; + *cindex = cmd[idx]? (idx + 1):idx; + return ev.str; + } + sign = 0; + if (cmd[idx] == '-') { + sign = 1; + idx++; + } + + if ('0' <= cmd[idx] && cmd[idx] <= '9') { + HIST_ENTRY *rl_he; + + num = 0; + while (cmd[idx] && '0' <= cmd[idx] && cmd[idx] <= '9') { + num = num * 10 + cmd[idx] - '0'; + idx++; + } + if (sign) + num = history_length - num + 1; + + if (!(rl_he = history_get(num))) + return NULL; + + *cindex = idx; + return rl_he->line; + } + sub = 0; + if (cmd[idx] == '?') { + sub = 1; + idx++; + } + begin = idx; + while (cmd[idx]) { + if (cmd[idx] == '\n') + break; + if (sub && cmd[idx] == '?') + break; + if (!sub && (cmd[idx] == ':' || cmd[idx] == ' ' + || cmd[idx] == '\t' || cmd[idx] == qchar)) + break; + idx++; + } + len = (size_t)idx - (size_t)begin; + if (sub && cmd[idx] == '?') + idx++; + if (sub && len == 0 && last_search_pat && *last_search_pat) + pat = last_search_pat; + else if (len == 0) + return NULL; + else { + if ((pat = el_malloc((len + 1) * sizeof(*pat))) == NULL) + return NULL; + (void)strncpy(pat, cmd + begin, len); + pat[len] = '\0'; + } + + if (history(h, &ev, H_CURR) != 0) { + if (pat != last_search_pat) + el_free(pat); + return NULL; + } + num = ev.num; + + if (sub) { + if (pat != last_search_pat) { + if (last_search_pat) + el_free(last_search_pat); + last_search_pat = pat; + } + ret = history_search(pat, -1); + } else + ret = history_search_prefix(pat, -1); + + if (ret == -1) { + /* restore to end of list on failed search */ + history(h, &ev, H_FIRST); + (void)fprintf(rl_outstream, "%s: Event not found\n", pat); + if (pat != last_search_pat) + el_free(pat); + return NULL; + } + + if (sub && len) { + if (last_search_match && last_search_match != pat) + el_free(last_search_match); + last_search_match = pat; + } + + if (pat != last_search_pat) + el_free(pat); + + if (history(h, &ev, H_CURR) != 0) + return NULL; + *cindex = idx; + rptr = ev.str; + + /* roll back to original position */ + (void)history(h, &ev, H_SET, num); + + return rptr; +} + +/* + * the real function doing history expansion - takes as argument command + * to do and data upon which the command should be executed + * does expansion the way I've understood readline documentation + * + * returns 0 if data was not modified, 1 if it was and 2 if the string + * should be only printed and not executed; in case of error, + * returns -1 and *result points to NULL + * it's the caller's responsibility to free() the string returned in *result + */ +static int +_history_expand_command(const char *command, size_t offs, size_t cmdlen, + char **result) +{ + char *tmp, *search = NULL, *aptr; + const char *ptr, *cmd; + static char *from = NULL, *to = NULL; + int start, end, idx, has_mods = 0; + int p_on = 0, g_on = 0; + + *result = NULL; + aptr = NULL; + ptr = NULL; + + /* First get event specifier */ + idx = 0; + + if (strchr(":^*$", command[offs + 1])) { + char str[4]; + /* + * "!:" is shorthand for "!!:". + * "!^", "!*" and "!$" are shorthand for + * "!!:^", "!!:*" and "!!:$" respectively. + */ + str[0] = str[1] = '!'; + str[2] = '0'; + ptr = get_history_event(str, &idx, 0); + idx = (command[offs + 1] == ':')? 1:0; + has_mods = 1; + } else { + if (command[offs + 1] == '#') { + /* use command so far */ + if ((aptr = el_malloc((offs + 1) * sizeof(*aptr))) + == NULL) + return -1; + (void)strncpy(aptr, command, offs); + aptr[offs] = '\0'; + idx = 1; + } else { + int qchar; + + qchar = (offs > 0 && command[offs - 1] == '"')? '"':0; + ptr = get_history_event(command + offs, &idx, qchar); + } + has_mods = command[offs + (size_t)idx] == ':'; + } + + if (ptr == NULL && aptr == NULL) + return -1; + + if (!has_mods) { + *result = strdup(aptr ? aptr : ptr); + if (aptr) + el_free(aptr); + if (*result == NULL) + return -1; + return 1; + } + + cmd = command + offs + idx + 1; + + /* Now parse any word designators */ + + if (*cmd == '%') /* last word matched by ?pat? */ + tmp = strdup(last_search_match? last_search_match:""); + else if (strchr("^*$-0123456789", *cmd)) { + start = end = -1; + if (*cmd == '^') + start = end = 1, cmd++; + else if (*cmd == '$') + start = -1, cmd++; + else if (*cmd == '*') + start = 1, cmd++; + else if (*cmd == '-' || isdigit((unsigned char) *cmd)) { + start = 0; + while (*cmd && '0' <= *cmd && *cmd <= '9') + start = start * 10 + *cmd++ - '0'; + + if (*cmd == '-') { + if (isdigit((unsigned char) cmd[1])) { + cmd++; + end = 0; + while (*cmd && '0' <= *cmd && *cmd <= '9') + end = end * 10 + *cmd++ - '0'; + } else if (cmd[1] == '$') { + cmd += 2; + end = -1; + } else { + cmd++; + end = -2; + } + } else if (*cmd == '*') + end = -1, cmd++; + else + end = start; + } + tmp = history_arg_extract(start, end, aptr? aptr:ptr); + if (tmp == NULL) { + (void)fprintf(rl_outstream, "%s: Bad word specifier", + command + offs + idx); + if (aptr) + el_free(aptr); + return -1; + } + } else + tmp = strdup(aptr? aptr:ptr); + + if (aptr) + el_free(aptr); + + if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) { + *result = tmp; + return 1; + } + + for (; *cmd; cmd++) { + if (*cmd == ':') + continue; + else if (*cmd == 'h') { /* remove trailing path */ + if ((aptr = strrchr(tmp, '/')) != NULL) + *aptr = '\0'; + } else if (*cmd == 't') { /* remove leading path */ + if ((aptr = strrchr(tmp, '/')) != NULL) { + aptr = strdup(aptr + 1); + el_free(tmp); + tmp = aptr; + } + } else if (*cmd == 'r') { /* remove trailing suffix */ + if ((aptr = strrchr(tmp, '.')) != NULL) + *aptr = '\0'; + } else if (*cmd == 'e') { /* remove all but suffix */ + if ((aptr = strrchr(tmp, '.')) != NULL) { + aptr = strdup(aptr); + el_free(tmp); + tmp = aptr; + } + } else if (*cmd == 'p') /* print only */ + p_on = 1; + else if (*cmd == 'g') + g_on = 2; + else if (*cmd == 's' || *cmd == '&') { + char *what, *with, delim; + size_t len, from_len; + size_t size; + + if (*cmd == '&' && (from == NULL || to == NULL)) + continue; + else if (*cmd == 's') { + delim = *(++cmd), cmd++; + size = 16; + what = el_realloc(from, size * sizeof(*what)); + if (what == NULL) { + el_free(from); + el_free(tmp); + return 0; + } + len = 0; + for (; *cmd && *cmd != delim; cmd++) { + if (*cmd == '\\' && cmd[1] == delim) + cmd++; + if (len >= size) { + char *nwhat; + nwhat = el_realloc(what, + (size <<= 1) * + sizeof(*nwhat)); + if (nwhat == NULL) { + el_free(what); + el_free(tmp); + return 0; + } + what = nwhat; + } + what[len++] = *cmd; + } + what[len] = '\0'; + from = what; + if (*what == '\0') { + el_free(what); + if (search) { + from = strdup(search); + if (from == NULL) { + el_free(tmp); + return 0; + } + } else { + from = NULL; + el_free(tmp); + return -1; + } + } + cmd++; /* shift after delim */ + if (!*cmd) + continue; + + size = 16; + with = el_realloc(to, size * sizeof(*with)); + if (with == NULL) { + el_free(to); + el_free(tmp); + return -1; + } + len = 0; + from_len = strlen(from); + for (; *cmd && *cmd != delim; cmd++) { + if (len + from_len + 1 >= size) { + char *nwith; + size += from_len + 1; + nwith = el_realloc(with, + size * sizeof(*nwith)); + if (nwith == NULL) { + el_free(with); + el_free(tmp); + return -1; + } + with = nwith; + } + if (*cmd == '&') { + /* safe */ + (void)strcpy(&with[len], from); + len += from_len; + continue; + } + if (*cmd == '\\' + && (*(cmd + 1) == delim + || *(cmd + 1) == '&')) + cmd++; + with[len++] = *cmd; + } + with[len] = '\0'; + to = with; + } + + aptr = _rl_compat_sub(tmp, from, to, g_on); + if (aptr) { + el_free(tmp); + tmp = aptr; + } + g_on = 0; + } + } + *result = tmp; + return p_on? 2:1; +} + + +/* + * csh-style history expansion + */ +int +history_expand(char *str, char **output) +{ + int ret = 0; + size_t idx, i, size; + char *tmp, *result; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (history_expansion_char == 0) { + *output = strdup(str); + return 0; + } + + *output = NULL; + if (str[0] == history_subst_char) { + /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */ + *output = el_malloc((strlen(str) + 4 + 1) * sizeof(**output)); + if (*output == NULL) + return 0; + (*output)[0] = (*output)[1] = history_expansion_char; + (*output)[2] = ':'; + (*output)[3] = 's'; + (void)strcpy((*output) + 4, str); + str = *output; + } else { + *output = strdup(str); + if (*output == NULL) + return 0; + } + +#define ADD_STRING(what, len, fr) \ + { \ + if (idx + len + 1 > size) { \ + char *nresult = el_realloc(result, \ + (size += len + 1) * sizeof(*nresult)); \ + if (nresult == NULL) { \ + el_free(*output); \ + if (/*CONSTCOND*/fr) \ + el_free(tmp); \ + return 0; \ + } \ + result = nresult; \ + } \ + (void)strncpy(&result[idx], what, len); \ + idx += len; \ + result[idx] = '\0'; \ + } + + result = NULL; + size = idx = 0; + tmp = NULL; + for (i = 0; str[i];) { + int qchar, loop_again; + size_t len, start, j; + + qchar = 0; + loop_again = 1; + start = j = i; +loop: + for (; str[j]; j++) { + if (str[j] == '\\' && + str[j + 1] == history_expansion_char) { + len = strlen(&str[j + 1]) + 1; + memmove(&str[j], &str[j + 1], len); + continue; + } + if (!loop_again) { + if (isspace((unsigned char) str[j]) + || str[j] == qchar) + break; + } + if (str[j] == history_expansion_char + && !strchr(history_no_expand_chars, str[j + 1]) + && (!history_inhibit_expansion_function || + (*history_inhibit_expansion_function)(str, + (int)j) == 0)) + break; + } + + if (str[j] && loop_again) { + i = j; + qchar = (j > 0 && str[j - 1] == '"' )? '"':0; + j++; + if (str[j] == history_expansion_char) + j++; + loop_again = 0; + goto loop; + } + len = i - start; + ADD_STRING(&str[start], len, 0); + + if (str[i] == '\0' || str[i] != history_expansion_char) { + len = j - i; + ADD_STRING(&str[i], len, 0); + if (start == 0) + ret = 0; + else + ret = 1; + break; + } + ret = _history_expand_command (str, i, (j - i), &tmp); + if (ret > 0 && tmp) { + len = strlen(tmp); + ADD_STRING(tmp, len, 1); + } + if (tmp) { + el_free(tmp); + tmp = NULL; + } + i = j; + } + + /* ret is 2 for "print only" option */ + if (ret == 2) { + add_history(result); +#ifdef GDB_411_HACK + /* gdb 4.11 has been shipped with readline, where */ + /* history_expand() returned -1 when the line */ + /* should not be executed; in readline 2.1+ */ + /* it should return 2 in such a case */ + ret = -1; +#endif + } + el_free(*output); + *output = result; + + return ret; +} + +/* +* Return a string consisting of arguments of "str" from "start" to "end". +*/ +char * +history_arg_extract(int start, int end, const char *str) +{ + size_t i, len, max; + char **arr, *result = NULL; + + arr = history_tokenize(str); + if (!arr) + return NULL; + if (arr && *arr == NULL) + goto out; + + for (max = 0; arr[max]; max++) + continue; + max--; + + if (start == '$') + start = (int)max; + if (end == '$') + end = (int)max; + if (end < 0) + end = (int)max + end + 1; + if (start < 0) + start = end; + + if (start < 0 || end < 0 || (size_t)start > max || + (size_t)end > max || start > end) + goto out; + + for (i = (size_t)start, len = 0; i <= (size_t)end; i++) + len += strlen(arr[i]) + 1; + len++; + result = el_malloc(len * sizeof(*result)); + if (result == NULL) + goto out; + + for (i = (size_t)start, len = 0; i <= (size_t)end; i++) { + (void)strcpy(result + len, arr[i]); + len += strlen(arr[i]); + if (i < (size_t)end) + result[len++] = ' '; + } + result[len] = '\0'; + +out: + for (i = 0; arr[i]; i++) + el_free(arr[i]); + el_free(arr); + + return result; +} + +/* + * Parse the string into individual tokens, + * similar to how shell would do it. + */ +char ** +history_tokenize(const char *str) +{ + int size = 1, idx = 0, i, start; + size_t len; + char **result = NULL, *temp, delim = '\0'; + + for (i = 0; str[i];) { + while (isspace((unsigned char) str[i])) + i++; + start = i; + for (; str[i];) { + if (str[i] == '\\') { + if (str[i+1] != '\0') + i++; + } else if (str[i] == delim) + delim = '\0'; + else if (!delim && + (isspace((unsigned char) str[i]) || + strchr("()<>;&|$", str[i]))) + break; + else if (!delim && strchr("'`\"", str[i])) + delim = str[i]; + if (str[i]) + i++; + } + + if (idx + 2 >= size) { + char **nresult; + size <<= 1; + nresult = el_realloc(result, (size_t)size * sizeof(*nresult)); + if (nresult == NULL) { + el_free(result); + return NULL; + } + result = nresult; + } + len = (size_t)i - (size_t)start; + temp = el_malloc((size_t)(len + 1) * sizeof(*temp)); + if (temp == NULL) { + for (i = 0; i < idx; i++) + el_free(result[i]); + el_free(result); + return NULL; + } + (void)strncpy(temp, &str[start], len); + temp[len] = '\0'; + result[idx++] = temp; + result[idx] = NULL; + if (str[i]) + i++; + } + return result; +} + + +/* + * limit size of history record to ``max'' events + */ +void +stifle_history(int max) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (history(h, &ev, H_SETSIZE, max) == 0) + max_input_history = max; +} + + +/* + * "unlimit" size of history - set the limit to maximum allowed int value + */ +int +unstifle_history(void) +{ + HistEvent ev; + int omax; + + history(h, &ev, H_SETSIZE, INT_MAX); + omax = max_input_history; + max_input_history = INT_MAX; + return omax; /* some value _must_ be returned */ +} + + +int +history_is_stifled(void) +{ + + /* cannot return true answer */ + return max_input_history != INT_MAX; +} + +static const char _history_tmp_template[] = "/tmp/.historyXXXXXX"; + +int +history_truncate_file (const char *filename, int nlines) +{ + int ret = 0; + FILE *fp, *tp; + char template[sizeof(_history_tmp_template)]; + char buf[4096]; + int fd; + char *cp; + off_t off; + int count = 0; + ssize_t left = 0; + + if (filename == NULL && (filename = _default_history_file()) == NULL) + return errno; + if ((fp = fopen(filename, "r+")) == NULL) + return errno; + strcpy(template, _history_tmp_template); + if ((fd = mkstemp(template)) == -1) { + ret = errno; + goto out1; + } + + if ((tp = fdopen(fd, "r+")) == NULL) { + close(fd); + ret = errno; + goto out2; + } + + for(;;) { + if (fread(buf, sizeof(buf), (size_t)1, fp) != 1) { + if (ferror(fp)) { + ret = errno; + break; + } + if (fseeko(fp, (off_t)sizeof(buf) * count, SEEK_SET) == + (off_t)-1) { + ret = errno; + break; + } + left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), fp); + if (ferror(fp)) { + ret = errno; + break; + } + if (left == 0) { + count--; + left = sizeof(buf); + } else if (fwrite(buf, (size_t)left, (size_t)1, tp) + != 1) { + ret = errno; + break; + } + fflush(tp); + break; + } + if (fwrite(buf, sizeof(buf), (size_t)1, tp) != 1) { + ret = errno; + break; + } + count++; + } + if (ret) + goto out3; + cp = buf + left - 1; + if(*cp != '\n') + cp++; + for(;;) { + while (--cp >= buf) { + if (*cp == '\n') { + if (--nlines == 0) { + if (++cp >= buf + sizeof(buf)) { + count++; + cp = buf; + } + break; + } + } + } + if (nlines <= 0 || count == 0) + break; + count--; + if (fseeko(tp, (off_t)sizeof(buf) * count, SEEK_SET) < 0) { + ret = errno; + break; + } + if (fread(buf, sizeof(buf), (size_t)1, tp) != 1) { + if (ferror(tp)) { + ret = errno; + break; + } + ret = EAGAIN; + break; + } + cp = buf + sizeof(buf); + } + + if (ret || nlines > 0) + goto out3; + + if (fseeko(fp, (off_t)0, SEEK_SET) == (off_t)-1) { + ret = errno; + goto out3; + } + + if (fseeko(tp, (off_t)sizeof(buf) * count + (cp - buf), SEEK_SET) == + (off_t)-1) { + ret = errno; + goto out3; + } + + for(;;) { + if ((left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), tp)) == 0) { + if (ferror(fp)) + ret = errno; + break; + } + if (fwrite(buf, (size_t)left, (size_t)1, fp) != 1) { + ret = errno; + break; + } + } + fflush(fp); + if((off = ftello(fp)) > 0) + (void)ftruncate(fileno(fp), off); +out3: + fclose(tp); +out2: + unlink(template); +out1: + fclose(fp); + + return ret; +} + + +/* + * read history from a file given + */ +int +read_history(const char *filename) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + if (filename == NULL && (filename = _default_history_file()) == NULL) + return errno; + return history(h, &ev, H_LOAD, filename) == -1 ? + (errno ? errno : EINVAL) : 0; +} + + +/* + * write history to a file given + */ +int +write_history(const char *filename) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + if (filename == NULL && (filename = _default_history_file()) == NULL) + return errno; + return history(h, &ev, H_SAVE, filename) == -1 ? + (errno ? errno : EINVAL) : 0; +} + + +/* + * returns history ``num''th event + * + * returned pointer points to static variable + */ +HIST_ENTRY * +history_get(int num) +{ + static HIST_ENTRY she; + HistEvent ev; + int curr_num; + + if (h == NULL || e == NULL) + rl_initialize(); + + /* save current position */ + if (history(h, &ev, H_CURR) != 0) + return NULL; + curr_num = ev.num; + + /* start from the oldest */ + if (history(h, &ev, H_LAST) != 0) + return NULL; /* error */ + + /* look forwards for event matching specified offset */ + if (history(h, &ev, H_NEXT_EVDATA, num, &she.data)) + return NULL; + + she.line = ev.str; + + /* restore pointer to where it was */ + (void)history(h, &ev, H_SET, curr_num); + + return &she; +} + + +/* + * add the line to history table + */ +int +add_history(const char *line) +{ + HistEvent ev; + + if (line == NULL) + return 0; + + if (h == NULL || e == NULL) + rl_initialize(); + + (void)history(h, &ev, H_ENTER, line); + if (history(h, &ev, H_GETSIZE) == 0) + history_length = ev.num; + + return !(history_length > 0); /* return 0 if all is okay */ +} + + +/* + * remove the specified entry from the history list and return it. + */ +HIST_ENTRY * +remove_history(int num) +{ + HIST_ENTRY *he; + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + + if ((he = el_malloc(sizeof(*he))) == NULL) + return NULL; + + if (history(h, &ev, H_DELDATA, num, &he->data) != 0) { + el_free(he); + return NULL; + } + + he->line = ev.str; + if (history(h, &ev, H_GETSIZE) == 0) + history_length = ev.num; + + return he; +} + + +/* + * replace the line and data of the num-th entry + */ +HIST_ENTRY * +replace_history_entry(int num, const char *line, histdata_t data) +{ + HIST_ENTRY *he; + HistEvent ev; + int curr_num; + + if (h == NULL || e == NULL) + rl_initialize(); + + /* save current position */ + if (history(h, &ev, H_CURR) != 0) + return NULL; + curr_num = ev.num; + + /* start from the oldest */ + if (history(h, &ev, H_LAST) != 0) + return NULL; /* error */ + + if ((he = el_malloc(sizeof(*he))) == NULL) + return NULL; + + /* look forwards for event matching specified offset */ + if (history(h, &ev, H_NEXT_EVDATA, num, &he->data)) + goto out; + + he->line = strdup(ev.str); + if (he->line == NULL) + goto out; + + if (history(h, &ev, H_REPLACE, line, data)) + goto out; + + /* restore pointer to where it was */ + if (history(h, &ev, H_SET, curr_num)) + goto out; + + return he; +out: + el_free(he); + return NULL; +} + +/* + * clear the history list - delete all entries + */ +void +clear_history(void) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + + (void)history(h, &ev, H_CLEAR); + history_length = 0; +} + + +/* + * returns offset of the current history event + */ +int +where_history(void) +{ + HistEvent ev; + int curr_num, off; + + if (history(h, &ev, H_CURR) != 0) + return 0; + curr_num = ev.num; + + (void)history(h, &ev, H_FIRST); + off = 1; + while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0) + off++; + + return off; +} + + +/* + * returns current history event or NULL if there is no such event + */ +HIST_ENTRY * +current_history(void) +{ + + return _move_history(H_CURR); +} + + +/* + * returns total number of bytes history events' data are using + */ +int +history_total_bytes(void) +{ + HistEvent ev; + int curr_num; + size_t size; + + if (history(h, &ev, H_CURR) != 0) + return -1; + curr_num = ev.num; + + (void)history(h, &ev, H_FIRST); + size = 0; + do + size += strlen(ev.str) * sizeof(*ev.str); + while (history(h, &ev, H_NEXT) == 0); + + /* get to the same position as before */ + history(h, &ev, H_PREV_EVENT, curr_num); + + return (int)size; +} + + +/* + * sets the position in the history list to ``pos'' + */ +int +history_set_pos(int pos) +{ + HistEvent ev; + int curr_num; + + if (pos >= history_length || pos < 0) + return -1; + + (void)history(h, &ev, H_CURR); + curr_num = ev.num; + + /* + * use H_DELDATA to set to nth history (without delete) by passing + * (void **)-1 + */ + if (history(h, &ev, H_DELDATA, pos, (void **)-1)) { + (void)history(h, &ev, H_SET, curr_num); + return -1; + } + return 0; +} + + +/* + * returns previous event in history and shifts pointer accordingly + */ +HIST_ENTRY * +previous_history(void) +{ + + return _move_history(H_PREV); +} + + +/* + * returns next event in history and shifts pointer accordingly + */ +HIST_ENTRY * +next_history(void) +{ + + return _move_history(H_NEXT); +} + + +/* + * searches for first history event containing the str + */ +int +history_search(const char *str, int direction) +{ + HistEvent ev; + const char *strp; + int curr_num; + + if (history(h, &ev, H_CURR) != 0) + return -1; + curr_num = ev.num; + + for (;;) { + if ((strp = strstr(ev.str, str)) != NULL) + return (int)(strp - ev.str); + if (history(h, &ev, direction < 0 ? H_NEXT:H_PREV) != 0) + break; + } + (void)history(h, &ev, H_SET, curr_num); + return -1; +} + + +/* + * searches for first history event beginning with str + */ +int +history_search_prefix(const char *str, int direction) +{ + HistEvent ev; + + return (history(h, &ev, direction < 0 ? + H_PREV_STR : H_NEXT_STR, str)); +} + + +/* + * search for event in history containing str, starting at offset + * abs(pos); continue backward, if pos<0, forward otherwise + */ +/* ARGSUSED */ +int +history_search_pos(const char *str, + int direction __attribute__((__unused__)), int pos) +{ + HistEvent ev; + int curr_num, off; + + off = (pos > 0) ? pos : -pos; + pos = (pos > 0) ? 1 : -1; + + if (history(h, &ev, H_CURR) != 0) + return -1; + curr_num = ev.num; + + if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0) + return -1; + + for (;;) { + if (strstr(ev.str, str)) + return off; + if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0) + break; + } + + /* set "current" pointer back to previous state */ + (void)history(h, &ev, + pos < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); + + return -1; +} + + +/********************************/ +/* completion functions */ + +char * +tilde_expand(char *name) +{ + return fn_tilde_expand(name); +} + +char * +filename_completion_function(const char *name, int state) +{ + return fn_filename_completion_function(name, state); +} + +/* + * a completion generator for usernames; returns _first_ username + * which starts with supplied text + * text contains a partial username preceded by random character + * (usually '~'); state resets search from start (??? should we do that anyway) + * it's the caller's responsibility to free the returned value + */ +char * +username_completion_function(const char *text, int state) +{ +#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) + struct passwd pwres; + char pwbuf[1024]; +#endif + struct passwd *pass = NULL; + + if (text[0] == '\0') + return NULL; + + if (*text == '~') + text++; + + if (state == 0) + setpwent(); + + while ( +#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) + getpwent_r(&pwres, pwbuf, sizeof(pwbuf), &pass) == 0 && pass != NULL +#else + (pass = getpwent()) != NULL +#endif + && text[0] == pass->pw_name[0] + && strcmp(text, pass->pw_name) == 0) + continue; + + if (pass == NULL) { + endpwent(); + return NULL; + } + return strdup(pass->pw_name); +} + + +/* + * el-compatible wrapper to send TSTP on ^Z + */ +/* ARGSUSED */ +static unsigned char +_el_rl_tstp(EditLine *el __attribute__((__unused__)), int ch __attribute__((__unused__))) +{ + (void)kill(0, SIGTSTP); + return CC_NORM; +} + +/* + * Display list of strings in columnar format on readline's output stream. + * 'matches' is list of strings, 'len' is number of strings in 'matches', + * 'max' is maximum length of string in 'matches'. + */ +void +rl_display_match_list(char **matches, int len, int max) +{ + + fn_display_match_list(e, matches, (size_t)len, (size_t)max); +} + +static const char * +/*ARGSUSED*/ +_rl_completion_append_character_function(const char *dummy + __attribute__((__unused__))) +{ + static char buf[2]; + buf[0] = (char)rl_completion_append_character; + buf[1] = '\0'; + return buf; +} + + +/* + * complete word at current point + */ +/* ARGSUSED */ +int +rl_complete(int ignore __attribute__((__unused__)), int invoking_key) +{ +#ifdef WIDECHAR + static ct_buffer_t wbreak_conv, sprefix_conv; +#endif + char *breakchars; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (rl_inhibit_completion) { + char arr[2]; + arr[0] = (char)invoking_key; + arr[1] = '\0'; + el_insertstr(e, arr); + return CC_REFRESH; + } + + if (rl_completion_word_break_hook != NULL) + breakchars = (*rl_completion_word_break_hook)(); + else + breakchars = rl_basic_word_break_characters; + + _rl_update_pos(); + + /* Just look at how many global variables modify this operation! */ + return fn_complete(e, + (rl_compentry_func_t *)rl_completion_entry_function, + rl_attempted_completion_function, + ct_decode_string(rl_basic_word_break_characters, &wbreak_conv), + ct_decode_string(breakchars, &sprefix_conv), + _rl_completion_append_character_function, + (size_t)rl_completion_query_items, + &rl_completion_type, &rl_attempted_completion_over, + &rl_point, &rl_end, NULL, NULL, NULL); + + +} + + +/* ARGSUSED */ +static unsigned char +_el_rl_complete(EditLine *el __attribute__((__unused__)), int ch) +{ + return (unsigned char)rl_complete(0, ch); +} + +/* + * misc other functions + */ + +/* + * bind key c to readline-type function func + */ +int +rl_bind_key(int c, rl_command_func_t *func) +{ + int retval = -1; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (func == rl_insert) { + /* XXX notice there is no range checking of ``c'' */ + e->el_map.key[c] = ED_INSERT; + retval = 0; + } + return retval; +} + + +/* + * read one key from input - handles chars pushed back + * to input stream also + */ +int +rl_read_key(void) +{ + char fooarr[2 * sizeof(int)]; + + if (e == NULL || h == NULL) + rl_initialize(); + + return el_getc(e, fooarr); +} + + +/* + * reset the terminal + */ +/* ARGSUSED */ +void +rl_reset_terminal(const char *p __attribute__((__unused__))) +{ + + if (h == NULL || e == NULL) + rl_initialize(); + el_reset(e); +} + + +/* + * insert character ``c'' back into input stream, ``count'' times + */ +int +rl_insert(int count, int c) +{ + char arr[2]; + + if (h == NULL || e == NULL) + rl_initialize(); + + /* XXX - int -> char conversion can lose on multichars */ + arr[0] = (char)c; + arr[1] = '\0'; + + for (; count > 0; count--) + el_push(e, arr); + + return 0; +} + +int +rl_insert_text(const char *text) +{ + if (!text || *text == 0) + return 0; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (el_insertstr(e, text) < 0) + return 0; + return (int)strlen(text); +} + +/*ARGSUSED*/ +int +rl_newline(int count __attribute__((__unused__)), + int c __attribute__((__unused__))) +{ + /* + * Readline-4.0 appears to ignore the args. + */ + return rl_insert(1, '\n'); +} + +/*ARGSUSED*/ +static unsigned char +rl_bind_wrapper(EditLine *el __attribute__((__unused__)), unsigned char c) +{ + if (map[c] == NULL) + return CC_ERROR; + + _rl_update_pos(); + + (*map[c])(1, c); + + /* If rl_done was set by the above call, deal with it here */ + if (rl_done) + return CC_EOF; + + return CC_NORM; +} + +int +rl_add_defun(const char *name, rl_command_func_t *fun, int c) +{ + char dest[8]; + if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0) + return -1; + map[(unsigned char)c] = fun; + el_set(e, EL_ADDFN, name, name, rl_bind_wrapper); + vis(dest, c, VIS_WHITE|VIS_NOSLASH, 0); + el_set(e, EL_BIND, dest, name, NULL); + return 0; +} + +void +rl_callback_read_char(void) +{ + int count = 0, done = 0; + const char *buf = el_gets(e, &count); + char *wbuf; + + if (buf == NULL || count-- <= 0) + return; + if (count == 0 && buf[0] == e->el_tty.t_c[TS_IO][C_EOF]) + done = 1; + if (buf[count] == '\n' || buf[count] == '\r') + done = 2; + + if (done && rl_linefunc != NULL) { + el_set(e, EL_UNBUFFERED, 0); + if (done == 2) { + if ((wbuf = strdup(buf)) != NULL) + wbuf[count] = '\0'; + } else + wbuf = NULL; + (*(void (*)(const char *))rl_linefunc)(wbuf); + el_set(e, EL_UNBUFFERED, 1); + } +} + +void +rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *linefunc) +{ + if (e == NULL) { + rl_initialize(); + } + (void)rl_set_prompt(prompt); + rl_linefunc = linefunc; + el_set(e, EL_UNBUFFERED, 1); +} + +void +rl_callback_handler_remove(void) +{ + el_set(e, EL_UNBUFFERED, 0); + rl_linefunc = NULL; +} + +void +rl_redisplay(void) +{ + char a[2]; + a[0] = (char)e->el_tty.t_c[TS_IO][C_REPRINT]; + a[1] = '\0'; + el_push(e, a); +} + +int +rl_get_previous_history(int count, int key) +{ + char a[2]; + a[0] = (char)key; + a[1] = '\0'; + while (count--) + el_push(e, a); + return 0; +} + +void +/*ARGSUSED*/ +rl_prep_terminal(int meta_flag __attribute__((__unused__))) +{ + el_set(e, EL_PREP_TERM, 1); +} + +void +rl_deprep_terminal(void) +{ + el_set(e, EL_PREP_TERM, 0); +} + +int +rl_read_init_file(const char *s) +{ + return el_source(e, s); +} + +int +rl_parse_and_bind(const char *line) +{ + const char **argv; + int argc; + Tokenizer *tok; + + tok = tok_init(NULL); + tok_str(tok, line, &argc, &argv); + argc = el_parse(e, argc, argv); + tok_end(tok); + return argc ? 1 : 0; +} + +int +rl_variable_bind(const char *var, const char *value) +{ + /* + * The proper return value is undocument, but this is what the + * readline source seems to do. + */ + return el_set(e, EL_BIND, "", var, value, NULL) == -1 ? 1 : 0; +} + +void +rl_stuff_char(int c) +{ + char buf[2]; + + buf[0] = (char)c; + buf[1] = '\0'; + el_insertstr(e, buf); +} + +static int +_rl_event_read_char(EditLine *el, wchar_t *wc) +{ + char ch; + int n; + ssize_t num_read = 0; + + ch = '\0'; + *wc = L'\0'; + while (rl_event_hook) { + + (*rl_event_hook)(); + +#if defined(FIONREAD) + if (ioctl(el->el_infd, FIONREAD, &n) < 0) + return -1; + if (n) + num_read = read(el->el_infd, &ch, (size_t)1); + else + num_read = 0; +#elif defined(F_SETFL) && defined(O_NDELAY) + if ((n = fcntl(el->el_infd, F_GETFL, 0)) < 0) + return -1; + if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0) + return -1; + num_read = read(el->el_infd, &ch, 1); + if (fcntl(el->el_infd, F_SETFL, n)) + return -1; +#else + /* not non-blocking, but what you gonna do? */ + num_read = read(el->el_infd, &ch, 1); + return -1; +#endif + + if (num_read < 0 && errno == EAGAIN) + continue; + if (num_read == 0) + continue; + break; + } + if (!rl_event_hook) + el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN); + *wc = (wchar_t)ch; + return (int)num_read; +} + +static void +_rl_update_pos(void) +{ + const LineInfo *li = el_line(e); + + rl_point = (int)(li->cursor - li->buffer); + rl_end = (int)(li->lastchar - li->buffer); +} + +void +rl_get_screen_size(int *rows, int *cols) +{ + if (rows) + el_get(e, EL_GETTC, "li", rows, (void *)0); + if (cols) + el_get(e, EL_GETTC, "co", cols, (void *)0); +} + +void +rl_set_screen_size(int rows, int cols) +{ + char buf[64]; + (void)snprintf(buf, sizeof(buf), "%d", rows); + el_set(e, EL_SETTC, "li", buf, NULL); + (void)snprintf(buf, sizeof(buf), "%d", cols); + el_set(e, EL_SETTC, "co", buf, NULL); +} + +char ** +rl_completion_matches(const char *str, rl_compentry_func_t *fun) +{ + size_t len, max, i, j, min; + char **list, *match, *a, *b; + + len = 1; + max = 10; + if ((list = el_malloc(max * sizeof(*list))) == NULL) + return NULL; + + while ((match = (*fun)(str, (int)(len - 1))) != NULL) { + list[len++] = match; + if (len == max) { + char **nl; + max += 10; + if ((nl = el_realloc(list, max * sizeof(*nl))) == NULL) + goto out; + list = nl; + } + } + if (len == 1) + goto out; + list[len] = NULL; + if (len == 2) { + if ((list[0] = strdup(list[1])) == NULL) + goto out; + return list; + } + qsort(&list[1], len - 1, sizeof(*list), + (int (*)(const void *, const void *)) strcmp); + min = SIZE_MAX; + for (i = 1, a = list[i]; i < len - 1; i++, a = b) { + b = list[i + 1]; + for (j = 0; a[j] && a[j] == b[j]; j++) + continue; + if (min > j) + min = j; + } + if (min == 0 && *str) { + if ((list[0] = strdup(str)) == NULL) + goto out; + } else { + if ((list[0] = el_malloc((min + 1) * sizeof(*list[0]))) == NULL) + goto out; + (void)memcpy(list[0], list[1], min); + list[0][min] = '\0'; + } + return list; + +out: + el_free(list); + return NULL; +} + +char * +rl_filename_completion_function (const char *text, int state) +{ + return fn_filename_completion_function(text, state); +} + +void +rl_forced_update_display(void) +{ + el_set(e, EL_REFRESH); +} + +int +_rl_abort_internal(void) +{ + el_beep(e); + longjmp(topbuf, 1); + /*NOTREACHED*/ +} + +int +_rl_qsort_string_compare(char **s1, char **s2) +{ + return strcoll(*s1, *s2); +} + +HISTORY_STATE * +history_get_history_state(void) +{ + HISTORY_STATE *hs; + + if ((hs = el_malloc(sizeof(*hs))) == NULL) + return NULL; + hs->length = history_length; + return hs; +} + +int +/*ARGSUSED*/ +rl_kill_text(int from __attribute__((__unused__)), + int to __attribute__((__unused__))) +{ + return 0; +} + +Keymap +rl_make_bare_keymap(void) +{ + return NULL; +} + +Keymap +rl_get_keymap(void) +{ + return NULL; +} + +void +/*ARGSUSED*/ +rl_set_keymap(Keymap k __attribute__((__unused__))) +{ +} + +int +/*ARGSUSED*/ +rl_generic_bind(int type __attribute__((__unused__)), + const char * keyseq __attribute__((__unused__)), + const char * data __attribute__((__unused__)), + Keymap k __attribute__((__unused__))) +{ + return 0; +} + +int +/*ARGSUSED*/ +rl_bind_key_in_map(int key __attribute__((__unused__)), + rl_command_func_t *fun __attribute__((__unused__)), + Keymap k __attribute__((__unused__))) +{ + return 0; +} + +/* unsupported, but needed by python */ +void +rl_cleanup_after_signal(void) +{ +} + +int +rl_on_new_line(void) +{ + return 0; +} + +void +rl_free_line_state(void) +{ +} + +int +/*ARGSUSED*/ +rl_set_keyboard_input_timeout(int u __attribute__((__unused__))) +{ + return 0; +} diff --git a/lib/libedit/refresh.c b/lib/libedit/refresh.c new file mode 100644 index 0000000..698dc40 --- /dev/null +++ b/lib/libedit/refresh.c @@ -0,0 +1,1187 @@ +/* $NetBSD: refresh.c,v 1.45 2016/03/02 19:24:20 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: refresh.c,v 1.45 2016/03/02 19:24:20 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/refresh.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * refresh.c: Lower level screen refreshing functions + */ +#include +#include +#include + +#include "el.h" + +private void re_nextline(EditLine *); +private void re_addc(EditLine *, wint_t); +private void re_update_line(EditLine *, Char *, Char *, int); +private void re_insert (EditLine *, Char *, int, int, Char *, int); +private void re_delete(EditLine *, Char *, int, int, int); +private void re_fastputc(EditLine *, wint_t); +private void re_clear_eol(EditLine *, int, int, int); +private void re__strncopy(Char *, Char *, size_t); +private void re__copy_and_pad(Char *, const Char *, size_t); + +#ifdef DEBUG_REFRESH +private void re_printstr(EditLine *, const char *, Char *, Char *); +#define __F el->el_errfile +#define ELRE_ASSERT(a, b, c) do \ + if (/*CONSTCOND*/ a) { \ + (void) fprintf b; \ + c; \ + } \ + while (/*CONSTCOND*/0) +#define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;) + +/* re_printstr(): + * Print a string on the debugging pty + */ +private void +re_printstr(EditLine *el, const char *str, Char *f, Char *t) +{ + + ELRE_DEBUG(1, (__F, "%s:\"", str)); + while (f < t) + ELRE_DEBUG(1, (__F, "%c", *f++ & 0177)); + ELRE_DEBUG(1, (__F, "\"\r\n")); +} +#else +#define ELRE_ASSERT(a, b, c) +#define ELRE_DEBUG(a, b) +#endif + +/* re_nextline(): + * Move to the next line or scroll + */ +private void +re_nextline(EditLine *el) +{ + el->el_refresh.r_cursor.h = 0; /* reset it. */ + + /* + * If we would overflow (input is longer than terminal size), + * emulate scroll by dropping first line and shuffling the rest. + * We do this via pointer shuffling - it's safe in this case + * and we avoid memcpy(). + */ + if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) { + int i, lins = el->el_terminal.t_size.v; + Char *firstline = el->el_vdisplay[0]; + + for(i = 1; i < lins; i++) + el->el_vdisplay[i - 1] = el->el_vdisplay[i]; + + firstline[0] = '\0'; /* empty the string */ + el->el_vdisplay[i - 1] = firstline; + } else + el->el_refresh.r_cursor.v++; + + ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v, + (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n", + el->el_refresh.r_cursor.v, el->el_terminal.t_size.v), + abort()); +} + +/* re_addc(): + * Draw c, expanding tabs, control chars etc. + */ +private void +re_addc(EditLine *el, wint_t c) +{ + switch (ct_chr_class((Char)c)) { + case CHTYPE_TAB: /* expand the tab */ + for (;;) { + re_putc(el, ' ', 1); + if ((el->el_refresh.r_cursor.h & 07) == 0) + break; /* go until tab stop */ + } + break; + case CHTYPE_NL: { + int oldv = el->el_refresh.r_cursor.v; + re_putc(el, '\0', 0); /* assure end of line */ + if (oldv == el->el_refresh.r_cursor.v) /* XXX */ + re_nextline(el); + break; + } + case CHTYPE_PRINT: + re_putc(el, c, 1); + break; + default: { + Char visbuf[VISUAL_WIDTH_MAX]; + ssize_t i, n = + ct_visual_char(visbuf, VISUAL_WIDTH_MAX, (Char)c); + for (i = 0; n-- > 0; ++i) + re_putc(el, visbuf[i], 1); + break; + } + } +} + + +/* re_putc(): + * Draw the character given + */ +protected void +re_putc(EditLine *el, wint_t c, int shift) +{ + int i, w = Width(c); + ELRE_DEBUG(1, (__F, "printing %5x '%lc'\r\n", c, c)); + + while (shift && (el->el_refresh.r_cursor.h + w > el->el_terminal.t_size.h)) + re_putc(el, ' ', 1); + + el->el_vdisplay[el->el_refresh.r_cursor.v] + [el->el_refresh.r_cursor.h] = (Char)c; + /* assumes !shift is only used for single-column chars */ + i = w; + while (--i > 0) + el->el_vdisplay[el->el_refresh.r_cursor.v] + [el->el_refresh.r_cursor.h + i] = MB_FILL_CHAR; + + if (!shift) + return; + + el->el_refresh.r_cursor.h += w; /* advance to next place */ + if (el->el_refresh.r_cursor.h >= el->el_terminal.t_size.h) { + /* assure end of line */ + el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_terminal.t_size.h] + = '\0'; + re_nextline(el); + } +} + + +/* re_refresh(): + * draws the new virtual screen image from the current input + * line, then goes line-by-line changing the real image to the new + * virtual image. The routine to re-draw a line can be replaced + * easily in hopes of a smarter one being placed there. + */ +protected void +re_refresh(EditLine *el) +{ + int i, rhdiff; + Char *cp, *st; + coord_t cur; +#ifdef notyet + size_t termsz; +#endif + + ELRE_DEBUG(1, (__F, "el->el_line.buffer = :" FSTR ":\r\n", + el->el_line.buffer)); + + /* reset the Drawing cursor */ + el->el_refresh.r_cursor.h = 0; + el->el_refresh.r_cursor.v = 0; + + /* temporarily draw rprompt to calculate its size */ + prompt_print(el, EL_RPROMPT); + + /* reset the Drawing cursor */ + el->el_refresh.r_cursor.h = 0; + el->el_refresh.r_cursor.v = 0; + + if (el->el_line.cursor >= el->el_line.lastchar) { + if (el->el_map.current == el->el_map.alt + && el->el_line.lastchar != el->el_line.buffer) + el->el_line.cursor = el->el_line.lastchar - 1; + else + el->el_line.cursor = el->el_line.lastchar; + } + + cur.h = -1; /* set flag in case I'm not set */ + cur.v = 0; + + prompt_print(el, EL_PROMPT); + + /* draw the current input buffer */ +#if notyet + termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v; + if (el->el_line.lastchar - el->el_line.buffer > termsz) { + /* + * If line is longer than terminal, process only part + * of line which would influence display. + */ + size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz; + + st = el->el_line.lastchar - rem + - (termsz - (((rem / el->el_terminal.t_size.v) - 1) + * el->el_terminal.t_size.v)); + } else +#endif + st = el->el_line.buffer; + + for (cp = st; cp < el->el_line.lastchar; cp++) { + if (cp == el->el_line.cursor) { + int w = Width(*cp); + /* save for later */ + cur.h = el->el_refresh.r_cursor.h; + cur.v = el->el_refresh.r_cursor.v; + /* handle being at a linebroken doublewidth char */ + if (w > 1 && el->el_refresh.r_cursor.h + w > + el->el_terminal.t_size.h) { + cur.h = 0; + cur.v++; + } + } + re_addc(el, *cp); + } + + if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */ + cur.h = el->el_refresh.r_cursor.h; + cur.v = el->el_refresh.r_cursor.v; + } + rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h - + el->el_rprompt.p_pos.h; + if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v && + !el->el_refresh.r_cursor.v && rhdiff > 1) { + /* + * have a right-hand side prompt that will fit + * on the end of the first line with at least + * one character gap to the input buffer. + */ + while (--rhdiff > 0) /* pad out with spaces */ + re_putc(el, ' ', 1); + prompt_print(el, EL_RPROMPT); + } else { + el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */ + el->el_rprompt.p_pos.v = 0; + } + + re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */ + + el->el_refresh.r_newcv = el->el_refresh.r_cursor.v; + + ELRE_DEBUG(1, (__F, + "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n", + el->el_terminal.t_size.h, el->el_refresh.r_cursor.h, + el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0], + &el->el_scratch))); + + ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv)); + for (i = 0; i <= el->el_refresh.r_newcv; i++) { + /* NOTE THAT re_update_line MAY CHANGE el_display[i] */ + re_update_line(el, el->el_display[i], el->el_vdisplay[i], i); + + /* + * Copy the new line to be the current one, and pad out with + * spaces to the full width of the terminal so that if we try + * moving the cursor by writing the character that is at the + * end of the screen line, it won't be a NUL or some old + * leftover stuff. + */ + re__copy_and_pad(el->el_display[i], el->el_vdisplay[i], + (size_t) el->el_terminal.t_size.h); + } + ELRE_DEBUG(1, (__F, + "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n", + el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i)); + + if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv) + for (; i <= el->el_refresh.r_oldcv; i++) { + terminal_move_to_line(el, i); + terminal_move_to_char(el, 0); + /* This Strlen should be safe even with MB_FILL_CHARs */ + terminal_clear_EOL(el, (int) Strlen(el->el_display[i])); +#ifdef DEBUG_REFRESH + terminal_overwrite(el, STR("C\b"), 2); +#endif /* DEBUG_REFRESH */ + el->el_display[i][0] = '\0'; + } + + el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */ + ELRE_DEBUG(1, (__F, + "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n", + el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, + cur.h, cur.v)); + terminal_move_to_line(el, cur.v); /* go to where the cursor is */ + terminal_move_to_char(el, cur.h); +} + + +/* re_goto_bottom(): + * used to go to last used screen line + */ +protected void +re_goto_bottom(EditLine *el) +{ + + terminal_move_to_line(el, el->el_refresh.r_oldcv); + terminal__putc(el, '\n'); + re_clear_display(el); + terminal__flush(el); +} + + +/* re_insert(): + * insert num characters of s into d (in front of the character) + * at dat, maximum length of d is dlen + */ +private void +/*ARGSUSED*/ +re_insert(EditLine *el __attribute__((__unused__)), + Char *d, int dat, int dlen, Char *s, int num) +{ + Char *a, *b; + + if (num <= 0) + return; + if (num > dlen - dat) + num = dlen - dat; + + ELRE_DEBUG(1, + (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n", + num, dat, dlen, ct_encode_string(d, &el->el_scratch))); + ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, + &el->el_scratch))); + + /* open up the space for num chars */ + if (num > 0) { + b = d + dlen - 1; + a = b - num; + while (a >= &d[dat]) + *b-- = *a--; + d[dlen] = '\0'; /* just in case */ + } + + ELRE_DEBUG(1, (__F, + "re_insert() after insert: %d at %d max %d, d == \"%s\"\n", + num, dat, dlen, ct_encode_string(d, &el->el_scratch))); + ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, + &el->el_scratch))); + + /* copy the characters */ + for (a = d + dat; (a < d + dlen) && (num > 0); num--) + *a++ = *s++; + +#ifdef notyet + /* ct_encode_string() uses a static buffer, so we can't conveniently + * encode both d & s here */ + ELRE_DEBUG(1, + (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n", + num, dat, dlen, d, s)); + ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); +#endif +} + + +/* re_delete(): + * delete num characters d at dat, maximum length of d is dlen + */ +private void +/*ARGSUSED*/ +re_delete(EditLine *el __attribute__((__unused__)), + Char *d, int dat, int dlen, int num) +{ + Char *a, *b; + + if (num <= 0) + return; + if (dat + num >= dlen) { + d[dat] = '\0'; + return; + } + ELRE_DEBUG(1, + (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n", + num, dat, dlen, ct_encode_string(d, &el->el_scratch))); + + /* open up the space for num chars */ + if (num > 0) { + b = d + dat; + a = b + num; + while (a < &d[dlen]) + *b++ = *a++; + d[dlen] = '\0'; /* just in case */ + } + ELRE_DEBUG(1, + (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n", + num, dat, dlen, ct_encode_string(d, &el->el_scratch))); +} + + +/* re__strncopy(): + * Like strncpy without padding. + */ +private void +re__strncopy(Char *a, Char *b, size_t n) +{ + + while (n-- && *b) + *a++ = *b++; +} + +/* re_clear_eol(): + * Find the number of characters we need to clear till the end of line + * in order to make sure that we have cleared the previous contents of + * the line. fx and sx is the number of characters inserted or deleted + * in the first or second diff, diff is the difference between the + * number of characters between the new and old line. + */ +private void +re_clear_eol(EditLine *el, int fx, int sx, int diff) +{ + + ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n", + sx, fx, diff)); + + if (fx < 0) + fx = -fx; + if (sx < 0) + sx = -sx; + if (fx > diff) + diff = fx; + if (sx > diff) + diff = sx; + + ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff)); + terminal_clear_EOL(el, diff); +} + +/***************************************************************** + re_update_line() is based on finding the middle difference of each line + on the screen; vis: + + /old first difference + /beginning of line | /old last same /old EOL + v v v v +old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as +new: eddie> Oh, my little buggy says to me, as lurgid as + ^ ^ ^ ^ + \beginning of line | \new last same \new end of line + \new first difference + + all are character pointers for the sake of speed. Special cases for + no differences, as well as for end of line additions must be handled. +**************************************************************** */ + +/* Minimum at which doing an insert it "worth it". This should be about + * half the "cost" of going into insert mode, inserting a character, and + * going back out. This should really be calculated from the termcap + * data... For the moment, a good number for ANSI terminals. + */ +#define MIN_END_KEEP 4 + +private void +re_update_line(EditLine *el, Char *old, Char *new, int i) +{ + Char *o, *n, *p, c; + Char *ofd, *ols, *oe, *nfd, *nls, *ne; + Char *osb, *ose, *nsb, *nse; + int fx, sx; + size_t len; + + /* + * find first diff + */ + for (o = old, n = new; *o && (*o == *n); o++, n++) + continue; + ofd = o; + nfd = n; + + /* + * Find the end of both old and new + */ + while (*o) + o++; + /* + * Remove any trailing blanks off of the end, being careful not to + * back up past the beginning. + */ + while (ofd < o) { + if (o[-1] != ' ') + break; + o--; + } + oe = o; + *oe = '\0'; + + while (*n) + n++; + + /* remove blanks from end of new */ + while (nfd < n) { + if (n[-1] != ' ') + break; + n--; + } + ne = n; + *ne = '\0'; + + /* + * if no diff, continue to next line of redraw + */ + if (*ofd == '\0' && *nfd == '\0') { + ELRE_DEBUG(1, (__F, "no difference.\r\n")); + return; + } + /* + * find last same pointer + */ + while ((o > ofd) && (n > nfd) && (*--o == *--n)) + continue; + ols = ++o; + nls = ++n; + + /* + * find same beginning and same end + */ + osb = ols; + nsb = nls; + ose = ols; + nse = nls; + + /* + * case 1: insert: scan from nfd to nls looking for *ofd + */ + if (*ofd) { + for (c = *ofd, n = nfd; n < nls; n++) { + if (c == *n) { + for (o = ofd, p = n; + p < nls && o < ols && *o == *p; + o++, p++) + continue; + /* + * if the new match is longer and it's worth + * keeping, then we take it + */ + if (((nse - nsb) < (p - n)) && + (2 * (p - n) > n - nfd)) { + nsb = n; + nse = p; + osb = ofd; + ose = o; + } + } + } + } + /* + * case 2: delete: scan from ofd to ols looking for *nfd + */ + if (*nfd) { + for (c = *nfd, o = ofd; o < ols; o++) { + if (c == *o) { + for (n = nfd, p = o; + p < ols && n < nls && *p == *n; + p++, n++) + continue; + /* + * if the new match is longer and it's worth + * keeping, then we take it + */ + if (((ose - osb) < (p - o)) && + (2 * (p - o) > o - ofd)) { + nsb = nfd; + nse = n; + osb = o; + ose = p; + } + } + } + } + /* + * Pragmatics I: If old trailing whitespace or not enough characters to + * save to be worth it, then don't save the last same info. + */ + if ((oe - ols) < MIN_END_KEEP) { + ols = oe; + nls = ne; + } + /* + * Pragmatics II: if the terminal isn't smart enough, make the data + * dumber so the smart update doesn't try anything fancy + */ + + /* + * fx is the number of characters we need to insert/delete: in the + * beginning to bring the two same begins together + */ + fx = (int)((nsb - nfd) - (osb - ofd)); + /* + * sx is the number of characters we need to insert/delete: in the + * end to bring the two same last parts together + */ + sx = (int)((nls - nse) - (ols - ose)); + + if (!EL_CAN_INSERT) { + if (fx > 0) { + osb = ols; + ose = ols; + nsb = nls; + nse = nls; + } + if (sx > 0) { + ols = oe; + nls = ne; + } + if ((ols - ofd) < (nls - nfd)) { + ols = oe; + nls = ne; + } + } + if (!EL_CAN_DELETE) { + if (fx < 0) { + osb = ols; + ose = ols; + nsb = nls; + nse = nls; + } + if (sx < 0) { + ols = oe; + nls = ne; + } + if ((ols - ofd) > (nls - nfd)) { + ols = oe; + nls = ne; + } + } + /* + * Pragmatics III: make sure the middle shifted pointers are correct if + * they don't point to anything (we may have moved ols or nls). + */ + /* if the change isn't worth it, don't bother */ + /* was: if (osb == ose) */ + if ((ose - osb) < MIN_END_KEEP) { + osb = ols; + ose = ols; + nsb = nls; + nse = nls; + } + /* + * Now that we are done with pragmatics we recompute fx, sx + */ + fx = (int)((nsb - nfd) - (osb - ofd)); + sx = (int)((nls - nse) - (ols - ose)); + + ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx)); + ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n", + ofd - old, osb - old, ose - old, ols - old, oe - old)); + ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n", + nfd - new, nsb - new, nse - new, nls - new, ne - new)); + ELRE_DEBUG(1, (__F, + "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n")); + ELRE_DEBUG(1, (__F, + "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n")); +#ifdef DEBUG_REFRESH + re_printstr(el, "old- oe", old, oe); + re_printstr(el, "new- ne", new, ne); + re_printstr(el, "old-ofd", old, ofd); + re_printstr(el, "new-nfd", new, nfd); + re_printstr(el, "ofd-osb", ofd, osb); + re_printstr(el, "nfd-nsb", nfd, nsb); + re_printstr(el, "osb-ose", osb, ose); + re_printstr(el, "nsb-nse", nsb, nse); + re_printstr(el, "ose-ols", ose, ols); + re_printstr(el, "nse-nls", nse, nls); + re_printstr(el, "ols- oe", ols, oe); + re_printstr(el, "nls- ne", nls, ne); +#endif /* DEBUG_REFRESH */ + + /* + * el_cursor.v to this line i MUST be in this routine so that if we + * don't have to change the line, we don't move to it. el_cursor.h to + * first diff char + */ + terminal_move_to_line(el, i); + + /* + * at this point we have something like this: + * + * /old /ofd /osb /ose /ols /oe + * v.....................v v..................v v........v + * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as + * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as + * ^.....................^ ^..................^ ^........^ + * \new \nfd \nsb \nse \nls \ne + * + * fx is the difference in length between the chars between nfd and + * nsb, and the chars between ofd and osb, and is thus the number of + * characters to delete if < 0 (new is shorter than old, as above), + * or insert (new is longer than short). + * + * sx is the same for the second differences. + */ + + /* + * if we have a net insert on the first difference, AND inserting the + * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful + * character (which is ne if nls != ne, otherwise is nse) off the edge + * of the screen (el->el_terminal.t_size.h) else we do the deletes first + * so that we keep everything we need to. + */ + + /* + * if the last same is the same like the end, there is no last same + * part, otherwise we want to keep the last same part set p to the + * last useful old character + */ + p = (ols != oe) ? oe : ose; + + /* + * if (There is a diffence in the beginning) && (we need to insert + * characters) && (the number of characters to insert is less than + * the term width) + * We need to do an insert! + * else if (we need to delete characters) + * We need to delete characters! + * else + * No insert or delete + */ + if ((nsb != nfd) && fx > 0 && + ((p - old) + fx <= el->el_terminal.t_size.h)) { + ELRE_DEBUG(1, + (__F, "first diff insert at %td...\r\n", nfd - new)); + /* + * Move to the first char to insert, where the first diff is. + */ + terminal_move_to_char(el, (int)(nfd - new)); + /* + * Check if we have stuff to keep at end + */ + if (nsb != ne) { + ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); + /* + * insert fx chars of new starting at nfd + */ + if (fx > 0) { + ELRE_DEBUG(!EL_CAN_INSERT, (__F, + "ERROR: cannot insert in early first diff\n")); + terminal_insertwrite(el, nfd, fx); + re_insert(el, old, (int)(ofd - old), + el->el_terminal.t_size.h, nfd, fx); + } + /* + * write (nsb-nfd) - fx chars of new starting at + * (nfd + fx) + */ + len = (size_t) ((nsb - nfd) - fx); + terminal_overwrite(el, (nfd + fx), len); + re__strncopy(ofd + fx, nfd + fx, len); + } else { + ELRE_DEBUG(1, (__F, "without anything to save\r\n")); + len = (size_t)(nsb - nfd); + terminal_overwrite(el, nfd, len); + re__strncopy(ofd, nfd, len); + /* + * Done + */ + return; + } + } else if (fx < 0) { + ELRE_DEBUG(1, + (__F, "first diff delete at %td...\r\n", ofd - old)); + /* + * move to the first char to delete where the first diff is + */ + terminal_move_to_char(el, (int)(ofd - old)); + /* + * Check if we have stuff to save + */ + if (osb != oe) { + ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); + /* + * fx is less than zero *always* here but we check + * for code symmetry + */ + if (fx < 0) { + ELRE_DEBUG(!EL_CAN_DELETE, (__F, + "ERROR: cannot delete in first diff\n")); + terminal_deletechars(el, -fx); + re_delete(el, old, (int)(ofd - old), + el->el_terminal.t_size.h, -fx); + } + /* + * write (nsb-nfd) chars of new starting at nfd + */ + len = (size_t) (nsb - nfd); + terminal_overwrite(el, nfd, len); + re__strncopy(ofd, nfd, len); + + } else { + ELRE_DEBUG(1, (__F, + "but with nothing left to save\r\n")); + /* + * write (nsb-nfd) chars of new starting at nfd + */ + terminal_overwrite(el, nfd, (size_t)(nsb - nfd)); + re_clear_eol(el, fx, sx, + (int)((oe - old) - (ne - new))); + /* + * Done + */ + return; + } + } else + fx = 0; + + if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) { + ELRE_DEBUG(1, (__F, + "second diff delete at %td...\r\n", (ose - old) + fx)); + /* + * Check if we have stuff to delete + */ + /* + * fx is the number of characters inserted (+) or deleted (-) + */ + + terminal_move_to_char(el, (int)((ose - old) + fx)); + /* + * Check if we have stuff to save + */ + if (ols != oe) { + ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); + /* + * Again a duplicate test. + */ + if (sx < 0) { + ELRE_DEBUG(!EL_CAN_DELETE, (__F, + "ERROR: cannot delete in second diff\n")); + terminal_deletechars(el, -sx); + } + /* + * write (nls-nse) chars of new starting at nse + */ + terminal_overwrite(el, nse, (size_t)(nls - nse)); + } else { + ELRE_DEBUG(1, (__F, + "but with nothing left to save\r\n")); + terminal_overwrite(el, nse, (size_t)(nls - nse)); + re_clear_eol(el, fx, sx, + (int)((oe - old) - (ne - new))); + } + } + /* + * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... + */ + if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { + ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n", + nfd - new)); + + terminal_move_to_char(el, (int)(nfd - new)); + /* + * Check if we have stuff to keep at the end + */ + if (nsb != ne) { + ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); + /* + * We have to recalculate fx here because we set it + * to zero above as a flag saying that we hadn't done + * an early first insert. + */ + fx = (int)((nsb - nfd) - (osb - ofd)); + if (fx > 0) { + /* + * insert fx chars of new starting at nfd + */ + ELRE_DEBUG(!EL_CAN_INSERT, (__F, + "ERROR: cannot insert in late first diff\n")); + terminal_insertwrite(el, nfd, fx); + re_insert(el, old, (int)(ofd - old), + el->el_terminal.t_size.h, nfd, fx); + } + /* + * write (nsb-nfd) - fx chars of new starting at + * (nfd + fx) + */ + len = (size_t) ((nsb - nfd) - fx); + terminal_overwrite(el, (nfd + fx), len); + re__strncopy(ofd + fx, nfd + fx, len); + } else { + ELRE_DEBUG(1, (__F, "without anything to save\r\n")); + len = (size_t) (nsb - nfd); + terminal_overwrite(el, nfd, len); + re__strncopy(ofd, nfd, len); + } + } + /* + * line is now NEW up to nse + */ + if (sx >= 0) { + ELRE_DEBUG(1, (__F, + "second diff insert at %d...\r\n", (int)(nse - new))); + terminal_move_to_char(el, (int)(nse - new)); + if (ols != oe) { + ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); + if (sx > 0) { + /* insert sx chars of new starting at nse */ + ELRE_DEBUG(!EL_CAN_INSERT, (__F, + "ERROR: cannot insert in second diff\n")); + terminal_insertwrite(el, nse, sx); + } + /* + * write (nls-nse) - sx chars of new starting at + * (nse + sx) + */ + terminal_overwrite(el, (nse + sx), + (size_t)((nls - nse) - sx)); + } else { + ELRE_DEBUG(1, (__F, "without anything to save\r\n")); + terminal_overwrite(el, nse, (size_t)(nls - nse)); + + /* + * No need to do a clear-to-end here because we were + * doing a second insert, so we will have over + * written all of the old string. + */ + } + } + ELRE_DEBUG(1, (__F, "done.\r\n")); +} + + +/* re__copy_and_pad(): + * Copy string and pad with spaces + */ +private void +re__copy_and_pad(Char *dst, const Char *src, size_t width) +{ + size_t i; + + for (i = 0; i < width; i++) { + if (*src == '\0') + break; + *dst++ = *src++; + } + + for (; i < width; i++) + *dst++ = ' '; + + *dst = '\0'; +} + + +/* re_refresh_cursor(): + * Move to the new cursor position + */ +protected void +re_refresh_cursor(EditLine *el) +{ + Char *cp; + int h, v, th, w; + + if (el->el_line.cursor >= el->el_line.lastchar) { + if (el->el_map.current == el->el_map.alt + && el->el_line.lastchar != el->el_line.buffer) + el->el_line.cursor = el->el_line.lastchar - 1; + else + el->el_line.cursor = el->el_line.lastchar; + } + + /* first we must find where the cursor is... */ + h = el->el_prompt.p_pos.h; + v = el->el_prompt.p_pos.v; + th = el->el_terminal.t_size.h; /* optimize for speed */ + + /* do input buffer to el->el_line.cursor */ + for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { + switch (ct_chr_class(*cp)) { + case CHTYPE_NL: /* handle newline in data part too */ + h = 0; + v++; + break; + case CHTYPE_TAB: /* if a tab, to next tab stop */ + while (++h & 07) + continue; + break; + default: + w = Width(*cp); + if (w > 1 && h + w > th) { /* won't fit on line */ + h = 0; + v++; + } + h += ct_visual_width(*cp); + break; + } + + if (h >= th) { /* check, extra long tabs picked up here also */ + h -= th; + v++; + } + } + /* if we have a next character, and it's a doublewidth one, we need to + * check whether we need to linebreak for it to fit */ + if (cp < el->el_line.lastchar && (w = Width(*cp)) > 1) + if (h + w > th) { + h = 0; + v++; + } + + /* now go there */ + terminal_move_to_line(el, v); + terminal_move_to_char(el, h); + terminal__flush(el); +} + + +/* re_fastputc(): + * Add a character fast. + */ +private void +re_fastputc(EditLine *el, wint_t c) +{ + int w = Width((Char)c); + while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h) + re_fastputc(el, ' '); + + terminal__putc(el, c); + el->el_display[el->el_cursor.v][el->el_cursor.h++] = (Char)c; + while (--w > 0) + el->el_display[el->el_cursor.v][el->el_cursor.h++] + = MB_FILL_CHAR; + + if (el->el_cursor.h >= el->el_terminal.t_size.h) { + /* if we must overflow */ + el->el_cursor.h = 0; + + /* + * If we would overflow (input is longer than terminal size), + * emulate scroll by dropping first line and shuffling the rest. + * We do this via pointer shuffling - it's safe in this case + * and we avoid memcpy(). + */ + if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) { + int i, lins = el->el_terminal.t_size.v; + Char *firstline = el->el_display[0]; + + for(i = 1; i < lins; i++) + el->el_display[i - 1] = el->el_display[i]; + + re__copy_and_pad(firstline, STR(""), (size_t)0); + el->el_display[i - 1] = firstline; + } else { + el->el_cursor.v++; + el->el_refresh.r_oldcv++; + } + if (EL_HAS_AUTO_MARGINS) { + if (EL_HAS_MAGIC_MARGINS) { + terminal__putc(el, ' '); + terminal__putc(el, '\b'); + } + } else { + terminal__putc(el, '\r'); + terminal__putc(el, '\n'); + } + } +} + + +/* re_fastaddc(): + * we added just one char, handle it fast. + * Assumes that screen cursor == real cursor + */ +protected void +re_fastaddc(EditLine *el) +{ + Char c; + int rhdiff; + + c = el->el_line.cursor[-1]; + + if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { + re_refresh(el); /* too hard to handle */ + return; + } + rhdiff = el->el_terminal.t_size.h - el->el_cursor.h - + el->el_rprompt.p_pos.h; + if (el->el_rprompt.p_pos.h && rhdiff < 3) { + re_refresh(el); /* clear out rprompt if less than 1 char gap */ + return; + } /* else (only do at end of line, no TAB) */ + switch (ct_chr_class(c)) { + case CHTYPE_TAB: /* already handled, should never happen here */ + break; + case CHTYPE_NL: + case CHTYPE_PRINT: + re_fastputc(el, c); + break; + case CHTYPE_ASCIICTL: + case CHTYPE_NONPRINT: { + Char visbuf[VISUAL_WIDTH_MAX]; + ssize_t i, n = + ct_visual_char(visbuf, VISUAL_WIDTH_MAX, (Char)c); + for (i = 0; n-- > 0; ++i) + re_fastputc(el, visbuf[i]); + break; + } + } + terminal__flush(el); +} + + +/* re_clear_display(): + * clear the screen buffers so that new new prompt starts fresh. + */ +protected void +re_clear_display(EditLine *el) +{ + int i; + + el->el_cursor.v = 0; + el->el_cursor.h = 0; + for (i = 0; i < el->el_terminal.t_size.v; i++) + el->el_display[i][0] = '\0'; + el->el_refresh.r_oldcv = 0; +} + + +/* re_clear_lines(): + * Make sure all lines are *really* blank + */ +protected void +re_clear_lines(EditLine *el) +{ + + if (EL_CAN_CEOL) { + int i; + for (i = el->el_refresh.r_oldcv; i >= 0; i--) { + /* for each line on the screen */ + terminal_move_to_line(el, i); + terminal_move_to_char(el, 0); + terminal_clear_EOL(el, el->el_terminal.t_size.h); + } + } else { + terminal_move_to_line(el, el->el_refresh.r_oldcv); + /* go to last line */ + terminal__putc(el, '\r'); /* go to BOL */ + terminal__putc(el, '\n'); /* go to new line */ + } +} diff --git a/lib/libedit/refresh.h b/lib/libedit/refresh.h index 9496995..afc57ff 100644 --- a/lib/libedit/refresh.h +++ b/lib/libedit/refresh.h @@ -1,3 +1,5 @@ +/* $NetBSD: refresh.h,v 1.9 2016/02/16 15:53:48 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)refresh.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: refresh.h,v 1.5 2003/08/07 16:44:33 agc Exp $ - * $FreeBSD: src/lib/libedit/refresh.h,v 1.2.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/refresh.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -40,15 +41,13 @@ #ifndef _h_el_refresh #define _h_el_refresh -#include "histedit.h" - typedef struct { coord_t r_cursor; /* Refresh cursor position */ int r_oldcv; /* Vertical locations */ int r_newcv; } el_refresh_t; -protected void re_putc(EditLine *, int, int); +protected void re_putc(EditLine *, wint_t, int); protected void re_clear_lines(EditLine *); protected void re_clear_display(EditLine *); protected void re_refresh(EditLine *); diff --git a/lib/libedit/search.c b/lib/libedit/search.c new file mode 100644 index 0000000..8dc557f --- /dev/null +++ b/lib/libedit/search.c @@ -0,0 +1,647 @@ +/* $NetBSD: search.c,v 1.39 2016/02/24 14:25:38 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: search.c,v 1.39 2016/02/24 14:25:38 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/search.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * search.c: History and character search functions + */ +#include +#include +#if defined(REGEX) +#include +#elif defined(REGEXP) +#include +#endif + +#include "el.h" +#include "common.h" + +/* + * Adjust cursor in vi mode to include the character under it + */ +#define EL_CURSOR(el) \ + ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \ + ((el)->el_map.current == (el)->el_map.alt))) + +/* search_init(): + * Initialize the search stuff + */ +protected int +search_init(EditLine *el) +{ + + el->el_search.patbuf = el_malloc(EL_BUFSIZ * + sizeof(*el->el_search.patbuf)); + if (el->el_search.patbuf == NULL) + return -1; + el->el_search.patlen = 0; + el->el_search.patdir = -1; + el->el_search.chacha = '\0'; + el->el_search.chadir = CHAR_FWD; + el->el_search.chatflg = 0; + return 0; +} + + +/* search_end(): + * Initialize the search stuff + */ +protected void +search_end(EditLine *el) +{ + + el_free(el->el_search.patbuf); + el->el_search.patbuf = NULL; +} + + +#ifdef REGEXP +/* regerror(): + * Handle regular expression errors + */ +public void +/*ARGSUSED*/ +regerror(const char *msg) +{ +} +#endif + + +/* el_match(): + * Return if string matches pattern + */ +protected int +el_match(const Char *str, const Char *pat) +{ +#ifdef WIDECHAR + static ct_buffer_t conv; +#endif +#if defined (REGEX) + regex_t re; + int rv; +#elif defined (REGEXP) + regexp *rp; + int rv; +#else + extern char *re_comp(const char *); + extern int re_exec(const char *); +#endif + + if (Strstr(str, pat) != 0) + return 1; + +#if defined(REGEX) + if (regcomp(&re, ct_encode_string(pat, &conv), 0) == 0) { + rv = regexec(&re, ct_encode_string(str, &conv), (size_t)0, NULL, + 0) == 0; + regfree(&re); + } else { + rv = 0; + } + return rv; +#elif defined(REGEXP) + if ((re = regcomp(ct_encode_string(pat, &conv))) != NULL) { + rv = regexec(re, ct_encode_string(str, &conv)); + el_free(re); + } else { + rv = 0; + } + return rv; +#else + if (re_comp(ct_encode_string(pat, &conv)) != NULL) + return 0; + else + return re_exec(ct_encode_string(str, &conv)) == 1; +#endif +} + + +/* c_hmatch(): + * return True if the pattern matches the prefix + */ +protected int +c_hmatch(EditLine *el, const Char *str) +{ +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "match `%s' with `%s'\n", + el->el_search.patbuf, str); +#endif /* SDEBUG */ + + return el_match(str, el->el_search.patbuf); +} + + +/* c_setpat(): + * Set the history seatch pattern + */ +protected void +c_setpat(EditLine *el) +{ + if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY && + el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) { + el->el_search.patlen = + (size_t)(EL_CURSOR(el) - el->el_line.buffer); + if (el->el_search.patlen >= EL_BUFSIZ) + el->el_search.patlen = EL_BUFSIZ - 1; + if (el->el_search.patlen != 0) { + (void) Strncpy(el->el_search.patbuf, el->el_line.buffer, + el->el_search.patlen); + el->el_search.patbuf[el->el_search.patlen] = '\0'; + } else + el->el_search.patlen = Strlen(el->el_search.patbuf); + } +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "\neventno = %d\n", + el->el_history.eventno); + (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen); + (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n", + el->el_search.patbuf); + (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n", + EL_CURSOR(el) - el->el_line.buffer, + el->el_line.lastchar - el->el_line.buffer); +#endif +} + + +/* ce_inc_search(): + * Emacs incremental search + */ +protected el_action_t +ce_inc_search(EditLine *el, int dir) +{ + static const Char STRfwd[] = {'f', 'w', 'd', '\0'}, + STRbck[] = {'b', 'c', 'k', '\0'}; + static Char pchar = ':';/* ':' = normal, '?' = failed */ + static Char endcmd[2] = {'\0', '\0'}; + Char *ocursor = el->el_line.cursor, oldpchar = pchar, ch; + const Char *cp; + wchar_t wch; + + el_action_t ret = CC_NORM; + + int ohisteventno = el->el_history.eventno; + size_t oldpatlen = el->el_search.patlen; + int newdir = dir; + int done, redo; + + if (el->el_line.lastchar + sizeof(STRfwd) / + sizeof(*el->el_line.lastchar) + 2 + + el->el_search.patlen >= el->el_line.limit) + return CC_ERROR; + + for (;;) { + + if (el->el_search.patlen == 0) { /* first round */ + pchar = ':'; +#ifdef ANCHOR +#define LEN 2 + el->el_search.patbuf[el->el_search.patlen++] = '.'; + el->el_search.patbuf[el->el_search.patlen++] = '*'; +#else +#define LEN 0 +#endif + } + done = redo = 0; + *el->el_line.lastchar++ = '\n'; + for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd; + *cp; *el->el_line.lastchar++ = *cp++) + continue; + *el->el_line.lastchar++ = pchar; + for (cp = &el->el_search.patbuf[LEN]; + cp < &el->el_search.patbuf[el->el_search.patlen]; + *el->el_line.lastchar++ = *cp++) + continue; + *el->el_line.lastchar = '\0'; + re_refresh(el); + + if (el_wgetc(el, &wch) != 1) + return ed_end_of_file(el, 0); + + ch = (Char)wch; + + switch (el->el_map.current[(unsigned char) ch]) { + case ED_INSERT: + case ED_DIGIT: + if (el->el_search.patlen >= EL_BUFSIZ - LEN) + terminal_beep(el); + else { + el->el_search.patbuf[el->el_search.patlen++] = + ch; + *el->el_line.lastchar++ = ch; + *el->el_line.lastchar = '\0'; + re_refresh(el); + } + break; + + case EM_INC_SEARCH_NEXT: + newdir = ED_SEARCH_NEXT_HISTORY; + redo++; + break; + + case EM_INC_SEARCH_PREV: + newdir = ED_SEARCH_PREV_HISTORY; + redo++; + break; + + case EM_DELETE_PREV_CHAR: + case ED_DELETE_PREV_CHAR: + if (el->el_search.patlen > LEN) + done++; + else + terminal_beep(el); + break; + + default: + switch (ch) { + case 0007: /* ^G: Abort */ + ret = CC_ERROR; + done++; + break; + + case 0027: /* ^W: Append word */ + /* No can do if globbing characters in pattern */ + for (cp = &el->el_search.patbuf[LEN];; cp++) + if (cp >= &el->el_search.patbuf[ + el->el_search.patlen]) { + el->el_line.cursor += + el->el_search.patlen - LEN - 1; + cp = c__next_word(el->el_line.cursor, + el->el_line.lastchar, 1, + ce__isword); + while (el->el_line.cursor < cp && + *el->el_line.cursor != '\n') { + if (el->el_search.patlen >= + EL_BUFSIZ - LEN) { + terminal_beep(el); + break; + } + el->el_search.patbuf[el->el_search.patlen++] = + *el->el_line.cursor; + *el->el_line.lastchar++ = + *el->el_line.cursor++; + } + el->el_line.cursor = ocursor; + *el->el_line.lastchar = '\0'; + re_refresh(el); + break; + } else if (isglob(*cp)) { + terminal_beep(el); + break; + } + break; + + default: /* Terminate and execute cmd */ + endcmd[0] = ch; + FUN(el,push)(el, endcmd); + /* FALLTHROUGH */ + + case 0033: /* ESC: Terminate */ + ret = CC_REFRESH; + done++; + break; + } + break; + } + + while (el->el_line.lastchar > el->el_line.buffer && + *el->el_line.lastchar != '\n') + *el->el_line.lastchar-- = '\0'; + *el->el_line.lastchar = '\0'; + + if (!done) { + + /* Can't search if unmatched '[' */ + for (cp = &el->el_search.patbuf[el->el_search.patlen-1], + ch = L']'; + cp >= &el->el_search.patbuf[LEN]; + cp--) + if (*cp == '[' || *cp == ']') { + ch = *cp; + break; + } + if (el->el_search.patlen > LEN && ch != L'[') { + if (redo && newdir == dir) { + if (pchar == '?') { /* wrap around */ + el->el_history.eventno = + newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff; + if (hist_get(el) == CC_ERROR) + /* el->el_history.event + * no was fixed by + * first call */ + (void) hist_get(el); + el->el_line.cursor = newdir == + ED_SEARCH_PREV_HISTORY ? + el->el_line.lastchar : + el->el_line.buffer; + } else + el->el_line.cursor += + newdir == + ED_SEARCH_PREV_HISTORY ? + -1 : 1; + } +#ifdef ANCHOR + el->el_search.patbuf[el->el_search.patlen++] = + '.'; + el->el_search.patbuf[el->el_search.patlen++] = + '*'; +#endif + el->el_search.patbuf[el->el_search.patlen] = + '\0'; + if (el->el_line.cursor < el->el_line.buffer || + el->el_line.cursor > el->el_line.lastchar || + (ret = ce_search_line(el, newdir)) + == CC_ERROR) { + /* avoid c_setpat */ + el->el_state.lastcmd = + (el_action_t) newdir; + ret = (el_action_t) + (newdir == ED_SEARCH_PREV_HISTORY ? + ed_search_prev_history(el, 0) : + ed_search_next_history(el, 0)); + if (ret != CC_ERROR) { + el->el_line.cursor = newdir == + ED_SEARCH_PREV_HISTORY ? + el->el_line.lastchar : + el->el_line.buffer; + (void) ce_search_line(el, + newdir); + } + } + el->el_search.patlen -= LEN; + el->el_search.patbuf[el->el_search.patlen] = + '\0'; + if (ret == CC_ERROR) { + terminal_beep(el); + if (el->el_history.eventno != + ohisteventno) { + el->el_history.eventno = + ohisteventno; + if (hist_get(el) == CC_ERROR) + return CC_ERROR; + } + el->el_line.cursor = ocursor; + pchar = '?'; + } else { + pchar = ':'; + } + } + ret = ce_inc_search(el, newdir); + + if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') + /* + * break abort of failed search at last + * non-failed + */ + ret = CC_NORM; + + } + if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { + /* restore on normal return or error exit */ + pchar = oldpchar; + el->el_search.patlen = oldpatlen; + if (el->el_history.eventno != ohisteventno) { + el->el_history.eventno = ohisteventno; + if (hist_get(el) == CC_ERROR) + return CC_ERROR; + } + el->el_line.cursor = ocursor; + if (ret == CC_ERROR) + re_refresh(el); + } + if (done || ret != CC_NORM) + return ret; + } +} + + +/* cv_search(): + * Vi search. + */ +protected el_action_t +cv_search(EditLine *el, int dir) +{ + Char ch; + Char tmpbuf[EL_BUFSIZ]; + ssize_t tmplen; + +#ifdef ANCHOR + tmpbuf[0] = '.'; + tmpbuf[1] = '*'; +#endif + tmplen = LEN; + + el->el_search.patdir = dir; + + tmplen = c_gets(el, &tmpbuf[LEN], + dir == ED_SEARCH_PREV_HISTORY ? STR("\n/") : STR("\n?") ); + if (tmplen == -1) + return CC_REFRESH; + + tmplen += LEN; + ch = tmpbuf[tmplen]; + tmpbuf[tmplen] = '\0'; + + if (tmplen == LEN) { + /* + * Use the old pattern, but wild-card it. + */ + if (el->el_search.patlen == 0) { + re_refresh(el); + return CC_ERROR; + } +#ifdef ANCHOR + if (el->el_search.patbuf[0] != '.' && + el->el_search.patbuf[0] != '*') { + (void) Strncpy(tmpbuf, el->el_search.patbuf, + sizeof(tmpbuf) / sizeof(*tmpbuf) - 1); + el->el_search.patbuf[0] = '.'; + el->el_search.patbuf[1] = '*'; + (void) Strncpy(&el->el_search.patbuf[2], tmpbuf, + EL_BUFSIZ - 3); + el->el_search.patlen++; + el->el_search.patbuf[el->el_search.patlen++] = '.'; + el->el_search.patbuf[el->el_search.patlen++] = '*'; + el->el_search.patbuf[el->el_search.patlen] = '\0'; + } +#endif + } else { +#ifdef ANCHOR + tmpbuf[tmplen++] = '.'; + tmpbuf[tmplen++] = '*'; +#endif + tmpbuf[tmplen] = '\0'; + (void) Strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1); + el->el_search.patlen = (size_t)tmplen; + } + el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */ + el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer; + if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : + ed_search_next_history(el, 0)) == CC_ERROR) { + re_refresh(el); + return CC_ERROR; + } + if (ch == 0033) { + re_refresh(el); + return ed_newline(el, 0); + } + return CC_REFRESH; +} + + +/* ce_search_line(): + * Look for a pattern inside a line + */ +protected el_action_t +ce_search_line(EditLine *el, int dir) +{ + Char *cp = el->el_line.cursor; + Char *pattern = el->el_search.patbuf; + Char oc, *ocp; +#ifdef ANCHOR + ocp = &pattern[1]; + oc = *ocp; + *ocp = '^'; +#else + ocp = pattern; + oc = *ocp; +#endif + + if (dir == ED_SEARCH_PREV_HISTORY) { + for (; cp >= el->el_line.buffer; cp--) { + if (el_match(cp, ocp)) { + *ocp = oc; + el->el_line.cursor = cp; + return CC_NORM; + } + } + *ocp = oc; + return CC_ERROR; + } else { + for (; *cp != '\0' && cp < el->el_line.limit; cp++) { + if (el_match(cp, ocp)) { + *ocp = oc; + el->el_line.cursor = cp; + return CC_NORM; + } + } + *ocp = oc; + return CC_ERROR; + } +} + + +/* cv_repeat_srch(): + * Vi repeat search + */ +protected el_action_t +cv_repeat_srch(EditLine *el, wint_t c) +{ + +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n", + c, el->el_search.patlen, ct_encode_string(el->el_search.patbuf)); +#endif + + el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */ + el->el_line.lastchar = el->el_line.buffer; + + switch (c) { + case ED_SEARCH_NEXT_HISTORY: + return ed_search_next_history(el, 0); + case ED_SEARCH_PREV_HISTORY: + return ed_search_prev_history(el, 0); + default: + return CC_ERROR; + } +} + + +/* cv_csearch(): + * Vi character search + */ +protected el_action_t +cv_csearch(EditLine *el, int direction, wint_t ch, int count, int tflag) +{ + Char *cp; + + if (ch == 0) + return CC_ERROR; + + if (ch == (wint_t)-1) { + if (el_wgetc(el, &ch) != 1) + return ed_end_of_file(el, 0); + } + + /* Save for ';' and ',' commands */ + el->el_search.chacha = (Char)ch; + el->el_search.chadir = direction; + el->el_search.chatflg = (char)tflag; + + cp = el->el_line.cursor; + while (count--) { + if ((wint_t)*cp == ch) + cp += direction; + for (;;cp += direction) { + if (cp >= el->el_line.lastchar) + return CC_ERROR; + if (cp < el->el_line.buffer) + return CC_ERROR; + if ((wint_t)*cp == ch) + break; + } + } + + if (tflag) + cp -= direction; + + el->el_line.cursor = cp; + + if (el->el_chared.c_vcmd.action != NOP) { + if (direction > 0) + el->el_line.cursor++; + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} diff --git a/lib/libedit/search.h b/lib/libedit/search.h index 2b63675..3940d03 100644 --- a/lib/libedit/search.h +++ b/lib/libedit/search.h @@ -1,3 +1,5 @@ +/* $NetBSD: search.h,v 1.12 2016/02/16 15:53:48 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)search.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: search.h,v 1.8 2003/10/18 23:27:36 christos Exp $ - * $FreeBSD: src/lib/libedit/search.h,v 1.3.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/search.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -40,27 +41,25 @@ #ifndef _h_el_search #define _h_el_search -#include "histedit.h" - typedef struct el_search_t { - char *patbuf; /* The pattern buffer */ + Char *patbuf; /* The pattern buffer */ size_t patlen; /* Length of the pattern buffer */ int patdir; /* Direction of the last search */ int chadir; /* Character search direction */ - char chacha; /* Character we are looking for */ + Char chacha; /* Character we are looking for */ char chatflg; /* 0 if f, 1 if t */ } el_search_t; -protected int el_match(const char *, const char *); +protected int el_match(const Char *, const Char *); protected int search_init(EditLine *); protected void search_end(EditLine *); -protected int c_hmatch(EditLine *, const char *); +protected int c_hmatch(EditLine *, const Char *); protected void c_setpat(EditLine *); protected el_action_t ce_inc_search(EditLine *, int); protected el_action_t cv_search(EditLine *, int); protected el_action_t ce_search_line(EditLine *, int); -protected el_action_t cv_repeat_srch(EditLine *, int); -protected el_action_t cv_csearch(EditLine *, int, int, int, int); +protected el_action_t cv_repeat_srch(EditLine *, wint_t); +protected el_action_t cv_csearch(EditLine *, int, wint_t, int, int); #endif /* _h_el_search */ diff --git a/lib/libedit/sig.c b/lib/libedit/sig.c new file mode 100644 index 0000000..f7cb920 --- /dev/null +++ b/lib/libedit/sig.c @@ -0,0 +1,207 @@ +/* $NetBSD: sig.c,v 1.24 2016/02/16 19:08:41 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)sig.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: sig.c,v 1.24 2016/02/16 19:08:41 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/sig.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * sig.c: Signal handling stuff. + * our policy is to trap all signals, set a good state + * and pass the ball to our caller. + */ +#include +#include + +#include "el.h" +#include "common.h" + +private EditLine *sel = NULL; + +private const int sighdl[] = { +#define _DO(a) (a), + ALLSIGS +#undef _DO + - 1 +}; + +private void sig_handler(int); + +/* sig_handler(): + * This is the handler called for all signals + * XXX: we cannot pass any data so we just store the old editline + * state in a private variable + */ +private void +sig_handler(int signo) +{ + int i, save_errno; + sigset_t nset, oset; + + save_errno = errno; + (void) sigemptyset(&nset); + (void) sigaddset(&nset, signo); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + sel->el_signal->sig_no = signo; + + switch (signo) { + case SIGCONT: + tty_rawmode(sel); + if (ed_redisplay(sel, 0) == CC_REFRESH) + re_refresh(sel); + terminal__flush(sel); + break; + + case SIGWINCH: + el_resize(sel); + break; + + default: + tty_cookedmode(sel); + break; + } + + for (i = 0; sighdl[i] != -1; i++) + if (signo == sighdl[i]) + break; + + (void) sigaction(signo, &sel->el_signal->sig_action[i], NULL); + sel->el_signal->sig_action[i].sa_handler = SIG_ERR; + sel->el_signal->sig_action[i].sa_flags = 0; + sigemptyset(&sel->el_signal->sig_action[i].sa_mask); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + (void) kill(0, signo); + errno = save_errno; +} + + +/* sig_init(): + * Initialize all signal stuff + */ +protected int +sig_init(EditLine *el) +{ + size_t i; + sigset_t *nset, oset; + + el->el_signal = el_malloc(sizeof(*el->el_signal)); + if (el->el_signal == NULL) + return -1; + + nset = &el->el_signal->sig_set; + (void) sigemptyset(nset); +#define _DO(a) (void) sigaddset(nset, a); + ALLSIGS +#undef _DO + (void) sigprocmask(SIG_BLOCK, nset, &oset); + + for (i = 0; sighdl[i] != -1; i++) { + el->el_signal->sig_action[i].sa_handler = SIG_ERR; + el->el_signal->sig_action[i].sa_flags = 0; + sigemptyset(&el->el_signal->sig_action[i].sa_mask); + } + + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + + return 0; +} + + +/* sig_end(): + * Clear all signal stuff + */ +protected void +sig_end(EditLine *el) +{ + + el_free(el->el_signal); + el->el_signal = NULL; +} + + +/* sig_set(): + * set all the signal handlers + */ +protected void +sig_set(EditLine *el) +{ + size_t i; + sigset_t oset; + struct sigaction osa, nsa; + + nsa.sa_handler = sig_handler; + nsa.sa_flags = 0; + sigemptyset(&nsa.sa_mask); + + (void) sigprocmask(SIG_BLOCK, &el->el_signal->sig_set, &oset); + + for (i = 0; sighdl[i] != -1; i++) { + /* This could happen if we get interrupted */ + if (sigaction(sighdl[i], &nsa, &osa) != -1 && + osa.sa_handler != sig_handler) + el->el_signal->sig_action[i] = osa; + } + sel = el; + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +} + + +/* sig_clr(): + * clear all the signal handlers + */ +protected void +sig_clr(EditLine *el) +{ + size_t i; + sigset_t oset; + + (void) sigprocmask(SIG_BLOCK, &el->el_signal->sig_set, &oset); + + for (i = 0; sighdl[i] != -1; i++) + if (el->el_signal->sig_action[i].sa_handler != SIG_ERR) + (void)sigaction(sighdl[i], + &el->el_signal->sig_action[i], NULL); + + sel = NULL; /* we are going to die if the handler is + * called */ + (void)sigprocmask(SIG_SETMASK, &oset, NULL); +} diff --git a/lib/libedit/sig.h b/lib/libedit/sig.h index 23cb9ab..cd80ad9 100644 --- a/lib/libedit/sig.h +++ b/lib/libedit/sig.h @@ -1,3 +1,5 @@ +/* $NetBSD: sig.h,v 1.10 2016/02/16 15:53:48 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)sig.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: sig.h,v 1.5 2003/08/07 16:44:33 agc Exp $ - * $FreeBSD: src/lib/libedit/sig.h,v 1.2.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/sig.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -42,8 +43,6 @@ #include -#include "histedit.h" - /* * Define here all the signals we are going to handle * The _DO macro is used to iterate in the source code @@ -51,15 +50,18 @@ #define ALLSIGS \ _DO(SIGINT) \ _DO(SIGTSTP) \ - _DO(SIGSTOP) \ _DO(SIGQUIT) \ _DO(SIGHUP) \ _DO(SIGTERM) \ _DO(SIGCONT) \ _DO(SIGWINCH) +#define ALLSIGSNO 7 -typedef void (*el_signalhandler_t)(int); -typedef el_signalhandler_t *el_signal_t; +typedef struct { + struct sigaction sig_action[ALLSIGSNO]; + sigset_t sig_set; + volatile sig_atomic_t sig_no; +} *el_signal_t; protected void sig_end(EditLine*); protected int sig_init(EditLine*); diff --git a/lib/libedit/sys.h b/lib/libedit/sys.h index 162efae..08ceeb8 100644 --- a/lib/libedit/sys.h +++ b/lib/libedit/sys.h @@ -1,3 +1,5 @@ +/* $NetBSD: sys.h,v 1.23 2016/02/17 19:47:49 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)sys.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: sys.h,v 1.9 2004/01/17 17:57:40 christos Exp $ - * $FreeBSD: src/lib/libedit/sys.h,v 1.4.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/sys.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -40,7 +41,23 @@ #ifndef _h_sys #define _h_sys +#ifdef HAVE_SYS_CDEFS_H #include +#endif + +#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) +# define __attribute__(A) +#endif + +#ifndef __BEGIN_DECLS +# ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +# else +# define __BEGIN_DECLS +# define __END_DECLS +# endif +#endif #ifndef public # define public /* Externally visible functions/variables */ @@ -55,55 +72,58 @@ /* When we want to hide everything */ #endif -#ifndef _PTR_T -# define _PTR_T -typedef void *ptr_t; -#endif - -#ifndef _IOCTL_T -# define _IOCTL_T -typedef void *ioctl_t; +#ifndef __arraycount +# define __arraycount(a) (sizeof(a) / sizeof(*(a))) #endif #include +#ifndef HAVE_STRLCAT +#define strlcat libedit_strlcat +size_t strlcat(char *dst, const char *src, size_t size); +#endif + +#ifndef HAVE_STRLCPY +#define strlcpy libedit_strlcpy +size_t strlcpy(char *dst, const char *src, size_t size); +#endif + +#ifndef HAVE_GETLINE +#define getline libedit_getline +ssize_t getline(char **line, size_t *len, FILE *fp); +#endif + +#ifndef _DIAGASSERT +#define _DIAGASSERT(x) +#endif + +#ifndef __RCSID +#define __RCSID(x) +#endif + +#ifndef HAVE_U_INT32_T +typedef unsigned int u_int32_t; +#endif + +#ifndef HAVE_SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif + #define REGEX /* Use POSIX.2 regular expression functions */ #undef REGEXP /* Use UNIX V8 regular expression functions */ -#ifdef notdef -# undef REGEX -# undef REGEXP -# include -# ifdef __GNUC__ -/* - * Broken hdrs. - */ -extern int tgetent(const char *bp, char *name); -extern int tgetflag(const char *id); -extern int tgetnum(const char *id); -extern char *tgetstr(const char *id, char **area); -extern char *tgoto(const char *cap, int col, int row); -extern int tputs(const char *str, int affcnt, int (*putc)(int)); -extern char *getenv(const char *); -extern int fprintf(FILE *, const char *, ...); -extern int sigsetmask(int); -extern int sigblock(int); -extern int fputc(int, FILE *); -extern int fgetc(FILE *); -extern int fflush(FILE *); -extern int tolower(int); -extern int toupper(int); -extern int errno, sys_nerr; -extern char *sys_errlist[]; -extern void perror(const char *); -# include -# define strerror(e) sys_errlist[e] -# endif -# ifdef SABER -extern ptr_t memcpy(ptr_t, const ptr_t, size_t); -extern ptr_t memset(ptr_t, int, size_t); -# endif -extern char *fgetline(FILE *, int *); +#ifndef WIDECHAR +#define setlocale(c, l) /*LINTED*/NULL +#define nl_langinfo(i) "" +#endif + +#if defined(__sun) +extern int tgetent(char *, const char *); +extern int tgetflag(char *); +extern int tgetnum(char *); +extern int tputs(const char *, int, int (*)(int)); +extern char* tgoto(const char*, int, int); +extern char* tgetstr(char*, char**); #endif #endif /* _h_sys */ diff --git a/lib/libedit/term.h b/lib/libedit/term.h deleted file mode 100644 index 6e0301d..0000000 --- a/lib/libedit/term.h +++ /dev/null @@ -1,123 +0,0 @@ -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Christos Zoulas of Cornell University. - * - * 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. - * - * @(#)term.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: term.h,v 1.16 2005/03/15 00:10:40 christos Exp $ - * $FreeBSD: src/lib/libedit/term.h,v 1.5.14.1 2005/10/09 03:44:01 delphij Exp $ - */ - -/* - * el.term.h: Termcap header - */ -#ifndef _h_el_term -#define _h_el_term - -#include "histedit.h" - -typedef struct { /* Symbolic function key bindings */ - const char *name; /* name of the key */ - int key; /* Index in termcap table */ - key_value_t fun; /* Function bound to it */ - int type; /* Type of function */ -} fkey_t; - -typedef struct { - const char *t_name; /* the terminal name */ - coord_t t_size; /* # lines and cols */ - int t_flags; -#define TERM_CAN_INSERT 0x001 /* Has insert cap */ -#define TERM_CAN_DELETE 0x002 /* Has delete cap */ -#define TERM_CAN_CEOL 0x004 /* Has CEOL cap */ -#define TERM_CAN_TAB 0x008 /* Can use tabs */ -#define TERM_CAN_ME 0x010 /* Can turn all attrs. */ -#define TERM_CAN_UP 0x020 /* Can move up */ -#define TERM_HAS_META 0x040 /* Has a meta key */ -#define TERM_HAS_AUTO_MARGINS 0x080 /* Has auto margins */ -#define TERM_HAS_MAGIC_MARGINS 0x100 /* Has magic margins */ - char *t_buf; /* Termcap buffer */ - int t_loc; /* location used */ - char **t_str; /* termcap strings */ - int *t_val; /* termcap values */ - char *t_cap; /* Termcap buffer */ - fkey_t *t_fkey; /* Array of keys */ -} el_term_t; - -/* - * fKey indexes - */ -#define A_K_DN 0 -#define A_K_UP 1 -#define A_K_LT 2 -#define A_K_RT 3 -#define A_K_HO 4 -#define A_K_EN 5 -#define A_K_NKEYS 6 - -protected void term_move_to_line(EditLine *, int); -protected void term_move_to_char(EditLine *, int); -protected void term_clear_EOL(EditLine *, int); -protected void term_overwrite(EditLine *, const char *, int); -protected void term_insertwrite(EditLine *, char *, int); -protected void term_deletechars(EditLine *, int); -protected void term_clear_screen(EditLine *); -protected void term_beep(EditLine *); -protected int term_change_size(EditLine *, int, int); -protected int term_get_size(EditLine *, int *, int *); -protected int term_init(EditLine *); -protected void term_bind_arrow(EditLine *); -protected void term_print_arrow(EditLine *, const char *); -protected int term_clear_arrow(EditLine *, const char *); -protected int term_set_arrow(EditLine *, const char *, key_value_t *, int); -protected void term_end(EditLine *); -protected void term_get(EditLine *, const char **); -protected int term_set(EditLine *, const char *); -protected int term_settc(EditLine *, int, const char **); -protected int term_telltc(EditLine *, int, const char **); -protected int term_echotc(EditLine *, int, const char **); -protected int term__putc(int); -protected void term__flush(void); - -/* - * Easy access macros - */ -#define EL_FLAGS (el)->el_term.t_flags - -#define EL_CAN_INSERT (EL_FLAGS & TERM_CAN_INSERT) -#define EL_CAN_DELETE (EL_FLAGS & TERM_CAN_DELETE) -#define EL_CAN_CEOL (EL_FLAGS & TERM_CAN_CEOL) -#define EL_CAN_TAB (EL_FLAGS & TERM_CAN_TAB) -#define EL_CAN_ME (EL_FLAGS & TERM_CAN_ME) -#define EL_CAN_UP (EL_FLAGS & TERM_CAN_UP) -#define EL_HAS_META (EL_FLAGS & TERM_HAS_META) -#define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS) -#define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS) - -#endif /* _h_el_term */ diff --git a/lib/libedit/terminal.c b/lib/libedit/terminal.c new file mode 100644 index 0000000..7cac0b5 --- /dev/null +++ b/lib/libedit/terminal.c @@ -0,0 +1,1689 @@ +/* $NetBSD: terminal.c,v 1.24 2016/03/22 01:38:17 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)term.c 8.2 (Berkeley) 4/30/95"; +#else +__RCSID("$NetBSD: terminal.c,v 1.24 2016/03/22 01:38:17 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/terminal.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * terminal.c: Editor/termcap-curses interface + * We have to declare a static variable here, since the + * termcap putchar routine does not take an argument! + */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_TERMCAP_H +#include +#endif +#ifdef HAVE_CURSES_H +#include +#elif HAVE_NCURSES_H +#include +#endif + +/* Solaris's term.h does horrid things. */ +#if defined(HAVE_TERM_H) && !defined(__sun) && !defined(HAVE_TERMCAP_H) +#include +#endif + +#ifdef _REENTRANT +#include +#endif + +#include "el.h" + +/* + * IMPORTANT NOTE: these routines are allowed to look at the current screen + * and the current position assuming that it is correct. If this is not + * true, then the update will be WRONG! This is (should be) a valid + * assumption... + */ + +#define TC_BUFSIZE ((size_t)2048) + +#define GoodStr(a) (el->el_terminal.t_str[a] != NULL && \ + el->el_terminal.t_str[a][0] != '\0') +#define Str(a) el->el_terminal.t_str[a] +#define Val(a) el->el_terminal.t_val[a] + +private const struct termcapstr { + const char *name; + const char *long_name; +} tstr[] = { +#define T_al 0 + { "al", "add new blank line" }, +#define T_bl 1 + { "bl", "audible bell" }, +#define T_cd 2 + { "cd", "clear to bottom" }, +#define T_ce 3 + { "ce", "clear to end of line" }, +#define T_ch 4 + { "ch", "cursor to horiz pos" }, +#define T_cl 5 + { "cl", "clear screen" }, +#define T_dc 6 + { "dc", "delete a character" }, +#define T_dl 7 + { "dl", "delete a line" }, +#define T_dm 8 + { "dm", "start delete mode" }, +#define T_ed 9 + { "ed", "end delete mode" }, +#define T_ei 10 + { "ei", "end insert mode" }, +#define T_fs 11 + { "fs", "cursor from status line" }, +#define T_ho 12 + { "ho", "home cursor" }, +#define T_ic 13 + { "ic", "insert character" }, +#define T_im 14 + { "im", "start insert mode" }, +#define T_ip 15 + { "ip", "insert padding" }, +#define T_kd 16 + { "kd", "sends cursor down" }, +#define T_kl 17 + { "kl", "sends cursor left" }, +#define T_kr 18 + { "kr", "sends cursor right" }, +#define T_ku 19 + { "ku", "sends cursor up" }, +#define T_md 20 + { "md", "begin bold" }, +#define T_me 21 + { "me", "end attributes" }, +#define T_nd 22 + { "nd", "non destructive space" }, +#define T_se 23 + { "se", "end standout" }, +#define T_so 24 + { "so", "begin standout" }, +#define T_ts 25 + { "ts", "cursor to status line" }, +#define T_up 26 + { "up", "cursor up one" }, +#define T_us 27 + { "us", "begin underline" }, +#define T_ue 28 + { "ue", "end underline" }, +#define T_vb 29 + { "vb", "visible bell" }, +#define T_DC 30 + { "DC", "delete multiple chars" }, +#define T_DO 31 + { "DO", "cursor down multiple" }, +#define T_IC 32 + { "IC", "insert multiple chars" }, +#define T_LE 33 + { "LE", "cursor left multiple" }, +#define T_RI 34 + { "RI", "cursor right multiple" }, +#define T_UP 35 + { "UP", "cursor up multiple" }, +#define T_kh 36 + { "kh", "send cursor home" }, +#define T_at7 37 + { "@7", "send cursor end" }, +#define T_kD 38 + { "kD", "send cursor delete" }, +#define T_str 39 + { NULL, NULL } +}; + +private const struct termcapval { + const char *name; + const char *long_name; +} tval[] = { +#define T_am 0 + { "am", "has automatic margins" }, +#define T_pt 1 + { "pt", "has physical tabs" }, +#define T_li 2 + { "li", "Number of lines" }, +#define T_co 3 + { "co", "Number of columns" }, +#define T_km 4 + { "km", "Has meta key" }, +#define T_xt 5 + { "xt", "Tab chars destructive" }, +#define T_xn 6 + { "xn", "newline ignored at right margin" }, +#define T_MT 7 + { "MT", "Has meta key" }, /* XXX? */ +#define T_val 8 + { NULL, NULL, } +}; +/* do two or more of the attributes use me */ + +private void terminal_setflags(EditLine *); +private int terminal_rebuffer_display(EditLine *); +private void terminal_free_display(EditLine *); +private int terminal_alloc_display(EditLine *); +private void terminal_alloc(EditLine *, const struct termcapstr *, + const char *); +private void terminal_init_arrow(EditLine *); +private void terminal_reset_arrow(EditLine *); +private int terminal_putc(int); +private void terminal_tputs(EditLine *, const char *, int); + +#ifdef _REENTRANT +private pthread_mutex_t terminal_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +private FILE *terminal_outfile = NULL; + + +/* terminal_setflags(): + * Set the terminal capability flags + */ +private void +terminal_setflags(EditLine *el) +{ + EL_FLAGS = 0; + if (el->el_tty.t_tabs) + EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0; + + EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0; + EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0; + EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0; + EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ? + TERM_CAN_INSERT : 0; + EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0; + EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0; + EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0; + + if (GoodStr(T_me) && GoodStr(T_ue)) + EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ? + TERM_CAN_ME : 0; + else + EL_FLAGS &= ~TERM_CAN_ME; + if (GoodStr(T_me) && GoodStr(T_se)) + EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ? + TERM_CAN_ME : 0; + + +#ifdef DEBUG_SCREEN + if (!EL_CAN_UP) { + (void) fprintf(el->el_errfile, + "WARNING: Your terminal cannot move up.\n"); + (void) fprintf(el->el_errfile, + "Editing may be odd for long lines.\n"); + } + if (!EL_CAN_CEOL) + (void) fprintf(el->el_errfile, "no clear EOL capability.\n"); + if (!EL_CAN_DELETE) + (void) fprintf(el->el_errfile, "no delete char capability.\n"); + if (!EL_CAN_INSERT) + (void) fprintf(el->el_errfile, "no insert char capability.\n"); +#endif /* DEBUG_SCREEN */ +} + +/* terminal_init(): + * Initialize the terminal stuff + */ +protected int +terminal_init(EditLine *el) +{ + + el->el_terminal.t_buf = el_malloc(TC_BUFSIZE * + sizeof(*el->el_terminal.t_buf)); + if (el->el_terminal.t_buf == NULL) + goto fail1; + el->el_terminal.t_cap = el_malloc(TC_BUFSIZE * + sizeof(*el->el_terminal.t_cap)); + if (el->el_terminal.t_cap == NULL) + goto fail2; + el->el_terminal.t_fkey = el_malloc(A_K_NKEYS * + sizeof(*el->el_terminal.t_fkey)); + if (el->el_terminal.t_fkey == NULL) + goto fail3; + el->el_terminal.t_loc = 0; + el->el_terminal.t_str = el_malloc(T_str * + sizeof(*el->el_terminal.t_str)); + if (el->el_terminal.t_str == NULL) + goto fail4; + (void) memset(el->el_terminal.t_str, 0, T_str * + sizeof(*el->el_terminal.t_str)); + el->el_terminal.t_val = el_malloc(T_val * + sizeof(*el->el_terminal.t_val)); + if (el->el_terminal.t_val == NULL) + goto fail5; + (void) memset(el->el_terminal.t_val, 0, T_val * + sizeof(*el->el_terminal.t_val)); + (void) terminal_set(el, NULL); + terminal_init_arrow(el); + return 0; +fail5: + free(el->el_terminal.t_str); + el->el_terminal.t_str = NULL; +fail4: + free(el->el_terminal.t_fkey); + el->el_terminal.t_fkey = NULL; +fail3: + free(el->el_terminal.t_cap); + el->el_terminal.t_cap = NULL; +fail2: + free(el->el_terminal.t_buf); + el->el_terminal.t_buf = NULL; +fail1: + return -1; +} + +/* terminal_end(): + * Clean up the terminal stuff + */ +protected void +terminal_end(EditLine *el) +{ + + el_free(el->el_terminal.t_buf); + el->el_terminal.t_buf = NULL; + el_free(el->el_terminal.t_cap); + el->el_terminal.t_cap = NULL; + el->el_terminal.t_loc = 0; + el_free(el->el_terminal.t_str); + el->el_terminal.t_str = NULL; + el_free(el->el_terminal.t_val); + el->el_terminal.t_val = NULL; + el_free(el->el_terminal.t_fkey); + el->el_terminal.t_fkey = NULL; + terminal_free_display(el); +} + + +/* terminal_alloc(): + * Maintain a string pool for termcap strings + */ +private void +terminal_alloc(EditLine *el, const struct termcapstr *t, const char *cap) +{ + char termbuf[TC_BUFSIZE]; + size_t tlen, clen; + char **tlist = el->el_terminal.t_str; + char **tmp, **str = &tlist[t - tstr]; + + (void) memset(termbuf, 0, sizeof(termbuf)); + if (cap == NULL || *cap == '\0') { + *str = NULL; + return; + } else + clen = strlen(cap); + + tlen = *str == NULL ? 0 : strlen(*str); + + /* + * New string is shorter; no need to allocate space + */ + if (clen <= tlen) { + if (*str) + (void) strcpy(*str, cap); /* XXX strcpy is safe */ + return; + } + /* + * New string is longer; see if we have enough space to append + */ + if (el->el_terminal.t_loc + 3 < TC_BUFSIZE) { + /* XXX strcpy is safe */ + (void) strcpy(*str = &el->el_terminal.t_buf[ + el->el_terminal.t_loc], cap); + el->el_terminal.t_loc += clen + 1; /* one for \0 */ + return; + } + /* + * Compact our buffer; no need to check compaction, cause we know it + * fits... + */ + tlen = 0; + for (tmp = tlist; tmp < &tlist[T_str]; tmp++) + if (*tmp != NULL && **tmp != '\0' && *tmp != *str) { + char *ptr; + + for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++) + continue; + termbuf[tlen++] = '\0'; + } + memcpy(el->el_terminal.t_buf, termbuf, TC_BUFSIZE); + el->el_terminal.t_loc = tlen; + if (el->el_terminal.t_loc + 3 >= TC_BUFSIZE) { + (void) fprintf(el->el_errfile, + "Out of termcap string space.\n"); + return; + } + /* XXX strcpy is safe */ + (void) strcpy(*str = &el->el_terminal.t_buf[el->el_terminal.t_loc], + cap); + el->el_terminal.t_loc += (size_t)clen + 1; /* one for \0 */ + return; +} + + +/* terminal_rebuffer_display(): + * Rebuffer the display after the screen changed size + */ +private int +terminal_rebuffer_display(EditLine *el) +{ + coord_t *c = &el->el_terminal.t_size; + + terminal_free_display(el); + + c->h = Val(T_co); + c->v = Val(T_li); + + if (terminal_alloc_display(el) == -1) + return -1; + return 0; +} + + +/* terminal_alloc_display(): + * Allocate a new display. + */ +private int +terminal_alloc_display(EditLine *el) +{ + int i; + Char **b; + coord_t *c = &el->el_terminal.t_size; + + b = el_malloc(sizeof(*b) * (size_t)(c->v + 1)); + if (b == NULL) + goto done; + for (i = 0; i < c->v; i++) { + b[i] = el_malloc(sizeof(**b) * (size_t)(c->h + 1)); + if (b[i] == NULL) { + while (--i >= 0) + el_free(b[i]); + el_free(b); + goto done; + } + } + b[c->v] = NULL; + el->el_display = b; + + b = el_malloc(sizeof(*b) * (size_t)(c->v + 1)); + if (b == NULL) + goto done; + for (i = 0; i < c->v; i++) { + b[i] = el_malloc(sizeof(**b) * (size_t)(c->h + 1)); + if (b[i] == NULL) { + while (--i >= 0) + el_free(b[i]); + el_free(b); + goto done; + } + } + b[c->v] = NULL; + el->el_vdisplay = b; + return 0; +done: + terminal_free_display(el); + return -1; +} + + +/* terminal_free_display(): + * Free the display buffers + */ +private void +terminal_free_display(EditLine *el) +{ + Char **b; + Char **bufp; + + b = el->el_display; + el->el_display = NULL; + if (b != NULL) { + for (bufp = b; *bufp != NULL; bufp++) + el_free(*bufp); + el_free(b); + } + b = el->el_vdisplay; + el->el_vdisplay = NULL; + if (b != NULL) { + for (bufp = b; *bufp != NULL; bufp++) + el_free(*bufp); + el_free(b); + } +} + + +/* terminal_move_to_line(): + * move to line (first line == 0) + * as efficiently as possible + */ +protected void +terminal_move_to_line(EditLine *el, int where) +{ + int del; + + if (where == el->el_cursor.v) + return; + + if (where > el->el_terminal.t_size.v) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "%s: where is ridiculous: %d\r\n", __func__, where); +#endif /* DEBUG_SCREEN */ + return; + } + if ((del = where - el->el_cursor.v) > 0) { + while (del > 0) { + if (EL_HAS_AUTO_MARGINS && + el->el_display[el->el_cursor.v][0] != '\0') { + size_t h = (size_t) + (el->el_terminal.t_size.h - 1); +#ifdef WIDECHAR + for (; h > 0 && + el->el_display[el->el_cursor.v][h] == + MB_FILL_CHAR; + h--) + continue; +#endif + /* move without newline */ + terminal_move_to_char(el, (int)h); + terminal_overwrite(el, &el->el_display + [el->el_cursor.v][el->el_cursor.h], + (size_t)(el->el_terminal.t_size.h - + el->el_cursor.h)); + /* updates Cursor */ + del--; + } else { + if ((del > 1) && GoodStr(T_DO)) { + terminal_tputs(el, tgoto(Str(T_DO), del, + del), del); + del = 0; + } else { + for (; del > 0; del--) + terminal__putc(el, '\n'); + /* because the \n will become \r\n */ + el->el_cursor.h = 0; + } + } + } + } else { /* del < 0 */ + if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up))) + terminal_tputs(el, tgoto(Str(T_UP), -del, -del), -del); + else { + if (GoodStr(T_up)) + for (; del < 0; del++) + terminal_tputs(el, Str(T_up), 1); + } + } + el->el_cursor.v = where;/* now where is here */ +} + + +/* terminal_move_to_char(): + * Move to the character position specified + */ +protected void +terminal_move_to_char(EditLine *el, int where) +{ + int del, i; + +mc_again: + if (where == el->el_cursor.h) + return; + + if (where > el->el_terminal.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "%s: where is ridiculous: %d\r\n", __func__, where); +#endif /* DEBUG_SCREEN */ + return; + } + if (!where) { /* if where is first column */ + terminal__putc(el, '\r'); /* do a CR */ + el->el_cursor.h = 0; + return; + } + del = where - el->el_cursor.h; + + if ((del < -4 || del > 4) && GoodStr(T_ch)) + /* go there directly */ + terminal_tputs(el, tgoto(Str(T_ch), where, where), where); + else { + if (del > 0) { /* moving forward */ + if ((del > 4) && GoodStr(T_RI)) + terminal_tputs(el, tgoto(Str(T_RI), del, del), + del); + else { + /* if I can do tabs, use them */ + if (EL_CAN_TAB) { + if ((el->el_cursor.h & 0370) != + (where & ~0x7) +#ifdef WIDECHAR + && (el->el_display[ + el->el_cursor.v][where & 0370] != + MB_FILL_CHAR) +#endif + ) { + /* if not within tab stop */ + for (i = + (el->el_cursor.h & 0370); + i < (where & ~0x7); + i += 8) + terminal__putc(el, + '\t'); + /* then tab over */ + el->el_cursor.h = where & ~0x7; + } + } + /* + * it's usually cheaper to just write the + * chars, so we do. + */ + /* + * NOTE THAT terminal_overwrite() WILL CHANGE + * el->el_cursor.h!!! + */ + terminal_overwrite(el, &el->el_display[ + el->el_cursor.v][el->el_cursor.h], + (size_t)(where - el->el_cursor.h)); + + } + } else { /* del < 0 := moving backward */ + if ((-del > 4) && GoodStr(T_LE)) + terminal_tputs(el, tgoto(Str(T_LE), -del, -del), + -del); + else { /* can't go directly there */ + /* + * if the "cost" is greater than the "cost" + * from col 0 + */ + if (EL_CAN_TAB ? + ((unsigned int)-del > + (((unsigned int) where >> 3) + + (where & 07))) + : (-del > where)) { + terminal__putc(el, '\r');/* do a CR */ + el->el_cursor.h = 0; + goto mc_again; /* and try again */ + } + for (i = 0; i < -del; i++) + terminal__putc(el, '\b'); + } + } + } + el->el_cursor.h = where; /* now where is here */ +} + + +/* terminal_overwrite(): + * Overstrike num characters + * Assumes MB_FILL_CHARs are present to keep the column count correct + */ +protected void +terminal_overwrite(EditLine *el, const Char *cp, size_t n) +{ + if (n == 0) + return; + + if (n > (size_t)el->el_terminal.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "%s: n is ridiculous: %zu\r\n", __func__, n); +#endif /* DEBUG_SCREEN */ + return; + } + + do { + /* terminal__putc() ignores any MB_FILL_CHARs */ + terminal__putc(el, *cp++); + el->el_cursor.h++; + } while (--n); + + if (el->el_cursor.h >= el->el_terminal.t_size.h) { /* wrap? */ + if (EL_HAS_AUTO_MARGINS) { /* yes */ + el->el_cursor.h = 0; + el->el_cursor.v++; + if (EL_HAS_MAGIC_MARGINS) { + /* force the wrap to avoid the "magic" + * situation */ + Char c; + if ((c = el->el_display[el->el_cursor.v] + [el->el_cursor.h]) != '\0') { + terminal_overwrite(el, &c, (size_t)1); +#ifdef WIDECHAR + while (el->el_display[el->el_cursor.v] + [el->el_cursor.h] == MB_FILL_CHAR) + el->el_cursor.h++; +#endif + } else { + terminal__putc(el, ' '); + el->el_cursor.h = 1; + } + } + } else /* no wrap, but cursor stays on screen */ + el->el_cursor.h = el->el_terminal.t_size.h - 1; + } +} + + +/* terminal_deletechars(): + * Delete num characters + */ +protected void +terminal_deletechars(EditLine *el, int num) +{ + if (num <= 0) + return; + + if (!EL_CAN_DELETE) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, " ERROR: cannot delete \n"); +#endif /* DEBUG_EDIT */ + return; + } + if (num > el->el_terminal.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "%s: num is ridiculous: %d\r\n", __func__, num); +#endif /* DEBUG_SCREEN */ + return; + } + if (GoodStr(T_DC)) /* if I have multiple delete */ + if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more + * expen. */ + terminal_tputs(el, tgoto(Str(T_DC), num, num), num); + return; + } + if (GoodStr(T_dm)) /* if I have delete mode */ + terminal_tputs(el, Str(T_dm), 1); + + if (GoodStr(T_dc)) /* else do one at a time */ + while (num--) + terminal_tputs(el, Str(T_dc), 1); + + if (GoodStr(T_ed)) /* if I have delete mode */ + terminal_tputs(el, Str(T_ed), 1); +} + + +/* terminal_insertwrite(): + * Puts terminal in insert character mode or inserts num + * characters in the line + * Assumes MB_FILL_CHARs are present to keep column count correct + */ +protected void +terminal_insertwrite(EditLine *el, Char *cp, int num) +{ + if (num <= 0) + return; + if (!EL_CAN_INSERT) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, " ERROR: cannot insert \n"); +#endif /* DEBUG_EDIT */ + return; + } + if (num > el->el_terminal.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "%s: num is ridiculous: %d\r\n", __func__, num); +#endif /* DEBUG_SCREEN */ + return; + } + if (GoodStr(T_IC)) /* if I have multiple insert */ + if ((num > 1) || !GoodStr(T_ic)) { + /* if ic would be more expensive */ + terminal_tputs(el, tgoto(Str(T_IC), num, num), num); + terminal_overwrite(el, cp, (size_t)num); + /* this updates el_cursor.h */ + return; + } + if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */ + terminal_tputs(el, Str(T_im), 1); + + el->el_cursor.h += num; + do + terminal__putc(el, *cp++); + while (--num); + + if (GoodStr(T_ip)) /* have to make num chars insert */ + terminal_tputs(el, Str(T_ip), 1); + + terminal_tputs(el, Str(T_ei), 1); + return; + } + do { + if (GoodStr(T_ic)) /* have to make num chars insert */ + terminal_tputs(el, Str(T_ic), 1); + + terminal__putc(el, *cp++); + + el->el_cursor.h++; + + if (GoodStr(T_ip)) /* have to make num chars insert */ + terminal_tputs(el, Str(T_ip), 1); + /* pad the inserted char */ + + } while (--num); +} + + +/* terminal_clear_EOL(): + * clear to end of line. There are num characters to clear + */ +protected void +terminal_clear_EOL(EditLine *el, int num) +{ + int i; + + if (EL_CAN_CEOL && GoodStr(T_ce)) + terminal_tputs(el, Str(T_ce), 1); + else { + for (i = 0; i < num; i++) + terminal__putc(el, ' '); + el->el_cursor.h += num; /* have written num spaces */ + } +} + + +/* terminal_clear_screen(): + * Clear the screen + */ +protected void +terminal_clear_screen(EditLine *el) +{ /* clear the whole screen and home */ + + if (GoodStr(T_cl)) + /* send the clear screen code */ + terminal_tputs(el, Str(T_cl), Val(T_li)); + else if (GoodStr(T_ho) && GoodStr(T_cd)) { + terminal_tputs(el, Str(T_ho), Val(T_li)); /* home */ + /* clear to bottom of screen */ + terminal_tputs(el, Str(T_cd), Val(T_li)); + } else { + terminal__putc(el, '\r'); + terminal__putc(el, '\n'); + } +} + + +/* terminal_beep(): + * Beep the way the terminal wants us + */ +protected void +terminal_beep(EditLine *el) +{ + if (GoodStr(T_bl)) + /* what termcap says we should use */ + terminal_tputs(el, Str(T_bl), 1); + else + terminal__putc(el, '\007'); /* an ASCII bell; ^G */ +} + + +protected void +terminal_get(EditLine *el, const char **term) +{ + *term = el->el_terminal.t_name; +} + + +/* terminal_set(): + * Read in the terminal capabilities from the requested terminal + */ +protected int +terminal_set(EditLine *el, const char *term) +{ + int i; + char buf[TC_BUFSIZE]; + char *area; + const struct termcapstr *t; + sigset_t oset, nset; + int lins, cols; + + (void) sigemptyset(&nset); + (void) sigaddset(&nset, SIGWINCH); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + area = buf; + + + if (term == NULL) + term = getenv("TERM"); + + if (!term || !term[0]) + term = "dumb"; + + if (strcmp(term, "emacs") == 0) + el->el_flags |= EDIT_DISABLED; + + (void) memset(el->el_terminal.t_cap, 0, TC_BUFSIZE); + + i = tgetent(el->el_terminal.t_cap, term); + + if (i <= 0) { + if (i == -1) + (void) fprintf(el->el_errfile, + "Cannot read termcap database;\n"); + else if (i == 0) + (void) fprintf(el->el_errfile, + "No entry for terminal type \"%s\";\n", term); + (void) fprintf(el->el_errfile, + "using dumb terminal settings.\n"); + Val(T_co) = 80; /* do a dumb terminal */ + Val(T_pt) = Val(T_km) = Val(T_li) = 0; + Val(T_xt) = Val(T_MT); + for (t = tstr; t->name != NULL; t++) + terminal_alloc(el, t, NULL); + } else { + /* auto/magic margins */ + Val(T_am) = tgetflag("am"); + Val(T_xn) = tgetflag("xn"); + /* Can we tab */ + Val(T_pt) = tgetflag("pt"); + Val(T_xt) = tgetflag("xt"); + /* do we have a meta? */ + Val(T_km) = tgetflag("km"); + Val(T_MT) = tgetflag("MT"); + /* Get the size */ + Val(T_co) = tgetnum("co"); + Val(T_li) = tgetnum("li"); + for (t = tstr; t->name != NULL; t++) { + /* XXX: some systems' tgetstr needs non const */ + terminal_alloc(el, t, tgetstr(strchr(t->name, *t->name), + &area)); + } + } + + if (Val(T_co) < 2) + Val(T_co) = 80; /* just in case */ + if (Val(T_li) < 1) + Val(T_li) = 24; + + el->el_terminal.t_size.v = Val(T_co); + el->el_terminal.t_size.h = Val(T_li); + + terminal_setflags(el); + + /* get the correct window size */ + (void) terminal_get_size(el, &lins, &cols); + if (terminal_change_size(el, lins, cols) == -1) + return -1; + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + terminal_bind_arrow(el); + el->el_terminal.t_name = term; + return i <= 0 ? -1 : 0; +} + + +/* terminal_get_size(): + * Return the new window size in lines and cols, and + * true if the size was changed. + */ +protected int +terminal_get_size(EditLine *el, int *lins, int *cols) +{ + + *cols = Val(T_co); + *lins = Val(T_li); + +#ifdef TIOCGWINSZ + { + struct winsize ws; + if (ioctl(el->el_infd, TIOCGWINSZ, &ws) != -1) { + if (ws.ws_col) + *cols = ws.ws_col; + if (ws.ws_row) + *lins = ws.ws_row; + } + } +#endif +#ifdef TIOCGSIZE + { + struct ttysize ts; + if (ioctl(el->el_infd, TIOCGSIZE, &ts) != -1) { + if (ts.ts_cols) + *cols = ts.ts_cols; + if (ts.ts_lines) + *lins = ts.ts_lines; + } + } +#endif + return Val(T_co) != *cols || Val(T_li) != *lins; +} + + +/* terminal_change_size(): + * Change the size of the terminal + */ +protected int +terminal_change_size(EditLine *el, int lins, int cols) +{ + /* + * Just in case + */ + Val(T_co) = (cols < 2) ? 80 : cols; + Val(T_li) = (lins < 1) ? 24 : lins; + + /* re-make display buffers */ + if (terminal_rebuffer_display(el) == -1) + return -1; + re_clear_display(el); + return 0; +} + + +/* terminal_init_arrow(): + * Initialize the arrow key bindings from termcap + */ +private void +terminal_init_arrow(EditLine *el) +{ + funckey_t *arrow = el->el_terminal.t_fkey; + + arrow[A_K_DN].name = STR("down"); + arrow[A_K_DN].key = T_kd; + arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY; + arrow[A_K_DN].type = XK_CMD; + + arrow[A_K_UP].name = STR("up"); + arrow[A_K_UP].key = T_ku; + arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY; + arrow[A_K_UP].type = XK_CMD; + + arrow[A_K_LT].name = STR("left"); + arrow[A_K_LT].key = T_kl; + arrow[A_K_LT].fun.cmd = ED_PREV_CHAR; + arrow[A_K_LT].type = XK_CMD; + + arrow[A_K_RT].name = STR("right"); + arrow[A_K_RT].key = T_kr; + arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR; + arrow[A_K_RT].type = XK_CMD; + + arrow[A_K_HO].name = STR("home"); + arrow[A_K_HO].key = T_kh; + arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG; + arrow[A_K_HO].type = XK_CMD; + + arrow[A_K_EN].name = STR("end"); + arrow[A_K_EN].key = T_at7; + arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END; + arrow[A_K_EN].type = XK_CMD; + + arrow[A_K_DE].name = STR("delete"); + arrow[A_K_DE].key = T_kD; + arrow[A_K_DE].fun.cmd = ED_DELETE_NEXT_CHAR; + arrow[A_K_DE].type = XK_CMD; +} + + +/* terminal_reset_arrow(): + * Reset arrow key bindings + */ +private void +terminal_reset_arrow(EditLine *el) +{ + funckey_t *arrow = el->el_terminal.t_fkey; + static const Char strA[] = {033, '[', 'A', '\0'}; + static const Char strB[] = {033, '[', 'B', '\0'}; + static const Char strC[] = {033, '[', 'C', '\0'}; + static const Char strD[] = {033, '[', 'D', '\0'}; + static const Char strH[] = {033, '[', 'H', '\0'}; + static const Char strF[] = {033, '[', 'F', '\0'}; + static const Char stOA[] = {033, 'O', 'A', '\0'}; + static const Char stOB[] = {033, 'O', 'B', '\0'}; + static const Char stOC[] = {033, 'O', 'C', '\0'}; + static const Char stOD[] = {033, 'O', 'D', '\0'}; + static const Char stOH[] = {033, 'O', 'H', '\0'}; + static const Char stOF[] = {033, 'O', 'F', '\0'}; + + keymacro_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); + keymacro_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); + keymacro_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); + keymacro_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); + keymacro_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type); + keymacro_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type); + keymacro_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); + keymacro_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); + keymacro_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); + keymacro_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); + keymacro_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type); + keymacro_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type); + + if (el->el_map.type != MAP_VI) + return; + keymacro_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); + keymacro_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); + keymacro_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); + keymacro_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); + keymacro_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type); + keymacro_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type); + keymacro_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); + keymacro_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); + keymacro_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); + keymacro_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); + keymacro_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type); + keymacro_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type); +} + + +/* terminal_set_arrow(): + * Set an arrow key binding + */ +protected int +terminal_set_arrow(EditLine *el, const Char *name, keymacro_value_t *fun, + int type) +{ + funckey_t *arrow = el->el_terminal.t_fkey; + int i; + + for (i = 0; i < A_K_NKEYS; i++) + if (Strcmp(name, arrow[i].name) == 0) { + arrow[i].fun = *fun; + arrow[i].type = type; + return 0; + } + return -1; +} + + +/* terminal_clear_arrow(): + * Clear an arrow key binding + */ +protected int +terminal_clear_arrow(EditLine *el, const Char *name) +{ + funckey_t *arrow = el->el_terminal.t_fkey; + int i; + + for (i = 0; i < A_K_NKEYS; i++) + if (Strcmp(name, arrow[i].name) == 0) { + arrow[i].type = XK_NOD; + return 0; + } + return -1; +} + + +/* terminal_print_arrow(): + * Print the arrow key bindings + */ +protected void +terminal_print_arrow(EditLine *el, const Char *name) +{ + int i; + funckey_t *arrow = el->el_terminal.t_fkey; + + for (i = 0; i < A_K_NKEYS; i++) + if (*name == '\0' || Strcmp(name, arrow[i].name) == 0) + if (arrow[i].type != XK_NOD) + keymacro_kprint(el, arrow[i].name, + &arrow[i].fun, arrow[i].type); +} + + +/* terminal_bind_arrow(): + * Bind the arrow keys + */ +protected void +terminal_bind_arrow(EditLine *el) +{ + el_action_t *map; + const el_action_t *dmap; + int i, j; + char *p; + funckey_t *arrow = el->el_terminal.t_fkey; + + /* Check if the components needed are initialized */ + if (el->el_terminal.t_buf == NULL || el->el_map.key == NULL) + return; + + map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key; + dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs; + + terminal_reset_arrow(el); + + for (i = 0; i < A_K_NKEYS; i++) { + Char wt_str[VISUAL_WIDTH_MAX]; + Char *px; + size_t n; + + p = el->el_terminal.t_str[arrow[i].key]; + if (!p || !*p) + continue; + for (n = 0; n < VISUAL_WIDTH_MAX && p[n]; ++n) + wt_str[n] = p[n]; + while (n < VISUAL_WIDTH_MAX) + wt_str[n++] = '\0'; + px = wt_str; + j = (unsigned char) *p; + /* + * Assign the arrow keys only if: + * + * 1. They are multi-character arrow keys and the user + * has not re-assigned the leading character, or + * has re-assigned the leading character to be + * ED_SEQUENCE_LEAD_IN + * 2. They are single arrow keys pointing to an + * unassigned key. + */ + if (arrow[i].type == XK_NOD) + keymacro_clear(el, map, px); + else { + if (p[1] && (dmap[j] == map[j] || + map[j] == ED_SEQUENCE_LEAD_IN)) { + keymacro_add(el, px, &arrow[i].fun, + arrow[i].type); + map[j] = ED_SEQUENCE_LEAD_IN; + } else if (map[j] == ED_UNASSIGNED) { + keymacro_clear(el, map, px); + if (arrow[i].type == XK_CMD) + map[j] = arrow[i].fun.cmd; + else + keymacro_add(el, px, &arrow[i].fun, + arrow[i].type); + } + } + } +} + +/* terminal_putc(): + * Add a character + */ +private int +terminal_putc(int c) +{ + if (terminal_outfile == NULL) + return -1; + return fputc(c, terminal_outfile); +} + +private void +terminal_tputs(EditLine *el, const char *cap, int affcnt) +{ +#ifdef _REENTRANT + pthread_mutex_lock(&terminal_mutex); +#endif + terminal_outfile = el->el_outfile; + (void)tputs(cap, affcnt, terminal_putc); +#ifdef _REENTRANT + pthread_mutex_unlock(&terminal_mutex); +#endif +} + +/* terminal__putc(): + * Add a character + */ +protected int +terminal__putc(EditLine *el, wint_t c) +{ + char buf[MB_LEN_MAX +1]; + ssize_t i; + if (c == (wint_t)MB_FILL_CHAR) + return 0; + i = ct_encode_char(buf, (size_t)MB_LEN_MAX, (Char)c); + if (i <= 0) + return (int)i; + buf[i] = '\0'; + return fputs(buf, el->el_outfile); +} + +/* terminal__flush(): + * Flush output + */ +protected void +terminal__flush(EditLine *el) +{ + + (void) fflush(el->el_outfile); +} + +/* terminal_writec(): + * Write the given character out, in a human readable form + */ +protected void +terminal_writec(EditLine *el, wint_t c) +{ + Char visbuf[VISUAL_WIDTH_MAX +1]; + ssize_t vcnt = ct_visual_char(visbuf, VISUAL_WIDTH_MAX, (Char)c); + if (vcnt < 0) + vcnt = 0; + visbuf[vcnt] = '\0'; + terminal_overwrite(el, visbuf, (size_t)vcnt); + terminal__flush(el); +} + + +/* terminal_telltc(): + * Print the current termcap characteristics + */ +protected int +/*ARGSUSED*/ +terminal_telltc(EditLine *el, int argc __attribute__((__unused__)), + const Char **argv __attribute__((__unused__))) +{ + const struct termcapstr *t; + char **ts; + + (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n"); + (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n"); + (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n", + Val(T_co), Val(T_li)); + (void) fprintf(el->el_outfile, + "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no"); + (void) fprintf(el->el_outfile, + "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not "); + (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n", + EL_HAS_AUTO_MARGINS ? "has" : "does not have"); + if (EL_HAS_AUTO_MARGINS) + (void) fprintf(el->el_outfile, "\tIt %s magic margins\n", + EL_HAS_MAGIC_MARGINS ? "has" : "does not have"); + + for (t = tstr, ts = el->el_terminal.t_str; t->name != NULL; t++, ts++) { + const char *ub; + if (*ts && **ts) { + ub = ct_encode_string(ct_visual_string( + ct_decode_string(*ts, &el->el_scratch)), + &el->el_scratch); + } else { + ub = "(empty)"; + } + (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n", + t->long_name, t->name, ub); + } + (void) fputc('\n', el->el_outfile); + return 0; +} + + +/* terminal_settc(): + * Change the current terminal characteristics + */ +protected int +/*ARGSUSED*/ +terminal_settc(EditLine *el, int argc __attribute__((__unused__)), + const Char **argv) +{ + const struct termcapstr *ts; + const struct termcapval *tv; + char what[8], how[8]; + + if (argv == NULL || argv[1] == NULL || argv[2] == NULL) + return -1; + + strncpy(what, ct_encode_string(argv[1], &el->el_scratch), sizeof(what)); + what[sizeof(what) - 1] = '\0'; + strncpy(how, ct_encode_string(argv[2], &el->el_scratch), sizeof(how)); + how[sizeof(how) - 1] = '\0'; + + /* + * Do the strings first + */ + for (ts = tstr; ts->name != NULL; ts++) + if (strcmp(ts->name, what) == 0) + break; + + if (ts->name != NULL) { + terminal_alloc(el, ts, how); + terminal_setflags(el); + return 0; + } + /* + * Do the numeric ones second + */ + for (tv = tval; tv->name != NULL; tv++) + if (strcmp(tv->name, what) == 0) + break; + + if (tv->name != NULL) + return -1; + + if (tv == &tval[T_pt] || tv == &tval[T_km] || + tv == &tval[T_am] || tv == &tval[T_xn]) { + if (strcmp(how, "yes") == 0) + el->el_terminal.t_val[tv - tval] = 1; + else if (strcmp(how, "no") == 0) + el->el_terminal.t_val[tv - tval] = 0; + else { + (void) fprintf(el->el_errfile, + "" FSTR ": Bad value `%s'.\n", argv[0], how); + return -1; + } + terminal_setflags(el); + if (terminal_change_size(el, Val(T_li), Val(T_co)) == -1) + return -1; + return 0; + } else { + long i; + char *ep; + + i = strtol(how, &ep, 10); + if (*ep != '\0') { + (void) fprintf(el->el_errfile, + "" FSTR ": Bad value `%s'.\n", argv[0], how); + return -1; + } + el->el_terminal.t_val[tv - tval] = (int) i; + el->el_terminal.t_size.v = Val(T_co); + el->el_terminal.t_size.h = Val(T_li); + if (tv == &tval[T_co] || tv == &tval[T_li]) + if (terminal_change_size(el, Val(T_li), Val(T_co)) + == -1) + return -1; + return 0; + } +} + + +/* terminal_gettc(): + * Get the current terminal characteristics + */ +protected int +/*ARGSUSED*/ +terminal_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv) +{ + const struct termcapstr *ts; + const struct termcapval *tv; + char *what; + void *how; + + if (argv == NULL || argv[1] == NULL || argv[2] == NULL) + return -1; + + what = argv[1]; + how = argv[2]; + + /* + * Do the strings first + */ + for (ts = tstr; ts->name != NULL; ts++) + if (strcmp(ts->name, what) == 0) + break; + + if (ts->name != NULL) { + *(char **)how = el->el_terminal.t_str[ts - tstr]; + return 0; + } + /* + * Do the numeric ones second + */ + for (tv = tval; tv->name != NULL; tv++) + if (strcmp(tv->name, what) == 0) + break; + + if (tv->name == NULL) + return -1; + + if (tv == &tval[T_pt] || tv == &tval[T_km] || + tv == &tval[T_am] || tv == &tval[T_xn]) { + static char yes[] = "yes"; + static char no[] = "no"; + if (el->el_terminal.t_val[tv - tval]) + *(char **)how = yes; + else + *(char **)how = no; + return 0; + } else { + *(int *)how = el->el_terminal.t_val[tv - tval]; + return 0; + } +} + +/* terminal_echotc(): + * Print the termcap string out with variable substitution + */ +protected int +/*ARGSUSED*/ +terminal_echotc(EditLine *el, int argc __attribute__((__unused__)), + const Char **argv) +{ + char *cap, *scap; + Char *ep; + int arg_need, arg_cols, arg_rows; + int verbose = 0, silent = 0; + char *area; + static const char fmts[] = "%s\n", fmtd[] = "%d\n"; + const struct termcapstr *t; + char buf[TC_BUFSIZE]; + long i; + + area = buf; + + if (argv == NULL || argv[1] == NULL) + return -1; + argv++; + + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'v': + verbose = 1; + break; + case 's': + silent = 1; + break; + default: + /* stderror(ERR_NAME | ERR_TCUSAGE); */ + break; + } + argv++; + } + if (!*argv || *argv[0] == '\0') + return 0; + if (Strcmp(*argv, STR("tabs")) == 0) { + (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no"); + return 0; + } else if (Strcmp(*argv, STR("meta")) == 0) { + (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no"); + return 0; + } else if (Strcmp(*argv, STR("xn")) == 0) { + (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ? + "yes" : "no"); + return 0; + } else if (Strcmp(*argv, STR("am")) == 0) { + (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ? + "yes" : "no"); + return 0; + } else if (Strcmp(*argv, STR("baud")) == 0) { + (void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed); + return 0; + } else if (Strcmp(*argv, STR("rows")) == 0 || + Strcmp(*argv, STR("lines")) == 0) { + (void) fprintf(el->el_outfile, fmtd, Val(T_li)); + return 0; + } else if (Strcmp(*argv, STR("cols")) == 0) { + (void) fprintf(el->el_outfile, fmtd, Val(T_co)); + return 0; + } + /* + * Try to use our local definition first + */ + scap = NULL; + for (t = tstr; t->name != NULL; t++) + if (strcmp(t->name, + ct_encode_string(*argv, &el->el_scratch)) == 0) { + scap = el->el_terminal.t_str[t - tstr]; + break; + } + if (t->name == NULL) { + /* XXX: some systems' tgetstr needs non const */ + scap = tgetstr(ct_encode_string(*argv, &el->el_scratch), &area); + } + if (!scap || scap[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Termcap parameter `" FSTR "' not found.\n", + *argv); + return -1; + } + /* + * Count home many values we need for this capability. + */ + for (cap = scap, arg_need = 0; *cap; cap++) + if (*cap == '%') + switch (*++cap) { + case 'd': + case '2': + case '3': + case '.': + case '+': + arg_need++; + break; + case '%': + case '>': + case 'i': + case 'r': + case 'n': + case 'B': + case 'D': + break; + default: + /* + * hpux has lot's of them... + */ + if (verbose) + (void) fprintf(el->el_errfile, + "echotc: Warning: unknown termcap %% `%c'.\n", + *cap); + /* This is bad, but I won't complain */ + break; + } + + switch (arg_need) { + case 0: + argv++; + if (*argv && *argv[0]) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Extra argument `" FSTR "'.\n", + *argv); + return -1; + } + terminal_tputs(el, scap, 1); + break; + case 1: + argv++; + if (!*argv || *argv[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Missing argument.\n"); + return -1; + } + arg_cols = 0; + i = Strtol(*argv, &ep, 10); + if (*ep != '\0' || i < 0) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `" FSTR "' for rows.\n", + *argv); + return -1; + } + arg_rows = (int) i; + argv++; + if (*argv && *argv[0]) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Extra argument `" FSTR + "'.\n", *argv); + return -1; + } + terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), 1); + break; + default: + /* This is wrong, but I will ignore it... */ + if (verbose) + (void) fprintf(el->el_errfile, + "echotc: Warning: Too many required arguments (%d).\n", + arg_need); + /* FALLTHROUGH */ + case 2: + argv++; + if (!*argv || *argv[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Missing argument.\n"); + return -1; + } + i = Strtol(*argv, &ep, 10); + if (*ep != '\0' || i < 0) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `" FSTR "' for cols.\n", + *argv); + return -1; + } + arg_cols = (int) i; + argv++; + if (!*argv || *argv[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Missing argument.\n"); + return -1; + } + i = Strtol(*argv, &ep, 10); + if (*ep != '\0' || i < 0) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `" FSTR "' for rows.\n", + *argv); + return -1; + } + arg_rows = (int) i; + if (*ep != '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `" FSTR "'.\n", *argv); + return -1; + } + argv++; + if (*argv && *argv[0]) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Extra argument `" FSTR + "'.\n", *argv); + return -1; + } + terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows); + break; + } + return 0; +} diff --git a/lib/libedit/terminal.h b/lib/libedit/terminal.h new file mode 100644 index 0000000..61e3e96 --- /dev/null +++ b/lib/libedit/terminal.h @@ -0,0 +1,125 @@ +/* $NetBSD: terminal.h,v 1.7 2016/02/16 15:53:48 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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. + * + * @(#)term.h 8.1 (Berkeley) 6/4/93 + * $FreeBSD: releng/11.1/lib/libedit/terminal.h 313981 2017-02-20 03:33:59Z pfg $ + */ + +/* + * el.term.h: Termcap header + */ +#ifndef _h_el_terminal +#define _h_el_terminal + +typedef struct { /* Symbolic function key bindings */ + const Char *name; /* name of the key */ + int key; /* Index in termcap table */ + keymacro_value_t fun; /* Function bound to it */ + int type; /* Type of function */ +} funckey_t; + +typedef struct { + const char *t_name; /* the terminal name */ + coord_t t_size; /* # lines and cols */ + int t_flags; +#define TERM_CAN_INSERT 0x001 /* Has insert cap */ +#define TERM_CAN_DELETE 0x002 /* Has delete cap */ +#define TERM_CAN_CEOL 0x004 /* Has CEOL cap */ +#define TERM_CAN_TAB 0x008 /* Can use tabs */ +#define TERM_CAN_ME 0x010 /* Can turn all attrs. */ +#define TERM_CAN_UP 0x020 /* Can move up */ +#define TERM_HAS_META 0x040 /* Has a meta key */ +#define TERM_HAS_AUTO_MARGINS 0x080 /* Has auto margins */ +#define TERM_HAS_MAGIC_MARGINS 0x100 /* Has magic margins */ + char *t_buf; /* Termcap buffer */ + size_t t_loc; /* location used */ + char **t_str; /* termcap strings */ + int *t_val; /* termcap values */ + char *t_cap; /* Termcap buffer */ + funckey_t *t_fkey; /* Array of keys */ +} el_terminal_t; + +/* + * fKey indexes + */ +#define A_K_DN 0 +#define A_K_UP 1 +#define A_K_LT 2 +#define A_K_RT 3 +#define A_K_HO 4 +#define A_K_EN 5 +#define A_K_DE 6 +#define A_K_NKEYS 7 + +protected void terminal_move_to_line(EditLine *, int); +protected void terminal_move_to_char(EditLine *, int); +protected void terminal_clear_EOL(EditLine *, int); +protected void terminal_overwrite(EditLine *, const Char *, size_t); +protected void terminal_insertwrite(EditLine *, Char *, int); +protected void terminal_deletechars(EditLine *, int); +protected void terminal_clear_screen(EditLine *); +protected void terminal_beep(EditLine *); +protected int terminal_change_size(EditLine *, int, int); +protected int terminal_get_size(EditLine *, int *, int *); +protected int terminal_init(EditLine *); +protected void terminal_bind_arrow(EditLine *); +protected void terminal_print_arrow(EditLine *, const Char *); +protected int terminal_clear_arrow(EditLine *, const Char *); +protected int terminal_set_arrow(EditLine *, const Char *, keymacro_value_t *, int); +protected void terminal_end(EditLine *); +protected void terminal_get(EditLine *, const char **); +protected int terminal_set(EditLine *, const char *); +protected int terminal_settc(EditLine *, int, const Char **); +protected int terminal_gettc(EditLine *, int, char **); +protected int terminal_telltc(EditLine *, int, const Char **); +protected int terminal_echotc(EditLine *, int, const Char **); +protected void terminal_writec(EditLine *, wint_t); +protected int terminal__putc(EditLine *, wint_t); +protected void terminal__flush(EditLine *); + +/* + * Easy access macros + */ +#define EL_FLAGS (el)->el_terminal.t_flags + +#define EL_CAN_INSERT (EL_FLAGS & TERM_CAN_INSERT) +#define EL_CAN_DELETE (EL_FLAGS & TERM_CAN_DELETE) +#define EL_CAN_CEOL (EL_FLAGS & TERM_CAN_CEOL) +#define EL_CAN_TAB (EL_FLAGS & TERM_CAN_TAB) +#define EL_CAN_ME (EL_FLAGS & TERM_CAN_ME) +#define EL_CAN_UP (EL_FLAGS & TERM_CAN_UP) +#define EL_HAS_META (EL_FLAGS & TERM_HAS_META) +#define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS) +#define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS) + +#endif /* _h_el_terminal */ diff --git a/lib/libedit/tokenizer.c b/lib/libedit/tokenizer.c new file mode 100644 index 0000000..67abdcf --- /dev/null +++ b/lib/libedit/tokenizer.c @@ -0,0 +1,455 @@ +/* $NetBSD: tokenizer.c,v 1.24 2016/02/17 19:47:49 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)tokenizer.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: tokenizer.c,v 1.24 2016/02/17 19:47:49 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/tokenizer.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* We build this file twice, once as NARROW, once as WIDE. */ +/* + * tokenize.c: Bourne shell like tokenizer + */ +#include +#include + +#include "histedit.h" +#include "chartype.h" + +typedef enum { + Q_none, Q_single, Q_double, Q_one, Q_doubleone +} quote_t; + +#define TOK_KEEP 1 +#define TOK_EAT 2 + +#define WINCR 20 +#define AINCR 10 + +#define IFS STR("\t \n") + +#define tok_malloc(a) malloc(a) +#define tok_free(a) free(a) +#define tok_realloc(a, b) realloc(a, b) +#define tok_strdup(a) Strdup(a) + + +struct TYPE(tokenizer) { + Char *ifs; /* In field separator */ + size_t argc, amax; /* Current and maximum number of args */ + Char **argv; /* Argument list */ + Char *wptr, *wmax; /* Space and limit on the word buffer */ + Char *wstart; /* Beginning of next word */ + Char *wspace; /* Space of word buffer */ + quote_t quote; /* Quoting state */ + int flags; /* flags; */ +}; + + +private void FUN(tok,finish)(TYPE(Tokenizer) *); + + +/* FUN(tok,finish)(): + * Finish a word in the tokenizer. + */ +private void +FUN(tok,finish)(TYPE(Tokenizer) *tok) +{ + + *tok->wptr = '\0'; + if ((tok->flags & TOK_KEEP) || tok->wptr != tok->wstart) { + tok->argv[tok->argc++] = tok->wstart; + tok->argv[tok->argc] = NULL; + tok->wstart = ++tok->wptr; + } + tok->flags &= ~TOK_KEEP; +} + + +/* FUN(tok,init)(): + * Initialize the tokenizer + */ +public TYPE(Tokenizer) * +FUN(tok,init)(const Char *ifs) +{ + TYPE(Tokenizer) *tok = tok_malloc(sizeof(*tok)); + + if (tok == NULL) + return NULL; + tok->ifs = tok_strdup(ifs ? ifs : IFS); + if (tok->ifs == NULL) { + tok_free(tok); + return NULL; + } + tok->argc = 0; + tok->amax = AINCR; + tok->argv = tok_malloc(sizeof(*tok->argv) * tok->amax); + if (tok->argv == NULL) { + tok_free(tok->ifs); + tok_free(tok); + return NULL; + } + tok->argv[0] = NULL; + tok->wspace = tok_malloc(WINCR * sizeof(*tok->wspace)); + if (tok->wspace == NULL) { + tok_free(tok->argv); + tok_free(tok->ifs); + tok_free(tok); + return NULL; + } + tok->wmax = tok->wspace + WINCR; + tok->wstart = tok->wspace; + tok->wptr = tok->wspace; + tok->flags = 0; + tok->quote = Q_none; + + return tok; +} + + +/* FUN(tok,reset)(): + * Reset the tokenizer + */ +public void +FUN(tok,reset)(TYPE(Tokenizer) *tok) +{ + + tok->argc = 0; + tok->wstart = tok->wspace; + tok->wptr = tok->wspace; + tok->flags = 0; + tok->quote = Q_none; +} + + +/* FUN(tok,end)(): + * Clean up + */ +public void +FUN(tok,end)(TYPE(Tokenizer) *tok) +{ + + tok_free(tok->ifs); + tok_free(tok->wspace); + tok_free(tok->argv); + tok_free(tok); +} + + + +/* FUN(tok,line)(): + * Bourne shell (sh(1)) like tokenizing + * Arguments: + * tok current tokenizer state (setup with FUN(tok,init)()) + * line line to parse + * Returns: + * -1 Internal error + * 3 Quoted return + * 2 Unmatched double quote + * 1 Unmatched single quote + * 0 Ok + * Modifies (if return value is 0): + * argc number of arguments + * argv argument array + * cursorc if !NULL, argv element containing cursor + * cursorv if !NULL, offset in argv[cursorc] of cursor + */ +public int +FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line, + int *argc, const Char ***argv, int *cursorc, int *cursoro) +{ + const Char *ptr; + int cc, co; + + cc = co = -1; + ptr = line->buffer; + for (ptr = line->buffer; ;ptr++) { + if (ptr >= line->lastchar) + ptr = STR(""); + if (ptr == line->cursor) { + cc = (int)tok->argc; + co = (int)(tok->wptr - tok->wstart); + } + switch (*ptr) { + case '\'': + tok->flags |= TOK_KEEP; + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: + tok->quote = Q_single; /* Enter single quote + * mode */ + break; + + case Q_single: /* Exit single quote mode */ + tok->quote = Q_none; + break; + + case Q_one: /* Quote this ' */ + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + case Q_double: /* Stay in double quote mode */ + *tok->wptr++ = *ptr; + break; + + case Q_doubleone: /* Quote this ' */ + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + default: + return -1; + } + break; + + case '"': + tok->flags &= ~TOK_EAT; + tok->flags |= TOK_KEEP; + switch (tok->quote) { + case Q_none: /* Enter double quote mode */ + tok->quote = Q_double; + break; + + case Q_double: /* Exit double quote mode */ + tok->quote = Q_none; + break; + + case Q_one: /* Quote this " */ + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + case Q_single: /* Stay in single quote mode */ + *tok->wptr++ = *ptr; + break; + + case Q_doubleone: /* Quote this " */ + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + default: + return -1; + } + break; + + case '\\': + tok->flags |= TOK_KEEP; + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: /* Quote next character */ + tok->quote = Q_one; + break; + + case Q_double: /* Quote next character */ + tok->quote = Q_doubleone; + break; + + case Q_one: /* Quote this, restore state */ + *tok->wptr++ = *ptr; + tok->quote = Q_none; + break; + + case Q_single: /* Stay in single quote mode */ + *tok->wptr++ = *ptr; + break; + + case Q_doubleone: /* Quote this \ */ + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + default: + return -1; + } + break; + + case '\n': + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: + goto tok_line_outok; + + case Q_single: + case Q_double: + *tok->wptr++ = *ptr; /* Add the return */ + break; + + case Q_doubleone: /* Back to double, eat the '\n' */ + tok->flags |= TOK_EAT; + tok->quote = Q_double; + break; + + case Q_one: /* No quote, more eat the '\n' */ + tok->flags |= TOK_EAT; + tok->quote = Q_none; + break; + + default: + return 0; + } + break; + + case '\0': + switch (tok->quote) { + case Q_none: + /* Finish word and return */ + if (tok->flags & TOK_EAT) { + tok->flags &= ~TOK_EAT; + return 3; + } + goto tok_line_outok; + + case Q_single: + return 1; + + case Q_double: + return 2; + + case Q_doubleone: + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + case Q_one: + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + default: + return -1; + } + break; + + default: + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: + if (Strchr(tok->ifs, *ptr) != NULL) + FUN(tok,finish)(tok); + else + *tok->wptr++ = *ptr; + break; + + case Q_single: + case Q_double: + *tok->wptr++ = *ptr; + break; + + + case Q_doubleone: + *tok->wptr++ = '\\'; + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + case Q_one: + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + default: + return -1; + + } + break; + } + + if (tok->wptr >= tok->wmax - 4) { + size_t size = (size_t)(tok->wmax - tok->wspace + WINCR); + Char *s = tok_realloc(tok->wspace, + size * sizeof(*s)); + if (s == NULL) + return -1; + + if (s != tok->wspace) { + size_t i; + for (i = 0; i < tok->argc; i++) { + tok->argv[i] = + (tok->argv[i] - tok->wspace) + s; + } + tok->wptr = (tok->wptr - tok->wspace) + s; + tok->wstart = (tok->wstart - tok->wspace) + s; + tok->wspace = s; + } + tok->wmax = s + size; + } + if (tok->argc >= tok->amax - 4) { + Char **p; + tok->amax += AINCR; + p = tok_realloc(tok->argv, tok->amax * sizeof(*p)); + if (p == NULL) { + tok->amax -= AINCR; + return -1; + } + tok->argv = p; + } + } + tok_line_outok: + if (cc == -1 && co == -1) { + cc = (int)tok->argc; + co = (int)(tok->wptr - tok->wstart); + } + if (cursorc != NULL) + *cursorc = cc; + if (cursoro != NULL) + *cursoro = co; + FUN(tok,finish)(tok); + *argv = (const Char **)tok->argv; + *argc = (int)tok->argc; + return 0; +} + +/* FUN(tok,str)(): + * Simpler version of tok_line, taking a NUL terminated line + * and splitting into words, ignoring cursor state. + */ +public int +FUN(tok,str)(TYPE(Tokenizer) *tok, const Char *line, int *argc, + const Char ***argv) +{ + TYPE(LineInfo) li; + + memset(&li, 0, sizeof(li)); + li.buffer = line; + li.cursor = li.lastchar = Strchr(line, '\0'); + return FUN(tok,line)(tok, &li, argc, argv, NULL, NULL); +} diff --git a/lib/libedit/tty.c b/lib/libedit/tty.c new file mode 100644 index 0000000..22dfe03 --- /dev/null +++ b/lib/libedit/tty.c @@ -0,0 +1,1340 @@ +/* $NetBSD: tty.c,v 1.59 2016/03/22 01:34:32 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: tty.c,v 1.59 2016/03/22 01:34:32 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/tty.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * tty.c: tty interface stuff + */ +#include +#include +#include /* for abort */ +#include +#include /* for ffs */ +#include /* for isatty */ + +#include "el.h" +#include "parse.h" + +typedef struct ttymodes_t { + const char *m_name; + unsigned int m_value; + int m_type; +} ttymodes_t; + +typedef struct ttymap_t { + wint_t nch, och; /* Internal and termio rep of chars */ + el_action_t bind[3]; /* emacs, vi, and vi-cmd */ +} ttymap_t; + + +private const ttyperm_t ttyperm = { + { + {"iflag:", ICRNL, (INLCR | IGNCR)}, + {"oflag:", (OPOST | ONLCR), ONLRET}, + {"cflag:", 0, 0}, + {"lflag:", (ISIG | ICANON | ECHO | ECHOE | ECHOCTL | IEXTEN), + (NOFLSH | ECHONL | EXTPROC | FLUSHO)}, + {"chars:", 0, 0}, + }, + { + {"iflag:", (INLCR | ICRNL), IGNCR}, + {"oflag:", (OPOST | ONLCR), ONLRET}, + {"cflag:", 0, 0}, + {"lflag:", ISIG, + (NOFLSH | ICANON | ECHO | ECHOK | ECHONL | EXTPROC | IEXTEN | FLUSHO)}, + {"chars:", (C_SH(C_MIN) | C_SH(C_TIME) | C_SH(C_SWTCH) | C_SH(C_DSWTCH) | + C_SH(C_SUSP) | C_SH(C_DSUSP) | C_SH(C_EOL) | C_SH(C_DISCARD) | + C_SH(C_PGOFF) | C_SH(C_PAGE) | C_SH(C_STATUS)), 0} + }, + { + {"iflag:", 0, IXON | IXOFF | INLCR | ICRNL}, + {"oflag:", 0, 0}, + {"cflag:", 0, 0}, + {"lflag:", 0, ISIG | IEXTEN}, + {"chars:", 0, 0}, + } +}; + +private const ttychar_t ttychar = { + { + CINTR, CQUIT, CERASE, CKILL, + CEOF, CEOL, CEOL2, CSWTCH, + CDSWTCH, CERASE2, CSTART, CSTOP, + CWERASE, CSUSP, CDSUSP, CREPRINT, + CDISCARD, CLNEXT, CSTATUS, CPAGE, + CPGOFF, CKILL2, CBRK, CMIN, + CTIME + }, + { + CINTR, CQUIT, CERASE, CKILL, + _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, + _POSIX_VDISABLE, CERASE2, CSTART, CSTOP, + _POSIX_VDISABLE, CSUSP, _POSIX_VDISABLE, _POSIX_VDISABLE, + CDISCARD, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, + _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1, + 0 + }, + { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0 + } +}; + +private const ttymap_t tty_map[] = { +#ifdef VERASE + {C_ERASE, VERASE, + {EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, +#endif /* VERASE */ +#ifdef VERASE2 + {C_ERASE2, VERASE2, + {EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, +#endif /* VERASE2 */ +#ifdef VKILL + {C_KILL, VKILL, + {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, +#endif /* VKILL */ +#ifdef VKILL2 + {C_KILL2, VKILL2, + {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, +#endif /* VKILL2 */ +#ifdef VEOF + {C_EOF, VEOF, + {EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED}}, +#endif /* VEOF */ +#ifdef VWERASE + {C_WERASE, VWERASE, + {ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD}}, +#endif /* VWERASE */ +#ifdef VREPRINT + {C_REPRINT, VREPRINT, + {ED_REDISPLAY, ED_INSERT, ED_REDISPLAY}}, +#endif /* VREPRINT */ +#ifdef VLNEXT + {C_LNEXT, VLNEXT, + {ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}}, +#endif /* VLNEXT */ + {(wint_t)-1, (wint_t)-1, + {ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}} +}; + +private const ttymodes_t ttymodes[] = { +#ifdef IGNBRK + {"ignbrk", IGNBRK, MD_INP}, +#endif /* IGNBRK */ +#ifdef BRKINT + {"brkint", BRKINT, MD_INP}, +#endif /* BRKINT */ +#ifdef IGNPAR + {"ignpar", IGNPAR, MD_INP}, +#endif /* IGNPAR */ +#ifdef PARMRK + {"parmrk", PARMRK, MD_INP}, +#endif /* PARMRK */ +#ifdef INPCK + {"inpck", INPCK, MD_INP}, +#endif /* INPCK */ +#ifdef ISTRIP + {"istrip", ISTRIP, MD_INP}, +#endif /* ISTRIP */ +#ifdef INLCR + {"inlcr", INLCR, MD_INP}, +#endif /* INLCR */ +#ifdef IGNCR + {"igncr", IGNCR, MD_INP}, +#endif /* IGNCR */ +#ifdef ICRNL + {"icrnl", ICRNL, MD_INP}, +#endif /* ICRNL */ +#ifdef IUCLC + {"iuclc", IUCLC, MD_INP}, +#endif /* IUCLC */ +#ifdef IXON + {"ixon", IXON, MD_INP}, +#endif /* IXON */ +#ifdef IXANY + {"ixany", IXANY, MD_INP}, +#endif /* IXANY */ +#ifdef IXOFF + {"ixoff", IXOFF, MD_INP}, +#endif /* IXOFF */ +#ifdef IMAXBEL + {"imaxbel", IMAXBEL, MD_INP}, +#endif /* IMAXBEL */ + +#ifdef OPOST + {"opost", OPOST, MD_OUT}, +#endif /* OPOST */ +#ifdef OLCUC + {"olcuc", OLCUC, MD_OUT}, +#endif /* OLCUC */ +#ifdef ONLCR + {"onlcr", ONLCR, MD_OUT}, +#endif /* ONLCR */ +#ifdef OCRNL + {"ocrnl", OCRNL, MD_OUT}, +#endif /* OCRNL */ +#ifdef ONOCR + {"onocr", ONOCR, MD_OUT}, +#endif /* ONOCR */ +#ifdef ONOEOT + {"onoeot", ONOEOT, MD_OUT}, +#endif /* ONOEOT */ +#ifdef ONLRET + {"onlret", ONLRET, MD_OUT}, +#endif /* ONLRET */ +#ifdef OFILL + {"ofill", OFILL, MD_OUT}, +#endif /* OFILL */ +#ifdef OFDEL + {"ofdel", OFDEL, MD_OUT}, +#endif /* OFDEL */ +#ifdef NLDLY + {"nldly", NLDLY, MD_OUT}, +#endif /* NLDLY */ +#ifdef CRDLY + {"crdly", CRDLY, MD_OUT}, +#endif /* CRDLY */ +#ifdef TABDLY + {"tabdly", TABDLY, MD_OUT}, +#endif /* TABDLY */ +#ifdef XTABS + {"xtabs", XTABS, MD_OUT}, +#endif /* XTABS */ +#ifdef BSDLY + {"bsdly", BSDLY, MD_OUT}, +#endif /* BSDLY */ +#ifdef VTDLY + {"vtdly", VTDLY, MD_OUT}, +#endif /* VTDLY */ +#ifdef FFDLY + {"ffdly", FFDLY, MD_OUT}, +#endif /* FFDLY */ +#ifdef PAGEOUT + {"pageout", PAGEOUT, MD_OUT}, +#endif /* PAGEOUT */ +#ifdef WRAP + {"wrap", WRAP, MD_OUT}, +#endif /* WRAP */ + +#ifdef CIGNORE + {"cignore", CIGNORE, MD_CTL}, +#endif /* CBAUD */ +#ifdef CBAUD + {"cbaud", CBAUD, MD_CTL}, +#endif /* CBAUD */ +#ifdef CSTOPB + {"cstopb", CSTOPB, MD_CTL}, +#endif /* CSTOPB */ +#ifdef CREAD + {"cread", CREAD, MD_CTL}, +#endif /* CREAD */ +#ifdef PARENB + {"parenb", PARENB, MD_CTL}, +#endif /* PARENB */ +#ifdef PARODD + {"parodd", PARODD, MD_CTL}, +#endif /* PARODD */ +#ifdef HUPCL + {"hupcl", HUPCL, MD_CTL}, +#endif /* HUPCL */ +#ifdef CLOCAL + {"clocal", CLOCAL, MD_CTL}, +#endif /* CLOCAL */ +#ifdef LOBLK + {"loblk", LOBLK, MD_CTL}, +#endif /* LOBLK */ +#ifdef CIBAUD + {"cibaud", CIBAUD, MD_CTL}, +#endif /* CIBAUD */ +#ifdef CRTSCTS +#ifdef CCTS_OFLOW + {"ccts_oflow", CCTS_OFLOW, MD_CTL}, +#else + {"crtscts", CRTSCTS, MD_CTL}, +#endif /* CCTS_OFLOW */ +#endif /* CRTSCTS */ +#ifdef CRTS_IFLOW + {"crts_iflow", CRTS_IFLOW, MD_CTL}, +#endif /* CRTS_IFLOW */ +#ifdef CDTRCTS + {"cdtrcts", CDTRCTS, MD_CTL}, +#endif /* CDTRCTS */ +#ifdef MDMBUF + {"mdmbuf", MDMBUF, MD_CTL}, +#endif /* MDMBUF */ +#ifdef RCV1EN + {"rcv1en", RCV1EN, MD_CTL}, +#endif /* RCV1EN */ +#ifdef XMT1EN + {"xmt1en", XMT1EN, MD_CTL}, +#endif /* XMT1EN */ + +#ifdef ISIG + {"isig", ISIG, MD_LIN}, +#endif /* ISIG */ +#ifdef ICANON + {"icanon", ICANON, MD_LIN}, +#endif /* ICANON */ +#ifdef XCASE + {"xcase", XCASE, MD_LIN}, +#endif /* XCASE */ +#ifdef ECHO + {"echo", ECHO, MD_LIN}, +#endif /* ECHO */ +#ifdef ECHOE + {"echoe", ECHOE, MD_LIN}, +#endif /* ECHOE */ +#ifdef ECHOK + {"echok", ECHOK, MD_LIN}, +#endif /* ECHOK */ +#ifdef ECHONL + {"echonl", ECHONL, MD_LIN}, +#endif /* ECHONL */ +#ifdef NOFLSH + {"noflsh", NOFLSH, MD_LIN}, +#endif /* NOFLSH */ +#ifdef TOSTOP + {"tostop", TOSTOP, MD_LIN}, +#endif /* TOSTOP */ +#ifdef ECHOCTL + {"echoctl", ECHOCTL, MD_LIN}, +#endif /* ECHOCTL */ +#ifdef ECHOPRT + {"echoprt", ECHOPRT, MD_LIN}, +#endif /* ECHOPRT */ +#ifdef ECHOKE + {"echoke", ECHOKE, MD_LIN}, +#endif /* ECHOKE */ +#ifdef DEFECHO + {"defecho", DEFECHO, MD_LIN}, +#endif /* DEFECHO */ +#ifdef FLUSHO + {"flusho", FLUSHO, MD_LIN}, +#endif /* FLUSHO */ +#ifdef PENDIN + {"pendin", PENDIN, MD_LIN}, +#endif /* PENDIN */ +#ifdef IEXTEN + {"iexten", IEXTEN, MD_LIN}, +#endif /* IEXTEN */ +#ifdef NOKERNINFO + {"nokerninfo", NOKERNINFO, MD_LIN}, +#endif /* NOKERNINFO */ +#ifdef ALTWERASE + {"altwerase", ALTWERASE, MD_LIN}, +#endif /* ALTWERASE */ +#ifdef EXTPROC + {"extproc", EXTPROC, MD_LIN}, +#endif /* EXTPROC */ + +#if defined(VINTR) + {"intr", C_SH(C_INTR), MD_CHAR}, +#endif /* VINTR */ +#if defined(VQUIT) + {"quit", C_SH(C_QUIT), MD_CHAR}, +#endif /* VQUIT */ +#if defined(VERASE) + {"erase", C_SH(C_ERASE), MD_CHAR}, +#endif /* VERASE */ +#if defined(VKILL) + {"kill", C_SH(C_KILL), MD_CHAR}, +#endif /* VKILL */ +#if defined(VEOF) + {"eof", C_SH(C_EOF), MD_CHAR}, +#endif /* VEOF */ +#if defined(VEOL) + {"eol", C_SH(C_EOL), MD_CHAR}, +#endif /* VEOL */ +#if defined(VEOL2) + {"eol2", C_SH(C_EOL2), MD_CHAR}, +#endif /* VEOL2 */ +#if defined(VSWTCH) + {"swtch", C_SH(C_SWTCH), MD_CHAR}, +#endif /* VSWTCH */ +#if defined(VDSWTCH) + {"dswtch", C_SH(C_DSWTCH), MD_CHAR}, +#endif /* VDSWTCH */ +#if defined(VERASE2) + {"erase2", C_SH(C_ERASE2), MD_CHAR}, +#endif /* VERASE2 */ +#if defined(VSTART) + {"start", C_SH(C_START), MD_CHAR}, +#endif /* VSTART */ +#if defined(VSTOP) + {"stop", C_SH(C_STOP), MD_CHAR}, +#endif /* VSTOP */ +#if defined(VWERASE) + {"werase", C_SH(C_WERASE), MD_CHAR}, +#endif /* VWERASE */ +#if defined(VSUSP) + {"susp", C_SH(C_SUSP), MD_CHAR}, +#endif /* VSUSP */ +#if defined(VDSUSP) + {"dsusp", C_SH(C_DSUSP), MD_CHAR}, +#endif /* VDSUSP */ +#if defined(VREPRINT) + {"reprint", C_SH(C_REPRINT), MD_CHAR}, +#endif /* VREPRINT */ +#if defined(VDISCARD) + {"discard", C_SH(C_DISCARD), MD_CHAR}, +#endif /* VDISCARD */ +#if defined(VLNEXT) + {"lnext", C_SH(C_LNEXT), MD_CHAR}, +#endif /* VLNEXT */ +#if defined(VSTATUS) + {"status", C_SH(C_STATUS), MD_CHAR}, +#endif /* VSTATUS */ +#if defined(VPAGE) + {"page", C_SH(C_PAGE), MD_CHAR}, +#endif /* VPAGE */ +#if defined(VPGOFF) + {"pgoff", C_SH(C_PGOFF), MD_CHAR}, +#endif /* VPGOFF */ +#if defined(VKILL2) + {"kill2", C_SH(C_KILL2), MD_CHAR}, +#endif /* VKILL2 */ +#if defined(VBRK) + {"brk", C_SH(C_BRK), MD_CHAR}, +#endif /* VBRK */ +#if defined(VMIN) + {"min", C_SH(C_MIN), MD_CHAR}, +#endif /* VMIN */ +#if defined(VTIME) + {"time", C_SH(C_TIME), MD_CHAR}, +#endif /* VTIME */ + {NULL, 0, -1}, +}; + + + +#define tty__gettabs(td) ((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1) +#define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8) +#define tty__cooked_mode(td) ((td)->c_lflag & ICANON) + +private int tty_getty(EditLine *, struct termios *); +private int tty_setty(EditLine *, int, const struct termios *); +private int tty__getcharindex(int); +private void tty__getchar(struct termios *, unsigned char *); +private void tty__setchar(struct termios *, unsigned char *); +private speed_t tty__getspeed(struct termios *); +private int tty_setup(EditLine *); +private void tty_setup_flags(EditLine *, struct termios *, int); + +#define t_qu t_ts + +/* tty_getty(): + * Wrapper for tcgetattr to handle EINTR + */ +private int +tty_getty(EditLine *el, struct termios *t) +{ + int rv; + while ((rv = tcgetattr(el->el_infd, t)) == -1 && errno == EINTR) + continue; + return rv; +} + +/* tty_setty(): + * Wrapper for tcsetattr to handle EINTR + */ +private int +tty_setty(EditLine *el, int action, const struct termios *t) +{ + int rv; + while ((rv = tcsetattr(el->el_infd, action, t)) == -1 && errno == EINTR) + continue; + return rv; +} + +/* tty_setup(): + * Get the tty parameters and initialize the editing state + */ +private int +tty_setup(EditLine *el) +{ + int rst = 1; + + if (el->el_flags & EDIT_DISABLED) + return 0; + + if (el->el_tty.t_initialized) + return -1; + + if (!isatty(el->el_outfd)) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: isatty: %s\n", __func__, + strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + if (tty_getty(el, &el->el_tty.t_or) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_getty: %s\n", __func__, + strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed = el->el_tty.t_or; + + el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex); + el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex); + el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex); + + tty_setup_flags(el, &el->el_tty.t_ex, EX_IO); + + /* + * Reset the tty chars to reasonable defaults + * If they are disabled, then enable them. + */ + if (rst) { + if (tty__cooked_mode(&el->el_tty.t_ts)) { + tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); + /* + * Don't affect CMIN and CTIME for the editor mode + */ + for (rst = 0; rst < C_NCC - 2; rst++) + if (el->el_tty.t_c[TS_IO][rst] != + el->el_tty.t_vdisable + && el->el_tty.t_c[ED_IO][rst] != + el->el_tty.t_vdisable) + el->el_tty.t_c[ED_IO][rst] = + el->el_tty.t_c[TS_IO][rst]; + for (rst = 0; rst < C_NCC; rst++) + if (el->el_tty.t_c[TS_IO][rst] != + el->el_tty.t_vdisable) + el->el_tty.t_c[EX_IO][rst] = + el->el_tty.t_c[TS_IO][rst]; + } + tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", + __func__, strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + } + + tty_setup_flags(el, &el->el_tty.t_ed, ED_IO); + + tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); + tty_bind_char(el, 1); + el->el_tty.t_initialized = 1; + return 0; +} + +protected int +tty_init(EditLine *el) +{ + + el->el_tty.t_mode = EX_IO; + el->el_tty.t_vdisable = _POSIX_VDISABLE; + el->el_tty.t_initialized = 0; + (void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t)); + (void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t)); + return tty_setup(el); +} + + +/* tty_end(): + * Restore the tty to its original settings + */ +protected void +/*ARGSUSED*/ +tty_end(EditLine *el) +{ + if (el->el_flags & EDIT_DISABLED) + return; + + if (!el->el_tty.t_initialized) + return; + + if (tty_setty(el, TCSAFLUSH, &el->el_tty.t_or) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, + "%s: tty_setty: %s\n", __func__, strerror(errno)); +#endif /* DEBUG_TTY */ + } +} + + +/* tty__getspeed(): + * Get the tty speed + */ +private speed_t +tty__getspeed(struct termios *td) +{ + speed_t spd; + + if ((spd = cfgetispeed(td)) == 0) + spd = cfgetospeed(td); + return spd; +} + +/* tty__getspeed(): + * Return the index of the asked char in the c_cc array + */ +private int +tty__getcharindex(int i) +{ + switch (i) { +#ifdef VINTR + case C_INTR: + return VINTR; +#endif /* VINTR */ +#ifdef VQUIT + case C_QUIT: + return VQUIT; +#endif /* VQUIT */ +#ifdef VERASE + case C_ERASE: + return VERASE; +#endif /* VERASE */ +#ifdef VKILL + case C_KILL: + return VKILL; +#endif /* VKILL */ +#ifdef VEOF + case C_EOF: + return VEOF; +#endif /* VEOF */ +#ifdef VEOL + case C_EOL: + return VEOL; +#endif /* VEOL */ +#ifdef VEOL2 + case C_EOL2: + return VEOL2; +#endif /* VEOL2 */ +#ifdef VSWTCH + case C_SWTCH: + return VSWTCH; +#endif /* VSWTCH */ +#ifdef VDSWTCH + case C_DSWTCH: + return VDSWTCH; +#endif /* VDSWTCH */ +#ifdef VERASE2 + case C_ERASE2: + return VERASE2; +#endif /* VERASE2 */ +#ifdef VSTART + case C_START: + return VSTART; +#endif /* VSTART */ +#ifdef VSTOP + case C_STOP: + return VSTOP; +#endif /* VSTOP */ +#ifdef VWERASE + case C_WERASE: + return VWERASE; +#endif /* VWERASE */ +#ifdef VSUSP + case C_SUSP: + return VSUSP; +#endif /* VSUSP */ +#ifdef VDSUSP + case C_DSUSP: + return VDSUSP; +#endif /* VDSUSP */ +#ifdef VREPRINT + case C_REPRINT: + return VREPRINT; +#endif /* VREPRINT */ +#ifdef VDISCARD + case C_DISCARD: + return VDISCARD; +#endif /* VDISCARD */ +#ifdef VLNEXT + case C_LNEXT: + return VLNEXT; +#endif /* VLNEXT */ +#ifdef VSTATUS + case C_STATUS: + return VSTATUS; +#endif /* VSTATUS */ +#ifdef VPAGE + case C_PAGE: + return VPAGE; +#endif /* VPAGE */ +#ifdef VPGOFF + case C_PGOFF: + return VPGOFF; +#endif /* VPGOFF */ +#ifdef VKILL2 + case C_KILL2: + return VKILL2; +#endif /* KILL2 */ +#ifdef VMIN + case C_MIN: + return VMIN; +#endif /* VMIN */ +#ifdef VTIME + case C_TIME: + return VTIME; +#endif /* VTIME */ + default: + return -1; + } +} + +/* tty__getchar(): + * Get the tty characters + */ +private void +tty__getchar(struct termios *td, unsigned char *s) +{ + +#ifdef VINTR + s[C_INTR] = td->c_cc[VINTR]; +#endif /* VINTR */ +#ifdef VQUIT + s[C_QUIT] = td->c_cc[VQUIT]; +#endif /* VQUIT */ +#ifdef VERASE + s[C_ERASE] = td->c_cc[VERASE]; +#endif /* VERASE */ +#ifdef VKILL + s[C_KILL] = td->c_cc[VKILL]; +#endif /* VKILL */ +#ifdef VEOF + s[C_EOF] = td->c_cc[VEOF]; +#endif /* VEOF */ +#ifdef VEOL + s[C_EOL] = td->c_cc[VEOL]; +#endif /* VEOL */ +#ifdef VEOL2 + s[C_EOL2] = td->c_cc[VEOL2]; +#endif /* VEOL2 */ +#ifdef VSWTCH + s[C_SWTCH] = td->c_cc[VSWTCH]; +#endif /* VSWTCH */ +#ifdef VDSWTCH + s[C_DSWTCH] = td->c_cc[VDSWTCH]; +#endif /* VDSWTCH */ +#ifdef VERASE2 + s[C_ERASE2] = td->c_cc[VERASE2]; +#endif /* VERASE2 */ +#ifdef VSTART + s[C_START] = td->c_cc[VSTART]; +#endif /* VSTART */ +#ifdef VSTOP + s[C_STOP] = td->c_cc[VSTOP]; +#endif /* VSTOP */ +#ifdef VWERASE + s[C_WERASE] = td->c_cc[VWERASE]; +#endif /* VWERASE */ +#ifdef VSUSP + s[C_SUSP] = td->c_cc[VSUSP]; +#endif /* VSUSP */ +#ifdef VDSUSP + s[C_DSUSP] = td->c_cc[VDSUSP]; +#endif /* VDSUSP */ +#ifdef VREPRINT + s[C_REPRINT] = td->c_cc[VREPRINT]; +#endif /* VREPRINT */ +#ifdef VDISCARD + s[C_DISCARD] = td->c_cc[VDISCARD]; +#endif /* VDISCARD */ +#ifdef VLNEXT + s[C_LNEXT] = td->c_cc[VLNEXT]; +#endif /* VLNEXT */ +#ifdef VSTATUS + s[C_STATUS] = td->c_cc[VSTATUS]; +#endif /* VSTATUS */ +#ifdef VPAGE + s[C_PAGE] = td->c_cc[VPAGE]; +#endif /* VPAGE */ +#ifdef VPGOFF + s[C_PGOFF] = td->c_cc[VPGOFF]; +#endif /* VPGOFF */ +#ifdef VKILL2 + s[C_KILL2] = td->c_cc[VKILL2]; +#endif /* KILL2 */ +#ifdef VMIN + s[C_MIN] = td->c_cc[VMIN]; +#endif /* VMIN */ +#ifdef VTIME + s[C_TIME] = td->c_cc[VTIME]; +#endif /* VTIME */ +} /* tty__getchar */ + + +/* tty__setchar(): + * Set the tty characters + */ +private void +tty__setchar(struct termios *td, unsigned char *s) +{ + +#ifdef VINTR + td->c_cc[VINTR] = s[C_INTR]; +#endif /* VINTR */ +#ifdef VQUIT + td->c_cc[VQUIT] = s[C_QUIT]; +#endif /* VQUIT */ +#ifdef VERASE + td->c_cc[VERASE] = s[C_ERASE]; +#endif /* VERASE */ +#ifdef VKILL + td->c_cc[VKILL] = s[C_KILL]; +#endif /* VKILL */ +#ifdef VEOF + td->c_cc[VEOF] = s[C_EOF]; +#endif /* VEOF */ +#ifdef VEOL + td->c_cc[VEOL] = s[C_EOL]; +#endif /* VEOL */ +#ifdef VEOL2 + td->c_cc[VEOL2] = s[C_EOL2]; +#endif /* VEOL2 */ +#ifdef VSWTCH + td->c_cc[VSWTCH] = s[C_SWTCH]; +#endif /* VSWTCH */ +#ifdef VDSWTCH + td->c_cc[VDSWTCH] = s[C_DSWTCH]; +#endif /* VDSWTCH */ +#ifdef VERASE2 + td->c_cc[VERASE2] = s[C_ERASE2]; +#endif /* VERASE2 */ +#ifdef VSTART + td->c_cc[VSTART] = s[C_START]; +#endif /* VSTART */ +#ifdef VSTOP + td->c_cc[VSTOP] = s[C_STOP]; +#endif /* VSTOP */ +#ifdef VWERASE + td->c_cc[VWERASE] = s[C_WERASE]; +#endif /* VWERASE */ +#ifdef VSUSP + td->c_cc[VSUSP] = s[C_SUSP]; +#endif /* VSUSP */ +#ifdef VDSUSP + td->c_cc[VDSUSP] = s[C_DSUSP]; +#endif /* VDSUSP */ +#ifdef VREPRINT + td->c_cc[VREPRINT] = s[C_REPRINT]; +#endif /* VREPRINT */ +#ifdef VDISCARD + td->c_cc[VDISCARD] = s[C_DISCARD]; +#endif /* VDISCARD */ +#ifdef VLNEXT + td->c_cc[VLNEXT] = s[C_LNEXT]; +#endif /* VLNEXT */ +#ifdef VSTATUS + td->c_cc[VSTATUS] = s[C_STATUS]; +#endif /* VSTATUS */ +#ifdef VPAGE + td->c_cc[VPAGE] = s[C_PAGE]; +#endif /* VPAGE */ +#ifdef VPGOFF + td->c_cc[VPGOFF] = s[C_PGOFF]; +#endif /* VPGOFF */ +#ifdef VKILL2 + td->c_cc[VKILL2] = s[C_KILL2]; +#endif /* VKILL2 */ +#ifdef VMIN + td->c_cc[VMIN] = s[C_MIN]; +#endif /* VMIN */ +#ifdef VTIME + td->c_cc[VTIME] = s[C_TIME]; +#endif /* VTIME */ +} /* tty__setchar */ + + +/* tty_bind_char(): + * Rebind the editline functions + */ +protected void +tty_bind_char(EditLine *el, int force) +{ + + unsigned char *t_n = el->el_tty.t_c[ED_IO]; + unsigned char *t_o = el->el_tty.t_ed.c_cc; + Char new[2], old[2]; + const ttymap_t *tp; + el_action_t *map, *alt; + const el_action_t *dmap, *dalt; + new[1] = old[1] = '\0'; + + map = el->el_map.key; + alt = el->el_map.alt; + if (el->el_map.type == MAP_VI) { + dmap = el->el_map.vii; + dalt = el->el_map.vic; + } else { + dmap = el->el_map.emacs; + dalt = NULL; + } + + for (tp = tty_map; tp->nch != (wint_t)-1; tp++) { + new[0] = (Char)t_n[tp->nch]; + old[0] = (Char)t_o[tp->och]; + if (new[0] == old[0] && !force) + continue; + /* Put the old default binding back, and set the new binding */ + keymacro_clear(el, map, old); + map[UC(old[0])] = dmap[UC(old[0])]; + keymacro_clear(el, map, new); + /* MAP_VI == 1, MAP_EMACS == 0... */ + map[UC(new[0])] = tp->bind[el->el_map.type]; + if (dalt) { + keymacro_clear(el, alt, old); + alt[UC(old[0])] = dalt[UC(old[0])]; + keymacro_clear(el, alt, new); + alt[UC(new[0])] = tp->bind[el->el_map.type + 1]; + } + } +} + + +private tcflag_t * +tty__get_flag(struct termios *t, int kind) { + switch (kind) { + case MD_INP: + return &t->c_iflag; + case MD_OUT: + return &t->c_oflag; + case MD_CTL: + return &t->c_cflag; + case MD_LIN: + return &t->c_lflag; + default: + abort(); + /*NOTREACHED*/ + } +} + + +private tcflag_t +tty_update_flag(EditLine *el, tcflag_t f, int mode, int kind) +{ + f &= ~el->el_tty.t_t[mode][kind].t_clrmask; + f |= el->el_tty.t_t[mode][kind].t_setmask; + return f; +} + + +private void +tty_update_flags(EditLine *el, int kind) +{ + tcflag_t *tt, *ed, *ex; + tt = tty__get_flag(&el->el_tty.t_ts, kind); + ed = tty__get_flag(&el->el_tty.t_ed, kind); + ex = tty__get_flag(&el->el_tty.t_ex, kind); + + if (*tt != *ex && (kind != MD_CTL || *tt != *ed)) { + *ed = tty_update_flag(el, *tt, ED_IO, kind); + *ex = tty_update_flag(el, *tt, EX_IO, kind); + } +} + + +private void +tty_update_char(EditLine *el, int mode, int c) { + if (!((el->el_tty.t_t[mode][MD_CHAR].t_setmask & C_SH(c))) + && (el->el_tty.t_c[TS_IO][c] != el->el_tty.t_c[EX_IO][c])) + el->el_tty.t_c[mode][c] = el->el_tty.t_c[TS_IO][c]; + if (el->el_tty.t_t[mode][MD_CHAR].t_clrmask & C_SH(c)) + el->el_tty.t_c[mode][c] = el->el_tty.t_vdisable; +} + + +/* tty_rawmode(): + * Set terminal into 1 character at a time mode. + */ +protected int +tty_rawmode(EditLine *el) +{ + + if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO) + return 0; + + if (el->el_flags & EDIT_DISABLED) + return 0; + + if (tty_getty(el, &el->el_tty.t_ts) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_getty: %s\n", __func__, + strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + /* + * We always keep up with the eight bit setting and the speed of the + * tty. But we only believe changes that are made to cooked mode! + */ + el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts); + el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts); + + if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed || + tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) { + (void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed); + (void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed); + (void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed); + (void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed); + } + if (tty__cooked_mode(&el->el_tty.t_ts)) { + int i; + + for (i = MD_INP; i <= MD_LIN; i++) + tty_update_flags(el, i); + + if (tty__gettabs(&el->el_tty.t_ex) == 0) + el->el_tty.t_tabs = 0; + else + el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0; + + tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); + /* + * Check if the user made any changes. + * If he did, then propagate the changes to the + * edit and execute data structures. + */ + for (i = 0; i < C_NCC; i++) + if (el->el_tty.t_c[TS_IO][i] != + el->el_tty.t_c[EX_IO][i]) + break; + + if (i != C_NCC) { + /* + * Propagate changes only to the unprotected + * chars that have been modified just now. + */ + for (i = 0; i < C_NCC; i++) + tty_update_char(el, ED_IO, i); + + tty_bind_char(el, 0); + tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); + + for (i = 0; i < C_NCC; i++) + tty_update_char(el, EX_IO, i); + + tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); + } + } + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, + strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + el->el_tty.t_mode = ED_IO; + return 0; +} + + +/* tty_cookedmode(): + * Set the tty back to normal mode + */ +protected int +tty_cookedmode(EditLine *el) +{ /* set tty in normal setup */ + + if (el->el_tty.t_mode == EX_IO) + return 0; + + if (el->el_flags & EDIT_DISABLED) + return 0; + + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, + strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + el->el_tty.t_mode = EX_IO; + return 0; +} + + +/* tty_quotemode(): + * Turn on quote mode + */ +protected int +tty_quotemode(EditLine *el) +{ + if (el->el_tty.t_mode == QU_IO) + return 0; + + el->el_tty.t_qu = el->el_tty.t_ed; + + tty_setup_flags(el, &el->el_tty.t_qu, QU_IO); + + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_qu) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, + strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + el->el_tty.t_mode = QU_IO; + return 0; +} + + +/* tty_noquotemode(): + * Turn off quote mode + */ +protected int +tty_noquotemode(EditLine *el) +{ + + if (el->el_tty.t_mode != QU_IO) + return 0; + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__, + strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + el->el_tty.t_mode = ED_IO; + return 0; +} + + +/* tty_stty(): + * Stty builtin + */ +protected int +/*ARGSUSED*/ +tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv) +{ + const ttymodes_t *m; + char x; + int aflag = 0; + const Char *s, *d; + char name[EL_BUFSIZ]; + struct termios *tios = &el->el_tty.t_ex; + int z = EX_IO; + + if (argv == NULL) + return -1; + strncpy(name, ct_encode_string(*argv++, &el->el_scratch), sizeof(name)); + name[sizeof(name) - 1] = '\0'; + + while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0') + switch (argv[0][1]) { + case 'a': + aflag++; + argv++; + break; + case 'd': + argv++; + tios = &el->el_tty.t_ed; + z = ED_IO; + break; + case 'x': + argv++; + tios = &el->el_tty.t_ex; + z = EX_IO; + break; + case 'q': + argv++; + tios = &el->el_tty.t_ts; + z = QU_IO; + break; + default: + (void) fprintf(el->el_errfile, + "%s: Unknown switch `%lc'.\n", + name, (wint_t)argv[0][1]); + return -1; + } + + if (!argv || !*argv) { + int i = -1; + size_t len = 0, st = 0, cu; + for (m = ttymodes; m->m_name; m++) { + if (m->m_type != i) { + (void) fprintf(el->el_outfile, "%s%s", + i != -1 ? "\n" : "", + el->el_tty.t_t[z][m->m_type].t_name); + i = m->m_type; + st = len = + strlen(el->el_tty.t_t[z][m->m_type].t_name); + } + if (i != -1) { + x = (el->el_tty.t_t[z][i].t_setmask & m->m_value) + ? '+' : '\0'; + + if (el->el_tty.t_t[z][i].t_clrmask & m->m_value) + x = '-'; + } else { + x = '\0'; + } + + if (x != '\0' || aflag) { + + cu = strlen(m->m_name) + (x != '\0') + 1; + + if (len + cu >= + (size_t)el->el_terminal.t_size.h) { + (void) fprintf(el->el_outfile, "\n%*s", + (int)st, ""); + len = st + cu; + } else + len += cu; + + if (x != '\0') + (void) fprintf(el->el_outfile, "%c%s ", + x, m->m_name); + else + (void) fprintf(el->el_outfile, "%s ", + m->m_name); + } + } + (void) fprintf(el->el_outfile, "\n"); + return 0; + } + while (argv && (s = *argv++)) { + const Char *p; + switch (*s) { + case '+': + case '-': + x = (char)*s++; + break; + default: + x = '\0'; + break; + } + d = s; + p = Strchr(s, '='); + for (m = ttymodes; m->m_name; m++) + if ((p ? strncmp(m->m_name, ct_encode_string(d, + &el->el_scratch), (size_t)(p - d)) : + strcmp(m->m_name, ct_encode_string(d, + &el->el_scratch))) == 0 && + (p == NULL || m->m_type == MD_CHAR)) + break; + + if (!m->m_name) { + (void) fprintf(el->el_errfile, + "%s: Invalid argument `" FSTR "'.\n", name, d); + return -1; + } + if (p) { + int c = ffs((int)m->m_value); + int v = *++p ? parse__escape(&p) : + el->el_tty.t_vdisable; + assert(c != 0); + c--; + c = tty__getcharindex(c); + assert(c != -1); + tios->c_cc[c] = (cc_t)v; + continue; + } + switch (x) { + case '+': + el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value; + el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; + break; + case '-': + el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; + el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value; + break; + default: + el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; + el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; + break; + } + } + + tty_setup_flags(el, tios, z); + if (el->el_tty.t_mode == z) { + if (tty_setty(el, TCSADRAIN, tios) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", + __func__, strerror(errno)); +#endif /* DEBUG_TTY */ + return -1; + } + } + + return 0; +} + + +#ifdef notyet +/* tty_printchar(): + * DEbugging routine to print the tty characters + */ +private void +tty_printchar(EditLine *el, unsigned char *s) +{ + ttyperm_t *m; + int i; + + for (i = 0; i < C_NCC; i++) { + for (m = el->el_tty.t_t; m->m_name; m++) + if (m->m_type == MD_CHAR && C_SH(i) == m->m_value) + break; + if (m->m_name) + (void) fprintf(el->el_errfile, "%s ^%c ", + m->m_name, s[i] + 'A' - 1); + if (i % 5 == 0) + (void) fprintf(el->el_errfile, "\n"); + } + (void) fprintf(el->el_errfile, "\n"); +} +#endif /* notyet */ + + +private void +tty_setup_flags(EditLine *el, struct termios *tios, int mode) +{ + int kind; + for (kind = MD_INP; kind <= MD_LIN; kind++) { + tcflag_t *f = tty__get_flag(tios, kind); + *f = tty_update_flag(el, *f, mode, kind); + } +} diff --git a/lib/libedit/tty.h b/lib/libedit/tty.h index 3a3a0b8..5432731 100644 --- a/lib/libedit/tty.h +++ b/lib/libedit/tty.h @@ -1,3 +1,5 @@ +/* $NetBSD: tty.h,v 1.19 2016/02/27 18:13:21 christos Exp $ */ + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +32,7 @@ * SUCH DAMAGE. * * @(#)tty.h 8.1 (Berkeley) 6/4/93 - * $NetBSD: tty.h,v 1.11 2005/06/01 11:37:52 lukem Exp $ - * $FreeBSD: src/lib/libedit/tty.h,v 1.4.14.1 2005/10/09 03:44:01 delphij Exp $ + * $FreeBSD: releng/11.1/lib/libedit/tty.h 313981 2017-02-20 03:33:59Z pfg $ */ /* @@ -40,7 +41,6 @@ #ifndef _h_el_tty #define _h_el_tty -#include "histedit.h" #include #include @@ -430,7 +430,7 @@ #define C_MIN 23 #define C_TIME 24 #define C_NCC 25 -#define C_SH(A) (1 << (A)) +#define C_SH(A) ((unsigned int)(1 << (A))) /* * Terminal dependend data structures @@ -441,6 +441,7 @@ #define QU_IO 2 /* used only for quoted chars */ #define NN_IO 3 /* The number of entries */ +/* Don't re-order */ #define MD_INP 0 #define MD_OUT 1 #define MD_CTL 2 @@ -458,7 +459,7 @@ protected int tty_init(EditLine *); protected void tty_end(EditLine *); -protected int tty_stty(EditLine *, int, const char **); +protected int tty_stty(EditLine *, int, const Char **); protected int tty_rawmode(EditLine *); protected int tty_cookedmode(EditLine *); protected int tty_quotemode(EditLine *); @@ -468,12 +469,13 @@ typedef struct { ttyperm_t t_t; ttychar_t t_c; - struct termios t_ex, t_ed, t_ts; + struct termios t_or, t_ex, t_ed, t_ts; int t_tabs; int t_eight; speed_t t_speed; - int t_mode; + unsigned char t_mode; unsigned char t_vdisable; + unsigned char t_initialized; } el_tty_t; diff --git a/lib/libedit/vi.c b/lib/libedit/vi.c new file mode 100644 index 0000000..4b09169 --- /dev/null +++ b/lib/libedit/vi.c @@ -0,0 +1,1158 @@ +/* $NetBSD: vi.c,v 1.55 2016/03/02 19:24:20 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christos Zoulas of Cornell University. + * + * 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 "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: vi.c,v 1.55 2016/03/02 19:24:20 christos Exp $"); +#endif +#endif /* not lint && not SCCSID */ +#include +__FBSDID("$FreeBSD: releng/11.1/lib/libedit/vi.c 313981 2017-02-20 03:33:59Z pfg $"); + +/* + * vi.c: Vi mode commands. + */ +#include +#include +#include +#include +#include +#include + +#include "el.h" +#include "common.h" +#include "emacs.h" +#include "vi.h" + +private el_action_t cv_action(EditLine *, wint_t); +private el_action_t cv_paste(EditLine *, wint_t); + +/* cv_action(): + * Handle vi actions. + */ +private el_action_t +cv_action(EditLine *el, wint_t c) +{ + + if (el->el_chared.c_vcmd.action != NOP) { + /* 'cc', 'dd' and (possibly) friends */ + if (c != (wint_t)el->el_chared.c_vcmd.action) + return CC_ERROR; + + if (!(c & YANK)) + cv_undo(el); + cv_yank(el, el->el_line.buffer, + (int)(el->el_line.lastchar - el->el_line.buffer)); + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = 0; + if (!(c & YANK)) { + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + } + if (c & INSERT) + el->el_map.current = el->el_map.key; + + return CC_REFRESH; + } + el->el_chared.c_vcmd.pos = el->el_line.cursor; + el->el_chared.c_vcmd.action = c; + return CC_ARGHACK; +} + +/* cv_paste(): + * Paste previous deletion before or after the cursor + */ +private el_action_t +cv_paste(EditLine *el, wint_t c) +{ + c_kill_t *k = &el->el_chared.c_kill; + size_t len = (size_t)(k->last - k->buf); + + if (k->buf == NULL || len == 0) + return CC_ERROR; +#ifdef DEBUG_PASTE + (void) fprintf(el->el_errfile, "Paste: \"" FSTARSTR "\"\n", (int)len, + k->buf); +#endif + + cv_undo(el); + + if (!c && el->el_line.cursor < el->el_line.lastchar) + el->el_line.cursor++; + + c_insert(el, (int)len); + if (el->el_line.cursor + len > el->el_line.lastchar) + return CC_ERROR; + (void) memcpy(el->el_line.cursor, k->buf, len * + sizeof(*el->el_line.cursor)); + + return CC_REFRESH; +} + + +/* vi_paste_next(): + * Vi paste previous deletion to the right of the cursor + * [p] + */ +protected el_action_t +/*ARGSUSED*/ +vi_paste_next(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + return cv_paste(el, 0); +} + + +/* vi_paste_prev(): + * Vi paste previous deletion to the left of the cursor + * [P] + */ +protected el_action_t +/*ARGSUSED*/ +vi_paste_prev(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + return cv_paste(el, 1); +} + + +/* vi_prev_big_word(): + * Vi move to the previous space delimited word + * [B] + */ +protected el_action_t +/*ARGSUSED*/ +vi_prev_big_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor == el->el_line.buffer) + return CC_ERROR; + + el->el_line.cursor = cv_prev_word(el->el_line.cursor, + el->el_line.buffer, + el->el_state.argument, + cv__isWord); + + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* vi_prev_word(): + * Vi move to the previous word + * [b] + */ +protected el_action_t +/*ARGSUSED*/ +vi_prev_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor == el->el_line.buffer) + return CC_ERROR; + + el->el_line.cursor = cv_prev_word(el->el_line.cursor, + el->el_line.buffer, + el->el_state.argument, + cv__isword); + + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* vi_next_big_word(): + * Vi move to the next space delimited word + * [W] + */ +protected el_action_t +/*ARGSUSED*/ +vi_next_big_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor >= el->el_line.lastchar - 1) + return CC_ERROR; + + el->el_line.cursor = cv_next_word(el, el->el_line.cursor, + el->el_line.lastchar, el->el_state.argument, cv__isWord); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* vi_next_word(): + * Vi move to the next word + * [w] + */ +protected el_action_t +/*ARGSUSED*/ +vi_next_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor >= el->el_line.lastchar - 1) + return CC_ERROR; + + el->el_line.cursor = cv_next_word(el, el->el_line.cursor, + el->el_line.lastchar, el->el_state.argument, cv__isword); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* vi_change_case(): + * Vi change case of character under the cursor and advance one character + * [~] + */ +protected el_action_t +vi_change_case(EditLine *el, wint_t c) +{ + int i; + + if (el->el_line.cursor >= el->el_line.lastchar) + return CC_ERROR; + cv_undo(el); + for (i = 0; i < el->el_state.argument; i++) { + + c = *el->el_line.cursor; + if (Isupper(c)) + *el->el_line.cursor = Tolower(c); + else if (Islower(c)) + *el->el_line.cursor = Toupper(c); + + if (++el->el_line.cursor >= el->el_line.lastchar) { + el->el_line.cursor--; + re_fastaddc(el); + break; + } + re_fastaddc(el); + } + return CC_NORM; +} + + +/* vi_change_meta(): + * Vi change prefix command + * [c] + */ +protected el_action_t +/*ARGSUSED*/ +vi_change_meta(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + /* + * Delete with insert == change: first we delete and then we leave in + * insert mode. + */ + return cv_action(el, DELETE | INSERT); +} + + +/* vi_insert_at_bol(): + * Vi enter insert mode at the beginning of line + * [I] + */ +protected el_action_t +/*ARGSUSED*/ +vi_insert_at_bol(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_line.cursor = el->el_line.buffer; + cv_undo(el); + el->el_map.current = el->el_map.key; + return CC_CURSOR; +} + + +/* vi_replace_char(): + * Vi replace character under the cursor with the next character typed + * [r] + */ +protected el_action_t +/*ARGSUSED*/ +vi_replace_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor >= el->el_line.lastchar) + return CC_ERROR; + + el->el_map.current = el->el_map.key; + el->el_state.inputmode = MODE_REPLACE_1; + cv_undo(el); + return CC_ARGHACK; +} + + +/* vi_replace_mode(): + * Vi enter replace mode + * [R] + */ +protected el_action_t +/*ARGSUSED*/ +vi_replace_mode(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_map.current = el->el_map.key; + el->el_state.inputmode = MODE_REPLACE; + cv_undo(el); + return CC_NORM; +} + + +/* vi_substitute_char(): + * Vi replace character under the cursor and enter insert mode + * [s] + */ +protected el_action_t +/*ARGSUSED*/ +vi_substitute_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + c_delafter(el, el->el_state.argument); + el->el_map.current = el->el_map.key; + return CC_REFRESH; +} + + +/* vi_substitute_line(): + * Vi substitute entire line + * [S] + */ +protected el_action_t +/*ARGSUSED*/ +vi_substitute_line(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + cv_undo(el); + cv_yank(el, el->el_line.buffer, + (int)(el->el_line.lastchar - el->el_line.buffer)); + (void) em_kill_line(el, 0); + el->el_map.current = el->el_map.key; + return CC_REFRESH; +} + + +/* vi_change_to_eol(): + * Vi change to end of line + * [C] + */ +protected el_action_t +/*ARGSUSED*/ +vi_change_to_eol(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + cv_undo(el); + cv_yank(el, el->el_line.cursor, + (int)(el->el_line.lastchar - el->el_line.cursor)); + (void) ed_kill_line(el, 0); + el->el_map.current = el->el_map.key; + return CC_REFRESH; +} + + +/* vi_insert(): + * Vi enter insert mode + * [i] + */ +protected el_action_t +/*ARGSUSED*/ +vi_insert(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_map.current = el->el_map.key; + cv_undo(el); + return CC_NORM; +} + + +/* vi_add(): + * Vi enter insert mode after the cursor + * [a] + */ +protected el_action_t +/*ARGSUSED*/ +vi_add(EditLine *el, wint_t c __attribute__((__unused__))) +{ + int ret; + + el->el_map.current = el->el_map.key; + if (el->el_line.cursor < el->el_line.lastchar) { + el->el_line.cursor++; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + ret = CC_CURSOR; + } else + ret = CC_NORM; + + cv_undo(el); + + return (el_action_t)ret; +} + + +/* vi_add_at_eol(): + * Vi enter insert mode at end of line + * [A] + */ +protected el_action_t +/*ARGSUSED*/ +vi_add_at_eol(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_map.current = el->el_map.key; + el->el_line.cursor = el->el_line.lastchar; + cv_undo(el); + return CC_CURSOR; +} + + +/* vi_delete_meta(): + * Vi delete prefix command + * [d] + */ +protected el_action_t +/*ARGSUSED*/ +vi_delete_meta(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + return cv_action(el, DELETE); +} + + +/* vi_end_big_word(): + * Vi move to the end of the current space delimited word + * [E] + */ +protected el_action_t +/*ARGSUSED*/ +vi_end_big_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor == el->el_line.lastchar) + return CC_ERROR; + + el->el_line.cursor = cv__endword(el->el_line.cursor, + el->el_line.lastchar, el->el_state.argument, cv__isWord); + + if (el->el_chared.c_vcmd.action != NOP) { + el->el_line.cursor++; + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* vi_end_word(): + * Vi move to the end of the current word + * [e] + */ +protected el_action_t +/*ARGSUSED*/ +vi_end_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor == el->el_line.lastchar) + return CC_ERROR; + + el->el_line.cursor = cv__endword(el->el_line.cursor, + el->el_line.lastchar, el->el_state.argument, cv__isword); + + if (el->el_chared.c_vcmd.action != NOP) { + el->el_line.cursor++; + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* vi_undo(): + * Vi undo last change + * [u] + */ +protected el_action_t +/*ARGSUSED*/ +vi_undo(EditLine *el, wint_t c __attribute__((__unused__))) +{ + c_undo_t un = el->el_chared.c_undo; + + if (un.len == -1) + return CC_ERROR; + + /* switch line buffer and undo buffer */ + el->el_chared.c_undo.buf = el->el_line.buffer; + el->el_chared.c_undo.len = el->el_line.lastchar - el->el_line.buffer; + el->el_chared.c_undo.cursor = + (int)(el->el_line.cursor - el->el_line.buffer); + el->el_line.limit = un.buf + (el->el_line.limit - el->el_line.buffer); + el->el_line.buffer = un.buf; + el->el_line.cursor = un.buf + un.cursor; + el->el_line.lastchar = un.buf + un.len; + + return CC_REFRESH; +} + + +/* vi_command_mode(): + * Vi enter command mode (use alternative key bindings) + * [] + */ +protected el_action_t +/*ARGSUSED*/ +vi_command_mode(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + /* [Esc] cancels pending action */ + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = 0; + + el->el_state.doingarg = 0; + + el->el_state.inputmode = MODE_INSERT; + el->el_map.current = el->el_map.alt; +#ifdef VI_MOVE + if (el->el_line.cursor > el->el_line.buffer) + el->el_line.cursor--; +#endif + return CC_CURSOR; +} + + +/* vi_zero(): + * Vi move to the beginning of line + * [0] + */ +protected el_action_t +vi_zero(EditLine *el, wint_t c) +{ + + if (el->el_state.doingarg) + return ed_argument_digit(el, c); + + el->el_line.cursor = el->el_line.buffer; + if (el->el_chared.c_vcmd.action != NOP) { + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + + +/* vi_delete_prev_char(): + * Vi move to previous character (backspace) + * [^H] in insert mode only + */ +protected el_action_t +/*ARGSUSED*/ +vi_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_line.cursor <= el->el_line.buffer) + return CC_ERROR; + + c_delbefore1(el); + el->el_line.cursor--; + return CC_REFRESH; +} + + +/* vi_list_or_eof(): + * Vi list choices for completion or indicate end of file if empty line + * [^D] + */ +protected el_action_t +/*ARGSUSED*/ +vi_list_or_eof(EditLine *el, wint_t c) +{ + + if (el->el_line.cursor == el->el_line.lastchar) { + if (el->el_line.cursor == el->el_line.buffer) { + terminal_writec(el, c); /* then do a EOF */ + return CC_EOF; + } else { + /* + * Here we could list completions, but it is an + * error right now + */ + terminal_beep(el); + return CC_ERROR; + } + } else { +#ifdef notyet + re_goto_bottom(el); + *el->el_line.lastchar = '\0'; /* just in case */ + return CC_LIST_CHOICES; +#else + /* + * Just complain for now. + */ + terminal_beep(el); + return CC_ERROR; +#endif + } +} + + +/* vi_kill_line_prev(): + * Vi cut from beginning of line to cursor + * [^U] + */ +protected el_action_t +/*ARGSUSED*/ +vi_kill_line_prev(EditLine *el, wint_t c __attribute__((__unused__))) +{ + Char *kp, *cp; + + cp = el->el_line.buffer; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.cursor) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + c_delbefore(el, (int)(el->el_line.cursor - el->el_line.buffer)); + el->el_line.cursor = el->el_line.buffer; /* zap! */ + return CC_REFRESH; +} + + +/* vi_search_prev(): + * Vi search history previous + * [?] + */ +protected el_action_t +/*ARGSUSED*/ +vi_search_prev(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + return cv_search(el, ED_SEARCH_PREV_HISTORY); +} + + +/* vi_search_next(): + * Vi search history next + * [/] + */ +protected el_action_t +/*ARGSUSED*/ +vi_search_next(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + return cv_search(el, ED_SEARCH_NEXT_HISTORY); +} + + +/* vi_repeat_search_next(): + * Vi repeat current search in the same search direction + * [n] + */ +protected el_action_t +/*ARGSUSED*/ +vi_repeat_search_next(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_search.patlen == 0) + return CC_ERROR; + else + return cv_repeat_srch(el, el->el_search.patdir); +} + + +/* vi_repeat_search_prev(): + * Vi repeat current search in the opposite search direction + * [N] + */ +/*ARGSUSED*/ +protected el_action_t +vi_repeat_search_prev(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + if (el->el_search.patlen == 0) + return CC_ERROR; + else + return (cv_repeat_srch(el, + el->el_search.patdir == ED_SEARCH_PREV_HISTORY ? + ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY)); +} + + +/* vi_next_char(): + * Vi move to the character specified next + * [f] + */ +protected el_action_t +/*ARGSUSED*/ +vi_next_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0); +} + + +/* vi_prev_char(): + * Vi move to the character specified previous + * [F] + */ +protected el_action_t +/*ARGSUSED*/ +vi_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0); +} + + +/* vi_to_next_char(): + * Vi move up to the character specified next + * [t] + */ +protected el_action_t +/*ARGSUSED*/ +vi_to_next_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1); +} + + +/* vi_to_prev_char(): + * Vi move up to the character specified previous + * [T] + */ +protected el_action_t +/*ARGSUSED*/ +vi_to_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1); +} + + +/* vi_repeat_next_char(): + * Vi repeat current character search in the same search direction + * [;] + */ +protected el_action_t +/*ARGSUSED*/ +vi_repeat_next_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + return cv_csearch(el, el->el_search.chadir, el->el_search.chacha, + el->el_state.argument, el->el_search.chatflg); +} + + +/* vi_repeat_prev_char(): + * Vi repeat current character search in the opposite search direction + * [,] + */ +protected el_action_t +/*ARGSUSED*/ +vi_repeat_prev_char(EditLine *el, wint_t c __attribute__((__unused__))) +{ + el_action_t r; + int dir = el->el_search.chadir; + + r = cv_csearch(el, -dir, el->el_search.chacha, + el->el_state.argument, el->el_search.chatflg); + el->el_search.chadir = dir; + return r; +} + + +/* vi_match(): + * Vi go to matching () {} or [] + * [%] + */ +protected el_action_t +/*ARGSUSED*/ +vi_match(EditLine *el, wint_t c __attribute__((__unused__))) +{ + const Char match_chars[] = STR("()[]{}"); + Char *cp; + size_t delta, i, count; + Char o_ch, c_ch; + + *el->el_line.lastchar = '\0'; /* just in case */ + + i = Strcspn(el->el_line.cursor, match_chars); + o_ch = el->el_line.cursor[i]; + if (o_ch == 0) + return CC_ERROR; + delta = (size_t)(Strchr(match_chars, o_ch) - match_chars); + c_ch = match_chars[delta ^ 1]; + count = 1; + delta = 1 - (delta & 1) * 2; + + for (cp = &el->el_line.cursor[i]; count; ) { + cp += delta; + if (cp < el->el_line.buffer || cp >= el->el_line.lastchar) + return CC_ERROR; + if (*cp == o_ch) + count++; + else if (*cp == c_ch) + count--; + } + + el->el_line.cursor = cp; + + if (el->el_chared.c_vcmd.action != NOP) { + /* NB posix says char under cursor should NOT be deleted + for -ve delta - this is different to netbsd vi. */ + if (delta > 0) + el->el_line.cursor++; + cv_delfini(el); + return CC_REFRESH; + } + return CC_CURSOR; +} + +/* vi_undo_line(): + * Vi undo all changes to line + * [U] + */ +protected el_action_t +/*ARGSUSED*/ +vi_undo_line(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + cv_undo(el); + return hist_get(el); +} + +/* vi_to_column(): + * Vi go to specified column + * [|] + * NB netbsd vi goes to screen column 'n', posix says nth character + */ +protected el_action_t +/*ARGSUSED*/ +vi_to_column(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_line.cursor = el->el_line.buffer; + el->el_state.argument--; + return ed_next_char(el, 0); +} + +/* vi_yank_end(): + * Vi yank to end of line + * [Y] + */ +protected el_action_t +/*ARGSUSED*/ +vi_yank_end(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + cv_yank(el, el->el_line.cursor, + (int)(el->el_line.lastchar - el->el_line.cursor)); + return CC_REFRESH; +} + +/* vi_yank(): + * Vi yank + * [y] + */ +protected el_action_t +/*ARGSUSED*/ +vi_yank(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + return cv_action(el, YANK); +} + +/* vi_comment_out(): + * Vi comment out current command + * [#] + */ +protected el_action_t +/*ARGSUSED*/ +vi_comment_out(EditLine *el, wint_t c __attribute__((__unused__))) +{ + + el->el_line.cursor = el->el_line.buffer; + c_insert(el, 1); + *el->el_line.cursor = '#'; + re_refresh(el); + return ed_newline(el, 0); +} + +/* vi_alias(): + * Vi include shell alias + * [@] + * NB: posix implies that we should enter insert mode, however + * this is against historical precedent... + */ +protected el_action_t +/*ARGSUSED*/ +vi_alias(EditLine *el, wint_t c __attribute__((__unused__))) +{ + char alias_name[3]; + const char *alias_text; + + if (el->el_chared.c_aliasfun == NULL) + return CC_ERROR; + + alias_name[0] = '_'; + alias_name[2] = 0; + if (el_getc(el, &alias_name[1]) != 1) + return CC_ERROR; + + alias_text = (*el->el_chared.c_aliasfun)(el->el_chared.c_aliasarg, + alias_name); + if (alias_text != NULL) + FUN(el,push)(el, ct_decode_string(alias_text, &el->el_scratch)); + return CC_NORM; +} + +/* vi_to_history_line(): + * Vi go to specified history file line. + * [G] + */ +protected el_action_t +/*ARGSUSED*/ +vi_to_history_line(EditLine *el, wint_t c __attribute__((__unused__))) +{ + int sv_event_no = el->el_history.eventno; + el_action_t rval; + + + if (el->el_history.eventno == 0) { + (void) Strncpy(el->el_history.buf, el->el_line.buffer, + EL_BUFSIZ); + el->el_history.last = el->el_history.buf + + (el->el_line.lastchar - el->el_line.buffer); + } + + /* Lack of a 'count' means oldest, not 1 */ + if (!el->el_state.doingarg) { + el->el_history.eventno = 0x7fffffff; + hist_get(el); + } else { + /* This is brain dead, all the rest of this code counts + * upwards going into the past. Here we need count in the + * other direction (to match the output of fc -l). + * I could change the world, but this seems to suffice. + */ + el->el_history.eventno = 1; + if (hist_get(el) == CC_ERROR) + return CC_ERROR; + el->el_history.eventno = 1 + el->el_history.ev.num + - el->el_state.argument; + if (el->el_history.eventno < 0) { + el->el_history.eventno = sv_event_no; + return CC_ERROR; + } + } + rval = hist_get(el); + if (rval == CC_ERROR) + el->el_history.eventno = sv_event_no; + return rval; +} + +/* vi_histedit(): + * Vi edit history line with vi + * [v] + */ +protected el_action_t +/*ARGSUSED*/ +vi_histedit(EditLine *el, wint_t c __attribute__((__unused__))) +{ + int fd; + pid_t pid; + ssize_t st; + int status; + char tempfile[] = "/tmp/histedit.XXXXXXXXXX"; + char *cp = NULL; + size_t len; + Char *line = NULL; + + if (el->el_state.doingarg) { + if (vi_to_history_line(el, 0) == CC_ERROR) + return CC_ERROR; + } + + fd = mkstemp(tempfile); + if (fd < 0) + return CC_ERROR; + len = (size_t)(el->el_line.lastchar - el->el_line.buffer); +#define TMP_BUFSIZ (EL_BUFSIZ * MB_LEN_MAX) + cp = el_malloc(TMP_BUFSIZ * sizeof(*cp)); + if (cp == NULL) + goto error; + line = el_malloc(len * sizeof(*line) + 1); + if (line == NULL) + goto error; + Strncpy(line, el->el_line.buffer, len); + line[len] = '\0'; + ct_wcstombs(cp, line, TMP_BUFSIZ - 1); + cp[TMP_BUFSIZ - 1] = '\0'; + len = strlen(cp); + write(fd, cp, len); + write(fd, "\n", (size_t)1); + pid = fork(); + switch (pid) { + case -1: + goto error; + case 0: + close(fd); + execlp("vi", "vi", tempfile, (char *)NULL); + exit(0); + /*NOTREACHED*/ + default: + while (waitpid(pid, &status, 0) != pid) + continue; + lseek(fd, (off_t)0, SEEK_SET); + st = read(fd, cp, TMP_BUFSIZ - 1); + if (st > 0) { + cp[st] = '\0'; + len = (size_t)(el->el_line.limit - el->el_line.buffer); + len = ct_mbstowcs(el->el_line.buffer, cp, len); + if (len > 0 && el->el_line.buffer[len - 1] == '\n') + --len; + } + else + len = 0; + el->el_line.cursor = el->el_line.buffer; + el->el_line.lastchar = el->el_line.buffer + len; + el_free(cp); + el_free(line); + break; + } + + close(fd); + unlink(tempfile); + /* return CC_REFRESH; */ + return ed_newline(el, 0); +error: + el_free(line); + el_free(cp); + close(fd); + unlink(tempfile); + return CC_ERROR; +} + +/* vi_history_word(): + * Vi append word from previous input line + * [_] + * Who knows where this one came from! + * '_' in vi means 'entire current line', so 'cc' is a synonym for 'c_' + */ +protected el_action_t +/*ARGSUSED*/ +vi_history_word(EditLine *el, wint_t c __attribute__((__unused__))) +{ + const Char *wp = HIST_FIRST(el); + const Char *wep, *wsp; + int len; + Char *cp; + const Char *lim; + + if (wp == NULL) + return CC_ERROR; + + wep = wsp = NULL; + do { + while (Isspace(*wp)) + wp++; + if (*wp == 0) + break; + wsp = wp; + while (*wp && !Isspace(*wp)) + wp++; + wep = wp; + } while ((!el->el_state.doingarg || --el->el_state.argument > 0) + && *wp != 0); + + if (wsp == NULL || (el->el_state.doingarg && el->el_state.argument != 0)) + return CC_ERROR; + + cv_undo(el); + len = (int)(wep - wsp); + if (el->el_line.cursor < el->el_line.lastchar) + el->el_line.cursor++; + c_insert(el, len + 1); + cp = el->el_line.cursor; + lim = el->el_line.limit; + if (cp < lim) + *cp++ = ' '; + while (wsp < wep && cp < lim) + *cp++ = *wsp++; + el->el_line.cursor = cp; + + el->el_map.current = el->el_map.key; + return CC_REFRESH; +} + +/* vi_redo(): + * Vi redo last non-motion command + * [.] + */ +protected el_action_t +/*ARGSUSED*/ +vi_redo(EditLine *el, wint_t c __attribute__((__unused__))) +{ + c_redo_t *r = &el->el_chared.c_redo; + + if (!el->el_state.doingarg && r->count) { + el->el_state.doingarg = 1; + el->el_state.argument = r->count; + } + + el->el_chared.c_vcmd.pos = el->el_line.cursor; + el->el_chared.c_vcmd.action = r->action; + if (r->pos != r->buf) { + if (r->pos + 1 > r->lim) + /* sanity */ + r->pos = r->lim - 1; + r->pos[0] = 0; + FUN(el,push)(el, r->buf); + } + + el->el_state.thiscmd = r->cmd; + el->el_state.thisch = r->ch; + return (*el->el_map.func[r->cmd])(el, r->ch); +} diff --git a/lib/libedit/vi.h b/lib/libedit/vi.h deleted file mode 100644 index fec33b4..0000000 --- a/lib/libedit/vi.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Automatically generated file, do not edit */ -#ifndef _h_vi_c -#define _h_vi_c -protected el_action_t vi_paste_next (EditLine *, int); -protected el_action_t vi_paste_prev (EditLine *, int); -protected el_action_t vi_prev_big_word (EditLine *, int); -protected el_action_t vi_prev_word (EditLine *, int); -protected el_action_t vi_next_big_word (EditLine *, int); -protected el_action_t vi_next_word (EditLine *, int); -protected el_action_t vi_change_case (EditLine *, int); -protected el_action_t vi_change_meta (EditLine *, int); -protected el_action_t vi_insert_at_bol (EditLine *, int); -protected el_action_t vi_replace_char (EditLine *, int); -protected el_action_t vi_replace_mode (EditLine *, int); -protected el_action_t vi_substitute_char (EditLine *, int); -protected el_action_t vi_substitute_line (EditLine *, int); -protected el_action_t vi_change_to_eol (EditLine *, int); -protected el_action_t vi_insert (EditLine *, int); -protected el_action_t vi_add (EditLine *, int); -protected el_action_t vi_add_at_eol (EditLine *, int); -protected el_action_t vi_delete_meta (EditLine *, int); -protected el_action_t vi_end_big_word (EditLine *, int); -protected el_action_t vi_end_word (EditLine *, int); -protected el_action_t vi_undo (EditLine *, int); -protected el_action_t vi_command_mode (EditLine *, int); -protected el_action_t vi_zero (EditLine *, int); -protected el_action_t vi_delete_prev_char (EditLine *, int); -protected el_action_t vi_list_or_eof (EditLine *, int); -protected el_action_t vi_kill_line_prev (EditLine *, int); -protected el_action_t vi_search_prev (EditLine *, int); -protected el_action_t vi_search_next (EditLine *, int); -protected el_action_t vi_repeat_search_next (EditLine *, int); -protected el_action_t vi_repeat_search_prev (EditLine *, int); -protected el_action_t vi_next_char (EditLine *, int); -protected el_action_t vi_prev_char (EditLine *, int); -protected el_action_t vi_to_next_char (EditLine *, int); -protected el_action_t vi_to_prev_char (EditLine *, int); -protected el_action_t vi_repeat_next_char (EditLine *, int); -protected el_action_t vi_repeat_prev_char (EditLine *, int); -protected el_action_t vi_match (EditLine *, int); -protected el_action_t vi_undo_line (EditLine *, int); -protected el_action_t vi_to_column (EditLine *, int); -protected el_action_t vi_yank_end (EditLine *, int); -protected el_action_t vi_yank (EditLine *, int); -protected el_action_t vi_comment_out (EditLine *, int); -protected el_action_t vi_alias (EditLine *, int); -protected el_action_t vi_to_history_line (EditLine *, int); -protected el_action_t vi_histedit (EditLine *, int); -protected el_action_t vi_history_word (EditLine *, int); -protected el_action_t vi_redo (EditLine *, int); -#endif /* _h_vi_c */ diff --git a/share/mk/dirdeps.mk b/share/mk/dirdeps.mk new file mode 100644 index 0000000..e2c2e54 --- /dev/null +++ b/share/mk/dirdeps.mk @@ -0,0 +1,712 @@ +# $FreeBSD: releng/11.1/share/mk/dirdeps.mk 298229 2016-04-18 22:00:26Z sjg $ +# $Id: dirdeps.mk,v 1.62 2016/03/16 00:11:53 sjg Exp $ + +# Copyright (c) 2010-2013, Juniper Networks, 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. +# +# 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. + +# Much of the complexity here is for supporting cross-building. +# If a tree does not support that, simply using plain Makefile.depend +# should provide sufficient clue. +# Otherwise the recommendation is to use Makefile.depend.${MACHINE} +# as expected below. + +# Note: this file gets multiply included. +# This is what we do with DIRDEPS + +# DIRDEPS: +# This is a list of directories - relative to SRCTOP, it is +# normally only of interest to .MAKE.LEVEL 0. +# In some cases the entry may be qualified with a . +# or . suffix (see TARGET_SPEC_VARS below), +# for example to force building something for the pseudo +# machines "host" or "common" regardless of current ${MACHINE}. +# +# All unqualified entries end up being qualified with .${TARGET_SPEC} +# and partially qualified (if TARGET_SPEC_VARS has multiple +# entries) are also expanded to a full .. +# The _DIRDEP_USE target uses the suffix to set TARGET_SPEC +# correctly when visiting each entry. +# +# The fully qualified directory entries are used to construct a +# dependency graph that will drive the build later. +# +# Also, for each fully qualified directory target, we will search +# using ${.MAKE.DEPENDFILE_PREFERENCE} to find additional +# dependencies. We use Makefile.depend (default value for +# .MAKE.DEPENDFILE_PREFIX) to refer to these makefiles to +# distinguish them from others. +# +# Each Makefile.depend file sets DEP_RELDIR to be the +# the RELDIR (path relative to SRCTOP) for its directory, and +# since each Makefile.depend file includes dirdeps.mk, this +# processing is recursive and results in .MAKE.LEVEL 0 learning the +# dependencies of the tree wrt the initial directory (_DEP_RELDIR). +# +# BUILD_AT_LEVEL0 +# Indicates whether .MAKE.LEVEL 0 builds anything: +# if "no" sub-makes are used to build everything, +# if "yes" sub-makes are only used to build for other machines. +# It is best to use "no", but this can require fixing some +# makefiles to not do anything at .MAKE.LEVEL 0. +# +# TARGET_SPEC_VARS +# The default value is just MACHINE, and for most environments +# this is sufficient. The _DIRDEP_USE target actually sets +# both MACHINE and TARGET_SPEC to the suffix of the current +# target so that in the general case TARGET_SPEC can be ignored. +# +# If more than MACHINE is needed then sys.mk needs to decompose +# TARGET_SPEC and set the relevant variables accordingly. +# It is important that MACHINE be included in and actually be +# the first member of TARGET_SPEC_VARS. This allows other +# variables to be considered optional, and some of the treatment +# below relies on MACHINE being the first entry. +# Note: TARGET_SPEC cannot contain any '.'s so the target +# triple used by compiler folk won't work (directly anyway). +# +# For example: +# +# # Always list MACHINE first, +# # other variables might be optional. +# TARGET_SPEC_VARS = MACHINE TARGET_OS +# .if ${TARGET_SPEC:Uno:M*,*} != "" +# _tspec := ${TARGET_SPEC:S/,/ /g} +# MACHINE := ${_tspec:[1]} +# TARGET_OS := ${_tspec:[2]} +# # etc. +# # We need to stop that TARGET_SPEC affecting any submakes +# # and deal with MACHINE=${TARGET_SPEC} in the environment. +# TARGET_SPEC = +# # export but do not track +# .export-env TARGET_SPEC +# .export ${TARGET_SPEC_VARS} +# .for v in ${TARGET_SPEC_VARS:O:u} +# .if empty($v) +# .undef $v +# .endif +# .endfor +# .endif +# # make sure we know what TARGET_SPEC is +# # as we may need it to find Makefile.depend* +# TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,} +# + +# touch this at your peril +_DIRDEP_USE_LEVEL?= 0 +.if ${.MAKE.LEVEL} == ${_DIRDEP_USE_LEVEL} +# only the first instance is interested in all this + +# First off, we want to know what ${MACHINE} to build for. +# This can be complicated if we are using a mixture of ${MACHINE} specific +# and non-specific Makefile.depend* + +.if !target(_DIRDEP_USE) + +.if ${MAKEFILE:T} == ${.PARSEFILE} && empty(DIRDEPS) && ${.TARGETS:Uall:M*/*} != "" +# This little trick let's us do +# +# mk -f dirdeps.mk some/dir.${TARGET_SPEC} +# +all: +${.TARGETS:Nall}: all +DIRDEPS := ${.TARGETS:M*[/.]*} +# so that -DNO_DIRDEPS works +DEP_RELDIR := ${DIRDEPS:[1]:R} +# this will become DEP_MACHINE below +TARGET_MACHINE := ${DIRDEPS:[1]:E:C/,.*//} +.if ${TARGET_MACHINE:N*/*} == "" +TARGET_MACHINE := ${MACHINE} +.endif +# disable DIRDEPS_CACHE as it does not like this trick +MK_DIRDEPS_CACHE = no +.endif + +# make sure we get the behavior we expect +.MAKE.SAVE_DOLLARS = no + +# do some setup we only need once +_CURDIR ?= ${.CURDIR} +_OBJDIR ?= ${.OBJDIR} + +now_utc = ${%s:L:gmtime} +.if !defined(start_utc) +start_utc := ${now_utc} +.endif + +# make sure these are empty to start with +_DEP_TARGET_SPEC = + +# If TARGET_SPEC_VARS is other than just MACHINE +# it should be set by sys.mk or similar by now. +# TARGET_SPEC must not contain any '.'s. +TARGET_SPEC_VARS ?= MACHINE +# this is what we started with +TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,} +# this is what we mostly use below +DEP_TARGET_SPEC = ${TARGET_SPEC_VARS:S,^,DEP_,:@v@${$v:U}@:ts,} +# make sure we have defaults +.for v in ${TARGET_SPEC_VARS} +DEP_$v ?= ${$v} +.endfor + +.if ${TARGET_SPEC_VARS:[#]} > 1 +# Ok, this gets more complex (putting it mildly). +# In order to stay sane, we need to ensure that all the build_dirs +# we compute below are fully qualified wrt DEP_TARGET_SPEC. +# The makefiles may only partially specify (eg. MACHINE only), +# so we need to construct a set of modifiers to fill in the gaps. +# jot 10 should output 1 2 3 .. 10 +JOT ?= jot +_tspec_x := ${${JOT} ${TARGET_SPEC_VARS:[#]}:L:sh} +# this handles unqualified entries +M_dep_qual_fixes = C;(/[^/.,]+)$$;\1.$${DEP_TARGET_SPEC}; +# there needs to be at least one item missing for these to make sense +.for i in ${_tspec_x:[2..-1]} +_tspec_m$i := ${TARGET_SPEC_VARS:[2..$i]:@w@[^,]+@:ts,} +_tspec_a$i := ,${TARGET_SPEC_VARS:[$i..-1]:@v@$$$${DEP_$v}@:ts,} +M_dep_qual_fixes += C;(\.${_tspec_m$i})$$;\1${_tspec_a$i}; +.endfor +.else +# A harmless? default. +M_dep_qual_fixes = U +.endif + +.if !defined(.MAKE.DEPENDFILE_PREFERENCE) +# .MAKE.DEPENDFILE_PREFERENCE makes the logic below neater? +# you really want this set by sys.mk or similar +.MAKE.DEPENDFILE_PREFERENCE = ${_CURDIR}/${.MAKE.DEPENDFILE:T} +.if ${.MAKE.DEPENDFILE:E} == "${TARGET_SPEC}" +.if ${TARGET_SPEC} != ${MACHINE} +.MAKE.DEPENDFILE_PREFERENCE += ${_CURDIR}/${.MAKE.DEPENDFILE:T:R}.$${MACHINE} +.endif +.MAKE.DEPENDFILE_PREFERENCE += ${_CURDIR}/${.MAKE.DEPENDFILE:T:R} +.endif +.endif + +_default_dependfile := ${.MAKE.DEPENDFILE_PREFERENCE:[1]:T} +_machine_dependfiles := ${.MAKE.DEPENDFILE_PREFERENCE:T:M*${MACHINE}*} + +# for machine specific dependfiles we require ${MACHINE} to be at the end +# also for the sake of sanity we require a common prefix +.if !defined(.MAKE.DEPENDFILE_PREFIX) +# knowing .MAKE.DEPENDFILE_PREFIX helps +.if !empty(_machine_dependfiles) +.MAKE.DEPENDFILE_PREFIX := ${_machine_dependfiles:[1]:T:R} +.else +.MAKE.DEPENDFILE_PREFIX := ${_default_dependfile:T} +.endif +.endif + + +# this is how we identify non-machine specific dependfiles +N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}} + +.endif # !target(_DIRDEP_USE) + +# if we were included recursively _DEP_TARGET_SPEC should be valid. +.if empty(_DEP_TARGET_SPEC) +# we may or may not have included a dependfile yet +.if defined(.INCLUDEDFROMFILE) +_last_dependfile := ${.INCLUDEDFROMFILE:M${.MAKE.DEPENDFILE_PREFIX}*} +.else +_last_dependfile := ${.MAKE.MAKEFILES:M*/${.MAKE.DEPENDFILE_PREFIX}*:[-1]} +.endif +.if ${_debug_reldir:U0} +.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _last_dependfile='${_last_dependfile}' +.endif + +.if empty(_last_dependfile) || ${_last_dependfile:E:${N_notmachine}} == "" +# this is all we have to work with +DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}} +_DEP_TARGET_SPEC := ${DEP_TARGET_SPEC} +.else +_DEP_TARGET_SPEC = ${_last_dependfile:${M_dep_qual_fixes:ts:}:E} +.endif +.if !empty(_last_dependfile) +# record that we've read dependfile for this +_dirdeps_checked.${_CURDIR}.${TARGET_SPEC}: +.endif +.endif + +# by now _DEP_TARGET_SPEC should be set, parse it. +.if ${TARGET_SPEC_VARS:[#]} > 1 +# we need to parse DEP_MACHINE may or may not contain more info +_tspec := ${_DEP_TARGET_SPEC:S/,/ /g} +.for i in ${_tspec_x} +DEP_${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]} +.endfor +.for v in ${TARGET_SPEC_VARS:O:u} +.if empty(DEP_$v) +.undef DEP_$v +.endif +.endfor +.else +DEP_MACHINE := ${_DEP_TARGET_SPEC} +.endif + +# reset each time through +_build_all_dirs = + +# the first time we are included the _DIRDEP_USE target will not be defined +# we can use this as a clue to do initialization and other one time things. +.if !target(_DIRDEP_USE) +# make sure this target exists +dirdeps: beforedirdeps .WAIT +beforedirdeps: + +# We normally expect to be included by Makefile.depend.* +# which sets the DEP_* macros below. +DEP_RELDIR ?= ${RELDIR} + +# this can cause lots of output! +# set to a set of glob expressions that might match RELDIR +DEBUG_DIRDEPS ?= no + +# remember the initial value of DEP_RELDIR - we test for it below. +_DEP_RELDIR := ${DEP_RELDIR} + +.endif + +# pickup customizations +# as below you can use !target(_DIRDEP_USE) to protect things +# which should only be done once. +.-include + +.if !target(_DIRDEP_USE) +# things we skip for host tools +SKIP_HOSTDIR ?= + +NSkipHostDir = ${SKIP_HOSTDIR:N*.host*:S,$,.host*,:N.host*:S,^,${SRCTOP}/,:${M_ListToSkip}} + +# things we always skip +# SKIP_DIRDEPS allows for adding entries on command line. +SKIP_DIR += .host *.WAIT ${SKIP_DIRDEPS} +SKIP_DIR.host += ${SKIP_HOSTDIR} + +DEP_SKIP_DIR = ${SKIP_DIR} \ + ${SKIP_DIR.${DEP_TARGET_SPEC}:U} \ + ${SKIP_DIR.${DEP_MACHINE}:U} \ + ${SKIP_DIRDEPS.${DEP_MACHINE}:U} + +NSkipDir = ${DEP_SKIP_DIR:${M_ListToSkip}} + +.if defined(NODIRDEPS) || defined(WITHOUT_DIRDEPS) +NO_DIRDEPS = +.elif defined(WITHOUT_DIRDEPS_BELOW) +NO_DIRDEPS_BELOW = +.endif + +.if defined(NO_DIRDEPS) +# confine ourselves to the original dir and below. +DIRDEPS_FILTER += M${_DEP_RELDIR}* +.elif defined(NO_DIRDEPS_BELOW) +DIRDEPS_FILTER += M${_DEP_RELDIR} +.endif + +# this is what we run below +DIRDEP_MAKE?= ${.MAKE} + +# we suppress SUBDIR when visiting the leaves +# we assume sys.mk will set MACHINE_ARCH +# you can add extras to DIRDEP_USE_ENV +# if there is no makefile in the target directory, we skip it. +_DIRDEP_USE: .USE .MAKE + @for m in ${.MAKE.MAKEFILE_PREFERENCE}; do \ + test -s ${.TARGET:R}/$$m || continue; \ + echo "${TRACER}Checking ${.TARGET:R} for ${.TARGET:E} ..."; \ + MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \ + TARGET_SPEC=${.TARGET:E} \ + MACHINE=${.TARGET:E} \ + ${DIRDEP_MAKE} -C ${.TARGET:R} || exit 1; \ + break; \ + done + +.ifdef ALL_MACHINES +# this is how you limit it to only the machines we have been built for +# previously. +.if empty(ONLY_MACHINE_LIST) +.if !empty(ALL_MACHINE_LIST) +# ALL_MACHINE_LIST is the list of all legal machines - ignore anything else +_machine_list != cd ${_CURDIR} && 'ls' -1 ${ALL_MACHINE_LIST:O:u:@m@${.MAKE.DEPENDFILE:T:R}.$m@} 2> /dev/null; echo +.else +_machine_list != 'ls' -1 ${_CURDIR}/${.MAKE.DEPENDFILE_PREFIX}.* 2> /dev/null; echo +.endif +_only_machines := ${_machine_list:${NIgnoreFiles:UN*.bak}:E:O:u} +.else +_only_machines := ${ONLY_MACHINE_LIST} +.endif + +.if empty(_only_machines) +# we must be boot-strapping +_only_machines := ${TARGET_MACHINE:U${ALL_MACHINE_LIST:U${DEP_MACHINE}}} +.endif + +.else # ! ALL_MACHINES +# if ONLY_MACHINE_LIST is set, we are limited to that +# if TARGET_MACHINE is set - it is really the same as ONLY_MACHINE_LIST +# otherwise DEP_MACHINE is it - so DEP_MACHINE will match. +_only_machines := ${ONLY_MACHINE_LIST:U${TARGET_MACHINE:U${DEP_MACHINE}}:M${DEP_MACHINE}} +.endif + +.if !empty(NOT_MACHINE_LIST) +_only_machines := ${_only_machines:${NOT_MACHINE_LIST:${M_ListToSkip}}} +.endif + +# make sure we have a starting place? +DIRDEPS ?= ${RELDIR} +.endif # target + +# if repeatedly building the same target, +# we can avoid the overhead of re-computing the tree dependencies. +MK_DIRDEPS_CACHE ?= no +BUILD_DIRDEPS_CACHE ?= no +BUILD_DIRDEPS ?= yes + +.if !defined(NO_DIRDEPS) && !defined(NO_DIRDEPS_BELOW) +.if ${MK_DIRDEPS_CACHE} == "yes" +# this is where we will cache all our work +DIRDEPS_CACHE?= ${_OBJDIR}/dirdeps.cache${.TARGETS:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.} + +# just ensure this exists +build-dirdeps: + +M_oneperline = @x@\\${.newline} $$x@ + +.if ${BUILD_DIRDEPS_CACHE} == "no" +.if !target(dirdeps-cached) +# we do this via sub-make +BUILD_DIRDEPS = no + +dirdeps: dirdeps-cached +dirdeps-cached: ${DIRDEPS_CACHE} .MAKE + @echo "${TRACER}Using ${DIRDEPS_CACHE}" + @MAKELEVEL=${.MAKE.LEVEL} ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \ + dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no + +# these should generally do +BUILD_DIRDEPS_MAKEFILE ?= ${MAKEFILE} +BUILD_DIRDEPS_TARGETS ?= ${.TARGETS} + +# we need the .meta file to ensure we update if +# any of the Makefile.depend* changed. +# We do not want to compare the command line though. +${DIRDEPS_CACHE}: .META .NOMETA_CMP + +@{ echo '# Autogenerated - do NOT edit!'; echo; \ + echo 'BUILD_DIRDEPS=no'; echo; \ + echo '.include '; \ + } > ${.TARGET}.new + +@MAKELEVEL=${.MAKE.LEVEL} DIRDEPS_CACHE=${DIRDEPS_CACHE} \ + DIRDEPS="${DIRDEPS}" \ + MAKEFLAGS= ${.MAKE} -C ${_CURDIR} -f ${BUILD_DIRDEPS_MAKEFILE} \ + ${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \ + .MAKE.DEPENDFILE=.none \ + ${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \ + 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g' >> ${.TARGET}.new && \ + mv ${.TARGET}.new ${.TARGET} + +.endif +.elif !target(_count_dirdeps) +# we want to capture the dirdeps count in the cache +.END: _count_dirdeps +_count_dirdeps: .NOMETA + @echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]}' >&3 + +.endif +.elif !make(dirdeps) && !target(_count_dirdeps) +beforedirdeps: _count_dirdeps +_count_dirdeps: .NOMETA + @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} seconds=`expr ${now_utc} - ${start_utc}`" + +.endif +.endif + +.if ${BUILD_DIRDEPS} == "yes" +.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != "" +_debug_reldir = 1 +.else +_debug_reldir = 0 +.endif +.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend:L:M$x}@} != "" +_debug_search = 1 +.else +_debug_search = 0 +.endif + +# the rest is done repeatedly for every Makefile.depend we read. +# if we are anything but the original dir we care only about the +# machine type we were included for.. + +.if ${DEP_RELDIR} == "." +_this_dir := ${SRCTOP} +.else +_this_dir := ${SRCTOP}/${DEP_RELDIR} +.endif + +# on rare occasions, there can be a need for extra help +_dep_hack := ${_this_dir}/${.MAKE.DEPENDFILE_PREFIX}.inc +.-include <${_dep_hack}> + +.if ${DEP_RELDIR} != ${_DEP_RELDIR} || ${DEP_TARGET_SPEC} != ${TARGET_SPEC} +# this should be all +_machines := ${DEP_MACHINE} +.else +# this is the machine list we actually use below +_machines := ${_only_machines} + +.if defined(HOSTPROG) || ${DEP_MACHINE} == "host" +# we need to build this guy's dependencies for host as well. +_machines += host +.endif + +_machines := ${_machines:O:u} +.endif + +.if ${TARGET_SPEC_VARS:[#]} > 1 +# we need to tweak _machines +_dm := ${DEP_MACHINE} +# apply the same filtering that we do when qualifying DIRDEPS. +# M_dep_qual_fixes expects .${MACHINE}* so add (and remove) '.' +_machines := ${_machines:@DEP_MACHINE@${DEP_TARGET_SPEC}@:S,^,.,:${M_dep_qual_fixes:ts:}:O:u:S,^.,,} +DEP_MACHINE := ${_dm} +.endif + +# reset each time through +_build_dirs = + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# pickup other machines for this dir if necessary +.if ${BUILD_AT_LEVEL0:Uyes} == "no" +_build_dirs += ${_machines:@m@${_CURDIR}.$m@} +.else +_build_dirs += ${_machines:N${DEP_TARGET_SPEC}:@m@${_CURDIR}.$m@} +.if ${DEP_TARGET_SPEC} == ${TARGET_SPEC} +# pickup local dependencies now +.if ${MAKE_VERSION} < 20160220 +.-include <.depend> +.else +.dinclude <.depend> +.endif +.endif +.endif +.endif + +.if ${_debug_reldir} +.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: DIRDEPS='${DIRDEPS}' +.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _machines='${_machines}' +.endif + +.if !empty(DIRDEPS) +# these we reset each time through as they can depend on DEP_MACHINE +DEP_DIRDEPS_FILTER = \ + ${DIRDEPS_FILTER.${DEP_TARGET_SPEC}:U} \ + ${DIRDEPS_FILTER.${DEP_MACHINE}:U} \ + ${DIRDEPS_FILTER:U} +.if empty(DEP_DIRDEPS_FILTER) +# something harmless +DEP_DIRDEPS_FILTER = U +.endif + +# this is what we start with +__depdirs := ${DIRDEPS:${NSkipDir}:${DEP_DIRDEPS_FILTER:ts:}:C,//+,/,g:O:u:@d@${SRCTOP}/$d@} + +# some entries may be qualified with . +# the :M*/*/*.* just tries to limit the dirs we check to likely ones. +# the ${d:E:M*/*} ensures we don't consider junos/usr.sbin/mgd +__qual_depdirs := ${__depdirs:M*/*/*.*:@d@${exists($d):?:${"${d:E:M*/*}":?:${exists(${d:R}):?$d:}}}@} +__unqual_depdirs := ${__depdirs:${__qual_depdirs:Uno:${M_ListToSkip}}} + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# if it was called out - we likely need it. +__hostdpadd := ${DPADD:U.:M${HOST_OBJTOP}/*:S,${HOST_OBJTOP}/,,:H:${NSkipDir}:${DIRDEPS_FILTER:ts:}:S,$,.host,:N.*:@d@${SRCTOP}/$d@} +__qual_depdirs += ${__hostdpadd} +.endif + +.if ${_debug_reldir} +.info depdirs=${__depdirs} +.info qualified=${__qual_depdirs} +.info unqualified=${__unqual_depdirs} +.endif + +# _build_dirs is what we will feed to _DIRDEP_USE +_build_dirs += \ + ${__qual_depdirs:M*.host:${NSkipHostDir}:N.host} \ + ${__qual_depdirs:N*.host} \ + ${_machines:Mhost*:@m@${__unqual_depdirs:@d@$d.$m@}@:${NSkipHostDir}:N.host} \ + ${_machines:Nhost*:@m@${__unqual_depdirs:@d@$d.$m@}@} + +# qualify everything now +_build_dirs := ${_build_dirs:${M_dep_qual_fixes:ts:}:O:u} + +_build_all_dirs += ${_build_dirs} +_build_all_dirs := ${_build_all_dirs:O:u} + +.endif # empty DIRDEPS + +# Normally if doing make -V something, +# we do not want to waste time chasing DIRDEPS +# but if we want to count the number of Makefile.depend* read, we do. +.if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS}} == "" +.if !empty(_build_all_dirs) +.if ${BUILD_DIRDEPS_CACHE} == "yes" +x!= { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; \ + echo 'dirdeps: ${_build_all_dirs:${M_oneperline}}'; echo; } >&3; echo +x!= { ${_build_all_dirs:@x@${target($x):?:echo '$x: _DIRDEP_USE';}@} echo; } >&3; echo +.else +# this makes it all happen +dirdeps: ${_build_all_dirs} +.endif +${_build_all_dirs}: _DIRDEP_USE + +.if ${_debug_reldir} +.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs} +.endif + +# this builds the dependency graph +.for m in ${_machines} +# it would be nice to do :N${.TARGET} +.if !empty(__qual_depdirs) +.for q in ${__qual_depdirs:${M_dep_qual_fixes:ts:}:E:O:u:N$m} +.if ${_debug_reldir} || ${DEBUG_DIRDEPS:@x@${${DEP_RELDIR}.$m:L:M$x}${${DEP_RELDIR}.$q:L:M$x}@} != "" +.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q} +.endif +.if ${BUILD_DIRDEPS_CACHE} == "yes" +x!= { echo; echo '${_this_dir}.$m: ${_build_dirs:M*.$q:${M_oneperline}}'; echo; } >&3; echo +.else +${_this_dir}.$m: ${_build_dirs:M*.$q} +.endif +.endfor +.endif +.if ${_debug_reldir} +.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m} +.endif +.if ${BUILD_DIRDEPS_CACHE} == "yes" +x!= { echo; echo '${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m:${M_oneperline}}'; echo; } >&3; echo +.else +${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m} +.endif +.endfor + +.endif + +# Now find more dependencies - and recurse. +.for d in ${_build_all_dirs} +.if !target(_dirdeps_checked.$d) +# once only +_dirdeps_checked.$d: +.if ${_debug_search} +.info checking $d +.endif +# Note: _build_all_dirs is fully qualifed so d:R is always the directory +.if exists(${d:R}) +# Warning: there is an assumption here that MACHINE is always +# the first entry in TARGET_SPEC_VARS. +# If TARGET_SPEC and MACHINE are insufficient, you have a problem. +_m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:S;${MACHINE};${d:E:C/,.*//};:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]} +.if !empty(_m) +# M_dep_qual_fixes isn't geared to Makefile.depend +_qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}} +.if ${_debug_search} +.info Looking for ${_qm} +.endif +# we pass _DEP_TARGET_SPEC to tell the next step what we want +_DEP_TARGET_SPEC := ${d:E} +# some makefiles may still look at this +_DEP_MACHINE := ${d:E:C/,.*//} +# set this "just in case" +# we can skip :tA since we computed the path above +DEP_RELDIR := ${_m:H:S,${SRCTOP}/,,} +# and reset this +DIRDEPS = +.if ${_debug_reldir} && ${_qm} != ${_m} +.info loading ${_m} for ${d:E} +.endif +.include <${_m}> +.endif +.endif +.endif +.endfor + +.endif # -V +.endif # BUILD_DIRDEPS + +.elif ${.MAKE.LEVEL} > 42 +.error You should have stopped recursing by now. +.else +# we are building something +DEP_RELDIR := ${RELDIR} +_DEP_RELDIR := ${RELDIR} +# pickup local dependencies +.if ${MAKE_VERSION} < 20160220 +.-include <.depend> +.else +.dinclude <.depend> +.endif +.endif + +# bootstrapping new dependencies made easy? +.if !target(bootstrap) && (make(bootstrap) || \ + make(bootstrap-this) || \ + make(bootstrap-recurse) || \ + make(bootstrap-empty)) + +.if exists(${.CURDIR}/${.MAKE.DEPENDFILE:T}) +# stop here +${.TARGETS:Mboot*}: +.elif !make(bootstrap-empty) +# find a Makefile.depend to use as _src +_src != cd ${.CURDIR} && for m in ${.MAKE.DEPENDFILE_PREFERENCE:T:S,${MACHINE},*,}; do test -s $$m || continue; echo $$m; break; done; echo +.if empty(_src) +.error cannot find any of ${.MAKE.DEPENDFILE_PREFERENCE:T}${.newline}Use: bootstrap-empty +.endif + +_src?= ${.MAKE.DEPENDFILE:T} + +# just create Makefile.depend* for this dir +bootstrap-this: .NOTMAIN + @echo Bootstrapping ${RELDIR}/${.MAKE.DEPENDFILE:T} from ${_src:T} + (cd ${.CURDIR} && sed 's,${_src:E},${MACHINE},g' ${_src} > ${.MAKE.DEPENDFILE:T}) + +# create Makefile.depend* for this dir and its dependencies +bootstrap: bootstrap-recurse +bootstrap-recurse: bootstrap-this + +_mf := ${.PARSEFILE} +bootstrap-recurse: .NOTMAIN .MAKE + @cd ${SRCTOP} && \ + for d in `cd ${RELDIR} && ${.MAKE} -B -f ${"${.MAKEFLAGS:M-n}":?${_src}:${.MAKE.DEPENDFILE:T}} -V DIRDEPS`; do \ + test -d $$d || d=$${d%.*}; \ + test -d $$d || continue; \ + echo "Checking $$d for bootstrap ..."; \ + (cd $$d && ${.MAKE} -f ${_mf} bootstrap-recurse); \ + done + +.endif + +# create an empty Makefile.depend* to get the ball rolling. +bootstrap-empty: .NOTMAIN .NOMETA + @echo Creating empty ${RELDIR}/${.MAKE.DEPENDFILE:T}; \ + echo You need to build ${RELDIR} to correctly populate it. + @{ echo DIRDEPS=; echo ".include "; } > ${.CURDIR}/${.MAKE.DEPENDFILE:T} + +.endif diff --git a/share/mk/local.dirdeps.mk b/share/mk/local.dirdeps.mk index a934225..183596e 100644 --- a/share/mk/local.dirdeps.mk +++ b/share/mk/local.dirdeps.mk @@ -22,7 +22,7 @@ #.info M_dep_qual_fixes=${M_dep_qual_fixes} # Cheat for including src.libnames.mk -____: +____: # Pull in _INTERNALLIBS .include @@ -109,7 +109,7 @@ _PROGS_SRCS+= ${SRCS${s}${_prog}} .endif .endfor # .for s in . _ -# Add in assumed source (bsd.prog.mk) +# Add in assumed source (ubix.prog.mk) .if !target(${_prog}) .if defined(PROG_CXX) _PROGS_SRCS+= ${_prog}.cc diff --git a/share/mk/local.init.mk b/share/mk/local.init.mk index ba7c731..52fafac 100644 --- a/share/mk/local.init.mk +++ b/share/mk/local.init.mk @@ -1,7 +1,7 @@ # $FreeBSD: releng/11.1/share/mk/local.init.mk 294349 2016-01-19 22:41:44Z bdrewery $ .if ${.MAKE.MODE:Mmeta*} != "" -.if !empty(SUBDIR) && !defined(LIB) && !defined(PROG) && ${.MAKE.MAKEFILES:M*bsd.prog.mk} == "" +.if !empty(SUBDIR) && !defined(LIB) && !defined(PROG) && ${.MAKE.MAKEFILES:M*ubix.prog.mk} == "" .if ${.MAKE.MODE:Mleaf*} != "" # we only want leaf dirs to build in meta mode... and we are not one .MAKE.MODE = normal diff --git a/share/mk/local.sys.mk b/share/mk/local.sys.mk index 59b3cc8..cc1816f 100644 --- a/share/mk/local.sys.mk +++ b/share/mk/local.sys.mk @@ -50,9 +50,9 @@ # we can afford to use cookies to prevent some targets # re-running needlessly but only when using filemon. # Targets that should support the meta mode cookie handling should just be -# added to META_TARGETS. If bsd.sys.mk cannot be included then ${META_DEPS} +# added to META_TARGETS. If ubix.sys.mk cannot be included then ${META_DEPS} # should be added as a target dependency as well. Otherwise the target -# is added to in bsd.sys.mk since it comes last. +# is added to in ubix.sys.mk since it comes last. .if ${.MAKE.MODE:Mnofilemon} == "" # Prepend .OBJDIR if not already there. _META_COOKIE_COND= "${.TARGET:M${.OBJDIR}/*}" == "" diff --git a/share/mk/src.sys.mk b/share/mk/src.sys.mk index 15bcaaf..a3d4eba 100644 --- a/share/mk/src.sys.mk +++ b/share/mk/src.sys.mk @@ -13,7 +13,7 @@ !target(_srcconf_included_) # Validate that the user didn't try setting an env-only variable in -# their src.conf. This benefits from already including bsd.mkopt.mk. +# their src.conf. This benefits from already including ubix.mkopt.mk. .for var in ${__ENV_ONLY_OPTIONS} __presrcconf_${var}:= ${MK_${var}:U-}${WITHOUT_${var}:Uno:Dyes}${WITH_${var}:Uno:Dyes} .endfor @@ -34,6 +34,6 @@ .endif # SRCCONF .endif -# tempting, but bsd.compiler.mk causes problems this early -# probably need to remove dependence on bsd.own.mk +# tempting, but ubix.compiler.mk causes problems this early +# probably need to remove dependence on ubix.own.mk #.include "src.opts.mk" diff --git a/share/mk/sys.mk b/share/mk/sys.mk index 27b0899..e5c4ff7 100644 --- a/share/mk/sys.mk +++ b/share/mk/sys.mk @@ -1,7 +1,7 @@ # from: @(#)sys.mk 8.2 (Berkeley) 3/21/94 # $FreeBSD: releng/11.1/share/mk/sys.mk 320297 2017-06-23 20:25:58Z bdrewery $ -unix ?= We run FreeBSD, not UNIX. +unix ?= We run UbixOS, not UNIX. .FreeBSD ?= true .if !defined(%POSIX)