diff options
author | midipix <writeonce@midipix.org> | 2016-07-04 00:50:42 -0400 |
---|---|---|
committer | midipix <writeonce@midipix.org> | 2016-07-21 03:47:24 -0400 |
commit | 0efa8cf1d20712cbfaa549d31a8ebc11a23f78ec (patch) | |
tree | 784e6b7220f8f7b75fddf4e95776e3e9f9f1642a /src | |
parent | 9afa0dab7afbc1f86c0c3ee656c6c92c872734b7 (diff) | |
download | ptycon-0efa8cf1d20712cbfaa549d31a8ebc11a23f78ec.tar.bz2 ptycon-0efa8cf1d20712cbfaa549d31a8ebc11a23f78ec.tar.xz |
created free-standing project skeleton.
Diffstat (limited to 'src')
-rw-r--r-- | src/driver/ptyc_amain.c | 74 | ||||
-rw-r--r-- | src/driver/ptyc_driver_ctx.c | 209 | ||||
-rw-r--r-- | src/internal/argv/argv.h | 907 | ||||
-rw-r--r-- | src/internal/nolibc/nt32/ptyc_compiler.s | 20 | ||||
-rw-r--r-- | src/internal/nolibc/nt64/ptyc_compiler.s | 20 | ||||
-rw-r--r-- | src/internal/nolibc/stdbool.h | 11 | ||||
-rw-r--r-- | src/internal/nolibc/stddef.h | 6 | ||||
-rw-r--r-- | src/internal/nolibc/stdint.h | 6 | ||||
-rw-r--r-- | src/internal/nolibc/stdio.h | 8 | ||||
-rw-r--r-- | src/internal/nolibc/stdlib.h | 4 | ||||
-rw-r--r-- | src/internal/nolibc/string.h | 4 | ||||
-rw-r--r-- | src/internal/nolibc/unistd.h | 4 | ||||
-rw-r--r-- | src/internal/ptycon_driver_impl.h | 26 | ||||
-rw-r--r-- | src/internal/ptycon_init_impl.h | 24 | ||||
-rw-r--r-- | src/internal/ptycon_memfn_impl.c | 94 | ||||
-rw-r--r-- | src/internal/ptycon_memfn_impl.h | 22 | ||||
-rw-r--r-- | src/internal/ptycon_nolibc_impl.c | 73 | ||||
-rw-r--r-- | src/internal/ptycon_nolibc_impl.h | 52 | ||||
-rw-r--r-- | src/internal/ptycon_ntaio_impl.c | 163 | ||||
-rw-r--r-- | src/ptycon.c | 169 | ||||
-rw-r--r-- | src/skin/ptyc_skin_default.c | 12 |
21 files changed, 1908 insertions, 0 deletions
diff --git a/src/driver/ptyc_amain.c b/src/driver/ptyc_amain.c new file mode 100644 index 0000000..4762d3a --- /dev/null +++ b/src/driver/ptyc_amain.c @@ -0,0 +1,74 @@ +/*********************************************************/ +/* ptycon: a pty-console bridge */ +/* Copyright (C) 2016 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ +/*********************************************************/ + +#include <ntapi/ntapi.h> +#include <stdio.h> +#include <unistd.h> + +#include <ptycon/ptycon.h> +#include "ptycon_init_impl.h" +#include "ptycon_driver_impl.h" +#include "ptycon_nolibc_impl.h" + +#ifndef PTYC_DRIVER_FLAGS +#define PTYC_DRIVER_FLAGS PTYC_DRIVER_VERBOSITY_ERRORS \ + | PTYC_DRIVER_VERBOSITY_USAGE +#endif + +static const char vermsg[] = "%s%s%s (git://midipix.org/ptycon): " + "version %s%d.%d.%d%s.\n" + "[commit reference: %s%s%s]\n"; + +static const char * const ptyc_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 ptyc_ver_plain[6] = { + "","", + "","", + "","" +}; + +static ssize_t ptyc_version(struct ptyc_driver_ctx * dctx) +{ + const struct ptyc_source_version * verinfo; + const char * const * verclr; + + verinfo = ptyc_source_version(); + verclr = isatty(STDOUT_FILENO) ? ptyc_ver_color : ptyc_ver_plain; + + return fprintf(stdout,vermsg, + verclr[0],dctx->program,verclr[1], + verclr[2],verinfo->major,verinfo->minor, + verinfo->revision,verclr[3], + verclr[4],verinfo->commit,verclr[5]); +} + +static int ptyc_exit(struct ptyc_driver_ctx * dctx, int nerrors) +{ + ptyc_free_driver_ctx(dctx); + return nerrors ? 2 : 0; +} + +int ptyc_main(int argc, char ** argv, char ** envp) +{ + int ret; + struct ptyc_driver_ctx * dctx; + + if ((ret = ptyc_init())) + return ret; + + if ((ret = ptyc_get_driver_ctx(argv,envp,PTYC_DRIVER_FLAGS,&dctx))) + return (ret == PTYC_USAGE) ? !--argc : 2; + + if (dctx->cctx->drvflags & PTYC_DRIVER_VERSION) + if ((ptyc_version(dctx)) < 0) + return ptyc_exit(dctx,2); + + return ptyc_exit(dctx,ret); +} diff --git a/src/driver/ptyc_driver_ctx.c b/src/driver/ptyc_driver_ctx.c new file mode 100644 index 0000000..cc50e36 --- /dev/null +++ b/src/driver/ptyc_driver_ctx.c @@ -0,0 +1,209 @@ +/*********************************************************/ +/* ptycon: a pty-console bridge */ +/* Copyright (C) 2016 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ +/*********************************************************/ + +#include <ntapi/ntapi.h> +#include <stdint.h> + +#include <ptycon/ptycon.h> +#include "ptycon_init_impl.h" +#include "ptycon_nolibc_impl.h" + +#define ARGV_DRIVER + +#include "ptycon_version.h" +#include "ptycon_driver_impl.h" +#include "argv/argv.h" + +/* ntapi accessor table */ +const ntapi_vtbl * ptyc_ntapi; + +/* package info */ +static const struct ptyc_source_version ptyc_src_version = { + PTYC_TAG_VER_MAJOR, + PTYC_TAG_VER_MINOR, + PTYC_TAG_VER_PATCH, + PTYCON_GIT_VERSION +}; + +struct ptyc_driver_ctx_alloc { + struct argv_meta * meta; + struct ptyc_driver_ctx_impl ctx; + uint64_t guard; + const char * units[]; +}; + +static uint32_t ptyc_argv_flags(uint32_t flags) +{ + uint32_t ret = 0; + + if (flags & PTYC_DRIVER_VERBOSITY_NONE) + ret |= ARGV_VERBOSITY_NONE; + + if (flags & PTYC_DRIVER_VERBOSITY_ERRORS) + ret |= ARGV_VERBOSITY_ERRORS; + + if (flags & PTYC_DRIVER_VERBOSITY_STATUS) + ret |= ARGV_VERBOSITY_STATUS; + + return ret; +} + +static int ptyc_driver_usage( + const char * program, + const char * arg, + const struct argv_option * options, + struct argv_meta * meta) +{ + char header[512]; + + snprintf(header,sizeof(header), + "Usage: %s [options] <file>...\n" "Options:\n", + program); + + argv_usage(stdout,header,options,arg); + argv_free(meta); + + return PTYC_USAGE; +} + +static struct ptyc_driver_ctx_impl * ptyc_driver_ctx_alloc( + struct argv_meta * meta, + const struct ptyc_common_ctx * cctx, + size_t nunits) +{ + struct ptyc_driver_ctx_alloc * ictx; + size_t size; + struct argv_entry * entry; + const char ** units; + + size = sizeof(struct ptyc_driver_ctx_alloc); + size += (nunits+1)*sizeof(const char *); + + if (!(ictx = calloc(1,size))) + return 0; + + if (cctx) + memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx)); + + for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++) + if (!entry->fopt) + *units++ = entry->arg; + + ictx->meta = meta; + ictx->ctx.ctx.units = ictx->units; + return &ictx->ctx; +} + +static int ptyc_get_driver_ctx_fail(struct argv_meta * meta) +{ + argv_free(meta); + return -1; +} + +int ptyc_get_driver_ctx( + char ** argv, + char ** envp, + uint32_t flags, + struct ptyc_driver_ctx ** pctx) +{ + struct ptyc_driver_ctx_impl * ctx; + struct ptyc_common_ctx cctx; + const struct argv_option * options; + struct argv_meta * meta; + struct argv_entry * entry; + size_t nunits; + const char * program; + + (void)envp; + + if (ptyc_init()) + return -1; + + options = ptyc_default_options; + + if (!(meta = argv_get(argv,options,ptyc_argv_flags(flags)))) + return -1; + + nunits = 0; + program = argv_program_name(argv[0]); + memset(&cctx,0,sizeof(cctx)); + cctx.drvflags = flags; + + if (!argv[1] && (flags & PTYC_DRIVER_VERBOSITY_USAGE)) + return ptyc_driver_usage(program,0,options,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 & PTYC_DRIVER_VERBOSITY_USAGE) + return ptyc_driver_usage(program,entry->arg,options,meta); + + case TAG_VERSION: + cctx.drvflags |= PTYC_DRIVER_VERSION; + break; + } + } else + nunits++; + } + + if (!(ctx = ptyc_driver_ctx_alloc(meta,&cctx,nunits))) + return ptyc_get_driver_ctx_fail(meta); + + ctx->ctx.program = program; + ctx->ctx.cctx = &ctx->cctx; + + *pctx = &ctx->ctx; + return PTYC_OK; +} + +int ptyc_create_driver_ctx( + const struct ptyc_common_ctx * cctx, + struct ptyc_driver_ctx ** pctx) +{ + struct argv_meta * meta; + struct ptyc_driver_ctx_impl * ctx; + char * argv[] = {"ptycon_driver",0}; + + if (ptyc_init()) + return -1; + + if (!(meta = argv_get(argv,ptyc_default_options,0))) + return -1; + + if (!(ctx = ptyc_driver_ctx_alloc(meta,cctx,0))) + return ptyc_get_driver_ctx_fail(0); + + ctx->ctx.cctx = &ctx->cctx; + memcpy(&ctx->cctx,cctx,sizeof(*cctx)); + *pctx = &ctx->ctx; + return PTYC_OK; +} + +static void ptyc_free_driver_ctx_impl(struct ptyc_driver_ctx_alloc * ictx) +{ + argv_free(ictx->meta); + free(ictx); +} + +void ptyc_free_driver_ctx(struct ptyc_driver_ctx * ctx) +{ + struct ptyc_driver_ctx_alloc * ictx; + uintptr_t addr; + + if (ctx) { + addr = (uintptr_t)ctx - offsetof(struct ptyc_driver_ctx_alloc,ctx); + addr = addr - offsetof(struct ptyc_driver_ctx_impl,ctx); + ictx = (struct ptyc_driver_ctx_alloc *)addr; + ptyc_free_driver_ctx_impl(ictx); + } +} + +const struct ptyc_source_version * ptyc_source_version(void) +{ + return &ptyc_src_version; +} diff --git a/src/internal/argv/argv.h b/src/internal/argv/argv.h new file mode 100644 index 0000000..ece92c7 --- /dev/null +++ b/src/internal/argv/argv.h @@ -0,0 +1,907 @@ +/****************************************************************************/ +/* argv.h: a thread-safe argument vector parser and usage screen generator */ +/* Copyright (C) 2015--2016 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ +/* This file is (also) part of sofort: portable software project template. */ +/****************************************************************************/ + +#ifndef ARGV_H +#define ARGV_H + +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#define ARGV_VERBOSITY_NONE 0x00 +#define ARGV_VERBOSITY_ERRORS 0x01 +#define ARGV_VERBOSITY_STATUS 0x02 +#define ARGV_CLONE_VECTOR 0x80 + +#ifndef ARGV_TAB_WIDTH +#define ARGV_TAB_WIDTH 8 +#endif + +/*******************************************/ +/* */ +/* support of hybrid options */ +/* ------------------------- */ +/* hybrid options are very similar to */ +/* long options, yet are prefixed by */ +/* a single dash rather than two */ +/* (i.e. -std, -isystem). */ +/* hybrid options are supported by this */ +/* driver for compatibility with legacy */ +/* tools; note, however, that the use */ +/* of hybrid options should be strongly */ +/* discouraged due to the limitations */ +/* they impose on short options (for */ +/* example, a driver implementing -std */ +/* may not provide -s as a short option */ +/* that takes an arbitrary value). */ +/* */ +/* SPACE: -hybrid VALUE (i.e. -MF file) */ +/* EQUAL: -hybrid=VALUE (i.e. -std=c99) */ +/* COMMA: -hybrid,VALUE (i.e. -Wl,<arg>) */ +/* ONLY: -opt accepted, --opt rejected */ +/* JOINED: -optVALUE */ +/* */ +/*******************************************/ + + +#define ARGV_OPTION_HYBRID_NONE 0x00 +#define ARGV_OPTION_HYBRID_ONLY 0x01 +#define ARGV_OPTION_HYBRID_SPACE 0x02 +#define ARGV_OPTION_HYBRID_EQUAL 0x04 +#define ARGV_OPTION_HYBRID_COMMA 0x08 +#define ARGV_OPTION_HYBRID_JOINED 0x10 +#define ARGV_OPTION_HYBRID_CIRCUS (ARGV_OPTION_HYBRID_SPACE \ + | ARGV_OPTION_HYBRID_JOINED) +#define ARGV_OPTION_HYBRID_DUAL (ARGV_OPTION_HYBRID_SPACE \ + | ARGV_OPTION_HYBRID_EQUAL) +#define ARGV_OPTION_HYBRID_SWITCH (ARGV_OPTION_HYBRID_ONLY \ + | ARGV_OPTION_HYBRID_SPACE \ + | ARGV_OPTION_HYBRID_EQUAL \ + | ARGV_OPTION_HYBRID_COMMA \ + | ARGV_OPTION_HYBRID_JOINED) + +enum argv_optarg { + ARGV_OPTARG_NONE, + ARGV_OPTARG_REQUIRED, + ARGV_OPTARG_OPTIONAL, +}; + +enum argv_mode { + ARGV_MODE_SCAN, + ARGV_MODE_COPY, +}; + +enum argv_error { + ARGV_ERROR_OK, + ARGV_ERROR_INTERNAL, + ARGV_ERROR_SHORT_OPTION, + ARGV_ERROR_LONG_OPTION, + ARGV_ERROR_OPTARG_NONE, + ARGV_ERROR_OPTARG_REQUIRED, + ARGV_ERROR_OPTARG_PARADIGM, + ARGV_ERROR_HYBRID_NONE, + ARGV_ERROR_HYBRID_ONLY, + ARGV_ERROR_HYBRID_SPACE, + ARGV_ERROR_HYBRID_EQUAL, + ARGV_ERROR_HYBRID_COMMA, +}; + +struct argv_option { + const char * long_name; + const char short_name; + int tag; + enum argv_optarg optarg; + int flags; + const char * paradigm; + const char * argname; + const char * description; +}; + +struct argv_entry { + const char * arg; + int tag; + bool fopt; + bool fval; + bool fnoscan; + enum argv_error errcode; +}; + +struct argv_meta { + char ** argv; + struct argv_entry * entries; +}; + +struct argv_meta_impl { + char ** argv; + char * strbuf; + struct argv_meta meta; +}; + +struct argv_ctx { + int flags; + int mode; + int nentries; + int unitidx; + int erridx; + enum argv_error errcode; + const char * errch; + const struct argv_option * erropt; + const char * program; +}; + +#ifdef ARGV_DRIVER + +static const char * argv_program_name(const char *); + +static void argv_usage( + FILE *, + const char * header, + const struct argv_option[], + const char * mode); + +static struct argv_meta * argv_get( + char **, + const struct argv_option[], + int flags); + +static void argv_free(struct argv_meta *); + + + + +/*------------------------------------*/ +/* implementation of static functions */ +/*------------------------------------*/ + +static const struct argv_option * argv_short_option( + const char * ch, + const struct argv_option options[], + struct argv_entry * entry) +{ + const struct argv_option * option; + + for (option=options; option->long_name || option->short_name; option++) { + if (option->short_name == *ch) { + entry->tag = option->tag; + entry->fopt = true; + return option; + } + } + + return 0; +} + +static const struct argv_option * argv_long_option( + const char * ch, + const struct argv_option options[], + struct argv_entry * entry) +{ + const struct argv_option * option; + const char * arg; + size_t len; + + for (option=options; option->long_name || option->short_name; option++) { + len = option->long_name ? strlen(option->long_name) : 0; + + if (len && !(strncmp(option->long_name,ch,len))) { + arg = ch + len; + + if (!*arg + || (*arg == '=') + || (option->flags & ARGV_OPTION_HYBRID_JOINED) + || ((option->flags & ARGV_OPTION_HYBRID_COMMA) + && (*arg == ','))) { + entry->tag = option->tag; + entry->fopt = true; + return option; + } + } + } + + return 0; +} + +static inline bool is_short_option(const char * arg) +{ + return (arg[0]=='-') && arg[1] && (arg[1]!='-'); +} + +static inline bool is_long_option(const char * arg) +{ + return (arg[0]=='-') && (arg[1]=='-') && arg[2]; +} + +static inline bool is_last_option(const char * arg) +{ + return (arg[0]=='-') && (arg[1]=='-') && !arg[2]; +} + +static inline bool is_hybrid_option( + const char * arg, + const struct argv_option options[]) +{ + const struct argv_option * option; + struct argv_entry entry; + + if (!is_short_option(arg)) + return false; + + if (!(option = argv_long_option(++arg,options,&entry))) + return false; + + if (!(option->flags & ARGV_OPTION_HYBRID_SWITCH)) + if (argv_short_option(arg,options,&entry)) + return false; + + return true; +} + +static inline bool is_arg_in_paradigm(const char * arg, const char * paradigm) +{ + size_t len; + const char * ch; + + for (ch=paradigm,len=strlen(arg); ch; ) { + if (!strncmp(arg,ch,len)) { + if (!*(ch += len)) + return true; + else if (*ch == '|') + return true; + } + + if ((ch = strchr(ch,'|'))) + ch++; + } + + return false; +} + +static inline const struct argv_option * option_from_tag( + const struct argv_option options[], + int tag) +{ + const struct argv_option * option; + + for (option=options; option->short_name || option->long_name; option++) + if (option->tag == tag) + return option; + return 0; +} + +static void argv_scan( + char ** argv, + const struct argv_option options[], + struct argv_ctx * ctx, + struct argv_meta * meta) +{ + char ** parg; + const char * ch; + const char * val; + const struct argv_option * option; + struct argv_entry entry; + struct argv_entry * mentry; + enum argv_error ferr; + bool fval; + bool fnext; + bool fshort; + bool fhybrid; + bool fnoscan; + + parg = &argv[1]; + ch = *parg; + ferr = ARGV_ERROR_OK; + fshort = false; + fnoscan = false; + fval = false; + mentry = meta ? meta->entries : 0; + + ctx->unitidx = 0; + ctx->erridx = 0; + + while (ch && (ferr == ARGV_ERROR_OK)) { + option = 0; + fhybrid = false; + + if (fnoscan) + fval = true; + + else if (is_last_option(ch)) + fnoscan = true; + + else if (!fshort && is_hybrid_option(ch,options)) + fhybrid = true; + + if (!fnoscan && !fhybrid && (fshort || is_short_option(ch))) { + if (!fshort) + ch++; + + if ((option = argv_short_option(ch,options,&entry))) { + if (ch[1]) { + ch++; + fnext = false; + fshort = (option->optarg == ARGV_OPTARG_NONE); + } else { + parg++; + ch = *parg; + fnext = true; + fshort = false; + } + + if (option->optarg == ARGV_OPTARG_NONE) { + if (!fnext && ch && (*ch == '-')) + ferr = ARGV_ERROR_OPTARG_NONE; + else + fval = false; + } else if (!fnext) + fval = true; + else if (option->optarg == ARGV_OPTARG_REQUIRED) { + if (ch && is_short_option(ch)) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else if (ch && is_long_option(ch)) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else if (ch && is_last_option(ch)) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else if (ch) + fval = true; + else + ferr = ARGV_ERROR_OPTARG_REQUIRED; + } else { + /* ARGV_OPTARG_OPTIONAL */ + if (ch && is_short_option(ch)) + fval = false; + else if (ch && is_long_option(ch)) + fval = false; + else if (ch && is_last_option(ch)) + fval = false; + else + fval = ch; + } + } else + ferr = ARGV_ERROR_SHORT_OPTION; + + } else if (!fnoscan && (fhybrid || is_long_option(ch))) { + ch += (fhybrid ? 1 : 2); + + if ((option = argv_long_option(ch,options,&entry))) { + val = ch + strlen(option->long_name); + + /* val[0] is either '=' (or ',') or '\0' */ + if (!val[0]) { + parg++; + ch = *parg; + } + + if (fhybrid && !(option->flags & ARGV_OPTION_HYBRID_SWITCH)) + ferr = ARGV_ERROR_HYBRID_NONE; + else if (!fhybrid && (option->flags & ARGV_OPTION_HYBRID_ONLY)) + ferr = ARGV_ERROR_HYBRID_ONLY; + else if (option->optarg == ARGV_OPTARG_NONE) { + if (val[0]) { + ferr = ARGV_ERROR_OPTARG_NONE; + ctx->errch = val + 1; + } else + fval = false; + } else if (val[0] && (option->flags & ARGV_OPTION_HYBRID_JOINED)) { + fval = true; + ch = val; + } else if (fhybrid && !val[0] && !(option->flags & ARGV_OPTION_HYBRID_SPACE)) + ferr = ARGV_ERROR_HYBRID_SPACE; + else if (fhybrid && (val[0]=='=') && !(option->flags & ARGV_OPTION_HYBRID_EQUAL)) + ferr = ARGV_ERROR_HYBRID_EQUAL; + else if (fhybrid && (val[0]==',') && !(option->flags & ARGV_OPTION_HYBRID_COMMA)) + ferr = ARGV_ERROR_HYBRID_COMMA; + else if (!fhybrid && (val[0]==',')) + ferr = ARGV_ERROR_HYBRID_COMMA; + else if (val[0] && !val[1]) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else if (val[0] && val[1]) { + fval = true; + ch = ++val; + } else if (option->optarg == ARGV_OPTARG_REQUIRED) { + if (!val[0] && !*parg) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else if (*parg && is_short_option(*parg)) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else if (*parg && is_long_option(*parg)) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else if (*parg && is_last_option(*parg)) + ferr = ARGV_ERROR_OPTARG_REQUIRED; + else + fval = true; + } else { + /* ARGV_OPTARG_OPTIONAL */ + fval = val[0]; + } + } else + ferr = ARGV_ERROR_LONG_OPTION; + } + + if (ferr == ARGV_ERROR_OK) + if (option && fval && option->paradigm) + if (!is_arg_in_paradigm(ch,option->paradigm)) + ferr = ARGV_ERROR_OPTARG_PARADIGM; + + if (ferr == ARGV_ERROR_OK) + if (!option && !ctx->unitidx) + ctx->unitidx = parg - argv; + + if (ferr != ARGV_ERROR_OK) { + ctx->errcode = ferr; + ctx->errch = ctx->errch ? ctx->errch : ch; + ctx->erropt = option; + ctx->erridx = parg - argv; + return; + } else if (ctx->mode == ARGV_MODE_SCAN) { + if (!fnoscan) + ctx->nentries++; + else if (fval) + ctx->nentries++; + + if (fval || !option) { + parg++; + ch = *parg; + } + } else if (ctx->mode == ARGV_MODE_COPY) { + if (fnoscan) { + if (fval) { + mentry->arg = ch; + mentry->fnoscan = true; + mentry++; + } + + parg++; + ch = *parg; + } else if (option) { + mentry->arg = fval ? ch : 0; + mentry->tag = option->tag; + mentry->fopt = true; + mentry->fval = fval; + mentry++; + + if (fval) { + parg++; + ch = *parg; + } + } else { + mentry->arg = ch; + mentry++; + parg++; + ch = *parg; + } + } + } +} + +static const char * argv_program_name(const char * program_path) +{ + const char * ch; + + if (program_path) { + if ((ch = strrchr(program_path,'/'))) + return *(++ch) ? ch : 0; + + if ((ch = strrchr(program_path,'\\'))) + return *(++ch) ? ch : 0; + } + + return program_path; +} + +static void argv_show_error(struct argv_ctx * ctx) +{ + fprintf(stderr,"%s: error: ",ctx->program); + + switch (ctx->errcode) { + case ARGV_ERROR_SHORT_OPTION: + fprintf(stderr,"'-%c' is not a valid short option\n",*ctx->errch); + break; + + case ARGV_ERROR_LONG_OPTION: + fprintf(stderr,"'--%s' is not a valid long option\n",ctx->errch); + break; + + case ARGV_ERROR_OPTARG_NONE: + fprintf(stderr,"'%s' is not a valid option value for [%s%c%s%s%s] " + "(option values may not be specified)\n", + ctx->errch, + ctx->erropt->short_name ? "-" : "", + ctx->erropt->short_name, + ctx->erropt->short_name ? "," : "", + ctx->erropt->long_name ? "--" : "", + ctx->erropt->long_name); + break; + + case ARGV_ERROR_OPTARG_REQUIRED: + fprintf(stderr,"option [%s%c%s%s%s] requires %s %s%s%s\n", + ctx->erropt->short_name ? "-" : "", + ctx->erropt->short_name, + ctx->erropt->short_name ? "," : "", + ctx->erropt->long_name ? "--" : "", + ctx->erropt->long_name, + ctx->erropt->paradigm ? "one of the following values:" : "a value", + ctx->erropt->paradigm ? "{" : "", + ctx->erropt->paradigm ? ctx->erropt->paradigm : "", + ctx->erropt->paradigm ? "}" : ""); + break; + + case ARGV_ERROR_OPTARG_PARADIGM: + fprintf(stderr,"'%s' is not a valid option value for [%s%c%s%s%s]={%s}\n", + ctx->errch, + ctx->erropt->short_name ? "-" : "", + ctx->erropt->short_name, + ctx->erropt->short_name ? "," : "", + ctx->erropt->long_name ? "--" : "", + ctx->erropt->long_name, + ctx->erropt->paradigm); + break; + + case ARGV_ERROR_HYBRID_NONE: + fprintf(stderr,"-%s is not a synonym for --%s\n", + ctx->erropt->long_name, + ctx->erropt->long_name); + break; + + case ARGV_ERROR_HYBRID_ONLY: + fprintf(stderr,"--%s is not a synonym for -%s\n", + ctx->erropt->long_name, + ctx->erropt->long_name); + break; + + case ARGV_ERROR_HYBRID_SPACE: + case ARGV_ERROR_HYBRID_EQUAL: + case ARGV_ERROR_HYBRID_COMMA: + fprintf(stderr,"-%s: illegal value assignment; valid syntax is " + "-%s%sVAL\n", + ctx->erropt->long_name, + ctx->erropt->long_name, + (ctx->erropt->flags & ARGV_OPTION_HYBRID_SPACE) + ? " " : (ctx->erropt->flags & ARGV_OPTION_HYBRID_EQUAL) + ? "=" : (ctx->erropt->flags & ARGV_OPTION_HYBRID_COMMA) + ? "," : ""); + + break; + + case ARGV_ERROR_INTERNAL: + fputs("internal error",stderr); + break; + + default: + break; + } +} + +static void argv_show_status( + const struct argv_option options[], + struct argv_ctx * ctx, + struct argv_meta * meta) +{ + int argc; + char ** argv; + struct argv_entry * entry; + const struct argv_option * option; + char short_name[2] = {0}; + const char * space = ""; + + (void)ctx; + + fputs("\n\nconcatenated command line:\n",stderr); + for (argv=meta->argv; *argv; argv++) { + fprintf(stderr,"%s%s",space,*argv); + space = " "; + } + + fputs("\n\nargument vector:\n",stderr); + for (argc=0,argv=meta->argv; *argv; argc++,argv++) + fprintf(stderr,"argv[%d]: %s\n",argc,*argv); + + fputs("\n\nparsed entries:\n",stderr); + for (entry=meta->entries; entry->arg || entry->fopt; entry++) + if (entry->fopt) { + option = option_from_tag(options,entry->tag); + short_name[0] = option->short_name; + + if (entry->fval) + fprintf(stderr,"[-%s,--%s] := %s\n", + short_name,option->long_name,entry->arg); + else + fprintf(stderr,"[-%s,--%s]\n", + short_name,option->long_name); + } else + fprintf(stderr,"<program arg> := %s\n",entry->arg); + + fputs("\n\n",stderr); +} + +static struct argv_meta * argv_free_impl(struct argv_meta_impl * imeta) +{ + if (imeta->argv) + free(imeta->argv); + + if (imeta->strbuf) + free(imeta->strbuf); + + if (imeta->meta.entries) + free(imeta->meta.entries); + + free(imeta); + return 0; +} + +static struct argv_meta * argv_alloc(char ** argv, struct argv_ctx * ctx) +{ + struct argv_meta_impl * imeta; + char ** vector; + char * dst; + size_t size; + int argc; + int i; + + if (!(imeta = calloc(1,sizeof(*imeta)))) + return 0; + + if (ctx->flags & ARGV_CLONE_VECTOR) { + for (vector=argv,argc=0,size=0; *vector; vector++) { + size += strlen(*vector) + 1; + argc++; + } + + if (!(imeta->argv = calloc(argc+1,sizeof(char *)))) + return argv_free_impl(imeta); + else if (!(imeta->strbuf = calloc(1,size+1))) + return argv_free_impl(imeta); + + for (i=0,dst=imeta->strbuf; i<argc; i++) { + strcpy(dst,argv[i]); + imeta->argv[i] = dst; + dst += strlen(dst)+1; + } + + imeta->meta.argv = imeta->argv; + } else + imeta->meta.argv = argv; + + if (!(imeta->meta.entries = calloc( + ctx->nentries+1, + sizeof(struct argv_entry)))) + return argv_free_impl(imeta); + else + return &imeta->meta; +} + +static struct argv_meta * argv_get( + char * argv[], + const struct argv_option options[], + int flags) +{ + struct argv_meta * meta; + struct argv_ctx ctx = {flags,ARGV_MODE_SCAN,0,0,0,0,0,0,0}; + + argv_scan(argv,options,&ctx,0); + + if (ctx.errcode != ARGV_ERROR_OK) { + if (!ctx.program) + ctx.program = argv_program_name(argv[0]); + + if (ctx.flags & ARGV_VERBOSITY_ERRORS) + argv_show_error(&ctx); + + return 0; + } + + if (!(meta = argv_alloc(argv,&ctx))) + return 0; + + ctx.mode = ARGV_MODE_COPY; + argv_scan(meta->argv,options,&ctx,meta); + + if (ctx.errcode != ARGV_ERROR_OK) { + if (!ctx.program) + ctx.program = argv[0]; + + ctx.errcode = ARGV_ERROR_INTERNAL; + argv_show_error(&ctx); + argv_free(meta); + + return 0; + } + + if (ctx.flags & ARGV_VERBOSITY_STATUS) + argv_show_status(options,&ctx,meta); + + return meta; +} + +static void argv_free(struct argv_meta * xmeta) +{ + struct argv_meta_impl * imeta; + uintptr_t addr; + + if (xmeta) { + addr = (uintptr_t)xmeta - offsetof(struct argv_meta_impl,meta); + imeta = (struct argv_meta_impl *)addr; + argv_free_impl(imeta); + } +} + +static void argv_usage( + FILE * file, + const char * header, + const struct argv_option options[], + const char * mode) +{ + const struct argv_option * option; + bool fshort,flong,fboth; + size_t len,optlen,desclen; + char cache; + char * prefix; + char * desc; + char * mark; + char * cap; + char description[4096]; + char optstr [72]; + const size_t optcap = 64; + const size_t width = 80; + const char indent[] = " "; + const char creset[] = "\x1b[0m"; + const char cbold [] = "\x1b[1m"; + const char cgreen[] = "\x1b[32m"; + const char cblue [] = "\x1b[34m"; + const char ccyan [] = "\x1b[36m"; + const char * color = ccyan; + bool fcolor; + + fshort = mode ? !strcmp(mode,"short") : 0; + flong = fshort ? 0 : mode && !strcmp(mode,"long"); + fboth = !fshort && !flong; + fcolor = isatty(fileno(file)); + + if (fcolor) + fprintf(file,"%s%s",cbold,cgreen); + + if (header) + fprintf(file,"%s",header); + + option = options; + optlen = 0; + + for (; option->short_name || option->long_name; option++) { + /* indent + comma */ + len = fboth ? sizeof(indent) + 1 : sizeof(indent); + + /* -o */ + if (fshort || fboth) + len += option->short_name + ? 2 : 0; + + /* --option */ + if (flong || fboth) + len += option->long_name + ? 2 + strlen(option->long_name) : 0; + + /* optlen */ + if (len > optlen) + optlen = len; + } + + if (optlen >= optcap) { + fprintf(stderr, + "Option strings exceed %zu characters, " + "please generate the usage screen manually.\n", + optcap); + return; + } + + optlen += ARGV_TAB_WIDTH; + optlen &= (~(ARGV_TAB_WIDTH-1)); + desclen = (optlen < width / 2) ? width - optlen : optlen; + + for (option=options; option->short_name || option->long_name; option++) { + /* color */ + if (fcolor) { + color = (color == ccyan) ? cblue : ccyan; + fputs(color,file); + } + + /* description, using either paradigm or argname if applicable */ + snprintf(description,sizeof(description),option->description, + option->paradigm + ? option->paradigm + : option->argname ? option->argname : ""); + description[sizeof(description)-1] = 0; + + /* long/hybrid option prefix (-/--) */ + prefix = option->flags & ARGV_OPTION_HYBRID_ONLY + ? " -" : "--"; + + /* option string */ + if (fboth && option->short_name && option->long_name) + sprintf(optstr,"%s-%c,%s%s", + indent,option->short_name,prefix,option->long_name); + + else if ((fshort || fboth) && option->short_name) + sprintf(optstr,"%s-%d",indent,option->short_name); + + else if (flong && option->long_name) + sprintf(optstr,"%s%s%s", + indent,prefix,option->long_name); + + else if (fboth && option->long_name) + sprintf(optstr,"%s %s%s", + indent,prefix,option->long_name); + + else + optstr[0] = 0; + + /* right-indented option buffer */ + if (description[0]) { + len = strlen(optstr); + sprintf(&optstr[len],"%-*c",(int)(optlen-len),' '); + } + + /* single line? */ + if (optlen + strlen(description) < width) { + fprintf(file,"%s%s\n",optstr,description); + + } else { + desc = description; + cap = desc + strlen(description); + + while (desc < cap) { + mark = (desc + desclen >= cap) + ? cap : desc + desclen; + + while (*mark && (mark > desc) + && (*mark != ' ') + && (*mark != '|') + && (*mark != '\t') + && (*mark != '\n')) + mark--; + + if (mark == desc) { + mark = (desc + desclen >= cap) + ? cap : desc + desclen; + cache = *mark; + *mark = 0; + } else if (*mark == '|') { + cache = *mark; + *mark = 0; + } else { + cache = 0; + *mark = 0; + } + + /* first line? */ + if (desc == description) + fprintf(file,"%s%s\n",optstr,desc); + else + fprintf(file,"%-*c %s\n", + (*desc == '|') + ? (int)(optlen+1) + : (int)optlen, + ' ',desc); + + if (cache) + *mark = cache; + else + mark++; + + desc = mark; + } + } + } + + if (fcolor) + fputs(creset,file); +} + +#endif + +#endif diff --git a/src/internal/nolibc/nt32/ptyc_compiler.s b/src/internal/nolibc/nt32/ptyc_compiler.s new file mode 100644 index 0000000..6144edf --- /dev/null +++ b/src/internal/nolibc/nt32/ptyc_compiler.s @@ -0,0 +1,20 @@ +########################################################### +## ptycon: a pty-console bridge ## +## Copyright (C) 2016 Z. Gilboa ## +## Released under GPLv2 and GPLv3; see COPYING.PTYCON. ## +########################################################### + +# ___chkstk_ms and _pei386_runtime_relocator are not needed by the framework +# and are provided here in the form of no-op functions in order to satisfy +# compiler-generated dependencies. + +.section .text + +.global _pei386_runtime_relocator +.global ___chkstk_ms + +_pei386_runtime_relocatorr: + ret + +___chkstk_ms: + ret diff --git a/src/internal/nolibc/nt64/ptyc_compiler.s b/src/internal/nolibc/nt64/ptyc_compiler.s new file mode 100644 index 0000000..6144edf --- /dev/null +++ b/src/internal/nolibc/nt64/ptyc_compiler.s @@ -0,0 +1,20 @@ +########################################################### +## ptycon: a pty-console bridge ## +## Copyright (C) 2016 Z. Gilboa ## +## Released under GPLv2 and GPLv3; see COPYING.PTYCON. ## +########################################################### + +# ___chkstk_ms and _pei386_runtime_relocator are not needed by the framework +# and are provided here in the form of no-op functions in order to satisfy +# compiler-generated dependencies. + +.section .text + +.global _pei386_runtime_relocator +.global ___chkstk_ms + +_pei386_runtime_relocatorr: + ret + +___chkstk_ms: + ret diff --git a/src/internal/nolibc/stdbool.h b/src/internal/nolibc/stdbool.h new file mode 100644 index 0000000..45fc6f9 --- /dev/null +++ b/src/internal/nolibc/stdbool.h @@ -0,0 +1,11 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#ifndef __cplusplus + +#define true 1 +#define false 0 +#define bool _Bool + +#endif +#endif diff --git a/src/internal/nolibc/stddef.h b/src/internal/nolibc/stddef.h new file mode 100644 index 0000000..8288bae --- /dev/null +++ b/src/internal/nolibc/stddef.h @@ -0,0 +1,6 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +#define offsetof(type, member) ((size_t)( (char *)&(((type *)0)->member) - (char *)0 )) + +#endif diff --git a/src/internal/nolibc/stdint.h b/src/internal/nolibc/stdint.h new file mode 100644 index 0000000..831d865 --- /dev/null +++ b/src/internal/nolibc/stdint.h @@ -0,0 +1,6 @@ +#ifndef _STDINT_H +#define _STDINT_H + +#include <psxtypes/psxtypes.h> + +#endif diff --git a/src/internal/nolibc/stdio.h b/src/internal/nolibc/stdio.h new file mode 100644 index 0000000..610533e --- /dev/null +++ b/src/internal/nolibc/stdio.h @@ -0,0 +1,8 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#endif diff --git a/src/internal/nolibc/stdlib.h b/src/internal/nolibc/stdlib.h new file mode 100644 index 0000000..8a24bec --- /dev/null +++ b/src/internal/nolibc/stdlib.h @@ -0,0 +1,4 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#endif diff --git a/src/internal/nolibc/string.h b/src/internal/nolibc/string.h new file mode 100644 index 0000000..f8e3b8e --- /dev/null +++ b/src/internal/nolibc/string.h @@ -0,0 +1,4 @@ +#ifndef _STRING_H +#define _STRING_H + +#endif diff --git a/src/internal/nolibc/unistd.h b/src/internal/nolibc/unistd.h new file mode 100644 index 0000000..2e39dc8 --- /dev/null +++ b/src/internal/nolibc/unistd.h @@ -0,0 +1,4 @@ +#ifndef _UNISTD_H +#define _UNISTD_H + +#endif diff --git a/src/internal/ptycon_driver_impl.h b/src/internal/ptycon_driver_impl.h new file mode 100644 index 0000000..dbc3278 --- /dev/null +++ b/src/internal/ptycon_driver_impl.h @@ -0,0 +1,26 @@ +#ifndef PTYCON_DRIVER_IMPL_H +#define PTYCON_DRIVER_IMPL_H + +#include <ntapi/ntapi.h> +#include <stdint.h> +#include <stdio.h> + +#include <ptycon/ptycon.h> +#include "argv/argv.h" + +extern const struct argv_option ptyc_default_options[]; +extern const ntapi_vtbl * ptyc_ntapi; + +#define ntapi ptyc_ntapi + +enum app_tags { + TAG_HELP, + TAG_VERSION, +}; + +struct ptyc_driver_ctx_impl { + struct ptyc_common_ctx cctx; + struct ptyc_driver_ctx ctx; +}; + +#endif diff --git a/src/internal/ptycon_init_impl.h b/src/internal/ptycon_init_impl.h new file mode 100644 index 0000000..c136a4d --- /dev/null +++ b/src/internal/ptycon_init_impl.h @@ -0,0 +1,24 @@ +#ifndef PTYCON_INIT_IMPL_H +#define PTYCON_INIT_IMPL_H + +#include <ntapi/ntapi.h> +#include <ntapi/nt_atomic.h> + +extern const ntapi_vtbl * ptyc_ntapi; + +static int ptyc_init(void) +{ + int32_t status; + ntapi_vtbl * pvtbl; + + if ((status = ntapi_init(&pvtbl))) + return status; + + at_locked_cas( + (intptr_t *)&ptyc_ntapi, + 0,(intptr_t)pvtbl); + + return 0; +} + +#endif diff --git a/src/internal/ptycon_memfn_impl.c b/src/internal/ptycon_memfn_impl.c new file mode 100644 index 0000000..1aa625a --- /dev/null +++ b/src/internal/ptycon_memfn_impl.c @@ -0,0 +1,94 @@ +/*********************************************************/ +/* ptycon: a pty-console bridge */ +/* Copyright (C) 2016 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ +/*********************************************************/ + +#include <stddef.h> +#include <ntapi/ntapi.h> +#include "ptycon_memfn_impl.h" +#include "ptycon_driver_impl.h" + +void * ptyc_calloc(size_t n, size_t size) +{ + struct ptyc_memory_block block; + + if (!n || (size > (size_t)(-1) / n)) + return 0; + + size *= n; + size += 0xf; + size &= ~(size_t)0xf; + + block.addr = 0; + block.size = size + sizeof(block); + + if (ntapi->zw_allocate_virtual_memory( + NT_CURRENT_PROCESS_HANDLE, + &block.addr, + 0, + &block.size, + NT_MEM_COMMIT, + NT_PAGE_READWRITE)) + return 0; + + block.used = size; + block.avail = block.size - block.used; + + return (char *)block.addr + offsetof(struct ptyc_memory_block,buffer); +} + +void * ptyc_balloc( + struct ptyc_memory_block * cache, + struct ptyc_memory_block * block, + size_t n, size_t size) +{ + char * baddr; + void * addr = 0; + + if (!n || (size > (size_t)(-1) / n)) + return 0; + + if (!cache || !cache->addr) + addr = ptyc_calloc(n,size); + + size *= n; + size += 0xf; + size &= ~(size_t)0xf; + + if (size < block->avail) + addr = ptyc_calloc(n,size); + + /* newly allocated block? */ + if (addr) { + baddr = addr; + baddr -= offsetof(struct ptyc_memory_block,buffer); + + ntapi->tt_aligned_block_memcpy( + (uintptr_t *)block, + (uintptr_t *)baddr, + sizeof(*block)); + + return addr; + } + + /* cache */ + addr = &block->buffer[block->used / sizeof(size_t)]; + block->used += size; + block->avail -= size; + return addr; +} + +void ptyc_free(void * addr) +{ + struct ptyc_memory_block block; + + block.addr = addr; + block.size = 0; + + ntapi->zw_free_virtual_memory( + NT_CURRENT_PROCESS_HANDLE, + &block.addr, + &block.size, + NT_MEM_RELEASE); +} diff --git a/src/internal/ptycon_memfn_impl.h b/src/internal/ptycon_memfn_impl.h new file mode 100644 index 0000000..9a58752 --- /dev/null +++ b/src/internal/ptycon_memfn_impl.h @@ -0,0 +1,22 @@ +#ifndef PTYCON_MEMFN_IMPL_H +#define PTYCON_MEMFN_IMPL_H + +#include <stdint.h> + +struct ptyc_memory_block { + void * addr; + size_t size; + size_t used; + size_t avail; + size_t buffer[]; +}; + +void * ptyc_calloc(size_t n, size_t size); +void ptyc_free(void * addr); + +void * ptyc_balloc( + struct ptyc_memory_block * cache, + struct ptyc_memory_block * block, + size_t n, size_t size); + +#endif diff --git a/src/internal/ptycon_nolibc_impl.c b/src/internal/ptycon_nolibc_impl.c new file mode 100644 index 0000000..8a32fd7 --- /dev/null +++ b/src/internal/ptycon_nolibc_impl.c @@ -0,0 +1,73 @@ +/*********************************************************/ +/* ptycon: a pty-console bridge */ +/* Copyright (C) 2016 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ +/*********************************************************/ + +#include <ntapi/ntapi.h> + +extern const ntapi_vtbl * ptyc_ntapi; + +void * ptyc_memcpy(void * dst, const void * src, size_t n) +{ + return ptyc_ntapi->tt_generic_memcpy(dst,src,n); +} + +void * ptyc_memset(void * ch, int c, size_t n) +{ + return ptyc_ntapi->tt_generic_memset(ch,c,n); +} + +char * ptyc_strcpy(char * dst, const char * src) +{ + return ptyc_ntapi->tt_generic_memcpy( + dst,src, + ptyc_ntapi->tt_string_null_offset_multibyte(src)); +} + +size_t ptyc_strlen(const char * ch) +{ + return ptyc_ntapi->tt_string_null_offset_multibyte(ch); +} + +int ptyc_strcmp(const char * a, const char * b) +{ + return ptyc_ntapi->tt_strcmp_multibyte(a,b); +} + +int ptyc_strncmp(const char * a, const char * b, size_t n) +{ + return ptyc_ntapi->tt_strncmp_multibyte(a,b,n); +} + +char * ptyc_strchr(const char * ch, int c) +{ + for (; *ch; ch++) + if (*ch == c) + return (char *)ch; + return 0; +} + +char * ptyc_strrchr(const char * ch, int c) +{ + const char * base; + + base = ch; + ch += ptyc_ntapi->tt_string_null_offset_multibyte(ch); + + for (; ch >= base; ch--) + if (*ch == c) + return (char *)ch; + return 0; +} + +#ifdef PTYC_BUILD +int __stdcall ptycon_entry_point(void * hinstance, uint32_t reason, void * reserved) +{ + (void)hinstance; + (void)reason; + (void)reserved; + + return 1; +} +#endif diff --git a/src/internal/ptycon_nolibc_impl.h b/src/internal/ptycon_nolibc_impl.h new file mode 100644 index 0000000..7050c03 --- /dev/null +++ b/src/internal/ptycon_nolibc_impl.h @@ -0,0 +1,52 @@ +#ifndef PTYCON_NOLIBC_IMPL_H +#define PTYCON_NOLIBC_IMPL_H + +#define isatty ptyc_isatty +#define fileno ptyc_fileno + +#define fputs ptyc_fputs +#define fprintf ptyc_fprintf +#define sprintf ptyc_sprintf +#define snprintf ptyc_snprintf + +#define memcpy ptyc_memcpy +#define memset ptyc_memset + +#define strcpy ptyc_strcpy +#define strlen ptyc_strlen +#define strcmp ptyc_strcmp +#define strncmp ptyc_strncmp +#define strchr ptyc_strchr +#define strrchr ptyc_strrchr + +#define calloc ptyc_calloc +#define free ptyc_free + +#define stdin (void *)0 +#define stdout (void *)1 +#define stderr (void *)2 + +typedef struct ptyc_file FILE; + +int ptyc_isatty(int fildes); +int ptyc_fileno(void * any); + +int ptyc_sprintf(char * str, const char * fmt, ...); +int ptyc_snprintf(char * str, size_t n, const char * fmt, ...); +int ptyc_fprintf(FILE *__restrict, const char *__restrict, ...); +int ptyc_fputs(const char * str, FILE * file); + +void * ptyc_memcpy(void * dst, const void * src, size_t n); +void * memset(void * ch, int c, size_t n); + +char * ptyc_strcpy(char * dst, const char * src); +size_t ptyc_strlen(const char * ch); +int ptyc_strcmp(const char * a, const char * b); +int ptyc_strncmp(const char * a, const char * b, size_t n); +char * ptyc_strchr(const char * ch, int c); +char * ptyc_strrchr(const char * ch, int c); + +void * ptyc_calloc(size_t n, size_t size); +void ptyc_free(void *); + +#endif diff --git a/src/internal/ptycon_ntaio_impl.c b/src/internal/ptycon_ntaio_impl.c new file mode 100644 index 0000000..c990ff7 --- /dev/null +++ b/src/internal/ptycon_ntaio_impl.c @@ -0,0 +1,163 @@ +/*********************************************************/ +/* ptycon: a pty-console bridge */ +/* Copyright (C) 2016 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ +/*********************************************************/ + +#include <ntapi/ntapi.h> + +extern const ntapi_vtbl * ptyc_ntapi; + +typedef struct ptyc_file { + void * hany; +} FILE; + +int ptyc_fputs(const char * str, FILE * file) +{ + int32_t status; + nt_runtime_data * rtdata; + ntapi_zw_write_file * iofn; + void * hio; + void * hevent; + int fdtype; + size_t size; + size_t nbytes; + nt_iosb iosb; + + /* rtdata */ + if (ptyc_ntapi->tt_get_runtime_data(&rtdata,0)) + return -1; + + /* io type */ + if (file == (void *)0) { + hio = rtdata->hstdin; + fdtype = rtdata->stdin_type; + + } else if (file == (void *)1) { + hio = rtdata->hstdout; + fdtype = rtdata->stdout_type; + + } else if (file == (void *)2) { + hio = rtdata->hstderr; + fdtype = rtdata->stderr_type; + } else { + return -1; + } + + if (!hio) + return -1; + + /* buffer */ + size = ptyc_ntapi->tt_string_null_offset_multibyte(str); + nbytes = size; + + /* iofn */ + iofn = (fdtype == NT_FILE_TYPE_PTY) + ? (ntapi_zw_write_file *)ptyc_ntapi->pty_write + : ptyc_ntapi->zw_write_file; + + /* hevent */ + if ((status = ptyc_ntapi->tt_create_private_event( + &hevent, + NT_NOTIFICATION_EVENT, + NT_EVENT_NOT_SIGNALED))) + return -1; + + while (nbytes) { + /* iowrite */ + status = iofn( + hio,hevent, + 0,0,&iosb, + (void *)str, + nbytes,0,0); + + /* wait */ + if (status == NT_STATUS_PENDING) + status = ptyc_ntapi->zw_wait_for_single_object( + hevent,0,0); + + /* check */ + if (status || iosb.status) { + ptyc_ntapi->zw_close(hevent); + return -1; + } + + str += iosb.info; + nbytes -= iosb.info; + } + + /* all done */ + ptyc_ntapi->zw_close( + hevent); + + return (int)size; +} + +int ptyc_fprintf(FILE * file, const char * fmt, ...) +{ + va_list ap; + char * str; + size_t buffer[32768/sizeof(size_t)]; + + ptyc_ntapi->tt_aligned_block_memset( + buffer,0,sizeof(buffer)); + + str = (char *)buffer; + + va_start(ap, fmt); + ptyc_ntapi->vsnprintf(str, sizeof(buffer), fmt, ap); + va_end(ap); + + str[sizeof(buffer)-1] = 0; + + return ptyc_fputs(str,file); +} + +int ptyc_sprintf(char * str, const char * fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = ptyc_ntapi->vsprintf(str, fmt, ap); + va_end(ap); + + return ret; +} + +int ptyc_snprintf(char * str, size_t n, const char * fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = ptyc_ntapi->vsnprintf(str, n, fmt, ap); + va_end(ap); + + return ret; +} + +int ptyc_isatty(int fildes) +{ + nt_runtime_data * rtdata; + + if ((ptyc_ntapi->tt_get_runtime_data(&rtdata,0))) + return 0; + + if (fildes == 0) + return (rtdata->stdin_type == NT_FILE_TYPE_PTY); + + else if (fildes == 1) + return (rtdata->stdout_type == NT_FILE_TYPE_PTY); + + else if (fildes == 2) + return (rtdata->stderr_type == NT_FILE_TYPE_PTY); + + else + return 0; +} + +int ptyc_fileno(void * any) +{ + return (int)(intptr_t)any; +} diff --git a/src/ptycon.c b/src/ptycon.c new file mode 100644 index 0000000..dd483b8 --- /dev/null +++ b/src/ptycon.c @@ -0,0 +1,169 @@ +/*********************************************************/ +/* ptycon: a pty-console bridge */ +/* Copyright (C) 2016 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */ +/*********************************************************/ + +#include <pemagine/pemagine.h> +#include <ntapi/ntapi.h> +#include <ptycon/ptycon.h> +#include "ptycon_init_impl.h" +#include "ptycon_driver_impl.h" + +static const nt_tty_affiliation tty_affiliation + __attr_section__(".midipix") + = NT_TTY_AFFILIATION_DEFAULT; + +static void ptycon_exit(int code) +{ + /* posix exit code? */ + if (code <= 0xff) + code <<= 8; + + ntapi->zw_terminate_process( + NT_CURRENT_PROCESS_HANDLE, + code); +} + +static int32_t ptycon_start(int argc, char ** argv, char ** envp) +{ + int32_t status; + nt_runtime_data * rtdata; + nt_port_attr port_attr; + nt_pty_client_info client_info; + nt_pty * hpty; + nt_iosb iosb; + + /* rtdata */ + if ((status = ntapi->tt_get_runtime_data(&rtdata,0))) + return status; + + if (rtdata->argv) { + argc = rtdata->argc; + argv = rtdata->argv; + envp = rtdata->envp; + } + + /* no tty session? */ + if (!rtdata->srv_keys[0]) + return ptyc_main(argc,argv,envp); + + /* tty */ + ntapi->tt_aligned_block_memset( + &port_attr,0,sizeof(port_attr)); + + port_attr.type = NT_PORT_TYPE_SUBSYSTEM; + port_attr.subtype = NT_PORT_SUBTYPE_DEFAULT; + + port_attr.keys.key[0] = rtdata->srv_keys[0]; + port_attr.keys.key[1] = rtdata->srv_keys[1]; + port_attr.keys.key[2] = rtdata->srv_keys[2]; + port_attr.keys.key[3] = rtdata->srv_keys[3]; + port_attr.keys.key[4] = rtdata->srv_keys[4]; + port_attr.keys.key[5] = rtdata->srv_keys[5]; + + ntapi->tt_port_guid_from_type( + &port_attr.guid, + port_attr.type, + port_attr.subtype); + + if ((status = ntapi->tty_join_session( + &rtdata->hsession,0, + &port_attr, + NT_TTY_SESSION_PRIMARY))) + return status; + + /* pty */ + client_info.any[0] = rtdata->ptyany[0]; + client_info.any[1] = rtdata->ptyany[1]; + client_info.any[2] = rtdata->ptyany[2]; + client_info.any[3] = rtdata->ptyany[3]; + + status = ntapi->pty_inherit( + rtdata->hsession, + &hpty, + &client_info); + + /* ntaio */ + if (status == NT_STATUS_SUCCESS) { + if (rtdata->stdin_type == NT_FILE_TYPE_PTY) + rtdata->hstdin = hpty; + + if (rtdata->stdout_type == NT_FILE_TYPE_PTY) + rtdata->hstdout = hpty; + + if (rtdata->stderr_type == NT_FILE_TYPE_PTY) + rtdata->hstderr = hpty; + + /* ctty identification */ + client_info.any[0] = 0; + client_info.any[1] = 0; + client_info.any[2] = 0; + client_info.any[3] = 0; + + if ((status = ntapi->pty_set( + hpty,&iosb, + &client_info,sizeof(client_info), + NT_PTY_CLIENT_INFORMATION))) + return status; + } + + /* ready */ + if (rtdata->hready) + if ((status = ntapi->zw_set_event(rtdata->hready,0))) + return status; + + /* main */ + return ptyc_main(argc,argv,envp); +} + +static int ptycon_daemon_entry_point(void * arg) +{ + int32_t status; + int argc; + char ** argv; + char ** envp; + + (void)arg; + (void)tty_affiliation; + + if ((status = ntapi->tt_get_argv_envp_utf8( + &argc,&argv,&envp, + 0,0,0))) + ptycon_exit(status); + + ptycon_exit(ptycon_start( + argc,argv,envp)); + + return NT_STATUS_INTERNAL_ERROR; +} + +int ptycon_entry_point(void) +{ + int32_t status; + nt_thread_params params; + + if ((status = ptyc_init())) + return status; + + ntapi->tt_aligned_block_memset( + ¶ms,0,sizeof(params)); + + params.hprocess = NT_CURRENT_PROCESS_HANDLE; + params.start = ptycon_daemon_entry_point; + params.stack_size_commit = 128 * 1024; + params.stack_size_reserve = 128 * 1024; + params.creation_flags = NT_CREATE_LOCAL_THREAD; + + if ((status = ntapi->tt_create_thread(¶ms))) + ptycon_exit(status); + + ntapi->zw_close( + params.hthread); + + ntapi->zw_terminate_thread( + NT_CURRENT_THREAD_HANDLE, + NT_STATUS_SUCCESS); + + return NT_STATUS_INTERNAL_ERROR; +} diff --git a/src/skin/ptyc_skin_default.c b/src/skin/ptyc_skin_default.c new file mode 100644 index 0000000..85ce12e --- /dev/null +++ b/src/skin/ptyc_skin_default.c @@ -0,0 +1,12 @@ +#include "ptycon_driver_impl.h" +#include "argv/argv.h" + +const struct argv_option ptyc_default_options[] = { + {"version", 'v',TAG_VERSION,ARGV_OPTARG_NONE,0,0,0, + "show version information"}, + + {"help", 'h',TAG_HELP,ARGV_OPTARG_OPTIONAL,0,"short|long",0, + "show usage information [listing %s options only]"}, + + {0,0,0,0,0,0,0,0} +}; |