#include <stdio.h>
#include <string.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/file.h>

#define BUFLEN 8192

int main (int argc,char *argv[])
{
  struct stat sbuf;
  struct utimbuf ubuf;
  size_t i,j,count;
  char *tmpfile,buf[BUFLEN];
  int tfd;
  int fd = -1;
  int ret = 1;
  if (argc != 2) fprintf (stderr,"usage: %s filename\n",
			  (argc > 0) ? argv[0] : "defrag");
  else if (((fd = open (argv[1],O_RDWR)) < 0) ||
	   flock (fd,LOCK_EX | LOCK_NB) || fstat (fd,&sbuf))
    perror (argv[1]);
  else if (!S_ISREG (sbuf.st_mode))
    fprintf (stderr,"%s: not a regular file\n",argv[1]);
  else if (!(tmpfile = (char *) malloc (strlen (argv[1] + 7))))
    fputs ("impossible malloc failure\n",stderr);
  else {
    ubuf.actime = sbuf.st_atime;
    ubuf.modtime = sbuf.st_mtime;
    sprintf (tmpfile,"%sXXXXXX",argv[1]);
    if ((tfd = mkstemp (tmpfile)) < 0)
      perror (tmpfile);
    else {			/* copy to tmpfile */
      for (count = sbuf.st_size; count; count -= i) {
	if ((i = read (fd,buf,(count > BUFLEN) ? BUFLEN : count)) < 0)
	  perror (argv[1]);
	else if ((j = write (tfd,buf,i)) < 0) perror (tmpfile);
	else if (i != j) fprintf (stderr,"partial write %lu != %lu\n",
				  (unsigned long) i,(unsigned long) j);
	else continue;
	_exit (ret);
      }
      if (fsync (tfd) || lseek (tfd,0,SEEK_SET)) perror (tmpfile);
      else if (lseek (fd,0,SEEK_SET) || ftruncate (fd)) perror (argv[1]);
      else {			/* copy back to main file */
	for (count = sbuf.st_size; count; count -= i) {
	  if ((i = read (tfd,buf,(count > BUFLEN) ? BUFLEN : count)) < 0)
	    perror (argv[1]);
	  else if ((j = write (fd,buf,i)) < 0) perror (tmpfile);
	  else if (i != j)
	    fprintf (stderr,"partial write %lu != %lu, file clobbered\n",
		     (unsigned long) i,(unsigned long) j);
	  else continue;
	  _exit (ret);
	}
	if (fsync (fd)) {	/* not good if sync fails */
	  perror (argv[1]);
	  _exit (ret);
	}
	else if (ret = (utime (argv[1],&ubuf) || flock (fd,LOCK_UN)))
	  perror (argv[1]);
      }
      if (unlink (tmpfile) || close (tfd)) perror (tmpfile);
    }
  }
  if ((fd >= 0) && close (fd)) perror (argv[1]);
  return ret;
}
