/*
 * CPSC411 Assignment 2
 * Anthony Roberts <acrobert@gmail.ca>
 *
 * please see README
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "hash_table.h"
#include "token.h"
#include "fatal_error.h"
#include "parser.h"
#include "scanner.h"
#include "callcheck.h"
#include "wrappers.h"

#include <errno.h>

#define FIRST_ARG 0

#define PIPE_READER 0
#define PIPE_WRITER 1

#define DESIRED_ARGS 2
#define MYNAME 0
#define ARG_INFILE 1

#define NO_ERROR 0


/*
 * the name the program was invoked by
 *
 * my error handling routines make use of this to output the program name
 */
char *myname;

/*
 * The one argument should be the input file name.
 */
int main (int argc, char **argv) {
  int ret;
  struct stat sb;
  pid_t fork_ret;
  int pipe_fd[2];
  FILE *scanner_in, *scanner_out;
  FILE *parser_in, *parser_out;

  myname = argv[MYNAME];

  if (argc != DESIRED_ARGS && argc != 1) {
    fprintf(stderr, "usage: a2 [filename]\n");
    exit(EXIT_FAILURE);
  }

  if (argc == 1) {
    /*
     * input file is stdin
     */
    scanner_in = stdin;
  }
  else {
    /*
     * test for file existence and read permissions
     */
    ret = access(argv[ARG_INFILE], F_OK | R_OK);
    scallcheck(ret == 0, "error: cannot open the specified file");

    /*
     * fopen won't fail if the argument is a directory
     */
    ret = stat(argv[ARG_INFILE], &sb);
    if (sb.st_mode & S_IFDIR) {
      fatal_directory();
    }

    /*
     * opens the input file
     */
    scanner_in = fopen(argv[ARG_INFILE], "r");
    callcheck(scanner_in != NULL);
  }

  /*
   * creates the pipe that the scanner uses to send tokens to the parser
   */
  ret = pipe(pipe_fd);
  callcheck(ret == NO_ERROR);

  /*
   * opens streams with the pipe file descriptors
   */
  scanner_out = fdopen(pipe_fd[PIPE_WRITER], "w");
  callcheck(scanner_out != NULL);
  parser_in = fdopen(pipe_fd[PIPE_READER], "r");
  callcheck(parser_in != NULL);

  /*
   * The stream open functions can cause illegal seeks on pipes. This should be
   * ignored because the functions handle this internally, and don't return
   * with an error.
   */
  errno = 0;
  
  parser_out = stdout;

  fork_ret = fork();
  scallcheck(fork_ret >= 0, "couldn't fork a scanner process");

  if (fork_ret) {
    /*
     * the parser is the child process
     */
    parser(parser_in, parser_out);
  }
  else {
    /*
     * the scanner is the parent process
     */
    scanner(scanner_in, scanner_out);

    wait(&ret);

    if (ret != EXIT_SUCCESS) {
      /*
       * it is assumed the parser will print an error message if it's the one
       * that terminated with an error
       */
      exit(EXIT_FAILURE);
    }
  }

  exit(EXIT_SUCCESS);
}

