/* -*-c-*-
*
- * $Id: txport.c,v 1.1 2002/01/25 19:34:45 mdw Exp $
+ * $Id: txport.c,v 1.2 2002/01/30 09:27:10 mdw Exp $
*
* Transport switch glue
*
/*----- Revision history --------------------------------------------------*
*
* $Log: txport.c,v $
+ * Revision 1.2 2002/01/30 09:27:10 mdw
+ * Transport configuration overhaul. Configuration string is now a list of
+ * `;'-separated `key=value' strings, which can be handled either by the
+ * core or the transport module. The newline character(s) are a core
+ * parameter now.
+ *
* Revision 1.1 2002/01/25 19:34:45 mdw
* Initial revision
*
# include "config.h"
#endif
+#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <mLib/dstr.h>
#include <mLib/lbuf.h>
#include <mLib/sub.h>
-#include <mLib/trace.h>
#include <mLib/tv.h>
#include "err.h"
+#include "jog.h"
#include "txport.h"
/*----- Global variables --------------------------------------------------*/
if (!s)
return;
+ T( trace(T_TX, "tx: completed line: `%s'", s); )
l = CREATE(txline);
l->s = xmalloc(len + 1);
memcpy(l->s, s, len + 1);
tx->ll_tail = l;
}
+/* --- @tx_configure@ --- *
+ *
+ * Arguments: @txport *tx@ = pointer to transport block
+ * @const char *config@ = config string
+ *
+ * Returns: Zero if OK, nonzero on errors.
+ *
+ * Use: Applies a configuration string to a transport.
+ */
+
+int tx_configure(txport *tx, const char *config)
+{
+ char *c;
+ char *k, *v;
+ int rc = -1;
+
+ if (!config)
+ return (0);
+ c = xstrdup(config);
+ for (k = strtok(c, ";"); k; k = strtok(0, ";")) {
+ if ((v = strchr(k, '=')) != 0)
+ *v++ = 0;
+ if (strcmp(k, "nl") == 0 || strcmp(k, "newline") == 0) {
+ int d;
+ if (!v)
+ d = LBUF_CRLF;
+ else if (strcmp(v, "none") == 0)
+ d = 0;
+ else if (strcmp(v, "crlf") == 0)
+ d = LBUF_CRLF;
+ else if (strcmp(v, "crlf-strict") == 0 ||
+ strcmp(v, "strict-crlf") == 0)
+ d = LBUF_STRICTCRLF;
+ else if (strcmp(v, "cr") == 0)
+ d = '\r';
+ else if (strcmp(v, "lf") == 0)
+ d = '\n';
+ else if (v[0] == '\\') switch (v[1]) {
+ case 0: d = '\\'; break;
+ case 'a': d = 0x07; goto d_single;
+ case 'b': d = 0x08; goto d_single;
+ case 'f': d = 0x0c; goto d_single;
+ case 'n': d = 0x0a; goto d_single;
+ case 'r': d = 0x0d; goto d_single;
+ case 't': d = 0x09; goto d_single;
+ case 'v': d = 0x0b; goto d_single;
+ case 'e': d = 0x1b; goto d_single;
+ case 'x':
+ if (!isxdigit((unsigned char)v[2]) ||
+ (d = strtoul(v + 2, &v, 16) || *v)) {
+ err_report(ERR_TXPORT, ERRTX_CONFIG, 0,
+ "bad hex escape `%s' in `newline' config", v);
+ goto err;
+ }
+ break;
+ default:
+ if (isdigit((unsigned char)v[0])) {
+ d = strtoul(v + 1, &v, 8);
+ if (*v) {
+ err_report(ERR_TXPORT, ERRTX_CONFIG, 0,
+ "bad octal escape `%s' in `newline' config", v);
+ goto err;
+ }
+ }
+ d = v[1];
+ d_single:
+ if (v[2]) {
+ err_report(ERR_TXPORT, ERRTX_CONFIG, 0,
+ "unknown escape `%s' in `newline' config", v);
+ goto err;
+ }
+ break;
+ } else if (v[1] == 0)
+ d = v[0];
+ else {
+ err_report(ERR_TXPORT, ERRTX_CONFIG, 0,
+ "unknown delimiter `%s' in `newline' config", v);
+ goto err;
+ }
+ tx->lb.delim = d;
+ } else {
+ int e = 0;
+ if (tx->ops->configure)
+ e = tx->ops->configure(tx, k, v);
+ if (!e) {
+ err_report(ERR_TXPORT, ERRTX_CONFIG, 0,
+ "unrecognized configuration keyword `%s'", k);
+ }
+ if (e <= 0)
+ goto err;
+ }
+ }
+
+ rc = 0;
+err:
+ xfree(c);
+ return (rc);
+}
+
/* --- @tx_create@ --- *
*
* Arguments: @const char *name@ = name of transport to instantiate
}
if (!config)
config = o->config;
- if ((tx = o->create(file, config)) == 0)
+ if ((tx = o->create(file)) == 0)
goto fail_0;
tx->ops = o;
- DA_CREATE(&tx->buf);
+ lbuf_init(&tx->lb, newline, tx);
+ if (tx_configure(tx, config))
+ goto fail_1;
tx->ll = 0;
tx->ll_tail = 0;
if ((e = pthread_mutex_init(&tx->mx, 0)) != 0) {
goto fail_4;
}
pthread_attr_destroy(&ta);
- lbuf_init(&tx->lb, newline, tx);
- tx->lb.delim = '\r';
+ DA_CREATE(&tx->buf);
tx->s = TX_READY;
DDESTROY(&d);
return (tx);
fail_2:
pthread_mutex_destroy(&tx->mx);
fail_1:
+ lbuf_destroy(&tx->lb);
tx->ops->destroy(tx);
fail_0:
DDESTROY(&d);
int tx_write(txport *tx, const void *p, size_t sz)
{
+ T( trace_block(T_TX, "tx: outgoing data", p, sz); )
if (tx->ops->write(tx, p, sz) < 0) {
err_report(ERR_TXPORT, ERRTX_WRITE, errno,
"error writing to transport: %s", strerror(errno));
return (rc);
}
+/* --- @tx_newline@ --- *
+ *
+ * Arguments: @txport *tx@ = pointer to transport context
+ *
+ * Returns: Zero if OK, nonzero on error.
+ *
+ * Use: Writes a newline (record boundary) to the output.
+ */
+
+int tx_newline(txport *tx)
+{
+ static const char crlf[2] = { 0x0d, 0x0a };
+ char c;
+ const char *p;
+ size_t sz;
+
+ switch (tx->lb.delim) {
+ case LBUF_CRLF:
+ case LBUF_STRICTCRLF:
+ p = crlf;
+ sz = 2;
+ break;
+ case 0:
+ return (0);
+ default:
+ c = tx->lb.delim;
+ p = &c;
+ sz = 1;
+ break;
+ }
+ return (tx_write(tx, p, sz));
+}
+
/* --- @tx_read@, @tx_readx@ --- *
*
* Arguments: @txport *tx@ = pointer to transport context
/* --- Get the time to wait until --- */
+ T( trace(T_TXSYS, "txsys: tx_readx begin"); )
if (t != FOREVER) {
gettimeofday(&now, 0);
tv_addl(&tv, &now, t / 1000, (t % 1000) * 1000);
again:
for (; *ll; ll = &l->next) {
l = *ll;
- if (!filter || filter(l->s, p))
+ if (!filter || filter(l->s, p)) {
+ T( trace(T_TXSYS, "txsys: matched line; done"); )
goto done;
+ }
}
l = 0;
goto done;
}
f |= f_lock;
+ T( trace(T_TXSYS, "txsys: locked buffer"); )
}
/* --- 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));
+ T( trace_block(T_TX, "tx: incoming data",
+ DA(&tx->buf), DA_LEN(&tx->buf)); )
+ if (tx->lb.delim)
+ lbuf_snarf(&tx->lb, DA(&tx->buf), DA_LEN(&tx->buf));
+ else
+ newline((char *)DA(&tx->buf), DA_LEN(&tx->buf), tx);
DA_SHRINK(&tx->buf, DA_LEN(&tx->buf));
goto again;
}
if (tx->s == TX_CLOSE) {
lbuf_close(&tx->lb);
+ T( trace(T_TXSYS, "txsys: transport closed; flushing"); )
tx->s = TX_CLOSED;
goto again;
}
- if (!t || tx->s == TX_CLOSED)
- goto done;
- gettimeofday(&now, 0);
- if (TV_CMP(&now, >=, &tv))
+ if (!t || tx->s == TX_CLOSED) {
+ T( trace(T_TX, "tx: transport is closed"); )
goto done;
+ }
/* --- Wait for some more data to arrive --- */
+ T( trace(T_TXSYS, "txsys: waiting for data"); )
if (t == FOREVER)
e = pthread_cond_wait(&tx->cv, &tx->mx);
- else
+ else {
+ gettimeofday(&now, 0);
+ if (TV_CMP(&now, >=, &tv)) {
+ T( trace(T_TXSYS, "txsys: timed out"); )
+ goto done;
+ }
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;
}
+ T( trace(T_TXSYS, "txsys: woken, checking again"); )
goto check;
/* --- Everything is finished --- */
done:
- if (f & f_lock)
+ if (f & f_lock) {
pthread_mutex_unlock(&tx->mx);
+ T( trace(T_TXSYS, "txsys: unlock buffer"); )
+ }
+ T( trace(T_TXSYS, "tx_readx done"); )
return (l);
#undef f_lock
/* -*-c-*-
*
- * $Id: txport.h,v 1.1 2002/01/25 19:34:45 mdw Exp $
+ * $Id: txport.h,v 1.2 2002/01/30 09:27:10 mdw Exp $
*
* Transport switch glue
*
/*----- Revision history --------------------------------------------------*
*
* $Log: txport.h,v $
+ * Revision 1.2 2002/01/30 09:27:10 mdw
+ * Transport configuration overhaul. Configuration string is now a list of
+ * `;'-separated `key=value' strings, which can be handled either by the
+ * core or the transport module. The newline character(s) are a core
+ * parameter now.
+ *
* Revision 1.1 2002/01/25 19:34:45 mdw
* Initial revision
*
const char *name;
const struct txfile *fv;
const char *config;
- txport *(*create)(const char */*file*/, const char */*config*/);
+ txport *(*create)(const char */*file*/);
+ int (*configure)(txport */*tx*/, const char */*k*/, const char */*v*/);
void *(*fetch)(void */*txv*/);
ssize_t (*write)(txport */*tx*/, const void */*p*/, size_t /*sz*/);
void (*destroy)(txport */*tx*/);
extern txport *tx_create(const char */*name*/, const char */*file*/,
const char */*config*/);
+/* --- @tx_configure@ --- *
+ *
+ * Arguments: @txport *tx@ = pointer to transport block
+ * @const char *config@ = config string
+ *
+ * Returns: Zero if OK, nonzero on errors.
+ *
+ * Use: Applies a configuration string to a transport.
+ */
+
+extern int tx_configure(txport */*tx*/, const char */*config*/);
+
/* --- @tx_write@ --- *
*
* Arguments: @txport *tx@ = pointer to transport context
extern int tx_printf(txport */*tx*/, const char */*p*/, ...);
+/* --- @tx_newline@ --- *
+ *
+ * Arguments: @txport *tx@ = pointer to transport context
+ *
+ * Returns: Zero if OK, nonzero on error.
+ *
+ * Use: Writes a newline (record boundary) to the output.
+ */
+
+int tx_newline(txport */*tx*/);
+
/* --- @tx_read@, @tx_readx@ --- *
*
* Arguments: @txport *tx@ = pointer to transport context