#ifndef LOCFILE_H
#define LOCFILE_H 1
/* $Id: locfile.h,v 1.2 1999/01/03 02:06:13 sybalsky Exp $ (C) Copyright Venue, All Rights Reserved  */

/************************************************************************/
/*									*/
/*	(C) Copyright 1989-94 Venue. All Rights Reserved.		*/
/*	Manufactured in the United States of America.			*/
/*									*/
/************************************************************************/
#include "lispemul.h" /* for DLword */

#define	FDEV_PAGE_SIZE		512	/* 1 page == 512 byte */

#define	RECOG_OLD		(S_POSITIVE | 0)
#define	RECOG_OLDEST		(S_POSITIVE | 1)
#define	RECOG_NEW		(S_POSITIVE | 2)
#define	RECOG_OLD_NEW		(S_POSITIVE | 3)
#define	RECOG_NON		(S_POSITIVE | 5)

#define	ACCESS_INPUT		(S_POSITIVE | 0)
#define	ACCESS_OUTPUT		(S_POSITIVE | 1)
#define	ACCESS_BOTH		(S_POSITIVE | 2)
#define	ACCESS_APPEND		(S_POSITIVE | 3)

/* For getfileinfo */
#define	LENGTH			(S_POSITIVE | 1)
#define	WDATE			(S_POSITIVE | 2)
#define	RDATE			(S_POSITIVE | 3)
#define	AUTHOR			(S_POSITIVE | 5)
#define	PROTECTION		(S_POSITIVE | 6)
#define	EOL			(S_POSITIVE | 7)
#define	ALL			(S_POSITIVE | 8)


extern	DLword	*Lisp_world;	/* To access LispSysout area */


#define	ToLispTime(x)	((int)(x) + 29969152)
			/* For getfileinfo. For WDATE&RDATE */
			/* 29969152 == (timer.c)LISP_UNIX_TIME_DIFF */
			/* - 61200 == - 17hours */

#define	ToUnixTime(x)	((int)(x) - 29969152)
			/* For getfileinfo. For WDATE&RDATE */
			/* 29969152 == (timer.c)LISP_UNIX_TIME_DIFF */

#define StrNCpyFromCToLisp(lispbuf, cbuf ,len)	{int lf_i;	\
			char *lf_sptr,*lf_dptr;		\
			for(lf_i=0,lf_sptr=(cbuf),lf_dptr =(lispbuf);lf_i<(len);lf_i++)\
				GETBYTE(lf_dptr++) = *lf_sptr++;		\
		}

#define StrNCpyFromLispToC(cbuf , lispbuf, len)	{int lf_i;	\
			char *lf_sptr,*lf_dptr;			\
			for(lf_i=0,lf_sptr=(lispbuf),lf_dptr =(cbuf);lf_i<(len);lf_i++)\
				*lf_dptr++ = GETBYTE(lf_sptr++);		\
		}

#define FGetNum(ptr, place) { \
             if(((ptr) & SEGMASK)== S_POSITIVE) {(place) = ((ptr) & 0xffff);}\
        else if(((ptr) & SEGMASK)== S_NEGATIVE) {(place) = (int)((ptr)| 0xffff0000);}\
        else {return(NIL);}}


/************************************************************************/
/*									*/
/*		  L i s p S t r i n g T o C S t r i n g			*/
/*									*/
/*	Convert the Lisp string in Lisp into a null-terminated C	*/
/*	string in C.  MaxLen is, for safety, the maximum length of	*/
/*	the resulting string, so the buffer doesn't overflow and	*/
/*	smash memory.							*/
/*									*/
/*	WARNINGS:  The Lisp string is truncated to fit the C string	*/
/*	without warning to the user.  FAT Lisp strings have only	*/
/*	the low 8 bits of each character copied over.			*/
/*									*/
/************************************************************************/
#ifndef BYTESWAP
#define	LispStringToCString(Lisp, C, MaxLen)				\
  {									\
    OneDArray	*lf_arrayp;						\
    char	*lf_base, *lf_dp;						\
    short	*lf_sbase;							\
    int	lf_i, lf_length;						\
    lf_arrayp = (OneDArray *)(Addr68k_from_LADDR(Lisp));			\
    lf_length = min(MaxLen, lf_arrayp->fillpointer);			\
    switch(lf_arrayp->typenumber)						\
      {									\
	case THIN_CHAR_TYPENUMBER:					\
		lf_base = ((char *)(Addr68k_from_LADDR(lf_arrayp->base)))  	\
		       + ((int)(lf_arrayp->offset));			\
		strncpy(C, lf_base, lf_length);				\
		(C)[lf_length] = '\0';					\
		break;							\
									\
	case FAT_CHAR_TYPENUMBER:					\
		lf_sbase = ((short *)(Addr68k_from_LADDR(lf_arrayp->base)))	\
		       + ((int)(lf_arrayp->offset));			\
		for(lf_i=0,lf_dp=C;lf_i<(lf_length);lf_i++)	\
		  *lf_dp++ = (char)(*lf_sbase++);				\
		*lf_dp = '\0';						\
		break;							\
	default:							\
		error("LispStringToCString: Not a character array.\n");	\
      }									\
  }
