home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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 <ctype.h>
-
- /*
- * 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;
- }
-