home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / prgramer / rcs / sources / dirent.c < prev    next >
C/C++ Source or Header  |  1992-03-06  |  8KB  |  297 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 file names, 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. #ifdef __TURBOC__
  63. #    include <alloc.h>
  64. #    include <dir.h>
  65. #    include <mem.h>
  66.     typedef struct ffblk FIND_T;
  67. #    define findclose(f)
  68. #    define FA_RDONLY    0x01
  69. #    define FA_HIDDEN    0x02
  70. #    define FA_SYSTEM    0x04
  71. #    define FA_LABEL        0x08
  72. #    define FA_DIREC        0x10
  73. #    define FA_ARCH        0x20
  74. #endif
  75.  
  76. #if defined(__MSC__) && defined(__MSDOS__)
  77. #    include <dos.h>
  78. #    include <malloc.h>
  79. #    include <memory.h>
  80.     typedef struct find_t FIND_T;
  81. #    define findfirst(n,f,a)    _dos_findfirst(n,a,f)
  82. #    define findnext(f)    _dos_findnext(f)
  83. #    define findclose(f)
  84. #    define ff_name        name
  85. #    define FA_RDONLY    _A_RDONLY
  86. #    define FA_HIDDEN    _A_HIDDEN
  87. #    define FA_SYSTEM    _A_SYSTEM
  88. #    define FA_LABEL        _A_VOLID
  89. #    define FA_DIREC        _A_SUBDIR
  90. #    define FA_ARCH        _A_ARCH
  91. #endif
  92.  
  93. #if defined(__MSC__) && defined(__OS2__)
  94. #    define INCL_DOS
  95. #    include <os2.h>
  96. #    include <malloc.h>
  97. #    include <memory.h>
  98.     typedef struct {
  99.         HDIR ft_handle;
  100.         FILEFINDBUF ft_ffb;
  101.         int ft_count;
  102.     } FIND_T;
  103. #    define findfirst(n,f,a)    \
  104.         ( \
  105.             (f)->ft_handle = HDIR_CREATE, \
  106.             (f)->ft_count = 1, \
  107.             DosFindFirst(n, &(f)->ft_handle, a, &(f)->ft_ffb, \
  108.                 sizeof((f)->ft_ffb), &(f)->ft_count, 0L \
  109.             ) \
  110.         )
  111. #    define findnext(f) \
  112.         DosFindNext( \
  113.             (f)->ft_handle, &(f)->ft_ffb, \
  114.             sizeof((f)->ft_ffb), &(f)->ft_count \
  115.         )
  116. #    define findclose(f)    DosFindClose((f)->ft_handle);
  117. #    define ff_name        ft_ffb.achName
  118. #    define FA_RDONLY    0x01
  119. #    define FA_HIDDEN    0x02
  120. #    define FA_SYSTEM    0x04
  121. #    define FA_LABEL        0x08
  122. #    define FA_DIREC        0x10
  123. #    define FA_ARCH        0x20
  124. #endif
  125.  
  126.     /*  mask for all interesting files  */
  127. #define ALL    (FA_RDONLY+FA_HIDDEN+FA_SYSTEM+FA_DIREC)
  128.  
  129. struct dirnames {
  130.     struct dirnames *next;    /* next name in directory */
  131.     char name[1];        /* null terminated name; may be larger */
  132. };
  133.  
  134. struct __DIRENT {
  135.     struct dirnames *names;    /* list of names */
  136.     struct dirnames const *current;    /* position (0 if at directory end) */
  137.     char path[1];        /* null terminated path name; may be larger */
  138. };
  139.  
  140. static char const pattern[] = "\\*.*";
  141.  
  142. /* forward declarations */
  143. static int loadDir(DIR *dir);
  144. static void freenames(DIR *dir);
  145.  
  146. /* Open the directory NAME for reading.  */
  147.     DIR *
  148. opendir(char const *name)
  149. {
  150.     DIR *dir;
  151.  
  152.     if (!*name) {
  153.         errno = ENOENT;
  154.         return 0;
  155.     }
  156.  
  157.     if (!(dir = malloc(sizeof(DIR) + strlen(name) + sizeof(pattern)-1))) {
  158.         errno = ENOMEM;
  159.         return 0;
  160.     }
  161.  
  162.     strcpy(dir->path, name);
  163.     if (loadDir(dir) != 0) {
  164.         int e = errno;
  165.         free(dir);
  166.         errno = e;
  167.         return 0;
  168.     }
  169.  
  170.     return dir;
  171. }
  172.  
  173. /* Close the directory DIR.  */
  174.     int
  175. closedir(DIR *dir)
  176. {
  177.     freenames(dir);
  178.     free(dir);
  179.     return 0;
  180. }
  181.  
  182. /* Yield the next entry in the directory DIR.  */
  183.     struct dirent *
  184. readdir(DIR *dir)
  185. {
  186.     struct dirnames const *c = dir->current;
  187.     if (!c)
  188.         return 0;
  189.     dir->current = c->next;
  190.     return (struct dirent *) c->name;
  191. }
  192.  
  193. /* Rewind the directory DIR; this requires rereading it.  */
  194.     void
  195. rewinddir(DIR *dir)
  196. {
  197.     freenames(dir);
  198.     loadDir(dir);
  199. }
  200.  
  201. /* Load the directory DIR from disk.  */
  202.     static int
  203. loadDir(DIR *dir)
  204. {
  205.     char *path = dir->path,  *pathend = path + strlen(path);
  206.     int mode;
  207.     FIND_T ff;
  208.     struct stat statb;
  209.  
  210.     /* Is it a directory?  */
  211.     if (stat(path, &statb) != 0)
  212.         return -1;
  213.         if ((statb.st_mode & S_IFMT) != S_IFDIR) {
  214.         errno = ENOTDIR;
  215.         return -1;
  216.     }
  217.  
  218.     /* Build pattern string.  */
  219.     strcpy(pathend,  pattern  +  !!strchr("\\/:", pathend[-1]));
  220.  
  221.     if (findfirst(path, &ff, ALL) == 0) {
  222.         struct dirnames **np = &dir->names;
  223.         do {
  224.             /*  Add name if not "." or ".."  */
  225.             if (ff.ff_name[0]!='.'  ||
  226.                 ff.ff_name[1] && (ff.ff_name[1]!='.'||ff.ff_name[2])
  227.             ) {
  228.                 struct dirnames *p = malloc(
  229.                     sizeof(struct dirnames)+strlen(ff.ff_name)
  230.                 );
  231.                 if (!p) {
  232.                     freenames(dir);
  233.                     errno = ENOMEM;
  234.                     *pathend = 0;
  235.                     return -1;
  236.                 }
  237.                 strcpy(p->name, ff.ff_name);
  238.                 p->next = 0;
  239.                 *np = p;
  240.                 np = &p->next;
  241.             }
  242.         } while (findnext(&ff) == 0);
  243.     }
  244.     findclose(&ff);
  245.     dir->current = dir->names;
  246.     *pathend = 0;
  247.     return 0;
  248. }
  249.  
  250. /* Free all names in the directory DIR.  */
  251.     static void
  252. freenames(DIR *dir)
  253. {
  254.     struct dirnames *o = dir->names, *n;
  255.     for (o = dir->names;  o;  o = n) {
  256.         n = o->next;
  257.         free(o);
  258.     }
  259. }
  260.  
  261. #ifdef TEST
  262.     int
  263. main(int argc, char *argv[])
  264. {
  265.     DIR *dir;
  266.     struct dirent const *d;
  267.     long pos;
  268.  
  269.     /* Check arguments.  */
  270.     if (argc != 2) {
  271.         fprintf(stderr, "Usage: dirent <directory>\n");
  272.         return 1;
  273.     }
  274.  
  275.     /* Try to open the given directory.  */
  276.     if (!(dir = opendir(argv[1]))) {
  277.         fprintf(stderr, "cannot open %s\n", argv[1]);
  278.         return 1;
  279.     }
  280.  
  281.     /* Walk the directory once forward.  */
  282.     while ((d = readdir(dir)))
  283.         printf("%s\n", d->d_name);
  284.  
  285.     /* Rewind.  */
  286.     rewinddir(dir);
  287.  
  288.     /* Scan to the end again.  */
  289.     while ((d = readdir(dir)))
  290.         continue;
  291.  
  292.     /* Close and exit.  */
  293.     printf("closedir() returns %d\n", closedir(dir));
  294.     return 0;
  295. }
  296. #endif
  297.