+ if (l == class_none || r == class_all) {
+ class_dec(l);
+ class_dec(r);
+ return (class_none);
+ }
+
+ if (r == class_none)
+ return (l);
+
+ /* --- Do the job --- */
+
+ return (class__binop(l, r, clNode_diff));
+}
+
+/* --- @class_isect@ --- *
+ *
+ * Arguments: @class_node *l@ = left argument
+ * @class_node *r@ = right argument
+ *
+ * Returns: A class node representing the intersection of the two
+ * classes.
+ *
+ * Use: Performs the intersecion operation on classes. If the types
+ * don't match, then a null pointer is returned. Both @l@ and
+ * @r@ may be modified, and will be decremented before they get
+ * returned to you. If you don't want that to happen, ensure
+ * that you've claimed a reference to the original versions.
+ */
+
+class_node *class_isect(class_node *l, class_node *r)
+{
+ /* --- Check for the really simple cases --- */
+
+ if (l == class_none || r == class_none) {
+ class_dec(l);
+ class_dec(r);
+ return (class_none);
+ }
+
+ if (l == class_all)
+ return (r);
+ if (r == class_all)
+ return (l);
+
+ /* --- Do the job --- */
+
+ return (class__binop(l, r, clNode_isect));
+}
+
+/*----- Building the predefined classes -----------------------------------*/
+
+/* --- @class_addUser@ --- *
+ *
+ * Arguments: @class_node *c@ = pointer to a class node (may be null)
+ * @uid_t u@ = user id number
+ *
+ * Returns: Pointer to the combined node.
+ *
+ * Use: Adds a user to a node, maybe hashifying it.
+ */
+
+class_node *class_addUser(class_node *c, uid_t u)
+{
+ if (!c)
+ return (class_fromUser(clType_user, u));
+ if ((c->type & clNode_mask) == clNode_immed)
+ c = class__hashify(c);
+ sym_find(&c->v.t, (char *)&u, sizeof(u), sizeof(sym_base), 0);
+ return (c);
+}
+
+/* --- @class_addString@ --- *
+ *
+ * Arguments: @class_node *c@ = pointer to a class node (may be null)
+ * @const char *s@ = pointer to a string
+ *
+ * Returns: Pointer to the combined node.
+ *
+ * Use: Adds a user to a node, maybe hashifying it.
+ */
+
+class_node *class_addString(class_node *c, const char *s)
+{
+ class_node *n = class_fromString(clType_host, s); /* hack */
+ if (c)
+ return (class_union(c, n));
+ else
+ return (n);
+}
+
+/*----- Matching functions ------------------------------------------------*/
+
+/* --- @class_matchUser@ --- *
+ *
+ * Arguments: @class_node *c@ = pointer to root class node
+ * @uid_t u@ = user id number
+ *
+ * Returns: Nonzero if it matches, zero if it doesn't.
+ *
+ * Use: Determines whether a user is matched by a class. Assumes
+ * that the types are correct.
+ */
+
+int class_matchUser(class_node *c, uid_t u)
+{
+ class_node *cc;
+
+ for (cc = 0; c; c = cc, cc = 0) {
+ if (c == class_none)
+ return (0);
+ if (c == class_all)
+ return (1);
+ switch (c->type & clNode_mask) {
+ case clNode_immed:
+ return (u == c->v.u);
+ break;
+ case clNode_hash:
+ return (sym_find(&c->v.t, (char *)&u, sizeof(u), 0, 0) != 0);
+ break;
+ case clNode_union:
+ if (class_matchUser(c->v.c.l, u))
+ return (1);
+ cc = c->v.c.r;
+ break;
+ case clNode_isect:
+ if (!class_matchUser(c->v.c.l, u))
+ return (0);
+ cc = c->v.c.r;
+ break;
+ case clNode_diff:
+ return (class_matchUser(c->v.c.l, u) &&
+ !class_matchUser(c->v.c.r, u));
+ break;
+ }
+ }
+
+ die("internal error: can't get here in class_matchUser");
+ return (0);
+}
+
+/* --- @class_matchCommand@ --- *
+ *
+ * Arguments: @class_node *c@ = pointer to root class node
+ * @const char *s@ = pointer to a string
+ *
+ * Returns: Nonzero if it matches, zero if it doesn't.
+ *
+ * Use: Determines whether a string is matched by a class. Assumes
+ * that the types are correct.
+ */
+
+int class_matchCommand(class_node *c, const char *s)
+{
+ class_node *cc;
+
+ for (cc = 0; c; c = cc, cc = 0) {
+ if (c == class_none)
+ return (0);
+ if (c == class_all)
+ return (1);
+ switch (c->type & clNode_mask) {
+ case clNode_immed:
+ return (class__wildMatch(c->v.s, s));
+ break;
+ case clNode_hash:
+ return (sym_find(&c->v.t, s, -1, 0, 0) != 0);
+ break;
+ case clNode_union:
+ if (class_matchCommand(c->v.c.l, s))
+ return (1);
+ cc = c->v.c.r;
+ break;
+ case clNode_isect:
+ if (!class_matchCommand(c->v.c.l, s))
+ return (0);
+ cc = c->v.c.r;
+ break;
+ case clNode_diff:
+ return (class_matchCommand(c->v.c.l, s) &&
+ !class_matchCommand(c->v.c.r, s));
+ break;
+ }
+ }
+
+ die("internal error: can't get here in class_matchCommand");
+ return (0);
+}
+
+/* --- @class_matchHost@ --- *
+ *
+ * Arguments: @class_node *c@ = pointer to root class node
+ * @struct in_addr a@ = IP address to match
+ *
+ * Returns: Nonzero if it matches, zero if it doesn't.
+ *
+ * Use: Determines whether a host matches a host class. Assumes
+ * that the types are correct. The actual mechanism is a bit
+ * odd here, but I think this is the Right Thing. At each stage
+ * I try to match %%@/all/%% of the possible names for the host.
+ * Thus host `splat' with address 1.2.3.4 would fail to match
+ * the class "1.2.*" - "splat". This seems to be what the
+ * author intuitively expects. It's just a bit weird.
+ */
+
+static int class__doMatchHost(class_node *c, const char *ip,
+ const char *name, char **aliases)
+{
+ class_node *cc;