/*****************************************************************************\
*                                                                             *
*   Filename:	    dirent.h						      *
*                                                                             *
*   Description:    DOS/WIN32 port of standard C library's dirent.h.	      *
*                                                                             *
*   Notes:	    There are also remains of an OS/2 implementation here.    *
*                   This code did work c. 1990, in an OS/2 1.3 port of dirc.c.*
*		    It's not maintained anymore, and unlikely to still work.  *
*		    Left in as a historic reference, in the very unlikely     *
*		    case somebody wants to revive it.			      *
*									      *
*   History:								      *
*    2012-01-09 JFL Created this file, based on dirc and other programs.      *
*    2013-03-09 JFL Rewrote the stat/fstat/lstat and fseeko/ftello definitions*
*                   based on _FILE_OFFSET_BITS, _LARGEFILE_SOURCE, and        *
*		    _LARGEFILE_SOURCE64 definitions.			      *
*    2014-02-06 JFL Moved stat extensions to statx.h.			      *
*    2014-02-27 JFL Use a WIN32_FIND_DATAW in Windows, to support UTF-8 names.*
*    2014-03-20 JFL Restructured Windows opendir and readdir functions into   *
*		    Wide and MultiByte versions, and changed the Unicode and  *
*		    Ansi versions to macros.				      *
*    2014-06-06 JFL Fixed macro _D_EXACT_NAMLEN().			      *
*    2015-12-07 JFL Added the conditional definition of symlink constants, so *
*		    that our code builds even in XP and older Windows SDKs.   *
*									      *
*         © Copyright 2016 Hewlett Packard Enterprise Development LP          *
* Licensed under the Apache 2.0 license - www.apache.org/licenses/LICENSE-2.0 *
\*****************************************************************************/

#ifndef	_DIRENT_H
#define	_DIRENT_H	1

#include "msvclibx.h"

#define _DIRENT_FOR_DOS_WINDOWS	/* Uniquely identify this module */

#include <inttypes.h>
#include <time.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>

#ifndef ENAMETOOLONG /* Not defined in DOS' errno.h */
#define ENAMETOOLONG 38
#endif

