mirror of
https://github.com/darlinghq/darling-shell_cmds.git
synced 2024-11-23 03:29:46 +00:00
shell_cmds-207.40.1
This commit is contained in:
parent
3222d2ad05
commit
74fe64ca01
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*~
|
||||
xcuserdata/
|
||||
build/
|
||||
project.xcworkspace/
|
@ -307,6 +307,7 @@ but are implemented as scripts using a builtin command of the same name.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr csh 1 ,
|
||||
.Xr dash 1 ,
|
||||
.Xr echo 1 ,
|
||||
.Xr false 1 ,
|
||||
.Xr info 1 ,
|
||||
@ -320,7 +321,8 @@ but are implemented as scripts using a builtin command of the same name.
|
||||
.Xr test 1 ,
|
||||
.Xr time 1 ,
|
||||
.Xr true 1 ,
|
||||
.Xr which 1
|
||||
.Xr which 1 ,
|
||||
.Xr zsh 1
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
|
45
date/date.1
45
date/date.1
@ -30,9 +30,9 @@
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" @(#)date.1 8.3 (Berkeley) 4/28/95
|
||||
.\" $FreeBSD: src/bin/date/date.1,v 1.72 2005/02/13 22:25:09 ru Exp $
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd August 16, 2007
|
||||
.Dd May 7, 2015
|
||||
.Dt DATE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -40,8 +40,8 @@
|
||||
.Nd display or set date and time
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl ju
|
||||
.Op Fl r Ar seconds
|
||||
.Op Fl jRu
|
||||
.Op Fl r Ar seconds | Ar filename
|
||||
.Oo
|
||||
.Fl v
|
||||
.Sm off
|
||||
@ -58,7 +58,7 @@
|
||||
.Ar MM Oo Oo Ar cc Oc Ar yy Oc Op Ar .ss
|
||||
.Sm on
|
||||
.Nm
|
||||
.Op Fl jnu
|
||||
.Op Fl jnRu
|
||||
.Fl f Ar input_fmt new_date
|
||||
.Op Cm + Ns Ar output_fmt
|
||||
.Nm
|
||||
@ -80,7 +80,7 @@ both the kernel clock and the hardware clock are updated.
|
||||
.Pp
|
||||
Only the superuser may set the date,
|
||||
and if the system securelevel (see
|
||||
.Xr securelevel 8 )
|
||||
.Xr securelevel 7 )
|
||||
is greater than 1,
|
||||
the time may not be changed by more than 1 second.
|
||||
.Pp
|
||||
@ -132,6 +132,16 @@ The
|
||||
.Fl n
|
||||
option suppresses this behavior and causes the time to be set only on the
|
||||
current machine.
|
||||
.It Fl R
|
||||
Use RFC 2822 date and time output format. This is equivalent to use
|
||||
.Dq Li %a, %d %b %Y \&%T %z
|
||||
as
|
||||
.Ar output_fmt
|
||||
while
|
||||
.Ev LC_TIME
|
||||
is set to the
|
||||
.Dq C
|
||||
locale .
|
||||
.It Fl r Ar seconds
|
||||
Print the date and time represented by
|
||||
.Ar seconds ,
|
||||
@ -142,6 +152,9 @@ is the number of seconds since the Epoch
|
||||
see
|
||||
.Xr time 3 ) ,
|
||||
and can be specified in decimal, octal, or hex.
|
||||
.It Fl r Ar filename
|
||||
Print the date and time of the last modification of
|
||||
.Ar filename .
|
||||
.It Fl t Ar minutes_west
|
||||
Set the system's value for minutes west of
|
||||
.Tn GMT .
|
||||
@ -221,6 +234,14 @@ When the date is adjusted to a specific value that occurs twice
|
||||
the resulting timezone will be set so that the date matches the earlier of
|
||||
the two times.
|
||||
.Pp
|
||||
It is not possible to adjust a date to an invalid absolute day, so using
|
||||
the switches
|
||||
.Fl v No 31d Fl v No 12m
|
||||
will simply fail five months of the year.
|
||||
It is therefore usual to set the month before setting the day; using
|
||||
.Fl v No 12m Fl v No 31d
|
||||
always works.
|
||||
.Pp
|
||||
Adjusting the date by months is inherently ambiguous because
|
||||
a month is a unit of variable length depending on the current date.
|
||||
This kind of date adjustment is applied in the most intuitive way.
|
||||
@ -339,9 +360,9 @@ will display the last day of February in the year 2000:
|
||||
.Pp
|
||||
.Dl "Tue Feb 29 03:18:00 GMT 2000"
|
||||
.Pp
|
||||
So will do the command:
|
||||
So will the command:
|
||||
.Pp
|
||||
.Dl "date -v30d -v3m -v0y -v-1m"
|
||||
.Dl "date -v3m -v30d -v0y -v-1m"
|
||||
.Pp
|
||||
because there is no such date as the 30th of February.
|
||||
.Pp
|
||||
@ -423,10 +444,11 @@ Able to set the local date, but unable to set it globally
|
||||
For more information about legacy mode, see
|
||||
.Xr compat 5 .
|
||||
.Sh SEE ALSO
|
||||
.Xr locale 1 ,
|
||||
.Xr gettimeofday 2 ,
|
||||
.Xr getutxent 3 ,
|
||||
.Xr strftime 3 ,
|
||||
.Xr strptime 3 ,
|
||||
.Xr utmpx 5 ,
|
||||
.Xr timed 8
|
||||
.Rs
|
||||
.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD"
|
||||
@ -438,6 +460,11 @@ The
|
||||
.Nm
|
||||
utility is expected to be compatible with
|
||||
.St -p1003.2 .
|
||||
The
|
||||
.Fl d , f , j , n , r , t ,
|
||||
and
|
||||
.Fl v
|
||||
options are all extensions to the standard.
|
||||
.Sh HISTORY
|
||||
A
|
||||
.Nm
|
||||
|
81
date/date.c
81
date/date.c
@ -40,17 +40,15 @@ static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95";
|
||||
#endif
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: src/bin/date/date.c,v 1.47 2005/01/10 08:39:21 imp Exp $");
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <locale.h>
|
||||
#ifndef __APPLE__
|
||||
#include <libutil.h>
|
||||
#endif /* !__APPLE__ */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -60,7 +58,6 @@ __FBSDID("$FreeBSD: src/bin/date/date.c,v 1.47 2005/01/10 08:39:21 imp Exp $");
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <get_compat.h>
|
||||
#include <util.h>
|
||||
#else
|
||||
#define COMPAT_MODE(a,b) (1)
|
||||
#endif /* __APPLE__ */
|
||||
@ -72,20 +69,26 @@ __FBSDID("$FreeBSD: src/bin/date/date.c,v 1.47 2005/01/10 08:39:21 imp Exp $");
|
||||
#define TM_YEAR_BASE 1900
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define st_mtim st_mtimespec
|
||||
#endif
|
||||
|
||||
static time_t tval;
|
||||
int retval;
|
||||
int unix2003_std = 0; /* to determine legacy vs std mode */
|
||||
static int unix2003_std; /* to determine legacy vs std mode */
|
||||
|
||||
static void setthetime(const char *, const char *, int, int);
|
||||
static void badformat(void);
|
||||
static void usage(void);
|
||||
|
||||
static const char *rfc2822_format = "%a, %d %b %Y %T %z";
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct timezone tz;
|
||||
int ch, rflag;
|
||||
int jflag, nflag;
|
||||
int jflag, nflag, Rflag;
|
||||
const char *format;
|
||||
char buf[1024];
|
||||
char *endptr, *fmt;
|
||||
@ -94,6 +97,7 @@ main(int argc, char *argv[])
|
||||
struct vary *v;
|
||||
const struct vary *badv;
|
||||
struct tm lt;
|
||||
struct stat sb;
|
||||
|
||||
unix2003_std = COMPAT_MODE("bin/date", "unix2003"); /* Determine the STD */
|
||||
|
||||
@ -102,9 +106,9 @@ main(int argc, char *argv[])
|
||||
(void) setlocale(LC_TIME, "");
|
||||
tz.tz_dsttime = tz.tz_minuteswest = 0;
|
||||
rflag = 0;
|
||||
jflag = nflag = 0;
|
||||
jflag = nflag = Rflag = 0;
|
||||
set_timezone = 0;
|
||||
while ((ch = getopt(argc, argv, "d:f:jnr:t:uv:")) != -1)
|
||||
while ((ch = getopt(argc, argv, "d:f:jnRr:t:uv:")) != -1)
|
||||
switch((char)ch) {
|
||||
case 'd': /* daylight savings time */
|
||||
tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0;
|
||||
@ -121,11 +125,18 @@ main(int argc, char *argv[])
|
||||
case 'n': /* don't set network */
|
||||
nflag = 1;
|
||||
break;
|
||||
case 'R': /* RFC 2822 datetime format */
|
||||
Rflag = 1;
|
||||
break;
|
||||
case 'r': /* user specified seconds */
|
||||
rflag = 1;
|
||||
tval = strtoq(optarg, &tmp, 0);
|
||||
if (*tmp != 0)
|
||||
usage();
|
||||
if (*tmp != 0) {
|
||||
if (stat(optarg, &sb) == 0)
|
||||
tval = sb.st_mtim.tv_sec;
|
||||
else
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 't': /* minutes west of UTC */
|
||||
/* error check; don't allow "PST" */
|
||||
@ -150,7 +161,7 @@ main(int argc, char *argv[])
|
||||
* If -d or -t, set the timezone or daylight savings time; this
|
||||
* doesn't belong here; the kernel should not know about either.
|
||||
*/
|
||||
if (set_timezone && settimeofday((struct timeval *)NULL, &tz))
|
||||
if (set_timezone && settimeofday(NULL, &tz) != 0)
|
||||
err(1, "settimeofday (timezone)");
|
||||
|
||||
if (!rflag && time(&tval) == -1)
|
||||
@ -158,6 +169,9 @@ main(int argc, char *argv[])
|
||||
|
||||
format = "%+";
|
||||
|
||||
if (Rflag)
|
||||
format = rfc2822_format;
|
||||
|
||||
/* allow the operands in any order */
|
||||
if (*argv && **argv == '+') {
|
||||
format = *argv + 1;
|
||||
@ -191,6 +205,14 @@ main(int argc, char *argv[])
|
||||
usage();
|
||||
}
|
||||
vary_destroy(v);
|
||||
|
||||
if (format == rfc2822_format)
|
||||
/*
|
||||
* When using RFC 2822 datetime format, don't honor the
|
||||
* locale.
|
||||
*/
|
||||
setlocale(LC_TIME, "C");
|
||||
|
||||
(void)strftime(buf, sizeof(buf), format, <);
|
||||
(void)printf("%s\n", buf);
|
||||
if (fflush(stdout))
|
||||
@ -211,13 +233,17 @@ main(int argc, char *argv[])
|
||||
static void
|
||||
setthetime(const char *fmt, const char *p, int jflag, int nflag)
|
||||
{
|
||||
struct utmpx utx;
|
||||
struct tm *lt;
|
||||
struct timeval tv;
|
||||
const char *dot, *t;
|
||||
int century;
|
||||
size_t length;
|
||||
|
||||
lt = localtime(&tval);
|
||||
lt->tm_isdst = -1; /* divine correct DST */
|
||||
|
||||
if (fmt != NULL) {
|
||||
lt = localtime(&tval);
|
||||
t = strptime(p, fmt, lt);
|
||||
if (t == NULL) {
|
||||
fprintf(stderr, "Failed conversion of ``%s''"
|
||||
@ -238,8 +264,6 @@ setthetime(const char *fmt, const char *p, int jflag, int nflag)
|
||||
badformat();
|
||||
}
|
||||
|
||||
lt = localtime(&tval);
|
||||
|
||||
if (dot != NULL) { /* .ss */
|
||||
dot++; /* *dot++ = '\0'; */
|
||||
if (strlen(dot) != 2)
|
||||
@ -294,9 +318,6 @@ setthetime(const char *fmt, const char *p, int jflag, int nflag)
|
||||
}
|
||||
}
|
||||
|
||||
/* Let mktime() decide whether summer time is in effect. */
|
||||
lt->tm_isdst = -1;
|
||||
|
||||
/* convert broken-down time to GMT clock time */
|
||||
if ((tval = mktime(lt)) == -1)
|
||||
errx(1, "nonexistent time");
|
||||
@ -304,17 +325,16 @@ setthetime(const char *fmt, const char *p, int jflag, int nflag)
|
||||
if (!jflag) {
|
||||
/* set the time */
|
||||
if (nflag || netsettime(tval)) {
|
||||
struct utmpx ut;
|
||||
bzero(&ut, sizeof(ut));
|
||||
ut.ut_type = OLD_TIME;
|
||||
(void)gettimeofday(&ut.ut_tv, NULL);
|
||||
pututxline(&ut);
|
||||
ut.ut_tv.tv_sec = tval;
|
||||
ut.ut_tv.tv_usec = 0;
|
||||
if (settimeofday(&ut.ut_tv, NULL))
|
||||
utx.ut_type = OLD_TIME;
|
||||
(void)gettimeofday(&utx.ut_tv, NULL);
|
||||
pututxline(&utx);
|
||||
tv.tv_sec = tval;
|
||||
tv.tv_usec = 0;
|
||||
if (settimeofday(&tv, NULL) != 0)
|
||||
err(1, "settimeofday (timeval)");
|
||||
ut.ut_type = NEW_TIME;
|
||||
pututxline(&ut);
|
||||
utx.ut_type = NEW_TIME;
|
||||
(void)gettimeofday(&utx.ut_tv, NULL);
|
||||
pututxline(&utx);
|
||||
}
|
||||
|
||||
if ((p = getlogin()) == NULL)
|
||||
@ -334,9 +354,10 @@ static void
|
||||
usage(void)
|
||||
{
|
||||
(void)fprintf(stderr, "%s\n%s\n",
|
||||
"usage: date [-jnu] [-d dst] [-r seconds] [-t west] "
|
||||
"usage: date [-jnRu] [-d dst] [-r seconds] [-t west] "
|
||||
"[-v[+|-]val[ymwdHMS]] ... ",
|
||||
unix2003_std ? " "
|
||||
unix2003_std ?
|
||||
" "
|
||||
"[-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]" :
|
||||
" "
|
||||
"[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]");
|
||||
|
22
date/date.plist.part
Normal file
22
date/date.plist.part
Normal file
@ -0,0 +1,22 @@
|
||||
<dict>
|
||||
<key>OpenSourceProject</key>
|
||||
<string>date</string>
|
||||
<key>OpenSourceVersion</key>
|
||||
<string>2015-05-07</string>
|
||||
<key>OpenSourceWebsiteURL</key>
|
||||
<string>http://svnweb.freebsd.org/base/head/bin/date/</string>
|
||||
<key>OpenSourceSCM</key>
|
||||
<string>svn co http://svn.freebsd.org/base/head/bin/date/</string>
|
||||
<key>OpenSourceImportDate</key>
|
||||
<string>2015-11-06</string>
|
||||
<key>OpenSourceModifications</key>
|
||||
<array>
|
||||
<string>date.1: discuss conformance changes</string>
|
||||
<string>date.1: remove bogus utmpx path</string>
|
||||
<string>date.c: conformance changes for date format and exit value</string>
|
||||
<string>date.c: fix crash on invalid dates (7999711)</string>
|
||||
<string>date.c: st_mtim compatibility</string>
|
||||
</array>
|
||||
<key>OpenSourceLicense</key>
|
||||
<string>bsd</string>
|
||||
</dict>
|
@ -27,7 +27,9 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)extern.h 8.1 (Berkeley) 5/31/93
|
||||
* $FreeBSD: src/bin/date/extern.h,v 1.7 2004/04/06 20:06:45 markm Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
extern int retval;
|
||||
|
||||
int netsettime(time_t);
|
||||
|
@ -34,7 +34,7 @@ static char sccsid[] = "@(#)netdate.c 8.1 (Berkeley) 5/31/93";
|
||||
#endif
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: src/bin/date/netdate.c,v 1.18 2004/04/06 20:06:45 markm Exp $");
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
@ -55,8 +55,6 @@ __FBSDID("$FreeBSD: src/bin/date/netdate.c,v 1.18 2004/04/06 20:06:45 markm Exp
|
||||
#define WAITACK 2 /* seconds */
|
||||
#define WAITDATEACK 5 /* seconds */
|
||||
|
||||
extern int retval;
|
||||
|
||||
/*
|
||||
* Set the date in the machines controlled by timedaemons by communicating the
|
||||
* new date to the local timedaemon. If the timedaemon is in the master state,
|
||||
@ -87,7 +85,7 @@ netsettime(time_t tval)
|
||||
dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
|
||||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s < 0) {
|
||||
if (errno != EPROTONOSUPPORT)
|
||||
if (errno != EAFNOSUPPORT)
|
||||
warn("timed");
|
||||
return (retval = 2);
|
||||
}
|
||||
@ -108,14 +106,14 @@ netsettime(time_t tval)
|
||||
warnx("all ports in use");
|
||||
goto bad;
|
||||
}
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.tsp_type = TSP_SETDATE;
|
||||
msg.tsp_vers = TSPVERSION;
|
||||
if (gethostname(hostname, sizeof(hostname))) {
|
||||
warn("gethostname");
|
||||
goto bad;
|
||||
}
|
||||
(void)strncpy(msg.tsp_name, hostname, sizeof(msg.tsp_name) - 1);
|
||||
msg.tsp_name[sizeof(msg.tsp_name) - 1] = '\0';
|
||||
(void)strlcpy(msg.tsp_name, hostname, sizeof(msg.tsp_name));
|
||||
msg.tsp_seq = htons((u_short)0);
|
||||
msg.tsp_time.tv_sec = htonl((u_long)tval);
|
||||
msg.tsp_time.tv_usec = htonl((u_long)0);
|
||||
|
@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: src/bin/date/vary.c,v 1.16 2004/08/09 13:43:39 yar Exp $");
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <err.h>
|
||||
#include <time.h>
|
||||
|
@ -23,7 +23,7 @@
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: src/bin/date/vary.h,v 1.4 1999/08/27 23:14:00 peter Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
struct vary {
|
||||
|
@ -101,9 +101,6 @@ queryuser(char *argv[])
|
||||
(void)fprintf(stderr, "\n");
|
||||
(void)fflush(stderr);
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
return (resp[0] == 'y');
|
||||
#else
|
||||
return (rpmatch(resp) == 1);
|
||||
#endif
|
||||
|
||||
return (rpmatch(resp) == 1);
|
||||
}
|
||||
|
58
kill/kill.1
58
kill/kill.1
@ -1,3 +1,4 @@
|
||||
.\"-
|
||||
.\" Copyright (c) 1980, 1990, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
.\"
|
||||
@ -12,10 +13,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 University of
|
||||
.\" California, Berkeley and its contributors.
|
||||
.\" 4. Neither the name of the University nor the names of its contributors
|
||||
.\" may be used to endorse or promote products derived from this software
|
||||
.\" without specific prior written permission.
|
||||
@ -33,7 +30,7 @@
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" @(#)kill.1 8.2 (Berkeley) 4/28/95
|
||||
.\" $FreeBSD: src/bin/kill/kill.1,v 1.11.2.1 2000/12/08 13:34:35 ru Exp $
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd April 28, 1995
|
||||
.Dt KILL 1
|
||||
@ -44,29 +41,27 @@
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl s Ar signal_name
|
||||
.Ar pid
|
||||
\&...
|
||||
.Ar pid ...
|
||||
.Nm
|
||||
.Fl l
|
||||
.Op Ar exit_status
|
||||
.Nm
|
||||
.Fl signal_name
|
||||
.Ar pid
|
||||
\&...
|
||||
.Fl Ar signal_name
|
||||
.Ar pid ...
|
||||
.Nm
|
||||
.Fl signal_number
|
||||
.Ar pid
|
||||
\&...
|
||||
.Fl Ar signal_number
|
||||
.Ar pid ...
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility sends a signal to the processes specified by the pid operand(s).
|
||||
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:
|
||||
.Pp
|
||||
.Bl -tag -width Ds
|
||||
.Bl -tag -width indent
|
||||
.It Fl s Ar signal_name
|
||||
A symbolic signal name specifying the signal to be sent instead of the
|
||||
default
|
||||
@ -75,25 +70,26 @@ default
|
||||
If no operand is given, list the signal names; otherwise, write
|
||||
the signal name corresponding to
|
||||
.Ar exit_status .
|
||||
.It Fl signal_name
|
||||
.It Fl Ar signal_name
|
||||
A symbolic signal name specifying the signal to be sent instead of the
|
||||
default
|
||||
.Dv TERM .
|
||||
.It Fl signal_number
|
||||
.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 Ds -compact
|
||||
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:
|
||||
.Bl -tag -width Ds -compact
|
||||
.Pp
|
||||
.Bl -tag -width indent -compact
|
||||
.It 1
|
||||
HUP (hang up)
|
||||
.It 2
|
||||
@ -116,27 +112,41 @@ command which is similar or identical to this utility.
|
||||
Consult the
|
||||
.Xr builtin 1
|
||||
manual page.
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh EXAMPLES
|
||||
Terminate
|
||||
the processes with PIDs 142 and 157:
|
||||
.Pp
|
||||
.Dl "kill 142 157"
|
||||
.Pp
|
||||
Send the hangup signal
|
||||
.Pq Dv SIGHUP
|
||||
to the process with PID 507:
|
||||
.Pp
|
||||
.Dl "kill -s HUP 507"
|
||||
.Sh SEE ALSO
|
||||
.Xr builtin 1 ,
|
||||
.Xr csh 1 ,
|
||||
.Xr killall 1 ,
|
||||
.Xr ps 1 ,
|
||||
.Xr sh 1 ,
|
||||
.Xr kill 2 ,
|
||||
.Xr sigaction 2
|
||||
.Sh STANDARDS
|
||||
The
|
||||
.Nm
|
||||
function is expected to be
|
||||
utility is expected to be
|
||||
.St -p1003.2
|
||||
compatible.
|
||||
.Sh HISTORY
|
||||
A
|
||||
.Nm
|
||||
command appeared in
|
||||
.At v6 .
|
||||
.At v3 .
|
||||
.Sh BUGS
|
||||
A replacement for the command
|
||||
.Dq Li kill 0
|
||||
for
|
||||
.Xr csh 1
|
||||
.Xr csh 1
|
||||
users should be provided.
|
||||
|
117
kill/kill.c
117
kill/kill.c
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*-
|
||||
* Copyright (c) 1988, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
@ -10,10 +10,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 University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
@ -30,7 +26,12 @@
|
||||
* 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\
|
||||
@ -38,12 +39,11 @@ static char const copyright[] =
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)kill.c 8.4 (Berkeley) 4/28/95";
|
||||
#endif
|
||||
static const char rcsid[] =
|
||||
"$FreeBSD: src/bin/kill/kill.c,v 1.11.2.1 2001/08/01 02:42:56 obrien Exp $";
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
@ -53,18 +53,24 @@ static const char rcsid[] =
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main __P((int, char *[]));
|
||||
void nosig __P((char *));
|
||||
void printsignals __P((FILE *));
|
||||
int signame_to_signum __P((char *));
|
||||
void usage __P((void));
|
||||
#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);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define sys_nsig NSIG
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int errors, numsig, pid;
|
||||
int errors, numsig, pid, ret;
|
||||
char *ep;
|
||||
|
||||
if (argc < 2)
|
||||
@ -82,16 +88,16 @@ main(argc, argv)
|
||||
usage();
|
||||
numsig = strtol(*argv, &ep, 10);
|
||||
if (!**argv || *ep)
|
||||
errx(1, "illegal signal number: %s", *argv);
|
||||
errx(2, "illegal signal number: %s", *argv);
|
||||
if (numsig >= 128)
|
||||
numsig -= 128;
|
||||
if (numsig <= 0 || numsig >= NSIG)
|
||||
if (numsig <= 0 || numsig >= sys_nsig)
|
||||
nosig(*argv);
|
||||
printf("%s\n", sys_signame[numsig]);
|
||||
exit(0);
|
||||
return (0);
|
||||
}
|
||||
printsignals(stdout);
|
||||
exit(0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (!strcmp(*argv, "-s")) {
|
||||
@ -106,7 +112,7 @@ main(argc, argv)
|
||||
} else
|
||||
numsig = 0;
|
||||
argc--, argv++;
|
||||
} else if (**argv == '-') {
|
||||
} else if (**argv == '-' && *(*argv + 1) != '-') {
|
||||
++*argv;
|
||||
if (isalpha(**argv)) {
|
||||
if ((numsig = signame_to_signum(*argv)) < 0)
|
||||
@ -114,73 +120,84 @@ main(argc, argv)
|
||||
} else if (isdigit(**argv)) {
|
||||
numsig = strtol(*argv, &ep, 10);
|
||||
if (!**argv || *ep)
|
||||
errx(1, "illegal signal number: %s", *argv);
|
||||
if (numsig < 0 || numsig >= NSIG)
|
||||
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++) {
|
||||
pid = strtol(*argv, &ep, 10);
|
||||
if (!**argv || *ep) {
|
||||
warnx("illegal process id: %s", *argv);
|
||||
errors = 1;
|
||||
} else if (kill(pid, numsig) == -1) {
|
||||
#ifdef SHELL
|
||||
if (**argv == '%')
|
||||
ret = killjob(*argv, numsig);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
pid = strtol(*argv, &ep, 10);
|
||||
if (!**argv || *ep)
|
||||
errx(2, "illegal process id: %s", *argv);
|
||||
ret = kill(pid, numsig);
|
||||
}
|
||||
if (ret == -1) {
|
||||
warn("%s", *argv);
|
||||
errors = 1;
|
||||
}
|
||||
}
|
||||
|
||||
exit(errors);
|
||||
return (errors);
|
||||
}
|
||||
|
||||
int
|
||||
signame_to_signum(sig)
|
||||
char *sig;
|
||||
static int
|
||||
signame_to_signum(const char *sig)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (!strncasecmp(sig, "sig", (size_t)3))
|
||||
if (strncasecmp(sig, "SIG", 3) == 0)
|
||||
sig += 3;
|
||||
for (n = 1; n < NSIG; n++) {
|
||||
for (n = 1; n < sys_nsig; n++) {
|
||||
if (!strcasecmp(sys_signame[n], sig))
|
||||
return (n);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
nosig(name)
|
||||
char *name;
|
||||
static void
|
||||
nosig(const char *name)
|
||||
{
|
||||
|
||||
warnx("unknown signal %s; valid signals:", name);
|
||||
printsignals(stderr);
|
||||
exit(1);
|
||||
#ifdef SHELL
|
||||
error(NULL);
|
||||
#else
|
||||
exit(2);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
printsignals(fp)
|
||||
FILE *fp;
|
||||
static void
|
||||
printsignals(FILE *fp)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 1; n < NSIG; n++) {
|
||||
for (n = 1; n < sys_nsig; n++) {
|
||||
(void)fprintf(fp, "%s", sys_signame[n]);
|
||||
if (n == (NSIG / 2) || n == (NSIG - 1))
|
||||
if (n == (sys_nsig / 2) || n == (sys_nsig - 1))
|
||||
(void)fprintf(fp, "\n");
|
||||
else
|
||||
(void)fprintf(fp, " ");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
usage()
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
|
||||
@ -188,5 +205,9 @@ usage()
|
||||
" kill -l [exit_status]",
|
||||
" kill -signal_name pid ...",
|
||||
" kill -signal_number pid ...");
|
||||
exit(1);
|
||||
#ifdef SHELL
|
||||
error(NULL);
|
||||
#else
|
||||
exit(2);
|
||||
#endif
|
||||
}
|
||||
|
18
kill/kill.plist.part
Normal file
18
kill/kill.plist.part
Normal file
@ -0,0 +1,18 @@
|
||||
<dict>
|
||||
<key>OpenSourceProject</key>
|
||||
<string>kill</string>
|
||||
<key>OpenSourceVersion</key>
|
||||
<string>2015-03-01</string>
|
||||
<key>OpenSourceWebsiteURL</key>
|
||||
<string>http://svnweb.freebsd.org/base/head/bin/kill/</string>
|
||||
<key>OpenSourceSCM</key>
|
||||
<string>svn co http://svn.freebsd.org/base/head/bin/kill/</string>
|
||||
<key>OpenSourceImportDate</key>
|
||||
<string>2015-07-24</string>
|
||||
<key>OpenSourceModifications</key>
|
||||
<array>
|
||||
<string>kill.c: sys_nsig</string>
|
||||
</array>
|
||||
<key>OpenSourceLicense</key>
|
||||
<string>bsd</string>
|
||||
</dict>
|
@ -333,8 +333,12 @@ main(int ac, char **av)
|
||||
miblen = 4;
|
||||
}
|
||||
|
||||
st = sysctl(mib, miblen, NULL, &size, NULL, 0);
|
||||
do {
|
||||
st = sysctl(mib, miblen, NULL, &size, NULL, 0);
|
||||
if (st == -1)
|
||||
err(1, "could not sysctl(KERN_PROC)");
|
||||
if (!size)
|
||||
errx(1, "could not get size from sysctl(KERN_PROC)");
|
||||
size += size / 10;
|
||||
newprocs = realloc(procs, size);
|
||||
if (newprocs == 0) {
|
||||
@ -380,11 +384,7 @@ main(int ac, char **av)
|
||||
continue;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
#if defined(__APPLE__) && TARGET_OS_EMBEDDED
|
||||
mib[1] = KERN_PROCARGS2;
|
||||
#else
|
||||
mib[1] = KERN_PROCARGS;
|
||||
#endif
|
||||
mib[2] = thispid;
|
||||
|
||||
syssize = (size_t)argmax;
|
||||
|
@ -134,6 +134,7 @@ main(int argc, char *argv[])
|
||||
*p = '?';
|
||||
if (!*argv || requested(argv, &ab)) {
|
||||
|
||||
time_t timelong = ab.ac_btime;
|
||||
t = expand(ab.ac_utime) + expand(ab.ac_stime);
|
||||
(void)printf(
|
||||
"%-*.*s %-7s %-*.*s %-*.*s %6.2f secs %.16s",
|
||||
@ -143,7 +144,7 @@ main(int argc, char *argv[])
|
||||
UT_NAMESIZE, UT_NAMESIZE,
|
||||
user_from_uid(ab.ac_uid, 0), UT_LINESIZE,
|
||||
UT_LINESIZE, getdev(ab.ac_tty),
|
||||
t / (double)AHZ, ctime(&ab.ac_btime));
|
||||
t / (double)AHZ, ctime(&timelong));
|
||||
delta = expand(ab.ac_etime) / (double)AHZ;
|
||||
printf(" (%1.0f:%02.0f:%05.2f)\n",
|
||||
floor(delta / SECSPERHOUR),
|
||||
|
@ -10,10 +10,8 @@
|
||||
<array>
|
||||
<string>/usr/libexec/locate.updatedb</string>
|
||||
</array>
|
||||
<key>LowPriorityIO</key>
|
||||
<true/>
|
||||
<key>Nice</key>
|
||||
<integer>5</integer>
|
||||
<key>ProcessType</key>
|
||||
<string>Background</string>
|
||||
<key>KeepAlive</key>
|
||||
<dict>
|
||||
<key>PathState</key>
|
||||
|
@ -23,4 +23,4 @@
|
||||
# an empty database.
|
||||
#
|
||||
# be careful if you add 'nfs'
|
||||
#FILESYSTEMS="hfs ufs"
|
||||
#FILESYSTEMS="hfs ufs apfs"
|
||||
|
@ -56,11 +56,11 @@ PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH
|
||||
# 6497475
|
||||
set -o noglob
|
||||
|
||||
: ${mklocatedb:=locate.mklocatedb} # make locate database program
|
||||
: ${FCODES:=/var/db/locate.database} # the database
|
||||
: ${SEARCHPATHS:="/"} # directories to be put in the database
|
||||
: ${mklocatedb:=locate.mklocatedb} # make locate database program
|
||||
: ${FCODES:=/var/db/locate.database} # the database
|
||||
: ${SEARCHPATHS:="/"} # directories to be put in the database
|
||||
: ${PRUNEPATHS:="/private/tmp /private/var/folders /private/var/tmp */Backups.backupdb"} # unwanted directories
|
||||
: ${FILESYSTEMS:="hfs ufs"} # allowed filesystems
|
||||
: ${FILESYSTEMS:="hfs ufs apfs"} # allowed filesystems
|
||||
: ${find:=find}
|
||||
|
||||
case X"$SEARCHPATHS" in
|
||||
@ -72,22 +72,27 @@ case X"$FILESYSTEMS" in
|
||||
excludes="! (" or=""
|
||||
for fstype in $FILESYSTEMS
|
||||
do
|
||||
excludes="$excludes $or -fstype $fstype"
|
||||
or="-or"
|
||||
excludes="$excludes $or -fstype $fstype"
|
||||
or="-or"
|
||||
done
|
||||
excludes="$excludes ) -prune"
|
||||
|
||||
case X"$PRUNEPATHS" in
|
||||
X) ;;
|
||||
*) for path in $PRUNEPATHS
|
||||
do
|
||||
do
|
||||
excludes="$excludes -or -path $path -prune"
|
||||
done;;
|
||||
done;;
|
||||
esac
|
||||
|
||||
# Ignore the target of firmlinks
|
||||
while read firmlink; do
|
||||
excludes="$excludes -or -path $firmlink -prune"
|
||||
done <<< "$(awk -F'\t' '{print "/System/Volumes/Data/" $2}' /usr/share/firmlinks)"
|
||||
|
||||
tmp=$TMPDIR/_updatedb$$
|
||||
trap 'rm -f $tmp; rmdir $TMPDIR; exit' 0 1 2 3 5 10 15
|
||||
|
||||
|
||||
# search locally
|
||||
# echo $find $SEARCHPATHS $excludes -or -print && exit
|
||||
if $find -s $SEARCHPATHS $excludes -or -print 2>/dev/null |
|
||||
|
@ -94,11 +94,13 @@ option is given,
|
||||
will generate a template string based on the
|
||||
.Ar prefix
|
||||
and the
|
||||
.Dv _CS_DARWIN_USER_TEMP_DIR
|
||||
configuration variable if available.
|
||||
Fallback locations if
|
||||
.Dv _CS_DARWIN_USER_TEMP_DIR
|
||||
is not available are
|
||||
.Ev TMPDIR
|
||||
environment variable if set.
|
||||
The default location if
|
||||
.Ev TMPDIR
|
||||
is not set is
|
||||
and
|
||||
.Pa /tmp .
|
||||
Care should
|
||||
be taken to ensure that it is appropriate to use an environment variable
|
||||
@ -197,6 +199,7 @@ fi
|
||||
.Xr mkdtemp 3 ,
|
||||
.Xr mkstemp 3 ,
|
||||
.Xr mktemp 3 ,
|
||||
.Xr confstr 3 ,
|
||||
.Xr environ 7
|
||||
.Sh HISTORY
|
||||
A
|
||||
|
@ -14,6 +14,7 @@
|
||||
<string>Avoid double-slash in output. (5334050)</string>
|
||||
<string>Document case-insensitive file system behavior. (14280248)</string>
|
||||
<string>Prefer _CS_DARWIN_USER_TEMP_DIR over TMPDIR and _PATH_TMP. (14338140)</string>
|
||||
<string>mktemp.1: Clarification of _CS_DARWIN_USER_TEMP_DIR/TMPDIR/_PATH_TMP behavior. (22954705)</string>
|
||||
</array>
|
||||
<key>OpenSourceLicense</key>
|
||||
<string>bsd</string>
|
||||
|
@ -95,7 +95,7 @@ main(int argc, char *argv[])
|
||||
|
||||
(void)signal(SIGHUP, SIG_IGN);
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
|
||||
#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
|
||||
if (_vprocmgr_detach_from_console(0) != NULL)
|
||||
err(EXIT_MISC, "can't detach from console");
|
||||
#endif
|
||||
|
@ -12,10 +12,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 University of
|
||||
.\" California, Berkeley and its contributors.
|
||||
.\" 4. Neither the name of the University nor the names of its contributors
|
||||
.\" may be used to endorse or promote products derived from this software
|
||||
.\" without specific prior written permission.
|
||||
@ -33,9 +29,9 @@
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" @(#)printf.1 8.1 (Berkeley) 6/6/93
|
||||
.\" $FreeBSD: src/usr.bin/printf/printf.1,v 1.34 2005/06/14 11:50:52 ru Exp $
|
||||
.\" $FreeBSD: head/usr.bin/printf/printf.1 264743 2014-04-21 22:47:18Z pfg $
|
||||
.\"
|
||||
.Dd April 14, 2005
|
||||
.Dd April 21, 2014
|
||||
.Dt PRINTF 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -72,8 +68,7 @@ otherwise it is evaluated as a C constant, with the following extensions:
|
||||
A leading plus or minus sign is allowed.
|
||||
.It
|
||||
If the leading character is a single or double quote, the value is the
|
||||
.Tn ASCII
|
||||
code of the next character.
|
||||
character code of the next character.
|
||||
.El
|
||||
.Pp
|
||||
The format string is reused as often as necessary to satisfy the
|
||||
@ -109,12 +104,13 @@ Write a <single quote> character.
|
||||
.It Cm \e\e
|
||||
Write a backslash character.
|
||||
.It Cm \e Ns Ar num
|
||||
.It Cm \e0 Ns Ar num
|
||||
Write an 8-bit character whose
|
||||
.Tn ASCII
|
||||
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
|
||||
@ -128,9 +124,9 @@ in the following order:
|
||||
A `#' character
|
||||
specifying that the value should be printed in an ``alternate form''.
|
||||
For
|
||||
.Cm c , d ,
|
||||
.Cm b , c , d , s
|
||||
and
|
||||
.Cm s ,
|
||||
.Cm u
|
||||
formats, this option has no effect.
|
||||
For the
|
||||
.Cm o
|
||||
@ -144,9 +140,9 @@ format, a non-zero result has the string
|
||||
.Pq Li 0X
|
||||
prepended to it.
|
||||
For
|
||||
.Cm e , E , f , g ,
|
||||
.Cm a , A , e , E , f , F , g
|
||||
and
|
||||
.Cm G ,
|
||||
.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).
|
||||
@ -175,7 +171,7 @@ A `\-' overrides a `0' if both are used;
|
||||
.It "Field Width:"
|
||||
An optional digit string specifying a
|
||||
.Em field width ;
|
||||
if the output string has fewer characters than the field width it will
|
||||
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);
|
||||
@ -189,7 +185,7 @@ for
|
||||
.Cm e
|
||||
and
|
||||
.Cm f
|
||||
formats, or the maximum number of characters to be printed
|
||||
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:
|
||||
@ -275,20 +271,28 @@ and
|
||||
.Ql nan ,
|
||||
respectively.
|
||||
.It Cm c
|
||||
The first character of
|
||||
The first byte of
|
||||
.Ar argument
|
||||
is printed.
|
||||
.It Cm s
|
||||
Characters from the string
|
||||
Bytes from the string
|
||||
.Ar argument
|
||||
are printed until the end is reached or until the number of characters
|
||||
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, all characters in the string are printed.
|
||||
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
|
||||
@ -300,6 +304,13 @@ character is defined in the program's locale (category
|
||||
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
|
||||
@ -310,12 +321,14 @@ 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 mostly compatible with the
|
||||
command is expected to be compatible with the
|
||||
.St -p1003.2
|
||||
specification.
|
||||
.Sh HISTORY
|
||||
@ -326,6 +339,27 @@ command appeared in
|
||||
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
|
||||
@ -337,9 +371,6 @@ The
|
||||
.Cm L
|
||||
modifier may produce additional precision, depending on the hardware platform.)
|
||||
.Pp
|
||||
.Tn ANSI
|
||||
hexadecimal character constants were deliberately not provided.
|
||||
.Pp
|
||||
The escape sequence \e000 is the string terminator.
|
||||
When present in the argument for the
|
||||
.Cm b
|
||||
@ -349,8 +380,3 @@ Multibyte characters are not recognized in format strings (this is only
|
||||
a problem if
|
||||
.Ql %
|
||||
can appear inside a multibyte character).
|
||||
.Pp
|
||||
Parsing of - arguments is also somewhat different from
|
||||
.Xr printf 3 ,
|
||||
where unknown arguments are simply printed instead of being
|
||||
flagged as errors.
|
||||
|
327
printf/printf.c
327
printf/printf.c
@ -1,4 +1,6 @@
|
||||
/*
|
||||
/*-
|
||||
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
|
||||
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
@ -10,10 +12,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 University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
@ -30,8 +28,12 @@
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#if !defined(BUILTIN) && !defined(SHELL)
|
||||
#ifndef SHELL
|
||||
#ifndef lint
|
||||
static char const copyright[] =
|
||||
"@(#) Copyright (c) 1989, 1993\n\
|
||||
@ -44,53 +46,48 @@ static char const copyright[] =
|
||||
static char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93";
|
||||
#endif
|
||||
static const char rcsid[] =
|
||||
"$FreeBSD: src/usr.bin/printf/printf.c,v 1.37 2005/08/05 08:18:00 stefanf Exp $";
|
||||
"$FreeBSD: head/usr.bin/printf/printf.c 279503 2015-03-01 21:46:55Z jilles $";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef SHELL
|
||||
#define main printfcmd
|
||||
#define main printfcmd
|
||||
#include "bltin/bltin.h"
|
||||
#include "memalloc.h"
|
||||
#else
|
||||
#define warnx1(a, b, c) warnx(a)
|
||||
#define warnx2(a, b, c) warnx(a, b)
|
||||
#define warnx3(a, b, c) warnx(a, b, c)
|
||||
#include "options.h"
|
||||
#endif
|
||||
|
||||
#ifndef BUILTIN
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#define PF(f, func) do { \
|
||||
char *b = NULL; \
|
||||
if (havewidth) \
|
||||
if (haveprec) \
|
||||
#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); \
|
||||
} \
|
||||
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 *doformat(char *, int *);
|
||||
static char *printf_doformat(char *, int *);
|
||||
static int escape(char *, int, size_t *);
|
||||
static int getchr(void);
|
||||
static int getfloating(long double *, int);
|
||||
@ -98,25 +95,35 @@ static int getint(int *);
|
||||
static int getnum(intmax_t *, uintmax_t *, int);
|
||||
static const char
|
||||
*getstr(void);
|
||||
static char *mknum(char *, int);
|
||||
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
|
||||
#ifdef BUILTIN
|
||||
progprintf(int argc, char *argv[])
|
||||
#else
|
||||
main(int argc, char *argv[])
|
||||
#endif
|
||||
{
|
||||
size_t len;
|
||||
int ch, chopped, end, rval;
|
||||
int end, rval;
|
||||
char *format, *fmt, *start;
|
||||
#ifndef SHELL
|
||||
int ch;
|
||||
|
||||
#ifndef BUILTIN
|
||||
(void) setlocale(LC_NUMERIC, "");
|
||||
(void) setlocale(LC_ALL, "");
|
||||
#endif
|
||||
|
||||
#ifdef SHELL
|
||||
nextopt("");
|
||||
argc -= argptr - argv;
|
||||
argv = argptr;
|
||||
#else
|
||||
while ((ch = getopt(argc, argv, "")) != -1)
|
||||
switch (ch) {
|
||||
case '?':
|
||||
@ -126,12 +133,16 @@ main(int argc, char *argv[])
|
||||
}
|
||||
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
|
||||
@ -141,10 +152,16 @@ main(int argc, char *argv[])
|
||||
* up the format string.
|
||||
*/
|
||||
fmt = format = *argv;
|
||||
chopped = escape(fmt, 1, &len); /* backslash interpretation */
|
||||
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] == '%') {
|
||||
@ -154,23 +171,37 @@ main(int argc, char *argv[])
|
||||
putchar('%');
|
||||
fmt += 2;
|
||||
} else {
|
||||
fmt = doformat(fmt, &rval);
|
||||
if (fmt == NULL)
|
||||
return (1);
|
||||
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) {
|
||||
warnx1("missing format character", NULL, NULL);
|
||||
warnx("missing format character");
|
||||
#ifdef SHELL
|
||||
INTON;
|
||||
#endif
|
||||
return (1);
|
||||
}
|
||||
fwrite(start, 1, fmt - start, stdout);
|
||||
if (chopped || !*gargv)
|
||||
if (!*gargv) {
|
||||
#ifdef SHELL
|
||||
INTON;
|
||||
#endif
|
||||
return (rval);
|
||||
}
|
||||
/* Restart at the beginning of the format string. */
|
||||
fmt = format;
|
||||
end = 1;
|
||||
@ -180,48 +211,136 @@ main(int argc, char *argv[])
|
||||
|
||||
|
||||
static char *
|
||||
doformat(char *start, int *rval)
|
||||
printf_doformat(char *fmt, int *rval)
|
||||
{
|
||||
static const char skip1[] = "#'-+ 0";
|
||||
static const char skip2[] = "0123456789";
|
||||
char *fmt;
|
||||
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;
|
||||
}
|
||||
|
||||
fmt = start + 1;
|
||||
/* skip to field width */
|
||||
fmt += strspn(fmt, skip1);
|
||||
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;
|
||||
++fmt;
|
||||
|
||||
*dptr++ = '*';
|
||||
*dptr = 0;
|
||||
} else {
|
||||
havewidth = 0;
|
||||
|
||||
/* skip to possible '.', get following precision */
|
||||
fmt += strspn(fmt, skip2);
|
||||
while (isdigit(*fmt)) {
|
||||
*dptr++ = *fmt++;
|
||||
*dptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (*fmt == '.') {
|
||||
/* precision present? */
|
||||
++fmt;
|
||||
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;
|
||||
++fmt;
|
||||
*dptr++ = '*';
|
||||
*dptr = 0;
|
||||
} else {
|
||||
haveprec = 0;
|
||||
|
||||
/* skip to conversion char */
|
||||
fmt += strspn(fmt, skip2);
|
||||
while (isdigit(*fmt)) {
|
||||
*dptr++ = *fmt++;
|
||||
*dptr = 0;
|
||||
}
|
||||
}
|
||||
} else
|
||||
haveprec = 0;
|
||||
if (!*fmt) {
|
||||
warnx1("missing format character", NULL, NULL);
|
||||
warnx("missing format character");
|
||||
return (NULL);
|
||||
}
|
||||
*dptr++ = *fmt;
|
||||
*dptr = 0;
|
||||
|
||||
/*
|
||||
* Look for a length modifier. POSIX doesn't have these, so
|
||||
@ -237,15 +356,21 @@ doformat(char *start, int *rval)
|
||||
mod_ldbl = 1;
|
||||
fmt++;
|
||||
if (!strchr("aAeEfFgG", *fmt)) {
|
||||
warnx2("bad modifier L for %%%c", *fmt, NULL);
|
||||
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': {
|
||||
@ -253,26 +378,16 @@ doformat(char *start, int *rval)
|
||||
char *p;
|
||||
int getout;
|
||||
|
||||
#ifdef SHELL
|
||||
p = savestr(getstr());
|
||||
#else
|
||||
p = strdup(getstr());
|
||||
#endif
|
||||
if (p == NULL) {
|
||||
warnx2("%s", strerror(ENOMEM), NULL);
|
||||
warnx("%s", strerror(ENOMEM));
|
||||
return (NULL);
|
||||
}
|
||||
getout = escape(p, 0, &len);
|
||||
*(fmt - 1) = 's';
|
||||
PF(start, p);
|
||||
*(fmt - 1) = 'b';
|
||||
#ifdef SHELL
|
||||
ckfree(p);
|
||||
#else
|
||||
fputs(p, stdout);
|
||||
free(p);
|
||||
#endif
|
||||
if (getout)
|
||||
return (fmt);
|
||||
return (end_fmt);
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
@ -321,15 +436,16 @@ doformat(char *start, int *rval)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
warnx2("illegal format character %c", convch, NULL);
|
||||
warnx("illegal format character %c", convch);
|
||||
return (NULL);
|
||||
}
|
||||
*fmt = nextch;
|
||||
/* return the gargv to the next element */
|
||||
return (fmt);
|
||||
}
|
||||
|
||||
static char *
|
||||
mknum(char *str, int ch)
|
||||
mknum(char *str, char ch)
|
||||
{
|
||||
static char *copy;
|
||||
static size_t copy_size;
|
||||
@ -339,13 +455,8 @@ mknum(char *str, int ch)
|
||||
len = strlen(str) + 2;
|
||||
if (len > copy_size) {
|
||||
newlen = ((len + 1023) >> 10) << 10;
|
||||
#ifdef SHELL
|
||||
if ((newcopy = ckrealloc(copy, newlen)) == NULL)
|
||||
#else
|
||||
if ((newcopy = realloc(copy, newlen)) == NULL)
|
||||
#endif
|
||||
{
|
||||
warnx2("%s", strerror(ENOMEM), NULL);
|
||||
if ((newcopy = realloc(copy, newlen)) == NULL) {
|
||||
warnx("%s", strerror(ENOMEM));
|
||||
return (NULL);
|
||||
}
|
||||
copy = newcopy;
|
||||
@ -362,10 +473,10 @@ mknum(char *str, int ch)
|
||||
static int
|
||||
escape(char *fmt, int percent, size_t *len)
|
||||
{
|
||||
char *save, *store;
|
||||
int value, c;
|
||||
char *save, *store, c;
|
||||
int value;
|
||||
|
||||
for (save = store = fmt; (c = *fmt); ++fmt, ++store) {
|
||||
for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
|
||||
if (c != '\\') {
|
||||
*store = c;
|
||||
continue;
|
||||
@ -387,9 +498,13 @@ escape(char *fmt, int percent, size_t *len)
|
||||
*store = '\b';
|
||||
break;
|
||||
case 'c':
|
||||
*store = '\0';
|
||||
*len = store - save;
|
||||
return (1);
|
||||
if (!percent) {
|
||||
*store = '\0';
|
||||
*len = store - save;
|
||||
return (1);
|
||||
}
|
||||
*store = 'c';
|
||||
break;
|
||||
case 'f': /* form-feed */
|
||||
*store = '\f';
|
||||
break;
|
||||
@ -408,7 +523,8 @@ escape(char *fmt, int percent, size_t *len)
|
||||
/* octal constant */
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
for (c = *fmt == '0' ? 4 : 3, value = 0;
|
||||
c = (!percent && *fmt == '0') ? 4 : 3;
|
||||
for (value = 0;
|
||||
c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
|
||||
value <<= 3;
|
||||
value += *fmt - '0';
|
||||
@ -418,7 +534,7 @@ escape(char *fmt, int percent, size_t *len)
|
||||
*store++ = '%';
|
||||
*store = '%';
|
||||
} else
|
||||
*store = value;
|
||||
*store = (char)value;
|
||||
break;
|
||||
default:
|
||||
*store = *fmt;
|
||||
@ -457,7 +573,7 @@ getint(int *ip)
|
||||
return (1);
|
||||
rval = 0;
|
||||
if (val < INT_MIN || val > INT_MAX) {
|
||||
warnx3("%s: %s", *gargv, strerror(ERANGE));
|
||||
warnx("%s: %s", *gargv, strerror(ERANGE));
|
||||
rval = 1;
|
||||
}
|
||||
*ip = (int)val;
|
||||
@ -471,7 +587,7 @@ getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
|
||||
int rval;
|
||||
|
||||
if (!*gargv) {
|
||||
*ip = 0;
|
||||
*ip = *uip = 0;
|
||||
return (0);
|
||||
}
|
||||
if (**gargv == '"' || **gargv == '\'') {
|
||||
@ -488,15 +604,15 @@ getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
|
||||
else
|
||||
*uip = strtoumax(*gargv, &ep, 0);
|
||||
if (ep == *gargv) {
|
||||
warnx2("%s: expected numeric value", *gargv, NULL);
|
||||
warnx("%s: expected numeric value", *gargv);
|
||||
rval = 1;
|
||||
}
|
||||
else if (*ep != '\0') {
|
||||
warnx2("%s: not completely converted", *gargv, NULL);
|
||||
warnx("%s: not completely converted", *gargv);
|
||||
rval = 1;
|
||||
}
|
||||
if (errno == ERANGE) {
|
||||
warnx3("%s: %s", *gargv, strerror(ERANGE));
|
||||
warnx("%s: %s", *gargv, strerror(ERANGE));
|
||||
rval = 1;
|
||||
}
|
||||
++gargv;
|
||||
@ -524,14 +640,14 @@ getfloating(long double *dp, int mod_ldbl)
|
||||
else
|
||||
*dp = strtod(*gargv, &ep);
|
||||
if (ep == *gargv) {
|
||||
warnx2("%s: expected numeric value", *gargv, NULL);
|
||||
warnx("%s: expected numeric value", *gargv);
|
||||
rval = 1;
|
||||
} else if (*ep != '\0') {
|
||||
warnx2("%s: not completely converted", *gargv, NULL);
|
||||
warnx("%s: not completely converted", *gargv);
|
||||
rval = 1;
|
||||
}
|
||||
if (errno == ERANGE) {
|
||||
warnx3("%s: %s", *gargv, strerror(ERANGE));
|
||||
warnx("%s: %s", *gargv, strerror(ERANGE));
|
||||
rval = 1;
|
||||
}
|
||||
++gargv;
|
||||
@ -542,10 +658,23 @@ static int
|
||||
asciicode(void)
|
||||
{
|
||||
int ch;
|
||||
wchar_t wch;
|
||||
mbstate_t mbs;
|
||||
|
||||
ch = **gargv;
|
||||
if (ch == '\'' || ch == '"')
|
||||
ch = (*gargv)[1];
|
||||
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);
|
||||
}
|
||||
|
17
printf/printf.plist.part
Normal file
17
printf/printf.plist.part
Normal file
@ -0,0 +1,17 @@
|
||||
<dict>
|
||||
<key>OpenSourceProject</key>
|
||||
<string>printf</string>
|
||||
<key>OpenSourceVersion</key>
|
||||
<string>2015-03-01</string>
|
||||
<key>OpenSourceWebsiteURL</key>
|
||||
<string>http://svnweb.freebsd.org/base/head/usr.bin/printf/</string>
|
||||
<key>OpenSourceSCM</key>
|
||||
<string>svn co http://svn.freebsd.org/base/head/usr.bin/printf/</string>
|
||||
<key>OpenSourceImportDate</key>
|
||||
<string>2015-07-23</string>
|
||||
<key>OpenSourceModifications</key>
|
||||
<array>
|
||||
</array>
|
||||
<key>OpenSourceLicense</key>
|
||||
<string>bsd</string>
|
||||
</dict>
|
22
printf/tests/Makefile
Normal file
22
printf/tests/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
# $FreeBSD: head/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 <bsd.test.mk>
|
11
printf/tests/Makefile.depend
Normal file
11
printf/tests/Makefile.depend
Normal file
@ -0,0 +1,11 @@
|
||||
# $FreeBSD: head/usr.bin/printf/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $
|
||||
# Autogenerated - do NOT edit!
|
||||
|
||||
DIRDEPS = \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
||||
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||
# local dependencies - needed for -jN in clean tree
|
||||
.endif
|
6
printf/tests/legacy_test.sh
Normal file
6
printf/tests/legacy_test.sh
Normal file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# $FreeBSD: head/usr.bin/printf/tests/legacy_test.sh 263227 2014-03-16 08:04:06Z jmmv $
|
||||
|
||||
SRCDIR="$(dirname "${0}")"; export SRCDIR
|
||||
|
||||
m4 /AppleInternal/Tests/shell_cmds/regress.m4 "${SRCDIR}/regress.sh" | sh
|
1
printf/tests/regress.b.out
Normal file
1
printf/tests/regress.b.out
Normal file
@ -0,0 +1 @@
|
||||
abcdef
|
1
printf/tests/regress.d.out
Normal file
1
printf/tests/regress.d.out
Normal file
@ -0,0 +1 @@
|
||||
123, 123,00123,00123,00123
|
1
printf/tests/regress.f.out
Normal file
1
printf/tests/regress.f.out
Normal file
@ -0,0 +1 @@
|
||||
42.250000,-42.250 ,inf,nan
|
1
printf/tests/regress.l1.out
Normal file
1
printf/tests/regress.l1.out
Normal file
@ -0,0 +1 @@
|
||||
228
|
1
printf/tests/regress.l2.out
Normal file
1
printf/tests/regress.l2.out
Normal file
@ -0,0 +1 @@
|
||||
228
|
BIN
printf/tests/regress.m1.out
Normal file
BIN
printf/tests/regress.m1.out
Normal file
Binary file not shown.
2
printf/tests/regress.m2.out
Normal file
2
printf/tests/regress.m2.out
Normal file
@ -0,0 +1,2 @@
|
||||
abc
|
||||
cdef
|
4
printf/tests/regress.m3.out
Normal file
4
printf/tests/regress.m3.out
Normal file
@ -0,0 +1,4 @@
|
||||
%abc
|
||||
%def
|
||||
%ghi
|
||||
%jkl
|
1
printf/tests/regress.m4.out
Normal file
1
printf/tests/regress.m4.out
Normal file
@ -0,0 +1 @@
|
||||
0,0.000000,,
|
1
printf/tests/regress.m5.out
Normal file
1
printf/tests/regress.m5.out
Normal file
@ -0,0 +1 @@
|
||||
-d
|
1
printf/tests/regress.missingpos1.out
Normal file
1
printf/tests/regress.missingpos1.out
Normal file
@ -0,0 +1 @@
|
||||
printf: incomplete use of n$
|
1
printf/tests/regress.s.out
Normal file
1
printf/tests/regress.s.out
Normal file
@ -0,0 +1 @@
|
||||
abc,abc
|
33
printf/tests/regress.sh
Normal file
33
printf/tests/regress.sh
Normal file
@ -0,0 +1,33 @@
|
||||
# $FreeBSD: head/usr.bin/printf/tests/regress.sh 266854 2014-05-29 19:48:18Z pfg $
|
||||
|
||||
enable -n printf
|
||||
|
||||
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()
|
1
printf/tests/regress.zero.out
Normal file
1
printf/tests/regress.zero.out
Normal file
@ -0,0 +1 @@
|
||||
150
|
70
sh/Makefile
Normal file
70
sh/Makefile
Normal file
@ -0,0 +1,70 @@
|
||||
# @(#)Makefile 8.4 (Berkeley) 5/5/95
|
||||
# $FreeBSD: head/bin/sh/Makefile 322515 2017-08-14 19:21:37Z ngie $
|
||||
|
||||
.include <src.opts.mk>
|
||||
|
||||
PACKAGE=runtime
|
||||
PROG= sh
|
||||
INSTALLFLAGS= -S
|
||||
SHSRCS= alias.c arith_yacc.c arith_yylex.c cd.c echo.c error.c eval.c \
|
||||
exec.c expand.c \
|
||||
histedit.c input.c jobs.c kill.c mail.c main.c memalloc.c miscbltin.c \
|
||||
mystring.c options.c output.c parser.c printf.c redir.c show.c \
|
||||
test.c trap.c var.c
|
||||
GENSRCS= builtins.c nodes.c syntax.c
|
||||
GENHDRS= builtins.h nodes.h syntax.h token.h
|
||||
SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS}
|
||||
|
||||
# MLINKS for Shell built in commands for which there are no userland
|
||||
# utilities of the same name are handled with the associated manpage,
|
||||
# builtin.1 in share/man/man1/.
|
||||
|
||||
LIBADD= edit
|
||||
|
||||
CFLAGS+=-DSHELL -I. -I${.CURDIR}
|
||||
# for debug:
|
||||
# DEBUG_FLAGS+= -g -DDEBUG=2 -fno-inline
|
||||
WARNS?= 2
|
||||
WFORMAT=0
|
||||
|
||||
.PATH: ${.CURDIR}/bltin \
|
||||
${.CURDIR:H}/kill \
|
||||
${.CURDIR:H}/test \
|
||||
${SRCTOP}/usr.bin/printf
|
||||
|
||||
CLEANFILES+= mknodes mknodes.o \
|
||||
mksyntax mksyntax.o
|
||||
CLEANFILES+= ${GENSRCS} ${GENHDRS}
|
||||
|
||||
build-tools: mknodes mksyntax
|
||||
|
||||
.ORDER: builtins.c builtins.h
|
||||
builtins.h: .NOMETA
|
||||
builtins.c builtins.h: mkbuiltins builtins.def
|
||||
sh ${.CURDIR}/mkbuiltins ${.CURDIR}
|
||||
|
||||
# XXX this is just to stop the default .c rule being used, so that the
|
||||
# intermediate object has a fixed name.
|
||||
# XXX we have a default .c rule, but no default .o rule.
|
||||
mknodes.o mksyntax.o: ${BUILD_TOOLS_META}
|
||||
${CC} ${CFLAGS} ${LDFLAGS} ${.IMPSRC} ${LDLIBS} -o ${.TARGET}
|
||||
mknodes: mknodes.o ${BUILD_TOOLS_META}
|
||||
mksyntax: mksyntax.o ${BUILD_TOOLS_META}
|
||||
|
||||
.ORDER: nodes.c nodes.h
|
||||
nodes.h: .NOMETA
|
||||
nodes.c nodes.h: mknodes nodetypes nodes.c.pat
|
||||
${BTOOLSPATH:U.}/mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat
|
||||
|
||||
.ORDER: syntax.c syntax.h
|
||||
syntax.h: .NOMETA
|
||||
syntax.c syntax.h: mksyntax
|
||||
${BTOOLSPATH:U.}/mksyntax
|
||||
|
||||
token.h: mktokens
|
||||
sh ${.CURDIR}/mktokens
|
||||
|
||||
HAS_TESTS=
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
.include <bsd.prog.mk>
|
19
sh/Makefile.depend
Normal file
19
sh/Makefile.depend
Normal file
@ -0,0 +1,19 @@
|
||||
# $FreeBSD: head/bin/sh/Makefile.depend 325188 2017-10-31 00:07:04Z bdrewery $
|
||||
# Autogenerated - do NOT edit!
|
||||
|
||||
DIRDEPS = \
|
||||
gnu/lib/csu \
|
||||
include \
|
||||
include/xlocale \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
lib/libedit \
|
||||
lib/ncurses/ncursesw \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
||||
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
|
||||
# local dependencies - needed for -jN in clean tree
|
||||
.endif
|
301
sh/TOUR
Normal file
301
sh/TOUR
Normal file
@ -0,0 +1,301 @@
|
||||
# @(#)TOUR 8.1 (Berkeley) 5/31/93
|
||||
# $FreeBSD: head/bin/sh/TOUR 317882 2017-05-06 13:28:42Z jilles $
|
||||
|
||||
NOTE -- This is the original TOUR paper distributed with ash and
|
||||
does not represent the current state of the shell. It is provided anyway
|
||||
since it provides helpful information for how the shell is structured,
|
||||
but be warned that things have changed -- the current shell is
|
||||
still under development.
|
||||
|
||||
================================================================
|
||||
|
||||
A Tour through Ash
|
||||
|
||||
Copyright 1989 by Kenneth Almquist.
|
||||
|
||||
|
||||
DIRECTORIES: The subdirectory bltin contains commands which can
|
||||
be compiled stand-alone. The rest of the source is in the main
|
||||
ash directory.
|
||||
|
||||
SOURCE CODE GENERATORS: Files whose names begin with "mk" are
|
||||
programs that generate source code. A complete list of these
|
||||
programs is:
|
||||
|
||||
program input files generates
|
||||
------- ----------- ---------
|
||||
mkbuiltins builtins.def builtins.h builtins.c
|
||||
mknodes nodetypes nodes.h nodes.c
|
||||
mksyntax - syntax.h syntax.c
|
||||
mktokens - token.h
|
||||
|
||||
There are undoubtedly too many of these.
|
||||
|
||||
EXCEPTIONS: Code for dealing with exceptions appears in
|
||||
exceptions.c. The C language doesn't include exception handling,
|
||||
so I implement it using setjmp and longjmp. The global variable
|
||||
exception contains the type of exception. EXERROR is raised by
|
||||
calling error. EXINT is an interrupt.
|
||||
|
||||
INTERRUPTS: In an interactive shell, an interrupt will cause an
|
||||
EXINT exception to return to the main command loop. (Exception:
|
||||
EXINT is not raised if the user traps interrupts using the trap
|
||||
command.) The INTOFF and INTON macros (defined in exception.h)
|
||||
provide uninterruptible critical sections. Between the execution
|
||||
of INTOFF and the execution of INTON, interrupt signals will be
|
||||
held for later delivery. INTOFF and INTON can be nested.
|
||||
|
||||
MEMALLOC.C: Memalloc.c defines versions of malloc and realloc
|
||||
which call error when there is no memory left. It also defines a
|
||||
stack oriented memory allocation scheme. Allocating off a stack
|
||||
is probably more efficient than allocation using malloc, but the
|
||||
big advantage is that when an exception occurs all we have to do
|
||||
to free up the memory in use at the time of the exception is to
|
||||
restore the stack pointer. The stack is implemented using a
|
||||
linked list of blocks.
|
||||
|
||||
STPUTC: If the stack were contiguous, it would be easy to store
|
||||
strings on the stack without knowing in advance how long the
|
||||
string was going to be:
|
||||
p = stackptr;
|
||||
*p++ = c; /* repeated as many times as needed */
|
||||
stackptr = p;
|
||||
The following three macros (defined in memalloc.h) perform these
|
||||
operations, but grow the stack if you run off the end:
|
||||
STARTSTACKSTR(p);
|
||||
STPUTC(c, p); /* repeated as many times as needed */
|
||||
grabstackstr(p);
|
||||
|
||||
We now start a top-down look at the code:
|
||||
|
||||
MAIN.C: The main routine performs some initialization, executes
|
||||
the user's profile if necessary, and calls cmdloop. Cmdloop
|
||||
repeatedly parses and executes commands.
|
||||
|
||||
OPTIONS.C: This file contains the option processing code. It is
|
||||
called from main to parse the shell arguments when the shell is
|
||||
invoked, and it also contains the set builtin. The -i and -m op-
|
||||
tions (the latter turns on job control) require changes in signal
|
||||
handling. The routines setjobctl (in jobs.c) and setinteractive
|
||||
(in trap.c) are called to handle changes to these options.
|
||||
|
||||
PARSING: The parser code is all in parser.c. A recursive des-
|
||||
cent parser is used. Syntax tables (generated by mksyntax) are
|
||||
used to classify characters during lexical analysis. There are
|
||||
four tables: one for normal use, one for use when inside single
|
||||
quotes and dollar single quotes, one for use when inside double
|
||||
quotes and one for use in arithmetic. The tables are machine
|
||||
dependent because they are indexed by character variables and
|
||||
the range of a char varies from machine to machine.
|
||||
|
||||
PARSE OUTPUT: The output of the parser consists of a tree of
|
||||
nodes. The various types of nodes are defined in the file node-
|
||||
types.
|
||||
|
||||
Nodes of type NARG are used to represent both words and the con-
|
||||
tents of here documents. An early version of ash kept the con-
|
||||
tents of here documents in temporary files, but keeping here do-
|
||||
cuments in memory typically results in significantly better per-
|
||||
formance. It would have been nice to make it an option to use
|
||||
temporary files for here documents, for the benefit of small
|
||||
machines, but the code to keep track of when to delete the tem-
|
||||
porary files was complex and I never fixed all the bugs in it.
|
||||
(AT&T has been maintaining the Bourne shell for more than ten
|
||||
years, and to the best of my knowledge they still haven't gotten
|
||||
it to handle temporary files correctly in obscure cases.)
|
||||
|
||||
The text field of a NARG structure points to the text of the
|
||||
word. The text consists of ordinary characters and a number of
|
||||
special codes defined in parser.h. The special codes are:
|
||||
|
||||
CTLVAR Parameter expansion
|
||||
CTLENDVAR End of parameter expansion
|
||||
CTLBACKQ Command substitution
|
||||
CTLBACKQ|CTLQUOTE Command substitution inside double quotes
|
||||
CTLARI Arithmetic expansion
|
||||
CTLENDARI End of arithmetic expansion
|
||||
CTLESC Escape next character
|
||||
|
||||
A variable substitution contains the following elements:
|
||||
|
||||
CTLVAR type name '=' [ alternative-text CTLENDVAR ]
|
||||
|
||||
The type field is a single character specifying the type of sub-
|
||||
stitution. The possible types are:
|
||||
|
||||
VSNORMAL $var
|
||||
VSMINUS ${var-text}
|
||||
VSMINUS|VSNUL ${var:-text}
|
||||
VSPLUS ${var+text}
|
||||
VSPLUS|VSNUL ${var:+text}
|
||||
VSQUESTION ${var?text}
|
||||
VSQUESTION|VSNUL ${var:?text}
|
||||
VSASSIGN ${var=text}
|
||||
VSASSIGN|VSNUL ${var:=text}
|
||||
VSTRIMLEFT ${var#text}
|
||||
VSTRIMLEFTMAX ${var##text}
|
||||
VSTRIMRIGHT ${var%text}
|
||||
VSTRIMRIGHTMAX ${var%%text}
|
||||
VSLENGTH ${#var}
|
||||
VSERROR delayed error
|
||||
|
||||
In addition, the type field will have the VSQUOTE flag set if the
|
||||
variable is enclosed in double quotes and the VSLINENO flag if
|
||||
LINENO is being expanded (the parameter name is the decimal line
|
||||
number). The parameter's name comes next, terminated by an equals
|
||||
sign. If the type is not VSNORMAL (including when it is VSLENGTH),
|
||||
then the text field in the substitution follows, terminated by a
|
||||
CTLENDVAR byte.
|
||||
|
||||
The type VSERROR is used to allow parsing bad substitutions like
|
||||
${var[7]} and generate an error when they are expanded.
|
||||
|
||||
Commands in back quotes are parsed and stored in a linked list.
|
||||
The locations of these commands in the string are indicated by
|
||||
CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether
|
||||
the back quotes were enclosed in double quotes.
|
||||
|
||||
Arithmetic expansion starts with CTLARI and ends with CTLENDARI.
|
||||
|
||||
The character CTLESC escapes the next character, so that in case
|
||||
any of the CTL characters mentioned above appear in the input,
|
||||
they can be passed through transparently. CTLESC is also used to
|
||||
escape '*', '?', '[', and '!' characters which were quoted by the
|
||||
user and thus should not be used for file name generation.
|
||||
|
||||
CTLESC characters have proved to be particularly tricky to get
|
||||
right. In the case of here documents which are not subject to
|
||||
variable and command substitution, the parser doesn't insert any
|
||||
CTLESC characters to begin with (so the contents of the text
|
||||
field can be written without any processing). Other here docu-
|
||||
ments, and words which are not subject to file name generation,
|
||||
have the CTLESC characters removed during the variable and command
|
||||
substitution phase. Words which are subject to file name
|
||||
generation have the CTLESC characters removed as part of the file
|
||||
name phase.
|
||||
|
||||
EXECUTION: Command execution is handled by the following files:
|
||||
eval.c The top level routines.
|
||||
redir.c Code to handle redirection of input and output.
|
||||
jobs.c Code to handle forking, waiting, and job control.
|
||||
exec.c Code to do path searches and the actual exec sys call.
|
||||
expand.c Code to evaluate arguments.
|
||||
var.c Maintains the variable symbol table. Called from expand.c.
|
||||
|
||||
EVAL.C: Evaltree recursively executes a parse tree. The exit
|
||||
status is returned in the global variable exitstatus. The alter-
|
||||
native entry evalbackcmd is called to evaluate commands in back
|
||||
quotes. It saves the result in memory if the command is a buil-
|
||||
tin; otherwise it forks off a child to execute the command and
|
||||
connects the standard output of the child to a pipe.
|
||||
|
||||
JOBS.C: To create a process, you call makejob to return a job
|
||||
structure, and then call forkshell (passing the job structure as
|
||||
an argument) to create the process. Waitforjob waits for a job
|
||||
to complete. These routines take care of process groups if job
|
||||
control is defined.
|
||||
|
||||
REDIR.C: Ash allows file descriptors to be redirected and then
|
||||
restored without forking off a child process. This is accom-
|
||||
plished by duplicating the original file descriptors. The redir-
|
||||
tab structure records where the file descriptors have been dupli-
|
||||
cated to.
|
||||
|
||||
EXEC.C: The routine find_command locates a command, and enters
|
||||
the command in the hash table if it is not already there. The
|
||||
third argument specifies whether it is to print an error message
|
||||
if the command is not found. (When a pipeline is set up,
|
||||
find_command is called for all the commands in the pipeline be-
|
||||
fore any forking is done, so to get the commands into the hash
|
||||
table of the parent process. But to make command hashing as
|
||||
transparent as possible, we silently ignore errors at that point
|
||||
and only print error messages if the command cannot be found
|
||||
later.)
|
||||
|
||||
The routine shellexec is the interface to the exec system call.
|
||||
|
||||
EXPAND.C: As the routine argstr generates words by parameter
|
||||
expansion, command substitution and arithmetic expansion, it
|
||||
performs word splitting on the result. As each word is output,
|
||||
the routine expandmeta performs file name generation (if enabled).
|
||||
|
||||
VAR.C: Variables are stored in a hash table. Probably we should
|
||||
switch to extensible hashing. The variable name is stored in the
|
||||
same string as the value (using the format "name=value") so that
|
||||
no string copying is needed to create the environment of a com-
|
||||
mand. Variables which the shell references internally are preal-
|
||||
located so that the shell can reference the values of these vari-
|
||||
ables without doing a lookup.
|
||||
|
||||
When a program is run, the code in eval.c sticks any environment
|
||||
variables which precede the command (as in "PATH=xxx command") in
|
||||
the variable table as the simplest way to strip duplicates, and
|
||||
then calls "environment" to get the value of the environment.
|
||||
|
||||
BUILTIN COMMANDS: The procedures for handling these are scat-
|
||||
tered throughout the code, depending on which location appears
|
||||
most appropriate. They can be recognized because their names al-
|
||||
ways end in "cmd". The mapping from names to procedures is
|
||||
specified in the file builtins.def, which is processed by the
|
||||
mkbuiltins command.
|
||||
|
||||
A builtin command is invoked with argc and argv set up like a
|
||||
normal program. A builtin command is allowed to overwrite its
|
||||
arguments. Builtin routines can call nextopt to do option pars-
|
||||
ing. This is kind of like getopt, but you don't pass argc and
|
||||
argv to it. Builtin routines can also call error. This routine
|
||||
normally terminates the shell (or returns to the main command
|
||||
loop if the shell is interactive), but when called from a non-
|
||||
special builtin command it causes the builtin command to
|
||||
terminate with an exit status of 2.
|
||||
|
||||
The directory bltins contains commands which can be compiled in-
|
||||
dependently but can also be built into the shell for efficiency
|
||||
reasons. The header file bltin.h takes care of most of the
|
||||
differences between the ash and the stand-alone environment.
|
||||
The user should call the main routine "main", and #define main to
|
||||
be the name of the routine to use when the program is linked into
|
||||
ash. This #define should appear before bltin.h is included;
|
||||
bltin.h will #undef main if the program is to be compiled
|
||||
stand-alone. A similar approach is used for a few utilities from
|
||||
bin and usr.bin.
|
||||
|
||||
CD.C: This file defines the cd and pwd builtins.
|
||||
|
||||
SIGNALS: Trap.c implements the trap command. The routine set-
|
||||
signal figures out what action should be taken when a signal is
|
||||
received and invokes the signal system call to set the signal ac-
|
||||
tion appropriately. When a signal that a user has set a trap for
|
||||
is caught, the routine "onsig" sets a flag. The routine dotrap
|
||||
is called at appropriate points to actually handle the signal.
|
||||
When an interrupt is caught and no trap has been set for that
|
||||
signal, the routine "onint" in error.c is called.
|
||||
|
||||
OUTPUT: Ash uses its own output routines. There are three out-
|
||||
put structures allocated. "Output" represents the standard out-
|
||||
put, "errout" the standard error, and "memout" contains output
|
||||
which is to be stored in memory. This last is used when a buil-
|
||||
tin command appears in backquotes, to allow its output to be col-
|
||||
lected without doing any I/O through the UNIX operating system.
|
||||
The variables out1 and out2 normally point to output and errout,
|
||||
respectively, but they are set to point to memout when appropri-
|
||||
ate inside backquotes.
|
||||
|
||||
INPUT: The basic input routine is pgetc, which reads from the
|
||||
current input file. There is a stack of input files; the current
|
||||
input file is the top file on this stack. The code allows the
|
||||
input to come from a string rather than a file. (This is for the
|
||||
-c option and the "." and eval builtin commands.) The global
|
||||
variable plinno is saved and restored when files are pushed and
|
||||
popped from the stack. The parser routines store the number of
|
||||
the current line in this variable.
|
||||
|
||||
DEBUGGING: If DEBUG is defined in shell.h, then the shell will
|
||||
write debugging information to the file $HOME/trace. Most of
|
||||
this is done using the TRACE macro, which takes a set of printf
|
||||
arguments inside two sets of parenthesis. Example:
|
||||
"TRACE(("n=%d0, n))". The double parenthesis are necessary be-
|
||||
cause the preprocessor can't handle functions with a variable
|
||||
number of arguments. Defining DEBUG also causes the shell to
|
||||
generate a core dump if it is sent a quit signal. The tracing
|
||||
code is in show.c.
|
256
sh/alias.c
Normal file
256
sh/alias.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*-
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/alias.c 317039 2017-04-16 22:10:02Z jilles $");
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "shell.h"
|
||||
#include "output.h"
|
||||
#include "error.h"
|
||||
#include "memalloc.h"
|
||||
#include "mystring.h"
|
||||
#include "alias.h"
|
||||
#include "options.h"
|
||||
#include "builtins.h"
|
||||
|
||||
#define ATABSIZE 39
|
||||
|
||||
static struct alias *atab[ATABSIZE];
|
||||
static int aliases;
|
||||
|
||||
static void setalias(const char *, const char *);
|
||||
static int unalias(const char *);
|
||||
static struct alias **hashalias(const char *);
|
||||
|
||||
static
|
||||
void
|
||||
setalias(const char *name, const char *val)
|
||||
{
|
||||
struct alias *ap, **app;
|
||||
|
||||
unalias(name);
|
||||
app = hashalias(name);
|
||||
INTOFF;
|
||||
ap = ckmalloc(sizeof (struct alias));
|
||||
ap->name = savestr(name);
|
||||
ap->val = savestr(val);
|
||||
ap->flag = 0;
|
||||
ap->next = *app;
|
||||
*app = ap;
|
||||
aliases++;
|
||||
INTON;
|
||||
}
|
||||
|
||||
static void
|
||||
freealias(struct alias *ap)
|
||||
{
|
||||
ckfree(ap->name);
|
||||
ckfree(ap->val);
|
||||
ckfree(ap);
|
||||
}
|
||||
|
||||
static int
|
||||
unalias(const char *name)
|
||||
{
|
||||
struct alias *ap, **app;
|
||||
|
||||
app = hashalias(name);
|
||||
|
||||
for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
|
||||
if (equal(name, ap->name)) {
|
||||
/*
|
||||
* if the alias is currently in use (i.e. its
|
||||
* buffer is being used by the input routine) we
|
||||
* just null out the name instead of freeing it.
|
||||
* We could clear it out later, but this situation
|
||||
* is so rare that it hardly seems worth it.
|
||||
*/
|
||||
if (ap->flag & ALIASINUSE)
|
||||
*ap->name = '\0';
|
||||
else {
|
||||
INTOFF;
|
||||
*app = ap->next;
|
||||
freealias(ap);
|
||||
INTON;
|
||||
}
|
||||
aliases--;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
rmaliases(void)
|
||||
{
|
||||
struct alias *ap, **app;
|
||||
int i;
|
||||
|
||||
INTOFF;
|
||||
for (i = 0; i < ATABSIZE; i++) {
|
||||
app = &atab[i];
|
||||
while (*app) {
|
||||
ap = *app;
|
||||
if (ap->flag & ALIASINUSE) {
|
||||
*ap->name = '\0';
|
||||
app = &(*app)->next;
|
||||
} else {
|
||||
*app = ap->next;
|
||||
freealias(ap);
|
||||
}
|
||||
}
|
||||
}
|
||||
aliases = 0;
|
||||
INTON;
|
||||
}
|
||||
|
||||
struct alias *
|
||||
lookupalias(const char *name, int check)
|
||||
{
|
||||
struct alias *ap;
|
||||
|
||||
if (aliases == 0)
|
||||
return (NULL);
|
||||
for (ap = *hashalias(name); ap; ap = ap->next) {
|
||||
if (equal(name, ap->name)) {
|
||||
if (check && (ap->flag & ALIASINUSE))
|
||||
return (NULL);
|
||||
return (ap);
|
||||
}
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
comparealiases(const void *p1, const void *p2)
|
||||
{
|
||||
const struct alias *const *a1 = p1;
|
||||
const struct alias *const *a2 = p2;
|
||||
|
||||
return strcmp((*a1)->name, (*a2)->name);
|
||||
}
|
||||
|
||||
static void
|
||||
printalias(const struct alias *a)
|
||||
{
|
||||
out1fmt("%s=", a->name);
|
||||
out1qstr(a->val);
|
||||
out1c('\n');
|
||||
}
|
||||
|
||||
static void
|
||||
printaliases(void)
|
||||
{
|
||||
int i, j;
|
||||
struct alias **sorted, *ap;
|
||||
|
||||
INTOFF;
|
||||
sorted = ckmalloc(aliases * sizeof(*sorted));
|
||||
j = 0;
|
||||
for (i = 0; i < ATABSIZE; i++)
|
||||
for (ap = atab[i]; ap; ap = ap->next)
|
||||
if (*ap->name != '\0')
|
||||
sorted[j++] = ap;
|
||||
qsort(sorted, aliases, sizeof(*sorted), comparealiases);
|
||||
for (i = 0; i < aliases; i++) {
|
||||
printalias(sorted[i]);
|
||||
if (int_pending())
|
||||
break;
|
||||
}
|
||||
ckfree(sorted);
|
||||
INTON;
|
||||
}
|
||||
|
||||
int
|
||||
aliascmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
char *n, *v;
|
||||
int ret = 0;
|
||||
struct alias *ap;
|
||||
|
||||
nextopt("");
|
||||
|
||||
if (*argptr == NULL) {
|
||||
printaliases();
|
||||
return (0);
|
||||
}
|
||||
while ((n = *argptr++) != NULL) {
|
||||
if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */
|
||||
if ((ap = lookupalias(n, 0)) == NULL) {
|
||||
warning("%s: not found", n);
|
||||
ret = 1;
|
||||
} else
|
||||
printalias(ap);
|
||||
else {
|
||||
*v++ = '\0';
|
||||
setalias(n, v);
|
||||
}
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
unaliascmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
int i;
|
||||
|
||||
while ((i = nextopt("a")) != '\0') {
|
||||
if (i == 'a') {
|
||||
rmaliases();
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
for (i = 0; *argptr; argptr++)
|
||||
i |= unalias(*argptr);
|
||||
|
||||
return (i);
|
||||
}
|
||||
|
||||
static struct alias **
|
||||
hashalias(const char *p)
|
||||
{
|
||||
unsigned int hashval;
|
||||
|
||||
hashval = (unsigned char)*p << 4;
|
||||
while (*p)
|
||||
hashval+= *p++;
|
||||
return &atab[hashval % ATABSIZE];
|
||||
}
|
45
sh/alias.h
Normal file
45
sh/alias.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*-
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)alias.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/alias.h 314436 2017-02-28 23:42:47Z imp $
|
||||
*/
|
||||
|
||||
#define ALIASINUSE 1
|
||||
|
||||
struct alias {
|
||||
struct alias *next;
|
||||
char *name;
|
||||
char *val;
|
||||
int flag;
|
||||
};
|
||||
|
||||
struct alias *lookupalias(const char *, int);
|
37
sh/arith.h
Normal file
37
sh/arith.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*-
|
||||
* Copyright (c) 1995
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)arith.h 1.1 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/arith.h 315511 2017-03-18 20:41:07Z jilles $
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
#define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3)
|
||||
|
||||
arith_t arith(const char *);
|
381
sh/arith_yacc.c
Normal file
381
sh/arith_yacc.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*-
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* Copyright (c) 2007
|
||||
* Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/arith_yacc.c 270246 2014-08-20 20:15:43Z jilles $");
|
||||
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "arith.h"
|
||||
#include "arith_yacc.h"
|
||||
#include "expand.h"
|
||||
#include "shell.h"
|
||||
#include "error.h"
|
||||
#include "memalloc.h"
|
||||
#include "output.h"
|
||||
#include "options.h"
|
||||
#include "var.h"
|
||||
|
||||
#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
|
||||
#error Arithmetic tokens are out of order.
|
||||
#endif
|
||||
|
||||
static const char *arith_startbuf;
|
||||
|
||||
const char *arith_buf;
|
||||
union yystype yylval;
|
||||
|
||||
static int last_token;
|
||||
|
||||
#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
|
||||
|
||||
static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
|
||||
ARITH_PRECEDENCE(ARITH_MUL, 0),
|
||||
ARITH_PRECEDENCE(ARITH_DIV, 0),
|
||||
ARITH_PRECEDENCE(ARITH_REM, 0),
|
||||
ARITH_PRECEDENCE(ARITH_ADD, 1),
|
||||
ARITH_PRECEDENCE(ARITH_SUB, 1),
|
||||
ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
|
||||
ARITH_PRECEDENCE(ARITH_RSHIFT, 2),
|
||||
ARITH_PRECEDENCE(ARITH_LT, 3),
|
||||
ARITH_PRECEDENCE(ARITH_LE, 3),
|
||||
ARITH_PRECEDENCE(ARITH_GT, 3),
|
||||
ARITH_PRECEDENCE(ARITH_GE, 3),
|
||||
ARITH_PRECEDENCE(ARITH_EQ, 4),
|
||||
ARITH_PRECEDENCE(ARITH_NE, 4),
|
||||
ARITH_PRECEDENCE(ARITH_BAND, 5),
|
||||
ARITH_PRECEDENCE(ARITH_BXOR, 6),
|
||||
ARITH_PRECEDENCE(ARITH_BOR, 7),
|
||||
};
|
||||
|
||||
#define ARITH_MAX_PREC 8
|
||||
|
||||
int letcmd(int, char **);
|
||||
|
||||
static __dead2 void yyerror(const char *s)
|
||||
{
|
||||
error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static arith_t arith_lookupvarint(char *varname)
|
||||
{
|
||||
const char *str;
|
||||
char *p;
|
||||
arith_t result;
|
||||
|
||||
str = lookupvar(varname);
|
||||
if (uflag && str == NULL)
|
||||
yyerror("variable not set");
|
||||
if (str == NULL || *str == '\0')
|
||||
str = "0";
|
||||
errno = 0;
|
||||
result = strtoarith_t(str, &p, 0);
|
||||
if (errno != 0 || *p != '\0')
|
||||
yyerror("variable conversion error");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int arith_prec(int op)
|
||||
{
|
||||
return prec[op - ARITH_BINOP_MIN];
|
||||
}
|
||||
|
||||
static inline int higher_prec(int op1, int op2)
|
||||
{
|
||||
return arith_prec(op1) < arith_prec(op2);
|
||||
}
|
||||
|
||||
static arith_t do_binop(int op, arith_t a, arith_t b)
|
||||
{
|
||||
|
||||
switch (op) {
|
||||
default:
|
||||
case ARITH_REM:
|
||||
case ARITH_DIV:
|
||||
if (!b)
|
||||
yyerror("division by zero");
|
||||
if (a == ARITH_MIN && b == -1)
|
||||
yyerror("divide error");
|
||||
return op == ARITH_REM ? a % b : a / b;
|
||||
case ARITH_MUL:
|
||||
return (uintmax_t)a * (uintmax_t)b;
|
||||
case ARITH_ADD:
|
||||
return (uintmax_t)a + (uintmax_t)b;
|
||||
case ARITH_SUB:
|
||||
return (uintmax_t)a - (uintmax_t)b;
|
||||
case ARITH_LSHIFT:
|
||||
return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
|
||||
case ARITH_RSHIFT:
|
||||
return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
|
||||
case ARITH_LT:
|
||||
return a < b;
|
||||
case ARITH_LE:
|
||||
return a <= b;
|
||||
case ARITH_GT:
|
||||
return a > b;
|
||||
case ARITH_GE:
|
||||
return a >= b;
|
||||
case ARITH_EQ:
|
||||
return a == b;
|
||||
case ARITH_NE:
|
||||
return a != b;
|
||||
case ARITH_BAND:
|
||||
return a & b;
|
||||
case ARITH_BXOR:
|
||||
return a ^ b;
|
||||
case ARITH_BOR:
|
||||
return a | b;
|
||||
}
|
||||
}
|
||||
|
||||
static arith_t assignment(int var, int noeval);
|
||||
|
||||
static arith_t primary(int token, union yystype *val, int op, int noeval)
|
||||
{
|
||||
arith_t result;
|
||||
|
||||
again:
|
||||
switch (token) {
|
||||
case ARITH_LPAREN:
|
||||
result = assignment(op, noeval);
|
||||
if (last_token != ARITH_RPAREN)
|
||||
yyerror("expecting ')'");
|
||||
last_token = yylex();
|
||||
return result;
|
||||
case ARITH_NUM:
|
||||
last_token = op;
|
||||
return val->val;
|
||||
case ARITH_VAR:
|
||||
last_token = op;
|
||||
return noeval ? val->val : arith_lookupvarint(val->name);
|
||||
case ARITH_ADD:
|
||||
token = op;
|
||||
*val = yylval;
|
||||
op = yylex();
|
||||
goto again;
|
||||
case ARITH_SUB:
|
||||
*val = yylval;
|
||||
return -primary(op, val, yylex(), noeval);
|
||||
case ARITH_NOT:
|
||||
*val = yylval;
|
||||
return !primary(op, val, yylex(), noeval);
|
||||
case ARITH_BNOT:
|
||||
*val = yylval;
|
||||
return ~primary(op, val, yylex(), noeval);
|
||||
default:
|
||||
yyerror("expecting primary");
|
||||
}
|
||||
}
|
||||
|
||||
static arith_t binop2(arith_t a, int op, int precedence, int noeval)
|
||||
{
|
||||
for (;;) {
|
||||
union yystype val;
|
||||
arith_t b;
|
||||
int op2;
|
||||
int token;
|
||||
|
||||
token = yylex();
|
||||
val = yylval;
|
||||
|
||||
b = primary(token, &val, yylex(), noeval);
|
||||
|
||||
op2 = last_token;
|
||||
if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX &&
|
||||
higher_prec(op2, op)) {
|
||||
b = binop2(b, op2, arith_prec(op), noeval);
|
||||
op2 = last_token;
|
||||
}
|
||||
|
||||
a = noeval ? b : do_binop(op, a, b);
|
||||
|
||||
if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX ||
|
||||
arith_prec(op2) >= precedence)
|
||||
return a;
|
||||
|
||||
op = op2;
|
||||
}
|
||||
}
|
||||
|
||||
static arith_t binop(int token, union yystype *val, int op, int noeval)
|
||||
{
|
||||
arith_t a = primary(token, val, op, noeval);
|
||||
|
||||
op = last_token;
|
||||
if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX)
|
||||
return a;
|
||||
|
||||
return binop2(a, op, ARITH_MAX_PREC, noeval);
|
||||
}
|
||||
|
||||
static arith_t and(int token, union yystype *val, int op, int noeval)
|
||||
{
|
||||
arith_t a = binop(token, val, op, noeval);
|
||||
arith_t b;
|
||||
|
||||
op = last_token;
|
||||
if (op != ARITH_AND)
|
||||
return a;
|
||||
|
||||
token = yylex();
|
||||
*val = yylval;
|
||||
|
||||
b = and(token, val, yylex(), noeval | !a);
|
||||
|
||||
return a && b;
|
||||
}
|
||||
|
||||
static arith_t or(int token, union yystype *val, int op, int noeval)
|
||||
{
|
||||
arith_t a = and(token, val, op, noeval);
|
||||
arith_t b;
|
||||
|
||||
op = last_token;
|
||||
if (op != ARITH_OR)
|
||||
return a;
|
||||
|
||||
token = yylex();
|
||||
*val = yylval;
|
||||
|
||||
b = or(token, val, yylex(), noeval | !!a);
|
||||
|
||||
return a || b;
|
||||
}
|
||||
|
||||
static arith_t cond(int token, union yystype *val, int op, int noeval)
|
||||
{
|
||||
arith_t a = or(token, val, op, noeval);
|
||||
arith_t b;
|
||||
arith_t c;
|
||||
|
||||
if (last_token != ARITH_QMARK)
|
||||
return a;
|
||||
|
||||
b = assignment(yylex(), noeval | !a);
|
||||
|
||||
if (last_token != ARITH_COLON)
|
||||
yyerror("expecting ':'");
|
||||
|
||||
token = yylex();
|
||||
*val = yylval;
|
||||
|
||||
c = cond(token, val, yylex(), noeval | !!a);
|
||||
|
||||
return a ? b : c;
|
||||
}
|
||||
|
||||
static arith_t assignment(int var, int noeval)
|
||||
{
|
||||
union yystype val = yylval;
|
||||
int op = yylex();
|
||||
arith_t result;
|
||||
char sresult[DIGITS(result) + 1];
|
||||
|
||||
if (var != ARITH_VAR)
|
||||
return cond(var, &val, op, noeval);
|
||||
|
||||
if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX))
|
||||
return cond(var, &val, op, noeval);
|
||||
|
||||
result = assignment(yylex(), noeval);
|
||||
if (noeval)
|
||||
return result;
|
||||
|
||||
if (op != ARITH_ASS)
|
||||
result = do_binop(op - 11, arith_lookupvarint(val.name), result);
|
||||
snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result);
|
||||
setvar(val.name, sresult, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
arith_t arith(const char *s)
|
||||
{
|
||||
struct stackmark smark;
|
||||
arith_t result;
|
||||
|
||||
setstackmark(&smark);
|
||||
|
||||
arith_buf = arith_startbuf = s;
|
||||
|
||||
result = assignment(yylex(), 0);
|
||||
|
||||
if (last_token)
|
||||
yyerror("expecting EOF");
|
||||
|
||||
popstackmark(&smark);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* The exp(1) builtin.
|
||||
*/
|
||||
int
|
||||
letcmd(int argc, char **argv)
|
||||
{
|
||||
const char *p;
|
||||
char *concat;
|
||||
char **ap;
|
||||
arith_t i;
|
||||
|
||||
if (argc > 1) {
|
||||
p = argv[1];
|
||||
if (argc > 2) {
|
||||
/*
|
||||
* Concatenate arguments.
|
||||
*/
|
||||
STARTSTACKSTR(concat);
|
||||
ap = argv + 2;
|
||||
for (;;) {
|
||||
while (*p)
|
||||
STPUTC(*p++, concat);
|
||||
if ((p = *ap++) == NULL)
|
||||
break;
|
||||
STPUTC(' ', concat);
|
||||
}
|
||||
STPUTC('\0', concat);
|
||||
p = grabstackstr(concat);
|
||||
}
|
||||
} else
|
||||
p = "";
|
||||
|
||||
i = arith(p);
|
||||
|
||||
out1fmt(ARITH_FORMAT_STR "\n", i);
|
||||
return !i;
|
||||
}
|
93
sh/arith_yacc.h
Normal file
93
sh/arith_yacc.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*-
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* Copyright (c) 2007
|
||||
* Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: head/bin/sh/arith_yacc.h 279503 2015-03-01 21:46:55Z jilles $
|
||||
*/
|
||||
|
||||
#define ARITH_ASS 1
|
||||
|
||||
#define ARITH_OR 2
|
||||
#define ARITH_AND 3
|
||||
#define ARITH_BAD 4
|
||||
#define ARITH_NUM 5
|
||||
#define ARITH_VAR 6
|
||||
#define ARITH_NOT 7
|
||||
|
||||
#define ARITH_BINOP_MIN 8
|
||||
#define ARITH_LE 8
|
||||
#define ARITH_GE 9
|
||||
#define ARITH_LT 10
|
||||
#define ARITH_GT 11
|
||||
#define ARITH_EQ 12
|
||||
#define ARITH_REM 13
|
||||
#define ARITH_BAND 14
|
||||
#define ARITH_LSHIFT 15
|
||||
#define ARITH_RSHIFT 16
|
||||
#define ARITH_MUL 17
|
||||
#define ARITH_ADD 18
|
||||
#define ARITH_BOR 19
|
||||
#define ARITH_SUB 20
|
||||
#define ARITH_BXOR 21
|
||||
#define ARITH_DIV 22
|
||||
#define ARITH_NE 23
|
||||
#define ARITH_BINOP_MAX 24
|
||||
|
||||
#define ARITH_ASS_MIN 24
|
||||
#define ARITH_REMASS 24
|
||||
#define ARITH_BANDASS 25
|
||||
#define ARITH_LSHIFTASS 26
|
||||
#define ARITH_RSHIFTASS 27
|
||||
#define ARITH_MULASS 28
|
||||
#define ARITH_ADDASS 29
|
||||
#define ARITH_BORASS 30
|
||||
#define ARITH_SUBASS 31
|
||||
#define ARITH_BXORASS 32
|
||||
#define ARITH_DIVASS 33
|
||||
#define ARITH_ASS_MAX 34
|
||||
|
||||
#define ARITH_LPAREN 34
|
||||
#define ARITH_RPAREN 35
|
||||
#define ARITH_BNOT 36
|
||||
#define ARITH_QMARK 37
|
||||
#define ARITH_COLON 38
|
||||
|
||||
extern const char *arith_buf;
|
||||
|
||||
union yystype {
|
||||
arith_t val;
|
||||
char *name;
|
||||
};
|
||||
|
||||
extern union yystype yylval;
|
||||
|
||||
int yylex(void);
|
248
sh/arith_yylex.c
Normal file
248
sh/arith_yylex.c
Normal file
@ -0,0 +1,248 @@
|
||||
/*-
|
||||
* Copyright (c) 2002
|
||||
* Herbert Xu.
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/arith_yylex.c 279503 2015-03-01 21:46:55Z jilles $");
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "shell.h"
|
||||
#include "arith_yacc.h"
|
||||
#include "expand.h"
|
||||
#include "error.h"
|
||||
#include "memalloc.h"
|
||||
#include "parser.h"
|
||||
#include "syntax.h"
|
||||
|
||||
#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ
|
||||
#error Arithmetic tokens are out of order.
|
||||
#endif
|
||||
|
||||
int
|
||||
yylex(void)
|
||||
{
|
||||
int value;
|
||||
const char *buf = arith_buf;
|
||||
char *end;
|
||||
const char *p;
|
||||
|
||||
for (;;) {
|
||||
value = *buf;
|
||||
switch (value) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
buf++;
|
||||
continue;
|
||||
default:
|
||||
return ARITH_BAD;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
yylval.val = strtoarith_t(buf, &end, 0);
|
||||
arith_buf = end;
|
||||
return ARITH_NUM;
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'G':
|
||||
case 'H':
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'O':
|
||||
case 'P':
|
||||
case 'Q':
|
||||
case 'R':
|
||||
case 'S':
|
||||
case 'T':
|
||||
case 'U':
|
||||
case 'V':
|
||||
case 'W':
|
||||
case 'X':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
case '_':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'h':
|
||||
case 'i':
|
||||
case 'j':
|
||||
case 'k':
|
||||
case 'l':
|
||||
case 'm':
|
||||
case 'n':
|
||||
case 'o':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'r':
|
||||
case 's':
|
||||
case 't':
|
||||
case 'u':
|
||||
case 'v':
|
||||
case 'w':
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'z':
|
||||
p = buf;
|
||||
while (buf++, is_in_name(*buf))
|
||||
;
|
||||
yylval.name = stalloc(buf - p + 1);
|
||||
memcpy(yylval.name, p, buf - p);
|
||||
yylval.name[buf - p] = '\0';
|
||||
value = ARITH_VAR;
|
||||
goto out;
|
||||
case '=':
|
||||
value += ARITH_ASS - '=';
|
||||
checkeq:
|
||||
buf++;
|
||||
checkeqcur:
|
||||
if (*buf != '=')
|
||||
goto out;
|
||||
value += 11;
|
||||
break;
|
||||
case '>':
|
||||
switch (*++buf) {
|
||||
case '=':
|
||||
value += ARITH_GE - '>';
|
||||
break;
|
||||
case '>':
|
||||
value += ARITH_RSHIFT - '>';
|
||||
goto checkeq;
|
||||
default:
|
||||
value += ARITH_GT - '>';
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
switch (*++buf) {
|
||||
case '=':
|
||||
value += ARITH_LE - '<';
|
||||
break;
|
||||
case '<':
|
||||
value += ARITH_LSHIFT - '<';
|
||||
goto checkeq;
|
||||
default:
|
||||
value += ARITH_LT - '<';
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case '|':
|
||||
if (*++buf != '|') {
|
||||
value += ARITH_BOR - '|';
|
||||
goto checkeqcur;
|
||||
}
|
||||
value += ARITH_OR - '|';
|
||||
break;
|
||||
case '&':
|
||||
if (*++buf != '&') {
|
||||
value += ARITH_BAND - '&';
|
||||
goto checkeqcur;
|
||||
}
|
||||
value += ARITH_AND - '&';
|
||||
break;
|
||||
case '!':
|
||||
if (*++buf != '=') {
|
||||
value += ARITH_NOT - '!';
|
||||
goto out;
|
||||
}
|
||||
value += ARITH_NE - '!';
|
||||
break;
|
||||
case 0:
|
||||
goto out;
|
||||
case '(':
|
||||
value += ARITH_LPAREN - '(';
|
||||
break;
|
||||
case ')':
|
||||
value += ARITH_RPAREN - ')';
|
||||
break;
|
||||
case '*':
|
||||
value += ARITH_MUL - '*';
|
||||
goto checkeq;
|
||||
case '/':
|
||||
value += ARITH_DIV - '/';
|
||||
goto checkeq;
|
||||
case '%':
|
||||
value += ARITH_REM - '%';
|
||||
goto checkeq;
|
||||
case '+':
|
||||
if (buf[1] == '+')
|
||||
return ARITH_BAD;
|
||||
value += ARITH_ADD - '+';
|
||||
goto checkeq;
|
||||
case '-':
|
||||
if (buf[1] == '-')
|
||||
return ARITH_BAD;
|
||||
value += ARITH_SUB - '-';
|
||||
goto checkeq;
|
||||
case '~':
|
||||
value += ARITH_BNOT - '~';
|
||||
break;
|
||||
case '^':
|
||||
value += ARITH_BXOR - '^';
|
||||
goto checkeq;
|
||||
case '?':
|
||||
value += ARITH_QMARK - '?';
|
||||
break;
|
||||
case ':':
|
||||
value += ARITH_COLON - ':';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
buf++;
|
||||
out:
|
||||
arith_buf = buf;
|
||||
return value;
|
||||
}
|
81
sh/bltin/bltin.h
Normal file
81
sh/bltin/bltin.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)bltin.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/bltin/bltin.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is included by programs which are optionally built into the
|
||||
* shell. If SHELL is defined, we try to map the standard UNIX library
|
||||
* routines to ash routines using defines.
|
||||
*/
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../mystring.h"
|
||||
#ifdef SHELL
|
||||
#include "../error.h"
|
||||
#include "../output.h"
|
||||
#include "builtins.h"
|
||||
#define FILE struct output
|
||||
#undef stdout
|
||||
#define stdout out1
|
||||
#undef stderr
|
||||
#define stderr out2
|
||||
#define printf out1fmt
|
||||
#undef putc
|
||||
#define putc(c, file) outc(c, file)
|
||||
#undef putchar
|
||||
#define putchar(c) out1c(c)
|
||||
#define fprintf outfmt
|
||||
#define fputs outstr
|
||||
#define fwrite(ptr, size, nmemb, file) outbin(ptr, (size) * (nmemb), file)
|
||||
#define fflush flushout
|
||||
#define INITARGS(argv)
|
||||
#define warnx warning
|
||||
#define warn(fmt, ...) warning(fmt ": %s", __VA_ARGS__, strerror(errno))
|
||||
#define errx(exitstatus, ...) error(__VA_ARGS__)
|
||||
|
||||
#else
|
||||
#undef NULL
|
||||
#include <stdio.h>
|
||||
#undef main
|
||||
#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
pointer stalloc(int);
|
||||
int killjob(const char *, int);
|
||||
|
||||
extern char *commandname;
|
109
sh/bltin/echo.c
Normal file
109
sh/bltin/echo.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)echo.c 8.2 (Berkeley) 5/4/95
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/bltin/echo.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
/*
|
||||
* Echo command.
|
||||
*/
|
||||
|
||||
#define main echocmd
|
||||
|
||||
#include "bltin.h"
|
||||
|
||||
/* #define eflag 1 */
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char **ap;
|
||||
char *p;
|
||||
char c;
|
||||
int count;
|
||||
int nflag = 0;
|
||||
#ifndef eflag
|
||||
int eflag = 0;
|
||||
#endif
|
||||
|
||||
ap = argv;
|
||||
if (argc)
|
||||
ap++;
|
||||
if ((p = *ap) != NULL) {
|
||||
if (equal(p, "-n")) {
|
||||
nflag++;
|
||||
ap++;
|
||||
} else if (equal(p, "-e")) {
|
||||
#ifndef eflag
|
||||
eflag++;
|
||||
#endif
|
||||
ap++;
|
||||
}
|
||||
}
|
||||
while ((p = *ap++) != NULL) {
|
||||
while ((c = *p++) != '\0') {
|
||||
if (c == '\\' && eflag) {
|
||||
switch (*p++) {
|
||||
case 'a': c = '\a'; break;
|
||||
case 'b': c = '\b'; break;
|
||||
case 'c': return 0; /* exit */
|
||||
case 'e': c = '\033'; break;
|
||||
case 'f': c = '\f'; break;
|
||||
case 'n': c = '\n'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 't': c = '\t'; break;
|
||||
case 'v': c = '\v'; break;
|
||||
case '\\': break; /* c = '\\' */
|
||||
case '0':
|
||||
c = 0;
|
||||
count = 3;
|
||||
while (--count >= 0 && (unsigned)(*p - '0') < 8)
|
||||
c = (c << 3) + (*p++ - '0');
|
||||
break;
|
||||
default:
|
||||
p--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
putchar(c);
|
||||
}
|
||||
if (*ap)
|
||||
putchar(' ');
|
||||
}
|
||||
if (! nflag)
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
96
sh/builtins.def
Normal file
96
sh/builtins.def
Normal file
@ -0,0 +1,96 @@
|
||||
#!/bin/sh -
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)builtins.def 8.4 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/builtins.def 319576 2017-06-04 21:02:48Z bdrewery $
|
||||
|
||||
#
|
||||
# This file lists all the builtin commands. The first column is the name
|
||||
# of a C routine.
|
||||
# The -j flag specifies that this command is to be excluded from systems
|
||||
# without job control.
|
||||
# The -h flag specifies that this command is to be excluded from systems
|
||||
# based on the NO_HISTORY compile-time symbol.
|
||||
# The -n flag specifies that this command can safely be run in the same
|
||||
# process when it is the only command in a command substitution. Some
|
||||
# commands have special logic defined in safe_builtin().
|
||||
# The -s flag specifies that this is a POSIX 'special built-in' command.
|
||||
# The rest of the line specifies the command name or names used to run the
|
||||
# command. The entry for bltincmd, which is run when the user does not specify
|
||||
# a command, must come first.
|
||||
#
|
||||
# NOTE: bltincmd must come first!
|
||||
|
||||
bltincmd -n builtin
|
||||
aliascmd alias
|
||||
bgcmd -j bg
|
||||
bindcmd bind
|
||||
breakcmd -s break -s continue
|
||||
cdcmd cd chdir
|
||||
commandcmd -n command
|
||||
dotcmd -s .
|
||||
echocmd -n echo
|
||||
evalcmd -s eval
|
||||
execcmd -s exec
|
||||
exitcmd -s exit
|
||||
letcmd let
|
||||
exportcmd -s export -s readonly
|
||||
#exprcmd expr
|
||||
falsecmd -n false
|
||||
fgcmd -j fg
|
||||
freebsd_wordexpcmd freebsd_wordexp
|
||||
getoptscmd getopts
|
||||
hashcmd hash
|
||||
histcmd -h fc
|
||||
jobidcmd -n jobid
|
||||
jobscmd -n jobs
|
||||
killcmd -n kill
|
||||
localcmd local
|
||||
printfcmd -n printf
|
||||
pwdcmd -n pwd
|
||||
readcmd read
|
||||
returncmd -s return
|
||||
setcmd -s set
|
||||
setvarcmd setvar
|
||||
shiftcmd -s shift
|
||||
testcmd -n test [
|
||||
timescmd -n -s times
|
||||
trapcmd -s trap
|
||||
truecmd -n -s : true
|
||||
typecmd -n type
|
||||
ulimitcmd ulimit
|
||||
umaskcmd umask
|
||||
unaliascmd unalias
|
||||
unsetcmd -s unset
|
||||
waitcmd wait
|
||||
wordexpcmd wordexp
|
430
sh/cd.c
Normal file
430
sh/cd.c
Normal file
@ -0,0 +1,430 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/cd.c 320340 2017-06-25 21:53:08Z jilles $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* The cd and pwd commands.
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
#include "var.h"
|
||||
#include "nodes.h" /* for jobs.h */
|
||||
#include "jobs.h"
|
||||
#include "options.h"
|
||||
#include "output.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "exec.h"
|
||||
#include "redir.h"
|
||||
#include "mystring.h"
|
||||
#include "show.h"
|
||||
#include "cd.h"
|
||||
#include "builtins.h"
|
||||
|
||||
static int cdlogical(char *);
|
||||
static int cdphysical(char *);
|
||||
static int docd(char *, int, int);
|
||||
static char *getcomponent(char **);
|
||||
static char *findcwd(char *);
|
||||
static void updatepwd(char *);
|
||||
static char *getpwd(void);
|
||||
static char *getpwd2(void);
|
||||
|
||||
static char *curdir = NULL; /* current working directory */
|
||||
|
||||
int
|
||||
cdcmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
const char *dest;
|
||||
const char *path;
|
||||
char *p;
|
||||
struct stat statb;
|
||||
int ch, phys, print = 0, getcwderr = 0;
|
||||
int rc;
|
||||
int errno1 = ENOENT;
|
||||
|
||||
phys = Pflag;
|
||||
while ((ch = nextopt("eLP")) != '\0') {
|
||||
switch (ch) {
|
||||
case 'e':
|
||||
getcwderr = 1;
|
||||
break;
|
||||
case 'L':
|
||||
phys = 0;
|
||||
break;
|
||||
case 'P':
|
||||
phys = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*argptr != NULL && argptr[1] != NULL)
|
||||
error("too many arguments");
|
||||
|
||||
if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
|
||||
error("HOME not set");
|
||||
if (*dest == '\0')
|
||||
dest = ".";
|
||||
if (dest[0] == '-' && dest[1] == '\0') {
|
||||
dest = bltinlookup("OLDPWD", 1);
|
||||
if (dest == NULL)
|
||||
error("OLDPWD not set");
|
||||
print = 1;
|
||||
}
|
||||
if (dest[0] == '/' ||
|
||||
(dest[0] == '.' && (dest[1] == '/' || dest[1] == '\0')) ||
|
||||
(dest[0] == '.' && dest[1] == '.' && (dest[2] == '/' || dest[2] == '\0')) ||
|
||||
(path = bltinlookup("CDPATH", 1)) == NULL)
|
||||
path = "";
|
||||
while ((p = padvance(&path, dest)) != NULL) {
|
||||
if (stat(p, &statb) < 0) {
|
||||
if (errno != ENOENT)
|
||||
errno1 = errno;
|
||||
} else if (!S_ISDIR(statb.st_mode))
|
||||
errno1 = ENOTDIR;
|
||||
else {
|
||||
if (!print) {
|
||||
/*
|
||||
* XXX - rethink
|
||||
*/
|
||||
if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
|
||||
print = strcmp(p + 2, dest);
|
||||
else
|
||||
print = strcmp(p, dest);
|
||||
}
|
||||
rc = docd(p, print, phys);
|
||||
if (rc >= 0)
|
||||
return getcwderr ? rc : 0;
|
||||
if (errno != ENOENT)
|
||||
errno1 = errno;
|
||||
}
|
||||
}
|
||||
error("%s: %s", dest, strerror(errno1));
|
||||
/*NOTREACHED*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Actually change the directory. In an interactive shell, print the
|
||||
* directory name if "print" is nonzero.
|
||||
*/
|
||||
static int
|
||||
docd(char *dest, int print, int phys)
|
||||
{
|
||||
int rc;
|
||||
|
||||
TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
|
||||
|
||||
/* If logical cd fails, fall back to physical. */
|
||||
if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0)
|
||||
return (-1);
|
||||
|
||||
if (print && iflag && curdir) {
|
||||
out1fmt("%s\n", curdir);
|
||||
/*
|
||||
* Ignore write errors to preserve the invariant that the
|
||||
* current directory is changed iff the exit status is 0
|
||||
* (or 1 if -e was given and the full pathname could not be
|
||||
* determined).
|
||||
*/
|
||||
flushout(out1);
|
||||
outclearerror(out1);
|
||||
}
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static int
|
||||
cdlogical(char *dest)
|
||||
{
|
||||
char *p;
|
||||
char *q;
|
||||
char *component;
|
||||
char *path;
|
||||
struct stat statb;
|
||||
int first;
|
||||
int badstat;
|
||||
|
||||
/*
|
||||
* Check each component of the path. If we find a symlink or
|
||||
* something we can't stat, clear curdir to force a getcwd()
|
||||
* next time we get the value of the current directory.
|
||||
*/
|
||||
badstat = 0;
|
||||
path = stsavestr(dest);
|
||||
STARTSTACKSTR(p);
|
||||
if (*dest == '/') {
|
||||
STPUTC('/', p);
|
||||
path++;
|
||||
}
|
||||
first = 1;
|
||||
while ((q = getcomponent(&path)) != NULL) {
|
||||
if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
|
||||
continue;
|
||||
if (! first)
|
||||
STPUTC('/', p);
|
||||
first = 0;
|
||||
component = q;
|
||||
STPUTS(q, p);
|
||||
if (equal(component, ".."))
|
||||
continue;
|
||||
STACKSTRNUL(p);
|
||||
if (lstat(stackblock(), &statb) < 0) {
|
||||
badstat = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INTOFF;
|
||||
if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) {
|
||||
INTON;
|
||||
return (-1);
|
||||
}
|
||||
updatepwd(p);
|
||||
INTON;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
cdphysical(char *dest)
|
||||
{
|
||||
char *p;
|
||||
int rc = 0;
|
||||
|
||||
INTOFF;
|
||||
if (chdir(dest) < 0) {
|
||||
INTON;
|
||||
return (-1);
|
||||
}
|
||||
p = findcwd(NULL);
|
||||
if (p == NULL) {
|
||||
warning("warning: failed to get name of current directory");
|
||||
rc = 1;
|
||||
}
|
||||
updatepwd(p);
|
||||
INTON;
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the next component of the path name pointed to by *path.
|
||||
* This routine overwrites *path and the string pointed to by it.
|
||||
*/
|
||||
static char *
|
||||
getcomponent(char **path)
|
||||
{
|
||||
char *p;
|
||||
char *start;
|
||||
|
||||
if ((p = *path) == NULL)
|
||||
return NULL;
|
||||
start = *path;
|
||||
while (*p != '/' && *p != '\0')
|
||||
p++;
|
||||
if (*p == '\0') {
|
||||
*path = NULL;
|
||||
} else {
|
||||
*p++ = '\0';
|
||||
*path = p;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
findcwd(char *dir)
|
||||
{
|
||||
char *new;
|
||||
char *p;
|
||||
char *path;
|
||||
|
||||
/*
|
||||
* If our argument is NULL, we don't know the current directory
|
||||
* any more because we traversed a symbolic link or something
|
||||
* we couldn't stat().
|
||||
*/
|
||||
if (dir == NULL || curdir == NULL)
|
||||
return getpwd2();
|
||||
path = stsavestr(dir);
|
||||
STARTSTACKSTR(new);
|
||||
if (*dir != '/') {
|
||||
STPUTS(curdir, new);
|
||||
if (STTOPC(new) == '/')
|
||||
STUNPUTC(new);
|
||||
}
|
||||
while ((p = getcomponent(&path)) != NULL) {
|
||||
if (equal(p, "..")) {
|
||||
while (new > stackblock() && (STUNPUTC(new), *new) != '/');
|
||||
} else if (*p != '\0' && ! equal(p, ".")) {
|
||||
STPUTC('/', new);
|
||||
STPUTS(p, new);
|
||||
}
|
||||
}
|
||||
if (new == stackblock())
|
||||
STPUTC('/', new);
|
||||
STACKSTRNUL(new);
|
||||
return stackblock();
|
||||
}
|
||||
|
||||
/*
|
||||
* Update curdir (the name of the current directory) in response to a
|
||||
* cd command. We also call hashcd to let the routines in exec.c know
|
||||
* that the current directory has changed.
|
||||
*/
|
||||
static void
|
||||
updatepwd(char *dir)
|
||||
{
|
||||
char *prevdir;
|
||||
|
||||
hashcd(); /* update command hash table */
|
||||
|
||||
setvar("PWD", dir, VEXPORT);
|
||||
setvar("OLDPWD", curdir, VEXPORT);
|
||||
prevdir = curdir;
|
||||
curdir = dir ? savestr(dir) : NULL;
|
||||
ckfree(prevdir);
|
||||
}
|
||||
|
||||
int
|
||||
pwdcmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
char *p;
|
||||
int ch, phys;
|
||||
|
||||
phys = Pflag;
|
||||
while ((ch = nextopt("LP")) != '\0') {
|
||||
switch (ch) {
|
||||
case 'L':
|
||||
phys = 0;
|
||||
break;
|
||||
case 'P':
|
||||
phys = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*argptr != NULL)
|
||||
error("too many arguments");
|
||||
|
||||
if (!phys && getpwd()) {
|
||||
out1str(curdir);
|
||||
out1c('\n');
|
||||
} else {
|
||||
if ((p = getpwd2()) == NULL)
|
||||
error(".: %s", strerror(errno));
|
||||
out1str(p);
|
||||
out1c('\n');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current directory and cache the result in curdir.
|
||||
*/
|
||||
static char *
|
||||
getpwd(void)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (curdir)
|
||||
return curdir;
|
||||
|
||||
p = getpwd2();
|
||||
if (p != NULL)
|
||||
curdir = savestr(p);
|
||||
|
||||
return curdir;
|
||||
}
|
||||
|
||||
#define MAXPWD 256
|
||||
|
||||
/*
|
||||
* Return the current directory.
|
||||
*/
|
||||
static char *
|
||||
getpwd2(void)
|
||||
{
|
||||
char *pwd;
|
||||
int i;
|
||||
|
||||
for (i = MAXPWD;; i *= 2) {
|
||||
pwd = stalloc(i);
|
||||
if (getcwd(pwd, i) != NULL)
|
||||
return pwd;
|
||||
stunalloc(pwd);
|
||||
if (errno != ERANGE)
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize PWD in a new shell.
|
||||
* If the shell is interactive, we need to warn if this fails.
|
||||
*/
|
||||
void
|
||||
pwd_init(int warn)
|
||||
{
|
||||
char *pwd;
|
||||
struct stat stdot, stpwd;
|
||||
|
||||
pwd = lookupvar("PWD");
|
||||
if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
|
||||
stat(pwd, &stpwd) != -1 &&
|
||||
stdot.st_dev == stpwd.st_dev &&
|
||||
stdot.st_ino == stpwd.st_ino) {
|
||||
if (curdir)
|
||||
ckfree(curdir);
|
||||
curdir = savestr(pwd);
|
||||
}
|
||||
if (getpwd() == NULL && warn)
|
||||
out2fmt_flush("sh: cannot determine working directory\n");
|
||||
setvar("PWD", curdir, VEXPORT);
|
||||
}
|
32
sh/cd.h
Normal file
32
sh/cd.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*-
|
||||
* Copyright (c) 1995
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: head/bin/sh/cd.h 314436 2017-02-28 23:42:47Z imp $
|
||||
*/
|
||||
|
||||
void pwd_init(int);
|
199
sh/error.c
Normal file
199
sh/error.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/error.c 314436 2017-02-28 23:42:47Z imp $");
|
||||
|
||||
/*
|
||||
* Errors and exceptions.
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
#include "eval.h"
|
||||
#include "main.h"
|
||||
#include "options.h"
|
||||
#include "output.h"
|
||||
#include "error.h"
|
||||
#include "nodes.h" /* show.h needs nodes.h */
|
||||
#include "show.h"
|
||||
#include "trap.h"
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
/*
|
||||
* Code to handle exceptions in C.
|
||||
*/
|
||||
|
||||
struct jmploc *handler;
|
||||
volatile sig_atomic_t exception;
|
||||
volatile sig_atomic_t suppressint;
|
||||
volatile sig_atomic_t intpending;
|
||||
|
||||
|
||||
static void exverror(int, const char *, va_list) __printf0like(2, 0) __dead2;
|
||||
|
||||
/*
|
||||
* Called to raise an exception. Since C doesn't include exceptions, we
|
||||
* just do a longjmp to the exception handler. The type of exception is
|
||||
* stored in the global variable "exception".
|
||||
*
|
||||
* Interrupts are disabled; they should be reenabled when the exception is
|
||||
* caught.
|
||||
*/
|
||||
|
||||
void
|
||||
exraise(int e)
|
||||
{
|
||||
INTOFF;
|
||||
if (handler == NULL)
|
||||
abort();
|
||||
exception = e;
|
||||
longjmp(handler->loc, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called from trap.c when a SIGINT is received and not suppressed, or when
|
||||
* an interrupt is pending and interrupts are re-enabled using INTON.
|
||||
* (If the user specifies that SIGINT is to be trapped or ignored using the
|
||||
* trap builtin, then this routine is not called.) Suppressint is nonzero
|
||||
* when interrupts are held using the INTOFF macro. If SIGINTs are not
|
||||
* suppressed and the shell is not a root shell, then we want to be
|
||||
* terminated if we get here, as if we were terminated directly by a SIGINT.
|
||||
* Arrange for this here.
|
||||
*/
|
||||
|
||||
void
|
||||
onint(void)
|
||||
{
|
||||
sigset_t sigs;
|
||||
|
||||
intpending = 0;
|
||||
sigemptyset(&sigs);
|
||||
sigprocmask(SIG_SETMASK, &sigs, NULL);
|
||||
|
||||
/*
|
||||
* This doesn't seem to be needed, since main() emits a newline.
|
||||
*/
|
||||
#if 0
|
||||
if (tcgetpgrp(0) == getpid())
|
||||
write(STDERR_FILENO, "\n", 1);
|
||||
#endif
|
||||
if (rootshell && iflag)
|
||||
exraise(EXINT);
|
||||
else {
|
||||
signal(SIGINT, SIG_DFL);
|
||||
kill(getpid(), SIGINT);
|
||||
_exit(128 + SIGINT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
vwarning(const char *msg, va_list ap)
|
||||
{
|
||||
if (commandname)
|
||||
outfmt(out2, "%s: ", commandname);
|
||||
else if (arg0)
|
||||
outfmt(out2, "%s: ", arg0);
|
||||
doformat(out2, msg, ap);
|
||||
out2fmt_flush("\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
warning(const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vwarning(msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Exverror is called to raise the error exception. If the first argument
|
||||
* is not NULL then error prints an error message using printf style
|
||||
* formatting. It then raises the error exception.
|
||||
*/
|
||||
static void
|
||||
exverror(int cond, const char *msg, va_list ap)
|
||||
{
|
||||
/*
|
||||
* An interrupt trumps an error. Certain places catch error
|
||||
* exceptions or transform them to a plain nonzero exit code
|
||||
* in child processes, and if an error exception can be handled,
|
||||
* an interrupt can be handled as well.
|
||||
*
|
||||
* exraise() will disable interrupts for the exception handler.
|
||||
*/
|
||||
FORCEINTON;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (msg)
|
||||
TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
|
||||
else
|
||||
TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
|
||||
#endif
|
||||
if (msg)
|
||||
vwarning(msg, ap);
|
||||
flushall();
|
||||
exraise(cond);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
error(const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
exverror(EXERROR, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
exerror(int cond, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
exverror(cond, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
95
sh/error.h
Normal file
95
sh/error.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)error.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/error.h 319591 2017-06-04 21:58:02Z jilles $
|
||||
*/
|
||||
|
||||
/*
|
||||
* We enclose jmp_buf in a structure so that we can declare pointers to
|
||||
* jump locations. The global variable handler contains the location to
|
||||
* jump to when an exception occurs, and the global variable exception
|
||||
* contains a code identifying the exception. To implement nested
|
||||
* exception handlers, the user should save the value of handler on entry
|
||||
* to an inner scope, set handler to point to a jmploc structure for the
|
||||
* inner scope, and restore handler on exit from the scope.
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
|
||||
struct jmploc {
|
||||
jmp_buf loc;
|
||||
};
|
||||
|
||||
extern struct jmploc *handler;
|
||||
extern volatile sig_atomic_t exception;
|
||||
|
||||
/* exceptions */
|
||||
#define EXINT 0 /* SIGINT received */
|
||||
#define EXERROR 1 /* a generic error */
|
||||
#define EXEXEC 2 /* command execution failed */
|
||||
#define EXEXIT 3 /* call exitshell(exitstatus) */
|
||||
|
||||
|
||||
/*
|
||||
* These macros allow the user to suspend the handling of interrupt signals
|
||||
* over a period of time. This is similar to SIGHOLD to or sigblock, but
|
||||
* much more efficient and portable. (But hacking the kernel is so much
|
||||
* more fun than worrying about efficiency and portability. :-))
|
||||
*/
|
||||
|
||||
extern volatile sig_atomic_t suppressint;
|
||||
extern volatile sig_atomic_t intpending;
|
||||
|
||||
#define INTOFF suppressint++
|
||||
#define INTON { if (--suppressint == 0 && intpending) onint(); }
|
||||
#define is_int_on() suppressint
|
||||
#define SETINTON(s) do { suppressint = (s); if (suppressint == 0 && intpending) onint(); } while (0)
|
||||
#define FORCEINTON {suppressint = 0; if (intpending) onint();}
|
||||
#define SET_PENDING_INT intpending = 1
|
||||
#define CLEAR_PENDING_INT intpending = 0
|
||||
#define int_pending() intpending
|
||||
|
||||
void exraise(int) __dead2;
|
||||
void onint(void) __dead2;
|
||||
void warning(const char *, ...) __printflike(1, 2);
|
||||
void error(const char *, ...) __printf0like(1, 2) __dead2;
|
||||
void exerror(int, const char *, ...) __printf0like(2, 3) __dead2;
|
||||
|
||||
|
||||
/*
|
||||
* BSD setjmp saves the signal mask, which violates ANSI C and takes time,
|
||||
* so we use _setjmp instead.
|
||||
*/
|
||||
|
||||
#define setjmp(jmploc) _setjmp(jmploc)
|
||||
#define longjmp(jmploc, val) _longjmp(jmploc, val)
|
70
sh/eval.h
Normal file
70
sh/eval.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)eval.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/eval.h 314436 2017-02-28 23:42:47Z imp $
|
||||
*/
|
||||
|
||||
extern char *commandname; /* currently executing command */
|
||||
extern int exitstatus; /* exit status of last command */
|
||||
extern int oexitstatus; /* saved exit status */
|
||||
extern struct arglist *cmdenviron; /* environment for builtin command */
|
||||
|
||||
|
||||
struct backcmd { /* result of evalbackcmd */
|
||||
int fd; /* file descriptor to read from */
|
||||
char *buf; /* buffer */
|
||||
int nleft; /* number of chars in buffer */
|
||||
struct job *jp; /* job structure for command */
|
||||
};
|
||||
|
||||
void reseteval(void);
|
||||
|
||||
/* flags in argument to evaltree/evalstring */
|
||||
#define EV_EXIT 01 /* exit after evaluating tree */
|
||||
#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
|
||||
#define EV_BACKCMD 04 /* command executing within back quotes */
|
||||
|
||||
void evalstring(const char *, int);
|
||||
union node; /* BLETCH for ansi C */
|
||||
void evaltree(union node *, int);
|
||||
void evalbackcmd(union node *, struct backcmd *);
|
||||
|
||||
/* in_function returns nonzero if we are currently evaluating a function */
|
||||
#define in_function() funcnest
|
||||
extern int funcnest;
|
||||
extern int evalskip;
|
||||
extern int skipcount;
|
||||
|
||||
/* reasons for skipping commands (see comment on breakcmd routine) */
|
||||
#define SKIPBREAK 1
|
||||
#define SKIPCONT 2
|
||||
#define SKIPRETURN 3
|
780
sh/exec.c
Normal file
780
sh/exec.c
Normal file
@ -0,0 +1,780 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/exec.c 317882 2017-05-06 13:28:42Z jilles $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* When commands are first encountered, they are entered in a hash table.
|
||||
* This ensures that a full path search will not have to be done for them
|
||||
* on each invocation.
|
||||
*
|
||||
* We should investigate converting to a linear search, even though that
|
||||
* would make the command name "hash" a misnomer.
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
#include "main.h"
|
||||
#include "nodes.h"
|
||||
#include "parser.h"
|
||||
#include "redir.h"
|
||||
#include "eval.h"
|
||||
#include "exec.h"
|
||||
#include "builtins.h"
|
||||
#include "var.h"
|
||||
#include "options.h"
|
||||
#include "input.h"
|
||||
#include "output.h"
|
||||
#include "syntax.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "mystring.h"
|
||||
#include "show.h"
|
||||
#include "jobs.h"
|
||||
#include "alias.h"
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define eaccess(path, mode) faccessat(AT_FDCWD, path, mode, AT_EACCESS)
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#define CMDTABLESIZE 31 /* should be prime */
|
||||
|
||||
|
||||
|
||||
struct tblentry {
|
||||
struct tblentry *next; /* next entry in hash chain */
|
||||
union param param; /* definition of builtin function */
|
||||
int special; /* flag for special builtin commands */
|
||||
signed char cmdtype; /* index identifying command */
|
||||
char cmdname[]; /* name of command */
|
||||
};
|
||||
|
||||
|
||||
static struct tblentry *cmdtable[CMDTABLESIZE];
|
||||
static int cmdtable_cd = 0; /* cmdtable contains cd-dependent entries */
|
||||
int exerrno = 0; /* Last exec error */
|
||||
|
||||
|
||||
static void tryexec(char *, char **, char **);
|
||||
static void printentry(struct tblentry *, int);
|
||||
static struct tblentry *cmdlookup(const char *, int);
|
||||
static void delete_cmd_entry(void);
|
||||
static void addcmdentry(const char *, struct cmdentry *);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Exec a program. Never returns. If you change this routine, you may
|
||||
* have to change the find_command routine as well.
|
||||
*
|
||||
* The argv array may be changed and element argv[-1] should be writable.
|
||||
*/
|
||||
|
||||
void
|
||||
shellexec(char **argv, char **envp, const char *path, int idx)
|
||||
{
|
||||
char *cmdname;
|
||||
int e;
|
||||
|
||||
if (strchr(argv[0], '/') != NULL) {
|
||||
tryexec(argv[0], argv, envp);
|
||||
e = errno;
|
||||
} else {
|
||||
e = ENOENT;
|
||||
while ((cmdname = padvance(&path, argv[0])) != NULL) {
|
||||
if (--idx < 0 && pathopt == NULL) {
|
||||
tryexec(cmdname, argv, envp);
|
||||
if (errno != ENOENT && errno != ENOTDIR)
|
||||
e = errno;
|
||||
if (e == ENOEXEC)
|
||||
break;
|
||||
}
|
||||
stunalloc(cmdname);
|
||||
}
|
||||
}
|
||||
|
||||
/* Map to POSIX errors */
|
||||
if (e == ENOENT || e == ENOTDIR) {
|
||||
exerrno = 127;
|
||||
exerror(EXEXEC, "%s: not found", argv[0]);
|
||||
} else {
|
||||
exerrno = 126;
|
||||
exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tryexec(char *cmd, char **argv, char **envp)
|
||||
{
|
||||
int e, in;
|
||||
ssize_t n;
|
||||
char buf[256];
|
||||
|
||||
execve(cmd, argv, envp);
|
||||
e = errno;
|
||||
if (e == ENOEXEC) {
|
||||
INTOFF;
|
||||
in = open(cmd, O_RDONLY | O_NONBLOCK);
|
||||
if (in != -1) {
|
||||
n = pread(in, buf, sizeof buf, 0);
|
||||
close(in);
|
||||
if (n > 0 && memchr(buf, '\0', n) != NULL) {
|
||||
errno = ENOEXEC;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*argv = cmd;
|
||||
*--argv = __DECONST(char *, _PATH_BSHELL);
|
||||
execve(_PATH_BSHELL, argv, envp);
|
||||
}
|
||||
errno = e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a path search. The variable path (passed by reference) should be
|
||||
* set to the start of the path before the first call; padvance will update
|
||||
* this value as it proceeds. Successive calls to padvance will return
|
||||
* the possible path expansions in sequence. If an option (indicated by
|
||||
* a percent sign) appears in the path entry then the global variable
|
||||
* pathopt will be set to point to it; otherwise pathopt will be set to
|
||||
* NULL.
|
||||
*/
|
||||
|
||||
const char *pathopt;
|
||||
|
||||
char *
|
||||
padvance(const char **path, const char *name)
|
||||
{
|
||||
const char *p, *start;
|
||||
char *q;
|
||||
size_t len, namelen;
|
||||
|
||||
if (*path == NULL)
|
||||
return NULL;
|
||||
start = *path;
|
||||
for (p = start; *p && *p != ':' && *p != '%'; p++)
|
||||
; /* nothing */
|
||||
namelen = strlen(name);
|
||||
len = p - start + namelen + 2; /* "2" is for '/' and '\0' */
|
||||
STARTSTACKSTR(q);
|
||||
CHECKSTRSPACE(len, q);
|
||||
if (p != start) {
|
||||
memcpy(q, start, p - start);
|
||||
q += p - start;
|
||||
*q++ = '/';
|
||||
}
|
||||
memcpy(q, name, namelen + 1);
|
||||
pathopt = NULL;
|
||||
if (*p == '%') {
|
||||
pathopt = ++p;
|
||||
while (*p && *p != ':') p++;
|
||||
}
|
||||
if (*p == ':')
|
||||
*path = p + 1;
|
||||
else
|
||||
*path = NULL;
|
||||
return stalloc(len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** Command hashing code ***/
|
||||
|
||||
|
||||
int
|
||||
hashcmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
struct tblentry **pp;
|
||||
struct tblentry *cmdp;
|
||||
int c;
|
||||
int verbose;
|
||||
struct cmdentry entry;
|
||||
char *name;
|
||||
int errors;
|
||||
|
||||
errors = 0;
|
||||
verbose = 0;
|
||||
while ((c = nextopt("rv")) != '\0') {
|
||||
if (c == 'r') {
|
||||
clearcmdentry();
|
||||
} else if (c == 'v') {
|
||||
verbose++;
|
||||
}
|
||||
}
|
||||
if (*argptr == NULL) {
|
||||
for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
|
||||
for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
|
||||
if (cmdp->cmdtype == CMDNORMAL)
|
||||
printentry(cmdp, verbose);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
while ((name = *argptr) != NULL) {
|
||||
if ((cmdp = cmdlookup(name, 0)) != NULL
|
||||
&& cmdp->cmdtype == CMDNORMAL)
|
||||
delete_cmd_entry();
|
||||
find_command(name, &entry, DO_ERR, pathval());
|
||||
if (entry.cmdtype == CMDUNKNOWN)
|
||||
errors = 1;
|
||||
else if (verbose) {
|
||||
cmdp = cmdlookup(name, 0);
|
||||
if (cmdp != NULL)
|
||||
printentry(cmdp, verbose);
|
||||
else {
|
||||
outfmt(out2, "%s: not found\n", name);
|
||||
errors = 1;
|
||||
}
|
||||
flushall();
|
||||
}
|
||||
argptr++;
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
printentry(struct tblentry *cmdp, int verbose)
|
||||
{
|
||||
int idx;
|
||||
const char *path;
|
||||
char *name;
|
||||
|
||||
if (cmdp->cmdtype == CMDNORMAL) {
|
||||
idx = cmdp->param.index;
|
||||
path = pathval();
|
||||
do {
|
||||
name = padvance(&path, cmdp->cmdname);
|
||||
stunalloc(name);
|
||||
} while (--idx >= 0);
|
||||
out1str(name);
|
||||
} else if (cmdp->cmdtype == CMDBUILTIN) {
|
||||
out1fmt("builtin %s", cmdp->cmdname);
|
||||
} else if (cmdp->cmdtype == CMDFUNCTION) {
|
||||
out1fmt("function %s", cmdp->cmdname);
|
||||
if (verbose) {
|
||||
INTOFF;
|
||||
name = commandtext(getfuncnode(cmdp->param.func));
|
||||
out1c(' ');
|
||||
out1str(name);
|
||||
ckfree(name);
|
||||
INTON;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
} else {
|
||||
error("internal error: cmdtype %d", cmdp->cmdtype);
|
||||
#endif
|
||||
}
|
||||
out1c('\n');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Resolve a command name. If you change this routine, you may have to
|
||||
* change the shellexec routine as well.
|
||||
*/
|
||||
|
||||
void
|
||||
find_command(const char *name, struct cmdentry *entry, int act,
|
||||
const char *path)
|
||||
{
|
||||
struct tblentry *cmdp, loc_cmd;
|
||||
int idx;
|
||||
char *fullname;
|
||||
struct stat statb;
|
||||
int e;
|
||||
int i;
|
||||
int spec;
|
||||
int cd;
|
||||
|
||||
/* If name contains a slash, don't use the hash table */
|
||||
if (strchr(name, '/') != NULL) {
|
||||
entry->cmdtype = CMDNORMAL;
|
||||
entry->u.index = 0;
|
||||
entry->special = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
cd = 0;
|
||||
|
||||
/* If name is in the table, we're done */
|
||||
if ((cmdp = cmdlookup(name, 0)) != NULL) {
|
||||
if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
|
||||
cmdp = NULL;
|
||||
else
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* Check for builtin next */
|
||||
if ((i = find_builtin(name, &spec)) >= 0) {
|
||||
INTOFF;
|
||||
cmdp = cmdlookup(name, 1);
|
||||
if (cmdp->cmdtype == CMDFUNCTION)
|
||||
cmdp = &loc_cmd;
|
||||
cmdp->cmdtype = CMDBUILTIN;
|
||||
cmdp->param.index = i;
|
||||
cmdp->special = spec;
|
||||
INTON;
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* We have to search path. */
|
||||
|
||||
e = ENOENT;
|
||||
idx = -1;
|
||||
for (;(fullname = padvance(&path, name)) != NULL; stunalloc(fullname)) {
|
||||
idx++;
|
||||
if (pathopt) {
|
||||
if (strncmp(pathopt, "func", 4) == 0) {
|
||||
/* handled below */
|
||||
} else {
|
||||
continue; /* ignore unimplemented options */
|
||||
}
|
||||
}
|
||||
if (fullname[0] != '/')
|
||||
cd = 1;
|
||||
if (stat(fullname, &statb) < 0) {
|
||||
if (errno != ENOENT && errno != ENOTDIR)
|
||||
e = errno;
|
||||
continue;
|
||||
}
|
||||
e = EACCES; /* if we fail, this will be the error */
|
||||
if (!S_ISREG(statb.st_mode))
|
||||
continue;
|
||||
if (pathopt) { /* this is a %func directory */
|
||||
readcmdfile(fullname);
|
||||
if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
|
||||
error("%s not defined in %s", name, fullname);
|
||||
stunalloc(fullname);
|
||||
goto success;
|
||||
}
|
||||
#ifdef notdef
|
||||
if (statb.st_uid == geteuid()) {
|
||||
if ((statb.st_mode & 0100) == 0)
|
||||
goto loop;
|
||||
} else if (statb.st_gid == getegid()) {
|
||||
if ((statb.st_mode & 010) == 0)
|
||||
goto loop;
|
||||
} else {
|
||||
if ((statb.st_mode & 01) == 0)
|
||||
goto loop;
|
||||
}
|
||||
#endif
|
||||
TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
|
||||
INTOFF;
|
||||
stunalloc(fullname);
|
||||
cmdp = cmdlookup(name, 1);
|
||||
if (cmdp->cmdtype == CMDFUNCTION)
|
||||
cmdp = &loc_cmd;
|
||||
cmdp->cmdtype = CMDNORMAL;
|
||||
cmdp->param.index = idx;
|
||||
cmdp->special = 0;
|
||||
INTON;
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (act & DO_ERR) {
|
||||
if (e == ENOENT || e == ENOTDIR)
|
||||
outfmt(out2, "%s: not found\n", name);
|
||||
else
|
||||
outfmt(out2, "%s: %s\n", name, strerror(e));
|
||||
}
|
||||
entry->cmdtype = CMDUNKNOWN;
|
||||
entry->u.index = 0;
|
||||
entry->special = 0;
|
||||
return;
|
||||
|
||||
success:
|
||||
if (cd)
|
||||
cmdtable_cd = 1;
|
||||
entry->cmdtype = cmdp->cmdtype;
|
||||
entry->u = cmdp->param;
|
||||
entry->special = cmdp->special;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Search the table of builtin commands.
|
||||
*/
|
||||
|
||||
int
|
||||
find_builtin(const char *name, int *special)
|
||||
{
|
||||
const unsigned char *bp;
|
||||
size_t len;
|
||||
|
||||
len = strlen(name);
|
||||
for (bp = builtincmd ; *bp ; bp += 2 + bp[0]) {
|
||||
if (bp[0] == len && memcmp(bp + 2, name, len) == 0) {
|
||||
*special = (bp[1] & BUILTIN_SPECIAL) != 0;
|
||||
return bp[1] & ~BUILTIN_SPECIAL;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Called when a cd is done. If any entry in cmdtable depends on the current
|
||||
* directory, simply clear cmdtable completely.
|
||||
*/
|
||||
|
||||
void
|
||||
hashcd(void)
|
||||
{
|
||||
if (cmdtable_cd)
|
||||
clearcmdentry();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Called before PATH is changed. The argument is the new value of PATH;
|
||||
* pathval() still returns the old value at this point. Called with
|
||||
* interrupts off.
|
||||
*/
|
||||
|
||||
void
|
||||
changepath(const char *newval __unused)
|
||||
{
|
||||
clearcmdentry();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Clear out cached utility locations.
|
||||
*/
|
||||
|
||||
void
|
||||
clearcmdentry(void)
|
||||
{
|
||||
struct tblentry **tblp;
|
||||
struct tblentry **pp;
|
||||
struct tblentry *cmdp;
|
||||
|
||||
INTOFF;
|
||||
for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
|
||||
pp = tblp;
|
||||
while ((cmdp = *pp) != NULL) {
|
||||
if (cmdp->cmdtype == CMDNORMAL) {
|
||||
*pp = cmdp->next;
|
||||
ckfree(cmdp);
|
||||
} else {
|
||||
pp = &cmdp->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
cmdtable_cd = 0;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Locate a command in the command hash table. If "add" is nonzero,
|
||||
* add the command to the table if it is not already present. The
|
||||
* variable "lastcmdentry" is set to point to the address of the link
|
||||
* pointing to the entry, so that delete_cmd_entry can delete the
|
||||
* entry.
|
||||
*/
|
||||
|
||||
static struct tblentry **lastcmdentry;
|
||||
|
||||
|
||||
static struct tblentry *
|
||||
cmdlookup(const char *name, int add)
|
||||
{
|
||||
unsigned int hashval;
|
||||
const char *p;
|
||||
struct tblentry *cmdp;
|
||||
struct tblentry **pp;
|
||||
size_t len;
|
||||
|
||||
p = name;
|
||||
hashval = (unsigned char)*p << 4;
|
||||
while (*p)
|
||||
hashval += *p++;
|
||||
pp = &cmdtable[hashval % CMDTABLESIZE];
|
||||
for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
|
||||
if (equal(cmdp->cmdname, name))
|
||||
break;
|
||||
pp = &cmdp->next;
|
||||
}
|
||||
if (add && cmdp == NULL) {
|
||||
INTOFF;
|
||||
len = strlen(name);
|
||||
cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1);
|
||||
cmdp->next = NULL;
|
||||
cmdp->cmdtype = CMDUNKNOWN;
|
||||
memcpy(cmdp->cmdname, name, len + 1);
|
||||
INTON;
|
||||
}
|
||||
lastcmdentry = pp;
|
||||
return cmdp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the command entry returned on the last lookup.
|
||||
*/
|
||||
|
||||
static void
|
||||
delete_cmd_entry(void)
|
||||
{
|
||||
struct tblentry *cmdp;
|
||||
|
||||
INTOFF;
|
||||
cmdp = *lastcmdentry;
|
||||
*lastcmdentry = cmdp->next;
|
||||
ckfree(cmdp);
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Add a new command entry, replacing any existing command entry for
|
||||
* the same name.
|
||||
*/
|
||||
|
||||
static void
|
||||
addcmdentry(const char *name, struct cmdentry *entry)
|
||||
{
|
||||
struct tblentry *cmdp;
|
||||
|
||||
INTOFF;
|
||||
cmdp = cmdlookup(name, 1);
|
||||
if (cmdp->cmdtype == CMDFUNCTION) {
|
||||
unreffunc(cmdp->param.func);
|
||||
}
|
||||
cmdp->cmdtype = entry->cmdtype;
|
||||
cmdp->param = entry->u;
|
||||
cmdp->special = entry->special;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Define a shell function.
|
||||
*/
|
||||
|
||||
void
|
||||
defun(const char *name, union node *func)
|
||||
{
|
||||
struct cmdentry entry;
|
||||
|
||||
INTOFF;
|
||||
entry.cmdtype = CMDFUNCTION;
|
||||
entry.u.func = copyfunc(func);
|
||||
entry.special = 0;
|
||||
addcmdentry(name, &entry);
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Delete a function if it exists.
|
||||
* Called with interrupts off.
|
||||
*/
|
||||
|
||||
int
|
||||
unsetfunc(const char *name)
|
||||
{
|
||||
struct tblentry *cmdp;
|
||||
|
||||
if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
|
||||
unreffunc(cmdp->param.func);
|
||||
delete_cmd_entry();
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check if a function by a certain name exists.
|
||||
*/
|
||||
int
|
||||
isfunc(const char *name)
|
||||
{
|
||||
struct tblentry *cmdp;
|
||||
cmdp = cmdlookup(name, 0);
|
||||
return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Shared code for the following builtin commands:
|
||||
* type, command -v, command -V
|
||||
*/
|
||||
|
||||
int
|
||||
typecmd_impl(int argc, char **argv, int cmd, const char *path)
|
||||
{
|
||||
struct cmdentry entry;
|
||||
struct tblentry *cmdp;
|
||||
const char *const *pp;
|
||||
struct alias *ap;
|
||||
int i;
|
||||
int error1 = 0;
|
||||
|
||||
if (path != pathval())
|
||||
clearcmdentry();
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
/* First look at the keywords */
|
||||
for (pp = parsekwd; *pp; pp++)
|
||||
if (**pp == *argv[i] && equal(*pp, argv[i]))
|
||||
break;
|
||||
|
||||
if (*pp) {
|
||||
if (cmd == TYPECMD_SMALLV)
|
||||
out1fmt("%s\n", argv[i]);
|
||||
else
|
||||
out1fmt("%s is a shell keyword\n", argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Then look at the aliases */
|
||||
if ((ap = lookupalias(argv[i], 1)) != NULL) {
|
||||
if (cmd == TYPECMD_SMALLV) {
|
||||
out1fmt("alias %s=", argv[i]);
|
||||
out1qstr(ap->val);
|
||||
outcslow('\n', out1);
|
||||
} else
|
||||
out1fmt("%s is an alias for %s\n", argv[i],
|
||||
ap->val);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Then check if it is a tracked alias */
|
||||
if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
|
||||
entry.cmdtype = cmdp->cmdtype;
|
||||
entry.u = cmdp->param;
|
||||
entry.special = cmdp->special;
|
||||
}
|
||||
else {
|
||||
/* Finally use brute force */
|
||||
find_command(argv[i], &entry, 0, path);
|
||||
}
|
||||
|
||||
switch (entry.cmdtype) {
|
||||
case CMDNORMAL: {
|
||||
if (strchr(argv[i], '/') == NULL) {
|
||||
const char *path2 = path;
|
||||
char *name;
|
||||
int j = entry.u.index;
|
||||
do {
|
||||
name = padvance(&path2, argv[i]);
|
||||
stunalloc(name);
|
||||
} while (--j >= 0);
|
||||
if (cmd == TYPECMD_SMALLV)
|
||||
out1fmt("%s\n", name);
|
||||
else
|
||||
out1fmt("%s is%s %s\n", argv[i],
|
||||
(cmdp && cmd == TYPECMD_TYPE) ?
|
||||
" a tracked alias for" : "",
|
||||
name);
|
||||
} else {
|
||||
if (eaccess(argv[i], X_OK) == 0) {
|
||||
if (cmd == TYPECMD_SMALLV)
|
||||
out1fmt("%s\n", argv[i]);
|
||||
else
|
||||
out1fmt("%s is %s\n", argv[i],
|
||||
argv[i]);
|
||||
} else {
|
||||
if (cmd != TYPECMD_SMALLV)
|
||||
outfmt(out2, "%s: %s\n",
|
||||
argv[i], strerror(errno));
|
||||
error1 |= 127;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMDFUNCTION:
|
||||
if (cmd == TYPECMD_SMALLV)
|
||||
out1fmt("%s\n", argv[i]);
|
||||
else
|
||||
out1fmt("%s is a shell function\n", argv[i]);
|
||||
break;
|
||||
|
||||
case CMDBUILTIN:
|
||||
if (cmd == TYPECMD_SMALLV)
|
||||
out1fmt("%s\n", argv[i]);
|
||||
else if (entry.special)
|
||||
out1fmt("%s is a special shell builtin\n",
|
||||
argv[i]);
|
||||
else
|
||||
out1fmt("%s is a shell builtin\n", argv[i]);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (cmd != TYPECMD_SMALLV)
|
||||
outfmt(out2, "%s: not found\n", argv[i]);
|
||||
error1 |= 127;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path != pathval())
|
||||
clearcmdentry();
|
||||
|
||||
return error1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate and print what a word is...
|
||||
*/
|
||||
|
||||
int
|
||||
typecmd(int argc, char **argv)
|
||||
{
|
||||
if (argc > 2 && strcmp(argv[1], "--") == 0)
|
||||
argc--, argv++;
|
||||
return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
|
||||
}
|
77
sh/exec.h
Normal file
77
sh/exec.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)exec.h 8.3 (Berkeley) 6/8/95
|
||||
* $FreeBSD: head/bin/sh/exec.h 314436 2017-02-28 23:42:47Z imp $
|
||||
*/
|
||||
|
||||
/* values of cmdtype */
|
||||
#define CMDUNKNOWN -1 /* no entry in table for command */
|
||||
#define CMDNORMAL 0 /* command is an executable program */
|
||||
#define CMDBUILTIN 1 /* command is a shell builtin */
|
||||
#define CMDFUNCTION 2 /* command is a shell function */
|
||||
|
||||
/* values for typecmd_impl's third parameter */
|
||||
enum {
|
||||
TYPECMD_SMALLV, /* command -v */
|
||||
TYPECMD_BIGV, /* command -V */
|
||||
TYPECMD_TYPE /* type */
|
||||
};
|
||||
|
||||
union node;
|
||||
struct cmdentry {
|
||||
int cmdtype;
|
||||
union param {
|
||||
int index;
|
||||
struct funcdef *func;
|
||||
} u;
|
||||
int special;
|
||||
};
|
||||
|
||||
|
||||
/* action to find_command() */
|
||||
#define DO_ERR 0x01 /* prints errors */
|
||||
#define DO_NOFUNC 0x02 /* don't return shell functions, for command */
|
||||
|
||||
extern const char *pathopt; /* set by padvance */
|
||||
extern int exerrno; /* last exec error */
|
||||
|
||||
void shellexec(char **, char **, const char *, int) __dead2;
|
||||
char *padvance(const char **, const char *);
|
||||
void find_command(const char *, struct cmdentry *, int, const char *);
|
||||
int find_builtin(const char *, int *);
|
||||
void hashcd(void);
|
||||
void changepath(const char *);
|
||||
void defun(const char *, union node *);
|
||||
int unsetfunc(const char *);
|
||||
int isfunc(const char *);
|
||||
int typecmd_impl(int, char **, int, const char *);
|
||||
void clearcmdentry(void);
|
1550
sh/expand.c
Normal file
1550
sh/expand.c
Normal file
File diff suppressed because it is too large
Load Diff
62
sh/expand.h
Normal file
62
sh/expand.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)expand.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/expand.h 314436 2017-02-28 23:42:47Z imp $
|
||||
*/
|
||||
|
||||
struct arglist {
|
||||
char **args;
|
||||
int count;
|
||||
int capacity;
|
||||
char *smallarg[1];
|
||||
};
|
||||
|
||||
/*
|
||||
* expandarg() flags
|
||||
*/
|
||||
#define EXP_SPLIT 0x1 /* perform word splitting */
|
||||
#define EXP_TILDE 0x2 /* do normal tilde expansion */
|
||||
#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
|
||||
#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
|
||||
#define EXP_SPLIT_LIT 0x20 /* IFS split literal text ${v+-a b c} */
|
||||
#define EXP_LIT_QUOTED 0x40 /* for EXP_SPLIT_LIT, start off quoted */
|
||||
#define EXP_GLOB 0x80 /* perform file globbing */
|
||||
|
||||
#define EXP_FULL (EXP_SPLIT | EXP_GLOB)
|
||||
|
||||
|
||||
void emptyarglist(struct arglist *);
|
||||
void appendarglist(struct arglist *, char *);
|
||||
union node;
|
||||
void expandarg(union node *, struct arglist *, int);
|
||||
void rmescapes(char *);
|
||||
int casematch(union node *, const char *);
|
49
sh/funcs/cmv
Normal file
49
sh/funcs/cmv
Normal file
@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)cmv 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/funcs/cmv 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
# Conditional move--don't replace an existing file.
|
||||
|
||||
cmv() {
|
||||
if test $# != 2
|
||||
then echo "cmv: arg count"
|
||||
return 2
|
||||
fi
|
||||
if test -f "$2" -o -w "$2"
|
||||
then echo "$2 exists"
|
||||
return 2
|
||||
fi
|
||||
/bin/mv "$1" "$2"
|
||||
}
|
73
sh/funcs/dirs
Normal file
73
sh/funcs/dirs
Normal file
@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)dirs 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/funcs/dirs 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
# pushd, popd, and dirs --- written by Chris Bertin
|
||||
# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
|
||||
# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
|
||||
|
||||
pushd () {
|
||||
SAVE=`pwd`
|
||||
if [ "$1" = "" ]
|
||||
then if [ "$DSTACK" = "" ]
|
||||
then echo "pushd: directory stack empty."
|
||||
return 1
|
||||
fi
|
||||
set $DSTACK
|
||||
cd $1 || return
|
||||
shift 1
|
||||
DSTACK="$*"
|
||||
else cd $1 > /dev/null || return
|
||||
fi
|
||||
DSTACK="$SAVE $DSTACK"
|
||||
dirs
|
||||
}
|
||||
|
||||
popd () {
|
||||
if [ "$DSTACK" = "" ]
|
||||
then echo "popd: directory stack empty."
|
||||
return 1
|
||||
fi
|
||||
set $DSTACK
|
||||
cd $1
|
||||
shift
|
||||
DSTACK=$*
|
||||
dirs
|
||||
}
|
||||
|
||||
dirs () {
|
||||
echo "`pwd` $DSTACK"
|
||||
return 0
|
||||
}
|
38
sh/funcs/login
Normal file
38
sh/funcs/login
Normal file
@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)login 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/funcs/login 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
# replaces the login builtin in the BSD shell
|
||||
login () exec login "$@"
|
37
sh/funcs/newgrp
Normal file
37
sh/funcs/newgrp
Normal file
@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)newgrp 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/funcs/newgrp 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
newgrp() exec newgrp "$@"
|
73
sh/funcs/popd
Normal file
73
sh/funcs/popd
Normal file
@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)popd 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/funcs/popd 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
# pushd, popd, and dirs --- written by Chris Bertin
|
||||
# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
|
||||
# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
|
||||
|
||||
pushd () {
|
||||
SAVE=`pwd`
|
||||
if [ "$1" = "" ]
|
||||
then if [ "$DSTACK" = "" ]
|
||||
then echo "pushd: directory stack empty."
|
||||
return 1
|
||||
fi
|
||||
set $DSTACK
|
||||
cd $1 || return
|
||||
shift 1
|
||||
DSTACK="$*"
|
||||
else cd $1 > /dev/null || return
|
||||
fi
|
||||
DSTACK="$SAVE $DSTACK"
|
||||
dirs
|
||||
}
|
||||
|
||||
popd () {
|
||||
if [ "$DSTACK" = "" ]
|
||||
then echo "popd: directory stack empty."
|
||||
return 1
|
||||
fi
|
||||
set $DSTACK
|
||||
cd $1
|
||||
shift
|
||||
DSTACK=$*
|
||||
dirs
|
||||
}
|
||||
|
||||
dirs () {
|
||||
echo "`pwd` $DSTACK"
|
||||
return 0
|
||||
}
|
73
sh/funcs/pushd
Normal file
73
sh/funcs/pushd
Normal file
@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)pushd 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/funcs/pushd 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
# pushd, popd, and dirs --- written by Chris Bertin
|
||||
# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
|
||||
# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
|
||||
|
||||
pushd () {
|
||||
SAVE=`pwd`
|
||||
if [ "$1" = "" ]
|
||||
then if [ "$DSTACK" = "" ]
|
||||
then echo "pushd: directory stack empty."
|
||||
return 1
|
||||
fi
|
||||
set $DSTACK
|
||||
cd $1 || return
|
||||
shift 1
|
||||
DSTACK="$*"
|
||||
else cd $1 > /dev/null || return
|
||||
fi
|
||||
DSTACK="$SAVE $DSTACK"
|
||||
dirs
|
||||
}
|
||||
|
||||
popd () {
|
||||
if [ "$DSTACK" = "" ]
|
||||
then echo "popd: directory stack empty."
|
||||
return 1
|
||||
fi
|
||||
set $DSTACK
|
||||
cd $1
|
||||
shift
|
||||
DSTACK=$*
|
||||
dirs
|
||||
}
|
||||
|
||||
dirs () {
|
||||
echo "`pwd` $DSTACK"
|
||||
return 0
|
||||
}
|
39
sh/funcs/suspend
Normal file
39
sh/funcs/suspend
Normal file
@ -0,0 +1,39 @@
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)suspend 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/funcs/suspend 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
suspend() {
|
||||
local -
|
||||
set +m
|
||||
kill -TSTP 0
|
||||
}
|
502
sh/histedit.c
Normal file
502
sh/histedit.c
Normal file
@ -0,0 +1,502 @@
|
||||
/*-
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/histedit.c 319635 2017-06-06 21:08:05Z jilles $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
/*
|
||||
* Editline and history functions (and glue).
|
||||
*/
|
||||
#include "shell.h"
|
||||
#include "parser.h"
|
||||
#include "var.h"
|
||||
#include "options.h"
|
||||
#include "main.h"
|
||||
#include "output.h"
|
||||
#include "mystring.h"
|
||||
#ifndef NO_HISTORY
|
||||
#include "myhistedit.h"
|
||||
#include "error.h"
|
||||
#include "eval.h"
|
||||
#include "memalloc.h"
|
||||
#include "builtins.h"
|
||||
|
||||
#define MAXHISTLOOPS 4 /* max recursions through fc */
|
||||
#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */
|
||||
|
||||
History *hist; /* history cookie */
|
||||
EditLine *el; /* editline cookie */
|
||||
int displayhist;
|
||||
static FILE *el_in, *el_out, *el_err;
|
||||
|
||||
static char *fc_replace(const char *, char *, char *);
|
||||
static int not_fcnumber(const char *);
|
||||
static int str_to_event(const char *, int);
|
||||
|
||||
/*
|
||||
* Set history and editing status. Called whenever the status may
|
||||
* have changed (figures out what to do).
|
||||
*/
|
||||
void
|
||||
histedit(void)
|
||||
{
|
||||
|
||||
#define editing (Eflag || Vflag)
|
||||
|
||||
if (iflag) {
|
||||
if (!hist) {
|
||||
/*
|
||||
* turn history on
|
||||
*/
|
||||
INTOFF;
|
||||
hist = history_init();
|
||||
INTON;
|
||||
|
||||
if (hist != NULL)
|
||||
sethistsize(histsizeval());
|
||||
else
|
||||
out2fmt_flush("sh: can't initialize history\n");
|
||||
}
|
||||
if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
|
||||
/*
|
||||
* turn editing on
|
||||
*/
|
||||
char *term;
|
||||
|
||||
INTOFF;
|
||||
if (el_in == NULL)
|
||||
el_in = fdopen(0, "r");
|
||||
if (el_err == NULL)
|
||||
el_err = fdopen(1, "w");
|
||||
if (el_out == NULL)
|
||||
el_out = fdopen(2, "w");
|
||||
if (el_in == NULL || el_err == NULL || el_out == NULL)
|
||||
goto bad;
|
||||
term = lookupvar("TERM");
|
||||
if (term)
|
||||
setenv("TERM", term, 1);
|
||||
else
|
||||
unsetenv("TERM");
|
||||
el = el_init(arg0, el_in, el_out, el_err);
|
||||
if (el != NULL) {
|
||||
if (hist)
|
||||
el_set(el, EL_HIST, history, hist);
|
||||
el_set(el, EL_PROMPT, getprompt);
|
||||
el_set(el, EL_ADDFN, "sh-complete",
|
||||
"Filename completion",
|
||||
_el_fn_sh_complete);
|
||||
} else {
|
||||
bad:
|
||||
out2fmt_flush("sh: can't initialize editing\n");
|
||||
}
|
||||
INTON;
|
||||
} else if (!editing && el) {
|
||||
INTOFF;
|
||||
el_end(el);
|
||||
el = NULL;
|
||||
INTON;
|
||||
}
|
||||
if (el) {
|
||||
if (Vflag)
|
||||
el_set(el, EL_EDITOR, "vi");
|
||||
else if (Eflag)
|
||||
el_set(el, EL_EDITOR, "emacs");
|
||||
el_set(el, EL_BIND, "^I", "sh-complete", NULL);
|
||||
el_source(el, NULL);
|
||||
}
|
||||
} else {
|
||||
INTOFF;
|
||||
if (el) { /* no editing if not interactive */
|
||||
el_end(el);
|
||||
el = NULL;
|
||||
}
|
||||
if (hist) {
|
||||
history_end(hist);
|
||||
hist = NULL;
|
||||
}
|
||||
INTON;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sethistsize(const char *hs)
|
||||
{
|
||||
int histsize;
|
||||
HistEvent he;
|
||||
|
||||
if (hist != NULL) {
|
||||
if (hs == NULL || !is_number(hs))
|
||||
histsize = 100;
|
||||
else
|
||||
histsize = atoi(hs);
|
||||
history(hist, &he, H_SETSIZE, histsize);
|
||||
history(hist, &he, H_SETUNIQUE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setterm(const char *term)
|
||||
{
|
||||
if (rootshell && el != NULL && term != NULL)
|
||||
el_set(el, EL_TERMINAL, term);
|
||||
}
|
||||
|
||||
int
|
||||
histcmd(int argc, char **argv __unused)
|
||||
{
|
||||
int ch;
|
||||
const char *editor = NULL;
|
||||
HistEvent he;
|
||||
int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
|
||||
int i, retval;
|
||||
const char *firststr, *laststr;
|
||||
int first, last, direction;
|
||||
char *pat = NULL, *repl = NULL;
|
||||
static int active = 0;
|
||||
struct jmploc jmploc;
|
||||
struct jmploc *savehandler;
|
||||
char editfilestr[PATH_MAX];
|
||||
char *volatile editfile;
|
||||
FILE *efp = NULL;
|
||||
int oldhistnum;
|
||||
|
||||
if (hist == NULL)
|
||||
error("history not active");
|
||||
|
||||
if (argc == 1)
|
||||
error("missing history argument");
|
||||
|
||||
while (not_fcnumber(*argptr) && (ch = nextopt("e:lnrs")) != '\0')
|
||||
switch ((char)ch) {
|
||||
case 'e':
|
||||
editor = shoptarg;
|
||||
break;
|
||||
case 'l':
|
||||
lflg = 1;
|
||||
break;
|
||||
case 'n':
|
||||
nflg = 1;
|
||||
break;
|
||||
case 'r':
|
||||
rflg = 1;
|
||||
break;
|
||||
case 's':
|
||||
sflg = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
savehandler = handler;
|
||||
/*
|
||||
* If executing...
|
||||
*/
|
||||
if (lflg == 0 || editor || sflg) {
|
||||
lflg = 0; /* ignore */
|
||||
editfile = NULL;
|
||||
/*
|
||||
* Catch interrupts to reset active counter and
|
||||
* cleanup temp files.
|
||||
*/
|
||||
if (setjmp(jmploc.loc)) {
|
||||
active = 0;
|
||||
if (editfile)
|
||||
unlink(editfile);
|
||||
handler = savehandler;
|
||||
longjmp(handler->loc, 1);
|
||||
}
|
||||
handler = &jmploc;
|
||||
if (++active > MAXHISTLOOPS) {
|
||||
active = 0;
|
||||
displayhist = 0;
|
||||
error("called recursively too many times");
|
||||
}
|
||||
/*
|
||||
* Set editor.
|
||||
*/
|
||||
if (sflg == 0) {
|
||||
if (editor == NULL &&
|
||||
(editor = bltinlookup("FCEDIT", 1)) == NULL &&
|
||||
(editor = bltinlookup("EDITOR", 1)) == NULL)
|
||||
editor = DEFEDITOR;
|
||||
if (editor[0] == '-' && editor[1] == '\0') {
|
||||
sflg = 1; /* no edit */
|
||||
editor = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If executing, parse [old=new] now
|
||||
*/
|
||||
if (lflg == 0 && *argptr != NULL &&
|
||||
((repl = strchr(*argptr, '=')) != NULL)) {
|
||||
pat = *argptr;
|
||||
*repl++ = '\0';
|
||||
argptr++;
|
||||
}
|
||||
/*
|
||||
* determine [first] and [last]
|
||||
*/
|
||||
if (*argptr == NULL) {
|
||||
firststr = lflg ? "-16" : "-1";
|
||||
laststr = "-1";
|
||||
} else if (argptr[1] == NULL) {
|
||||
firststr = argptr[0];
|
||||
laststr = lflg ? "-1" : argptr[0];
|
||||
} else if (argptr[2] == NULL) {
|
||||
firststr = argptr[0];
|
||||
laststr = argptr[1];
|
||||
} else
|
||||
error("too many arguments");
|
||||
/*
|
||||
* Turn into event numbers.
|
||||
*/
|
||||
first = str_to_event(firststr, 0);
|
||||
last = str_to_event(laststr, 1);
|
||||
|
||||
if (rflg) {
|
||||
i = last;
|
||||
last = first;
|
||||
first = i;
|
||||
}
|
||||
/*
|
||||
* XXX - this should not depend on the event numbers
|
||||
* always increasing. Add sequence numbers or offset
|
||||
* to the history element in next (diskbased) release.
|
||||
*/
|
||||
direction = first < last ? H_PREV : H_NEXT;
|
||||
|
||||
/*
|
||||
* If editing, grab a temp file.
|
||||
*/
|
||||
if (editor) {
|
||||
int fd;
|
||||
INTOFF; /* easier */
|
||||
sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP);
|
||||
if ((fd = mkstemp(editfilestr)) < 0)
|
||||
error("can't create temporary file %s", editfile);
|
||||
editfile = editfilestr;
|
||||
if ((efp = fdopen(fd, "w")) == NULL) {
|
||||
close(fd);
|
||||
error("Out of space");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop through selected history events. If listing or executing,
|
||||
* do it now. Otherwise, put into temp file and call the editor
|
||||
* after.
|
||||
*
|
||||
* The history interface needs rethinking, as the following
|
||||
* convolutions will demonstrate.
|
||||
*/
|
||||
history(hist, &he, H_FIRST);
|
||||
retval = history(hist, &he, H_NEXT_EVENT, first);
|
||||
for (;retval != -1; retval = history(hist, &he, direction)) {
|
||||
if (lflg) {
|
||||
if (!nflg)
|
||||
out1fmt("%5d ", he.num);
|
||||
out1str(he.str);
|
||||
} else {
|
||||
const char *s = pat ?
|
||||
fc_replace(he.str, pat, repl) : he.str;
|
||||
|
||||
if (sflg) {
|
||||
if (displayhist) {
|
||||
out2str(s);
|
||||
flushout(out2);
|
||||
}
|
||||
evalstring(s, 0);
|
||||
if (displayhist && hist) {
|
||||
/*
|
||||
* XXX what about recursive and
|
||||
* relative histnums.
|
||||
*/
|
||||
oldhistnum = he.num;
|
||||
history(hist, &he, H_ENTER, s);
|
||||
/*
|
||||
* XXX H_ENTER moves the internal
|
||||
* cursor, set it back to the current
|
||||
* entry.
|
||||
*/
|
||||
history(hist, &he,
|
||||
H_NEXT_EVENT, oldhistnum);
|
||||
}
|
||||
} else
|
||||
fputs(s, efp);
|
||||
}
|
||||
/*
|
||||
* At end? (if we were to lose last, we'd sure be
|
||||
* messed up).
|
||||
*/
|
||||
if (he.num == last)
|
||||
break;
|
||||
}
|
||||
if (editor) {
|
||||
char *editcmd;
|
||||
|
||||
fclose(efp);
|
||||
INTON;
|
||||
editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
|
||||
sprintf(editcmd, "%s %s", editor, editfile);
|
||||
evalstring(editcmd, 0); /* XXX - should use no JC command */
|
||||
readcmdfile(editfile); /* XXX - should read back - quick tst */
|
||||
unlink(editfile);
|
||||
}
|
||||
|
||||
if (lflg == 0 && active > 0)
|
||||
--active;
|
||||
if (displayhist)
|
||||
displayhist = 0;
|
||||
handler = savehandler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
fc_replace(const char *s, char *p, char *r)
|
||||
{
|
||||
char *dest;
|
||||
int plen = strlen(p);
|
||||
|
||||
STARTSTACKSTR(dest);
|
||||
while (*s) {
|
||||
if (*s == *p && strncmp(s, p, plen) == 0) {
|
||||
STPUTS(r, dest);
|
||||
s += plen;
|
||||
*p = '\0'; /* so no more matches */
|
||||
} else
|
||||
STPUTC(*s++, dest);
|
||||
}
|
||||
STPUTC('\0', dest);
|
||||
dest = grabstackstr(dest);
|
||||
|
||||
return (dest);
|
||||
}
|
||||
|
||||
static int
|
||||
not_fcnumber(const char *s)
|
||||
{
|
||||
if (s == NULL)
|
||||
return (0);
|
||||
if (*s == '-')
|
||||
s++;
|
||||
return (!is_number(s));
|
||||
}
|
||||
|
||||
static int
|
||||
str_to_event(const char *str, int last)
|
||||
{
|
||||
HistEvent he;
|
||||
const char *s = str;
|
||||
int relative = 0;
|
||||
int i, retval;
|
||||
|
||||
retval = history(hist, &he, H_FIRST);
|
||||
switch (*s) {
|
||||
case '-':
|
||||
relative = 1;
|
||||
/*FALLTHROUGH*/
|
||||
case '+':
|
||||
s++;
|
||||
}
|
||||
if (is_number(s)) {
|
||||
i = atoi(s);
|
||||
if (relative) {
|
||||
while (retval != -1 && i--) {
|
||||
retval = history(hist, &he, H_NEXT);
|
||||
}
|
||||
if (retval == -1)
|
||||
retval = history(hist, &he, H_LAST);
|
||||
} else {
|
||||
retval = history(hist, &he, H_NEXT_EVENT, i);
|
||||
if (retval == -1) {
|
||||
/*
|
||||
* the notion of first and last is
|
||||
* backwards to that of the history package
|
||||
*/
|
||||
retval = history(hist, &he, last ? H_FIRST : H_LAST);
|
||||
}
|
||||
}
|
||||
if (retval == -1)
|
||||
error("history number %s not found (internal error)",
|
||||
str);
|
||||
} else {
|
||||
/*
|
||||
* pattern
|
||||
*/
|
||||
retval = history(hist, &he, H_PREV_STR, str);
|
||||
if (retval == -1)
|
||||
error("history pattern not found: %s", str);
|
||||
}
|
||||
return (he.num);
|
||||
}
|
||||
|
||||
int
|
||||
bindcmd(int argc, char **argv)
|
||||
{
|
||||
|
||||
if (el == NULL)
|
||||
error("line editing is disabled");
|
||||
return (el_parse(el, argc, __DECONST(const char **, argv)));
|
||||
}
|
||||
|
||||
#else
|
||||
#include "error.h"
|
||||
|
||||
int
|
||||
histcmd(int argc, char **argv)
|
||||
{
|
||||
|
||||
error("not compiled with history support");
|
||||
/*NOTREACHED*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bindcmd(int argc, char **argv)
|
||||
{
|
||||
|
||||
error("not compiled with line editing support");
|
||||
return (0);
|
||||
}
|
||||
#endif
|
536
sh/input.c
Normal file
536
sh/input.c
Normal file
@ -0,0 +1,536 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/input.c 314436 2017-02-28 23:42:47Z imp $");
|
||||
|
||||
#include <stdio.h> /* defines BUFSIZ */
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* This file implements the input routines used by the parser.
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
#include "redir.h"
|
||||
#include "syntax.h"
|
||||
#include "input.h"
|
||||
#include "output.h"
|
||||
#include "options.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "alias.h"
|
||||
#include "parser.h"
|
||||
#include "myhistedit.h"
|
||||
#include "trap.h"
|
||||
|
||||
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
|
||||
|
||||
struct strpush {
|
||||
struct strpush *prev; /* preceding string on stack */
|
||||
const char *prevstring;
|
||||
int prevnleft;
|
||||
int prevlleft;
|
||||
struct alias *ap; /* if push was associated with an alias */
|
||||
};
|
||||
|
||||
/*
|
||||
* The parsefile structure pointed to by the global variable parsefile
|
||||
* contains information about the current file being read.
|
||||
*/
|
||||
|
||||
struct parsefile {
|
||||
struct parsefile *prev; /* preceding file on stack */
|
||||
int linno; /* current line */
|
||||
int fd; /* file descriptor (or -1 if string) */
|
||||
int nleft; /* number of chars left in this line */
|
||||
int lleft; /* number of lines left in this buffer */
|
||||
const char *nextc; /* next char in buffer */
|
||||
char *buf; /* input buffer */
|
||||
struct strpush *strpush; /* for pushing strings at this level */
|
||||
struct strpush basestrpush; /* so pushing one is fast */
|
||||
};
|
||||
|
||||
|
||||
int plinno = 1; /* input line number */
|
||||
int parsenleft; /* copy of parsefile->nleft */
|
||||
static int parselleft; /* copy of parsefile->lleft */
|
||||
const char *parsenextc; /* copy of parsefile->nextc */
|
||||
static char basebuf[BUFSIZ + 1];/* buffer for top level input file */
|
||||
static struct parsefile basepf = { /* top level input file */
|
||||
.nextc = basebuf,
|
||||
.buf = basebuf
|
||||
};
|
||||
static struct parsefile *parsefile = &basepf; /* current input file */
|
||||
int whichprompt; /* 1 == PS1, 2 == PS2 */
|
||||
|
||||
#ifndef __APPLE__
|
||||
EditLine *el; /* cookie for editline package */
|
||||
#endif /* !__APPLE__ */
|
||||
|
||||
static void pushfile(void);
|
||||
static int preadfd(void);
|
||||
static void popstring(void);
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Remove when 28988691 is fixed. Added _ to avoid conflict with future libc definition.
|
||||
static char *
|
||||
strchrnul_(const char *p, int ch)
|
||||
{
|
||||
char c;
|
||||
|
||||
c = ch;
|
||||
for (;; ++p) {
|
||||
if (*p == c || *p == '\0')
|
||||
return ((char *)p);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
void
|
||||
resetinput(void)
|
||||
{
|
||||
popallfiles();
|
||||
parselleft = parsenleft = 0; /* clear input buffer */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Read a character from the script, returning PEOF on end of file.
|
||||
* Nul characters in the input are silently discarded.
|
||||
*/
|
||||
|
||||
int
|
||||
pgetc(void)
|
||||
{
|
||||
return pgetc_macro();
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
preadfd(void)
|
||||
{
|
||||
int nr;
|
||||
parsenextc = parsefile->buf;
|
||||
|
||||
retry:
|
||||
#ifndef NO_HISTORY
|
||||
if (parsefile->fd == 0 && el) {
|
||||
static const char *rl_cp;
|
||||
static int el_len;
|
||||
|
||||
if (rl_cp == NULL) {
|
||||
el_resize(el);
|
||||
rl_cp = el_gets(el, &el_len);
|
||||
}
|
||||
if (rl_cp == NULL)
|
||||
nr = el_len == 0 ? 0 : -1;
|
||||
else {
|
||||
nr = el_len;
|
||||
if (nr > BUFSIZ)
|
||||
nr = BUFSIZ;
|
||||
memcpy(parsefile->buf, rl_cp, nr);
|
||||
if (nr != el_len) {
|
||||
el_len -= nr;
|
||||
rl_cp += nr;
|
||||
} else
|
||||
rl_cp = NULL;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
nr = read(parsefile->fd, parsefile->buf, BUFSIZ);
|
||||
|
||||
if (nr <= 0) {
|
||||
if (nr < 0) {
|
||||
if (errno == EINTR)
|
||||
goto retry;
|
||||
if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
|
||||
int flags = fcntl(0, F_GETFL, 0);
|
||||
if (flags >= 0 && flags & O_NONBLOCK) {
|
||||
flags &=~ O_NONBLOCK;
|
||||
if (fcntl(0, F_SETFL, flags) >= 0) {
|
||||
out2fmt_flush("sh: turning off NDELAY mode\n");
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nr = -1;
|
||||
}
|
||||
return nr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Refill the input buffer and return the next input character:
|
||||
*
|
||||
* 1) If a string was pushed back on the input, pop it;
|
||||
* 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
|
||||
* from a string so we can't refill the buffer, return EOF.
|
||||
* 3) If there is more in this buffer, use it else call read to fill it.
|
||||
* 4) Process input up to the next newline, deleting nul characters.
|
||||
*/
|
||||
|
||||
int
|
||||
preadbuffer(void)
|
||||
{
|
||||
char *p, *q, *r, *end;
|
||||
char savec;
|
||||
|
||||
while (parsefile->strpush) {
|
||||
/*
|
||||
* Add a space to the end of an alias to ensure that the
|
||||
* alias remains in use while parsing its last word.
|
||||
* This avoids alias recursions.
|
||||
*/
|
||||
if (parsenleft == -1 && parsefile->strpush->ap != NULL)
|
||||
return ' ';
|
||||
popstring();
|
||||
if (--parsenleft >= 0)
|
||||
return (*parsenextc++);
|
||||
}
|
||||
if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
|
||||
return PEOF;
|
||||
|
||||
again:
|
||||
if (parselleft <= 0) {
|
||||
if ((parselleft = preadfd()) == -1) {
|
||||
parselleft = parsenleft = EOF_NLEFT;
|
||||
return PEOF;
|
||||
}
|
||||
}
|
||||
|
||||
p = parsefile->buf + (parsenextc - parsefile->buf);
|
||||
end = p + parselleft;
|
||||
*end = '\0';
|
||||
q = strchrnul_(p, '\n');
|
||||
if (q != end && *q == '\0') {
|
||||
/* delete nul characters */
|
||||
for (r = q; q != end; q++) {
|
||||
if (*q != '\0')
|
||||
*r++ = *q;
|
||||
}
|
||||
parselleft -= end - r;
|
||||
if (parselleft == 0)
|
||||
goto again;
|
||||
end = p + parselleft;
|
||||
*end = '\0';
|
||||
q = strchrnul_(p, '\n');
|
||||
}
|
||||
if (q == end) {
|
||||
parsenleft = parselleft;
|
||||
parselleft = 0;
|
||||
} else /* *q == '\n' */ {
|
||||
q++;
|
||||
parsenleft = q - parsenextc;
|
||||
parselleft -= parsenleft;
|
||||
}
|
||||
parsenleft--;
|
||||
|
||||
savec = *q;
|
||||
*q = '\0';
|
||||
|
||||
#ifndef NO_HISTORY
|
||||
if (parsefile->fd == 0 && hist &&
|
||||
parsenextc[strspn(parsenextc, " \t\n")] != '\0') {
|
||||
HistEvent he;
|
||||
INTOFF;
|
||||
history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD,
|
||||
parsenextc);
|
||||
INTON;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vflag) {
|
||||
out2str(parsenextc);
|
||||
flushout(out2);
|
||||
}
|
||||
|
||||
*q = savec;
|
||||
|
||||
return *parsenextc++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns if we are certain we are at EOF. Does not cause any more input
|
||||
* to be read from the outside world.
|
||||
*/
|
||||
|
||||
int
|
||||
preadateof(void)
|
||||
{
|
||||
if (parsenleft > 0)
|
||||
return 0;
|
||||
if (parsefile->strpush)
|
||||
return 0;
|
||||
if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Undo the last call to pgetc. Only one character may be pushed back.
|
||||
* PEOF may be pushed back.
|
||||
*/
|
||||
|
||||
void
|
||||
pungetc(void)
|
||||
{
|
||||
parsenleft++;
|
||||
parsenextc--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push a string back onto the input at this current parsefile level.
|
||||
* We handle aliases this way.
|
||||
*/
|
||||
void
|
||||
pushstring(const char *s, int len, struct alias *ap)
|
||||
{
|
||||
struct strpush *sp;
|
||||
|
||||
INTOFF;
|
||||
/*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/
|
||||
if (parsefile->strpush) {
|
||||
sp = ckmalloc(sizeof (struct strpush));
|
||||
sp->prev = parsefile->strpush;
|
||||
parsefile->strpush = sp;
|
||||
} else
|
||||
sp = parsefile->strpush = &(parsefile->basestrpush);
|
||||
sp->prevstring = parsenextc;
|
||||
sp->prevnleft = parsenleft;
|
||||
sp->prevlleft = parselleft;
|
||||
sp->ap = ap;
|
||||
if (ap)
|
||||
ap->flag |= ALIASINUSE;
|
||||
parsenextc = s;
|
||||
parsenleft = len;
|
||||
INTON;
|
||||
}
|
||||
|
||||
static void
|
||||
popstring(void)
|
||||
{
|
||||
struct strpush *sp = parsefile->strpush;
|
||||
|
||||
INTOFF;
|
||||
if (sp->ap) {
|
||||
if (parsenextc != sp->ap->val &&
|
||||
(parsenextc[-1] == ' ' || parsenextc[-1] == '\t'))
|
||||
forcealias();
|
||||
sp->ap->flag &= ~ALIASINUSE;
|
||||
}
|
||||
parsenextc = sp->prevstring;
|
||||
parsenleft = sp->prevnleft;
|
||||
parselleft = sp->prevlleft;
|
||||
/*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/
|
||||
parsefile->strpush = sp->prev;
|
||||
if (sp != &(parsefile->basestrpush))
|
||||
ckfree(sp);
|
||||
INTON;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the input to take input from a file. If push is set, push the
|
||||
* old input onto the stack first.
|
||||
*/
|
||||
|
||||
void
|
||||
setinputfile(const char *fname, int push)
|
||||
{
|
||||
int fd;
|
||||
int fd2;
|
||||
|
||||
INTOFF;
|
||||
if ((fd = open(fname, O_RDONLY | O_CLOEXEC)) < 0)
|
||||
error("cannot open %s: %s", fname, strerror(errno));
|
||||
if (fd < 10) {
|
||||
fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 10);
|
||||
close(fd);
|
||||
if (fd2 < 0)
|
||||
error("Out of file descriptors");
|
||||
fd = fd2;
|
||||
}
|
||||
setinputfd(fd, push);
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Like setinputfile, but takes an open file descriptor (which should have
|
||||
* its FD_CLOEXEC flag already set). Call this with interrupts off.
|
||||
*/
|
||||
|
||||
void
|
||||
setinputfd(int fd, int push)
|
||||
{
|
||||
if (push) {
|
||||
pushfile();
|
||||
parsefile->buf = ckmalloc(BUFSIZ + 1);
|
||||
}
|
||||
if (parsefile->fd > 0)
|
||||
close(parsefile->fd);
|
||||
parsefile->fd = fd;
|
||||
if (parsefile->buf == NULL)
|
||||
parsefile->buf = ckmalloc(BUFSIZ + 1);
|
||||
parselleft = parsenleft = 0;
|
||||
plinno = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Like setinputfile, but takes input from a string.
|
||||
*/
|
||||
|
||||
void
|
||||
setinputstring(const char *string, int push)
|
||||
{
|
||||
INTOFF;
|
||||
if (push)
|
||||
pushfile();
|
||||
parsenextc = string;
|
||||
parselleft = parsenleft = strlen(string);
|
||||
parsefile->buf = NULL;
|
||||
plinno = 1;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* To handle the "." command, a stack of input files is used. Pushfile
|
||||
* adds a new entry to the stack and popfile restores the previous level.
|
||||
*/
|
||||
|
||||
static void
|
||||
pushfile(void)
|
||||
{
|
||||
struct parsefile *pf;
|
||||
|
||||
parsefile->nleft = parsenleft;
|
||||
parsefile->lleft = parselleft;
|
||||
parsefile->nextc = parsenextc;
|
||||
parsefile->linno = plinno;
|
||||
pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
|
||||
pf->prev = parsefile;
|
||||
pf->fd = -1;
|
||||
pf->strpush = NULL;
|
||||
pf->basestrpush.prev = NULL;
|
||||
parsefile = pf;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
popfile(void)
|
||||
{
|
||||
struct parsefile *pf = parsefile;
|
||||
|
||||
INTOFF;
|
||||
if (pf->fd >= 0)
|
||||
close(pf->fd);
|
||||
if (pf->buf)
|
||||
ckfree(pf->buf);
|
||||
while (pf->strpush)
|
||||
popstring();
|
||||
parsefile = pf->prev;
|
||||
ckfree(pf);
|
||||
parsenleft = parsefile->nleft;
|
||||
parselleft = parsefile->lleft;
|
||||
parsenextc = parsefile->nextc;
|
||||
plinno = parsefile->linno;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return current file (to go back to it later using popfilesupto()).
|
||||
*/
|
||||
|
||||
struct parsefile *
|
||||
getcurrentfile(void)
|
||||
{
|
||||
return parsefile;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pop files until the given file is on top again. Useful for regular
|
||||
* builtins that read shell commands from files or strings.
|
||||
* If the given file is not an active file, an error is raised.
|
||||
*/
|
||||
|
||||
void
|
||||
popfilesupto(struct parsefile *file)
|
||||
{
|
||||
while (parsefile != file && parsefile != &basepf)
|
||||
popfile();
|
||||
if (parsefile != file)
|
||||
error("popfilesupto() misused");
|
||||
}
|
||||
|
||||
/*
|
||||
* Return to top level.
|
||||
*/
|
||||
|
||||
void
|
||||
popallfiles(void)
|
||||
{
|
||||
while (parsefile != &basepf)
|
||||
popfile();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Close the file(s) that the shell is reading commands from. Called
|
||||
* after a fork is done.
|
||||
*/
|
||||
|
||||
void
|
||||
closescript(void)
|
||||
{
|
||||
popallfiles();
|
||||
if (parsefile->fd > 0) {
|
||||
close(parsefile->fd);
|
||||
parsefile->fd = 0;
|
||||
}
|
||||
}
|
65
sh/input.h
Normal file
65
sh/input.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)input.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/input.h 314436 2017-02-28 23:42:47Z imp $
|
||||
*/
|
||||
|
||||
/* PEOF (the end of file marker) is defined in syntax.h */
|
||||
|
||||
/*
|
||||
* The input line number. Input.c just defines this variable, and saves
|
||||
* and restores it when files are pushed and popped. The user of this
|
||||
* package must set its value.
|
||||
*/
|
||||
extern int plinno;
|
||||
extern int parsenleft; /* number of characters left in input buffer */
|
||||
extern const char *parsenextc; /* next character in input buffer */
|
||||
|
||||
struct alias;
|
||||
struct parsefile;
|
||||
|
||||
void resetinput(void);
|
||||
int pgetc(void);
|
||||
int preadbuffer(void);
|
||||
int preadateof(void);
|
||||
void pungetc(void);
|
||||
void pushstring(const char *, int, struct alias *);
|
||||
void setinputfile(const char *, int);
|
||||
void setinputfd(int, int);
|
||||
void setinputstring(const char *, int);
|
||||
void popfile(void);
|
||||
struct parsefile *getcurrentfile(void);
|
||||
void popfilesupto(struct parsefile *);
|
||||
void popallfiles(void);
|
||||
void closescript(void);
|
||||
|
||||
#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
|
67
sh/jobs.h
Normal file
67
sh/jobs.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)jobs.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/jobs.h 327475 2018-01-01 22:31:52Z jilles $
|
||||
*/
|
||||
|
||||
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
|
||||
#define FORK_FG 0
|
||||
#define FORK_BG 1
|
||||
#define FORK_NOJOB 2
|
||||
|
||||
#include <signal.h> /* for sig_atomic_t */
|
||||
|
||||
struct job;
|
||||
|
||||
enum {
|
||||
SHOWJOBS_DEFAULT, /* job number, status, command */
|
||||
SHOWJOBS_VERBOSE, /* job number, PID, status, command */
|
||||
SHOWJOBS_PIDS, /* PID only */
|
||||
SHOWJOBS_PGIDS /* PID of the group leader only */
|
||||
};
|
||||
|
||||
extern int job_warning; /* user was warned about stopped jobs */
|
||||
|
||||
void setjobctl(int);
|
||||
void showjobs(int, int);
|
||||
struct job *makejob(union node *, int);
|
||||
pid_t forkshell(struct job *, union node *, int);
|
||||
pid_t vforkexecshell(struct job *, char **, char **, const char *, int, int []);
|
||||
int waitforjob(struct job *, int *);
|
||||
int stoppedjobs(void);
|
||||
int backgndpidset(void);
|
||||
pid_t backgndpidval(void);
|
||||
char *commandtext(union node *);
|
||||
|
||||
#if ! JOBS
|
||||
#define setjobctl(on) /* do nothing */
|
||||
#endif
|
117
sh/mail.c
Normal file
117
sh/mail.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)mail.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/mail.c 314436 2017-02-28 23:42:47Z imp $");
|
||||
|
||||
/*
|
||||
* Routines to check for mail. (Perhaps make part of main.c?)
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
#include "exec.h" /* defines padvance() */
|
||||
#include "mail.h"
|
||||
#include "var.h"
|
||||
#include "output.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define MAXMBOXES 10
|
||||
|
||||
|
||||
static int nmboxes; /* number of mailboxes */
|
||||
static time_t mailtime[MAXMBOXES]; /* times of mailboxes */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Print appropriate message(s) if mail has arrived. If the argument is
|
||||
* nozero, then the value of MAIL has changed, so we just update the
|
||||
* values.
|
||||
*/
|
||||
|
||||
void
|
||||
chkmail(int silent)
|
||||
{
|
||||
int i;
|
||||
const char *mpath;
|
||||
char *p;
|
||||
char *q;
|
||||
struct stackmark smark;
|
||||
struct stat statb;
|
||||
|
||||
if (silent)
|
||||
nmboxes = 10;
|
||||
if (nmboxes == 0)
|
||||
return;
|
||||
setstackmark(&smark);
|
||||
mpath = mpathset()? mpathval() : mailval();
|
||||
for (i = 0 ; i < nmboxes ; i++) {
|
||||
p = padvance(&mpath, "");
|
||||
if (p == NULL)
|
||||
break;
|
||||
if (*p == '\0')
|
||||
continue;
|
||||
for (q = p ; *q ; q++);
|
||||
if (q[-1] != '/')
|
||||
abort();
|
||||
q[-1] = '\0'; /* delete trailing '/' */
|
||||
#ifdef notdef /* this is what the System V shell claims to do (it lies) */
|
||||
if (stat(p, &statb) < 0)
|
||||
statb.st_mtime = 0;
|
||||
if (statb.st_mtime > mailtime[i] && ! silent) {
|
||||
out2str(pathopt? pathopt : "you have mail");
|
||||
out2c('\n');
|
||||
}
|
||||
mailtime[i] = statb.st_mtime;
|
||||
#else /* this is what it should do */
|
||||
if (stat(p, &statb) < 0)
|
||||
statb.st_size = 0;
|
||||
if (statb.st_size > mailtime[i] && ! silent) {
|
||||
out2str(pathopt? pathopt : "you have mail");
|
||||
out2c('\n');
|
||||
}
|
||||
mailtime[i] = statb.st_size;
|
||||
#endif
|
||||
}
|
||||
nmboxes = i;
|
||||
popstackmark(&smark);
|
||||
}
|
38
sh/mail.h
Normal file
38
sh/mail.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)mail.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/mail.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
void chkmail(int);
|
351
sh/main.c
Normal file
351
sh/main.c
Normal file
@ -0,0 +1,351 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char const copyright[] =
|
||||
"@(#) Copyright (c) 1991, 1993\n\
|
||||
The Regents of the University of California. All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/28/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/main.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <locale.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "main.h"
|
||||
#include "mail.h"
|
||||
#include "options.h"
|
||||
#include "output.h"
|
||||
#include "parser.h"
|
||||
#include "nodes.h"
|
||||
#include "expand.h"
|
||||
#include "eval.h"
|
||||
#include "jobs.h"
|
||||
#include "input.h"
|
||||
#include "trap.h"
|
||||
#include "var.h"
|
||||
#include "show.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "mystring.h"
|
||||
#include "exec.h"
|
||||
#include "cd.h"
|
||||
#include "redir.h"
|
||||
#include "builtins.h"
|
||||
|
||||
int rootpid;
|
||||
int rootshell;
|
||||
struct jmploc main_handler;
|
||||
int localeisutf8, initial_localeisutf8;
|
||||
|
||||
static void reset(void);
|
||||
static void cmdloop(int);
|
||||
static void read_profile(const char *);
|
||||
static char *find_dot_file(char *);
|
||||
|
||||
/*
|
||||
* Main routine. We initialize things, parse the arguments, execute
|
||||
* profiles if we're a login shell, and then call cmdloop to execute
|
||||
* commands. The setjmp call sets up the location to jump to when an
|
||||
* exception occurs. When an exception occurs the variable "state"
|
||||
* is used to figure out how far we had gotten.
|
||||
*/
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct stackmark smark, smark2;
|
||||
volatile int state;
|
||||
char *shinit;
|
||||
|
||||
(void) setlocale(LC_ALL, "");
|
||||
initcharset();
|
||||
state = 0;
|
||||
if (setjmp(main_handler.loc)) {
|
||||
switch (exception) {
|
||||
case EXEXEC:
|
||||
exitstatus = exerrno;
|
||||
break;
|
||||
|
||||
case EXERROR:
|
||||
exitstatus = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == 0 || iflag == 0 || ! rootshell ||
|
||||
exception == EXEXIT)
|
||||
exitshell(exitstatus);
|
||||
reset();
|
||||
if (exception == EXINT)
|
||||
out2fmt_flush("\n");
|
||||
popstackmark(&smark);
|
||||
FORCEINTON; /* enable interrupts */
|
||||
if (state == 1)
|
||||
goto state1;
|
||||
else if (state == 2)
|
||||
goto state2;
|
||||
else if (state == 3)
|
||||
goto state3;
|
||||
else
|
||||
goto state4;
|
||||
}
|
||||
handler = &main_handler;
|
||||
#ifdef DEBUG
|
||||
opentrace();
|
||||
trputs("Shell args: "); trargs(argv);
|
||||
#endif
|
||||
rootpid = getpid();
|
||||
rootshell = 1;
|
||||
INTOFF;
|
||||
initvar();
|
||||
setstackmark(&smark);
|
||||
setstackmark(&smark2);
|
||||
procargs(argc, argv);
|
||||
pwd_init(iflag);
|
||||
INTON;
|
||||
if (iflag)
|
||||
chkmail(1);
|
||||
if (argv[0] && argv[0][0] == '-') {
|
||||
state = 1;
|
||||
read_profile("/etc/profile");
|
||||
state1:
|
||||
state = 2;
|
||||
if (privileged == 0)
|
||||
read_profile("${HOME-}/.profile");
|
||||
else
|
||||
read_profile("/etc/suid_profile");
|
||||
}
|
||||
state2:
|
||||
state = 3;
|
||||
if (!privileged && iflag) {
|
||||
if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
|
||||
state = 3;
|
||||
read_profile(shinit);
|
||||
}
|
||||
}
|
||||
state3:
|
||||
state = 4;
|
||||
popstackmark(&smark2);
|
||||
if (minusc) {
|
||||
evalstring(minusc, sflag ? 0 : EV_EXIT);
|
||||
}
|
||||
state4:
|
||||
if (sflag || minusc == NULL) {
|
||||
cmdloop(1);
|
||||
}
|
||||
exitshell(exitstatus);
|
||||
/*NOTREACHED*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
reset(void)
|
||||
{
|
||||
reseteval();
|
||||
resetinput();
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and execute commands. "Top" is nonzero for the top level command
|
||||
* loop; it turns on prompting if the shell is interactive.
|
||||
*/
|
||||
|
||||
static void
|
||||
cmdloop(int top)
|
||||
{
|
||||
union node *n;
|
||||
struct stackmark smark;
|
||||
int inter;
|
||||
int numeof = 0;
|
||||
|
||||
TRACE(("cmdloop(%d) called\n", top));
|
||||
setstackmark(&smark);
|
||||
for (;;) {
|
||||
if (pendingsig)
|
||||
dotrap();
|
||||
inter = 0;
|
||||
if (iflag && top) {
|
||||
inter++;
|
||||
showjobs(1, SHOWJOBS_DEFAULT);
|
||||
chkmail(0);
|
||||
flushout(&output);
|
||||
}
|
||||
n = parsecmd(inter);
|
||||
/* showtree(n); DEBUG */
|
||||
if (n == NEOF) {
|
||||
if (!top || numeof >= 50)
|
||||
break;
|
||||
if (!stoppedjobs()) {
|
||||
if (!Iflag)
|
||||
break;
|
||||
out2fmt_flush("\nUse \"exit\" to leave shell.\n");
|
||||
}
|
||||
numeof++;
|
||||
} else if (n != NULL && nflag == 0) {
|
||||
job_warning = (job_warning == 2) ? 1 : 0;
|
||||
numeof = 0;
|
||||
evaltree(n, 0);
|
||||
}
|
||||
popstackmark(&smark);
|
||||
setstackmark(&smark);
|
||||
if (evalskip != 0) {
|
||||
if (evalskip == SKIPRETURN)
|
||||
evalskip = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
popstackmark(&smark);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Read /etc/profile or .profile. Return on error.
|
||||
*/
|
||||
|
||||
static void
|
||||
read_profile(const char *name)
|
||||
{
|
||||
int fd;
|
||||
const char *expandedname;
|
||||
|
||||
expandedname = expandstr(name);
|
||||
if (expandedname == NULL)
|
||||
return;
|
||||
INTOFF;
|
||||
if ((fd = open(expandedname, O_RDONLY | O_CLOEXEC)) >= 0)
|
||||
setinputfd(fd, 1);
|
||||
INTON;
|
||||
if (fd < 0)
|
||||
return;
|
||||
cmdloop(0);
|
||||
popfile();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Read a file containing shell functions.
|
||||
*/
|
||||
|
||||
void
|
||||
readcmdfile(const char *name)
|
||||
{
|
||||
setinputfile(name, 1);
|
||||
cmdloop(0);
|
||||
popfile();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Take commands from a file. To be compatible we should do a path
|
||||
* search for the file, which is necessary to find sub-commands.
|
||||
*/
|
||||
|
||||
|
||||
static char *
|
||||
find_dot_file(char *basename)
|
||||
{
|
||||
char *fullname;
|
||||
const char *path = pathval();
|
||||
struct stat statb;
|
||||
|
||||
/* don't try this for absolute or relative paths */
|
||||
if( strchr(basename, '/'))
|
||||
return basename;
|
||||
|
||||
while ((fullname = padvance(&path, basename)) != NULL) {
|
||||
if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
|
||||
/*
|
||||
* Don't bother freeing here, since it will
|
||||
* be freed by the caller.
|
||||
*/
|
||||
return fullname;
|
||||
}
|
||||
stunalloc(fullname);
|
||||
}
|
||||
return basename;
|
||||
}
|
||||
|
||||
int
|
||||
dotcmd(int argc, char **argv)
|
||||
{
|
||||
char *filename, *fullname;
|
||||
|
||||
if (argc < 2)
|
||||
error("missing filename");
|
||||
|
||||
exitstatus = 0;
|
||||
|
||||
/*
|
||||
* Because we have historically not supported any options,
|
||||
* only treat "--" specially.
|
||||
*/
|
||||
filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1];
|
||||
|
||||
fullname = find_dot_file(filename);
|
||||
setinputfile(fullname, 1);
|
||||
commandname = fullname;
|
||||
cmdloop(0);
|
||||
popfile();
|
||||
return exitstatus;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
exitcmd(int argc, char **argv)
|
||||
{
|
||||
if (stoppedjobs())
|
||||
return 0;
|
||||
if (argc > 1)
|
||||
exitshell(number(argv[1]));
|
||||
else
|
||||
exitshell_savedstatus();
|
||||
}
|
42
sh/main.h
Normal file
42
sh/main.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)main.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/main.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
extern int rootpid; /* pid of main shell */
|
||||
extern int rootshell; /* true if we aren't a child of the main shell */
|
||||
extern struct jmploc main_handler; /* top level exception handler */
|
||||
|
||||
void readcmdfile(const char *);
|
344
sh/memalloc.c
Normal file
344
sh/memalloc.c
Normal file
@ -0,0 +1,344 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/memalloc.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include "shell.h"
|
||||
#include "output.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "mystring.h"
|
||||
#include "expand.h"
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Like malloc, but returns an error when out of space.
|
||||
*/
|
||||
|
||||
pointer
|
||||
ckmalloc(size_t nbytes)
|
||||
{
|
||||
pointer p;
|
||||
|
||||
INTOFF;
|
||||
p = malloc(nbytes);
|
||||
INTON;
|
||||
if (p == NULL)
|
||||
error("Out of space");
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Same for realloc.
|
||||
*/
|
||||
|
||||
pointer
|
||||
ckrealloc(pointer p, int nbytes)
|
||||
{
|
||||
INTOFF;
|
||||
p = realloc(p, nbytes);
|
||||
INTON;
|
||||
if (p == NULL)
|
||||
error("Out of space");
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
ckfree(pointer p)
|
||||
{
|
||||
INTOFF;
|
||||
free(p);
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Make a copy of a string in safe storage.
|
||||
*/
|
||||
|
||||
char *
|
||||
savestr(const char *s)
|
||||
{
|
||||
char *p;
|
||||
size_t len;
|
||||
|
||||
len = strlen(s);
|
||||
p = ckmalloc(len + 1);
|
||||
memcpy(p, s, len + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse trees for commands are allocated in lifo order, so we use a stack
|
||||
* to make this more efficient, and also to avoid all sorts of exception
|
||||
* handling code to handle interrupts in the middle of a parse.
|
||||
*
|
||||
* The size 496 was chosen because with 16-byte alignment the total size
|
||||
* for the allocated block is 512.
|
||||
*/
|
||||
|
||||
#define MINSIZE 496 /* minimum size of a block. */
|
||||
|
||||
|
||||
struct stack_block {
|
||||
struct stack_block *prev;
|
||||
/* Data follows */
|
||||
};
|
||||
#define SPACE(sp) ((char*)(sp) + ALIGN(sizeof(struct stack_block)))
|
||||
|
||||
static struct stack_block *stackp;
|
||||
char *stacknxt;
|
||||
int stacknleft;
|
||||
char *sstrend;
|
||||
|
||||
|
||||
static void
|
||||
stnewblock(int nbytes)
|
||||
{
|
||||
struct stack_block *sp;
|
||||
int allocsize;
|
||||
|
||||
if (nbytes < MINSIZE)
|
||||
nbytes = MINSIZE;
|
||||
|
||||
allocsize = ALIGN(sizeof(struct stack_block)) + ALIGN(nbytes);
|
||||
|
||||
INTOFF;
|
||||
sp = ckmalloc(allocsize);
|
||||
sp->prev = stackp;
|
||||
stacknxt = SPACE(sp);
|
||||
stacknleft = allocsize - (stacknxt - (char*)sp);
|
||||
sstrend = stacknxt + stacknleft;
|
||||
stackp = sp;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
pointer
|
||||
stalloc(int nbytes)
|
||||
{
|
||||
char *p;
|
||||
|
||||
nbytes = ALIGN(nbytes);
|
||||
if (nbytes > stacknleft)
|
||||
stnewblock(nbytes);
|
||||
p = stacknxt;
|
||||
stacknxt += nbytes;
|
||||
stacknleft -= nbytes;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
stunalloc(pointer p)
|
||||
{
|
||||
if (p == NULL) { /*DEBUG */
|
||||
write(STDERR_FILENO, "stunalloc\n", 10);
|
||||
abort();
|
||||
}
|
||||
stacknleft += stacknxt - (char *)p;
|
||||
stacknxt = p;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
stsavestr(const char *s)
|
||||
{
|
||||
char *p;
|
||||
size_t len;
|
||||
|
||||
len = strlen(s);
|
||||
p = stalloc(len + 1);
|
||||
memcpy(p, s, len + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
setstackmark(struct stackmark *mark)
|
||||
{
|
||||
mark->stackp = stackp;
|
||||
mark->stacknxt = stacknxt;
|
||||
mark->stacknleft = stacknleft;
|
||||
/* Ensure this block stays in place. */
|
||||
if (stackp != NULL && stacknxt == SPACE(stackp))
|
||||
stalloc(1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
popstackmark(struct stackmark *mark)
|
||||
{
|
||||
struct stack_block *sp;
|
||||
|
||||
INTOFF;
|
||||
while (stackp != mark->stackp) {
|
||||
sp = stackp;
|
||||
stackp = sp->prev;
|
||||
ckfree(sp);
|
||||
}
|
||||
stacknxt = mark->stacknxt;
|
||||
stacknleft = mark->stacknleft;
|
||||
sstrend = stacknxt + stacknleft;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When the parser reads in a string, it wants to stick the string on the
|
||||
* stack and only adjust the stack pointer when it knows how big the
|
||||
* string is. Stackblock (defined in stack.h) returns a pointer to a block
|
||||
* of space on top of the stack and stackblocklen returns the length of
|
||||
* this block. Growstackblock will grow this space by at least one byte,
|
||||
* possibly moving it (like realloc). Grabstackblock actually allocates the
|
||||
* part of the block that has been used.
|
||||
*/
|
||||
|
||||
static void
|
||||
growstackblock(int min)
|
||||
{
|
||||
char *p;
|
||||
int newlen;
|
||||
char *oldspace;
|
||||
int oldlen;
|
||||
struct stack_block *sp;
|
||||
struct stack_block *oldstackp;
|
||||
|
||||
if (min < stacknleft)
|
||||
min = stacknleft;
|
||||
if ((unsigned int)min >=
|
||||
INT_MAX / 2 - ALIGN(sizeof(struct stack_block)))
|
||||
error("Out of space");
|
||||
min += stacknleft;
|
||||
min += ALIGN(sizeof(struct stack_block));
|
||||
newlen = 512;
|
||||
while (newlen < min)
|
||||
newlen <<= 1;
|
||||
oldspace = stacknxt;
|
||||
oldlen = stacknleft;
|
||||
|
||||
if (stackp != NULL && stacknxt == SPACE(stackp)) {
|
||||
INTOFF;
|
||||
oldstackp = stackp;
|
||||
stackp = oldstackp->prev;
|
||||
sp = ckrealloc((pointer)oldstackp, newlen);
|
||||
sp->prev = stackp;
|
||||
stackp = sp;
|
||||
stacknxt = SPACE(sp);
|
||||
stacknleft = newlen - (stacknxt - (char*)sp);
|
||||
sstrend = stacknxt + stacknleft;
|
||||
INTON;
|
||||
} else {
|
||||
newlen -= ALIGN(sizeof(struct stack_block));
|
||||
p = stalloc(newlen);
|
||||
if (oldlen != 0)
|
||||
memcpy(p, oldspace, oldlen);
|
||||
stunalloc(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The following routines are somewhat easier to use that the above.
|
||||
* The user declares a variable of type STACKSTR, which may be declared
|
||||
* to be a register. The macro STARTSTACKSTR initializes things. Then
|
||||
* the user uses the macro STPUTC to add characters to the string. In
|
||||
* effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
|
||||
* grown as necessary. When the user is done, she can just leave the
|
||||
* string there and refer to it using stackblock(). Or she can allocate
|
||||
* the space for it using grabstackstr(). If it is necessary to allow
|
||||
* someone else to use the stack temporarily and then continue to grow
|
||||
* the string, the user should use grabstack to allocate the space, and
|
||||
* then call ungrabstr(p) to return to the previous mode of operation.
|
||||
*
|
||||
* USTPUTC is like STPUTC except that it doesn't check for overflow.
|
||||
* CHECKSTACKSPACE can be called before USTPUTC to ensure that there
|
||||
* is space for at least one character.
|
||||
*/
|
||||
|
||||
static char *
|
||||
growstrstackblock(int n, int min)
|
||||
{
|
||||
growstackblock(min);
|
||||
return stackblock() + n;
|
||||
}
|
||||
|
||||
char *
|
||||
growstackstr(void)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = stackblocksize();
|
||||
return (growstrstackblock(len, 0));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called from CHECKSTRSPACE.
|
||||
*/
|
||||
|
||||
char *
|
||||
makestrspace(int min, char *p)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = p - stackblock();
|
||||
return (growstrstackblock(len, min));
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
stputbin(const char *data, size_t len, char *p)
|
||||
{
|
||||
CHECKSTRSPACE(len, p);
|
||||
memcpy(p, data, len);
|
||||
return (p + len);
|
||||
}
|
||||
|
||||
char *
|
||||
stputs(const char *data, char *p)
|
||||
{
|
||||
return (stputbin(data, strlen(data), p));
|
||||
}
|
88
sh/memalloc.h
Normal file
88
sh/memalloc.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)memalloc.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/memalloc.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct stackmark {
|
||||
struct stack_block *stackp;
|
||||
char *stacknxt;
|
||||
int stacknleft;
|
||||
};
|
||||
|
||||
|
||||
extern char *stacknxt;
|
||||
extern int stacknleft;
|
||||
extern char *sstrend;
|
||||
|
||||
pointer ckmalloc(size_t);
|
||||
pointer ckrealloc(pointer, int);
|
||||
void ckfree(pointer);
|
||||
char *savestr(const char *);
|
||||
pointer stalloc(int);
|
||||
void stunalloc(pointer);
|
||||
char *stsavestr(const char *);
|
||||
void setstackmark(struct stackmark *);
|
||||
void popstackmark(struct stackmark *);
|
||||
char *growstackstr(void);
|
||||
char *makestrspace(int, char *);
|
||||
char *stputbin(const char *data, size_t len, char *p);
|
||||
char *stputs(const char *data, char *p);
|
||||
|
||||
|
||||
|
||||
#define stackblock() stacknxt
|
||||
#define stackblocksize() stacknleft
|
||||
#define grabstackblock(n) stalloc(n)
|
||||
#define STARTSTACKSTR(p) p = stackblock()
|
||||
#define STPUTC(c, p) do { if (p == sstrend) p = growstackstr(); *p++ = (c); } while(0)
|
||||
#define CHECKSTRSPACE(n, p) { if ((size_t)(sstrend - p) < n) p = makestrspace(n, p); }
|
||||
#define USTPUTC(c, p) (*p++ = (c))
|
||||
/*
|
||||
* STACKSTRNUL's use is where we want to be able to turn a stack
|
||||
* (non-sentinel, character counting string) into a C string,
|
||||
* and later pretend the NUL is not there.
|
||||
* Note: Because of STACKSTRNUL's semantics, STACKSTRNUL cannot be used
|
||||
* on a stack that will grabstackstr()ed.
|
||||
*/
|
||||
#define STACKSTRNUL(p) (p == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0'))
|
||||
#define STUNPUTC(p) (--p)
|
||||
#define STTOPC(p) p[-1]
|
||||
#define STADJUST(amount, p) (p += (amount))
|
||||
#define grabstackstr(p) stalloc((char *)p - stackblock())
|
||||
#define ungrabstackstr(s, p) stunalloc((s))
|
||||
#define STPUTBIN(s, len, p) p = stputbin((s), (len), p)
|
||||
#define STPUTS(s, p) p = stputs((s), p)
|
534
sh/miscbltin.c
Normal file
534
sh/miscbltin.c
Normal file
@ -0,0 +1,534 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/miscbltin.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
/*
|
||||
* Miscellaneous builtins.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "options.h"
|
||||
#include "var.h"
|
||||
#include "output.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "mystring.h"
|
||||
#include "syntax.h"
|
||||
#include "trap.h"
|
||||
|
||||
#undef eflag
|
||||
|
||||
int readcmd(int, char **);
|
||||
int umaskcmd(int, char **);
|
||||
int ulimitcmd(int, char **);
|
||||
|
||||
/*
|
||||
* The read builtin. The -r option causes backslashes to be treated like
|
||||
* ordinary characters.
|
||||
*
|
||||
* This uses unbuffered input, which may be avoidable in some cases.
|
||||
*
|
||||
* Note that if IFS=' :' then read x y should work so that:
|
||||
* 'a b' x='a', y='b'
|
||||
* ' a b ' x='a', y='b'
|
||||
* ':b' x='', y='b'
|
||||
* ':' x='', y=''
|
||||
* '::' x='', y=''
|
||||
* ': :' x='', y=''
|
||||
* ':::' x='', y='::'
|
||||
* ':b c:' x='', y='b c:'
|
||||
*/
|
||||
|
||||
int
|
||||
readcmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
char **ap;
|
||||
int backslash;
|
||||
char c;
|
||||
int rflag;
|
||||
char *prompt;
|
||||
const char *ifs;
|
||||
char *p;
|
||||
int startword;
|
||||
int status;
|
||||
int i;
|
||||
int is_ifs;
|
||||
int saveall = 0;
|
||||
ptrdiff_t lastnonifs, lastnonifsws;
|
||||
struct timeval tv;
|
||||
char *tvptr;
|
||||
fd_set ifds;
|
||||
ssize_t nread;
|
||||
int sig;
|
||||
|
||||
rflag = 0;
|
||||
prompt = NULL;
|
||||
tv.tv_sec = -1;
|
||||
tv.tv_usec = 0;
|
||||
while ((i = nextopt("erp:t:")) != '\0') {
|
||||
switch(i) {
|
||||
case 'p':
|
||||
prompt = shoptarg;
|
||||
break;
|
||||
case 'e':
|
||||
break;
|
||||
case 'r':
|
||||
rflag = 1;
|
||||
break;
|
||||
case 't':
|
||||
tv.tv_sec = strtol(shoptarg, &tvptr, 0);
|
||||
if (tvptr == shoptarg)
|
||||
error("timeout value");
|
||||
switch(*tvptr) {
|
||||
case 0:
|
||||
case 's':
|
||||
break;
|
||||
case 'h':
|
||||
tv.tv_sec *= 60;
|
||||
/* FALLTHROUGH */
|
||||
case 'm':
|
||||
tv.tv_sec *= 60;
|
||||
break;
|
||||
default:
|
||||
error("timeout unit");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prompt && isatty(0)) {
|
||||
out2str(prompt);
|
||||
flushall();
|
||||
}
|
||||
if (*(ap = argptr) == NULL)
|
||||
error("arg count");
|
||||
if ((ifs = bltinlookup("IFS", 1)) == NULL)
|
||||
ifs = " \t\n";
|
||||
|
||||
if (tv.tv_sec >= 0) {
|
||||
/*
|
||||
* Wait for something to become available.
|
||||
*/
|
||||
FD_ZERO(&ifds);
|
||||
FD_SET(0, &ifds);
|
||||
status = select(1, &ifds, NULL, NULL, &tv);
|
||||
/*
|
||||
* If there's nothing ready, return an error.
|
||||
*/
|
||||
if (status <= 0) {
|
||||
sig = pendingsig;
|
||||
return (128 + (sig != 0 ? sig : SIGALRM));
|
||||
}
|
||||
}
|
||||
|
||||
status = 0;
|
||||
startword = 2;
|
||||
backslash = 0;
|
||||
STARTSTACKSTR(p);
|
||||
lastnonifs = lastnonifsws = -1;
|
||||
for (;;) {
|
||||
nread = read(STDIN_FILENO, &c, 1);
|
||||
if (nread == -1) {
|
||||
if (errno == EINTR) {
|
||||
sig = pendingsig;
|
||||
if (sig == 0)
|
||||
continue;
|
||||
status = 128 + sig;
|
||||
break;
|
||||
}
|
||||
warning("read error: %s", strerror(errno));
|
||||
status = 2;
|
||||
break;
|
||||
} else if (nread != 1) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
if (c == '\0')
|
||||
continue;
|
||||
CHECKSTRSPACE(1, p);
|
||||
if (backslash) {
|
||||
backslash = 0;
|
||||
if (c != '\n') {
|
||||
startword = 0;
|
||||
lastnonifs = lastnonifsws = p - stackblock();
|
||||
USTPUTC(c, p);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!rflag && c == '\\') {
|
||||
backslash++;
|
||||
continue;
|
||||
}
|
||||
if (c == '\n')
|
||||
break;
|
||||
if (strchr(ifs, c))
|
||||
is_ifs = strchr(" \t\n", c) ? 1 : 2;
|
||||
else
|
||||
is_ifs = 0;
|
||||
|
||||
if (startword != 0) {
|
||||
if (is_ifs == 1) {
|
||||
/* Ignore leading IFS whitespace */
|
||||
if (saveall)
|
||||
USTPUTC(c, p);
|
||||
continue;
|
||||
}
|
||||
if (is_ifs == 2 && startword == 1) {
|
||||
/* Only one non-whitespace IFS per word */
|
||||
startword = 2;
|
||||
if (saveall) {
|
||||
lastnonifsws = p - stackblock();
|
||||
USTPUTC(c, p);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_ifs == 0) {
|
||||
/* append this character to the current variable */
|
||||
startword = 0;
|
||||
if (saveall)
|
||||
/* Not just a spare terminator */
|
||||
saveall++;
|
||||
lastnonifs = lastnonifsws = p - stackblock();
|
||||
USTPUTC(c, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* end of variable... */
|
||||
startword = is_ifs;
|
||||
|
||||
if (ap[1] == NULL) {
|
||||
/* Last variable needs all IFS chars */
|
||||
saveall++;
|
||||
if (is_ifs == 2)
|
||||
lastnonifsws = p - stackblock();
|
||||
USTPUTC(c, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
STACKSTRNUL(p);
|
||||
setvar(*ap, stackblock(), 0);
|
||||
ap++;
|
||||
STARTSTACKSTR(p);
|
||||
lastnonifs = lastnonifsws = -1;
|
||||
}
|
||||
STACKSTRNUL(p);
|
||||
|
||||
/*
|
||||
* Remove trailing IFS chars: always remove whitespace, don't remove
|
||||
* non-whitespace unless it was naked
|
||||
*/
|
||||
if (saveall <= 1)
|
||||
lastnonifsws = lastnonifs;
|
||||
stackblock()[lastnonifsws + 1] = '\0';
|
||||
setvar(*ap, stackblock(), 0);
|
||||
|
||||
/* Set any remaining args to "" */
|
||||
while (*++ap != NULL)
|
||||
setvar(*ap, "", 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
umaskcmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
char *ap;
|
||||
int mask;
|
||||
int i;
|
||||
int symbolic_mode = 0;
|
||||
|
||||
while ((i = nextopt("S")) != '\0') {
|
||||
symbolic_mode = 1;
|
||||
}
|
||||
|
||||
INTOFF;
|
||||
mask = umask(0);
|
||||
umask(mask);
|
||||
INTON;
|
||||
|
||||
if ((ap = *argptr) == NULL) {
|
||||
if (symbolic_mode) {
|
||||
char u[4], g[4], o[4];
|
||||
|
||||
i = 0;
|
||||
if ((mask & S_IRUSR) == 0)
|
||||
u[i++] = 'r';
|
||||
if ((mask & S_IWUSR) == 0)
|
||||
u[i++] = 'w';
|
||||
if ((mask & S_IXUSR) == 0)
|
||||
u[i++] = 'x';
|
||||
u[i] = '\0';
|
||||
|
||||
i = 0;
|
||||
if ((mask & S_IRGRP) == 0)
|
||||
g[i++] = 'r';
|
||||
if ((mask & S_IWGRP) == 0)
|
||||
g[i++] = 'w';
|
||||
if ((mask & S_IXGRP) == 0)
|
||||
g[i++] = 'x';
|
||||
g[i] = '\0';
|
||||
|
||||
i = 0;
|
||||
if ((mask & S_IROTH) == 0)
|
||||
o[i++] = 'r';
|
||||
if ((mask & S_IWOTH) == 0)
|
||||
o[i++] = 'w';
|
||||
if ((mask & S_IXOTH) == 0)
|
||||
o[i++] = 'x';
|
||||
o[i] = '\0';
|
||||
|
||||
out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
|
||||
} else {
|
||||
out1fmt("%.4o\n", mask);
|
||||
}
|
||||
} else {
|
||||
if (is_digit(*ap)) {
|
||||
mask = 0;
|
||||
do {
|
||||
if (*ap >= '8' || *ap < '0')
|
||||
error("Illegal number: %s", *argptr);
|
||||
mask = (mask << 3) + (*ap - '0');
|
||||
} while (*++ap != '\0');
|
||||
umask(mask);
|
||||
} else {
|
||||
void *set;
|
||||
INTOFF;
|
||||
if ((set = setmode (ap)) == NULL)
|
||||
error("Illegal number: %s", ap);
|
||||
|
||||
mask = getmode (set, ~mask & 0777);
|
||||
umask(~mask & 0777);
|
||||
free(set);
|
||||
INTON;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ulimit builtin
|
||||
*
|
||||
* This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
|
||||
* Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
|
||||
* ash by J.T. Conklin.
|
||||
*
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
struct limits {
|
||||
const char *name;
|
||||
const char *units;
|
||||
int cmd;
|
||||
short factor; /* multiply by to get rlim_{cur,max} values */
|
||||
char option;
|
||||
};
|
||||
|
||||
static const struct limits limits[] = {
|
||||
#ifdef RLIMIT_CPU
|
||||
{ "cpu time", "seconds", RLIMIT_CPU, 1, 't' },
|
||||
#endif
|
||||
#ifdef RLIMIT_FSIZE
|
||||
{ "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' },
|
||||
#endif
|
||||
#ifdef RLIMIT_DATA
|
||||
{ "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' },
|
||||
#endif
|
||||
#ifdef RLIMIT_STACK
|
||||
{ "stack size", "kbytes", RLIMIT_STACK, 1024, 's' },
|
||||
#endif
|
||||
#ifdef RLIMIT_CORE
|
||||
{ "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' },
|
||||
#endif
|
||||
#ifdef RLIMIT_RSS
|
||||
{ "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' },
|
||||
#endif
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
{ "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' },
|
||||
#endif
|
||||
#ifdef RLIMIT_NPROC
|
||||
{ "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' },
|
||||
#endif
|
||||
#ifdef RLIMIT_NOFILE
|
||||
{ "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' },
|
||||
#endif
|
||||
#ifdef RLIMIT_VMEM
|
||||
{ "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' },
|
||||
#endif
|
||||
#ifdef RLIMIT_SWAP
|
||||
{ "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' },
|
||||
#endif
|
||||
#ifdef RLIMIT_SBSIZE
|
||||
{ "socket buffer size", "bytes", RLIMIT_SBSIZE, 1, 'b' },
|
||||
#endif
|
||||
#ifdef RLIMIT_NPTS
|
||||
{ "pseudo-terminals", (char *)0, RLIMIT_NPTS, 1, 'p' },
|
||||
#endif
|
||||
#ifdef RLIMIT_KQUEUES
|
||||
{ "kqueues", (char *)0, RLIMIT_KQUEUES, 1, 'k' },
|
||||
#endif
|
||||
#ifdef RLIMIT_UMTXP
|
||||
{ "umtx shared locks", (char *)0, RLIMIT_UMTXP, 1, 'o' },
|
||||
#endif
|
||||
{ (char *) 0, (char *)0, 0, 0, '\0' }
|
||||
};
|
||||
|
||||
enum limithow { SOFT = 0x1, HARD = 0x2 };
|
||||
|
||||
static void
|
||||
printlimit(enum limithow how, const struct rlimit *limit,
|
||||
const struct limits *l)
|
||||
{
|
||||
rlim_t val = 0;
|
||||
|
||||
if (how & SOFT)
|
||||
val = limit->rlim_cur;
|
||||
else if (how & HARD)
|
||||
val = limit->rlim_max;
|
||||
if (val == RLIM_INFINITY)
|
||||
out1str("unlimited\n");
|
||||
else
|
||||
{
|
||||
val /= l->factor;
|
||||
out1fmt("%jd\n", (intmax_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ulimitcmd(int argc __unused, char **argv __unused)
|
||||
{
|
||||
rlim_t val = 0;
|
||||
enum limithow how = SOFT | HARD;
|
||||
const struct limits *l;
|
||||
int set, all = 0;
|
||||
int optc, what;
|
||||
struct rlimit limit;
|
||||
|
||||
what = 'f';
|
||||
while ((optc = nextopt("HSatfdsmcnuvlbpwko")) != '\0')
|
||||
switch (optc) {
|
||||
case 'H':
|
||||
how = HARD;
|
||||
break;
|
||||
case 'S':
|
||||
how = SOFT;
|
||||
break;
|
||||
case 'a':
|
||||
all = 1;
|
||||
break;
|
||||
default:
|
||||
what = optc;
|
||||
}
|
||||
|
||||
for (l = limits; l->name && l->option != what; l++)
|
||||
;
|
||||
if (!l->name)
|
||||
error("internal error (%c)", what);
|
||||
|
||||
set = *argptr ? 1 : 0;
|
||||
if (set) {
|
||||
char *p = *argptr;
|
||||
|
||||
if (all || argptr[1])
|
||||
error("too many arguments");
|
||||
if (strcmp(p, "unlimited") == 0)
|
||||
val = RLIM_INFINITY;
|
||||
else {
|
||||
char *end;
|
||||
uintmax_t uval;
|
||||
|
||||
if (*p < '0' || *p > '9')
|
||||
error("bad number");
|
||||
errno = 0;
|
||||
uval = strtoumax(p, &end, 10);
|
||||
if (errno != 0 || *end != '\0')
|
||||
error("bad number");
|
||||
if (uval > UINTMAX_MAX / l->factor)
|
||||
error("bad number");
|
||||
uval *= l->factor;
|
||||
val = (rlim_t)uval;
|
||||
if ((uintmax_t)val != uval ||
|
||||
val == RLIM_INFINITY)
|
||||
error("bad number");
|
||||
}
|
||||
}
|
||||
if (all) {
|
||||
for (l = limits; l->name; l++) {
|
||||
char optbuf[40];
|
||||
if (getrlimit(l->cmd, &limit) < 0)
|
||||
error("can't get limit: %s", strerror(errno));
|
||||
|
||||
if (l->units)
|
||||
snprintf(optbuf, sizeof(optbuf),
|
||||
"(%s, -%c) ", l->units, l->option);
|
||||
else
|
||||
snprintf(optbuf, sizeof(optbuf),
|
||||
"(-%c) ", l->option);
|
||||
out1fmt("%-18s %18s ", l->name, optbuf);
|
||||
printlimit(how, &limit, l);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (getrlimit(l->cmd, &limit) < 0)
|
||||
error("can't get limit: %s", strerror(errno));
|
||||
if (set) {
|
||||
if (how & SOFT)
|
||||
limit.rlim_cur = val;
|
||||
if (how & HARD)
|
||||
limit.rlim_max = val;
|
||||
if (setrlimit(l->cmd, &limit) < 0)
|
||||
error("bad limit: %s", strerror(errno));
|
||||
} else
|
||||
printlimit(how, &limit, l);
|
||||
return 0;
|
||||
}
|
137
sh/mkbuiltins
Executable file
137
sh/mkbuiltins
Executable file
@ -0,0 +1,137 @@
|
||||
#!/bin/sh -
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/mkbuiltins 328934 2018-02-06 15:41:35Z arichardson $
|
||||
|
||||
temp=`mktemp -t ka`
|
||||
havehist=1
|
||||
if [ "X$1" = "X-h" ]; then
|
||||
havehist=0
|
||||
shift
|
||||
fi
|
||||
srcdir=$1
|
||||
havejobs=0
|
||||
if grep '^#define[ ]*JOBS[ ]*1' $srcdir/shell.h > /dev/null
|
||||
then havejobs=1
|
||||
fi
|
||||
exec > builtins.c
|
||||
cat <<\!
|
||||
/*
|
||||
* This file was generated by the mkbuiltins program.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "shell.h"
|
||||
#include "builtins.h"
|
||||
|
||||
!
|
||||
awk '/^[^#]/ {if(('$havejobs' || $2 != "-j") && ('$havehist' || $2 != "-h")) \
|
||||
print $0}' $srcdir/builtins.def | sed 's/-[hj]//' > $temp
|
||||
echo 'int (*const builtinfunc[])(int, char **) = {'
|
||||
awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp
|
||||
echo '};
|
||||
|
||||
const unsigned char builtincmd[] = {'
|
||||
awk '{ for (i = 2 ; i <= NF ; i++) {
|
||||
if ($i == "-s") {
|
||||
spc = 1;
|
||||
} else if ($i == "-n") {
|
||||
# Handled later for builtins.h
|
||||
continue
|
||||
} else {
|
||||
printf "\t\"\\%03o\\%03o%s\"\n", length($i), (spc ? 128 : 0) + NR-1, $i
|
||||
spc = 0;
|
||||
}
|
||||
}}' $temp
|
||||
echo '};'
|
||||
|
||||
exec > builtins.h
|
||||
cat <<\!
|
||||
/*
|
||||
* This file was generated by the mkbuiltins program.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
!
|
||||
tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp |
|
||||
awk '{ printf "#define %s %d\n", $1, NR-1}'
|
||||
echo '
|
||||
#define BUILTIN_SPECIAL 0x80
|
||||
|
||||
extern int (*const builtinfunc[])(int, char **);
|
||||
extern const unsigned char builtincmd[];
|
||||
'
|
||||
awk '{ printf "int %s(int, char **);\n", $1}' $temp
|
||||
|
||||
# Build safe_builtin_always()
|
||||
cat <<EOF
|
||||
|
||||
static inline int
|
||||
safe_builtin_always(int idx)
|
||||
{
|
||||
EOF
|
||||
awk '
|
||||
BEGIN { printed = 0 }
|
||||
{
|
||||
for (i = 2 ; i <= NF ; i++) {
|
||||
if ($i == "-s") {
|
||||
continue
|
||||
} else if ($i == "-n") {
|
||||
nofork = 1;
|
||||
} else {
|
||||
if (nofork == 0) {
|
||||
continue
|
||||
}
|
||||
if (printed == 1) {
|
||||
printf " || \n\t "
|
||||
} else {
|
||||
printf "\tif ("
|
||||
}
|
||||
printf "idx == " toupper($1)
|
||||
printed = 1
|
||||
nofork = 0;
|
||||
# Only need to check each once
|
||||
break
|
||||
}
|
||||
}
|
||||
}' $temp
|
||||
|
||||
cat << EOF
|
||||
)
|
||||
return (1);
|
||||
return(0);
|
||||
}
|
||||
EOF
|
||||
|
||||
rm -f $temp
|
467
sh/mknodes.c
Normal file
467
sh/mknodes.c
Normal file
@ -0,0 +1,467 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#ifndef lint
|
||||
static char const copyright[] =
|
||||
"@(#) Copyright (c) 1991, 1993\n\
|
||||
The Regents of the University of California. All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)mknodes.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/mknodes.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
/*
|
||||
* This program reads the nodetypes file and nodes.c.pat file. It generates
|
||||
* the files nodes.h and nodes.c.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// When building for iOS, we don't have a new enough OS X SDK.
|
||||
#ifndef __printf0like
|
||||
#define __printf0like(fmtarg, firstvararg) \
|
||||
__attribute__((__format__ (__printf0__, fmtarg, firstvararg)))
|
||||
#endif
|
||||
|
||||
#define MAXTYPES 50 /* max number of node types */
|
||||
#define MAXFIELDS 20 /* max fields in a structure */
|
||||
#define BUFLEN 100 /* size of character buffers */
|
||||
|
||||
/* field types */
|
||||
#define T_NODE 1 /* union node *field */
|
||||
#define T_NODELIST 2 /* struct nodelist *field */
|
||||
#define T_STRING 3
|
||||
#define T_INT 4 /* int field */
|
||||
#define T_OTHER 5 /* other */
|
||||
#define T_TEMP 6 /* don't copy this field */
|
||||
|
||||
|
||||
struct field { /* a structure field */
|
||||
char *name; /* name of field */
|
||||
int type; /* type of field */
|
||||
char *decl; /* declaration of field */
|
||||
};
|
||||
|
||||
|
||||
struct str { /* struct representing a node structure */
|
||||
char *tag; /* structure tag */
|
||||
int nfields; /* number of fields in the structure */
|
||||
struct field field[MAXFIELDS]; /* the fields of the structure */
|
||||
int done; /* set if fully parsed */
|
||||
};
|
||||
|
||||
|
||||
static int ntypes; /* number of node types */
|
||||
static char *nodename[MAXTYPES]; /* names of the nodes */
|
||||
static struct str *nodestr[MAXTYPES]; /* type of structure used by the node */
|
||||
static int nstr; /* number of structures */
|
||||
static struct str str[MAXTYPES]; /* the structures */
|
||||
static struct str *curstr; /* current structure */
|
||||
static char line[1024];
|
||||
static int linno;
|
||||
static char *linep;
|
||||
|
||||
static void parsenode(void);
|
||||
static void parsefield(void);
|
||||
static void output(char *);
|
||||
static void outsizes(FILE *);
|
||||
static void outfunc(FILE *, int);
|
||||
static void indent(int, FILE *);
|
||||
static int nextfield(char *);
|
||||
static void skipbl(void);
|
||||
static int readline(FILE *);
|
||||
static void error(const char *, ...) __printf0like(1, 2) __dead2;
|
||||
static char *savestr(const char *);
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
FILE *infp;
|
||||
|
||||
if (argc != 3)
|
||||
error("usage: mknodes file");
|
||||
if ((infp = fopen(argv[1], "r")) == NULL)
|
||||
error("Can't open %s: %s", argv[1], strerror(errno));
|
||||
while (readline(infp)) {
|
||||
if (line[0] == ' ' || line[0] == '\t')
|
||||
parsefield();
|
||||
else if (line[0] != '\0')
|
||||
parsenode();
|
||||
}
|
||||
fclose(infp);
|
||||
output(argv[2]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
parsenode(void)
|
||||
{
|
||||
char name[BUFLEN];
|
||||
char tag[BUFLEN];
|
||||
struct str *sp;
|
||||
|
||||
if (curstr && curstr->nfields > 0)
|
||||
curstr->done = 1;
|
||||
nextfield(name);
|
||||
if (! nextfield(tag))
|
||||
error("Tag expected");
|
||||
if (*linep != '\0')
|
||||
error("Garbage at end of line");
|
||||
nodename[ntypes] = savestr(name);
|
||||
for (sp = str ; sp < str + nstr ; sp++) {
|
||||
if (strcmp(sp->tag, tag) == 0)
|
||||
break;
|
||||
}
|
||||
if (sp >= str + nstr) {
|
||||
sp->tag = savestr(tag);
|
||||
sp->nfields = 0;
|
||||
curstr = sp;
|
||||
nstr++;
|
||||
}
|
||||
nodestr[ntypes] = sp;
|
||||
ntypes++;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parsefield(void)
|
||||
{
|
||||
char name[BUFLEN];
|
||||
char type[BUFLEN];
|
||||
char decl[2 * BUFLEN];
|
||||
struct field *fp;
|
||||
|
||||
if (curstr == NULL || curstr->done)
|
||||
error("No current structure to add field to");
|
||||
if (! nextfield(name))
|
||||
error("No field name");
|
||||
if (! nextfield(type))
|
||||
error("No field type");
|
||||
fp = &curstr->field[curstr->nfields];
|
||||
fp->name = savestr(name);
|
||||
if (strcmp(type, "nodeptr") == 0) {
|
||||
fp->type = T_NODE;
|
||||
sprintf(decl, "union node *%s", name);
|
||||
} else if (strcmp(type, "nodelist") == 0) {
|
||||
fp->type = T_NODELIST;
|
||||
sprintf(decl, "struct nodelist *%s", name);
|
||||
} else if (strcmp(type, "string") == 0) {
|
||||
fp->type = T_STRING;
|
||||
sprintf(decl, "char *%s", name);
|
||||
} else if (strcmp(type, "int") == 0) {
|
||||
fp->type = T_INT;
|
||||
sprintf(decl, "int %s", name);
|
||||
} else if (strcmp(type, "other") == 0) {
|
||||
fp->type = T_OTHER;
|
||||
} else if (strcmp(type, "temp") == 0) {
|
||||
fp->type = T_TEMP;
|
||||
} else {
|
||||
error("Unknown type %s", type);
|
||||
}
|
||||
if (fp->type == T_OTHER || fp->type == T_TEMP) {
|
||||
skipbl();
|
||||
fp->decl = savestr(linep);
|
||||
} else {
|
||||
if (*linep)
|
||||
error("Garbage at end of line");
|
||||
fp->decl = savestr(decl);
|
||||
}
|
||||
curstr->nfields++;
|
||||
}
|
||||
|
||||
|
||||
static const char writer[] = "\
|
||||
/*\n\
|
||||
* This file was generated by the mknodes program.\n\
|
||||
*/\n\
|
||||
\n";
|
||||
|
||||
static void
|
||||
output(char *file)
|
||||
{
|
||||
FILE *hfile;
|
||||
FILE *cfile;
|
||||
FILE *patfile;
|
||||
int i;
|
||||
struct str *sp;
|
||||
struct field *fp;
|
||||
char *p;
|
||||
|
||||
if ((patfile = fopen(file, "r")) == NULL)
|
||||
error("Can't open %s: %s", file, strerror(errno));
|
||||
if ((hfile = fopen("nodes.h", "w")) == NULL)
|
||||
error("Can't create nodes.h: %s", strerror(errno));
|
||||
if ((cfile = fopen("nodes.c", "w")) == NULL)
|
||||
error("Can't create nodes.c");
|
||||
fputs(writer, hfile);
|
||||
for (i = 0 ; i < ntypes ; i++)
|
||||
fprintf(hfile, "#define %s %d\n", nodename[i], i);
|
||||
fputs("\n\n\n", hfile);
|
||||
for (sp = str ; sp < &str[nstr] ; sp++) {
|
||||
fprintf(hfile, "struct %s {\n", sp->tag);
|
||||
for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) {
|
||||
fprintf(hfile, " %s;\n", fp->decl);
|
||||
}
|
||||
fputs("};\n\n\n", hfile);
|
||||
}
|
||||
fputs("union node {\n", hfile);
|
||||
fprintf(hfile, " int type;\n");
|
||||
for (sp = str ; sp < &str[nstr] ; sp++) {
|
||||
fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag);
|
||||
}
|
||||
fputs("};\n\n\n", hfile);
|
||||
fputs("struct nodelist {\n", hfile);
|
||||
fputs("\tstruct nodelist *next;\n", hfile);
|
||||
fputs("\tunion node *n;\n", hfile);
|
||||
fputs("};\n\n\n", hfile);
|
||||
fputs("struct funcdef;\n", hfile);
|
||||
fputs("struct funcdef *copyfunc(union node *);\n", hfile);
|
||||
fputs("union node *getfuncnode(struct funcdef *);\n", hfile);
|
||||
fputs("void reffunc(struct funcdef *);\n", hfile);
|
||||
fputs("void unreffunc(struct funcdef *);\n", hfile);
|
||||
if (ferror(hfile))
|
||||
error("Can't write to nodes.h");
|
||||
if (fclose(hfile))
|
||||
error("Can't close nodes.h");
|
||||
|
||||
fputs(writer, cfile);
|
||||
while (fgets(line, sizeof line, patfile) != NULL) {
|
||||
for (p = line ; *p == ' ' || *p == '\t' ; p++);
|
||||
if (strcmp(p, "%SIZES\n") == 0)
|
||||
outsizes(cfile);
|
||||
else if (strcmp(p, "%CALCSIZE\n") == 0)
|
||||
outfunc(cfile, 1);
|
||||
else if (strcmp(p, "%COPY\n") == 0)
|
||||
outfunc(cfile, 0);
|
||||
else
|
||||
fputs(line, cfile);
|
||||
}
|
||||
fclose(patfile);
|
||||
if (ferror(cfile))
|
||||
error("Can't write to nodes.c");
|
||||
if (fclose(cfile))
|
||||
error("Can't close nodes.c");
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
outsizes(FILE *cfile)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes);
|
||||
for (i = 0 ; i < ntypes ; i++) {
|
||||
fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag);
|
||||
}
|
||||
fprintf(cfile, "};\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
outfunc(FILE *cfile, int calcsize)
|
||||
{
|
||||
struct str *sp;
|
||||
struct field *fp;
|
||||
int i;
|
||||
|
||||
fputs(" if (n == NULL)\n", cfile);
|
||||
if (calcsize)
|
||||
fputs(" return;\n", cfile);
|
||||
else
|
||||
fputs(" return NULL;\n", cfile);
|
||||
if (calcsize)
|
||||
fputs(" result->blocksize += nodesize[n->type];\n", cfile);
|
||||
else {
|
||||
fputs(" new = state->block;\n", cfile);
|
||||
fputs(" state->block = (char *)state->block + nodesize[n->type];\n", cfile);
|
||||
}
|
||||
fputs(" switch (n->type) {\n", cfile);
|
||||
for (sp = str ; sp < &str[nstr] ; sp++) {
|
||||
for (i = 0 ; i < ntypes ; i++) {
|
||||
if (nodestr[i] == sp)
|
||||
fprintf(cfile, " case %s:\n", nodename[i]);
|
||||
}
|
||||
for (i = sp->nfields ; --i >= 1 ; ) {
|
||||
fp = &sp->field[i];
|
||||
switch (fp->type) {
|
||||
case T_NODE:
|
||||
if (calcsize) {
|
||||
indent(12, cfile);
|
||||
fprintf(cfile, "calcsize(n->%s.%s, result);\n",
|
||||
sp->tag, fp->name);
|
||||
} else {
|
||||
indent(12, cfile);
|
||||
fprintf(cfile, "new->%s.%s = copynode(n->%s.%s, state);\n",
|
||||
sp->tag, fp->name, sp->tag, fp->name);
|
||||
}
|
||||
break;
|
||||
case T_NODELIST:
|
||||
if (calcsize) {
|
||||
indent(12, cfile);
|
||||
fprintf(cfile, "sizenodelist(n->%s.%s, result);\n",
|
||||
sp->tag, fp->name);
|
||||
} else {
|
||||
indent(12, cfile);
|
||||
fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s, state);\n",
|
||||
sp->tag, fp->name, sp->tag, fp->name);
|
||||
}
|
||||
break;
|
||||
case T_STRING:
|
||||
if (calcsize) {
|
||||
indent(12, cfile);
|
||||
fprintf(cfile, "result->stringsize += strlen(n->%s.%s) + 1;\n",
|
||||
sp->tag, fp->name);
|
||||
} else {
|
||||
indent(12, cfile);
|
||||
fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s, state);\n",
|
||||
sp->tag, fp->name, sp->tag, fp->name);
|
||||
}
|
||||
break;
|
||||
case T_INT:
|
||||
case T_OTHER:
|
||||
if (! calcsize) {
|
||||
indent(12, cfile);
|
||||
fprintf(cfile, "new->%s.%s = n->%s.%s;\n",
|
||||
sp->tag, fp->name, sp->tag, fp->name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
indent(12, cfile);
|
||||
fputs("break;\n", cfile);
|
||||
}
|
||||
fputs(" };\n", cfile);
|
||||
if (! calcsize)
|
||||
fputs(" new->type = n->type;\n", cfile);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
indent(int amount, FILE *fp)
|
||||
{
|
||||
while (amount >= 8) {
|
||||
putc('\t', fp);
|
||||
amount -= 8;
|
||||
}
|
||||
while (--amount >= 0) {
|
||||
putc(' ', fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
nextfield(char *buf)
|
||||
{
|
||||
char *p, *q;
|
||||
|
||||
p = linep;
|
||||
while (*p == ' ' || *p == '\t')
|
||||
p++;
|
||||
q = buf;
|
||||
while (*p != ' ' && *p != '\t' && *p != '\0')
|
||||
*q++ = *p++;
|
||||
*q = '\0';
|
||||
linep = p;
|
||||
return (q > buf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
skipbl(void)
|
||||
{
|
||||
while (*linep == ' ' || *linep == '\t')
|
||||
linep++;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
readline(FILE *infp)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (fgets(line, 1024, infp) == NULL)
|
||||
return 0;
|
||||
for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++);
|
||||
while (p > line && (p[-1] == ' ' || p[-1] == '\t'))
|
||||
p--;
|
||||
*p = '\0';
|
||||
linep = line;
|
||||
linno++;
|
||||
if (p - line > BUFLEN)
|
||||
error("Line too long");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
error(const char *msg, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
|
||||
(void) fprintf(stderr, "line %d: ", linno);
|
||||
(void) vfprintf(stderr, msg, va);
|
||||
(void) fputc('\n', stderr);
|
||||
|
||||
va_end(va);
|
||||
|
||||
exit(2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *
|
||||
savestr(const char *s)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if ((p = malloc(strlen(s) + 1)) == NULL)
|
||||
error("Out of space");
|
||||
(void) strcpy(p, s);
|
||||
return p;
|
||||
}
|
331
sh/mksyntax.c
Normal file
331
sh/mksyntax.c
Normal file
@ -0,0 +1,331 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#ifndef lint
|
||||
static char const copyright[] =
|
||||
"@(#) Copyright (c) 1991, 1993\n\
|
||||
The Regents of the University of California. All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)mksyntax.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/mksyntax.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
/*
|
||||
* This program creates syntax.h and syntax.c.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "parser.h"
|
||||
|
||||
|
||||
struct synclass {
|
||||
const char *name;
|
||||
const char *comment;
|
||||
};
|
||||
|
||||
/* Syntax classes */
|
||||
static const struct synclass synclass[] = {
|
||||
{ "CWORD", "character is nothing special" },
|
||||
{ "CNL", "newline character" },
|
||||
{ "CBACK", "a backslash character" },
|
||||
{ "CSBACK", "a backslash character in single quotes" },
|
||||
{ "CSQUOTE", "single quote" },
|
||||
{ "CDQUOTE", "double quote" },
|
||||
{ "CENDQUOTE", "a terminating quote" },
|
||||
{ "CBQUOTE", "backwards single quote" },
|
||||
{ "CVAR", "a dollar sign" },
|
||||
{ "CENDVAR", "a '}' character" },
|
||||
{ "CLP", "a left paren in arithmetic" },
|
||||
{ "CRP", "a right paren in arithmetic" },
|
||||
{ "CEOF", "end of file" },
|
||||
{ "CCTL", "like CWORD, except it must be escaped" },
|
||||
{ "CSPCL", "these terminate a word" },
|
||||
{ "CIGN", "character should be ignored" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Syntax classes for is_ functions. Warning: if you add new classes
|
||||
* you may have to change the definition of the is_in_name macro.
|
||||
*/
|
||||
static const struct synclass is_entry[] = {
|
||||
{ "ISDIGIT", "a digit" },
|
||||
{ "ISUPPER", "an upper case letter" },
|
||||
{ "ISLOWER", "a lower case letter" },
|
||||
{ "ISUNDER", "an underscore" },
|
||||
{ "ISSPECL", "the name of a special parameter" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const char writer[] = "\
|
||||
/*\n\
|
||||
* This file was generated by the mksyntax program.\n\
|
||||
*/\n\
|
||||
\n";
|
||||
|
||||
|
||||
static FILE *cfile;
|
||||
static FILE *hfile;
|
||||
|
||||
static void add_default(void);
|
||||
static void finish(void);
|
||||
static void init(const char *);
|
||||
static void add(const char *, const char *);
|
||||
static void output_type_macros(void);
|
||||
|
||||
int
|
||||
main(int argc __unused, char **argv __unused)
|
||||
{
|
||||
int i;
|
||||
char buf[80];
|
||||
int pos;
|
||||
|
||||
/* Create output files */
|
||||
if ((cfile = fopen("syntax.c", "w")) == NULL) {
|
||||
perror("syntax.c");
|
||||
exit(2);
|
||||
}
|
||||
if ((hfile = fopen("syntax.h", "w")) == NULL) {
|
||||
perror("syntax.h");
|
||||
exit(2);
|
||||
}
|
||||
fputs(writer, hfile);
|
||||
fputs(writer, cfile);
|
||||
|
||||
fputs("#include <sys/cdefs.h>\n", hfile);
|
||||
fputs("#include <limits.h>\n\n", hfile);
|
||||
|
||||
/* Generate the #define statements in the header file */
|
||||
fputs("/* Syntax classes */\n", hfile);
|
||||
for (i = 0 ; synclass[i].name ; i++) {
|
||||
sprintf(buf, "#define %s %d", synclass[i].name, i);
|
||||
fputs(buf, hfile);
|
||||
for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07)
|
||||
putc('\t', hfile);
|
||||
fprintf(hfile, "/* %s */\n", synclass[i].comment);
|
||||
}
|
||||
putc('\n', hfile);
|
||||
fputs("/* Syntax classes for is_ functions */\n", hfile);
|
||||
for (i = 0 ; is_entry[i].name ; i++) {
|
||||
sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i);
|
||||
fputs(buf, hfile);
|
||||
for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07)
|
||||
putc('\t', hfile);
|
||||
fprintf(hfile, "/* %s */\n", is_entry[i].comment);
|
||||
}
|
||||
putc('\n', hfile);
|
||||
fputs("#define SYNBASE (1 - CHAR_MIN)\n", hfile);
|
||||
fputs("#define PEOF -SYNBASE\n\n", hfile);
|
||||
putc('\n', hfile);
|
||||
fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile);
|
||||
fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile);
|
||||
fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile);
|
||||
fputs("#define ARISYNTAX (arisyntax + SYNBASE)\n", hfile);
|
||||
putc('\n', hfile);
|
||||
output_type_macros(); /* is_digit, etc. */
|
||||
putc('\n', hfile);
|
||||
|
||||
/* Generate the syntax tables. */
|
||||
fputs("#include \"parser.h\"\n", cfile);
|
||||
fputs("#include \"shell.h\"\n", cfile);
|
||||
fputs("#include \"syntax.h\"\n\n", cfile);
|
||||
|
||||
fputs("/* syntax table used when not in quotes */\n", cfile);
|
||||
init("basesyntax");
|
||||
add_default();
|
||||
add("\n", "CNL");
|
||||
add("\\", "CBACK");
|
||||
add("'", "CSQUOTE");
|
||||
add("\"", "CDQUOTE");
|
||||
add("`", "CBQUOTE");
|
||||
add("$", "CVAR");
|
||||
add("}", "CENDVAR");
|
||||
add("<>();&| \t", "CSPCL");
|
||||
finish();
|
||||
|
||||
fputs("\n/* syntax table used when in double quotes */\n", cfile);
|
||||
init("dqsyntax");
|
||||
add_default();
|
||||
add("\n", "CNL");
|
||||
add("\\", "CBACK");
|
||||
add("\"", "CENDQUOTE");
|
||||
add("`", "CBQUOTE");
|
||||
add("$", "CVAR");
|
||||
add("}", "CENDVAR");
|
||||
/* ':/' for tilde expansion, '-^]' for [a\-x] pattern ranges */
|
||||
add("!*?[]=~:/-^", "CCTL");
|
||||
finish();
|
||||
|
||||
fputs("\n/* syntax table used when in single quotes */\n", cfile);
|
||||
init("sqsyntax");
|
||||
add_default();
|
||||
add("\n", "CNL");
|
||||
add("\\", "CSBACK");
|
||||
add("'", "CENDQUOTE");
|
||||
/* ':/' for tilde expansion, '-^]' for [a\-x] pattern ranges */
|
||||
add("!*?[]=~:/-^", "CCTL");
|
||||
finish();
|
||||
|
||||
fputs("\n/* syntax table used when in arithmetic */\n", cfile);
|
||||
init("arisyntax");
|
||||
add_default();
|
||||
add("\n", "CNL");
|
||||
add("\\", "CBACK");
|
||||
add("`", "CBQUOTE");
|
||||
add("\"", "CIGN");
|
||||
add("$", "CVAR");
|
||||
add("}", "CENDVAR");
|
||||
add("(", "CLP");
|
||||
add(")", "CRP");
|
||||
finish();
|
||||
|
||||
fputs("\n/* character classification table */\n", cfile);
|
||||
init("is_type");
|
||||
add("0123456789", "ISDIGIT");
|
||||
add("abcdefghijklmnopqrstuvwxyz", "ISLOWER");
|
||||
add("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ISUPPER");
|
||||
add("_", "ISUNDER");
|
||||
add("#?$!-*@", "ISSPECL");
|
||||
finish();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Output the header and declaration of a syntax table.
|
||||
*/
|
||||
|
||||
static void
|
||||
init(const char *name)
|
||||
{
|
||||
fprintf(hfile, "extern const char %s[];\n", name);
|
||||
fprintf(cfile, "const char %s[SYNBASE + CHAR_MAX + 1] = {\n", name);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_one(const char *key, const char *type)
|
||||
{
|
||||
fprintf(cfile, "\t[SYNBASE + %s] = %s,\n", key, type);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add default values to the syntax table.
|
||||
*/
|
||||
|
||||
static void
|
||||
add_default(void)
|
||||
{
|
||||
add_one("PEOF", "CEOF");
|
||||
add_one("CTLESC", "CCTL");
|
||||
add_one("CTLVAR", "CCTL");
|
||||
add_one("CTLENDVAR", "CCTL");
|
||||
add_one("CTLBACKQ", "CCTL");
|
||||
add_one("CTLBACKQ + CTLQUOTE", "CCTL");
|
||||
add_one("CTLARI", "CCTL");
|
||||
add_one("CTLENDARI", "CCTL");
|
||||
add_one("CTLQUOTEMARK", "CCTL");
|
||||
add_one("CTLQUOTEEND", "CCTL");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Output the footer of a syntax table.
|
||||
*/
|
||||
|
||||
static void
|
||||
finish(void)
|
||||
{
|
||||
fputs("};\n", cfile);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add entries to the syntax table.
|
||||
*/
|
||||
|
||||
static void
|
||||
add(const char *p, const char *type)
|
||||
{
|
||||
for (; *p; ++p) {
|
||||
char c = *p;
|
||||
switch (c) {
|
||||
case '\t': c = 't'; break;
|
||||
case '\n': c = 'n'; break;
|
||||
case '\'': c = '\''; break;
|
||||
case '\\': c = '\\'; break;
|
||||
|
||||
default:
|
||||
fprintf(cfile, "\t[SYNBASE + '%c'] = %s,\n", c, type);
|
||||
continue;
|
||||
}
|
||||
fprintf(cfile, "\t[SYNBASE + '\\%c'] = %s,\n", c, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Output character classification macros (e.g. is_digit). If digits are
|
||||
* contiguous, we can test for them quickly.
|
||||
*/
|
||||
|
||||
static const char *macro[] = {
|
||||
"#define is_digit(c)\t((unsigned int)((c) - '0') <= 9)",
|
||||
"#define is_eof(c)\t((c) == PEOF)",
|
||||
"#define is_alpha(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER))",
|
||||
"#define is_name(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER|ISUNDER))",
|
||||
"#define is_in_name(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))",
|
||||
"#define is_special(c)\t((is_type+SYNBASE)[(int)c] & (ISSPECL|ISDIGIT))",
|
||||
"#define digit_val(c)\t((c) - '0')",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void
|
||||
output_type_macros(void)
|
||||
{
|
||||
const char **pp;
|
||||
|
||||
for (pp = macro ; *pp ; pp++)
|
||||
fprintf(hfile, "%s\n", *pp);
|
||||
}
|
93
sh/mktokens
Normal file
93
sh/mktokens
Normal file
@ -0,0 +1,93 @@
|
||||
#!/bin/sh -
|
||||
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)mktokens 8.1 (Berkeley) 5/31/93
|
||||
# $FreeBSD: head/bin/sh/mktokens 328934 2018-02-06 15:41:35Z arichardson $
|
||||
|
||||
# The following is a list of tokens. The second column is nonzero if the
|
||||
# token marks the end of a list. The third column is the name to print in
|
||||
# error messages.
|
||||
|
||||
temp=`mktemp -t ka`
|
||||
cat > $temp <<\!
|
||||
TEOF 1 end of file
|
||||
TNL 0 newline
|
||||
TSEMI 0 ";"
|
||||
TBACKGND 0 "&"
|
||||
TAND 0 "&&"
|
||||
TOR 0 "||"
|
||||
TPIPE 0 "|"
|
||||
TLP 0 "("
|
||||
TRP 1 ")"
|
||||
TENDCASE 1 ";;"
|
||||
TFALLTHRU 1 ";&"
|
||||
TREDIR 0 redirection
|
||||
TWORD 0 word
|
||||
TIF 0 "if"
|
||||
TTHEN 1 "then"
|
||||
TELSE 1 "else"
|
||||
TELIF 1 "elif"
|
||||
TFI 1 "fi"
|
||||
TWHILE 0 "while"
|
||||
TUNTIL 0 "until"
|
||||
TFOR 0 "for"
|
||||
TDO 1 "do"
|
||||
TDONE 1 "done"
|
||||
TBEGIN 0 "{"
|
||||
TEND 1 "}"
|
||||
TCASE 0 "case"
|
||||
TESAC 1 "esac"
|
||||
TNOT 0 "!"
|
||||
!
|
||||
nl=`wc -l $temp`
|
||||
exec > token.h
|
||||
awk '{print "#define " $1 " " NR-1}' $temp
|
||||
echo '
|
||||
/* Array indicating which tokens mark the end of a list */
|
||||
static const char tokendlist[] = {'
|
||||
awk '{print "\t" $2 ","}' $temp
|
||||
echo '};
|
||||
|
||||
static const char *const tokname[] = {'
|
||||
sed -e 's/"/\\"/g' \
|
||||
-e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \
|
||||
$temp
|
||||
echo '};
|
||||
'
|
||||
sed 's/"//g' $temp | awk '
|
||||
/TIF/{print "#define KWDOFFSET " NR-1; print ""; print "const char *const parsekwd[] = {"}
|
||||
/TIF/,/neverfound/{print " \"" $3 "\","}'
|
||||
echo ' 0
|
||||
};'
|
||||
|
||||
rm $temp
|
44
sh/myhistedit.h
Normal file
44
sh/myhistedit.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)myhistedit.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/myhistedit.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
#include <histedit.h>
|
||||
|
||||
extern History *hist;
|
||||
extern EditLine *el;
|
||||
extern int displayhist;
|
||||
|
||||
void histedit(void);
|
||||
void sethistsize(const char *);
|
||||
void setterm(const char *);
|
||||
|
100
sh/mystring.c
Normal file
100
sh/mystring.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/mystring.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
/*
|
||||
* String functions.
|
||||
*
|
||||
* equal(s1, s2) Return true if strings are equal.
|
||||
* number(s) Convert a string of digits to an integer.
|
||||
* is_number(s) Return true if s is a string of digits.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "shell.h"
|
||||
#include "syntax.h"
|
||||
#include "error.h"
|
||||
#include "mystring.h"
|
||||
|
||||
|
||||
char nullstr[1]; /* zero length string */
|
||||
|
||||
/*
|
||||
* equal - #defined in mystring.h
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Convert a string of digits to an integer, printing an error message on
|
||||
* failure.
|
||||
*/
|
||||
|
||||
int
|
||||
number(const char *s)
|
||||
{
|
||||
if (! is_number(s))
|
||||
error("Illegal number: %s", s);
|
||||
return atoi(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Check for a valid number. This should be elsewhere.
|
||||
*/
|
||||
|
||||
int
|
||||
is_number(const char *p)
|
||||
{
|
||||
const char *q;
|
||||
|
||||
if (*p == '\0')
|
||||
return 0;
|
||||
while (*p == '0')
|
||||
p++;
|
||||
for (q = p; *q != '\0'; q++)
|
||||
if (! is_digit(*q))
|
||||
return 0;
|
||||
if (q - p > 10 ||
|
||||
(q - p == 10 && memcmp(p, "2147483647", 10) > 0))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
43
sh/mystring.h
Normal file
43
sh/mystring.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)mystring.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/mystring.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int number(const char *);
|
||||
int is_number(const char *);
|
||||
|
||||
#define equal(s1, s2) (strcmp(s1, s2) == 0)
|
193
sh/nodes.c.pat
Normal file
193
sh/nodes.c.pat
Normal file
@ -0,0 +1,193 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/nodes.c.pat 314436 2017-02-28 23:42:47Z imp $
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
/*
|
||||
* Routine for dealing with parsed shell commands.
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
#include "nodes.h"
|
||||
#include "memalloc.h"
|
||||
#include "mystring.h"
|
||||
|
||||
|
||||
struct nodesize {
|
||||
int blocksize; /* size of structures in function */
|
||||
int stringsize; /* size of strings in node */
|
||||
};
|
||||
|
||||
struct nodecopystate {
|
||||
pointer block; /* block to allocate function from */
|
||||
char *string; /* block to allocate strings from */
|
||||
};
|
||||
|
||||
%SIZES
|
||||
|
||||
|
||||
static void calcsize(union node *, struct nodesize *);
|
||||
static void sizenodelist(struct nodelist *, struct nodesize *);
|
||||
static union node *copynode(union node *, struct nodecopystate *);
|
||||
static struct nodelist *copynodelist(struct nodelist *, struct nodecopystate *);
|
||||
static char *nodesavestr(const char *, struct nodecopystate *);
|
||||
|
||||
|
||||
struct funcdef {
|
||||
unsigned int refcount;
|
||||
union node n;
|
||||
};
|
||||
|
||||
/*
|
||||
* Make a copy of a parse tree.
|
||||
*/
|
||||
|
||||
struct funcdef *
|
||||
copyfunc(union node *n)
|
||||
{
|
||||
struct nodesize sz;
|
||||
struct nodecopystate st;
|
||||
struct funcdef *fn;
|
||||
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
sz.blocksize = offsetof(struct funcdef, n);
|
||||
sz.stringsize = 0;
|
||||
calcsize(n, &sz);
|
||||
fn = ckmalloc(sz.blocksize + sz.stringsize);
|
||||
fn->refcount = 1;
|
||||
st.block = (char *)fn + offsetof(struct funcdef, n);
|
||||
st.string = (char *)fn + sz.blocksize;
|
||||
copynode(n, &st);
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
union node *
|
||||
getfuncnode(struct funcdef *fn)
|
||||
{
|
||||
return fn == NULL ? NULL : &fn->n;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
calcsize(union node *n, struct nodesize *result)
|
||||
{
|
||||
%CALCSIZE
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
sizenodelist(struct nodelist *lp, struct nodesize *result)
|
||||
{
|
||||
while (lp) {
|
||||
result->blocksize += ALIGN(sizeof(struct nodelist));
|
||||
calcsize(lp->n, result);
|
||||
lp = lp->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static union node *
|
||||
copynode(union node *n, struct nodecopystate *state)
|
||||
{
|
||||
union node *new;
|
||||
|
||||
%COPY
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
static struct nodelist *
|
||||
copynodelist(struct nodelist *lp, struct nodecopystate *state)
|
||||
{
|
||||
struct nodelist *start;
|
||||
struct nodelist **lpp;
|
||||
|
||||
lpp = &start;
|
||||
while (lp) {
|
||||
*lpp = state->block;
|
||||
state->block = (char *)state->block +
|
||||
ALIGN(sizeof(struct nodelist));
|
||||
(*lpp)->n = copynode(lp->n, state);
|
||||
lp = lp->next;
|
||||
lpp = &(*lpp)->next;
|
||||
}
|
||||
*lpp = NULL;
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *
|
||||
nodesavestr(const char *s, struct nodecopystate *state)
|
||||
{
|
||||
const char *p = s;
|
||||
char *q = state->string;
|
||||
char *rtn = state->string;
|
||||
|
||||
while ((*q++ = *p++) != '\0')
|
||||
continue;
|
||||
state->string = q;
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
reffunc(struct funcdef *fn)
|
||||
{
|
||||
if (fn)
|
||||
fn->refcount++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decrement the reference count of a function definition, freeing it
|
||||
* if it falls to 0.
|
||||
*/
|
||||
|
||||
void
|
||||
unreffunc(struct funcdef *fn)
|
||||
{
|
||||
if (fn) {
|
||||
fn->refcount--;
|
||||
if (fn->refcount > 0)
|
||||
return;
|
||||
ckfree(fn);
|
||||
}
|
||||
}
|
145
sh/nodetypes
Normal file
145
sh/nodetypes
Normal file
@ -0,0 +1,145 @@
|
||||
#-
|
||||
# Copyright (c) 1991, 1993
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# This code is derived from software contributed to Berkeley by
|
||||
# Kenneth Almquist.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# @(#)nodetypes 8.2 (Berkeley) 5/4/95
|
||||
# $FreeBSD: head/bin/sh/nodetypes 314436 2017-02-28 23:42:47Z imp $
|
||||
|
||||
# This file describes the nodes used in parse trees. Unindented lines
|
||||
# contain a node type followed by a structure tag. Subsequent indented
|
||||
# lines specify the fields of the structure. Several node types can share
|
||||
# the same structure, in which case the fields of the structure should be
|
||||
# specified only once.
|
||||
#
|
||||
# A field of a structure is described by the name of the field followed
|
||||
# by a type. The currently implemented types are:
|
||||
# nodeptr - a pointer to a node
|
||||
# nodelist - a pointer to a list of nodes
|
||||
# string - a pointer to a nul terminated string
|
||||
# int - an integer
|
||||
# other - any type that can be copied by assignment
|
||||
# temp - a field that doesn't have to be copied when the node is copied
|
||||
# The last two types should be followed by the text of a C declaration for
|
||||
# the field.
|
||||
|
||||
NSEMI nbinary # two commands separated by a semicolon
|
||||
type int
|
||||
ch1 nodeptr # the first child
|
||||
ch2 nodeptr # the second child
|
||||
|
||||
NCMD ncmd # a simple command
|
||||
type int
|
||||
args nodeptr # the arguments
|
||||
redirect nodeptr # list of file redirections
|
||||
|
||||
NPIPE npipe # a pipeline
|
||||
type int
|
||||
backgnd int # set to run pipeline in background
|
||||
cmdlist nodelist # the commands in the pipeline
|
||||
|
||||
NREDIR nredir # redirection (of a compex command)
|
||||
type int
|
||||
n nodeptr # the command
|
||||
redirect nodeptr # list of file redirections
|
||||
|
||||
NBACKGND nredir # run command in background
|
||||
NSUBSHELL nredir # run command in a subshell
|
||||
|
||||
NAND nbinary # the && operator
|
||||
NOR nbinary # the || operator
|
||||
|
||||
NIF nif # the if statement. Elif clauses are handled
|
||||
type int # using multiple if nodes.
|
||||
test nodeptr # if test
|
||||
ifpart nodeptr # then ifpart
|
||||
elsepart nodeptr # else elsepart
|
||||
|
||||
NWHILE nbinary # the while statement. First child is the test
|
||||
NUNTIL nbinary # the until statement
|
||||
|
||||
NFOR nfor # the for statement
|
||||
type int
|
||||
args nodeptr # for var in args
|
||||
body nodeptr # do body; done
|
||||
var string # the for variable
|
||||
|
||||
NCASE ncase # a case statement
|
||||
type int
|
||||
expr nodeptr # the word to switch on
|
||||
cases nodeptr # the list of cases (NCLIST nodes)
|
||||
|
||||
NCLIST nclist # a case ending with ;;
|
||||
type int
|
||||
next nodeptr # the next case in list
|
||||
pattern nodeptr # list of patterns for this case
|
||||
body nodeptr # code to execute for this case
|
||||
|
||||
NCLISTFALLTHRU nclist # a case ending with ;&
|
||||
|
||||
NDEFUN narg # define a function. The "next" field contains
|
||||
# the body of the function.
|
||||
|
||||
NARG narg # represents a word
|
||||
type int
|
||||
next nodeptr # next word in list
|
||||
text string # the text of the word
|
||||
backquote nodelist # list of commands in back quotes
|
||||
|
||||
NTO nfile # fd> fname
|
||||
NFROM nfile # fd< fname
|
||||
NFROMTO nfile # fd<> fname
|
||||
NAPPEND nfile # fd>> fname
|
||||
NCLOBBER nfile # fd>| fname
|
||||
type int
|
||||
fd int # file descriptor being redirected
|
||||
next nodeptr # next redirection in list
|
||||
fname nodeptr # file name, in a NARG node
|
||||
expfname temp char *expfname # actual file name
|
||||
|
||||
NTOFD ndup # fd<&dupfd
|
||||
NFROMFD ndup # fd>&dupfd
|
||||
type int
|
||||
fd int # file descriptor being redirected
|
||||
next nodeptr # next redirection in list
|
||||
dupfd int # file descriptor to duplicate
|
||||
vname nodeptr # file name if fd>&$var
|
||||
|
||||
|
||||
NHERE nhere # fd<<\!
|
||||
NXHERE nhere # fd<<!
|
||||
type int
|
||||
fd int # file descriptor being redirected
|
||||
next nodeptr # next redirection in list
|
||||
doc nodeptr # input to command (NARG node)
|
||||
expdoc temp const char *expdoc # actual document (for NXHERE)
|
||||
|
||||
NNOT nnot # ! command (actually pipeline)
|
||||
type int
|
||||
com nodeptr
|
594
sh/options.c
Normal file
594
sh/options.c
Normal file
@ -0,0 +1,594 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/options.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "shell.h"
|
||||
#define DEFINE_OPTIONS
|
||||
#include "options.h"
|
||||
#undef DEFINE_OPTIONS
|
||||
#include "nodes.h" /* for other header files */
|
||||
#include "eval.h"
|
||||
#include "jobs.h"
|
||||
#include "input.h"
|
||||
#include "output.h"
|
||||
#include "trap.h"
|
||||
#include "var.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "mystring.h"
|
||||
#include "builtins.h"
|
||||
#ifndef NO_HISTORY
|
||||
#include "myhistedit.h"
|
||||
#endif
|
||||
|
||||
char *arg0; /* value of $0 */
|
||||
struct shparam shellparam; /* current positional parameters */
|
||||
char **argptr; /* argument list for builtin commands */
|
||||
char *shoptarg; /* set by nextopt (like getopt) */
|
||||
char *nextopt_optptr; /* used by nextopt */
|
||||
|
||||
char *minusc; /* argument to -c option */
|
||||
|
||||
|
||||
static void options(int);
|
||||
static void minus_o(char *, int);
|
||||
static void setoption(int, int);
|
||||
static void setoptionbyindex(int, int);
|
||||
static void setparam(int, char **);
|
||||
static int getopts(char *, char *, char **, char ***, char **);
|
||||
|
||||
|
||||
/*
|
||||
* Process the shell command line arguments.
|
||||
*/
|
||||
|
||||
void
|
||||
procargs(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
char *scriptname;
|
||||
|
||||
argptr = argv;
|
||||
if (argc > 0)
|
||||
argptr++;
|
||||
for (i = 0; i < NOPTS; i++)
|
||||
optval[i] = 2;
|
||||
privileged = (getuid() != geteuid() || getgid() != getegid());
|
||||
options(1);
|
||||
if (*argptr == NULL && minusc == NULL)
|
||||
sflag = 1;
|
||||
if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) {
|
||||
iflag = 1;
|
||||
if (Eflag == 2)
|
||||
Eflag = 1;
|
||||
}
|
||||
if (mflag == 2)
|
||||
mflag = iflag;
|
||||
for (i = 0; i < NOPTS; i++)
|
||||
if (optval[i] == 2)
|
||||
optval[i] = 0;
|
||||
arg0 = argv[0];
|
||||
if (sflag == 0 && minusc == NULL) {
|
||||
scriptname = *argptr++;
|
||||
setinputfile(scriptname, 0);
|
||||
commandname = arg0 = scriptname;
|
||||
}
|
||||
/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
|
||||
if (argptr && minusc && *argptr)
|
||||
arg0 = *argptr++;
|
||||
|
||||
shellparam.p = argptr;
|
||||
shellparam.reset = 1;
|
||||
/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
|
||||
while (*argptr) {
|
||||
shellparam.nparam++;
|
||||
argptr++;
|
||||
}
|
||||
optschanged();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
optschanged(void)
|
||||
{
|
||||
setinteractive();
|
||||
#ifndef NO_HISTORY
|
||||
histedit();
|
||||
#endif
|
||||
setjobctl(mflag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process shell options. The global variable argptr contains a pointer
|
||||
* to the argument list; we advance it past the options.
|
||||
* If cmdline is true, process the shell's argv; otherwise, process arguments
|
||||
* to the set special builtin.
|
||||
*/
|
||||
|
||||
static void
|
||||
options(int cmdline)
|
||||
{
|
||||
char *kp, *p;
|
||||
int val;
|
||||
int c;
|
||||
|
||||
if (cmdline)
|
||||
minusc = NULL;
|
||||
while ((p = *argptr) != NULL) {
|
||||
argptr++;
|
||||
if ((c = *p++) == '-') {
|
||||
val = 1;
|
||||
/* A "-" or "--" terminates options */
|
||||
if (p[0] == '\0')
|
||||
goto end_options1;
|
||||
if (p[0] == '-' && p[1] == '\0')
|
||||
goto end_options2;
|
||||
/**
|
||||
* For the benefit of `#!' lines in shell scripts,
|
||||
* treat a string of '-- *#.*' the same as '--'.
|
||||
* This is needed so that a script starting with:
|
||||
* #!/bin/sh -- # -*- perl -*-
|
||||
* will continue to work after a change is made to
|
||||
* kern/imgact_shell.c to NOT token-ize the options
|
||||
* specified on a '#!' line. A bit of a kludge,
|
||||
* but that trick is recommended in documentation
|
||||
* for some scripting languages, and we might as
|
||||
* well continue to support it.
|
||||
*/
|
||||
if (p[0] == '-') {
|
||||
kp = p + 1;
|
||||
while (*kp == ' ' || *kp == '\t')
|
||||
kp++;
|
||||
if (*kp == '#' || *kp == '\0')
|
||||
goto end_options2;
|
||||
}
|
||||
} else if (c == '+') {
|
||||
val = 0;
|
||||
} else {
|
||||
argptr--;
|
||||
break;
|
||||
}
|
||||
while ((c = *p++) != '\0') {
|
||||
if (c == 'c' && cmdline) {
|
||||
char *q;
|
||||
|
||||
q = *argptr++;
|
||||
if (q == NULL || minusc != NULL)
|
||||
error("Bad -c option");
|
||||
minusc = q;
|
||||
} else if (c == 'o') {
|
||||
minus_o(*argptr, val);
|
||||
if (*argptr)
|
||||
argptr++;
|
||||
} else
|
||||
setoption(c, val);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
/* When processing `set', a single "-" means turn off -x and -v */
|
||||
end_options1:
|
||||
if (!cmdline) {
|
||||
xflag = vflag = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* When processing `set', a "--" means the remaining arguments
|
||||
* replace the positional parameters in the active shell. If
|
||||
* there are no remaining options, then all the positional
|
||||
* parameters are cleared (equivalent to doing ``shift $#'').
|
||||
*/
|
||||
end_options2:
|
||||
if (!cmdline) {
|
||||
if (*argptr == NULL)
|
||||
setparam(0, argptr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point we are processing options given to 'sh' on a command
|
||||
* line. If an end-of-options marker ("-" or "--") is followed by an
|
||||
* arg of "#", then skip over all remaining arguments. Some scripting
|
||||
* languages (e.g.: perl) document that /bin/sh will implement this
|
||||
* behavior, and they recommend that users take advantage of it to
|
||||
* solve certain issues that can come up when writing a perl script.
|
||||
* Yes, this feature is in /bin/sh to help users write perl scripts.
|
||||
*/
|
||||
p = *argptr;
|
||||
if (p != NULL && p[0] == '#' && p[1] == '\0') {
|
||||
while (*argptr != NULL)
|
||||
argptr++;
|
||||
/* We need to keep the final argument */
|
||||
argptr--;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
minus_o(char *name, int val)
|
||||
{
|
||||
int i;
|
||||
const unsigned char *on;
|
||||
size_t len;
|
||||
|
||||
if (name == NULL) {
|
||||
if (val) {
|
||||
/* "Pretty" output. */
|
||||
out1str("Current option settings\n");
|
||||
for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
|
||||
out1fmt("%-16.*s%s\n", *on, on + 1,
|
||||
optval[i] ? "on" : "off");
|
||||
} else {
|
||||
/* Output suitable for re-input to shell. */
|
||||
for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
|
||||
out1fmt("%s %co %.*s%s",
|
||||
i % 6 == 0 ? "set" : "",
|
||||
optval[i] ? '-' : '+',
|
||||
*on, on + 1,
|
||||
i % 6 == 5 || i == NOPTS - 1 ? "\n" : "");
|
||||
}
|
||||
} else {
|
||||
len = strlen(name);
|
||||
for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
|
||||
if (*on == len && memcmp(on + 1, name, len) == 0) {
|
||||
setoptionbyindex(i, val);
|
||||
return;
|
||||
}
|
||||
error("Illegal option -o %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
setoptionbyindex(int idx, int val)
|
||||
{
|
||||
if (&optval[idx] == &privileged && !val && privileged) {
|
||||
if (setgid(getgid()) == -1)
|
||||
error("setgid");
|
||||
if (setuid(getuid()) == -1)
|
||||
error("setuid");
|
||||
}
|
||||
optval[idx] = val;
|
||||
if (val) {
|
||||
/* #%$ hack for ksh semantics */
|
||||
if (&optval[idx] == &Vflag)
|
||||
Eflag = 0;
|
||||
else if (&optval[idx] == &Eflag)
|
||||
Vflag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setoption(int flag, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NSHORTOPTS; i++)
|
||||
if (optletter[i] == flag) {
|
||||
setoptionbyindex(i, val);
|
||||
return;
|
||||
}
|
||||
error("Illegal option -%c", flag);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the shell parameters.
|
||||
*/
|
||||
|
||||
static void
|
||||
setparam(int argc, char **argv)
|
||||
{
|
||||
char **newparam;
|
||||
char **ap;
|
||||
|
||||
ap = newparam = ckmalloc((argc + 1) * sizeof *ap);
|
||||
while (*argv) {
|
||||
*ap++ = savestr(*argv++);
|
||||
}
|
||||
*ap = NULL;
|
||||
freeparam(&shellparam);
|
||||
shellparam.malloc = 1;
|
||||
shellparam.nparam = argc;
|
||||
shellparam.p = newparam;
|
||||
shellparam.optp = NULL;
|
||||
shellparam.reset = 1;
|
||||
shellparam.optnext = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free the list of positional parameters.
|
||||
*/
|
||||
|
||||
void
|
||||
freeparam(struct shparam *param)
|
||||
{
|
||||
char **ap;
|
||||
|
||||
if (param->malloc) {
|
||||
for (ap = param->p ; *ap ; ap++)
|
||||
ckfree(*ap);
|
||||
ckfree(param->p);
|
||||
}
|
||||
if (param->optp) {
|
||||
for (ap = param->optp ; *ap ; ap++)
|
||||
ckfree(*ap);
|
||||
ckfree(param->optp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The shift builtin command.
|
||||
*/
|
||||
|
||||
int
|
||||
shiftcmd(int argc, char **argv)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
n = 1;
|
||||
if (argc > 1)
|
||||
n = number(argv[1]);
|
||||
if (n > shellparam.nparam)
|
||||
return 1;
|
||||
INTOFF;
|
||||
shellparam.nparam -= n;
|
||||
if (shellparam.malloc)
|
||||
for (i = 0; i < n; i++)
|
||||
ckfree(shellparam.p[i]);
|
||||
memmove(shellparam.p, shellparam.p + n,
|
||||
(shellparam.nparam + 1) * sizeof(shellparam.p[0]));
|
||||
shellparam.reset = 1;
|
||||
INTON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The set builtin command.
|
||||
*/
|
||||
|
||||
int
|
||||
setcmd(int argc, char **argv)
|
||||
{
|
||||
if (argc == 1)
|
||||
return showvarscmd(argc, argv);
|
||||
INTOFF;
|
||||
options(0);
|
||||
optschanged();
|
||||
if (*argptr != NULL) {
|
||||
setparam(argc - (argptr - argv), argptr);
|
||||
}
|
||||
INTON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
getoptsreset(const char *value)
|
||||
{
|
||||
while (*value == '0')
|
||||
value++;
|
||||
if (strcmp(value, "1") == 0)
|
||||
shellparam.reset = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The getopts builtin. Shellparam.optnext points to the next argument
|
||||
* to be processed. Shellparam.optptr points to the next character to
|
||||
* be processed in the current argument. If shellparam.optnext is NULL,
|
||||
* then it's the first time getopts has been called.
|
||||
*/
|
||||
|
||||
int
|
||||
getoptscmd(int argc, char **argv)
|
||||
{
|
||||
char **optbase = NULL, **ap;
|
||||
int i;
|
||||
|
||||
if (argc < 3)
|
||||
error("usage: getopts optstring var [arg]");
|
||||
|
||||
if (shellparam.reset == 1) {
|
||||
INTOFF;
|
||||
if (shellparam.optp) {
|
||||
for (ap = shellparam.optp ; *ap ; ap++)
|
||||
ckfree(*ap);
|
||||
ckfree(shellparam.optp);
|
||||
shellparam.optp = NULL;
|
||||
}
|
||||
if (argc > 3) {
|
||||
shellparam.optp = ckmalloc((argc - 2) * sizeof *ap);
|
||||
memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap);
|
||||
for (i = 0; i < argc - 3; i++)
|
||||
shellparam.optp[i] = savestr(argv[i + 3]);
|
||||
}
|
||||
INTON;
|
||||
optbase = argc == 3 ? shellparam.p : shellparam.optp;
|
||||
shellparam.optnext = optbase;
|
||||
shellparam.optptr = NULL;
|
||||
shellparam.reset = 0;
|
||||
} else
|
||||
optbase = shellparam.optp ? shellparam.optp : shellparam.p;
|
||||
|
||||
return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
|
||||
&shellparam.optptr);
|
||||
}
|
||||
|
||||
static int
|
||||
getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
|
||||
char **optptr)
|
||||
{
|
||||
char *p, *q;
|
||||
char c = '?';
|
||||
int done = 0;
|
||||
int ind = 0;
|
||||
int err = 0;
|
||||
char s[10];
|
||||
const char *newoptarg = NULL;
|
||||
|
||||
if ((p = *optptr) == NULL || *p == '\0') {
|
||||
/* Current word is done, advance */
|
||||
if (*optnext == NULL)
|
||||
return 1;
|
||||
p = **optnext;
|
||||
if (p == NULL || *p != '-' || *++p == '\0') {
|
||||
atend:
|
||||
ind = *optnext - optfirst + 1;
|
||||
*optnext = NULL;
|
||||
p = NULL;
|
||||
done = 1;
|
||||
goto out;
|
||||
}
|
||||
(*optnext)++;
|
||||
if (p[0] == '-' && p[1] == '\0') /* check for "--" */
|
||||
goto atend;
|
||||
}
|
||||
|
||||
c = *p++;
|
||||
for (q = optstr; *q != c; ) {
|
||||
if (*q == '\0') {
|
||||
if (optstr[0] == ':') {
|
||||
s[0] = c;
|
||||
s[1] = '\0';
|
||||
newoptarg = s;
|
||||
}
|
||||
else
|
||||
out2fmt_flush("Illegal option -%c\n", c);
|
||||
c = '?';
|
||||
goto out;
|
||||
}
|
||||
if (*++q == ':')
|
||||
q++;
|
||||
}
|
||||
|
||||
if (*++q == ':') {
|
||||
if (*p == '\0' && (p = **optnext) == NULL) {
|
||||
if (optstr[0] == ':') {
|
||||
s[0] = c;
|
||||
s[1] = '\0';
|
||||
newoptarg = s;
|
||||
c = ':';
|
||||
}
|
||||
else {
|
||||
out2fmt_flush("No arg for -%c option\n", c);
|
||||
c = '?';
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p == **optnext)
|
||||
(*optnext)++;
|
||||
newoptarg = p;
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
if (*optnext != NULL)
|
||||
ind = *optnext - optfirst + 1;
|
||||
*optptr = p;
|
||||
if (newoptarg != NULL)
|
||||
err |= setvarsafe("OPTARG", newoptarg, 0);
|
||||
else {
|
||||
INTOFF;
|
||||
err |= unsetvar("OPTARG");
|
||||
INTON;
|
||||
}
|
||||
fmtstr(s, sizeof(s), "%d", ind);
|
||||
err |= setvarsafe("OPTIND", s, VNOFUNC);
|
||||
s[0] = c;
|
||||
s[1] = '\0';
|
||||
err |= setvarsafe(optvar, s, 0);
|
||||
if (err) {
|
||||
*optnext = NULL;
|
||||
*optptr = NULL;
|
||||
flushall();
|
||||
exraise(EXERROR);
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard option processing (a la getopt) for builtin routines. The
|
||||
* only argument that is passed to nextopt is the option string; the
|
||||
* other arguments are unnecessary. It returns the option, or '\0' on
|
||||
* end of input.
|
||||
*/
|
||||
|
||||
int
|
||||
nextopt(const char *optstring)
|
||||
{
|
||||
char *p;
|
||||
const char *q;
|
||||
char c;
|
||||
|
||||
if ((p = nextopt_optptr) == NULL || *p == '\0') {
|
||||
p = *argptr;
|
||||
if (p == NULL || *p != '-' || *++p == '\0')
|
||||
return '\0';
|
||||
argptr++;
|
||||
if (p[0] == '-' && p[1] == '\0') /* check for "--" */
|
||||
return '\0';
|
||||
}
|
||||
c = *p++;
|
||||
for (q = optstring ; *q != c ; ) {
|
||||
if (*q == '\0')
|
||||
error("Illegal option -%c", c);
|
||||
if (*++q == ':')
|
||||
q++;
|
||||
}
|
||||
if (*++q == ':') {
|
||||
if (*p == '\0' && (p = *argptr++) == NULL)
|
||||
error("No arg for -%c option", c);
|
||||
shoptarg = p;
|
||||
p = NULL;
|
||||
}
|
||||
nextopt_optptr = p;
|
||||
return c;
|
||||
}
|
115
sh/options.h
Normal file
115
sh/options.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)options.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/options.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
struct shparam {
|
||||
int nparam; /* # of positional parameters (without $0) */
|
||||
unsigned char malloc; /* if parameter list dynamically allocated */
|
||||
unsigned char reset; /* if getopts has been reset */
|
||||
char **p; /* parameter list */
|
||||
char **optp; /* parameter list for getopts */
|
||||
char **optnext; /* next parameter to be processed by getopts */
|
||||
char *optptr; /* used by getopts */
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define eflag optval[0]
|
||||
#define fflag optval[1]
|
||||
#define Iflag optval[2]
|
||||
#define iflag optval[3]
|
||||
#define mflag optval[4]
|
||||
#define nflag optval[5]
|
||||
#define sflag optval[6]
|
||||
#define xflag optval[7]
|
||||
#define vflag optval[8]
|
||||
#define Vflag optval[9]
|
||||
#define Eflag optval[10]
|
||||
#define Cflag optval[11]
|
||||
#define aflag optval[12]
|
||||
#define bflag optval[13]
|
||||
#define uflag optval[14]
|
||||
#define privileged optval[15]
|
||||
#define Tflag optval[16]
|
||||
#define Pflag optval[17]
|
||||
#define hflag optval[18]
|
||||
#define nologflag optval[19]
|
||||
|
||||
#define NSHORTOPTS 19
|
||||
#define NOPTS 20
|
||||
|
||||
extern char optval[NOPTS];
|
||||
extern const char optletter[NSHORTOPTS];
|
||||
#ifdef DEFINE_OPTIONS
|
||||
char optval[NOPTS];
|
||||
const char optletter[NSHORTOPTS] = "efIimnsxvVECabupTPh";
|
||||
static const unsigned char optname[] =
|
||||
"\007errexit"
|
||||
"\006noglob"
|
||||
"\011ignoreeof"
|
||||
"\013interactive"
|
||||
"\007monitor"
|
||||
"\006noexec"
|
||||
"\005stdin"
|
||||
"\006xtrace"
|
||||
"\007verbose"
|
||||
"\002vi"
|
||||
"\005emacs"
|
||||
"\011noclobber"
|
||||
"\011allexport"
|
||||
"\006notify"
|
||||
"\007nounset"
|
||||
"\012privileged"
|
||||
"\012trapsasync"
|
||||
"\010physical"
|
||||
"\010trackall"
|
||||
"\005nolog"
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
extern char *minusc; /* argument to -c option */
|
||||
extern char *arg0; /* $0 */
|
||||
extern struct shparam shellparam; /* $@ */
|
||||
extern char **argptr; /* argument list for builtin commands */
|
||||
extern char *shoptarg; /* set by nextopt */
|
||||
extern char *nextopt_optptr; /* used by nextopt */
|
||||
|
||||
void procargs(int, char **);
|
||||
void optschanged(void);
|
||||
void freeparam(struct shparam *);
|
||||
int nextopt(const char *);
|
||||
void getoptsreset(const char *);
|
370
sh/output.c
Normal file
370
sh/output.c
Normal file
@ -0,0 +1,370 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/output.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
/*
|
||||
* Shell output routines. We use our own output routines because:
|
||||
* When a builtin command is interrupted we have to discard
|
||||
* any pending output.
|
||||
* When a builtin command appears in back quotes, we want to
|
||||
* save the output of the command in a region obtained
|
||||
* via malloc, rather than doing a fork and reading the
|
||||
* output of the command via a pipe.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* defines BUFSIZ */
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "syntax.h"
|
||||
#include "output.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "var.h"
|
||||
|
||||
|
||||
#define OUTBUFSIZ BUFSIZ
|
||||
#define MEM_OUT -2 /* output to dynamically allocated memory */
|
||||
#define OUTPUT_ERR 01 /* error occurred on output */
|
||||
|
||||
static int doformat_wr(void *, const char *, int);
|
||||
|
||||
struct output output = {NULL, NULL, NULL, OUTBUFSIZ, 1, 0};
|
||||
struct output errout = {NULL, NULL, NULL, 256, 2, 0};
|
||||
struct output memout = {NULL, NULL, NULL, 64, MEM_OUT, 0};
|
||||
struct output *out1 = &output;
|
||||
struct output *out2 = &errout;
|
||||
|
||||
void
|
||||
outcslow(int c, struct output *file)
|
||||
{
|
||||
outc(c, file);
|
||||
}
|
||||
|
||||
void
|
||||
out1str(const char *p)
|
||||
{
|
||||
outstr(p, out1);
|
||||
}
|
||||
|
||||
void
|
||||
out1qstr(const char *p)
|
||||
{
|
||||
outqstr(p, out1);
|
||||
}
|
||||
|
||||
void
|
||||
out2str(const char *p)
|
||||
{
|
||||
outstr(p, out2);
|
||||
}
|
||||
|
||||
void
|
||||
out2qstr(const char *p)
|
||||
{
|
||||
outqstr(p, out2);
|
||||
}
|
||||
|
||||
void
|
||||
outstr(const char *p, struct output *file)
|
||||
{
|
||||
outbin(p, strlen(p), file);
|
||||
}
|
||||
|
||||
static void
|
||||
byteseq(int ch, struct output *file)
|
||||
{
|
||||
char seq[4];
|
||||
|
||||
seq[0] = '\\';
|
||||
seq[1] = (ch >> 6 & 0x3) + '0';
|
||||
seq[2] = (ch >> 3 & 0x7) + '0';
|
||||
seq[3] = (ch & 0x7) + '0';
|
||||
outbin(seq, 4, file);
|
||||
}
|
||||
|
||||
static void
|
||||
outdqstr(const char *p, struct output *file)
|
||||
{
|
||||
const char *end;
|
||||
mbstate_t mbs;
|
||||
size_t clen;
|
||||
wchar_t wc;
|
||||
|
||||
memset(&mbs, '\0', sizeof(mbs));
|
||||
end = p + strlen(p);
|
||||
outstr("$'", file);
|
||||
while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
|
||||
if (clen == (size_t)-2) {
|
||||
while (p < end)
|
||||
byteseq(*p++, file);
|
||||
break;
|
||||
}
|
||||
if (clen == (size_t)-1) {
|
||||
memset(&mbs, '\0', sizeof(mbs));
|
||||
byteseq(*p++, file);
|
||||
continue;
|
||||
}
|
||||
if (wc == L'\n')
|
||||
outcslow('\n', file), p++;
|
||||
else if (wc == L'\r')
|
||||
outstr("\\r", file), p++;
|
||||
else if (wc == L'\t')
|
||||
outstr("\\t", file), p++;
|
||||
else if (!iswprint(wc)) {
|
||||
for (; clen > 0; clen--)
|
||||
byteseq(*p++, file);
|
||||
} else {
|
||||
if (wc == L'\'' || wc == L'\\')
|
||||
outcslow('\\', file);
|
||||
outbin(p, clen, file);
|
||||
p += clen;
|
||||
}
|
||||
}
|
||||
outcslow('\'', file);
|
||||
}
|
||||
|
||||
/* Like outstr(), but quote for re-input into the shell. */
|
||||
void
|
||||
outqstr(const char *p, struct output *file)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (p[0] == '\0') {
|
||||
outstr("''", file);
|
||||
return;
|
||||
}
|
||||
for (i = 0; p[i] != '\0'; i++) {
|
||||
if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') ||
|
||||
(p[i] & 0x80) != 0 || p[i] == '\'') {
|
||||
outdqstr(p, file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' ||
|
||||
strcmp(p, "[") == 0) {
|
||||
outstr(p, file);
|
||||
return;
|
||||
}
|
||||
|
||||
outcslow('\'', file);
|
||||
outstr(p, file);
|
||||
outcslow('\'', file);
|
||||
}
|
||||
|
||||
void
|
||||
outbin(const void *data, size_t len, struct output *file)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
p = data;
|
||||
while (len-- > 0)
|
||||
outc(*p++, file);
|
||||
}
|
||||
|
||||
void
|
||||
emptyoutbuf(struct output *dest)
|
||||
{
|
||||
int offset, newsize;
|
||||
|
||||
if (dest->buf == NULL) {
|
||||
INTOFF;
|
||||
dest->buf = ckmalloc(dest->bufsize);
|
||||
dest->nextc = dest->buf;
|
||||
dest->bufend = dest->buf + dest->bufsize;
|
||||
INTON;
|
||||
} else if (dest->fd == MEM_OUT) {
|
||||
offset = dest->nextc - dest->buf;
|
||||
newsize = dest->bufsize << 1;
|
||||
INTOFF;
|
||||
dest->buf = ckrealloc(dest->buf, newsize);
|
||||
dest->bufsize = newsize;
|
||||
dest->bufend = dest->buf + newsize;
|
||||
dest->nextc = dest->buf + offset;
|
||||
INTON;
|
||||
} else {
|
||||
flushout(dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
flushall(void)
|
||||
{
|
||||
flushout(&output);
|
||||
flushout(&errout);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
flushout(struct output *dest)
|
||||
{
|
||||
|
||||
if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
|
||||
return;
|
||||
if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
|
||||
dest->flags |= OUTPUT_ERR;
|
||||
dest->nextc = dest->buf;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
freestdout(void)
|
||||
{
|
||||
output.nextc = output.buf;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
outiserror(struct output *file)
|
||||
{
|
||||
return (file->flags & OUTPUT_ERR);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
outclearerror(struct output *file)
|
||||
{
|
||||
file->flags &= ~OUTPUT_ERR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
outfmt(struct output *file, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
doformat(file, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
out1fmt(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
doformat(out1, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
out2fmt_flush(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
doformat(out2, fmt, ap);
|
||||
va_end(ap);
|
||||
flushout(out2);
|
||||
}
|
||||
|
||||
void
|
||||
fmtstr(char *outbuf, int length, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
INTOFF;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(outbuf, length, fmt, ap);
|
||||
va_end(ap);
|
||||
INTON;
|
||||
}
|
||||
|
||||
static int
|
||||
doformat_wr(void *cookie, const char *buf, int len)
|
||||
{
|
||||
struct output *o;
|
||||
|
||||
o = (struct output *)cookie;
|
||||
outbin(buf, len, o);
|
||||
|
||||
return (len);
|
||||
}
|
||||
|
||||
void
|
||||
doformat(struct output *dest, const char *f, va_list ap)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fwopen(dest, doformat_wr)) != NULL) {
|
||||
vfprintf(fp, f, ap);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Version of write which resumes after a signal is caught.
|
||||
*/
|
||||
|
||||
int
|
||||
xwrite(int fd, const char *buf, int nbytes)
|
||||
{
|
||||
int ntry;
|
||||
int i;
|
||||
int n;
|
||||
|
||||
n = nbytes;
|
||||
ntry = 0;
|
||||
for (;;) {
|
||||
i = write(fd, buf, n);
|
||||
if (i > 0) {
|
||||
if ((n -= i) <= 0)
|
||||
return nbytes;
|
||||
buf += i;
|
||||
ntry = 0;
|
||||
} else if (i == 0) {
|
||||
if (++ntry > 10)
|
||||
return nbytes - n;
|
||||
} else if (errno != EINTR) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
85
sh/output.h
Normal file
85
sh/output.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)output.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/output.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
#ifndef OUTPUT_INCL
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct output {
|
||||
char *nextc;
|
||||
char *bufend;
|
||||
char *buf;
|
||||
int bufsize;
|
||||
short fd;
|
||||
short flags;
|
||||
};
|
||||
|
||||
extern struct output output; /* to fd 1 */
|
||||
extern struct output errout; /* to fd 2 */
|
||||
extern struct output memout;
|
||||
extern struct output *out1; /* &memout if backquote, otherwise &output */
|
||||
extern struct output *out2; /* &memout if backquote with 2>&1, otherwise
|
||||
&errout */
|
||||
|
||||
void outcslow(int, struct output *);
|
||||
void out1str(const char *);
|
||||
void out1qstr(const char *);
|
||||
void out2str(const char *);
|
||||
void out2qstr(const char *);
|
||||
void outstr(const char *, struct output *);
|
||||
void outqstr(const char *, struct output *);
|
||||
void outbin(const void *, size_t, struct output *);
|
||||
void emptyoutbuf(struct output *);
|
||||
void flushall(void);
|
||||
void flushout(struct output *);
|
||||
void freestdout(void);
|
||||
int outiserror(struct output *);
|
||||
void outclearerror(struct output *);
|
||||
void outfmt(struct output *, const char *, ...) __printflike(2, 3);
|
||||
void out1fmt(const char *, ...) __printflike(1, 2);
|
||||
void out2fmt_flush(const char *, ...) __printflike(1, 2);
|
||||
void fmtstr(char *, int, const char *, ...) __printflike(3, 4);
|
||||
void doformat(struct output *, const char *, va_list) __printflike(2, 0);
|
||||
int xwrite(int, const char *, int);
|
||||
|
||||
#define outc(c, file) ((file)->nextc == (file)->bufend ? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c)))
|
||||
#define out1c(c) outc(c, out1);
|
||||
#define out2c(c) outcslow(c, out2);
|
||||
|
||||
#define OUTPUT_INCL
|
||||
#endif
|
2120
sh/parser.c
Normal file
2120
sh/parser.c
Normal file
File diff suppressed because it is too large
Load Diff
87
sh/parser.h
Normal file
87
sh/parser.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)parser.h 8.3 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/parser.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
/* control characters in argument strings */
|
||||
#define CTLESC '\300'
|
||||
#define CTLVAR '\301'
|
||||
#define CTLENDVAR '\371'
|
||||
#define CTLBACKQ '\372'
|
||||
#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
|
||||
/* CTLBACKQ | CTLQUOTE == '\373' */
|
||||
#define CTLARI '\374'
|
||||
#define CTLENDARI '\375'
|
||||
#define CTLQUOTEMARK '\376'
|
||||
#define CTLQUOTEEND '\377' /* only for ${v+-...} */
|
||||
|
||||
/* variable substitution byte (follows CTLVAR) */
|
||||
#define VSTYPE 0x0f /* type of variable substitution */
|
||||
#define VSNUL 0x10 /* colon--treat the empty string as unset */
|
||||
#define VSLINENO 0x20 /* expansion of $LINENO, the line number \
|
||||
follows immediately */
|
||||
#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
|
||||
|
||||
/* values of VSTYPE field */
|
||||
#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
|
||||
#define VSMINUS 0x2 /* ${var-text} */
|
||||
#define VSPLUS 0x3 /* ${var+text} */
|
||||
#define VSQUESTION 0x4 /* ${var?message} */
|
||||
#define VSASSIGN 0x5 /* ${var=text} */
|
||||
#define VSTRIMLEFT 0x6 /* ${var#pattern} */
|
||||
#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */
|
||||
#define VSTRIMRIGHT 0x8 /* ${var%pattern} */
|
||||
#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */
|
||||
#define VSLENGTH 0xa /* ${#var} */
|
||||
#define VSERROR 0xb /* Syntax error, issue when expanded */
|
||||
|
||||
|
||||
/*
|
||||
* NEOF is returned by parsecmd when it encounters an end of file. It
|
||||
* must be distinct from NULL.
|
||||
*/
|
||||
#define NEOF ((union node *)-1)
|
||||
extern int whichprompt; /* 1 == PS1, 2 == PS2 */
|
||||
extern const char *const parsekwd[];
|
||||
|
||||
|
||||
union node *parsecmd(int);
|
||||
union node *parsewordexp(void);
|
||||
void forcealias(void);
|
||||
void fixredir(union node *, const char *, int);
|
||||
int goodname(const char *);
|
||||
int isassignment(const char *);
|
||||
char *getprompt(void *);
|
||||
const char *expandstr(const char *);
|
365
sh/redir.c
Normal file
365
sh/redir.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95";
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD: head/bin/sh/redir.c 326025 2017-11-20 19:49:47Z pfg $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Code for dealing with input/output redirection.
|
||||
*/
|
||||
|
||||
#include "shell.h"
|
||||
#include "nodes.h"
|
||||
#include "jobs.h"
|
||||
#include "expand.h"
|
||||
#include "redir.h"
|
||||
#include "output.h"
|
||||
#include "memalloc.h"
|
||||
#include "error.h"
|
||||
#include "options.h"
|
||||
|
||||
|
||||
#define EMPTY -2 /* marks an unused slot in redirtab */
|
||||
#define CLOSED -1 /* fd was not open before redir */
|
||||
|
||||
|
||||
struct redirtab {
|
||||
struct redirtab *next;
|
||||
int renamed[10];
|
||||
int fd0_redirected;
|
||||
unsigned int empty_redirs;
|
||||
};
|
||||
|
||||
|
||||
static struct redirtab *redirlist;
|
||||
|
||||
/*
|
||||
* We keep track of whether or not fd0 has been redirected. This is for
|
||||
* background commands, where we want to redirect fd0 to /dev/null only
|
||||
* if it hasn't already been redirected.
|
||||
*/
|
||||
static int fd0_redirected = 0;
|
||||
|
||||
/* Number of redirtabs that have not been allocated. */
|
||||
static unsigned int empty_redirs = 0;
|
||||
|
||||
static void openredirect(union node *, char[10 ]);
|
||||
static int openhere(union node *);
|
||||
|
||||
|
||||
/*
|
||||
* Process a list of redirection commands. If the REDIR_PUSH flag is set,
|
||||
* old file descriptors are stashed away so that the redirection can be
|
||||
* undone by calling popredir. If the REDIR_BACKQ flag is set, then the
|
||||
* standard output, and the standard error if it becomes a duplicate of
|
||||
* stdout, is saved in memory.
|
||||
*
|
||||
* We suppress interrupts so that we won't leave open file
|
||||
* descriptors around. Because the signal handler remains
|
||||
* installed and we do not use system call restart, interrupts
|
||||
* will still abort blocking opens such as fifos (they will fail
|
||||
* with EINTR). There is, however, a race condition if an interrupt
|
||||
* arrives after INTOFF and before open blocks.
|
||||
*/
|
||||
|
||||
void
|
||||
redirect(union node *redir, int flags)
|
||||
{
|
||||
union node *n;
|
||||
struct redirtab *sv = NULL;
|
||||
int i;
|
||||
int fd;
|
||||
char memory[10]; /* file descriptors to write to memory */
|
||||
|
||||
INTOFF;
|
||||
for (i = 10 ; --i >= 0 ; )
|
||||
memory[i] = 0;
|
||||
memory[1] = flags & REDIR_BACKQ;
|
||||
if (flags & REDIR_PUSH) {
|
||||
empty_redirs++;
|
||||
if (redir != NULL) {
|
||||
sv = ckmalloc(sizeof (struct redirtab));
|
||||
for (i = 0 ; i < 10 ; i++)
|
||||
sv->renamed[i] = EMPTY;
|
||||
sv->fd0_redirected = fd0_redirected;
|
||||
sv->empty_redirs = empty_redirs - 1;
|
||||
sv->next = redirlist;
|
||||
redirlist = sv;
|
||||
empty_redirs = 0;
|
||||
}
|
||||
}
|
||||
for (n = redir ; n ; n = n->nfile.next) {
|
||||
fd = n->nfile.fd;
|
||||
if (fd == 0)
|
||||
fd0_redirected = 1;
|
||||
if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
|
||||
n->ndup.dupfd == fd)
|
||||
continue; /* redirect from/to same file descriptor */
|
||||
|
||||
if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
|
||||
INTOFF;
|
||||
if ((i = fcntl(fd, F_DUPFD_CLOEXEC, 10)) == -1) {
|
||||
switch (errno) {
|
||||
case EBADF:
|
||||
i = CLOSED;
|
||||
break;
|
||||
default:
|
||||
INTON;
|
||||
error("%d: %s", fd, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
sv->renamed[fd] = i;
|
||||
INTON;
|
||||
}
|
||||
openredirect(n, memory);
|
||||
INTON;
|
||||
INTOFF;
|
||||
}
|
||||
if (memory[1])
|
||||
out1 = &memout;
|
||||
if (memory[2])
|
||||
out2 = &memout;
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
openredirect(union node *redir, char memory[10])
|
||||
{
|
||||
struct stat sb;
|
||||
int fd = redir->nfile.fd;
|
||||
const char *fname;
|
||||
int f;
|
||||
int e;
|
||||
|
||||
memory[fd] = 0;
|
||||
switch (redir->nfile.type) {
|
||||
case NFROM:
|
||||
fname = redir->nfile.expfname;
|
||||
if ((f = open(fname, O_RDONLY)) < 0)
|
||||
error("cannot open %s: %s", fname, strerror(errno));
|
||||
break;
|
||||
case NFROMTO:
|
||||
fname = redir->nfile.expfname;
|
||||
if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
|
||||
error("cannot create %s: %s", fname, strerror(errno));
|
||||
break;
|
||||
case NTO:
|
||||
if (Cflag) {
|
||||
fname = redir->nfile.expfname;
|
||||
if (stat(fname, &sb) == -1) {
|
||||
if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
|
||||
error("cannot create %s: %s", fname, strerror(errno));
|
||||
} else if (!S_ISREG(sb.st_mode)) {
|
||||
if ((f = open(fname, O_WRONLY, 0666)) < 0)
|
||||
error("cannot create %s: %s", fname, strerror(errno));
|
||||
if (fstat(f, &sb) != -1 && S_ISREG(sb.st_mode)) {
|
||||
close(f);
|
||||
error("cannot create %s: %s", fname,
|
||||
strerror(EEXIST));
|
||||
}
|
||||
} else
|
||||
error("cannot create %s: %s", fname,
|
||||
strerror(EEXIST));
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case NCLOBBER:
|
||||
fname = redir->nfile.expfname;
|
||||
if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
|
||||
error("cannot create %s: %s", fname, strerror(errno));
|
||||
break;
|
||||
case NAPPEND:
|
||||
fname = redir->nfile.expfname;
|
||||
if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
|
||||
error("cannot create %s: %s", fname, strerror(errno));
|
||||
break;
|
||||
case NTOFD:
|
||||
case NFROMFD:
|
||||
if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
|
||||
if (memory[redir->ndup.dupfd])
|
||||
memory[fd] = 1;
|
||||
else {
|
||||
if (dup2(redir->ndup.dupfd, fd) < 0)
|
||||
error("%d: %s", redir->ndup.dupfd,
|
||||
strerror(errno));
|
||||
}
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
return;
|
||||
case NHERE:
|
||||
case NXHERE:
|
||||
f = openhere(redir);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (f != fd) {
|
||||
if (dup2(f, fd) == -1) {
|
||||
e = errno;
|
||||
close(f);
|
||||
error("%d: %s", fd, strerror(e));
|
||||
}
|
||||
close(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle here documents. Normally we fork off a process to write the
|
||||
* data to a pipe. If the document is short, we can stuff the data in
|
||||
* the pipe without forking.
|
||||
*/
|
||||
|
||||
static int
|
||||
openhere(union node *redir)
|
||||
{
|
||||
const char *p;
|
||||
int pip[2];
|
||||
size_t len = 0;
|
||||
int flags;
|
||||
ssize_t written = 0;
|
||||
|
||||
if (pipe(pip) < 0)
|
||||
error("Pipe call failed: %s", strerror(errno));
|
||||
|
||||
if (redir->type == NXHERE)
|
||||
p = redir->nhere.expdoc;
|
||||
else
|
||||
p = redir->nhere.doc->narg.text;
|
||||
len = strlen(p);
|
||||
if (len == 0)
|
||||
goto out;
|
||||
flags = fcntl(pip[1], F_GETFL, 0);
|
||||
if (flags != -1 && fcntl(pip[1], F_SETFL, flags | O_NONBLOCK) != -1) {
|
||||
written = write(pip[1], p, len);
|
||||
if (written < 0)
|
||||
written = 0;
|
||||
if ((size_t)written == len)
|
||||
goto out;
|
||||
fcntl(pip[1], F_SETFL, flags);
|
||||
}
|
||||
|
||||
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
|
||||
close(pip[0]);
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
xwrite(pip[1], p + written, len - written);
|
||||
_exit(0);
|
||||
}
|
||||
out:
|
||||
close(pip[1]);
|
||||
return pip[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Undo the effects of the last redirection.
|
||||
*/
|
||||
|
||||
void
|
||||
popredir(void)
|
||||
{
|
||||
struct redirtab *rp = redirlist;
|
||||
int i;
|
||||
|
||||
INTOFF;
|
||||
if (empty_redirs > 0) {
|
||||
empty_redirs--;
|
||||
INTON;
|
||||
return;
|
||||
}
|
||||
for (i = 0 ; i < 10 ; i++) {
|
||||
if (rp->renamed[i] != EMPTY) {
|
||||
if (rp->renamed[i] >= 0) {
|
||||
dup2(rp->renamed[i], i);
|
||||
close(rp->renamed[i]);
|
||||
} else {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
fd0_redirected = rp->fd0_redirected;
|
||||
empty_redirs = rp->empty_redirs;
|
||||
redirlist = rp->next;
|
||||
ckfree(rp);
|
||||
INTON;
|
||||
}
|
||||
|
||||
/* Return true if fd 0 has already been redirected at least once. */
|
||||
int
|
||||
fd0_redirected_p(void)
|
||||
{
|
||||
return fd0_redirected != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Discard all saved file descriptors.
|
||||
*/
|
||||
|
||||
void
|
||||
clearredir(void)
|
||||
{
|
||||
struct redirtab *rp;
|
||||
int i;
|
||||
|
||||
for (rp = redirlist ; rp ; rp = rp->next) {
|
||||
for (i = 0 ; i < 10 ; i++) {
|
||||
if (rp->renamed[i] >= 0) {
|
||||
close(rp->renamed[i]);
|
||||
}
|
||||
rp->renamed[i] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
47
sh/redir.h
Normal file
47
sh/redir.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Kenneth Almquist.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)redir.h 8.2 (Berkeley) 5/4/95
|
||||
* $FreeBSD: head/bin/sh/redir.h 326025 2017-11-20 19:49:47Z pfg $
|
||||
*/
|
||||
|
||||
/* flags passed to redirect */
|
||||
#define REDIR_PUSH 01 /* save previous values of file descriptors */
|
||||
#define REDIR_BACKQ 02 /* save the command output in memory */
|
||||
|
||||
union node;
|
||||
void redirect(union node *, int);
|
||||
void popredir(void);
|
||||
int fd0_redirected_p(void);
|
||||
void clearredir(void);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user