 /*
  * Khoros: $Id: toolbox.c,v 1.4 1992/03/20 22:52:48 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: toolbox.c,v 1.4 1992/03/20 22:52:48 dkhoros Exp $";
#endif

 /*
  * $Log: toolbox.c,v $
 * Revision 1.4  1992/03/20  22:52:48  dkhoros
 * VirtualPatch5
 *
  */

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1991, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as to the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including, for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1991 by UNM */
#include "kraftsman.h"
#include "ghost.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    file name: toolbox.c                              <<<<
   >>>>                                                       <<<<
   >>>>   description:                                        <<<<
   >>>>                                                       <<<<
   >>>>      routines: create_toolbox()                       <<<<
   >>>>                add_toolbox()                          <<<<
   >>>>                _xv_perror_wait()                      <<<<
   >>>>                _create_files()                        <<<<
   >>>>                _expandinternals()                     <<<<
   >>>>                _getnextvar()                          <<<<
   >>>>                _parsedirs()                           <<<<
   >>>>                _create_dirs()                         <<<<
   >>>>                _freeconfigkeys()                      <<<<
   >>>>                _fcopy()                               <<<<
   >>>>                                                       <<<<
   >>>> modifications:					      <<<<
   >>>>		Removed cludges from create_toolbox and       <<<<
   >>>>     _create_files that were used to get the beta      <<<<
   >>>>     version of kraftsman done for patch 3.  Also      <<<<
   >>>>     created add_toolbox() to better define the        <<<<
   >>>>     split between creating and adding a toolbox.      <<<<
   >>>>     Created _xv_perror_wait() to be able to have      <<<<
   >>>>     perror type error messages in an X popup.         <<<<
   >>>>                  -- Steven Jorgensen  Jan 3, 1992     <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */

/***********************************************************************
*
*  Routine Name: create_toolbox()
*
*          Date:  12/8/91
*        
*       Purpose:  To create a basic toolbox.  This means creating
*		  all the directories and config files necessary
*		  for it to work with the Imake system, as well
*		  as updating the toolbox file
*
*         Input:  name -- Toolbox name
*		  path -- Toolbox path
*		  author -- Toolbox author
*		  user -- Toolbox user
*		  title -- Toolbox title
*		  desc -- Toolbox desc
*		  info -- Toolbox info
*
*        Output: TRUE if successful, FALSE on fail
*
*    Written By:  Steven Jorgensen
*
* Modifications:
*	Took the part dealing with updating the KHOROS_TOOLBOX file
*	out of this routine, and created add_toolbox does that function.
*	This routine now ONLY creates the directory tree associated with
*	a toolbox.  NOTE: as of this date, no config files are created
*	in the toolbox itself; this will need to be added when configure
*	is written.
*			-- Steven Jorgensen  Jan 3, 1992
*
***********************************************************************/
int create_toolbox(name, path, user, info)
char *name, *path, *user, *info;
  {
    char *cfgfile = NULL;
    char **km_config;
    FILE *cfile;
    char **keynames[2];
    char fpath[LENGTH];

	/*
	 * check to see if we can get to the path specified by the
	 * path variable
	 */
    (void) vexpandpath(path, NULL, fpath);
    if (access(fpath,F_OK) == -1 && errno != ENOENT)
      {
	_xv_perror_wait("create_toolbox: ","create_toolbox:");
	return(FALSE);
      }

	/*
	 * Check to see if we can open the config file
	 */
    if ((cfgfile = vfullpath(CFGFILE, NULL, NULL)) == NULL)
      {
	xvf_error_wait("create_toolbox: Can't open config file",
		       "Kraftsman Error", "ok");
	return(FALSE);
      }
    if ((cfile = fopen (cfgfile,"r")) == NULL)
      {
	free(cfgfile);
	xvf_error_wait("create_toolbox: Can't open config file",
		       "Kraftsman Error", "ok");
	return(FALSE);
      }
    free(cfgfile);

	/*
	 * Make sure directory "path" exists
	 */
    if (vmkdir(fpath, 0777) != 0 && errno != EEXIST)
      {
	xvf_error_wait("create_toolbox: Can't create toolbox directory",
		       "Kraftsman Error", "ok");
	return(FALSE);
      }

	/*
	 * Read the configuration file
	 */
    kraftsman_init_config_keys(keynames);
    km_config = gw_read_kraft_config(cfile,keynames,MaxKraftConfig);
    if (km_config == NULL)
      {
	xvf_error_wait("create_toolbox: Error in reading config file",
		       "Kraftsman Error", "ok");
	return(FALSE);
      }

	/*
	 * This section creates the directories required by the
	 * toolbox
	 */
    if (_create_dirs(km_config[DirFlag],fpath) == FALSE)
      {
	xvf_error_wait("create_toolbox: Unable to create toolbox directories",
			"Kraftsman Error", "ok");
	_freeconfigkeys(km_config,keynames,MaxKraftConfig);
	return(FALSE);
      }

	/*
	 * This section generates the files required by the toolbox
	 */
    if (_create_files(km_config[FileCreate], km_config[FileVars], name,
	fpath, user, info) == FALSE)
      {
	xvf_error_wait("create_toolbox: Unable to create toolbox configuration files", "Kraftsman Error", "ok");
	_freeconfigkeys(km_config,keynames,MaxKraftConfig);
	return(FALSE);
      }

    _freeconfigkeys(km_config, keynames, MaxKraftConfig);

    return(TRUE);

  }