#else  /* BYTESWAP == T CHANGED-BY-TAKE */
#define	LispStringToCString(Lisp, C, MaxLen)				\
  {									\
    OneDArray	*lf_arrayp;						\
    char	*lf_base, *lf_dp;						\
    short	*lf_sbase;							\
    int	lf_ii, lf_length;						\
    lf_arrayp = (OneDArray *)(Addr68k_from_LADDR(Lisp));			\
    lf_length = min(MaxLen, lf_arrayp->fillpointer);			\
    switch(lf_arrayp->typenumber)						\
      {									\
	case THIN_CHAR_TYPENUMBER:					\
		lf_base = ((char *)(Addr68k_from_LADDR(lf_arrayp->base)))  	\
		       + ((int)(lf_arrayp->offset));			\
		StrNCpyFromLispToC(C , lf_base , lf_length );		\
		(C)[lf_length] = '\0';					\
		break;							\
									\
	case FAT_CHAR_TYPENUMBER:					\
		lf_sbase = ((short *)(Addr68k_from_LADDR(lf_arrayp->base)))	\
		       + ((int)(lf_arrayp->offset));			\
		for(lf_ii=0,lf_dp=C;lf_ii<(lf_length);lf_ii++,lf_sbase++)  \
                    *lf_dp++ = (char)(GETWORD(lf_sbase));               \
		*lf_dp = '\0';						\
		break;							\
	default:							\
		error("LispStringToCString: Not a character array.\n");	\
      }									\
  }

#endif /* BYTESWAP */


/************************************************************************/
/*									*/
/*		  L i s p S t r i n g L e n g t h			*/
/*									*/
/*	Get the byte length of the string in Lisp.			*/
/*                                                                      */
/*  As a side effect, sets FatP to 1 if LispString is fat, 0 otherwise  */
/*  Errors if LispString isn't a string (1-d array of characters)       */
/*									*/
/************************************************************************/
#define	LispStringLength(LispString, Length, FatP)			\
  {									\
    OneDArray	*lf_arrayp;						\
    lf_arrayp = (OneDArray *)(Addr68k_from_LADDR(LispString));		\
    switch(lf_arrayp->typenumber)					\
      {									\
	case THIN_CHAR_TYPENUMBER:					\
        	(Length) = lf_arrayp->fillpointer;			\
		(FatP) = 0;						\
		break;							\
									\
	case FAT_CHAR_TYPENUMBER:					\
            	(Length) = lf_arrayp->fillpointer * 2;			\
            	(FatP) = 1;                                             \
		break;							\
	default:							\
		error("LispStringLength: Not a character array.\n");	\
      }									\
  }




/************************************************************************/
/*									*/
/*		S T R I N G _ B A S E				*/
/*									*/
/*  Return (C-pointer) to the characters in lstringp, in cstringp.      */
/*									*/
/************************************************************************/
#define STRING_BASE(lstringp, cstringp) \
  {				\
	LispPTR	*lf_naddress;				  \
	lf_naddress = (LispPTR *)(Addr68k_from_LADDR(lstringp));		  \
	(cstringp) = (char *)(Addr68k_from_LADDR(((OneDArray *)lf_naddress)->base)); \
  }

#ifndef min
#define min(a, b) (((a) <= (b))?(a):(b))
#endif /* min */

#define	LispNumToCInt(Lisp)					\
	( (((Lisp) & SEGMASK) == S_POSITIVE) ?                  \
          ((Lisp) & 0xFFFF) : (*((int *)(Addr68k_from_LADDR(Lisp)))) )

#define	UPLOWDIFF	0x20

#define	DOWNCASE(name){						\
								\
	char	*lf_cp;						\
								\
	for(lf_cp = (name); *lf_cp!='\0'; ++lf_cp)                      \
	  if((*lf_cp >= 'A') && (*lf_cp <= 'Z')) *lf_cp += UPLOWDIFF;	\
}

