summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-02-16 01:14:10 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2021-02-16 01:14:10 -0500
commitacfbdf2359f533f88e284b5f5219b5c671992fc7 (patch)
tree784b7694e86634adeb8a0ec819c1745b5c09a0f9
parent62cf95e9d3ad2b3b58cf3c67ad1a2b923d36a9b7 (diff)
processes
-rw-r--r--arr.c11
-rw-r--r--main.c56
-rw-r--r--process-posix.c119
-rw-r--r--process.h26
-rw-r--r--ted.h2
5 files changed, 210 insertions, 4 deletions
diff --git a/arr.c b/arr.c
index 0e545f7..4e37892 100644
--- a/arr.c
+++ b/arr.c
@@ -157,11 +157,16 @@ static void arr_set_len_(void **arr, size_t member_size, size_t n) {
}
}
-static void arr_remove_(void *arr, size_t member_size, size_t index) {
+static void *arr_remove_(void *arr, size_t member_size, size_t index) {
ArrHeader *hdr = arr_hdr_(arr);
assert(index < hdr->len);
memmove((char *)arr + index * member_size, (char *)arr + (index+1) * member_size, (hdr->len - (index+1)) * member_size);
- --hdr->len;
+ if (--hdr->len == 0) {
+ free(hdr);
+ return NULL;
+ } else {
+ return arr;
+ }
}
#ifdef __cplusplus
@@ -189,7 +194,7 @@ static void arr_remove_(void *arr, size_t member_size, size_t index) {
// the newly-added elements are zero-initialized.
#define arr_qsort(a, cmp) qsort((a), arr_len(a), sizeof *(a), (cmp))
#define arr_remove_last(a) do { assert(a); if (--arr_hdr_(a)->len == 0) arr_free(a); } while (0)
-#define arr_remove(a, i) arr_remove_((a), sizeof *(a), (i))
+#define arr_remove(a, i) (void)((a) = arr_remove_((a), sizeof *(a), (i)))
#define arr_pop_last(a) ((a)[--arr_hdr_(a)->len])
#define arr_size_in_bytes(a) (arr_len(a) * sizeof *(a))
#define arr_lastp(a) ((a) ? &(a)[arr_len(a)-1] : NULL)
diff --git a/main.c b/main.c
index 81a9d90..8c6308b 100644
--- a/main.c
+++ b/main.c
@@ -42,6 +42,13 @@ no_warn_end
#include "arr.c"
#include "math.c"
+#if _WIN32
+#include "process-win.c"
+#elif __unix__
+#include "process-posix.c"
+#else
+#error "Unrecognized operating system."
+#endif
#include "text.h"
#include "command.h"
@@ -110,6 +117,11 @@ static void ted_update_window_dimensions(Ted *ted) {
gl_window_height = ted->window_height = (float)h;
}
+void chld_handler(int x, siginfo_t *y, void *z) {
+ (void)x; (void)y; (void)z;
+ printf("got singal!\n");
+}
+
#if _WIN32
INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR lpCmdLine, INT nCmdShow) {
@@ -134,6 +146,50 @@ int main(int argc, char **argv) {
#endif
setlocale(LC_ALL, ""); // allow unicode
+ process_init();
+
+
+ char *program = "/usr/bin/make";
+ char *args[] = {program, NULL};
+ Process *process = process_exec(program, args);
+ if (!process) {
+ printf("Error: %s\n", process_geterr());
+ return EXIT_FAILURE;
+ }
+ #if 1
+ {
+ i64 bytes = 0;
+ char buf[256];
+ while (1) {
+ bytes = process_read(process, buf, sizeof buf);
+ if (bytes == -2) {
+ printf("Error: %s\n", process_geterr());
+ } else if (bytes == -1) {
+ usleep(1000);
+ } else if (bytes == 0) {
+ break;
+ } else {
+ fwrite(buf, 1, (size_t)bytes, stdout);
+ }
+ }
+ }
+ {
+ char message[256];
+ while (1) {
+ int status = process_check_status(process, message, sizeof message);
+ if (status == -1) {
+ printf("%s!!!\n",message);
+ break;
+ } else if (status == +1) {
+ printf("%s\n", message);
+ break;
+ } else {
+ usleep(1000);
+ }
+ }
+ }
+ #endif
+ return 0;
// read command-line arguments
char const *starting_filename = NULL;
diff --git a/process-posix.c b/process-posix.c
new file mode 100644
index 0000000..53363b6
--- /dev/null
+++ b/process-posix.c
@@ -0,0 +1,119 @@
+#include "process.h"
+
+#include <fcntl.h>
+#include <sys/wait.h>
+
+struct Process {
+ pid_t pid;
+ int pipe;
+ bool stopped;
+ int exit_status; // INT_MAX if process was terminated
+};
+
+static char process_error[64];
+static Process *processes;
+
+static void process_sigaction_sigchld(int signal, siginfo_t *info, void *context) {
+ (void)context;
+ if (signal == SIGCHLD) {
+ pid_t pid = info->si_pid;
+ arr_foreach_ptr(processes, Process, process) {
+ if (process->pid == pid) {
+ process->stopped = true;
+ if (info->si_code == CLD_EXITED)
+ process->exit_status = info->si_status;
+ else
+ process->exit_status = INT_MAX;
+ }
+ }
+ }
+}
+
+void process_init(void) {
+ struct sigaction act = {0};
+ act.sa_sigaction = process_sigaction_sigchld;
+ act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, NULL);
+}
+
+
+
+Process *process_exec(char const *program, char **argv) {
+ Process *proc = NULL;
+
+ int pipefd[2];
+ if (pipe(pipefd) == 0) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child process
+ // send stdout and stderr to pipe
+ dup2(pipefd[1], STDOUT_FILENO);
+ dup2(pipefd[1], STDERR_FILENO);
+ close(pipefd[0]);
+ close(pipefd[1]);
+ if (execv(program, argv) == -1) {
+ dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno));
+ exit(127);
+ }
+ } else if (pid > 0) {
+ proc = arr_addp(processes);
+ // parent process
+ close(pipefd[1]);
+ fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); // set pipe to non-blocking
+ proc->pid = pid;
+ proc->pipe = pipefd[0];
+ }
+ } else {
+ strbuf_printf(process_error, "%s", strerror(errno));
+ }
+ return proc;
+}
+
+char const *process_geterr(void) {
+ return *process_error ? process_error : NULL;
+}
+
+long long process_read(Process *proc, char *data, size_t size) {
+ assert(proc->pipe);
+ ssize_t bytes_read = read(proc->pipe, data, size);
+ if (bytes_read >= 0) {
+ return (long long)bytes_read;
+ } else if (errno == EAGAIN) {
+ return -1;
+ } else {
+ strbuf_printf(process_error, "%s", strerror(errno));
+ return -2;
+ }
+
+}
+
+void process_stop(Process *proc) {
+ kill(proc->pid, SIGKILL);
+ // get rid of zombie process
+ waitpid(proc->pid, NULL, 0);
+ close(proc->pipe);
+ for (u32 i = 0; i < arr_len(processes); ++i)
+ if (processes[i].pid == proc->pid)
+ arr_remove(processes, i);
+}
+#include <sys/resource.h>
+int process_check_status(Process *proc, char *message, size_t message_size) {
+ if (proc->stopped) {
+ int status = proc->exit_status;
+ process_stop(proc);
+
+ switch (status) {
+ case 0:
+ if (message) str_printf(message, message_size, "exited successfully");
+ return +1;
+ case INT_MAX:
+ if (message) str_printf(message, message_size, "terminated by a signal");
+ return -1;
+ default:
+ if (message) str_printf(message, message_size, "exited with code %d", status);
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+}
diff --git a/process.h b/process.h
new file mode 100644
index 0000000..2b34120
--- /dev/null
+++ b/process.h
@@ -0,0 +1,26 @@
+// like popen, but allowing for non-blocking reads
+#ifndef PROCESS_H_
+#define PROCESS_H_
+
+typedef struct Process Process;
+
+// returns NULL on failure
+Process *process_exec(char const *program, char **argv);
+// returns the error last error produced, or NULL if there was no error.
+char const *process_geterr(void);
+// returns:
+// -2 on error
+// -1 if no data is available right now
+// 0 on end of file
+// or a positive number indicating the number of bytes read to data (at most size)
+long long process_read(Process *process, char *data, size_t size);
+// Checks if the process has exited. Returns:
+// -1 if the process returned a non-zero exit code, or got a signal.
+// 1 if the process exited successfully
+// 0 if the process hasn't exited.
+// If message is not NULL, it will be set to a description of what happened (e.g. "exited successfully")
+int process_check_status(Process *process, char *message, size_t message_size);
+// kills process if still running, and frees data associated with it.
+void process_stop(Process *process);
+
+#endif
diff --git a/ted.h b/ted.h
index a1150f3..0341ec1 100644
--- a/ted.h
+++ b/ted.h
@@ -112,7 +112,7 @@ typedef struct {
} Line;
// this refers to replacing prev_len characters (found in prev_text) at pos with new_len characters
-typedef struct BufferEdit {
+typedef struct {
bool chain; // should this + the next edit be treated as one?
BufferPos pos;
u32 new_len;