/***********************************************************************
*
*  Routine Name: add_toolbox()
*
*          Date: 1/2/92
*
*       Purpose: This routine adds a toolbox entry to the toolbox file
*
*         Input:  name -- Toolbox name
*		  path -- Toolbox path
*		  author -- Toolbox author
*		  user -- Toolbox user
*		  title -- Toolbox title
*		  desc -- Toolbox desc
*		  info -- Toolbox info
*        Output: returns TRUE on success, and FALSE on fail
*
*    Written By: Steven Jorgensen
*
* Modifications:
*
***********************************************************************/
int add_toolbox(name, path, author, title, desc, info)
char *name, *path, *author, *title, *desc, *info;
  {
    int status;

    if ((status = vtoolbox_add(name, path, title, desc, author, info))==0)
      {
	xvf_error_wait("create_toolbox: Unable to create toolbox",
		       "Kraftsman Error", "ok");
	return(FALSE);
      }
    if ( status == -1)
      {
	xvf_error_wait("create_toolbox: Toolbox already exists. (This error shouldn't ever happen)","Kraftsman Error", "ok");
	return(FALSE);
      }

    if (vtoolbox_write("$KHOROS_TOOLBOX") == FALSE)
      {
	xvf_error_wait("create_toolbox: Unable to update Toolbox file",
		       "Kraftsman Error", "ok");
	return(FALSE);
      }
    return(TRUE);
  }

