#include #include #include #include #include #include #include extern const char *__progname; #include "pollloop.h" #if defined(EWOUDBLOCK) #if defined(EAGAIN) #if EWOULDBLOCK == EAGAIN #define BLOCKING EWOULDBLOCK #else #define BLOCKING EWOULDBLOCK: case EAGAIN #endif #else #define BLOCKING EWOULDBLOCK #endif #else #if defined(EAGAIN) #define BLOCKING EAGAIN #else #error "Neither EWOULDBLOCK nor EAGAIN defined" #endif #endif typedef struct chunk CHUNK; typedef struct chunkq CHUNKQ; struct chunk { CHUNK *link; char *data; int max; int fill; int ptr; } ; struct chunkq { CHUNK *list; CHUNK *last; int len; } ; #define IBUFSIZE 65536 #define OQLIMIT 65536 #define CHUNKSIZE 1024 static int ieof; static char ibuf[IBUFSIZE]; static int iblen; static CHUNKQ iline; static int ilequals; static CHUNKQ oq; /* * Initialize a CHUNKQ. */ static void chunkq_init(CHUNKQ *cq) { cq->list = 0; cq->last = 0; cq->len = 0; } /* * Append the contents of CHUNKQ src to CHUNKQ dst. */ static void chunkq_append(CHUNKQ *src, CHUNKQ *dst) { if (src->last == 0) { /* nothing to do */ } else if (dst->last == 0) { *dst = *src; chunkq_init(src); } else { dst->last->link = src->list; dst->last = src->last; dst->len += src->len; chunkq_init(src); } } /* * Reset a CHUNKQ to contain zero bytes. */ static void chunkq_reset(CHUNKQ *cq) { CHUNK *c; if (! cq->last) return; while (cq->list != cq->last) { c = cq->list; cq->list = c->link; free(c->data); free(c); } c = cq->list; c->fill = 0; c->ptr = 0; cq->len = 0; } /* * Append one char to a CHUNKQ. */ static void chunkq_appendc(CHUNKQ *cq, char ch) { if ( !cq->last || (cq->last->fill >= cq->last->max) ) { CHUNK *ch; ch = malloc(sizeof(CHUNK)); ch->data = malloc(CHUNKSIZE); ch->max = CHUNKSIZE; ch->fill = 0; ch->ptr = 0; if (cq->last) cq->last->link = ch; else cq->list = ch; cq->last = ch; ch->link = 0; } cq->last->data[cq->last->fill++] = ch; cq->len ++; } /* * We've reached input EOF. Exit as soon as we have no queued output. * This depends on two things: * * (a) We never enter "at input EOF" state except on trying to * read, and we never try to read if there's input waiting to * be processed. * * (b) Input processing always consumes all available input. * * Thus, the only thing we can have waiting to happen here is output * draining. We ignore isaveq; even if there's something there, if * we're at input EOF, it can't be completed with a match. */ static int maybe_exit(void *cookie __attribute__((__unused__))) { if (oq.len < 1) exit(0); return(BLOCK_NIL); } /* * We want more input whenever we * (a) haven't reached EOF, * (b) have nothing waiting to be processed, * and (c) the output queue is small enough. */ static int rtest_in(int id __attribute__((__unused__)), void *cookie __attribute__((__unused__))) { return( !ieof && (iblen == 0) && (oq.len < OQLIMIT) ); } /* * We want to write output whenever we have anything queued. */ static int wtest_out(int id __attribute__((__unused__)), void *cookie __attribute__((__unused__))) { return(oq.len>0); } /* * We aren't called unless ibuf has nothing in it, so we know we can * read starting at the beginning of the buffer. */ static void rd_in(int id __attribute__((__unused__)), void *cookie __attribute__((__unused__))) { int r; r = read(0,&ibuf[0],sizeof(ibuf)); if (r < 0) { switch (errno) { case BLOCKING: case EINTR: return; break; } fprintf(stderr,"%s: input read: %s\n",__progname,strerror(errno)); exit(1); } if (r == 0) { ieof = 1; add_block_fn(&maybe_exit,0); return; } iblen = r; } static void wr_out(int id __attribute__((__unused__)), void *cookie __attribute__((__unused__))) { int w; CHUNK *c; c = oq.list; if (! c) { if (oq.len > 0) abort(); return; } w = write(1,c->data+c->ptr,c->fill-c->ptr); if (w < 0) { switch (errno) { case BLOCKING: case EINTR: return; break; } fprintf(stderr,"%s: output write: %s\n",__progname,strerror(errno)); exit(1); } c->ptr += w; oq.len -= w; if (c->ptr < c->fill) return; free(c->data); oq.list = c->link; free(c); if (! oq.list) { if (oq.len != 0) abort(); oq.last = 0; } else { if (oq.len <= 0) abort(); } } /* * Process any pending input we can. */ static int process(void *cookie __attribute__((__unused__))) { int ibx; char c; if ( (iblen < 1) || (oq.len > OQLIMIT) ) return(BLOCK_NIL); for (ibx=0;ibx 2) { chunkq_appendc(&iline,'\n'); chunkq_append(&iline,&oq); } else { chunkq_reset(&iline); } ilequals = 0; break; default: if (ilequals < 3) { if (c == '=') ilequals ++; else ilequals = 0; } chunkq_appendc(&iline,c); break; } } iblen = 0; return(BLOCK_LOOP); } /* * Initialize on startup. */ static void setup(void) { ieof = 0; iblen = 0; chunkq_init(&iline); ilequals = 0; chunkq_init(&oq); fcntl(0,F_SETFL,fcntl(0,F_GETFL,0)|O_NONBLOCK); fcntl(1,F_SETFL,fcntl(1,F_GETFL,0)|O_NONBLOCK); add_poll_fd(0,&rtest_in,&rwtest_never,&rd_in,0,0); add_poll_fd(1,&rwtest_never,&wtest_out,0,&wr_out,0); add_block_fn(&process,0); } int main(void); int main(void) { init_polling(); setup(); while (1) { pre_poll(); if (do_poll() < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: poll: %s",__progname,strerror(errno)); exit(1); } post_poll(); } }