#define INCL_BASE
#define INCL_NOPM
#define INCL_VIO
#define INCL_KBD
#define INCL_DOS
#define INCL_DOSSEMAPHORES
#ifdef LONG
#undef LONG
#endif
#include <os2.h>

#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <process.h>
#include <dos.h>
#include <errno.h>

#include "config.h"
#include "sysdep.h"
#include "screen.h"
#include "buffer.h"
#include "misc.h"

extern char *get_cwd (void);
void delay (int time);

void set_kbd (void);
void thread_getkey (void);

#define lowercase strlwr

/* If non zero, map numlock key to F1 */
int NumLock_Is_Gold = 0;

int PC_Alt_Char = 27;

KBDINFO	initialKbdInfo;	/* keyboard info		*/

/* Code to read keystrokes in a separate thread */

typedef struct kbdcodes {
  UCHAR ascii;
  UCHAR scan;
  USHORT shift;
} KBDCODES;

#define BUFFER_LEN 4096
static KBDCODES threadKeys[BUFFER_LEN];
static int atEnd = 0;
static int startBuf;
static int endBuf;


#ifdef __os2_16__

typedef USHORT APIRET;
static HSEM Hmtx;

#define DosCreateMutexSem(p1,phmtx,p3,p4) \
        DosCreateSem(0, phmtx, "\\SEM\\keybuf.sem")
#define DosRequestMutexSem(hmtx,timeout) DosSemRequest(hmtx,timeout)
#define DosReleaseMutexSem(hmtx) DosSemClear(hmtx)
#define DosCloseMutexSem(hmtx) DosCloseSem(hmtx)

#else /* !defined(__os2_16__) */

static HMTX Hmtx;     /* Mutex Semaphore */

#endif


APIRET CreateSem(void)
{
  return ( DosCreateMutexSem (NULL, &Hmtx, 0, 0) );
}

APIRET RequestSem(void)
{
  return ( DosRequestMutexSem (Hmtx, -1) );
}

APIRET ReleaseSem(void)
{
  return ( DosReleaseMutexSem (Hmtx) );
}

APIRET CloseSem(void)
{
  return( DosCloseMutexSem (Hmtx) );
}

void thread_getkey ()
{
   KBDKEYINFO keyInfo;

   while (!atEnd) {     /* at end is a flag */
      set_kbd();
      KbdCharIn(&keyInfo, IO_NOWAIT, 0);       /* get a character	*/
      if (keyInfo.fbStatus & 0x040) {          /* found a char process it */
	if (keyInfo.chChar == Abort_Char) {
	  SLang_Error = 2;
	  KeyBoard_Quit = 1;
	}
	if ( ((endBuf + 1) % BUFFER_LEN) == startBuf) {
	  DosBeep (500, 20);
	  KbdFlushBuffer(0);
	  continue;
	}
	RequestSem();
	endBuf = (endBuf + 1) % BUFFER_LEN;
	threadKeys [endBuf].ascii = keyInfo.chChar;
	threadKeys [endBuf].scan = keyInfo.chScan;
	threadKeys [endBuf].shift = keyInfo.fsState;
	ReleaseSem();
      } else                    /* no char available*/
	DosSleep (20);
   }
}

void thread_code (void *Args)
{
  (void) Args;
  startBuf = -1;      /* initialize the buffer pointers */
  endBuf = -1;
  thread_getkey ();
  atEnd = 0;          /* reset the flag */
  _endthread();
}


/* The code below is in the main thread */

void set_kbd()
{
  KBDINFO kbdInfo;

  kbdInfo = initialKbdInfo;
  kbdInfo.fsMask &= ~0x0001;		/* not echo on		*/
  kbdInfo.fsMask |= 0x0002;		/* echo off		*/
  kbdInfo.fsMask &= ~0x0008;		/* cooked mode off	*/
  kbdInfo.fsMask |= 0x0004;		/* raw mode		*/
  kbdInfo.fsMask &= ~0x0100;		/* shift report	off	*/
  KbdSetStatus(&kbdInfo, 0);
}

static int enhancedKeyboard;

