/*
 *	f o p e n x
 */

/*)LIBRARY
*/

#ifdef	DOCUMENTATION

Title	fopenx	Open a file (extended)
Index		Open a file (extended)

synopsis
	.s.nf
	FILE *
	fopenx(name, mode, inisiz, recsiz);
	char		*name;	/* File to open 	*/
	char		*mode;	/* Open modes		*/
	int		inisiz;	/* initial size of file */
	int		recsiz;	/* record size for random access files */
	.s.f
Description

	Fopenx is an extended version of the standard DecusC fopen
	routine. It opens a new or existing file in the indicated 
	mode:

	.s.nf
		r	Read the existing file sequentially
		w	Create and write the file sequentially
		a	Append to the file
		n	Not record oriented
		u	RSX-mode "unbuffered i/o"

	.s.f
	"eXtended" modes are:
	.s.nf

		m	Open existing file for modification
		p	Preallocate file size
		x	Open file as a random access file
		s	Open file for shared access
		l	Don't lock file on write error
	.s.f

	Either "r", "w", "a" or "m" must be given.  "n" and "u" are
	optional.  "n" should be given for "binary" files.
	Note that "n" mode will create fixed-block records on
	RSX systems with record size 512.
	.s
	Inisiz must be 0 unless the "p" option is given, then it
	should be the initial number of blocks to be allocated for the
	file. A positive value for inisiz requests a contiguous file,
	a negative value gives a non-contiguous file.
	.s
	Recsiz must be 0 unless the "x" option is given, then it is the
	record size for a random access file. The size should be given
	in bytes. 
	.s
	The "p" and "x" options are only valid when creating a new file.
	.s
	The "l" option prevents locking a file if the task writing to
	it exits before closing the file, in other words, if the task
	aborted.
	.s
	Note that "n", "u", "m", "p", "x" and "l" are not compatible with 
	Unix systems.

Implementation Details

	On RSX, "u" mode files will be created with the
	"variable-length" attribute.  
	.s
	On RSX, if the record type bits in the record attribute byte
	(F.RATT in the FDB) is zero, the file will be read as if
	the "n" was specified.  Files created with the "x" option
	also have the F.RATT byte set to 0. Note that, if the file contains
	carriage-return line-feed sequences, the entire sequence
	will be passed to the user's program.  If record attributes
	are understandable, the carriage-return will be deleted
	from <CR><LF> sequences.
	.s
	The "l"ock options is implemented by setting FA.ENB | FA.DLK
	in F.ACTL.
	.s
	Don't use fopenx on non-disk devices, the results are
	unpredicatable.
	.s
	Fopenx() returns NULL on errors -- $$ferr gets an error code.
	On RSX, this will be the FCS error code.
	.s
	Note that "no buffer space available" (IE.NBF or E$$NSP) and
	"invalid lun" (IE.ILU or E$$NOC) may be generated by fopen.
	.s
	The same file may not be used for both reading and writing
	except if the program writes a disk file and uses fgetb and
	fputb. As with the standard DecusC fopen, using regular sequential
	io (fget,fput) will work only if you write, then repositions
	and reads it using ftell()/fseek().  In this case, the program
	should call rewind() or freopen() to reinitialize the file before
	using fseek().
	.s
	You can get the status block info from FCS by defining $$stbk
	as an external word array. Look in the FCS manual for the
	meaning of each word. Note this value is volatile, the next
	fopenx overwrites it.
Bugs

	Only works with RSX FCS. You need to known what your doing.

#endif

#include <stdio.h>

#define IE_BAD	-1	/* bad parameter ferr value */

/* file open types */

extern char	*FO_RD;
extern char	*FO_WRT;
extern char	*FO_MFY;
extern char	*FO_UPD;
extern char	*FO_APD;
extern char	*FA_SHR;
extern char	*FA_ENB;
extern char	*FA_DLK;

/* fdb offsets */

extern char	*F_RTYP;
extern char	*F_RATT;
extern char	*F_RACC;
extern char	*F_FACC;
extern char	*F_CNTG;
extern char	*F_RSIZ;
extern char	*F_ERR;
extern char	*F_STBK;
extern char	*F_ACTL;

