/*
 *----------------------------------------------------------------------
 *           "THE BEER-WARE LICENSE" (Revision 42):
 * <jeang3nie@HitchHiker-Linux.org> wrote this file. As long as you
 * retain this notice you can do whatever you want with this stuff. If
 * we meet some day, and you think this stuff is worth it, you can buy
 * me a beer in return.
 * ---------------------------------------------------------------------
 *            ______   _______  _          _________
 *           (  __  \ (  ___  )( (    /|( )\__   __/
 *           | (  \  )| (   ) ||  \  ( ||/    ) (
 *           | |   ) || |   | ||   \ | |      | |
 *           | |   | || |   | || (\ \) |      | |
 *           | |   ) || |   | || | \   |      | |
 *           | (__/  )| (___) || )  \  |      | |
 *           (______/ (_______)|/    )_)      )_(
 *
 *         _______  _______  _       _________ _______
 *        (  ____ )(  ___  )( \    /|\__   __/(  ____ \
 *        | (    )|| (   ) ||  \  ( |   ) (   | (    |/
 *        | (____)|| (___) ||   \ | |   | |   | |
 *        |  _____)|  ___  || (\ \) |   | |   | |
 *        | (      | (   ) || | \   |   | |   | |
 *        | )      | )   ( || )  \  |___) (___| (____|\
 *        |/       |/     \||/    \_)\_______/(_______/
 *
 */

#define _GNU_SOURCE   // getline, getopt

#include <ctype.h>    // isprint
#include <err.h>      // err, perror
#include <errno.h>    // errno
#include <inttypes.h> // uint*_t
#include <libgen.h>   // basename
#include <stdio.h>    // fclose, fopen, getline, nread, perror, printf
#include <stdlib.h>   // exit
#include <string.h>   // strcmp
#include <unistd.h>   // getopt

uint8_t cflag = 0, nflag = 0, qflag = 0, vflag = 0;
int lines = 10, j = 0, chars = 0;
static const char *__progname;

static void usage() {
  fprintf(stderr, "Usage: %s [-hqv] [-c count] [-n lines] [FILE]...\n", __progname);
}

void _head(char *file) {
  FILE *fp;
  char *line = NULL;
  size_t len = 0;
  ssize_t nread;
  int i = 0;
  if (strcmp(file, "-") == 0)
    fp = stdin;
  else
    fp = fopen(file, "r");
  if (fp == NULL) {
    perror("fopen");
    exit(EXIT_FAILURE);
  }
  if (vflag) {
    if (j)
      printf("\n==> %s <==\n", file);
    else
      printf("==> %s <==\n", file);
  }
  if (cflag && !nflag) {
    while (!feof(fp)) {
      putchar(fgetc(fp));
      i++;
      if (i >= chars)
        break;
    }
  } else {
    while ((nread = getline(&line, &len, fp)) != -1) {
      printf("%s", line);
      i++;
      if (i >= lines)
        break;
    }
    free(line);
  }
  if (strcmp(file, "-") != 0) {
    fclose(fp);
    j = 1;
  }
}

long parselong(char *buf) {
  errno = 0;
  char *endptr;
  int num = strtol(optarg, &endptr, 10);
  if (errno != 0) {
        perror("Error parsing integer from input: ");
        exit(EXIT_FAILURE);
  } else if (num == 0 && endptr == optarg) {
        fprintf(stderr, "Error: invalid input: %s\n", optarg);
        exit(EXIT_FAILURE);
  } else if (*endptr != '\0') {
        fprintf(stderr, "Error: invalid input: %s\n", optarg);
        exit(EXIT_FAILURE);
  }
  return num;
}

int main(int argc, char *argv[]) {
  int c, index;
  __progname = basename(argv[0]);

  /* Handle the old school -[0-9] style options */
  if ((argv[1])[0] == '-' && isdigit((argv[1])[1])) {
    char *x;
    x = argv[1] + 1;
    int i;
    for (i = 0; i < sizeof(argv[1]); i++) {
      if (!isdigit(x[i])) {
        x[i] = '\0';
        break;
      }
    }
    lines = (int)strtol(x, NULL, 10);
  }

  while ((c = getopt(argc, argv, "c:hqvn:0123456789")) != -1)
    switch (c) {
    case 'c':
      cflag = 1;
      chars = (int)parselong(optarg);
      break;
    case 'q':
      qflag = 1;
      vflag = 0;
      break;
    case 'v':
      if (!qflag)
        vflag = 1;
      break;
    case 'h':
      usage();
      exit(EXIT_SUCCESS);
    case 'n':
      nflag = 1;
      lines = (int)parselong(optarg);
      break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      break;
    default:
      usage();
      exit(EXIT_FAILURE);
    }

  if (argv[optind] == NULL)
    _head("-");

  if (argv[optind + 1] != NULL && (!qflag))
    vflag = 1;

  for (index = optind; index < argc; index++) {
    _head(argv[index]);
  }
  return 0;
}
