[net.sources] renewd posting of kb

jchvr@ihlpg.UUCP (04/01/87)

sorry about that, but my last posting was not complete.
here is the full source i hope.

# ---- cut here -----
: To unbundle, sh this file
echo Makefile 1>&2
cat >Makefile <<'@@@ Fin de Makefile'
kbq:	kb.o kbq.o fsindex.o sindex.o dir.o
	cc -o kbq kb.o fsindex.o dir.o kbq.o
	chmod +x kbq
	strip kbq
	ln kbq kbu
	ln kbq kbc
	ln kbq kbk
	ln kbq kbx

kbq.o:	kbq.c kb.h
	cc -O -c kbq.c

kb.o:	kbq.c kb.h
	cc -O -c kb.c

@@@ Fin de Makefile
echo dir.c 1>&2
cat >dir.c <<'@@@ Fin de dir.c'
#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/dir.h>
#include	<sys/stat.h>

/* dir - to execute a function for each file in a directory
**
** int dir(dirname,f,recursive,ignore)
**
** char *dirname;
** int (*f)();
** int recursive;
** int ignore;
**
** For each file in dirname (except '.' and '..') the function f will
** be called with the name of the file as argument (eg. f(name))
** When recursive = 0 then only 1 level of directory is done, 
** all other values of recursive will make dir() do a recursive decent.
** 
** When (*f)(name) returns to dir() with return code != 0 then dir
** aborts immediately with the return code from  (*f)(name).
** Setting ignore != 0 will make dir() ignore errors from subdirectories
** in the case that recursive != 0.
**
** Return code values for dir() are:
**	0	all okay no error
**	1	directory does not exist
**	2	argument is not a directory
**	3	argument length too long to handle
**	4	cannot read directory file
**	*	return code from (*f)(name)
**
** The definition of f should be:
**
** int f(name)
** char *name;
**
** f should ALWAYS return an error code or 0 if all okay.
*/

#define	BUFSIZE	512

int dir(dname,f,recursive,ignore)
char	*dname;	/* name of directory to work on */
int	(*f)();	/* function to call for every file in dir */
int	recursive; /* if != 0 then recursive decent */
int	ignore;	/* if !=0 then ignore subdir in error */
{
	struct	direct	dirbuf;
	struct	stat	stbuf;
	char 	*nbp;
	char	*nep;
	register int i;
	int	fd;
	char	fname[BUFSIZE];
	int	code;

	/* check if dname  is a dir */
	if (stat(dname, &stbuf) == -1) {
#ifdef DEBUG
		fprintf(stderr,"dir(): can't find '%s'\n",dname);
#endif
		return(1);
	}
	if ((stbuf.st_mode & S_IFMT) != S_IFDIR) {
#ifdef DEBUG
		fprintf(stderr,"dir(): '%s' is not a directory\n",dname);
#endif
		return(2);
	}

	/* check for length overflow: name = dname / fname */
	if (strlen(dname) + 2 + DIRSIZ > BUFSIZE) {
#ifdef DEBUG
		fprintf(stderr,"dir(): '%s' string too long\n",dname);
#endif
		return(3);
	}

	if ((fd = open(dname,0)) == -1) { /* cannot open */
#ifdef DEBUG
		fprintf(stderr,"dir(): cannot open '%s'\n",dname);
#endif
		return(4);
	}

	while (read(fd, (char *)&dirbuf, sizeof(dirbuf)) > 0) {
		if (dirbuf.d_ino == 0) { /* slot not in use */
			continue;
		}
		if (strcmp(dirbuf.d_name, ".")  == 0 ||
		    strcmp(dirbuf.d_name, "..") == 0) {/* skip . and .. */
		    	continue;
		}

		/* construct fname */
		strcpy(fname,dname);
		strcat(fname,"/");
		strcat(fname,dirbuf.d_name);

		/* call function and check result */
		code = (*f)(fname);
		if (code != 0) goto end;

		/* check if we need recursion */
		if ( recursive == 0 ) continue;

		/* else see if this is a dir. */
		if (stat(fname, &stbuf) == -1) {
			return(1);
		}
		if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
			code = dir(fname,f,recursive,ignore);
			if (ignore != 0 ) continue;
			if (code != 0) goto end;
		}
	}/*while*/
	code = 0;
end:
	close(fd);
	return(code);
}/*dir*/
@@@ Fin de dir.c
echo fsindex.c 1>&2
cat >fsindex.c <<'@@@ Fin de fsindex.c'
#include <stdio.h>
#include <ctype.h>

#define MIN(a,b)	( (a) < (b) ? (a) : (b) )
#define	TSIZE		128

void initfsindex(s,t)
register char	*s;
register int	t[];
{ 
	register int i;
	register int slength;
	register int *p;

	slength = strlen(s);
	for ( i = 0 ; i < TSIZE ; i++ ) t[i] = slength;

	for ( i = 0 ; i < slength ; i++ ) {
		p = &t[s[i]];
		*p = MIN( *p, slength-1-i );
	}
}/*initfsindex*/

