 /*
  * Khoros: $Id: save.c,v 1.4 1992/03/25 17:29:52 dkhoros Exp $
  */

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

 /*
  * $Log: save.c,v $
 * Revision 1.4  1992/03/25  17:29:52  dkhoros
 * VirtualPatch5
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, 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 too 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 1990 by UNM */
#include "cantata.h"


static int  save_workspace();
static int  save_connection_list();
static void save_environment(); 
static void print_line();
static int  save_files();

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    file name:  save.c                                <<<<
   >>>>                                                       <<<<
   >>>>   description:                                        <<<<
   >>>>                                                       <<<<
   >>>>      routines:  xvl_save_workspace()                  <<<<
   >>>>                 static save_workspace()               <<<<
   >>>>                 save_environment()                    <<<<
   >>>>                 save_connection_list()                <<<<
   >>>>                 xvl_save_files()                      <<<<
   >>>>                 static save_files()                   <<<<
   >>>>                 xvl_read_data()                       <<<<
   >>>>                                                       <<<<
   >>>> modifications:					      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



static void close_file(file, compress)

FILE	*file;
int	compress;
{
	if (compress)
	   xvf_pclose(file);
	else
	   fclose(file);
}


static void remove_file(filename)

char	*filename;
{
	int	i;
	char	temp[512];


	if (filename == NULL || xvf_strlen(filename) == 0)
	   return;

	if ((i = xvf_strlen(filename)) > 2)
	{
	   if (strncmp(&filename[i-2],".Z", 2) == 0)
	   {
	      strncpy(temp, filename, i-2);
	      temp[i-2] = '\0';
	   }
	   else
	      strcpy(temp, filename);
	}
	else
	   strcpy(temp, filename);

	unlink(temp);
	strcat(temp,".Z");
	unlink(temp);
}

static FILE *open_file(filename, compress, workname)

char	*filename;
int	*compress;
char	workname[];
{
	int	i;
	FILE	*file, *xvf_popen();
	char	compname[512], temp[512];


	if (filename == NULL || xvf_strlen(filename) == 0)
	{
	   xvf_error_wait("No filename found.  Please specify the filename of\
 in which the workspace is to be saved", "xvl_save_workspace", NULL);
	   return(False);
	}

	if (*compress)
	{
	   if ((i = xvf_strlen(filename)) > 2)
	   {
	      if (strncmp(&filename[i-2],".Z",2) == 0)
	         strcpy(compname, filename);
	      else
	         sprintf(compname,"%s.Z", filename);
	   }
	   else
	      sprintf(compname,"%s.Z", filename);

	   if (access(compname, W_OK | F_OK) == -1)
	   {
	      if (!(file = fopen(compname, "w")))
	      {
	         sprintf(temp, "Unable to open '%s' for saving workspace. \
Please make sure that you are saving the workspace to a directory where you \
have write permission.", compname);
	         xvf_error_wait(temp, "xvl_save_workspace", NULL);
	         return(False);
	      }
	      else
	      {
	         fclose(file);
	         unlink(compname);
	      }
	   }

	   sprintf(temp,"compress -c > %s", compname);
	   if (!(file = xvf_popen(temp, "w")))
	   {
	      sprintf(temp, "Error! Cannot find 'compress' command for \
compressing cantata workspace file (%s).  Workspace files will be saved \
uncompressed.", compname);
	      xvf_error_wait(temp, "xvl_save_workspace", NULL);
	      *compress = false;
	   }
	   else
	   {
	      strcpy(workname, compname);
	      return(file);
	   }
	}

	if (!(file = fopen(filename, "w")))
	{
	   sprintf(temp, "Unable to open %s for saving workspace", filename);
	   xvf_error_wait(temp, "xvl_save_workspace", NULL);
	   return(False);
	}
	strcpy(workname, filename);
	return(file);
}



