+ }
+
+ /* --- Handle `friendly' nodes --- */
+
+ if ((l->type & r->type & clFlag_friendly)) {
+
+ /* --- Consider promoting an item to a hash --- *
+ *
+ * If both are immediate nodes, and they're both `friendly', we can
+ * profitably hash them together.
+ *
+ * Life gets complicated when we do subtraction, because it's not
+ * commutative. In this case, I have to promote the left operand anyway.
+ */
+
+ if (lnode == clNode_immed &&
+ (rnode == clNode_immed || op == clNode_diff)) {
+
+ /* --- Quick check for triviality --- *
+ *
+ * There are some more short-cuts I can employ if the values are
+ * the same.
+ */
+
+ if (rnode == clNode_immed) {
+
+ /* --- See whether the two items are equal --- */
+
+ int eq = (type & clType_user ?
+ l->v.u == r->v.u : strcmp(l->v.s, r->v.s) == 0);
+
+ /* --- Now do something appropriate --- */
+
+ switch (op) {
+ case clNode_union:
+ if (eq) {
+ class_dec(r);
+ return (l);
+ }
+ break;
+ case clNode_diff:
+ if (eq) {
+ class_dec(l);
+ class_dec(r);
+ return (class_none);
+ }
+ break;
+ case clNode_isect:
+ if (eq) {
+ class_dec(r);
+ return (l);
+ } else {
+ class_dec(l);
+ class_dec(r);
+ return (class_none);
+ }
+ break;
+ }
+ }
+
+ /* --- Turn @l@ into a hash containing itself --- */
+
+ l = class__hashify(l);
+ lnode = clNode_hash;
+ }
+
+ /* --- Otherwise, make @l@ the hash --- *
+ *
+ * Both @l@ and @r@ are friendly. Since they're not both immediates,
+ * one must be a hash.
+ */
+
+ else if ((l->type & clNode_mask) == clNode_immed) {
+ class_node *tn;
+ unsigned tt;
+
+ tn = l, l = r, r = tn;
+ tt = lnode, lnode = rnode, rnode = tt;
+ }
+
+ /* --- Now merge @r@ with @l@ --- */
+
+ l = class_mod(l);
+
+ switch (op) {
+
+ /* --- The union operation isn't hard --- */
+
+ case clNode_union:
+ if (rnode == clNode_immed) {
+ if (type & clType_user) {
+ sym_find(&l->v.t, (char *)&r->v.u,
+ sizeof(r->v.u), sizeof(sym_base), 0);
+ } else
+ sym_find(&l->v.t, r->v.s, -1, sizeof(sym_base), 0);
+ } else {
+ sym_iter i;
+ sym_base *b;
+
+ for (sym_createIter(&i, &r->v.t); (b = sym_next(&i)) != 0; )
+ sym_find(&l->v.t, b->name, b->len, sizeof(sym_base), 0);
+ }
+ break;
+
+ /* --- Set difference is similar in spirit --- */
+
+ case clNode_diff:
+ if (rnode == clNode_immed) {
+ sym_base *f;
+
+ if (type & clType_user)
+ f = sym_find(&l->v.t, (char *)&r->v.u, sizeof(r->v.u), 0, 0);
+ else
+ f = sym_find(&l->v.t, r->v.s, -1, 0, 0);
+ if (f)
+ sym_remove(&l->v.t, f);
+ } else {
+ sym_iter i;
+ sym_base *b, *f;
+
+ for (sym_createIter(&i, &r->v.t); (b = sym_next(&i)) != 0; ) {
+ if ((f = sym_find(&l->v.t, b->name, b->len, 0, 0)) != 0)
+ sym_remove(&l->v.t, f);
+ }
+ }
+ break;
+
+ /* --- Intersection is wild and wacky --- */
+
+ case clNode_isect:
+ if (rnode == clNode_immed) {
+ sym_base *f;
+
+ if (type & clType_user)
+ f = sym_find(&l->v.t, (char *)&r->v.u, sizeof(r->v.u), 0, 0);
+ else
+ f = sym_find(&l->v.t, r->v.s, -1, 0, 0);
+ if (f) {
+ class_dec(l);
+ return (r);
+ } else {
+ class_dec(l);
+ class_dec(r);
+ return (class_none);
+ }
+ } else {
+ sym_iter i;
+ sym_base *b;
+
+ for (sym_createIter(&i, &l->v.t); (b = sym_next(&i)) != 0; ) {
+ if (!sym_find(&r->v.t, b->name, b->len, 0, 0))
+ sym_remove(&l->v.t, b);
+ }
+ }
+ break;
+ }
+
+ /* --- Now trim the @l@ table to size --- *
+ *
+ * It may have lost a load of elements. Maybe it can be represented
+ * better as an immediate or even as @class_none@.
+ */
+
+ {
+ sym_iter i;
+ sym_base *b;
+
+ class_dec(r);
+
+ sym_createIter(&i, &l->v.t);
+ if ((b = sym_next(&i)) == 0) {
+ class_dec(l);
+ return (class_none);
+ }
+ if (!sym_next(&i)) {
+ if (type & clType_user) {
+ uid_t u = *(uid_t *)b->name;
+ sym_destroyTable(&l->v.t);
+ l->type = (l->type & ~clNode_mask) | clNode_immed;
+ l->v.u = u;
+ } else {
+ char *s = xstrdup(b->name);
+ sym_destroyTable(&l->v.t);
+ l->type = (l->type & ~clNode_mask) | clNode_immed;
+ l->v.s = s;
+ }
+ }
+ }
+
+ /* --- Done --- */
+
+ return (l);
+ }
+
+ /* --- Unfriendly nodes --- *
+ *
+ * Create a binop node and return that. If @l@ is a binop node, and @r@
+ * isn't, and the operation isn't set difference (i.e., it's commutative)
+ * then swap the two over to lessen the depth of recursion later.
+ */
+