/* vu-utils.c - vu helper functions
 *
 ****************************************************************
 * Copyright (C) 2000 Thomas Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */



#include "hackerlab/os/errno.h"
#include "hackerlab/mem/must-malloc.h"
#include "hackerlab/char/str.h"
#include "hackerlab/vu/vu-utils.h"


/************************************************************************
 *(h0 "VU Utilities")
 * 
 * These functions provide higher-level abstractions that capture common
 * ways of using the lower-level VU functions.
 */



/*(c vu_file_to_string)
 * int vu_file_to_string (int * errn,
 *                        t_uchar ** buf,
 *                        size_t * len,
 *                        int fd);
 * 
 * Read the entire contents of `fd' into a newly allocated string.
 * 
 * If no error occurs, return 0.  The new string is returned in `*buf';
 * its length in `*len'.
 * 
 * If an I/O error occurs, return -1 and fill `*errn'.
 * 
 * If `vu_fstat' is able to report the length of the file, a single call to 
 * `vu_read_retry' is used to read its contents.  Otherwise, repeated calls
 * to `vu_read_retry' are used.
 */
int
vu_file_to_string (int * errn,
		   t_uchar ** buf,
		   size_t * len,
		   int fd)
{
  struct stat sb;

  if (!vu_fstat (errn, fd, &sb))
    {
      *buf = (t_uchar *)must_malloc (sb.st_size);

      if (sb.st_size != vu_read_retry (errn, fd, *buf, sb.st_size))
	{
	  must_free (*buf);
	  return -1;
	}
	  
      *len = sb.st_size;
      return 0;
    }
  else
    {
#define chunk 8192
      size_t total;

      *buf = (t_uchar *)must_malloc (chunk);
      total = 0;

      while (1)
	{
	  ssize_t amt;
      
	  amt = vu_read_retry (errn, fd, *buf + total, chunk);
	  if (amt < 0)
	    {
	      must_free (*buf);
	      return -1;
	    }
	  if (amt == 0)
	    {
	      *buf = (t_uchar *)must_realloc ((void *)*buf, total);
	      *len = total;
	      return 0;
	    }
          total += amt;
	  *buf = (t_uchar *)must_realloc ((void *)*buf, total + chunk);
	}
    }
}




/*(c vu_move_fd)
 * int vu_move_fd (int * errn, int fd, int newfd);
 * 
 * Relocate `fd' to `newfd'.  `fd' must already be handled by VU.
 * 
 * Thus function performs a `vu_dup' or `vu_dup2'
 * 
 * This is useful when performing file redirections after `fork' and
 * before `exec'.
 * 
 */
int
vu_move_fd (int * errn, int fd, int newfd)
{
  int ign;

  if (fd < 0)
    {
      if (errn) *errn = EINVAL;
      return -1;
    }
  if (fd == newfd)
    return 0;
  
  if (newfd == -1)
    newfd = vu_dup (errn, fd);
  else
    newfd = vu_dup2 (errn, fd, newfd);

  if (newfd < 0)
    return -1;

  if (0 > vu_move_state (errn, fd, newfd))
    {
      vu_close (&ign, newfd);
      return -1;
    }

  vu_close (&ign, fd);
  return newfd;
}




/*(c vu_file_is_directory)
 * int vu_file_is_directory (int * errn, const t_uchar * name);
 * 
 * Return 1 if `name' names a directory, 0 if not, -1 on error.
 * 
 * Non-existence of any file called `name' is not an error.
 */
int
vu_file_is_directory (int * errn, const t_uchar * name)
{
  int e;
  struct stat sb;

  if (0 > vu_lstat (&e, name, &sb))
    {
      if (e == ENOENT)
	return 0;
      if (errn) *errn = e;
      return -1;
    }

  return (S_ISDIR (sb.st_mode));
}


/*(c vu_file_is_directory_following)
 * int vu_file_is_directory_following (int * errn, const t_uchar * name);
 * 
 * Return 1 if `name' names a directory or symlink to a directory, 
 * 0 if not, -1 on error.
 * 
 * Non-existence of any file called `name' is not an error.
 */
int
vu_file_is_directory_following (int * errn, const t_uchar * name)
{
  int e;
  struct stat sb;

  if (0 > vu_stat (&e, name, &sb))
    {
      if (e == ENOENT)
	return 0;
      if (errn) *errn = e;
      return -1;
    }

  return (S_ISDIR (sb.st_mode));
}
