/*=-=-=-=-=-=-=-=-=-=-  RCCL/RCI Notice of Copyright  -=-=-=-=-=-=-=-=-=-=-=*
 *									    *
 *	Copyright (C) 1987 by all authors described below. Permission to    *
 *	use, distribute and copy is granted, but only for non-commercial    *
 *	purposes and providing the authors are acknowledged and this 	    *
 *	copyright notice is displayed prominently.			    *
 *									    *
 *	The authors provide this software in hope that it will be useful,   *
 *	but do not assume responsibility for its use.			    *
 *									    *
 *=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

/*---------McGill-Computer-Vision-and-Robotics-Laboratory--C-Module---------
 *		
 * NAME:	rcc
 *
 * USAGE:	rcc [user-list] CTRL [control-list]
 *
 *		  where 'user-list' is a list of cc options and user
 *		  level files, and 'control-list' is a list of cc options
 *		  and control level files.
 *
 * DESCRIPTION: The rcc command provides a convenient compilation interface
 *		to the RCI system and libraries. The command is meant to
 *		behave in a manner similar to the C complier "cc".
 *		
 *		The main difference is in the separating of the argument list
 *		into two parts by the reserved argument CTRL. Files specified
 *		to the left of CTRL are assumed to contribute to the user
 *		level part of the program, while files specified to the right
 *		are assumed to form the control part of the program.
 *
 *		If CTRL is not specified, it implicitly appears at the end of
 *		the argument list.
 *
 *		The files used for control (i.e, the ones on the right) are 
 *		compiled and linked with the rest of the program in a special
 *		way so as to ensure that their text and data can in fact be 
 *		locked in memory if necessary.  Compilation of control files
 *		is also automatically accompanied by a -DCTRL.
 *
 *		By default, rcc places its output into a file named after the
 *		first file argument encountered.  No effort is made to remove
 *		.o files left by the compile.
 *
 *		There is another keyword which can be specified: LIBFILE.
 *		Rcc needs certain object and library files.  It finds these
 *		by reading configuration files.  Each file contains lines of
 *		the form
 *			keyword=value
 *		where keyword is one of "begin", "end", "ctrl", "util",
 *		"user", or "include".  The value is the file or files to be
 *		associated with that keyword.  The configuration files are
 *		called .rcc by default and are searched for in $RCCL, $HOME,
 *		and ., with lines in "more local" files overriding those in
 *		earlier files.  The LIBFILE option allows the user to change
 *		the name .rcc to something else, as in
 *			% rcc LIBFILE .rcc-local .....
 *		This also supersedes the old -DLOCAL compile-time option to
 *		rcc, for specifying an alternate config file can be used to
 *		get the same effect.
 *
 *		Config file lines which begin with a # or are blank are
 *		considered to be comments.
 * 
 * FILES:
 * 
 * NOTES:
 *
 * AUTHOR/DATE: John Lloyd and der Mouse (Mike Parker), March, 1985
 *
 * UPDATES:	Wed May 20 03:23:42 1987		der Mouse
 *			fixed to use $RCCL instead of /u1/robot/rccl
 *
 *		Sun Nov 29 02:25:00 1987		der Mouse
 *			fixed to look for configuration file(s) to find
 *			libraries.  Added the LIBFILE option.  Got rid of
 *			-DLOCAL compile-time option.  Got rid of REAL and
 *			SIM; added CTRL.
 *
 *		Wed Jun 15 10:25:42 1988		JL
 *			added shell name expansion to file name specs.
 *			This was done by adding the routine expandFileName.
 *
 *-------------McGill-Computer-Vision-and-Robotics-Laboratory--------------*/

#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <sys/wait.h>
#include <sys/param.h> /* for MAXPATHLEN */

char **argvec;

char *index();
char *rindex();
char *malloc();
char *copyofstr();
char *strcpy();
char *getenv();
char *expandFileName();

#define MAXARGS 256

#define YES	1
#define NO	0

char *RCCL_env;
char *HOME_env;
struct files {
  char *keyword;
  char *format;
  int extra;
  int nfiles;
  char **files;
  char **names;
  } files[] = { { "begin", "%s", 0 },
#define BEGIN (&files[0])
		{ "end", "%s", 0 },
#define END (&files[1])
		{ "ctrl", "%s", 0 },
#define CTRL (&files[2])
		{ "util", "%s", 0 },
#define UTIL (&files[3])
		{ "user", "%s", 0 },
#define USER (&files[4])
		{ "include", "-I%s", 2 },
#define INCLUDE (&files[5])
		{ "cc", "%s", 0 },
#define CC_CMD (&files[6])
		};
