#ifdef MSDOS	/* { */
#include	"grep_msd.h"
#endif		/* } */
char sccsID[] =
	"make.c V1.0 Enhancements Copyright http://www.berklix.com (pjc) 1989\n" ;
char	**ARGV ;

#include <stdio.h>
#include <errno.h>
extern int errno;

#define UPGRADE
#define MACFILES

#ifdef ns32000
#define BSD4_2
#endif

#ifdef	MSDOS

#define SHELL "/dos/command.com"   /* the shell to execute */
#define PATHCHAR ';'	  /* character that separates elements of the PATH */
char linecont = '+';	 /* default line continuation character */
char switchc =	'/';	  /* default directory separation char */

#include <process.h>
#include <fcntl.h>

#ifdef	UPGRADE
char	*getcwd();
#endif

#else

#define SHELL "/bin/sh"   /* the shell to execute */
#define PATHCHAR ':'	  /* character that separates elements of the PATH */
char linecont = '\\';	  /* default line continuation character */
char switchc =	'/';	  /* default directory separation char */

#endif

/* the argument to malloc */
#define UI unsigned int

#define TIME long 

/* generic linked list */
struct llist {
    char *name;
    struct llist *next;
};

struct defnrec {
    char *name;
    int uptodate;
    TIME modified;
    struct llist *dependson;
    struct llist *howto;
    struct defnrec *nextdefn;
};

struct macrec {
    char *name,*mexpand;
    struct macrec *nextmac;
};

struct rulerec {
    char *dep,*targ;	/* order is important because there are defaults */
    struct llist *rule;
    int def_flag;
    struct rulerec *nextrule;
};

/* default rules for make */
/*
   1. Modifiy NDRULES to reflect the total number of rules...
   2. Enter rules in the following format
	"source-extension","object-extension","rule",NULL,
      and update NDRULES.
   3. Note that the default rules are searched from top to bottom in this
      list, and that within default rules, macros are ignored if not defined
      (You may specify FFLAGS in the rule without requiring the user to 
       define FFLAGS in his/her makefile)
   4. The rule applied will be the first one matching. If I ever get a
      public domain YACC working, the rule for it will be first, and look
      like ".y",".o","bison $(YFLAGS) $*.y","cc $* $(CFLAGS)",NULL,
      There are at present no depends in rules, so you need a .y.o: and
      a .y.c: rule
   5. The src and dest extensions are expected to be in lower case
   6. SUFFIX sets the name and precedence for the prerequisite macros,
      $< and $?, and for looking up rules. Basically, all <obj> type
      extensions must be to the left of <src> type extensions. All
      rules should have their suffix mentioned. See UNIX documentation or
      make.doc
*/

#ifdef	MSDOS
#define NDRULES 2
#else
#define NDRULES 3
#endif
char *def_list[] =
{
#ifdef	MSDOS
    ".c", ".obj", "$(CC) $*.c $(CFLAGS)", NULL,
    ".asm", ".obj", "$(AS) $*.asm $(AFLAGS)", NULL,
/*
    ".obj", ".exe", "$(LD) $*.obj $(LFLAGS)", NULL,
*/
#else
    ".c",".s","$(CC) -S $(CFLAGS) $*.c",NULL,
    ".c",".o","$(CC) -c $(CFLAGS) $*.c",NULL,
    ".s",".o","$(AS) $(AFLAGS) -o $*.o $*.s",NULL,
#endif
};

#ifdef	MSDOS
#define SUFFIXES ".exe .obj .asm .c"
#else
#define SUFFIXES ".o .s .c"
#endif

TIME time();
#define now() (TIME) time( (char *) 0)	/* the current time, in seconds */

char **ext_env;
char **my_env;
int  n_myenv;
int  max_myenv;
char *whoami = "Make";	


#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<=(b)?(a):(b))
#define BKSLSH '\134'
#undef ERROR
#define ERROR -1
#define TRUE 1
#define FALSE 0
#define EQ(X,Y) (strcmp(X,Y) == 0)
#define NUL '\0'
#define isnull(X) (X == '\0' ? TRUE : FALSE)
#define notnull(X) (X == '\0' ? FALSE : TRUE )
#define isspace(X) ( (X == ' ') || (X == '\011') )


/*
flags for expand. Report and ignore errors, and no_target means error if
$@ or $* is attempted ( as in $* : a.obj ) 
*/
#define NO_TARG  2
#define REPT_ERR 1
#define IGN_ERR  0

#define INMAX	4000	/* maximum input line length (allow for expansion)*/
#define INMAXSH 300	/* length for short strings */


#ifdef BSD4_2
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define WAIT union wait

#else
/* sysV and MONIX  and LATTICE */
#ifdef	MSDOS
#include <sys/types.h>
#include <sys/stat.h>
#else
struct stat {
    short int st_dev;
    unsigned short st_ino;
    unsigned short st_mode;
    short int st_nlink;
    short int st_uid;
    short int st_gid;
    short int st_rdev;
    long st_size;
    TIME st_atime;
    TIME st_mtime;
    TIME st_ctime;
} ; 
/*
#include <sys/types.h>
#include <sys/stat.h>
*/
#endif
#define WAIT int
#endif


struct macrec	*maclist = NULL;
struct defnrec	*defnlist = NULL;
struct llist	*dolist = NULL;
struct llist	*path_head = NULL;
struct llist	*suff_head = NULL;
struct rulerec	*rulelist = NULL;
struct llist	*makef = NULL;


#ifdef	MACFILES
char	MAC_sep;
char	MAC_cont[3] = " +";
char	MAC_file[INMAXSH] = "linker.mac";
#endif

int execute = TRUE;
int stopOnErr = TRUE;
int forgeahead = FALSE;
int madesomething = FALSE;
int knowhow = FALSE;
int no_file = TRUE;
int silentf = FALSE;
#ifdef DEBUG
int tree_and_quit = FALSE;
int post_tree = FALSE;
#endif
int path_set = FALSE;

/* return declarations for functions not returning an int */
TIME make(),getmodified(),findexec();
char *mov_in(),*get_mem();
struct llist *MkListMem(), *mkllist(), *mkexphow(), *mkexpdep(),*add_llist();

char	*index(), *rindex();

/* temp storage variables for the prerequisite lists */
struct m_preq {
    char m_dep[6];
    char m_targ[6];
    char m_name[INMAXSH];
};
char dothis[INMAX];    /* generic temporary storage */
FILE *fopen();

main(argc,argv,envp)
int argc;
char *argv[],*envp[];
{
#ifdef	VSL	/* { */
	extern vsl() ;

	ARGV = argv ;
#include	"../../include/vsl.h"
#endif		/* } */

    ext_env = envp;
    init(argc,argv);
#ifdef DEBUG
    if (tree_and_quit){
	prtree();
fprintf(stderr, "tree and quit\n");
	done(0);
    }
#endif
	

    /* now fall down the dolist and do them all */

    while (dolist != NULL) {
	madesomething = FALSE;
	make(dolist->name);
	if (!madesomething) {
	    if (knowhow) {
		if ( !silentf )
		    fprintf(stdout,"%s: '%s' is up to date.\n",whoami,dolist->name);
	    }
	    else {
		error2("Don't know how to make %s",dolist->name);
	    }
	}
	dolist = dolist->next;
    }
#ifdef DEBUG
    if (post_tree) prtree();
#endif 

    done( 0 );
}


init(argc,argv)
int argc;
char *argv[];
{
int i,k;
int pdep,ptarg;
struct llist *ptr;
int usedefault = TRUE;

    build_env();	/* build the environment */

    for (k=i=0; i < NDRULES; i++,k++) {
	/* couldn't init the linked list  with the compiler */
	pdep = k++;
	ptarg = k++;
	ptr = NULL;
	while ( def_list[k] != NULL ) {
	    ptr = add_llist(ptr,def_list[k]);
	    k++;
	}
	add_rule2(def_list[pdep],def_list[ptarg],ptr,TRUE);
    }

    /* initialize the suffix list */
    add_suff(SUFFIXES);

    /* load the macro MFLAGS */
    for  ( i = 1,dothis[0] = NUL;  i < argc ; i++ ) {
	strcat(dothis,argv[i]);
	strcat(dothis," ");
    }
    add_macro("MFLAGS",dothis, TRUE);

    /* add any default macros here, such as CC */
#ifdef	MSDOS
       add_macro("CC","ic86", TRUE);
       add_macro("AS","asm86", TRUE);
#else
       add_macro("CC","cc", TRUE);
       add_macro("AS","as", TRUE);
#endif

    /* step thru the args and read the flags, patterns and objects */
    for (i=1; i < argc; i++) {
	if (argv[i][0] == '-') {    /* option */
	    switch (argv[i][1]) {
		case 'f': case 'F': /* arg following is a makefile */
		    if (++i < argc) {
			makef = add_llist(makef,argv[i]);
			usedefault = FALSE;
		    }
		    else 
			panic("'-f' requires filename");
		    break;

		case 'c': case 'C':  /* line continuation char */
		   if ( argv[i][2] != NUL ) 
		      linecont = argv[i][2];
		   else {
		       if ( ++i < argc || notnull(argv[i][1]) )
			   linecont = argv[i][0];
		       else
			   error("'-c' requires single character");
		   }
		   break;

		case 'k':case 'K':  /* abandon work on this branch on error */
		    forgeahead = TRUE;
		    break;

		case 'b': case 'B':  /* switchchar change */
		   if ( argv[i][2] != NUL ) 
		      switchc = argv[i][2];
		   else {
		       if ( ++i < argc || notnull(argv[i][1]) )
			   switchc = argv[i][0];
		       else
			   error("'-b' requires single character");
		   }
		   break;

		case 'i': case 'I': /* ignore errors on execution */
		    stopOnErr = FALSE;
		    break;

		case 'n': case 'N': /* don't execute commands - just print */
		    execute = FALSE;
		    break;

		case 's': case 'S': /* execute, but don't print */
		    silentf = TRUE;
		    break;
		    
#ifdef DEBUG
		case 'p':case 'P':  /* print a tree and quit */
		    tree_and_quit = TRUE;
		    break;

		case 'v': case 'V': /* print the tree after all processing */
		    post_tree = TRUE;
		    break;
#endif 

		default:
		    error2("unknown option '%s'",argv[i]);
	    }

	}
	else {
	    if ( !maccheck(argv[i]) ) {
		/* if argv[i] is not of the pattern xxx=yyy, then
		   it must be something to make. maccheck will add the
		   macro if it is...
		*/
		dolist = add_llist(dolist,argv[i]);
		no_file = FALSE;
	    }
	}
    }
    /* 
       collect the flags, then read the makefile, otherwise a construct like
       make -f make2 aardvark
       will make both aardvark and the first target in make2
    */	 
    if (usedefault) {
#ifdef	MSDOS
	readmakefile("makefile");
#else
	if ( getmodified("Makefile") != (TIME) 0 ) readmakefile("Makefile");
	else readmakefile("makefile");
#endif
    }
    else {
	for ( ptr = makef; ptr != NULL; ptr = ptr->next )
	   readmakefile(ptr->name);
    }
    
    /* if not done by a PATH directive, set the basic PATH */
    if ( !path_set ) {mkpathlist();path_set = TRUE;}

    
}/* init */