void init_tty ()
{
  VIOCURSORINFO cursorInfo, OldcursorInfo;

  /*  set ^C off */
  signal (SIGINT, SIG_IGN);
  signal (SIGBREAK, SIG_IGN);

  /* set up the keyboard */

  initialKbdInfo.cb = sizeof(initialKbdInfo);
  KbdGetStatus(&initialKbdInfo, 0);
  set_kbd();
  enhancedKeyboard = 1;

  /* open a semaphore */
  CreateSem();

  /* start a separate to read the keyboard */
#if defined(__BORLANDC__)
  _beginthread (thread_code, 8096, NULL);
#else
  _beginthread (thread_code, NULL,  8096, NULL);
#endif

  VioGetCurType (&OldcursorInfo, 0);
  cursorInfo.yStart = 1;
  cursorInfo.cEnd = 15;
  cursorInfo.cx = 1;
  cursorInfo.attr = 1;
  if (VioSetCurType (&cursorInfo, 0))
    VioSetCurType (&OldcursorInfo, 0);   /* reset to previous value */
}

void sys_flush (int dummy)
{
   (void) dummy;
}

void reset_tty ()
{
  atEnd = 1;                      /* set flag and wait until thread ends */
  while (atEnd) {DosSleep (0);}

  CloseSem();

  /* close the keyboard */
  KbdSetStatus(&initialKbdInfo, 0); /* restore original state	*/
}

unsigned char sys_getkey ()
{
   char *keypad_scan =
      "\x4e\x53\x52\x4f\x50\x51\x4b\x4c\x4d\x47\x48\x49\x37\x4a";
   char *normal = "!@#$%^&*()-=\t*\0177QWERTYUIOP[]\r*ASDFGHJKL;'`*\\ZXCVBNM<>/";
   char *edt_chars = "lnpqrstuvwxyRS";
   int bra = 'O', P = 'P', keypad, weird;
   char *p;
   unsigned int c, c1, shft, shift, i;

   weird = 300;

   while (!input_pending(&weird))
     {
	if (Display_Time)
	  {
	     JWindow->trashed = 1;
	     update((Line *) NULL, 0, 1);
	  }
     }

   /* read codes from buffer */
   RequestSem();
   startBuf = (startBuf + 1) % BUFFER_LEN;
   c = threadKeys [startBuf].ascii;
   c1 = threadKeys [startBuf].scan;
   shift = threadKeys [startBuf].shift;
   ReleaseSem();

   shft = shift & 0xF;

   keypad = (c1 > 0x36) && NumLock_Is_Gold && (enhancedKeyboard);
/*   keypad = (c == 0) && (c1 > 0x36); */
   /* allow Shift-keypad to give non edt keypad chars */
   keypad = keypad && !(shft & 0x3);

   /* This is for the damned enter and slash keys */
   weird = keypad && ((c1 == 0xE0) && ((c == 0xD) || (c == 0x2F)));

   keypad = keypad && (c != 0xE0);  /* excludes small keypad */

   if ((c == 8) && ((c1) == 0x0e)) c =127;
   else if ((c == 32) && (shft & 0x04))    /* ^ space = ^@ */
     {
	c = 3;
	ungetkey ((int*) &c);
	c = 0;
     }
   else if (weird || (keypad && (NULL != (p = strchr(keypad_scan, c1)))))
     {
	if (c1 == 0xE0) if (c == 0x2f) c = 'Q'; else c = 'M';
	else c = (unsigned int) edt_chars[(int) (p - keypad_scan)];
	ungetkey ((int*) &c);
	ungetkey ((int*) &bra);
	c = 27;
     }
   else if ((!c) || ((c == 0xe0) && (enhancedKeyboard) && (c1 > 0x43)))
     {
	if (NumLock_Is_Gold && (c1 == 0x3B))
	  {
	     ungetkey((int*)&P); ungetkey((int*) &bra); c = 27;
	  }
	/* if key is 5 of keypad, get another char */
	else if ((c1 == 'L') && enhancedKeyboard && (c == 0)) return(sys_getkey());
	else
	  {
	     if ((shft == 0x8) && (c == 0) && PC_Alt_Char)
	       {
		  if ((c1 >= 14) && (c1 <= 53))
		    {
		       c1 = (unsigned int) normal[c1];
		       c = PC_Alt_Char;
		    }
		  else if ((c1 >= 120) && (c1 <= 131))
		    {
		       c1 = (unsigned int) normal[c1 - 120];
		       c = PC_Alt_Char;
		    }
		  else if (c1 == 165) /* tab */
		    {
		       c1 = (unsigned int) normal[c1 - 165 + 12];
		       c = PC_Alt_Char;
		    }
	       }
	     else c = 0;
	     ungetkey((int*) &c1);
	  }
     }
   return(c);
}