/***********************************************************************
*
*  Routine Name: _create_files()
*
*          Date: 12/5/91
*        
*       Purpose: This routine creates the files specified in the
*		 configuration file with the variables inserted
*		 in their proper place
*
*         Input: dirlist - The list of files to create
*		 varlist - The list of variables to insert
*		 name - toolbox name
*		 path - toolbox path
*		 user - toolbox user
*		 info - toolbox info
*
*        Output: returns TRUE on success, and FALSE on fail
*
*    Written By: Steven Jorgensen
*
* Modifications:
*	Fixed this routine so that no empty config files are required
*	when two or more variables followed each other directly.
*	Also got rid of some useless code to (hopefully) speed things up. :^)
*			-- Steven Jorgensen  Jan 3, 1992
*
***********************************************************************/
int _create_files(dirlist, varlist, name, path, user, info)
  char *dirlist, *varlist, *name, *path, *user, *info;
  {
    char **ldirlist, **cfiles;
    int i, j, fcnt, fvar = -1, ccnt, tcnt, tmax, ccur;
    char *cvarlist, *var = NULL, *dir;
    char basefile[LENGTH];
    FILE *infile = NULL, *outfile = NULL;
    
	/*
	 * Generate a list of files to create
	 */
    if ((ldirlist = _parsedirs(dirlist,path,&fcnt)) == NULL)
      {
	xvf_error_wait("create_toolbox: Can't read configuration filenames from config file", "Kraftsman Error", "ok");
	return(FALSE);
      }

	/*
	 * Loop through and create all the files in the ldirlist
	 */
    cvarlist = varlist;
    strcpy(basefile,"file_test");
    for (i = 0; i < fcnt; i++)
      {

	/*
	 * Remove #TOOLBOX, #PATH, #USER, #INFO from file path, and replace
	 * with name, path, user, and info respectively
	 */
	if((dir =  _expandinternals(ldirlist[i],name,path,user,info)) == NULL)
	  {
	    xvf_error_wait("create_toolbox: Invalid Pathname","Kraftsman Error",
			   "ok");
	    vfreelist(ldirlist,fcnt);
	    return(FALSE);
	  }

	/*
	 * Make sure file is not already there.. Don't want to 
	 * overwrite an existing toolbox
	 */
	if (access(dir,R_OK) == 0)
	  {
	    xvf_error_wait("create_toolbox: Toolbox already exists here, please remove it before creating a new one", "Kraftsman Error", "ok");
	    free(dir);
	    vfreelist(ldirlist,fcnt);
	    return(FALSE);
	  }

	/* 
	 * find all the template files associated with the current file we're
	 * trying to create
	 */
	sprintf(strrchr(basefile,'_'),"_%d",i);
	cfiles = vlistdir(basefile,CFGPATH,XV_READ|XV_PATH|XV_FILE,FALSE,&ccnt);
	if (ccnt == 0) return(FALSE);
	tmax = atoi(strrchr(cfiles[ccnt-1],'.')+1) + 1;

	if ((outfile = fopen(dir,"w")) == NULL)
	  {
	    xvf_error_wait("create_toolbox: Can't write toolbox configuration file", "Kraftsman Error", "ok");
	    free(dir);
	    vfreelist(ldirlist,fcnt);
	    return(FALSE);
	  }

	/*
	 * For each template file, copy contents to the file we're creating,
	 * and insert a variable after each template file is read.
	 */
	for (j = 0, tcnt = 0; tcnt < tmax; tcnt++)
	  {
	    if (var == NULL)
	      var =  _getnextvar(&cvarlist, name, path, user, info, &fvar);

	/*
	 * Copy the contents from template file to the new toolbox file
	 * if the current template file exists
	 */
	    ccur = (j < ccnt) ? atoi(strrchr(cfiles[j],'.')+1) : -1;
	    if (ccur == tcnt)
	      {
		if ((infile = fopen(cfiles[j],"r")) == NULL)
		  {
	            xvf_error_wait("create_toolbox: Can't read toolbox template file", "Kraftsman Error", "ok");
		    free(dir);
		    fclose(outfile);
		    vfreelist(cfiles,ccnt);
		    vfreelist(ldirlist,fcnt);
		    return(FALSE);
		  }
		_fcopy(infile,outfile);
		fclose(infile);
		free(cfiles[j]);
		j++;
	      }

	/*
	 * write out the variable if there is one
	 */
	    if (var == NULL)
	      cvarlist = NULL;
	    else if (i == fvar)
	      {
		fprintf(outfile,"%s\n",var);
		free (var);
		var = NULL;
	      }
	  }
	free(dir);
	free(cfiles);
	fclose(outfile);
      }

    vfreelist(ldirlist,fcnt);
    return(TRUE);
  }

