/* redirlib.c (emx+gcc) -- Copyright (c) 1994-1995 by Eberhard Mattes */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "redirlib.h"

#define FALSE 0
#define TRUE  1


int redir_restore (struct redir_save *save)
{
  int i, r, e;

  r = 0; e = errno;
  for (i = 0; i < 10; ++i)
    if (save->fds[i] != -1)
      {
        if (dup2 (save->fds[i], i) != 0)
          {
            r = -1; e = errno;
          }
        close (save->fds[i]);
        save->fds[i] = -1;
      }
  errno = e;
  return r;
}



static int failure (struct redir_save *save, const char *msg, int fd_close)
{
  int e;

  e = errno;
  redir_restore (save);
  if (fd_close != -1)
    close (fd_close);
  errno = e;
  perror (msg);
  return -1;
}


static int syntax (struct redir_save *save, int fd_close)
{
  int e;

  e = errno;
  redir_restore (save);
  if (fd_close != -1)
    close (fd_close);
  errno = e;
  return 1;
}


/* Return values:

   -1   Error; an error message has been printed to stderr
   0    OK
   1    Syntax error */

int redir_redirect (struct redir_save *save, const char *s)
{
  int fd_explicit, fd_default, fd_source, fd_target, fd_open, mode;
  char fname[270];
  char f_amp;
  int i;

  for (i = 0; i < 10; ++i)
    save->fds[i] = -1;

  for (;;)
    {
      while (isspace ((unsigned char)*s))
        ++s;
      if (*s == 0)
        break;
      fd_explicit = -1; fd_open = -1;
      if (isdigit ((unsigned char)*s))
        fd_explicit = *s++ - '0';
      switch (*s)
        {
        case '<':
          ++s;
          if (*s == '>' && fd_explicit != -1)
            {
              ++s;
              mode = O_RDWR; f_amp = FALSE;
            }
          else
            {
              mode = O_RDONLY; f_amp = TRUE;
            }
          fd_default = 0;
          break;

        case '>':
          ++s;
          if (*s == '>')
            {
              ++s;
              mode = O_WRONLY|O_CREAT|O_APPEND; f_amp = FALSE;
            }
          else
            {
              mode = O_WRONLY|O_CREAT|O_TRUNC; f_amp = TRUE;
            }
          fd_default = 1;
          break;

        default:
          return syntax (save, -1);
        }
      if (fd_explicit != -1)
        fd_target = fd_explicit;
      else
        fd_target = fd_default;
      fd_source = -1;
      if (*s == '&' && f_amp)
        {
          ++s;
          if (*s == '-' && fd_explicit == -1)
            ++s;                /* fd_source = -1 */
          else if (!isdigit ((unsigned char)*s))
            return syntax (save, -1);
          else
            fd_source = *s++ - '0';
        }
      else
        {
          i = 0;
          while (*s != 0 && !isspace ((unsigned char)*s) && i < sizeof (fname))
            fname[i++] = *s++;
          if (i >= sizeof (fname))
            return syntax (save, -1);
          fname[i] = 0;
          fd_source = fd_open = open (fname, mode, S_IREAD | S_IWRITE);
          if (fd_source == -1)
            return failure (save, fname, -1);
        }

      if (*s != 0 && !isspace ((unsigned char)*s))
        return syntax (save, fd_open);

      if (save->fds[fd_target] == -1)
        {
          save->fds[fd_target] = dup (fd_target);
          if (save->fds[fd_target] == -1)
            return failure (save, "dup()", fd_open);
        }

      if (fd_source == -1)
        close (fd_target);
      else if (fd_source == fd_target)
        return syntax (save, fd_open);
      else if (dup2 (fd_source, fd_target) == -1)
        return failure (save, "dup2()", fd_open);
      if (fd_open != -1)
        close (fd_open);
    }
  return 0;
}
