/*********************************************************************
 *
 *            EZFM---EZWGL based file manager for X11
 *
 *             Copyright (C) 1998  Maorong Zou
 *
 *
 *  This file is part of EZFM.
 *
 *  EZFM is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * EZFM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with EZFM; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 *************************************************************************/



/***************************************************************************
 *
 * Routines that do the work of encoding different targets about the 
 * drag src given file info. These routines are not directly registered 
 * to any DND objects. They are invoked by object DND encoders.  
 */
#define _DNDA_C_
#include "ezfm.h"

int  finfoEncodeFileName  MY_ANSIARGS((FileInfo *finfo, char **message_ret, 
				       int *length_ret, int *needFree));
int  finfoEncodeFileContent MY_ANSIARGS((FileInfo *finfo, char **message_ret,
					 int *length_ret, int *needFree));
int  finfoEncodeTaredFileContent MY_ANSIARGS((FileInfo *finfo, char **message_ret,
					      int *length_ret, int *needFree));
int  finfoEncodeFileInfo    MY_ANSIARGS((FileInfo *finfo, char **message_ret,
					 int *length_ret, int *needFree));

void finfoEncodeStatInfoAndPathname MY_ANSIARGS((char *pathname, struct stat *statInfo,
						 char *dest));
int  decodeStatInfoAndPathname MY_ANSIARGS((char *str, char **path, unsigned long *dev, 
					    unsigned long *ino, unsigned long *mode,
					    unsigned long *size));

int canSeeTheFile MY_ANSIARGS((char *path, char *info));

/****************************************************************************
 *
 * Remark: we pre-append the full pathname and some stat info to all encoded
 *         messages. These info is recorded in the first 256 byte of the
 *         encoded message. The first 80 bytes encodes the stat info, the 
 *         later 176 bytes is the pathname. (if pathname > 176, give up !)
 *
 ****************************************************************************/

/****************************************************************************
 *
 *  This is how pathname and stat info are encoded and decoded.
 */
void finfoEncodeStatInfoAndPathname(pathname, stat_buf, dest)
     char *pathname, *dest; struct stat *stat_buf;
{
  sprintf(dest, "%lx %lx %lx %lx", 
	  (unsigned long) stat_buf->st_dev,   /* device no       */
	  (unsigned long) stat_buf->st_ino,   /* inode no        */
	  (unsigned long) stat_buf->st_mode,  /* protection mode */		  
	  (unsigned long) stat_buf->st_size); /* size in bytes   */		  
  strcpy(dest+PATHNAME_OFFSET, pathname);  
}

int decodeStatInfoAndPathname(str, path, dev, ino, mode, size)
     char *str, **path; unsigned long *dev, *ino, *mode, *size;
{
  *path = str+PATHNAME_OFFSET;
  return(sscanf(str, "%lx %lx %lx %lx", dev, ino, mode, size));
}

/****************************************************************************
 *
 * Test if 'path' is a local file. We look at the pathname and the stat
 * info. return true if all match.
 */
int canSeeTheFile(path, message)
     char *path; char *message;
{
  struct stat stat_buf;
  unsigned long dev, ino, mode, size;
  char *filename;

  if((stat(path, &stat_buf) < 0)) return(0);
  if(decodeStatInfoAndPathname(message, &filename, &dev, &ino, &mode, &size) != 4 ||
     dev  != (unsigned long)stat_buf.st_dev ||
     ino  != (unsigned long)stat_buf.st_ino ||
     mode != (unsigned long)stat_buf.st_mode ||
     size != (unsigned long)stat_buf.st_size ||
     strcmp(filename, path)) return(0);
  return((int)mode);
}

/****************************************************************************
 *
 *  encode the pathname of file given finfo
 */
int finfoEncodeFileName(finfo, message_ret, length_ret, needFree)
     FileInfo *finfo; char **message_ret; int *length_ret; int *needFree;
{
  if(finfo)
    {
      char          *pathname = finfo->pathname;
      struct stat   stat_buf;
      int           len = strlen(pathname);

      if(len >= 175) return(EZ_DND_FAILURE);  
      if((stat(pathname, &stat_buf) >= 0))
	{
	  char          *msg;	  
	  int           total_length = 257;
	  if((msg = (char *)EZ_Malloc( total_length * sizeof(char))) == NULL)
	    return(EZ_DND_FAILURE);  

	  finfoEncodeStatInfoAndPathname(pathname, &stat_buf, msg);
#ifdef EZFM_DEBUG
	  fprintf(stderr, "`\t\tfinfoEncodeFileName [%s]\n",pathname);
#endif
	  *length_ret = 256;
	  *message_ret = msg;
	  *needFree = 1;
	  return(EZ_DND_SUCCESS);
	}
    }
  return(EZ_DND_FAILURE);  
}
/****************************************************************************
 *
 *  encode the content of the file given finfo
 */