/* fsindex: return pointer to where small starts in big, or NULL if
** not found. pos must be initialized by call to initfsindex */
char *fsindex(big,small,pos)
char	*big;
char	*small;
int	pos[];
{ 
	register char *pb;
	register char *ps;
	register char *lpb;	/* start value for pb */
	register char *pes;
	register char *peb;
	int slength;

	/* ABCD.     DEFGH.	where . means EOS
	** 01234     012345
	**  ^ ^       ^   ^
	**  | |       |   |
	** ps pes     pb  peb
	*/

	slength = strlen(small);
	if ( slength == 0 ) return(big);
	pes = &small[slength-1];	/* points to last char of small */
	peb = &big[strlen(big)];	/* points to EOS in big */
	lpb = &big[slength-1];		/* starting point for search */

l1:
	if ( lpb < peb ) {	/* then not past end of big */
		pb = lpb;	/* set start for big */
		ps = pes;	/* set to end of small */

l2:
		if ( *pb != *ps ) {	/* then not found so skip */
			lpb++;		/* skip one char */
			lpb += ( *lpb < (char)TSIZE ? pos[*lpb] : slength );
			goto l1;
		}
		if ( ps != small) {	/* then not full small found yet */
			ps--;
			pb--;
			goto l2;	/* try previous char in small */
		} else {
			return(pb);	/* full small found at pb */
		}
	} else {
		return(NULL);	/* not found */
	}
}/*fsindex*/

/* sindex: returns pointer to start of small in big, or NULL if not found
** calls fsindex after initializing */
char *sindex(big,small)
char big[];
char small[];
{ 
	int	table[TSIZE];

	initfsindex(small,table);
	return(fsindex(big,small,table));
}/*sindex*/
@@@ Fin de fsindex.c
echo kb.c 1>&2
cat >kb.c <<'@@@ Fin de kb.c'
/* kb.c to parse boolean expressions  -*-update-version-*-
** HFVR VERSION=Wed Jan  7 07:51:33 1987
**
** SYNTAX accepted:
** ------ ---------
** <query> ::= <term> { OR <term> }*
**
** <term> ::= <factor> { AND <factor> }*
**
** <factor> ::=   <key>
** 	        | NOT <factor>
**	        | ( <query> )
**
** <key> ::= <anything except ( ) ! & | SP TAB \n \0 > and or not ~
**
** alternative syntax for some keywords:
** -----------
** NOT		!	~ 	not
** AND		&	&&	and
** OR		|	||	or
** (		[	{	<
** )		]	}	>
*/

#include "kb.h"

/* all types of symbols */
typedef enum { LPARENTsymbol,	/* ( */
	       RPARENTsymbol,   /* ) */
	       ORsymbol,	/* | */
	       ANDsymbol,	/* & */
	       NOTsymbol,	/* ! */
	       EOFsymbol,	/* '\0' */
	       KEYsymbol,	/* rest */
	     } SYMBOL;

/* symbol as parsed */
typedef struct { SYMBOL	symbol;		/* symbol type */
        	 char	key[SIZE];	/* spelling */
	       } ITEM;

ITEM	next;			/* holds last symbol and spelling */
char	*string1;		/* ptr to start of string to parse */
char	*string;		/* ptr to rest of string to be parsed */
char	lastchar;		/* last char read */
int	lastindex;		/* index into string */
int	count = 0;		/* times true interpreter */

/* extern void initfsindex(); */

#define SETCODE(INS,ARG1,ARG2,ARG3,KEY1,CODESYM)	\
	{ CODE	*ptr; 					\
	  ptr = &(instruction[(INS)]);			\
	  ptr->arg1 = setptr((ARG1));			\
	  ptr->arg2 = setptr((ARG2));			\
	  ptr->arg3 = setptr((ARG3));			\
	  ptr->count = 0;				\
	  if ( strcmp(ptr->key,(KEY1)) != 0 ) {		\
	    strcpy(ptr->key,(KEY1));			\
	    ptr->table = (KEYTAB *) malloc(sizeof(KEYTAB));\
	    initfsindex(ptr->key,ptr->table);		\
	  }/*fi*/					\
	  ptr->code = (CODESYM);			\
	}

#define MAXNROFCODES 100
CODE instruction[MAXNROFCODES];
#define NOPC -1			/* the novalue value for PC */
int	PC;			/* Program Counter */

/* return pointer to instruction.value, NULL if PC=NOPC */
BOOL *setptr(pc)
int	pc;
{
 if ( pc != NOPC ) {
   return( &(instruction[pc].value) );
 } else {
   return(NULL);
 }
}/*setpc*/

/* print error message and exit */
void error(s)
char	s[];
{ int i;

  fprintf(stderr,"\007kb: ERROR in query\n");
  fprintf(stderr,"    query=%s\n",string1);
  fprintf(stderr,"           ");
  for (i = 1 ; i < lastindex ; i++) fprintf(stderr," ");
  fprintf(stderr,"^\n");
  fprintf(stderr,"    %s\n",s);
  exit(1);
}/*error*/