#ifdef  __cplusplus
extern "C" {
#endif

/* Macros to extract size information from a struct dirent */
#define _D_EXACT_NAMLEN(d) (strlen((d)->d_name)) /* Name length, not including NUL */

#define _DIRENT_HAVE_D_TYPE	/* We commit to providing this field */

/************************ MS-DOS-specific definitions ************************/

#ifdef _MSDOS	/* Automatically defined when targeting an MS-DOS application */

#include <dos.h>

#define NAME_MAX 12

#pragma pack(1)
struct _fileinfo { /* MS-DOS structure returning file search results */
  uint8_t  fiReserved[21];
  uint8_t  fiAttribute;		/* Attributes of file found */
  uint16_t fiFileTime;		/* Time of last write */
  uint16_t fiFileDate;		/* Time of last write */
  uint32_t fiSize;		/* File Size */
  char     fiFileName[13];	/* File name and extension */
};
#pragma pack()
typedef struct _fileinfo fileinfo;

#pragma pack(1)
struct dirent { /* Standard C library structure returning directory entries. */
  /* Standard fields */
  _ino_t d_ino;		/* We don't need it, but it's required by the spec */
  unsigned char d_type;	/* File type. Values defined in macros DT_xxxx */
  /* unsigned char d_namlen; /* File name length, not including NUL */
  /* OS-specific extensions (allowed by the Posix specification) */
  /* Matches exactly the struc _fileinfo above, to avoid doing any copying */
  uint8_t  d_reserved[21];
  uint8_t  d_attribs;		/* Attribute of file found */
  uint16_t d_time;		/* Time of last write */
  uint16_t d_date;		/* Time of last write */
  uint32_t d_filesize;		/* File Size */
  /* Standard field: The file name, which must be last in the dirent structure */
  char d_name[NAME_MAX+1];	/* Null-terminated file name. Must be last */
};
#pragma pack()

#pragma pack(1)
struct _dirhandle { /* Private structure, not for use by users */
  struct dirent sDirent;
  /* char dta_buf[128];		/* Protection area, in case MS-DOS uses the whole DTA, and not just the struct _fileinfo ahead of it. */
  char wildcards[4];		/* Leave room for the "\*.*" that we append initially. This field MUST follow sDirent. */
  char first;			/* Tracks if this is the first search call */
};
#pragma pack()

/* Macros to extract size information from a struct dirent */
#define _D_ALLOC_NAMLEN(d) 13 /* Upper bound of the block size to alloc for a name */

/* MS-DOS compatible functions for searching with a specific attribute */
int srch1st(char *pszFile, uint16_t wAttr, fileinfo *pFI); /* Search first matching file */
int srchnext(fileinfo *pFI);     /* Search next matching file */
#define DOS_SEARCH_FUNCTIONS_DEFINED 1

#endif /* defined(_MSDOS) */

/************************ Win32-specific definitions *************************/

#ifdef _WIN32	/* Automatically defined when targeting a Win32 application */

#ifndef WINVER
#define WINVER 0x0400
#endif

#include <dos.h>
#include <windows.h>

/* Add symlink definitions, that may be missing in XP and older Windows SDKs */
#ifndef IO_REPARSE_TAG_SYMLINK
#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
#endif
#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
#define SYMBOLIC_LINK_FLAG_DIRECTORY 1
#endif
#ifndef FSCTL_SET_REPARSE_POINT
#define FSCTL_SET_REPARSE_POINT     CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 
#endif
#ifndef FSCTL_GET_REPARSE_POINT
#define FSCTL_GET_REPARSE_POINT     CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) 
#endif
#ifndef FSCTL_DELETE_REPARSE_POINT
#define FSCTL_DELETE_REPARSE_POINT  CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 
#endif

#define NAME_MAX (4 * FILENAME_MAX) /* Worst case using UTF-8 encoding: 4 bytes/WCHAR */

struct dirent { /* Structure used to return information about directory entries. */
  /* OS-specific extensions */
  uint32_t d_attribs;
  uint32_t d_ReparseTag;
  FILETIME d_CreationTime;
  FILETIME d_LastAccessTime;
  FILETIME d_LastWriteTime;
  uint64_t d_filesize;
  char     d_shortname[14*sizeof(WCHAR)];
  /* Standard fields */
  _ino_t d_ino;		/* We don't need it, but it's required by the spec */
  unsigned char d_type;	/* File type. Values defined in macros DT_xxxx */
  /* unsigned char d_namlen; /* File name length, not including NUL */
  char d_name[(NAME_MAX+1)*sizeof(WCHAR)]; /* Null-terminated file name */
};

struct _dirhandle { /* Private structure, not for use by users */
  struct dirent sDirent;
  WCHAR wszDirName[MAX_PATH+1];	/* Null-terminated directory name */
  HANDLE hFindFile;		/* Search handle */
  WIN32_FIND_DATAW wfd;		/* Where Win32 will store the file information */
};

/* Macros to extract size information from a struct dirent */
#define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN(d)+1) /* Upper bound of the block size to alloc for a name */

#endif /* defined(_WIN32) */

/************************* OS/2-specific definitions *************************/

#ifdef _OS2	/* Automatically defined when targeting an OS/2 application? */

#include <dos.h>

#define INCL_DOSFILEMGR
#define INCL_DOSMISC
#define INCL_VIO
#include "os2.h"

#define NAME_MAX CCHMAXPATHCOMP

struct dirent { /* Structure used to return information about directory entries. */
  _ino_t d_ino;		/* We don't need it, but it's required by the spec */
  /* Non standard extensions, to ease adapting old DOS/WIN32 apps */
  uintmax_t d_filesize;	/* File size */
  uint16_t time;	/* MS-DOS time */
  uint16_t date;	/* MS-DOS date */
  uint8_t attribs;	/* Attributes, the MS-DOS way */
  /* Standard fields */
  unsigned char d_type;	/* File type. Values defined in macros DT_xxxx */
  /* unsigned char d_namlen; /* File name length, not including NUL */
  char d_name[NAME_MAX+1];	/* Null-terminated file name. Must be last */
};

struct _dirhandle { /* Private structure, not for use by users */
  struct dirent sDirent;
  short hDir; 			/* Directory handle */
  FILEFINDBUF buf;		/* Where OS/2 will store the file information */
};

/* Macros to extract size information from a struct dirent */
#define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN(d)+1) /* Upper bound of the block size to alloc for a name */

#endif /* defined(_OS2) */

/********************** End of OS-specific definitions ***********************/

typedef struct _dirhandle DIR; /* Directory enumerator handle */
typedef struct dirent _dirent; /* Directory entry */

#define MAXNAMELEN NAME_MAX /* Alias used by some Unix versions */

/* File types for struct dirent d_type. Must match the S_IFMT field in sys/stat.h. */
#define DT_UNKNOWN	 0
#define DT_FIFO		 1	/* Fifo (not used in DOS/Windows dirs) */
#define DT_CHR		 2	/* Character device (not used in DOS/Windows) */
#define DT_DIR		 4	/* Directory */
#define DT_BLK		 6	/* Block device (not used in DOS/Windows) */
#define DT_REG		 8	/* Normal file */
#define DT_LNK		10	/* Symbolic link */
#define DT_SOCK		12	/* Socket (not used in DOS/Windows dirs) */
#define DT_VOLID	15	/* Volume ID (non-standard extension for MS-DOS FAT) */

/* Functions operating on directories */
#if defined(_MSDOS)

extern DIR *opendir(const char *name);	    /* Open a directory */
extern int closedir(DIR *pDir);		    /* Close the directory. Return 0 if successful, -1 if not. */
extern _dirent *readdir(DIR *pDir);	    /* Read a directory entry. Return pDirEnt, or NULL for EOF or error. */

#elif defined(_WIN32)

#if defined(_UTF8_SOURCE) || defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
#define opendir opendirU
#define readdir readdirU
#define scandir scandirU
#else /* _ANSI_SOURCE */
#define opendir opendirA
#define readdir readdirA
#define scandir scandirA
#endif
extern DIR *opendirW(const WCHAR *name);	    /* Open a directory - Wide char version */
extern DIR *opendirM(const char *name, UINT cp);    /* Open a directory - MultiByte char version */
#define opendirA(name) opendirM(name, CP_ACP)	    /* Open a directory - ANSI version */
#define opendirU(name) opendirM(name, CP_UTF8)	    /* Open a directory - UTF-8 version */
extern int closedir(DIR *pDir);			    /* Close the directory. Return 0 if successful, -1 if not. */
extern _dirent *readdirW(DIR *pDir);		    /* Read a directory entry. Return pDirEnt, or NULL for EOF or error. */
extern _dirent *readdirM(DIR *pDir, UINT cp);	    /* Read a directory entry. Return pDirEnt, or NULL for EOF or error. */
#define readdirA(pDir) readdirM(pDir, CP_ACP)	    /* Read a directory entry. Return pDirEnt, or NULL for EOF or error. */
#define readdirU(pDir) readdirM(pDir, CP_UTF8)	    /* Read a directory entry. Return pDirEnt, or NULL for EOF or error. */

#endif /* defined(_WIN32) */

/* extern int readdir_r(DIR *pDir, _dirent *__entry, _dirent **__result); /* Reentrant readdir */
/* extern void rewinddir(DIR *pDir); /* Rewind DIRP to the beginning of the directory. */
/* extern void seekdir(DIR *pDir, long lPos); /* Seek to position POS on DIRP. */
/* extern long telldir(DIR *pDir); /* Return the current position of DIRP. */
/* extern int dirfd(DIR *pDir); /* Return the file descriptor used by DIRP. */

/* Scan the directory dir, calling cbSelect() on each directory entry.
   Entries for which cbSelect() returns nonzero are individually malloc'd,
   sorted using qsort with cbCompare(), and collected in a malloc'd array in
   *namelist.  Returns the number of entries selected, or -1 on error. */
extern int scandir(const char *dir,
		   _dirent ***namelist,
		   int (*cbSelect) (const _dirent *),
		   int (__cdecl *cbCompare) (const _dirent **,
					     const _dirent **));

/* Function to compare two `struct dirent's alphabetically.  */
extern int __cdecl alphasort (const _dirent **ppDE1, const _dirent **ppDEe2);
/* extern int versionsort (const _dirent **ppDE1, const _dirent **ppDEe2); */

#ifdef  __cplusplus
}
#endif

#endif /* defined(_DIRENT_H)  */