TIME make(s)	/* returns the modified date/time */
char *s;
{
	struct defnrec	*defnp,*tryrules(),*dummy;
	struct llist	*depp,*depp2;
	struct llist	*howp;
	char		*m_comp,*m_ood,*m_obj;
	char		*stradd(),*add_prereq();
	TIME		latest,timeof;
	struct m_preq	ma;
#ifdef	MSDOS
#ifdef	UPGRADE
	char	*getcwd();
	char	my_cwd[70];
#endif
#endif


    /* look for the definition */
    for ( defnp = defnlist; defnp != NULL; defnp = defnp->nextdefn ) 
	if ( EQ(defnp->name,s) )  break;
    
    /* 
       there might be some adjusting of the lists for implied compilations.
       these additions are not done in readmakefile;
       they might not be required for these targets ( ie not requested )
    */

    if (defnp == NULL ){
	defnp = tryrules(s);
	if (defnp == NULL){   /* tryrules returns a pointer to a defnrec */
	    /* tryrules fails, and there is no definition */	    
	    knowhow = FALSE;
	    latest = getmodified(s);
	    if (latest==0) {
		/* doesn't exist but don't know how to make */
#ifdef	UPGRADE
		panic2("Don't know how to make %s",s);
#else
		panic2("Can't make %s",s);
#endif
		/* NOTREACHED */
	    }
	    else {
		/*
		   exists - assume it's up to date since we don't know
		   how to make it. Make a defnrec with uptodate == TRUE.
		   This is not strictly necessary, and this line may be
		   deleted. It is a speed win, at the expense of core, when
		   there are a lot of header files.
		*/
		
		/*add_defn(s,TRUE,latest,
			 (struct llist *)NULL,(struct llist*)NULL); */
		return(latest);
	    }
	}
    }
    
    else{    /* there is a definition line */
	if (defnp->uptodate) {
	    return(defnp->modified);
	}
	dummy = tryrules(s); 
	if (dummy != NULL && defnp->howto == NULL){
	    /* any explicit howto overrides an implicit one */
	    /*add depend*/
	    if (defnp->dependson == NULL)
		defnp->dependson = dummy->dependson;
	    else{
		for (depp2 = defnp->dependson;
		     depp2->next != NULL;
		     depp2 = depp2->next); /* advance to end of list */
		depp2->next = dummy->dependson;
	    }
	    /* add howto line */
	    defnp->howto = dummy->howto;
	}
    }
    /* finished adjusting the lists */

    /* check that everything that the current target depends on is uptodate */
    m_comp = m_ood = m_obj = NULL;
    latest = 0;
    depp = defnp->dependson;
    while (depp != NULL) {

	m_comp = add_prereq(m_comp,depp->name,&ma);/* add to string for $< */
	timeof = make(depp->name);
	latest = max(timeof,latest);/*written this way to avoid side effects*/

	if ( defnp->modified < timeof ) { 
	    /* these are out of date with respect to the current target */
	    m_ood = stradd(m_ood,ma.m_name,ma.m_dep); /* add source */
	    m_obj = stradd(m_obj,ma.m_name,ma.m_targ);/* add object */
	}
	depp = depp->next;
    }

    knowhow = TRUE; /* has dependencies therefore we know how */

    /* if necessary, execute all of the commands to make it */
    /* if (out of date) || (depends on nothing)	    */

    if (latest > defnp->modified || defnp->dependson==NULL) {
	/* make those suckers */
	howp = defnp->howto;
#ifndef UPGRADE
	if ((howp == NULL) && !in_dolist(s)) {
      fprintf(stderr,"%s: %s is out of date, but there is no command line\n",
		  whoami,s);
	   if ( stopOnErr ) mystop_err();
	}
#else
#ifdef	MSDOS
	getcwd(my_cwd, 70);
#endif
#endif
	while (howp != NULL) {

	    /* the execute flag controls whether execution takes place */
	    p_expand(howp->name,dothis,m_comp,m_ood,m_obj);
	    if ( (exec_how(dothis) != 0) )
	       if (forgeahead) break;
	       else if (stopOnErr) panicstop();
	    howp = howp->next;
	}
#ifdef	UPGRADE
#ifdef	MSDOS
	chdir(my_cwd);
#endif
#endif
	/* we just made something. Set the time of modification to now. */
	defnp->modified =  now();
	defnp->uptodate = TRUE;
	if (defnp->howto != NULL)   /* we had instructions */
	    madesomething = TRUE;
    }
    if ( m_comp != NULL ) free( m_comp );
    if ( m_ood	!= NULL ) free( m_ood );
    if ( m_obj	!= NULL ) free( m_obj );
    return(defnp->modified);

}


struct llist *add_llist(head,s)  /*this adds something to a linked list */
char *s;
struct llist *head;
{
struct llist *ptr1;

    /* find a slot in the list */
    if (head == NULL) {
	ptr1 = MkListMem();
	ptr1->name = mov_in(s);
	ptr1->next = NULL;
	return ( ptr1 );
    }
    else {
	for (ptr1 = head; ptr1->next != NULL ; ptr1 = ptr1->next ) ;
	ptr1->next = MkListMem();
	ptr1->next->name = mov_in(s);
	ptr1->next->next = NULL;
	return( head );
    }
}

#ifdef	UPGRADE

char	*
inp_macro(start, src, dest)
char	*start, *src, *dest;
{
	char	echar;

	if(*++src == '\0')
		return(src);
	if(*src == '('){
		echar = ')';
		src++;
	}
	else if(*src == '{'){
		echar = '}';
		src++;
	}
	else {
		*dest++ = *src++;
		*dest = 0;
		return(src);
	}
	while(*src && *src != echar)
		*dest++ = *src++;
	if(!*src++)
		panic2("No closing brace/paren for %s",start);
	*dest = 0;
	return(src);
}

expand(src,dest,target,flag)	/* expand any macros found*/
char *src,*dest,*target;
int  flag;
{
	char  thismac[INMAX],*ismac();
	char  thismac2[INMAX],*macptr;
	char  cstr1[INMAXSH], cstr2[INMAXSH];
	char  *start, *macstart;
	int   back;

	back = 0;
	if ( src == NULL ) {   /* NULL pointer, shouldn't happen */
		dest[0] = NUL;
		return;
	}
	start = src;
	while( notnull(*src) ) {
		if (*src != '$'){
			dest[back++] = *src++;
			continue;
		}
		macstart = src;
		src = inp_macro(start, src, thismac);
		switch(*thismac){
		case '<':	/* pass meta characters through */
		case '?':
		case '>':
		case '$':
			while(macstart < src)
				dest[back++] = *macstart++;
			break;
		case '\0':
			break;
		default:
			submacro(thismac, cstr1, cstr2);
			if ( (macptr=ismac(thismac)) == NULL)
				break;
			if(start == macptr){
				error2("Recursive macro? %s", thismac);
				break;
			}
			expand(macptr,thismac2,target,flag);
			if(cstr1[0]){
				subexpand(thismac2, cstr1, cstr2, thismac);
				back = mv_install(thismac, dest, back);
			}
			else
				back = mv_install(thismac2, dest, back);
			break;
		case '*':    /*target without extension*/
		case '@':    /*whole target*/
			if ((flag & NO_TARG) && (flag & REPT_ERR) ){
				fprintf(stderr,
			"%s: '$%c' not in a command or dependency line\n",
						      whoami,*thismac);
				if ( stopOnErr )
					mystop_err();
				return;
			}
			submacro(thismac, cstr1, cstr2);
			fdmacro(target, *thismac, thismac[1], thismac2);
			if(cstr1[0]){
				subexpand(thismac2, cstr1, cstr2, thismac);
				back = mv_install(thismac, dest, back);
			}
			else
				back = mv_install(thismac2, dest, back);
			break;
		}
	}
	dest[back] = 0;
}

