#include "browsercomms.h" #define MAX_LINE_SIZE 4096 #define MAX_SYM_SIZE 1024 /** * Wait for a Redex Trace Browser to connect on a predefined port. * Return a file stream connected to the browser. **/ FILE * waitForBrowserConnection(int port) { int fdesc; char *fdesc_str = getenv("TRACE_FD"); int i; int s, len; #if defined(__linux) || defined(__NetBSD__) struct sockaddr addr; #else struct sockaddr_in addr; #endif struct hostent *host; if (fdesc_str == NULL) { i = 0; while (i < DBGMAXTRIES && listen(s = in_bind_ne(port+i, SOCK_STREAM), 5)) i++; if (i == DBGMAXTRIES) { fprintf(stderr, "Couldn't establish listen socket\n"); exit(1); } fflush(stdout); fprintf(stderr, "Waiting for connection from trace browser on port %d...\n", port+i); len=sizeof(addr); if ((fdesc = accept(s, &addr, &len)) == -1) { fprintf(stderr, "Couldn't accept connection\n"); exit(1); } close(s); } else { fdesc = atoi(fdesc_str); } return fdopen(fdesc, "rb+"); } /** * Send a string to the browser. **/ void ToBrowser(FILE *sock, char *s) { int fn = fileno(sock); int n = write(fn, s, strlen(s)); if (n != strlen(s)) { fprintf(stderr, "Couldn't write %s, errno=%d\n", s, errno); perror("Error: "); exit(1); } /*fprintf(stderr,"%s",s); -- See what's happening on the socket */ } /** * Dump a source reference. * An SR contains a file name and a position in that file. * A zero pointer is a place-holder constructor for "no SR". **/ void dumpSRToBrowser(FILE *sock, FileOffset t) { static char str[256]; SrcRef *sr; sr = readSRAt(t); if (sr) { sprintf(str,"\"%s\" %d ",sr->srcname,(sr->line*10000+sr->column)); ToBrowser(sock, str); free(sr); } else { ToBrowser(sock, "* "); } } /* We have a TAp in hand, and just want to know whether it is an application * of case, if, or guard. Returns 0==no, value=yes. */ int checkForCaseIfGuard(FileOffset fo) { char c; FileOffset fn; Ident *id; freadAt(fo,&c,sizeof(char),1,HatFile); if (c!=0x0) { fprintf(stderr,"%s: expected a TAp descriptor at position 0x%x\n" ,"checkCaseIfGuard",fo); exit(1); } fread(&c,sizeof(char),1,HatFile); /* next char is arity */ /*fprintf(stderr,"checkForCaseIfGuard: got arity %d\n",c);*/ /*if (c!=2) return 0; which must be 2 */ (void)readFO(); /* skip parent trace */ fn = readFO(); /* get fn trace ptr */ freadAt(fn,&c,sizeof(char),1,HatFile); if (c!=TNm) return 0; /* only interested in a TNm */ (void)readFO(); /* skip parent trace */ fn = readFO(); /* get nmtype ptr */ freadAt(fn,&c,sizeof(char),1,HatFile); /* then nmtype tag */ /*fprintf(stderr,"checkForCase: got tag 0x%x\n",c);*/ switch (c&0x1f) { case NTCase: case NTIf: case NTGuard: return (c&0x1f); break; default: return 0; break; } } /* followTrace() takes a trace pointer and skips over any TInd or TSat * nodes, returning the outermost "real" trace. If it does encounter a * Sat or Indirection, the pbot and/or pind pointers are filled in as * extra return values. */ FileOffset followTrace(FileOffset fo, FileOffset *pbot, int *pind) { char c; int ref; FileOffset arg1, arg2; /* defaults */ *pbot = (FileOffset)0; /* the file pointer itself */ *pind = 0; /* a nodecache'd reference */ while (1) { if (!fo) return fo; /* when trace is Root */ freadAt(fo,&c,sizeof(char),1,HatFile); switch (c&8 ? c-8 : c) { /* clear no-APP bit if set */ case TInd: arg1=readFO(); /* fst of indirection */ arg2=readFO(); /* snd of indirection */ if (*pind == 0) { ref = abs(ncFind(fo)); if (ref == 0) ref = ncInsert(fo, FALSE); *pind = ref; /* pind points to indirection */ } fo = arg2; break; case TSatA: case TSatC: fo = readFO(); break; case TSatB: *pbot = fo; /* not sure about this line */ fo = readFO(); return fo; break; case TAp: case TNm: case THidden: return fo; break; default: fprintf(stderr, "followTrace: expected trace tag, got 0x%x).\n",c); exit(1); } } } /* dumpRefToBrowser() sends a nodecache reference for a trace to the browser. * If the nodecache doesn't already contain the trace, we insert it, but * the trace is /not/ sent yet, just the reference. This allows * recursive structures to be sent without problems. */ int storeRef (FileOffset fo) { if (fo==0) return 0; else { int ref = ncFind(fo); if (ref==0) return ncInsert(fo, FALSE); else /* if (ref<0) --- a redundant test?? CR/OC */ return abs(ref); } } void dumpRefToBrowser(FILE *sock, FileOffset fo) { int ref; static char str[16]; ref = storeRef(fo); sprintf(str, " %d ", ref); ToBrowser(sock, str); } void dumpOutputRefToBrowser(FILE *sock, FileOffset fo) { int ref; char c; static char str[16]; ref = storeRef(fo); freadAt(fo,&c,sizeof(char),1,HatFile); if (c==THidden) { fo = readFO(); ref = storeRef(fo); } sprintf(str, " %d ", ref); ToBrowser(sock, str); } /* Character lists should be globbed up into whole strings in the browser. * This routine sets the global variable charListInProgress to ensure that * we know we are in the middle of a list. */ static FileOffset charListInProgress = 0; void charListP(FileOffset fo) { FileOffset old=fo, bot=0, fn, arg1, arg2; int ind; char c; Ident *id; while (1) { freadAt(fo,&c,sizeof(char),1,HatFile); switch (c) { case TAp: fread(&c,sizeof(char),1,HatFile); /* get arity */ if (c!=2) return; /* must be 2 */ (void)readFO(); /* skip parent */ fn = readFO(); /* get fn trace */ arg1= readFO(); /* get arg1 trace */ arg2= readFO(); /* get arg2 trace */ fn = followTrace(fn, &bot, &ind); if (bot != 0) return; /* fn must be fully evaluated */ freadAt(fn,&c,sizeof(char),1,HatFile); if (c!=TNm) return; /* must be a TNm */ (void)readFO(); /* skip parent */ fn = readFO(); /* get NmType ptr */ id = readNmTypeAt(fn); if (strcmp(id->idname,":")) return; /* must be cons */ arg1 = followTrace(arg1, &bot, &ind); if (bot != 0) return; freadAt(arg1,&c,sizeof(char),1,HatFile); if (c!=TNm) return; /* must be a TNm */ (void) readFO(); /* skip parent */ arg1 = readFO(); /* get NmType ptr */ freadAt(arg1,&c,sizeof(char),1,HatFile); if ((c&0x1f)!=NTChar) return; /* must be a char */ old = fo; fo = followTrace(arg2, &bot, &ind); /* follow list tail */ if (bot != 0) return; break; case TNm: (void)readFO(); /* skip parent */ fn = readFO(); /* get NmType ptr */ id = readNmTypeAt(fn); if (!strcmp(id->idname,"[]")) /* must be nil */ charListInProgress = old; /* to set flag */ return; default: return; } } } void dumpNodeToBrowser(FILE *sock, int level, FileOffset fo) { char c; int ref, ind; FileOffset bot; static char str[128]; char *s; fo = followTrace(fo, &bot, &ind); if (!fo) { /* found Root */ fprintf(stderr, "found trace Root in dumpNodeToBrowser. Hmmm.\n"); ToBrowser(sock, "P "); return; } if (bot != 0) { /* found a bottom */ ref = ncFind(bot); if (ref > 0) { sprintf(&str[0], "(R %d %d %d)",ref,ind,storeRef(peekTrace(ncRef(ind)))); ToBrowser(sock, str); } else { ref = ncInsert(bot, TRUE); sprintf(&str[0], "(B %d ", abs(ref)); ToBrowser(sock, str); dumpRefToBrowser(sock, fo); dumpRefToBrowser(sock, peekTrace(fo)); ToBrowser(sock, ")"); } return; } ref = ncFind(fo); if ((charListInProgress == 0) && (--level < 0)) { ToBrowser(sock, "(D "); dumpRefToBrowser(sock, fo); ToBrowser(sock, ")"); return; } freadAt(fo,&c,sizeof(char),1,HatFile); switch (c) { case TAp: if (level == 0) { ToBrowser(sock, "(D "); dumpRefToBrowser(sock, fo); ToBrowser(sock, ")"); charListInProgress = 0; return; } if (ref > 0) { sprintf(str, "(R %d %d ", abs(ref), ind); ToBrowser(sock, str); if (ind==0) { FileOffset parent; fread(&c,sizeof(char),1,HatFile); /* skip arity */ parent = readFO(); /* get parent */ sprintf(str, "%d)", storeRef(peekTrace(parent))); } else sprintf(str, "%d)", storeRef(peekTrace(ncRef(ind)))); ToBrowser(sock, str); charListInProgress = 0; } else { int nmType=0, i=0; FileOffset parent, foExprs[20], foSR; fread(&c,sizeof(char),1,HatFile); /* get arity */ parent = readFO(); /* get parent */ for (i=0; i<=c; i++) { /* get fn + args */ foExprs[i] = readFO(); } foSR = readFO(); /* get SR ptr */ if (ref < 0) /* update sent bit */ ncHist(abs(ref)); /* in node cache */ else ref = ncInsert(fo, TRUE); nmType = checkForCaseIfGuard(fo); /*fprintf(stderr,"checkForCaseIfGuard: %s\n",(nmType?"yes":"no"));*/ if (nmType == 0) { /* Ordinary application */ if (!charListInProgress) { charListP(fo); } sprintf(str,"(A%s %d ",charListInProgress==0?"":"s",abs(ref)); if (charListInProgress == fo) charListInProgress = 0; /* We're done with the string */ ToBrowser(sock, str); dumpSRToBrowser(sock, foSR); ToBrowser(sock, "("); dumpNodeToBrowser(sock, level, foExprs[0]); for (i=1; i<=c; i++) { ToBrowser(sock, " "); dumpNodeToBrowser(sock, level, foExprs[i]); } ToBrowser(sock, ")"); dumpRefToBrowser(sock, parent); sprintf(str, " %d %d)",ind,ind ? storeRef(peekTrace(ncRef(ind))) : storeRef(peekTrace(parent))); ToBrowser(sock, str); } else { /* case/if/guard */ sprintf(str,"(C %d %d ",nmType==NTIf?0:nmType==NTCase?1:2,abs(ref)); ToBrowser(sock, str); dumpSRToBrowser(sock, foSR); dumpNodeToBrowser(sock, level, foExprs[1]); dumpNodeToBrowser(sock, level, parent); sprintf(str, " %d %d)",ind,ind ? storeRef(peekTrace(ncRef(ind))) : storeRef(peekTrace(parent))); ToBrowser(sock, str); } } break; case TNm: if (ref > 0) { sprintf(str, "(R %d %d ",ref,ind); ToBrowser(sock, str); if (ind==0) { FileOffset parent; parent = readFO(); sprintf(str, "%d)",storeRef(peekTrace(parent))); } else sprintf(str, "%d)",storeRef(peekTrace(ncRef(ind)))); ToBrowser(sock, str); } else { char *mod, *name; int defpos, pri; FileOffset parent, nmtype, sr; Ident *id; if (ref<0) ncHist(abs(ref)); else ref = ncInsert(fo, TRUE); parent = readFO(); nmtype = readFO(); sr = readFO(); sprintf(str, "(N %d ", abs(ref)); ToBrowser(sock, str); dumpSRToBrowser(sock, sr); id = readNmTypeAt(nmtype); sprintf(str, "%s", id->srcname); ToBrowser(sock, str); ToBrowser(sock, " "); if (id->idname[0] == '\0') ToBrowser(sock, "\"()\""); else ToBrowser(sock, id->idname); sprintf(str," %d %d %d ",id->isIdentifier, (id->defnline*10000+id->defncolumn),id->priority); ToBrowser(sock, str); free(id->idname); free(id->modname); free(id->srcname); free(id); dumpRefToBrowser(sock, parent); sprintf(str, " %d %d)", ind, ind ? storeRef(peekTrace(ncRef(ind))) : storeRef(peekTrace(parent))); ToBrowser(sock, str); } break; case THidden: ref = ncFind(fo); if (ref > 0) { sprintf(str, "(R %d %d %d)",ref,ind,storeRef(peekTrace(ncRef(ind)))); ToBrowser(sock, str); } else { ref = ncInsert(fo, TRUE); sprintf(str, "(H %d ", abs(ref)); ToBrowser(sock, str); fo = readFO(); dumpRefToBrowser(sock, fo); ToBrowser(sock, ")"); } break; default: fprintf(stderr, "dumpNodeToBrowser: unexpected 0x%x tag.\n",c); exit(1); break; } } void dumpOutputToBrowser(FILE *sock) { int ind; char c; FileOffset fo, bot; static char str[128]; c = fgetc(OutputFile); while (c!=-1) { fread(&fo,sizeof(FileOffset),1,BridgeFile); fo = ntohl(fo); fo = followTrace(fo, &bot, &ind); if (bot != 0) { fprintf(stderr, "dumpOutput: character trace is bottom!\n"); exit(1); } /* Transfer the character */ sprintf(str, " %d ", c); ToBrowser(sock, str); /* Transfer its trace */ dumpOutputRefToBrowser(sock, fo); c = fgetc(OutputFile); } } int isRoot(FileOffset fo) { return (fo==0); } void dumpTraceToBrowser(FILE *sock, FileOffset fo) { charListInProgress = 0; dumpNodeToBrowser(sock, DEFAULT_DEPTH, fo); ToBrowser(sock, "\n"); fflush(sock); } extern char *paths[]; /* Filled in by init routine */ extern char *prelude_path; /* ditto */ char *preldirs[] = {"Prelude", "PreludeIO", "PreludeList", "PreludeText", "Ratio", "System", "Text", "Array", "Char", "Complex", "Directory", "IO", "Interrupt", "Ix", "List", "LowT", "Maybe", "Monad", "NonStd", "PackedString", NULL}; void sendFile(FILE *sock, char *filename) { FILE *file = NULL; char *ch; char path[MAX_SYM_SIZE]; char s[MAX_LINE_SIZE]; int i; if ((ch = strchr(filename, '\n')) != NULL) *ch = '\0'; if ((ch = strchr(filename, '\r')) != NULL) *ch = '\0'; i = 0; while (file == NULL && paths[i] != NULL) { sprintf(&path[0], "%s/%s", paths[i], filename); file = fopen(path, "r"); i++; } i = 0; while (file == NULL && preldirs[i] != NULL) { sprintf(&path[0], "%s/%s/%s", prelude_path, preldirs[i], filename); file = fopen(path, "r"); i++; } if (file == NULL) { /* Better way of giving an error message here, please!!! */ sprintf(&path[0], "Couldn't open source file %s.\n", filename); ToBrowser(sock, path); ToBrowser(sock, "\n"); return; } while (fgets(s, MAX_LINE_SIZE, file) != NULL) ToBrowser(sock, s); ToBrowser(sock, "\n\n"); } int getline(FILE *sock, char *s) { if (fgets(s, MAX_LINE_SIZE, sock) == NULL) { return FALSE; } return TRUE; } /* loop() is the main read-eval-print loop for "commands" from the browser. */ void loop(FILE *sock, FileOffset fo) { int refnr; char c; FileOffset next; char str[MAX_LINE_SIZE]; int done = FALSE; while (!done) { if (!getline(sock, str)) break; c = str[0]; if (!getline(sock, &str[0])) done = TRUE; switch (c) { case 'G': sscanf(str, "%d", &refnr); next = ncRef(refnr); dumpTraceToBrowser(sock, next); break; case 'I': sscanf(str, "%d", &refnr); next = ncRef(refnr); /* Must be an indirection!!! */ freadAt(next,&c,sizeof(char),1,HatFile); if (c != TInd) { fprintf(stderr, "Expected TInd, got 0x%x\n",c); exit(1); } next = readFO(); dumpTraceToBrowser(sock, next); break; case 'R': sscanf(str, "%d", &refnr); next = ncRef(refnr); dumpNodeToBrowser(sock, DEFAULT_DEPTH, next); ToBrowser(sock, "\n"); break; case 'N': if (!next) { /* Root */ ToBrowser(sock, "_\n"); } else { dumpNodeToBrowser(sock, DEFAULT_DEPTH, next); ToBrowser(sock, "\n"); } break; case 'F': sendFile(sock, str); break; case 'L':{ static char strout[16]; sscanf(str, "%d", &refnr); next = ncRef(refnr); sprintf(strout,"%d\n",next); ToBrowser(sock, strout); break; } } } }