#define	UPCASE(name){						\
								\
	char	*lf_cp;					\
								\
	for(lf_cp = (name); *lf_cp!='\0'; ++lf_cp)                      \
	  if((*lf_cp >= 'a') && (*lf_cp <= 'z')) *lf_cp -= UPLOWDIFF;	\
}

#define DIR_OR_FILE_P(name, type){				\
	int	lf_result;				\
	struct stat	lf_statbuf;				\
								\
	TIMEOUT(lf_result = stat(name, &lf_statbuf));		\
	if (lf_result < 0) {					\
		*Lisp_errno = errno;				\
		(type) = 0;					\
	} else {  						\
		switch (lf_statbuf.st_mode & S_IFMT) {		\
								\
		      case S_IFDIR:				\
                        (type) = -1;				\
			break;					\
								\
		      case S_IFREG:				\
                        (type) = 1;				\
			break;					\
								\
		      default:					\
			/*					\
			 * Should we deal with the other	\
			 * types?				\
			 */					\
                        (type) = 0;				\
			break;					\
		}						\
	}							\
}

#define	DIRP(path, dir, buf){				\
	int lf_rval;					\
	struct	stat	lf_statbuf;			\
		strcpy(buf, path);			\
		strcat(buf, dir);			\
		TIMEOUT( lf_rval=stat(buf, &lf_statbuf) );	\
		if( lf_rval == 0){				\
			if( (lf_statbuf.st_mode & S_IFMT) == S_IFDIR ){	\
				strcat(path, dir);			\
				return(1);				\
			}				\
		}					\
		if( lf_rval == -1 && errno == 60){		\
			*Lisp_errno = 60;		\
			return(0);			\
		}					\
		}

#define	FILEP(path, file, buf){				\
	int lf_rval;					\
		strcpy(buf, path);			\
		strcat(buf, file);			\
		TIMEOUT( lf_rval=access(buf, F_OK) );	\
		if( access(buf, F_OK) == 0){		\
			strcat(path, file);		\
			return(1);			\
		}					\
		}

#define	STREQ(name1, name2)(	\
                (*(name1) == *(name2)) && (strcmp(name1, name2) == 0)   \
                )

#define	SPECIALFILEMARK		(-1)

#define NumericStringP(str, truetag, falsetag) {			\
	char	*lfn_cp;						\
									\
        /* NOLINTNEXTLINE(bugprone-macro-parentheses) */		\
	if (*(str) == '\0') goto falsetag;				\
									\
	for(lfn_cp = str; *lfn_cp!='\0'; ++lfn_cp)			\
	  if(*lfn_cp < '0' || '9' < *lfn_cp)				\
	    goto falsetag; /* NOLINT(bugprone-macro-parentheses) */	\
	goto truetag;	/* NOLINT(bugprone-macro-parentheses) */	\
}

/*		
 * Name:	LispVersionToUnixVersion
 *
 * Argument:	char	*pathname
 *				Xerox Lisp syntax pathname.
 *
 * Value:	If succeed, returns 1, otherwise 0.
 *
 * Side Effect:	The version part of pathname is destructively modified.
 *
 * Description:
 *
 * Destructively modify the version part of pathname which is following the
 * Xerox Lisp file naming convention to UNIX one.
 * If the file name which is passed from Lisp has the version part, it must be
 * a valid one (i.e. constructed with only number).  This is guaranteed by Lisp
 * code.
 * This macro should be called at the top of the routines which accept the
 * file name from lisp before converting it into UNIX file name, because
 * locating the version part, the informations about quoted characters are needed.
 * They might be lost in the course of the conversion.
 *
 */
#ifdef DOS

/* DOS version of LispVersionToUnixVersion */
/* * * * * This is done this way because DOS can't handle the non-DOS version -- */
/* * * * * it gave "Too many characters in a character constant" errors!        */
#include "lispver1.h"
#else /* DOS */
/* NON-DOS version of the macro LispVersionToUnixVersion */
#include "lispver2.h"
#endif /* DOS */


/*		
 * Name:	UnixVersionToLispVersion
 *
 * Argument:	char	*pathname
 *				UNIX syntax pathname.
 *		int	vlessp
 *				If 0, versionless file is converted to version 1.
 *				Otherwise, remains as versionless.
 *
 * Value:	If succeed, returns 1, otherwise 0.
 *
 * Side Effect:	The version part of pathname is destructively modified.
 *
 * Description:
 *
 * Destructively modify the version part of pathname which is following the
 * UNIX file naming convention to Xerox Lisp one.
 * This macro should be called, in the routines which convert the UNIX pathname
 * to Lisp one, just before it returns the result to Lisp, because converting
 * version field will append a semicolon and it might make the routine be
 * confused.
 * The file which has not a valid version field, that is ".~##~" form, is
 * dealt with as version 1. 
 */