submacro(macro, cstr1, cstr2)
char	*macro, *cstr1, *cstr2;
{
	char	*tmp, *tp, *p;

	cstr1[0] = 0;
	for(p = macro; *p && *p != ':' ; p++);
	if(!*p)
		return;
	tmp = p;
	tp = cstr1;
	for(p++ ; *p && *p != '=' ; *tp++ = *p++);
	if(!*p){
		cstr1[0] = 0;
		return;
	}
	*tp = 0;
	*tmp = 0;
	p++;
	tmp = cstr2;
	while(*tmp++ = *p++);
}

subexpand(macro, cstr1, cstr2, dest)
char	*macro, *cstr1, *cstr2, *dest;
{
	char	*start;
	char	*p, *q;
	int	i = strlen(cstr1);
	int	j;

	for(;;){
		while(*macro == ' ' || *macro == '\t')
			*dest++ = *macro++;
		if(*macro == '\0'){
			*dest = 0;
			return;
		}
		start = macro;
		while(*macro != ' ' && *macro != '\t' && *macro)
			macro++;
		j = (macro - start) - i;
		if(j < 0){
			while(start != macro)
				*dest++ = *start++;
			continue;
		}
		while(j > 0){
			*dest++ = *start++;
			j--;
		}
		for(j = i, p = start, q = cstr1 ; j ; j-- , p++, q++)
			if(*p != *q)
				break;
		if(j){
			while(start != macro)
				*dest++ = *start++;
			continue;
		}
		p = cstr2;
		while(*p)
			*dest++ = *p++;
	}
}

fdmacro(target, echar, xchar, dest)
char	*target;
char	echar, xchar;
char	*dest;
{
	char	*q, *macstart;

	if(xchar == 'D'){
		macstart = rindex(target, switchc);
		if(macstart)
			for(q = target ; q < macstart;)
				*dest++ = *q++;
	}
	else if(xchar == 'F'){
		if(q = rindex(target, switchc))
			q++;
		else
			q = target;
		if(echar == '*'){
			while(*q && *q != '.')
				*dest++ = *q++;
		}
		else while(*q)
			*dest++ = *q++;
	}
	else {
		for(q = target; *q ; ){
			if(*q == '.' && echar == '*' && index(q, switchc) == 0)
				break;
			*dest++ = *q++;
		}
	}
	*dest = 0;
}

p_expand(src,dest,compl_preq,ood_preq,ood_obj)
char *src,*dest;
char *compl_preq,*ood_preq,*ood_obj;
{
	/*
	 * expand the special macros $< $? $> just before execution
	 */
	char	*sp, *start, *macstart;
	char	echar, xchar;
	struct	llist	*ptr, *xptr, *mlllist();
	char	tmp[INMAX];
	char	cstr1[INMAXSH], cstr2[INMAXSH];

	if ( src == NULL ) {
		dest[0] = NUL;
		return;
	}

	for(start = src ; *src ; ){
		if(*src != '$'){
			*dest++ = *src++;
			continue;
		}
		macstart = src;
		src = inp_macro(start, src, tmp);
		submacro(tmp, cstr1, cstr2);
		echar = *tmp;
		xchar = tmp[1];
		switch(*tmp){
		case '\0':
			continue;
		case '$':
			macstart++;
		default:
			while(macstart < src)
				*dest++ = *macstart++;
			continue;
		case '?':
			sp = ood_obj;
			break;
		case '<':
			sp = compl_preq;
			break;
		case '>':
			/* the src files that are out of date (NOT STANDARD)*/
			sp = ood_preq;
			break;
		}
		for(xptr = ptr = mkllist(sp); ptr ; ptr = ptr->next){
			if(cstr1[0]){
				fdmacro(ptr->name, echar, xchar, tmp);
				subexpand(tmp, cstr1, cstr2, dest);
			}
			else
				fdmacro(ptr->name, echar, xchar, dest);
			while(*dest)
				dest++;
			if(ptr->next)
				*dest++ = ' ';
		}
		free_list(xptr);
	} /* for */
	*dest = NUL;
}

#else

expand(src,dest,target,flag)	/* expand any macros found*/
char *src,*dest,*target;
int  flag;
{
char  thismac[INMAX],*ismac(),*ismac_c();
char  thismac2[INMAX],*macptr;
int   i,pos,back, j;


   back = pos = 0;
   if ( src == NULL ) {   /* NULL pointer, shouldn't happen */
       dest[0] = NUL;
       return;
   }
   while( notnull(src[pos]) ) {

       if (src[pos] != '$') dest[back++] = src[pos++];

       else {
	   pos++;
	   /*found '$'. What kind of macro is this? */
	   switch(src[pos]){
		case '(':   /*regular macro*/
		case '{':   /*regular macro*/
		   /* do macro stuff */
		   pos = x_scan(src,pos,thismac); /* get macro */
		   if ( maclist == NULL && (flag & REPT_ERR) )
			error2("No macros defined -- %s",thismac);
		   else if ( (macptr=ismac(thismac)) == NULL) {
			expand(thismac,thismac2,target,flag);
			if ( (macptr = ismac(thismac2)) != NULL)
			    /* the recursive expand found it */
			    back = mv_install(macptr,dest,back);
			else {
			    if ( flag & REPT_ERR )
				error2("Can't expand macro -- %s",thismac2);
			}
		   }
		   else {
			/* macptr points to appropriate macro */
			back = mv_install(macptr,dest,back);
		   }
		   break;

		case '*':    /*target without extension*/
		case '@':    /*whole target*/
		   if ((flag & NO_TARG) && (flag & REPT_ERR) ){
			 fprintf(stderr,"%s: '$%c' not in a command or dependency line\n",
				      whoami,src[pos]);
			  if ( stopOnErr ) mystop_err();
			  else return;

		   }
		   else {
			for (i=0; notnull(target[i]) ; i++) {
				if (target[i] == '.' && src[pos] == '*') {
					j = i;
					while (notnull(target[j]) && target[j] != switchc)
						j++;
					if (notnull(target[j]) == FALSE)
						break;
				}
			    dest[back++] = target[i];
			}
		   }
		   break;

		default:

		   if ( (macptr = ismac_c(src[pos])) != NULL )
			back = mv_install(macptr,dest,back);
		   else {
			/*not an approved macro, ignore*/
			dest[back++] = '$';
			dest[back++] = src[pos];
		   }
		   break;
	    }/* else */
	    pos++;
       }
   }
   dest[back] = NUL;
}


p_expand(src,dest,compl_preq,ood_preq,ood_obj)
char *src,*dest;
char *compl_preq,*ood_preq,*ood_obj;
{
    /*
       expand the special macros $< $? $> just before execution
    */
    int back,pos,i;
    
    if ( src == NULL ) {
	    dest[0] = NUL;
	    return;
    }

    back = pos = 0;
    while( notnull(src[pos]) ) {

    if (src[pos] != '$' ) dest[back++] = src[pos++];

    else {
	pos++;
	switch( src[pos] ) {

	case '<':     /* put in the complete list of prerequisites */
	    for ( i = 0; notnull(compl_preq[i]); i++ )
		dest[back++] = compl_preq[i];
	    break;

	case '?':    /* the prerequisites that are out of date */
	    for ( i = 0; notnull(ood_obj[i]); i++ )
		dest[back++] = ood_obj[i];
	    break;

	case '>':    /* the source files that are out of date (NOT STANDARD)*/
	    for ( i = 0; notnull(ood_preq[i]); i++ )
		dest[back++] = ood_preq[i];
	    break;

	case '$':     /* get rid of the doubled $$ */
	    dest[back++] = '$';
	    break;

	default:
	    dest[back++] = '$';
	    dest[back++] = src[pos];
	    break;
	}
	pos++;
    } /* else */

    } /* switch */
    dest[back] = NUL;
}


/* is this a character macro? */
char *ismac_c(cc)
char cc;
{
    char *ismac();
    static char str[2]; /* space for a one char string */

    str[0] = cc;
    return(ismac(str));
}

#endif

/* is this string a macro? */
char *ismac(test)
char *test;
{
struct macrec *ptr;

#ifndef UPGRADE
    ptr = maclist;
    if ( ptr == NULL ) return( NULL );
    while( TRUE ) {
       if      ( EQ(ptr->name,test) ) return( ptr->mexpand );
       else if ( ptr->nextmac == NULL )  return( NULL );
       else				 ptr = ptr->nextmac;
    }
#else
    for(ptr = maclist ; ptr ; ptr = ptr->nextmac)
	if(EQ(ptr->name, test))
		return(ptr->mexpand);
    return(NULL);
#endif
}

maccheck(sptr) /* if this string has a '=', then add it as a macro */
char *sptr;
{
int k;

    for ( k=0; notnull(sptr[k]) && (sptr[k] != '='); k++ );
    if ( isnull(sptr[k]) ) return( FALSE );
    else {
	/* found a macro */
	sptr[k] = NUL;
	add_macro(sptr,sptr + k + 1, TRUE);
	return( TRUE );
    }
}
	

#ifndef UPGRADE

x_scan(src,pos,dest)
char *src,*dest;
int pos;
{
char bterm,eterm;
int cnt;

    if ( src[pos] == '('  ) eterm = ')';
    else if ( src[pos] == '{'  ) eterm = '}';
    else panic("very bad things happening in x_scan");

    bterm = src[pos++];
    cnt = 1;

    while ( notnull(src[pos]) ) {
       if ( src[pos] == bterm ) cnt++;
       else if ( src[pos] == eterm ) {
	   cnt--;
	   if ( cnt == 0 ) {
	      *dest = NUL;
	      return( pos );
	   }
       }
       *dest++ = src[pos++];
    }
    panic2("No closing brace/paren for %s",src);
    /* NOTREACHED */
}
#endif
    