/* file attributes */

extern char	*FD_RAN;
extern char	*R_FIX;
extern char	*R_VAR;


extern int $$ferr;

unsigned $$stbk[5];

FILE *
fopenx(fnam, mode, inisiz, recsiz)
char *fnam;
char *mode;
int inisiz;	/* initial size of file */
int recsiz;	/* record size for fixed length file */
{
  FILE *fp;
  char options[8];	/* options string */
  char *p;		/* genral pointer */
  int ferr;		/* place to save $$ferr on open err */
  int facc;		/* access type */
  int af;		/* access count - must be 1 */

  /* special open flags */

  char mf;		/* modify flag */
  char pf;		/* preallocate flag */
  char sf;		/* shared access flag */
  char xf;		/* fixed length flag */
  char cf;		/* create flag */
  char lf;		/* don't lock flag */

  facc = af = mf = pf = sf = xf = cf =lf = 0;
  options[0] = NULL;	/* make sure we have a null string */

  /* scan for all file options */

  for (p = mode; *p != NULL; p++) {
    switch (*p)	{
      case 'r':	{ af++;
		facc = facc | ((int) &FO_RD);
      		strcat(options, "r");
      		continue;
      		}
      case 'w':	{ af++;
		facc = facc | ((int) &FO_WRT);
      		strcat(options, "w");
  		cf++;
      		continue;
      		}
      case 'a':	{ af++;
		facc = facc | ((int) &FO_APD);
      		strcat(options, "a");
      		continue;
      		}
      case 'u':	{ strcat(options, "u");
      		continue;
      		}
      case 'n':	{ strcat(options, "n");
      		continue;
      		}
      case 'm':	{ af++; mf++;
      		strcat(options, "r");  /* set "r" so fdbset works */
		facc = facc | ((int) &FO_MFY);
      		continue;
      		}
      case 'p':	{ pf++;
      		continue;
      		}
      case 's':	{ sf++;
		facc = facc | ((int) &FA_SHR);
      		continue;
      		}
      case 'x':	{ xf++;		/* fixed length records flag */
      		continue;
      		}
      case 'l':	{ lf++;		/* don't lock flag */
      		continue;
      		}
      default:	{ 
		$$ferr = IE_BAD;	/* report bad args */
  		return(NULL);
  		}
      }
    } /* end options string scan */

  if (af != 1) 	{ /* only one access type allowed */
    $$ferr = IE_BAD;	/* report bad args */
    return(NULL);
    }

  /* set up fdb and iov, return if it fails */

  if ((fp = fdbset(fnam, options)) == NULL) return(NULL);

  /* we have a good iov, now set up optional stuff */

  p =  fp->io_fdb;
  *(p + (int) &F_FACC) = facc;		/* set access type */

  *(p + (int) &F_RTYP) = (int) &R_VAR;	/* assume var. length */

  if (xf) {	/* fixed length records, random access r/w */
    *(p + (int) &F_RTYP) = (int) &R_FIX;
    *(p + (int) &F_RATT) = 0;
    *(p + (int) &F_RACC) = (int) &FD_RAN;
    *((int *) (p + (int) &F_RSIZ)) = recsiz;
    }

  if (pf) {		/* preallocate file to size */
    if (cf != 1) {	/* must be creating file */
      $$ferr = IE_BAD;	/* report bad args */
      return(NULL);
      }
    *((int *) (p + (int) &F_CNTG)) = inisiz;
    }

  *((int *) (p + (int) &F_STBK)) = $$stbk;	/* point to statistics */

  if (lf) *((int *) (p + (int) &F_ACTL)) = ((int) &FA_ENB) | ((int) &FA_DLK);

  if (($$ferr = opnfnb(fp->io_fdb)) != 1) {
    ferr = $$ferr;	/* save $$ferr across close */
    fclose(fp);
    fp = NULL;
    $$ferr = ferr;
  }
  return(fp);
}
