partcp.c 3.43 KB
#include <errno.h> // errno
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h> // stat()

#define MiB (1<<20)

// copy data using chunks, MiB
#define CHUNK_SIZE 4


void err (const char* s);

// copy SIZE bytes from SRCFD to DSTFD using buffer BUF
int fcopy (void* buf, size_t size, FILE* srcfd, FILE* dstfd);

off_t fsize (const char* path); // get file size
void perr (const char* s);
long stol (const char* s); // strtol() wrapper


int main (int argc, char** argv)
{
  FILE *srcfd, *dstfd;
  char *srcpath, *dstpath;
  off_t srcsize; // source file size
  long off, len; // slice offset and length
  int status;

  if (argc != 5)
    err ("Usage: partcp source_file slice_offset slice_length destination_file");

  srcpath = argv[1];
  dstpath = argv[4];
  if (strcmp (srcpath, dstpath) == 0)
    err ("SOURCE and DESTINATION must be different files");

  srcsize = fsize (srcpath);
  if (srcsize == 0) err ("Nothing to copy: source file is empty");

  off = stol (argv[2]);
  if (off < 0 || off > (srcsize-1)) err ("Wrong OFFSET for source file");

  len = stol (argv[3]);
  if (len < 1 || len > (srcsize-off)) err ("Wrong LENGTH for source file");

  srcfd = fopen (srcpath, "r");
  if (srcfd == NULL) perr ("Error opening source file");

  status = EXIT_FAILURE;

  dstfd = fopen (dstpath, "w");
  if (dstfd == NULL) {
    perror ("Error opening destination file");
    goto close_src;
  }

  if (fseek (srcfd, off, SEEK_SET) == -1) {
    perror ("Error seeking source file");
    goto close_dst;
  }

  /* copy LEN bytes using chunks and remainder:
     len = num * (CHUNK_SIZE * MiB) + rem */
  {
    void* buf; // memory buffer
    size_t
      size, // buf size
      num, // number of chunks to copy
      rem, // length of remainder
      i;

    size = CHUNK_SIZE * MiB;
    num = len / size;

    if (num == 0) size = rem = len; // if (len<CHUNK_SIZE) buf=malloc(len)
    else rem = len % size; // else buf=malloc(CHUNK_SIZE)

    buf = malloc (size);
    if (buf == NULL) {
      perror ("Memory allocation error");
      goto close_dst;
    }

    // copy NUM chunks
    for (i=0; i<num; i++)
      if (fcopy (buf, size, srcfd, dstfd) == -1)
        goto free_buf;

    // copy remainder
    if (rem != 0)
      if (fcopy (buf, rem, srcfd, dstfd) == -1)
        goto free_buf;

    // copying is complete
    status = EXIT_SUCCESS;
    puts ("Success");

  free_buf:
    free (buf);
  }

close_dst:
  if (fclose (dstfd) == EOF) perror ("Error closing destination file");

close_src:
  if (fclose (srcfd) == EOF) perror ("Error closing source file");

  exit (status);
}

void err (const char* s)
{
  fprintf (stderr, "%s\n", s);
  exit (EXIT_FAILURE);
}

int fcopy (void* buf, size_t size, FILE* srcfd, FILE* dstfd)
{
  if (fread (buf, size, 1, srcfd) == 0) {
    if (ferror (srcfd)) perror ("Error reading source file");
    else fputs ("Unexpected end of source file\n", stderr); // should not happen
    return -1;
  }

  if (fwrite (buf, size, 1, dstfd) == 0) {
    perror ("Error writing destination file");
    return -1;
  }

  return 0;
}

off_t fsize (const char* path)
{
  struct stat s;
  if (stat (path, &s) == -1) perr ("Unable to get source file size");
  return s.st_size;
}

void perr (const char* s)
{
  perror (s);
  exit (EXIT_FAILURE);
}

long stol (const char* s)
{
  long ret;
  errno = 0;
  ret = strtol (s, NULL, 0); // can also read hex (0x...), octal (0...) values
  if (errno) perr ("Unable to read command line parameter");
  return ret;
}