/* expand and move to dest */
mv_install(from,to,back)
char *from,*to;
int back;
{
int i;

#ifdef	UPGRADE
   to += back;
   while(*to++ = *from++)
	back++;
#else
    for ( i=0; notnull(from[i]) ; i++)
	to[back++] = from[i];
#endif
    return( back );
}


/*
  attempts to apply a rule.  If an applicable rule is found, returns a 
  pointer to a (new) struct which can be added to the defnlist
  An applicable rule is one in which target ext matches, and a source file
  exists.
*/

struct defnrec *tryrules(string)
char *string;
{
struct rulerec *rptr,*isrule();
struct llist   *sptr;
struct defnrec *retval;
char	       s[INMAXSH],buf[INMAXSH],sext[10];

	my_strcpy(s,string);
	get_ext(s,sext); /* s is now truncated */

	if ( sext[0] == NUL ) {
	    /*target has no extension*/
	    return(NULL);
	}

	/* look for this extension in the suffix list */
	for ( sptr = suff_head; sptr != NULL; sptr = sptr->next ){
	    if ( EQ(sptr->name,sext) ) break;
	}

	if ( sptr == NULL ) {
	    /* not a valid extension */
	    return( NULL );
	}

	/* continue looking, now for existence of a source file **/
	for ( sptr = sptr->next; sptr != NULL; sptr = sptr->next ) {
	    if ( exists(s,sptr->name) &&
		 ((rptr = isrule(rulelist,sptr->name,sext)) != NULL)
	       ) break;
	}


	if ( sptr == NULL ) {
	    /* no rule applies */
	    return( NULL );
	}

	retval = (struct defnrec *)get_mem((UI) sizeof(struct defnrec));
	my_strcpy(buf,s);     /* s --  is the stem of the object*/
	strcat(buf,rptr->targ);
	retval->name = mov_in(buf);
	my_strcpy(buf,s);
	strcat(buf,rptr->dep);
	retval->dependson = mkllist(buf);
	retval->uptodate = FALSE;
	retval->modified = getmodified(retval->name);
	retval->nextdefn = NULL;
	retval->howto = mkexphow(rptr->rule,retval->name,
				 (rptr->def_flag)? IGN_ERR : REPT_ERR);
	return(retval);		/*whew*/

}


/* does the file exist? */
exists(name,suffix)
char *name,*suffix;
{
char t[INMAXSH];

    my_strcpy(t,name);
    strcat(t,suffix);
    return (getmodified(t) != (TIME) 0 ? TRUE : FALSE );
}

struct rulerec *isrule(head,src,dest)
struct rulerec *head;
char *src,*dest;
{
    /* return ptr to rule if this is a legit rule */
    struct rulerec *ptr;

    if ( head == NULL ) return( NULL );
    else {
	for ( ptr = head; ptr != NULL; ptr = ptr->nextrule )
	    if ( EQ(ptr->dep,src) && EQ(ptr->targ,dest) )
		return( ptr );
	return( NULL );
    }
}

#ifdef DEBUG
/*debugging*/
/* print the dependencies and command lines... */
prtree()
{
struct defnrec *dummy;
struct macrec  *mdum;
struct llist   *dum2,*dum3,*rdum2;
struct rulerec *rdum;
int    cnt;

    dummy = defnlist;
    while (dummy != NULL){
	fprintf(stdout,"name '%s'  exists: %s\n",
		    dummy->name,(dummy->modified)?"no":"yes");
		    
	dum2 = dummy->dependson;
	fprintf(stdout,"   depends-on:");
	cnt =0;
	while (dum2 != NULL){
	    fprintf(stdout," %13s ",dum2->name);
	    cnt++;
	    if (cnt == 4){
		cnt = 0;
		fprintf(stdout,"\n		");
	    }
	    dum2 = dum2->next;
	}
	fprintf(stdout,"\n");

	dum3 = dummy->howto;
	while (dum3 != NULL){
	    fprintf(stdout,"	  command: %s\n",dum3->name);
	    dum3 = dum3->next;
	}
	dummy = dummy->nextdefn;
	fprintf(stdout,"\n");
    }

    fprintf(stdout,"\n	     *RULES*\n\n");
    fprintf(stdout,"src=     dest=     rule=\n");
    rdum = rulelist;
    while ( rdum != NULL ) {
	if ( rdum->rule == NULL )
	    fprintf(stdout,"%4s     %4s      %s\n",
		    rdum->dep,rdum->targ,"** Empty Rule **");
	else {
	    fprintf(stdout,"%4s     %4s      %s\n",
		    rdum->dep,rdum->targ,rdum->rule->name);
	    rdum2 = rdum->rule->next;
	    while ( rdum2 != NULL ) {
		fprintf(stdout,"		   %s\n",rdum2->name);
		rdum2 = rdum2->next;
	    }
	}
	rdum = rdum->nextrule;
    }

    mdum = maclist;
    if ( mdum == NULL ) 
	fprintf(stdout,"\n	  *NO MACROS*\n");
    else {
	fprintf(stdout,"\n	  *MACROS*\n\n");
	fprintf(stdout," macro		expansion\n");
	while ( mdum != NULL ) {
	   fprintf(stdout," %8s       %s\n",mdum->name,mdum->mexpand);
	   mdum = mdum->nextmac;
	}
    }
    fprintf(stdout,"\n\nsuffix list is");
    if ( suff_head == NULL ) fprintf(stdout," empty.");
    else for ( dum2 = suff_head; dum2 != NULL; dum2 = dum2->next){
	fprintf(stdout," %s",dum2->name);
    }
    fprintf(stdout,"\npath is ");
    if (path_head == NULL) fprintf(stdout," empty.");
    else 
		for ( dum2 = path_head; dum2 != NULL; dum2 = dum2->next) 
		 fprintf(stdout," %s:",dum2->name);

    fprintf(stdout,"\nswitch character	'%c'\n",switchc);
    fprintf(stdout,"line continuation '%c'\n",linecont);
} /*debug*/
#endif 


error(s1)
char *s1;
{
    fprintf(stderr,"%s: ",whoami);
    fprintf(stderr,s1);
    fprintf(stderr,"\n");
    if (stopOnErr) mystop_err();
    else return;
}

error2(s1,s2)
char *s1,*s2;
{
    fprintf(stderr,"%s: ",whoami);
    fprintf(stderr,s1,s2);
    fprintf(stderr,"\n");	
    if (stopOnErr) mystop_err();
    else return;
}

panic(s1)
char *s1;
{
    fprintf(stderr,"%s: ",whoami);
    fprintf(stderr,s1);
#ifdef	UPGRADE
    fprintf(stderr, ".	Stop.\n");
#else
    fprintf(stderr,"\n");	
#endif
    mystop_err();
    /* NOTREACHED */
}

panic2(s1,s2)
char *s1,*s2;
{
    fprintf(stderr,"%s: ",whoami);
    fprintf(stderr,s1,s2);
#ifdef	UPGRADE
    fprintf(stderr, ".	Stop.\n");
#else
    fprintf(stderr,"\n");	
#endif
    mystop_err();
    /* NOTREACHED */
}

panicstop()
{
#ifdef	UPGRADE
    panic("");
#else
    fprintf(stderr,"\n\n  ***Stop.\n");
    mystop_err();
#endif
    /* NOTREACHED */
}

mystop_err()
{
fprintf(stderr,"mystop_err\n");
    done( -1 );
    /* NOTREACHED */
}


#ifndef UPGRADE
in_dolist(s)  /* true if this is something we are to make */
char *s;
{
struct llist *ptr;

    for ( ptr = dolist; ptr != NULL ; ptr = ptr->next )
	if ( EQ(ptr->name,s) ) return( TRUE );

    return( FALSE );
}
#endif

char *add_prereq(head,nam,f)
char *head,*nam;
struct m_preq *f;
{
char *stradd();
struct llist *ptr;
    /*
       move the stem, src and dest extensions and the current time
       of modification to where make() can get at them. returns the
       updated list of prerequisites
    */

    /* get the prerequisite */

    /* if ext does not exist, return */
    my_strcpy(f->m_name,nam);
    get_ext(f->m_name,f->m_targ);
    if ( f->m_targ[0] == NUL ) return( head );

    /* if ext not on suffix list, return */
    for ( ptr = suff_head; ptr != NULL; ptr = ptr->next )
	if ( EQ(ptr->name,f->m_targ) ) break;
    if ( ptr == NULL ) return( head );

    /* if there does not exist a corresponding file, return */
    for ( ; ptr != NULL; ptr = ptr->next )
	if ( exists(f->m_name,ptr->name) ) break;
    if ( ptr == NULL ) return( head );

    /* add the source file to the string m_comp with a strcat */
    my_strcpy(f->m_dep,ptr->name);
    return( stradd(head,f->m_name,f->m_dep) );

}

char *stradd(f1,f2,f3)
char *f1,*f2,*f3;
{
char *ptr;
    /* return a new pointer to a string containing the three strings */
    ptr = get_mem((UI)(my_strlen(f1) + strlen(f2) + strlen(f3) + 2) );
    my_strcpy(ptr,f1);
    strcat(ptr," ");
    strcat(ptr,f2);
    strcat(ptr,f3);
    if ( f1 != NULL ) free( f1 );
    return( ptr );
}