void sys_flush_input()
{
  KbdFlushBuffer(0);
  endBuf = startBuf;
}

static int keyWaiting(void)
{
  return (endBuf != startBuf);
}

/* sleep for *tsecs tenths of a sec waiting for input */
int input_pending(int *tsecs)
{
   int count = *tsecs * 5;

   if (Batch || INPUT_BUFFER_LEN) return(INPUT_BUFFER_LEN);

   if (count)
     {
	while(count > 0)
	  {
	     delay(20);		       /* 20 ms or 1/50 sec */
	     if (keyWaiting ()) break;
	     count--;
	  }
	return(count);
     }
   else if (INPUT_BUFFER_LEN) return(1); else return(keyWaiting ());
}

/* returns 0 on failure, 1 on sucess */
int sys_delete_file(char *filename)
{
    return(1 + unlink(filename));
}

char *slash2slash(char *s)
{
  char *p = s;

  while (p = strchr(p, '/')) *p = '\\';
  return (s);
}

char *expand_filename(char *file)
{
   register char *p;
   char *p1, *mark, *last, slash = '\\', ch = 0;
   static char work[256];

   *work = 0;
#if 0
   p = file + strlen(file);
   while (p > file)
     {
	if (*p-- == ':')
	  {
	     drive = *p;
	     break;
	  }
     }
#endif
   /* This bit removes any // combinations.  */
   p = file + strlen(file);
   while (p > file)
     {
	if ((*p == slash) && (*(p - 1) == slash))
	  {
	     file = p;
	     break;
	  }
	p--;
     }

   if (*file != slash) strcpy(work, get_cwd());  /* assume relative dir */
   strcat(work, file);
   file = work;

   p = slash2slash(file) + strlen(file);
   /*  p--; */
   mark = p;			       /* mark points at null char */
   last = &ch;

   while(p != file) if (*p-- == ':')
     {
	/* if (p < file) break; */
	p1 = file;
	if (*(p + 2) != slash)  /* change c:file.dat to c:\file.dat */
	  {
	     while(*mark != ':')
	       {
		  *(mark + 1) = *mark;       /* copies null char as well */
		  mark--;
	       }
	     *(mark + 1) = slash;
	  }
	/* p points at drive, p1 = file so shift left */
	if (p != p1)
	  {
	     while(*p != 0) *p1++ = *p++;
	     *p1 = 0;
	  }
	/* start file past the drive spec */
	while (*file) if (*file++ == slash)
	 {
	    file--;
	    /* last = file - 1; */
	    break;
	 }
	break;
     }


    /* remove ../ and ./ stuff */

   p = p1 = file;

   while(*p != 0)
     {
	if ((*p == '.') && (*(p + 1) == '.'))
	  {
	     p += 2;
	     if ((*p == slash) && (*last == slash))
	       {
		  while ((--last >= file) && (*last != slash));
		  p1 = last + 1;
	       }
	     else
	       {
		  *p1++ = '.'; *p1++ = '.'; *p1++ = *p;
	       }
	  }
	else if (*p == '.')
	  {
	     p++;
	     if ((*p == slash) && (*last == slash)); /* nil */
	     else
	       {
		  *p1++ = '.';
		  *p1++ = *p;
	       }
	  }
	else if ((*p == slash) && (*(p + 1) == slash))
	  {
	     /* // is a signal that we go back to root */
	     p1 = file;
	     *p1++ = slash;
	     p++;
	  }
	else (*p1++ = *p);

	last = p1 - 1;
	if (*p != 0) p++;
     }
   *p1 = 0;
   return(work);
}

int sys_rename(char *from, char *to)
{
    return(rename(from,to));
}

void get_term_dimensions(int *w, int *h)
{
  VIOMODEINFO vioModeInfo;

  vioModeInfo.cb = sizeof(vioModeInfo);
  VioGetMode (&vioModeInfo, 0);
  *w = vioModeInfo.col;
  *h = vioModeInfo.row;
}

