|
|
BSD 4.3
#include "../h/rt.h"
#include "../h/record.h"
/*
* x[y] - access yth character or element of x.
*/
subsc(nargs, arg1v, arg2, arg1, arg0)
int nargs;
struct descrip arg1v, arg2, arg1, arg0;
{
register int i, j;
register union block *bp;
int typ1;
long l1;
struct descrip *dp;
char sbuf[MAXSTRING];
extern char *alcstr();
extern struct b_tvtbl *alctvtbl();
SetBound;
/*
* Make a copy of x.
*/
arg1v = arg1;
if ((typ1 = cvstr(&arg1, sbuf)) != NULL) {
/*
* x is a string, make sure that y is an integer.
*/
if (cvint(&arg2, &l1) == NULL)
runerr(101, &arg2);
/*
* Convert y to a position in x and fail if the position is out
* of bounds.
*/
i = cvpos(l1, STRLEN(arg1));
if (i > STRLEN(arg1))
fail();
if (typ1 == 1) {
/*
* x was converted to a string, so it can't be assigned back into.
* Just return a string containing the selected character.
*/
sneed(1);
STRLEN(arg0) = 1;
STRLOC(arg0) = alcstr(STRLOC(arg1)+i-1, 1);
}
else {
/*
* x is a string, make a substring trapped variable for the one
* character substring selected and return it.
*/
hneed(sizeof(struct b_tvsubs));
mksubs(&arg1v, &arg1, i, 1, &arg0);
}
ClearBound;
return;
}
/*
* x isn't a string or convertible to one, see if it's an aggregate.
*/
DeRef(arg1)
switch (TYPE(arg1)) {
case T_LIST:
/*
* x is a list. Make sure that y is an integer and that the
* subscript is in range.
*/
if (cvint(&arg2, &l1) == NULL)
runerr(101, &arg2);
i = cvpos(l1, BLKLOC(arg1)->list.cursize);
if (i > BLKLOC(arg1)->list.cursize)
fail();
/*
* Locate the list block containing the desired element.
*/
bp = BLKLOC(BLKLOC(arg1)->list.listhead);
j = 1;
while (i >= j + bp->lelem.nused) {
j += bp->lelem.nused;
if (TYPE(bp->lelem.listnext) != T_LELEM)
syserr("list reference out of bounds in subsc");
bp = BLKLOC(bp->lelem.listnext);
}
/*
* Locate the desired element in the block that contains it and
* return a pointer to it.
*/
i += bp->lelem.first - j;
if (i >= bp->lelem.nelem)
i -= bp->lelem.nelem;
dp = &bp->lelem.lslots[i];
arg0.type = D_VAR + ((int *)dp - (int *)bp);
VARLOC(arg0) = dp;
ClearBound;
return;
case T_TABLE:
/*
* x is a table. Dereference y and locate the appropriate bucket
* based on the hash value.
*/
DeRef(arg2)
hneed(sizeof(struct b_tvtbl));
i = hash(&arg2); /* get hash number of subscript */
bp = BLKLOC(BLKLOC(arg1)->table.buckets[i % NBUCKETS]);
/*
* Work down the chain of elements for the bucket and if an
* element with the desired subscript value is found, return
* a pointer to it.
* Elements are ordered in the chain by hashnumber value
* from smallest to largest.
*/
while (bp != NULL) {
if (bp->telem.hashnum > i) /* past it - not there */
break;
if ((bp->telem.hashnum == i) && (equiv(&bp->telem.tref, &arg2))) {
dp = &bp->telem.tval;
arg0.type = D_VAR + ((int *)dp - (int *)bp);
VARLOC(arg0) = dp;
ClearBound;
return;
}
/* We haven't reached the right hashnumber yet or
* the element is not the right one.
*/
bp = BLKLOC(bp->telem.blink);
}
/*
* x[y] is not in the table, make a table element trapped variable
* and return it as the result.
*/
arg0.type = D_TVTBL;
BLKLOC(arg0) = (union block *) alctvtbl(&arg1, &arg2, i);
ClearBound;
return;
case T_RECORD:
/*
* x is a record. Convert y to an integer and be sure that it
* it is in range as a field number.
*/
if (cvint(&arg2, &l1) == NULL)
runerr(101, &arg2);
bp = BLKLOC(arg1);
i = cvpos(l1, bp->record.recptr->nfields);
if (i > bp->record.recptr->nfields)
fail();
/*
* Locate the appropriate field and return a pointer to it.
*/
dp = &bp->record.fields[i-1];
arg0.type = D_VAR + ((int *)dp - (int *)bp);
VARLOC(arg0) = dp;
ClearBound;
return;
default:
/*
* x is of a type that can't be subscripted.
*/
runerr(114, &arg1);
}
ClearBound;
}
Opblockx(subsc,3,"[]",2)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.