char *prcodesym(code)
CODEsym	code;
{ static char	res[10];

  switch(code) {
  case RFIcode : strcpy(res,"RFI"); break;
  case IFFcode : strcpy(res,"IFF"); break;
  case IFTcode : strcpy(res,"IFT"); break;
  case NOTcode : strcpy(res,"NOT"); break;
  case KEYcode : strcpy(res,"KEY"); break;
  case COPcode : strcpy(res,"COP"); break;
  case CITcode : strcpy(res,"CIT"); break;
  case CIFcode : strcpy(res,"CIF"); break;
  default      : strcpy(res,"???"); break;
  }
  return(res);
}/*prcodesym*/

/* generates 3-address code and returns PC of newly generated instruction */
int generate(code,key,PC1,PC2,PC3)
CODEsym	code;
char	key[];
int	PC1;	/* PC at which result for arg1 can be found */
int	PC2;	/* PC at which result for arg2 can be found */
int	PC3;	/* PC at which result for arg3 can be found */
{ int	temp;

  SETCODE(PC,PC1,PC2,PC3,key,code);
  temp = PC;
  PC++;
  return(temp);
}/*generate */

/* read next character from string */
#define NEXTCHAR	{ lastchar = string[0]; \
			 if ( string[0] != '\0' ) { /* no skip past EOS */ \
			   string++; \
			   lastindex++; \
			}}

/* skip all white space characters */
void skipblanks()
{
 while ( (lastchar == '\t') ||
         (lastchar == '\n') ||
	 (lastchar == ' ' )
       ) {
  NEXTCHAR;
 }
}/*skipblanks*/

/* return TRUE if ch is NOT a special character (eg. not part of key) */
BOOL notspecial(ch)
register char ch;
{
  switch ( ch ) {
       case  '(' : return(FALSE); break;
       case  '[' : return(FALSE); break;
       case  '{' : return(FALSE); break;
       case  '<' : return(FALSE); break;
       case  ')' : return(FALSE); break;
       case  ']' : return(FALSE); break;
       case  '}' : return(FALSE); break;
       case  '>' : return(FALSE); break;
       case  '|' : return(FALSE); break;
       case  '&' : return(FALSE); break;
       case  '!' : return(FALSE); break;
       case  '~' : return(FALSE); break;
       case  '\n': return(FALSE); break;
       case  '\t': return(FALSE); break;
       case  ' ' : return(FALSE); break;
       case  '\0': return(FALSE); break;
       default   : return(TRUE);  break;
  }
  return(TRUE);
}/*notspecial*/

/* read in spelling of key into next */
void getkey()
{ register int i;

  i = 0;
  while ( notspecial(lastchar) ) {
   next.key[i] = lastchar;
   if (i < SIZE-1) i++;
   next.key[i] = '\0';
   NEXTCHAR;
  }
}/*getkey*/

/* to debug: print SYMBOL type */
void prsymbol(symbol)
ITEM	symbol;
{ 
  switch(symbol.symbol) {
   case LPARENTsymbol : printf("LEFT   (\n"); break;
   case RPARENTsymbol : printf("RIGHT  )\n"); break;
   case ORsymbol      : printf("OR     |\n"); break;
   case ANDsymbol     : printf("AND    &\n"); break;
   case NOTsymbol     : printf("NOT    !\n"); break;
   case EOFsymbol     : printf("EOF\n"); break;
   case KEYsymbol     : printf("KEY    %s\n",symbol.key); break;
   default            : error("Unknown symbol"); break;
  }
}/*prsymbol*/

/* search for keywords, if not found return KEYsymbol */
/* to search is in next.key */
SYMBOL keyword(key)
register char	key[];
{
  switch (key[0]) {
   case 'a'   : if ( strcmp(key,"and") == 0 ) {
		 return(ANDsymbol);
   		} else {
		 return(KEYsymbol);
		}
		break;
   case 'A'   : if ( strcmp(key,"AND") == 0 ) {
		 return(ANDsymbol);
   		} else {
		 return(KEYsymbol);
		}
		break;
   case 'o'   : if ( strcmp(key,"or") == 0 ) {
		 return(ORsymbol);
   		} else {
		 return(KEYsymbol);
		}
		break;
   case 'O'   : if ( strcmp(key,"OR") == 0 ) {
		 return(ORsymbol);
   		} else {
		 return(KEYsymbol);
		}
		break;
   case 'n'   : if ( strcmp(key,"not") == 0 ) {
		 return(NOTsymbol);
   		} else {
		 return(KEYsymbol);
		}
		break;
   case 'N'   : if ( strcmp(key,"NOT") == 0 ) {
		 return(NOTsymbol);
   		} else {
		 return(KEYsymbol);
		}
		break;
   default    : return(KEYsymbol);
   		break;
  }
  return(KEYsymbol);
}/*keyword*/

/* get next symbol from string, EOFsymbol set if no more */
void getsymbol()
{
  skipblanks();
  switch (lastchar) {
   case '('   :
   case '{'   :
   case '['   :
   case '<'   : next.symbol = LPARENTsymbol;
   		strcpy(next.key,"(");
		NEXTCHAR;
   		break;
   case ')'   :
   case '}'   :
   case ']'   :
   case '>'   : next.symbol = RPARENTsymbol;
   		strcpy(next.key,")");
		NEXTCHAR;
		break;
   case '|'   : next.symbol = ORsymbol;
   		strcpy(next.key,"|");
		NEXTCHAR;
		if (lastchar == '|') NEXTCHAR;	/* allow || */
		break;
   case '&'   : next.symbol = ANDsymbol;
   		strcpy(next.key,"&");
		NEXTCHAR;
		if (lastchar == '&' ) NEXTCHAR;	/* allow && */
		break;
   case '~'   :
   case '!'   : next.symbol = NOTsymbol;
   		strcpy(next.key,"!");
		NEXTCHAR;
		break;
   case '\0'  : next.symbol = EOFsymbol;
   		strcpy(next.key,"");
		break;
   default    : getkey();
   		next.symbol = keyword(next.key);
   		break;
  }
/*  prsymbol(next); */
}/* getsymbol*/

