Author | Jakob Wakeling <[email protected]> |
Date | 2023-12-27 00:01:35 |
Commit | a1eb486624da6a305653a383152eb1791351a6b8 |
Parent | 0811e1a731716c55924617b0c51c6aafae8e472c |
Add lineread from early ESH
Diffstat
A | README.md | | | 17 | +++++++++++++++++ |
A | src/lineread.c | | | 391 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/lineread.h | | | 10 | ++++++++++ |
A | src/main.c | | | 76 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/util/log.c | | | 36 | ++++++++++++++++++++++++++++++++++++ |
A | src/util/log.h | | | 19 | +++++++++++++++++++ |
A | src/util/optget.c | | | 92 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/util/optget.h | | | 26 | ++++++++++++++++++++++++++ |
A | src/util/util.c | | | 20 | ++++++++++++++++++++ |
A | src/util/util.h | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
10 files changed, 751 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..53a1cd7 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# ESH + +A modern shell with an emphasis on performance and usability. It is not indended +to be POSIX compliant, but may still exhibit POSIX-like behaviour. + +## Features + +At present, **ESH** is unfinished and is missing a number of basic features. + +## Usage + +To build **ESH**, from the project root, run `make build`. + +## Meta + +Copyright (C) 2023, Jakob Wakeling +All rights reserved. diff --git a/src/lineread.c b/src/lineread.c new file mode 100644 index 0000000..71050a3 --- /dev/null +++ b/src/lineread.c @@ -0,0 +1,391 @@ +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + +#include "lineread.h" +#include "util/log.h" +#include "util/util.h" + +#include <sys/ioctl.h> +#include <termios.h> +#include <unistd.h> + +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +typedef struct { + char *s; uptr sp, sl, sc; + char *prompt; uptr pl, cols; +} line; + +typedef struct { + line *a; uptr ap, ah, at, al, ac; +} hist; + +static struct termios tco, tcn; +static bool rawflag = false, ateflag = false; + +static hist h = { NULL, 0, 0, 0, 0 }; + +static char *linentty(void); +static char *lineedit(void); +static void line_esc(line *l, register int c); + +static inline void tcraw(void); +static inline void tcrestore(void); +static size_t getcols(void); +static void clearscreen(line *l); + +static void line_refresh(line *l); +static void line_reset(line *l); +static void line_move_left(line *l); +static void line_move_right(line *l); +static void line_move_word_home(line *l); +static void line_move_word_end(line *l); +static void line_move_home(line *l); +static void line_move_end(line *l); +static void line_push(line *l, char c); +static int line_insert(line *l, char c); +static void line_backspace(line *l); +static void line_delete(line *l); +static void line_delete_word_home(line *l); +static void line_delete_word_end(line *l); +static void line_delete_home(line *l); +static void line_delete_end(line *l); + +static int hist_init(hist *h); +static void hist_free(hist *h); +static void hist_move_prior(hist *h); +static void hist_move_next(hist *h); +static void hist_move_home(hist *h); +static void hist_move_end(hist *h); +static void hist_push(hist *h, line l); + +/* Read a line from stdin */ +char *lineread(void) { + if (!isatty(STDIN_FILENO)) { errno = 0; return linentty(); } + else { return lineedit(); } +} + +/* Free memory allocated by lineread */ +void linefree(void) { hist_free(&h); } + +/* Read from a non-terminal stdin */ +static char *linentty(void) { + line l; register char *r; + l.sp = 0; l.sl = 0; l.sc = 1024; + + if (!(l.s = xmalloc(l.sc * sizeof (*l.s)))) { return NULL; } + + for (register int c; (c = fgetc(stdin));) { + if (c == EOF) { if (l.sl) { break; } r = NULL; goto ret; } + else { line_push(&l, c); } continue; + } + +end:; + r = strndup(l.s, l.sl); +ret:; + free(l.s); return r; +} + +#define l h.a[h.ap] + +/* Dynamically read a line from stdin */ +static char *lineedit(void) { + register char *r; + + { + if (h.a == NULL) { if (hist_init(&h)) { return NULL; } } + + line m = { NULL, 0, 0, 1024 }; + m.prompt = "$ "; m.pl = strlen(m.prompt); + + if (!(m.cols = getcols())) { return NULL; } + if (!(m.s = xmalloc(m.sc * sizeof (*m.s)))) { return NULL; } m.s[0] = 0; + + hist_push(&h, m); + } + + tcraw(); fputs(l.prompt, stdout); + + for (register int c; (c = fgetc(stdin));) { + if (errno) { log_warn("FIXME: %s", strerror(errno)); errno = 0; } + // printf("%02X\n", c); continue; + + switch (c) { + case '\x01': { line_move_home(&l); } continue; // CTRL + A + case '\x02': { line_move_left(&l); } continue; // CTRL + B + case '\x03': { line_reset(&l); } continue; // CTRL + C + case '\x04': { r = NULL; } goto ret; // CTRL + D + case '\x05': { line_move_end(&l); } continue; // CTRL + E + case '\x06': { line_move_right(&l); } continue; // CTRL + F + case '\x07': {} continue; // IGNORE CTRL + G + case '\x08': { line_backspace(&l); } continue; // CTRL + H + case '\x09': {} continue; // IGNORE CTRL + I + case '\x0A': {} goto end; // CTRL + J + case '\x0B': { line_delete_end(&l); } continue; // CTRL + K + case '\x0C': { clearscreen(&l); } continue; // CTRL + L + case '\x0D': {} goto end; // ENTER or CTRL + M + case '\x0E': { /* Next history */ } continue; // CTRL + N + case '\x0F': {} goto end; // CTRL + O + case '\x10': { /* Prior history */ } continue; // CTRL + P + case '\x11': { /* Start output */ } continue; // CTRL + Q + case '\x12': {} continue; // IGNORE CTRL + R + case '\x13': { /* Stop output */ } continue; // CTRL + S + case '\x14': { /* Swap with prior */ } continue; // CTRL + T + case '\x15': { line_delete_home(&l); } continue; // CTRL + U + case '\x16': { /* Insert char code */ } continue; // CTRL + V + case '\x17': { line_delete_word_home(&l); } continue; // CTRL + W + case '\x18': {} continue; // IGNORE CTRL + X + case '\x19': { /* Paste deleted */ } continue; // CTRL + Y + case '\x1B': switch ((c = fgetc(stdin))) { + case 'b': { line_move_word_home(&l); } continue; // ALT + B + case 'd': { line_delete_word_end(&l); } continue; // ALT + D + case 'f': { line_move_word_end(&l); } continue; // ALT + F + case '[': switch ((c = fgetc(stdin))) { + case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': { line_esc(&l, c); } continue; + case 'A': { hist_move_prior(&h); } continue; // UP + case 'B': { hist_move_next(&h); } continue; // DOWN + case 'C': { line_move_right(&l); } continue; // RIGHT + case 'D': { line_move_left(&l); } continue; // LEFT + case 'F': { line_move_end(&l); } continue; // END + case 'H': { line_move_home(&l); } continue; // HOME + default: {} continue; + } + default: {} continue; + } + case '\x7F': { line_backspace(&l); } continue; // BACKSPACE + default: { line_insert(&l, c); } continue; + } + } + +end:; + r = strndup(l.s, l.sl); +ret:; + tcrestore(); fputc('\n', stdout); return r; +} + +#undef l // h.a[h.ap] + +/* Handle an extended ^[ sequence */ +static void line_esc(line *l, register int c) { + switch (c) { + case '2': switch ((c = fgetc(stdin))) { + case '~': { /* Insert char code */ } return; // INSERT + } + case '3': switch ((c = fgetc(stdin))) { + case '~': { line_delete(l); } return; // DELETE + } + case '5': switch ((c = fgetc(stdin))) { + case '~': { hist_move_home(&h); } return; // PAGE UP + } + case '6': switch ((c = fgetc(stdin))) { + case '~': { hist_move_end(&h); } return; // PAGE DOWN + } + } +} + +/* Put stdin into raw mode */ +static inline void tcraw(void) { + if (rawflag) { return; } + + tcgetattr(STDIN_FILENO, &tco); tcn = tco; + if (!ateflag) { atexit(tcrestore); ateflag = true; } + + /* No break, no CR to NL, no parity check, no strip bit, no flow control */ + tcn.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* No post process, 8-bit chars */ + tcn.c_oflag &= ~(OPOST); tcn.c_cflag |= (CS8); + /* Canonical off, echo off, no extensions, no signal characters */ + tcn.c_lflag &= ~(ICANON | ECHO | IEXTEN | ISIG); + /* Read every byte with no delay */ + tcn.c_cc[VMIN] = 1; tcn.c_cc[VTIME] = 0; + + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tcn); rawflag = true; return; +} + +/* Restore stdin to canonical mode */ +static inline void tcrestore(void) { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tco); rawflag = false; return; +} + +/* Get the number of columns in the terminal */ +static size_t getcols(void) { + struct winsize ws; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) { return 0; } + return ws.ws_col; +} + +/* Clear the screen */ +static void clearscreen(line *l) { + fputs("\x1B[H\x1B[2J", stdout); line_refresh(l); return; +} + +/* Refresh line */ +static void line_refresh(line *l) { + fputs("\r\x1B[0K", stdout); + fputs(l->prompt, stdout); fputs(l->s, stdout); + fprintf(stdout, "\r\x1B[%zuC", l->pl + l->sp); + return; +} + +/* Reset line */ +static void line_reset(line *l) { + l->s[0] = 0; l->sp = 0; l->sl = 0; + fputs("^C\n", stdout); line_refresh(l); return; +} + +/* Move cursor left */ +static void line_move_left(line *l) { + if (l->sp) { --l->sp; fputs("\x1B[D", stdout); } return; +} + +/* Move cursor right */ +static void line_move_right(line *l) { + if (l->sp != l->sl) { ++l->sp; fputs("\x1B[C", stdout); } return; +} + +/* Move cursor to the word home */ +static void line_move_word_home(line *l) { + for (; l->sp && l->s[l->sp - 1] == ' '; --l->sp); + for (; l->sp && l->s[l->sp - 1] != ' '; --l->sp); + line_refresh(l); return; +} + +/* Move cursor to the word end */ +static void line_move_word_end(line *l) { + for (; l->sp != l->sl && l->s[l->sp] == ' '; ++l->sp); + for (; l->sp != l->sl && l->s[l->sp] != ' '; ++l->sp); + line_refresh(l); return; +} + +/* Move cursor to the line home */ +static void line_move_home(line *l) { + l->sp = 0; line_refresh(l); return; +} + +/* Move cursor to the line end */ +static void line_move_end(line *l) { + l->sp = l->sl; line_refresh(l); return; +} + +/* Push character onto end of line */ +static void line_push(line *l, char c) { + if (l->sl + 1 > l->sc) { l->sc *= 2; + l->s = xrealloc(l->s, l->sc * sizeof (*l->s)); + } l->s[l->sl] = c; l->s[++l->sl] = 0; return; +} + +/* Insert character at cursor */ +static int line_insert(line *l, char c) { + if (l->sl + 1 == l->sc) { /* TODO expand string */ } + if (l->sp == l->sl) { // Cursor is at line end + l->s[l->sp++] = c; l->s[++l->sl] = 0; + return fputc(c, stdout); + } + else { + memmove(l->s + l->sp + 1, l->s + l->sp, l->sl - l->sp); + l->s[l->sp++] = c; l->s[++l->sl] = 0; line_refresh(l); + } return c; +} + +/* Delete character before cursor */ +static void line_backspace(line *l) { + if (l->sp) { + memmove(l->s + l->sp - 1, l->s + l->sp, l->sl - l->sp); + --l->sp; l->s[--l->sl] = 0; line_refresh(l); + } return; +} + +/* Delete character at cursor */ +static void line_delete(line *l) { + if (l->sp != l->sl) { + memmove(l->s + l->sp, l->s + l->sp + 1, l->sl - l->sp); + l->s[--l->sl] = 0; line_refresh(l); + } return; +} + +/* Delete the word preceeding the cursor */ +static void line_delete_word_home(line *l) { + size_t p = l->sp; + + for (; l->sp && l->s[l->sp - 1] == ' '; --l->sp); + for (; l->sp && l->s[l->sp - 1] != ' '; --l->sp); + + memmove(l->s + l->sp, l->s + p, l->sl - p + 1); + l->sl -= p - l->sp; line_refresh(l); return; +} + +/* Delete the word following the cursor */ +static void line_delete_word_end(line *l) { + size_t p = l->sp; + + for (; p != l->sl && l->s[p] == ' '; ++p); + for (; p != l->sl && l->s[p] != ' '; ++p); + + memmove(l->s + l->sp, l->s + p, l->sl - p + 1); + l->sl -= p - l->sp; line_refresh(l); return; +} + +/* Delete characters from cursor to home */ +static void line_delete_home(line *l) { + memmove(l->s, l->s + l->sp, l->sl - l->sp + 1); + l->sl -= l->sp; l->sp = 0; line_refresh(l); return; +} + +/* Delete characters from cursor to end */ +static void line_delete_end(line *l) { + l->s[(l->sl = l->sp)] = 0; line_refresh(l); return; +} + +/* Initialise history */ +static int hist_init(hist *h) { + /* FIXME do not hardcode history size */ + h->ap = 0; h->ah = 0; h->at = 0; h->al = 0; h->ac = 1000 + 1; + if (!(h->a = xcalloc(h->ac, sizeof (*h->a)))) { return 1; } + /* TODO load history from file */ return 0; +} + +/* Free history */ +static void hist_free(hist *h) { + for (size_t i = 0; i != h->ac; ++i) { free(h->a[i].s); } free(h->a); +} + +/* Move backwards in history */ +static void hist_move_prior(hist *h) { + if (h->ap != h->ah) { h->ap == 0 ? h->ap = h->ac - 1 : --h->ap; } + line_refresh(&h->a[h->ap]); return; +} + +/* Move forwards in history */ +static void hist_move_next(hist *h) { + register size_t an = h->ap == h->ac - 1 ? 0 : h->ap + 1; + if (an != h->at) { h->ap = an; } + line_refresh(&h->a[h->ap]); return; +} + +/* Move to the start of history */ +static void hist_move_home(hist *h) { + /* TODO */ + h->ap = h->ah; line_refresh(&h->a[h->ap]); return; +} + +/* Move to the end of history */ +static void hist_move_end(hist *h) { + /* TODO */ + // h->ap = h->at; lineRefresh(&h->a[h->ap]); return; +} + +/* Push a line onto the end of history */ +static void hist_push(hist *h, line l) { + if (h->al != h->ac) { + ++h->al; h->ap = h->at; if (++h->at == h->ac) { h->at = 0; } + } + else { h->ap = h->at; if (++h->at == h->ac) { h->at = 0; } h->ah = h->at; } + + free(h->a[h->ap].s); h->a[h->ap] = l; return; +} diff --git a/src/lineread.h b/src/lineread.h new file mode 100644 index 0000000..a79c0af --- /dev/null +++ b/src/lineread.h @@ -0,0 +1,10 @@ +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + +#ifndef LINEREAD_H_TVNWORP2 +#define LINEREAD_H_TVNWORP2 + +extern char *lineread(); +extern void linefree(); + +#endif // LINEREAD_H_TVNWORP2 diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..de6382e --- /dev/null +++ b/src/main.c @@ -0,0 +1,76 @@ +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#include "lineread.h" +#include "util/log.h" +#include "util/optget.h" +#include "util/util.h" + +#include <errno.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static void reset(int); + +static const char *const help; +static const char *const version; + +static jmp_buf jmp; +static sig_atomic_t jmpflag = false; + +int main(int, char *av[]) { + struct opt opt = OPTGET_INIT; opt.str = ""; opt.lops = (struct lop[]){ + { "help", ARG_NUL, 256 }, + { "version", ARG_NUL, 257 }, + { "debug", ARG_NUL, 258 }, + { NULL, 0, 0 }, + }; + + struct {} args = {}; + + for (int c; (c = optget(&opt, av, 1)) != -1;) { + switch (c) { + case 256: { fputs(help, stdout); } return 0; + case 257: { fputs(version, stdout); } return 0; + case 258: { __debug = true; } break; + default: { return -1; } + } + } + + signal(SIGINT, &reset); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); + atexit(&linefree); + + do { + if (sigsetjmp(jmp, 1)) { fputc('\n', stdout); } jmpflag = true; + + char *line = lineread(); + if (!line) { if (errno) { log_warn("lineread: %s", strerror(errno)); } break; } + + printf("%s", line); free(line); + } while (true); + + return __warned; +} + +static void reset(int) { + if (jmpflag) { siglongjmp(jmp, 1); } +} + +static const char *const help = + "ESH - Executive Shell\n" + "Usage:\n" + " esh [--debug]\n" + "Options:\n" + " --help Display help information\n" + " --version Display version information\n" + " --debug Enable debug logging\n" +; + +static const char *const version = + "ESH, version " PROJECT_VERSION "\n" + "Copyright (C) 2023, Jakob Wakeling\n" + "All rights reserved.\n" +; diff --git a/src/util/log.c b/src/util/log.c new file mode 100644 index 0000000..1eaf8d8 --- /dev/null +++ b/src/util/log.c @@ -0,0 +1,36 @@ +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#include "log.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +bool __debug, __warned; + +[[noreturn]] void log_abort(const char *restrict format, ...) { + fflush(stderr); va_list args; va_start(args, format); + vfprintf(stderr, format, args); fputc('\n', stderr); + va_end(args); abort(); +} + +[[noreturn]] void log_fatal(int status, const char *restrict format, ...) { + fflush(stderr); va_list args; va_start(args, format); + vfprintf(stderr, format, args); fputc('\n', stderr); + va_end(args); exit(status); +} + +void log_print(const char *restrict format, ...) { + fflush(stderr); va_list args; va_start(args, format); + vfprintf(stderr, format, args); fputc('\n', stderr); + va_end(args); +} + +void log_debug(const char *restrict format, ...) { + if (!__debug) { return; } + + fflush(stderr); va_list args; va_start(args, format); + vfprintf(stderr, format, args); fputc('\n', stderr); + va_end(args); +} diff --git a/src/util/log.h b/src/util/log.h new file mode 100644 index 0000000..8a5a09e --- /dev/null +++ b/src/util/log.h @@ -0,0 +1,19 @@ +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#ifndef UTIL_LOG_H_MNZFBC4G +#define UTIL_LOG_H_MNZFBC4G + +#define log_warn(format, ...) do { \ + log_print(format __VA_OPT__(,) __VA_ARGS__); \ + __warned = true; \ +} while (0) + +extern bool __debug, __warned; + +[[noreturn]] extern void log_abort(const char *restrict format, ...); +[[noreturn]] extern void log_fatal(int status, const char *restrict format, ...); +extern void log_print(const char *restrict format, ...); +extern void log_debug(const char *restrict format, ...); + +#endif // UTIL_LOG_H_MNZFBC4G diff --git a/src/util/optget.c b/src/util/optget.c new file mode 100644 index 0000000..a21e736 --- /dev/null +++ b/src/util/optget.c @@ -0,0 +1,92 @@ +// Copyright (C) 2020, Jakob Wakeling +// MIT Licence + +#include "optget.h" + +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +#define cur av[opt->ind] + +const struct opt OPTGET_INIT = { 1, 0, 1, 0, NULL, NULL, NULL, NULL }; + +static inline void permute(char **av, int i, int n); + +int optget(struct opt *opt, char *av[], int flags) { + if (flags & 1) { + for (; cur && (cur[0] != '-' || cur[1] == 0); ++opt->ind, ++opt->nop); + if (!cur) { opt->ind -= opt->nop; opt->nop = 0; return -1; } + } + else if (!cur || (cur[0] != '-' || cur[1] == 0)) { return -1; } + + int optind = opt->ind, optret; + + if (cur[1] == '-') { + if (cur[2] == 0) { if (opt->nop) { + permute(av, opt->ind++, opt->nop); + opt->ind -= opt->nop; opt->nop = 0; + } else { ++opt->ind; } return -1; + } + + int optend, lop; optret = '?'; opt->opt = 0; opt->lop = cur; + if (!opt->lops) { goto nol; } + for (optend = 2; cur[optend] != '=' && cur[optend] != 0; ++optend); + + for (lop = 0; opt->lops[lop].str; ++lop) { + if (strncmp(&cur[2], opt->lops[lop].str, (size_t)optend - 2) == 0) { + if (!opt->lops[lop].str[optend - 2]) { + optret = opt->opt = opt->lops[lop].val; break; + } + } + } + + if (opt->lops[lop].arg > ARG_NUL) { + if (cur[optend]) { opt->arg = &cur[optend + 1]; } + else if (av[opt->ind + 1]) { opt->arg = av[++opt->ind]; } + else { + if (opt->lops[lop].arg == ARG_REQ) { optret = ':'; } + opt->arg = NULL; + } + } + else { opt->arg = NULL; } + +nol: opt->pos = 0; + } + else { + optret = opt->opt = cur[opt->pos++]; opt->lop = NULL; + const char *optchr = strchr(opt->str, opt->opt); + + if (!optchr) { optret = '?'; } + else if (optchr[1] == ':') { + if (cur[opt->pos]) { opt->arg = &cur[opt->pos]; } + else if (av[opt->ind + 1]) { opt->arg = av[++opt->ind]; } + else { opt->arg = NULL; optret = ':'; } + opt->pos = 0; + } + else { opt->arg = NULL; } + } + + if (!opt->pos || !cur[opt->pos]) { + ++opt->ind; opt->pos = 1; + if (opt->nop) for (; optind < opt->ind; ++optind) { + permute(av, optind, opt->nop); + } + } + + if (optret == '?' && opt->str[0] != ':') { + if (opt->opt) { fprintf(stderr, "%c: invalid option", opt->opt); } + else if (opt->lop) { fprintf(stderr, "%s: invalid option", opt->lop); } + } + if (optret == ':' && opt->str[0] != ':') { + if (opt->opt) { fprintf(stderr, "%c: option requires argument", opt->opt); } + else if (opt->lop) { fprintf(stderr, "%s: option requires argument", opt->lop); } + } + + return optret; +} + +static inline void permute(char **av, int i, int n) { + char *a = av[i]; memmove(&av[i - n + 1], &av[i - n], n * sizeof (av)); + av[i - n] = a; return; +} diff --git a/src/util/optget.h b/src/util/optget.h new file mode 100644 index 0000000..dba5ab8 --- /dev/null +++ b/src/util/optget.h @@ -0,0 +1,26 @@ +// Copyright (C) 2020, Jakob Wakeling +// MIT Licence + +#ifndef UTIL_OPTGET_H_ZPCLTG8D +#define UTIL_OPTGET_H_ZPCLTG8D + +#define ARG_NUL 0 +#define ARG_REQ 1 +#define ARG_OPT 2 + +struct lop { + char *str; + int arg, val; +}; + +struct opt { + int ind, opt, pos, nop; + char *arg, *lop, *str; + struct lop *lops; +}; + +extern const struct opt OPTGET_INIT; + +extern int optget(struct opt *opt, char *av[], int flags); + +#endif // UTIL_OPTGET_H_ZPCLTG8D diff --git a/src/util/util.c b/src/util/util.c new file mode 100644 index 0000000..3e58f93 --- /dev/null +++ b/src/util/util.c @@ -0,0 +1,20 @@ +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#include "log.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +void *_xmalloc(size_t size, const char *file, int line) { + void *p = malloc(size); if (!p) { log_abort("%s:%d: %s", file, line, strerror(errno)); } return p; +} + +void *_xcalloc(size_t nmemb, size_t size, const char *file, int line) { + void *p = calloc(nmemb, size); if (!p) { log_abort("%s:%d: %s", file, line, strerror(errno)); } return p; +} + +void *_xrealloc(void *ptr, size_t size, const char *file, int line) { + void *p = realloc(ptr, size); if (!p) { log_abort("%s:%d: %s", file, line, strerror(errno)); } return p; +} diff --git a/src/util/util.h b/src/util/util.h new file mode 100644 index 0000000..216aaaa --- /dev/null +++ b/src/util/util.h @@ -0,0 +1,64 @@ +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#ifndef UTIL_UTIL_H_WIAX91EM +#define UTIL_UTIL_H_WIAX91EM + +#include <float.h> +#include <stddef.h> +#include <stdint.h> + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef uintptr_t uptr; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +typedef intptr_t sptr; + +typedef float f32; +typedef double f64; +typedef long double f128; + +#define U8_MIN UINT8_MIN +#define U8_MAX UINT8_MAX +#define U16_MIN UINT16_MIN +#define U16_MAX UINT16_MAX +#define U32_MIN UINT32_MIN +#define U32_MAX UINT32_MAX +#define U64_MIN UINT64_MIN +#define U64_MAX UINT64_MAX +#define UPTR_MIN UINTPTR_MIN +#define UPTR_MAX UINTPTR_MAX + +#define S8_MIN INT8_MIN +#define S8_MAX INT8_MAX +#define S16_MIN INT16_MIN +#define S16_MAX INT16_MAX +#define S32_MIN INT32_MIN +#define S32_MAX INT32_MAX +#define S64_MIN INT64_MIN +#define S64_MAX INT64_MAX +#define SPTR_MIN INTPTR_MIN +#define SPTR_MAX INTPTR_MAX + +#define F32_MIN FLT_MIN +#define F32_MAX FLT_MAX +#define F64_MIN DBL_MIN +#define F64_MAX DBL_MAX +#define F128_MIN LDBL_MIN +#define F128_MAX LDBL_MAX + +#define xmalloc(size) _xmalloc(size, __FILE__, __LINE__) +#define xcalloc(nmemb, size) _xcalloc(nmemb, size, __FILE__, __LINE__) +#define xrealloc(ptr, size) _xrealloc(ptr, size, __FILE__, __LINE__) + +extern void *_xmalloc(size_t size, const char *file, int line); +extern void *_xcalloc(size_t nmemb, size_t size, const char *file, int line); +extern void *_xrealloc(void *ptr, size_t size, const char *file, int line); + +#endif // UTIL_UTIL_H_WIAX91EM