/* Remote connection server.
   Copyright (C) 1994 Free Software Foundation, Inc.

   This file is part of GNU tar.

   GNU tar 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 2, or (at your option)
   any later version.

   GNU tar 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 GNU tar; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Copyright (c) 1983 Regents of the University of California.  All
   rights reserved.
   
   Redistribution and use in source and binary forms are permitted
   provided that the above copyright notice and this paragraph are
   duplicated in all such forms and that any documentation, advertising
   materials, and other materials related to such distribution and use
   acknowledge that the software was developed by the University of
   California, Berkeley.  The name of the University may not be used to
   endorse or promote products derived from this software without
   specific prior written permission.  THIS SOFTWARE IS PROVIDED ``AS
   IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
   LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
   A PARTICULAR PURPOSE.  */

#include "system.h"

#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#include <sgtty.h>
#include <sys/socket.h>

#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif

int tape = -1;

char *record = 0;
int maxrecsize = -1;

#define	STRING_SIZE	64
char device[STRING_SIZE];
char count[STRING_SIZE], mode[STRING_SIZE], pos[STRING_SIZE], op[STRING_SIZE];

extern char *sys_errlist[];
char resp[BUFSIZ];

FILE *debug;

#define	DEBUG(File) \
  if (debug) fprintf(debug, File)
#define	DEBUG1(File, Arg) \
  if (debug) fprintf(debug, File, Arg)
#define	DEBUG2(File, Arg1, Arg2) \
  if (debug) fprintf(debug, File, Arg1, Arg2)

/*---.
| ?  |
`---*/

static void
string_error (const char *string)
{
  DEBUG1 ("rmtd: E 0 (%s)\n", string);
  sprintf (resp, "E0\n%s\n", string);
  write (1, resp, strlen (resp));
}

/*---.
| ?  |
`---*/

static void
numeric_error (int num)
{
  DEBUG2 ("rmtd: E %d (%s)\n", num, sys_errlist[num]);
  sprintf (resp, "E%d\n%s\n", num, sys_errlist[num]);
  write (1, resp, strlen (resp));
}

/*---.
| ?  |
`---*/

static void
getstring (char *bp)
{
  int i;
  char *cp = bp;

  for (i = 0; i < STRING_SIZE; i++)
    {
      if (read (0, cp + i, 1) != 1)
	exit (EXIT_SUCCESS);

      if (cp[i] == '\n')
	break;
    }
  cp[i] = '\0';
}

/*---.
| ?  |
`---*/

static void
checkbuf (int size)
{
  if (size <= maxrecsize)
    return;
  if (record)
    free (record);
  record = malloc ((size_t) size);
  if (record == 0)
    {
      DEBUG (_("rmtd: Cannot allocate buffer space\n"));
      string_error (_("Cannot allocate buffer space"));
      exit (EXIT_FAILURE);      /* status used to be 4 */
    }
  maxrecsize = size;
#ifdef SO_RCVBUF
  while (size > 1024 &&
   setsockopt (0, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof (size)) < 0)
    size -= 1024;
#else
  size = 1 + ((size - 1) % 1024);
#endif
}

/*---.
| ?  |
`---*/

const char *program_name;

int
main (int argc, char *const *argv)
{
  int rval;
  char c;
  int n, i, cc;

  program_name = argv[0];
  setlocale (LC_ALL, "");

  argc--, argv++;
  if (argc > 0)
    {
      debug = fopen (*argv, "w");
      if (debug == 0)
	{
	  numeric_error (errno);
	  exit (EXIT_FAILURE);
	}
      setbuf (debug, (char *) 0);
    }

top:
  errno = 0;
  rval = 0;
  if (read (0, &c, 1) != 1)
    exit (EXIT_SUCCESS);

  switch (c)
    {

    case 'O':
      if (tape >= 0)
	close (tape);
      getstring (device);
      getstring (mode);
      DEBUG2 ("rmtd: O %s %s\n", device, mode);

#if defined (i386) && defined (AIX)

      /* This is alleged to fix a byte ordering problem.  I'm quite
	 suspicious if it's right. -- mib.  */

      {
	int oflag = atoi (mode);
	int nflag = 0;

	if ((oflag & 3) == 0)
	  nflag |= O_RDONLY;
	if (oflag & 1)
	  nflag |= O_WRONLY;
	if (oflag & 2)
	  nflag |= O_RDWR;
	if (oflag & 0x0008)
	  nflag |= O_APPEND;
	if (oflag & 0x0200)
	  nflag |= O_CREAT;
	if (oflag & 0x0400)
	  nflag |= O_TRUNC;
	if (oflag & 0x0800)
	  nflag |= O_EXCL;
	tape = open (device, nflag, 0666);
      }
#else
      tape = open (device, atoi (mode), 0666);
#endif
      if (tape < 0)
	goto ioerror;
      goto respond;

    case 'C':
      DEBUG ("rmtd: C\n");
      getstring (device);	/* discard */
      if (close (tape) < 0)
	goto ioerror;
      tape = -1;
      goto respond;

    case 'L':
      getstring (count);
      getstring (pos);
      DEBUG2 ("rmtd: L %s %s\n", count, pos);
      rval = lseek (tape, (off_t) atol (count), atoi (pos));
      if (rval < 0)
	goto ioerror;
      goto respond;

    case 'W':
      getstring (count);
      n = atoi (count);
      DEBUG1 ("rmtd: W %s\n", count);
      checkbuf (n);
      for (i = 0; i < n; i += cc)
	{
	  cc = read (0, &record[i], n - i);
	  if (cc <= 0)
	    {
	      DEBUG (_("rmtd: Premature eof\n"));
	      string_error (_("Premature end of file"));
	      exit (EXIT_FAILURE); /* status used to be 2 */
	    }
	}
      rval = write (tape, record, n);
      if (rval < 0)
	goto ioerror;
      goto respond;

    case 'R':
      getstring (count);
      DEBUG1 ("rmtd: R %s\n", count);
      n = atoi (count);
      checkbuf (n);
      rval = read (tape, record, n);
      if (rval < 0)
	goto ioerror;
      sprintf (resp, "A%d\n", rval);
      write (1, resp, strlen (resp));
      write (1, record, rval);
      goto top;

    case 'I':
      getstring (op);
      getstring (count);
      DEBUG2 ("rmtd: I %s %s\n", op, count);
#ifdef MTIOCTOP
      {
	struct mtop mtop;

	mtop.mt_op = atoi (op);
	mtop.mt_count = atoi (count);
	if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
	  goto ioerror;
	rval = mtop.mt_count;
      }
#endif
      goto respond;

    case 'S':			/* status */
      DEBUG ("rmtd: S\n");
      {
#ifdef MTIOCGET
	struct mtget mtget;

	if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
	  goto ioerror;
	rval = sizeof (mtget);
	sprintf (resp, "A%d\n", rval);
	write (1, resp, strlen (resp));
	write (1, (char *) &mtget, sizeof (mtget));
#endif
	goto top;
      }

    default:
      DEBUG1 (_("rmtd: Garbage command %c\n"), c);
      string_error (_("Garbage command"));
      exit (EXIT_FAILURE);	/* status used to be 3 */
    }

respond:
  DEBUG1 ("rmtd: A %d\n", rval);
  sprintf (resp, "A%d\n", rval);
  write (1, resp, strlen (resp));
  goto top;

ioerror:
  numeric_error (errno);
  goto top;
}