#if defined(_MSC_VER) || defined (__EMX__)
int sys_chmod(char *file, int what, int *mode, short *uid, short *gid)
{
   struct stat buf;
   int m;

#ifdef _MSC_VER 
   /* MSC stat() is broken on directory names ending with a slash */
   char path[_MAX_PATH];
   strcpy(path, file); 
   deslash(file = path);
#endif

   if (what)
     {
	chmod(file, *mode);
	return(0);
     }

   if (stat(file, &buf) < 0) switch (errno)
     {
	case EACCES: return(-1); /* es = "Access denied."; break; */
	case ENOENT: return(0);  /* ms = "File does not exist."; */
	case ENOTDIR: return(-2); /* es = "Invalid Path."; */
	default: return(-3); /* "stat: unknown error."; break;*/
     }

   m = buf.st_mode;

   *mode = m & 0777;

   if (m & S_IFDIR) return (2);
   return(1);
}

#else

int sys_chmod(char *file, int what, int *mode, short *dum1, short *dum2)
{
  FILESTATUS3 fileInfo;
  USHORT Result;
  (void) dum1; (void) dum2;
  Result = DosQueryPathInfo (file, FIL_STANDARD, &fileInfo, sizeof (fileInfo));
  if (Result == ERROR_FILE_NOT_FOUND) return 0;
  else if (Result == ERROR_ACCESS_DENIED) return -1;
  else if (Result == ERROR_INVALID_PATH) return -2;
  else if (Result != NO_ERROR) return -3;

  *mode = fileInfo.attrFile;

  if (what) {
    fileInfo.attrFile = *mode;
    DosSetPathInfo (file, FIL_STANDARD, &fileInfo, sizeof (fileInfo), 0);
  }

  if (*mode & 0x10) return(2);
  else return (1);
}

#endif

static char OS2_Dir[256];

#ifdef __os2_16__
#define DosFindFirst(FileName, DirHandle, Attribute, ResultBuf, \
		     ResultBufLen, SearchCount, Reserved) \
	DosFindFirst(FileName, DirHandle, Attribute, ResultBuf, \
		     ResultBufLen, SearchCount, 0)
#endif

int sys_findfirst(char *theFile)
{
   char *f, the_path[CCHMAXPATH], *file, *f1;
   char *pat;

#if defined(__os2_16__)
  FILEFINDBUF FindBuffer;
  USHORT FindCount;
  USHORT File_Attr;
#else
  FILEFINDBUF3 FindBuffer;
  ULONG FindCount;
  ULONG File_Attr;
#endif

  HDIR FindHandle;
  File_Attr = FILE_READONLY | FILE_DIRECTORY;

  file = expand_filename(theFile);
  f1 = f = extract_file(file);
  strcpy (OS2_Dir, file);

  OS2_Dir[(int) (f - file)] = 0;

  strcpy(the_path, file);

  while (*f1 && (*f1 != '*')) f1++;
  if (! *f1)
    {
	while (*f && (*f != '.')) f++;
	if (*f) strcat(the_path, "*"); else strcat(the_path, "*.*");
    }
  pat = the_path;

  FindHandle = 1;
  FindCount = 1;
  if (DosFindFirst(pat, &FindHandle, File_Attr, &FindBuffer,
    sizeof (FindBuffer), &FindCount, FIL_STANDARD) != NO_ERROR)
    return 0;
  strcpy (theFile, OS2_Dir);
  strcat (theFile, FindBuffer.achName);
  if (FindBuffer.attrFile & FILE_DIRECTORY) fixup_dir (theFile);
  return 1;
}

int sys_findnext(char *file)
{
#ifdef __os2_16__
  FILEFINDBUF FindBuffer;
  USHORT FileCount;
#else
  FILEFINDBUF3 FindBuffer;
  ULONG FileCount;
#endif

  FileCount = 1;

  if (DosFindNext(1, &FindBuffer, sizeof (FindBuffer), &FileCount))
    return 0;
  else {
    strcpy (file, OS2_Dir);
    strcat (file, FindBuffer.achName);
    if (FindBuffer.attrFile & FILE_DIRECTORY) fixup_dir (file);
    return 1;
  }
}