/************************************************************
*
*  Routine Name:  xvl_save_workspace
*
*      Purpose:  Save the workspace and all associated resources
*		 into the specified file.
*
*	 Input:  
*
*       Output:  
*               
*   Written By:  Mark Young & Carla Williams
*
*************************************************************/


int xvl_save_workspace(workspace, glyphs, filename, save_files)

Workspace *workspace;
GlyphList *glyphs;
char	  *filename;
int	  save_files;
{
	FILE	 *file;
	time_t	 clock;
	int	 i, compress, status;
	char	 temp[512], workname[512], *username, *getenv();


	/*
	 *  remove the file if it exists before saving it.
	 */
	remove_file(filename);

	/*
	 *  open the file that will contain the workspace
	 */
	compress = workspace->compress;
	if (!(file = open_file(filename, &compress, workname)))
	   return(False);

	clock = time(NULL);
	if (!(username = getenv("USER")))
	{
	   username = xvf_strcpy("(unknown)");
	}

	fprintf(file,"# Khoros Image Processing System Workspace\n#\n# cantata\
 workspace file (%s) was created \n# on %s# by user %s\n#\n", workname,
 ctime(&clock), username);

	/*
	 *   Call save_workspace routine.  This is the routine that really
	 *   saves the workspace.  It recursively goes thru the workspace
	 *   saving the glyphform specification (database) and other information
	 *   so that a user can restore the workspace.
	 */
	status = save_workspace(workspace, glyphs, file);

	/*
	 *   Call save_connection_list() routine.
	 *   save_connection_list() saves the connection information for each
	 *   of the glyphs.
	 */
	if (status == True)
	   status = save_connection_list(glyphs, file);

	close_file(file, compress);
	if (status == False)
	{
	   unlink(workname);
	   return(False);
	}

	/*
	 *  Save the temporary files.  Since we don't want to save the
	 *  temporary file over any old temporary files remove them first.
	 *  We will attempt to unlink the either the uncompressed or compressed
	 *  files, if neither exist unlink() will silently ignore us.
	 */
	if ((i = xvf_strlen(filename)) > 2)
	{
	   if (strncmp(&filename[i-2],".Z", 2) == 0)
	   {
	      strncpy(temp, filename, i-2);
	      temp[i-2] = '\0';
	      strcat(temp,".files");
	   }
	   else
	      (void) sprintf(temp, "%s.files", filename);
	}
	else
	   (void) sprintf(temp, "%s.files", filename);

	remove_file(temp);
	if (save_files == True)
	   xvl_save_files(glyphs, temp, compress);

	return(True);
}



/************************************************************
*
*  Routine Name:  static save_workspace
*
*      Purpose:  Save the workspace and all associated resources
*		 into the specified file.
*
*	 Input:  
*
*       Output:  
*               
*   Written By:  Mark Young & Carla Williams
*
*************************************************************/


static int save_workspace(workspace, glyphs, file)

