Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

/*
 * Filename:    dirent.c
 *
 * Purpose:     UNIX directory services
 *
 * Routines:    opendir
 *              closedir
 *              readdir
 *              seekdir
 *              rewindir
 *              telldir
 *              freedirlist       s
 *
 *              * = Routines called by function pointer.
 *              s = Static functions.
 *
 *  Modification History:
       25/08/95      - WATCOMC, and extended DIR to include
                       d_size, d_attr, d_time and d_date fields.
       29/05/96      - Documentation
                     - Rewinddir
                     - Modified readdir() to return a directory
                       based structure.
       12/06/00      - Skip volume labels
       19/07/00      - Changed search attribute from 0Xff to
                       explicit attributes, as 0XFF returns
                       only the share name under Win9x.
       01/07/01      WIN32
*
* $Name:  $Source: /cvsroot/device/DEVL/UTILS/LIBWIN32/dirent.c,v $
* $Revision: 1.2 $ $Date: 2004/05/10 09:35:02 $ $State: Exp $
* $Author: ayoung $ $Locker:  $
*.........................................................................*/
/*
*     Include files
*     -------------
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define  _DIRENT_SOURCE
#include <unistd.h>
#include "dirent.h"
#include <windows.h>

#define DISABLE_HARD_ERRORS     SetErrorMode (0)
#define ENABLE_HARD_ERRORS      SetErrorMode (SEM_FAILCRITICALERRORS | \
                                        SEM_NOOPENFILEERRORBOX)

static void                     freedirlist(struct _dirlist *);


/*-Open a directory-----------------------------------------------------------

   Synopsis:
         DIR *opendir
               (
                   const char *pName
               )

   Purpose:
       The opendir() function opens a directory stream corre-
       sponding to the directory name, and returns a pointer to
       the directory stream. The stream is positioned at the
       first entry in the directory.

   Parameters:
       const char *pName,  Specifies the name of directory

   Returns:

       The opendir() function returns a pointer to the  directory
       stream or NULL if an error occurred, and 'errno' is set to
       one of the following manifest constants.

       Constant      Description
       ---------------------------------------------------------------
       EACESS        Permission denied.
       EMFILE        Too many file descriptors in use by process.
       ENFILE        Too many files are currently open in the system.
       ENOENT        Directory does not exist.
       ENOMEM        Insufficient memory to complete the operation.
       ENOTDIR       Name is not a directory.

   Notes:
       SVID 3, POSIX, BSD 4.3

   See also:
       open(2), readdir(3), closedir(3), rewinddir(3),
       seekdir(3), telldir(3), scandir(3)
----------------------------------------------------------------------------*/
DIR *
opendir( char *pName )
{
   static       int id = 1;     /* singleton */
   WIN32_FIND_DATA finddata;
   HANDLE       hSearch;
   struct       _dirlist *pDirlist;
   char         path[ MAX_PATH ];
   BOOL         isHPFS;
   DIR          *pDir;
   int          i, len;
   int          rc;

/* Copy to working buffer */
   if ((len = strlen(pName)) == 0)
   {
      errno = ENOTDIR;
      return (DIR *)NULL;
   }

/* Convert path */
   if (_fullpath(path, pName, sizeof(path)) == NULL)
   {
   /* Unknown, assume DOS */
      char *last;

      (void) strcpy(path, pName);
      for (i = 0; path[i]; i++)
          if (path[i] == '/')
              path[i] = '\\';       /* convert */
      last = &path[len - 1];

   /* Ok, DOS is very picky about its directory names.
    * The following are valid.
    *  c:/
    *  c:.
    *  c:name/name1
    *
    *  c:name/ is not valid
    */
       if ((*last == '\\') && (len > 1) &&
                (!((len == 3) && (path[1] == ':'))))
           *(last--) = 0;
   }

/* Is a directory ? */
   if (strcmp(path, ".") != 0)
   {
       struct _stat sb;

       DISABLE_HARD_ERRORS;
       rc = _stat (path, &sb);
       ENABLE_HARD_ERRORS;
       if (rc)
          return (DIR *)NULL;
       if (!S_ISDIR(sb.st_mode))
       {
          errno = ENOTDIR;
          return (DIR *)NULL;
       }
   }
                                /* extended file system */
   isHPFS = IsHPFSFileSystem(path);

/* Strip trailing slashes, so we can append "\*.*" */
   len = strlen(path);
   while (len > 0)
   {
      len--;
      if (path[len] == '\\')
         path[len] = '\0';      /* remove slash */
      else {
         len++;                 /* end of path */
         break;
      }
   }

   path[len++] = '\\';          /* insert pattern */
   path[len++] = '*';
   path[len++] = '.';
   path[len++] = '*';
   path[len++] = 0;

/* Create DIR structure */
   if ((pDir = (DIR *)calloc(sizeof(DIR), 1)) == (DIR *)NULL)
      return (DIR *)NULL;

   if ((pDir->dd_buf =
          (char *)malloc(sizeof(struct dirent))) == (char *)NULL)
   {
      free(pDir);
      return (DIR *)NULL;
   }

   pDir->dd_id    = id++;       /* generate unique directory identifier */
   pDir->dd_fd    = -1;         /* file descriptor */
   pDir->dd_loc   = 0L;
   pDir->dd_ddloc = (struct _dirlist *)NULL;

/* Open directory */
   DISABLE_HARD_ERRORS;
   hSearch = FindFirstFile( path, &finddata );
   ENABLE_HARD_ERRORS;

   if (hSearch == 0)
   {
      rc = GetLastError();
#if defined(ERROR_EMPTY_DIR)
      if (rc == ERROR_EMPTY_DIR)
          return (pDir);        /* entry list */
#endif
      free(pDir);
      errno = ENOENT;
      return (DIR *)NULL;
   }

/* Create directory entries */
   do {
#if defined(FILE_ATTRIBUTE_VOLUME)
   /* Skip volume labels */
      if (finddata.ff_attrib & FILE_ATTRIBUTE_VOLUME)
         continue;
#endif
   /* Skip '.' */
      if ( strcmp(finddata.cFileName, ".") == 0 )
         continue;

   /* Create new entry */
      pDirlist = (struct _dirlist *)calloc(1, sizeof(struct _dirlist));
      if (pDirlist == (struct _dirlist *)NULL)
      {
         freedirlist(pDir->dd_contents);
         FindClose(hSearch);
         return (DIR *)NULL;
      }

      pDirlist->dl_entry   = malloc( strlen(finddata.cFileName)+1 );
      if (pDirlist->dl_entry == (char *)NULL)
      {
         free(pDirlist);
         freedirlist(pDir->dd_contents);
         FindClose(hSearch);
         return (DIR *)NULL;
      }

      if (pDir->dd_contents)
         pDir->dd_current  = pDir->dd_current->dl_next = pDirlist;
      else
      {
         pDir->dd_contents = pDir->dd_current = pDirlist;
      }

      (void) strcpy(pDirlist->dl_entry, finddata.cFileName);
      if (!isHPFS)
         strlwr(pDirlist->dl_entry);
//TODO..
//    pDirlist->dl_ctime   = finddata.ftCreationTime;
//    pDirlist->dl_mtime   = finddata.ftLastWriteTime;
//    pDirlist->dl_qsize   = (finddata.nFileSizeHigh * (MAXDWORD+1)) +
//                              finddata.nFileSizeLow;

      pDirlist->dl_size2   = finddata.nFileSizeHigh;
      pDirlist->dl_size    = finddata.nFileSizeLow;
      pDirlist->dl_attr    = finddata.dwFileAttributes;
      pDirlist->dl_next    = (struct _dirlist *)NULL;

   } while ( FindNextFile(hSearch, &finddata) );
   FindClose( hSearch );
   pDir->dd_current = pDir->dd_contents;

   return (pDir);
}


