/*
 * File:	io.c
 *
 * Author:	Ulli Horlacher (framstag@rus.uni-stuttgart.de)
 *
 * History:	
 *
 *   1995-08-11 Framstag	initial version
 *   1996-04-23 Framstag	added file copying function
 *   1996-05-02 Framstag	corrected file creating bug in fcopy()
 *   1996-06-24 Framstag	enhanced fcopy() to copy to stdout
 *   1997-02-02 Framstag	improved reliability for fcopy() with NFS
 *   1997-02-24 Framstag	sprintf() -> snprintf()
 *   1997-05-05 Framstag	better IRIX support (blksize)
 *   1997-12-09	Framstag	added whereis()
 *   2001-01-10	Framstag	added rfopen()
 *   2001-02-04	Framstag	added mktmpdir() and rmtmpdir()
 *   2001-02-16	Framstag	fixed cleanup loop
 *
 * Socket read and write routines and file copy function of the
 * sendfile package.
 *
 * Copyright © 1995-2001 Ulli Horlacher
 * This file is covered by the GNU General Public License
 */

#include "config.h"		/* various #defines */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "io.h"			/* (socket) read/write */
#include "net.h"		/* the network routines */
#include "string.h"		/* extended string functions */
#include "message.h"		/* information, warning and error messages */

#ifdef NEXT
  #include <sys/uio.h>
#endif

#if defined(SOLARIS2)
  #ifndef fileno
    int fileno(FILE *);
  #endif
#endif

/*
 * readn - read n bytes from network socket
 *
 * INPUT:  fd     - socket file descriptor
 *         ptr    - empty string
 *         nbytes - number of bytes to read
 *
 * RETURN: number of actual read bytes
 *
 * this function is derived from example code from
 * "Unix Networking Programming" by W. R. Stevens
 */
int readn(int fd, char *ptr, int nbytes) {
  int nleft, nread;

  nleft=nbytes;
  while (nleft>0) {
    nread=read(fd, ptr, nleft);
    if (nread<0)
      return(nread);
    else
      if (nread==0) break;
    nleft-=nread;
    ptr+=nread;
  }
  return(nbytes-nleft);
}


/*
 * writen - write n bytes to network socket
 *
 * INPUT:  fd     - socket file descriptor
 *         ptr    - string to send
 *         nbytes - number of bytes to send
 *
 * RETURN: number of actual written bytes
 *
 * this function is derived from example code from
 * "Unix Networking Programming" by W. R. Stevens
 */
int writen(int fd, char *ptr, int nbytes) {
  int nleft, nwritten;

  nleft=nbytes;
  while (nleft>0) {
    nwritten=write(fd, ptr, nleft);
    if (nwritten<0) return(nwritten);
    nleft-=nwritten;
    ptr+=nwritten;
  }
  return(nbytes-nleft);
}


/*
 * fcopy - copy a file (copy to stdout if there is no destination filename)
 *
 * INPUT:  from     - source file
 *         to       - destination file
 *         mode     - file protection mode
 *
 * RETURN: 0 if ok, -1 on failure
 *
 */
int fcopy(const char *from, const char *to, mode_t mode) {
  int fdin, fdout;	/* file descriptor in/out */
  int 
    rbytes,		/* read bytes */
    wbytes;		/* written bytes */
  char 
    tmp[MAXLEN],	/* temporary string */
    *buf;		/* copy buffer */
  struct stat finfo;	/* information about a file */
  off_t
    fsize,		/* original file size */
    wtotal,		/* total read bytes */
    blksize;		/* file system block size */

  wtotal=0;
  
  /* get the original file size */
  if (stat(from,&finfo)<0) {
    snprintf(MAXS(tmp),"cannot access '%s'",from);
    message("",'E',tmp);
    return(-1);
  }
  fsize=finfo.st_size;
      
  /* open source file */
  fdin=open(from,O_RDONLY,0);
  if (fdin<0) {
    snprintf(MAXS(tmp),"error opening '%s'",from);
    message("",'E',tmp);
    return(-1);
  }

  /* destination file specified? */
  if (*to) {
    
    /* open destination file */
    fdout=creat(to,mode);
    if (fdout<0) {
      snprintf(MAXS(tmp),"error creating '%s'",to);
      message("",'E',tmp);
      close(fdin);
      return(-1);
    }

    /* get file system block size for copy operation */
    stat(to,&finfo);
#ifdef HAVE_ST_BLKSIZE
    blksize=finfo.st_blksize;
#else
    blksize=1024;
#endif

    /* ANSI C can not dynamicly allocate with buf[blksize] */
    buf=(char *)malloc(blksize);
    if (!buf) message("",'F',"out of memory");

    /* read until EOF */
    while ((rbytes=read(fdin,buf,blksize)) > 0) {
     
      /* write to destination file */
      wbytes=write(fdout,buf,rbytes);
      if (wbytes!=rbytes) {
       
	/* write error */
	close(fdin);
	close(fdout);
	free(buf);
	snprintf(MAXS(tmp),"error writing '%s'",to);
	message("",'E',tmp);
	return(-1);
      }

      wtotal+=wbytes;
      
    }

    close(fdout);

  }
  else /* copy to stdout */ {
    
    /* get file system block size for copy operation */
    stat(from,&finfo);
#ifdef HAVE_ST_BLKSIZE
    blksize=finfo.st_blksize;
#else
    blksize=1024;
#endif

    /* ANSI C can not dynamicly allocate with buf[blksize] */
    buf=(char *)malloc(blksize);
    if (!buf) message("",'F',"out of memory");

    /* read until EOF */
    while ((rbytes=read(fdin,buf,blksize)) > 0) {
     
      /* write to stdout */
      wbytes=write(fileno(stdout),buf,rbytes);
      wtotal+=wbytes;
    }

  }

  close(fdin);
  free(buf);

  /* read error? */
  if (rbytes<0) {
    snprintf(MAXS(tmp),"error reading '%s'",from);
    message("",'E',tmp);
    return(-1);
  }

  /* count mismatch or read/write errors? */
  if (fsize!=wtotal) {
    errno=0;
    snprintf(MAXS(tmp),"wrong byte count for '%s'",from);
    message("",'E',tmp);
    return(-1);
  }
  
  return(0);
}