/* check if current symbol is correct and then skip it */
void skipsymbol(symbol)
SYMBOL	symbol;
{
  if (next.symbol != symbol) {
    switch (symbol) {
     case LPARENTsymbol : error("'(' expected"); break;
     case RPARENTsymbol : error("')' expected"); break;
     case ORsymbol      : error("'OR' expected"); break;
     case ANDsymbol     : error("'AND' expected"); break;
     case NOTsymbol     : error("'NOT' expected"); break;
     case KEYsymbol     : error("<key> expected"); break;
     case EOFsymbol     : error("End of query expected"); break;
     default            : error("Something is wrong!!"); break;
    }
  }
  getsymbol();
}/*skipsymbol*/

int query();	/* forward definition */

int factor()
{ int temp;

  switch ( next.symbol) {
   case LPARENTsymbol : getsymbol();	/* skip ( */
   			temp = query();
   			skipsymbol(RPARENTsymbol);
			return(temp);
			break;
   case NOTsymbol     : getsymbol();	/* skip ! */
			return(generate(NOTcode,"",factor(),NOPC,NOPC));
   			break;
   case KEYsymbol     : temp = generate(KEYcode,next.key,NOPC,NOPC,NOPC);
   			getsymbol();	/* skip KEYcode */
			return(temp);
      			break;
   default            : error("Factor expected");
   			break;
  }
  return(NOPC);	/* never */
}/*factor*/

/* remember that 'temp1 AND temp2' is equivalent to:
** [temp ] 1. eval temp1
** [pciff] 2. if ![temp] then [temp2]=false ; goto [temp2]+1
** [temp2] 3. eval temp2
**         4.
*/
int term()
{ int temp;
  int pciff;

  temp = factor();
  while ( next.symbol == ANDsymbol ) {
   getsymbol();	/* skip & */
   pciff = generate(IFFcode,"",temp,NOPC,NOPC);
   temp = factor();
   /* backpatch arg2 and arg3 from IFF */
   instruction[pciff].arg2 = &(instruction[temp].value);
   instruction[pciff].arg3 = (BOOL *)&(instruction[temp+1]);
  }
  return(temp);
}/*term*/

/* remember that 'temp1 OR temp2' is equivalent to:
** [temp] 1. eval temp1
** [pcift] 2. if [temp] then [temp2]=true ; goto [temp+1]
** [temp2] 3. eval temp2
**         4.
*/
int query()
{ int temp;
  int pcift;

  temp = term();
  while ( next.symbol == ORsymbol ) {
   getsymbol();	/* skip | */
   pcift = generate(IFTcode,"",temp,NOPC,NOPC);
   temp = term();
   /* backpatch arg2 and arg3 from IFT */
   instruction[pcift].arg2 = &(instruction[temp].value);
   instruction[pcift].arg3 = (BOOL *)&(instruction[temp+1]);
  }
  return(temp);
}/*query*/

/* findcode return NOPC if not found else place of KEYcode found
** with key=code.key , will always find at least itself */
int find1code(key)
char key[];
{ int i;
  
  for ( i=0 ;; i < MAXNROFCODES ) {
   switch(instruction[i].code) {
   case KEYcode : if ( strcmp(instruction[i].key,key) == 0 ) {
		    return(i);
		  } else {
   		  i++;	/* skip to next */
		  }
		  break;
   case RFIcode : return(NOPC);	/* not found */
   		  break;
   default      : i++; /* skip to next */ 
		  break;
  }/*switch*/
 }/*for*/
}/*find1code*/

/* findcode return NOPC if not found else place of KEYcode found
** with key is substring of code.key  will always find itself */
int find2code(key)
char key[];
{ extern char *sindex();	/* return NULL if not found */
  int i;
  
  for ( i=0 ;; i < MAXNROFCODES ) {
   switch(instruction[i].code) {
   case KEYcode : if ( sindex(instruction[i].key,key) != NULL ) {
		    return(i);
		  } else {
   		  i++;	/* skip to next */
		  }
		  break;
   case RFIcode : return(NOPC);	/* not found */
   		  break;
   default      : i++; /* skip to next */ 
		  break;
  }/*switch*/
 }/*for*/
}/*find2code*/

/* findcode return NOPC if not found else place of KEYcode found
** with code.key is substring of key will always find itself */
int find3code(key)
char key[];
{ extern char *sindex();	/* return NULL if not found */
  int i;
  
  for ( i=0 ;; i < MAXNROFCODES ) {
   switch(instruction[i].code) {
   case KEYcode : if ( sindex(key,instruction[i].key) != NULL ) {
		    return(i);
		  } else {
   		  i++;	/* skip to next */
		  }
		  break;
   case RFIcode : return(NOPC);	/* not found */
   		  break;
   default      : i++; /* skip to next */ 
		  break;
  }/*switch*/
 }/*for*/
}/*find3code*/

