Listing 1

#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, OL, 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;
   }

/* End of File */