#define UnixVersionToLispVersion(pathname, vlessp){				\
										\
	char	*start;							\
	char	*end;							\
	char	*lf_cp;							\
	int	ver_no;								\
	size_t	len;								\
	char		ver_buf[VERSIONLEN];					\
										\
	if ((start = strchr(pathname, '~')) != NULL) {				\
		/* First of all, find the version field in pathname. */		\
		end = start;							\
		lf_cp = start + 1;							\
		while (*lf_cp) {							\
			if (*lf_cp == '~') {					\
				start = end;					\
				end = lf_cp;					\
				lf_cp++;						\
			} else {						\
				lf_cp++;						\
			}							\
		}								\
										\
		if (start != end && *(start - 1) == '.' && end == (lf_cp - 1)) {	\
			/*							\
			 * pathname ends in the form ".~###~".  But we		\
			 * check ### is a valid number or not.			\
			 */							\
			len = (end - start) - 1;			\
			strncpy(ver_buf, start + 1, len);			\
			ver_buf[len] = '\0';					\
			NumericStringP(ver_buf, YES, NO);			\
		      YES:							\
			*(start - 1) = ';';					\
			*start = '\0';						\
			*end = '\0';						\
			/* call ato i to eliminate leading 0s. */		\
			ver_no = atoi(start + 1);				\
			sprintf(ver_buf, "%d", ver_no);				\
			strcat(pathname, ver_buf);				\
			goto CONT;						\
										\
		      NO:							\
			/* Dealt with as version 1 unless vlessp */		\
			if (!(vlessp)) strcat(pathname, ";1");                  \
		      CONT:							\
			lf_cp--;	/* Just for label */			\
		} else {							\
			/* Dealt with as version 1 unless vlessp. */		\
                        if (!(vlessp)) strcat(pathname, ";1");                  \
		}								\
	} else {								\
		/* Dealt with as version 1 unless vlessp. */			\
                if (!(vlessp)) strcat(pathname, ";1");                          \
	}									\
}

/*		
 * Name:	ConcDirAndName
 *
 * Argument:	char	*dir	The name of the directory.
 *		char	*name	The name of a file.
 *		char	*fname	The place where the full file name should be
 *				stored.
 * Value:	N/A
 *
 * Side Effect:	fname is replaced with the full file name.
 *
 * Description:
 *
 * Concatenate the directory name and root file name.  Checks if dir contains
 * the trail directory delimiter or not.
 *
 */

#define ConcDirAndName(dir, name, fname){					\
									\
	char	*lf_cp1, *lf_cp2;					\
									\
	lf_cp1 = dir;							\
	lf_cp2 = dir;							\
									\
	while (*lf_cp2 != '\0') {						\
		switch (*lf_cp2) {						\
									\
		      case '/':						\
			lf_cp1 = lf_cp2;					\
			lf_cp2++;						\
			break;						\
									\
		      default:						\
			lf_cp2++;						\
			break;						\
		}							\
	}								\
	if (lf_cp1 == (lf_cp2 - 1)) {						\
                if (lf_cp1 == (dir)) {					\
			/* dir is a root directory. */			\
			strcpy(fname, "/");				\
			strcat(fname, name);				\
		} else {						\
			/* The trail directory is included. */		\
			strcpy(fname, dir);				\
			strcat(fname, name);				\
		}							\
	} else {							\
		/* The trail directory is not included */		\
		strcpy(fname, dir);					\
		strcat(fname, "/");					\
		strcat(fname, name);					\
	}								\
}

/*		
 * Name:	ConcNameAndVersion
 *
 * Argument:	char	*name	The root file name.
 *		char	*ver	The file version.
 *		char	*rname 	The place where the concatenated file name will be
 *				stored.
 * Value:	N/A
 *
 * Side Effect:	rname is replaced with the concatenated file name.
 *
 * Description:
 *
 * Concatenate the root file name and its version in UNIX format.
 *
 */

#define ConcNameAndVersion(name, ver, rname){				\
	if (*(ver) != '\0') {						\
		strcpy(rname, name);					\
		strcat(rname, ".~");					\
		strcat(rname, ver);					\
		strcat(rname, "~");					\
	} else {							\
		strcpy(rname, name);					\
	}								\
}