void sys_suspend()
{
   if (-1 == spawnlp(P_WAIT, getenv("COMSPEC"), NULL, NULL))
     {
	switch(errno)
	  {
	     case ENOENT: msg_error("File not found."); break;
	     case ENOMEM: msg_error("Insufficient Memeory."); break;
	     default: msg_error("Unknown Error.");
	  }
     }
}



/* Here we do a find first followed by calling routine to conver time */
#if defined(_MSC_VER) || defined(__EMX__)

unsigned long sys_file_mod_time(char *file)
{
   struct stat buf;

   if (stat(file, &buf) < 0) return(0);
   return((unsigned long) buf.st_mtime);
}

#else

unsigned long sys_file_mod_time(char *file)
{
   FILESTATUS3 fileInfo;
   struct time t;
   struct date d;

   if (DosQueryPathInfo (file, FIL_STANDARD, &fileInfo, sizeof (fileInfo))
       != NO_ERROR)
       return 0;
   t.ti_min = fileInfo.ftimeLastWrite.minutes;
   t.ti_hour = fileInfo.ftimeLastWrite.hours;
   t.ti_hund = 0;
   t.ti_sec = fileInfo.ftimeLastWrite.twosecs;
   d.da_day = fileInfo.fdateLastWrite.day;
   d.da_mon = fileInfo.fdateLastWrite.month;
   d.da_year = fileInfo.fdateLastWrite.year;
   return dostounix(&d, &t);
}

#endif

void delay (int time)
{
  DosSleep (time);
}

int IsHPFSFileSystem (char *directory)
{
    ULONG		lMap;
    BYTE		bData[128];
    BYTE		bName[3];
    int			i;
    char		*FName;
#if defined (__os2_16__)
    USHORT		cbData;
    USHORT		nDrive;
#define DosQueryCurrentDisk DosQCurDisk
#else
    ULONG		cbData;
    ULONG		nDrive;
    PFSQBUFFER2		pFSQ = (PFSQBUFFER2)bData;
#endif

    if ( _osmode == DOS_MODE )
	return FALSE;

    if (isalpha (directory[0]) && (directory[1] == ':'))
	nDrive = toupper (directory[0]) - '@';
    else
	DosQueryCurrentDisk (&nDrive, &lMap);

/* Set up the drive name */

    bName[0] = (char) (nDrive + '@');
    bName[1] = ':';
    bName[2] = 0;

    cbData = sizeof (bData);

#if defined(__os2_16__)
    if (DosQFSAttach (bName, 0, FSAIL_QUERYNAME, bData, &cbData, 0L))
#else
    if (DosQueryFSAttach (bName, 0, FSAIL_QUERYNAME, pFSQ, &cbData))
#endif
      return FALSE;

#if defined(__os2_16__)
    FName = bData + (*((USHORT *) (bData + 2)) + 7);
#else
    FName = pFSQ->szName + pFSQ->cbName + 1;
#endif

    if (strcmp(FName, "HPFS"))
      return FALSE;
    return(TRUE);
}


/* Code for EAs */

#if defined(__os2_16__)
/* Variables and data structures used for handling EA's, these are
   defined in the 32 BIT API, so these will be included in os2.h */

typedef struct _FEA2         /* fea2 */
		{
#if 0			/* This field is in the 32 bit structure */
		ULONG oNextEntryOffset;		/* New field */
#endif
		BYTE fEA;
		BYTE cbName;
		USHORT cbValue;
		CHAR szName[1];				/* New field */
		} FEA2;
typedef FEA2 *PFEA2;

typedef struct _FEA2LIST		/* fea2l */
		{
		ULONG cbList;
		FEA2 list[ 1 ];
		} FEA2LIST;

typedef FEA2LIST *PFEA2LIST;

typedef struct _GEA2			/* gea2 */
		{
#if 0
		/* New field - in the 32 bit structure */
		ULONG oNextEntryOffset;
#endif /* OS2_32 */
		BYTE cbName;
		CHAR szName[ 1 ];		/* New field */
		} GEA2;

typedef GEA2 *PGEA2;

typedef struct _GEA2LIST		/* gea2l */
		{
		ULONG cbList;
		GEA2 list[ 1 ];
		} GEA2LIST;