get_ext(n,ex)
char *ex,*n;
{
int start,end,x;
    /* 
       put the extension of n in ex ( with the period )
       and truncate name ( at period )
    */
    start = my_strlen(n);
    end = ( start > 6 ) ? start - 6 : 0;

    for ( x = start; x > end; x-- ) if ( n[x] == '.' ) break;

    if ( x == end ) {
	ex[0] = NUL;
	return;
    }
    else {
	my_strcpy(ex,n + x);
	n[x] = NUL;
    }
}
/* read the makefile, and generate the linked lists */

#define DONE 1
#define ADEFN 2
#define ARULE 3
#define AMACRO 4
#define DIRECTIVE 5

/* local variables */
FILE *fil;
char *fword,*restline,line[INMAX],backup[INMAX];
char *frule;
struct llist *fhowto,*howp3;
struct llist *fdeps,*deprec3;
struct defnrec *defnp;
int sending,def_ready,gdone,rule_send,rule_ready;
struct llist  *targ,*q_how,*q_dep,*targ2;

readmakefile(s)
char *s;
{
int i,k;
char temp[50],tempdep[50];
  FILE *fopen();

    /* open the file */
    if ( EQ(s,"-") ) fil = stdin;
    else if ( (fil = fopen(s,"r")) == NULL) {
	error2("couldn't open %s",s);
	return;
    }

    /* initialise getnxt() */
    sending = def_ready = gdone = rule_send = rule_ready = FALSE;
    targ = q_how = q_dep = NULL;
    if (getline(fil, backup) == FALSE)
       panic("Empty Makefile");


    /* getnxt() parses the next line, and returns the parts */
    while (TRUE) switch( getnxt() ){

    case DONE: fclose(fil);return;

    case AMACRO:      /* add a macro to the list */
	 add_macro(fword,restline, TRUE);
	 break;

    case DIRECTIVE: /* perform the actions specified for a directive */
	 squeezesp(temp,fword);
	 if ( EQ(temp,"PATH") ) {
	     if ( my_strlen(restline)  == 0 )
		 {free_list(path_head);path_head = NULL;path_set = TRUE;}
	     else  {
		 /* this fooling around is because the switchchar may
		    not be set yet, and the PATH variable will use it
		 */
		 if ( !path_set ) {mkpathlist();path_set = TRUE;}
		 add_path(restline);
	     }
	 }
	 else if ( EQ(temp,"SUFFIXES") ) {
	     /* this is easier, suffix syntax characters don't change */
	     if ( my_strlen(restline) == 0 )
		 {free_list(suff_head);suff_head = NULL;}
	     else  add_suff(restline);
	 }
	 else if ( EQ(temp,"IGNORE") ) stopOnErr = FALSE;
	 else if ( EQ(temp,"SWITCH") )
	    switchc = (isnull(restline[0])) ? switchc : restline[0];
	 else if ( EQ(temp,"LINECONT") )
	    linecont = ( isnull( restline[0] ) ) ? linecont : restline[0];
	 else if ( EQ(temp,"SILENT") ) silentf = TRUE;
#ifdef	MACFILES
	 else if ( EQ(temp, "MACSEP") ) 
		    MAC_sep = restline[0];
	 else if ( EQ(temp, "MACCONT") )
		    MAC_cont[1] = restline[0];
#endif

     else {
	 error2("unknown directive \(rule?\) '%s'",temp);
     }
     break;


    case ARULE:      /* add a rule to the list */

	/*fword[0] is defined to be a period*/
	for (i=1; notnull(fword[i]) ;i++) if ( fword[i] == '.' ) break;

	if ( i == my_strlen(fword) ) {
	    panic2("Bad rule '%s'",fword);
	    /* NOTREACHED */
	}

	fword[i] = NUL; /* dep */
	my_strcpy(tempdep,fword);

	/* be sure object extension has no spaces */
	for ( k = i+1 ; notnull(fword[k]) && !isspace(fword[k]) ; k++ );

	if ( isspace(fword[k]) ) fword[k] = NUL;
	my_strcpy(temp,".");
	strcat(temp,fword + i + 1); /* targ */
	add_rule2(tempdep, temp, fhowto, FALSE);
	/*
	  update the suffix list if required. To get fancier than this,
	  the use has to do it himself. Start at the beginning, and
	  if not present, add to the end.
	*/
	add_s_suff(temp);    /* order is important -- add targ first **/
	add_s_suff(tempdep); /* then dep */
	break;

    case ADEFN:     /* add a definition */

	if (no_file) {	    /*if no target specified on command line... */
	    dolist = add_llist(dolist,fword);  /*push target on to-do list */
	    no_file = FALSE;
	}
	/* getnxt() returns target ( fword ) , pointer to expanded howto
	   list ( fhowto ) and pointer to expanded depends ( fdeps )
	*/
	if ( defnlist == NULL ) {
	    /* add the current definition to the end */
	    add_defn(fword,FALSE,getmodified(fword),fdeps,fhowto);
	}
	else {
	    defnp = defnlist;
	    while ( defnp != NULL ) {
		if ( EQ(defnp->name,fword) ) break;
		else defnp = defnp->nextdefn;
	    }
	    if ( defnp == NULL ) {
		/* target not currently in list */
		add_defn(fword,FALSE,getmodified(fword),fdeps,fhowto);
	    }
	    else {
		/* target is on list, add new depends and howtos */
		if (defnp->dependson == NULL)
		    defnp->dependson = fdeps;
		else {
		    deprec3 = defnp->dependson;
		    while (deprec3->next != NULL)
			 deprec3 = deprec3->next;
		    deprec3->next = fdeps;
		}
		/* add new howtos */
		if (defnp->howto == NULL)
		    defnp->howto = fhowto;
		else {
		    howp3 = defnp->howto;
		    while ( howp3->next != NULL)
			 howp3 = howp3->next;
		    howp3->next = fhowto;
		}
	    }
	}
	break;
    }
}

add_defn(n,u,m,d,h)	/* add a new definition */
char *n;   /* name */
int   u;   /* uptodate */
TIME  m;   /* time of modification */
struct llist *d,*h;  /* pointers to depends, howto */
{
struct defnrec *ptr,*ptr2;
    ptr = (struct defnrec *)get_mem(sizeof(struct defnrec));
    ptr->name = mov_in(n);
    ptr->uptodate = u;
    ptr->modified = m;
    ptr->dependson = d;
    ptr->howto = h;
    ptr->nextdefn = NULL;
    if ( defnlist == NULL ) {
	defnlist = ptr;
	return;
    }
    else {
	ptr2 = defnlist;
	while ( ptr2->nextdefn != NULL )
	    ptr2 = ptr2->nextdefn;
	ptr2->nextdefn = ptr;
    }
}