/*
 * whereis  - where is a program in the path
 *
 * INPUT:  prg     - program to look for
 * 
 * RETURN: NULL if not found else path to file
 */
char *whereis(char *prg) {
  int len;
  static char filepath[MAXLEN];	/* file with path */
  char *path;			/* $PATH */
  char *cp;
  
  /* when file contains a / test directly */
  if (strchr(prg,'/')) {
    if (access(prg,X_OK)==0)
      return(prg);
    else
      return(NULL);
  }
  
  len=strlen(prg);
  path=getenv("PATH");
  if (!path || !strchr(path,'/')) return(NULL);
  
  while (*path==':') path++;
  
  while (*path) {
    snprintf(filepath,MAXLEN-2-len,"%s",path);
    if ((cp=strchr(filepath,':'))) *cp=0;
    strcat(filepath,"/");
    strcat(filepath,prg);
    if (access(filepath,X_OK)==0) return(filepath);
    if ((cp=strchr(path,':'))) 
      path=cp+1;
    else
      return(NULL);
  }
  
  return(NULL);
  
}


/*
 * rfopen  - open a regular file
 *
 * INPUT:  file     - file name (with path)
 *	   mode	    - fopen mode 
 * 
 * RETURN: same like fopen(), but NULL if file is not a regular file
 */
FILE *rfopen(const char *file, const char *mode) {
  struct stat finfo;	/* information about a file */
  
  /* what kind of file ist it and do we have access at all? */
  if (stat(file,&finfo)<0) {
    
    /* no such file ==> open it! (mode can be "w" or "a+") */
    if (errno==ENOENT) 
      return(fopen(file,mode));
    else
      /* other error */
      return(NULL);
    
  } else {
    
    /* regular file? */
    if (finfo.st_mode&S_IFREG) {
      return(fopen(file,mode));
    } else {
      errno=EIO;
      return(NULL);
    }
    
  }
}


/* 
 * mktmpdir - create a new temporary directory
 * 
 * INPUT:  verbose - flag
 * 
 * RETURN: full path of the temporary directory
 * 
 * ENVIRONMENT: SF_TMPDIR, TMPDIR
 */
char *mktmpdir(int verbose) {
  char *cp;			/* a simple string pointer */
  char tmp[MAXLEN];		/* temporary string */
  static char tmpdir[FLEN];	/* directory for temporary files */
    
  /* get tmpdir base */
  if ((cp=getenv("SF_TMPDIR")))
    snprintf(tmpdir,FLEN-30,"%s",cp);
  else if ((cp=getenv("TMPDIR")))
    snprintf(tmpdir,FLEN-30,"%s",cp);
  else
    strcpy(tmpdir,"/tmp");
  
  snprintf(tmp,30,"/sf_%u.tmp",(unsigned int)(time(NULL)*getpid()));
  strcat(tmpdir,tmp);
  
  if (mkdir(tmpdir,S_IRWXU)<0 || chmod(tmpdir,S_IRWXU)<0) {
    snprintf(MAXS(tmp),"cannot create tmpdir %s",tmpdir);
    message("",'F',tmp);
  }
  
  if (verbose) {
    snprintf(MAXS(tmp),"directory for temporary files is: %s",tmpdir);
    message("",'I',tmp);
  }
  
  return(tmpdir);
}


/* 
 * rmtmpdir - delete the temporary directory
 * 
 * INPUT: full path of the temporary directory
 */
void rmtmpdir(char *tmpdir) {
  char cwd[FLEN];		/* current directory */
  char tmp[MAXLEN];		/* temporary string */
  struct dirent *dire;          /* directory entry */
  DIR *dp;                      /* directory pointer */

  if (!tmpdir || !*tmpdir) return;
  
  if (!getcwd(MAXS(cwd))) strcpy(cwd,"/tmp");
  
  if (chdir(tmpdir) < 0) return;
  
  /* open dir */
  if (!(dp=opendir(tmpdir))) {
    snprintf(MAXS(tmp),"cleanup: cannot open %s",tmpdir);
    message("",'X',tmp);
  }
  
  while ((dire=readdir(dp))) {
      
    /* skip . and .. */
    if (strcmp(dire->d_name,".")==0 || strcmp(dire->d_name,"..")==0) continue;

    /* delete file */
    if (unlink(dire->d_name) < 0) {
      snprintf(MAXS(tmp),"cannot remove %s/%s",tmpdir);
      message("",'X',tmp);
    }
    
  }

  chdir(cwd);
  if (rmdir(tmpdir) < 0) {
    snprintf(MAXS(tmp),"cannot remove %s",tmpdir);
    message("",'X',tmp);
  }
  
}


/*
int main(int argc, char *argv[]) {
  char *filepath;
  
  filepath=whereis(argv[1]);
  
  if (filepath) {
    printf("%s\n",filepath);
    exit(0);
  } 
  
  exit(1);
  
}
*/


syntax highlighted by Code2HTML, v. 0.9.1