typedef GEA2LIST *PGEA2LIST;

typedef struct _EAOP2			/* eaop2 */
		{
		PGEA2LIST fpGEA2List;	/* GEA set */
		PFEA2LIST fpFEA2List;	/* FEA set */
		ULONG oError;			/* Offset of FEA error */
		} EAOP2;

/* typedef EAOP2 *PEAOP2; */

#define DosSetPathInfo(PathName, PathInfoLevel, PathInfoBuf, \
		       PathInfoBufSize, PathInfoFlags) \
        DosSetPathInfo(PathName, PathInfoLevel, PathInfoBuf, \
		       PathInfoBufSize, PathInfoFlags, 0)
#define DosQueryPathInfo(PathName, PathInfoLevel, PathInfoBuf, \
			 PathInfoBufSize) \
        DosQPathInfo(PathName, PathInfoLevel, PathInfoBuf, PathInfoBufSize, 0)
#define DosEnumAttribute(RefType, FileRef, EntryNum, EnumBuf, \
			 EnumBufSize, EnumCnt, InfoLevel) \
        DosEnumAttribute(RefType, FileRef, EntryNum, EnumBuf, \
			 EnumBufSize, EnumCnt, InfoLevel, 0)
#endif

/* The HoldFEA is used to hold individual EAs.  The member names correspond
   directly to those of the FEA structure.  Note however, that both szName
   and aValue are pointers to the values.  An additional field, next, is
   used to link the HoldFEA's together to form a linked list. */

struct _HoldFEA
	{
	BYTE fEA;					/* Flag byte */
	BYTE cbName;
	USHORT cbValue;
	CHAR *szName;
	BYTE *aValue;
	struct _HoldFEA *next;
	};

typedef struct _HoldFEA HOLDFEA;

#define MAX_GEA				500L /* Max size for a GEA List */
#define REF_ASCIIZ			1	/* Reference type for DosEnumAttribute */

#define GET_INFO_LEVEL1		1	/* Get info from SFT */
#define GET_INFO_LEVEL2		2	/* Get size of FEAlist */
#define GET_INFO_LEVEL3		3	/* Get FEAlist given the GEAlist */
#define GET_INFO_LEVEL4		4	/* Get whole FEAlist */
#define GET_INFO_LEVEL5		5	/* Get FSDname */

#define SET_INFO_LEVEL1		1	/* Set info in SFT */
#define SET_INFO_LEVEL2		2	/* Set FEAlist */


/* #define BufferSize 1024
static char FileBuffer [BufferSize]; */
static HOLDFEA *pHoldFEA;			/* EA linked-list pointer */

/* Free_FEAList (pFEA)
   Frees the memory used by the linked list of HOLDFEA's pointed to by pFEA */

void Free_FEAList( HOLDFEA *pFEA )
	{
	HOLDFEA *next;	/* Holds the next field since we free the structure
					   before reading the current next field */

	/* Step through the list freeing all the fields */
	while( pFEA )
		{
		/* Free the fields of the struct */
		next = pFEA->next;
		if( pFEA->szName != NULL )
			/* Free if non-NULL name */
			free(pFEA->szName);
		if( pFEA->aValue != NULL )
			/* Free if non-NULL value */
			free(pFEA->aValue);

		/* Free the pFEA struct itself and move on to the next field */
		free(pFEA);
		pFEA = next;
		}
	}

#if 0
/* Read the EA type, this is stored at the start of the EA value */

ULONG getEAType( const CHAR *Value )
	{
	USHORT Type = *( USHORT * ) Value;

	return( Type );
	}

/* Return the EA length, stored in pFEA, this done so that it is calculated
   in only one place */

ULONG getEALength( const HOLDFEA *pFEA )
	{
	return( sizeof( FEA2 )
			- sizeof( CHAR )	/* Don't include the first element of aValue */
			+ pFEA->cbName + 1	/* Length of ASCIIZ name */
			+ pFEA->cbValue );	/* The value length */
	}

/* Set the first two words of the EA value, this is usually the
   EA type and EA size in pFEA, from the values Type and Size */

void setEATypeSize( const HOLDFEA *pFEA, const USHORT Type, const USHORT Size )
	{
	USHORT *valPtr = ( USHORT * ) pFEA->aValue;
	valPtr[ 0 ] = Type;
	valPtr[ 1 ] = Size;
	}