getnxt()
{
int pos,mark,parsed,x;
char exp_line[INMAX];
struct llist *q_how2,*q_how3;

  while ( TRUE ) {

   /* if we are currently sending targets */
   if ( sending ) {
	if ( targ2->next == NULL) {
	    sending = def_ready = FALSE;
	}
	fword = mov_in(targ2->name);
	fhowto = mkexphow(q_how,targ2->name,REPT_ERR);
	fdeps = mkexpdep(q_dep,targ2->name);
	targ2 = targ2->next;
	return ( ADEFN );
    }
    
    /* are we sending a rule? */
    if ( rule_send ) {
	fword = frule;
	fhowto = mkexphow(q_how,(char *)NULL,IGN_ERR); /* target == NULL -> don't expand */
	rule_send = rule_ready = FALSE;
	return( ARULE );
    }
    
    if ( gdone ) return ( DONE );
    /* if we are not currently sending... */

    /* load the next line into 'line' ( which may be backed-up ) */
    if ( backup[0] != NUL ) {
	my_strcpy(line,backup);
	backup[0] = NUL;
    }
    else {
	if ( getline(fil,line) == FALSE ) {
	    if ( def_ready ) sending = TRUE;
	    if ( rule_ready ) rule_send = TRUE;
	    gdone = TRUE;
	    continue;  /* break the loop, and flush any definitions */
	}
    }
    parsed = FALSE;

    /* check for rule or directive, and begin loading it if there */
    if (line[0] == '.'){
	for (pos = 0 ; line[pos] != ':' && notnull(line[pos]) ; pos++) ;

	if (isnull(line[pos]))
	   error2("bad rule or directive, needs colon separator:\n%s",line);
	mark = pos;
	for ( x = 1 ; x < mark ; x++ ) if ( line[x] == '.' ) break;
	if ( x == mark ) {
	    /* found DIRECTIVE -- .XXXXX: */
	    line[mark] = NUL;
	    fword = line + 1;
	    for ( x++ ; isspace(line[x]) ; x++ ) ;
	    restline = line + x ;
	    return( DIRECTIVE );
	}
	else {
	    /* found RULE -- .XX.XXX: */
	    parsed = TRUE;
	    if ( rule_ready || def_ready ) {
		if ( def_ready ) sending = TRUE;
		else rule_send = TRUE;
		/* push this line, and send what we already have */
		my_strcpy(backup,line);
	    }
	    else {
		/* begin a new rule */
		rule_ready = TRUE;
		line[mark] = NUL;
		frule = mov_in(line);
		free_list(q_how);
		/*
		  one last decision to make. If next non-space char is
		  ';', then the first howto of the rule follows, unless there
		  is a '#', in which case we ignore the comment
		*/
		for ( pos++ ; line[pos] != ';' && notnull(line[pos]) ; pos++)
		    if ( !isspace(line[pos]) ) break;
		if ( notnull(line[pos]) ) {
		    if ( line[pos] == '#' ) ; /* do nothing, fall thru */
		    else if ( line[pos] != ';' )
			/* found non-<ws>, non-';' after rule declaration */
			error("rule needs ';<rule>' or <newline> after ':'");
		    else {
			/* found <rule>:; <rule_body> */
			q_how = MkListMem();
			q_how->name = mov_in(line + pos + 1 );
			q_how->next = NULL;
		    }
		}
	    }
	}
    }
    
    /* check for macro, and return it if there */
    if ( !parsed ) {
	pos = 0;
	while ( line[pos] != '=' &&
		line[pos] != ':' &&
		!isspace(line[pos]) &&
		notnull(line[pos])) pos++;
	mark = pos;
	if (notnull(line[pos]) && line[pos] != ':'){
	    /* found a macro */
	    if (isspace(line[pos]))
	       while (isspace(line[pos]) && notnull(line[pos])) pos++;
	    if (isnull(line[pos]))
	       panic2("bad macro or definition '%s'",line);
	    if ( line[pos] == '=' ) {
		/* found a macro */
		line[mark] = NUL;
		fword = line;
		mark = pos + 1;
		while ( isspace(line[mark]) ) mark++; /* skip spaces before macro starts */
		restline = line + mark;
		return ( AMACRO );
	    }
	}
    }



    /* check for and add howto line */
    if ( isspace(line[0]) ) {
	if (!def_ready && !rule_ready)
	 error2("how-to line without preceeding definition or rule\n%s",line);
	q_how2 = MkListMem();
	if ( q_how == NULL ) {
	    q_how = q_how2;
	}
	else {
	   for (q_how3 = q_how; q_how3->next != NULL; q_how3 = q_how3->next); /* go to end of list */
	   q_how3->next = q_how2;
	}
	q_how2->name = mov_in(line);
	q_how2->next = NULL;
	parsed = TRUE;
    }

    
    /* check for definition */
    if (!parsed) {
	pos = 0;
	while ( notnull(line[pos]) && line[pos] != ':') pos++;
	if (line[pos] == ':') {
	    /* found a definition */
	    parsed = TRUE;
	    if (def_ready || rule_ready) {
		if ( def_ready ) sending = TRUE;
		else rule_send = TRUE;
		my_strcpy(backup,line);
	    }
	    else {
	       /* free up the space used by the previous lists */
	       free_list(targ);targ = NULL;
	       free_list(q_how);q_how = NULL;
	       free_list(q_dep);q_dep = NULL;
	       line[pos] = NUL;
	       expand(line,exp_line,"",NO_TARG);
	       targ2 = targ = mkllist(exp_line);
	       q_dep = mkllist(line + pos + 1);
	       def_ready = TRUE;
	    }
	}
    }
    if (!parsed)
       panic2("unable to parse line '%s'",line);
  }
}

    
/*
   load the next line from 'stream' to 'where' allowing for comment char
   and line continuations 
*/
getline(stream,where)
char *where;
FILE *stream;
{
int i;

    if (get_stripped_line(where,INMAX,stream) == TRUE) {
	i = my_strlen(where);
	where[--i] = NUL;
	while ( where[i-1] == linecont ) {
	    if ( get_stripped_line(where + i -1,INMAX,stream) == FALSE )
		panic("end of file before end of line");
	    i = my_strlen(where);
	    where[--i] = NUL;
	}
	if ( i >= INMAX ) {
	    where[INMAX] = NUL;
	    panic2("line too long\n'%s'",where);
	}
	return ( TRUE );
    }
    else  return ( FALSE );
}

get_stripped_line(where,len,stream)
char *where;
int len;
FILE *stream;
{
int x;
    /* return a meaningful line */
    while ( TRUE ) {
       if ( fgets(where,len,stream) == NULL ) return( FALSE );
       
       if ( where[my_strlen(where)-1] != '\n' ) {
	       x = my_strlen(where);
	       where[x] = '\n';
	       where[x+1] = NUL;
       }

       /* terminate standard input with a period alone */
       if ( (stream == stdin) && EQ(where,".\n") ) return( FALSE );
    
       /* if the line is only <ws> or <ws>#, skip it */
       for ( x = 0; isspace(where[x]) && (where[x] != '\n') ; x++) ;
       if ( (where[x] == '\n') || (where[x] == '#') ) continue;

       /* no reason to throw it out... */
       return( TRUE );
    }
    /* NOTREACHED */
}

struct llist *mkllist( s )  /* make a  linked list */
char *s;
{
int pos;
char lname[INMAX];
struct llist *ptr,*ptr2,*retval;

     pos = 0;
     retval = NULL;
     while ( TRUE ) {
	 /* get the next element, may have quotes */
	 pos = get_element(s,pos,lname);
	 if ( isnull(lname[0]) ) return( retval );

	 /* found something to add */
	 ptr = MkListMem();
	 if ( retval == NULL ) retval = ptr;
	 else {
	     for (ptr2 = retval; ptr2->next != NULL ; ptr2 = ptr2->next ) ;
	     ptr2->next = ptr;
	 }
	 ptr->name = mov_in(lname);
	 ptr->next = NULL;
     }
}

get_element(src,p,dest)
char *src,*dest;
int p;
{
int i,quotestop;

    i = 0;
    dest[0] = NUL;
    while ( notnull(src[p]) && isspace(src[p]) ) p++;
    if ( isnull(src[p]) ) return( p );

    if ( src[p] == '"' ) {
	quotestop = TRUE;
	p++;
    }
    else quotestop = FALSE;

    while ( TRUE ) {
	if ( isnull(src[p]) ) break;
	else if (src[p] == BKSLSH) {
	    if (src[p+1] == '"') p++;
	    dest[i++] = src[p++];
	}
	else if ( !quotestop && isspace(src[p]) ) break;
	else if (  quotestop && (src[p] == '"') ) break;
	else dest[i++] = src[p++];
    }
    dest[i] = NUL;
    return( p );
}


struct llist *mkexphow(head,target,eflag) /* make an expanded linked list for how */
struct llist *head;
char *target;
int eflag;
{
struct llist *p,*p2,*retval;
int x;
char temp[INMAX];

   if ( head == NULL ) {
       return ( NULL );
   }

   retval = NULL;
   while ( head != NULL ) {
      
      if ( target != NULL ) expand(head->name,temp,target,eflag);
      else my_strcpy(temp,head->name);
      p = MkListMem();
      for ( x = 0 ; notnull(temp[x]) ; x++ ) if ( !isspace(temp[x]) ) break;
      p->name = mov_in(temp + x);
      p->next = NULL;

      if ( retval == NULL ) retval = p;
      else {
	  p2 = retval;
	  while ( p2->next != NULL )
	      p2 = p2->next;
	  p2->next = p;
      }
      head = head->next;
   }
   return( retval );
}

struct llist *mkexpdep(head,target)  /* make an expanded linked list for dep*/
struct llist *head;
char *target;
{
struct llist *p,*p2,*p3,*retval;
char temp[INMAX];

 if ( head == NULL ) {
      return ( NULL );
 }

 retval = NULL;
 while ( head != NULL ) {
      
   expand(head->name,temp,target, REPT_ERR);
   p3 = mkllist(temp);
   while ( p3 != NULL ) {
      p = MkListMem();	    
      p->name = mov_in(p3->name);
      p->next = NULL;

      if ( retval == NULL ) retval = p;
      else {
	  p2 = retval;
	  while ( p2->next != NULL ) p2 = p2->next;
	  p2->next = p;
      }
      p3 = p3->next;
   }
   free_list(p3);
   head = head->next;
 }
 return( retval );
}


add_suff(lin)
char *lin;
{
struct llist *ptr;

    /* add *lin to the list suff_head */

    if ( lin == NULL ) return;

    if ( suff_head == NULL ) suff_head = mkllist(lin);
    else {
	/* go to the tail of suff_head */
	for ( ptr = suff_head; ptr->next != NULL; ptr = ptr->next );
	ptr->next = mkllist(lin);
    }

    /* do a little error checking */
    for ( ptr = suff_head; ptr != NULL; ptr = ptr->next )
       if ( ptr->name[0] != '.' )
	   error2("add_suffix: bad syntax '%s'",ptr->name);
}

add_s_suff(lext)
char *lext;
{
struct llist *sptr;

    /* add this extension to suff_list, if its not already there */

    for ( sptr = suff_head; sptr != NULL; sptr = sptr->next )
	if ( EQ(sptr->name,lext) ) return;
    /* must not be there... */
    suff_head = add_llist(suff_head,lext);
}


add_macro(mname,expan, env_add)
char *mname,*expan;
{
struct macrec *macp,*macp2;
    
    if(env_add)
	add_toenv(mname, expan);
    if (maclist == NULL)
	maclist = macp = (struct macrec *)get_mem((UI) sizeof(struct macrec));
    else {
	macp2 = maclist;
	while (macp2->nextmac != NULL) {
	    if ( EQ(macp2->name,mname) ) {
		macp2->mexpand = mov_in(expan);
		/* previous contents not freed cause mostly they were not
		   malloc()-ed
		*/
		return;
	    }
	    macp2 = macp2->nextmac;
	}
	if ( EQ(macp2->name,mname) ) {
	    /* if the last on the list matches,
	       replace it
	    */
	    macp2->mexpand = mov_in(expan);
	    return;
	}
	macp2->nextmac = macp =
			 (struct macrec *)get_mem((UI) sizeof(struct macrec));
    }
    macp->name = mov_in(mname);
    macp->mexpand = mov_in(expan);
    macp->nextmac = NULL;
}

