Compare commits
11 Commits
0f348c192b
...
2ace33176c
Author | SHA1 | Date |
---|---|---|
Jonas 'Sortie' Termansen | 2ace33176c | |
Jonas 'Sortie' Termansen | 159415c3b1 | |
Jonas 'Sortie' Termansen | c6af3bc074 | |
Jonas 'Sortie' Termansen | 3c69791078 | |
Jonas 'Sortie' Termansen | f4152b3863 | |
Jonas 'Sortie' Termansen | 6bab3819e2 | |
Juhani Krekelä | 212539c9de | |
Juhani Krekelä | cc9c031e5e | |
Jonas 'Sortie' Termansen | a8c05711aa | |
Jonas 'Sortie' Termansen | aefec2f7cd | |
Jonas 'Sortie' Termansen | 07dd21146b |
|
@ -440,7 +440,8 @@ static bool log_open(struct log* log)
|
|||
log->fd = open(log->path, logflags, log->file_mode);
|
||||
if ( log->fd < 0 )
|
||||
{
|
||||
log_error(log, "", NULL);
|
||||
if ( errno != EROFS )
|
||||
log_error(log, "", NULL);
|
||||
// Don't block daemon startup on read-only filesystems.
|
||||
return errno == EROFS;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2014, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2012, 2014, 2016, 2017, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -238,6 +238,7 @@ bool BGADevice::Initialize()
|
|||
Video::ConfigureDevice(this);
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
if ( guest_additions )
|
||||
guest_additions->ReadyVideoDevice(device_index);
|
||||
#endif
|
||||
|
@ -609,24 +610,24 @@ static void TryInitializeDevice(uint32_t devaddr)
|
|||
}
|
||||
}
|
||||
|
||||
static bool OnDevice(uint32_t devaddr, const pciid_t*, const pcitype_t*, void*,
|
||||
void*)
|
||||
{
|
||||
TryInitializeDevice(devaddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
pcifind_t bga_pcifind;
|
||||
memset(&bga_pcifind, 255, sizeof(bga_pcifind));
|
||||
bga_pcifind.vendorid = 0x1234;
|
||||
bga_pcifind.deviceid = 0x1111;
|
||||
pcifind_t patterns[2];
|
||||
memset(&patterns[0], 255, sizeof(patterns[0]));
|
||||
patterns[0].vendorid = 0x1234;
|
||||
patterns[0].deviceid = 0x1111;
|
||||
memset(&patterns[1], 255, sizeof(patterns[1]));
|
||||
patterns[1].vendorid = 0x80EE;
|
||||
patterns[1].deviceid = 0xBEEF;
|
||||
|
||||
uint32_t devaddr = 0;
|
||||
while ( (devaddr = PCI::SearchForDevices(bga_pcifind, devaddr)) )
|
||||
TryInitializeDevice(devaddr);
|
||||
|
||||
memset(&bga_pcifind, 255, sizeof(bga_pcifind));
|
||||
bga_pcifind.vendorid = 0x80EE;
|
||||
bga_pcifind.deviceid = 0xBEEF;
|
||||
|
||||
devaddr = 0;
|
||||
while ( (devaddr = PCI::SearchForDevices(bga_pcifind, devaddr)) )
|
||||
TryInitializeDevice(devaddr);
|
||||
PCI::Search(OnDevice, NULL, patterns, 2);
|
||||
}
|
||||
|
||||
} // namespace BGA
|
||||
|
|
|
@ -214,13 +214,16 @@ bool login(const char* username)
|
|||
int pipe_fds[2];
|
||||
if ( pipe2(pipe_fds, O_CLOEXEC) < 0 )
|
||||
return false;
|
||||
char* login_shell;
|
||||
if ( asprintf(&login_shell, "-%s", pwd->pw_shell) < 0 )
|
||||
return close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
||||
pid_t child_pid = fork();
|
||||
if ( child_pid < 0 )
|
||||
return close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
return free(login_shell), close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
if ( child_pid == 0 )
|
||||
{
|
||||
sigdelset(&oldset, SIGINT);
|
||||
|
@ -253,10 +256,11 @@ bool login(const char* username)
|
|||
errno != ENOENT && errno != EACCES) ||
|
||||
(execlp("/etc/session", "/etc/session", (const char*) NULL) < 0 &&
|
||||
errno != ENOENT && errno != EACCES) ||
|
||||
execlp(pwd->pw_shell, pwd->pw_shell, (const char*) NULL));
|
||||
execlp(pwd->pw_shell, login_shell, (const char*) NULL));
|
||||
write(pipe_fds[1], &errno, sizeof(errno));
|
||||
_exit(127);
|
||||
}
|
||||
free(login_shell);
|
||||
close(pipe_fds[1]);
|
||||
int errnum;
|
||||
if ( readall(pipe_fds[0], &errnum, sizeof(errnum)) < (ssize_t) sizeof(errnum) )
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
diff -Paur --no-dereference -- dash.upstream/src/cd.c dash/src/cd.c
|
||||
--- dash.upstream/src/cd.c
|
||||
+++ dash/src/cd.c
|
||||
@@ -252,7 +252,7 @@
|
||||
@@ -268,7 +268,7 @@
|
||||
STATIC char *
|
||||
getpwd()
|
||||
{
|
||||
|
@ -30,7 +30,24 @@ diff -Paur --no-dereference -- dash.upstream/src/exec.c dash/src/exec.c
|
|||
+#endif
|
||||
}
|
||||
|
||||
|
||||
static const char *legal_pathopt(const char *opt, const char *term, int magic)
|
||||
diff -Paur --no-dereference -- dash.upstream/src/expand.c dash/src/expand.c
|
||||
--- dash.upstream/src/expand.c
|
||||
+++ dash/src/expand.c
|
||||
@@ -44,6 +44,13 @@
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
+/* PATCH: Sortix does not currently have maximum filename or path length */
|
||||
+#ifndef PATH_MAX
|
||||
+#define PATH_MAX 4096
|
||||
+#endif
|
||||
+#ifndef NAME_MAX
|
||||
+#define NAME_MAX 255
|
||||
+#endif
|
||||
#include <string.h>
|
||||
#ifdef HAVE_FNMATCH
|
||||
#include <fnmatch.h>
|
||||
diff -Paur --no-dereference -- dash.upstream/src/histedit.c dash/src/histedit.c
|
||||
--- dash.upstream/src/histedit.c
|
||||
+++ dash/src/histedit.c
|
||||
|
@ -53,7 +70,7 @@ diff -Paur --no-dereference -- dash.upstream/src/jobs.c dash/src/jobs.c
|
|||
#ifdef BSD
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
@@ -207,7 +206,7 @@
|
||||
@@ -212,7 +211,7 @@
|
||||
mflag = on = 0;
|
||||
goto close;
|
||||
}
|
||||
|
@ -62,7 +79,7 @@ diff -Paur --no-dereference -- dash.upstream/src/jobs.c dash/src/jobs.c
|
|||
break;
|
||||
killpg(0, SIGTTIN);
|
||||
} while (1);
|
||||
@@ -457,7 +456,7 @@
|
||||
@@ -461,7 +460,7 @@
|
||||
|
||||
if (mode & SHOW_PGID) {
|
||||
/* just output process (group) id of pipeline */
|
||||
|
@ -71,7 +88,7 @@ diff -Paur --no-dereference -- dash.upstream/src/jobs.c dash/src/jobs.c
|
|||
return;
|
||||
}
|
||||
|
||||
@@ -470,7 +469,7 @@
|
||||
@@ -474,7 +473,7 @@
|
||||
s[col - 2] = '-';
|
||||
|
||||
if (mode & SHOW_PID)
|
||||
|
@ -80,7 +97,7 @@ diff -Paur --no-dereference -- dash.upstream/src/jobs.c dash/src/jobs.c
|
|||
|
||||
psend = ps + jp->nprocs;
|
||||
|
||||
@@ -490,7 +489,7 @@
|
||||
@@ -494,7 +493,7 @@
|
||||
|
||||
do {
|
||||
/* for each process */
|
||||
|
@ -89,43 +106,57 @@ diff -Paur --no-dereference -- dash.upstream/src/jobs.c dash/src/jobs.c
|
|||
|
||||
start:
|
||||
outfmt(
|
||||
@@ -1136,7 +1135,7 @@
|
||||
@@ -971,7 +970,7 @@
|
||||
sigblockall(NULL);
|
||||
vforked++;
|
||||
|
||||
- pid = vfork();
|
||||
+ pid = fork();
|
||||
|
||||
if (!pid) {
|
||||
forkchild(jp, n, FORK_FG);
|
||||
@@ -1179,7 +1178,7 @@
|
||||
do {
|
||||
gotsigchld = 0;
|
||||
- err = wait3(status, flags, NULL);
|
||||
+ err = waitpid(-1, status, flags);
|
||||
if (err || !block)
|
||||
break;
|
||||
do
|
||||
- err = wait3(status, flags, NULL);
|
||||
+ err = waitpid(-1, status, flags);
|
||||
while (err < 0 && errno == EINTR);
|
||||
|
||||
if (err || (err = -!block))
|
||||
diff -Paur --no-dereference -- dash.upstream/src/Makefile.in dash/src/Makefile.in
|
||||
--- dash.upstream/src/Makefile.in
|
||||
+++ dash/src/Makefile.in
|
||||
@@ -170,9 +170,9 @@
|
||||
AM_CFLAGS = $(COMMON_CFLAGS)
|
||||
AM_CPPFLAGS = $(COMMON_CPPFLAGS)
|
||||
AM_CFLAGS_FOR_BUILD = -g -O2 $(COMMON_CFLAGS)
|
||||
-AM_CPPFLAGS_FOR_BUILD = $(COMMON_CPPFLAGS)
|
||||
+AM_CPPFLAGS_FOR_BUILD = -DBSD=1 -DSHELL -DIFS_BROKEN
|
||||
COMPILE_FOR_BUILD = \
|
||||
- $(CC_FOR_BUILD) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS_FOR_BUILD) \
|
||||
+ unset HOST_SYSTEM_ROOT && $(CC_FOR_BUILD) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS_FOR_BUILD) \
|
||||
$(CPPFLAGS_FOR_BUILD) \
|
||||
$(AM_CFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD)
|
||||
|
||||
@@ -542,7 +542,11 @@
|
||||
|
||||
info-am:
|
||||
|
||||
-install-data-am: install-man
|
||||
+install-data-am: install-man install-proper-shells
|
||||
+
|
||||
+install-proper-shells:
|
||||
+ mkdir -p "$(DESTDIR)$(sysconfdir)/proper-shells"
|
||||
+ echo dash > "$(DESTDIR)$(sysconfdir)/proper-shells/dash"
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
@@ -669,12 +669,14 @@
|
||||
-rm -f ./$(DEPDIR)/alias.Po
|
||||
-rm -f ./$(DEPDIR)/arith_yacc.Po
|
||||
-rm -f ./$(DEPDIR)/arith_yylex.Po
|
||||
+ -rm -f ./$(DEPDIR)/builtins.Po
|
||||
-rm -f ./$(DEPDIR)/cd.Po
|
||||
-rm -f ./$(DEPDIR)/error.Po
|
||||
-rm -f ./$(DEPDIR)/eval.Po
|
||||
-rm -f ./$(DEPDIR)/exec.Po
|
||||
-rm -f ./$(DEPDIR)/expand.Po
|
||||
-rm -f ./$(DEPDIR)/histedit.Po
|
||||
+ -rm -f ./$(DEPDIR)/init.Po
|
||||
-rm -f ./$(DEPDIR)/input.Po
|
||||
-rm -f ./$(DEPDIR)/jobs.Po
|
||||
-rm -f ./$(DEPDIR)/mail.Po
|
||||
@@ -682,11 +684,14 @@
|
||||
-rm -f ./$(DEPDIR)/memalloc.Po
|
||||
-rm -f ./$(DEPDIR)/miscbltin.Po
|
||||
-rm -f ./$(DEPDIR)/mystring.Po
|
||||
+ -rm -f ./$(DEPDIR)/nodes.Po
|
||||
-rm -f ./$(DEPDIR)/options.Po
|
||||
-rm -f ./$(DEPDIR)/output.Po
|
||||
-rm -f ./$(DEPDIR)/parser.Po
|
||||
-rm -f ./$(DEPDIR)/redir.Po
|
||||
-rm -f ./$(DEPDIR)/show.Po
|
||||
+ -rm -f ./$(DEPDIR)/signames.Po
|
||||
+ -rm -f ./$(DEPDIR)/syntax.Po
|
||||
-rm -f ./$(DEPDIR)/system.Po
|
||||
-rm -f ./$(DEPDIR)/trap.Po
|
||||
-rm -f ./$(DEPDIR)/var.Po
|
||||
diff -Paur --no-dereference -- dash.upstream/src/miscbltin.c dash/src/miscbltin.c
|
||||
--- dash.upstream/src/miscbltin.c
|
||||
+++ dash/src/miscbltin.c
|
||||
|
@ -151,18 +182,18 @@ diff -Paur --no-dereference -- dash.upstream/src/output.c dash/src/output.c
|
|||
diff -Paur --no-dereference -- dash.upstream/src/parser.c dash/src/parser.c
|
||||
--- dash.upstream/src/parser.c
|
||||
+++ dash/src/parser.c
|
||||
@@ -32,10 +32,6 @@
|
||||
@@ -32,10 +32,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
-#if HAVE_ALLOCA_H
|
||||
-#include <alloca.h>
|
||||
#include <alloca.h>
|
||||
-#endif
|
||||
-
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "shell.h"
|
||||
@@ -1090,10 +1086,12 @@
|
||||
@@ -1137,10 +1134,12 @@
|
||||
if (len) {
|
||||
char *str;
|
||||
|
||||
|
@ -176,7 +207,7 @@ diff -Paur --no-dereference -- dash.upstream/src/parser.c dash/src/parser.c
|
|||
}
|
||||
}
|
||||
}
|
||||
@@ -1300,7 +1298,7 @@
|
||||
@@ -1373,7 +1372,7 @@
|
||||
str = NULL;
|
||||
savelen = out - (char *)stackblock();
|
||||
if (savelen > 0) {
|
||||
|
@ -185,7 +216,7 @@ diff -Paur --no-dereference -- dash.upstream/src/parser.c dash/src/parser.c
|
|||
memcpy(str, stackblock(), savelen);
|
||||
}
|
||||
if (oldstyle) {
|
||||
@@ -1400,6 +1398,7 @@
|
||||
@@ -1463,6 +1462,7 @@
|
||||
if (str) {
|
||||
memcpy(out, str, savelen);
|
||||
STADJUST(savelen, out);
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
NAME=dash
|
||||
BUILD_LIBRARIES=
|
||||
VERSION=0.5.7
|
||||
VERSION=0.5.11.5
|
||||
DISTNAME=$NAME-$VERSION
|
||||
COMPRESSION=tar.gz
|
||||
ARCHIVE=$DISTNAME.$COMPRESSION
|
||||
SHA256SUM=ae89fa9f1145b7748cf0740e1df04cd52fdf8a285da4911dd0f04983efba4e39
|
||||
SHA256SUM=db778110891f7937985f29bf23410fe1c5d669502760f584e54e0e7b29e123bd
|
||||
UPSTREAM_SITE='http://gondor.apana.org.au/~herbert/dash/files'
|
||||
UPSTREAM_ARCHIVE=$ARCHIVE
|
||||
LICENSE=BSD-3-Clause
|
||||
BUILD_SYSTEM=configure
|
||||
MAKE_VARS='V=1'
|
||||
|
|
|
@ -26,7 +26,7 @@ diff -Paur --no-dereference -- libssl.upstream/apps/nc/netcat.c libssl/apps/nc/n
|
|||
unix_dg_tmp_socket = unix_dg_tmp_socket_buf;
|
||||
}
|
||||
}
|
||||
@@ -1405,7 +1412,8 @@
|
||||
@@ -1403,7 +1410,8 @@
|
||||
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
|
||||
|
@ -36,7 +36,7 @@ diff -Paur --no-dereference -- libssl.upstream/apps/nc/netcat.c libssl/apps/nc/n
|
|||
mh.msg_controllen = sizeof(cmsgbuf.buf);
|
||||
cmsg = CMSG_FIRSTHDR(&mh);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
@@ -1442,6 +1450,7 @@
|
||||
@@ -1440,6 +1448,7 @@
|
||||
void
|
||||
atelnet(int nfd, unsigned char *buf, unsigned int size)
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ diff -Paur --no-dereference -- libssl.upstream/apps/nc/netcat.c libssl/apps/nc/n
|
|||
unsigned char *p, *end;
|
||||
unsigned char obuf[4];
|
||||
|
||||
@@ -1467,6 +1476,9 @@
|
||||
@@ -1465,6 +1474,9 @@
|
||||
if (atomicio(vwrite, nfd, obuf, 3) != 3)
|
||||
warn("Write Error!");
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ diff -Paur --no-dereference -- libssl.upstream/apps/nc/netcat.c libssl/apps/nc/n
|
|||
+#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1581,16 +1593,20 @@
|
||||
int
|
||||
@@ -1578,16 +1590,20 @@
|
||||
err(1, NULL);
|
||||
}
|
||||
if (Tflag != -1) {
|
||||
|
@ -77,7 +77,7 @@ diff -Paur --no-dereference -- libssl.upstream/apps/nc/netcat.c libssl/apps/nc/n
|
|||
errno = ENOPROTOOPT;
|
||||
err(1, "set IPv6 traffic class not supported");
|
||||
}
|
||||
@@ -1608,13 +1624,16 @@
|
||||
@@ -1605,13 +1621,16 @@
|
||||
}
|
||||
|
||||
if (ttl != -1) {
|
||||
|
@ -96,7 +96,7 @@ diff -Paur --no-dereference -- libssl.upstream/apps/nc/netcat.c libssl/apps/nc/n
|
|||
}
|
||||
|
||||
if (minttl != -1) {
|
||||
@@ -1652,7 +1671,9 @@
|
||||
@@ -1649,7 +1668,9 @@
|
||||
{ "af41", IPTOS_DSCP_AF41 },
|
||||
{ "af42", IPTOS_DSCP_AF42 },
|
||||
{ "af43", IPTOS_DSCP_AF43 },
|
||||
|
@ -106,7 +106,7 @@ diff -Paur --no-dereference -- libssl.upstream/apps/nc/netcat.c libssl/apps/nc/n
|
|||
{ "cs0", IPTOS_DSCP_CS0 },
|
||||
{ "cs1", IPTOS_DSCP_CS1 },
|
||||
{ "cs2", IPTOS_DSCP_CS2 },
|
||||
@@ -1662,11 +1683,21 @@
|
||||
@@ -1659,11 +1680,21 @@
|
||||
{ "cs6", IPTOS_DSCP_CS6 },
|
||||
{ "cs7", IPTOS_DSCP_CS7 },
|
||||
{ "ef", IPTOS_DSCP_EF },
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
NAME=libssl
|
||||
BUILD_LIBRARIES=
|
||||
VERSION=3.5.3
|
||||
VERSION=3.6.1
|
||||
DISTNAME=libressl-$VERSION
|
||||
COMPRESSION=tar.gz
|
||||
ARCHIVE=$DISTNAME.$COMPRESSION
|
||||
SHA256SUM=3ab5e5eaef69ce20c6b170ee64d785b42235f48f2e62b095fca5d7b6672b8b28
|
||||
SHA256SUM=acfac61316e93b919c28d62d53037ca734de85c46b4d703f19fd8395cf006774
|
||||
UPSTREAM_SITE=https://ftp.openbsd.org/pub/OpenBSD/LibreSSL
|
||||
UPSTREAM_ARCHIVE=$ARCHIVE
|
||||
LICENSE=OpenSSL
|
||||
|
|
|
@ -25,6 +25,13 @@ all: $(BINARIES)
|
|||
install: all
|
||||
mkdir -p $(DESTDIR)$(BINDIR)
|
||||
install $(BINARIES) $(DESTDIR)$(BINDIR)
|
||||
mkdir -p $(DESTDIR)$(MANDIR)/man1
|
||||
cp sh.1 $(DESTDIR)$(MANDIR)/man1/sh.1
|
||||
ln -sf sh.1 $(DESTDIR)$(MANDIR)/man1/sortix-sh.1
|
||||
mkdir -p $(DESTDIR)$(MANDIR)/man5
|
||||
cp profile.5 $(DESTDIR)$(MANDIR)/man5/profile.5
|
||||
cp proper-sh.5 $(DESTDIR)$(MANDIR)/man5/proper-sh.5
|
||||
cp shrc.5 $(DESTDIR)$(MANDIR)/man5/shrc.5
|
||||
|
||||
sortix-sh: $(SORTIX_SH_SRCS) *.h
|
||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(SORTIX_SH_SRCS) -o $@
|
||||
|
|
160
sh/editline.c
160
sh/editline.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2011-2016, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -18,6 +18,9 @@
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
@ -495,6 +498,161 @@ void edit_line_type_complete(struct edit_line* edit_state)
|
|||
free(partial);
|
||||
}
|
||||
|
||||
static size_t get_histsize(void)
|
||||
{
|
||||
const char* histfile = getenv("HISTSIZE");
|
||||
if ( histfile && isdigit(*histfile) )
|
||||
{
|
||||
errno = 0;
|
||||
char* end;
|
||||
size_t value = strtoul(histfile, &end, 10);
|
||||
// Enforce a reasonable upper limit to avoid OOM on misconfigurations,
|
||||
// when users try to have unlimited history size, but the below saving
|
||||
// will allocate an array of this size.
|
||||
if ( 1048576 <= value )
|
||||
value = 1048576;
|
||||
if ( !errno && !*end )
|
||||
return value;
|
||||
}
|
||||
return 500;
|
||||
}
|
||||
|
||||
bool edit_line_history_load(struct edit_line* edit_state, const char* path)
|
||||
{
|
||||
if ( !path )
|
||||
return true;
|
||||
FILE* fp = fopen(path, "r");
|
||||
if ( !fp )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
return true;
|
||||
warn("%s", path);
|
||||
return false;
|
||||
}
|
||||
char* line = NULL;
|
||||
size_t line_size;
|
||||
ssize_t line_length;
|
||||
while ( 0 < (line_length = getline(&line, &line_size, fp)) )
|
||||
{
|
||||
if ( line[line_length - 1] == '\n' )
|
||||
line[--line_length] = '\0';
|
||||
edit_line_append_history(edit_state, line);
|
||||
}
|
||||
edit_state->history_begun = edit_state->history_used;
|
||||
if ( ferror(fp) )
|
||||
warn("read: %s", path);
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool edit_line_history_save(struct edit_line* edit_state, const char* path)
|
||||
{
|
||||
size_t histsize = get_histsize();
|
||||
if ( !path || !histsize )
|
||||
return true;
|
||||
// Avoid replacing the null device if used to disable the history.
|
||||
if ( !strcmp(path, "/dev/null") )
|
||||
return true;
|
||||
// TODO: File locking is a better alternative to replacing the actual file.
|
||||
// A temporary file rename is used to atomically replace the contents
|
||||
// but may lose the race with other processes.
|
||||
char* tmp;
|
||||
if ( asprintf(&tmp, "%s.XXXXXXXXX", path) < 0 )
|
||||
{
|
||||
warn("malloc");
|
||||
return false;
|
||||
}
|
||||
int fd = mkstemp(tmp);
|
||||
if ( fd < 0 )
|
||||
{
|
||||
if ( errno == EROFS )
|
||||
return true;
|
||||
warn("%s", path);
|
||||
return false;
|
||||
}
|
||||
FILE* fpout = fdopen(fd, "w");
|
||||
if ( !fpout )
|
||||
{
|
||||
warn("%s", path);
|
||||
close(fd);
|
||||
unlink(tmp);
|
||||
free(tmp);
|
||||
return false;
|
||||
}
|
||||
// Merge with any updated history.
|
||||
bool success = true;
|
||||
char** history = calloc(sizeof(char*), histsize);
|
||||
if ( !history )
|
||||
warn("malloc"), success = false;
|
||||
size_t first = 0;
|
||||
size_t used = 0;
|
||||
FILE* fpin;
|
||||
if ( success && (fpin = fopen(path, "r")) )
|
||||
{
|
||||
char* line = NULL;
|
||||
size_t line_size;
|
||||
ssize_t line_length;
|
||||
while ( 0 < (line_length = getline(&line, &line_size, fpin)) )
|
||||
{
|
||||
if ( line[line_length - 1] == '\n' )
|
||||
line[--line_length] = '\0';
|
||||
size_t n = (first + used) % histsize;
|
||||
if ( history[n] )
|
||||
free(history[n]);
|
||||
history[n] = line;
|
||||
if ( used == histsize )
|
||||
first = (first + 1) % histsize;
|
||||
else
|
||||
used++;
|
||||
line = NULL;
|
||||
}
|
||||
if ( ferror(fpin) )
|
||||
warn("read: %s", path), success = false;
|
||||
fclose(fpin);
|
||||
}
|
||||
else if ( errno != ENOENT )
|
||||
warn("%s", path);
|
||||
for ( size_t i = edit_state->history_begun;
|
||||
success && i < edit_state->history_used;
|
||||
i++ )
|
||||
{
|
||||
char* line = strdup(edit_state->history[i]);
|
||||
if ( !line )
|
||||
{
|
||||
warn("malloc");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
size_t n = (first + used) % histsize;
|
||||
if ( history[n] )
|
||||
free(history[n]);
|
||||
history[n] = line;
|
||||
if ( used == histsize )
|
||||
first = (first + 1) % histsize;
|
||||
else
|
||||
used++;
|
||||
line = NULL;
|
||||
}
|
||||
for ( size_t i = 0; i < used; i++ )
|
||||
{
|
||||
size_t n = (first + i) % histsize;
|
||||
char* line = history[n];
|
||||
if ( success && fprintf(fpout, "%s\n", line) < 0 )
|
||||
warn("%s", tmp), success = false;
|
||||
free(line);
|
||||
}
|
||||
int ret = fclose(fpout);
|
||||
if ( success && ret == EOF )
|
||||
warn("%s", path), success = false;
|
||||
if ( success && rename(tmp, path) < 0 )
|
||||
warn("rename: %s -> %s", tmp, path), success = false;
|
||||
if ( !success )
|
||||
unlink(tmp);
|
||||
free(tmp);
|
||||
return success;
|
||||
}
|
||||
|
||||
#define SORTIX_LFLAGS (ISORTIX_KBKEY | ISORTIX_CHARS_DISABLE | ISORTIX_32BIT | \
|
||||
ISORTIX_NONBLOCK | ISORTIX_TERMMODE)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2011-2016, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -38,6 +38,7 @@ struct edit_line
|
|||
size_t history_used;
|
||||
size_t history_length;
|
||||
size_t history_target;
|
||||
size_t history_begun;
|
||||
void* check_input_incomplete_context;
|
||||
bool (*check_input_incomplete)(void*, const char*);
|
||||
void* trap_eof_opportunity_context;
|
||||
|
@ -81,6 +82,8 @@ void edit_line_type_clear(struct edit_line* edit_state);
|
|||
void edit_line_type_delete_word_before(struct edit_line* edit_state);
|
||||
int edit_line_completion_sort(const void* a_ptr, const void* b_ptr);
|
||||
void edit_line_type_complete(struct edit_line* edit_state);
|
||||
bool edit_line_history_load(struct edit_line* edit_state, const char* path);
|
||||
bool edit_line_history_save(struct edit_line* edit_state, const char* path);
|
||||
void edit_line(struct edit_line* edit_state);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
.Dd November 9, 2022
|
||||
.Dt PROFILE 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm profile
|
||||
.Nd login shell startup
|
||||
.Sh SYNOPSIS
|
||||
.Nm ~/.profile
|
||||
.Nm /etc/profile
|
||||
.Nm /etc/default/profile
|
||||
.Sh DESCRIPTION
|
||||
Interactive login shell sessions in
|
||||
.Xr sh 1
|
||||
execute the commands in the
|
||||
.Nm
|
||||
script upon startup, searching for the user's script at
|
||||
.Pa ~/.profile ,
|
||||
any system administrator provided script at
|
||||
.Pa /etc/profile ,
|
||||
or any operating system provided script at
|
||||
.Pa /etc/default/profile ,
|
||||
whichever exists first.
|
||||
.Pp
|
||||
The
|
||||
.Xr shrc 5
|
||||
script is run instead in interactive non-login shell sessions.
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/default/profile" -compact
|
||||
.It Pa ~/.profile
|
||||
The user's
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/profile
|
||||
The system administor provided
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/default/profile
|
||||
The operating system provided
|
||||
.Nm
|
||||
script.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
To portably run the
|
||||
.Xr shrc 5
|
||||
script upon startup of non-login interactive shells in all shells:
|
||||
.Bd -literal -offset indent
|
||||
export ENV="$HOME/.shrc"
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr dash 1 ,
|
||||
.Xr sh 1 ,
|
||||
.Xr shrc 5
|
||||
.Sh BUGS
|
||||
.Xr sh 1
|
||||
is currently primitive and cannot execute most scripts.
|
||||
Beware of sharing the
|
||||
.Nm
|
||||
script between it and other shells such as
|
||||
.Xr dash 1 .
|
|
@ -0,0 +1,49 @@
|
|||
.Dd November 16, 2022
|
||||
.Dt PROPER-SH 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm proper-sh
|
||||
.Nd name of a better non-interactive shell
|
||||
.Sh SYNOPSIS
|
||||
.Nm /etc/proper-sh
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Xr sh 1
|
||||
program is a thin wrapper that invokes
|
||||
.Xr sortix-sh 1
|
||||
on interactive use, but this shell is primitive and cannot execute most scripts,
|
||||
so non-interactive uses of
|
||||
.Xr sh 1
|
||||
instead searches for the name of a better shell to execute in this order:
|
||||
.Pp
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
The shell named in the
|
||||
.Ev SORTIX_SH_BACKEND
|
||||
environment variable.
|
||||
.It
|
||||
The shell named in the
|
||||
.Pa /etc/proper-sh
|
||||
file.
|
||||
.It
|
||||
.Xr dash 1 ,
|
||||
if installed.
|
||||
.It
|
||||
.Sy sortix-sh ,
|
||||
if no better shell was found.
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width "SORTIX_SH_BACKEND"
|
||||
.It Ev SORTIX_SH_BACKEND
|
||||
The name of a better non-interactive shell, taking precedence if set over the
|
||||
.Pa /etc/proper-sh
|
||||
file.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/proper-sh" -compact
|
||||
.It Pa /etc/proper-sh
|
||||
File containing the name of a better non-interactive shell.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr dash 1 ,
|
||||
.Xr sh 1
|
157
sh/proper-sh.c
157
sh/proper-sh.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2014, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -17,114 +17,85 @@
|
|||
* Forward execution to the best shell.
|
||||
*/
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const char* getenv_safe(const char* name, const char* def)
|
||||
static bool is_supported(int argc, char* argv[])
|
||||
{
|
||||
const char* ret = getenv(name);
|
||||
return ret ? ret : def;
|
||||
}
|
||||
|
||||
bool is_existing_shell(const char* candidate)
|
||||
{
|
||||
pid_t child_pid = fork();
|
||||
if ( child_pid < 0 )
|
||||
return false;
|
||||
if ( !child_pid )
|
||||
int i;
|
||||
for ( i = 1; i < argc; i++ )
|
||||
{
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
open("/dev/null", O_RDONLY);
|
||||
open("/dev/null", O_WRONLY);
|
||||
open("/dev/null", O_WRONLY);
|
||||
execlp("which", "which", "--", candidate, (const char*) NULL);
|
||||
exit(127);
|
||||
}
|
||||
int status;
|
||||
waitpid(child_pid, &status, 0);
|
||||
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
}
|
||||
|
||||
char* search_for_proper_shell(void)
|
||||
{
|
||||
if ( getenv("SORTIX_SH_BACKEND") )
|
||||
{
|
||||
if ( !getenv("SORTIX_SH_BACKEND")[0] )
|
||||
return NULL;
|
||||
if ( is_existing_shell(getenv("SORTIX_SH_BACKEND")) )
|
||||
return strdup(getenv("SORTIX_SH_BACKEND"));
|
||||
}
|
||||
|
||||
const char* backends_dir_path =
|
||||
getenv_safe("SORTIX_SH_BACKENDS_DIR", "/etc/proper-shells");
|
||||
|
||||
struct dirent** shell_entries;
|
||||
int num_shell_entries = scandir(backends_dir_path, &shell_entries, NULL, alphasort);
|
||||
if ( 0 <= num_shell_entries )
|
||||
{
|
||||
char* result = NULL;
|
||||
for ( int i = 0; i < num_shell_entries; i++ )
|
||||
{
|
||||
struct dirent* entry = shell_entries[i];
|
||||
const char* name = entry->d_name;
|
||||
if ( !strcmp(name, ".") || !strcmp(name, "..") )
|
||||
continue;
|
||||
size_t path_length = strlen(backends_dir_path) + 1 + strlen(name);
|
||||
char* path = (char*) malloc(path_length + 1);
|
||||
if ( !path )
|
||||
continue;
|
||||
stpcpy(stpcpy(stpcpy(path, backends_dir_path), "/"), name);
|
||||
FILE* fp = fopen(path, "r");
|
||||
free(path);
|
||||
if ( !fp )
|
||||
continue;
|
||||
size_t result_size = 0;
|
||||
ssize_t result_length = getline(&result, &result_size, fp);
|
||||
fclose(fp);
|
||||
if ( result_length < 0 )
|
||||
{
|
||||
free(result);
|
||||
continue;
|
||||
}
|
||||
if ( result[result_length-1] == '\n' )
|
||||
result[--result_length] = '\0';
|
||||
if ( !is_existing_shell(result) )
|
||||
{
|
||||
free(result);
|
||||
result = NULL;
|
||||
continue;
|
||||
}
|
||||
const char* arg = argv[i];
|
||||
if ( (arg[0] != '-' && arg[0] != '+') || !arg[1] )
|
||||
break; // Intentionally not continue and note '+' support.
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[0] == '+' || arg[1] != '-' )
|
||||
{
|
||||
char c;
|
||||
while ( (c = *++arg) ) switch ( c )
|
||||
{
|
||||
case 'e': break;
|
||||
case 'i': break;
|
||||
case 'l': break;
|
||||
case 's': break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < num_shell_entries; i++ )
|
||||
free(shell_entries[i]);
|
||||
free(shell_entries);
|
||||
if ( result )
|
||||
return result;
|
||||
else if ( !strcmp(arg, "--help") )
|
||||
;
|
||||
else if ( !strcmp(arg, "--version") )
|
||||
;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return i == argc;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if ( argc == 1 && isatty(0) && isatty(1) )
|
||||
execvp("sortix-sh", argv);
|
||||
if ( !isatty(0) || !isatty(1) || !is_supported(argc, argv) )
|
||||
{
|
||||
const char* env = getenv("SORTIX_SH_BACKEND");
|
||||
if ( env && env[0] )
|
||||
{
|
||||
execvp(env, argv);
|
||||
if ( errno != ENOENT )
|
||||
err(127, "%s", env);
|
||||
}
|
||||
|
||||
char* proper_shell = search_for_proper_shell();
|
||||
if ( proper_shell )
|
||||
execvp(proper_shell, argv);
|
||||
free(proper_shell);
|
||||
FILE* fp = fopen("/etc/proper-sh", "r");
|
||||
if ( !fp && errno != ENOENT )
|
||||
err(127, "/etc/proper-sh");
|
||||
if ( fp )
|
||||
{
|
||||
char* proper_sh = NULL;
|
||||
size_t proper_sh_size = 0;
|
||||
ssize_t proper_sh_length = getline(&proper_sh, &proper_sh_size, fp);
|
||||
if ( proper_sh_length < 0 )
|
||||
err(127, "/etc/proper-sh");
|
||||
if ( proper_sh_length && proper_sh[proper_sh_length - 1] == '\n' )
|
||||
proper_sh[--proper_sh_length] = '\0';
|
||||
if ( proper_sh[0] )
|
||||
{
|
||||
execvp(proper_sh, argv);
|
||||
if ( errno != ENOENT )
|
||||
err(127, "%s", proper_sh);
|
||||
}
|
||||
free(proper_sh);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
execvp("dash", argv);
|
||||
if ( errno != ENOENT )
|
||||
err(127, "dash");
|
||||
}
|
||||
|
||||
execvp("sortix-sh", argv);
|
||||
return 127;
|
||||
err(127, "sortix-sh");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
.Dd November 9, 2022
|
||||
.Dt SH 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm sh
|
||||
.Nd shell command interpreter
|
||||
.Sh SYNOPSIS
|
||||
.Nm sh
|
||||
.Op Fl ceils
|
||||
.Op Ar script Oo argument ... Oc
|
||||
.Nm sortix-sh
|
||||
.Op Fl ceils
|
||||
.Op Ar script Oo argument ... Oc
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the command line interpreter for the shell language.
|
||||
It reads and executes commands from the standard input or the
|
||||
.Ar script
|
||||
file if specified.
|
||||
.Nm
|
||||
is interactive if the standard input is a terminal and no
|
||||
.Ar script
|
||||
file was specified.
|
||||
.Pp
|
||||
The standard shell
|
||||
.Nm sortix-sh
|
||||
is currently primitive and cannot execute most scripts.
|
||||
.Nm sh
|
||||
is currently a thin wrapper that detects non-interactive use and invokes a
|
||||
better shell instead, named in the
|
||||
.Ev SORTIX_SH_BACKEND
|
||||
environment variable if set, or named in
|
||||
.Xr proper-sh 5
|
||||
if it exists, and otherwise
|
||||
.Xr dash 1
|
||||
is invoked.
|
||||
.Pp
|
||||
The options can be unset by prefixing them with a plus
|
||||
.Sq +
|
||||
instead of a dash
|
||||
.Sq - .
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "12345678"
|
||||
.It Fl c
|
||||
The
|
||||
.Ar script
|
||||
argument contains the script's text instead of a path to the script file.
|
||||
.It Fl e
|
||||
Exit if any command exit non-zero.
|
||||
.It Fl i
|
||||
Interactively read and execute commands.
|
||||
.It Fl l
|
||||
The shell is a login shell.
|
||||
Interactive shells run the
|
||||
.Xr profile 5
|
||||
script on startup instead of the
|
||||
.Xr shrc
|
||||
script.
|
||||
This option is set if the shell is invoked by a name starting with a dash
|
||||
.Sq - .
|
||||
.It Fl s
|
||||
Read commands from the standard input (the default).
|
||||
This option can be combined with the
|
||||
.Fl c
|
||||
option to execute the script text in the
|
||||
.Ar script
|
||||
argument before reading normally from the standard input
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Nm
|
||||
uses environment these variables:
|
||||
.Bl -tag -width "HISTFILE"
|
||||
.It Ev ENV
|
||||
File to execute on non-login interactive startup instead of
|
||||
.Pa ~/.shrc
|
||||
per
|
||||
.Xr shrc 5 .
|
||||
This variable is subject to path expansion.
|
||||
.It Ev HISTFILE
|
||||
Save the shell history in this file.
|
||||
The default is
|
||||
.Pa ~/.sh_history .
|
||||
.It Ev HISTSIZE
|
||||
Maximum number of commands in the saved shell history.
|
||||
The default is 500.
|
||||
.It Ev HOME
|
||||
The user's home directory
|
||||
.Sq ( ~ ) .
|
||||
.It Ev PATH
|
||||
The colon-separated list of directory paths to search for programs.
|
||||
.It Ev PS1
|
||||
Interactive shell prompt when expecting a new command.
|
||||
.It Ev PS2
|
||||
Interactive shell prompt when the current command continues onto another line.
|
||||
.It Ev PWD
|
||||
Set to the current working directory.
|
||||
.It Ev SHELL
|
||||
Set to
|
||||
.Nm .
|
||||
.It Ev SHLVL
|
||||
Depth of recursive shell sessions.
|
||||
The outermost interactive shell (depth 1) will currently refuse to exit on an
|
||||
end-of-file condition (^D) when on the
|
||||
.Pa /dev/tty1
|
||||
terminal to avoid accidentally powering off the machine.
|
||||
.It Ev SORTIX_SH_BACKEND
|
||||
Name of a better shell to use for non-interactive use per
|
||||
.Xr proper-sh 5 .
|
||||
This variable takes precedence over
|
||||
.Pa /etc/proper-sh .
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/proper-sh" -compact
|
||||
.It Pa ~/.profile , /etc/profile , /etc/default/profile
|
||||
.Xr profile 5
|
||||
script whose commands are run on non-login interactive shell startup.
|
||||
.It Pa /etc/proper-sh
|
||||
Name of a better shell to use for non-interactive use per
|
||||
.Xr proper-sh 5 .
|
||||
The
|
||||
.Ev SORTIX_SH_BACKEND
|
||||
environment variable takes precedence over this file if set.
|
||||
.Xr dash 1
|
||||
is used by default if it is installed.
|
||||
.It Pa ~/.sh_history
|
||||
The saved shell history.
|
||||
This location is controlled by the
|
||||
.Ev HISTFILE
|
||||
environment variable.
|
||||
.It Pa ~/.shrc , /etc/shrc , /etc/default/shrc
|
||||
.Xr shrc 5
|
||||
script whose commands are run on login interactive shell startup.
|
||||
The
|
||||
.Ev ENV
|
||||
environment variable overrides the search for the script if set.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Nm
|
||||
exits with the same exit status as the last run command, or 0 if no command has
|
||||
been run.
|
||||
.Sh SEE ALSO
|
||||
.Xr dash 1 ,
|
||||
.Xr profile 5 ,
|
||||
.Xr proper-sh 5 ,
|
||||
.Xr session 5 ,
|
||||
.Xr shrc 5 ,
|
||||
.Xr login 8
|
366
sh/sh.c
366
sh/sh.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2011-2016, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -23,6 +23,7 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -38,6 +39,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
@ -51,17 +53,19 @@
|
|||
#include "showline.h"
|
||||
#include "util.h"
|
||||
|
||||
const char* builtin_commands[] =
|
||||
static const char* builtin_commands[] =
|
||||
{
|
||||
"cd",
|
||||
"exit",
|
||||
"unset",
|
||||
"clearenv",
|
||||
"history",
|
||||
(const char*) NULL,
|
||||
};
|
||||
|
||||
int status = 0;
|
||||
bool foreground_shell;
|
||||
static bool foreground_shell;
|
||||
static int status = 0;
|
||||
static struct edit_line edit_state;
|
||||
|
||||
static bool is_proper_absolute_path(const char* path)
|
||||
{
|
||||
|
@ -1295,7 +1299,7 @@ struct execute_result execute(char** tokens,
|
|||
// foreground process group, but bar dies prior to foo's tcsetpgrp
|
||||
// call, because then the shell would run tcsetpgrp to take back
|
||||
// control, and only then would foo do its tcsetpgrp call.
|
||||
while ( foreground_shell && pgid == -1 && tcgetpgrp(0) != childpid )
|
||||
while ( interactive && pgid == -1 && tcgetpgrp(0) != childpid )
|
||||
sched_yield();
|
||||
|
||||
struct execute_result result;
|
||||
|
@ -1306,7 +1310,7 @@ struct execute_result execute(char** tokens,
|
|||
}
|
||||
|
||||
setpgid(0, pgid != -1 ? pgid : 0);
|
||||
if ( foreground_shell && pgid == -1 )
|
||||
if ( interactive && pgid == -1 )
|
||||
{
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
|
@ -1339,6 +1343,19 @@ struct execute_result execute(char** tokens,
|
|||
}
|
||||
}
|
||||
|
||||
if ( strcmp(argv[0], "history") == 0 )
|
||||
{
|
||||
for ( size_t i = 0; i < edit_state.history_used; i++ )
|
||||
{
|
||||
const char* line = edit_state.history[i];
|
||||
if ( fprintf(stdout, "%5zu %s\n", i + 1, line) < 0 )
|
||||
err(1, "stdout");
|
||||
}
|
||||
if ( fflush(stdout) == EOF )
|
||||
err(1, "stdout");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
execvp(argv[0], argv);
|
||||
|
||||
if ( interactive && errno == ENOENT )
|
||||
|
@ -1491,12 +1508,15 @@ readcmd:
|
|||
// as that may have side effects if a process checks for this
|
||||
// behavior and then unexpectedly the shell takes back support
|
||||
// without the usual ^Z mechanism.
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
||||
tcsetpgrp(0, getpgid(0));
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
if ( interactive )
|
||||
{
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
||||
tcsetpgrp(0, getpgid(0));
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
}
|
||||
pgid = -1;
|
||||
status = 0;
|
||||
goto readcmd;
|
||||
|
@ -1520,12 +1540,15 @@ readcmd:
|
|||
status = 1;
|
||||
return status;
|
||||
}
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
||||
tcsetpgrp(0, getpgid(0));
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
if ( interactive )
|
||||
{
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
||||
tcsetpgrp(0, getpgid(0));
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
}
|
||||
if ( WIFSIGNALED(exitstatus) && WTERMSIG(exitstatus) == SIGINT )
|
||||
printf("^C\n");
|
||||
else if ( WIFSIGNALED(exitstatus) && WTERMSIG(exitstatus) != SIGPIPE )
|
||||
|
@ -1834,6 +1857,152 @@ size_t do_complete(char*** completions_ptr,
|
|||
return *completions_ptr = completions, completions_count;
|
||||
}
|
||||
|
||||
static void eval_ps_append_c(struct stringbuf* buf, char c)
|
||||
{
|
||||
if ( c == '\\' || c == '\'' || c == '"' || c == '$' || c == '`' )
|
||||
stringbuf_append_c(buf, '\\');
|
||||
stringbuf_append_c(buf, c);
|
||||
}
|
||||
|
||||
static void eval_ps_append(struct stringbuf* buf, const char* str)
|
||||
{
|
||||
for ( size_t i = 0; str[i]; i++ )
|
||||
eval_ps_append_c(buf, str[i]);
|
||||
}
|
||||
|
||||
static char* eval_ps(const char* ps)
|
||||
{
|
||||
struct stringbuf buf;
|
||||
stringbuf_begin(&buf);
|
||||
bool escaped = false;
|
||||
while ( *ps )
|
||||
{
|
||||
char c = *ps++;
|
||||
if ( !escaped && c == '\\' )
|
||||
{
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
else if ( escaped && '0' <= c && c <= '7' )
|
||||
{
|
||||
unsigned char byte = c - '0';
|
||||
if ( '0' <= *ps && *ps <= '7' )
|
||||
{
|
||||
byte = byte * 8 + *ps++ - '0';
|
||||
if ( byte <= 037 && '0' <= *ps && *ps <= '7' )
|
||||
byte = byte * 8 + *ps++ - '0';
|
||||
}
|
||||
eval_ps_append_c(&buf, byte);
|
||||
}
|
||||
else if ( escaped && c == 'a' )
|
||||
eval_ps_append_c(&buf, '\a');
|
||||
else if ( escaped && c == 'e' )
|
||||
eval_ps_append_c(&buf, '\e');
|
||||
else if ( escaped && (c == 'h' || c == 'H') )
|
||||
{
|
||||
char hostname[HOST_NAME_MAX + 1] = "?";
|
||||
gethostname(hostname, sizeof(hostname));
|
||||
if ( c == 'h' )
|
||||
hostname[strcspn(hostname, ".")] = '\0';
|
||||
eval_ps_append(&buf, hostname);
|
||||
}
|
||||
else if ( escaped && c == 'l' )
|
||||
{
|
||||
char* tty = ttyname(0);
|
||||
if ( tty )
|
||||
eval_ps_append(&buf, basename(tty));
|
||||
else
|
||||
eval_ps_append_c(&buf, '?');
|
||||
}
|
||||
else if ( escaped && c == 'n' )
|
||||
eval_ps_append_c(&buf, '\n');
|
||||
else if ( escaped && c == 'r' )
|
||||
eval_ps_append_c(&buf, '\r');
|
||||
else if ( escaped && c == 's' )
|
||||
{
|
||||
const char* argv0 = getenv("0");
|
||||
if ( !argv0 )
|
||||
argv0 = program_invocation_short_name;
|
||||
char* base = strdup(argv0);
|
||||
if ( !base )
|
||||
eval_ps_append_c(&buf, '?');
|
||||
else
|
||||
{
|
||||
eval_ps_append(&buf, basename(base));
|
||||
free(base);
|
||||
}
|
||||
}
|
||||
else if ( escaped && (c == 't' || c == 'T' || c == '@' || c == 'A') )
|
||||
{
|
||||
const char* format = "";
|
||||
switch ( c )
|
||||
{
|
||||
case 't': format = "%H:%M:%S"; break;
|
||||
case 'T': format = "%I:%M:%S"; break;
|
||||
case '@': format = "%I:%M %p"; break;
|
||||
case 'A': format = "%H:%M"; break;
|
||||
}
|
||||
time_t now = time(NULL);
|
||||
struct tm tm;
|
||||
localtime_r(&now, &tm);
|
||||
char buffer[16] = "";
|
||||
strftime(buffer, sizeof(buffer), format, &tm);
|
||||
eval_ps_append(&buf, buffer);
|
||||
}
|
||||
else if ( escaped && c == 'u' )
|
||||
{
|
||||
char* user = getlogin();
|
||||
eval_ps_append(&buf, user ? user : "?");
|
||||
}
|
||||
else if ( escaped && (c == 'w' || c == 'W') )
|
||||
{
|
||||
char* dir = get_current_dir_name();
|
||||
const char* home = getenv("HOME");
|
||||
if ( !dir )
|
||||
eval_ps_append_c(&buf, '?');
|
||||
else if ( c == 'w' )
|
||||
{
|
||||
size_t home_len = home ? strlen(home) : 0;
|
||||
if ( home_len && !strncmp(dir, home, home_len) )
|
||||
{
|
||||
eval_ps_append_c(&buf, '~');
|
||||
eval_ps_append(&buf, dir + home_len);
|
||||
}
|
||||
else
|
||||
eval_ps_append(&buf, dir);
|
||||
}
|
||||
else if ( home && !strcmp(dir, home) )
|
||||
eval_ps_append_c(&buf, '~');
|
||||
else
|
||||
eval_ps_append(&buf, basename(dir));
|
||||
free(dir);
|
||||
}
|
||||
else if ( escaped && c == '$' )
|
||||
eval_ps_append_c(&buf, getuid() == 0 ? '#' : '$');
|
||||
else if ( escaped && (c == '[' || c == ']') )
|
||||
{
|
||||
// TODO: Ignoring this sequence when predicting cursor position.
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( escaped || c == '\'' || c == '"' )
|
||||
stringbuf_append_c(&buf, '\\');
|
||||
stringbuf_append_c(&buf, c);
|
||||
}
|
||||
escaped = false;
|
||||
}
|
||||
char* string = stringbuf_finish(&buf);
|
||||
if ( !string )
|
||||
return NULL;
|
||||
char* expanded = token_expand_variables(string);
|
||||
free(string);
|
||||
if ( !expanded )
|
||||
return NULL;
|
||||
char* finalized = token_finalize(expanded);
|
||||
free(expanded);
|
||||
return finalized;
|
||||
}
|
||||
|
||||
struct sh_read_command
|
||||
{
|
||||
char* command;
|
||||
|
@ -1846,7 +2015,6 @@ void read_command_interactive(struct sh_read_command* sh_read_command)
|
|||
{
|
||||
update_env();
|
||||
|
||||
static struct edit_line edit_state; // static to preserve command history.
|
||||
edit_state.in_fd = 0;
|
||||
edit_state.out_fd = 1;
|
||||
edit_state.check_input_incomplete_context = NULL;
|
||||
|
@ -1856,45 +2024,15 @@ void read_command_interactive(struct sh_read_command* sh_read_command)
|
|||
edit_state.complete_context = NULL;
|
||||
edit_state.complete = do_complete;
|
||||
|
||||
char* current_dir = get_current_dir_name();
|
||||
|
||||
const char* print_username = getlogin();
|
||||
if ( !print_username )
|
||||
print_username = getuid() == 0 ? "root" : "?";
|
||||
char hostname[HOST_NAME_MAX + 1];
|
||||
if ( gethostname(hostname, sizeof(hostname)) < 0 )
|
||||
strlcpy(hostname, "(none)", sizeof(hostname));
|
||||
const char* print_hostname = hostname;
|
||||
const char* print_dir = current_dir ? current_dir : "?";
|
||||
const char* home_dir = getenv_safe("HOME");
|
||||
|
||||
const char* print_dir_1 = print_dir;
|
||||
const char* print_dir_2 = "";
|
||||
char prompt_char = getuid() == 0 ? '#' : '$';
|
||||
|
||||
size_t home_dir_len = strlen(home_dir);
|
||||
if ( home_dir_len && strncmp(print_dir, home_dir, home_dir_len) == 0 )
|
||||
{
|
||||
print_dir_1 = "~";
|
||||
print_dir_2 = print_dir + home_dir_len;
|
||||
}
|
||||
|
||||
char* ps1;
|
||||
asprintf(&ps1, "\e[;1;32m%s@%s \e[1;34m%s%s %c\e[m ",
|
||||
print_username,
|
||||
print_hostname,
|
||||
print_dir_1,
|
||||
print_dir_2,
|
||||
prompt_char);
|
||||
|
||||
free(current_dir);
|
||||
|
||||
edit_state.ps1 = ps1;
|
||||
edit_state.ps2 = "> ";
|
||||
const char* def_ps1 = "\\033[;1;32m\\u@\\H \\033[1;34m\\w \\$\\033[m ";
|
||||
const char* def_ps2 = "> ";
|
||||
edit_state.ps1 = eval_ps(getenv_safe_def("PS1", def_ps1));
|
||||
edit_state.ps2 = eval_ps(getenv_safe_def("PS2", def_ps2));
|
||||
|
||||
edit_line(&edit_state);
|
||||
|
||||
free(ps1);
|
||||
free((char*) edit_state.ps1);
|
||||
free((char*) edit_state.ps2);
|
||||
|
||||
if ( edit_state.abort_editing )
|
||||
{
|
||||
|
@ -2004,12 +2142,12 @@ void read_command_non_interactive(struct sh_read_command* sh_read_command,
|
|||
sh_read_command->command = command;
|
||||
}
|
||||
|
||||
int run(FILE* fp,
|
||||
const char* fp_name,
|
||||
bool interactive,
|
||||
bool exit_on_error,
|
||||
bool* script_exited,
|
||||
int status)
|
||||
static int run(FILE* fp,
|
||||
const char* fp_name,
|
||||
bool interactive,
|
||||
bool exit_on_error,
|
||||
bool* script_exited,
|
||||
int status)
|
||||
{
|
||||
// TODO: The interactive read code should cope when the input is not a
|
||||
// terminal; it should print the prompt and then read normally without
|
||||
|
@ -2058,6 +2196,83 @@ int run(FILE* fp,
|
|||
return status;
|
||||
}
|
||||
|
||||
static char* find_rc(bool login)
|
||||
{
|
||||
const char* env = getenv("ENV");
|
||||
if ( !login && env )
|
||||
{
|
||||
// TODO: Path expansion.
|
||||
char* result = strdup(env);
|
||||
if ( !result )
|
||||
err(1, "malloc");
|
||||
return result;
|
||||
}
|
||||
const char* home = getenv("HOME");
|
||||
const char* rcname = login ? "profile" : "shrc";
|
||||
const char* dirs[] = { home, "/etc", "/etc/default" };
|
||||
bool found = false;
|
||||
for ( size_t i = 0; !found && i < sizeof(dirs) / sizeof(dirs[0]); i++ )
|
||||
{
|
||||
char* rc;
|
||||
if ( asprintf(&rc, "%s%s%s", dirs[i], i ? "/" : "/.", rcname) < 0 )
|
||||
err(1, "malloc");
|
||||
if ( (found = !access(rc, F_OK)) )
|
||||
return rc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int top(FILE* fp,
|
||||
const char* fp_name,
|
||||
bool interactive,
|
||||
bool exit_on_error,
|
||||
bool login,
|
||||
bool* script_exited,
|
||||
int status)
|
||||
{
|
||||
if ( interactive )
|
||||
{
|
||||
const char* home = getenv("HOME");
|
||||
const char* histfile = getenv("HISTFILE");
|
||||
if ( !histfile && home )
|
||||
{
|
||||
char* path;
|
||||
if ( asprintf(&path, "%s/.sh_history", home) < 0 ||
|
||||
setenv("HISTFILE", path, 1) < 0 )
|
||||
err(1, "malloc");
|
||||
free(path);
|
||||
}
|
||||
|
||||
char* rc = find_rc(login);
|
||||
if ( rc )
|
||||
{
|
||||
FILE* rcfp = fopen(rc, "r");
|
||||
if ( !rcfp )
|
||||
warn("%s", rc);
|
||||
else
|
||||
{
|
||||
status = run(rcfp, rc, false, exit_on_error, script_exited,
|
||||
status);
|
||||
fclose(rcfp);
|
||||
}
|
||||
free(rc);
|
||||
}
|
||||
|
||||
if ( *script_exited || (status != 0 && exit_on_error) )
|
||||
return status;
|
||||
|
||||
edit_line_history_load(&edit_state, getenv("HISTFILE"));
|
||||
}
|
||||
|
||||
status = run(fp, fp_name, interactive, exit_on_error, script_exited,
|
||||
status);
|
||||
|
||||
if ( interactive )
|
||||
edit_line_history_save(&edit_state, getenv("HISTFILE"));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void compact_arguments(int* argc, char*** argv)
|
||||
{
|
||||
for ( int i = 0; i < *argc; i++ )
|
||||
|
@ -2133,8 +2348,10 @@ int main(int argc, char* argv[])
|
|||
bool flag_c_first_operand_is_command = false;
|
||||
bool flag_e_exit_on_error = false;
|
||||
bool flag_i_interactive = false;
|
||||
bool flag_l_login = argv[0][0] == '-';
|
||||
bool flag_s_stdin = false;
|
||||
|
||||
// The well implemented options are recognized in proper-sh.c.
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
{
|
||||
|
@ -2149,7 +2366,11 @@ int main(int argc, char* argv[])
|
|||
char c;
|
||||
while ( (c = *++arg) ) switch ( c )
|
||||
{
|
||||
case 'c': flag_c_first_operand_is_command = false; break;
|
||||
case 'e': flag_e_exit_on_error = false; break;
|
||||
case 'i': flag_i_interactive = false; break;
|
||||
case 'l': flag_l_login = false; break;
|
||||
case 's': flag_s_stdin = false; break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
|
@ -2164,6 +2385,7 @@ int main(int argc, char* argv[])
|
|||
case 'c': flag_c_first_operand_is_command = true; break;
|
||||
case 'e': flag_e_exit_on_error = true; break;
|
||||
case 'i': flag_i_interactive = true; break;
|
||||
case 'l': flag_l_login = true; break;
|
||||
case 's': flag_s_stdin = true; break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
|
@ -2205,7 +2427,7 @@ int main(int argc, char* argv[])
|
|||
char ppidstr[3 * sizeof(pid_t)];
|
||||
snprintf(pidstr, sizeof(pidstr), "%ji", (intmax_t) getpid());
|
||||
snprintf(ppidstr, sizeof(ppidstr), "%ji", (intmax_t) getppid());
|
||||
setenv("SHELL", argv[0], 1);
|
||||
setenv("SHELL", argv[0] + (argv[0][0] == '-'), 1);
|
||||
setenv("$", pidstr, 1);
|
||||
setenv("PPID", ppidstr, 1);
|
||||
setenv("?", "0", 1);
|
||||
|
@ -2234,8 +2456,8 @@ int main(int argc, char* argv[])
|
|||
if ( !fp )
|
||||
error(2, errno, "fmemopen");
|
||||
|
||||
status = run(fp, "<command-line>", false, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
status = top(fp, "<command-line>", false, flag_e_exit_on_error,
|
||||
flag_l_login, &script_exited, status);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
|
@ -2245,8 +2467,8 @@ int main(int argc, char* argv[])
|
|||
if ( flag_s_stdin )
|
||||
{
|
||||
bool is_interactive = flag_i_interactive || isatty(fileno(stdin));
|
||||
status = run(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
status = top(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
flag_l_login, &script_exited, status);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
}
|
||||
|
@ -2261,8 +2483,8 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
bool is_interactive = flag_i_interactive || isatty(fileno(stdin));
|
||||
status = run(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
status = top(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
flag_l_login, &script_exited, status);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
}
|
||||
|
@ -2279,8 +2501,8 @@ int main(int argc, char* argv[])
|
|||
FILE* fp = fopen(path, "r");
|
||||
if ( !fp )
|
||||
error(127, errno, "%s", path);
|
||||
status = run(fp, path, false, flag_e_exit_on_error, &script_exited,
|
||||
status);
|
||||
status = top(fp, path, false, flag_e_exit_on_error, flag_l_login,
|
||||
&script_exited, status);
|
||||
fclose(fp);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
|
@ -2288,8 +2510,8 @@ int main(int argc, char* argv[])
|
|||
else
|
||||
{
|
||||
bool is_interactive = flag_i_interactive || isatty(fileno(stdin));
|
||||
status = run(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
status = top(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
flag_l_login, &script_exited, status);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
.Dd November 9, 2022
|
||||
.Dt SHRC 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm shrc
|
||||
.Nd login shell startup
|
||||
.Sh SYNOPSIS
|
||||
.Nm $ENV
|
||||
.Nm ~/.shrc
|
||||
.Nm /etc/shrc
|
||||
.Nm /etc/default/shrc
|
||||
.Sh DESCRIPTION
|
||||
Interactive non-login shell sessions in
|
||||
.Xr sh 1
|
||||
execute the commands in the
|
||||
.Nm
|
||||
script upon startup, using the
|
||||
.Pa ENV
|
||||
environment variable with path expansion if set, and otherwise searching for the
|
||||
user's script at
|
||||
.Pa ~/.shrc ,
|
||||
any system administrator provided script at
|
||||
.Pa /etc/shrc ,
|
||||
or any operating system provided script at
|
||||
.Pa /etc/default/shrc ,
|
||||
whichever exists first.
|
||||
.Pp
|
||||
The
|
||||
.Xr profile 5
|
||||
script is run instead in interactive login shell sessions.
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width "ENV"
|
||||
.It Ev ENV
|
||||
File to execute on non-login interactive startup instead of searching the
|
||||
standard paths for the
|
||||
.Nm
|
||||
script.
|
||||
This variable is subject to path expansion.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/default/shrc" -compact
|
||||
.It Pa ~/.shrc
|
||||
The user's
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/shrc
|
||||
The system administor provided
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/default/shrc
|
||||
The operating system provided
|
||||
.Nm
|
||||
script.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr dash 1 ,
|
||||
.Xr sh 1 ,
|
||||
.Xr shrc 5
|
||||
.Sh CAVEATS
|
||||
.Xr dash
|
||||
does not use the
|
||||
.Nm
|
||||
script, but instead only uses the
|
||||
.Ev ENV
|
||||
environment variable.
|
||||
To invoke the
|
||||
.Nm
|
||||
script portably across all standard shells upon startup of non-interactive login
|
||||
sells, set the
|
||||
.Ev ENV
|
||||
variable to the user's
|
||||
.Nm
|
||||
script per the example in
|
||||
.Xr profile 5 .
|
||||
.Sh BUGS
|
||||
.Xr sh 1
|
||||
is currently primitive and cannot execute most scripts.
|
||||
Beware of sharing the
|
||||
.Nm
|
||||
script between it and other shells such as
|
||||
.Xr dash 1 .
|
|
@ -6,4 +6,4 @@ need tty
|
|||
|
||||
cd "$HOME"
|
||||
exit-code-meaning poweroff-reboot
|
||||
exec "$SHELL"
|
||||
exec "$SHELL" -l
|
||||
|
|
Loading…
Reference in New Issue