Workspace *workspace;
GlyphList *glyphs;
FILE	  *file;
{
	Glyph	   *glyph;
	GlyphList  *glyphlist;

	long	   id;
	Workspace  *macro;
	xvf_form   *form;
	int	   i, index;
	char	   *remote_machine;


	/*
	 *  Write out the workspace.  First we write out the form specification,
	 *  then the variables and environment information.  Last we write out
	 *  all the glyph form which contain the specification for all the
	 *  glyph's that contain a specification.
	 */
	fprintf(file,"# BeginWorkspace\n");

	/*
	 *  Save the glyph information.
	 */
	form  = workspace->glyphform;
	index = form->index;
	for (i = 0; i < form->line_num; i++)
	{
	   print_line(file, form->db[index + i]);
	}

	/*
	 *  Save the variables associated with the workspace.
	 */
	(void) fprintf(file,"#  BeginVariables\n");
	id = (long) workspace->glyphform;
	(void) xve_list_variables(id, file);
	(void) fprintf(file,"#  EndVariables\n");

	/*
	 *  Save the current environment settings  associated with the
	 *  workspace.
	 */
	save_environment(workspace, file);

	/*
	 *  Get the glyph's geometry.  We want to save it's x & y
	 *  position on the screen so that we can restore it to it's
	 *  original position.
	 */
	glyphlist = glyphs;
	while (glyphlist != NULL)
	{
	   glyph = glyphlist->glyph;
	   if (glyph->machine != NULL)
	      remote_machine = glyph->machine->hostname;
	   else
	      remote_machine = NULL;

	   if (remote_machine != NULL)
	   {
	      fprintf(file,"# '%s' %d %d '%s' '%s' %d+%d\n", glyph->label_str,
			glyph->type, glyph->modified, glyph->toplevel_name,
			remote_machine, (int) glyph->xpos, (int) glyph->ypos);
	   }
	   else
	   {
	      fprintf(file,"# '%s' %d %d '%s' %d+%d\n", glyph->label_str,
			glyph->type, glyph->modified, glyph->toplevel_name,
			(int) glyph->xpos, (int) glyph->ypos);
	   }

	   if (glyph->type == PROCEDURE)
	   {
	      macro = glyph->val.macro;
	      if (save_workspace(macro, macro->glyphs, file) == False)
		 return(False);
	   }
	   glyphlist = glyphlist->next;
	}

	/*
	 *  Mark the end of the workspace.
	 */
	fprintf(file,"# EndWorkspace\n");
	return(True);
}

static void print_line(file, line)

FILE *file;
char *line;
{
	char	*temp;


	if (line == NULL)
	   return;

	while (line[0] != '\0')
	{
	   if ((temp = strchr(line,'\n')) == NULL)
	   {
	      fprintf(file,"%s\n",line);
	      return;
	   }
	   else
	   {
	      *temp = '\0';
	      fprintf(file,"%s\\n",line);
	      *temp = '\n';
	      line = temp+1;
	   }
	}
}



/************************************************************
*
*  Routine Name:  save_environment
*
*      Purpose:  Save the current environment setting information
*		 for the given workspace.
*
*	 Input:  workspace - the workspace environment.
*		 file      - the file to be written to
*
*       Output:  output the workspace's environment values to
*		 the given workspace.
*               
*   Written By:  Mark Young
*
*************************************************************/


static void save_environment(workspace, file)

Workspace *workspace;
FILE	  *file;
{
	(void) fprintf(file,"#  BeginEnvironment\n");
	(void) fprintf(file,"#    Grid %d\n",        workspace->show_grid);
	(void) fprintf(file,"#    GridSize %d\n",    workspace->grid_size);
	(void) fprintf(file,"#    SmallGlyph %d\n",  workspace->small_glyphs);
	(void) fprintf(file,"#    RubberBand %d\n",  workspace->rubberband);
	(void) fprintf(file,"#    ParallelExec %d\n", workspace->parallel);
	(void) fprintf(file,"#    Viewport %d\n",    workspace->view_port);
	(void) fprintf(file,"#    AttachCanvas %d\n", workspace->attach_canvas);
	(void) fprintf(file,"#    ShowDav %d\n",     workspace->show_dav);
	(void) fprintf(file,"#    ShowModified %d\n",workspace->show_modified);
	(void) fprintf(file,"#    EchoRoutine %d\n", workspace->echo_exec);
	(void) fprintf(file,"#    LeaveGlyph %d\n", workspace->leave_glyph);
	(void) fprintf(file,"#    DemandDriven %d\n", workspace->demand_driven);
	(void) fprintf(file,"#    ShowClipboard %d\n",
			workspace->show_clipboard);
	(void) fprintf(file,"#    InheritAttributes %d\n",
			workspace->inherit_attributes);
	(void) fprintf(file,"#    GlobalVariables %d\n",
			workspace->global_variables);
	(void) fprintf(file,"#    AutoPlacement %d\n",
			workspace->auto_placement);
	(void) fprintf(file,"#    GlyphPlacement %d\n",
			workspace->glyph_placement);
	(void) fprintf(file,"#    RemoteExec %d\n",
			workspace->remote_execution);
	(void) fprintf(file,"#    StreamExec %d\n",
			workspace->stream_execution);
	(void) fprintf(file,"#  EndEnvironment\n");
}



