#include "config.h"
#ifndef NON_UNIX_STDIO
#define _INCLUDE_POSIX_SOURCE	/* for HP-UX */
#define _INCLUDE_XOPEN_SOURCE	/* for HP-UX */
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "f2c.h"
#undef abs
#undef min
#undef max
#include <stdlib.h>
#include "fio.h"
#include "fmt.h"		/* for struct syl */

/*global definitions*/
unit f__units[MXUNIT];		/*unit table */
int f__init;			/*bit 0: set after initializations;
				   bit 1: set during I/O involving returns to
				   caller of library (or calls to user code) */
cilist *f__elist;		/*active external io list */
icilist *f__svic;		/*active internal io list */
flag f__reading;		/*1 if reading, 0 if writing */
flag f__cplus, f__cblank;
char *f__fmtbuf;
int f__fmtlen;
flag f__external;		/*1 if external io, 0 if internal */
int (*f__getn) (void);		/* for formatted input */
void (*f__putn) (int);		/* for formatted output */
int (*f__doed) (struct syl *, char *, ftnlen), (*f__doned) (struct syl *);
int (*f__dorevert) (void), (*f__donewrec) (void), (*f__doend) (void);
flag f__sequential;		/*1 if sequential io, 0 if direct */
flag f__formatted;		/*1 if formatted io, 0 if unformatted */
FILE *f__cf;			/*current file */
unit *f__curunit;		/*current unit */
int f__recpos;			/*place in current record */
int f__cursor, f__hiwater, f__scale;
char *f__icptr;

/*error messages*/
char *F_err[] = {
  "error in format",		/* 100 */
  "illegal unit number",	/* 101 */
  "formatted io not allowed",	/* 102 */
  "unformatted io not allowed",	/* 103 */
  "direct io not allowed",	/* 104 */
  "sequential io not allowed",	/* 105 */
  "can't backspace file",	/* 106 */
  "null file name",		/* 107 */
  "can't stat file",		/* 108 */
  "unit not connected",		/* 109 */
  "off end of record",		/* 110 */
  "truncation failed in endfile",	/* 111 */
  "incomprehensible list input",	/* 112 */
  "out of free space",		/* 113 */
  "unit not connected",		/* 114 */
  "read unexpected character",	/* 115 */
  "bad logical input field",	/* 116 */
  "bad variable type",		/* 117 */
  "bad namelist name",		/* 118 */
  "variable not in namelist",	/* 119 */
  "no end record",		/* 120 */
  "variable count incorrect",	/* 121 */
  "subscript for scalar variable",	/* 122 */
  "invalid array section",	/* 123 */
  "substring out of bounds",	/* 124 */
  "subscript out of bounds",	/* 125 */
  "can't read file",		/* 126 */
  "can't write file",		/* 127 */
  "'new' file exists",		/* 128 */
  "can't append to file",	/* 129 */
  "non-positive record number",	/* 130 */
  "I/O started while already doing I/O",	/* 131 */
  "Temporary file name (TMPDIR?) too long"	/* 132 */
};
#define MAXERR (sizeof(F_err)/sizeof(char *)+100)

int
f__canseek (FILE * f) /*SYSDEP*/
{
#ifdef NON_UNIX_STDIO
  return !isatty (fileno (f));
#else
  struct stat x;

  if (fstat (fileno (f), &x) < 0)
    return (0);
#ifdef S_IFMT
  switch (x.st_mode & S_IFMT)
    {
    case S_IFDIR:
    case S_IFREG:
      if (x.st_nlink > 0)	/* !pipe */
	return (1);
      else
	return (0);
    case S_IFCHR:
      if (isatty (fileno (f)))
	return (0);
      return (1);
#ifdef S_IFBLK
    case S_IFBLK:
      return (1);
#endif
    }
#else
#ifdef S_ISDIR
  /* POSIX version */
  if (S_ISREG (x.st_mode) || S_ISDIR (x.st_mode))
    {
      if (x.st_nlink > 0)	/* !pipe */
	return (1);
      else
	return (0);
    }
  if (S_ISCHR (x.st_mode))
    {
      if (isatty (fileno (f)))
	return (0);
      return (1);
    }
  if (S_ISBLK (x.st_mode))
    return (1);
#else
  Help ! How does fstat work on this system ?
#endif
#endif
    return (0);			/* who knows what it is? */
#endif
}

