/* 
 * 
 * The following routines are useful for dealing with programs which try to
 * create hard links or rename across devices.  Note that on AFS, it is not
 * possible to have hard links across directories.
 * 
 *   link_or_copy() calls link().  If link() returns EXDEV, it
 *                  execs /bin/cp.  0 is returned on success,
 *                  -1 on failure.  If cp exits with a non-zero status,
 *                  this is considered failure.
 * 
 *   rename_or_move() calls rename().  If rename returns EXDEV, it
 *                  execs /bin/mv.  0 is returned on success,
 *                  -1 on failure.  If mv exits with a non-zero status,
 *                  this is considered failure.
 *  
 * To use, link with this file, and put the following lines
 * in your source:
 * 
 *      #define link link_or_copy
 *      #define rename rename_or_move
 * 
 *   
 *   Calvin Clark <ckclark@mit.edu>
 * 
 */

#include <stdio.h>
#include <errno.h>
#include <sys/wait.h>

#ifndef WEXITSTATUS
#define LO(s)	((int)((s)&0377))
#define HI(s)	((int)(((s)>>8)&0377))
#define WIFEXITED(s)	(LO(s) == 0)
#define WEXITSTATUS(s)	HI(s)
#endif

#define LINK_OR_COPY 0
#define RENAME_OR_MOVE 1

int
cross_device_op (name1, name2, operation)
     char *name1;
     char *name2;
     int operation; /* one of LINK_OR_COPY or RENAME_OR_MOVE */
{
  int ret;

  if (((operation == RENAME_OR_MOVE)
       ? (ret = rename (name1, name2))
       : (ret = link (name1, name2))) < 0)
    {
      if (errno == EXDEV) /* different file systems */
	{
	  int pid;
	  pid = fork ();
	  if (pid == -1) 
	    return -1;
	  if (pid == 0)
	    {
	      /* child */
	      if (operation == LINK_OR_COPY)
		execl ("/bin/cp", "cp", name1, name2, NULL);
	      else /* RENAME_OR_MOVE */
		execl ("/bin/mv", "mv", name1, name2, NULL);
	      exit (1);
	    }
	  else
	    {
	      /* parent */

	      int pid;
	      int status; 

	      pid = wait (&status);
	      
	      if (pid < 0)
		return -1;
	     
	      if ((WIFEXITED(status) != 0 && ((WEXITSTATUS(status) == 0))))
		return 0;
	      return -1;
	    }
	}
      else
	return -1;
    }
  return ret;
}

int
link_or_copy (name1, name2)
     char *name1;
     char *name2;
{
  return (cross_device_op (name1, name2, LINK_OR_COPY));
}

int
rename_or_move (name1, name2)
     char *name1;
     char *name2;
{
  return (cross_device_op (name1, name2, RENAME_OR_MOVE));
}