/************************************************************
*
*  Routine Name:  save_connection_list
*
*      Purpose:  Save the connection information for each of the
*		 glyphs into the specified file.
*
*	 Input:  
*
*       Output:  
*               
*   Written By:  Mark Young & Carla Williams
*
*************************************************************/

static int connection_num(nodelist, node)

NodeList *nodelist;
Node *node;
{
	int num = 0;

	while (nodelist != NULL)
	{
	   if (node == nodelist->node)
	      return(num);
	   else
	      num++;
	   nodelist = nodelist->next;
	}
	return(-1);
}

static int save_connection_list(glyphs, file)

GlyphList *glyphs;
FILE	  *file;
{
	int	    num;
	char	    *name;

	Workspace   *macro;
	Glyph	    *glyph;
	GlyphList   *glyphlist;
	Node	    *onode, *inode;
	NodeList    *output_list, *links;


	glyphlist = glyphs;
	while (glyphlist != NULL)
	{
	   glyph = glyphlist->glyph;
	   if (glyph->type != PROCEDURE)
	   {
	      fprintf(file,"# GlyphBegin '%s'\n", glyph->toplevel_name);
	      output_list = glyph->output_list;
	      while (output_list != NULL)
	      {
	         fprintf(file,"# ConnectionBegin\n");

	         onode = output_list->node;
	         if (onode->filename != NULL)
	         {
	            (void) fprintf(file,"   #    %s %d %d\n", onode->filename,
		         onode->temp_file, onode->selected);

	            links = onode->links;
	            while (links != NULL)
	            {
	               inode = links->node;
		       if (inode->glyph->type != PROCEDURE)
		       {
			  name = inode->glyph->toplevel_name;
			  num = connection_num(inode->glyph->input_list, inode);
	                  (void) fprintf(file,"# '%s' %d %d\n", name,
					inode->selection->index, num);
		       }
		       links = links->next;
	            }
	         }
	         output_list = output_list->next;
	         (void) fprintf(file,"# ConnectionEnd\n");
	      }
	      (void) fprintf(file,"# GlyphEnd\n");
	   }
	   else
	   {
	      macro = glyph->val.macro;
	      if (save_connection_list(macro->glyphs, file) == False)
	         return(False);
	   }
	   glyphlist = glyphlist->next;
	}
	return(True);
}



/************************************************************
*
*  Routine Name:  xvl_read_data
*
*      Purpose:   This function is used in conjunction with
*		  xvl_save_files.  xvl_read_data is used
*		  to read the data, specified in the file
*		  "filename", into a character array.
*
*	 Input:   filename -  name of file to be read
*
*       Output:   data     - the character array that contains
*			     the raw data contained in the spec-
*			     ified file.
*
*		  num_bytes - number of bytes found in the file.
*
*
*   Written By:   Mark Young
*
*************************************************************/


#define ALLOCSIZE 50000


char *xvl_read_data(filename, num_bytes)

