/*
 * Copyright 2011 Steven Gribble
 *
 *  This file is part of the UW CSE 333 course project sequence
 *  (333proj).
 *
 *  333proj is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  333proj is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with 333proj.  If not, see <http://www.gnu.org/licenses/>.
 */

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

#define READBUFSIZE 128
int main(int argc, char **argv) {
  int fd_in, fd_out;
  char readbuf[READBUFSIZE];
  ssize_t readlen, writelen, written;

  // Take the filenames from command line arguments
  if (argc != 3) {
    fprintf(stderr, "usage: ./cp_example infile outfile\n");
    return EXIT_FAILURE;  // defined in stdlib.h
  }

  // Open the input file
  while (1) {
    fd_in = open(argv[1], O_RDONLY);
    if (fd_in == -1) {
      if ((errno == EINTR) || (errno == EWOULDBLOCK)) {
        // Try again.
        continue;
      }
      // Fail.
      fprintf(stderr, "Couldn't open %s for reading: ", argv[1]);
      perror(NULL);
      exit(EXIT_FAILURE);
    }
    // The open succeeded, so break out of the loop.
    break;
  }

  // Open the output file
  while (1) {
    fd_out = open(argv[2],
                  O_WRONLY | O_TRUNC | O_CREAT,
                  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (fd_out == -1) {
      if ((errno == EINTR) || (errno == EWOULDBLOCK)) {
        // Try again.
        continue;
      }
      // Fail.
      fprintf(stderr, "Couldn't open %s for writing: ", argv[2]);
      perror(NULL);
      exit(EXIT_FAILURE);
    }
    // The open succeeded, so break out of the loop.
    break;
  }

  // Do the copy.
  while (1) {
    // Try to read READBUFSIZE bytes from the input file.
    readlen = read(fd_in,
                   (void *) readbuf,
                   (size_t) READBUFSIZE);
    if (readlen == 0) {
      // we hit the end of file
      break;
    } else if (readlen == -1) {
      if ((errno == EINTR) ||
          (errno == EWOULDBLOCK) ||
          (errno == EAGAIN))
        continue;
      perror("read failed");
      exit(EXIT_FAILURE);
    }

    // Try to write readlen bytes to the output file.
    written = 0;
    while(readlen > 0) {
      writelen = write(fd_out,
                       (void *) (readbuf + written),
                       (size_t) readlen);
      if (writelen == -1) {
        if ((errno == EINTR) ||
            (errno == EWOULDBLOCK) ||
            (errno == EAGAIN))
          continue;

        perror("write failed");
        exit(EXIT_FAILURE);
      }
      written += writelen;
      readlen -= writelen;
    }
  }

  // Done!
  close(fd_in);
  close(fd_out);
  return EXIT_SUCCESS;  // defined in stdlib.h
}