From: mdw Date: Fri, 25 Jan 2002 19:34:45 +0000 (+0000) Subject: Initial revision X-Git-Url: https://git.distorted.org.uk/~mdw/jog/commitdiff_plain/2ec1e6937048636ec6761c3a76c5bf9544278601 Initial revision --- 2ec1e6937048636ec6761c3a76c5bf9544278601 diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..af3840d --- /dev/null +++ b/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +aclocal.m4 +config.h.in +configure +stamp-h.in +build diff --git a/.links b/.links new file mode 100644 index 0000000..b3f8cad --- /dev/null +++ b/.links @@ -0,0 +1,4 @@ +COPYING +install-sh +mkinstalldirs +missing diff --git a/.skelrc b/.skelrc new file mode 100644 index 0000000..d5d8340 --- /dev/null +++ b/.skelrc @@ -0,0 +1,8 @@ +;;; -*-emacs-lisp-*- + +(setq skel-alist + (append + '((author . "Mark Wooding") + (full-title . "Jog: Programming for a jogging machine") + (program . "Jog")) + skel-alist)) diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b536981 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,46 @@ +## -*-makefile-*- +## +## $Id: Makefile.am,v 1.1 2002/01/25 19:34:45 mdw Exp $ +## +## Makefile for jog +## +## (c) 2001 Mark Wooding +## + +##----- Licensing notice ---------------------------------------------------- +## +## This file is part of Jog: Programming for a jogging machine. +## +## Jog is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## Jog is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Jog; if not, write to the Free Software Foundation, +## Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +##----- Revision history ---------------------------------------------------- +## +## $Log: Makefile.am,v $ +## Revision 1.1 2002/01/25 19:34:45 mdw +## Initial revision +## + +AUTOMAKE_OPTIONS = foreign + +bin_PROGRAMS = jogrx + +jogrx_SOURCES = \ + main.c \ + err.c rxglue.c txport.c serial.c \ + err.h rxglue.h txport.h serial.h \ + tx-socket.c tx-socket.h \ + tx-serial-unix.c tx-serial-unix.h + +##----- That's all, folks --------------------------------------------------- diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..45739b8 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,65 @@ +/* -*-c-*- + * + * $Id: acconfig.h,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Configuration header + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: acconfig.h,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +#ifndef ACCONFIG_H +#define ACCONFIG_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Configuration macros ----------------------------------------------*/ +@TOP@ + +/* Package name. */ +#define PACKAGE "jog" + +/* Package version number. */ +#define VERSION "1.0.0" + +/* If it's not provided already, define to be a signed integer type capable + * of representing any object size. (If in doubt, make it an `int'.) */ +#undef ssize_t + +@BOTTOM@ + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..67475a3 --- /dev/null +++ b/configure.in @@ -0,0 +1,78 @@ +dnl -*-fundamental-*- +dnl +dnl $Id: configure.in,v 1.1 2002/01/25 19:34:45 mdw Exp $ +dnl +dnl Configuration script for jog +dnl +dnl (c) 2001 Mark Wooding +dnl + +dnl ----- Licensing notice -------------------------------------------------- +dnl +dnl This file is part of Jog: Programming for a jogging machine. +dnl +dnl Jog is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl Jog is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with Jog; if not, write to the Free Software Foundation, +dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +dnl ----- Revision history -------------------------------------------------- +dnl +dnl $Log: configure.in,v $ +dnl Revision 1.1 2002/01/25 19:34:45 mdw +dnl Initial revision +dnl + +AC_INIT(rxglue.c) +AM_INIT_AUTOMAKE(jog, 1.0.0) +AM_CONFIG_HEADER(config.h) + +AC_PROG_CC +AC_PROG_CPP +mdw_GCC_FLAGS + +AC_CACHE_CHECK([where to find ], [mdw_cv_rexxsaa_path], [ + bad=true + tmp_CPPFLAGS=$CPPFLAGS + for i in present /usr/include/regina /usr/local/include/regina; do + case $i in + present) ;; + /*) CPPFLAGS="-I$i $tmp_CPPFLAGS" ;; + *) AC_MSG_ERROR([Buggered!]) ;; + esac + ac_cpp='$CPP $CPPFLAGS' + AC_TRY_CPP([#include ], [bad=false; break;]) + done + if $bad; then + AC_MSG_ERROR([header file not found]) + fi + CPPFLAGS=$tmp_CPPFLAGS + mdw_cv_rexxsaa_path=$i +]) +case $mdw_cv_rexxsaa_path in + present) ;; + /*) CPPFLAGS="-I$mdw_cv_rexxsaa_path $tmp_CPPFLAGS" ;; + *) AC_MSG_ERROR([Buggered!]) ;; +esac + +mdw_CHECK_MANYLIBS(crypt, crypt) +mdw_CHECK_MANYLIBS(dlopen, dl) +mdw_CHECK_MANYLIBS(RexxStart, regina rexx) +mdw_CHECK_MANYLIBS(pthread_create, pthread) +mdw_CHECK_MANYLIBS(socket, socket) +mdw_MLIB(2.0.0pre4) + +mdw_TYPE_SSIZE_T + +AC_OUTPUT(Makefile) + +dnl ----- That's all, folks ------------------------------------------------- diff --git a/err.c b/err.c new file mode 100644 index 0000000..27065b0 --- /dev/null +++ b/err.c @@ -0,0 +1,206 @@ +/* -*-c-*- + * + * $Id: err.c,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Error reporting + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: err.c,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* #include "au.h" */ +#include "err.h" + +/*----- Static variables --------------------------------------------------*/ + +static FILE *logfp = 0; +static unsigned flags = 0; + +#define f_thread 1u + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @err_abort@ --- * + * + * Arguments: @int reason@ = abort reason code + * @unsigned long err@ = abort error code + * @const char *msg@ = error message + * + * Returns: Doesn't. + * + * Use: Reports a fatal error. + */ + +void err_abortv(int reason, unsigned long err, const char *msg, va_list *ap) +{ + fprintf(stderr, "%s: fatal error (code %d-%lu): ", QUIS, reason, err); + vfprintf(stderr, msg, *ap); + putc('\n', stderr); + /* au_abort(reason, err); */ + abort(); +} + +void err_abort(int reason, unsigned long err, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + err_abortv(reason, err, msg, &ap); + va_end(ap); +} + +/* --- @err_init@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Attempts to initialize the logging system. It is a + * catastrophic failure if logging can't start up. + */ + +static void err_exc(exc_extype ex, exc_exval v) +{ + switch (ex) { + case EXC_NOMEM: + err_report(ERR_EXC, 0, ex, "out of memory"); + break; + default: + err_report(ERR_EXC, 0, ex, "uncaught mLib exception"); + break; + } + exit(EXIT_FAILURE); +} + +void err_init(void) +{ + const char *lf; + + lf = getenv("JOG_LOGFILE"); + if (!lf) + logfp = stderr; + else if ((logfp = fopen(lf, "w")) == 0) { + err_abort(ERRABORT_LOGOPEN, errno, + "couldn't open logfile `%s': %s", lf, strerror(errno)); + } + exc_uncaught(err_exc); +} + +/* --- @err_report@ --- * + * + * Arguments: @int ctx@ = context code + * @int reason@ = reason code + * @unsigned long err@ = system error code + * @const char *msg@ = textual message to log + * + * Returns: --- + * + * Use: Reports an error. Doesn't abort anything unless something + * really serious happens. + */ + +void err_reportv(int ctx, int reason, unsigned long err, + const char *msg, va_list *ap) +{ + char buf[256]; + time_t t; + struct tm *tm; + + if (ctx && !(flags & f_thread)) { + unsigned f = flags; + flags |= f_thread; +/* au_misc(AU_ERROR, ctx, reason, err); */ + flags = f; + } + t = time(0); + tm = localtime(&t); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm); + if (fputs(buf, logfp) == EOF || + fprintf(stderr, " %s: ", QUIS) == EOF || + vfprintf(logfp, msg, *ap) == EOF || + (ctx && (fprintf(logfp, " (error %d", ctx) == EOF || + (reason && fprintf(logfp, "-%d", reason) == EOF) || + (err && fprintf(logfp, ":%lu", err) == EOF) || + putc(')', logfp) == EOF)) || + putc('\n', logfp) == EOF || + fflush(logfp) == EOF) { + err_abort(ERRABORT_LOGWRITE, errno, + "error writing logfile: %s", strerror(errno)); + } +} + +void err_report(int ctx, int reason, unsigned long err, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + err_reportv(ctx, reason, err, msg, &ap); + va_end(ap); +} + +/* --- @err_log@ --- * + * + * Arguments: @const char *msg@ = textual message to log + * + * Returns: --- + * + * Use: Logs a message. + */ + +void err_logv(const char *msg, va_list *ap) +{ + err_reportv(0, 0, 0, msg, ap); +} + +void err_log(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + err_reportv(0, 0, 0, msg, &ap); + va_end(ap); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/err.h b/err.h new file mode 100644 index 0000000..fef75b7 --- /dev/null +++ b/err.h @@ -0,0 +1,164 @@ +/* -*-c-*- + * + * $Id: err.h,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Error reporting + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: err.h,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +#ifndef ERR_H +#define ERR_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +/*----- Error codes -------------------------------------------------------*/ + +/* --- Abort codes --- * + * + * Only for the most serious errors, pertaining to the log file. + */ + +#define ERRABORT_LOGOPEN 1u /* Log file wouldn't open */ +#define ERRABORT_LOGWRITE 2u /* Couldn't write to log file */ + +/* --- Error contexts and reason codes --- * + * + * Most errors are reported as a group of three numeric codes. These are, in + * order: + * + * * the `context' code, which describes where whatever it was went wrong + * went wrong; + * + * * the `reason' code (specific to a particular context), which describes + * what the program was trying to do at the time; and + * + * * the `error' code (again specific to a particular context, but usually + * an @errno@ error code), which explains what the problem actually was. + */ + +#define ERR_EXC 1u /* mLib exception error code */ + /* Reason code is zero; error code is exception number */ + +#define ERR_RXGLUE 2u /* REXX interface stuff */ +# define ERRRX_SCRIPTREAD 1u /* Problem reading script file */ +# define ERRRX_INTERP 2u /* Error code from interpreter */ +# define ERRRX_INIT 3u /* Initialization error */ + +#define ERR_RXERR 3u /* REXX error */ + /* Reason code is line number; error code is REXX error number */ + +#define ERR_TXPORT 4u /* Transport switch stuff */ +# define ERRTX_BADTX 1u /* Unknown transport name */ +# define ERRTX_CREATE 2u /* Error creating transport */ +# define ERRTX_WRITE 3u /* Error writing to transport */ +# define ERRTX_READ 4u /* Read error from transport */ +# define ERRTX_CONFIG 5u /* Error in configuration */ + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @err_abort@ --- * + * + * Arguments: @int reason@ = abort reason code + * @unsigned long err@ = abort error code + * @const char *msg@ = error message + * + * Returns: Doesn't. + * + * Use: Reports a fatal error. + */ + +extern void err_abortv(int /*reason*/, unsigned long /*err*/, + const char */*msg*/, va_list */*ap*/); + +extern void err_abort(int /*reason*/, unsigned long /*err*/, + const char */*msg*/, ...); + +/* --- @err_init@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Attempts to initialize the logging system. It is a + * catastrophic failure if logging can't start up. + */ + +extern void err_init(void); + +/* --- @err_report@ --- * + * + * Arguments: @int ctx@ = context code + * @int reason@ = reason code + * @unsigned long err@ = system error code + * @const char *msg@ = textual message to log + * + * Returns: --- + * + * Use: Reports an error. Doesn't abort anything unless something + * really serious happens. + */ + +extern void err_reportv(int /*ctx*/, int /*reason*/, unsigned long /*err*/, + const char */*msg*/, va_list */*ap*/); + +extern void err_report(int /*ctx*/, int /*reason*/, unsigned long /*err*/, + const char */*msg*/, ...); + +/* --- @err_log@ --- * + * + * Arguments: @const char *msg@ = textual message to log + * + * Returns: --- + * + * Use: Logs a message. + */ + +extern void err_logv(const char */*msg*/, va_list */*ap*/); + +extern void err_log(const char */*msg*/, ...); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..6855a86 --- /dev/null +++ b/main.c @@ -0,0 +1,205 @@ +/* -*-c-*- + * + * $Id: main.c,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Main program + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: main.c,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "err.h" +#include "rxglue.h" +#include "txport.h" +#include "tx-serial-unix.h" + +/*----- Shutdown stuff ----------------------------------------------------*/ + +static int sigtab[] = { SIGINT, SIGQUIT, SIGTERM, SIGHUP, -1 }; + +static void tidy(void) +{ + txsu_shutdown(); +} + +static void sigtidy(int sig) +{ + tidy(); + signal(sig, SIG_DFL); + raise(sig); +} + +/*----- Help functions ----------------------------------------------------*/ + +static void usage(FILE *fp) +{ + pquis(fp, "Usage: $ [-t TRANSPORT] [-f FILE] [-c CONFIG] SCRIPT [ARG]\n"); +} + +static void version(FILE *fp) +{ + pquis(fp, "$, version " VERSION "\n"); +} + +static void help(FILE *fp) +{ + version(fp); + fputc('\n', fp); + usage(fp); + fputs("\n\ +Options provided:\n\ +\n\ +-h, --help Print this help message.\n\ +-v, --version Show the version number.\n\ +-u, --usage Show terse usage summary.\n\ +\n\ +-t, --transport=NAME Use transport type NAME.\n\ +-f, --tx-file=FILE Communicate using the named FILE.\n\ +-c, --tx-config=CONFIG Use CONFIG as transport configuration.\n\ +", + fp); +} + +/*----- Main code ---------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + unsigned f = 0; + int rc = 0; + int i; + +#define f_bogus 1u + + ego(argv[0]); + atexit(tidy); + for (i = 0; sigtab[i] >= 0; i++) + signal(sigtab[i], sigtidy); + + err_init(); + rx_init(); + trace_on(stderr, 0u); + if ((txname = getenv("JOGTX")) != 0) + ; + else + txname = txlist->name; + + for (;;) { + static const struct option opt[] = { + + /* --- Standard help options --- */ + + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "usage", 0, 0, 'u' }, + + /* --- Transport configuration stuff --- */ + + { "transport", OPTF_ARGREQ, 0, 't' }, + { "transport-config", + OPTF_ARGREQ, 0, 'c' }, + { "tx-config", OPTF_ARGREQ, 0, 'c' }, + { "txconfig", OPTF_ARGREQ, 0, 'c' }, + { "config", OPTF_ARGREQ, 0, 'c' }, + { "transport-file", + OPTF_ARGREQ, 0, 'f' }, + { "tx-file", OPTF_ARGREQ, 0, 'f' }, + { "txfile", OPTF_ARGREQ, 0, 'f' }, + { "file", OPTF_ARGREQ, 0, 'f' }, + + /* --- End marker --- */ + + { 0, 0, 0, 0 } + }; + + i = mdwopt(argc, argv, "hvut:c:f:", opt, 0, 0, 0); + if (i < 0) + break; + + switch (i) { + + /* --- Standard help options --- */ + + case 'h': + help(stdout); + exit(0); + case 'v': + version(stdout); + exit(0); + case 'u': + usage(stdout); + exit(0); + + /* --- Transport configuration stuff --- */ + + case 't': + txname = optarg; + break; + case 'c': + txconf = optarg; + break; + case 'f': + txfile = optarg; + break; + + /* --- Errors --- */ + + default: + f |= f_bogus; + break; + } + } + + if ((f & f_bogus) || (optind != argc - 1 && optind != argc - 2)) { + usage(stderr); + exit(EXIT_FAILURE); + } + + rc = rx_runfile(argv[optind], + argc - optind - 1, (const char *const *)argv + optind + 1); + return (rc ? EXIT_FAILURE : 0); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/rxglue.c b/rxglue.c new file mode 100644 index 0000000..75cd5c8 --- /dev/null +++ b/rxglue.c @@ -0,0 +1,632 @@ +/* -*-c-*- + * + * $Id: rxglue.c,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * REXX glue for C core functionality + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rxglue.c,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define INCL_RXFUNC +#define RX_STRONGTYPING +#include + +#include +#include + +#include "err.h" +#include "rxglue.h" +#include "txport.h" + +/*----- Static variables --------------------------------------------------*/ + +static txport *tx = 0; + +/*----- Conversion functions ----------------------------------------------*/ + +/* --- @rxs_putm@ --- * + * + * Arguments: @RXSTRING *x@ = pointer to REXX string structure + * For @rxs_putm@: + * @const void *p@ = pointer to data block + * @size_t sz@ = size of data + * For @rxs_putd@: + * @const dstr *d@ = pointer to source string + * For @rxs_putf@ and @rxs_vputf@: + * @const char *m@ = message format string + * + * Returns: --- + * + * Use: Stashes some text in an @RXSTRING@, overwriting whatever was + * there before. We assume that the previous contents don't + * require freeing. + */ + +#define RXS_PUTM(x, p, sz) do { \ + RXSTRING *_x = (x); \ + const void *_p = (p); \ + size_t _sz = (sz); \ + if (!_x->strptr || _x->strlength < _sz) \ + _x->strptr = xmalloc(_sz); \ + memcpy(_x->strptr, _p, _sz); \ + _x->strlength = _sz; \ +} while (0) + +static void rxs_putm(RXSTRING *x, const void *p, size_t sz) +{ + RXS_PUTM(x, p, sz); +} + +#define RXS_PUTD(x, d) do { \ + dstr *_d = (d); \ + RXS_PUTM((x), _d->buf, _d->len); \ +} while (0) + +static void rxs_putd(RXSTRING *x, dstr *d) { RXS_PUTD(x, d); } + +static void rxs_vputf(RXSTRING *x, const char *m, va_list *ap) +{ + dstr d = DSTR_INIT; + dstr_vputf(&d, m, ap); + RXS_PUTD(x, &d); + DDESTROY(&d); +} + +static void rxs_putf(RXSTRING *x, const char *m, ...) +{ + va_list ap; + dstr d = DSTR_INIT; + va_start(ap, m); + dstr_vputf(&d, m, &ap); + RXS_PUTD(x, &d); + va_end(ap); + DDESTROY(&d); +} + +/* --- @rxs_get@ --- * + * + * Arguments: @const RXSTRING *x@ = pointer to a REXX string + * @dstr *d@ = where to put it + * + * Returns: --- + * + * Use: Pulls a REXX string out and puts it in a dynamic string. + */ + +#define RXS_GET(x, d) do { \ + const RXSTRING *_x = (x); \ + dstr *_dd = (d); \ + DPUTM(_dd, _x->strptr, _x->strlength); \ + DPUTZ(_dd); \ +} while (0) + +static void rxs_get(const RXSTRING *x, dstr *d) { RXS_GET(x, d); } + +/* --- @rxs_tol@ --- * + * + * Arguments: @const RXSTRING *x@ = pointer to a REXX string + * @long *ii@ = where to put the answer + * + * Returns: Zero on success, or nonzero on error. + * + * Use: Fetches an integer from a REXX string. This doesn't cope + * with multiprecision integers or similar silliness. + */ + +static int rxs_tol(const RXSTRING *x, long *ii) +{ + long i = 0; + const char *p = x->strptr, *l = p + x->strlength; + unsigned f = 0; + +#define f_neg 1u +#define f_ok 2u + +#define MINR (LONG_MIN/10) +#define MIND (LONG_MIN%10) + + while (p < l && isspace((unsigned char)*p)) + p++; + if (p >= l) + return (-1); + if (*p == '+') + p++; + else if (*p == '-') { + f |= f_neg; + p++; + } + while (p < l && isspace((unsigned char)*p)) + p++; + while (p < l && isdigit((unsigned char)*p)) { + int j = *p++ - '0'; + if (i < MINR || (i == MINR && -j < MIND)) + return (-1); + i = (i * 10) - j; + f |= f_ok; + } + while (p < l && isspace((unsigned char)*p)) + p++; + if (p < l || !(f & f_ok)) + return (-1); + if (!(f & f_neg)) { + if (i < -LONG_MAX) + return (-1); + i = -i; + } + *ii = i; + return (0); + +#undef MINR +#undef MIND + +#undef f_neg +#undef f_ok +} + +/* --- @rxs_block@ --- * + * + * Arguments: @const RXSTRING *x@ = a REXX string + * @unsigned long *t@ = where to put the block spec + * + * Returns: Zero if OK, nonzero on error. + * + * Use: Picks out a blockingness spec. + */ + +static int rxs_block(const RXSTRING *x, unsigned long *t) +{ + long i; + + if (!x->strptr || x->strlength < 1) + return (-1); + switch (x->strptr[0]) { + case 'f': + case 'F': + *t = FOREVER; + break; + default: + if (rxs_tol(x, &i) || i < 0) + return (-1); + *t = i; + break; + } + return (0); +} + +/*----- REXX functions ----------------------------------------------------*/ + +static APIRET APIENTRY rxfn_test(unsigned char *fn, ULONG ac, RXSTRING *av, + char *sn, RXSTRING *r) +{ + ULONG i; + + printf("test entry\n" + " fn = `%s'\n", fn); + for (i = 0; i < ac; i++) { + long l; + + printf(" av[%lu] = `", i); + fwrite(av[i].strptr, 1, av[i].strlength, stdout); + if (rxs_tol(&av[i], &l)) + printf("'\n"); + else + printf("' (%ld)\n", l); + } + printf("tx = `%s'; f = `%s'; c = `%s'.", txname, txfile, txconf); + rxs_putf(r, "function `%s' completed ok", fn); + return (0); +} + +/* --- @txname()@ --- + * + * Arguments: --- + * + * Returns: The currently-selected transport name. + */ + +static APIRET APIENTRY rxfn_txname(unsigned char *fn, ULONG ac, RXSTRING *av, + char *sn, RXSTRING *r) +{ + if (ac) + return (-1); + rxs_putf(r, "%s", txname); + return (0); +} + +/* --- @txfile()@ --- + * + * Arguments: --- + * + * Returns: The currently-selected transport filename. + */ + +static APIRET APIENTRY rxfn_txfile(unsigned char *fn, ULONG ac, RXSTRING *av, + char *sn, RXSTRING *r) +{ + if (ac) + return (-1); + rxs_putf(r, "%s", txfile ? txfile : ""); + return (0); +} + +/* --- @txfile()@ --- + * + * Arguments: --- + * + * Returns: The currently-selected transport configuration string. + */ + +static APIRET APIENTRY rxfn_txconf(unsigned char *fn, ULONG ac, RXSTRING *av, + char *sn, RXSTRING *r) +{ + if (ac) + return (-1); + rxs_putf(r, "%s", txconf ? txconf : ""); + return (0); +} + +/* --- @txinit([NAME], [FILE], [CONFIG])@ --- + * + * Arguments: @NAME@ = transport name to select + * @FILE@ = transport filename + * @CONFIG@ = transport configuration string + * + * Returns: --- + * + * Use: Initializes a transport using the given settings. Omitted + * arguments are filled in from the command line, or internal + * defaults. + */ + +static APIRET APIENTRY rxfn_txinit(unsigned char *fn, ULONG ac, RXSTRING *av, + char *sn, RXSTRING *r) +{ + const char *n = txname, *f = txfile, *c = txconf; + dstr dn = DSTR_INIT, df = DSTR_INIT, dc = DSTR_INIT; + + if (tx) + return (-1); + if (ac > 3) + return (-1); + if (ac >= 1 && av[0].strptr) { + rxs_get(&av[0], &dn); + n = dn.buf; + } + if (ac >= 2 && av[1].strptr) { + rxs_get(&av[1], &df); + f = df.buf; + } + if (ac >= 3 && av[2].strptr) { + rxs_get(&av[2], &dn); + c = dc.buf; + } + tx = tx_create(n, f, c); + dstr_destroy(&dn); + dstr_destroy(&df); + dstr_destroy(&dc); + if (!tx) + return (-1); + return (0); +} + +/* --- @txsend(STRING)@ --- * + * + * Arguments: @STRING@ = string to send + * + * Returns: --- + * + * Use: Sends a string (exactly as written) to the transport. + */ + +static APIRET APIENTRY rxfn_txsend(unsigned char *fn, ULONG ac, RXSTRING *av, + char *sn, RXSTRING *r) +{ + if (ac != 1 || !tx || !av[0].strptr) + return (-1); + tx_write(tx, av[0].strptr, av[0].strlength); + return (0); +} + +/* --- @txrecv([MILLIS])@ --- * + * + * Arguments: @MILLIS@ = how long (in milliseconds) to wait, or `forever' + * + * Returns: The string read (may be null if nothing available -- sorry). + * + * Use: Reads the next line from the transport. If @MILLIS@ is an + * integer, then give up after that many milliseconds of + * waiting; if it is `forever' (or anything beginning with an + * `f') then don't give up. The default is to wait forever. + */ + +static APIRET APIENTRY rxfn_txrecv(unsigned char *fn, ULONG ac, RXSTRING *av, + char *sn, RXSTRING *r) +{ + txline *l; + unsigned long t = FOREVER; + + if (ac > 1 || !tx) + return (-1); + if (ac >= 1 && rxs_block(&av[0], &t)) + return (-1); + + l = tx_read(tx, t); + if (!l) + r->strlength = 0; + else { + rxs_putm(r, l->s, l->len); + tx_freeline(l); + } + return (0); +} + +/* --- @TXEOF()@ --- * + * + * Arguments: --- + * + * Returns: True if end-of-file has been seen on the transport, otherwise + * false. + */ + +static APIRET APIENTRY rxfn_txeof(unsigned char *fn, ULONG ac, + RXSTRING *av, char *sn, RXSTRING *r) +{ + if (ac || !tx) + return (-1); + rxs_putf(r, "%d", tx->s == TX_CLOSED && !tx->ll); + return (0); +} + +/* --- @txready([MILLIS])@ --- * + * + * Arguments: @MILLIS@ = how long (in milliseconds) to wait, or `forever' + * + * Returns: True if a line is ready, otherwise false. + * + * Use: Returns whether the transport is ready for reading. If + * @MILLIS@ is an integer, then wait for at most that many + * milliseconds before returning. If @MILLIS@ is `forever' (or + * anything beginning with `f') then wait forever for + * readiness. This isn't useless: it can trip the end-of-file + * detector. If @MILLIS@ is omitted, return immediately (as if + * 0 had been specified). + */ + +static APIRET APIENTRY rxfn_txready(unsigned char *fn, ULONG ac, + RXSTRING *av, char *sn, RXSTRING *r) +{ + unsigned long t = 0; + + if (ac > 1 || !tx) + return (-1); + if (ac >= 1 && rxs_block(&av[0], &t)) + return (-1); + rxs_putf(r, "%d", !!tx_read(tx, t)); + return (0); +} + +/* --- @MILLIWAIT(MILLIS)@ --- * + * + * Arguments: @MILLIS@ = how long (in milliseconds) to wait + * + * Returns: --- + * + * Use: Waits for @MILLIS@ milliseconds. Always. + */ + +static APIRET APIENTRY rxfn_milliwait(unsigned char *fn, ULONG ac, + RXSTRING *av, char *sn, RXSTRING *r) +{ + long l; + struct timeval tv; + + if (ac != 1 || !av[0].strptr) + return (-1); + if (rxs_tol(&av[0], &l) || l < 0) + return (-1); + tv.tv_sec = l / 1000; + tv.tv_usec = (l % 1000) * 1000; + select(0, 0, 0, 0, &tv); + return (0); +} + +/*----- Initialization ----------------------------------------------------*/ + +struct rxfntab { char *name; RexxFunctionHandler *fn; }; + +static const struct rxfntab rxfntab[] = { + { "test", rxfn_test }, + { "txname", rxfn_txname }, + { "txfile", rxfn_txfile }, + { "txconf", rxfn_txconf }, + { "txinit", rxfn_txinit }, + { "txsend", rxfn_txsend }, + { "txrecv", rxfn_txrecv }, + { "txeof", rxfn_txeof }, + { "txready", rxfn_txready }, + { "milliwait", rxfn_milliwait }, + { 0, 0 } +}; + +/* --- @rx_init@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Initializes the REXX external functions. + */ + +void rx_init(void) +{ + const struct rxfntab *f; + int rc; + + for (f = rxfntab; f->fn; f++) { + if ((rc = RexxRegisterFunctionExe(f->name, f->fn)) != 0) { + err_report(ERR_RXGLUE, ERRRX_INIT, rc, + "couldn't register function `%s' (code %d)", f->name, rc); + abort(); + } + } +} + +/*----- Running REXX programs ---------------------------------------------*/ + +/* --- @rx_run@ --- * + * + * Arguments: @const char *name@ = pointer to filename (or null) + * @const void *p@ = pointer to program text + * @size_t sz@ = size of program text + * @int ac@ = number of arguments + * @const char *const *av@ = vector of command-line arguments + * + * Returns: Exit code from program. + * + * Use: Runs a REXX script from memory. + */ + +int rx_run(const char *name, const void *p, size_t sz, + int ac, const char *const *av) +{ + RXSTRING prog[2]; + RXSTRING *argv; + RXSTRING res; + dstr d = DSTR_INIT; + short badrc; + int rc; + int i; + + /* --- Set things up --- */ + + if (!name) + name = "incore"; + MAKERXSTRING(prog[0], (void *)p, sz); + MAKERXSTRING(prog[1], 0, 0); + argv = xmalloc(ac * sizeof(*argv)); + for (i = 0; i < ac; i++) + MAKERXSTRING(argv[i], (char *)av[i], strlen(av[i])); + + /* --- Run the script --- */ + + MAKERXSTRING(res, 0, 0); + rc = RexxStart(ac, argv, (char *)name, prog, + "CMD", RXCOMMAND, 0, &badrc, &res); + if (rc) { + free(RXSTRPTR(res)); + if (rc < 0) + err_report(ERR_RXERR, 0, -rc, "rexx error from script `%s'", name); + else + err_report(ERR_RXGLUE, ERRRX_INTERP, rc, "intepreter internal error"); + return (-1); + } + + /* --- Pick apart the results --- */ + + dstr_putm(&d, RXSTRPTR(res), RXSTRLEN(res)); + free(RXSTRPTR(res)); + dstr_putz(&d); + rc = atoi(d.buf); + dstr_destroy(&d); + return (rc); +} + +/* --- @rx_runfile@ --- * + * + * Arguments: @const char *name@ = pointer to filename + * @int ac@ = number of command-line arguments + * @const char *const *av@ = vector of command-line arguments + * + * Returns: Exit code from program. + * + * Use: Runs a REXX script from a file, given its name. + */ + +int rx_runfile(const char *name, int ac, const char *const *av) +{ + FILE *fp; + dstr d = DSTR_INIT; + char buf[BUFSIZ]; + size_t n; + int rc; + + /* --- Read the file into memory --- * + * + * This way avoids any crapness in the REXX implementation and means we can + * report errors in a more sensible way. + */ + + if ((fp = fopen(name, "r")) == 0) + goto fail_0; + do { + n = fread(buf, 1, sizeof(buf), fp); + DPUTM(&d, buf, n); + } while (n == sizeof(buf)); + if (ferror(fp)) + goto fail_1; + fclose(fp); + + /* --- Now do the from-memory thing --- */ + + rc = rx_run(name, d.buf, d.len, ac, av); + dstr_destroy(&d); + return (rc); + + /* --- Tidy up on errors --- */ + +fail_1: + dstr_destroy(&d); + fclose(fp); +fail_0: + err_report(ERR_RXGLUE, ERRRX_SCRIPTREAD, errno, + "couldn't read script `%s': %s", name, strerror(errno)); + return (-1); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/rxglue.h b/rxglue.h new file mode 100644 index 0000000..ee9ad09 --- /dev/null +++ b/rxglue.h @@ -0,0 +1,101 @@ +/* -*-c-*- + * + * $Id: rxglue.h,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * REXX glue for C core functionality + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rxglue.h,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +#ifndef RXGLUE_H +#define RXGLUE_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @rx_run@ --- * + * + * Arguments: @const char *name@ = pointer to filename (or null) + * @const void *p@ = pointer to program text + * @size_t sz@ = size of program text + * @int ac@ = number of arguments + * @const char *const *av@ = vector of command-line arguments + * + * Returns: Exit code from program. + * + * Use: Runs a REXX script from memory. + */ + +extern int rx_run(const char */*name*/, const void */*p*/, size_t /*sz*/, + int /*ac*/, const char *const */*av*/); + +/* --- @rx_runfile@ --- * + * + * Arguments: @const char *name@ = pointer to filename + * @int ac@ = number of command-line arguments + * @const char *const *av@ = vector of command-line arguments + * + * Returns: Exit code from program. + * + * Use: Runs a REXX script from a file, given its name. + */ + +extern int rx_runfile(const char */*name*/, + int /*ac*/, const char *const */*av*/); + +/* --- @rx_init@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Initializes the REXX external functions. + */ + +extern void rx_init(void); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/serial.c b/serial.c new file mode 100644 index 0000000..65f0417 --- /dev/null +++ b/serial.c @@ -0,0 +1,223 @@ +/* -*-c-*- + * + * $Id: serial.c,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Common serial functionality + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: serial.c,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include "serial.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @getbaud@ --- * + * + * Arguments: @const char *p@ = pointer to string + * @const char **pp@ = where to store final pointer + * + * Returns: Baud rate, or @-1@ on failure. + * + * Use: Parses a baud rate, either as a raw number or as an + * expression like `9k6'. + */ + +static long getbaud(const char *p, const char **pp) +{ + long x = 0; + long f = 1, s = 10; + int ok = 0; + + /* --- Main loop --- */ + + for (;;) { + int ch = (unsigned char)*p; + + switch (ch) { + case 'k': + case 'K': + f = 1000; + goto factor; + case 'm': + case 'M': + f = 1000000; + goto factor; + factor: + if (ok != 1) + return (-1); + x *= f; + s = 1; + ok = 2; + break; + default: + if (!isdigit(ch)) + goto done; + if (!ok) + ok = 1; + if (ok == 2) { + if (f == 1) + return (-1); + f /= 10; + } + x = x * s + (ch - '0') * f; + break; + } + p++; + } +done: + if (!ok) + return (-1); + if (pp) + *pp = p; + return (x); +} + +/* --- @serial_parse@ --- * + * + * Arguments: @const char *p@ = configuration string to parse + * @serial_config *sc@ = pointer to serial config structure + * + * Returns: Zero if OK, or @-1@ on error. + * + * Use: Parses a serial config string into something more + * reasonable. The config structure should have been + * initialized to something sensible (e.g., @SERIAL_INIT@) + * already. + */ + +int serial_parse(const char *p, serial_config *sc) +{ + long x; + const char *q; + char *qq; + + /* --- Pick out an initial baud rate --- */ + + while (isspace((unsigned char)*p)) + p++; + if (*p != 0 && *p != ':') { + if ((x = getbaud(p, &p)) < 0) + goto fail; + sc->baud = x; + } + while (isspace((unsigned char)*p)) + p++; + if (*p == ':') + p++; + + /* --- Pick out a word length --- */ + + while (isspace((unsigned char)*p)) + p++; + if ((x = strtol(p, &qq, 10)) == 0) + goto fail; + p = qq; + sc->wordlen = x; + while (isspace((unsigned char)*p)) + p++; + if (*p == '-') + p++; + + /* --- Pick out a parity designation --- */ + + while (isspace((unsigned char)*p)) + p++; + for (q = p; isalpha((unsigned char)*q); q++) + ; + if (q == p) + goto fail; + if (strncmp(p, "none", q - p) == 0) + sc->parity = PARITY_NONE; + else if (strncmp(p, "odd", q - p) == 0) + sc->parity = PARITY_ODD; + else if (strncmp(p, "even", q - p) == 0) + sc->parity = PARITY_EVEN; + else + goto fail; + p = q; + while (isspace((unsigned char)*p)) + p++; + if (*p == '-') + p++; + + /* --- Pick out a number of stop bits --- */ + + while (isspace((unsigned char)*p)) + p++; + if ((x = strtol(p, &qq, 10)) == 0) + goto fail; + p = qq; + sc->stopbits = x; + while (isspace((unsigned char)*p)) + p++; + + /* --- Done --- */ + + if (*p) + goto fail; + return (0); + +fail: + return (-1); +} + +/*----- Test rig ----------------------------------------------------------*/ + +#ifdef TEST_RIG + +#include + +int main(int argc, char *argv[]) +{ + int i; + static const char *ptab[] = { "none", "odd", "even" }; + + for (i = 1; i < argc; i++) { + serial_config sc = SERIAL_INIT; + + if (serial_parse(argv[i], &sc)) + printf("invalid serial configuration `%s'\n", argv[i]); + else { + printf("%lu %u-%s-%u\n", sc.baud, sc.wordlen, + ptab[sc.parity], sc.stopbits); + } + } + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/serial.h b/serial.h new file mode 100644 index 0000000..f514bda --- /dev/null +++ b/serial.h @@ -0,0 +1,86 @@ +/* -*-c-*- + * + * $Id: serial.h,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Common serial functionality + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: serial.h,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +#ifndef SERIAL_H +#define SERIAL_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct serial_config { + unsigned long baud; /* Baud rate */ + unsigned wordlen; /* Word length */ + unsigned parity; /* Parity type (magic constant) */ + unsigned stopbits; /* Number of stop bits */ +} serial_config; + +enum { PARITY_NONE, PARITY_ODD, PARITY_EVEN }; + +#define SERIAL_INIT { 9600, 8, PARITY_NONE, 1 } + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @serial_parse@ --- * + * + * Arguments: @const char *p@ = configuration string to parse + * @serial_config *sc@ = pointer to serial config structure + * + * Returns: Zero if OK, or @-1@ on error. + * + * Use: Parses a serial config string into something more + * reasonable. The config structure should have been + * initialized to something sensible (e.g., @SERIAL_INIT@) + * already. + */ + +extern int serial_parse(const char */*p*/, serial_config */*sc*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/setup b/setup new file mode 100755 index 0000000..5173638 --- /dev/null +++ b/setup @@ -0,0 +1,9 @@ +#! /bin/sh + +set -e +mklinks +mkaclocal +autoheader +autoconf +automake +mkdir build diff --git a/tx-serial-unix.c b/tx-serial-unix.c new file mode 100644 index 0000000..3d45d9b --- /dev/null +++ b/tx-serial-unix.c @@ -0,0 +1,349 @@ +/* -*-c-*- + * + * $Id: tx-serial-unix.c,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Unix/POSIX serial transport + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: tx-serial-unix.c,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "err.h" +#include "serial.h" +#include "txport.h" + +#ifndef O_NOCTTY +# define O_NOCTTY 0 +#endif + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct txsu { + txport tx; /* Transport base */ + struct txsu *next, **prev; /* Chain of serial transports */ + int fd; /* File descriptor */ + struct termios old_ta; /* Old terminal settings */ +} txsu; + +/*----- Static variables --------------------------------------------------*/ + +struct baudmap { unsigned long baud; unsigned long magic; }; + +static struct baudmap baudmap[] = { +#ifdef B50 + { 50, B50 }, +#endif +#ifdef B75 + { 75, B75 }, +#endif +#ifdef B110 + { 110, B110 }, +#endif +#ifdef B134 + { 134, B134 }, +#endif +#ifdef B150 + { 150, B150 }, +#endif +#ifdef B200 + { 200, B200 }, +#endif +#ifdef B300 + { 300, B300 }, +#endif +#ifdef B600 + { 600, B600 }, +#endif +#ifdef B1200 + { 1200, B1200 }, +#endif +#ifdef B1800 + { 1800, B1800 }, +#endif +#ifdef B2400 + { 2400, B2400 }, +#endif +#ifdef B4800 + { 4800, B4800 }, +#endif +#ifdef B9600 + { 9600, B9600 }, +#endif +#ifdef B19200 + { 19200, B19200 }, +#endif +#ifdef B38400 + { 38400, B38400 }, +#endif +#ifdef B57600 + { 57600, B57600 }, +#endif +#ifdef B115200 + { 115200, B115200 }, +#endif +#ifdef B230400 + { 230400, B230400 }, +#endif + { 0, 0 } +}; + +static unsigned long csize[] = { CS5, CS6, CS7, CS8 }; + +static txsu *active = 0; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @txsu_shutdown@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Restores terminal settings on exit. + */ + +void txsu_shutdown(void) +{ + txsu *tx; + + for (tx = active; tx; tx = tx->next) + tcsetattr(tx->fd, TCSAFLUSH, &tx->old_ta); +} + +/* --- @txsu_create@ --- * + * + * Arguments: @const char *file@ = filename for serial port + * @const char *config@ = configuration string + * + * Returns: Pointer to created transport block. + * + * Use: Creates a serial port transport. + */ + +txport *txsu_create(const char *file, const char *config) +{ + txsu *tx; + serial_config sc = SERIAL_INIT; + struct termios ta, old_ta; + struct baudmap *b; + int fd; + + /* --- Parse the configuration and check it --- */ + + if (config && *config && serial_parse(config, &sc)) + goto conferr; + + for (b = baudmap; b->baud && b->baud != sc.baud; b++) + ; + if (!b->baud || + sc.wordlen < 5 || sc.wordlen > 8 || + sc.stopbits < 1 || sc.stopbits > 2) + goto conferr; + + /* --- Open the serial port and fetch attributes --- */ + + if ((fd = open(file, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) { + err_report(ERR_TXPORT, ERRTX_CREATE, errno, + "couldn't open device `%s': %s", file, strerror(errno)); + goto fail_0; + } + if (fdflags(fd, O_NONBLOCK, 0, 0, 0)) { + err_report(ERR_TXPORT, ERRTX_CREATE, errno, + "fcntl(clear O_NONBLOCK): %s", file, strerror(errno)); + goto fail_1; + } + + if (tcgetattr(fd, &ta)) { + err_report(ERR_TXPORT, ERRTX_CREATE, errno, + "couldn't get terminal attributes: %s", strerror(errno)); + goto fail_1; + } + old_ta = ta; + + /* --- Fix the attributes --- */ + + ta.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + ta.c_oflag &= ~OPOST; + ta.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + ta.c_cflag &= ~(CRTSCTS); + ta.c_cc[VMIN] = 1; + ta.c_cc[VTIME] = 0; + + cfsetospeed(&ta, b->magic); + cfsetispeed(&ta, b->magic); + ta.c_cflag = (ta.c_cflag & ~CSIZE) | csize[sc.wordlen - 5]; + switch (sc.parity) { + case PARITY_NONE: ta.c_cflag &= ~PARENB; break; + case PARITY_ODD: ta.c_cflag |= PARENB | PARODD; break; + case PARITY_EVEN: ta.c_cflag |= PARENB; ta.c_cflag &= ~PARODD; break; + } + switch (sc.stopbits) { + case 1: ta.c_cflag &= ~CSTOPB; break; + case 2: ta.c_cflag |= CSTOPB; break; + } + + /* --- Set attributes --- */ + + if (tcsetattr(fd, TCSAFLUSH, &ta)) { + err_report(ERR_TXPORT, ERRTX_CREATE, errno, + "couldn't set terminal attributes: %s", strerror(errno)); + goto fail_1; + } + + /* --- Done --- */ + + tx = CREATE(txsu); + tx->fd = fd; + tx->old_ta = old_ta; + tx->next = active; + tx->prev = &active; + active = tx; + return (&tx->tx); + + /* --- Tidy up because it all went horribly wrong --- */ + +fail_1: + close(fd); +fail_0: + return (0); + +conferr: + err_report(ERR_TXPORT, ERRTX_CONFIG, 0, + "bad configuration for serial port transport"); + goto fail_0; +} + +/* --- @txsu_write@ --- * + * + * Arguments: @txport *txg@ = pointer to transport context + * @const void *p@ = pointer to buffer + * @size_t sz@ = size of the buffer + * + * Returns: Number of bytes written, or @-1@ on error. + * + * Use: Writes data to a transport. + */ + +ssize_t txsu_write(txport *txg, const void *p, size_t sz) +{ + txsu *tx = (txsu *)txg; + + return (write(tx->fd, p, sz)); +} + +/* --- @txsu_fetch@ --- * + * + * Arguments: @void *txv@ = pointer to transport context + * + * Returns: Nothing of interest. + * + * Use: Thread to fetch data from a serial port. + */ + +void *txsu_fetch(void *txv) +{ + txsu *tx = txv; + unsigned char buf[BUFSIZ]; + ssize_t n; + int e; + + /* --- Read data while it arrives --- */ + + for (;;) { + n = read(tx->fd, buf, sizeof(buf)); + if (n < 0) { + err_report(ERR_TXPORT, ERRTX_READ, errno, + "error reading from serial port: %s", strerror(errno)); + break; + } +#ifdef TERMINAL_EOF + if (!n) break; +#else + if (!n) continue; +#endif + if ((e = pthread_mutex_lock(&tx->tx.mx)) != 0) { + err_report(ERR_TXPORT, ERRTX_READ, e, + "error locking mutex: %s", strerror(e)); + break; + } + DA_ENSURE(&tx->tx.buf, n); + memcpy(DA(&tx->tx.buf) + DA_LEN(&tx->tx.buf), buf, n); + DA_EXTEND(&tx->tx.buf, n); + pthread_cond_signal(&tx->tx.cv); + pthread_mutex_unlock(&tx->tx.mx); + } + + /* --- Deal with crapness --- */ + + e = pthread_mutex_lock(&tx->tx.mx); + tx->tx.s = TX_CLOSE; + pthread_cond_signal(&tx->tx.cv); + pthread_mutex_unlock(&tx->tx.mx); + return (0); +} + +/* --- @txsu_destroy@ --- * + * + * Arguments: @txport *txg@ = pointer to transport context + * + * Returns: --- + * + * Use: Destroys a serial port transport. + */ + +void txsu_destroy(txport *txg) +{ + txsu *tx = (txsu *)txg; + + tcsetattr(tx->fd, TCSAFLUSH, &tx->old_ta); + close(tx->fd); + *tx->prev = tx->next; + if (tx->next) + tx->next->prev = tx->prev; + DESTROY(tx); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/tx-serial-unix.h b/tx-serial-unix.h new file mode 100644 index 0000000..b61e4ae --- /dev/null +++ b/tx-serial-unix.h @@ -0,0 +1,93 @@ +/* -*-c-*- + * + * $Id: tx-serial-unix.h,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Unix/POSIX serial port transport + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: tx-serial-unix.h,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +#ifndef TX_SERIAL_UNIX_H +#define TX_SERIAL_UNIX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef TXPORT_H +# include "txport.h" +#endif + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @txsu_shutdown@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Restores terminal settings on exit. + */ + +extern void txsu_shutdown(void); + +extern txport *txsu_create(const char */*file*/, const char */*config*/); +extern void *txsu_fetch(void */*txv*/); +extern ssize_t txsu_write(txport */*txg*/, + const void */*p*/, size_t /*sz*/); +extern void txsu_destroy(txport */*txg*/); + +#ifdef TX_LIST + static const struct txfile txsu_fv[] = { + { "JOGSERIAL", 0 }, + { 0, "/dev/ttyS0" }, + }; + + static txport_ops txsu_ops = { + TX_LIST, "serial-unix", txsu_fv, "9600:8-none-1", + txsu_create, txsu_fetch, txsu_write, txsu_destroy + }; +# undef TX_LIST +# define TX_LIST &txsu_ops +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/tx-socket.c b/tx-socket.c new file mode 100644 index 0000000..8fcf968 --- /dev/null +++ b/tx-socket.c @@ -0,0 +1,223 @@ +/* -*-c-*- + * + * $Id: tx-socket.c,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Socket transport + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: tx-socket.c,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "err.h" +#include "txport.h" +#include "tx-socket.h" + +#undef sun + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct txsock { + txport tx; /* Transport base */ + int fd; /* File descriptor */ +} txsock; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @txsock_create@ --- * + * + * Arguments: @const char *file@ = filename for socket + * @const char *config@ = configuration string + * + * Returns: Pointer to created transport block. + * + * Use: Creates a socket transport. + */ + +txport *txsock_create(const char *file, const char *config) +{ + txsock *tx; + int fd; + struct sockaddr_un *sun; + size_t len, sunsz; + + /* --- Parse the configuration --- */ + + if (config && *config) { + err_report(ERR_TXPORT, ERRTX_CONFIG, 0, + "bad configuration for socket transport"); + goto fail_0; + } + + /* --- Set up the address block --- */ + + len = strlen(file) + 1; + sunsz = offsetof(struct sockaddr_un, sun_path) + len; + sun = xmalloc(sunsz); + sun->sun_family = AF_UNIX; + memcpy(sun->sun_path, file, len); + + /* --- Create the socket --- */ + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + err_report(ERR_TXPORT, ERRTX_CREATE, errno, + "error creating socket: %s", strerror(errno)); + goto fail_1; + } + + /* --- Connect --- */ + + if (connect(fd, (struct sockaddr *)sun, sunsz)) { + err_report(ERR_TXPORT, ERRTX_CREATE, errno, + "couldn't connect to `%s': %s", file, strerror(errno)); + goto fail_2; + } + + /* --- Done --- */ + + tx = CREATE(txsock); + tx->fd = fd; + xfree(sun); + return (&tx->tx); + + /* --- Tidy up because it all went horribly wrong --- */ + +fail_2: + close(fd); +fail_1: + xfree(sun); +fail_0: + return (0); +} + +/* --- @txsock_write@ --- * + * + * Arguments: @txport *txg@ = pointer to transport context + * @const void *p@ = pointer to buffer + * @size_t sz@ = size of the buffer + * + * Returns: Number of bytes written, or @-1@ on error. + * + * Use: Writes data to a transport. + */ + +ssize_t txsock_write(txport *txg, const void *p, size_t sz) +{ + txsock *tx = (txsock *)txg; + + return (write(tx->fd, p, sz)); +} + +/* --- @txsock_fetch@ --- * + * + * Arguments: @void *txv@ = pointer to transport context + * + * Returns: Nothing of interest. + * + * Use: Thread to fetch data from a socket. + */ + +void *txsock_fetch(void *txv) +{ + txsock *tx = txv; + unsigned char buf[BUFSIZ]; + ssize_t n; + int e; + + /* --- Read data while it arrives --- */ + + for (;;) { + n = read(tx->fd, buf, sizeof(buf)); + if (n < 0) { + err_report(ERR_TXPORT, ERRTX_READ, errno, + "error reading from socket: %s", strerror(errno)); + break; + } + if (!n) + break; + if ((e = pthread_mutex_lock(&tx->tx.mx)) != 0) { + err_report(ERR_TXPORT, ERRTX_READ, e, + "error locking mutex: %s", strerror(e)); + break; + } + DA_ENSURE(&tx->tx.buf, n); + memcpy(DA(&tx->tx.buf) + DA_LEN(&tx->tx.buf), buf, n); + DA_EXTEND(&tx->tx.buf, n); + pthread_cond_signal(&tx->tx.cv); + pthread_mutex_unlock(&tx->tx.mx); + } + + /* --- Deal with crapness --- */ + + e = pthread_mutex_lock(&tx->tx.mx); + tx->tx.s = TX_CLOSE; + pthread_cond_signal(&tx->tx.cv); + pthread_mutex_unlock(&tx->tx.mx); + return (0); +} + +/* --- @txsock_destroy@ --- * + * + * Arguments: @txport *txg@ = pointer to transport context + * + * Returns: --- + * + * Use: Destroys a socket transport. + */ + +void txsock_destroy(txport *txg) +{ + txsock *tx = (txsock *)txg; + + close(tx->fd); + DESTROY(tx); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/tx-socket.h b/tx-socket.h new file mode 100644 index 0000000..f6e4d00 --- /dev/null +++ b/tx-socket.h @@ -0,0 +1,83 @@ +/* -*-c-*- + * + * $Id: tx-socket.h,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Socket transport + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: tx-socket.h,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +#ifndef TX_SOCKET_H +#define TX_SOCKET_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef TXPORT_H +# include "txport.h" +#endif + +/*----- Functions provided ------------------------------------------------*/ + +extern txport *txsock_create(const char */*file*/, const char */*config*/); +extern void *txsock_fetch(void */*txv*/); +extern ssize_t txsock_write(txport */*txg*/, + const void */*p*/, size_t /*sz*/); +extern void txsock_destroy(txport */*txg*/); + +#ifdef TX_LIST + static const struct txfile txsock_fv[] = { + { "JOGSOCK", 0 }, + { DIRVAR, "sim" }, + { 0, "/var/lib/jog/sim" }, + }; + + static txport_ops txsock_ops = { + TX_LIST, "socket", txsock_fv, 0, + txsock_create, txsock_fetch, txsock_write, txsock_destroy + }; +# undef TX_LIST +# define TX_LIST &txsock_ops +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/txport.c b/txport.c new file mode 100644 index 0000000..d6bdcbd --- /dev/null +++ b/txport.c @@ -0,0 +1,435 @@ +/* -*-c-*- + * + * $Id: txport.c,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Transport switch glue + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: txport.c,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "err.h" +#include "txport.h" + +/*----- Global variables --------------------------------------------------*/ + +#define TX_LIST 0 +#include "tx-socket.h" +#include "tx-serial-unix.h" +txport_ops *txlist = TX_LIST; + +const char *txname = 0; +const char *txfile = 0; +const char *txconf = 0; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @newline@ --- * + * + * Arguments: @char *s@ = pointer to line + * @size_t len@ = length of line + * @void *txv@ = pointer to transport context + * + * Returns: --- + * + * Use: Adds a line to the list. + */ + +static void newline(char *s, size_t len, void *txv) +{ + txport *tx = txv; + txline *l; + + if (!s) + return; + l = CREATE(txline); + l->s = xmalloc(len + 1); + memcpy(l->s, s, len + 1); + l->len = len; + l->tx = tx; + l->next = 0; + l->prev = tx->ll_tail; + if (tx->ll_tail) + tx->ll_tail->next = l; + else + tx->ll = l; + tx->ll_tail = l; +} + +/* --- @tx_create@ --- * + * + * Arguments: @const char *name@ = name of transport to instantiate + * @const char *file@ = filename for transport + * @const char *config@ = config string + * + * Returns: A pointer to the transport context, or null on error. + * + * Use: Creates a new transport. + */ + +txport *tx_create(const char *name, const char *file, const char *config) +{ + txport_ops *o; + txport *tx; + pthread_attr_t ta; + dstr d = DSTR_INIT; + size_t len; + int e; + + /* --- Look up the transport by name --- */ + + if (!name) { + o = txlist; + goto found; + } + len = strlen(name); + for (o = txlist; o; o = o->next) { + if (strncmp(name, o->name, len) == 0) + goto found; + } + err_report(ERR_TXPORT, ERRTX_BADTX, 0, "unknown transport `%s'", name); + return (0); + + /* --- Set up the transport block --- */ + +found: + if (!file) { + const struct txfile *fv; + for (fv = o->fv; fv->env || fv->name; fv++) { + if (fv->env && (file = getenv(fv->env)) == 0) + continue; + DRESET(&d); + if (file) + DPUTS(&d, file); + if (file && fv->name) + DPUTC(&d, '/'); + if (fv->name) + DPUTS(&d, fv->name); + break; + } + file = d.buf; + } + if (!config) + config = o->config; + if ((tx = o->create(file, config)) == 0) + goto fail_0; + tx->ops = o; + DA_CREATE(&tx->buf); + tx->ll = 0; + tx->ll_tail = 0; + if ((e = pthread_mutex_init(&tx->mx, 0)) != 0) { + err_report(ERR_TXPORT, ERRTX_CREATE, e, + "mutex creation failed: %s", strerror(e)); + goto fail_1; + } + if ((e = pthread_cond_init(&tx->cv, 0)) != 0) { + err_report(ERR_TXPORT, ERRTX_CREATE, e, + "condvar creation failed: %s", strerror(e)); + goto fail_2; + } + if ((e = pthread_attr_init(&ta)) != 0) { + err_report(ERR_TXPORT, ERRTX_CREATE, e, + "thread attribute creation failed: %s", strerror(e)); + goto fail_3; + } + if ((e = pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED)) || + (e = pthread_create(&tx->tid, &ta, tx->ops->fetch, tx)) != 0) { + err_report(ERR_TXPORT, ERRTX_CREATE, e, + "thread creation failed: %s", strerror(e)); + goto fail_4; + } + pthread_attr_destroy(&ta); + lbuf_init(&tx->lb, newline, tx); + tx->lb.delim = '\r'; + tx->s = TX_READY; + DDESTROY(&d); + return (tx); + + /* --- Something went wrong --- */ + +fail_4: + pthread_attr_destroy(&ta); +fail_3: + pthread_cond_destroy(&tx->cv); +fail_2: + pthread_mutex_destroy(&tx->mx); +fail_1: + tx->ops->destroy(tx); +fail_0: + DDESTROY(&d); + return (0); +} + +/* --- @tx_write@ --- * + * + * Arguments: @txport *tx@ = pointer to transport context + * @const void *p@ = pointer to buffer to write + * @size_t sz@ = size of buffer + * + * Returns: Zero if OK, or @-1@ on error. + * + * Use: Writes some data to a transport. + */ + +int tx_write(txport *tx, const void *p, size_t sz) +{ + if (tx->ops->write(tx, p, sz) < 0) { + err_report(ERR_TXPORT, ERRTX_WRITE, errno, + "error writing to transport: %s", strerror(errno)); + return (-1); + } + return (0); +} + +/* --- @tx_printf@ --- * + * + * Arguments: @txport *tx@ = pointer to transport context + * @const char *p@ = pointer to string to write + * + * Returns: The number of characters printed, or @EOF@ on error. + * + * Use: Writes a textual message to a transport. + */ + +int tx_vprintf(txport *tx, const char *p, va_list *ap) +{ + dstr d = DSTR_INIT; + int rc; + + dstr_vputf(&d, p, *ap); + rc = d.len; + rc = tx_write(tx, d.buf, d.len); + DDESTROY(&d); + return (rc); +} + +int tx_printf(txport *tx, const char *p, ...) +{ + va_list ap; + int rc; + + va_start(ap, p); + rc = tx_vprintf(tx, p, &ap); + va_end(ap); + return (rc); +} + +/* --- @tx_read@, @tx_readx@ --- * + * + * Arguments: @txport *tx@ = pointer to transport context + * @unsigned long t@ = time to wait for data (ms) + * @int (*filter)(const char *s, void *p)@ = filtering function + * @void *p@ = pointer argument for filter + * + * Returns: A pointer to a line block, which must be freed using + * @tx_freeline@. + * + * Use: Fetches a line from the buffer. Each line is passed to the + * filter function in oldest-to-newest order; the filter + * function returns nonzero to choose a line. If no suitable + * line is waiting in the raw buffer, the program blocks while + * more data is fetched, until the time limit @t@ is exceeded, + * in which case a null pointer is returned. A null filter + * function is equivalent to one which always selects its line. + */ + +txline *tx_readx(txport *tx, unsigned long t, + int (*filter)(const char *s, void *p), void *p) +{ + txline *l, **ll = &tx->ll; + int e; + struct timeval now, tv; + struct timespec ts; + unsigned f = 0; + +#define f_lock 1u + + /* --- Get the time to wait until --- */ + + if (t != FOREVER) { + gettimeofday(&now, 0); + tv_addl(&tv, &now, t / 1000, (t % 1000) * 1000); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + } + + /* --- Check for a matching line --- */ + +again: + for (; *ll; ll = &l->next) { + l = *ll; + if (!filter || filter(l->s, p)) + goto done; + } + l = 0; + + /* --- Lock the buffer --- * + * + * The following operations require a lock on the buffer, so we obtain that + * here. + */ + + if (!(f & f_lock)) { + if ((e = pthread_mutex_lock(&tx->mx)) != 0) { + err_report(ERR_TXPORT, ERRTX_READ, e, + "error locking mutex: %s", strerror(errno)); + goto done; + } + f |= f_lock; + } + + /* --- Push more stuff through the line buffer --- */ + +check: + if (DA_LEN(&tx->buf)) { + trace_block(1u, "incoming data", DA(&tx->buf), DA_LEN(&tx->buf)); + lbuf_snarf(&tx->lb, DA(&tx->buf), DA_LEN(&tx->buf)); + DA_SHRINK(&tx->buf, DA_LEN(&tx->buf)); + goto again; + } + + /* --- If nothing else can arrive, give up --- */ + + if (tx->s == TX_CLOSE) { + lbuf_close(&tx->lb); + tx->s = TX_CLOSED; + goto again; + } + if (!t || tx->s == TX_CLOSED) + goto done; + gettimeofday(&now, 0); + if (TV_CMP(&now, >=, &tv)) + goto done; + + /* --- Wait for some more data to arrive --- */ + + if (t == FOREVER) + e = pthread_cond_wait(&tx->cv, &tx->mx); + else + e = pthread_cond_timedwait(&tx->cv, &tx->mx, &ts); + if (e && e != ETIMEDOUT && e != EINTR) { + err_report(ERR_TXPORT, ERRTX_READ, e, + "error waiting on condvar: %s", strerror(errno)); + goto done; + } + goto check; + + /* --- Everything is finished --- */ + +done: + if (f & f_lock) + pthread_mutex_unlock(&tx->mx); + return (l); + +#undef f_lock +} + +txline *tx_read(txport *tx, unsigned long t) +{ + return (tx_readx(tx, t, 0, 0)); +} + +/* --- @tx_freeline@ --- * + * + * Arguments: @txline *l@ = pointer to line + * + * Returns: --- + * + * Use: Frees a line block. + */ + +void tx_freeline(txline *l) +{ + txport *tx = l->tx; + if (l->next) + l->next->prev = l->prev; + else + tx->ll_tail = l->prev; + if (l->prev) + l->prev->next = l->next; + else + tx->ll = l->next; + xfree(l->s); + DESTROY(l); +} + +/* --- @tx_destroy@ --- * + * + * Arguments: @txport *tx@ = transport context + * + * Returns: --- + * + * Use: Destroys a transport. + */ + +void tx_destroy(txport *tx) +{ + txline *l, *ll; + + if (tx->s == TX_READY) { + pthread_mutex_lock(&tx->mx); + if (tx->s == TX_READY) + pthread_cancel(tx->tid); + pthread_mutex_unlock(&tx->mx); + } + pthread_mutex_destroy(&tx->mx); + pthread_cond_destroy(&tx->cv); + DA_DESTROY(&tx->buf); + lbuf_destroy(&tx->lb); + for (l = tx->ll; l; l = ll) { + ll = l->next; + xfree(l->s); + DESTROY(l); + } + tx->ops->destroy(tx); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/txport.h b/txport.h new file mode 100644 index 0000000..ad4f338 --- /dev/null +++ b/txport.h @@ -0,0 +1,223 @@ +/* -*-c-*- + * + * $Id: txport.h,v 1.1 2002/01/25 19:34:45 mdw Exp $ + * + * Transport switch glue + * + * (c) 2001 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Jog: Programming for a jogging machine. + * + * Jog is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Jog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jog; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: txport.h,v $ + * Revision 1.1 2002/01/25 19:34:45 mdw + * Initial revision + * + */ + +#ifndef TXPORTSW_H +#define TXPORTSW_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include + +#include +#include + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Vector of bytes --- */ + +#ifndef UCHAR_V +# define UCHAR_V + DA_DECL(uchar_v, unsigned char); +#endif + +/* --- Node for lines --- */ + +typedef struct txline { + struct txline *next, *prev; /* Next node in the list */ + struct txport *tx; /* Owning transport */ + char *s; /* Pointer to the text */ + size_t len; /* Length of the string */ +} txline; + +/* --- A transport context --- * + * + * The only members for which there is contention are the state @s@ and the + * raw incoming buffer @buf@. Other members may be accessed without locking + * the structure. Thus, the thread messing about is essentially isolated to + * the data- fetching thread and the line buffering code. + */ + +typedef struct txport { + struct txport_ops *ops; /* Operations table */ + pthread_t tid; /* Fetching thread id */ + pthread_mutex_t mx; /* Lock for this structure */ + pthread_cond_t cv; /* `New data has arrived' */ + unsigned s; /* State of this transport */ + uchar_v buf; /* Buffer of incoming data */ + lbuf lb; /* Buffer for extracting lines */ + txline *ll, *ll_tail; /* List of waiting lines, in order */ +} txport; + +enum { + TX_READY, /* More data may arrive */ + TX_CLOSE, /* No more data will arrive */ + TX_CLOSED /* Done the closure thing already */ +}; + +/* --- Transport operations --- */ + +struct txfile { const char *env; const char *name; }; + +typedef struct txport_ops { + struct txport_ops *next; + const char *name; + const struct txfile *fv; + const char *config; + txport *(*create)(const char */*file*/, const char */*config*/); + void *(*fetch)(void */*txv*/); + ssize_t (*write)(txport */*tx*/, const void */*p*/, size_t /*sz*/); + void (*destroy)(txport */*tx*/); +} txport_ops; + +/*----- Global variables --------------------------------------------------*/ + +extern txport_ops *txlist; +extern const char *txname; +extern const char *txfile; +extern const char *txconf; + +#define DIRVAR "JOGDIR" + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @tx_create@ --- * + * + * Arguments: @const char *name@ = name of transport to instantiate + * @const char *file@ = filename for transport + * @const char *config@ = config string + * + * Returns: A pointer to the transport context, or null on error. + * + * Use: Creates a new transport. + */ + +extern txport *tx_create(const char */*name*/, const char */*file*/, + const char */*config*/); + +/* --- @tx_write@ --- * + * + * Arguments: @txport *tx@ = pointer to transport context + * @const void *p@ = pointer to buffer to write + * @size_t sz@ = size of buffer + * + * Returns: Zero if OK, or @-1@ on error. + * + * Use: Writes some data to a transport. + */ + +extern int tx_write(txport */*tx*/, const void */*p*/, size_t /*sz*/); + +/* --- @tx_printf@ --- * + * + * Arguments: @txport *tx@ = pointer to transport context + * @const char *p@ = pointer to string to write + * + * Returns: The number of characters printed, or @EOF@ on error. + * + * Use: Writes a textual message to a transport. + */ + +extern int tx_vprintf(txport */*tx*/, const char */*p*/, va_list */*ap*/); + +extern int tx_printf(txport */*tx*/, const char */*p*/, ...); + +/* --- @tx_read@, @tx_readx@ --- * + * + * Arguments: @txport *tx@ = pointer to transport context + * @unsigned long t@ = time to wait for data (ms) + * @int (*filter)(const char *s, void *p)@ = filtering function + * @void *p@ = pointer argument for filter + * + * Returns: A pointer to a line block, which must be freed using + * @tx_freeline@. + * + * Use: Fetches a line from the buffer. Each line is passed to the + * filter function in oldest-to-newest order; the filter + * function returns nonzero to choose a line. If no suitable + * line is waiting in the raw buffer, the program blocks while + * more data is fetched, until the time limit @t@ is exceeded, + * in which case a null pointer is returned. A null filter + * function is equivalent to one which always selects its line. + */ + +#define FOREVER (~0ul) + +extern txline *tx_readx(txport */*tx*/, unsigned long /*t*/, + int (*/*filter*/)(const char */*s*/, void */*p*/), + void */*p*/); + +extern txline *tx_read(txport */*tx*/, unsigned long /*t*/); + +/* --- @tx_freeline@ --- * + * + * Arguments: @txline *l@ = pointer to line + * + * Returns: --- + * + * Use: Frees a line block. + */ + +extern void tx_freeline(txline */*l*/); + +/* --- @tx_destroy@ --- * + * + * Arguments: @txport *tx@ = transport context + * + * Returns: --- + * + * Use: Destroys a transport. + */ + +extern void tx_destroy(txport */*tx*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif