www.pudn.com > slow.zip > mystdio.C
#include#include #include #include #include "private.h" #include "mystdio.h" #define INTERVAL 100000 /* timer interrupts every 0.1 secs */ #define PERSEC (1000000 / INTERVAL) #define BUFFERSIZE 1024 /* power of 2 */ #define BUFFERMASK (BUFFERSIZE-1) #define IN_BIT 1 /* fd 0 */ #define OUT_BIT 2 /* fd 1 */ struct Buffer { Buffer() { head = tail = 0; eof = false; } int getch(); void putch(int); void fill(), empty(); int head, tail; bool eof; char buf[BUFFERSIZE]; }; static Buffer inbuffer, outbuffer; static int timeout; static termios cmode, rmode; static void getmode(termios*), setmode(termios*), clockhandler(int); static uint doselect(); static void fatal(char*); global void openstdio() { getmode(&cmode); /* get current mode */ rmode = cmode; /* copy the struct */ rmode.c_iflag = rmode.c_oflag = rmode.c_lflag = 0; /* modify for raw mode */ rmode.c_cc[VMIN] = 1; rmode.c_cc[VTIME] = 0; /* set timeout params */ setmode(&rmode); /* set raw mode */ timeout = 0; signal(SIGALRM, (SIG_PF) clockhandler); static itimerval itval = { { 0, INTERVAL }, { 0, INTERVAL } }; setitimer(ITIMER_REAL, &itval, NULL); /* set timer interrupting */ } global void closestdio() { setmode(&cmode); /* set cooked mode */ } static void getmode(termios *tm) { int code = tcgetattr(0, tm); unless (code == 0) fatal("tcgetattr failed"); } static void setmode(termios *tm) { tcsetattr(0, TCSADRAIN, tm); /* ignore errors here */ } global void my_alarm(int n) { timeout = n * PERSEC; } global int my_getchar() { /* get char from stdin */ return inbuffer.getch(); } int Buffer::getch() { return ((head-tail) > 0) ? buf[tail++ & BUFFERMASK] & 0xff : eof ? EOF : NOCHAR; } global void my_putchar(int ch) { /* put char to stdout */ outbuffer.putch(ch); } void Buffer::putch(int ch) { if (ch >= 0) { if ((head-tail) >= BUFFERSIZE) fatal("putchar buffer overflow"); buf[head++ & BUFFERMASK] = ch; } } static void clockhandler(int sig) { signal(SIGALRM, (SIG_PF) clockhandler); /* reset handler */ if (timeout > 0 && --timeout == 0) kill(getpid(), SIGUSR1); /* "Remote modem is not responding" */ inbuffer.fill(); outbuffer.empty(); } void Buffer::fill() { while ((head-tail) < BUFFERSIZE) { uint rdy = doselect(); unless (rdy & IN_BIT) break; int hd = head & BUFFERMASK; int tl = tail & BUFFERMASK; int nb = read(0, &buf[hd], (tl > hd) ? tl-hd : BUFFERSIZE-hd); if (nb < 0) fatal("read failed"); if (nb == 0) { eof = true; break; } head += nb; } } void Buffer::empty() { while ((head-tail) > 0) { uint rdy = doselect(); unless (rdy & OUT_BIT) break; int hd = head & BUFFERMASK; int tl = tail & BUFFERMASK; int nb = write(1, &buf[tl], (hd > tl) ? hd-tl : BUFFERSIZE-tl); if (nb < 0) fatal("write failed"); if (nb == 0) { eof = true; break; } tail += nb; } } static uint doselect() { uint ib = IN_BIT, ob = OUT_BIT; /* test stdin, stdout */ static int tmo[] = { 0, 0 }; int nb = select(2, &ib, &ob, NULL, tmo); if (nb < 0) fatal("select failed"); return (ib | ob); } extern int errno; static void fatal(char *msg) { fprintf(stderr, "%s [errno=%d]\r\n", msg, errno); abort(); }