/* t4.c: read table specification */
# include "t.h"
int     oncol;

void
getspec(void)
{
       int     icol, i;

       qcol = findcol() + 1;/* must allow one extra for line at right */
       garray(qcol);
       sep[-1] = -1;
       for (icol = 0; icol < qcol; icol++) {
               sep[icol] = -1;
               evenup[icol] = 0;
               cll[icol][0] = 0;
               for (i = 0; i < MAXHEAD; i++) {
                       csize[icol][i][0] = 0;
                       vsize[icol][i][0] = 0;
                       font[icol][i][0] = lefline[icol][i] = 0;
                       flags[icol][i] = 0;
                       style[icol][i] = 'l';
               }
       }
       for (i = 0; i < MAXHEAD; i++)
               lefline[qcol][i] = 0;   /* fixes sample55 looping */
       nclin = ncol = 0;
       oncol = 0;
       left1flg = rightl = 0;
       readspec();
       Bprint(&tabout, ".rm");
       for (i = 0; i < ncol; i++)
               Bprint(&tabout, " %2s", reg(i, CRIGHT));
       Bprint(&tabout, "\n");
}


void
readspec(void)
{
       int     icol, c, sawchar, stopc, i;
       char    sn[10], *snp, *temp;

       sawchar = icol = 0;
       while (c = get1char()) {
               switch (c) {
               default:
                       if (c != tab) {
                               char buf[64];
                               sprint(buf, "bad table specification character %c", c);
                               error(buf);
                       }
               case ' ': /* note this is also case tab */
                       continue;
               case '\n':
                       if (sawchar == 0)
                               continue;
               case ',':
               case '.': /* end of table specification */
                       ncol = max(ncol, icol);
                       if (lefline[ncol][nclin] > 0) {
                               ncol++;
                               rightl++;
                       };
                       if (sawchar)
                               nclin++;
                       if (nclin >= MAXHEAD)
                               error("too many lines in specification");
                       icol = 0;
                       if (ncol == 0 || nclin == 0)
                               error("no specification");
                       if (c == '.') {
                               while ((c = get1char()) && c != '\n')
                                       if (c != ' ' && c != '\t')
                                               error("dot not last character on format line");
                               /* fix up sep - default is 3 except at edge */
                               for (icol = 0; icol < ncol; icol++)
                                       if (sep[icol] < 0)
                                               sep[icol] =  icol + 1 < ncol ? 3 : 2;
                               if (oncol == 0)
                                       oncol = ncol;
                               else if (oncol + 2 < ncol)
                                       error("tried to widen table in T&, not allowed");
                               return;
                       }
                       sawchar = 0;
                       continue;
               case 'C':
               case 'S':
               case 'R':
               case 'N':
               case 'L':
               case 'A':
                       c += ('a' - 'A');
               case '_':
                       if (c == '_')
                               c = '-';
               case '=':
               case '-':
               case '^':
               case 'c':
               case 's':
               case 'n':
               case 'r':
               case 'l':
               case 'a':
                       style[icol][nclin] = c;
                       if (c == 's' && icol <= 0)
                               error("first column can not be S-type");
                       if (c == 's' && style[icol-1][nclin] == 'a') {
                               Bprint(&tabout, ".tm warning: can't span a-type cols, changed to l\n");
                               style[icol-1][nclin] = 'l';
                       }
                       if (c == 's' && style[icol-1][nclin] == 'n') {
                               Bprint(&tabout, ".tm warning: can't span n-type cols, changed to c\n");
                               style[icol-1][nclin] = 'c';
                       }
                       icol++;
                       if (c == '^' && nclin <= 0)
                               error("first row can not contain vertical span");
                       if (icol > qcol)
                               error("too many columns in table");
                       sawchar = 1;
                       continue;
               case 'b':
               case 'i':
                       c += 'A' - 'a';
               case 'B':
               case 'I':
                       if (icol == 0)
                               continue;
                       snp = font[icol-1][nclin];
                       snp[0] = (c == 'I' ? '2' : '3');
                       snp[1] = 0;
                       continue;
               case 't':
               case 'T':
                       if (icol > 0)
                               flags[icol-1][nclin] |= CTOP;
                       continue;
               case 'd':
               case 'D':
                       if (icol > 0)
                               flags[icol-1][nclin] |= CDOWN;
                       continue;
               case 'f':
               case 'F':
                       if (icol == 0)
                               continue;
                       snp = font[icol-1][nclin];
                       snp[0] = snp[1] = stopc = 0;
                       for (i = 0; i < 2; i++) {
                               c = get1char();
                               if (i == 0 && c == '(') {
                                       stopc = ')';
                                       c = get1char();
                               }
                               if (c == 0)
                                       break;
                               if (c == stopc) {
                                       stopc = 0;
                                       break;
                               }
                               if (stopc == 0)
                                       if (c == ' ' || c == tab )
                                               break;
                               if (c == '\n' || c == '|') {
                                       un1getc(c);
                                       break;
                               }
                               snp[i] = c;
                               if (c >= '0' && c <= '9')
                                       break;
                       }
                       if (stopc)
                               if (get1char() != stopc)
                                       error("Nonterminated font name");
                       continue;
               case 'P':
               case 'p':
                       if (icol <= 0)
                               continue;
                       temp = snp = csize[icol-1][nclin];
                       while (c = get1char()) {
                               if (c == ' ' || c == tab || c == '\n')
                                       break;
                               if (c == '-' || c == '+')
                                       if (snp > temp)
                                               break;
                                       else
                                               *snp++ = c;
                               else if (digit(c))
                                       *snp++ = c;
                               else
                                       break;
                               if (snp - temp > 4)
                                       error("point size too large");
                       }
                       *snp = 0;
                       if (atoi(temp) > 36)
                               error("point size unreasonable");
                       un1getc (c);
                       continue;
               case 'V':
               case 'v':
                       if (icol <= 0)
                               continue;
                       temp = snp = vsize[icol-1][nclin];
                       while (c = get1char()) {
                               if (c == ' ' || c == tab || c == '\n')
                                       break;
                               if (c == '-' || c == '+')
                                       if (snp > temp)
                                               break;
                                       else
                                               *snp++ = c;
                               else if (digit(c))
                                       *snp++ = c;
                               else
                                       break;
                               if (snp - temp > 4)
                                       error("vertical spacing value too large");
                       }
                       *snp = 0;
                       un1getc(c);
                       continue;
               case 'w':
               case 'W':
                       snp = cll [icol-1];
                       /* Dale Smith didn't like this check - possible to have two text blocks
                  of different widths now ....
                       if (*snp)
                               {
                               Bprint(&tabout, "Ignored second width specification");
                               continue;
                               }
               /* end commented out code ... */
                       stopc = 0;
                       while (c = get1char()) {
                               if (snp == cll[icol-1] && c == '(') {
                                       stopc = ')';
                                       continue;
                               }
                               if ( !stopc && (c > '9' || c < '0'))
                                       break;
                               if (stopc && c == stopc)
                                       break;
                               *snp++ = c;
                       }
                       *snp = 0;
                       if (snp - cll[icol-1] > CLLEN)
                               error ("column width too long");
                       if (!stopc)
                               un1getc(c);
                       continue;
               case 'e':
               case 'E':
                       if (icol < 1)
                               continue;
                       evenup[icol-1] = 1;
                       evenflg = 1;
                       continue;
               case 'z':
               case 'Z': /* zero width-ignre width this item */
                       if (icol < 1)
                               continue;
                       flags[icol-1][nclin] |= ZEROW;
                       continue;
               case 'u':
               case 'U': /* half line up */
                       if (icol < 1)
                               continue;
                       flags[icol-1][nclin] |= HALFUP;
                       continue;
               case '0':
               case '1':
               case '2':
               case '3':
               case '4':
               case '5':
               case '6':
               case '7':
               case '8':
               case '9':
                       sn[0] = c;
                       snp = sn + 1;
                       while (digit(*snp++ = c = get1char()))
                               ;
                       un1getc(c);
                       sep[icol-1] = max(sep[icol-1], numb(sn));
                       continue;
               case '|':
                       lefline[icol][nclin]++;
                       if (icol == 0)
                               left1flg = 1;
                       continue;
               }
       }
       error("EOF reading table specification");
}


int
findcol(void)
{
# define FLNLIM 200
       /* this counts the number of columns and then puts the line back*/
       char    *s, line[FLNLIM+2], *p;
       int     c, n = 0, inpar = 0;

       while ((c = get1char()) != 0 && c == ' ')
               ;
       if (c != '\n')
               un1getc(c);
       for (s = line; *s = c = get1char(); s++) {
               if (c == ')')
                       inpar = 0;
               if (inpar)
                       continue;
               if (c == '\n' || c == 0 || c == '.' || c == ',')
                       break;
               else if (c == '(')
                       inpar = 1;
               else if (s >= line + FLNLIM)
                       error("too long spec line");
       }
       for (p = line; p < s; p++)
               switch (*p) {
               case 'l':
               case 'r':
               case 'c':
               case 'n':
               case 'a':
               case 's':
               case 'L':
               case 'R':
               case 'C':
               case 'N':
               case 'A':
               case 'S':
               case '-':
               case '=':
               case '_':
                       n++;
               }
       while (p >= line)
               un1getc(*p--);
       return(n);
}


void
garray(int qcol)
{
       style =  (int (*)[]) getcore(MAXHEAD * qcol, sizeof(int));
       evenup = (int *) getcore(qcol, sizeof(int));
       lefline = (int (*)[]) getcore(MAXHEAD * (qcol + 1), sizeof (int)); /*+1 for sample55 loop - others may need it too*/
       font = (char (*)[][2]) getcore(MAXHEAD * qcol, 2);
       csize = (char (*)[MAXHEAD][4]) getcore(MAXHEAD * qcol, 4);
       vsize = (char (*)[MAXHEAD][4]) getcore(MAXHEAD * qcol, 4);
       flags =  (int (*)[]) getcore(MAXHEAD * qcol, sizeof(int));
       cll = (char (*)[])getcore(qcol, CLLEN);
       sep = (int *) getcore(qcol + 1, sizeof(int));
       sep++; /* sep[-1] must be legal */
       used = (int *) getcore(qcol + 1, sizeof(int));
       lused = (int *) getcore(qcol + 1, sizeof(int));
       rused = (int *) getcore(qcol + 1, sizeof(int));
       doubled = (int *) getcore(qcol + 1, sizeof(int));
       acase = (int *) getcore(qcol + 1, sizeof(int));
       topat = (int *) getcore(qcol + 1, sizeof(int));
}


char    *
getcore(int a, int b)
{
       char    *x;
       x = calloc(a, b);
       if (x == 0)
               error("Couldn't get memory");
       return(x);
}


void
freearr(void)
{
       free(style);
       free(evenup);
       free(lefline);
       free(flags);
       free(font);
       free(csize);
       free(vsize);
       free(cll);
       free(--sep);    /* netnews says this should be --sep because incremented earlier! */
       free(used);
       free(lused);
       free(rused);
       free(doubled);
       free(acase);
       free(topat);
}