| #include <assert.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "fsatrace.h" |
| #include "emit.h" |
| |
| static int write_fd; |
| static char bopts[256]; |
| |
| static const char * |
| mygetenv(const char *v) |
| { |
| const char *out = getenv(v); |
| if (!out) { |
| extern char **environ; |
| size_t l = strlen(v); |
| char **p = environ; |
| while (*p) { |
| char *s = *p; |
| if (0 == strncmp(s, v, l)) { |
| if (s[l] == '=') { |
| out = s + l + 1; |
| break; |
| } |
| } |
| p++; |
| } |
| } |
| #ifdef _WIN32 |
| // Workaround, bash distributed with ghc 8.6.5 seems to discard most |
| // environment variables, pass environment variables as the first few |
| // PATH components. |
| if (!out) { |
| const char *path = getenv("PATH"); |
| if (strcmp(v, ENVOUT) == 0) { |
| static char buf[PATH_MAX]; |
| unsigned i = 0; |
| unsigned j = 0; |
| while (path[i] != ';') |
| buf[j++] = path[i++]; |
| buf[j] = 0; |
| out = buf; |
| } |
| } |
| #endif |
| return out; |
| } |
| |
| int |
| emitInit() |
| { |
| const char *fifo = mygetenv(ENV_FIFO_PATH); |
| assert(fifo); |
| if ((write_fd = open(fifo, O_CREAT | O_WRONLY)) < 0) { |
| fprintf(stderr, "emitInit failed to open %s: %s\n", |
| fifo, strerror(errno)); |
| return -1; |
| } |
| |
| const char *opts = mygetenv(ENV_OPTS); |
| memset(bopts, 0, sizeof(bopts)); |
| while (*opts) |
| bopts[(unsigned char)*opts++] = 1; |
| |
| return 0; |
| } |
| |
| int |
| emitTerm() |
| { |
| return close(write_fd); |
| } |
| |
| // Write |bufsize| bytes from |buffer| into |fd|. |
| // Return true on success, and false/errno on failure. |
| bool |
| do_full_write(int fd, const void *buffer, size_t bufsize) |
| { |
| const char *buf = (const char *)(buffer); |
| while (bufsize > 0) { |
| ssize_t ret = write(fd, buf, bufsize); |
| if (ret < 0) { |
| if (errno == EINTR) |
| continue; |
| return false; // a real error occurred. |
| } |
| buf += ret; |
| bufsize -= (size_t)(ret); |
| } |
| return true; |
| } |
| |
| void |
| emitOp(int oc, const char *op1, const char *p2) |
| { |
| uint32_t sz; |
| uint32_t s1; |
| uint32_t s2; |
| char *p; |
| const char *p1; |
| int c; |
| |
| if (!bopts[tolower(oc)]) |
| return; |
| |
| p1 = op1 ? op1 : "<unknown>"; |
| c = op1 ? oc : toupper(oc); |
| s1 = strlen(p1); |
| sz = s1 + 3; |
| if (p2) { |
| s2 = strlen(p2); |
| sz += s2 + 1; |
| } |
| |
| char buf[sz]; |
| p = buf; |
| *p++ = c; |
| *p++ = '|'; |
| memcpy(p, p1, s1); |
| p += s1; |
| if (p2) { |
| *p++ = '|'; |
| memcpy(p, p2, s2); |
| p += s2; |
| } |
| *p++ = '\n'; |
| |
| if (!do_full_write(write_fd, buf, sz)) |
| fprintf(stderr, "emitOp failed to write to fd %d: %s\n", |
| write_fd, strerror(errno)); |
| } |