Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

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