#define	 VERSIONLEN		16

#define	MAXVERSION		999999999

#define	LASTVERSIONARRAY	(-1)
#define	VERSIONARRAYLENGTH	200

#define NoFileP(varray)						\
        (((varray)->version_no == LASTVERSIONARRAY)? 1 : 0)


#ifdef DOS
#define OnlyVersionlessP(varray) 0
#else
#define OnlyVersionlessP(varray)							 \
        (((varray)->version_no == 0 && ((varray) + 1)->version_no == LASTVERSIONARRAY) ? \
	 1 : 0)
#endif /* DOS */

/* An argument of AddDotNoExtension must be LispVersion convention */
/* Like "foo/fee.fee;3" or "/foo/foo;3" */
/* AddDotNoExtension must be put after UnixVersionToLispVersion */

#define	AddDotNoExtension(file){			\
		char	*lf_cp;			\
		char	*lf_cp1;			\
		if( (strrchr(file,'.')== 0) && ((lf_cp=strrchr(file,';'))!=0) ){	\
			for(lf_cp1=lf_cp;*lf_cp1!='\0';lf_cp1++);	\
			*(lf_cp1+1) = '\0';		\
			for(;lf_cp!=lf_cp1;lf_cp1--)		\
				*lf_cp1 = *(lf_cp1-1);	\
			*lf_cp = '.';			\
		}					\
		}

/* An argument of RemoveDotNoExtension must be LispVersion convention */
/* Like "foo/fee.fee;3" or "/foo/foo.;3" */
/* RemoveDotNoExtension must be put before LispVersionToUnixVersion */

#define RemoveDotNoExtension(file){					\
		char	*lf_cp;					\
		if( ((lf_cp=strrchr(file, ';'))!=0) && (*(lf_cp-1)=='.') ){	\
			for(;*lf_cp!='\0';++lf_cp)				\
				*(lf_cp-1) = *lf_cp;				\
			*(lf_cp-1) = '\0';					\
		}							\
		}
		

extern	int	errno;

#define	ChangeToVersionless(pathname){			\
		char	*lf_cp;			\
		if( (lf_cp=strrchr(pathname, ';')) != 0)	\
			*lf_cp = '\0';			\
		}


#define	UNLINK(x){					\
		TIMEOUT(lf_rval=unlink(x));		\
		if(lf_rval == -1){				\
			err_mess("unlink", errno);	\
			*Lisp_errno = errno;		\
			return(0);			\
		}					\
		}

#define LINK(x,y){					\
		TIMEOUT(lf_rval=link(x, y));		\
		if(lf_rval == -1){				\
			if(errno == 2)			\
				return(1);		\
			else{				\
				err_mess("link", errno);\
				*Lisp_errno = errno;	\
				return(0);		\
			}				\
		}					\
		}

#define RENAME(x,y){					\
		TIMEOUT(lf_rval=rename(x, y));		\
		if(lf_rval == -1){				\
			switch(errno){			\
			case 2:				\
				return(1);		\
			case 18:			\
				*Lisp_errno = errno;	\
				return(0);		\
			default:			\
				err_mess("rename", errno);\
				*Lisp_errno = errno;	\
				return(0);		\
			}				\
		}					\
		}

#define	STAT(x,y){					\
		TIMEOUT(lf_rval=stat(x, y));		\
		if(lf_rval != 0){				\
			err_mess("stat", errno);	\
			*Lisp_errno = errno;		\
			return(-1);			\
		}					\
		}

/*
 * For file name length check
 */
#define FNAMETOOLONG	200

#define FileNameTooLong(val) {				\
	*Lisp_errno = FNAMETOOLONG;			\
	return((val));					\
}









/********************************************************/
/*  file-system-specific defns                */
/*                            */
/*  DIRSEP = OS-specific directory separator character.   */
/*  UNIXDIRSEP = UNIX's    "              */
/*  DRIVESEP = OS-specific drive separator character. */
/*    (only used with DOS as of 3/93)         */
/********************************************************/
#ifdef DOS
#define DIRSEP '\\'
#define DIRSEPSTR "\\"
#define DRIVESEP ':'
#define UNIXDIRSEP '/'
#define MAXNAMLEN _MAX_PATH
#else
#define DIRSEPSTR "/"
#define DIRSEP '/'
#define UNIXDIRSEP '/'
/* system includes may already define MAXNAMLEN */
#if !defined(MAXNAMLEN)
#define MAXNAMLEN NAME_MAX
#endif
#endif

#endif /* LOCFILE_H */