char *configfile = ".rcc";
#ifdef _AIX
char *default_cc = "bsdcc";
#else
char *default_cc = "cc";
#endif
char *C_command;

char output[80];

char *libs[MAXARGS];
int n_libs;
char lib_is_ctrl[MAXARGS];
char *options[MAXARGS];
char *op_values[MAXARGS];
int n_options;
enum {
  first,
  second } part;
char *before[MAXARGS];
int n_before;
char *after[MAXARGS];
int n_after;
char *ofile;
int cflag;
int debuginfo;

char *readline (buf, size, fp)
char *buf;
int size;
FILE *fp;
{
	return (fgets (buf, size, fp));
}

readcf(cfn)
char *cfn;
{
 FILE *cf;
 char *cp;
 char *key;
 char *kend;
 char line[0x10000];
 int i;

 cf = fopen(cfn,"r");
 if (cf == 0)
  { return;
  }
 while (readline(line,sizeof(line),cf) == line)
  { cp = &line[0];
    for (;isascii(*cp)&&isspace(*cp);cp++) ;
    key = cp;
    if (!*key || (*key == '#'))
     { continue;
     }
    for (;isascii(*cp)&&isalnum(*cp);cp++) ;
    kend = cp;
    for (;isascii(*cp)&&isspace(*cp);cp++) ;
    if (*cp != '=')
     { fprintf(stderr,"%s: config file %s: missing or misplaced =\n",
			argvec[0],cfn);
       continue;
     }
    cp ++;
    *kend = '\0';
    for (i=0;i<(sizeof(files)/sizeof(files[0]));i++)
     { if (! strcmp(key,files[i].keyword))
	{ break;
	}
     }
    if (i >= sizeof(files)/sizeof(files[0]))
     { fprintf(stderr,"%s: config file %s: unrecognized keyword %s\n",
		argvec[0],cfn,key);
       continue;
     }
    else
     { char *base;
       int n;
       for (n=0;n<files[i].nfiles;n++)
	{ free(files[i].files[n]);
	  free(files[i].names[n]);
	}
       free((char *)files[i].files);
       free((char *)files[i].names);
       base = cp;
       n = 0;
       while (1)
	{ for (;isascii(*cp)&&isspace(*cp);cp++) ;
	  if (!*cp)
	   { break;
	   }
	  n ++;
	  for (;isascii(*cp)&&!isspace(*cp);cp++) ;
	}
       files[i].files = (char **) malloc(n*sizeof(char *));
       files[i].names = (char **) malloc(n*sizeof(char *));
       files[i].nfiles = n;
       n = 0;
       cp = base;
       while (1)
	{ 
#if 0
	  struct passwd *pwd;
	  char *tildedir;
	  char *sp;
	  char *tildetemp;
#else
	  char *expand;
#endif
	  for (;isascii(*cp)&&isspace(*cp);cp++) ;
	  if (!*cp)
	   { break;
	   }
	  if (n >= files[i].nfiles)
	   { fprintf(stderr,"%s: INTERNAL ERROR: nfiles overflow in readcf\n",
				argvec[0]);
	     abort();
	   }
	  base = cp;
	  for (;isascii(*cp)&&!isspace(*cp);cp++) ;
#if 1
	  if (*cp)
	   { *cp++ = '\0';
	   }
	  expand = expandFileName(base, cfn);
	  files[i].files[n] = malloc(strlen(expand)+files[i].extra+1);
	  sprintf(files[i].files[n],files[i].format,expand);
	  files[i].names[n] = malloc(strlen(base)+files[i].extra+1);
	  sprintf(files[i].names[n],files[i].format,base);
#else
	  if (*base == '~')
	   { int tdlen;
	     for (sp=base+1;(sp<cp)&&(*sp!='/');sp++) ;
	     if (debuginfo)
	      { printf("base=%s cp=%s sp=%s\n",base,cp,sp);
	      }
	     if (sp == base+1)
	      { tildedir = HOME_env;
		if (tildedir == 0)
		 { fprintf(stderr,
"%s: can't use bare ~ in config file without a HOME environment variable\n",
					argvec[0]);
		   exit(1);
		 }
	      }
	     else
	      { char savec;
		savec = *sp;
		*sp = '\0';
		pwd = getpwnam(base+1);
		if (! pwd)
		 { fprintf(stderr,"%s: config file %s: no such user `%s'\n",
					argvec[0],cfn,base+1);
		   exit(1);
		 }
		tildedir = pwd->pw_dir;
		*sp = savec;
	      }
	     tdlen = strlen(tildedir);
	     tildetemp = malloc((cp-sp)+tdlen+1);
	     files[i].files[n] = malloc((cp-sp)+tdlen+files[i].extra+1);
	     if (*cp)
	      { *cp++ = '\0';
	      }
	     sprintf(tildetemp,"%s%s",tildedir,sp);
	     sprintf(files[i].files[n],files[i].format,tildetemp);
	     free(tildetemp);
	   }
	  else
	   { files[i].files[n] = malloc((cp-base)+files[i].extra+1);
	     if (*cp)
	      { *cp++ = '\0';
	      }
	     sprintf(files[i].files[n],files[i].format,base);
	   }
#endif
	  n ++;
	}
       if (n != files[i].nfiles)
	{ fprintf(stderr,"%s: INTERNAL ERROR: nfiles underflow in readcf\n",
				argvec[0]);
	  abort();
	}
     }
  }
 fclose(cf);
}