/*-Close a directory----------------------------------------------------------

   Synopsis:
         int closedir
               (
                   DIR *pDir
               )

   Purpose:
       The  closedir() function closes the directory stream asso-
       ciated with dir.  The directory stream descriptor 'pDir' is
       not available after this call.

   Parameters:
       DIR *pDir,       Specifies the open directory

   Returns:
       The  closedir()  function  returns  0  on success or -1 on
       failure and 'errno' is set to one of the following manifest
       constants.

       Constant      Description
       ---------------------------------------------------------------
       EBADF         Invalid directory stream descriptor dir.

   Notes:
       SVID 3, POSIX, BSD 4.3

   See also
       close(2), opendir(3), readdir(3), rewinddir(3),
       seekdir(3), telldir(3), scandir(3)
----------------------------------------------------------------------------*/
int
closedir( DIR *pDir )
{
   if (pDir == (DIR *)NULL)
      return 1;                                 /* EBADF */
   freedirlist(pDir->dd_contents);
   free((char *)pDir->dd_buf);
   free((char *)pDir);
   return 0;
}


/*-Read a directory-----------------------------------------------------------

   Synopsis:
         struct dirent *readdir
               (
                   DIR  *pDir
               )

   Purpose:
       The readdir()  function  returns  a  pointer  to a dirent
       structure representing the next  directory  entry  in  the
       directory  stream  pointed  to be dir.  It returns NULL on
       reaching the end-of-file or if an error occurred.

       The data returned by readdir() is overwritten by subse-
       quent calls to readdir() for the same directory stream.

   Parameters:
       DIR *pDir,    Specifies the open directory

   Returns:
       The  readdir()  function  returns  a  pointer  to a dirent
       structure, or NULL if an error occurs  or  end-of-file  is
       reached, and 'errno' is set to one of the following manifest
       constants.

       Constant      Description
       ------------------------------------------------------------
       EBADF         Invalid directory stream descriptor dir.

   Notes:
       SVID 3, POSIX, BSD 4.3

   See also:
       read(2), opendir(3), closedir(3), rewinddir(3),
       seekdir(3), telldir(3), scandir(3)
----------------------------------------------------------------------------*/
struct dirent *
readdir( DIR *pDir )
{
   struct _dirlist *pEntry;
   struct dirent *pDirent;

/* Retrieve associated fields */
   if (pDir->dd_current == (struct _dirlist *)NULL)
      return (struct dirent *)NULL;
   pEntry  = pDir->dd_current;
   pDirent = (struct dirent *)pDir->dd_buf;

/* Standard fields */
   strcpy(pDirent->d_name, pEntry->dl_entry);
   pDirent->d_namlen = (u_short) strlen(pDirent->d_name);
   pDirent->d_reclen = sizeof(struct dirent);
   (void)strlwr(pDirent->d_name);
   pDirent->d_fileno = 0;

/* The following field are extensions */
   pDirent->d_ctime = pEntry->dl_ctime;
   pDirent->d_mtime = pEntry->dl_mtime;
   pDirent->d_size  = pEntry->dl_size;
   pDirent->d_attr  = pEntry->dl_attr;

/* Update current location */
   pDir->dd_current = pEntry->dl_next;
   pDir->dd_loc++;

   return (pDirent);
}