/***********************************************************************
*
*  Routine Name: _expandinternals()
*
*          Date: 12/5/91
*        
*       Purpose: Remove $TOOLBOX, $PATH, $USER, $INFO from file path, and
*		 replace with name, path, user, and info respectively
*
*         Input: s - input string to expand
*		 name - toolbox name
*		 path - toolbox path
*		 user - toolbox user
*		 info - toolbox info
*
*        Output: expanded string or NULL if there is an error
*
*    Written By: Steven Jorgensen 
*
* Modifications:
*
***********************************************************************/
char *_expandinternals(s, name, path, user, info)
  char *s, *name, *path, *user, *info;
  {
    char *dflag = NULL;
    char *tmp1, *tmp2, *tmp3, *tmp4, *tmp5;
    char *upper, *lower;

    if (s == NULL) return(NULL);
    dflag = strchr(s,'#');
    if (dflag == NULL) return(VStrcpy(s));
    upper = vupper_string(name,NULL);
    lower = vlower_string(name,NULL);
    tmp1 = vreplace_string(s, "#UTOOLBOX", upper, NULL);
    tmp2 = vreplace_string(tmp1, "#LTOOLBOX", lower, NULL);
    tmp3 = vreplace_string(tmp2, "#PATH", path, NULL);
    tmp4 = vreplace_string(tmp3, "#USER", user, NULL);
    tmp5 = vreplace_string(tmp4, "#INFO", info, NULL);
    free(upper);
    free(tmp1);
    free(tmp2);
    free(tmp3);
    free(tmp4);
    return(tmp5); 
  }

/***********************************************************************
*
*  Routine Name: _getnextvar()
*
*          Date: 12/5/91
*        
*       Purpose: to return next variable to be put in a toolbox file
*		 with all keys expanded (see _expandinternals)
*
*         Input: varlist - variable list
*		 name - toolbox name
*		 path - toolbox path
*		 user - toolbox user
*		 info - toolbox info
*
*        Output: returns expanded variable if one exists.  NULL else
*		 filenum - set to indicate which toolbox file the
*		 	   variable belongs in.
*
*    Written By:  
*
* Modifications:
*
***********************************************************************/
char *_getnextvar(varlist, name, path, user, info, filenum)
  char **varlist, *name, *path, *user, *info;
  int *filenum;
  {
    char *lvar, *nl, *fvar;

    if (*varlist == NULL) return(NULL);
    if((nl = strchr(*varlist,'\n')) == NULL) return(NULL);

    lvar = VStrncpy((strchr((*varlist),' ')+1), (int) ((nl != NULL) ?
		  (nl - (strchr((*varlist),' ') + 1)) : (VStrlen((*varlist)))));
    if (lvar == NULL) return(NULL);
    sscanf(*varlist,"%d",filenum);
    fvar = _expandinternals(lvar, name, path, user, info); 
    free(lvar);
    *varlist = nl + 1;
    return(fvar);
  }

/***********************************************************************
*
*  Routine Name: _parsedirs()
*
*          Date: 12/5/91
*        
*       Purpose: parse the paths of the dirlist string into an
*		 array of directories.
*
*         Input: dirlist - paths separated by \n in a single string
*		 path - Toolbox pathname
*
*        Output: The array of pathnames or NULL on error
*		 dircnt - # elements in the array, or 0 on error
*
*    Written By: Steven Jorgensen 
*
* Modifications:
*
***********************************************************************/
char **_parsedirs(dirlist, path, dircnt)
  char *dirlist, *path;
  int *dircnt;
  {
    char *nl, *next_nl, *ndir = NULL, *cpath = NULL, **ret_list = NULL;
    int size = 0, done = FALSE;

    nl = dirlist;
    *dircnt = 0;
    while (done == FALSE)
      {
	if ((next_nl = strchr(nl,'\n')) == NULL) done = TRUE;

	/*
	 * The following command is extremely gross, but it
	 * works.. It takes the first path name off of a string
	 * in the following format:
	 * <garbage>/path\n<garbage>/path\n<garbage>/path\n...
	 */
	ndir = VStrncpy(strchr(nl,'/'),(int) ((done == FALSE) ? 
		       (next_nl - strchr(nl,'/')) : (VStrlen(nl))));

	if (ndir != NULL && (cpath = vcomposepath(ndir,path,NULL)) != NULL)
	  {
	    if (*dircnt == size)
	      {
		size = ((size / 10) + 1) * 10;
		ret_list=(caddr_t *) krealloc(ret_list,(sizeof(char *)*size));
	      }
	    free(ndir);
	    ret_list[*dircnt] = cpath;
	    cpath = NULL;
	    (*dircnt)++;
	  }
	else
	  kfree(ndir);
	nl = (done == TRUE) ? nl : next_nl +1;
      }
    if (*dircnt == 0)
      {
	vfreelist(ret_list,*dircnt);
	return(NULL);
      }
    return(ret_list);
  }

