Call realloc() with double the current size
[minish.git] / src / minish.c
blobd04513fc79780d72204ebbdc947e649b2a8a9967
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
7 */
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) {\
22 pexit(#F "()");\
24 } while (0)
26 typedef void (*Handler)(int);
28 static noreturn void pexit(char const* restrict const message) {
29 int const tmp = errno;
30 fputs("minish: ", stderr);
31 errno = tmp;
32 perror(message);
33 exit(EXIT_FAILURE);
36 static noreturn void out_of_memory(void) {
37 errno = ENOMEM;
38 pexit("realloc()");
41 static int try_open(char const name[restrict const static 1]) {
42 int const fd = open(name, O_RDONLY);
43 if (fd == -1) {
44 pexit(name);
46 return fd;
49 static void try_signal(int const signum, Handler const handler) {
50 if (signal(signum, handler) == SIG_ERR) {
51 pexit("signal()");
55 static void sigints(bool const interactive, Handler const handler) {
56 if (!interactive) {
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();
65 if (pid == -1) {
66 pexit("fork()");
68 return pid;
71 static char** try_realloc(char** restrict p, size_t const n) {
72 p = realloc(p, n * sizeof(*p));
73 if (p) {
74 return p;
76 out_of_memory();
79 static void eval(FILE* const input) {
80 size_t size = 2u;
81 size_t argc = 0u;
82 char** argv = try_realloc(NULL, size);
83 char* line;
84 do {
85 line = NULL;
86 size_t n = 0u;
87 if (getdelim(&line, &n, '\0', input) == -1) {
88 if (errno) {
89 pexit("stdin");
91 line = NULL;
93 if (argc == size) {
94 if (size > SIZE_MAX / 2u) {
95 out_of_memory();
97 size *= 2u;
98 argv = try_realloc(argv, size);
100 argv[argc++] = line;
101 } while (line);
102 fclose(input);
103 if (argc > 1) {
104 execv(argv[0], argv);
105 pexit(argv[0]);
109 static int wait_and_check(pid_t const pid, bool const interactive) {
110 int s;
111 if (interactive) {
112 TRY(waitpid, pid, &s, 0);
113 } else {
114 TRY(waitpid, pid, &s, WUNTRACED);
115 while (WIFSTOPPED(s)) {
116 if (WSTOPSIG(s) == SIGTSTP) {
117 try_signal(SIGTSTP, SIG_DFL);
118 raise(SIGTSTP);
119 try_signal(SIGTSTP, SIG_IGN);
121 TRY(waitpid, pid, &s, WUNTRACED);
123 if (WIFSIGNALED(s)) {
124 switch (WTERMSIG(s)) {
125 case SIGINT:
126 try_signal(SIGINT, SIG_DFL);
127 raise(SIGINT);
128 case SIGQUIT: exit(EXIT_FAILURE);
132 return s;
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]);
140 while (true) {
141 int fds[2];
142 TRY(pipe, fds);
143 int const eval_pid = try_fork();
144 if (!eval_pid) {
145 sigints(interactive, SIG_DFL);
146 close(fds[1]);
147 if (interactive) {
148 close(script);
150 FILE* const input = fdopen(fds[0], "r");
151 if (!input) {
152 pexit("fdopen()");
154 eval(input);
155 return 0;
157 close(fds[0]);
158 if (interactive) {
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);
164 close(tty);
166 pid_t const read_pid = try_fork();
167 if (!read_pid) {
168 sigints(interactive, SIG_DFL);
169 close(fds[0]);
170 if (interactive) {
171 TRY(setpgid, 0, eval_pid);
172 } else {
173 TRY(dup2, script, 0);
174 close(script);
176 TRY(dup2, fds[1], 1);
177 close(fds[1]);
178 char const name[] = "minish-read";
179 execl(name, name, (char const*)NULL);
180 pexit(name);
182 close(fds[1]);
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);