From 02e59c946d51faf976a85610e44d7b0ccf4a6e59 Mon Sep 17 00:00:00 2001 From: midipix Date: May 07 2024 20:07:25 +0000 Subject: driver: created program skeleton. --- diff --git a/include/treebnf/treebnf.h b/include/treebnf/treebnf.h new file mode 100644 index 0000000..c10f115 --- /dev/null +++ b/include/treebnf/treebnf.h @@ -0,0 +1,142 @@ +#ifndef TREEBNF_H +#define TREEBNF_H + +#include +#include +#include + +#include "treebnf_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* pre-alpha */ +#ifndef TBNF_APP +#ifndef TBNF_PRE_ALPHA +#error libtreebnf: pre-alpha: ABI is not final! +#error to use the library, please pass -DTBNF_PRE_ALPHA to the compiler. +#endif +#endif + +/* status codes */ +#define TBNF_OK 0x00 +#define TBNF_USAGE 0x01 +#define TBNF_ERROR 0x02 + +/* driver flags */ +#define TBNF_DRIVER_XFLAG(x) ((uint64_t)(x) << 32) + +#define TBNF_DRIVER_VERBOSITY_NONE 0x0000 +#define TBNF_DRIVER_VERBOSITY_ERRORS 0x0001 +#define TBNF_DRIVER_VERBOSITY_STATUS 0x0002 +#define TBNF_DRIVER_VERBOSITY_USAGE 0x0004 +#define TBNF_DRIVER_CLONE_VECTOR 0x0008 + +#define TBNF_DRIVER_VERSION 0x0010 + +#define TBNF_DRIVER_ANNOTATE_ALWAYS 0x10000000 +#define TBNF_DRIVER_ANNOTATE_NEVER 0x20000000 +#define TBNF_DRIVER_ANNOTATE_FULL 0x40000000 + +/* error flags */ +#define TBNF_ERROR_TOP_LEVEL 0x0001 +#define TBNF_ERROR_NESTED 0x0002 +#define TBNF_ERROR_CHILD 0x0004 +#define TBNF_ERROR_CUSTOM 0x0008 + +enum tbnf_custom_error { + TBNF_ERR_FLOW_ERROR, + TBNF_ERR_NULL_CONTEXT, + TBNF_ERR_IMAGE_SIZE_ZERO, +}; + +struct tbnf_raw_input { + void * map_addr; + size_t map_size; +}; + +struct tbnf_source_version { + int major; + int minor; + int revision; + const char * commit; +}; + +struct tbnf_fd_ctx { + int fdin; + int fdout; + int fderr; + int fdlog; + int fdcwd; + int fddst; +}; + +struct tbnf_error_info { + const struct tbnf_driver_ctx * edctx; + int esyscode; + int elibcode; + const char * efunction; + int eline; + unsigned eflags; + void * eany; +}; + +struct tbnf_common_ctx { + uint64_t drvflags; + uint64_t actflags; + uint64_t fmtflags; +}; + +struct tbnf_driver_ctx { + const char * program; + const char * module; + const char ** units; + const struct tbnf_common_ctx * cctx; + struct tbnf_error_info ** errv; + void * any; +}; + +struct tbnf_unit_ctx { + const char * const * path; + const struct tbnf_raw_input * map; + void * any; +}; + +/* driver api */ +tbnf_api int tbnf_lib_get_driver_ctx (char ** argv, char ** envp, uint64_t flags, + const struct tbnf_fd_ctx *, + struct tbnf_driver_ctx **); + +tbnf_api void tbnf_lib_free_driver_ctx (struct tbnf_driver_ctx *); + +tbnf_api int tbnf_lib_get_unit_ctx (const struct tbnf_driver_ctx *, const char * path, + struct tbnf_unit_ctx **); + +tbnf_api void tbnf_lib_free_unit_ctx (struct tbnf_unit_ctx *); + +tbnf_api int tbnf_lib_get_driver_fdctx (const struct tbnf_driver_ctx *, struct tbnf_fd_ctx *); + +tbnf_api int tbnf_lib_set_driver_fdctx (struct tbnf_driver_ctx *, const struct tbnf_fd_ctx *); + +tbnf_api int tbnf_lib_map_raw_input (const struct tbnf_driver_ctx *, + int fd, const char * path, int prot, + struct tbnf_raw_input *); + +tbnf_api int tbnf_lib_unmap_raw_input (struct tbnf_raw_input *); + +/* utility api */ +tbnf_api int tbnf_main (char **, char **, + const struct tbnf_fd_ctx *); + +tbnf_api int tbnf_output_error_vector (const struct tbnf_driver_ctx *); +tbnf_api int tbnf_output_error_record (const struct tbnf_driver_ctx *, const struct tbnf_error_info *); + +/* package info */ +tbnf_api const struct tbnf_source_version * tbnf_api_source_version(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/treebnf/treebnf_api.h b/include/treebnf/treebnf_api.h new file mode 100644 index 0000000..ca55079 --- /dev/null +++ b/include/treebnf/treebnf_api.h @@ -0,0 +1,35 @@ +#ifndef TREEBNF_API_H +#define TREEBNF_API_H + +#include + +/* tbnf_export */ +#if defined(__dllexport) +#define tbnf_export __dllexport +#else +#define tbnf_export +#endif + +/* tbnf_import */ +#if defined(__dllimport) +#define tbnf_import __dllimport +#else +#define tbnf_import +#endif + +/* tbnf_api */ +#ifndef TBNF_APP +#if defined (TBNF_EXPORT) +#define tbnf_api tbnf_export +#elif defined (TBNF_IMPORT) +#define tbnf_api tbnf_import +#elif defined (TBNF_STATIC) +#define tbnf_api +#else +#define tbnf_api +#endif +#else +#define tbnf_api +#endif + +#endif diff --git a/project/common.mk b/project/common.mk index 0843a4c..7a77195 100644 --- a/project/common.mk +++ b/project/common.mk @@ -1,6 +1,15 @@ API_SRCS = \ + src/driver/tbnf_amain.c \ + src/driver/tbnf_driver_ctx.c \ + src/driver/tbnf_map_input.c \ + src/driver/tbnf_unit_ctx.c \ + src/output/tbnf_output_error.c \ + src/skin/tbnf_skin_default.c \ INTERNAL_SRCS = \ + src/internal/$(PACKAGE)_dprintf_impl.c \ + src/internal/$(PACKAGE)_errinfo_impl.c \ + src/internal/$(PACKAGE)_tmpfile_impl.c \ APP_SRCS = \ src/treebnf.c diff --git a/project/extras.mk b/project/extras.mk index e69de29..64725b0 100644 --- a/project/extras.mk +++ b/project/extras.mk @@ -0,0 +1,8 @@ +CFLAGS_SHARED_ATTR += -DTBNF_PRE_ALPHA -DTBNF_EXPORT +CFLAGS_STATIC_ATTR += -DTBNF_PRE_ALPHA -DTBNF_STATIC +CFLAGS_APP_ATTR += -DTBNF_APP + +CFLAGS_CONFIG += $(CFLAGS_ATTR_VISIBILITY_HIDDEN) + +src/driver/tbnf_driver_ctx.o: version.tag +src/driver/tbnf_driver_ctx.lo: version.tag diff --git a/project/headers.mk b/project/headers.mk index 9d50ee3..c5ae8b6 100644 --- a/project/headers.mk +++ b/project/headers.mk @@ -1,5 +1,13 @@ API_HEADERS = \ + $(SOURCE_DIR)/include/$(PACKAGE)/treebnf.h \ + $(SOURCE_DIR)/include/$(PACKAGE)/treebnf_api.h \ INTERNAL_HEADERS = \ + $(SOURCE_DIR)/src/internal/argv/argv.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_dprintf_impl.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_driver_impl.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_errinfo_impl.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_tmpfile_impl.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_visibility_impl.h \ ALL_HEADERS = $(API_HEADERS) $(INTERNAL_HEADERS) diff --git a/project/tree.mk b/project/tree.mk index 7d6d7e7..f3ae848 100644 --- a/project/tree.mk +++ b/project/tree.mk @@ -1,4 +1,8 @@ -TREE_DIRS = src \ +TREE_DIRS = src \ + src/driver \ + src/output \ + src/skin \ + src/internal \ tree.tag: mkdir -p $(TREE_DIRS) diff --git a/src/driver/tbnf_amain.c b/src/driver/tbnf_amain.c new file mode 100644 index 0000000..62e66bc --- /dev/null +++ b/src/driver/tbnf_amain.c @@ -0,0 +1,97 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#include +#include +#include +#include "treebnf_driver_impl.h" +#include "treebnf_dprintf_impl.h" + +#ifndef TBNF_DRIVER_FLAGS +#define TBNF_DRIVER_FLAGS TBNF_DRIVER_VERBOSITY_ERRORS \ + | TBNF_DRIVER_VERBOSITY_USAGE +#endif + +static const char vermsg[] = "%s%s%s (https://git.foss21.org/tulips/treebnf): " + "version %s%d.%d.%d%s.\n" + "[commit reference: %s%s%s]\n"; + +static const char * const tbnf_ver_color[6] = { + "\x1b[1m\x1b[35m","\x1b[0m", + "\x1b[1m\x1b[32m","\x1b[0m", + "\x1b[1m\x1b[34m","\x1b[0m" +}; + +static const char * const tbnf_ver_plain[6] = { + "","", + "","", + "","" +}; + +static ssize_t tbnf_version(struct tbnf_driver_ctx * dctx, int fdout) +{ + const struct tbnf_source_version * verinfo; + const char * const * verclr; + + verinfo = tbnf_api_source_version(); + verclr = isatty(fdout) ? tbnf_ver_color : tbnf_ver_plain; + + return tbnf_dprintf( + fdout,vermsg, + verclr[0],dctx->program,verclr[1], + verclr[2],verinfo->major,verinfo->minor, + verinfo->revision,verclr[3], + verclr[4],verinfo->commit,verclr[5]); +} + +static void tbnf_perform_unit_actions( + const struct tbnf_driver_ctx * dctx, + struct tbnf_unit_ctx * uctx) +{ + uint64_t flags = dctx->cctx->fmtflags; + + (void)dctx; + (void)uctx; + (void)flags; +} + +static int tbnf_exit(struct tbnf_driver_ctx * dctx, int ret) +{ + tbnf_output_error_vector(dctx); + tbnf_lib_free_driver_ctx(dctx); + return ret; +} + +int tbnf_main(char ** argv, char ** envp, const struct tbnf_fd_ctx * fdctx) +{ + int ret; + int fdout; + uint64_t flags; + struct tbnf_driver_ctx * dctx; + struct tbnf_unit_ctx * uctx; + const char ** unit; + + flags = TBNF_DRIVER_FLAGS; + fdout = fdctx ? fdctx->fdout : STDOUT_FILENO; + + if ((ret = tbnf_lib_get_driver_ctx(argv,envp,flags,fdctx,&dctx))) + return (ret == TBNF_USAGE) + ? !argv || !argv[0] || !argv[1] + : TBNF_ERROR; + + if (dctx->cctx->drvflags & TBNF_DRIVER_VERSION) + if ((tbnf_version(dctx,fdout)) < 0) + return tbnf_exit(dctx,TBNF_ERROR); + + for (unit=dctx->units; *unit; unit++) { + if (!(tbnf_lib_get_unit_ctx(dctx,*unit,&uctx))) { + tbnf_perform_unit_actions(dctx,uctx); + tbnf_lib_free_unit_ctx(uctx); + } + } + + return tbnf_exit(dctx,dctx->errv[0] ? TBNF_ERROR : TBNF_OK); +} diff --git a/src/driver/tbnf_driver_ctx.c b/src/driver/tbnf_driver_ctx.c new file mode 100644 index 0000000..8d4b5e5 --- /dev/null +++ b/src/driver/tbnf_driver_ctx.c @@ -0,0 +1,254 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#include +#include +#include + +#define ARGV_DRIVER + +#include +#include "treebnf_version.h" +#include "treebnf_driver_impl.h" +#include "argv/argv.h" + +/* package info */ +static const struct tbnf_source_version tbnf_src_version = { + TBNF_TAG_VER_MAJOR, + TBNF_TAG_VER_MINOR, + TBNF_TAG_VER_PATCH, + TREEBNF_GIT_VERSION +}; + +/* default fd context */ +static const struct tbnf_fd_ctx tbnf_default_fdctx = { + .fdin = STDIN_FILENO, + .fdout = STDOUT_FILENO, + .fderr = STDERR_FILENO, + .fdcwd = AT_FDCWD, + .fddst = AT_FDCWD, + .fdlog = (-1), +}; + +struct tbnf_driver_ctx_alloc { + struct argv_meta * meta; + struct tbnf_driver_ctx_impl ctx; + uint64_t guard; + const char * units[]; +}; + +static uint32_t tbnf_argv_flags(uint32_t flags) +{ + uint32_t ret = ARGV_CLONE_VECTOR; + + if (flags & TBNF_DRIVER_VERBOSITY_NONE) + ret |= ARGV_VERBOSITY_NONE; + + if (flags & TBNF_DRIVER_VERBOSITY_ERRORS) + ret |= ARGV_VERBOSITY_ERRORS; + + if (flags & TBNF_DRIVER_VERBOSITY_STATUS) + ret |= ARGV_VERBOSITY_STATUS; + + return ret; +} + +static int tbnf_driver_usage( + int fdout, + const char * program, + const char * arg, + const struct argv_option ** optv, + struct argv_meta * meta) +{ + char header[512]; + + snprintf(header,sizeof(header), + "Usage: %s [options] ...\n" "Options:\n", + program); + + argv_usage(fdout,header,optv,arg); + argv_free(meta); + + return TBNF_USAGE; +} + +static struct tbnf_driver_ctx_impl * tbnf_driver_ctx_alloc( + struct argv_meta * meta, + const struct tbnf_fd_ctx * fdctx, + const struct tbnf_common_ctx * cctx, + size_t nunits) +{ + struct tbnf_driver_ctx_alloc * ictx; + size_t size; + struct argv_entry * entry; + const char ** units; + int elements; + + size = sizeof(struct tbnf_driver_ctx_alloc); + size += (nunits+1)*sizeof(const char *); + + if (!(ictx = calloc(1,size))) + return 0; + + memcpy(&ictx->ctx.fdctx,fdctx,sizeof(*fdctx)); + memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx)); + + elements = sizeof(ictx->ctx.erribuf) / sizeof(*ictx->ctx.erribuf); + + ictx->ctx.errinfp = &ictx->ctx.erriptr[0]; + ictx->ctx.erricap = &ictx->ctx.erriptr[--elements]; + + ictx->meta = meta; + + for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++) + if (!entry->fopt) + *units++ = entry->arg; + + ictx->ctx.fdtmpin = (-1); + ictx->ctx.ctx.units = ictx->units; + ictx->ctx.ctx.errv = ictx->ctx.errinfp; + return &ictx->ctx; +} + +static int tbnf_get_driver_ctx_fail(struct argv_meta * meta) +{ + argv_free(meta); + return -1; +} + +int tbnf_lib_get_driver_ctx( + char ** argv, + char ** envp, + uint64_t flags, + const struct tbnf_fd_ctx * fdctx, + struct tbnf_driver_ctx ** pctx) +{ + struct tbnf_driver_ctx_impl * ctx; + struct tbnf_common_ctx cctx; + const struct argv_option * optv[TBNF_OPTV_ELEMENTS]; + struct argv_meta * meta; + struct argv_entry * entry; + size_t nunits; + const char * program; + + (void)envp; + + if (!fdctx) + fdctx = &tbnf_default_fdctx; + + argv_optv_init(tbnf_default_options,optv); + + if (!(meta = argv_get( + argv,optv, + tbnf_argv_flags(flags), + fdctx->fderr))) + return -1; + + nunits = 0; + program = argv_program_name(argv[0]); + memset(&cctx,0,sizeof(cctx)); + cctx.drvflags = flags; + + if (!argv[1] && (flags & TBNF_DRIVER_VERBOSITY_USAGE)) + return tbnf_driver_usage( + fdctx->fderr, + program, + 0,optv,meta); + + /* get options, count units */ + for (entry=meta->entries; entry->fopt || entry->arg; entry++) { + if (entry->fopt) { + switch (entry->tag) { + case TAG_HELP: + if (flags & TBNF_DRIVER_VERBOSITY_USAGE) + return tbnf_driver_usage( + fdctx->fdout, + program,entry->arg, + optv,meta); + break; + + case TAG_VERSION: + cctx.drvflags |= TBNF_DRIVER_VERSION; + break; + } + } else { + nunits++; + } + } + + if (!(ctx = tbnf_driver_ctx_alloc(meta,fdctx,&cctx,nunits))) + return tbnf_get_driver_ctx_fail(meta); + + ctx->ctx.program = program; + ctx->ctx.cctx = &ctx->cctx; + + *pctx = &ctx->ctx; + + return TBNF_OK; +} + +static void tbnf_free_driver_ctx_impl(struct tbnf_driver_ctx_alloc * ictx) +{ + if (ictx->ctx.fdtmpin) + close(ictx->ctx.fdtmpin); + + argv_free(ictx->meta); + free(ictx); +} + +void tbnf_lib_free_driver_ctx(struct tbnf_driver_ctx * ctx) +{ + struct tbnf_driver_ctx_alloc * ictx; + uintptr_t addr; + + if (ctx) { + addr = (uintptr_t)ctx - offsetof(struct tbnf_driver_ctx_impl,ctx); + addr = addr - offsetof(struct tbnf_driver_ctx_alloc,ctx); + ictx = (struct tbnf_driver_ctx_alloc *)addr; + tbnf_free_driver_ctx_impl(ictx); + } +} + +const struct tbnf_source_version * tbnf_api_source_version(void) +{ + return &tbnf_src_version; +} + +int tbnf_lib_get_driver_fdctx( + const struct tbnf_driver_ctx * dctx, + struct tbnf_fd_ctx * fdctx) +{ + struct tbnf_driver_ctx_impl * ictx; + + ictx = tbnf_get_driver_ictx(dctx); + + fdctx->fdin = ictx->fdctx.fdin; + fdctx->fdout = ictx->fdctx.fdout; + fdctx->fderr = ictx->fdctx.fderr; + fdctx->fdlog = ictx->fdctx.fdlog; + fdctx->fdcwd = ictx->fdctx.fdcwd; + fdctx->fddst = ictx->fdctx.fddst; + + return 0; +} + +int tbnf_lib_set_driver_fdctx( + struct tbnf_driver_ctx * dctx, + const struct tbnf_fd_ctx * fdctx) +{ + struct tbnf_driver_ctx_impl * ictx; + + ictx = tbnf_get_driver_ictx(dctx); + + ictx->fdctx.fdin = fdctx->fdin; + ictx->fdctx.fdout = fdctx->fdout; + ictx->fdctx.fderr = fdctx->fderr; + ictx->fdctx.fdlog = fdctx->fdlog; + ictx->fdctx.fdcwd = fdctx->fdcwd; + ictx->fdctx.fddst = fdctx->fddst; + + return 0; +} diff --git a/src/driver/tbnf_map_input.c b/src/driver/tbnf_map_input.c new file mode 100644 index 0000000..629672a --- /dev/null +++ b/src/driver/tbnf_map_input.c @@ -0,0 +1,66 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "treebnf_driver_impl.h" +#include "treebnf_errinfo_impl.h" + +int tbnf_lib_map_raw_input( + const struct tbnf_driver_ctx * dctx, + int fd, + const char * path, + int prot, + struct tbnf_raw_input * map) +{ + int ret; + struct stat st; + bool fnew; + int fdcwd; + + fdcwd = tbnf_driver_fdcwd(dctx); + + if ((fnew = (fd < 0))) + fd = openat(fdcwd,path,O_RDONLY | O_CLOEXEC); + + if (fd < 0) + return TBNF_SYSTEM_ERROR(dctx,path); + + if ((ret = fstat(fd,&st) < 0) && fnew) + close(fd); + + else if ((st.st_size == 0) && fnew) + close(fd); + + if (ret < 0) + return TBNF_SYSTEM_ERROR(dctx,path); + + else if (st.st_size == 0) + return TBNF_CUSTOM_ERROR( + dctx,TBNF_ERR_IMAGE_SIZE_ZERO); + + map->map_size = st.st_size; + map->map_addr = mmap(0,map->map_size,prot,MAP_PRIVATE,fd,0); + + if (fnew) + close(fd); + + return (map->map_addr == MAP_FAILED) + ? TBNF_SYSTEM_ERROR(dctx,0) + : 0; +} + +int tbnf_lib_unmap_raw_input(struct tbnf_raw_input * map) +{ + return munmap(map->map_addr,map->map_size); +} diff --git a/src/driver/tbnf_unit_ctx.c b/src/driver/tbnf_unit_ctx.c new file mode 100644 index 0000000..fedcbaa --- /dev/null +++ b/src/driver/tbnf_unit_ctx.c @@ -0,0 +1,139 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include "treebnf_driver_impl.h" +#include "treebnf_errinfo_impl.h" +#include "treebnf_tmpfile_impl.h" + +static int tbnf_free_unit_ctx_impl(struct tbnf_unit_ctx_impl * ctx, int ret) +{ + if (ctx) { + tbnf_lib_unmap_raw_input(&ctx->map); + free(ctx); + } + + return ret; +} + +static int tbnf_stdin_to_tmp(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_driver_ctx_impl * ictx; + int fdtmp; + + ssize_t ret; + ssize_t cnt; + char * ch; + char buf[4096]; + + ictx = tbnf_get_driver_ictx(dctx); + + if (ictx->fdtmpin >= 0) + return dup(ictx->fdtmpin); + + if ((fdtmp = tbnf_tmpfile()) < 0) + return -1; + + if ((ictx->fdtmpin = dup(fdtmp)) < 0) { + close(fdtmp); + return -1; + } + + for (;;) { + ret = read(0,buf,sizeof(buf)-1); + + while ((ret < 0) && (errno == EINTR)) + ret = read(0,buf,sizeof(buf)-1); + + if (ret < 0) { + close(fdtmp); + return -1; + + } else if (ret == 0) { + return fdtmp; + + } else { + ch = buf; + cnt = ret; + + for (; cnt; ) { + ret = write(fdtmp,ch,cnt); + + while ((ret < 0) && (errno == EINTR)) + ret = write(fdtmp,ch,cnt); + + if (ret < 0) { + close(fdtmp); + return -1; + } + + ch += ret; + cnt -= ret; + } + } + } +} + +int tbnf_lib_get_unit_ctx( + const struct tbnf_driver_ctx * dctx, + const char * path, + struct tbnf_unit_ctx ** pctx) +{ + struct tbnf_unit_ctx_impl * ctx; + int prot; + int fd; + + if (!dctx) + return TBNF_CUSTOM_ERROR( + dctx,TBNF_ERR_NULL_CONTEXT); + + else if (!(ctx = calloc(1,sizeof(*ctx)))) + return TBNF_BUFFER_ERROR(dctx); + + tbnf_driver_set_ectx( + dctx,0,path); + + prot = PROT_READ; + + if (strcmp(path,"-")) + fd = -1; + + else if ((fd = tbnf_stdin_to_tmp(dctx)) < 0) + return tbnf_free_unit_ctx_impl( + ctx,TBNF_FILE_ERROR(dctx)); + + if (tbnf_lib_map_raw_input(dctx,fd,path,prot,&ctx->map)) + return tbnf_free_unit_ctx_impl( + ctx,TBNF_NESTED_ERROR(dctx)); + + if (fd >= 0) + close(fd); + + ctx->path = path; + ctx->uctx.path = &ctx->path; + ctx->uctx.map = &ctx->map; + + *pctx = &ctx->uctx; + return 0; +} + +void tbnf_lib_free_unit_ctx(struct tbnf_unit_ctx * ctx) +{ + struct tbnf_unit_ctx_impl * ictx; + uintptr_t addr; + + if (ctx) { + addr = (uintptr_t)ctx - offsetof(struct tbnf_unit_ctx_impl,uctx); + ictx = (struct tbnf_unit_ctx_impl *)addr; + tbnf_free_unit_ctx_impl(ictx,0); + } +} diff --git a/src/internal/treebnf_dprintf_impl.c b/src/internal/treebnf_dprintf_impl.c new file mode 100644 index 0000000..8b24a93 --- /dev/null +++ b/src/internal/treebnf_dprintf_impl.c @@ -0,0 +1,65 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#include +#include +#include +#include +#include + +#include "treebnf_dprintf_impl.h" +#include "treebnf_visibility_impl.h" + +tbnf_hidden int tbnf_dprintf(int fd, const char * fmt, ...) +{ + int ret; + int cnt; + int size; + va_list ap; + char * ch; + char * buf; + char chbuf[2048]; + + va_start(ap,fmt); + + size = sizeof(chbuf); + buf = ((cnt = vsnprintf(chbuf,size,fmt,ap)) < size) + ? chbuf : malloc(cnt + 1); + + va_end(ap); + + if (buf == chbuf) { + (void)0; + + } else if (buf) { + va_start(ap,fmt); + vsprintf(buf,fmt,ap); + va_end(ap); + + } else { + return -1; + } + + ret = 0; + ch = buf; + + for (; cnt && ret>=0; ) { + ret = write(fd,ch,cnt); + + while ((ret < 0) && (errno == EINTR)) + ret = write(fd,ch,cnt); + + ch += ret; + cnt -= ret; + } + + ret = (ret < 0) ? -1 : ch - buf; + + if (buf != chbuf) + free(buf); + + return ret; +} diff --git a/src/internal/treebnf_dprintf_impl.h b/src/internal/treebnf_dprintf_impl.h new file mode 100644 index 0000000..d00ac0f --- /dev/null +++ b/src/internal/treebnf_dprintf_impl.h @@ -0,0 +1,10 @@ +#ifndef TREEBNF_DPRINTF_IMPL_H +#define TREEBNF_DPRINTF_IMPL_H + +#ifdef ARGV_DRIVER +#define argv_dprintf tbnf_dprintf +#endif + +int tbnf_dprintf(int fd, const char * fmt, ...); + +#endif diff --git a/src/internal/treebnf_driver_impl.h b/src/internal/treebnf_driver_impl.h new file mode 100644 index 0000000..7fcdd43 --- /dev/null +++ b/src/internal/treebnf_driver_impl.h @@ -0,0 +1,127 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#ifndef TREEBNF_DRIVER_IMPL_H +#define TREEBNF_DRIVER_IMPL_H + +#include +#include +#include +#include +#include + +#include +#include "argv/argv.h" + +#define TBNF_OPTV_ELEMENTS 128 + +extern const struct argv_option tbnf_default_options[]; + +enum app_tags { + TAG_HELP, + TAG_VERSION, +}; + +struct tbnf_driver_ctx_impl { + struct tbnf_common_ctx cctx; + struct tbnf_driver_ctx ctx; + struct tbnf_fd_ctx fdctx; + int fdtmpin; + const struct tbnf_unit_ctx * euctx; + const char * eunit; + struct tbnf_error_info ** errinfp; + struct tbnf_error_info ** erricap; + struct tbnf_error_info * erriptr[64]; + struct tbnf_error_info erribuf[64]; +}; + +struct tbnf_unit_ctx_impl { + const char * path; + struct tbnf_unit_ctx uctx; + struct tbnf_raw_input map; + struct stat st; +}; + + +static inline struct tbnf_driver_ctx_impl * tbnf_get_driver_ictx( + const struct tbnf_driver_ctx * dctx) +{ + uintptr_t addr; + + if (dctx) { + addr = (uintptr_t)dctx - offsetof(struct tbnf_driver_ctx_impl,ctx); + return (struct tbnf_driver_ctx_impl *)addr; + } + + return 0; +} + +static inline struct tbnf_unit_ctx_impl * tbnf_get_unit_ictx( + const struct tbnf_unit_ctx * uctx) +{ + struct tbnf_unit_ctx_impl * ictx; + uintptr_t addr; + + addr = (uintptr_t)uctx - offsetof(struct tbnf_unit_ctx_impl,uctx); + ictx = (struct tbnf_unit_ctx_impl *)addr; + return ictx; +} + +static inline void tbnf_driver_set_ectx( + const struct tbnf_driver_ctx * dctx, + const struct tbnf_unit_ctx * uctx, + const char * unit) +{ + struct tbnf_driver_ctx_impl * ictx; + + ictx = tbnf_get_driver_ictx(dctx); + ictx->euctx = uctx; + ictx->eunit = unit; +} + +static inline int tbnf_driver_fdin(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_fd_ctx fdctx; + tbnf_lib_get_driver_fdctx(dctx,&fdctx); + return fdctx.fdin; +} + +static inline int tbnf_driver_fdout(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_fd_ctx fdctx; + tbnf_lib_get_driver_fdctx(dctx,&fdctx); + return fdctx.fdout; +} + +static inline int tbnf_driver_fderr(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_fd_ctx fdctx; + tbnf_lib_get_driver_fdctx(dctx,&fdctx); + return fdctx.fderr; +} + +static inline int tbnf_driver_fdlog(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_fd_ctx fdctx; + tbnf_lib_get_driver_fdctx(dctx,&fdctx); + return fdctx.fdlog; +} + +static inline int tbnf_driver_fdcwd(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_fd_ctx fdctx; + tbnf_lib_get_driver_fdctx(dctx,&fdctx); + return fdctx.fdcwd; +} + +static inline int tbnf_driver_fddst(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_fd_ctx fdctx; + tbnf_lib_get_driver_fdctx(dctx,&fdctx); + return fdctx.fddst; +} + +#endif diff --git a/src/internal/treebnf_errinfo_impl.c b/src/internal/treebnf_errinfo_impl.c new file mode 100644 index 0000000..a639f27 --- /dev/null +++ b/src/internal/treebnf_errinfo_impl.c @@ -0,0 +1,43 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#include +#include "treebnf_driver_impl.h" +#include "treebnf_errinfo_impl.h" +#include "treebnf_visibility_impl.h" + +tbnf_hidden int tbnf_record_error( + const struct tbnf_driver_ctx * dctx, + int esyscode, + int elibcode, + const char * efunction, + int eline, + unsigned eflags, + void * eany) +{ + struct tbnf_driver_ctx_impl * ictx; + struct tbnf_error_info * erri; + + ictx = tbnf_get_driver_ictx(dctx); + + if (ictx->errinfp == ictx->erricap) + return -1; + + *ictx->errinfp = &ictx->erribuf[ictx->errinfp - ictx->erriptr]; + erri = *ictx->errinfp; + + erri->edctx = dctx; + erri->esyscode = esyscode; + erri->elibcode = elibcode; + erri->efunction = efunction; + erri->eline = eline; + erri->eflags = eflags; + erri->eany = (eany && (esyscode == ENOENT)) ? strdup(eany) : eany; + + ictx->errinfp++; + + return -1; +} diff --git a/src/internal/treebnf_errinfo_impl.h b/src/internal/treebnf_errinfo_impl.h new file mode 100644 index 0000000..45a7e14 --- /dev/null +++ b/src/internal/treebnf_errinfo_impl.h @@ -0,0 +1,85 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#ifndef TREEBNF_ERRINFO_IMPL_H +#define TREEBNF_ERRINFO_IMPL_H + +#include +#include + +int tbnf_record_error( + const struct tbnf_driver_ctx *, + int esyscode, + int elibcode, + const char * efunction, + int eline, + unsigned eflags, + void * eany); + +#define TBNF_SYSTEM_ERROR(dctx,eany) \ + tbnf_record_error( \ + dctx, \ + errno, \ + 0, \ + __func__, \ + __LINE__, \ + TBNF_ERROR_TOP_LEVEL, \ + (void *)eany) + +#define TBNF_BUFFER_ERROR(dctx) \ + tbnf_record_error( \ + dctx, \ + ENOBUFS, \ + 0, \ + __func__, \ + __LINE__, \ + TBNF_ERROR_TOP_LEVEL, \ + 0) + +#define TBNF_SPAWN_ERROR(dctx) \ + tbnf_record_error( \ + dctx, \ + errno, \ + 0, \ + __func__, \ + __LINE__, \ + TBNF_ERROR_TOP_LEVEL \ + | (errno ? 0 \ + : TBNF_ERROR_CHILD), \ + 0) + +#define TBNF_FILE_ERROR(dctx) \ + tbnf_record_error( \ + dctx, \ + EIO, \ + 0, \ + __func__, \ + __LINE__, \ + TBNF_ERROR_TOP_LEVEL, \ + 0) + +#define TBNF_CUSTOM_ERROR(dctx,elibcode) \ + tbnf_record_error( \ + dctx, \ + 0, \ + elibcode, \ + __func__, \ + __LINE__, \ + TBNF_ERROR_TOP_LEVEL \ + | TBNF_ERROR_CUSTOM, \ + 0) + +#define TBNF_NESTED_ERROR(dctx) \ + tbnf_record_error( \ + dctx, \ + 0, \ + 0, \ + __func__, \ + __LINE__, \ + TBNF_ERROR_NESTED, \ + 0) + +#endif diff --git a/src/internal/treebnf_tmpfile_impl.c b/src/internal/treebnf_tmpfile_impl.c new file mode 100644 index 0000000..2a5eb45 --- /dev/null +++ b/src/internal/treebnf_tmpfile_impl.c @@ -0,0 +1,89 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "treebnf_visibility_impl.h" + +#define PPRIX64 "%"PRIx64 + +/* mkostemp might be guarded by non-standard macros */ +/* unless HAVE_NO_MKOSTEMP, assume it is available */ +extern int mkstemp(char *); +extern int mkostemp(char *, int); + +/* __fs_tmpfile() atomically provides a private tmpfile */ +static int tbnf_tmpfile_by_framework(void) +{ +#ifdef _MIDIPIX_ABI + extern int __fs_tmpfile(int); + return __fs_tmpfile(O_CLOEXEC); +#else + return (-1); +#endif +} + +/* O_TMPFILE atomically provides a private tmpfile */ +static int tbnf_tmpfile_by_kernel(void) +{ +#ifdef O_TMPFILE + return openat(AT_FDCWD,"/tmp",O_RDWR|O_TMPFILE|O_CLOEXEC,0); +#else + return (-1); +#endif +} + +/* mk{o}stemp() provides a non-private tmpfile */ +static int tbnf_mkostemp(char * tmplate) +{ + int fd; +#ifdef HAVE_NO_MKOSTEMP + if ((fd = mkstemp(tmplate)) >= 0) + fcntl(fd,F_SETFD,FD_CLOEXEC); +#else + fd = mkostemp(tmplate,O_CLOEXEC); +#endif + return fd; +} + +tbnf_hidden int tbnf_tmpfile(void) +{ + int fd; + void * addr; + char tmplate[128]; + + /* try with __fs_tmpfile() */ + if ((fd = tbnf_tmpfile_by_framework()) >= 0) + return fd; + + /* try with O_TMPFILE */ + if ((fd = tbnf_tmpfile_by_kernel()) >= 0) + return fd; + + /* fallback to mk{o}stemp */ + addr = tmplate; + memset(tmplate,0,sizeof(tmplate)); + snprintf(tmplate,sizeof(tmplate), + "/tmp/" + ".treebnf.tmpfile" + ".time."PPRIX64 + ".salt.%p" + ".pid.%d" + ".XXXXXXXXXXXX", + time(0), + addr, + getpid()); + + return tbnf_mkostemp(tmplate); +} diff --git a/src/internal/treebnf_tmpfile_impl.h b/src/internal/treebnf_tmpfile_impl.h new file mode 100644 index 0000000..c295072 --- /dev/null +++ b/src/internal/treebnf_tmpfile_impl.h @@ -0,0 +1,6 @@ +#ifndef TREEBNF_TMPFILE_IMPL_H +#define TREEBNF_TMPFILE_IMPL_H + +int tbnf_tmpfile(void); + +#endif diff --git a/src/internal/treebnf_visibility_impl.h b/src/internal/treebnf_visibility_impl.h new file mode 100644 index 0000000..9599010 --- /dev/null +++ b/src/internal/treebnf_visibility_impl.h @@ -0,0 +1,26 @@ +#ifndef TREEBNF_VISIBILITY_IMPL_H +#define TREEBNF_VISIBILITY_IMPL_H + +/**********************************************************************/ +/* PE targets: __dllexport suffices for the purpose of exporting only */ +/* the desired subset of global symbols; this makes the visibility */ +/* attribute not only redundant, but also tricky if not properly */ +/* supported by the toolchain. */ +/* */ +/* When targeting Midipix, __PE__, __dllexport and __dllimport are */ +/* always defined by the toolchain. Otherwise, the absnece of these */ +/* macros has been detected by sofort's ccenv.sh during ./configure, */ +/* and they have accordingly been added to CFLAGS_OS. */ +/**********************************************************************/ + +#ifdef __PE__ +#define tbnf_hidden +#else +#ifdef _ATTR_VISIBILITY_HIDDEN +#define tbnf_hidden _ATTR_VISIBILITY_HIDDEN +#else +#define tbnf_hidden +#endif +#endif + +#endif diff --git a/src/output/tbnf_output_error.c b/src/output/tbnf_output_error.c new file mode 100644 index 0000000..d607e54 --- /dev/null +++ b/src/output/tbnf_output_error.c @@ -0,0 +1,192 @@ +/**************************************************************/ +/* treebnf: a tree oriented bnf library */ +/* Copyright (C) 2024 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ +/**************************************************************/ + +#include +#include +#include +#include +#include + +#include "treebnf_driver_impl.h" +#include "treebnf_dprintf_impl.h" + +static const char aclr_reset[] = "\x1b[0m"; +static const char aclr_bold[] = "\x1b[1m"; + +static const char aclr_green[] = "\x1b[32m"; +static const char aclr_blue[] = "\x1b[34m"; +static const char aclr_magenta[] = "\x1b[35m"; + +static const char * tbnf_output_error_header(const struct tbnf_error_info * erri) +{ + if (erri->eflags & TBNF_ERROR_CHILD) + return "exec error upon"; + + else if (erri->eflags & TBNF_ERROR_TOP_LEVEL) + return "error logged in"; + + else if (erri->eflags & TBNF_ERROR_NESTED) + return "< returned to >"; + + else + return "distorted state"; +} + +static const char * tbnf_output_strerror( + const struct tbnf_error_info * erri, + char (*errbuf)[256]) +{ + if (erri->eflags & TBNF_ERROR_CUSTOM) + return "flow error: unexpected condition or other"; + + else if (erri->eflags & TBNF_ERROR_NESTED) + return ""; + + else if (erri->eflags & TBNF_ERROR_CHILD) + return "(see child process error messages)"; + + else if (erri->esyscode == ENOBUFS) + return "input error: string length exceeds buffer size"; + + else if ((erri->esyscode == ENOENT) && erri->eany) + return "path not found: "; + + else + return strerror_r(erri->esyscode,*errbuf,sizeof(*errbuf)) + ? "internal error: strerror_r(3) call failed" + : *errbuf; +} + +static int tbnf_output_error_record_plain( + const struct tbnf_driver_ctx * dctx, + const struct tbnf_error_info * erri) +{ + char errbuf[256]; + + int fderr = tbnf_driver_fderr(dctx); + const char * errdesc = tbnf_output_strerror(erri,&errbuf); + const char * path; + + path = ((erri->esyscode == ENOENT) && erri->eany) + ? erri->eany : ""; + + if (tbnf_dprintf( + fderr, + "%s: %s %s(), line %d%s%s%s.\n", + dctx->program, + tbnf_output_error_header(erri), + erri->efunction, + erri->eline, + strlen(errdesc) ? ": " : "", + errdesc,path) < 0) + return -1; + + return 0; +} + +static int tbnf_output_error_record_annotated( + const struct tbnf_driver_ctx * dctx, + const struct tbnf_error_info * erri) +{ + char errbuf[256]; + + int fderr = tbnf_driver_fderr(dctx); + const char * errdesc = tbnf_output_strerror(erri,&errbuf); + const char * path; + + path = ((erri->esyscode == ENOENT) && erri->eany) + ? erri->eany : ""; + + if (tbnf_dprintf( + fderr, + "%s%s%s:%s %s%s%s %s%s%s()%s, %s%sline %d%s%s%s%s%s%s%s%s%s.\n", + + aclr_bold,aclr_magenta, + dctx->program, + aclr_reset, + + aclr_bold, + tbnf_output_error_header(erri), + aclr_reset, + + aclr_bold,aclr_blue, + erri->efunction, + aclr_reset, + + aclr_bold,aclr_green, + erri->eline, + aclr_reset, + strlen(errdesc) ? ": " : "", + + aclr_bold, + errdesc, + aclr_reset, + + aclr_bold,aclr_blue, + path, + aclr_reset) < 0) + return -1; + + return 0; +} + +int tbnf_output_error_record( + const struct tbnf_driver_ctx * dctx, + const struct tbnf_error_info * erri) +{ + int fderr = tbnf_driver_fderr(dctx); + + if (dctx->cctx->drvflags & TBNF_DRIVER_ANNOTATE_NEVER) + return tbnf_output_error_record_plain(dctx,erri); + + else if (dctx->cctx->drvflags & TBNF_DRIVER_ANNOTATE_ALWAYS) + return tbnf_output_error_record_annotated(dctx,erri); + + else if (isatty(fderr)) + return tbnf_output_error_record_annotated(dctx,erri); + + else + return tbnf_output_error_record_plain(dctx,erri); +} + +static int tbnf_output_error_vector_plain(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_error_info ** perr; + + for (perr=dctx->errv; *perr; perr++) + if (tbnf_output_error_record_plain(dctx,*perr)) + return -1; + + return 0; +} + +static int tbnf_output_error_vector_annotated(const struct tbnf_driver_ctx * dctx) +{ + struct tbnf_error_info ** perr; + + for (perr=dctx->errv; *perr; perr++) + if (tbnf_output_error_record_annotated(dctx,*perr)) + return -1; + + return 0; +} + +int tbnf_output_error_vector(const struct tbnf_driver_ctx * dctx) +{ + int fderr = tbnf_driver_fderr(dctx); + + if (dctx->cctx->drvflags & TBNF_DRIVER_ANNOTATE_NEVER) + return tbnf_output_error_vector_plain(dctx); + + else if (dctx->cctx->drvflags & TBNF_DRIVER_ANNOTATE_ALWAYS) + return tbnf_output_error_vector_annotated(dctx); + + else if (isatty(fderr)) + return tbnf_output_error_vector_annotated(dctx); + + else + return tbnf_output_error_vector_plain(dctx); +} diff --git a/src/skin/tbnf_skin_default.c b/src/skin/tbnf_skin_default.c new file mode 100644 index 0000000..219099a --- /dev/null +++ b/src/skin/tbnf_skin_default.c @@ -0,0 +1,12 @@ +#include "treebnf_driver_impl.h" +#include "argv/argv.h" + +const struct argv_option tbnf_default_options[] = { + {"version", 0,TAG_VERSION,ARGV_OPTARG_NONE,0,0,0, + "show version information"}, + + {"help", 0,TAG_HELP,ARGV_OPTARG_OPTIONAL,0,"short|long",0, + "show usage information [listing %s options only]"}, + + {0,0,0,0,0,0,0,0} +}; diff --git a/src/treebnf.c b/src/treebnf.c index 0b9862a..fa73ecc 100644 --- a/src/treebnf.c +++ b/src/treebnf.c @@ -4,12 +4,12 @@ /* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */ /**************************************************************/ +#include + #define TBNF_UNUSED_PARAMETER(p) (void)p int main(int argc, char ** argv, char ** envp) { TBNF_UNUSED_PARAMETER(argc); - TBNF_UNUSED_PARAMETER(argv); - TBNF_UNUSED_PARAMETER(envp); - return 0; + return tbnf_main(argv,envp,0); }