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

static int copy_files ();
static long file_len ();
static int do_copy ();
long atol ();

#define SIZE_BUFFER 1024
#define OK 0
#define NOT_OK -1
#define min(a,b)  ((a)<(b)?a:b)
#define IFTESTING
#ifdef IFTESTING
/*
 * To test the code compile with parameter -DTESTING and execute
 * a.out filename newlength
 */
 
main (int argc, char **argv)
   {
     printf ("%2d", truncate_file (argv[1], atol (argv[2])));
   }
#endif

truncate_file(char *path, long len)
   {
     int infile, tmp_file;    /* input file */
     int ret = 0;
     char tf_name[L_tmpnam];
       if (len < 0)
           ret = -10;
       else if (len == 0)
           ret = truncate_to_zero (path);
       else if ((infile = open(path, O_RDONLY, 0)) < 0)
           ret = -1;
       else if (file_len (infile) <= len)
           {
           close (infile);
           ret = -3;
           }
       else
           {
           tmpnam (tf_name);
           tmp_file = open (tf_name, O_RDWR | O_CREAT, 0666);
           ret = copy_file (infile, tmp_file, path, len);
           unlink (tf_name);
           }
       return ret;
   }
/*
 * file_len () returns the length of file, in bytes
 */

static long file_len (infile)
   {
   struct stat file_status;

   fstat (infile, &file_status);
   return file_status.st_size;
   }

 /*
 * copy_file () copies in_file to tmp_file then back to 
 * in_file, truncating to len bytes in the process
 */

copy_file (int infile, int tmp_file, char *path, long len)
    {
     int ret = 0;
     if ((ret = do_copy (infile, tmp_file, len)) == 0)
          {
          close (infile);
          lseek (tmp_file, 0L, SEEK_SET);
          if (( infile = open (path, O_WRONLY | O_TRUNC, 0666)) < 0)
               ret = -12;
          else
               ret = do_copy (tmp_file, infile, len);
          }
     return ret;
   }
/*
 * do_copy () copies len bytes from infile to tmp_file
 */
static int do_copy (int infile, int tmp_file, long len)
   {
     int to_read, to_write, ret = 0;
     char buffer [SIZE_BUFFER];

     do
          {
          to_read = (int) (min ((long) SIZE_BUFFER, len)); 
          if ((to_write = read (infile, buffer, to_read)) < to_read)
               {
               ret = -4;
               break;
               }
          if (write (tmp_file, buffer, to_write) < to_write) 
               {
               ret = -5;
               break;
               }
         } while (len -= to_write);
     return ret;
   }
/*
 * truncate_to_zero () opens the file *path with the 
 * option O_Trunc.
 */
truncate_to_zero (char *path)
   {
     int ret = OK;
     int fd = open (path, O_WRONLY | O_TRUNC, 0666);

     if (fd < 0)
          ret = NOT_OK;
     close (fd);
     return ret;
   }