int finfoEncodeFileContent(finfo, message_ret, length_ret, needFree)
     FileInfo *finfo; char **message_ret; int *length_ret; int *needFree;
{
  if(finfo)
    {
      char          *pathname = finfo->pathname;
      char          tarcmd[512];
      FILE          *fp;
      struct stat   stat_buf;
      
      if(strlen(pathname) >= 176) return(EZ_DND_FAILURE);  
      
      if((stat(pathname, &stat_buf) >= 0))
	{
	  /* if is directory or cannot read, stop */
	  if( S_ISDIR (stat_buf.st_mode) || access(pathname, R_OK)< 0)
	    return(EZ_DND_FAILURE);
	  else
	    {
	      size_t   totallength = (size_t)stat_buf.st_size;
	      int      readlength  = 0;
	      int      fd;
	      char     *msg;

	      msg  =  (char *)EZ_Malloc( (totallength + 257)*sizeof(char));
	      if(msg == NULL) return(EZ_DND_FAILURE);
	      if( (fd = open(pathname,O_RDONLY)) >= 0 )
		{
		  readlength = read(fd, msg + 256, totallength);
		  close(fd);
		}
	      if(readlength != totallength)
		{
		  EZ_Free((void *)msg);
		  return(EZ_DND_FAILURE);
		}
	      /* pre-append the stat info and pathname */
	      finfoEncodeStatInfoAndPathname(pathname, &stat_buf, msg);	      
#ifdef EZFM_DEBUG
	      fprintf(stderr,"\t\tfinfoEncodeFileContent [%s]\n",pathname);
#endif
	      *message_ret = msg;
	      *length_ret  = totallength + 256;
	      *needFree    = 1;
	      return(EZ_DND_SUCCESS);
	    }
	}
    }
  return(EZ_DND_FAILURE);
}

/****************************************************************************
 *
 *  tar up a file given finfo
 */

int finfoEncodeTaredFileContent(finfo, message_ret, length_ret, needFree)
     FileInfo *finfo; char **message_ret; int *length_ret; int *needFree;
{
  if(finfo)
    {
      char          *pathname = finfo->pathname;
      char          tarcmd[512];
      FILE          *fp;
      struct stat   stat_buf;
      
      if(strlen(pathname) >= 176) return(EZ_DND_FAILURE);  
      
      if((stat(pathname, &stat_buf) >= 0))
	{
	  char tpath[256], *ptr;
	  strcpy(tpath,pathname);
	  ptr = strrchr(tpath, '/');
	  if(ptr == NULL) return(EZ_DND_FAILURE);  
	  *ptr++ = '\0';
	  if(chdir(tpath) >= 0)
	    {
	      sprintf(tarcmd, TAR_CREATE_CMD, ptr);
	      if((fp = popen(tarcmd, "r")) != NULL)
		{
		  char          *msg, *ptr;
		  int           c, totalLength;	  
		  int           allocatedLength = 2000000; /* 2Mb */
	      
		  msg = (char *)malloc( allocatedLength * sizeof(char));
		  if(msg == NULL)
		    {
		      pclose(fp);
		      return(EZ_DND_FAILURE);
		    }
		  ptr = msg + 256;
		  totalLength = 256;
		  while( (c = fread(ptr, 1, 256, fp)) > 0)
		    {
		      ptr += c;
		      totalLength += c;
		      if(totalLength >= allocatedLength - 257)
			{
			  allocatedLength = allocatedLength << 1;
			  msg = (char *)EZ_Realloc(msg, allocatedLength * sizeof(char));
			  if(msg == NULL)
			    {
			      pclose(fp);
			      return(EZ_DND_FAILURE);
			    }
			  ptr = msg + totalLength;
			}
		    }
		  pclose(fp);
		  /* pre-append the stat info and pathname */	      
		  finfoEncodeStatInfoAndPathname(pathname, &stat_buf, msg);	      
#ifdef EZFM_DEBUG
		  fprintf(stderr, "\t\tfinfoEncodeTaredFileContent [%s], read %d bytes\n",
			  tarcmd, totalLength-256);
#endif

		  *length_ret = totalLength;
		  *message_ret = msg;
		  *needFree = 1;
		  return(EZ_DND_SUCCESS);
		}
	    }
	}
    }
  return(EZ_DND_FAILURE);
}

/***************************************************************************
 *
 *   Encode complete stat info
 */
int finfoEncodeFileInfo(finfo, message_ret, length_ret, needFree)
     FileInfo *finfo; char **message_ret; int *length_ret; int *needFree;
{
  if(finfo)
    {
      char *pathname = finfo->pathname;
      char *msg, tbuf[400];
      int  infolen;
      if(strlen(pathname) >= 176) return(EZ_DND_FAILURE);  
      
      infolen = getFileInfo(pathname, tbuf, NULL);
      if(infolen >= 90)
	{
	  struct stat stat_buf;
	  if(lstat(pathname, &stat_buf) >= 0)
	    {
	      msg  =  (char *)EZ_Malloc( (infolen + HEADER_OFFSET+1)*sizeof(char));
	      if(msg == NULL) return(EZ_DND_FAILURE);
	      memcpy((void *)(msg+HEADER_OFFSET), (void *)tbuf, infolen);
	      /* pre-append the stat info and pathname */
	      finfoEncodeStatInfoAndPathname(pathname, &stat_buf, msg); 
	      *length_ret = infolen + HEADER_OFFSET+1;
	      *message_ret = msg;
	      *needFree = 1;
	      return(EZ_DND_SUCCESS);
	    }
	}
    }
  return(EZ_DND_FAILURE);
}
/***************************************************************************/
#undef _DNDA_C_
