/* * 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 #include #include #include #include #include #define _DIRENT_SOURCE #include #include "dirent.h" #include #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); } }