home *** CD-ROM | disk | FTP | other *** search
/ PC Extra Super CD 1998 January / PCPLUS131.iso / DJGPP / V2 / DJLSR201.ZIP / src / libc / posix / sys / stat / xstat.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-23  |  9.0 KB  |  278 lines

  1. /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  2. /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  3. /*
  4.  * This is file XSTAT.C
  5.  *
  6.  * Internal assist functions which are common to stat() and fstat().
  7.  *
  8.  *
  9.  * Copyright (c) 1994-96 Eli Zaretskii <eliz@is.elta.co.il>
  10.  *
  11.  * This software may be used freely as long as the above copyright
  12.  * notice is left intact.  There is no warranty on this software.
  13.  *
  14.  */
  15.  
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <sys/types.h>
  19. #include <limits.h>
  20. #include <time.h>
  21. #include <errno.h>
  22. #include <dos.h>
  23. #include <dpmi.h>
  24. #include <libc/farptrgs.h>
  25. #include <libc/dosio.h>
  26. #include <libc/bss.h>
  27.  
  28. #include "xstat.h"
  29.  
  30. static int xstat_count = -1;
  31.  
  32. /* Some fields of struct stat are expensive to compute under DOS,
  33.    because they require multiple disk accesses.  Fortunately, many
  34.    DOS programs don't care about these.  To leave both pedants (like
  35.    me) and performance-oriented guys happy, a variable is provided
  36.    which controls which expensive fields should be computed.  To get
  37.    the fastest stat() for your program, clear the bits for only those
  38.    features you need and set the others.
  39.  
  40.    This improvement was suggested by Charles Sandmann
  41.    <sandmann@clio.rice.edu> and DJ Delorie <dj@delorie.com>.  */
  42.  
  43. #define _STAT_INODE         1   /* should we bother getting inode numbers? */
  44. #define _STAT_EXEC_EXT      2   /* get execute bits from file extension? */
  45. #define _STAT_EXEC_MAGIC    4   /* get execute bits from magic signature? */
  46. #define _STAT_DIRSIZE       8   /* compute directory size? */
  47. #define _STAT_ROOT_TIME  0x10   /* try to get root dir time stamp? */
  48. #define _STAT_WRITEBIT   0x20   /* fstat() needs write bit? */
  49.  
  50. /* Should we bother about executables at all? */
  51. #define _STAT_EXECBIT       (_STAT_EXEC_EXT | _STAT_EXEC_MAGIC)
  52.  
  53. /* By default, all the bits are reset (including as yet unused ones), so
  54.    people who don't care will transparently have the full version.  */
  55. unsigned short _djstat_flags;
  56.  
  57. /* As we depend on undocumented DOS features, we could fail in some
  58.    incompatible environment or future DOS versions.  If we do, the
  59.    following variable will have some of its bits set.  Each bit
  60.    describes a single feature which we tried to use and failed.
  61.    The function _djstat_describe_lossage() may be called to print a
  62.    human-readable description of the bits which were set by the last
  63.    call to f?stat().  This should make debugging f?stat() failures
  64.    in an unanticipated environment a lot easier.
  65.  
  66.    This improvement was suggested by Charles Sandmann
  67.    <sandmann@clio.rice.edu>.  */
  68.  
  69. unsigned short _djstat_fail_bits;
  70.  
  71. /* ----------------------------------------------------------------------- */
  72.  
  73. /* Convert file date and time to time_t value suitable for
  74.    struct stat fields.  */
  75.  
  76. time_t
  77. _file_time_stamp(unsigned int dos_ftime)
  78. {
  79.   struct tm file_tm;
  80.  
  81.   memset(&file_tm, 0, sizeof(struct tm));
  82.   file_tm.tm_isdst = -1;    /* let mktime() determine if DST is in effect */
  83.  
  84.   file_tm.tm_sec  = (dos_ftime & 0x1f) * 2;
  85.   file_tm.tm_min  = (dos_ftime >>  5) & 0x3f;
  86.   file_tm.tm_hour = (dos_ftime >> 11) & 0x1f;
  87.   file_tm.tm_mday = (dos_ftime >> 16) & 0x1f;
  88.   file_tm.tm_mon  = ((dos_ftime >> 21) & 0x0f) - 1; /* 0 = January */
  89.   file_tm.tm_year = (dos_ftime >> 25) + 80;
  90.  
  91.   return mktime(&file_tm);
  92. }
  93.  
  94. /* Get time stamp of a DOS file packed as a 32-bit int.
  95.  * This does what Borland's getftime() does, except it doesn't
  96.  * pollute the application namespace and returns an int instead
  97.  * of struct ftime with packed bit-fields.
  98.  */
  99.  
  100.  
  101. int
  102. _getftime(int fhandle, unsigned int *dos_ftime)
  103. {
  104.   __dpmi_regs regs;
  105.  
  106.   regs.x.ax = 0x5700;
  107.   regs.x.bx = fhandle;
  108.   __dpmi_int(0x21, ®s);
  109.  
  110.   if (regs.x.flags & 1)
  111.     {
  112.       errno = __doserr_to_errno(regs.x.ax);
  113.       return -1;
  114.     }
  115.  
  116.   *dos_ftime = ((unsigned int)regs.x.dx << 16) + (unsigned int)regs.x.cx;
  117.  
  118.   return 0;
  119. }
  120.  
  121. /* Invent an inode number for those files which don't have valid DOS
  122.  * cluster number.  These could be: devices like /dev/nul; empty
  123.  * files which were not allocated disk space yet; or files on
  124.  * networked drives, for which the redirector doesn't bring the
  125.  * cluster number.
  126.  *
  127.  * To ensure proper operation of this function, you must call it
  128.  * with a filename in some canonical form.  E.g., with a name
  129.  * returned by truename(), or that returned by _fixpath().  The
  130.  * point here is that the entire program MUST abide by these
  131.  * conventions through its operation, or else you risk getting
  132.  * different inode numbers for the same file.
  133.  *
  134.  * This function is due to Eric Backus and was taken with minor
  135.  * modifications from stat.c, as included in DJGPP 1.11m5.
  136.  * The function now returns 0 instead of -1 if it can't allocate
  137.  * memory for a new name, so that f?stat() won't fail if the inode
  138.  * is unavailable, but return zero inode instead.
  139.  */
  140.  
  141. /*
  142.   (c) Copyright 1992 Eric Backus
  143.  
  144.   This software may be used freely so long as this copyright notice is
  145.   left intact.  There is no warranty on this software.
  146. */
  147.  
  148. struct name_list
  149. {
  150.   struct name_list *next;
  151.   char             *name;
  152.   unsigned          mtime;
  153.   unsigned long     size;
  154.   long              inode;
  155. };
  156.  
  157. ino_t
  158. _invent_inode(const char *name, unsigned time_stamp, unsigned long fsize)
  159. {
  160.   static struct name_list  *name_list[256];
  161.  
  162.   /* If the st_inode is wider than a short int, we will count up
  163.    * from USHRT_MAX+1 and thus ensure there will be no clashes with
  164.    * actual cluster numbers.
  165.    * Otherwise, we must count downward from USHRT_MAX, which could
  166.    * yield two files with identical inode numbers: one from actual
  167.    * DOS cluster number, and another from this function.  In the
  168.    * latter case, we still have at least 80 inode numbers before
  169.    * we step into potentially used numbers, because some FAT entries
  170.    * are reserved to mean EOF, unused entry and other special codes,
  171.    * and the FAT itself uses up some clusters which aren't counted.
  172.    */
  173.   static int         dir = (sizeof(ino_t) > 2 ? 1 : -1);
  174.  
  175.   /* INODE_COUNT is declared LONG and not ino_t, because some DOS-based
  176.    * compilers use short or unsigned short for ino_t.
  177.    */
  178.   static long        inode_count = (sizeof(ino_t) > 2
  179.                                     ? (long)USHRT_MAX + 1L
  180.                                     : USHRT_MAX);
  181.  
  182.   struct name_list  *name_ptr, *prev_ptr;
  183.   const char        *p;
  184.   int                hash;
  185.  
  186.   /* Force initialization in restarted programs (emacs).  */
  187.   if (xstat_count != __bss_count)
  188.     {
  189.       xstat_count = __bss_count;
  190.       inode_count = (sizeof(ino_t) > 2 ? (long)USHRT_MAX + 1L : USHRT_MAX);
  191.       memset (name_list, 0, sizeof name_list);
  192.     }
  193.  
  194.   if (!name)
  195.     return 0;
  196.  
  197.   /* Skip over device and leading slash.  This will allow for less
  198.    * inode numbers to be used, because there is nothing bad in generating
  199.    * identical inode for two files which belong to different drives.
  200.    */
  201.   if (*name && name[1] == ':' && (name[2] == '\\' || name[2] == '/'))
  202.   {
  203.     /* If this is a root directory, return inode = 1.  This is compatible
  204.        with the code on stat.c which deals with root directories. */
  205.     if (name[3] == 0)
  206.       return (ino_t)1;
  207.  
  208.     name += 3;
  209.   }
  210.  
  211.   /* If the passed name is empty, invent a new inode unconditionally.
  212.    * This is for those unfortunate circumstances where we couldn't
  213.    * get a name (e.g., fstat() under Novell).  For these we want at
  214.    * least to ensure that no two calls will get the same inode number.
  215.    * The lossage here is that you get different inodes even if you call
  216.    * twice with the same file.  Sigh...
  217.    */
  218.   if (!*name)
  219.     {
  220.       ino_t retval = inode_count;
  221.  
  222.       inode_count += dir;
  223.       return retval;
  224.     }
  225.  
  226.   /* We could probably use a better hash than this */
  227.   p = name;
  228.   hash = 0;
  229.   while (*p != '\0')
  230.     hash += *p++;
  231.   hash &= 0xff;
  232.  
  233.   /* Have we seen this string? */
  234.   name_ptr = name_list[hash];
  235.   prev_ptr = name_ptr;
  236.   while (name_ptr)
  237.     {
  238.       if (strcmp(name, name_ptr->name) == 0 &&
  239.           name_ptr->mtime == time_stamp &&
  240.           name_ptr->size  == fsize)
  241.         break;
  242.       prev_ptr = name_ptr;
  243.       name_ptr = name_ptr->next;
  244.     }
  245.  
  246.   if (name_ptr)
  247.     /* Same string, time stamp, and size, so same inode */
  248.     return name_ptr->inode;
  249.   else
  250.     {
  251.       ino_t retval;
  252.       
  253.       /* New string with same hash code */
  254.       name_ptr = (struct name_list *)malloc(sizeof *name_ptr);
  255.       if (name_ptr == 0)
  256.         return 0;
  257.       name_ptr->next = (struct name_list *)0;
  258.       name_ptr->name = (char *)malloc(strlen(name)+1);
  259.       if (name_ptr->name == 0)
  260.       {
  261.     free(name_ptr);
  262.     return 0;
  263.       }
  264.       strcpy(name_ptr->name, name);
  265.       name_ptr->mtime = time_stamp;
  266.       name_ptr->size = fsize;
  267.       name_ptr->inode = inode_count;
  268.       if (prev_ptr)
  269.         prev_ptr->next = name_ptr;
  270.       else
  271.         name_list[hash] = name_ptr;
  272.       retval = inode_count;
  273.       inode_count += dir; /* increment or decrement as appropriate */
  274.  
  275.       return retval;
  276.     }
  277. }
  278.