From 2ec1e6937048636ec6761c3a76c5bf9544278601 Mon Sep 17 00:00:00 2001 From: mdw Date: Fri, 25 Jan 2002 19:34:45 +0000 Subject: [PATCH] Initial revision --- .cvsignore | 6 + .links | 4 + .skelrc | 8 + Makefile.am | 46 ++++ acconfig.h | 65 ++++++ configure.in | 78 +++++++ err.c | 206 ++++++++++++++++++ err.h | 164 +++++++++++++++ main.c | 205 ++++++++++++++++++ rxglue.c | 632 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ rxglue.h | 101 +++++++++ serial.c | 223 ++++++++++++++++++++ serial.h | 86 ++++++++ setup | 9 + tx-serial-unix.c | 349 ++++++++++++++++++++++++++++++ tx-serial-unix.h | 93 ++++++++ tx-socket.c | 223 ++++++++++++++++++++ tx-socket.h | 83 ++++++++ txport.c | 435 ++++++++++++++++++++++++++++++++++++++ txport.h | 223 ++++++++++++++++++++ 20 files changed, 3239 insertions(+) create mode 100644 .cvsignore create mode 100644 .links create mode 100644 .skelrc create mode 100644 Makefile.am create mode 100644 acconfig.h create mode 100644 configure.in create mode 100644 err.c create mode 100644 err.h create mode 100644 main.c create mode 100644 rxglue.c create mode 100644 rxglue.h create mode 100644 serial.c create mode 100644 serial.h create mode 100755 setup create mode 100644 tx-serial-unix.c create mode 100644 tx-serial-unix.h create mode 100644 tx-socket.c create mode 100644 tx-socket.h create mode 100644 txport.c create mode 100644 txport.h 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 -- 2.11.0