#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "vg4.h"

char * which_cmd(const char *);
int noshell_system(const char *, char *const[]);

char *argv0 = NULL;


/* find command from PATH, return path+file or NULL = not found */
char *
which_cmd(const char *cmd)
{
  static char pcmd[256] = "";
  const char *path, *pend;
  size_t clen, plen;

  if (cmd == NULL || *cmd == '\0') { return NULL; }
  clen = strlen(cmd);

  path = getenv("PATH");
  if (path == NULL || *path == '\0') { return NULL; }

  *pcmd = '\0';
  for (pend = strchr(path, ':');; path = pend + 1, pend = strchr(path, ':')) {
    if (pend != NULL) { plen = (size_t)(pend - path); } else { plen = strlen(path); }
    if (plen + 1 + clen >= sizeof(pcmd)) { fprintf(stderr, "path too long\n"); return NULL; }
    snprintf(pcmd, sizeof(pcmd), "%.*s/%s", (int)plen, path, cmd);
    if (access(pcmd, R_OK|X_OK) == 0) { break; }
    *pcmd = '\0';
    if (pend == NULL) { break; }
  }
  if (*pcmd == '\0') { return NULL; }

  return pcmd;
} /* Ende which_cmd */


/* like system(), but with full-path-commando and without shell */
int
noshell_system(const char *pcmd, char *const argp[])
{
  sigset_t mask, omask;
  struct sigaction sa, osa_int, osa_quit;
  pid_t pid, cpid;
  int status;

  if (pcmd == NULL || *pcmd == '\0') { return 1; }
  if (argp == NULL || *argp == NULL || **argp == '\0') { return 1; }

  sigemptyset(&mask);
  sigaddset(&mask, SIGCHLD);
  sigaddset(&mask, SIGINT);
  sigaddset(&mask, SIGQUIT);
  sigprocmask(SIG_BLOCK, &mask, &omask);

  if ((cpid = fork()) < 0) {
    sigprocmask(SIG_SETMASK, &omask, NULL);
    return -1;
  }

  if (cpid == 0) {  /* child */
    sigprocmask(SIG_SETMASK, &omask, NULL);
    execv(pcmd, argp);
    _exit(127);
  }

  /* ignore SIGINT and SIGQUIT */
  memset(&sa, 0, sizeof(sa));
  sigemptyset(&sa.sa_mask);
  sa.sa_handler = SIG_IGN;
  sigaction(SIGINT, &sa, &osa_int);
  sigaction(SIGQUIT, &sa, &osa_quit);
  sigemptyset(&mask);
  sigaddset(&mask, SIGINT);
  sigaddset(&mask, SIGQUIT);
  sigprocmask(SIG_UNBLOCK, &mask, NULL);

  /* wait for child exit */
  do {
    pid = waitpid(cpid, &status, 0);
  } while (pid == -1 && errno == EINTR);

  /* restore signals */
  sigaction(SIGINT, &osa_int, NULL);
  sigaction(SIGQUIT, &osa_quit, NULL);
  sigprocmask(SIG_SETMASK, &omask, NULL);

  if (pid < 0) { return -1; }
  if (!WIFEXITED(status)) { return -1; }
  return WEXITSTATUS(status);
} /* Ende noshell_system */


int
main(int argc, char **argv)
{
  static char *vg_no_info = "VG_NO_INFO=1";
  int retw = 0;

  if ((argv0 = strrchr(argv[0], '/')) == NULL) { argv0 = argv[0]; } else { argv0++; }
  argc--; argv++;

  putenv(vg_no_info);
  if (!VG_init("vg4")) { exit(1); }

  if (argc < 1 || strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0)  { goto help_main; }

  if (strcmp(argv[0], "font") == 0) {
    retw = cmd_font(argc, argv);
  } else if (strcmp(argv[0], "text") == 0) {
    retw = cmd_text(argc, argv);
  } else if (strcmp(argv[0], "sprite") == 0) {
    retw = cmd_sprite(argc, argv);
  } else if (strcmp(argv[0], "canvas") == 0) {
    retw = cmd_canvas(argc, argv);
  } else if (strcmp(argv[0], "film") == 0) {
    retw = cmd_film(argc, argv);
  } else {
    goto help_main;
  }

  VG_dest();
  exit(retw);

help_main:
  VG_dest();

  fprintf(stderr, "\nUsage: %s <command> [<options>]\n", argv0);
  fprintf(stderr, "Commands:\n");
  fprintf(stderr, "- font\n");
  fprintf(stderr, "- text\n");
  fprintf(stderr, "- sprite\n");
  fprintf(stderr, "- canvas\n");
  fprintf(stderr, "- film\n");
  fprintf(stderr, "(Use \"%s <command> -h|--help\" for more infos)\n", argv0);

  exit(1);
} /* Ende main */