void
f__fatal (int n, char *s)
{
  static int dead = 0;

  if (n < 100 && n >= 0)
    perror (s);
  /*SYSDEP*/
  else if (n >= (int) MAXERR || n < -1)
    {
      fprintf (stderr, "%s: illegal error number %d\n", s, n);
    }
  else if (n == -1)
    fprintf (stderr, "%s: end of file\n", s);
  else
    fprintf (stderr, "%s: %s\n", s, F_err[n - 100]);
  if (dead)
    {
      fprintf (stderr, "(libf2c f__fatal already called, aborting.)");
      abort ();
    }
  dead = 1;
  if (f__init & 1)
    {
      if (f__curunit)
	{
	  fprintf (stderr, "apparent state: unit %d ",
		   (int) (f__curunit - f__units));
	  fprintf (stderr, f__curunit->ufnm ? "named %s\n" : "(unnamed)\n",
		   f__curunit->ufnm);
	}
      else
	fprintf (stderr, "apparent state: internal I/O\n");
      if (f__fmtbuf)
	fprintf (stderr, "last format: %.*s\n", f__fmtlen, f__fmtbuf);
      fprintf (stderr, "lately %s %s %s %s",
	       f__reading ? "reading" : "writing",
	       f__sequential ? "sequential" : "direct",
	       f__formatted ? "formatted" : "unformatted",
	       f__external ? "external" : "internal");
    }
  f__init &= ~2;		/* No longer doing I/O (no more user code to be called). */
  sig_die (" IO", 1);
}

/*initialization routine*/
void
f_init (void)
{
  unit *p;

  if (f__init & 2)
    f__fatal (131, "I/O recursion");
  f__init = 1;
  p = &f__units[0];
  p->ufd = stderr;
  p->useek = f__canseek (stderr);
  p->ufmt = 1;
  p->uwrt = 1;
  p = &f__units[5];
  p->ufd = stdin;
  p->useek = f__canseek (stdin);
  p->ufmt = 1;
  p->uwrt = 0;
  p = &f__units[6];
  p->ufd = stdout;
  p->useek = f__canseek (stdout);
  p->ufmt = 1;
  p->uwrt = 1;
}

int
f__nowreading (unit * x)
{
  off_t loc;
  int ufmt, urw;
  extern char *f__r_mode[], *f__w_mode[];

  if (x->urw & 1)
    goto done;
  if (!x->ufnm)
    goto cantread;
  ufmt = x->url ? 0 : x->ufmt;
  loc = FTELL (x->ufd);
  urw = 3;
  if (!freopen (x->ufnm, f__w_mode[ufmt | 2], x->ufd))
    {
      urw = 1;
      if (!freopen (x->ufnm, f__r_mode[ufmt], x->ufd))
	{
	cantread:
	  errno = 126;
	  return 1;
	}
    }
  FSEEK (x->ufd, loc, SEEK_SET);
  x->urw = urw;
done:
  x->uwrt = 0;
  return 0;
}

int
f__nowwriting (unit * x)
{
  off_t loc;
  int ufmt;
  extern char *f__w_mode[];

  if (x->urw & 2)
    goto done;
  if (!x->ufnm)
    goto cantwrite;
  ufmt = x->url ? 0 : x->ufmt;
  if (x->uwrt == 3)
    {				/* just did write, rewind */
      if (!(f__cf = x->ufd = freopen (x->ufnm, f__w_mode[ufmt], x->ufd)))
	goto cantwrite;
      x->urw = 2;
    }
  else
    {
      loc = FTELL (x->ufd);
      if (!(f__cf = x->ufd = freopen (x->ufnm, f__w_mode[ufmt |= 2], x->ufd)))
	{
	  x->ufd = NULL;
	cantwrite:
	  errno = 127;
	  return (1);
	}
      x->urw = 3;
      FSEEK (x->ufd, loc, SEEK_SET);
    }
done:
  x->uwrt = 1;
  return 0;
}

int
err__fl (int f, int m, char *s)
{
  if (!f)
    f__fatal (m, s);
  if (f__doend)
    (*f__doend) ();
  f__init &= ~2;
  return errno = m;
}