config()
{
 char cf[MAXPATHLEN];
 int i;

 for (i=0;i<(sizeof(files)/sizeof(files[0]));i++)
  { files[i].files = (char **) malloc(0);
    files[i].names = (char **) malloc(0);
    files[i].nfiles = 0;
  }
 RCCL_env = getenv("RCCL");
 if (RCCL_env == 0)
  { struct passwd *pwd;
    fprintf(stderr,"Can't use rcc without RCCL environment variable\n");
    pwd = getpwnam("rccl");
    if (pwd)
     { fprintf(stderr,"Please add the following line to your .cshrc:\n");
       fprintf(stderr,"\tsetenv RCCL ~rccl\n");
     }
    exit(1);
  }
 sprintf(cf,"%s/conf/%s",RCCL_env,configfile);
 readcf(cf);
 HOME_env = getenv("HOME");
 if (HOME_env == 0)
  { fprintf(stderr,"Don't know where your home directory is;\n");
    fprintf(stderr,"  won't see any %s file there\n",configfile);
  }
 else
  { sprintf(cf,"%s/%s",HOME_env,configfile);
    readcf(cf);
  }
 readcf(configfile);
}

char *suffix(fn)
char *fn;
{
 char *cp;

 cp = rindex(fn,'.');
 return(cp?cp:"");
}

char *setsuf(fn,suf)
char *fn;
char *suf;
{
 char *sp;
 char *cp;

 sp = rindex(fn,'.');
 if (! sp)
  { int i = strlen(fn);
    cp = malloc(i+strlen(suf)+1);
    strcpy(cp,fn);
    strcpy(cp+i,suf);
    return(cp);
  }
 cp = malloc((sp-fn)+strlen(suf)+1);
 bcopy(fn,cp,sp-fn);
 strcpy(cp+(sp-fn),suf);
 return(cp);
}

addon(cppp,filesp)
char ***cppp;
struct files *filesp;
{
 int i;

 for (i=0;i<filesp->nfiles;i++)
  { *(*cppp)++ = filesp->files[i];
  }
}