add_rule2(adep,atarg,arule,aflag)
char *adep,*atarg;
struct llist *arule;
int aflag;
{
struct rulerec *rulep,*rulep2;
    
    if (rulelist == NULL)
	rulelist = rulep = 
	    (struct rulerec *)get_mem((UI) sizeof(struct rulerec));
    else {
	rulep2 = rulelist;
	while (rulep2->nextrule != NULL) {
	    if ( EQ(rulep2->dep,adep) && EQ(rulep2->targ,atarg) ) {
		free_list(rulep2->rule);
		rulep2->rule = arule;
		return;
	    }
	    rulep2 = rulep2->nextrule;
	}
	if ( EQ(rulep2->dep,adep) && EQ(rulep2->targ,atarg) ) {
	    free_list(rulep2->rule);
	    rulep2->rule = arule;
	    return;
	}
	rulep2->nextrule = rulep =
		 (struct rulerec *)get_mem((UI) sizeof(struct rulerec));
    }
    rulep->dep = mov_in(adep);
    rulep->targ = mov_in(atarg);
    rulep->rule = arule;
    rulep->def_flag = aflag;
    rulep->nextrule = NULL;
}

free_list(head)   /* kill a linked list */
struct llist *head;
{
struct llist *ptr;

    if ( head == NULL ) return;
    else if ( head->next == NULL ) {
	free( head->name );
	free( (char *)head );
	return;
    }
    else {
	while ( TRUE ) {
	    for ( ptr = head; ptr->next->next != NULL; ptr = ptr->next ) ;
	    free(ptr->next->name);
	    free((char *)ptr->next);
	    ptr->next = NULL;
	    if ( ptr == head ) {
		free( ptr->name );
		free( (char *)ptr);
		return;
	    }
	}
    }
}

exec_how(cmd)
char *cmd;
{
int pos,this_echo,this_ign,x,i,no_more_flags;
int err_ret;
char cmdname[INMAXSH];

     i = pos = 0;
     this_echo = !silentf;
     this_ign = FALSE;
     no_more_flags = FALSE;
     while ( TRUE ) {
	 while ( isspace( cmd[pos] ) ) pos++;
	 switch ( cmd[pos] ) {
	     case '@':this_echo = FALSE;break;

	     case '-':this_ign = TRUE;break;
	 
	     default: no_more_flags = TRUE;break;
	 }
	 if (no_more_flags) break;
	 else pos++;
     }

     /* get the name of the command */
     for (x=pos; !isspace(cmd[x]) && notnull(cmd[x]); x++) 
	 cmdname[i++] = cmd[x];  
     cmdname[i] = NUL;	

     /* echo if appropriate */
     if ( this_echo )  {
	fprintf(stdout,"	%s\n",cmd+pos);
     }

     else if ( !execute  && !this_echo) {
	 fprintf(stdout,"	 %s\n",cmd+pos);
	 return(0);
     }

     /* if we are not to actually do it... */
     if ( !execute ) return(0);
 
#ifdef	MACFILES
    if (EQ(cmdname, "macro-file")){
	struct	llist *ptr;
	ptr = mkllist(cmd + x);
	if(ptr != NULL){
		my_strcpy(MAC_file, ptr->name);
		free_list(ptr);
	}
	return(0);
    }
    i = 0;
    if(cmdname[0] == '+')
	i++;
    if (EQ(cmdname + i, "write-macro") || EQ(cmdname + i, "add-macro") ){
	err_ret = wr_macro((cmdname[i] == 'w'), i, cmd + x);
	return( (this_ign) ? 0 : err_ret);
    }
#endif
#ifdef	MSDOS
#ifdef	UPGRADE
    if (EQ(cmdname, "cd")){
	struct	llist *ptr;
	ptr = mkllist(cmd + x);
	if(ptr != NULL){
		err_ret = (chdir(ptr->name) < 0);
		if(err_ret)
			fprintf(stderr, "%s: Cannot chdir to '%s'.\n",
							whoami, ptr->name);
		free_list(ptr);
		return(  (this_ign) ? 0 : err_ret );
	}
	return(0);
     }
#endif
#endif
     err_ret = perform( cmdname, cmd+pos);
     return(  (this_ign) ? 0 : err_ret );
}

perform(cname, syscmd)
char *cname;	 /* the name of the command */
char *syscmd;	/* the string to send to 'system' */
{
int x,ccode;
int pid;
WAIT status;
struct llist *largs;
char  **vargs,**mkargs();
char wholenam[INMAXSH];


    /* if there are metacharacters, use 'system' */
    for ( x= 0; notnull(syscmd[x]); x++ ) 
	if ( (syscmd[x] == '>') ||
	     (syscmd[x] == '<') ||
	     (syscmd[x] == '|') ||
	     (syscmd[x] == '*') ||
#ifdef	MSDOS
	     (syscmd[x] == '?')
#else
	     (syscmd[x] == '?') ||
	     (syscmd[x] == '&')
#endif
	   ) {
		return( mysystem(syscmd) );
    }

    /* is this a builtin command? */
    if ( findexec(cname,wholenam) == (TIME) 0 ) {
	/* file doesn't exist -- yes */
	return(mysystem(syscmd));
    }

    /* directly exec a file with args */
    largs = mkllist(syscmd);
    vargs = mkargs(largs);
    
#ifdef	MSDOS

    status = spawnve(P_WAIT, wholenam, vargs, my_env);
    free( (char *)vargs );
    free_list( largs );
    return( pr_warning(&status) );

#else
    if ( (pid = fork()) == 0 ) {
	execve(wholenam,vargs, my_env);
	fprintf(stderr,"wholenam\n");
	done( -1 );
    }
    free( (char *)vargs );
    free_list( largs );

    if ( pid < 0 ) {
	perror(whoami);
	return( -1 );
    }
    else {
	while( ((ccode = wait(&status)) != pid) && (ccode != -1) );
	if ( pid < 0 ) {
	    perror(whoami);
	    return( -1 );
	}
	return( pr_warning(&status) );
    }
#endif
}

#ifdef	MSDOS
mysystem(cmd)	/* use the SHELL to execute a command line, print warnings */
char *cmd;
{
    WAIT status;
    extern	char	**environ;

/*    environ = my_env; */
    status = system(cmd);
    return( pr_warning(&status) );
}
#else
mysystem(cmd)	/* use the SHELL to execute a command line, print warnings */
char *cmd;
{
    int ccode,pid;
    WAIT status;

    if ( (pid = fork()) == 0 ) {
	execl(SHELL,"sh","-c",cmd,0);
	fprintf(stderr,"sh\n");
	done( -1 );
    }
    if ( pid < 0 ) {  
	perror(whoami);     /* say why we couldn't fork */
	return( -1 );
    }
    else {
	while ( ((ccode = wait(&status)) != pid) && (ccode != -1));
	if ( pid < 0 ) {     /* no children? */
	    perror(whoami);
	    return( -1 );
	}
	return( pr_warning(&status) );
    }
}
#endif

pr_warning(s)	 /* print a warning, if any */
WAIT *s;
{

#ifdef BSD4_2
#ifdef	UPGRADE
	char	*msg;
	int	code;
	if(s->w_T.w_Termsig == 0 && s->w_T.w_Retcode == 0)
		return( 0 );
	if ( s->w_T.w_Termsig ){
		msg = "Termination";
		code = s->w_T.w_Termsig;
	}
	else {
		msg = "Error";
		code = s->w_T.w_Retcode;
	}
	fprintf(stderr, "*** %s code %d%s\n", msg, code,
					stopOnErr ? "" : " (ignored)");
	return( -1 );
}
#else
    if ( (s->w_T.w_Termsig == 0) && (s->w_T.w_Retcode == 0) ) return( 0 );
    else {

	   if ( s->w_T.w_Termsig )
	       fprintf(stderr,"%s: received signal %x\n",whoami,s->w_T.w_Termsig);
	   else {
		   fprintf(stderr,"%s: Error code %x",whoami,s->w_T.w_Retcode);
		   fprintf(stderr,"%s\n",( stopOnErr ) ? "" : " (ignored)");
	   }
	   return( -1 );
    }
}
#endif
#else
#ifdef	MSDOS
    char *msg;
    if ( *s == 0 ) return( 0 );
    else {
	if( *s & 0xFF ){
		msg = "Error";
		*s &= 0xFF;
	}
	else {
		msg = "Termination";
		*s = (*s >> 8) & 0xFF;
	}
	fprintf(stderr, "*** %s code %d%s\n", msg, *s,
					stopOnErr ? "" : " (ignored)");
	return( -1 );
    }
#else
    if ( *s == 0 ) return( 0 );
    else {


	fprintf(stderr,"%s:",whoami);
	if ( *s & 0xFF) {
	    fprintf(stderr," received signal %x\n",*s & 0xFF);
	}
	else {
	    fprintf(stderr," Error code %x",(*s & ~0xFF) >> 8);
		fprintf(stderr,"%s\n",( stopOnErr ) ? "" : " (ignored)");
	}
	return( -1 );
    }
#endif
}
#endif


char **mkargs(arglist)
struct llist *arglist;
{
    struct llist *ptr;
    char **retval;
    int i;

    /* count up the number of args */
    for ( i=0,ptr = arglist; ptr != NULL; ptr = ptr->next,i++);