/* Read the first two words of the EA value, this is usually the
   EA type and EA size in pFEA, into the Type and Size */

void getEATypeSize( const HOLDFEA *pFEA, USHORT *Type, USHORT *Size )
	{
	USHORT *valPtr = ( USHORT * ) pFEA->aValue;
	*Type = valPtr[ 0 ];
	*Size = valPtr[ 1 ];
	}

/* Get the address of the EA value in pFEA, ie skip over the Type and Size */

void* getEADataVal (const HOLDFEA *pFEA)
	{
	/* Skip over the type and size */
	return pFEA->aValue + 2 * sizeof ( USHORT );
	}
#endif

/* QueryEAs (szFileName)
      find all EAs that file szFileName has
      return these in the linked list of HOLDFEAs
      if no EAs exist or the linked list cannot be created then
      return NULL
  This function is modelled after code from IBM's Toolkit */

HOLDFEA *QueryEAs( const CHAR *szFileName )
	{
	HOLDFEA *pHoldFEA;		/* The start of the linked list */

	CHAR *pAllocc = NULL;	/* Temp buffer used with DosEnumAttribute */
	CHAR *pBigAlloc = NULL;	/* Temp buffer to hold each EA as it is read in */
	USHORT cbBigAlloc = 0;

	ULONG ulEntryNum = 1;	/* Count of current EA to read (1-relative) */
	ULONG ulEnumCnt;		/* Number of EAs for Enum to return, always 1 */

	HOLDFEA *pLastIn = 0;	/* Points to last EA added, so new EA can link    */
	HOLDFEA *pNewFEA = NULL; /* Struct to build the new EA in                  */

	FEA2 *pFEA;				/* Used to read from Enum's return buffer */
	GEA2LIST *pGEAList;		/* Ptr used to set up buffer for DosQueryPathInfo() */
	EAOP2 eaopGet;			/* Used to call DosQueryPathInfo() */

	pAllocc = malloc( MAX_GEA );	/* Allocate room for a GEA list */
	pFEA = ( FEA2 * ) pAllocc;
	pHoldFEA = NULL;		/* Initialize the linked list */

	/* Loop through all the EA's adding them to the list */
	while( TRUE )
		{
		ulEnumCnt = 1;		/* No of EAs to retrieve */
		if( DosEnumAttribute( REF_ASCIIZ,	/* Read into EA name into */
							  szFileName,	/* pAlloc Buffer */
							  ulEntryNum, pAllocc, MAX_GEA, &ulEnumCnt,
							  ( LONG ) GET_INFO_LEVEL1 ) )
			break;	/* An error */

		/* Exit if all the EA's have been read */
		if( ulEnumCnt != 1 )
			break;

		/* Move on to next EA */
		ulEntryNum++;

		/* Try and allocate the HoldFEA structure */
		if( ( pNewFEA = malloc( sizeof( HOLDFEA ) ) ) == NULL )
			{
			free( pAllocc );
			Free_FEAList( pHoldFEA );
			return( NULL );
			}

		/* Fill in the HoldFEA structure */
		pNewFEA->cbName = pFEA->cbName;
		pNewFEA->cbValue= pFEA->cbValue;
		pNewFEA->fEA = pFEA->fEA;
		pNewFEA->next = '\0'; 
/*		pNewFEA->next = NULL; */

		/* Allocate the two arrays */
		if( ( pNewFEA->szName = malloc( pFEA->cbName + 1 ) ) == NULL || \
			( pNewFEA->aValue = malloc( pFEA->cbValue ) ) == NULL )
			{
			/* Out of memory, clean up and exit */
			if( pNewFEA->szName )
				free( pNewFEA->szName );
			if( pNewFEA->aValue )
				free( pNewFEA->aValue );

			free( pAllocc );
			free( pNewFEA );

			Free_FEAList( pHoldFEA );
			return( NULL );
			}

		/* Copy EA name across */
		strcpy( pNewFEA->szName, pFEA->szName );
		cbBigAlloc = sizeof( FEA2LIST ) + pNewFEA->cbName + 1 + pNewFEA->cbValue;
							/* +1 is for '\0' */
		if( ( pBigAlloc = malloc( cbBigAlloc ) ) == NULL )
			{
			free( pNewFEA->szName );
			free( pNewFEA->aValue );
			free( pAllocc );
			free( pNewFEA );
			Free_FEAList( pHoldFEA );
			return( NULL );
			}

		/* Set up GEAlist structure */
		pGEAList = ( GEA2LIST * ) pAllocc;
		pGEAList->cbList = sizeof( GEA2LIST ) + pNewFEA->cbName;	/* + 1 for NULL */
#ifndef __os2_16__
		pGEAList->list[ 0 ].oNextEntryOffset = 0L;
#endif
		pGEAList->list[ 0 ].cbName = pNewFEA->cbName;
		strcpy( pGEAList->list[ 0 ].szName, pNewFEA->szName );

		eaopGet.fpGEA2List = ( GEA2LIST FAR * ) pAllocc;
		eaopGet.fpFEA2List = ( FEA2LIST FAR * ) pBigAlloc;

		eaopGet.fpFEA2List->cbList = cbBigAlloc;

		/* Get the complete EA info and copy the EA value */
		DosQueryPathInfo( szFileName, FIL_QUERYEASFROMLIST, ( PVOID ) &eaopGet, \
						  sizeof( EAOP2 ) );
		memcpy( pNewFEA->aValue, \
				pBigAlloc + sizeof( FEA2LIST ) + pNewFEA->cbName, \
				pNewFEA->cbValue );

		/* Release the temp. Enum buffer */
		free( pBigAlloc );

		/* Add to the list */
		if( pHoldFEA == NULL )
			pHoldFEA = pNewFEA;
		else
			pLastIn->next = pNewFEA;
		pLastIn = pNewFEA;
		}

	/* Free up the GEA buffer for DosEnum() */
	free( pAllocc );

	return pHoldFEA;
	}