/* optimize instructions by looking to add COPcode instructions */
void optkeys1()
{ int i;
  int j;

  for ( i=0 ;; i < MAXNROFCODES ) {
   switch(instruction[i].code) {
    case KEYcode : j = find1code(instruction[i].key);
    		   if ( j != i) {	/* then used before */
		    SETCODE(i,j,NOPC,NOPC,instruction[i].key,COPcode);
		    instruction[i].arg2 = &(instruction[j].count);
		   }
		   i++; 	/* skip to next */
		   break;
    case RFIcode : return; 
		   break;
    default      : i++;  /* skip to next */
    		   break;
   }
  }
}/*optkeys1*/

/* optimize instructions by looking to add CITcode instructions */
/* must call AFTER optkeys1(), otherwise gets a mess */
void optkeys2()
{ int i;
  int j;

  for ( i=0 ;; i < MAXNROFCODES ) {
   switch(instruction[i].code) {
    case KEYcode : j = find2code(instruction[i].key);
    		   if ( j != i) {	/* then used before */
		     SETCODE(i,j,NOPC,NOPC,instruction[i].key,CITcode);
		     instruction[i].arg2 = &(instruction[j].count);
		   }
		   i++; 	/* skip to next */
		   break;
    case RFIcode : return; 
		   break;
    default      : i++;  /* skip to next */
    		   break;
   }
  }
}/*optkeys2*/

/* optimize instructions by looking to add CIFcode instructions */
/* must call AFTER optkeys2(), otherwise gets a mess */
void optkeys3()
{ int i;
  int j;

  for ( i=0 ;; i < MAXNROFCODES ) {
   switch(instruction[i].code) {
    case KEYcode : j = find3code(instruction[i].key);
    		   if ( j != i) {	/* then used before */
			SETCODE(i,j,NOPC,NOPC,instruction[i].key,CIFcode);
			instruction[i].arg2 = &(instruction[j].count);
		   }
		   i++; 	/* skip to next */
		   break;
    case RFIcode : return; 
		   break;
    default      : i++;  /* skip to next */
    		   break;
   }
  }
}/*optkeys3*/

/* just call all optimizations in right order */
void optimize()
{
  optkeys1();	/* add COP codes */
  optkeys2();	/* add CIT codes */
  optkeys3();	/* add CIF codes */
}/*optimize*/

/* return index of instruction where ptr=&instruction[i]{.value} */
int getpc(p)
BOOL	*p;
{ CODE	*ptr;
  int 	i;

 for (i = 0; ; i++) {
   ptr = &instruction[i];	/* for speed */
   if ( ((int *)ptr == p) || (&(ptr->value) == p) ) {
    return(i);
   }
   if (ptr->code == RFIcode) return(NOPC);
 }/*for*/
}/*getpc*/

printcode(i,ptr)
int i;
CODE *ptr;
{
 fprintf(stderr,"[%3d] %s %20s  ARG1=(%3d) ARG2=(%3d) ARG3=(%3d)\n",
    			i,
    			prcodesym(ptr->code),
			ptr->key,
			getpc(ptr->arg1),
			getpc(ptr->arg2),
			getpc(ptr->arg3) );
}/*printcode*/

void dumpcode()
{ int i;
  CODE *ptr;

  for ( i=0 ;; i < MAXNROFCODES ) {
    ptr = &instruction[i];
    printcode(i,ptr);
    if (ptr->code == RFIcode) return;
    i++;
  }
}/*dumpcode*/

/* call parse the first time */
void parse(s)
char s[];
{ int temp;
  extern BOOL dflag;

  string = s;
  string1 = s;
  lastindex = 0;
  PC = 0 ;
  NEXTCHAR;	/* get first char */
  getsymbol();	/* get first symbol */
  temp = query();
  skipsymbol(EOFsymbol);	/* must be at end here */
  (void)generate(RFIcode, "", temp, NOPC, NOPC);
  optimize();
  if (dflag) dumpcode();
}/*parse*/

#define NOT(b1)		( ( b1 )           ? FALSE : TRUE )

/* return result TRUE or FALSE from interpreting */
/* this is were we spend most of our time */
BOOL interpret()
{ register CODE *ptr;
  extern BOOL findkey();	/* and here we spend a lot of time */

 ptr = instruction;	/* for speed reasons */
 count++;
 for (;;) {	/*EVER*/
  switch(ptr->code) {
   case KEYcode :    ptr->value = findkey(ptr);
   		     ptr->count = count;	/* set it to done */
		     ptr++;	/* skip to next instruction */
                     break;
   case COPcode :    if ( *(ptr->arg2) == count ) { /* see if done */
   			ptr->value = *(ptr->arg1);
		     } else {
			ptr->value = findkey(ptr);
		     }
		     ptr++;	/* skip to next instruction */
   		     break;
   case CITcode :    if ( (*(ptr->arg2) == count) && (*(ptr->arg1)) ) {
			ptr->value = TRUE;
		     } else {
		        ptr->value = findkey(ptr);
		     }
		     ptr++;	/* skip to next instruction */
		     break;
   case CIFcode :    if ( (*(ptr->arg2) == count) && (!*(ptr->arg1)) ) {
   			ptr->value = FALSE;
		     } else {
			ptr->value = findkey(ptr);
		     }
		     ptr++;	/* skip to next instruction */
		     break;
   case IFFcode :    if ( *(ptr->arg1) ) {
			ptr++;	/* skip to next instruction */
   		     } else {
			*(ptr->arg2) = FALSE;
			ptr = (CODE *)ptr->arg3;	/* goto (arg3) */
		     }
		     break;
   case IFTcode :    if ( *(ptr->arg1) ) {
			*(ptr->arg2) = TRUE;
			ptr = (CODE *)ptr->arg3;	/* goto (arg3) */
   		     } else {
		        ptr++;	/* skip to next instruction */
		     }
		     break;
   case NOTcode :    ptr->value = NOT(*(ptr->arg1));
		     ptr++;	/* skip to next instruction */
                     break;
   case RFIcode :    return(*(ptr->arg1));
                     break;
   default      :    error("Unknown instruction, kb in error");   
                     break;
  }
 }
}/*interpret*/
@@@ Fin de kb.c
echo kb.h 1>&2
cat >kb.h <<'@@@ Fin de kb.h'
/* kb.h -*-update-version-*-
** HFVR VERSION=Fri May 23 14:46:26 1986
*/

#include <stdio.h>
#include <ctype.h>

typedef enum { EOFsym, FOUNDsym, NOTFOUNDsym } SYM;

#define FALSE	0
#define TRUE	1
typedef int  BOOL;

#define SIZE	20	/* max length of keywords */

#define	TSIZE 128
typedef int	KEYTAB[TSIZE];	/* for fsindex */

/* 3-address codes */
typedef enum {	RFIcode,	/* return (*arg1) */
				/* Return From Interpreter */
		IFTcode,	/* if *arg1 then val[arg2]=true, goto arg3 */
		IFFcode,	/* if !*arg1 then val[arg2]=false,goto arg3 */
		NOTcode,	/* val = NOT(*arg1) */
		KEYcode,	/* val = findkey(key) */
		COPcode,	/* val = *arg1 */
		CITcode,	/* val = (*arg1 ? *arg1 : findkey(key) ) */
				/* Copy If True */
		CIFcode,	/* val = (*arg1 ? findkey(key) : *arg1 ) */
				/* Copy If False */
	     } CODEsym;

typedef struct {	BOOL	*arg1;	/* ptr to argument 1 */
			BOOL	*arg2;	/* ptr to argument 2 */
			BOOL	*arg3;	/* ptr to argument 3 */
			int	count;	/* to indicate if used */
			BOOL	value;  /* result of this 3-address code */
			char	key[SIZE];
			KEYTAB	*table;	/* ptr to fsindex table */
			CODEsym code;	/* which code it is */
	       } CODE;

/* usage of CODE by the different codes
**
** CODE	arg1		arg2		arg3		count	key	value
** ----	------------	-------------	------------	-----	---	-----
** IFT	&val[pc-1]	&val[toassign]	&ins[togoto]	
** IFF	&val[pc-1]	&val[toassign]	&ins[togoto]	
** KEY							count	key	T/F
** COP	&val[copfrom]	&count[copfrom]				key	T/F
** CIT	&val[copfrom]	&count[copfrom]				key	T/F
** CIF	&val[copfrom]	&count[copfrom]				key	T/F
** RFI	&val[pc-1]
*/

extern void initfsindex();
extern char *fsindex();
extern char *sindex();
extern dflag;
@@@ Fin de kb.h
echo kb.man 1>&2
cat >kb.man <<'@@@ Fin de kb.man'
.TH kb 1 "HFVR"
.SH NAME
.\" name \- one-line description for in permuted index
kba, kbc, kbk, kbq, kbu, kbx, kbr \- to work on knowhow database
.SH SYNOPSIS
.\" bnf on command syntax
kba [-e]
.br
kbc [-d] <query>
.br
kbk [-d] <query>
.br
kbq [-d] <query>
.br
kbr
.br
kbu [-d] <query>
.br
kbx [-d] <query> <command>
.SH DESCRIPTION
.\" semantics
kb (Knowledge Base) is a simple database to store items of
information. The items can then be
retrieved from this database via any key words.
.sp
kba [-e] will prompt for info which is then stored into the knowhow
database. When the -e option is given the $EDITOR is used to enter
the information.
.sp
kbc <query> will give count of items from knowhow database which
are specified by the <query> (see below).
.sp
kbk <query> will print out the keys (filenames)
of the items from the knowhow database
which are specified by the <query>. (See below).
.sp
kbq <query> will print all items from the knowhow database which 
are specified by the <query> (see below).
.sp
kbr will recreate the database from the old format into the current format.
(only needed for old versions).
.sp
kbu <query> will present all items which are specified by <query> (see below)
to the editor one by one. After the edit session the items are updated in the
knowhow database.
.sp
Kbx <query> <command> will execute <command> for each item specified by
<query> (see below). The first argument of the <command> is the name of
the file with the information in it.
.SH QUERY
The <query> can be described in BNF as follows:
.nf
<query> ::= <term> { OR <term> }*

<term> ::= <factor> { AND <factor> }*

<factor> ::=   <key>
                | NOT <factor>
                | ( <query> )

<key> ::= <anything alphanumeric lower case only>

STANDARD        ALTERNATIVES
--------        ------------
NOT             !       ~       not
AND             &       &&      and
OR              |       ||      or
(               [       {       <
)               ]       }       >
.fi
.SH OPTIONS
.tr ^
.in +6
.ti -6
-d^^^^turns on debug mode, to show internal KBCODE. (For experts only).
.in -6
.SH EXAMPLES
.nf
kba      # just follow the prompt
kba -e   # use $EDITOR to input information
kbq test # list all items with keyword 'test'
kbq 'test or trial' # list all item with keywords test or trial
kbq 'sh & not ksh'  # list all items about sh but not about ksh
kbu test # edit and update all items with keyword 'test'
.fi
.SH FILES
$HOME/.knowhow.*
.SH VARABLES
.nf
$EDITOR set to favorite editor (default is /bin/ed)
$KNOWHOW set to knowhow database (directory)
.fi
.SH TRICKS
You can use any directory as knowlegde base try for example:
.nf
         KNOWHOW=/usr/news
         export KNOWHOW
         kbq down

.fi
This will list all news items with the key 'down' in it.
.SH ATTENTION
The knowhow database is implemented as a simple directory. All files
in this directory are scanned for the required keys. It is up to the user to 
keep this directory clean.
.sp
The keys are treated with upper and lowercase folded. So there is
not difference between key and KEY.
@@@ Fin de kb.man
echo kba 1>&2
cat >kba <<'@@@ Fin de kba'
# kba to add stuff to db

export TMP EDITOR KNOWHOW
: ${KNOWHOW=$HOME/.knowhow}
: ${EDITOR=/bin/ed}
LP}TMP=/usr/tmp/d$$

# create TMP file
/bin/rm -f  $TMP
touch $TMP

# check for db exist
if test -d $KNOWHOW
 then
  : # fine
 else
  mkdir $KNOWHOW
  echo "$KNOWHOW created."
fi

# get info to store
if test "$1" = "-e"
 then
  echo "Wait while I start $EDITOR...\c"
  $EDITOR $TMP
elif test "$1" = "-f"
 then
  cat $2 > $TMP
 else
  echo "\nEnter text (end with ^D)"
  /bin/cat >> $TMP
fi

# see if empty or not
if test -s $TMP
 then
  : # okay
 else
  echo "Aborted, no input"
  exit 1
fi


NOW=`date +%y%m%d%H%M%S`
cat $TMP > $KNOWHOW/$NOW
echo "\n$0: Item $NOW  added"
/bin/rm -f $TMP
@@@ Fin de kba
echo kbq.c 1>&2
cat >kbq.c <<'@@@ Fin de kbq.c'
/* kbq.c to query db-keys for keys -*-update-version-*-
** HFVR VERSION=Tue Jan  6 14:17:54 1987
*/

#include	<stdio.h>
#include	<ctype.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include "kb.h"

char	*blk = (char *)NULL;	/* ptr to block allocated */
int	blksize = 0;		/* size of blk block */
int	filsize;		/* size of file */

static int  count = 0;		/* number of files found matching */
char KNOWHOW[512];	/* holds copy of $KNOWHOW */
char *NAME;		/* pointer to program name */
char *CMD;		/* pointer to <command> */
BOOL dflag = FALSE;	/* TRUE if must print code */


/* findkey return TRUE if found else FALSE */
BOOL findkey(ptr)
CODE	*ptr;
{ 
	extern char *fsindex();

	if ( fsindex(blk,ptr->key,ptr->table) != NULL ) {
		return(TRUE);
	} else {
		return(FALSE);
	}
}/*findkey*/

/* chk file for expression match */
SYM chkfile()
{ 
	extern BOOL interpret();

	if ( TRUE == interpret() ) {
		return(FOUNDsym);
	} else {
		return(NOTFOUNDsym);
	}
}/*readkey*/

/* get dir to work in via $KNOWHOW or $HOME/.knowhow */
void getdir()
{
	extern char *getenv();
	char *ptr;

	ptr = getenv("KNOWHOW");
	if ( ptr != NULL ) {
		strcpy(KNOWHOW,ptr);
	} else {
		ptr = getenv("HOME");
		strcpy(KNOWHOW,ptr);
		strcat(KNOWHOW,"/.knowhow");
	}
}/*getdir*/

/* command */
void command(name)
char	*name;
{
	char cmd[512];
	
	strcpy(cmd,CMD);
	strcat(cmd," ");
	strcat(cmd,name);
	system(cmd);
}/*command*/

