📄 sfldir.c
字号:
/* ----------------------------------------------------------------<Prolog>-
Name: sfldir.c
Title: Directory access functions
Package: Standard Function Library (SFL)
Written: 1996/04/02 iMatix SFL project team <sfl@imatix.com>
Revised: 2000/01/19
Copyright: Copyright (c) 1996-2000 iMatix Corporation
License: This is free software; you can redistribute it and/or modify
it under the terms of the SFL License Agreement as provided
in the file LICENSE.TXT. This software is distributed in
the hope that it will be useful, but without any warranty.
------------------------------------------------------------------</Prolog>-*/
#include "prelude.h" /* Universal header file */
#include "sfldate.h" /* Date handling functions */
#include "sfluid.h" /* Uid/gid functions */
#include "sflstr.h" /* String functions */
#include "sfllist.h" /* Linked-list functions */
#include "sflmem.h" /* Memory allocation functions */
#include "sflnode.h" /* Linked-list functions */
#include "sflfile.h" /* File-access functions */
#include "sflcons.h" /* Console display functions */
#include "sflprint.h" /* snprintf functions */
#include "sfldir.h" /* Prototypes for functions */
/* Static variables used globally in this source file only */
static char
*sort_key;
/* Function prototypes */
static Bool populate_entry (DIRST *dir);
static time_t get_six_months_ago (void);
static char *format_mode (DIRST *dir);
static char *format_name (DIRST *dir, Bool full);
static char *format_time (DIRST *dir);
static Bool compare_dir (LIST *p1, LIST *p2);
static Bool path_delimiter (char delim);
/* ---------------------------------------------------------------------[<]-
Function: open_dir
Synopsis:
Creates a directory stream and returns the first entry in the directory.
The order of entries is arbitrary, and it is undefined whether you will
get entries like '.' and '..' or not. Returns TRUE if something was
found, else FALSE. If TRUE, fills-in the values in the directory stream
block. Use the same directory stream block for the read_dir and
close_dir() functions.
You must supply a DIRST block when calling open_dir(). If you do not
supply a dir_name (i.e. it is NULL or ""), open_dir() assumes you want
to read from the current working directory. You must call close_dir()
after an open_dir(), even if it fails.
The strings in DIRST all point to static areas that may change after a
further call to read_dir. If you need persistent data (i.e. because
you want to collect a set of DIRSTs and then sort them, call fix_dir()
after each call to open_dir and read_dir. You should then call
free_dir() to release each DIRST when you are finished.
---------------------------------------------------------------------[>]-*/
Bool
open_dir (
DIRST *dir,
const char *dir_name)
{
char
*dir_spec, /* Directory to search through */
*dir_spec_end; /* Points to NULL in dir_spec */
ASSERT (dir != NULL);
memset (dir, 0, sizeof (DIRST));
/* Copy and prepare the directory specification */
dir_spec = mem_alloc (NAME_MAX);
if (dir_name == NULL || *dir_name == 0)
strcpy (dir_spec, DEFAULT_DIR);
else
strcpy (dir_spec, dir_name);
#if (defined (MSDOS_FILESYSTEM))
strconvch (dir_spec, '/', '\\');
#endif
/* Remove a trailing slash from the directory name */
dir_spec_end = dir_spec + strlen (dir_spec);
if (dir_spec_end [-1] == PATHEND)
{
dir_spec_end [-1] = '\0';
dir_spec_end--;
}
/* Open directory stream or find first directory entry */
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
if (strnull (dir_spec))
strcpy (dir_spec, "/");
# if (defined (__OS2__))
if (dir_spec_end [-1] == ':') /* Special case d: to d:\ */
strcat (dir_spec, "\\");
# endif
if ((dir-> _dir_handle = opendir (dir_spec)) == NULL)
#elif (defined (WIN32))
strcat (dir_spec, "\\*");
if ((dir-> _dir_handle = FindFirstFile (dir_spec, &dir-> _dir_entry))
== INVALID_HANDLE_VALUE)
#elif (defined (_MSC_VER))
strcat (dir_spec, "\\*.*");
if ((dir-> _dir_handle = _dos_findfirst (dir_spec, _A_NORMAL | _A_SUBDIR,
&dir-> _dir_entry)) != 0)
#elif (defined (__TURBOC__) || defined (__DJGPP__))
strcat (dir_spec, "\\*.*");
if (findfirst (dir_spec, &dir-> _dir_entry, 255 - FA_LABEL) == -1)
#endif
{
mem_free (dir_spec);
return (FALSE); /* Could not open directory */
}
/* Save the directory name in directory stream structure */
#if (defined (__MSDOS__) || defined (__OS2__))
*dir_spec_end = '\0'; /* Kill the \*.* again */
#endif
dir-> dir_name = dir_spec; /* Now owned by DIRST structure */
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
/* Under UNIX & VMS we still need to fetch the first file entry */
return (read_dir (dir));
#elif (defined (WIN32))
/* Under Win32 we have read an entry, so return those values */
return (populate_entry (dir));
#elif (defined (_MSC_VER))
/* Under MSC we have read an entry, so return those values */
return (populate_entry (dir));
#elif (defined (__TURBOC__) || defined (__DJGPP__))
/* Under Borland C we have read an entry, so return those values */
return (populate_entry (dir));
#else
return (FALSE); /* Directory access not supported */
#endif
}
/* -------------------------------------------------------------------------
* populate_entry -- internal
*
* Sets the various public fields in the directory stream from the system-
* specific values in the private fields. Returns TRUE if okay, FALSE if
* there was a problem getting the file status information.
*/
static Bool
populate_entry (DIRST *dir)
{
#if (defined (WIN32))
static char
*default_user = "user";
#else
char
*full_path; /* Full path name of the file */
struct stat
stat_buf; /* File status structure */
int
rc;
#endif
ASSERT (dir != NULL);
if (dir-> _fixed)
free_dir (dir); /* De-allocate old strings if reqd */
/* Get name of file from directory structure */
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
dir-> file_name = dir-> _dir_entry-> d_name;
#elif (defined (WIN32))
dir-> file_name = dir-> _dir_entry.cFileName;
#elif (defined (_MSC_VER))
dir-> file_name = dir-> _dir_entry.name;
#elif (defined (__TURBOC__) || defined (__DJGPP__))
dir-> file_name = dir-> _dir_entry.ff_name;
#endif
#if (defined (__UNIX__) || defined (MSDOS_FILESYSTEM))
/* If the filename is . or .., skip this entry and do the next. We */
/* use a little bit of a recursive call to make this code simple. */
if (dir-> file_name [0] == '.' && (dir-> file_name [1] == '\0'
|| (dir-> file_name [1] == '.' && dir-> file_name [2] == '\0')))
return (read_dir (dir));
#endif
/* Prepare full path and get file status information. Most systems have
* some kind of stat() function; we call this, even if similar info is
* already available in the _dir_entry structures. Except for Win32
* where we use the native Win32 API to support compilers which may not
* have all the C wrapper functions (e.g. RSXNT).
*/
#if (defined (WIN32))
/* Get file info using Win32 calls */
{
unsigned long thi, tlo;
double dthi, dtlo;
double secs_since_1601;
double delta = 11644473600.;
double two_to_32 = 4294967296.;
thi = dir-> _dir_entry.ftLastWriteTime.dwHighDateTime;
tlo = dir-> _dir_entry.ftLastWriteTime.dwLowDateTime;
dthi = (double) thi;
dtlo = (double) tlo;
secs_since_1601 = (dthi * two_to_32 + dtlo) / 1.0e7;
dir-> file_time = (unsigned long) (secs_since_1601 - delta);
dir-> file_size = dir->_dir_entry.nFileSizeLow;
dir-> file_mode = 0;
if (dir-> _dir_entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
dir-> file_mode |= S_IFDIR;
else
dir-> file_mode |= S_IFREG;
if (!(dir-> _dir_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
dir-> file_mode |= S_IREAD;
if (!(dir-> _dir_entry.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
dir-> file_mode |= S_IWRITE;
dir-> file_nlink = 1;
dir-> owner = NULL;
dir-> group = NULL;
}
#else
full_path = xstrcpy (NULL, dir-> dir_name, "/", dir-> file_name, NULL);
rc = stat (full_path , &stat_buf);
mem_free (full_path);
if (rc == -1)
return (FALSE);
dir-> file_time = stat_buf.st_mtime; /* Modification time */
dir-> file_size = stat_buf.st_size; /* Size in bytes */
dir-> file_mode = stat_buf.st_mode; /* UNIX-ish permissions */
dir-> file_nlink = stat_buf.st_nlink; /* Number of links to file */
/* Get owner and group names */
dir-> owner = get_uid_name (stat_buf.st_uid);
dir-> group = get_gid_name (stat_buf.st_gid);
#endif
/* Prepare DOS-ish permission bits */
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
dir-> file_attrs = 0;
if ((stat_buf.st_mode & S_IREAD) == 0)
dir-> file_attrs |= ATTR_HIDDEN;
if ((stat_buf.st_mode & S_IWRITE) == 0)
dir-> file_attrs |= ATTR_RDONLY;
if ((stat_buf.st_mode & S_IFDIR) != 0)
dir-> file_attrs |= ATTR_SUBDIR;
#elif (defined (WIN32))
dir-> file_attrs = 0;
if (dir-> _dir_entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
dir-> file_attrs |= ATTR_SUBDIR;
if (dir-> _dir_entry.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
dir-> file_attrs |= ATTR_RDONLY;
if (dir-> _dir_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
dir-> file_attrs |= ATTR_HIDDEN;
if (dir-> _dir_entry.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
dir-> file_attrs |= ATTR_SYSTEM;
dir-> owner = dir-> group = default_user;
#elif (defined (_MSC_VER))
dir-> file_attrs = (byte) dir-> _dir_entry.attrib & ATTR_MASK;
#elif (defined (__TURBOC__) || defined (__DJGPP__))
dir-> file_attrs = (byte) dir-> _dir_entry.ff_attrib & ATTR_MASK;
#endif
return (TRUE); /* No errors */
}
/* ---------------------------------------------------------------------[<]-
Function: read_dir
Synopsis:
Reads the next entry from the directory stream. Returns TRUE if there
was more data to read; returns FALSE if there was nothing more to read.
Updates the fields in the directory structure when it returns TRUE.
The strings in DIRST all point to static areas that may change after a
further call to read_dir. If you need persistent data (i.e. because
you want to collect a set of DIRSTs and then sort them, call fix_dir()
after each call to open_dir and read_dir. You should then call
free_dir() to release each DIRST when you are finished.
---------------------------------------------------------------------[>]-*/
Bool
read_dir (
DIRST *dir)
{
ASSERT (dir != NULL);
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
if ((dir-> _dir_entry =
(struct Dirent *) readdir (dir-> _dir_handle)) != NULL)
return (populate_entry (dir));
else
#elif (defined (WIN32))
if (FindNextFile (dir-> _dir_handle, &dir-> _dir_entry))
return (populate_entry (dir));
else
#elif (defined (_MSC_VER))
if (_dos_findnext (&dir-> _dir_entry) == 0)
return (populate_entry (dir));
else
#elif (defined (__TURBOC__) || defined (__DJGPP__))
if (findnext (&dir-> _dir_entry) == 0)
return (populate_entry (dir));
else
#endif
return (FALSE);
}
/* ---------------------------------------------------------------------[<]-
Function: close_dir
Synopsis:
Close the directory stream, and free any allocated memory. You should
call this function when you are done reading a directory, or you will
get memory leaks. Returns TRUE if okay, FALSE if there was an error.
---------------------------------------------------------------------[>]-*/
Bool
close_dir (
DIRST *dir)
{
Bool
rc;
ASSERT (dir != NULL);
mem_free (dir-> dir_name); /* Free dynamically-allocated name */
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
ASSERT (dir-> _dir_handle);
rc = (closedir (dir-> _dir_handle) == 0);
#elif (defined (WIN32))
rc = FindClose (dir-> _dir_handle);
#elif (defined (_MSC_VER))
rc = TRUE; /* No function to close a dir */
#elif (defined (__TURBOC__) || defined (__DJGPP__))
rc = TRUE; /* No function to close a dir */
#else
rc = FALSE; /* Directory access not supported */
#endif
return (rc);
}
/* ---------------------------------------------------------------------[<]-
Function: format_dir
Synopsis:
Formats the directory entry information using the same conventions as
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -