home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d197 / nro.lha / Nro / nrocmd.c < prev    next >
C/C++ Source or Header  |  1989-03-28  |  34KB  |  1,154 lines

  1. /*
  2.  *      Command processor for NRO text processor
  3.  *
  4.  *      Originally by Stephen L. Browning, 5723 North Parker Avenue
  5.  *      Indianapolis, Indiana 46220
  6.  *
  7.  *      Transformed beyond immediate recognition, and
  8.  *      adapted for Amiga by Olaf Seibert, KosmoSoft
  9.  *
  10.  *      Vossendijk 149-1 (study)   Beek 5 (home)
  11.  *      5634 TN  Nijmegen          5815 CS  Merselo
  12.  *      The Netherlands            The Netherlands
  13.  *      Phone:
  14.  *             (...-31)80561045     (...-31)4786205
  15.  *          or 080-561045           04786-205
  16.  *
  17.  *      This program is NOT in the public domain. It may, however
  18.  *      be distributed only at no charge, and this notice must be
  19.  *      included unaltered.
  20.  */
  21.  
  22. #include <stdio.h>
  23. #include "nro.h"
  24. #include "nroxtrn.h"
  25.  
  26. uchar *skipbl();
  27. uchar *skipwd();
  28.  
  29. /*
  30.  *      Communicating the current file through a global
  31.  *      variable is a bit of a kludge. We could as well use
  32.  *      it everywhere instead of passing it as a parameter
  33.  *      all the time. But that would not be very `structured',
  34.  *      whatever that may be.
  35.  */
  36.  
  37. comand(word)
  38. uchar *word;
  39. {
  40.         int ct, val;
  41.         int spval;
  42.         uchar argtyp, chr;
  43.         uchar *macexp;
  44.  
  45.         val = getcmdwrd(word, infile);
  46.         if (word[0] == env.c2chr) {
  47.                 env.dontbrk = TRUE;
  48.                 word++;
  49.         } else if (word[0] == env.cmdchr) {
  50.                 word++;
  51.         } else if (val > 0) {
  52.                 putbak(' ');
  53.                 pbstr(word);
  54.                 return;
  55.         }
  56.  
  57.         ct = comtyp(word, &macexp);
  58.  
  59.         if (ct == UNKNOWN) {
  60.                 error("nro: unrecognized command %s\n", word);
  61.                 env.dontbrk = FALSE;
  62.                 return;
  63.         }
  64.         if (! (ct & NOARGS))
  65.                 val = getval(&argtyp, infile);  /* Eat more of line */
  66.  
  67.         switch (ct & ~NOARGS) {
  68.         case BO: /* Bold face */
  69.                 set(&env.boval, val, argtyp, 1, -1, HUGE);
  70.                 if (env.boval)  env.reqmode |= FXBO;
  71.                 else            env.reqmode &= ~FXBO;
  72.                 if (dc.bsflg != BSAMIGA)
  73.                         env.cuval = env.ulval = 0;
  74.                 break;
  75.         case BP: /* Begin page */
  76.                 if (pg.lineno > 0) space(pg.plval);
  77.                 set(&pg.curpag, val, argtyp, pg.curpag+1, -HUGE, HUGE);
  78.                 pg.newpag = pg.curpag;
  79.                 break;
  80.         case BR: /* Break */
  81.                 dobrk();
  82.                 break;
  83.         case BS: /* Backspaces in output: 0=No, Amiga; 1=Yes; 2=Use CR */
  84.                 set(&dc.bsflg, val, argtyp, 1, 0, 2);
  85.                 break;
  86.         case C2: /* No-break command character */
  87.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  88.                 if (iseol(chr)) env.c2chr = C2CHAR;
  89.                 else { env.c2chr = chr; ct = 0; }
  90.                 break;
  91.         case CC: /* Command character */
  92.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  93.                 if (iseol(chr)) env.cmdchr = CMDCHAR;
  94.                 else { env.cmdchr = chr; ct = 0; }
  95.                 break;
  96.         case CE: /* Center */
  97.                 dobrk();
  98.                 set(&env.ceval, val, argtyp, 1, -1, HUGE);
  99.                 break;
  100.         case COMMENT:
  101.                 while (chr = ngetc(infile), isnteol(chr));
  102.                 break;
  103.         case CU: /* Continuous underline */
  104.                 set(&env.cuval, val, argtyp, 1, -1, HUGE);
  105.                 if (env.cuval)  env.reqmode |= FXUL;
  106.                 else            env.reqmode &= ~FXUL;
  107.                 if (dc.bsflg != BSAMIGA)
  108.                         env.ulval = env.boval = 0;
  109.                 break;
  110.         case DE: /* Define macro */
  111.                 defmac(word, infile);
  112.                 break;
  113.         case EF: /* Even footer */
  114.                 gettl(infile, pg.efoot, NULL, &pg.eflim[0], NULL);
  115.                 break;
  116.         case EH: /* Even header */
  117.                 gettl(infile, pg.ehead, NULL, &pg.ehlim[0], NULL);
  118.                 break;
  119.         case EL: /* Else */
  120.                 doel(infile);
  121.                 break;
  122.         case EN: /* End macro definition */
  123.                 error("nro: missing .de command\n");
  124.                 break;
  125.         case EV: /* Environment switch */
  126.                 if (isdigit(argtyp)) {  /* Supplied argument: push */
  127.                         if (dc.envsp >= ENVSTACK-1) {
  128.                                 error("nro: cannot push environment.\n"); break;
  129.                         }
  130.                         spval = dc.envstack[dc.envsp];  /* Current environment */
  131.                         dc.envstack[++dc.envsp] = val;  /* Save number on stack*/
  132.                         storenv(spval);                                 /* Save current envir. */
  133.                         loadenv(val);                                   /* Get new one         */
  134.                 } else {        /* Pop an environment */
  135.                         if ((int) dc.envsp <= 0) {
  136.                                 error("*** nro: cannot pop environment.\n"); break;
  137.                         }
  138.                         spval = dc.envstack[dc.envsp];  /* Current environment */
  139.                         val = dc.envstack[--dc.envsp];  /* Number of old env   */
  140.                         if (argtyp == '-') freenv(spval);/* Dump current environ*/
  141.                         else storenv(spval);                    /* ..or save it        */
  142.                         loadenv(val);                                   /* Get old one back    */
  143.                 }
  144.                 break;
  145.         case FI: /* Fill */
  146.                 dobrk();
  147.                 env.fill = YES;
  148.                 break;
  149.         case FO: /* Footer */
  150.                 gettl(infile, pg.efoot, pg.ofoot, &pg.eflim[0], &pg.oflim[0]);
  151.                 break;
  152.         case HE: /* Header */
  153.                 gettl(infile, pg.ehead, pg.ohead, &pg.ehlim[0], &pg.ohlim[0]);
  154.                 break;
  155.         case IE:
  156.         case IF:
  157.                 doieif(infile, ct & ~NOARGS);
  158.                 break;
  159.         case IN: /* Indenting */
  160.                 dobrk();
  161.                 set(&env.inval, val, argtyp, 0, 0, env.tmval-1);
  162.                 env.tival = env.inval;
  163.                 break;
  164.         case IT: /* Italic face */
  165.                 set(&env.itval, val, argtyp, 1, -1, HUGE);
  166.                 if (env.itval)  env.reqmode |= FXIT;
  167.                 else            env.reqmode &= ~FXIT;
  168.                 if (dc.bsflg != BSAMIGA)
  169.                         error("nro: italics cannot be done by overstrike :-)\n");
  170.                 break;
  171.         case JU: /* Justify */
  172.                 env.juval = YES;
  173.                 break;
  174.         case LS: /* Line spacing */
  175.                 set(&env.lsval, val, argtyp, 1, 1, HUGE);
  176.                 break;
  177.         case M1: /* Set topmost margin */
  178.                 set(&pg.m1val, val, argtyp, 2, 0, HUGE);
  179.                 break;
  180.         case M2: /* Set second top margin */
  181.                 set(&pg.m2val, val, argtyp, 2, 0, HUGE);
  182.                 break;
  183.         case M3: /* Set first bottom margin */
  184.                 set(&pg.m3val, val, argtyp, 2, 0, HUGE);
  185.                 pg.bottom = pg.plval - pg.m4val - pg.m3val;
  186.                 break;
  187.         case M4: /* Set bottom-most margin */
  188.                 set(&pg.m4val, val, argtyp, 2, 0, HUGE);
  189.                 pg.bottom = pg.plval - pg.m4val - pg.m3val;
  190.                 break;
  191.         case MACRO: /* Macro expansion */
  192.                 maceval(macexp, infile);
  193.                 break;
  194.         case NE: /* Need n lines */
  195.                 /* dobrk(); */
  196.                 if ((pg.bottom-pg.lineno+1) < (val*env.lsval)) space(pg.plval);
  197.                 break;
  198.         case NF: /* No fill */
  199.                 dobrk();
  200.                 env.fill = NO;
  201.                 break;
  202.         case NJ: /* No justify */
  203.                 env.juval = NO;
  204.                 break;
  205.         case NR: /* Set number register */
  206.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  207.                 if (!isalpha(chr)) {
  208.                         error("nro: invalid or missing number register name\n");
  209.                 } else {
  210.                         ct = tolower(chr) - 'a';
  211.                         val = getval(&chr, infile);
  212.                         set(&dc.nr[ct], val, chr, 0, -HUGE, HUGE);
  213.                 }
  214.                 ct = 0;
  215.                 break;
  216.         case OF: /* Odd footer */
  217.                 gettl(infile, pg.ofoot, NULL, &pg.oflim[0], NULL);
  218.                 break;
  219.         case OH: /* Odd header */
  220.                 gettl(infile, pg.ohead, NULL, &pg.ohlim[0], NULL);
  221.                 break;
  222.         case PC: /* Page number character */
  223.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  224.                 if (iseol(chr)) env.pgchr = EOS;
  225.                 else { env.pgchr = chr; ct = 0; }
  226.                 break;
  227.         case PL: /* Page length */
  228.                 set(&pg.plval, val, argtyp, PAGELEN,
  229.                         pg.m1val+pg.m2val+pg.m3val+pg.m4val+1, HUGE);
  230.                 pg.bottom = pg.plval - pg.m3val - pg.m4val;
  231.                 break;
  232.         case PN: /* Page numbering mode */
  233.                 set(&env.pnflg, val, argtyp, PNARABIC, PNARABIC, PNUROMAN);
  234.                 break;
  235.         case PO: /* Page offset */
  236.                 set(&pg.offset, val, argtyp, 0, 0, HUGE);
  237.                 break;
  238.         case RM: /* Right margin */
  239.                 set(&env.rmval, val, argtyp, PAGEWIDTH, env.tival+1, HUGE);
  240.                 env.tmval = env.rmval;
  241.                 break;
  242.         case SO: /* Source file */
  243.                 if (getcmdwrd(word, infile) == EOF) break;
  244.                 skipeol(infile);
  245.                 ct = NOARGS;
  246.                 if (dc.flevel+1 >= NFILES) {
  247.                         error("nro: .so commands nested too deeply\n");
  248.                         break;
  249.                 }
  250.                 if ((sofile[dc.flevel+1] = fopen(word, "r")) == NULL) {
  251.                         error("nro: unable to open %s\n", word);
  252.                         break;
  253.                 }
  254.                 if (verbose > 2) error("nro: processing file '%s'\n", word);
  255.                 sopbb[dc.flevel] = mac.pbb;     /* Stack push back stacks */
  256.                 mac.pbb = mac.ppb + 1;
  257.                 dc.flevel++;
  258.                 infile = sofile[dc.flevel];
  259.                 break;
  260.         case SP: /* Space */
  261.                 set(&spval, val, argtyp, 1, 0, HUGE);
  262.                 space(spval * env.lsval);
  263.                 break;
  264.         case TA: /* Tab settings */
  265.                 tabs(infile);
  266.                 break;
  267.         case TI: /* Temporary indent */
  268.                 dobrk();
  269.                 set(&env.tival, val, argtyp, 0, 0, env.tmval);
  270.                 break;
  271.         case TM: /* Terminal Message */
  272.                 fflush(stdout);
  273.                 if (env.dontbrk == FALSE || pout != stdout) {
  274.                         while (val = ngetc(infile), isnteol(val) && val != EOF)
  275.                                 putc(val, stderr);
  276.                         putc('\n', stderr);
  277.                         fflush(stderr);
  278.                 } else ct = 0; /* Skip rest of command line */
  279.                 break;
  280.         case UL: /* Underline */
  281.                 set(&env.ulval, val, argtyp, -1, 1, HUGE);
  282.                 if (env.ulval)  env.reqmode |= FXUL;
  283.                 else            env.reqmode &= ~FXUL;
  284.                 if (dc.bsflg != BSAMIGA)
  285.                         env.cuval = env.boval = 0;
  286.                 break;
  287.         case UN: /* Undefine macro */
  288.                 undefmac(word, infile);
  289.                 break;
  290.         }
  291.         env.dontbrk = FALSE;
  292.  
  293.         if (argtyp != STRINGTYP && !(ct & NOARGS))      skipeol(infile);
  294.  
  295.         return;
  296. }
  297.  
  298.  
  299. /*
  300.  *      Decodes nro command and returns its associated
  301.  *      value.
  302.  */
  303.  
  304. comtyp(line, macdef)
  305. uchar *line;
  306. uchar **macdef;
  307. {
  308.         uchar c1, c2;
  309.  
  310.         /*
  311.          *      First check to see if the command is a macro.
  312.          *      If it is, return expansion in macdef.
  313.          *      Note that upper and lower case characters are handled
  314.          *      differently for macro names, but not for normal command names.
  315.          */
  316.  
  317.         if ((*macdef = getmac(line)) != NULL) {
  318.                 return MACRO | NOARGS;
  319.         }
  320.  
  321.         c1 = tolower(*line++);
  322.         c2 = tolower(*line  );
  323.  
  324.         switch (c1) {
  325.         case '"':
  326.         case '*':
  327.                 return COMMENT | NOARGS;
  328.         case 'b':
  329.                 if              (c2 == 'o') return BO;
  330.                 else if (c2 == 'p') return BP;
  331.                 else if (c2 == 'r') return BR;
  332.                 else if (c2 == 's') return BS;
  333.                 break;
  334.         case 'c':
  335.                 if              (c2 == '2') return C2 | NOARGS;
  336.                 else if (c2 == 'c') return CC | NOARGS;
  337.                 else if (c2 == 'e') return CE;
  338.                 else if (c2 == 'u') return CU;
  339.                 break;
  340.         case 'd':
  341.                 if      (c2 == 'e') return DE | NOARGS;
  342.                 break;
  343.         case 'e':
  344.                 if              (c2 == 'f') return EF | NOARGS;
  345.                 else if (c2 == 'h') return EH | NOARGS;
  346.                 else if (c2 == 'l') return EL | NOARGS;
  347.                 else if (c2 == 'n') return EN;
  348.                 else if (c2 == 'v') return EV;
  349.                 break;
  350.         case 'f':
  351.                 if              (c2 == 'i') return FI;
  352.                 else if (c2 == 'o') return FO | NOARGS;
  353.                 break;
  354.         case 'h':
  355.                 if      (c2 == 'e') return HE | NOARGS;
  356.                 break;
  357.         case 'i':
  358.                 if              (c2 == 'e') return IE | NOARGS;
  359.                 else if (c2 == 'f') return IF | NOARGS;
  360.                 else if (c2 == 'n') return IN;
  361.                 else if (c2 == 't') return IT;
  362.                 break;
  363.         case 'j':
  364.                 if      (c2 == 'u') return JU;
  365.                 break;
  366.         case 'l':
  367.                 if      (c2 == 's') return LS;
  368.                 break;
  369.         case 'm':
  370.                 if              (c2 == '1') return M1;
  371.                 else if (c2 == '2') return M2;
  372.                 else if (c2 == '3') return M3;
  373.                 else if (c2 == '4') return M4;
  374.                 break;
  375.         case 'n':
  376.                 if              (c2 == 'e') return NE;
  377.                 else if (c2 == 'f') return NF;
  378.                 else if (c2 == 'j') return NJ;
  379.                 else if (c2 == 'r') return NR | NOARGS;
  380.                 break;
  381.         case 'o':
  382.                 if              (c2 == 'f') return OF | NOARGS;
  383.                 else if (c2 == 'h') return OH | NOARGS;
  384.                 break;
  385.         case 'p':
  386.                 if              (c2 == 'c') return PC | NOARGS;
  387.                 else if (c2 == 'l') return PL;
  388.                 else if (c2 == 'n') return PN;
  389.                 else if (c2 == 'o') return PO;
  390.                 break;
  391.         case 'r':
  392.                 if      (c2 == 'm') return RM;
  393.                 break;
  394.         case 's':
  395.                 if              (c2 == 'o') return SO | NOARGS;
  396.                 else if (c2 == 'p') return SP;
  397.                 break;
  398.         case 't':
  399.                 if      (c2 == 'a') return TA | NOARGS;
  400.                 else if (c2 == 'i') return TI;
  401.                 else if (c2 == 'm') return TM | NOARGS;
  402.                 break;
  403.         case 'u':
  404.                 if              (c2 == 'l') return UL;
  405.                 else if (c2 == 'n') return UN | NOARGS;
  406.         }
  407.         return UNKNOWN;
  408. }
  409.  
  410.  
  411. /*
  412.  *      Retrieves optional argument following nro command.
  413.  *      returns positive integer value with sign (if any)
  414.  *      saved in character addressed by pargtyp.
  415.  *      In case of a string, puts in a quote.
  416.  *      It won't read the character it doesn't understand,
  417.  *      i.e. semicolons and newlines, and garbage.
  418.  */
  419.  
  420. getval(pargtyp, infile)
  421. uchar *pargtyp;
  422. register FILE *infile;
  423. {
  424.         register uchar chr, operator = '+';
  425.         short digit;
  426.         register int val, cumval;
  427.  
  428.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  429.  
  430.         *pargtyp = chr;
  431.         if (chr == '+' || chr == '-' || chr == '/' || chr == '*')
  432.                 chr = ngetc(infile);
  433.         else if (chr == STRINGTYP)
  434.                 return 1;
  435.  
  436.         cumval = 0;
  437.  
  438. again:
  439.         val = 0;
  440.  
  441.         if (chr == '(') {       /* Parenthesized subexpression */
  442.                 val = getval(&digit, infile); /* Dummy argtype pointer */
  443.                 chr = ngetc(infile);
  444.                 if (chr != ')')
  445.                         error("nro: missing close parenthesis in expression\n");
  446.                 else
  447.                         chr = ngetc(infile); /* Get next operator or anything */
  448.                 if (*pargtyp == '(') *pargtyp = '0';
  449.         } else {                        /* Try to collect a number from the input */
  450.                 while ((digit = atod(chr)) >= 0) {
  451.                         val = 10 * val + digit;
  452.                         chr = ngetc(infile);
  453.                 }
  454.         }
  455.  
  456.         /* Check if we need to evaluate an operator */
  457.         if (operator) {
  458.                 switch (operator) {
  459.                 case '+': cumval += val; break;
  460.                 case '-': cumval -= val; break;
  461.                 case '*': cumval *= val; break;
  462.                 case '/':
  463.                         if (val != 0) cumval /= val;
  464.                         else error("nro: division by zero\n");
  465.                         break;
  466.                 case '%':
  467.                         if (val != 0) cumval %= val;
  468.                         else error("nro: modulo by zero\n");
  469.                         break;
  470.                 case '<': cumval = cumval < val;  break;
  471.                 case '=': cumval = cumval == val; break;
  472.                 case '>': cumval = cumval > val;  break;
  473.                 case '&': cumval = cumval & val; break;
  474.                 case '|': cumval = cumval | val; break;
  475.                 }
  476.                 operator = '\0';
  477.         }
  478.  
  479.         /* See if there is more to come */
  480.         switch (chr) {
  481.         case '+': case '-': case '*': case '/': case '%':
  482.         case '<': case '=': case '>':
  483.         case '&': case '|':
  484.                 operator = chr;
  485.                 chr = ngetc(infile);
  486.                 goto again;
  487.         default:
  488.                 putbak(chr);    /* Put back what we can't interpret */
  489.  
  490.                 return cumval;
  491.         }
  492. }
  493.  
  494.  
  495. /*
  496.  *      Convert string to decimal.
  497.  *      processes only positive values.
  498.  */
  499.  
  500. ctod(p)
  501. uchar *p;
  502. {
  503.         int val, d;
  504.  
  505.         val = 0;
  506.         while (*p != EOS) {
  507.                 d = atod(*p++);
  508.                 if (d == -1) return val;
  509.                 val = 10 * val + d;
  510.         }
  511.         return val;
  512. }
  513.  
  514.  
  515. /*
  516.  *      Convert ascii character to decimal.
  517.  */
  518.  
  519. atod(c)
  520. uchar c;
  521. {
  522.         return ((c < '0') || (c > '9')) ? -1 : c-'0';
  523. }
  524.  
  525.  
  526. /*
  527.  *      Get non-blank word from the input file.
  528.  *      Returns the number of spaces skipped before the word,
  529.  *      or EOF on end of file.
  530.  *      It won't read past a newline or semicolon,
  531.  *      but will skip the first space following the word.
  532.  */
  533.  
  534. getcmdwrd(to, infile)
  535. register uchar *to;
  536. register FILE *infile;
  537. {
  538.         register short chr;
  539.         int skipped = 0;
  540.         short length = 0;
  541.  
  542.  
  543.         chr = ngetc(infile);
  544.         if (chr == EOF) {
  545.                 *to = EOS;
  546.                 return EOF;
  547.         }
  548.  
  549.         /* Skip spaces */
  550.  
  551.         while (isspace(chr)) {
  552.                 chr = ngetc(infile);
  553.                 skipped++;
  554.         }
  555.  
  556.         while (isntspace(chr) && isnteol(chr) && chr != EOF &&
  557.                         chr != MORETXT && length < MAXWORD-3) {
  558.                 *to++ = chr;
  559.                 length++;
  560.                 chr = ngetc(infile);
  561.         }
  562.  
  563.         if (iseol(chr) || chr == MORETXT) putbak(chr);
  564.  
  565.         *to = EOS;
  566.         if (length >= MAXWORD-3) error("nro: command word buffer overflow\n");
  567.  
  568.         return skipped;
  569. }
  570.  
  571.  
  572. /*
  573.  *      Skip the rest of the current input line
  574.  */
  575.  
  576. skipeol(infile)
  577. FILE *infile;
  578. {
  579.         int chr;
  580.  
  581.         while ((chr = ngetc(infile)) != '\n' && chr != MORETXT &&
  582.                 chr != EOF);
  583. }
  584.  
  585.  
  586. /*
  587.  *      Process an IF or IE request
  588.  */
  589.  
  590. doieif(infile, request)
  591. FILE *infile;
  592. int request;
  593. {
  594.         uchar *strp;
  595.         short negation;
  596.         int chr, val;
  597.         uchar delim;
  598.         uchar string[MAXWORD];
  599.  
  600.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  601.         if (chr == '!') {
  602.                 negation = TRUE;
  603.                 chr = ngetc(infile);
  604.         } else
  605.                 negation = FALSE;
  606.  
  607.         switch (chr) {
  608.         case 'e':       /* Even page number */
  609.                 val = (pg.curpag % 2) == 0;
  610.                 break;
  611.         case 'o':       /* Odd page number */
  612.                 val = (pg.curpag % 2) != 0;
  613.                 break;
  614.         case 'n':       /* Nro(ff) is formatter */
  615.                 val = TRUE; break;
  616.         case 't':       /* Troff certainly not */
  617.                 val = FALSE; break;
  618.         case '0': case '1': case '2': case '3': case '4':
  619.         case '5': case '6': case '7': case '8': case '9':
  620.         case '+': case '-': case '(':
  621.                 /* Must be an expression */
  622.                 putbak(chr);
  623.                 chr = getval(&string[0], infile);
  624.                 val = 0;
  625.                 set(&val, chr, string[0], 0, -HUGE, HUGE);
  626.                 val = val > 0;
  627.                 break;
  628.         default:        /* String comparison */
  629.                 delim  = chr;
  630.                 strp   = string;        /* Collect first string */
  631.                 while ((chr = ngetc(infile)) != delim &&
  632.                                 strp < string + sizeof(string) - 2)
  633.                         *strp++ = chr;
  634.                 *strp = EOS;
  635.                 val = TRUE;
  636.  
  637.                 strp = string;  /* Compare with second string */
  638.                 while ((chr = ngetc(infile)) != delim) {
  639.                         if (*strp++ != chr) val = FALSE;
  640.                 }
  641.                 if (*strp != EOS) val = FALSE;
  642.                 break;
  643.         }
  644.         if (negation) val = !val;
  645.         if (request == IE) env.lastie = val;
  646.  
  647.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  648.  
  649.         if (val) {     /* Condition is true. Don't skip any text */
  650.                 if (chr != BEGIF) putbak(chr);
  651.         } else { /* Need to skip some text, maybe even very much */
  652.                 if (chr == BEGIF)          /* Skip until end of line */
  653.                         dc.iflvl = 1;
  654.                 while (isnteol(chr)) chr = ngetc(infile);
  655.         }
  656. }
  657.  
  658.  
  659. /*
  660.  *      Process an EL request
  661.  */
  662.  
  663. doel(infile)
  664. FILE *infile;
  665. {
  666.         int chr, val;
  667.  
  668.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  669.  
  670.         /* Toggle last remembered condition */
  671.         val = env.lastie = !env.lastie;
  672.  
  673.         if (val) {     /* Condition is true. Don't skip any text */
  674.                 if (chr != BEGIF) putbak(chr);
  675.         } else { /* Need to skip some text, maybe even very much */
  676.                 if (chr == BEGIF)          /* Skip until end of line */
  677.                         dc.iflvl = 1;
  678.                 while (isnteol(chr)) chr = ngetc(infile);
  679.         }
  680. }
  681.  
  682.  
  683. /*
  684.  *      Process a TA request
  685.  */
  686.  
  687. tabs(infile)
  688. FILE *infile;
  689. {
  690.         int chr, val, i, j;
  691.  
  692.         while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  693.  
  694.         if (iseol(chr) || chr == EOF) { /* No arguments */
  695.                 for (i=0; i<MAXTAB; i++)
  696.                         env.tabstop[i] = NOTAB;
  697.                 return;
  698.         }
  699.  
  700.         i = env.inval;
  701.  
  702.         for (;;) {      /* Collect the values */
  703.                 putbak(chr);
  704.                 val = getval((uchar *)&chr, infile);
  705.  
  706.                 if (*(uchar *)&chr == '+') {    /* Saves a temporary uchar :-) */
  707.                         val += i;
  708.                 }
  709.  
  710.                 /* Find where to insert the tab */
  711.                 i=0;
  712.                 while (i<MAXTAB && env.tabstop[i] < val) i++;
  713.  
  714.                 if (i < MAXTAB && env.tabstop[i] != val)  {
  715.                         /* Insert the tab */
  716.                         for (j=MAXTAB-1; j > i; j--)
  717.                                 env.tabstop[j] = env.tabstop[j-1];
  718.  
  719.                         env.tabstop[i] = val;
  720.                 }
  721.                 i = val;
  722.  
  723.                 while ((chr = ngetc(infile)) == ' ' || chr == '\t');
  724.                 if (iseol(chr) || chr == MORETXT || chr == EOF) break;
  725.         }
  726. }
  727.  
  728.  
  729.  
  730. /*
  731.  *      Loadenv - get an environment
  732.  */
  733.  
  734. loadenv(number)
  735. int number;
  736. {
  737.         struct environ *envp;
  738.         short freeit = env.dontbrk;
  739.  
  740.         if (number < 0 || number >= NUMENV) return ERR;
  741.  
  742.         envp = environ[number];
  743.  
  744.         if (envp) {     /* It't there. Copy it. */
  745.                 env = *envp;
  746.                 if (freeit) {           /* We may free the saved image of it, */
  747.                         freenv(number); /* if we are tight on memory.         */
  748.                 }
  749.         } else {        /* It isn't. Just use default values. */
  750.                 initenv();
  751.         }
  752.  
  753.         return OK;
  754. }
  755.  
  756. /*
  757.  *      Storenv - save an environment
  758.  */
  759.  
  760. storenv(number)
  761. int number;
  762. {
  763.         struct environ *envp;
  764.  
  765.         if (number < 0 || number >= NUMENV) return ERR;
  766.  
  767.         envp = environ[number];
  768.  
  769.         if (envp == NULL) {     /* Never saw this guy before */
  770.                 if ((envp = (struct environ *)malloc(sizeof(*envp))) == NULL) {
  771.                         error("*** nro: cannot allocate environment #%d\n", number);
  772.                         return ERR;
  773.                 } else if (verbose > 2)
  774.                         error("nro: allocated environment #%d\n", number);
  775.                 environ[number] = envp;
  776.         }
  777.  
  778.         *envp = env;
  779.  
  780.         return OK;
  781. }
  782.  
  783.  
  784. /*
  785.  *      Freenv - throw an environment away
  786.  */
  787.  
  788. freenv(number)
  789. int number;
  790. {
  791.         struct environ *envp;
  792.  
  793.         if (number < 0 || number >= NUMENV) return ERR;
  794.  
  795.         envp = environ[number];
  796.  
  797.         if (envp) {
  798.                 free(envp);
  799.                 environ[number] = NULL;
  800.                 if (verbose > 2) error("nro: freed environment #%d\n", number);
  801.         }
  802.  
  803.         return OK;
  804. }
  805.  
  806.  
  807. /*
  808.  *      End current filled line
  809.  */
  810.  
  811. dobrk()
  812. {
  813.         if (!env.dontbrk) {             /* Breaks may be disabled by using .'xx */
  814.                 if (env.outp > 0) {
  815. #ifdef CPM
  816.                         env.outbuf[env.outp++] = '\r';
  817.                         env.outbuf[env.outp++] = '\n';
  818.                         env.outbuf[env.outp  ] = EOS;
  819. #else  CPM
  820.                         env.outbuf[env.outp++] = '\n';
  821.                         env.outbuf[env.outp  ] = EOS;
  822. #endif CPM
  823.                         if (env.ceval != 0)     /* Centering */
  824.                                 center(env.outbuf);
  825.                         put(env.outbuf);
  826.                 }
  827.                 env.outp = 0;
  828.                 env.outw = 0;
  829.                 env.outwds = 0;
  830.         }
  831. }
  832.  
  833.  
  834.  
  835. /*
  836.  *      Define a macro
  837.  */
  838.  
  839. defmac(word, infile)
  840. uchar *word;
  841. FILE *infile;
  842. {
  843.         uchar line[MAXLINE];
  844.  
  845.         getcmdwrd(word, infile);
  846.         skipeol(infile);
  847.  
  848.         if (word[0] == EOS && verbose)
  849.                 error("nro: appending to macro definition\n");
  850.  
  851.         while (getlin(line, infile) != EOF) {
  852.                 if (line[0] == env.cmdchr && line[1] == 'e' && line[2] == 'n')
  853.                         break;
  854.                 if (putmac(word, line) == ERR) {
  855.                         error("*** nro: macro definition table full\n");
  856.                 }
  857.                 word[0] = EOS;
  858.         }
  859. }
  860.  
  861.  
  862. /*
  863.  *      Put macro definition into table.
  864.  *      If name == "", concatenate this definition to the previous one.
  865.  */
  866.  
  867. putmac(name, def)
  868. uchar *name;
  869. uchar *def;
  870. {
  871.         int lenofname, lenofdef;
  872.  
  873.         lenofname = strlen(name);
  874.         lenofdef = strlen(def);
  875.  
  876.         if ((lenofname && mac.lastp >= mac.mxmdef) ||
  877.                 (mac.emb + lenofname + lenofdef + 2 > &mac.mb[mac.macbuf])) {
  878.                 return ERR;
  879.         }
  880.         if (lenofname) {
  881.                 ++mac.lastp;
  882.                 mac.mnames[mac.lastp] = mac.emb;
  883.                 strcpy(mac.emb, name);
  884.                 mac.emb += lenofname + 1;
  885.         } else
  886.                 mac.emb--;
  887.  
  888.         strcpy(mac.emb, def);
  889.         mac.emb += lenofdef + 1;
  890.         return OK;
  891. }
  892.  
  893.  
  894.  
  895. /*
  896.  *      Get macro definition from table
  897.  */
  898.  
  899. uchar *getmac(name)
  900. uchar *name;
  901. {
  902.         register int i;
  903.         register uchar *name1, *mname;
  904.  
  905.  
  906.         for (i = mac.lastp; i > 0; --i) {       /*V1.5*/
  907.                 name1 = name;
  908.                 mname = mac.mnames[i];
  909.                 while (*(name1++) == *(mname++)) {
  910.                         if (name1[-1] == EOS)   return mname;
  911.                 }
  912.         }
  913.         return NULL;
  914. }
  915.  
  916.  
  917. /*
  918.  *      Delete macro definition from table
  919.  */
  920.  
  921. undefmac(word, infile)
  922. uchar *word;
  923. FILE *infile;
  924. {
  925.         register int i;
  926.         register uchar *name, *mname;
  927.  
  928.         getcmdwrd(word, infile);
  929.         skipeol(infile);
  930.  
  931.         for (i = mac.lastp; i >= 0; --i) {
  932.                 name = word;
  933.                 mname = mac.mnames[i];
  934.                 while (*(name++) == *(mname++)) {
  935.                         if (name[-1] == EOS) {
  936.                                 mac.lastp = i-1;
  937.                                 mac.emb = mac.mnames[i];
  938.                                 return OK;
  939.                         }
  940.                 }
  941.         }
  942.         return ERR;
  943. }
  944.  
  945.  
  946.  
  947. /*
  948.  *      Evaluate macro expansion for re-evaluation
  949.  */
  950.  
  951. maceval(macdef, infile)
  952. register uchar *macdef;
  953. FILE *infile;
  954. {
  955.         uchar *argp[10];
  956.         int i;
  957.         uchar c;
  958.         uchar line[MAXLINE];
  959.         register uchar *linep = line;
  960.  
  961.         *linep++ = EOS;
  962.         getlin(linep, infile);
  963.         /*
  964.          *       Initialize argp array to substitute empty string
  965.          *       string for any undefined argument
  966.          */
  967.         for (i=0; i<10; ++i) argp[i] = line;
  968.  
  969.         for (i=0; i<10; ++i) {
  970.                 linep = skipbl(linep);
  971.                 if (iseol(*linep) || *linep == MORETXT || *linep == EOS) break;
  972.                 if (*linep == '\'' || *linep == '"') {
  973.                         c = *linep++;
  974.                         argp[i] = linep;
  975.                         while (*linep != c && isnteol(*linep) && *linep != EOS)
  976.                                 linep++;
  977.                         *linep++ = EOS;
  978.                 } else {
  979.                         argp[i] = linep;
  980.                         linep = skipwd(linep);
  981.                         *linep++ = EOS;
  982.                 }
  983.         }
  984.  
  985.         /* First push back any remaining text or commands */
  986.  
  987.         if (*linep == MORETXT) pbstr(linep+1);
  988.  
  989.         /* Now push back macro body */
  990.  
  991.         for (i=strlen(macdef)-1; i>=0; --i) {
  992.                 /* Need we substitute an argument? */
  993.                 if (i > 0 && macdef[i-1] == '$') {
  994.                         if (!isdigit(macdef[i]) || macdef[i-2] == '$') {
  995.                                 /* Re-evaluate escape characters from macro body */
  996.                                 putbak(macdef[i] | NOGUARD);
  997.                         } else {
  998.                                 /* but not of any arguments */
  999.                                 pbstr(argp[macdef[i]-'0']);
  1000.                                 --i;
  1001.                         }
  1002.                 } else {
  1003.                         putbak(macdef[i] | NOGUARD);
  1004.                 }
  1005.         }
  1006. }
  1007.  
  1008.  
  1009. /*
  1010.  *      Push back string into input stream for re-evaluation
  1011.  */
  1012.  
  1013. pbstr(p)
  1014. register uchar p[];
  1015. {
  1016.         register int i;
  1017.  
  1018.         for (i=strlen(p)-1; i>=0; --i) {
  1019.                 putbak(p[i]);
  1020.         }
  1021. }
  1022.  
  1023.  
  1024.  
  1025. /*
  1026.  *      Push character back into input stream
  1027.  */
  1028.  
  1029. putbak(c)
  1030. int c;
  1031. {
  1032.         if (mac.ppb < mac.pbb) {
  1033.                 mac.ppb = mac.pbb;
  1034.                 *mac.ppb = c;
  1035.         } else {
  1036.                 if (mac.ppb >= mac.pbbend-2) {
  1037.                         error("*** nro: push back buffer overflow\n");
  1038.                 }
  1039.                 *++mac.ppb = c;
  1040.         }
  1041.  
  1042.         /* Avoid evaluating escaped escape characters twice */
  1043.         if (c == ESCCHAR)
  1044.                 *++mac.ppb = ESCCHAR;
  1045. }
  1046.  
  1047.  
  1048.  
  1049. /*
  1050.  *      Get header or footer title
  1051.  */
  1052.  
  1053. gettl(infile, line1, line2, limit1, limit2)
  1054. FILE *infile;
  1055. uchar *line1, *line2;
  1056. int limit1[], limit2[];
  1057. {
  1058.         uchar c;
  1059.  
  1060.         while (c = ngetc(infile), isspace(c));
  1061.  
  1062.         line1[0] = c;
  1063.         if (isnteol(c)) getlin(line1+1, infile);
  1064.         limit1[LEFT] = env.inval;
  1065.         limit1[RIGHT] = env.rmval;
  1066.         if (line2) {
  1067.                 strcpy(line2, line1);
  1068.                 limit2[LEFT] = limit1[LEFT];
  1069.                 limit2[RIGHT] = limit1[RIGHT];
  1070.         }
  1071. }
  1072.  
  1073.  
  1074.  
  1075. /*
  1076.  *      Set parameter and check range
  1077.  */
  1078.  
  1079. set(param, val, type, defval, minval, maxval)
  1080. int *param;
  1081. int val;
  1082. uchar type;
  1083. int defval, minval, maxval;
  1084. {
  1085.         switch(type) {
  1086.         case '+':
  1087.                 *param += val; break;
  1088.         case '-':
  1089.                 *param -= val; break;
  1090.         case '*':
  1091.                 *param *= val; break;
  1092.         case '/':
  1093.                 if (val != 0) *param /= val;
  1094.                 else error ("nro: division by zero modification\n");
  1095.                 break;
  1096.         case '%':
  1097.                 if (val != 0) *param %= val;
  1098.                 else error ("nro: modulo by zero modification\n");
  1099.                 break;
  1100.         default:
  1101.                 *param = isdigit(type)? val : defval;
  1102.                 break;
  1103.         }
  1104.         if (*param < minval)
  1105.                 *param = minval;
  1106.         else if (*param > maxval)
  1107.                 *param = maxval;
  1108. }
  1109.  
  1110.  
  1111.  
  1112. /*
  1113.  *      Skip blanks and tabs in character buffer.
  1114.  *      return pointer to first non-space char.
  1115.  */
  1116.  
  1117. uchar *skipbl(p)
  1118. uchar *p;
  1119. {
  1120.         while (isspace(*p)) ++p;
  1121.         return p;
  1122. }
  1123.  
  1124.  
  1125. /*
  1126.  *      Skip over word and punctuation
  1127.  */
  1128.  
  1129. uchar *skipwd(p)
  1130. uchar *p;
  1131. {
  1132.         while (isntspace(*p) && isnteol(*p) && *p != EOS)
  1133.                 ++p;
  1134.         return p;
  1135. }
  1136.  
  1137.  
  1138.  
  1139. /*
  1140.  *      Space vertically n lines
  1141.  */
  1142.  
  1143. space(n)
  1144. int n;
  1145. {
  1146.         dobrk();
  1147.         if (pg.lineno > pg.bottom) return;
  1148.         if (pg.lineno == 0) phead();
  1149.         skip(min(n, pg.bottom+1-pg.lineno));
  1150.         pg.lineno += n;
  1151.         if (pg.lineno > pg.bottom) pfoot();
  1152. }
  1153.  
  1154.