j5

aditionnal tools for the s6 service manager
Log | Files | Refs | README

commit 2dc59dbb440cf97b20bd52f93e3393e4e5033274
Author: Josuah Demangeon <mail@josuah.net>
Date:   Sat, 26 Jan 2019 22:29:12 +0100

initial commit

Diffstat:
A.gitignore | 2++
AMakefile | 32++++++++++++++++++++++++++++++++
AREADME | 14++++++++++++++
Aarg.h | 16++++++++++++++++
Abuffer.c | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuffer.h | 40++++++++++++++++++++++++++++++++++++++++
Aenv.h | 10++++++++++
Aenv/test1 | 1+
Aenv/test2 | 0
Aenv/test3 | 0
Afmt.c | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afmt.h | 21+++++++++++++++++++++
Agenalloc.h | 23+++++++++++++++++++++++
Ahier.c | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahier.h | 9+++++++++
Aj5-envdir.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alog.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alog.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amake-a | 4++++
Amake-bin | 2++
Amake-h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Amake-man | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amake-mk | 8++++++++
Amake-o | 2++
Amem.c | 22++++++++++++++++++++++
Amem.h | 19+++++++++++++++++++
Aopen.h | 12++++++++++++
Aopenreadclose.c | 28++++++++++++++++++++++++++++
Aopenreadclose.h | 8++++++++
Aopenwriteclose.c | 27+++++++++++++++++++++++++++
Aopenwriteclose.h | 8++++++++
Astat.c | 27+++++++++++++++++++++++++++
Astat.h | 8++++++++
Astr.c | 15+++++++++++++++
Astr.h | 18++++++++++++++++++
Astralloc.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astralloc.h | 38++++++++++++++++++++++++++++++++++++++
37 files changed, 1555 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +*.o +j5-*[!.]? diff --git a/Makefile b/Makefile @@ -0,0 +1,32 @@ +bin = j5-envdir +obj = openwriteclose.o openreadclose.o buffer.o fmt.o stat.o hier.o log.o mem.o str.o stralloc.o + +.PHONY: all +all: ${bin} + +.PHONY: clean +clean: + rm -f *.o leapsecs leapsecs.dat + +.PHONY: install +install: all + mkdir -p ${PREFIX}/bin + cp ${bin} ${PREFIX}/bin + +j5-envdir: j5-envdir.o ${obj} + ./make-bin $@ j5-envdir.o ${obj} + +.c.o: + ./make-o $< + +buffer.o: buffer.c buffer.h mem.h stralloc.h +fmt.o: fmt.c fmt.h str.h +hier.o: hier.c hier.h open.h str.h stralloc.h +j5-envdir.o: j5-envdir.c openreadclose.h openwriteclose.h log.h str.h mem.h arg.h stat.h stralloc.h genalloc.h +log.o: log.c buffer.h log.h +mem.o: mem.c mem.h +openreadclose.o: openreadclose.c stralloc.h open.h +openwriteclose.o: openwriteclose.c stralloc.h open.h +stat.o: stat.c stat.h +str.o: str.c str.h +stralloc.o: stralloc.c mem.h fmt.h str.h stralloc.h diff --git a/README b/README @@ -0,0 +1,14 @@ +import PACK_LOG +import PACK_DEF +import PACK_OUT + +export PREFIX +export one variable per dependency with all non-alnum replaced by '_'. + +copy ${PACK_DEF}/${pack}/${version}/data and ${PACK_DEF}/${pack}/default/data + to ${PACK_OUT}/ + +read ${PACK_DEF}/${pack}/default/version + +read ${PACK_LOG}/${pack}/${version}/dependencies +read ${PACK_LOG}/${pack}/default/dependencies diff --git a/arg.h b/arg.h @@ -0,0 +1,16 @@ +#ifndef ARG_H +#define ARG_H + +extern char *arg_0; + +#define ARG_BEGIN \ + arg_0 = *argv; (void)argc; \ + while ((*++argv) && (*argv)[0] == '-' && (*argv)[1] != '\0') { \ + if ((*argv)[1] == '-' && (*argv)[2] == '\0') { ++argv; break; } \ + for (int brk = 0; !brk && *++*argv != '\0' ;) { switch (**argv) + +#define ARG_END } } + +#define ARG ((*++*argv != '\0' || *++argv != NULL) ? ((brk = 1), *argv) : (usage(arg_0), NULL)) + +#endif diff --git a/buffer.c b/buffer.c @@ -0,0 +1,176 @@ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include "buffer.h" +#include "mem.h" +#include "stralloc.h" + +char buffer_0_buf[8192]; +static buffer buffer_0_x = BUFFER_INIT(read, 0, buffer_0_buf, sizeof buffer_0_buf); +buffer *buffer_0 = &buffer_0_x; + +char buffer_1_buf[8192]; +static buffer buffer_1_x = BUFFER_INIT(write, 1, buffer_1_buf, sizeof buffer_1_buf); +buffer *buffer_1 = &buffer_1_x; + +char buffer_2_buf[256]; +static buffer buffer_2_x = BUFFER_INIT(write, 2, buffer_2_buf, sizeof buffer_2_buf); +buffer *buffer_2 = &buffer_2_x; + +ssize_t +buffer_fill(buffer *b) +{ + ssize_t r; + + if (b->n < b->a) return 0; + if (b->p) return 0; // Eat your spinach first! + r = buffer_readall(b->op, b->fd, b->x, b->n); + if (r == -1) return -1; + b->p = b->n = r; + return r; +} + +ssize_t +buffer_flush(buffer *b) +{ + ssize_t w; + + w = buffer_writeall(b->op, b->fd, b->x, b->p); + if (w == -1) return -1; + b->p = 0; + return w; +} + +static size_t +buffer_getthis(buffer *b, char *buf, size_t len) +{ + if (len > b->p) len = b->p; + mem_copy(buf, b->x + (b->n - b->p), len); + b->p -= len; + return len; +} + +ssize_t +buffer_get(buffer *b, char *buf, size_t len) +{ + ssize_t r; + size_t n = 0; + + if (b->p > 0) { + n += r = buffer_getthis(b, buf, len); + len -= r; buf += r; + } + if (len == 0) return n; + if (len > b->n && b->n == b->a) { + n += r = buffer_readall(b->op, b->fd, buf + n, len); + if (r == -1) return -1; + } else { + r = buffer_fill(b); if (r == -1) return -1; + n += buffer_getthis(b, buf, len); + } + + return n; +} + +void +buffer_init(buffer *b, ssize_t (*op)(), int fd, char *s, size_t n) +{ + b->fd = fd; + b->op = op; + b->x = s; + b->a = b->n = n; + b->p = 0; +} + +int +buffer_put(buffer *b, const char *buf, size_t len) +{ + if (len > b->n - b->p) if (buffer_flush(b) == -1) return -1; + if (len > b->n) return buffer_write(b->op, b->fd, buf, len) < 0 ? -1 : 0; + mem_copy(b->x + b->p, buf, len); + b->p += len; + return 0; +} + +ssize_t +buffer_write(ssize_t (*op)(), int fd, char const *buf, size_t len) +{ + ssize_t w; + + while (1) { + w = op(fd, buf, len); + if (w > 0) break; + if (errno == EINTR) continue; + if (errno == EAGAIN) continue; + if (errno == EWOULDBLOCK) continue; + break; + } + return w; +} + +ssize_t +buffer_read(ssize_t (*op)(), int fd, char *buf, size_t len) +{ + ssize_t r; + + while (1) { + r = op(fd, buf, len); + if (r > 0) break; + if (errno == EINTR) continue; + if (errno == EAGAIN) continue; + if (errno == EWOULDBLOCK) continue; + break; + } + return r; +} + +ssize_t +buffer_writeall(ssize_t (*op)(), int fd, char const *buf, size_t len) +{ + ssize_t w = 0; + size_t n = 0; + + while (len > 0) { + w = buffer_write(op, fd, buf, len); + if (w == -1) return -1; + if (w == 0) break; + buf += w; len -= w; n += w; + } + return n; +} + +ssize_t +buffer_readall(ssize_t (*op)(), int fd, char *buf, size_t len) +{ + ssize_t r = 0; + size_t n = 0; + + while (len > 0) { + r = buffer_read(op, fd, buf, len); + if (r == -1) return -1; + if (r == 0) break; + buf += r; len -= r; n += r; + } + return n; +} + +ssize_t +buffer_gettoken(buffer *b, stralloc *sa, char x) +{ + ssize_t r; + ssize_t n = 0; + char c; + + while ((r = buffer_getc(b, &c)) > 0) { + stralloc_catb(sa, &c, 1); + if (c == x) return n; + ++n; + } + return (r == 0) ? n : r; +} + +ssize_t +buffer_putc(buffer *b, char c) +{ + return buffer_put(b, &c, 1); +} diff --git a/buffer.h b/buffer.h @@ -0,0 +1,40 @@ +#ifndef BUFFER_H +#define BUFFER_H + +#include <stddef.h> +#include <unistd.h> +#include "stralloc.h" +#include "str.h" + +typedef struct { + char *x; + size_t a; // allocated size + size_t n; // used size, (n != a) means end of feed + size_t p; // amount of data held in + int fd; + ssize_t (*op)(); +} buffer; + +#define BUFFER_INIT(op, fd, x, n) { (x), (n), (n), (0), (fd), (op) } + +extern buffer *buffer_0; +extern buffer *buffer_1; +extern buffer *buffer_2; + +#define buffer_puts(b, s) buffer_put(b, s, str_len(s)) +#define buffer_getc(b, p) buffer_get(b, p, 1) +#define buffer_getline(b, sa) buffer_gettoken(b, sa, '\n') + +int buffer_put(buffer *, const char *, size_t); +ssize_t buffer_fill(buffer *); +ssize_t buffer_flush(buffer *); +ssize_t buffer_get(buffer *, char *, size_t); +ssize_t buffer_gettoken(buffer *, stralloc *, char); +ssize_t buffer_putc(buffer *, char); +ssize_t buffer_read(ssize_t (*op)(), int, char *, size_t); +ssize_t buffer_readall(ssize_t (*op)(), int, char *, size_t); +ssize_t buffer_write(ssize_t (*op)(), int, char const *, size_t); +ssize_t buffer_writeall(ssize_t (*op)(), int, char const *, size_t); +void buffer_init(buffer *, ssize_t (*op)(), int, char *, size_t); + +#endif diff --git a/env.h b/env.h @@ -0,0 +1,10 @@ +#ifndef ENV_H +#define ENV_H + +#include <stdlib.h> + +#define env_get(name) getenv(name) +#define env_set(name, value) !setenv(name, value, 1) +#define env_unset(name, value) !unsetenv(name, value, 1) + +#endif diff --git a/env/test1 b/env/test1 @@ -0,0 +1 @@ +content diff --git a/env/test2 b/env/test2 diff --git a/env/test3 b/env/test3 diff --git a/fmt.c b/fmt.c @@ -0,0 +1,180 @@ +#include "fmt.h" +#include "str.h" + +size_t +fmt_8long(char *buf, unsigned long i) +{ + size_t n = 0; + unsigned long x; + + x = i; do { x >>= 3; ++n; } while (x); + if (!buf) return n; + + buf += n; + x = i; do { *--buf = (x & 7) + '0'; x >>= 3; } while (x); + return n; +} + +size_t +fmt_long(char *dest, long i) +{ + if (i < 0) { + if (dest) *dest++ = '-'; + return fmt_ulong(dest, (unsigned long) -i) + 1; + } else { + return fmt_ulong(dest, (unsigned long) i); + } +} + +size_t +fmt_minus(char *dest, int i) +{ + if (i >= 0) return 0; + if (dest) *dest = '-'; + return 1; +} + +/* "foo" -> "foo " */ +size_t +fmt_fill(char *dest, size_t size, char const *src, size_t fill) +{ + size_t n = 0; + size_t max; + size_t srclen = str_len(src); + + if (!dest) return srclen > fill ? srclen : fill; + + max = size < srclen ? size : srclen; + while (n < max) dest[n++] = *src++; + while (n < fill) dest[n++] = ' '; + + return n; +} + +/* "foo" -> " foo" */ +size_t +fmt_pad(char *dest, size_t size, char const *src, size_t pad) +{ + size_t n = 0; + size_t max; + size_t srclen = str_len(src); + + if (!dest) return srclen > pad ? srclen : pad; + + max = size < pad + srclen ? size : pad + srclen; + while (n++ < pad - srclen) *dest++ = ' '; + while (n++ < max) *dest++ = *src++; + + return n; +} + +size_t +fmt_plusminus(char *dest, int i) +{ + if (dest) *dest = (i > 0 ? '+' : i < 0 ? '-' : ' '); + return 1; +} + +size_t +fmt_strn(char *dest, size_t n, char const *src) +{ + size_t len; + + if (*dest) return str_copy(dest, src, n); + len = str_len(src); + return n < len ? n : len; +} + +size_t +fmt_ulong(char *buf, unsigned long i) +{ + size_t n = 0; + unsigned long x; + + x = i; do { x /= 10; ++n; } while (x); + if (!buf) return n; + + buf += n; + x = i; do { *--buf = '0' + x % 10; x /= 10; } while (x); + return n; +} + +size_t +fmt_ulong0(char *dest, unsigned long i, size_t pad) +{ + size_t n = 1; + unsigned long x; + + x = i; while (x > 9) { x /= 10; ++n; } + if (!dest) return pad > n ? pad : n; + + while (n <= pad) { *dest++ = '0'; ++n; } + fmt_ulong(dest, i); + return n; +} + +size_t +fmt_utf8(char *dest, uint32_t u) +{ + size_t v, n, n2; + + if (u <= 0x7f) { + if (dest) *dest = u; + return 1; + } + + for (v = 0x3f, n = 0; v >= u; ++n) v = (v << 5) | 0x1f; + if (v >= 0x7fffffff) return 0; /* cannot be encoded */ + if (!dest) return 1 + n; + + *dest++ = (0xff << (7 - n)) | (u >> n * 6); + for (n2 = n - 1; n2 ; --n2) { + *dest++ = 0x80 | (u & 0x3f); + u >>= 6; + } + return 1 + n; +} + +size_t +fmt_xlong(char *buf, unsigned long u) +{ + size_t n = 0; + unsigned long x; + char *ascii = "0123456789abcdef"; + + x = u; do { x >>= 4; ++n; } while (x); + if (!buf) return n; + + buf += n; + x = u; do { *--buf = ascii[x & 15]; x >>= 4; } while (x); + return n; +} + +size_t +fmt_xlong0(char *buf, unsigned long i, size_t pad) +{ + size_t n = 0; + unsigned long x = i; + + do { x >>= 4; ++n; } while (x > 15); + if (!buf) return pad > n ? pad : n; + + while (n < pad) { *buf++ = '0'; ++n; } + if (buf) fmt_xlong(buf, i); + return n; +} + +size_t +fmt_xbuf(char const *buf, size_t n, char *dest) +{ + char *ascii = "0123456789abcdef"; + + if (!dest) return n * 2; + + for (size_t i = 0; i < n; ++i) { + dest[i * 2] = ascii[(unsigned char)buf[i] >> 4]; + dest[i * 2 + 1] = ascii[(unsigned char)buf[i] & 0x0f]; + } + + return n * 2; +} diff --git a/fmt.h b/fmt.h @@ -0,0 +1,21 @@ +#ifndef FMT_H +#define FMT_H + +#include <stddef.h> +#include <stdint.h> + +size_t fmt_8long(char *, unsigned long); +size_t fmt_fill(char *, size_t, char const *, size_t); +size_t fmt_long(char *, long); +size_t fmt_minus(char *, int); +size_t fmt_pad(char *, size_t, char const *, size_t); +size_t fmt_plusminus(char *, int); +size_t fmt_strn(char *, size_t, char const *); +size_t fmt_ulong(char *, unsigned long); +size_t fmt_ulong0(char *, unsigned long, size_t); +size_t fmt_utf8(char *, uint32_t); +size_t fmt_xbuf(char const *, size_t, char *); +size_t fmt_xlong(char *, unsigned long); +size_t fmt_xlong0(char *, unsigned long, size_t); + +#endif diff --git a/genalloc.h b/genalloc.h @@ -0,0 +1,23 @@ +#ifndef GENALLOC_H +#define GENALLOC_H + +#include <stddef.h> +#include "stralloc.h" + +typedef stralloc genalloc; + +#define GENALLOC_ZERO STRALLOC_ZERO +#define genalloc_ready(sa, type, n) stralloc_ready((sa), (n) * sizeof (type)) +#define genalloc_readyplus(sa, type, n) stralloc_readyplus((sa), (n) * sizeof (type)) +#define genalloc_len(sa, type) ((sa)->n / sizeof (type)) +#define genalloc_get(sa, type, i) (((type *)(sa)->x) + i) +#define genalloc_cat(sa1, sa2) stralloc_cat((sa1), (sa2)) +#define genalloc_catb(sa, b) stralloc_catb((sa), (x), sizeof *(x) * (n)) +#define genalloc_append(sa, x) stralloc_catb((sa), (char *)&(x), sizeof (x)) +#define genalloc_copy(sa1, sa2) stralloc_copy((sa1), (sa2)) +#define genalloc_copyb(sa, x, n) genalloc_copyb((sa), (char const) (x), (n) * sizeof (x)) +#define genalloc_free(sa) stralloc_free(sa) +#define genalloc_init(sa) stralloc_init(sa) +#define genalloc_zero(sa) stralloc_zero(sa) + +#endif diff --git a/hier.c b/hier.c @@ -0,0 +1,190 @@ +#include <dirent.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include "hier.h" +#include "open.h" +#include "str.h" +#include "stralloc.h" + +int +hier_mkdir(char *dir, unsigned int mode) +{ + size_t n; + + for (n = (dir[0] == '/') ;; ++n) { + if (dir[n] == '/') { + dir[n] = '\0'; + if (mkdir(dir, mode) == -1 && errno != EEXIST) return 0; + dir[n] = '/'; + } else if (dir[n] == '\0') { + if (mkdir(dir, mode) == -1 && errno != EEXIST) return 0; + break; + } + } + + return 1; +} + +static int +hier_remove_recurse(stralloc *sa) +{ + DIR *dp; + struct dirent *de; + struct stat st; + size_t n = sa->n; + + if (!stralloc_cats(sa, "/")) return 0; + n = sa->n; + + dp = opendir(sa->x); if (dp == NULL) return 0; + errno = 0; + while ((de = readdir(dp))) { + if (str_equal(de->d_name, ".")) continue; + if (str_equal(de->d_name, "..")) continue; + + sa->n = n; + if (!stralloc_cats(sa, de->d_name)) break; + if (!stralloc_cat0(sa)) break; + if (lstat(sa->x, &st) == -1) break; + sa->n--; + + if (S_ISDIR(st.st_mode)) { + if (!hier_remove_recurse(sa)) break; + } else { + if (unlink(sa->x) == -1) break; + } + } + + closedir(dp); + rmdir(sa->x); + return (errno == 0); +} + +int +hier_remove(char const *dir) +{ + stralloc path = STRALLOC_ZERO; + int ret = 0; + + if (!stralloc_cats(&path, dir)) goto error; + ret = hier_remove_recurse(&path); +error: + stralloc_free(&path); + return ret; +} + +static int +copy(char const *from, char const *to, unsigned int mode) +{ + char buf[1024 * 8]; + int fd_from = -1; + int fd_to = -1; + int r = -1; + int w = 1; /* for empty files */ + + fd_from = open_read(from); if (fd_from == -1) goto error; + fd_to = open_trunc(to); if (fd_to == -1) goto error; + while (1) { + r = read(fd_from, buf, sizeof buf); if (r <= 0) break; + w = write(fd_to, buf, r); if (w < r) break; + } + if (chmod(to, mode) == -1) goto error; + +error: close(fd_from); + close(fd_to); + return r == 0 && w > 0; +} + +static int +hier_copy_recurse(stralloc *from, stralloc *to, int action) +{ + DIR *dp; + struct dirent *de; + struct stat st; + char ln[4096]; + size_t to_n = to->n; + size_t from_n = from->n; + int ret = 0; + + if (!stralloc_cats(from, "/")) return 0; + from_n = from->n; + + if (!stralloc_cats(to, "/")) return 0; + to_n = to->n; + + if (!stralloc_cat0(from)) return 0; + dp = opendir(from->x); if (!dp) return 0; + errno = 0; + while ((de = readdir(dp))) { + if (str_equal(de->d_name, ".")) continue; + if (str_equal(de->d_name, "..")) continue; + + from->n = from_n; + if (!stralloc_cats(from, de->d_name)) goto error; + if (!stralloc_cat0(from)) goto error; + from->n--; + + to->n = to_n; + if (!stralloc_cats(to, de->d_name)) goto error; + if (!stralloc_cat0(to)) goto error; + to->n--; + + if (lstat(from->x, &st) == -1) goto error; + if (S_ISDIR(st.st_mode)) { + if (mkdir(to->x, st.st_mode) == -1 && errno != EEXIST) goto error; + if (!hier_copy_recurse(from, to, action)) goto error; + } else if (S_ISREG(st.st_mode)) { + switch (action) { + case 0: + if (!copy(from->x, to->x, st.st_mode)) goto error; + break; + case 1: + unlink(to->x); + if (symlink(from->x, to->x) == -1) goto error; + break; + } + } else if (S_ISLNK(st.st_mode)) { + unlink(to->x); + if (readlink(from->x, ln, sizeof ln) == -1) goto error; + if (symlink(ln, to->x) == -1) goto error; + } else goto error; + } + ret = 1; +error: + closedir(dp); + return ret; +} + +int +hier_copy(char const *from, char const *to) +{ + stralloc sa_from = STRALLOC_ZERO; + stralloc sa_to = STRALLOC_ZERO; + int ret = 0; + + if (!stralloc_cats(&sa_from, from)) goto error; + if (!stralloc_cats(&sa_to, to)) goto error; + ret = hier_copy_recurse(&sa_from, &sa_to, 0); +error: + stralloc_free(&sa_from); + stralloc_free(&sa_to); + return ret; +} + +int +hier_symlink(char const *from, char const *to) +{ + stralloc sa_from = STRALLOC_ZERO; + stralloc sa_to = STRALLOC_ZERO; + int ret = 0; + + if (!stralloc_cats(&sa_from, from)) goto error; + if (!stralloc_cats(&sa_to, to)) goto error; + ret = hier_copy_recurse(&sa_from, &sa_to, 1); +error: + stralloc_free(&sa_from); + stralloc_free(&sa_to); + return ret; +} diff --git a/hier.h b/hier.h @@ -0,0 +1,9 @@ +#ifndef HIER_H +#define HIER_H + +int hier_copy(char const *, char const *); +int hier_mkdir(char *, unsigned int); +int hier_remove(char const *); +int hier_symlink(char const *, char const *); + +#endif diff --git a/j5-envdir.c b/j5-envdir.c @@ -0,0 +1,149 @@ +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> + +#include "openreadclose.h" +#include "openwriteclose.h" +#include "log.h" +#include "str.h" +#include "mem.h" +#include "arg.h" +#include "stat.h" +#include "stralloc.h" +#include "genalloc.h" + +char *arg_0; + +typedef struct { + char *key; + stralloc val; +} keyval; + +void +die_nomem(void) +{ + log_fatal_sys_1(103, "malloc"); +} + +void +usage(char *progname) +{ + log_usage(progname, ""); +} + +#include <stdio.h> + +int +replace(stralloc *sa, char const *src, size_t siz, char const *pre, genalloc const *ga, char const *suf) +{ + size_t i = 0; + size_t len_pre = str_len(pre); + size_t len_suf = str_len(suf); + int found = 0; + int ret = 0; + + do { + i = mem_mem(src, siz, pre, len_pre); + if (!stralloc_catb(sa, src, i)) goto error; + if (i == siz) break; + src += i + len_pre; + siz -= i + len_pre; + dprintf(1, "i:%zd siz:%zd\n", i, siz); + + for (size_t k = 0; k < genalloc_len(ga, keyval); ++k) { + keyval *a = genalloc_get(ga, keyval, k); + size_t len_key = str_len(a->key); + + if (siz < len_key + len_suf) continue; + if (mem_diff(src + len_key, suf, len_suf)) continue; + if (mem_equal(src, a->key, len_key)) { + if (!stralloc_cat(sa, &a->val)) goto error; + siz -= len_key + len_suf; + src += len_key + len_suf; + found = 1; + break; + } + } + } while (found); + + ret = 1; +error: + return ret; +} + +void +chomp(stralloc *sa, char const *c) +{ + while (c[str_chr(c, sa->x[sa->n - 1])] != '\0') --sa->n; +} + +int +main(int argc, char **argv) +{ + DIR *dp; + struct dirent *de; + char *pre = "${"; + char *suf = "}"; + char *flag_f = NULL; + char *flag_n = " \t\n"; + int flag_a = 0; + genalloc ga; + + ARG_BEGIN { + case 'p': pre = ARG; break; + case 's': suf = ARG; break; + case 'n': flag_n = ARG; break; + case 'f': flag_f = ARG; break; + case 'a': flag_a = 1; break; + default: usage(arg_0); break; + } ARG_END; + if (!*argv) usage(arg_0); + + if (!(dp = opendir(*argv))) log_fatal_sys_2(101, "opendir ", *argv); + while ((de = readdir(dp))) { + keyval a; + stralloc sa = STRALLOC_ZERO; + + stralloc_init(&a.val); + stralloc_cats(&sa, *argv); + stralloc_cats(&sa, "/"); + stralloc_cats(&sa, de->d_name); + stralloc_cat0(&sa); + if (!stat_isfile(sa.x)) continue; + if (!openreadclose(sa.x, &a.val, 256)) + log_fatal_sys_2(100, "openreadclose ", sa.x); + a.key = de->d_name; + chomp(&a.val, flag_n); + if (!genalloc_append(&ga, a)) die_nomem(); + } + ++argv; + + if (flag_a) { + for (char **av = argv; *av; ++av) { + stralloc sa = STRALLOC_ZERO; + if (!replace(&sa, *av, str_len(*av), pre, &ga, suf)) die_nomem(); + if (!stralloc_cat0(&sa)) die_nomem(); + *av = sa.x; + } + } + + if (flag_f) { + stralloc src = STRALLOC_ZERO; + stralloc dst = STRALLOC_ZERO; + openreadclose(flag_f, &src, 1024); + if (!replace(&dst, src.x, src.n, pre, &ga, suf)) die_nomem(); + openwriteclose(flag_f, &dst); + } + + for (size_t i = 0; i < genalloc_len(&ga, keyval); ++i) { + keyval *a = genalloc_get(&ga, keyval, i); + stralloc_free(&a->val); + } + genalloc_free(&ga); + + if (!*argv) { usage(arg_0); } + execvp(*argv, argv); + + log_fatal_sys_2(127, "exec ", *argv); + return 127; +} diff --git a/log.c b/log.c @@ -0,0 +1,107 @@ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> + +#include "buffer.h" +#include "log.h" + +int log_level = 0; + +void +log_init(int level) +{ + log_level = level; +} + +void +log_put(int level, char const *type, int argc, ...) +{ + va_list a; + + if (level < log_level) return; + va_start(a, argc); + buffer_puts(buffer_2, type); + buffer_puts(buffer_2, ": "); + for (int x = 0; x < argc; x++) { + char *s = va_arg(a, char *); + buffer_puts(buffer_2, s); + } + buffer_puts(buffer_2, "\n"); + buffer_flush(buffer_2); + va_end(a); +} + +void +log_put_sys(int level, char const *type, int argc, ...) +{ + va_list a; + + if (level < log_level) return; + va_start(a, argc); + buffer_puts(buffer_2, type); + buffer_puts(buffer_2, ": "); + for (int x = 0; x < argc; x++) { + char *s = va_arg(a, char *); + buffer_puts(buffer_2, s); + } + buffer_puts(buffer_2, ": "); + buffer_puts(buffer_2, strerror(errno)); + buffer_puts(buffer_2, "\n"); + buffer_flush(buffer_2); + va_end(a); +} + +void +log_fatal(int e, char const *type, int argc, ...) +{ + va_list a; + + va_start(a, argc); + buffer_puts(buffer_2, type); + buffer_puts(buffer_2, ": "); + for (int x = 0; x < argc; x++) { + char *s = va_arg(a, char *); + buffer_puts(buffer_2, s); + } + buffer_puts(buffer_2, "\n"); + buffer_flush(buffer_2); + va_end(a); + + exit(e); +} + +void +log_fatal_sys(int e, char const *type, int argc, ...) +{ + va_list a; + + va_start(a, argc); + buffer_puts(buffer_2, type); + buffer_puts(buffer_2, ": "); + for (int x = 0; x < argc; x++) { + char *s = va_arg(a, char *); + buffer_puts(buffer_2, s); + } + buffer_puts(buffer_2, ": "); + buffer_puts(buffer_2, strerror(errno)); + buffer_puts(buffer_2, "\n"); + buffer_flush(buffer_2); + va_end(a); + + exit(e); +} + +void +log_usage(char const *prog, char const *usage) +{ + buffer_puts(buffer_2, "usage: "); + buffer_puts(buffer_2, prog); + buffer_puts(buffer_2, " "); + buffer_puts(buffer_2, usage); + buffer_puts(buffer_2, "\n"); + buffer_flush(buffer_2); + + exit(1); +} diff --git a/log.h b/log.h @@ -0,0 +1,90 @@ +#ifndef STRERR_H +#define STRERR_H + +#define log_fatal_1(x, a) log_fatal(x, "fatal", 1, a) +#define log_fatal_2(x, a,b) log_fatal(x, "fatal", 2, a,b) +#define log_fatal_3(x, a,b,c) log_fatal(x, "fatal", 3, a,b,c) +#define log_fatal_4(x, a,b,c,d) log_fatal(x, "fatal", 4, a,b,c,d) +#define log_fatal_5(x, a,b,c,d,e) log_fatal(x, "fatal", 5, a,b,c,d,e) +#define log_fatal_6(x, a,b,c,d,e,f) log_fatal(x, "fatal", 6, a,b,c,d,e,f) +#define log_fatal_7(x, a,b,c,d,e,f,g) log_fatal(x, "fatal", 7, a,b,c,d,e,f,g) +#define log_fatal_8(x, a,b,c,d,e,f,g,h) log_fatal(x, "fatal", 8, a,b,c,d,e,f,g,h) +#define log_fatal_9(x, a,b,c,d,e,f,g,h,i) log_fatal(x, "fatal", 9, a,b,c,d,e,f,g,h,i) +#define log_fatal_sys_1(x, a) log_fatal_sys(x, "fatal", 1, a) +#define log_fatal_sys_2(x, a,b) log_fatal_sys(x, "fatal", 2, a,b) +#define log_fatal_sys_3(x, a,b,c) log_fatal_sys(x, "fatal", 3, a,b,c) +#define log_fatal_sys_4(x, a,b,c,d) log_fatal_sys(x, "fatal", 4, a,b,c,d) +#define log_fatal_sys_5(x, a,b,c,d,e) log_fatal_sys(x, "fatal", 5, a,b,c,d,e) +#define log_fatal_sys_6(x, a,b,c,d,e,f) log_fatal_sys(x, "fatal", 6, a,b,c,d,e,f) +#define log_fatal_sys_7(x, a,b,c,d,e,f,g) log_fatal_sys(x, "fatal", 7, a,b,c,d,e,f,g) +#define log_fatal_sys_8(x, a,b,c,d,e,f,g,h) log_fatal_sys(x, "fatal", 8, a,b,c,d,e,f,g,h) +#define log_fatal_sys_9(x, a,b,c,d,e,f,g,h,i) log_fatal_sys(x, "fatal", 9, a,b,c,d,e,f,g,h,i) + +#define log_error_1(a) log_put(2, "error", 1, a) +#define log_error_2(a,b) log_put(2, "error", 2, a,b) +#define log_error_3(a,b,c) log_put(2, "error", 3, a,b,c) +#define log_error_4(a,b,c,d) log_put(2, "error", 4, a,b,c,d) +#define log_error_5(a,b,c,d,e) log_put(2, "error", 5, a,b,c,d,e) +#define log_error_6(a,b,c,d,e,f) log_put(2, "error", 6, a,b,c,d,e,f) +#define log_error_7(a,b,c,d,e,f,g) log_put(2, "error", 7, a,b,c,d,e,f,g) +#define log_error_8(a,b,c,d,e,f,g,h) log_put(2, "error", 8, a,b,c,d,e,f,g,h) +#define log_error_9(a,b,c,d,e,f,g,h,i) log_put(2, "error", 9, a,b,c,d,e,f,g,h,i) +#define log_error_sys_1(a) log_put_sys(2, "error", 1, a) +#define log_error_sys_2(a,b) log_put_sys(2, "error", 2, a,b) +#define log_error_sys_3(a,b,c) log_put_sys(2, "error", 3, a,b,c) +#define log_error_sys_4(a,b,c,d) log_put_sys(2, "error", 4, a,b,c,d) +#define log_error_sys_5(a,b,c,d,e) log_put_sys(2, "error", 5, a,b,c,d,e) +#define log_error_sys_6(a,b,c,d,e,f) log_put_sys(2, "error", 6, a,b,c,d,e,f) +#define log_error_sys_7(a,b,c,d,e,f,g) log_put_sys(2, "error", 7, a,b,c,d,e,f,g) +#define log_error_sys_8(a,b,c,d,e,f,g,h) log_put_sys(2, "error", 8, a,b,c,d,e,f,g,h) +#define log_error_sys_9(a,b,c,d,e,f,g,h,i) log_put_sys(2, "error", 9, a,b,c,d,e,f,g,h,i) + +#define log_warn_1(a) log_put(3, "warning", 1, a) +#define log_warn_2(a,b) log_put(3, "warning", 2, a,b) +#define log_warn_3(a,b,c) log_put(3, "warning", 3, a,b,c) +#define log_warn_4(a,b,c,d) log_put(3, "warning", 4, a,b,c,d) +#define log_warn_5(a,b,c,d,e) log_put(3, "warning", 5, a,b,c,d,e) +#define log_warn_6(a,b,c,d,e,f) log_put(3, "warning", 6, a,b,c,d,e,f) +#define log_warn_7(a,b,c,d,e,f,g) log_put(3, "warning", 7, a,b,c,d,e,f,g) +#define log_warn_8(a,b,c,d,e,f,g,h) log_put(3, "warning", 8, a,b,c,d,e,f,g,h) +#define log_warn_9(a,b,c,d,e,f,g,h,i) log_put(3, "warning", 9, a,b,c,d,e,f,g,h,i) +#define log_warn_sys_1(a) log_put_sys(3, "warning", 1, a) +#define log_warn_sys_2(a,b) log_put_sys(3, "warning", 2, a,b) +#define log_warn_sys_3(a,b,c) log_put_sys(3, "warning", 3, a,b,c) +#define log_warn_sys_4(a,b,c,d) log_put_sys(3, "warning", 4, a,b,c,d) +#define log_warn_sys_5(a,b,c,d,e) log_put_sys(3, "warning", 5, a,b,c,d,e) +#define log_warn_sys_6(a,b,c,d,e,f) log_put_sys(3, "warning", 6, a,b,c,d,e,f) +#define log_warn_sys_7(a,b,c,d,e,f,g) log_put_sys(3, "warning", 7, a,b,c,d,e,f,g) +#define log_warn_sys_8(a,b,c,d,e,f,g,h) log_put_sys(3, "warning", 8, a,b,c,d,e,f,g,h) +#define log_warn_sys_9(a,b,c,d,e,f,g,h,i) log_put_sys(3, "warning", 9, a,b,c,d,e,f,g,h,i) + +#define log_info_1(a) log_put(4, "info", 1, a) +#define log_info_2(a,b) log_put(4, "info", 2, a,b) +#define log_info_3(a,b,c) log_put(4, "info", 3, a,b,c) +#define log_info_4(a,b,c,d) log_put(4, "info", 4, a,b,c,d) +#define log_info_5(a,b,c,d,e) log_put(4, "info", 5, a,b,c,d,e) +#define log_info_6(a,b,c,d,e,f) log_put(4, "info", 6, a,b,c,d,e,f) +#define log_info_7(a,b,c,d,e,f,g) log_put(4, "info", 7, a,b,c,d,e,f,g) +#define log_info_8(a,b,c,d,e,f,g,h) log_put(4, "info", 8, a,b,c,d,e,f,g,h) +#define log_info_9(a,b,c,d,e,f,g,h,i) log_put(4, "info", 9, a,b,c,d,e,f,g,h,i) + +#define log_debug_1(a) log_put(5, "debug", 1, a) +#define log_debug_2(a,b) log_put(5, "debug", 2, a,b) +#define log_debug_3(a,b,c) log_put(5, "debug", 3, a,b,c) +#define log_debug_4(a,b,c,d) log_put(5, "debug", 4, a,b,c,d) +#define log_debug_5(a,b,c,d,e) log_put(5, "debug", 5, a,b,c,d,e) +#define log_debug_6(a,b,c,d,e,f) log_put(5, "debug", 6, a,b,c,d,e,f) +#define log_debug_7(a,b,c,d,e,f,g) log_put(5, "debug", 7, a,b,c,d,e,f,g) +#define log_debug_8(a,b,c,d,e,f,g,h) log_put(5, "debug", 8, a,b,c,d,e,f,g,h) +#define log_debug_9(a,b,c,d,e,f,g,h,i) log_put(5, "debug", 9, a,b,c,d,e,f,g,h,i) + +extern int log_level; + +void log_fatal(int, char const *, int, ...); +void log_fatal_sys(int, char const *, int, ...); +void log_init(int); +void log_put(int, char const *, int, ...); +void log_put_sys(int, char const *, int, ...); +void log_usage(char const *, char const *); + +#endif diff --git a/make-a b/make-a @@ -0,0 +1,4 @@ +#!/bin/sh -e +rm -f "$1" +ar cr "$@" +ranlib "$1" diff --git a/make-bin b/make-bin @@ -0,0 +1,2 @@ +#!/bin/sh +exec cc -s -o "$@" diff --git a/make-h b/make-h @@ -0,0 +1,47 @@ +#!/bin/sh -e +# usage: tool/h prefix + +awk ' +function declare(type, symbol, args) +{ + printf("%-7s %s(%s);\n", type, symbol, args); + +} + +/^[a-zA-Z0-9_]+\(/ { + if (match(type, "static") || match($0, ";$")) + next; + + symbol = $0; + sub("\\(.*", "", symbol); + sub("[a-zA-Z0-9_]*\\(", "", $0); + + if (symbol == "main") + next; + + IN = 1; +} + +IN { + args = args " " $0; +} + +IN && /\)/ { + gsub("[ \t]+", " ", args); + sub("^ ", "", args); + gsub("[*][a-zA-Z0-9_]+,", "*,", args); + gsub("[*][a-zA-Z0-9_]+\\)[^)]*$", "*", args); + gsub("[ ][a-zA-Z0-9_]+,", ",", args); + gsub("[ ][a-zA-Z0-9_]+\\)[^)]*$", "", args); + gsub("\\.\\.\\. *\\)", "...", args); + + declare(type, symbol, args); + + args = "" + IN = 0; +} + +!IN { + type = $0; +} +' "$@" | sed -r 's,^([a-zA-Z_ ]+)( *)\*+( +),\1\2\3*,' | sort diff --git a/make-man b/make-man @@ -0,0 +1,119 @@ +#!/bin/sh + +if [ "$#" != 3 ]; then + echo "usage: ${0##*/} <section> <name> <description>" + exit 1 +fi + +section="$1" +name="$2" +description="$3" +author=${NAME:-<author-name>} +mail=${EMAIL:-<author-email>} + +case "$section" in (*) cat <<EOF ;; esac +.Dt $(echo "$name" | tr a-z A-Z) $section +.Dd $(date +'$Mdocdate: %B %d %Y$') +.Os +. +. +.Sh NAME +. +.Nm $name +.Nd $description +. +. +.Sh SYNOPSIS +. +.Nm $name +.Op Fl options +.Ar +. +. +.Sh DESCRIPTION +. +The +.Nm +utility +. +EOF +case "$section" in (9) cat <<EOF ;; esac +. +. +.Sh CONTEXT +. +EOF +case "$section" in (2|3|9) cat <<EOF ;; esac +. +. +.Sh RETURN VALUES +. +.Rv -std +. +EOF +case "$section" in (1|6|7|8) cat <<EOF ;; esac +. +. +.Sh ENVIRONMENT +. +EOF +cat <<EOF +. +. +.Sh FILES +. +EOF +case "$section" in (1|6|8) cat <<EOF ;; esac +. +. +.Sh EXIT STATUS +. +.Ex -std +EOF +cat <<EOF +. +. +.Sh EXAMPLES +. +EOF +case "$section" in (1|4|6|7|8) cat <<EOF ;; esac +. +. +.Sh DIAGNOSTICS +. +EOF +case "$section" in (2|3|4) cat <<EOF ;; esac +. +. +.Sh ERRORS +. +EOF +cat <<EOF +. +. +.Sh SEE ALSO +. +.Xr foobar 1 +. +. +.Sh STANDARDS +. +. +. +.Sh HISTORY +. +. +. +.Sh AUTHORS +. +.An $author +.Aq Mt $mail +. +. +.Sh CAVEATS +. +. +. +.Sh BUGS +. +EOF diff --git a/make-mk b/make-mk @@ -0,0 +1,8 @@ +#!/bin/sh +# usage: ./make-mk libprefix + +for file in "$@" +do + printf '%-17s %-17s ' "${file%.c}.o:" "$file" | tr -d '\n' + sed -rn 's/^#include "([^"]+.h)".*$/\1/p' "$file" | xargs +done diff --git a/make-o b/make-o @@ -0,0 +1,2 @@ +#!/bin/sh +exec cc -D_BSD_SOURCE -D_POSIX_SOURCE -Wall -Wextra -std=c99 -pedantic -c "$@" diff --git a/mem.c b/mem.c @@ -0,0 +1,22 @@ +#include "mem.h" + +size_t +mem_chr(char const *buf, size_t n, char c) +{ + char *p = memchr(buf, c, n); + return p ? p - buf : n; +} + +void +mem_xor(unsigned char *out, const unsigned char *in, size_t n) +{ + for (size_t i = 0; i < n; i++) out[i] ^= in[i]; +} + +size_t +mem_mem(char const *buf1, size_t n1, char const *buf2, size_t n2) +{ + for (size_t i = 0; i + n2 <= n1; ++i) + if (mem_equal(buf1 + i, buf2, n2)) return i; + return n1; +} diff --git a/mem.h b/mem.h @@ -0,0 +1,19 @@ +#ifndef BYTE_H +#define BYTE_H + +#include <stddef.h> +#include <string.h> + +#define mem_copy(to, from, n) memcpy(to, from, n) +#define mem_move(to, from, n) memmove(to, from, n) +#define mem_diff(buf1, buf2, n) memcmp(buf1, buf2, n) +#define mem_equal(buf1, buf2, n) (!memcmp(buf1, buf2, n)) +#define mem_set(buf, n, c) memset(buf, c, n) +#define mem_zero(buf, n) memset(buf, 0, n) +#define mem_str(buf, n, str) mem_mem(buf, n, str, strlen(str)) + +size_t mem_chr(char const *, size_t, char); +size_t mem_mem(char const *, size_t, char const *, size_t); +void mem_xor(unsigned char *, const unsigned char *, size_t); + +#endif diff --git a/open.h b/open.h @@ -0,0 +1,12 @@ +#ifndef OPEN_H +#define OPEN_H + +#include <fcntl.h> + +#define open_append(path) open(path, O_WRONLY | O_NONBLOCK | O_APPEND | O_CREAT, 0600) +#define open_excl(path) open(path, O_WRONLY | O_EXCL | O_CREAT, 0600) +#define open_read(path) open(path, O_RDONLY | O_NONBLOCK) +#define open_trunc(path) open(path, O_WRONLY | O_NONBLOCK | O_TRUNC | O_CREAT, 0600) +#define open_write(path) open(path, O_WRONLY | O_NDELAY) + +#endif diff --git a/openreadclose.c b/openreadclose.c @@ -0,0 +1,28 @@ +#include <errno.h> +#include <unistd.h> +#include "stralloc.h" +#include "open.h" + +int +openreadclose(char const *path, stralloc *sa, size_t bufsize) +{ + ssize_t r; + int fd = open_read(path); + + if (fd == -1) return 0; + do { + if (!stralloc_readyplus(sa, bufsize)) { close(fd); return 0; } + r = read(fd, sa->x + sa->n, bufsize); + if (r == -1) { + if (errno == EINTR) continue; + if (errno == EAGAIN) continue; + if (errno == EWOULDBLOCK) continue; + close(fd); + return 0; + } + sa->n += r; + } while (r != 0); + + close(fd); + return 1; +} diff --git a/openreadclose.h b/openreadclose.h @@ -0,0 +1,8 @@ +#ifndef OPENREADCLOSE_H +#define OPENREADCLOSE_H + +#include "stralloc.h" + +int openreadclose(char const *, stralloc *, size_t); + +#endif diff --git a/openwriteclose.c b/openwriteclose.c @@ -0,0 +1,27 @@ +#include <errno.h> +#include <unistd.h> +#include "open.h" + +int +openwriteclose(char const *path, char const *s, size_t n) +{ + ssize_t w; + int fd = open_write(path); + + if (fd == -1) return 0; + do { + w = write(fd, s, n); + if (w == -1) { + if (errno == EINTR) continue; + if (errno == EAGAIN) continue; + if (errno == EWOULDBLOCK) continue; + close(fd); + return 0; + } + s += w; + n -= w; + } while (n > 0); + + close(fd); + return 1; +} diff --git a/openwriteclose.h b/openwriteclose.h @@ -0,0 +1,8 @@ +#ifndef OPENWRITECLOSE_H +#define OPENWRITECLOSE_H + +#include "stralloc.h" + +int openwriteclose(char const *, stralloc const *); + +#endif diff --git a/stat.c b/stat.c @@ -0,0 +1,27 @@ +#include <sys/stat.h> + +#include "stat.h" + +int +stat_isdir(char const *path) +{ + struct stat st; + if (stat(path, &st) == -1) return 0; + return S_ISDIR(st.st_mode); +} + +int +stat_islink(char const *path) +{ + struct stat st; + if (lstat(path, &st) == -1) return 0; + return S_ISLNK(st.st_mode); +} + +int +stat_isfile(char const *path) +{ + struct stat st; + if (stat(path, &st) == -1) return 0; + return S_ISREG(st.st_mode); +} diff --git a/stat.h b/stat.h @@ -0,0 +1,8 @@ +#ifndef STAT_H +#define STAT_H + +int stat_isdir(char const *); +int stat_isfile(char const *); +int stat_islink(char const *); + +#endif diff --git a/str.c b/str.c @@ -0,0 +1,15 @@ +#include "str.h" + +size_t +str_chr(char const *buf, char c) +{ + char *s = strchr(buf, c); + return s ? (size_t)(s - buf) : strlen(buf); +} + +size_t +str_rchr(char const *buf, char c) +{ + char *s = strrchr(buf, c); + return s ? (size_t)(s - buf) : strlen(buf); +} diff --git a/str.h b/str.h @@ -0,0 +1,18 @@ +#ifndef STR_H +#define STR_H + +#include <stddef.h> +#include <string.h> + +#define str_copy(to, from, n) strlen(strncpy(to, from, n)) +#define str_diff(buf1, buf2) strcmp(buf1, buf2) +#define str_diffn(buf1, buf2, n) strncmp(buf1, buf2, n) +#define str_equal(buf1, buf2) (!strcmp(buf1, buf2)) +#define str_equaln(buf1, buf2, n) (!strncmp(buf1, buf2, n)) +#define str_len(buf) strlen(buf) +#define str_start(buf1, buf2) (!strncmp(buf1, buf2, strlen(buf2))) + +size_t str_chr(char const *, char); +size_t str_rchr(char const *, char); + +#endif diff --git a/stralloc.c b/stralloc.c @@ -0,0 +1,83 @@ +#include <stdlib.h> +#include "mem.h" +#include "fmt.h" +#include "str.h" +#include "stralloc.h" + +int +stralloc_catb(stralloc *sa, const char *x, size_t n) +{ + if (!stralloc_readyplus(sa, n)) return 0; + mem_copy(sa->x + sa->n, x, n); + sa->n += n; + return 1; +} + +int +stralloc_catlong0(stralloc *sa, signed long int in, size_t n) +{ + int neg = neg = -(in < 0); + size_t f; + + if (neg) in = -in; + f = fmt_minus(0, neg) + fmt_ulong0(0, (unsigned long)in, n); + if (!stralloc_readyplus(sa, f)) return 0; + sa->n += fmt_minus(sa->x + sa->n, neg); + sa->n += fmt_ulong0(sa->x + sa->n, (unsigned long)in, n); + return 1; +} + +int +stralloc_catulong0(stralloc *sa, unsigned long int in, size_t n) +{ + if (!stralloc_readyplus(sa, fmt_ulong0(0, in, n))) return 0; + sa->n += fmt_ulong0(sa->x + sa->n, in, n); + return 1; +} + +int +stralloc_copyb(stralloc *sa, const char *buf, size_t n) +{ + if (!stralloc_ready(sa, n)) return 0; + sa->n = n; + mem_copy(sa->x, buf, n); + return 1; +} + +void +stralloc_init(stralloc *sa) +{ + sa->x = 0; + sa->a = 0; + sa->n = 0; +} + +void +stralloc_free(stralloc *sa) +{ + free(sa->x); + sa->x = 0; + sa->a = 0; + sa->n = 0; +} + +int +stralloc_ready(stralloc *sa, size_t n) +{ + size_t wanted = n + (n >> 3) + 30; /* heuristic from djb */ + + if (wanted < n) wanted = n; + if (!sa->x || sa->a < n) { + char *x = realloc(sa->x, wanted); if (!x) return 0; + sa->a = wanted; + sa->x = x; + } + return 1; +} + +int +stralloc_starts(stralloc *sa, const char *in) +{ + size_t n = str_len(in); + return (n <= sa->n && mem_equal(sa->x, in, n)); +} diff --git a/stralloc.h b/stralloc.h @@ -0,0 +1,38 @@ +#ifndef STRALLOC_H +#define STRALLOC_H + +#include <stddef.h> +#include "str.h" + +typedef struct stralloc { + char* x; + size_t n; + size_t a; +} stralloc; + +#define STRALLOC_ZERO { 0, 0, 0 } + +#define stralloc_cat0(sa) stralloc_catb((sa), "", 1) +#define stralloc_cat(sa1, sa2) stralloc_catb((sa1), (sa2)->x, (sa2)->n) +#define stralloc_catint(sa, i) stralloc_catlong0((sa), (i), 0) +#define stralloc_catint0(sa, i, n) stralloc_catlong0((sa), (i), (n)) +#define stralloc_catlong(sa, l) stralloc_catlong0((sa), (l), 0) +#define stralloc_cats(sa, buf) stralloc_catb((sa), (buf), str_len(buf)) +#define stralloc_catuint(sa, i) stralloc_catulong0((sa), (i), 0) +#define stralloc_catuint0(sa, i, n) stralloc_catulong0((sa), (i), (n)) +#define stralloc_catulong(sa, l) stralloc_catulong0((sa), (l), 0) +#define stralloc_copy(sa1, sa2) stralloc_copyb((sa1), (sa2)->x, (sa2)->n) +#define stralloc_copys(sa, buf) stralloc_copyb((sa), (buf), str_len(buf)) +#define stralloc_readyplus(sa, len) stralloc_ready((sa), (sa)->n + (len)) +#define stralloc_zero(sa) ((sa)->n = 0) + +int stralloc_catb(stralloc *, const char *, size_t); +int stralloc_catlong0(stralloc *, signed long int, size_t); +int stralloc_catulong0(stralloc *, unsigned long int, size_t); +int stralloc_copyb(stralloc *, const char *, size_t); +int stralloc_ready(stralloc *, size_t); +int stralloc_starts(stralloc *, const char *); +void stralloc_free(stralloc *); +void stralloc_init(stralloc *); + +#endif