/* * File: fstr.c * Contents: center, detab, entab, left, map, repl, reverse, right, trim */ #include "..\h\config.h" #include "..\h\rt.h" #include "rproto.h" #include /* * Prototype. */ hidden int nxttab Params((int col)); /* * center(s1,n,s2) - pad s1 on left and right with s2 to length n. */ FncDcl(center,3) { register char *s, *st; word cnt, slen, hcnt; char *sbuf, *s3; char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen]; /* * Arg1 must be a string. Arg2 must be a non-negative integer and defaults * to 1. Arg3 must be a string and defaults to a blank. */ if (cvstr(&Arg1, sbuf1) == CvtFail) RunErr(103, &Arg1); if (defshort(&Arg2, 1) == Error) RunErr(0, NULL); if ((cnt = IntVal(Arg2)) < 0) RunErr(205, &Arg2); if (defstr(&Arg3, sbuf2, &blank) == Error) RunErr(0, NULL); if (strreq(cnt) == Error) RunErr(0, NULL); if (StrLen(Arg3) == 0) { /* * The padding string is null; make it a blank. */ slen = 1; s3 = " "; } else { slen = StrLen(Arg3); s3 = StrLoc(Arg3); } /* * Get space for the new string. Start at the right * of the new string and copy Arg3 into it from right to left as * many times as will fit in the right half of the new string. */ sbuf = alcstr(NULL, cnt); hcnt = cnt / 2; s = sbuf + cnt; while (s > sbuf + hcnt) { st = s3 + slen; while (st > s3 && s > sbuf + hcnt) *--s = *--st; } /* * Start at the left end of the new string and copy Arg1 into it from * left to right as many time as will fit in the left half of the * new string. */ s = sbuf; while (s < sbuf + hcnt) { st = s3; while (st < s3 + slen && s < sbuf + hcnt) *s++ = *st++; } slen = StrLen(Arg1); if (cnt < slen) { /* * Arg1 is larger than the field to center it in. The source for the * copy starts at the appropriate point in Arg1 and the destination * starts at the left end of of the new string. */ s = sbuf; st = StrLoc(Arg1) + slen/2 - hcnt + (~cnt&slen&1); } else { /* * Arg1 is smaller than the field to center it in. The source for the * copy starts at the left end of Arg1 and the destination starts at * the appropriate point in the new string. */ s = sbuf + hcnt - slen/2 - (~cnt&slen&1); st = StrLoc(Arg1); } /* * Perform the copy, moving min(*Arg1,Arg2) bytes from st to s. */ if (slen > cnt) slen = cnt; while (slen-- > 0) *s++ = *st++; /* * Return the new string. */ StrLen(Arg0) = cnt; StrLoc(Arg0) = sbuf; Return; } /* * detab(s,i,...) - replace tabs with spaces, with stops at columns indicated. */ FncDclV(detab) { int i, last, interval, cnt, col, target; char *in, *out, *iend, c, sbuf1[MaxCvtLen]; float expan, etmp; /* * Arg1 is required and must be a string. * Additional args must be strictly increasing positive integers. * Calculate maximum expansion factor while checking. */ if (nargs < 1) RunErr(103, &nulldesc); if (cvstr(&Arg(1), sbuf1) == CvtFail) RunErr(103, &Arg(1)); last = 1; if (nargs < 2) { interval = 8; expan = 8.0; } else { expan = 1.0; for (i = 2; i <= nargs; i++) { if (ArgType(i) != D_Integer) { if (cvint(&Arg(i)) != T_Integer) { RunErr(101, &Arg(i)); } } interval = ArgVal(i) - last; if (interval <= 0) RunErr(210, &Arg(i)); etmp = (float) (ArgVal(i) - 1) / (float) (i - 1); if (etmp > expan) expan = etmp; last = (int)ArgVal(i); } last -= interval; if (interval > expan) expan = interval; } /* * Get memory for worst case expansion. This would be a string of all tabs, * or repeated newlines after tabbing past a large tab interval. */ cnt = expan * StrLen(Arg1) + 1; if (strreq((word)cnt) == Error) RunErr(0, NULL); if (strfree + cnt > strend) syserr("detab allocation botch"); /* * Copy the string, expanding tabs. */ col = 1; target = 0; iend = StrLoc(Arg(1)) + StrLen(Arg(1)); for (in = StrLoc(Arg(1)), out = strfree; in < iend; ) switch (c = *out++ = *in++) { case '\b': col--; break; case LineFeed: case CarriageReturn: col = 1; break; case '\t': out--; if (col >= last) target = col + interval - (col - last) % interval; else { for (i = 2; col >= ArgVal(i); i++) ; target = (int)ArgVal(i); } while (col < target) { *out++ = ' '; col++; } break; default: #if SASC if (isascii(c) && !iscntrl(c)) /* if "printable ASCII" */ #else /* SASC */ if (isprint(c)) #endif /* SASC */ col++; } /* * Return new string if indeed there were tabs; otherwise return original * string to conserve memory. */ i = DiffPtrs(out, strfree); if (i > cnt) syserr("overenthusiastic tab expansion"); if (target > 0) { StrLen(Arg0) = i; /* set string length */ StrLoc(Arg0) = alcstr(NULL, (word)i); /* allocate the space we just filled */ } else Arg0 = Arg1; /* don't allocate, reuse old string */ Return; } /* * entab(s,i,...) - replace spaces with tabs, with stops at columns indicated. */ /* temps for communication with nxttab(), following entab() */ static dptr tablist; /* explicit tab stops (descriptors of ints) */ static int last, interval; /* last explicit stop, and repeat interval */ FncDclV(entab) { int i, target; char *in, *out, *iend, c, sbuf1[MaxCvtLen]; long col, cnt; /* * Arg1 is required and must be a string. * Additional args must be strictly increasing positive integers. */ if (nargs < 1) RunErr(103, &nulldesc); if (cvstr(&Arg(1), sbuf1) == CvtFail) RunErr(103, &Arg(1)); last = 1; interval = 8; for (i = 2; i <= nargs; i++) { if (ArgType(i) != D_Integer) { if (cvint(&Arg(i)) != T_Integer) { RunErr(101, &Arg(i)); } } interval = ArgVal(i) - last; if (interval <= 0) RunErr(210, &Arg(i)); last = (int)ArgVal(i); } if (last > 1) last -= interval; tablist = &Arg(2); /* if there is no arg 2, this won't be used, so ok */ /* * Get memory for result at end of string space. We may give some back * if not all needed, or all of it if no tabs can be inserted. */ cnt = StrLen(Arg1); if (strreq((word)cnt) == Error) RunErr(0, NULL); if (strfree + cnt > strend) syserr("entab allocation botch"); /* * Copy the string, looking for runs of spaces. */ col = 1; target = 0; iend = StrLoc(Arg(1)) + StrLen(Arg(1)); for (in = StrLoc(Arg(1)), out = strfree; in < iend; ) switch (c = *out++ = *in++) { case '\b': col--; break; case LineFeed: case CarriageReturn: col = 1; break; case '\t': if (col >= last) col += interval - (col - last) % interval; else { for (i = 2; col >= ArgVal(i); i++) ; col = ArgVal(i); } break; case ' ': target = col + 1; while (in < iend && *in == ' ') target++, in++; cnt = target - col; if (cnt > 1) { /* never tab just 1; already copied space */ if (nxttab(col) == col+1 && nxttab(col+1) > target) col++; /* keep space to avoid 1-col tab then spaces */ else out--; /* back up to begin tabbing */ while ((i = nxttab(col)) <= target) { *out++ = '\t'; /* put tabs to tab positions */ col = i; } while (col++ < target) *out++ = ' '; /* complete gap with spaces */ } col = target; break; default: #if SASC if (isascii(c) && !iscntrl(c)) /* if "printable ASCII" */ #else /* SASC */ if (isprint(c)) #endif /* SASC */ col++; } /* * Return new string if indeed there were tabs; otherwise return original * string to conserve memory. */ if (out > strend) syserr("entab allocation botch"); if (target) { /* if we did indeed insert tabs */ cnt = DiffPtrs(out, strfree); StrLen(Arg0) = cnt; /* set string length */ StrLoc(Arg0) = alcstr(NULL, cnt); /* allocate the space we just filled */ } else Arg0 = Arg1; /* don't allocate, return old string */ Return; } /* nxttab(col) -- helper routine for entab, returns next tab beyond col */ static int nxttab(col) int col; { dptr dp; long n; if (col >= last) return col + interval - (col - last) % interval; dp = tablist; while ((n = IntVal(*dp)) <= col) dp++; return n; } /* * left(s1,n,s2) - pad s1 on right with s2 to length n. */ FncDcl(left,3) { register char *s, *st; word cnt, slen; char *sbuf, *s3, sbuf1[MaxCvtLen], sbuf2[MaxCvtLen]; /* * Arg1 must be a string. Arg2 must be a non-negative integer and defaults * to 1. Arg3 must be a string and defaults to a blank. */ if (cvstr(&Arg1, sbuf1) == CvtFail) RunErr(103, &Arg1); if (defshort(&Arg2, 1) == Error) RunErr(0, NULL); if ((cnt = IntVal(Arg2)) < 0) RunErr(205, &Arg2); if (defstr(&Arg3, sbuf2, &blank) == Error) RunErr(0, NULL); if (strreq(cnt) == Error) RunErr(0, NULL); if (StrLen(Arg3) == 0) { /* * The padding string is null; make it a blank. */ slen = 1; s3 = " "; } else { slen = StrLen(Arg3); s3 = StrLoc(Arg3); } /* * Get Arg2 bytes of string space. Start at the right end of the new * string and copy Arg3 into the new string as many times as it fits. * Note that Arg3 is copied from right to left. */ sbuf = alcstr(NULL, cnt); s = sbuf + cnt; while (s > sbuf) { st = s3 + slen; while (st > s3 && s > sbuf) *--s = *--st; } /* * Copy Arg1 into the new string, starting at the left end. * If *Arg1 > Arg2, only copy Arg2 bytes. */ s = sbuf; slen = StrLen(Arg1); st = StrLoc(Arg1); if (slen > cnt) slen = cnt; while (slen-- > 0) *s++ = *st++; /* * Return the new string. */ StrLen(Arg0) = cnt; StrLoc(Arg0) = sbuf; Return; } /* * map(s1,s2,s3) - map s1, using s2 and s3. */ FncDcl(map,3) { register int i; register word slen; register char *s1, *s2, *s3; char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen], sbuf3[MaxCvtLen]; static char maptab[256]; /* * Arg1 must be a string; Arg2 and Arg3 default to &ucase and &lcase, * respectively. */ if (cvstr(&Arg1, sbuf1) == CvtFail) RunErr(103, &Arg1); if (ChkNull(Arg2)) Arg2 = ucase; if (ChkNull(Arg3)) Arg3 = lcase; /* * If Arg2 and Arg3 are the same as for the last call of map, * the current values in maptab can be used. Otherwise, the * mapping information must be recomputed. */ if (!EqlDesc(maps2,Arg2) || !EqlDesc(maps3,Arg3)) { maps2 = Arg2; maps3 = Arg3; /* * Convert Arg2 and Arg3 to strings. They must be of the * same length. */ if (cvstr(&Arg2, sbuf2) == CvtFail) RunErr(103, &Arg2); if (cvstr(&Arg3, sbuf3) == CvtFail) RunErr(103, &Arg3); if (StrLen(Arg2) != StrLen(Arg3)) RunErr(-208, NULL); /* * The array maptab is used to perform the mapping. First, * maptab[i] is initialized with i for i from 0 to 255. * Then, for each character in Arg2, the position in maptab * corresponding to the value of the character is assigned * the value of the character in Arg3 that is in the same * position as the character from Arg2. */ s2 = StrLoc(Arg2); s3 = StrLoc(Arg3); for (i = 0; i <= 255; i++) maptab[i] = i; for (slen = 0; slen < StrLen(Arg2); slen++) maptab[s2[slen]&0377] = s3[slen]; } if (StrLen(Arg1) == 0) { Arg0 = emptystr; Return; } /* * The result is a string the size of Arg1; ensure that much space. */ slen = StrLen(Arg1); if (strreq(slen) == Error) RunErr(0, NULL); s1 = StrLoc(Arg1); /* * Create the result string, but specify no value for it. */ StrLen(Arg0) = slen; StrLoc(Arg0) = alcstr(NULL, slen); s2 = StrLoc(Arg0); /* * Run through the string, using values in maptab to do the * mapping. */ while (slen-- > 0) *s2++ = maptab[(*s1++)&0377]; Return; } /* * repl(s,n) - concatenate n copies of string s. */ FncDcl(repl,2) { register char *sloc; register int cnt; char sbuf[MaxCvtLen]; /* * Make sure that Arg1 is a string. */ if (cvstr(&Arg1, sbuf) == CvtFail) RunErr(103, &Arg1); /* * Make sure that Arg2 is an integer. */ switch (cvint(&Arg2)) { /* * Make sure count is not negative. */ case T_Integer: if ((cnt = (int)IntVal(Arg2)) >= 0) break; RunErr(205, &Arg2); default: RunErr(101, &Arg2); } /* * Make sure the resulting string will not be too long. */ if ((IntVal(Arg2) * StrLen(Arg1)) > MaxStrLen) RunErr(-205, NULL); /* * Return an empty string if Arg2 is 0. */ if (cnt == 0) Arg0 = emptystr; else { /* * Ensure enough space for the replicated string and allocate * a copy of s. Then allocate and copy s n - 1 times. */ if (strreq(cnt * StrLen(Arg1)) == Error) RunErr(0, NULL); sloc = alcstr(StrLoc(Arg1), StrLen(Arg1)); cnt--; while (cnt--) alcstr(StrLoc(Arg1), StrLen(Arg1)); /* * Make Arg0 a descriptor for the replicated string. */ StrLen(Arg0) = (int)IntVal(Arg2) * StrLen(Arg1); StrLoc(Arg0) = sloc; } Return; } /* * reverse(s) - reverse string s. */ FncDcl(reverse,1) { register char c, *floc, *lloc; register word slen; char sbuf[MaxCvtLen]; /* * Make sure that Arg1 is a string. */ if (cvstr(&Arg1, sbuf) == CvtFail) RunErr(103, &Arg1); /* * Ensure that there is enough room and allocate a copy of Arg1. */ slen = StrLen(Arg1); if (strreq(slen) == Error) RunErr(0, NULL); StrLen(Arg0) = slen; StrLoc(Arg0) = alcstr(StrLoc(Arg1), slen); /* * Point floc at the start of Arg0 and lloc at the end of Arg0. Work floc * and lloc along Arg0 in opposite directions, swapping the characters * at floc and lloc. */ floc = StrLoc(Arg0); lloc = floc + --slen; while (floc < lloc) { c = *floc; *floc++ = *lloc; *lloc-- = c; } Return; } /* * right(s1,n,s2) - pad s1 on left with s2 to length n. */ FncDcl(right,3) { register char *s, *st; word cnt, slen; char *sbuf, *s3, sbuf1[MaxCvtLen], sbuf2[MaxCvtLen]; /* * Arg1 must be a string. Arg2 must be a non-negative integer and defaults * to 1. eArg3 must be a string and defaults to a blank. */ if (cvstr(&Arg1, sbuf1) == CvtFail) RunErr(103, &Arg1); if (defshort(&Arg2, 1) == Error) RunErr(0, NULL); if ((cnt = IntVal(Arg2)) < 0) RunErr(205, &Arg2); if (defstr(&Arg3, sbuf2, &blank) == Error) RunErr(0, NULL); if (strreq(cnt) == Error) RunErr(0, NULL); if (StrLen(Arg3) == 0) { /* * The padding string is null; make it a blank. */ slen = 1; s3 = " "; } else { slen = StrLen(Arg3); s3 = StrLoc(Arg3); } /* * Get Arg2 bytes of string space. Start at the left end of the new * string and copy Arg3 into the new string as many times as it fits. */ sbuf = alcstr(NULL, cnt); s = sbuf; while (s < sbuf + cnt) { st = s3; while (st < s3 + slen && s < sbuf + cnt) *s++ = *st++; } /* * Copy Arg1 into the new string, starting at the right end and copying * Arg3 from right to left. If *Arg1 > Arg2, only copy Arg2 bytes. */ s = sbuf + cnt; slen = StrLen(Arg1); st = StrLoc(Arg1) + slen; if (slen > cnt) slen = cnt; while (slen-- > 0) *--s = *--st; /* * Return the new string. */ StrLen(Arg0) = cnt; StrLoc(Arg0) = sbuf; Return; } /* * trim(s,c) - trim trailing characters in c from s. */ FncDcl(trim,2) { char *sloc; char sbuf[MaxCvtLen]; int *cs, csbuf[CsetSize], cvted; static int spcset[CsetSize] = /* ' ' */ #if EBCDIC != 1 cset_display(0, 0, 01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); #else /* EBCDIC != 1 */ cset_display(0, 0, 0, 0, 01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); #endif /* EBCDIC != 1 */ /* * Arg1 must be a string. */ if ((cvted = cvstr(&Arg1, sbuf)) == CvtFail) RunErr(103, &Arg1); /* * Arg2 defaults to a cset containing a blank. */ if (defcset(&Arg2, &cs, csbuf, spcset) == Error) RunErr(0, NULL); /* * Start at the end of Arg1 and then back up until a character that is * not in Arg2 is found. The actual trimming is done by having a * descriptor that points at a substring of Arg1, but with the length * reduced. */ Arg0 = Arg1; sloc = StrLoc(Arg1) + StrLen(Arg1) - 1; while (sloc >= StrLoc(Arg1) && Testb(ToAscii(*sloc), cs)) { sloc--; StrLen(Arg0)--; } /* * Save the temporary string in the string region if conversion was done. */ if (cvted == Cvt) { if (strreq(StrLen(Arg0)) == Error) RunErr(0, NULL); StrLoc(Arg0) = alcstr(StrLoc(Arg0), StrLen(Arg0)); } Return; }