/***********************************************************************
*
*  Routine Name: _create_dirs()
*
*          Date: 12/5/91
*        
*       Purpose: To create the directories required in the toolbox
*
*         Input: dirlist - list of directories to make
*		 path - Toolbox path
*
*        Output: Returns TRUE on success or FALSE on error
*
*    Written By: Steven Jorgensen
*
* Modifications:
*
***********************************************************************/
int _create_dirs(dirlist, path )
  char *dirlist, *path;
  {
    char **ldirlist = NULL;
    int i, dcnt, ret = TRUE;

    if ((ldirlist = _parsedirs(dirlist,path,&dcnt)) == NULL)
      return(FALSE);
    for ( i = 0 ; i < dcnt; i++)
      if(vmkdir(ldirlist[i], 0777) != 0 && errno != EEXIST)
	{
	  _xv_perror_wait("create_toolbox: ","create_toolbox:");
	  ret = FALSE;
	  break;
	}
    vfreelist(ldirlist, dcnt);
    return(ret);
  }

/***********************************************************************
*
*  Routine Name: _freeconfigkeys()
*
*          Date: 12/5/91
*        
*       Purpose: To free the malloced space associated with the keyword
*		 arrays and file arrays
*		
*         Input: s - Array of parsed strings from kraftsman config file
*		 k - Array of keywords
*		 keycnt - size of both arrays
*
*        Output: NONE
*
*    Written By: Steven Jorgensen
*
* Modifications:
*
***********************************************************************/
_freeconfigkeys(s,k,keycnt)
  char **s, **k[2];
  int keycnt;
  {
    int cnt;

    for (cnt = 0 ; cnt < keycnt ; cnt++)
      {
	free(s[cnt]);
	free(k[0][cnt]);
	free(k[1][cnt]);
      }
    free(s);
    free(k[0]);
    free(k[1]);
  }

/***********************************************************************
*
*  Routine Name: _fcopy()
*
*          Date: 12/5/91
*        
*       Purpose: To perform a buffered file copy from one file to another
*		 NOTE:  this routine uses file pointers, NOT filenames.
*		 Thus this routine can be used to copy multiple
*		 input files to an output file without reopening the
*		 output file everytime.
*
*         Input: f1 - Src file pointer.  This must point at a file opened
*		      for reading
*		 f2 - Dest file pointer.  This must point at a file opened
*		      for writing or appending
*
*        Output: Returns TRUE on success, and FALSE on error
*
*    Written By: Steven Jorgensen
*
* Modifications:
*
***********************************************************************/
_fcopy(f1,f2)
  FILE *f1, *f2;
  {
    char *buf;
    int items;

    if (f1 == NULL || f2 == NULL)
      return(FALSE);
    if((buf = (char *) malloc( 2000 * sizeof (char))) == NULL)
      {
	fprintf(stderr,"_fcopy: Out of Memory");
	return(FALSE);
      }
    while ((items = fread(buf,1,2000,f1)) > 0)
      if (fwrite(buf,1,items,f2) != items)
	return(FALSE);
    return(TRUE);
  }

/***********************************************************************
*
*  Routine Name: _xv_perror_wait()
*
*          Date: 1/2/92
*
*       Purpose: Pop up Perror
*
*         Input: s - String to prepend onto error message
*
*	 Output: none
*
*    Written By: Steven Jorgensen
*
* Modifications:
*
***********************************************************************/
void _xv_perror_wait(title,s)
  char *s, *title;
  {
    char *message, *out_title, num[10];

    perror(s);
    sprintf(num,"%d",errno);
    message = VStrcat("Can't continue, Error number is ",num);
    out_title = (title == NULL) ? VStrcpy("_xv_perror_wait ")
				: VStrcpy(title);
    xvf_error_wait(message, out_title, "ok");
    free(message);
    kfree(out_title);
  }