/* do update on result of query */
void update(name)
char	*name;
{ 
	char *ptr;
	char EDITOR[512];
	char cmd[512];

	/* : ${EDITOR=/bin/ed} */
	ptr = getenv("EDITOR");
	if ( ptr != NULL ) {
		strcpy(EDITOR,ptr);
	} else {
		strcpy(EDITOR,"/bin/ed");
	}

	/* $EDITOR $KNOWHOW/name */
	printf("%s: Wait while I start %s ...",NAME,EDITOR); 
	fflush(stdout);
	strcpy(cmd,EDITOR);
	strcat(cmd," ");
	strcat(cmd,name);
	system(cmd);

	printf("%s: item %s updated\n",NAME,name);
}/*update*/

/* set global NAME to start of program name */
void getname(name)
char *name;
{ 
	extern char *strrchr();

	NAME = strrchr(name,'/');
	if ( NULL == NAME ) {
		NAME = name;
	} else {
		NAME++;
	}
}/*getname*/

void printkey(name)
char	*name;
{
	printf("%s\n",name);
}/*printkey*/

void printitem(name)
{
	char cmd[512];

	strcpy(cmd,"cat ");
	strcat(cmd,name);
	strcat(cmd," ; echo");
	system(cmd);
}/*printitem*/

/* strtolower convert string to uppercase */
void strtolower(s)
register char	*s;
{
	while ( *s ) {
		*s = tolower(*s);
		s++;
	}
}/*strtolower*/

/* determine whether we should print or update */
int workonid(name)
{
	struct stat stbuf;
	int f1;

	/* see if file exists, if so read it in buffer */
	if (stat(name,&stbuf) == -1) {
		return(10);	/* no such file */
	}

	/* see if we need more space */
	filsize = stbuf.st_size;
	if ( filsize + 1 > blksize ) {
		blksize = filsize + 1;
		if (blk != NULL) free(blk);
		blk = (char *)malloc(blksize);
		if (blk == NULL) {
			fprintf(stderr,"%s : ERROR : file %s too big\n",NAME,name);
			return(11);
		}
	}

	/* read in file */
	f1 = open(name,0);
	if (f1 == -1) {
		fprintf(stderr,"%s : ERROR : cannot read %s\n",NAME,name);
		return(12);
	}
	read(f1, blk, blksize);
	close(f1);
	blk[filsize] = '\0';
	strtolower(blk);

	if ( NOTFOUNDsym == chkfile() ) return(0);

	count++;
	switch(NAME[2]) {
	case 'u'     : 
		update(name);
		break;	/* kbu */
	case 'x'     :
		command(name);
		break;	/* kbx */
	case 'q'     : 
		printitem(name);
		break;	/* kbq */
	case 'c'     :
		break;   /* kbc */
	case 'k'     : 
		printkey(name);
		break;	/* kbk */
	default	     : 
		printitem(name);
		break;
	}
	return(0);
}/*workonid*/

main(argc,argv)
int	argc;
char	*argv[];
{ 
	register SYM result;
	extern void parse();
	int code;

	if ( argc < 2) {
		fprintf(stderr,"\007Usage: %s [-d] <query>\n",argv[0]);
		exit(1);
	}
	getname(argv[0]);
	if ( strcmp(argv[1],"-d") == 0 ) {
		dflag = TRUE;
		CMD = argv[3];
		strtolower(argv[2]);
		parse(argv[2]);
	} else {
		CMD = argv[2];
		strtolower(argv[1]);
		parse(argv[1]);
	}
	getdir();
	code = dir(KNOWHOW,workonid,1,1);
	switch(code) {
	case 0 : 
		break;
	case 1 : 
		fprintf(stderr,"%s : ERROR : %s directory does not exist\n",NAME,KNOWHOW);
		break;
	case 2 : 
		fprintf(stderr,"%s : ERROR : %s is not a directory\n",NAME,KNOWHOW);
		break;
	case 3 : 
		fprintf(stderr,"%s : ERROR : string '%s' too long to handle\n",NAME,KNOWHOW);
		break;
	case 4 : 
		fprintf(stderr,"%s : ERROR : cannot read %s\n",NAME,KNOWHOW);
		break;
	case 10: 
		break;
	case 11: 
		break;
	case 12: 
		break;
	default: 
		fprintf(stderr,"%s : ERROR : unknown cause\n");
		break;
	}
	fprintf(stderr,"%s: %d items found\n",NAME,count);
	return(code);
}/*main*/

@@@ Fin de kbq.c
echo sindex.c 1>&2
cat >sindex.c <<'@@@ Fin de sindex.c'
#include <stdio.h>

/* sindex looks for an occurance  of the string s2 in the string
** s1. If s2 does not ocurr, sindex returns NULL. Otherwise, sindex
** returns pointer in s1 to where s2 ocurrs.
*/
char *sindex(big,small)
register  char *big;
register  char *small;
{ int biglen;
  int smlen;
  register char *lastchar;
  register char *bigptr, *smptr, *curptr;
  
  biglen = strlen(big);
  smlen = strlen(small);
  if (biglen < smlen || smlen == 0) return(NULL);
  lastchar = big + biglen - smlen;

  for (bigptr=big ; bigptr <= lastchar ; bigptr++) {
	curptr = bigptr;
	smptr = small;
	while ((*smptr) && (*smptr == *curptr++)) smptr++;
	if (*smptr == '\0') return(bigptr);
  }
  return(NULL);
}/*sindex*/

@@@ Fin de sindex.c