#include #include #include #include #include #include #include #include #include #include SRCFILE(__FILE__) const char *TapTo; static void mallocerr(){ PadsError("Pads library: malloc failed"); } void Pick( const char *s, Action a, long o ){ Index ix; trace("Pick(%d,%d)", a, o); ix = ICache->place(Item(s, a, o)); R->pktstart( P_PICK ); R->sendlong( ix.indx ); R->pktend(); } const char *padsterm = "padsterm"; const char *loadterm(int argc, char **argv, const char *cmd){ int targc = 0; char *targv[20]; int ph2t[2], pt2h[2]; char err[128]; targv[targc++] = StrDup(cmd); while( argc > 1 ){ targv[targc++] = argv[1]; --argc, argv++; } targv[targc] = 0; if(pipe(ph2t)==-1 || pipe(pt2h)==-1){ perror("pipe"); exit(-1); sprintf(err, "loadterm: pipe: %s", strerror(errno)); return err; } switch(fork()){ case 0: dup2(ph2t[0], 0); dup2(pt2h[1], 1); close(ph2t[0]); close(ph2t[1]); close(pt2h[0]); close(pt2h[1]); execvp(padsterm, targv); fprintf(stderr, "can't exec: "); perror(padsterm); exit(127); case -1: sprintf(err, "can't fork padsterm: %s", strerror(errno)); return err; } dup2(pt2h[0], 0); dup2(ph2t[1], 1); close(ph2t[0]); close(ph2t[1]); close(pt2h[0]); close(pt2h[1]); return 0; } const char *PadsInit(int argc, char **argv){ const char *c = loadterm(argc, argv, padsterm); if (c) return c; # ifdef USE_SET_NEW_HANDLER std::set_new_handler(mallocerr); # endif R = new Remote(1); R->pktstart(P_VERSION); R->sendlong(PADS_VERSION); R->pktend(); R->pktstart(P_BUSY); R->pktend(); ICache = new ItemCache; CCache = new CarteCache; return 0; } const char *PadsTermInit(int argc, char** argv, char* term){ return "not implemented."; } void PadsRemInit(){ int ph2t[2] = {0, 1}; R = new Remote(1); # ifdef USE_SET_NEW_HANDLER std::set_new_handler(mallocerr); # endif R->pktstart(P_VERSION); R->sendlong(PADS_VERSION); R->pktend(); R->pktstart(P_BUSY); R->pktend(); ICache = new ItemCache; CCache = new CarteCache; } void WireTap(PRINTF_ARGS){ PRINTF_PROLOGUE; static int fd = -1; static long t0; struct stat s; time_t t; if( !TapTo ) return; t = time(0); # ifdef DAK if( ::stat("/usr/tmp/.logpads", &s) || ctime(&t)[23]!='6' ) # else if( ::stat("/usr/tmp/.logpads", &s) ) # endif goto BailOut; if( t0 ) t -= t0; else t0 = t; char buf[256]; sprintf(buf, "%x:", t); sprintf(buf+strlen(buf), PRINTF_COPY); if( fd < 0 ){ if( ::stat(TapTo, &s) ) creat(TapTo, 0777); fd = open(TapTo, 1); } #define PILOGSIZE 32000 if( fd<0 || fstat(fd, &s) || s.st_size > PILOGSIZE ) goto BailOut; lseek(fd, s.st_size, 0); write(fd, buf, strlen(buf)); return; BailOut: TapTo = 0; } int BothValid(PadRcv *p, PadRcv *o){ return p && o; } void TermAction(PadRcv *parent, PadRcv *obj, int pick){ Item *item; Index ix((int)R->rcvlong()); trace( "TermAction(%d,%d,%d)", parent, obj, pick ); if( ix.null() ) return; item = ICache->take(ix); if( !BothValid(parent,obj) || (pick && !obj->accept(item->action)) ) return; if( item->action ) (obj->*item->action)(item->opand, 0, 0); } const char *DoKbd(PadRcv *obj, char *buf){ WireTap("%x->%x(%x) %s\n", obj, &PadRcv::kbd, strlen(buf), buf); const char *e = obj->kbd(buf); if( e ) PadsWarn("%s", e); return e; } void Shell(){ char cmd[256]; R->rcvstring(cmd); FILE *fp = Popen(cmd, "w"); for( long lines = R->rcvlong(); lines>0; --lines ){ char data[256]; if( fp ) fprintf(fp, "%s\n", R->rcvstring(data)); } if( !fp ){ PadsWarn("cannot write to pipe"); return; } int x = Pclose(fp); if( x ) PadsWarn( "exit(%d): %s", x, cmd ); } void ShKbd(PadRcv *obj, char *cmd){ FILE *fp = Popen(cmd, "r"); trace( "ShKbd(%d,%s)", obj, cmd ); if( !fp ){ PadsWarn("cannot read from pipe"); return; } char buf[256]; while( fgets(buf, sizeof buf, fp) ){ buf[strlen(buf)-1] = 0; if( DoKbd(obj, buf) ) break; } int x = Pclose(fp); if( x ) PadsWarn( "exit(%d): %s", x, cmd ); } void Kbd(PadRcv *parent, PadRcv *obj){ char buf[256]; R->rcvstring(buf); trace( "Kbd %d %s", obj, buf ); if( !BothValid(parent,obj) ) return; if( !strcmp( buf, "?" ) ){ # ifdef SHOWHELP obj->showhelp(HELP_KEY); # else const char *h = obj->help(); PadsWarn( "%s", (h && *h) ? h : "error: null help string" ); # endif } else if( buf[0] == '<' ){ ShKbd(obj, buf+1); } else DoKbd(obj, buf); } void TermServe(){ Protocol p; long n, to, pick = 0; char errbuf[64]; trace("TermServe()"); R->pktstart(P_IDLE); R->pktflush(); p = (Protocol) R->get(); if( p == P_PICK ) { pick = 1; p = (Protocol) R->get(); } PadRcv *par = R->rcvobj(); PadRcv *obj = R->rcvobj(); if( p != P_CYCLE ) { R->pktstart(P_BUSY); R->pktflush(); } switch( (int) p ){ case P_ACTION: TermAction(par, obj, (int)pick); break; case P_KBDSTR: Kbd(par, obj); break; case P_SHELL: Shell(); break; case P_NUMERIC: case P_CYCLE: case P_USERCLOSE: case P_USERCUT: n = R->rcvlong(); if( !BothValid(par,obj) ) return; switch( (int) p ){ case P_NUMERIC: WireTap("%x->%x(%x)\n", obj, &PadRcv::numeric, n); obj->numeric(n); break; case P_CYCLE: trace("P_CYCLE (par.0x%02X obj.0x%02X) n.%d", par, obj, n); obj->cycle(); break; case P_USERCLOSE: WireTap("%x->%x(0)\n", obj, &PadRcv::userclose); obj->userclose(); break; case P_USERCUT : WireTap("%x->%x(0)\n", obj, &PadRcv::usercut); obj->usercut(); break; default: R->err("Protocol error: User-defined"); } break; case P_LINEREQ: n = R->rcvlong(); to = R->rcvlong(); trace("P_LINEREQ %d %d %d", p, n, to); if( !BothValid(par,obj) ) return; WireTap("%x->%x(%x)", obj, &PadRcv::linereq, to-n+1); while( n <= to ) obj->linereq((long) n++, 0); break; case P_QUIT: trace("P_QUIT"); exit(0); break; default: trace("Protocol error: Unknown protocol %x (%d)", p, p); sprintf(errbuf, "Protocol error: Unknown protocol %x (%d)", p, p); R->err(errbuf); } } void PadsServe(long n){ if ( n ) { while( n-->0 ) TermServe(); } else { for( ;; ) TermServe(); } } void PadsWarn(PRINTF_ARGS){ pid_t pid = getpid(); va_list av; char t[256]; va_start(av, fmt); vsprintf(t, fmt, av); va_end(av); R->pktstart( P_HELPSTR ); R->sendstring( t ); R->pktend(); } void PadsError(PRINTF_ARGS){ va_list av; char t[256]; va_start(av, fmt); vsprintf(t, fmt, av); va_end(av); R->pktstart( P_ERROR ); R->sendstring( t ); R->pktflush(); } void PadsQuit(){ R->pktstart( P_QUIT ); R->pktflush(); exit(0); }