    retval = (char **)get_mem(((UI)(i+1)*sizeof(char *)));
    for ( i=0,ptr=arglist; ptr != NULL; ptr = ptr->next,i++)
	retval[i] = ptr->name;
    retval[i] = NULL;
    return( retval );
}

char *get_mem(size)
UI size;
{
char *p,*malloc();

    if ((p = malloc(size)) == 0)
	panic("Ran out of memory");
    return(p);
}

struct llist *MkListMem()
{
struct llist *p;
char *malloc();

    if ((p = (struct llist *)malloc(sizeof(struct llist))) == 0 )
	panic("Ran out of memory");
    return(p);
}

char *mov_in(string) /* return pointer to fresh memory with copy of string*/
char *string;
{
char   *ptr;
    
       ptr = get_mem((UI)(my_strlen(string) + 1));
       my_strcpy(ptr,string);
       return(ptr);
}

mkpathlist()
{
char *getenv();

    /* go get the PATH variable, and turn it into a linked list */
    char *path;

    path_head = NULL;

    path = getenv("PATH");
    add_path(path);

}

squeezesp(to,from)
char *to,*from;
{
    /* copy from from to to, squeezing out any spaces */
    if ( from == NULL ) return;
    while( *from ) {
       if ( isspace(*from) ) from++;
       else *to++ = *from++;
    }
    *to = NUL;
}

#ifdef	MSDOS
TIME
msgetm(s)
char	*s;
{
    struct stat statb;
    char *strp;

/*
    if ( stat(s,&statb) == 0 )
	return( statb.st_mtime );
*/
    strp = s + strlen(s);
    if(*(strp - 1) == '.' || *(strp - 4) == '.')
	return( (TIME)0);
    strcpy(strp, ".exe");
    if ( stat(s,&statb) == 0 )
	return( statb.st_mtime );
    strcpy(strp, ".com");
    if ( stat(s,&statb) == 0 )
	return( statb.st_mtime );
    return( (TIME)0);
}

TIME findexec(s,where)
char *s,*where;
{
    int i;
    TIME retval;
    struct llist *ptr;


    my_strcpy(where,s);

    /* search for switch character, if present, then this is full name
       and we won't search the path
    */
    for ( i = 0; notnull(s[i]); i++)
	if ( s[i] == switchc)
		return(msgetm(where));

    if ( (retval=msgetm(where)) != (TIME) 0 ) return( retval );

    /* if there is no prefix to this file name */
    for ( ptr = path_head; ptr != NULL; ptr = ptr->next) {
	my_strcpy( where, ptr->name);
	strcat( where, s);
	if ( (retval=msgetm(where)) != (TIME) 0 ) return( retval );
    }

    return( (TIME)0 );	/* didn't find it */
}
#else
TIME findexec(s,where)
char *s,*where;
{
int i;
TIME retval;
struct llist *ptr;

    my_strcpy(where,s);

    /* search for switch character, if present, then this is full name
       and we won't search the path
    */
    for ( i = 0; notnull(s[i]); i++)
	if ( s[i] == switchc ) return(getmodified(where));

    if ( (retval=getmodified(where)) != (TIME) 0 ) return( retval );

    /* if there is no prefix to this file name */
    for ( ptr = path_head; ptr != NULL; ptr = ptr->next) {
	my_strcpy( where, ptr->name);
	strcat( where, s);
	if ( (retval=getmodified(where)) != (TIME) 0 ) return( retval );
    }

    return( (TIME)0 );	/* didn't find it */
}
#endif

TIME getmodified(s)
char *s;
{
struct stat statb;

    if ( stat(s,&statb) != 0 ) {
	if ( errno == ENOENT ) return( (TIME) 0 );
	else {
	    perror(whoami);
	    if (stopOnErr) panicstop();
	    else return( (TIME) 0 );
	}
    }
    else {
	return( statb.st_mtime );
    }
    /* NOTREACHED */
}

add_path(p)
char *p;
{
char temp[50];
int i,k;
       
    /* add to the linked list path_head */
    k = i = 0;
    squeezesp(p,p);
    if ( p == NULL ) return;
    while ( TRUE ) {

	while ( notnull(p[k]) && (p[k] != PATHCHAR ) )
	    temp[i++] = p[k++];
	if ( temp[i-1] != switchc ) temp[i++] = switchc;
	temp[i] = NUL;
	if ( i == 0 ) return;

	path_head = add_llist(path_head,temp);
	if ( isnull(p[k]) ) {
	    return;
	}
	k++;
	i = 0;
    }
}

/* emulation of getenv() */
char *getenv(s)
char *s;
{
char **p,*tp,*ematch();

    p = ext_env;

    while ( *p != NULL ) {
	if ( (tp = ematch(s,*p)) != NULL ) return(mov_in(tp));
	p++;
    }
    return( NULL );
}

char *ematch(s,p)
char *s,*p;
{
    /* if match up to the '=', return second half */
    while( *s != NULL )
	if ( *s++ != *p++ ) return( NULL );
    if ( *p++ != '=' ) return( NULL );
    return( p );
}

/*
 * routines to pass variables to and from the environment
 */
build_env()
{
	char	*p, **tp, **xtp;

	for(n_myenv = 0 , tp = ext_env ; *tp ; tp++, n_myenv++);
	max_myenv = n_myenv + 10;
	my_env = (char **)get_mem( (UI)(max_myenv * sizeof(char *)));
	for(xtp = my_env , tp = ext_env ; *xtp++ = *tp++ ;);
	for(tp = my_env ; p = *tp ; tp++){
		while(*p && *p != '=')
			p++;
		if(*p){
			*p = 0;
			add_macro(*tp, p+1, FALSE);
			*p = '=';
		}
	}
}
	
add_toenv(macname, macval)
char	*macname, *macval;
{
	char	**tp, **xtp, *p, *q;
	char	*to_env();

	for(tp = my_env ; p = *tp ; tp++){
		for(q = macname ; *q ; q++)
			if(*q != *p++)
				break;
		if(*q || *p != '=')
			continue;
		/* found a variable in the environment */
		/* now check to see if we have set it */
		for(xtp = ext_env ; *xtp ; xtp++)
			if(*xtp == *tp)
				break;
		if(!*xtp)
			/* can free. */
			free(*tp);
		*tp = to_env(macname, macval);
		return;
	}
	if(max_myenv < n_myenv + 2){
		max_myenv += 10;
		tp = my_env;
		my_env = (char **)get_mem( (UI) (max_myenv * sizeof(char *)));
		for(xtp = my_env ; *xtp++ = *tp++ ;);
	}
	tp = my_env + n_myenv++;
	*tp++ = to_env(macname, macval);
	*tp = 0;
}
	
char	*
to_env(macname, macval)
char	*macname, *macval;
{
	char	*tmem;

	tmem = get_mem( (UI)(strlen(macname) + strlen(macval) + 2));
	strcpy(tmem, macname);
	strcat(tmem, "=");
	strcat(tmem, macval);
	return(tmem);
}

warn2(s1,s2)
char *s1,*s2;
{
    fprintf(stderr,"%s: ",whoami);
    fprintf(stderr,s1,s2);
    fprintf(stderr,"\n");
}

#ifdef	MACFILES

#define MAC_LLEN 72

wr_macro(add_sep, append, list)
char	*list;
{
	FILE	*fp;
	struct	llist	*ptr, *ptr2;
	char	w_buf[100];
	int	strl;
	int	ret_val;

	w_buf[0] = 0;
	fp = fopen(MAC_file, append ? "a" : "w");
	if(fp == NULL){
		fprintf(stderr, "Cannot %s macro file '%s'\n", 
			append ? "write" : "create", MAC_file);
		return(1);
	}
	ptr = mkllist(list);
	for(ptr2 = ptr ; ptr2 != NULL ; ptr2 = ptr2->next){
		if(w_buf[0] == NUL){
			strcpy(w_buf, ptr2->name);
			strl = strlen(w_buf);
		}
		else if(strl + strlen(ptr2->name) > MAC_LLEN){
			fprintf(fp, "%s%s\n", w_buf, MAC_cont);
			strcpy(w_buf, ptr2->name);
			strl = strlen(w_buf);
		}
		else {
			strcat(w_buf, ptr2->name);
			strl += strlen(ptr2->name);
		}
		if(ptr2->next){
			if(add_sep && MAC_sep &&
					!EQ(ptr2->next->name, MAC_cont+1))
				w_buf[strl++] = MAC_sep;
			w_buf[strl++] = ' ';
			w_buf[strl] = NUL;
		}
	}
	free_list(ptr);
	if(w_buf[0] != NUL)
		fprintf(fp,"%s\n",w_buf);
	fflush(fp);
	ret_val = ferror(fp);
	fclose(fp);
	if(ret_val)
		fprintf(stderr, "Write error on macro file.\n");
	return(ret_val);
}
#endif

done(n)
int n;
{
  /* _cleanup(); */
  exit(n);
}

my_strlen (src)
char *src;
{
	if (src == NULL)
		return 0;
	return strlen (src);
}

my_strcpy (dest, src)
char *dest, *src;
{
	if (src == NULL)
		*dest = '\0';
	else
		return strcpy (dest, src);
}

#ifdef	UPGRADE

char	*index(str, c)
char	*str, c;
{
	while(*str && *str != c)
		str++;
	return(*str ? str : 0);
}

char	*rindex(str, c)
char	*str, c;
{
	char	*tmp = NULL;
	
	while(*str){
		if(*str == c)
			tmp = str;
		str++;
	}
	return(tmp);
}
#endif