char	*filename;
int	*num_bytes;
{
	int	fid, num, size, count;
	char	*data, temp[512];

	if ((fid = open(filename, O_RDONLY, 0444)) <= 0)
	{
	   sprintf(temp, "Unable to open temporary file %s for saving",
		   filename);
	   xvf_error_wait(temp, "xvl_read_data", NULL);
	   return(NULL);
	}

	/* allocate memory space for data */
	size =  ALLOCSIZE;
	if (!(data = (char *) malloc ((unsigned int) size)))
	{
	   (void) sprintf(temp,"Not enough memory!  Tried to malloc %d \
bytes.", size);
	   xvf_error_wait(temp, "xvl_read_data", NULL);
	   return(NULL);
	}

	/* read data and determine number of points */
	count = 0;

	do
	{
	   if (count == size)
	   {
	      size += ALLOCSIZE;
	      if (!(data = (char *) realloc(data, (unsigned) size)))
	      {
	         (void) sprintf(temp,"Not enough memory!  Tried to malloc %d \
bytes.", size);
	         xvf_error_wait(temp, "xvl_read_data", NULL);
	         return(NULL);
	      }
	   }

	   if ((num = read(fid, (char *) (data + count), (size - count))) > 0)
	   {
	      count += num;
	   }
	} while (num > 0);

	*num_bytes = count;
	return(data);
}



/************************************************************
*
*  Routine Name:  xvl_save_files
*
*      Purpose:  to save all temporary files associated with
*                a workspace
*
*	 Input:  filename under which to save temp files
*
*       Output:  1 if save was successful
*                0 if could not open file
*
*
*   Written By:   Mark Young & Carla Williams
*
*************************************************************/


xvl_save_files(glyphs, filename, compress)

GlyphList *glyphs;
char	  *filename;
int	  compress;
{
	FILE	 *file;
	time_t	 clock;
	int	 status;
	char	 workname[512], *user, *getenv();


	if (!(file = open_file(filename, &compress, workname)))
	   return;

	clock = time(NULL);
	if (!(user = getenv("USER")))
	{
	   user = xvf_strcpy("(unknown)");
	}

	fprintf(file,"# Khoros Image Processing System Workspace Temp \
Files\n#\n# cantata workspace file (%s) was created \n# on %s# by user %s\n",
			filename, ctime(&clock), user);

	/*
	 *  Loop through the temporary file list saving the temporary
	 *  files into our temporary workspace file.  We do this by
	 *  reading in the data "xvl_read_data" and then writing
	 *  the data to "filename". 
	 */
	status = save_files(glyphs, file);
	close_file(file, compress);

	if (status == False)
	   unlink(workname);
}



/************************************************************
*
*  Routine Name:  static save_files
*
*      Purpose:  Save the temporary files of the workspace
*		 into the specified file.
*
*	 Input:  
*
*       Output:  
*               
*   Written By:  Mark Young & Carla Williams
*
*************************************************************/


static int save_files(glyphs, file)

GlyphList *glyphs;
FILE	  *file;
{
	Node	  *node;
	NodeList  *links;
	Glyph     *glyph;
	GlyphList *glyphlist;

	Workspace *macro;
	int	  num_bytes;
	char	  temp[512], *data;


	/*
	 *  Go thru the glyphs looking for the temporary files.
	 */
	glyphlist = glyphs;
	while (glyphlist != NULL)
	{
	   glyph = glyphlist->glyph;

	   if (glyph->type == PROCEDURE)
	   {
	      macro = glyph->val.macro;
	      if (save_files(macro->glyphs, file) == False)
	         return(False);
	   }
	   else
	   {
	      links = glyph->output_list;
	      while (links != NULL)
	      {
	         node = links->node;
	         if (node->temp_file)
	         {
		    data = xvl_read_data(node->filename, &num_bytes);
	            if (data != NULL)
		    {
		       /*
		        *  Write out the name of the file and how many bytes
			*  are in the file.  Then write out the data returned
			*  from xvl_read_data().
		        */
		       fprintf(file, "%s %d\n", node->filename, num_bytes);

		       if (fwrite(data, 1, num_bytes, file) != num_bytes)
		       {
		          sprintf(temp, "Error! Unable to write data for saving\
 temporary files.  The workspace will be saved but not the temporary files \
associated with that workspace.");
		          xvf_error_wait(temp, "xvl_save_files", NULL);
		          free(data);
		          return(False);
		       }
		       free(data);
		    }
	         }
	         links = links->next;
	      }
	   }
	   glyphlist = glyphlist->next;
	}
        return(True);
}
