home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / rcs567s.zip / rcs / src / ms / dirent.c < prev    next >
C/C++ Source or Header  |  1993-12-31  |  8KB  |  301 lines

  1. /*
  2.  *  dirent.c - POSIX directory access routines for MS-DOS and OS/2
  3.  *
  4.  *  Author: Frank Whaley (few@well.sf.ca.us)
  5.  *
  6.  *  Copyright Frank Whaley 1992.  All rights reserved.
  7.  *
  8.  *  Permission to use, copy, modify, distribute, and sell this software
  9.  *  and its documentation for any purpose is hereby granted without fee,
  10.  *  provided that the above copyright notice appears in all copies of the
  11.  *  source code.  The name of the author may not be used to endorse or
  12.  *  promote products derived from this software without specific prior
  13.  *  written permission.
  14.  *
  15.  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  16.  *  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  17.  *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  *
  19.  *  CAVEATS:
  20.  *    The associated 'dirent.h' file should be copied into your system
  21.  *    include directory if the '#include <dirent.h>' syntax will be used,
  22.  *    otherwise a '-I.' switch must be added to command lines.
  23.  *
  24.  *    This code was originally developed with Turbo C, and continues to
  25.  *    use TC's function and structure names.  Numerous macros make the
  26.  *    code palatable to MSC 5.1/6.0 for MS-DOS and OS/2.  The macros
  27.  *    depend on four defines: __TURBOC__, __MSC__, __MSDOS__, and __OS2__.
  28.  *    The TC and BC compilers provide __TURBOC__ and __MSDOS__ as
  29.  *    appropriate; MSC doesn't provide any of these flags so they must
  30.  *    be given on the command line.  Sample commands for building test
  31.  *    programs (see '#ifdef TEST' below):
  32.  *      tcc -DTEST dirent.c
  33.  *      cl -DTEST -D__MSC__ -D__MSDOS__ dirent.c
  34.  *      cl -Lp -DTEST -D__MSC__ -D__OS2__ dirent.c
  35.  *
  36.  *    This code reads an entire directory into memory, and thus is not
  37.  *    a good choice for scanning very large directories.
  38.  *
  39.  *    POSIX requires that the rewinddir() function re-scan the directory,
  40.  *    so this code must preserve the original directory name.  If the
  41.  *    name given is a relative path (".", "..", etc.) and the current
  42.  *    directory is changed, moved, or deleted between opendir() and
  43.  *    rewinddir(), a different directory will be scanned by rewinddir().
  44.  *    The directory name could be qualified by opendir(), but this process
  45.  *    yields unusable names for network drives.
  46.  *
  47.  *    This code provides only filenames, as that is all that is required
  48.  *    by POSIX.  Considerable other information is available from the
  49.  *    MS-DOS and OS/2 directory search functions.  This package should not
  50.  *    be considered as a general-purpose directory scanner, but rather as
  51.  *    a tool to simplify porting other programs.
  52.  */
  53.  
  54. #include <dirent.h>
  55. #include <errno.h>
  56. #include <stdio.h>
  57. #include <stdlib.h>
  58. #include <string.h>
  59. #include <sys/types.h>
  60. #include <sys/stat.h>
  61.  
  62. #ifndef S_ISDIR
  63. #define S_ISDIR(n) ((n)&S_IFDIR)
  64. #endif
  65.  
  66. #ifdef __TURBOC__
  67. #    include <alloc.h>
  68. #    include <dir.h>
  69. #    include <mem.h>
  70.     typedef struct ffblk FIND_T;
  71. #    define findclose(f)
  72. #    define FA_RDONLY    0x01
  73. #    define FA_HIDDEN    0x02
  74. #    define FA_SYSTEM    0x04
  75. #    define FA_LABEL        0x08
  76. #    define FA_DIREC        0x10
  77. #    define FA_ARCH        0x20
  78. #endif
  79.  
  80. #if defined(__MSC__) && defined(__MSDOS__)
  81. #    include <dos.h>
  82. #    include <malloc.h>
  83. #    include <memory.h>
  84.     typedef struct find_t FIND_T;
  85. #    define findfirst(n,f,a)    _dos_findfirst(n,a,f)
  86. #    define findnext(f)    _dos_findnext(f)
  87. #    define findclose(f)
  88. #    define ff_name        name
  89. #    define FA_RDONLY    _A_RDONLY
  90. #    define FA_HIDDEN    _A_HIDDEN
  91. #    define FA_SYSTEM    _A_SYSTEM
  92. #    define FA_LABEL        _A_VOLID
  93. #    define FA_DIREC        _A_SUBDIR
  94. #    define FA_ARCH        _A_ARCH
  95. #endif
  96.  
  97. #if defined(__MSC__) && defined(__OS2__)
  98. #    define INCL_DOS
  99. #    include <os2.h>
  100. #    include <malloc.h>
  101. #    include <memory.h>
  102.     typedef struct {
  103.         HDIR ft_handle;
  104.         FILEFINDBUF ft_ffb;
  105.         int ft_count;
  106.     } FIND_T;
  107. #    define findfirst(n,f,a)    \
  108.         ( \
  109.             (f)->ft_handle = HDIR_CREATE, \
  110.             (f)->ft_count = 1, \
  111.             DosFindFirst(n, &(f)->ft_handle, a, &(f)->ft_ffb, \
  112.                 sizeof((f)->ft_ffb), &(f)->ft_count, 0L \
  113.             ) \
  114.         )
  115. #    define findnext(f) \
  116.         DosFindNext( \
  117.             (f)->ft_handle, &(f)->ft_ffb, \
  118.             sizeof((f)->ft_ffb), &(f)->ft_count \
  119.         )
  120. #    define findclose(f)    DosFindClose((f)->ft_handle);
  121. #    define ff_name        ft_ffb.achName
  122. #    define FA_RDONLY    0x01
  123. #    define FA_HIDDEN    0x02
  124. #    define FA_SYSTEM    0x04
  125. #    define FA_LABEL        0x08
  126. #    define FA_DIREC        0x10
  127. #    define FA_ARCH        0x20
  128. #endif
  129.  
  130.     /*  mask for all interesting files  */
  131. #define ALL    (FA_RDONLY+FA_HIDDEN+FA_SYSTEM+FA_DIREC)
  132.  
  133. struct dirnames {
  134.     struct dirnames *next;    /* next name in directory */
  135.     char name[1];        /* null terminated name; may be larger */
  136. };
  137.  
  138. struct __DIRENT {
  139.     struct dirnames *names;    /* list of names */
  140.     struct dirnames const *current;    /* position (0 if at directory end) */
  141.     char path[1];        /* null terminated pathname; may be larger */
  142. };
  143.  
  144. static char const pattern[] = "\\*.*";
  145.  
  146. /* forward declarations */
  147. static int loadDir(DIR *dir);
  148. static void freenames(DIR *dir);
  149.  
  150. /* Open the directory NAME for reading.  */
  151.     DIR *
  152. opendir(char const *name)
  153. {
  154.     DIR *dir;
  155.  
  156.     if (!*name) {
  157.         errno = ENOENT;
  158.         return 0;
  159.     }
  160.  
  161.     if (!(dir = malloc(sizeof(DIR) + strlen(name) + sizeof(pattern)-1))) {
  162.         errno = ENOMEM;
  163.         return 0;
  164.     }
  165.  
  166.     strcpy(dir->path, name);
  167.     if (loadDir(dir) != 0) {
  168.         int e = errno;
  169.         free(dir);
  170.         errno = e;
  171.         return 0;
  172.     }
  173.  
  174.     return dir;
  175. }
  176.  
  177. /* Close the directory DIR.  */
  178.     int
  179. closedir(DIR *dir)
  180. {
  181.     freenames(dir);
  182.     free(dir);
  183.     return 0;
  184. }
  185.  
  186. /* Yield the next entry in the directory DIR.  */
  187.     struct dirent *
  188. readdir(DIR *dir)
  189. {
  190.     struct dirnames const *c = dir->current;
  191.     if (!c)
  192.         return 0;
  193.     dir->current = c->next;
  194.     return (struct dirent *) c->name;
  195. }
  196.  
  197. /* Rewind the directory DIR; this requires rereading it.  */
  198.     void
  199. rewinddir(DIR *dir)
  200. {
  201.     freenames(dir);
  202.     loadDir(dir);
  203. }
  204.  
  205. /* Load the directory DIR from disk.  */
  206.     static int
  207. loadDir(DIR *dir)
  208. {
  209.     char *path = dir->path,  *pathend = path + strlen(path);
  210.     int mode;
  211.     FIND_T ff;
  212.     struct stat statb;
  213.  
  214.     /* Is it a directory?  */
  215.     if (stat(path, &statb) != 0)
  216.         return -1;
  217.     if (! S_ISDIR(statb.st_mode)) {
  218.         errno = ENOTDIR;
  219.         return -1;
  220.     }
  221.  
  222.     /* Build pattern string.  */
  223.     strcpy(pathend,  pattern  +  !!strchr("\\/:", pathend[-1]));
  224.  
  225.     if (findfirst(path, &ff, ALL) == 0) {
  226.         struct dirnames **np = &dir->names;
  227.         do {
  228.             /*  Add name if not "." or ".."  */
  229.             if (ff.ff_name[0]!='.'  ||
  230.                 ff.ff_name[1] && (ff.ff_name[1]!='.'||ff.ff_name[2])
  231.             ) {
  232.                 struct dirnames *p = malloc(
  233.                     sizeof(struct dirnames)+strlen(ff.ff_name)
  234.                 );
  235.                 if (!p) {
  236.                     freenames(dir);
  237.                     errno = ENOMEM;
  238.                     *pathend = 0;
  239.                     return -1;
  240.                 }
  241.                 strcpy(p->name, ff.ff_name);
  242.                 p->next = 0;
  243.                 *np = p;
  244.                 np = &p->next;
  245.             }
  246.         } while (findnext(&ff) == 0);
  247.     }
  248.     findclose(&ff);
  249.     dir->current = dir->names;
  250.     *pathend = 0;
  251.     return 0;
  252. }
  253.  
  254. /* Free all names in the directory DIR.  */
  255.     static void
  256. freenames(DIR *dir)
  257. {
  258.     struct dirnames *o = dir->names, *n;
  259.     for (o = dir->names;  o;  o = n) {
  260.         n = o->next;
  261.         free(o);
  262.     }
  263. }
  264.  
  265. #ifdef TEST
  266.     int
  267. main(int argc, char *argv[])
  268. {
  269.     DIR *dir;
  270.     struct dirent const *d;
  271.     long pos;
  272.  
  273.     /* Check arguments.  */
  274.     if (argc != 2) {
  275.         fprintf(stderr, "Usage: dirent <directory>\n");
  276.         return 1;
  277.     }
  278.  
  279.     /* Try to open the given directory.  */
  280.     if (!(dir = opendir(argv[1]))) {
  281.         fprintf(stderr, "cannot open %s\n", argv[1]);
  282.         return 1;
  283.     }
  284.  
  285.     /* Walk the directory once forward.  */
  286.     while ((d = readdir(dir)))
  287.         printf("%s\n", d->d_name);
  288.  
  289.     /* Rewind.  */
  290.     rewinddir(dir);
  291.  
  292.     /* Scan to the end again.  */
  293.     while ((d = readdir(dir)))
  294.         continue;
  295.  
  296.     /* Close and exit.  */
  297.     printf("closedir() returns %d\n", closedir(dir));
  298.     return 0;
  299. }
  300. #endif
  301.