home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
- /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
- /*
- * This is file XSTAT.C
- *
- * Internal assist functions which are common to stat() and fstat().
- *
- *
- * Copyright (c) 1994-96 Eli Zaretskii <eliz@is.elta.co.il>
- *
- * This software may be used freely as long as the above copyright
- * notice is left intact. There is no warranty on this software.
- *
- */
-
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <limits.h>
- #include <time.h>
- #include <errno.h>
- #include <dos.h>
- #include <dpmi.h>
- #include <libc/farptrgs.h>
- #include <libc/dosio.h>
- #include <libc/bss.h>
-
- #include "xstat.h"
-
- static int xstat_count = -1;
-
- /* Some fields of struct stat are expensive to compute under DOS,
- because they require multiple disk accesses. Fortunately, many
- DOS programs don't care about these. To leave both pedants (like
- me) and performance-oriented guys happy, a variable is provided
- which controls which expensive fields should be computed. To get
- the fastest stat() for your program, clear the bits for only those
- features you need and set the others.
-
- This improvement was suggested by Charles Sandmann
- <sandmann@clio.rice.edu> and DJ Delorie <dj@delorie.com>. */
-
- #define _STAT_INODE 1 /* should we bother getting inode numbers? */
- #define _STAT_EXEC_EXT 2 /* get execute bits from file extension? */
- #define _STAT_EXEC_MAGIC 4 /* get execute bits from magic signature? */
- #define _STAT_DIRSIZE 8 /* compute directory size? */
- #define _STAT_ROOT_TIME 0x10 /* try to get root dir time stamp? */
- #define _STAT_WRITEBIT 0x20 /* fstat() needs write bit? */
-
- /* Should we bother about executables at all? */
- #define _STAT_EXECBIT (_STAT_EXEC_EXT | _STAT_EXEC_MAGIC)
-
- /* By default, all the bits are reset (including as yet unused ones), so
- people who don't care will transparently have the full version. */
- unsigned short _djstat_flags;
-
- /* As we depend on undocumented DOS features, we could fail in some
- incompatible environment or future DOS versions. If we do, the
- following variable will have some of its bits set. Each bit
- describes a single feature which we tried to use and failed.
- The function _djstat_describe_lossage() may be called to print a
- human-readable description of the bits which were set by the last
- call to f?stat(). This should make debugging f?stat() failures
- in an unanticipated environment a lot easier.
-
- This improvement was suggested by Charles Sandmann
- <sandmann@clio.rice.edu>. */
-
- unsigned short _djstat_fail_bits;
-
- /* ----------------------------------------------------------------------- */
-
- /* Convert file date and time to time_t value suitable for
- struct stat fields. */
-
- time_t
- _file_time_stamp(unsigned int dos_ftime)
- {
- struct tm file_tm;
-
- memset(&file_tm, 0, sizeof(struct tm));
- file_tm.tm_isdst = -1; /* let mktime() determine if DST is in effect */
-
- file_tm.tm_sec = (dos_ftime & 0x1f) * 2;
- file_tm.tm_min = (dos_ftime >> 5) & 0x3f;
- file_tm.tm_hour = (dos_ftime >> 11) & 0x1f;
- file_tm.tm_mday = (dos_ftime >> 16) & 0x1f;
- file_tm.tm_mon = ((dos_ftime >> 21) & 0x0f) - 1; /* 0 = January */
- file_tm.tm_year = (dos_ftime >> 25) + 80;
-
- return mktime(&file_tm);
- }
-
- /* Get time stamp of a DOS file packed as a 32-bit int.
- * This does what Borland's getftime() does, except it doesn't
- * pollute the application namespace and returns an int instead
- * of struct ftime with packed bit-fields.
- */
-
-
- int
- _getftime(int fhandle, unsigned int *dos_ftime)
- {
- __dpmi_regs regs;
-
- regs.x.ax = 0x5700;
- regs.x.bx = fhandle;
- __dpmi_int(0x21, ®s);
-
- if (regs.x.flags & 1)
- {
- errno = __doserr_to_errno(regs.x.ax);
- return -1;
- }
-
- *dos_ftime = ((unsigned int)regs.x.dx << 16) + (unsigned int)regs.x.cx;
-
- return 0;
- }
-
- /* Invent an inode number for those files which don't have valid DOS
- * cluster number. These could be: devices like /dev/nul; empty
- * files which were not allocated disk space yet; or files on
- * networked drives, for which the redirector doesn't bring the
- * cluster number.
- *
- * To ensure proper operation of this function, you must call it
- * with a filename in some canonical form. E.g., with a name
- * returned by truename(), or that returned by _fixpath(). The
- * point here is that the entire program MUST abide by these
- * conventions through its operation, or else you risk getting
- * different inode numbers for the same file.
- *
- * This function is due to Eric Backus and was taken with minor
- * modifications from stat.c, as included in DJGPP 1.11m5.
- * The function now returns 0 instead of -1 if it can't allocate
- * memory for a new name, so that f?stat() won't fail if the inode
- * is unavailable, but return zero inode instead.
- */
-
- /*
- (c) Copyright 1992 Eric Backus
-
- This software may be used freely so long as this copyright notice is
- left intact. There is no warranty on this software.
- */
-
- struct name_list
- {
- struct name_list *next;
- char *name;
- unsigned mtime;
- unsigned long size;
- long inode;
- };
-
- ino_t
- _invent_inode(const char *name, unsigned time_stamp, unsigned long fsize)
- {
- static struct name_list *name_list[256];
-
- /* If the st_inode is wider than a short int, we will count up
- * from USHRT_MAX+1 and thus ensure there will be no clashes with
- * actual cluster numbers.
- * Otherwise, we must count downward from USHRT_MAX, which could
- * yield two files with identical inode numbers: one from actual
- * DOS cluster number, and another from this function. In the
- * latter case, we still have at least 80 inode numbers before
- * we step into potentially used numbers, because some FAT entries
- * are reserved to mean EOF, unused entry and other special codes,
- * and the FAT itself uses up some clusters which aren't counted.
- */
- static int dir = (sizeof(ino_t) > 2 ? 1 : -1);
-
- /* INODE_COUNT is declared LONG and not ino_t, because some DOS-based
- * compilers use short or unsigned short for ino_t.
- */
- static long inode_count = (sizeof(ino_t) > 2
- ? (long)USHRT_MAX + 1L
- : USHRT_MAX);
-
- struct name_list *name_ptr, *prev_ptr;
- const char *p;
- int hash;
-
- /* Force initialization in restarted programs (emacs). */
- if (xstat_count != __bss_count)
- {
- xstat_count = __bss_count;
- inode_count = (sizeof(ino_t) > 2 ? (long)USHRT_MAX + 1L : USHRT_MAX);
- memset (name_list, 0, sizeof name_list);
- }
-
- if (!name)
- return 0;
-
- /* Skip over device and leading slash. This will allow for less
- * inode numbers to be used, because there is nothing bad in generating
- * identical inode for two files which belong to different drives.
- */
- if (*name && name[1] == ':' && (name[2] == '\\' || name[2] == '/'))
- {
- /* If this is a root directory, return inode = 1. This is compatible
- with the code on stat.c which deals with root directories. */
- if (name[3] == 0)
- return (ino_t)1;
-
- name += 3;
- }
-
- /* If the passed name is empty, invent a new inode unconditionally.
- * This is for those unfortunate circumstances where we couldn't
- * get a name (e.g., fstat() under Novell). For these we want at
- * least to ensure that no two calls will get the same inode number.
- * The lossage here is that you get different inodes even if you call
- * twice with the same file. Sigh...
- */
- if (!*name)
- {
- ino_t retval = inode_count;
-
- inode_count += dir;
- return retval;
- }
-
- /* We could probably use a better hash than this */
- p = name;
- hash = 0;
- while (*p != '\0')
- hash += *p++;
- hash &= 0xff;
-
- /* Have we seen this string? */
- name_ptr = name_list[hash];
- prev_ptr = name_ptr;
- while (name_ptr)
- {
- if (strcmp(name, name_ptr->name) == 0 &&
- name_ptr->mtime == time_stamp &&
- name_ptr->size == fsize)
- break;
- prev_ptr = name_ptr;
- name_ptr = name_ptr->next;
- }
-
- if (name_ptr)
- /* Same string, time stamp, and size, so same inode */
- return name_ptr->inode;
- else
- {
- ino_t retval;
-
- /* New string with same hash code */
- name_ptr = (struct name_list *)malloc(sizeof *name_ptr);
- if (name_ptr == 0)
- return 0;
- name_ptr->next = (struct name_list *)0;
- name_ptr->name = (char *)malloc(strlen(name)+1);
- if (name_ptr->name == 0)
- {
- free(name_ptr);
- return 0;
- }
- strcpy(name_ptr->name, name);
- name_ptr->mtime = time_stamp;
- name_ptr->size = fsize;
- name_ptr->inode = inode_count;
- if (prev_ptr)
- prev_ptr->next = name_ptr;
- else
- name_list[hash] = name_ptr;
- retval = inode_count;
- inode_count += dir; /* increment or decrement as appropriate */
-
- return retval;
- }
- }
-