/* * File: rmisc.c * Contents: deref, eq, [gcvt], getvar, hash, outimage, [qsort], * qtos, trace, pushact, popact, topact, [dumpact], putpos, putsub, putint, * findline, findipc, findfile, [llqsort], doimage, prescan, getimage * printable. * * Integer overflow checking. */ #ifdef IconAlloc #define free mem_free #endif /* IconAlloc */ #include #include "..\h\config.h" #include "..\h\rt.h" #include "rproto.h" #include /* * Prototypes. */ hidden novalue listimage Params((FILE *f,struct b_list *lp, int restrict)); hidden novalue printimage Params((FILE *f,int c,int q)); #ifdef IconQsort hidden novalue qswap Params((char *a, char *b, int w)); #endif /* IconQsort */ hidden novalue showlevel Params((int n)); hidden novalue showline Params((char *f,int l)); /* * deref - dereference a descriptor. */ int deref(dp) dptr dp; { register uword hn; register union block *bp; struct descrip v, tref; union block *tbl; if (!Tvar(*dp)) /* * An ordinary variable is being dereferenced; just replace * *dp with the descriptor *dp is pointing to. */ *dp = *(dptr)((word *)VarLoc(*dp) + Offset(*dp)); else switch (Type(*dp)) { case T_Tvsubs: /* * A substring trapped variable is being dereferenced. * Point bp to the trapped variable block and v to * the string. */ bp = TvarLoc(*dp); v = bp->tvsubs.ssvar; if (DeRef(v) == Error) return Error; if (!Qual(v)) RetError(103, v); if (bp->tvsubs.sspos + bp->tvsubs.sslen - 1 > StrLen(v)) RetError(-205, nulldesc); /* * Make a descriptor for the substring by getting the * length and pointing into the string. */ StrLen(*dp) = bp->tvsubs.sslen; StrLoc(*dp) = StrLoc(v) + bp->tvsubs.sspos - 1; break; case T_Tvtbl: if (BlkLoc(*dp)->tvtbl.title == T_Telem) { /* * The tvtbl has been converted to a telem and is * in the table. Replace the descriptor pointed to * by dp with the value of the element. */ *dp = BlkLoc(*dp)->telem.tval; break; } /* * Point tbl to the table header block, tref to the * subscripting value, and bp to the appropriate * chain. Point dp to a descriptor for the default * value in case the value referenced by the subscript * is not in the table. */ tbl = BlkLoc(*dp)->tvtbl.clink; tref = BlkLoc(*dp)->tvtbl.tref; hn = BlkLoc(*dp)->tvtbl.hashnum; *dp = tbl->table.defvalue; bp = *(hchain((union block *)tbl, hn)); /* * Traverse the element chain looking for the subscript * value. If found, replace the descriptor pointed to * by dp with the value of the element. */ while (bp != NULL && bp->telem.hashnum <= hn) { if ((bp->telem.hashnum == hn) && (equiv(&bp->telem.tref, &tref))) { *dp = bp->telem.tval; break; } bp = bp->telem.clink; } break; case T_Tvkywd: bp = TvarLoc(*dp); *dp = bp->tvkywd.kyval; break; default: syserr("deref: illegal trapped variable"); } #ifdef DeBugIconx if (Var(*dp)) syserr("deref: didn't get dereferenced"); #endif /* DeBugIconx */ return Success; } #ifdef IconGcvt /* * gcvt - Convert number to a string in buf. If possible, ndigit * significant digits are produced, otherwise a form with an exponent is used. * * The name is actually #defined as "icon_gcvt" in config.h. */ char *gcvt(number, ndigit, buf) double number; int ndigit; char *buf; { int sign, decpt; register char *p1, *p2; register i; p1 = ecvt(number, ndigit, &decpt, &sign); p2 = buf; if (sign) *p2++ = '-'; for (i=ndigit-1; i>0 && p1[i]=='0'; i--) ndigit--; if (decpt >= 0 && decpt-ndigit > 4 || decpt < 0 && decpt < -3) { /* use E-style */ decpt--; *p2++ = *p1++; *p2++ = '.'; for (i=1; i 0) *p2++ = decpt/100 + '0'; if (decpt/10 > 0) *p2++ = (decpt%100)/10 + '0'; *p2++ = decpt%10 + '0'; } else { if (decpt<=0) { /* if (*p1!='0') */ *p2++ = '0'; *p2++ = '.'; while (decpt<0) { decpt++; *p2++ = '0'; } } for (i=1; i<=ndigit; i++) { *p2++ = *p1++; if (i==decpt) *p2++ = '.'; } if (ndigitdword = D_Tvkywd; VarLoc(*vp) = (dptr)&tvky_err; return Success; } else if (strcmp(s,"&pos") == 0) { vp->dword = D_Tvkywd; VarLoc(*vp) = (dptr)&tvky_pos; return Success; } else if (strcmp(s,"&random") == 0) { vp->dword = D_Tvkywd; VarLoc(*vp) = (dptr)&tvky_ran; return Success; } else if (strcmp(s,"&subject") == 0) { vp->dword = D_Tvkywd; VarLoc(*vp) = (dptr)&tvky_sub; return Success; } else if (strcmp(s,"&trace") == 0) { vp->dword = D_Tvkywd; VarLoc(*vp) = (dptr)&tvky_trc; return Success; } else return Failure; } /* * Look for the variable with the name of the local identifiers, * parameters, and static names in each Icon procedure frame on the stack. * If not found among the locals, check the global variables. * If a variable with name is found, variable() returns a variable * descriptor that points to the corresponding value descriptor. * If no such variable exits, it fails. */ /* * If no procedure has been called (as can happen with icon_call(), * dont' try to find local identifier. */ if (pfp == NULL) goto glbvars; dp = argp; bp = (struct b_proc *)BlkLoc(*dp); /* get address of procedure block */ np = bp->lnames; /* Check the formal parameter names. */ for (i = abs((int)bp->nparam); i > 0; i--) { dp++; if (strcmp(s,StrLoc(*np)) == 0) { vp->dword = D_Var; VarLoc(*vp) = (dptr)dp; return Success; } np++; } dp = &fp->pf_locals[0]; for (i = (int)bp->ndynam; i > 0; i--) { /* Check the local dynamic names. */ if (strcmp(s,StrLoc(*np)) == 0) { vp->dword = D_Var; VarLoc(*vp) = (dptr)dp; return Success; } np++; dp++; } dp = &statics[bp->fstatic]; /* Check the local static names. */ for (i = (int)bp->nstatic; i > 0; i--) { if (strcmp(s,StrLoc(*np)) == 0) { vp->dword = D_Var; VarLoc(*vp) = (dptr)dp; return Success; } np++; dp++; } glbvars: dp = globals; /* Check the global variable names. */ np = gnames; while (dp < eglobals) { if (strcmp(s,StrLoc(*np)) == 0) { vp->dword = D_Var; VarLoc(*vp) = (dptr)(dp); return Success; } np++; dp++; } return Failure; } /* * hash - compute hash value of arbitrary object for table and set accessing. */ uword hash(dp) dptr dp; { register char *s; register uword i; register word j, n; register int *bitarr; double r; if (Qual(*dp)) { /* * Compute the hash value for the string based on a scaled sum * of its first ten characters, plus its length. */ i = 0; s = StrLoc(*dp); j = n = StrLen(*dp); if (j > 10) /* limit scan to first ten characters */ j = 10; while (j-- > 0) { i += *s++ & 0xFF; /* add unsigned version of next char */ i *= 39; /* scale total by a nice prime number */ } i += n; /* add the (untruncated) string length */ } else { switch (Type(*dp)) { /* * The hash value of an integer is itself times eight times the golden * ratio. We do this calculation in fixed point. We don't just use * the integer itself, for that would give bad results with sets * having entries that are multiples of a power of two. */ case T_Integer: i = (13255 * (uword)IntVal(*dp)) >> 10; break; #ifdef LargeInts /* * The hash value of a bignum is based on its length and its * most and least significant digits. */ case T_Bignum: { struct b_bignum *b = &BlkLoc(*dp)->bignumblk; i = ((b->lsd - b->msd) << 16) ^ (b->digits[b->msd] << 8) ^ b->digits[b->lsd]; } break; #endif /* LargeInts */ /* * The hash value of a real number is itself times a constant, * converted to an unsigned integer. The intent is to scramble * the bits well, in the case of integral values, and to scale up * fractional values so they don't all land in the same bin. * The constant below is 32749 / 29, the quotient of two primes, * and was observed to work well in empirical testing. */ case T_Real: GetReal(dp,r); i = r * 1129.27586206896558; break; /* * The hash value of a cset is based on a convoluted combination * of all its bits. */ case T_Cset: i = 0; bitarr = BlkLoc(*dp)->cset.bits + CsetSize - 1; for (j = 0; j < CsetSize; j++) { i += *bitarr--; i *= 37; /* better distribution */ } i %= 1048583; /* scramble the bits */ break; /* * The hash value of a list, set, table, or record is its id, * hashed like an integer. */ case T_List: i = (13255 * BlkLoc(*dp)->list.id) >> 10; break; case T_Set: i = (13255 * BlkLoc(*dp)->set.id) >> 10; break; case T_Table: i = (13255 * BlkLoc(*dp)->table.id) >> 10; break; case T_Record: i = (13255 * BlkLoc(*dp)->record.id) >> 10; break; default: /* * For other types, use the type code as the hash * value. */ i = Type(*dp); break; } } return i; } #define StringLimit 16 /* limit on length of imaged string */ #define ListLimit 6 /* limit on list items in image */ /* * outimage - print image of *dp on file f. If restrict is nonzero, * fields of records will not be imaged. */ novalue outimage(f, dp, restrict) FILE *f; dptr dp; int restrict; { register word i, j; register char *s; register union block *bp, *vp; char *type; FILE *fd; struct descrip q; extern char *blkname[]; double rresult; outimg: if (Qual(*dp)) { /* * *dp is a string qualifier. Print StringLimit characters of it * using printimage and denote the presence of additional characters * by terminating the string with "...". */ i = StrLen(*dp); s = StrLoc(*dp); j = Min(i, StringLimit); putc('"', f); while (j-- > 0) printimage(f, *s++, '"'); if (i > StringLimit) fprintf(f, "..."); putc('"', f); return; } if (Var(*dp) && !Tvar(*dp)) { /* * *d is a variable. Print "variable =", dereference it, and * call outimage to handle the value. */ fprintf(f, "(variable = "); dp = (dptr)((word *)VarLoc(*dp) + Offset(*dp)); outimage(f, dp, restrict); putc(')', f); return; } switch (Type(*dp)) { case T_Null: fprintf(f, "&null"); return; case T_Integer: fprintf(f, "%ld", (long)IntVal(*dp)); return; #ifdef LargeInts case T_Bignum: bigprint(f, dp); return; #endif /* LargeInts */ case T_Real: { char s[30]; struct descrip rd; GetReal(dp,rresult); rtos(rresult, &rd, s); fprintf(f, "%s", StrLoc(rd)); return; } case T_Cset: /* * Check for distinguished csets by looking at the address of * of the object to image. If one is found, print its name. */ if ((char *)BlkLoc(*dp) == (char *)&k_ascii) { fprintf(f, "&ascii"); return; } else if ((char *)BlkLoc(*dp) == (char *)&k_cset) { fprintf(f, "&cset"); return; } else if ((char *)BlkLoc(*dp) == (char *)&k_digits) { fprintf(f, "&digits"); return; } else if ((char *)BlkLoc(*dp) == (char *)&k_lcase) { fprintf(f, "&lcase"); return; } else if ((char *)BlkLoc(*dp) == (char *)&k_letters) { fprintf(f, "&letters"); return; } else if ((char *)BlkLoc(*dp) == (char *)&k_ucase) { fprintf(f, "&ucase"); return; } /* * Use printimage to print each character in the cset. Follow * with "..." if the cset contains more than StringLimit * characters. */ putc('\'', f); j = StringLimit; for (i = 0; i < 256; i++) { if (Testb(i, BlkLoc(*dp)->cset.bits)) { if (j-- <= 0) { fprintf(f, "..."); break; } printimage(f, (int)FromAscii(i), '\''); } } putc('\'', f); return; case T_File: /* * Check for distinguished files by looking at the address of * of the object to image. If one is found, print its name. */ if ((fd = BlkLoc(*dp)->file.fd) == stdin) fprintf(f, "&input"); else if (fd == stdout) fprintf(f, "&output"); else if (fd == stderr) fprintf(f, "&errout"); else { /* * The file isn't a special one, just print "file(name)". */ i = StrLen(BlkLoc(*dp)->file.fname); s = StrLoc(BlkLoc(*dp)->file.fname); fprintf(f, "file("); while (i-- > 0) printimage(f, *s++, '\0'); putc(')', f); } return; case T_Proc: /* * Produce one of: * "procedure name" * "function name" * "record constructor name" * * Note that the number of dynamic locals is used to determine * what type of "procedure" is at hand. */ i = StrLen(BlkLoc(*dp)->proc.pname); s = StrLoc(BlkLoc(*dp)->proc.pname); switch ((int)BlkLoc(*dp)->proc.ndynam) { default: type = "procedure"; break; case -1: type = "function"; break; case -2: type = "record constructor"; break; } fprintf(f, "%s ", type); while (i-- > 0) printimage(f, *s++, '\0'); return; case T_List: /* * listimage does the work for lists. */ listimage(f, (struct b_list *)BlkLoc(*dp), restrict); return; case T_Table: /* * Print "table_m(n)" where n is the size of the table. */ fprintf(f, "table_%ld(%ld)", (long)BlkLoc(*dp)->table.id, (long)BlkLoc(*dp)->table.size); return; case T_Set: /* * print "set_m(n)" where n is the cardinality of the set */ fprintf(f,"set_%ld(%ld)",(long)BlkLoc(*dp)->set.id, (long)BlkLoc(*dp)->set.size); return; case T_Record: /* * If restrict is nonzero, print "record(n)" where n is the * number of fields in the record. If restrict is zero, print * the image of each field instead of the number of fields. */ bp = BlkLoc(*dp); i = StrLen(bp->record.recdesc->proc.recname); s = StrLoc(bp->record.recdesc->proc.recname); fprintf(f, "record "); while (i-- > 0) printimage(f, *s++, '\0'); fprintf(f, "_%ld", bp->record.id); j = bp->record.recdesc->proc.nfields; if (j <= 0) fprintf(f, "()"); else if (restrict > 0) fprintf(f, "(%ld)", (long)j); else { putc('(', f); i = 0; for (;;) { outimage(f, &bp->record.fields[i], restrict+1); if (++i >= j) break; putc(',', f); } putc(')', f); } return; case T_Tvsubs: /* * Produce "v[i+:j] = value" where v is the image of the variable * containing the substring, i is starting position of the substring * j is the length, and value is the string v[i+:j]. If the length * (j) is one, just produce "v[i] = value". */ bp = BlkLoc(*dp); dp = VarLoc(bp->tvsubs.ssvar); if (!Tvar(bp->tvsubs.ssvar)) dp = (dptr)((word *)dp + Offset(bp->tvsubs.ssvar)); if (dp == (dptr)&tvky_sub) fprintf(f, "&subject"); else outimage(f, dp, restrict); if (bp->tvsubs.sslen == 1) fprintf(f, "[%ld]", (long)bp->tvsubs.sspos); else fprintf(f, "[%ld+:%ld]", (long)bp->tvsubs.sspos, (long)bp->tvsubs.sslen); if (dp == (dptr)&tvky_sub) { vp = BlkLoc(bp->tvsubs.ssvar); if (bp->tvsubs.sspos + bp->tvsubs.sslen - 1 > StrLen(vp->tvkywd.kyval)) return; StrLen(q) = bp->tvsubs.sslen; StrLoc(q) = StrLoc(vp->tvkywd.kyval) + bp->tvsubs.sspos - 1; fprintf(f, " = "); dp = &q; goto outimg; } else if (Qual(*dp)) { if (bp->tvsubs.sspos + bp->tvsubs.sslen - 1 > StrLen(*dp)) return; StrLen(q) = bp->tvsubs.sslen; StrLoc(q) = StrLoc(*dp) + bp->tvsubs.sspos - 1; fprintf(f, " = "); dp = &q; goto outimg; } return; case T_Tvtbl: bp = BlkLoc(*dp); /* * It is possible that the descriptor that thinks it is pointing * to a tabel-element trapped variable may actually be pointing * at a table element block which had been converted from a * trapped variable. Check for this first and if it is a table * element block, produce the outimage of its value. */ if (bp->tvtbl.title == T_Telem) { outimage(f, &bp->tvtbl.tval, restrict); return; } /* * It really was a tvtbl - produce "t[s]" where t is the image of * the table containing the element and s is the image of the * subscript. */ else { dp->dword = D_Table; BlkLoc(*dp) = bp->tvtbl.clink; outimage(f, dp, restrict); putc('[', f); outimage(f, &bp->tvtbl.tref, restrict); putc(']', f); return; } case T_Tvkywd: bp = BlkLoc(*dp); i = StrLen(bp->tvkywd.kyname); s = StrLoc(bp->tvkywd.kyname); while (i-- > 0) putc(*s++, f); fprintf(f, " = "); outimage(f, &bp->tvkywd.kyval, restrict); return; case T_Coexpr: fprintf(f, "co-expression_%ld(%ld)", (long)((struct b_coexpr *)BlkLoc(*dp))->id, (long)((struct b_coexpr *)BlkLoc(*dp))->size); return; case T_External: fprintf(f, "external(%d)",((struct b_external *)BlkLoc(*dp))->blksize); return; default: if (Type(*dp) <= MaxType) fprintf(f, "%s", blkname[Type(*dp)]); else syserr("outimage: unknown type"); } } /* * printimage - print character c on file f using escape conventions * if c is unprintable, '\', or equal to q. */ static novalue printimage(f, c, q) FILE *f; int c, q; { if (printable(c)) { /* * c is printable, but special case ", ', and \. */ switch (c) { case '"': if (c != q) goto def; fprintf(f, "\\\""); return; case '\'': if (c != q) goto def; fprintf(f, "\\'"); return; case '\\': fprintf(f, "\\\\"); return; default: def: putc(c, f); return; } } /* * c is some sort of unprintable character. If it one of the common * ones, produce a special representation for it, otherwise, produce * its hex value. */ switch (c) { case '\b': /* backspace */ fprintf(f, "\\b"); return; #if !EBCDIC case '\177': /* delete */ #else /* !EBCDIC */ case '\x07': #endif /* !EBCDIC */ fprintf(f, "\\d"); return; #if !EBCDIC case '\33': /* escape */ #else /* !EBCDIC */ case '\x27': #endif /* !EBCDIC */ fprintf(f, "\\e"); return; case '\f': /* form feed */ fprintf(f, "\\f"); return; case LineFeed: /* new line */ fprintf(f, "\\n"); return; #if EBCDIC == 1 case '\x25': /* EBCDIC line feed */ fprintf(f, "\\l"); return; #endif /* EBCDIC == 1 */ case CarriageReturn: /* carriage return */ fprintf(f, "\\r"); return; case '\t': /* horizontal tab */ fprintf(f, "\\t"); return; case '\13': /* vertical tab */ fprintf(f, "\\v"); return; default: /* hex escape sequence */ fprintf(f, "\\x%02x", ToAscii(c & 0xff)); return; } } /* * listimage - print an image of a list. */ static novalue listimage(f, lp, restrict) FILE *f; struct b_list *lp; int restrict; { register word i, j; register struct b_lelem *bp; word size, count; bp = (struct b_lelem *) lp->listhead; size = lp->size; if (restrict > 0 && size > 0) { /* * Just give indication of size if the list isn't empty. */ fprintf(f, "list_%ld(%ld)", (long)lp->id, (long)size); return; } /* * Print [e1,...,en] on f. If more than ListLimit elements are in the * list, produce the first ListLimit/2 elements, an ellipsis, and the * last ListLimit elements. */ fprintf(f, "list_%ld = [", (long)lp->id); count = 1; i = 0; if (size > 0) { for (;;) { if (++i > bp->nused) { i = 1; bp = (struct b_lelem *) bp->listnext; } if (count <= ListLimit/2 || count > size - ListLimit/2) { j = bp->first + i - 1; if (j >= bp->nslots) j -= bp->nslots; outimage(f, &bp->lslots[j], restrict+1); if (count >= size) break; putc(',', f); } else if (count == ListLimit/2 + 1) fprintf(f, "...,"); count++; } } putc(']', f); } #ifdef IconQsort /* qsort(base,nel,width,compar) - quicksort routine * * A Unix-compatible public domain quicksort. * Based on Bentley, CACM 28,7 (July, 1985), p. 675. */ novalue qsort(base, nel, w, compar) char *base; int nel, w; int (*compar)(); { int i, lastlow; if (nel < 2) return; qswap(base, base + w * (rand() % nel), w); lastlow = 0; for (i = 1; i < nel; i++) if ((*compar) (base + w * i, base) < 0) qswap(base + w * i, base + w * (++lastlow), w); qswap(base, base + w * lastlow, w); qsort(base, lastlow, w, compar); qsort(base + w * (lastlow+1), nel-lastlow-1, w, compar); } static novalue qswap(a, b, w) /* swap *a and *b of width w for qsort*/ char *a, *b; int w; { register t; while (w--) { t = *a; *a++ = *b; *b++ = t; } } #endif /* IconQsort */ /* * qtos - convert a qualified string named by *dp to a C-style string. * Put the C-style string in sbuf if it will fit, otherwise put it * in the string region. */ int qtos(dp, sbuf) dptr dp; char *sbuf; { register word slen; register char *c; c = StrLoc(*dp); slen = StrLen(*dp)++; if (slen >= MaxCvtLen) { if (strreq(slen + 1) == Error) return Error; if (c + slen != strfree) StrLoc(*dp) = alcstr(c, slen); alcstr("",(word)1); } else { StrLoc(*dp) = sbuf; for ( ; slen > 0; slen--) *sbuf++ = *c++; *sbuf = '\0'; } return Success; } /* * ctrace - procedure named s is being called with nargs arguments, the first * of which is at arg; produce a trace message. */ novalue ctrace(dp, nargs, arg) dptr dp; int nargs; dptr arg; { showline(findfile(ipc.opnd), findline(ipc.opnd)); showlevel(k_level); putstr(stderr, dp); putc('(', stderr); while (nargs--) { outimage(stderr, arg++, 0); if (nargs) putc(',', stderr); } putc(')', stderr); putc('\n', stderr); fflush(stderr); } /* * rtrace - procedure named s is returning *rval; produce a trace message. */ novalue rtrace(dp, rval) dptr dp; dptr rval; { inst t_ipc; /* * Compute the ipc of the return instruction. */ t_ipc.op = ipc.op - 1; showline(findfile(t_ipc.opnd), findline(t_ipc.opnd)); showlevel(k_level); putstr(stderr, dp); fprintf(stderr, " returned "); outimage(stderr, rval, 0); putc('\n', stderr); fflush(stderr); } /* * failtrace - procedure named s is failing; produce a trace message. */ novalue failtrace(dp) dptr dp; { inst t_ipc; /* * Compute the ipc of the fail instruction. */ t_ipc.op = ipc.op - 1; showline(findfile(t_ipc.opnd), findline(t_ipc.opnd)); showlevel(k_level); putstr(stderr, dp); fprintf(stderr, " failed"); putc('\n', stderr); fflush(stderr); } /* * strace - procedure named s is suspending *rval; produce a trace message. */ novalue strace(dp, rval) dptr dp; dptr rval; { inst t_ipc; /* * Compute the ipc of the suspend instruction. */ t_ipc.op = ipc.op - 1; showline(findfile(t_ipc.opnd), findline(t_ipc.opnd)); showlevel(k_level); putstr(stderr, dp); fprintf(stderr, " suspended "); outimage(stderr, rval, 0); putc('\n', stderr); fflush(stderr); } /* * atrace - procedure named s is being resumed; produce a trace message. */ novalue atrace(dp) dptr dp; { inst t_ipc; /* * Compute the ipc of the instruction causing resumption. */ t_ipc.op = ipc.op - 1; showline(findfile(t_ipc.opnd), findline(t_ipc.opnd)); showlevel(k_level); putstr(stderr, dp); fprintf(stderr, " resumed"); putc('\n', stderr); fflush(stderr); } #ifdef Coexpr /* * coacttrace -- co-expression is being activated; produce a trace message. */ novalue coacttrace(ccp, ncp) struct b_coexpr *ccp; struct b_coexpr *ncp; { struct b_proc *bp; inst t_ipc; bp = (struct b_proc *)BlkLoc(*argp); /* * Compute the ipc of the activation instruction. */ t_ipc.op = ipc.op - 1; showline(findfile(t_ipc.opnd), findline(t_ipc.opnd)); showlevel(k_level); putstr(stderr, &(bp->pname)); fprintf(stderr,"; co-expression_%ld : ", (long)ccp->id); outimage(stderr, (dptr)(sp - 3), 0); fprintf(stderr," @ co-expression_%ld\n", (long)ncp->id); fflush(stderr); } /* * corettrace -- return from co-expression; produce a trace message. */ novalue corettrace(ccp, ncp) struct b_coexpr *ccp; struct b_coexpr *ncp; { struct b_proc *bp; inst t_ipc; bp = (struct b_proc *)BlkLoc(*argp); /* * Compute the ipc of the coret instruction. */ t_ipc.op = ipc.op - 1; showline(findfile(t_ipc.opnd), findline(t_ipc.opnd)); showlevel(k_level); putstr(stderr, &(bp->pname)); fprintf(stderr,"; co-expression_%ld returned ", (long)ccp->id); outimage(stderr, (dptr)(&ncp->es_sp[-3]), 0); fprintf(stderr," to co-expression_%ld\n", (long)ncp->id); fflush(stderr); } /* * cofailtrace -- failure return from co-expression; produce a trace message. */ novalue cofailtrace(ccp, ncp) struct b_coexpr *ccp; struct b_coexpr *ncp; { struct b_proc *bp; inst t_ipc; bp = (struct b_proc *)BlkLoc(*argp); /* * Compute the ipc of the cofail instruction. */ t_ipc.op = ipc.op - 1; showline(findfile(t_ipc.opnd), findline(t_ipc.opnd)); showlevel(k_level); putstr(stderr, &(bp->pname)); fprintf(stderr,"; co-epression_%ld failed to co-expression_%ld\n", (long)ccp->id, (long)ncp->id); fflush(stderr); } #endif /* Coexpr */ /* * showline - print file and line number information. */ static novalue showline(f, l) char *f; int l; { int i; i = (int)strlen(f); #if MVS while (i > 22) { #else /* MVS */ while (i > 13) { #endif /* MVS */ f++; i--; } if (l > 0) #if MVS fprintf(stderr, "%-22s: %4d ",f, l); else fprintf(stderr, " : "); #else /* MVS */ fprintf(stderr, "%-13s: %4d ",f, l); else fprintf(stderr, " : "); #endif /* MVS */ } /* * showlevel - print "| " n times. */ static novalue showlevel(n) register int n; { while (n-- > 0) { putc('|', stderr); putc(' ', stderr); } } /* * putpos - assign value to &pos */ int putpos(dp,bp) dptr dp; struct b_tvkywd *bp; { #if MACINTOSH && MPW /* #pragma unused(bp) */ #endif /* MACINTOSH && MPW */ register word l1; switch (cvint(dp)) { case T_Integer: l1 = cvpos(IntVal(*dp), StrLen(k_subject)); if (l1 == CvtFail) return Failure; k_pos = l1; return Success; default: RetError(101, *dp); } } /* * putsub - assign value to &subject */ int putsub(dp,bp) dptr dp; struct b_tvkywd *bp; { #if MACINTOSH && MPW /* #pragma unused(bp) */ #endif /* MACINTOSH && MPW */ char sbuf[MaxCvtLen]; switch (cvstr(dp, sbuf)) { case Cvt: if (strreq(StrLen(*dp)) == Error) return Error; StrLoc(*dp) = alcstr(StrLoc(*dp), StrLen(*dp)); /* no break */ case NoCvt: k_subject = *dp; k_pos = 1; return Success; default: RetError(103, *dp); } } /* * putint - assign integer value to keyword */ int putint(dp,bp) dptr dp; struct b_tvkywd *bp; { switch (cvint(dp)) { case T_Integer: IntVal(bp->kyval) = IntVal(*dp); return Success; default: RetError(101, *dp); } } #ifdef Coexpr /* * pushact - push actvtr on the activator stack of ce */ int pushact(ce, actvtr) struct b_coexpr *ce, *actvtr; { struct astkblk *abp = ce->es_actstk, *nabp; struct actrec *arp; /* * If the last activator is the same as this one, just increment * its count. */ if (abp->nactivators > 0) { arp = &abp->arec[abp->nactivators - 1]; if (arp->activator == actvtr) { arp->acount++; return Success; } } /* * This activator is different from the last one. Push this activator * on the stack, possibly adding another block. */ if (abp->nactivators + 1 > ActStkBlkEnts) { nabp = alcactiv(); if (nabp == NULL) return Error; nabp->astk_nxt = abp; abp = nabp; } abp->nactivators++; arp = &abp->arec[abp->nactivators - 1]; arp->acount = 1; arp->activator = actvtr; ce->es_actstk = abp; return Success; } /* * popact - pop the most recent activator from the activator stack of ce * and return it. */ struct b_coexpr *popact(ce) struct b_coexpr *ce; { struct astkblk *abp = ce->es_actstk, *oabp; struct actrec *arp; struct b_coexpr *actvtr; /* * If the current stack block is empty, pop it. */ if (abp->nactivators == 0) { oabp = abp; abp = abp->astk_nxt; free((pointer)oabp); } if (abp == NULL || abp->nactivators == 0) syserr("empty activator stack\n"); /* * Find the activation record for the most recent co-expression. * Decrement the activation count and if it is zero, pop that * activation record and decrement the count of activators. */ arp = &abp->arec[abp->nactivators - 1]; actvtr = arp->activator; if (--arp->acount == 0) abp->nactivators--; ce->es_actstk = abp; return actvtr; } /* * topact - return the most recent activator of ce. */ struct b_coexpr *topact(ce) struct b_coexpr *ce; { struct astkblk *abp = ce->es_actstk; if (abp->nactivators == 0) abp = abp->astk_nxt; return abp->arec[abp->nactivators-1].activator; } #ifdef DeBugIconx /* * dumpact - dump an activator stack */ novalue dumpact(ce) struct b_coexpr *ce; { struct astkblk *abp = ce->es_actstk; struct actrec *arp; int i; if (abp) fprintf(stderr, "Ce %ld ", (long)ce->id); while (abp) { fprintf(stderr, "--- Activation stack block (%x) --- nact = %d\n", abp, abp->nactivators); for (i = abp->nactivators; i >= 1; i--) { arp = &abp->arec[i-1]; /*for (j = 1; j <= arp->acount; j++)*/ fprintf(stderr, "co-expression_%ld(%d)\n", (long)(arp->activator->id), arp->acount); } abp = abp->astk_nxt; } } #endif /* DeBugIconx */ #endif /* Coexpr */ /* * findline - find the source line number associated with the ipc */ int findline(ipc) word *ipc; { uword ipc_offset; uword size; struct ipc_line *base; extern struct ipc_line *ilines, *elines; extern word *records; static two = 2; /* some compilers generate bad code for division by a constant that is a power of two ... */ if (!InRange(code,ipc,records)) return 0; ipc_offset = DiffPtrs((char *)ipc,(char *)code); base = ilines; size = DiffPtrs((char *)elines,(char *)ilines) / sizeof(struct ipc_line *); while (size > 1) { if (ipc_offset >= base[size / two].ipc) { base = &base[size / two]; size -= size / two; } else size = size / two; } return (int)base->line; } /* * findipc - find the first ipc associated with a source-code line number. */ int findipc(line) int line; { uword size; struct ipc_line *base; extern struct ipc_line *ilines, *elines; static two = 2; /* some compilers generate bad code for division by a constant that is a power of two ... */ base = ilines; size = DiffPtrs((char *)elines,(char *)ilines) / sizeof(struct ipc_line *); while (size > 1) { if (line >= base[size / two].line) { base = &base[size / two]; size -= size / two; } else size = size / two; } return base->ipc; } /* * findfile - find source file name associated with the ipc */ char *findfile(ipc) word *ipc; { uword ipc_offset; struct ipc_fname *p; extern struct ipc_fname *filenms, *efilenms; extern word *records; extern char *strcons; if (!InRange(code,ipc,records)) return "?"; ipc_offset = DiffPtrs((char *)ipc,(char *)code); for (p = efilenms - 1; p >= filenms; p--) if (ipc_offset >= p->ipc) return strcons + p->fname; fprintf(stderr,"bad ipc/file name table"); fflush(stderr); c_exit(ErrorExit); } #if IntBits == 16 /* Shell sort with some enhancements from Knuth.. */ novalue llqsort(base, nel, width, cmp ) char *base; int nel; int width; int (*cmp)(); { register long i, j; long int gap; int k; char *p1, *p2, tmp; for( gap=1; gap <= nel; gap = 3*gap + 1 ) ; for( gap /= 3; gap > 0 ; gap /= 3 ) for( i = gap; i < nel; i++ ) for( j = i-gap; j >= 0 ; j -= gap ) { p1 = base + ( j * width); p2 = base + ((j+gap) * width); if( (*cmp)( p1, p2 ) <= 0 ) break; for( k = width; --k >= 0 ;) { tmp = *p1; *p1++ = *p2; *p2++ = tmp; } } } #endif /* IntBits == 16 */ /* * doimage(c,q) - allocate character c in string space, with escape * conventions if c is unprintable, '\', or equal to q. * Returns number of characters allocated. */ doimage(c, q) int c, q; { static char cbuf[5]; if (printable(c)) { /* * c is printable, but special case ", ', and \. */ switch (c) { case '"': if (c != q) goto def; alcstr("\\\"", (word)(2)); return 2; case '\'': if (c != q) goto def; alcstr("\\'", (word)(2)); return 2; case '\\': alcstr("\\\\", (word)(2)); return 2; default: def: cbuf[0] = c; alcstr(cbuf, (word)(1)); return 1; } } /* * c is some sort of unprintable character. If it is one of the common * ones, produce a special representation for it, otherwise, produce * its hex value. */ switch (c) { case '\b': /* backspace */ alcstr("\\b", (word)(2)); return 2; #if !EBCDIC case '\177': /* delete */ #else /* !EBCDIC */ case '\x07': /* delete */ #endif /* !EBCDIC */ alcstr("\\d", (word)(2)); return 2; #if !EBCDIC case '\33': /* escape */ #else /* !EBCDIC */ case '\x27': /* escape */ #endif /* !EBCDIC */ alcstr("\\e", (word)(2)); return 2; case '\f': /* form feed */ alcstr("\\f", (word)(2)); return 2; #if EBCDIC == 1 case '\x25': /* EBCDIC line feed */ alcstr("\\l", (word)(2)); return 2; #endif /* EBCDIC */ case LineFeed: /* new line */ alcstr("\\n", (word)(2)); return 2; case CarriageReturn: /* return */ alcstr("\\r", (word)(2)); return 2; case '\t': /* horizontal tab */ alcstr("\\t", (word)(2)); return 2; case '\13': /* vertical tab */ alcstr("\\v", (word)(2)); return 2; default: /* hex escape sequence */ sprintf(cbuf, "\\x%02x", ToAscii(c & 0xff)); alcstr(cbuf, (word)(4)); return 4; } } /* * prescan(d) - return upper bound on length of expanded string. Note * that the only time that prescan is wrong is when the string contains * one of the "special" unprintable characters, e.g. tab. */ word prescan(d) dptr d; { register word slen, len; register char *s, c; s = StrLoc(*d); len = 0; for (slen = StrLen(*d); slen > 0; slen--) #if EBCDIC #if SASC if (!isascii(c = (*s++)) || iscntrl(c)) #else /* SASC */ if (!isprint(c = (*s++))) #endif /* SASC */ #else /* EBCDIC */ if ((c = (*s++)) < ' ' || c >= 0177) #endif /* EBCDIC */ len += 4; else if (c == '"' || c == '\\' || c == '\'') len += 2; else len++; return len; } /* * getimage(dp1,dp2) - return string image of object dp1 in dp2. */ int getimage(dp1,dp2) dptr dp1, dp2; { register word len, outlen, rnlen; register char *s; register union block *bp; char *type; char sbuf[MaxCvtLen]; FILE *fd; if (Qual(*dp1)) { /* * Get some string space. The magic 2 is for the double quote at each * end of the resulting string. */ if (strreq(prescan(dp1) + 2) == Error) return Error; len = StrLen(*dp1); s = StrLoc(*dp1); outlen = 2; /* * Form the image by putting a quote in the string space, calling * doimage with each character in the string, and then putting * a quote at then end. Note that doimage directly writes into the * string space. (Hence the indentation.) This techinique is used * several times in this routine. */ StrLoc(*dp2) = alcstr("\"", (word)(1)); while (len-- > 0) outlen += doimage(*s++, '"'); alcstr("\"", (word)(1)); StrLen(*dp2) = outlen; return Success; } switch (Type(*dp1)) { case T_Null: StrLoc(*dp2) = "&null"; StrLen(*dp2) = 5; return Success; #ifdef LargeInts case T_Bignum: { word slen; word dlen; slen = (BlkLoc(*dp1)->bignumblk.lsd - BlkLoc(*dp1)->bignumblk.msd + 1); dlen = slen * NB * 0.3010299956639812; /* 1 / log2(10) */ if (dlen > MaxDigits) { sprintf(sbuf,"integer(~%ld)",dlen - 2); /* center estimage */ len = strlen(sbuf); if (strreq(len) == Error) return Error; StrLoc(*dp2) = alcstr(sbuf,strlen(sbuf)); StrLen(*dp2) = len; return Success; } } #endif /* LargeInts */ case T_Integer: case T_Real: /* * Form a string representing the number and allocate it. */ *dp2 = *dp1; /* don't clobber dp1 */ cvstr(dp2, sbuf); len = StrLen(*dp2); if (strreq(len) == Error) return Error; StrLoc(*dp2) = alcstr(StrLoc(*dp2), len); StrLen(*dp2) = len; return Success; case T_Cset: /* * Check for distinguished csets by looking at the address of * of the object to image. If one is found, make a string * naming it and return. */ if (BlkLoc(*dp1) == ((union block *)&k_ascii)) { StrLoc(*dp2) = "&ascii"; StrLen(*dp2) = 6; return Success; } else if (BlkLoc(*dp1) == ((union block *)&k_cset)) { StrLoc(*dp2) = "&cset"; StrLen(*dp2) = 5; return Success; } else if (BlkLoc(*dp1) == ((union block *)&k_digits)) { StrLoc(*dp2) = "&digits"; StrLen(*dp2) = 7; return Success; } else if (BlkLoc(*dp1) == ((union block *)&k_lcase)) { StrLoc(*dp2) = "&lcase"; StrLen(*dp2) = 6; return Success; } else if (BlkLoc(*dp1) == ((union block *)&k_letters)) { StrLoc(*dp2) = "&letters"; StrLen(*dp2) = 8; return Success; } else if (BlkLoc(*dp1) == ((union block *)&k_ucase)) { StrLoc(*dp2) = "&ucase"; StrLen(*dp2) = 6; return Success; } /* * Convert the cset to a string and proceed as is done for * string images but use a ' rather than " to bound the * result string. */ cvstr(dp1, sbuf); if (strreq(prescan(dp1) + 2) == Error) return Error; len = StrLen(*dp1); s = StrLoc(*dp1); outlen = 2; StrLoc(*dp2) = alcstr("'", (word)(1)); while (len-- > 0) outlen += doimage(*s++, '\''); alcstr("'", (word)(1)); StrLen(*dp2) = outlen; return Success; case T_File: /* * Check for distinguished files by looking at the address of * of the object to image. If one is found, make a string * naming it and return. */ if ((fd = BlkLoc(*dp1)->file.fd) == stdin) { StrLen(*dp2) = 6; StrLoc(*dp2) = "&input"; } else if (fd == stdout) { StrLen(*dp2) = 7; StrLoc(*dp2) = "&output"; } else if (fd == stderr) { StrLen(*dp2) = 7; StrLoc(*dp2) = "&errout"; } else { /* * The file is not a standard one; form a string of the form * file(nm) where nm is the argument originally given to * open. */ if (strreq(prescan(&BlkLoc(*dp1)->file.fname)+6) == Error) return Error; len = StrLen(BlkLoc(*dp1)->file.fname); s = StrLoc(BlkLoc(*dp1)->file.fname); outlen = 6; StrLoc(*dp2) = alcstr("file(", (word)(5)); while (len-- > 0) outlen += doimage(*s++, '\0'); alcstr(")", (word)(1)); StrLen(*dp2) = outlen; } return Success; case T_Proc: /* * Produce one of: * "procedure name" * "function name" * "record constructor name" * * Note that the number of dynamic locals is used to determine * what type of "procedure" is at hand. */ len = StrLen(BlkLoc(*dp1)->proc.pname); s = StrLoc(BlkLoc(*dp1)->proc.pname); switch ((int)BlkLoc(*dp1)->proc.ndynam) { default: type = "procedure "; break; case -1: type = "function "; break; case -2: type = "record constructor "; break; } outlen = strlen(type); if (strreq(len + outlen) == Error) return Error; StrLoc(*dp2) = alcstr(type, outlen); alcstr(s, len); StrLen(*dp2) = len + outlen; return Success; case T_List: /* * Produce: * "list_m(n)" * where n is the current size of the list. */ bp = BlkLoc(*dp1); sprintf(sbuf, "list_%ld(%ld)", (long)bp->list.id, (long)bp->list.size); len = strlen(sbuf); if (strreq(len) == Error) return Error; StrLoc(*dp2) = alcstr(sbuf, len); StrLen(*dp2) = len; return Success; case T_Table: /* * Produce: * "table_m(n)" * where n is the size of the table. */ bp = BlkLoc(*dp1); sprintf(sbuf, "table_%ld(%ld)", (long)bp->table.id, (long)bp->table.size); len = strlen(sbuf); if (strreq(len) == Error) return Error; StrLoc(*dp2) = alcstr(sbuf, len); StrLen(*dp2) = len; return Success; case T_Set: /* * Produce "set_m(n)" where n is size of the set. */ bp = BlkLoc(*dp1); sprintf(sbuf, "set_%ld(%ld)", (long)bp->set.id, (long)bp->set.size); len = strlen(sbuf); if (strreq(len) == Error) return Error; StrLoc(*dp2) = alcstr(sbuf,len); StrLen(*dp2) = len; return Success; case T_Record: /* * Produce: * "record name_m(n)" -- under construction * where n is the number of fields. */ bp = BlkLoc(*dp1); rnlen = StrLen(bp->record.recdesc->proc.recname); if (strreq(15 + rnlen) == Error) /* 15 = *"record " + *"(nnnnnn)"*/ return Error; bp = BlkLoc(*dp1); sprintf(sbuf, "_%ld(%ld)", (long)bp->record.id, (long)bp->record.recdesc->proc.nfields); len = strlen(sbuf); StrLoc(*dp2) = alcstr("record ", (word)(7)); alcstr(StrLoc(bp->record.recdesc->proc.recname),rnlen); alcstr(sbuf, len); StrLen(*dp2) = 7 + len + rnlen; return Success; case T_Coexpr: /* * Produce: * "co-expression_m(n)" * where m is the number of the co-expressions and n is the * number of results that have been produced. */ if (strreq((uword)30) == Error) return Error; sprintf(sbuf, "_%ld(%ld)", (long)BlkLoc(*dp1)->coexpr.id, (long)BlkLoc(*dp1)->coexpr.size); len = strlen(sbuf); StrLoc(*dp2) = alcstr("co-expression", (word)(13)); alcstr(sbuf, len); StrLen(*dp2) = 13 + len; return Success; case T_External: /* * For now, just produce "external(n)". */ sprintf(sbuf, "external(%ld)", (long)BlkLoc(*dp1)->externl.blksize); len = strlen(sbuf); if (strreq(len) == Error) return Error; StrLoc(*dp2) = alcstr(sbuf, len); StrLen(*dp2) = len; return Success; default: RetError(123,*dp1); } } /* * printable(c) -- is c a "printable" character? */ int printable(c) int c; { /* * The following code is operating-system dependent [@rmisc.01]. * Determine if a character is "printable". */ #if PORT return isprint(c); Deliberate Syntax Error #endif /* PORT */ #if MACINTOSH return isprint(c); #endif /* MACINTOSH */ #if MVS || VM #if SASC return isascii(c) && !iscntrl(c); #else /* SASC */ return isprint(c); #endif /* SASC */ #endif /* MVS || VM */ #if AMIGA || ATARI_ST || HIGHC_386 || MSDOS || OS2 || UNIX || VMS return (isascii(c) && isprint(c)); #endif /* AMIGA || ATARI_ST ... */ /* * End of operating-system specific code. */ } #ifndef AsmOver /* * add, sub, mul, neg with overflow check * all return 1 if ok, 0 if would overflow */ /* * Note: on some systems an improvement in performance can be obtained by * replacing the C functions that follow by checks written in assembly * language. To do so, add #define AsmOver to ../h/define.h. If your * C compiler supports the asm directive, but the new code at the end * of this section under control of #else. Otherwise put it a separate * file. */ extern int over_flow; word add(a, b) word a, b; { if ((a ^ b) >= 0 && (a >= 0 ? b > MaxLong - a : b < MinLong - a)) { over_flow = 1; return 0; } else { over_flow = 0; return a + b; } } word sub(a, b) word a, b; { if ((a ^ b) < 0 && (a >= 0 ? b < a - MaxLong : b > a - MinLong)) { over_flow = 1; return 0; } else { over_flow = 0; return a - b; } } word mul(a, b) word a, b; { if (b != 0) { if ((a ^ b) >= 0) { if (a >= 0 ? a > MaxLong / b : a < MaxLong / b) { over_flow = 1; return 0; } } else if (b != -1 && (a >= 0 ? a > MinLong / b : a < MinLong / b)) { over_flow = 1; return 0; } } over_flow = 0; return a * b; } /* MinLong / -1 overflows; need div3 too */ word neg(a) word a; { if (a == MinLong) { over_flow = 1; return 0; } over_flow = 0; return -a; } #endif /* AsmOver */