main(ac,av)
int ac;
char **av;
{
 int i;
 int skip;
 char *args[MAXARGS];
 char *cp;
 char **cpp;

 argvec = av;

 n_libs = 0;
 n_options = 0;
 n_before = 0;
 n_after = 0;
 ofile = 0;
 cflag = 0;
 debuginfo = 0;
 part = first;
 skip = 0;
 for (ac--,av++;ac;ac--,av++)
  { if (skip > 0)
     { skip --;
       continue;
     }
    if (strcmp(*av,"CTRL") == 0)
     { if (part != first)
	{ fprintf(stderr,"%s: CTRL already specified, ignoring extra copy\n",
						argvec[0]);
	}
       else
	{ part = second;
	}
     }
    else if (strcmp(*av,"LIBFILE") == 0)
     { if (av[1])
	{ configfile = av[1];
	  av ++;
	  ac --;
	}
       else
	{ fprintf(stderr,"%s: %s needs another argument!\n",argvec[0],*av);
	}
     }
    else if (**av == '-')
     { static char *needarg = "oAeTu";
       static char *needrest = "qgpDUIBtyfs";
       int rest;
       if (strcmp(*av,"-debug") == 0)
	{ debuginfo ++;
	}
       else
	{ for (++*av;**av;++*av)
	   { if ((*av)[1] == '\0' && index(needarg,**av))
	      { if (++skip >= ac)
		 { fprintf(stderr,"%s: -%c needs another argument!\n",
							argvec[0],**av);
		   exit(1);
		 }
	      }
/*	     rest = (index(needrest,**av) != 0); */
	     if (strcmp(*av,"o") == 0)
	      { ofile = av[skip];
	      }
	     else if (strcmp(*av,"c") == 0)
	      { cflag = 1;
	      }
	     else if (**av == 'l')
	      { if (n_libs >= MAXARGS)
		 { fprintf(stderr,"%s fatal error: too many libraries\n",
								argvec[0]);
		   exit(1);
		 }
		libs[n_libs] = copyofstr((*av)-1);
		libs[n_libs][0] = '-';
		lib_is_ctrl[n_libs] = (part == second);
		n_libs ++;
		break;
	      }
	     else
	      { if (n_options >= MAXARGS)
		 { fprintf(stderr,"%s fatal error: too many options\n",
								argvec[0]);
		   exit(1);
		 }
		op_values[n_options] = (skip>0) ? av[skip] : 0;
		options[n_options] = copyofstr((*av)-1);
		options[n_options][0] = '-';
		n_options ++;
		break;
#if 0
		if (rest)
		 { options[n_options] = copyofstr((*av)-1);
		   options[n_options][0] = '-';
		   n_options ++;
		   break;
		 }
		else
		 { options[n_options] = malloc(3);
		   options[n_options][0] = '-';
		   options[n_options][1] = **av;
		   options[n_options][2] = '\0';
		   n_options ++;
		 }
#endif
	      }
	   }
	}
     }
    else if ((part == second) && strcmp(suffix(*av),".a") == 0)
     { /* treat this as a library and move it to the end */
       if (n_libs >= MAXARGS)
	{ fprintf(stderr,"%s fatal error: too many libraries\n",
						       argvec[0]);
	  exit(1);
	}
       libs[n_libs] = copyofstr(*av);
       lib_is_ctrl[n_libs] = 1;
       n_libs ++;
     }
    else
     { int *ip;
       char **cpp;
       switch (part)
	{ case first:
	     ip = &n_before;
	     cpp = &before[n_before];
	     break;
	  case second:
	     ip = &n_after;
	     cpp = &after[n_after];
	     break;
	}
       if (*ip >= MAXARGS)
	{ fprintf(stderr,"%s fatal error: too many arguments\n",argvec[0]);
	  exit(1);
	}
       ++ *ip;
       *cpp = *av;
     }
  }
 if (part == first)
  { n_after = 0;
  }
 config();
 if (! ofile)
  { for (i=0;i<n_before;i++)
     { char *s;
       s = suffix(before[i]);
       if ( (strcmp(s,".c") == 0) ||
	    (strcmp(s,".s") == 0) ||
	    (strcmp(s,".o") == 0) )
	{ s = rindex(before[i],'/');
	  ofile = setsuf(s?s+1:before[i],"");
	  break;
	}
     }
    if (! ofile)
     { for (i=0;i<n_after;i++)
	{ char *s;
	  s = suffix(after[i]);
	  if ( (strcmp(s,".c") == 0) ||
	       (strcmp(s,".s") == 0) ||
	       (strcmp(s,".o") == 0) )
	   { s = rindex(after[i],'/');
	     ofile = setsuf(s?s+1:after[i],"");
	     break;
	   }
	}
     }
  }
 if (debuginfo)
  { printf("%d libraries:\n",n_libs);
    for (i=0;i<n_libs;i++)
     { printf("\t%s%s\n",libs[i],(lib_is_ctrl[i]?" (control)":""));
     }
    printf("%d options:\n",n_options);
    for (i=0;i<n_options;i++)
     { if (op_values[i])
	{ printf("\t%s %s\n",options[i],op_values[i]);
	}
       else
	{ printf("\t%s\n",options[i]);
	}
     }
    printf("%d before:\n",n_before);
    for (i=0;i<n_before;i++)
     { printf("\t%s\n",before[i]);
     }
    printf("%d after:\n",n_after);
    for (i=0;i<n_after;i++)
     { printf("\t%s\n",after[i]);
     }
    if (ofile)
     { printf("-o %s\n",ofile);
     }
    for (i=0;i<(sizeof(files)/sizeof(files[0]));i++)
     { int j;
       printf("%s=",files[i].keyword);
       for (j=0;j<files[i].nfiles;j++)
	{ if (j)
	   { printf(" ");
	   }
	  printf("%s",files[i].names[j]);
	}
       printf("\n");
     }
  }
 for (i=0;i<n_after;i++)
  { char *s;
    s = suffix(after[i]);
    if ((strcmp(s,".c") == 0) || (strcmp(s,".s") == 0))
     { break;
     }
  }
 if (CC_CMD->nfiles > 0)
  { C_command = *(CC_CMD->files);
  }
 else
  { C_command = default_cc;
  }
 if (i < n_after)
  { cpp = args;
    if (C_command == default_cc)
     { *cpp++ = C_command;
     }
    else
     { addon(&cpp,CC_CMD);
     }
    addon(&cpp,INCLUDE);
    *cpp++ = "-c";
#if uVAX_CFG
    *cpp++ = "-b";
#endif
    for (i=0;i<n_options;i++)
     { *cpp++ = options[i];
       if (op_values[i])
	{ *cpp++ = op_values[i];
	}
     }
    for (i=0;i<n_after;i++)
     { char *s;
       s = suffix(after[i]);
       if ((strcmp(s,".c") == 0) || (strcmp(s,".s") == 0))
	{ *cpp++ = after[i];
	  after[i] = setsuf(after[i],".o");
	}
     }
/*    *cpp++ = "-DCTRL";	*/
    *cpp++ = 0;
    if (cpp-args >= MAXARGS)
     { fprintf(stderr,"Command argument overflow\n");
       exit(1);
     }
    do_command(C_command,args);
  }
 if (cflag)
  { for (i=0;i<n_before;i++)
     { char *s;
       s = suffix(before[i]);
       if ((strcmp(s,".c") == 0) || (strcmp(s,".s") == 0))
	{ break;
	}
     } 
    if (i < n_before)
     { cpp = args;
       if (C_command == default_cc)
	{ *cpp++ = C_command;
	}
       else
	{ addon(&cpp,CC_CMD);
	}
       addon(&cpp,INCLUDE);
       *cpp++ = "-c";
       for (i=0;i<n_options;i++)
	{ *cpp++ = options[i];
	  if (op_values[i])
	   { *cpp++ = op_values[i];
	   }
	}
       for (i=0;i<n_before;i++)
	{ char *s;
	  s = suffix(before[i]);
	  if ((strcmp(s,".c") == 0) || (strcmp(s,".s") == 0))
	   { *cpp++ = before[i];
	     after[i] = setsuf(before[i],".o");
	   }
	}
/*       *cpp++ = "-DCTRL";	*/
       *cpp++ = 0;
       if (cpp-args >= MAXARGS)
	{ fprintf(stderr,"Command argument overflow\n");
	  exit(1);
	}
       do_command(C_command,args);
     }
  }
 else
  { cpp = args;
    if (C_command == default_cc)
     { *cpp++ = C_command;
     }
    else
     { addon(&cpp,CC_CMD);
     }
    addon(&cpp,INCLUDE);
    if (ofile)
     { *cpp++ = "-o";
       *cpp++ = ofile;
     }
    for (i=0;i<n_options;i++)
     { *cpp++ = options[i];
       if (op_values[i])
	{ *cpp++ = op_values[i];
	}
     }
    for (i=0;i<n_before;i++)
     { *cpp++ = before[i];
     }
    for (i=0;i<n_libs;i++)
     { if (! lib_is_ctrl[i])
	{ *cpp++ = libs[i];
	}
     }
    addon(&cpp,USER);
#ifdef uVAX_CFG
    *cpp++ = "-aa";
#endif
#ifdef sun
    *cpp++ = "-Bstatic";
#endif
    addon(&cpp,BEGIN);
    for (i=0;i<n_after;i++)
     { *cpp++ = after[i];
     }
    addon(&cpp,CTRL);
    addon(&cpp,UTIL);
    for (i=0;i<n_libs;i++)
     { if (lib_is_ctrl[i])
	{ *cpp++ = libs[i];
	}
     }
#ifdef uVAX_CFG
    *cpp++ = "-aa";
#endif
    addon(&cpp,END);
    *cpp++ = 0;
    if (cpp-args >= MAXARGS)
     { fprintf(stderr,"Command argument overflow\n");
       exit(1);
     }
    do_command(C_command,args);
  }
 exit(0);
}