/*-Set the next of next readdir()---------------------------------------------

   Synopsis:
       void seekdir
               (
                   DIR *pDir, off_t off
               )

   Purpose:
       The seekdir() function sets the location in the  directory
       stream  from  which  the  next  readdir() call will start.
       seekdir() should  be  used  with  an  offset  returned  by
       telldir().

   Parameters:
       DIR *pDir,    Specifies the open directory
       off_t off     Offset

   Returns:
       The seekdir() function returns no value.

   Notes:
       BSD 4.3

   See also:
       lseek(2), opendir(3), readdir(3), closedir(3),  rewinddir(3),
       telldir(3), scandir(3)
----------------------------------------------------------------------------*/
void
seekdir( DIR *pDir, long off )
{
   struct _dirlist *pDirlist;
   long i = off;

   if (off > 0) {
      for (pDirlist = pDir->dd_contents;
            --i >= 0 && pDirlist;
             pDirlist = pDirlist->dl_next
          )
         ;
      pDir->dd_loc = off - (i+1);
      pDir->dd_current = pDirlist;
   }
}


/*-Reset directory stream-----------------------------------------------------

   Synopsis:
       void rewinddir
            (
               DIR *pDir
            )

   Purpose:
       The rewinddir() function resets the position of the direc-
       tory stream dir to the beginning of the directory.

   Parameters:
       DIR *pDir,    Specifies the open directory

   Returns:
       The readdir() function returns no value.

   Notes:
       SVID 3, POSIX, BSD 4.3

   See also:
       opendir(3), readdir(3), closedir(3), seekdir(3),
       telldir(3), scandir(3)
----------------------------------------------------------------------------*/
void
rewinddir( DIR *pDir )
{
   seekdir(pDir, (long)0);
}


/*-Return current location in directory stream-------------------------------

   Synopsis:
       long telldir
            (
               DIR *pDir
            )

   Purpose:
       The telldir() function returns the current location
       associated with the directory stream dir.

   Parameters:
       DIR *pDir,    Specifies the open directory

   Returns:
       The telldir() function returns the current location in the
       directory stream or -1 if an error occurs.

       EBADF         Invalid directory stream descriptor dir.

   Notes:
       BSD 4.3

   See also;
       opendir(3), readdir(3), closedir(3), rewinddir(3),
       seekdir(3), scandir(3)
----------------------------------------------------------------------------*/
long
telldir( DIR *pDir )
{
   return (pDir->dd_loc);
}


/*
 * IsHPFS ---
 *      Is High Performance File System
 */
int
IsHPFSFileSystem( const char *directory )
{
        char             bName[4];
        DWORD            flags;
        DWORD            maxname;
        BOOL             rc;
        unsigned int     nDrive;
        char             szCurDir [MAX_PATH];

        if (isalpha (directory[0]) && (directory[1] == ':'))
                nDrive = toupper (directory[0]) - '@';
        else
        {
                GetCurrentDirectory (MAX_PATH, szCurDir);
                nDrive = szCurDir[0] - 'A' + 1;
        }

/* Set up the drive name */
        strcpy(bName, "x:\\");
        bName[0] = (char) (nDrive + '@');

/* Read the volume info, if we fail - assume non-HPFS */
        DISABLE_HARD_ERRORS;
        rc = GetVolumeInformation (bName, (LPTSTR)NULL, 0,
                (LPDWORD)NULL, &maxname, &flags, (LPTSTR)NULL, 0);
        ENABLE_HARD_ERRORS;

        return ((rc) && (flags & (FS_CASE_SENSITIVE | \
                        FS_CASE_IS_PRESERVED))) ? TRUE : FALSE;
}


/*
 * freedirlist ---
 *      Dispose of the directory list
 */
static void
freedirlist( struct _dirlist *pDirlist )
{
   struct _dirlist *opDirlist;

   while (pDirlist)
   {
      if (pDirlist->dl_entry)
         free(pDirlist->dl_entry);
      pDirlist = (opDirlist = pDirlist)->dl_next;
      free(opDirlist);
   }
}