+/* Copyright (c) 1979 Regents of the University of California */
+
+static char sccsid[] = "@(#)case.c 1.1 %G%";
+
+#include "whoami.h"
+#include "0.h"
+#include "tree.h"
+#include "opcode.h"
+
+/*
+ * The structure used to
+ * hold information about
+ * each case label.
+ */
+struct ct {
+ long clong;
+ int cline;
+};
+
+#ifdef OBJ
+/*
+ * Caseop generates the
+ * pascal case statement code
+ */
+caseop(r)
+ int *r;
+{
+ register struct nl *p;
+ register struct ct *ctab;
+ register *cs;
+ int *cl;
+ double low, high;
+ short *brtab;
+ char *brtab0;
+ char *csend;
+ int w, i, j, m, n;
+ int nr, goc;
+
+ goc = gocnt;
+ /*
+ * Obtain selector attributes:
+ * p type
+ * w width
+ * low lwb(p)
+ * high upb(p)
+ */
+ p = rvalue((int *) r[2], NLNIL , RREQ );
+ if (p != NIL) {
+ if (isnta(p, "bcsi")) {
+ error("Case selectors cannot be %ss", nameof(p));
+ p = NIL;
+ } else {
+ cl = p;
+ if (p->class != RANGE)
+ cl = p->type;
+ if (cl == NIL)
+ p = NIL;
+ else {
+ w = width(p);
+#ifdef DEBUG
+ if (hp21mx)
+ w = 2;
+#endif
+ low = cl->range[0];
+ high = cl->range[1];
+ }
+ }
+ }
+ /*
+ * Count # of cases
+ */
+ n = 0;
+ for (cl = r[3]; cl != NIL; cl = cl[2]) {
+ cs = cl[1];
+ if (cs == NIL)
+ continue;
+ for (cs = cs[2]; cs != NIL; cs = cs[2])
+ n++;
+ }
+ /*
+ * Allocate case table space
+ */
+ ctab = i = malloc(n * sizeof *ctab);
+ if (i == -1) {
+ error("Ran out of memory (case)");
+ pexit(DIED);
+ }
+ /*
+ * Check the legality of the
+ * labels and count the number
+ * of good labels
+ */
+ m = 0;
+ for (cl = r[3]; cl != NIL; cl = cl[2]) {
+ cs = cl[1];
+ if (cs == NIL)
+ continue;
+ line = cs[1];
+ for (cs = cs[2]; cs != NIL; cs = cs[2]) {
+ gconst(cs[1]);
+ if (p == NIL || con.ctype == NIL)
+ continue;
+ if (incompat(con.ctype, p, NIL )) {
+ cerror("Case label type clashed with case selector expression type");
+ continue;
+ }
+ if (con.crval < low || con.crval > high) {
+ error("Case label out of range");
+ continue;
+ }
+ ctab[m].clong = con.crval;
+ ctab[m].cline = line;
+ m++;
+ }
+ }
+
+ /*
+ * Check for duplicate labels
+ */
+ for (i = 0; i < m; i++)
+ for (j = 0; j < m; j++)
+ if (ctab[i].clong == ctab[j].clong) {
+ if (i == j)
+ continue;
+ if (j < i)
+ break;
+ error("Multiply defined label in case, lines %d and %d", ctab[i].cline, ctab[j].cline);
+ }
+ /*
+ * Put out case operator and
+ * leave space for the
+ * branch table
+ */
+ if (p != NIL) {
+ put(2, O_CASE1OP + (w >> 1), n);
+ brtab = brtab0 = lc;
+ putspace(n * 2);
+ put(1, O_CASEBEG);
+ for (i=0; i<m; i++)
+ put( 2 , O_CASE1 + (w >> 1), ctab[i].clong);
+ put(1, O_CASEEND);
+ }
+ csend = getlab();
+ put(2, O_TRA, csend);
+ /*
+ * Free the case
+ * table space.
+ */
+ free(ctab);
+ /*
+ * Generate code for each
+ * statement. Patch branch
+ * table to beginning of each
+ * statement and follow each
+ * statement with a branch back
+ * to the TRA above.
+ */
+ nr = 1;
+ for (cl = r[3]; cl != NIL; cl = cl[2]) {
+ cs = cl[1];
+ if (cs == NIL)
+ continue;
+ if (p != NIL)
+ for (cs = cs[2]; cs != NIL; cs = cs[2]) {
+ patchfil(brtab - 1, lc - brtab0, 1);
+ brtab++;
+ }
+ cs = cl[1];
+ putcnt();
+ level++;
+ statement(cs[3]);
+ nr &= noreach;
+ noreach = 0;
+ put(2, O_TRA, csend);
+ level--;
+ if (gotos[cbn])
+ ungoto();
+ }
+ /*
+ * Patch the termination branch
+ */
+ patch(csend);
+ noreach = nr;
+ if (goc != gocnt)
+ putcnt();
+}
+#endif OBJ