do_command(pgm,args)
char *pgm;
char **args;
{
 char **cpp;
 int pid;

 printf("(%s)",pgm);
 for (cpp=args;*cpp;cpp++)
  { printf(" %s",*cpp);
  }
 putchar('\n');
 pid = fork();
 if (pid < 0)
  { fprintf(stderr,"%s: cannot fork: ",argvec[0]);
    perror(0);
    exit(1);
  }
 if (pid == 0)
  { execvp(pgm,args);
    fprintf(stderr,"%s: cannot exec %s: ",argvec[0],pgm);
    perror(0);
    exit(1);
  }
 while (1)
  { int p;
    union wait status;
    p = wait(&status);
    if (p < 0)
     { break;
     }
    if (p != pid)
     { fprintf(stderr,"%s: unexpected child %d died\n",argvec[0],p);
     }
    else
     { if (WIFSIGNALED(status))
	{ static char *sigmsgs[] = { "Signal 0",
				     "Hanged up",
				     "Interrupted",
				     "Quit",
				     "Illegal instruction",
				     "Trace trap",
				     "IOT trap",
				     "EMT trap",
				     "Floating exception",
				     "Killed",
				     "Bus error",
				     "Segmentation violation",
				     "Bad syscall",
				     "Broken pipe",
				     "Alarm",
				     "Terminated",
				     "I/O condition",
				     "Stopped (signal)",
				     "Stopped",
				     "Continued",
				     "Child died",
				     "Tty input",
				     "Tty output",
				     "I/O possible",
				     "CPU limit exceeded",
				     "File size limit exceeded",
				     "Virtual alarm",
				     "Profiling alarm",
				     "Signal 28",
				     "Signal 29",
				     "Signal 30",
				     "Signal 31",
				     "Signal 32" };
	  fprintf(stderr,"%s",sigmsgs[status.w_termsig]);
	  if (status.w_coredump)
	   { fprintf(stderr," (core dumped)");
	   }
	  fprintf(stderr,"\n");
	  exit(1);
	}
       else if (status.w_retcode)
	{ fprintf(stderr,"Exit %d\n",status.w_retcode);
	  exit(status.w_retcode);
	}
       break;
     }
  }
}

