2.11BSD/ingres/source/qrymod/protect.c
# include "../ingres.h"
# include "../aux.h"
# include "../unix.h"
# include "../catalog.h"
# include "../symbol.h"
# include "../tree.h"
# include "qrymod.h"
/*
** PROTECT -- protection algorithm
**
** This module performs the INGRES protection algorithm, as
** presented in Stonebraker & Rubinstein, "The INGRES Protection
** System", with a few modifications.
**
** The basic algorithm is as follows:
**
** The algorithm is applied once to each variable used in the
** query. Each variable has an initial check performed to
** determine applicability -- if the current user owns the
** relation, or if the relation is specially marked as being
** "all access to everyone", then the algorithm is skipped,
** thereby having effectively no restriction.
**
** The set of all such variables is computed in 'protect',
** and then 'dopro' is called to process each of those. This
** is so the protection algorithm does not get applied recursively
** to constraints which define more than one variable. Notice
** that this could in itself be a protection violation, if it
** were acceptable to reference a relation you do not own in a
** PERMIT statement.
**
** The effective query mode for this variable is then computed.
** This is the same as the query mode of the whole query if
** the variable in question is the result variable, otherwise
** it is "retrieve" mode.
**
** The next step is to scan the query tree and create sets of
** domains referenced. Four sets are created:
** uset -- the set of domains updated (actually,
** referenced in the target list -- on a
** retrieve, this will be the set of domains
** retrieved to the user).
** rset -- the set of domains retrieved in some
** context other than the left hand side of
** an equal sign.
** aset -- the set of domains aggregated. This only
** includes domains aggregated with a simple
** aggregate (not an aggregate function) with
** no qualification, since it may be possible
** to come up with too much information other-
** wise.
** qset -- the set of domains retrieved for use in
** a qualification, but never stored. This
** includes domains in a qualification of an
** aggregate or aggregate function.
** For more details of domains in each of these sets, look at
** the routine 'makedset'.
**
** If we had a retrieve operation in the first place, we will
** then merge 'uset' into 'rset' and clear 'uset', so that
** now 'uset' only contains domains which are actually updated,
** and 'rset' contains all domains which are retrieved.
**
** Now that we know what is referenced, we can scan the protection
** catalog. We scan the entire catalog once for each variable
** mentioned in the query (except as already taken care of as
** described above).
**
** We must create a set of all operations on this variable which
** are not yet resolved, that is, for which no PERMIT statements
** which qualify have been issued. We store this set in the
** variable "noperm". As PERMIT statements are found, bits will
** be cleared. If the variable is not zero by the end of the
** scan of the protection catalog, then we reject the query,
** saying that we don't have permission -- giving us default
** to deny.
**
** For each tuple in the protection catalog for this relation,
** we call "proappl" to see if it applies to this query. This
** routine checks the user, terminal, time of day, and so forth
** (in fact, everything which is query-independent) and tells
** whether this tuple might apply.
**
** If the tuple passes this initial check, we then do the query-
** dependent checking. This amounts to calling "prochk" once
** for each operation (and domain set) in the query. What we
** get back is a set of operations which this tuple applies to.
** If zero, the tuple doesn't apply at all; otherwise, it
** applies to at least one operation. If it applies to some-
** thing, we call it "interesting".
**
** For "interesting" tuples, we now get the corresponding
** qualification (if one exists), and disjoin it to a set of
** protection constraints held in "pqual". Also, we mark
** any operations we found as having been done, by clearing
** bits in "noperm".
**
** When we have completed scanning the protection catalog,
** we check "noperm". If it is non-zero, then we have some
** operation for which a PERMIT statement has not been issued,
** and we issue an error message. If this variable is ok,
** then we go on and try the next variable.
**
** When all variables have been accounted for, we check to
** see if we have any qualifications collected from the
** protection algorithm. If so, we conjoin them to the
** query tree.
**
** Finally, we return the root of the modified tree. This
** tree is guaranteed to have no authorization violations,
** and may be run as a regular query.
**
** Parameters:
** root -- the root of the tree.
**
** Returns:
** The root of the modified and authorized tree.
**
** Side Effects:
** A possible non-local return on access violation.
**
** Defined Constants:
** none
**
** Defines:
** Proopmap -- a mapping from query modes to operation
** set bits in the protection catalog.
** protect -- the driver for the protection algorithm.
** dopro -- the guts of the algorithm.
** makedset -- the routine to make the domain sets.
** proappl -- to check if a protection tuple applies.
** prochk -- to check what operations it handles.
** pr_set -- debugging set print.
**
** Requires:
** Prodes -- an open descriptor for the protection
** catalog.
** access methods.
** gettree() -- to get the qualification for a particular
** PERMIT statement.
** ferror -- to signal an error on a protection
** violation.
** appqual() -- to append the protection qualification
** (conjunctively) to the query tree.
** Usercode -- set to the current user -- used to
** determine applicability of a particular
** protection tuple.
** Terminal -- set to the terminal id for the current
** terminal, blank if background. This is used
** similarly.
** localtime() -- system routine to convert a system
** date to a vector of day, month, day-of-week,
** etc.
** tree() -- to create an OR node.
** norml() -- to renormalize the tree.
**
** Called By:
** qrymod()
**
** Files:
** 'protect' relation -- the relation holding the
** protection information.
** 'violation' relation -- the relation into which
** protection violations are logged.
** Not supported in this version.
** 'tree' relation -- holds the query trees for the
** PERMIT statements.
**
** Trace Flags:
** 50 - 59
**
** Diagnostics:
** 3500 -- Protection violation.
**
** Syserrs:
** protect: null tree
** The tree fetched from the 'tree' catalog
** was effectively null.
** protect: get
** A bad return from the 'get' on the protect
** catalog.
**
** History:
** 2/14/79 -- version 6.2 release.
*/
int Proopmap[MAXPROQM + 1] =
{
PRO_RETR, /* 0 -- mdRETTERM */
PRO_RETR, /* 1 -- mdRETR */
PRO_APP, /* 2 -- mdAPP */
PRO_REPL, /* 3 -- mdREPL */
PRO_DEL, /* 4 -- mdDEL */
};
extern struct descriptor Prodes;
# ifndef xV7_UNIX
extern char Terminal;
# endif
# ifdef xV7_UNIX
extern char Terminal[3];
# endif
extern QTREE *gettree();
QTREE *protect(root)
QTREE *root;
{
QTREE *r;
register int i;
register int vn;
int qmode;
int varset;
r = root;
# ifdef xQTR1
tTfp(50, -1, "\n->PROTECT\n\n");
# endif
varset = 0;
/*
** Scan the range table and create a set of all variables
** which are 'interesting', that is, on which the protectin
** algorithm should be performed.
*/
for (vn = 0; vn < MAXVAR + 1; vn++)
{
if (!Rangev[vn].rused)
continue;
/* if owner, accept any query */
if (bequal(Rangev[vn].rowner, Usercode, 2))
continue;
/* check for "no restriction" bit asserted (= clear) */
if ((Rangev[vn].rstat & S_PROTALL) == 0)
continue;
if ((Rangev[vn].rstat & S_PROTRET) == 0 &&
(Qmode == mdRETR || Qmode == mdRET_UNI))
continue;
varset |= 1 << vn;
}
/*
** For each variable specified in varset (that is, for each
** variable in the initial query), do the real algorithm.
*/
for (vn = 0; vn < MAXVAR + 1; vn++)
{
if ((varset & (1 << vn)) == 0)
continue;
# ifdef xQTR1
if (tTf(50, 1))
printf("\nvn=%d: %.12s\n", vn, Rangev[vn].relid);
# endif
/*
** Determine the query mode for this variable. This
** is not the query mode of the original query,
** unless the variable is the result variable.
*/
qmode = Qmode;
if (vn != Resultvar || qmode == mdRET_UNI)
qmode = mdRETTERM;
# ifdef xQTR3
if (qmode == 1 || qmode > 4 || qmode < 0)
syserr("protect: bad qmode %d", qmode);
# endif
/* do the interesting part of the algorithm */
dopro(vn, r, qmode, NULL);
}
/* return the (authorized) tree */
# ifdef xQTR1
if (tTf(50, 15))
treepr(r, "PROTECT->");
# endif
return (r);
}
/*
** DOPRO -- actually do the protection algorithm
**
** This is the guts of it, broken off because it must be called
** recursively on aggregates. The algorithm is as discussed
** in the module header.
**
** Parameters:
** varno -- the variable number of interest.
** root -- the root of the tree to modify.
** qmode -- the effective query mode for this relation.
** byset -- if non-NULL, a set of domains passed back
** which gets bound out of the aggregate func,
** in other words, the by list.
**
** Returns:
** none
**
** Side Effects:
** The tree pointed at by 'root' gets modified.
** Quite possibly 'Rangev' and 'Remap' get clobbered.
**
** Requires:
** makedset -- to create domain usage sets, also
** detects aggregates and calls 'dopro'
** recursively.
** proappl -- to do query-independent checking for
** protection tuple applicability.
** prochk -- to do query-dependent checking for protection
** tuple applicability.
** appqual -- to add the protection qualification.
** norml -- to renormalize the tree, what with all those
** messy OR nodes and all.
** trimqlend -- to adjust the tree for norml.
**
** Called By:
** protect
** makedset -- on aggregates and aggregate functions.
**
** Trace Flags:
** 51
*/
dopro(varno, root, qmode, byset)
int varno;
QTREE *root;
int qmode;
int byset[8];
{
int qset[8];
int uset[8];
int aset[8];
int rset[8];
int zeros[8];
QTREE *p;
QTREE *pqual;
register int i;
register int vn;
register QTREE *t;
int noperm;
int noqual;
struct protect prokey, protup;
struct tup_id lotid, hitid;
struct qvect
{
QTREE *q_qual;
int q_mode;
};
struct qvect quals[4];
int j;
QTREE *trimqlend(), *tree(), *norml();
t = root;
vn = varno;
/* create domain usage sets */
for (i = 0; i < 8; i++)
{
zeros[i] = uset[i] = rset[i] = qset[i] = aset[i] = 0;
if (byset != NULL)
byset[i] = 0;
}
/*
** Create domain usage set for target list side. There are
** two general cases: this is the root of the tree, or this
** is the head of an aggregate.
*/
switch (t->sym.type)
{
case AGHEAD:
/*
** An aggregate head falls into two classes: simple
** aggregate and aggregate function. In an aggregate
** function, care must be taken to bind the variables
** in the by-list outside of the aggregate. We use
** 'rset' as a temporary here.
*/
if (t->left->sym.type == BYHEAD)
{
/* make by-list set */
makedset(vn, t->left->left, NULL, rset, aset, qset);
/* merge by-list set into qualification set */
for (i = 0; i < 8; i++)
{
if (byset != NULL)
byset[i] |= rset[i];
qset[i] |= rset[i];
rset[i] = 0;
}
/* make aggregate list set */
makedset(vn, t->left->right->right, NULL, rset, aset, qset);
}
else
{
/* simple aggregate */
# ifdef xQTR3
if (t->left->sym.type != AOP)
syserr("dopro: AGHEAD->left %d", t->left->sym.type);
# endif
/* check for qualification */
if (t->right->sym.type == QLEND)
{
/* simple, unqualified aggregate */
makedset(vn, t->left->right, NULL, aset, aset, qset);
}
else
{
# ifdef xQTR3
if (t->right->sym.type != AND)
syserr("dopro: AND=%d", t->right->sym.type);
# endif
makedset(vn, t->left->right, NULL, rset, aset, qset);
}
}
break;
case ROOT:
makedset(vn, t->left, uset, rset, aset, qset);
break;
}
/* scan qualifcation */
makedset(vn, t->right, NULL, &qset, &aset, &qset);
/* if retrieval, drop the 'update' set */
if (qmode == mdRETTERM)
{
for (i = 0; i < 8; i++)
{
uset[i] = 0;
}
}
# ifdef xQTR1
if (tTf(51, 2))
{
printf("qmode %d\n", qmode);
pr_set(uset, "uset");
pr_set(rset, "rset");
pr_set(aset, "aset");
pr_set(qset, "qset");
}
# endif
/* create a bit map of all referenced operations */
noperm = 0;
if (!bequal(uset, zeros, sizeof zeros))
noperm |= Proopmap[qmode];
if (!bequal(&rset, &zeros, sizeof zeros))
noperm |= PRO_RETR;
if (!bequal(&aset, &zeros, sizeof zeros))
noperm |= PRO_AGGR;
if (!bequal(&qset, &zeros, sizeof zeros))
noperm |= PRO_TEST;
/* if no oper, then the query was probably just aggr's */
if (noperm == 0)
return;
/* initialize qualification portion */
for (i = 0; i < 4; )
quals[i++].q_qual = NULL;
noqual = FALSE;
/* check the protection catalog */
opencatalog("protect", 0);
setkey(&Prodes, &prokey, Rangev[vn].relid, PRORELID);
setkey(&Prodes, &prokey, Rangev[vn].rowner, PRORELOWN);
find(&Prodes, EXACTKEY, &lotid, &hitid, &prokey);
while ((i = get(&Prodes, &lotid, &hitid, &protup, TRUE)) == 0)
{
if (kcompare(&Prodes, &prokey, &protup) != 0)
continue;
# ifdef xQTR2
if (tTf(51, 4))
{
printf("PROTECT: ");
printup(&Prodes, &protup);
}
# endif
/* check if this is the correct user, terminal, etc */
if (!proappl(&protup))
continue;
/* alright, let's check the operation */
i = 0;
if (qmode != mdRETTERM)
i = quals[0].q_mode = prochk(Proopmap[qmode], &uset, &protup);
i |= quals[1].q_mode = prochk(PRO_RETR, &rset, &protup);
i |= quals[2].q_mode = prochk(PRO_AGGR, &aset, &protup);
i |= quals[3].q_mode = prochk(PRO_TEST, &qset, &protup);
# ifdef xQTR2
if (tTf(51, 5))
printf("Satisfies operations %o\n", i);
# endif
/* see if this tuple is "interesting" */
if (i == 0)
continue;
/* it is! get the qualification (if any) */
if (protup.protree >= 0)
{
p = gettree(Rangev[vn].relid, Rangev[vn].rowner, mdPROT, protup.protree, FALSE);
# ifdef xQTR2
if (tTf(51, 6))
treepr(p, "Protection Clause");
# endif
p = trimqlend(p->right);
# ifdef xQTR3
/* check for a non-null qualification */
if (p == NULL)
syserr("protect: null tree");
# endif
/* translate to the interesting variable */
j = protup.proresvar;
if (Remap[j] >= 0)
j = Remap[j];
mergevar(j, varno, p);
/* disjoin the protection qual to real qual */
for (j = 0; j < 4; j++)
{
if (quals[j].q_mode == 0)
continue;
if (quals[j].q_qual == NULL)
quals[j].q_qual = p;
else
quals[j].q_qual = tree(quals[j].q_qual, p, OR, 0);
}
}
else
noqual = TRUE;
/* mark this operation as having been handled */
noperm &= ~i;
}
/* test 'get' return code */
if (i < 0)
syserr("protect: get");
# ifdef xQTR1
if (tTf(51, 12))
printf("No perm on %o\n", noperm);
# endif
/* see if no tuples applied for some operation */
if (noperm != 0)
ferror(3500, Qmode, vn, 0);
/* see if we want to modify the query at all */
if (!noqual)
{
/* conjoin the qualification */
pqual = NULL;
for (i = 0; i < 4; i++)
if (quals[i].q_qual != NULL)
if (pqual == NULL)
pqual = quals[i].q_qual;
else
pqual = tree(pqual, quals[i].q_qual, AND, 0);
pqual = tree(pqual, tree(NULL, NULL, QLEND, 0), AND, 0);
appqual(pqual, t);
/* normalize the tree */
t->right = norml(trimqlend(t->right));
}
}
/*
** MAKEDSET -- make domain reference sets
**
** This routine creates some sets which reflect the usage of
** domains for a particular variable.
**
** The interesting nodes are 'case' labels in the large
** switch statement which comprises most of the code. To
** describe briefly:
**
** VAR nodes are easy: if they are for the current variable,
** set the bit corresponding to the domain in the
** 'retrieval' set. They can have no descendents,
** so just return.
** RESDOM nodes are also easy: they can be handled the same,
** but the bit is set in the 'update' set instead.
** AGHEAD nodes signal the beginning of an aggregate or
** aggregate function. In this case, we scan the
** qualification first (noting that RESDOM and VAR
** nodes are processed as 'qualification' sets
** instead of 'retrieval' or 'update' sets). Then,
** if the aggregate has a WHERE clause or a BY list,
** we treat it as a retrieve; otherwise, we call our-
** selves recursively treating VAR nodes as 'aggregate'
** types rather than 'retrieve' types.
** BYHEAD nodes signal the beginning of a BY list. The left
** subtree (the actual BY-list) is processed with
** RESDOM nodes ignored (since they are pseudo-domains
** anyhow) and VAR nodes mapped into the 'qualification'
** set. Then we check the right subtree (which better
** begin with an AOP node!) and continue processing.
** AOP nodes must have a null left subtree, so we just drop
** to the right subtree and iterate. Notice that we
** do NOT map VAR nodes into the 'aggregate' set for
** this node, since this has already been done by the
** AGHEAD node; also, this aggregate might be counted
** as a retrieve operation instead of an aggregate
** operation (as far as the protection system is con-
** cerned) -- this has been handled by the AGHEAD
** node.
** All other nodes are processed recursively along both edges.
**
** Parameters:
** vn -- the variable number that we are currently
** interested in.
** tree -- the root of the tree to scan. Notice that this
** will in general be only one half of the tree --
** makedset will be called once for the target
** list and once for the qualification, with
** different sets for the following parameters.
** uset -- adjusted to be the set of all domains
** updated.
** rset -- adjusted to be the set of all domains
** retrieved implicitly, that is, on the right-
** hand-side of an assignment operator.
** aset -- adjusted to be the set of all domains
** aggregated. Notice that this set is not
** adjusted explicitly, but rather is passed
** to recursive incarnations of this routine
** as 'rset'.
** qset -- adjusted to be the set of domains retrieved
** implicitly in a qualification. Like 'aset',
** this is passed as 'rset' to recursive
** incarnations.
**
** Returns:
** none
**
** Side Effects:
** none
**
** Requires:
** lsetbit()
**
** Called By:
** protect() -- in two places.
**
** Trace Flags:
** 53
**
** Diagnostics:
** none
**
** Syserrs:
** makedset: AOP->left
** A truly obscure error message, this means
** that the left subtree of an AOP node was
** not null.
** makedset: AOP %d
** Another goodie, this one means that the
** right branch of a BYHEAD node was not an
** AOP node as required; the %d is the type
** it actually was.
*/
makedset(vn, tree, uset, rset, aset, qset)
int vn;
QTREE *tree;
int uset[8];
int rset[8];
int aset[8];
int qset[8];
{
register QTREE *t;
register int i;
int byset[8];
t = tree;
# ifdef xQTR1
if (tTf(53, 0))
{
printf("->makedset\n");
pr_set(uset, "uset");
pr_set(rset, "rset");
pr_set(aset, "aset");
pr_set(qset, "qset");
}
# endif
while (t != NULL)
{
switch (t->sym.type)
{
case VAR:
if (((struct qt_var *)t)->varno == vn)
lsetbit(((struct qt_var *)t)->attno, rset);
break;
case AGHEAD:
/* do protection on qualification */
dopro(vn, t, -1, byset);
/* merge by-list set into qualification set */
for (i = 0; i < 8; i++)
qset[i] |= byset[i];
break;
case BYHEAD:
case AOP:
syserr("makedset: node %d", t->sym.type);
case RESDOM:
if (((struct qt_res *)t)->resno == 0)
{
/* tid -- ignore right subtree (and this node) */
t = t->left;
continue;
}
if (uset != NULL)
lsetbit(((struct qt_res *)t)->resno, uset);
/* explicit fall-through to "default" case */
default:
/* handle left subtree (recursively) */
makedset(vn, t->left, uset, rset, aset, qset);
/* handle right subtree (iteratively) */
t = t->right;
continue;
}
break;
}
# ifdef xQTR1
if (tTf(53, 15))
{
printf("makedset->\n");
pr_set(uset, "uset");
pr_set(rset, "rset");
pr_set(aset, "aset");
pr_set(qset, "qset");
}
# endif
return;
}
/*
** PROAPPL -- check for protection tuple applicable
**
** A given protection catalog tuple is checked in a query-
** independent way for applicability.
**
** This routine checks such environmental constraints as the
** user, the terminal, and the time of day. The code is
** fairly straightforward, just take a look.
**
** One note: the user and terminal codes contained in the
** protection catalog are blank to mean 'any value' of the
** corresponding field.
**
** Parameters:
** protup -- the protection tuple to compare against.
**
** Returns:
** TRUE -- this tuple applies to the current environment.
** FALSE -- this tuple does not apply.
**
** Side Effects:
** none (unless you include trashing the static vector
** returned by localtime).
**
** Requires:
** localtime()
** Usercode -- the code for the current user.
** Terminal -- the result of a 'ttyn()' call, that is,
** the current terminal id. This should be
** blank if running in background.
**
** Called By:
** protect()
**
** Trace Flags:
** 54
**
** Diagnostics:
** none
**
** Syserrs:
** none
**
** History:
** 8/21/78 (eric) -- written.
*/
proappl(protup)
struct protect *protup;
{
register struct protect *p;
int tvect[2];
register int *tt;
extern int *localtime();
register int mtime;
p = protup;
/* check for correct user [insert clique code here] */
if (!bequal(" ", p->prouser, 2))
{
if (!bequal(p->prouser, Usercode, 2))
return (FALSE);
}
/* check for correct terminal */
# ifndef xV7_UNIX
if (p->proterm != ' ')
{
if (p->proterm != Terminal)
return (FALSE);
}
# endif
# ifdef xV7_UNIX
if (p->proterm[0] != ' ')
{
if (!bequal(p->proterm, Terminal, 2))
return (FALSE);
}
# endif
/* check for correct time of day & week */
time(tvect);
tt = localtime(tvect);
mtime = tt[2] * 60 + tt[1];
if (p->protodbgn > mtime || p->protodend < mtime)
return (FALSE);
if (p->prodowbgn > tt[6] || p->prodowend < tt[6])
return (FALSE);
/* hasn't failed yet -- I guess it's ok */
return (TRUE);
}
/*
** PROCHK -- query-dependent protection tuple check
**
** This routine does the query-dependent part of checking
** the validity of a protection tuple. Unlike proappl,
** which looked at aspects of the environment but not the
** query being run, this routine assumes that the environ-
** ment is ok, and checks that if it applies to this tuple.
**
** Two things are checked. The first is if this tuple applies
** to the operation in question (passed as 'inbit'). The
** second is if the set of domains in the tuple contains the
** set of domains in the query. If either of these fail,
** the return is zero. Otherwise the return is the operation
** bit. In otherwise, the return is the operation to which
** this tuple applies (if any).
**
** As a special check, the domain set is checked for all
** zero. If so, no domains have been referenced for this
** operation at all, and we return zero. In other words, this
** tuple might apply to this operation, but since we don't
** use the operation anyhow we will ignore it. It is important
** to handle things in this way so that the qualification for
** this tuple doesn't get appended if the variable is not
** used in a particular context.
**
** Parameters:
** inbit -- the bit describing the operation to be
** checked. Note that only one bit should
** be set in this word, although this is
** not checked.
** domset -- the set of domains actually referenced
** in this query for the operation described
** by 'inbit'.
** protup -- the tuple in question.
**
** Returns:
** The operation (if any) to which this tuple applies.
**
** Side Effects:
** none
**
** Called By:
** protect() -- in four places.
**
** Trace Flags:
** 55
**
** Diagnostics:
** none
**
** Syserrs:
** none
**
** History:
** 8/21/78 (eric) -- written.
*/
prochk(inbit, domset, protup)
int inbit;
int domset[8];
struct protect *protup;
{
register struct protect *p;
register int *d;
register int i;
p = protup;
d = domset;
# ifdef xQTR1
if (tTf(55, 0))
{
printf("->prochk, inbit=%o, proopset=%o\n", inbit, p->proopset);
pr_set(d, "domset");
pr_set(p->prodomset, "prodomset");
}
# endif
/* check for null domain set, if so return zero */
for (i = 0; i < 8; i++)
if (d[i] != 0)
break;
if (i >= 8)
{
# ifdef xQTR2
tTfp(55, 15, "prochk-> null set\n");
# endif
return (0);
}
/* see if this tuple applies to this operation */
if ((inbit & p->proopset) == 0)
{
# ifdef xQTR2
tTfp(55, 15, "prochk-> no op\n");
# endif
return (0);
}
/* check if domains are a subset */
for (i = 0; i < 8; i++)
{
if ((d[i] & ~p->prodomset[i]) != 0)
{
/* failure */
# ifdef xQTR2
tTfp(55, 15, "prochk-> not subset\n");
# endif
return (0);
}
}
/* this is hereby an "interesting" tuple */
# ifdef xQTR2
if (tTf(55, 15))
printf("prochk-> %d\n", inbit);
# endif
return (inbit);
}
# ifdef xQTR1
/*
** PR_SET -- print set for debugging
**
** This routine prints a 128-bit set for debugging.
**
** Parameters:
** xset -- the set to convert.
** labl -- a label to print before the set.
**
** Returns:
** a pointer to the converted string.
**
** Side Effects:
** none
**
** History:
** 8/22/78 (eric) -- written.
*/
pr_set(xset, labl)
int xset[8];
char *labl;
{
register int *x;
register int i;
register long *y;
printf("\t%s: ", labl);
y = (long *) (x = xset);
if (x == NULL)
{
printf("<NULL>\n");
return;
}
for (i = 7; i >= 0; i--)
printf("%o/", x[i]);
printf(" <> ");
for (i = 0; i < 4; i++)
printf("/%s", locv(y[i]));
printf("\n");
}
# endif