1 #define _POSIX_C_SOURCE 200809L
2 #include <errno.h> // errno
3 #include <fcntl.h> // O_RDONLY,open
4 #include <locale.h> // LC_ALL,setlocale
5 #include <signal.h> /* SIGHUP,SIGINT,SIGQUIT,SIGTSTP,SIGTTOU,
6 * SIG_DFL,SIG_ERR,SIG_IGN,kill,signal,raise
8 #include <stdbool.h> // bool,true
9 #include <stdint.h> // SIZE_MAX
10 #include <stdio.h> // fclose,fdopen,fputs,getdelim,perror,stderr,stdin
11 #include <stdlib.h> // EXIT_FAILURE,NULL,exit,realloc,size_t
12 #include <stdnoreturn.h> // noreturn
13 #include <sys/wait.h> /* WEXITSTATUS,WIFEXITED,WIFSIGNALED,WIFSTOPPED,
14 * WSTOPSIG,WTERMSIG,WUNTRACED,waitpid
16 #include <unistd.h> /*
17 * close,dup2,execl,fork,pid_t,pipe,setpgid,tcsetpgrp
20 #define TRY(F, ...) do {\
21 if (F(__VA_ARGS__) == -1) {\
26 typedef void (*Handler
)(int);
28 static noreturn
void pexit(char const* restrict
const message
) {
29 int const tmp
= errno
;
30 fputs("minish: ", stderr
);
36 static noreturn
void out_of_memory(void) {
41 static int try_open(char const name
[restrict
const static 1]) {
42 int const fd
= open(name
, O_RDONLY
);
49 static void try_signal(int const signum
, Handler
const handler
) {
50 if (signal(signum
, handler
) == SIG_ERR
) {
55 static void sigints(bool const interactive
, Handler
const handler
) {
57 try_signal(SIGINT
, handler
);
58 try_signal(SIGQUIT
, handler
);
59 try_signal(SIGTSTP
, handler
);
63 static pid_t
try_fork(void) {
64 pid_t
const pid
= fork();
71 static char** try_realloc(char** restrict p
, size_t const n
) {
72 p
= realloc(p
, n
* sizeof(*p
));
79 static void eval(FILE* const input
) {
82 char** argv
= try_realloc(NULL
, size
);
87 if (getdelim(&line
, &n
, '\0', input
) == -1) {
94 if (size
> SIZE_MAX
/ 2u) {
98 argv
= try_realloc(argv
, size
);
104 execv(argv
[0], argv
);
109 static int wait_and_check(pid_t
const pid
, bool const interactive
) {
112 TRY(waitpid
, pid
, &s
, 0);
114 TRY(waitpid
, pid
, &s
, WUNTRACED
);
115 while (WIFSTOPPED(s
)) {
116 if (WSTOPSIG(s
) == SIGTSTP
) {
117 try_signal(SIGTSTP
, SIG_DFL
);
119 try_signal(SIGTSTP
, SIG_IGN
);
121 TRY(waitpid
, pid
, &s
, WUNTRACED
);
123 if (WIFSIGNALED(s
)) {
124 switch (WTERMSIG(s
)) {
126 try_signal(SIGINT
, SIG_DFL
);
128 case SIGQUIT
: exit(EXIT_FAILURE
);
135 int main(int argc
, char** argv
) {
136 setlocale(LC_ALL
, "");
137 bool const interactive
= argc
<= 1;
138 sigints(interactive
, SIG_IGN
);
139 int const script
= interactive
? 0 : try_open(argv
[1]);
143 int const eval_pid
= try_fork();
145 sigints(interactive
, SIG_DFL
);
150 FILE* const input
= fdopen(fds
[0], "r");
159 TRY(setpgid
, eval_pid
, 0);
160 int const tty
= try_open("/dev/tty");
161 signal(SIGTTOU
, SIG_IGN
);
162 TRY(tcsetpgrp
, tty
, eval_pid
);
163 signal(SIGTTOU
, SIG_DFL
);
166 pid_t
const read_pid
= try_fork();
168 sigints(interactive
, SIG_DFL
);
171 TRY(setpgid
, 0, eval_pid
);
173 TRY(dup2
, script
, 0);
176 TRY(dup2
, fds
[1], 1);
178 char const name
[] = "minish-read";
179 execl(name
, name
, (char const*)NULL
);
183 int const s
= wait_and_check(read_pid
, interactive
);
184 if (WIFEXITED(s
) && WEXITSTATUS(s
)) {
185 kill(eval_pid
, SIGHUP
);
186 return WEXITSTATUS(s
) == 1 ? EXIT_FAILURE
: 0;
188 wait_and_check(eval_pid
, interactive
);