char *expandFileName(name, cfn)
char *name;
char *cfn;
{
	/* expands a file name. Assumes that the file name contains no
	   white space. Expands leading ~'s, plus environment
	   variables
	 */
 	
	static char buf[MAXPATHLEN+64];
	register char *cp, *np;
	char *base;
	char *tildedir;
	char *tildetemp;
	struct passwd *pwd;

	cp = name;
	np = buf;
	*np = '\0';
	
	/* If the first character is ~, do home directory expansion */

	if (*cp == '~')
	 { int tdlen;
	   base = cp;
	   for (cp = base+1; *cp && (*cp != '/'); cp++)
	      ;
	   if (cp == base+1)
	    { if ((tildedir = getenv("HOME")) == NULL)
	       { fprintf (stderr, 
"Config file %s: bare ~ used without HOME environment variable\n", cfn);
		 exit (-1);
	       }
	    }
	   else
	    { char savec;
	      savec = *cp;
	      *cp = '\0';
	      if ((pwd = getpwnam(base+1)) == NULL)
	       { fprintf (stderr,
"Config file %s: no such user '%s'\n", cfn, base+1);
		 exit (-1);
	       }
	      tildedir = pwd->pw_dir;
	      *cp = savec;
	    }
	   strcpy (np, tildedir);
	   np += strlen (np);
	 }

	/* Now proceed with regular expansion */

	while (*cp)
	 { if (*cp == '\\')
	    { cp++;
	      if (*cp)
	       { *np++ = *cp++;
	       }
	    }
	   else if (*cp == '$')
	    { char savec;
	      char *env;

	      cp++;
	      base = cp;
	      for (; *cp && (isalnum(*cp) || *cp == '_'); cp++)
		 ;
	      savec = *cp;
	      *cp = '\0';
	      if (cp == base)
	       { fprintf (stderr, 
"Conf file %s: NULL environment variable in file name\n", cfn);
		 exit (-1);
	       }
	      if ((env = getenv(base)) == NULL)
	       { fprintf (stderr, 
"Conf file %s: environment variable %s not defined\n", cfn, base);
		 exit (-1);
	       }
	      strcpy (np, env);
	      np += strlen(np);
	      *cp = savec;
	    }
	   else
	    { *np++ = *cp++;
	    }
	 }
	*np = '\0';
	return (buf);
}

static char *copyofstr(s)
char *s;
{
	return (strcpy (malloc (strlen(s)+1), s));
}