/* WriteEAs(szFileName, pHoldFEA)

   Write the EAs contained in the linked list pointed to by pHoldFEA
   to the file szFileName.

   Returns TRUE if the write was successful, FALSE otherwise */

int WriteEAs( const char *szFileName, HOLDFEA *pHoldFEA )
{
  HOLDFEA *pHFEA = pHoldFEA;
  EAOP2 eaopWrite;
  CHAR *aPtr = NULL;
  USHORT usMemNeeded;
  APIRET rc;

  eaopWrite.fpGEA2List = NULL;
  while( pHFEA )                                  /* Go through each HoldFEA */
    {
      usMemNeeded = sizeof( FEA2LIST ) + pHFEA->cbName + 1 + pHFEA->cbValue;

      if( ( aPtr = malloc( usMemNeeded ) ) == NULL )
	return FALSE;
      
      /* Fill in eaop structure */
      eaopWrite.fpFEA2List = ( FEA2LIST FAR * ) aPtr;
      eaopWrite.fpFEA2List->cbList = usMemNeeded;
#ifdef __os2_16__  /* ugly hack for difference in 16-bit FEA struct */
      eaopWrite.fpFEA2List->cbList -= ( sizeof(FEA2LIST) - sizeof(FEALIST) );
#else
      eaopWrite.fpFEA2List->list[ 0 ].oNextEntryOffset = 0L;
#endif
      eaopWrite.fpFEA2List->list[ 0 ].fEA = pHFEA->fEA;
      eaopWrite.fpFEA2List->list[ 0 ].cbName = pHFEA->cbName;
      eaopWrite.fpFEA2List->list[ 0 ].cbValue = pHFEA->cbValue;
      strcpy( eaopWrite.fpFEA2List->list[ 0 ].szName, pHFEA->szName );
      memcpy( eaopWrite.fpFEA2List->list[ 0 ].szName + pHFEA->cbName + 1, \
	     pHFEA->aValue, pHFEA->cbValue );

      /* Write out the EA */
      rc = DosSetPathInfo( szFileName, FIL_QUERYEASIZE,
			     ( PVOID ) &eaopWrite, sizeof( EAOP2 ),
			     DSPI_WRTTHRU );

      /* Free up the FEALIST struct */
      free( aPtr );
      
      /* If the write failed, leave now */
      if( rc )
	return FALSE;
      
      pHFEA = pHFEA->next;
    }
  
  return( TRUE );
}
