/*  mylib3.c -- source for deff3.crl
     author: David H. Wolen
     last change: 6/12/83
          contents:
               fefc      rcarg     error          out       argrange
               eqs       addstr    dodash         isalpnum  esc 
               cindex    amatch    patsize        omatch    locate 
               getpat    makepat   getccl         stclos    undoc

*/   

#include "a:bdscio.h"
#define  STDERR 4
#define  MAXI   5
#define  ESCAPE '\\'
#define  NEGATE  '!'
#define  LITCHAR  'c'
#define  BOL  '%'
#define  EOL  '$'
#define  ANY  '?'
#define  CCL  '['
#define  CCLEND  ']'
#define  NCCL  '^'
#define  CLOSURE  '*'
#define  CLOSIZE  1

/*  fefc -- end, fflush, close output file  */
fefc(iobuf)
char  *iobuf;
{
     putc(CPMEOF,iobuf);
     fflush(iobuf);
     fclose(iobuf);
}




/* rcarg -- restore case of argument.  Intended to deal
          with CP/M desire to make command line args
          upper case.  Any characters within # # in
          inarg are turned to upper case; other characters
          are turned to lower case.
          usage: rcarg(outarg,inarg);
*/
rcarg(outarg,inarg)
char *outarg, *inarg;
{
     int  makelow;

     makelow=TRUE;

     for(; *inarg ; inarg++)
          {if(*inarg=='#')
               makelow=!makelow;
          else if(makelow)
               *outarg++ = tolower(*inarg);
          else if(!makelow)
               *outarg++ = *inarg;
          }

     *outarg = '\0';
}




/*  error -- print error message, dioflush and stop  */
error(msg)
char *msg;
{
     fputs(msg,STDERR);
     fputs("\n",STDERR);
     exit(dioflush());
}




/*  out -- display message on console regardless of dio */
out(string)
char *string;
{
     fprintf(STDERR,"%s\n",string);
}




/*  argrange -- converts an integer range argument of the form
          n1-n2 to two positive integers.  Returns ERROR if 
          no "-" or ni has too many digits or <0 or missing
          usage:
               if(argrange(arg,&n1,&n2)==ERROR) ...
*/
argrange(arg,n1,n2)
char *arg;
int  *n1, *n2;
{
     char cn[MAXI+1];
     int  i, j;

     for(i=0; (arg[i]!='-')&&(arg[i]!='\0'); i++) ;

     if(i>MAXI)       return(ERROR);
     if(arg[i]!='-')  return(ERROR);

     for(j=0; j<i; j++)
          cn[j]=arg[j];

     if(j==0)  return(ERROR);
     cn[j]='\0';
     if( (*n1=atoi(cn)) < 0)  return(ERROR);

     for(j=0; j<=MAXI+1; j++)
          {cn[j]=arg[++i];
          if(arg[i]=='\0')  break;
          }

     if(cn[0]=='\0')          return(ERROR);
     if(arg[i]!='\0')         return(ERROR);
     if( (*n2=atoi(cn)) < 0)  return(ERROR);
     return(OK);
}





/*  eqs -- return TRUE if strings are equal including length,
          else FALSE
*/
eqs(s,t)
char *s, *t;
{

     for(; *s==*t; s++, t++)
          if(*s == '\0')  return(TRUE);

     return(FALSE);
}



/* addstr -- put c in outset[j] and increment j. Returns FALSE if won't fit.
     usage:  if(!addstr(c,outset,&j,maxset)) ...
*/
addstr(c,outset,j,maxset)
char c, *outset;
int  *j, maxset;
{
     if(*j >= maxset)  return(FALSE);
     else
          {outset[*j]=c;
          (*j)++;
          return(TRUE);
          }
}



/* dodash -- expand set at src[i] into dest[j]. Stop at delim.
     usage: dodash('\0',inset,&i,outset,&j,maxset);
*/
dodash(delim,src,i,dest,j,maxset)
char delim, *src, *dest;
int  *i, *j, maxset;
{
     int  k;
     char esc();

     while(src[*i] != delim && src[*i] != '\0')
          {if(src[*i] == ESCAPE)
               addstr(esc(src,i),dest,j,maxset);
          else if(src[*i] != '-')
               addstr(src[*i],dest,j,maxset);
          else if(*j <= 0 || src[*i+1] == '\0')
               addstr('-',dest,j,maxset);         /* literal - */
          else if(isalpnum(src[*i-1])
               && isalpnum(src[*i+1])
               && src[*i-1] <= src[*i+1])
               {for(k=src[*i-1]+1; k <= src[*i+1]; k++)
                    addstr(k,dest,j,maxset);
               (*i)++;
               }
          else
               addstr('-',dest,j,maxset);
          (*i)++;
          }
}



/* isalpnum -- true if c is letter or digit 
     usage: if(isalpnum(c)) ...
*/
isalpnum(c)
char c;
{
     if(c >= 'A' && c <= 'Z')  return(TRUE);
     if(c >= 'a' && c <= 'z')  return(TRUE);
     if(c >= '0' && c <= '9')  return(TRUE);
     return(FALSE);
}



/* esc -- map s[i] into escaped character, increment i.
     usage: c=esc(inset,&i);
*/
char esc(s,i)
char *s;
int  *i;
{
     if(s[*i] != ESCAPE)  return(s[*i]);
     if(s[*i+1] == '\0')           /* \ not special at end */
          return(ESCAPE);

     (*i)++;
     if(s[*i] == 'n' || s[*i] == 'N')  return('\n');
     if(s[*i] == 't' || s[*i] == 'T')  return('\t');
     if(s[*i] == 'b' || s[*i] == 'B')  return('\b');
     if(s[*i] == 'r' || s[*i] == 'R')  return('\r');
     if(s[*i] == 'f' || s[*i] == 'F')  return('\f');
     if(s[*i] == 'p' || s[*i] == 'P')  return('#');
     if(s[*i] == 'q' || s[*i] == 'Q')  return('"');

     return(s[*i]);
}



/* cindex -- find position of character c in string s. -1 for not found.
     usage: if(cindex(string,c) >= 0) ...
*/
cindex(s,c)
char *s, c;
{
     int  i;

     i=0;
     while(s[i] != c && s[i] != '\0')  i++;

     if(s[i] == '\0')  return(-1);
     else
          return(i);
}



/*  amatch -- look for match of pat[j]... at lin[offset]...
     usage: pos=amatch(lin,i,pat,0);
*/
amatch(lin,offset,pat,j)
char *lin, *pat;
int  offset, j;
{
     int  i, k, done, offset2;

     done=FALSE;
     offset2=offset;
     while(!done && pat[j] != '\0')
          {if(pat[j] == CLOSURE)
               {j += patsize(pat,j);
               i=offset2;
               while(!done && lin[i] != '\0')
                    if(!omatch(lin,&i,pat,j))
                         done=TRUE;
               done=FALSE;
               while(!done && i >= offset2)
                    {k=amatch(lin,i,pat,j+patsize(pat,j));
                    if(k >= 0)     /* matched rest of pattern */
                         done=TRUE;
                    else
                         i--;
                    }
               offset2=k;
               done=TRUE;
               }
          else if(!omatch(lin,&offset2,pat,j))
               {offset2=-1;
               done=TRUE;
               }
          else
               j += patsize(pat,j);
          }

     return(offset2);
}



/*  patsize -- return size of pattern entry at pat[n]
     usage:  j += patsize(pat,j);
*/
patsize(pat,n)
char *pat;
int  n;
{
     switch(pat[n])
          {case LITCHAR:
               return(2);
          case BOL:  case EOL:  case ANY:
               return(1);
          case CCL:  case NCCL:
               return(pat[n+1]+2);
          case CLOSURE:
               return(CLOSIZE);
          default:
               error("in patsize: can't happen");
          }
}



/*  omatch -- match one pattern element at pat[j]
     usage:  if(!omatch(lin,&i,pat,j)) ...
*/
omatch(lin,i,pat,j)
char *lin, *pat;
int  *i, j;
{
     int  advance, retval;

     advance=-1;
     if(lin[*i] == '\0')
          retval=FALSE;
     else
          switch(pat[j])
               {case LITCHAR:
                    if(lin[*i] == pat[j+1])
                         advance=1;
                    break;
               case BOL:
                    if(*i == 0)
                         advance=0;
                    break;
               case ANY:
                    if(lin[*i] != '\n')
                         advance=1;
                    break;
               case EOL:
                    if(lin[*i] == '\n')
                         advance=0;
                    break;
               case CCL:
                    if(locate(lin[*i],pat,j+1))
                         advance=1;
                    break;
               case NCCL:
                    if(lin[*i] != '\n' && !locate(lin[*i],pat,j+1))
                         advance=1;
                    break;
               default:
                    error("in omatch: can't happen");
               }

     if(advance >= 0)
          {(*i) += advance;
          retval=TRUE;
          }
     else
          retval=FALSE;

     return(retval);
}



/* locate -- look for c in character class at pat[offset]
     usage: if(locate(lin[i],pat,j+1)) ...
*/
locate(c,pat,offset)
char c, *pat;
int  offset;
{
     int i;

     i=offset+pat[offset];

     while(i > offset)
          if(c == pat[i])
               return(TRUE);
          else
               i--;

     return(FALSE);
}



/* getpat -- convert argument into pattern
     usage: if(!getpat(arg,pat)) ...
*/
getpat(arg,pat)
char *arg, *pat;
{
     return(makepat(arg,0,'\0',pat) >= 0);
}



/* makepat -- make pattern from arg[i], terminate at delim.
     returns i>=0 if ok, else ERROR (-1).
     usage: if(makepat(arg,0,'\0',pat) == ERROR) ...
*/
makepat(arg,start,delim,pat)
char *arg, delim, *pat;
int  start;
{
     int  i, j, lastj, lj, done;

     j=0;      /* pat index */
     i=start;  /* arg index */
     lastj=0;
     done=FALSE;

     while(!done && arg[i] != delim && arg[i] != '\0')
          {lj=j;
          if(arg[i] == ANY)
               addstr(ANY,pat,&j,MAXLINE);
          else if(arg[i] == BOL && i == start)
               addstr(BOL,pat,&j,MAXLINE);
          else if(arg[i] == EOL && arg[i+1] == delim)
               addstr(EOL,pat,&j,MAXLINE);
          else if(arg[i] == CCL)
               done=(getccl(arg,&i,pat,&j) == FALSE);
          else if(arg[i] == CLOSURE && i > start)
               {lj=lastj;
               if(pat[lj] == BOL || pat[lj] == EOL || pat[lj] == CLOSURE)
                    done=TRUE;
               else
                    stclose(pat,&j,lastj);
               }
          else
               {addstr(LITCHAR,pat,&j,MAXLINE);
               addstr(esc(arg,&i),pat,&j,MAXLINE);
               }
          lastj=lj;
          if(!done)
               i++;
          }

     if(done || arg[i] != delim)
          return(ERROR);
     else if(!addstr('\0',pat,&j,MAXLINE))
          return(ERROR);
     else
          return(i);
}



/* getccl -- expand char class at arg[i] into pat[j].
     Return TRUE if find CCLEND, else FALSE.
     usage: if(!getccl(arg,&i,pat,&j)) ...
*/
getccl(arg,i,pat,j)
char *arg, *pat;
int  *i, *j;
{
     int  jstart;

     (*i)++;   /* skip over [ */
     if(arg[*i] == NEGATE)
          {addstr(NCCL,pat,j,MAXLINE);
          (*i)++;
          }
     else
          addstr(CCL,pat,j,MAXLINE);

     jstart=*j;
     addstr(0,pat,j,MAXLINE);      /* room for count */
     dodash(CCLEND,arg,i,pat,j,MAXLINE);
     pat[jstart]=*j -jstart -1;
     return(arg[*i] == CCLEND);
}



/* stclose -- insert closure entry at pat[j]
     usage: stclose(pat,&j,lastj);
*/
stclose(pat,j,lastj)
char *pat;
int  *j, lastj;
{
     int  jp, jt;

     for(jp=*j -1; jp >= lastj; jp--)
          {jt=jp +CLOSIZE;
          addstr(pat[jp],pat,&jt,MAXLINE);
          }

     (*j) += CLOSIZE;
     pat[lastj] = CLOSURE;    /* where orig pattern began */
}




/* undoc -- zero hi bit & replace strange chars by blanks (WS doc files) 
     usage: putchar(undoc(c));
*/
char undoc(c)
char(c);
{
     c &= 0177;     /* zero hi bit */

     if(c == '\0' || c == '\n' || c == '\r')      /* leave as is */
          return(c);
     else if(c < ' ' || c == 0x7f)               /* replace by blank */
          return(' ');
     else 
          return(c);
}
