home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume10 / dir-lib.pch < prev    next >
Text File  |  1987-07-06  |  13KB  |  500 lines

  1. Path: uunet!rs
  2. From: rs@uunet.UU.NET (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v10i042: Bugfix to Doug Gwyn's portable directory library
  5. Message-ID: <582@uunet.UU.NET>
  6. Date: 7 Jul 87 23:23:20 GMT
  7. Organization: UUNET Communications Services, Arlington, VA
  8. Lines: 489
  9. Approved: rs@uunet.uu.net
  10.  
  11. Mod.sources: Volume 10, Number 42
  12. Submitted by: Doug Gwyn (VLD/VMB) <gwyn@brl.arpa>
  13. Archive-name: dir-lib.pch
  14.  
  15. [ This was recently put out on comp.sources.bugs; it fixes a problem
  16.   in V7 directories when the name is 14 characters long.   I think
  17.   Doug would agree with me that Amiga, MS-DOS, Minix, and other ports
  18.   would be a great thing to have...  --r$ ]
  19.  
  20. #!/bin/sh
  21. # Self-unpacking archive format.  To unbundle, sh this file.
  22. echo 'opendir.c' 1>&2
  23. cat >'opendir.c' <<'END OF opendir.c'
  24. /*
  25.     opendir -- open a directory stream
  26.  
  27.     last edit:    16-Jun-1987    D A Gwyn
  28. */
  29.  
  30. #include    <sys/errno.h>
  31. #include    <sys/types.h>
  32. #include    <sys/stat.h>
  33. #include    <dirent.h>
  34.  
  35. #ifdef BSD_SYSV
  36. #define open    _open            /* avoid emulation overhead */
  37. #endif
  38.  
  39. typedef char    *pointer;        /* (void *) if you have it */
  40.  
  41. extern void    free();
  42. extern pointer    malloc();
  43. extern int    open(), close(), fstat();
  44.  
  45. extern int    errno;
  46.  
  47. #ifndef NULL
  48. #define    NULL    0
  49. #endif
  50.  
  51. #ifndef O_RDONLY
  52. #define    O_RDONLY    0
  53. #endif
  54.  
  55. #ifndef S_ISDIR                /* macro to test for directory file */
  56. #define    S_ISDIR( mode )        (((mode) & S_IFMT) == S_IFDIR)
  57. #endif
  58.  
  59. DIR *
  60. opendir( dirname )
  61.     char        *dirname;    /* name of directory */
  62.     {
  63.     register DIR    *dirp;        /* -> malloc'ed storage */
  64.     register int    fd;        /* file descriptor for read */
  65.     struct stat    sbuf;        /* result of fstat() */
  66.  
  67.     if ( (fd = open( dirname, O_RDONLY )) < 0 )
  68.         return NULL;        /* errno set by open() */
  69.  
  70.     if ( fstat( fd, &sbuf ) != 0 || !S_ISDIR( sbuf.st_mode ) )
  71.         {
  72.         (void)close( fd );
  73.         errno = ENOTDIR;
  74.         return NULL;        /* not a directory */
  75.         }
  76.  
  77.     if ( (dirp = (DIR *)malloc( sizeof(DIR) )) == NULL
  78.       || (dirp->dd_buf = (char *)malloc( (unsigned)DIRBUF )) == NULL
  79.        )    {
  80.         register int    serrno = errno;
  81.                     /* errno set to ENOMEM by sbrk() */
  82.  
  83.         if ( dirp != NULL )
  84.             free( (pointer)dirp );
  85.  
  86.         (void)close( fd );
  87.         errno = serrno;
  88.         return NULL;        /* not enough memory */
  89.         }
  90.  
  91.     dirp->dd_fd = fd;
  92.     dirp->dd_loc = dirp->dd_size = 0;    /* refill needed */
  93.  
  94.     return dirp;
  95.     }
  96. END OF opendir.c
  97. echo 'seekdir.c' 1>&2
  98. cat >'seekdir.c' <<'END OF seekdir.c'
  99. /*
  100.     seekdir -- reposition a directory stream
  101.  
  102.     last edit:    24-May-1987    D A Gwyn
  103.  
  104.     An unsuccessful seekdir() will in general alter the current
  105.     directory position; beware.
  106.  
  107.     NOTE:    4.nBSD directory compaction makes seekdir() & telldir()
  108.         practically impossible to do right.  Avoid using them!
  109. */
  110.  
  111. #include    <sys/errno.h>
  112. #include    <sys/types.h>
  113. #include    <dirent.h>
  114.  
  115. extern off_t    lseek();
  116.  
  117. extern int    errno;
  118.  
  119. #ifndef NULL
  120. #define    NULL    0
  121. #endif
  122.  
  123. #ifndef SEEK_SET
  124. #define    SEEK_SET    0
  125. #endif
  126.  
  127. typedef int    bool;            /* Boolean data type */
  128. #define    false    0
  129. #define    true    1
  130.  
  131. void
  132. seekdir( dirp, loc )
  133.     register DIR    *dirp;        /* stream from opendir() */
  134.     register off_t    loc;        /* position from telldir() */
  135.     {
  136.     register bool    rewind;        /* "start over when stymied" flag */
  137.  
  138.     if ( dirp == NULL || dirp->dd_buf == NULL )
  139.         {
  140.         errno = EFAULT;
  141.         return;            /* invalid pointer */
  142.         }
  143.  
  144.     /* A (struct dirent)'s d_off is an invented quantity on 4.nBSD
  145.        NFS-supporting systems, so it is not safe to lseek() to it. */
  146.  
  147.     /* Monotonicity of d_off is heavily exploited in the following. */
  148.  
  149.     /* This algorithm is tuned for modest directory sizes.  For
  150.        huge directories, it might be more efficient to read blocks
  151.        until the first d_off is too large, then back up one block,
  152.        or even to use binary search on the directory blocks.  I
  153.        doubt that the extra code for that would be worthwhile. */
  154.  
  155.     if ( dirp->dd_loc >= dirp->dd_size    /* invalid index */
  156.       || ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off > loc
  157.                     /* too far along in buffer */
  158.        )
  159.         dirp->dd_loc = 0;    /* reset to beginning of buffer */
  160.     /* else save time by starting at current dirp->dd_loc */
  161.  
  162.     for ( rewind = true; ; )
  163.         {
  164.         register struct dirent    *dp;
  165.  
  166.         /* See whether the matching entry is in the current buffer. */
  167.  
  168.         if ( (dirp->dd_loc < dirp->dd_size    /* valid index */
  169.            || readdir( dirp ) != NULL    /* next buffer read */
  170.            && (dirp->dd_loc = 0, true)    /* beginning of buffer set */
  171.              )
  172.           && (dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off
  173.             <= loc        /* match possible in this buffer */
  174.            )    {
  175.             for ( /* dp initialized above */ ;
  176.                   (char *)dp < &dirp->dd_buf[dirp->dd_size];
  177.                   dp = (struct dirent *)((char *)dp + dp->d_reclen)
  178.                 )
  179.                 if ( dp->d_off == loc )
  180.                     {    /* found it! */
  181.                     dirp->dd_loc =
  182.                         (char *)dp - dirp->dd_buf;
  183.                     return;
  184.                     }
  185.  
  186.             rewind = false;    /* no point in backing up later */
  187.             dirp->dd_loc = dirp->dd_size;    /* set end of buffer */
  188.             }
  189.         else            /* whole buffer past matching entry */
  190.             if ( !rewind )
  191.                 {    /* no point in searching further */
  192.                 errno = EINVAL;
  193.                 return;    /* no entry at specified loc */
  194.                 }
  195.             else    {    /* rewind directory and start over */
  196.                 rewind = false;    /* but only once! */
  197.  
  198.                 dirp->dd_loc = dirp->dd_size = 0;
  199.  
  200.                 if ( lseek( dirp->dd_fd, (off_t)0, SEEK_SET )
  201.                     != 0
  202.                    )
  203.                     return;    /* errno already set (EBADF) */
  204.  
  205.                 if ( loc == 0 )
  206.                     return; /* save time */
  207.                 }
  208.         }
  209.     }
  210. END OF seekdir.c
  211. echo 'getdents.c' 1>&2
  212. cat >'getdents.c' <<'END OF getdents.c'
  213. /*
  214.     getdents -- get directory entries in a file system independent format
  215.             (SVR3 system call emulation)
  216.  
  217.     last edit:    06-Jul-1987    D A Gwyn
  218.  
  219.     This single source file supports several different methods of
  220.     getting directory entries from the operating system.  Define
  221.     whichever one of the following describes your system:
  222.  
  223.     UFS    original UNIX filesystem (14-character name limit)
  224.     BFS    4.2BSD (also 4.3BSD) native filesystem (long names)
  225.     NFS    getdirentries() system call
  226.  
  227.     Also define any of the following that are pertinent:
  228.  
  229.     ATT_SPEC    check user buffer address for longword alignment
  230.     BSD_SYSV    BRL UNIX System V emulation environment on 4.nBSD
  231.     UNK        have _getdents() system call, but kernel may not
  232.             support it
  233.  
  234.     If your C library has a getdents() system call interface, but you
  235.     can't count on all kernels on which your application binaries may
  236.     run to support it, change the system call interface name to
  237.     _getdents() and define "UNK" to enable the system-call validity
  238.     test in this "wrapper" around _getdents().
  239.  
  240.     If your system has a getdents() system call that is guaranteed 
  241.     to always work, you shouldn't be using this source file at all.
  242. */
  243.  
  244. #include    <sys/errno.h>
  245. #include    <sys/types.h>
  246. #ifdef BSD_SYSV
  247. #include    <sys/_dir.h>        /* BSD flavor, not System V */
  248. #else
  249. #include    <sys/dir.h>
  250. #undef    MAXNAMLEN            /* avoid conflict with SVR3 */
  251.     /* Good thing we don't need to use the DIRSIZ() macro! */
  252. #ifdef d_ino                /* 4.3BSD/NFS using d_fileno */
  253. #undef    d_ino                /* (not absolutely necessary) */
  254. #else
  255. #define    d_fileno    d_ino        /* (struct direct) member */
  256. #endif
  257. #endif
  258. #include    <sys/dirent.h>
  259. #include    <sys/stat.h>
  260. #ifdef UNK
  261. #ifndef UFS
  262. #include "***** ERROR ***** UNK applies only to UFS"
  263. /* One could do something similar for getdirentries(), but I didn't bother. */
  264. #endif
  265. #include    <signal.h>
  266. #endif
  267.  
  268. #if defined(UFS) + defined(BFS) + defined(NFS) != 1    /* sanity check */
  269. #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
  270. #endif
  271.  
  272. #ifdef UFS
  273. #define    RecLen( dp )    (sizeof(struct direct))    /* fixed-length entries */
  274. #else    /* BFS || NFS */
  275. #define    RecLen( dp )    ((dp)->d_reclen)    /* variable-length entries */
  276. #endif
  277.  
  278. #ifdef NFS
  279. #ifdef BSD_SYSV
  280. #define    getdirentries    _getdirentries    /* package hides this system call */
  281. #endif
  282. extern int    getdirentries();
  283. static long    dummy;            /* getdirentries() needs basep */
  284. #define    GetBlock( fd, buf, n )    getdirentries( fd, buf, (unsigned)n, &dummy )
  285. #else    /* UFS || BFS */
  286. #ifdef BSD_SYSV
  287. #define read    _read            /* avoid emulation overhead */
  288. #endif
  289. extern int    read();
  290. #define    GetBlock( fd, buf, n )    read( fd, buf, (unsigned)n )
  291. #endif
  292.  
  293. #ifdef UNK
  294. extern int    _getdents();        /* actual system call */
  295. #endif
  296.  
  297. extern char    *strncpy();
  298. extern int    fstat();
  299. extern off_t    lseek();
  300.  
  301. extern int    errno;
  302.  
  303. #ifndef DIRBLKSIZ
  304. #define    DIRBLKSIZ    4096        /* directory file read buffer size */
  305. #endif
  306.  
  307. #ifndef NULL
  308. #define    NULL    0
  309. #endif
  310.  
  311. #ifndef SEEK_CUR
  312. #define    SEEK_CUR    1
  313. #endif
  314.  
  315. #ifndef S_ISDIR                /* macro to test for directory file */
  316. #define    S_ISDIR( mode )        (((mode) & S_IFMT) == S_IFDIR)
  317. #endif
  318.  
  319. #ifdef UFS
  320.  
  321. /*
  322.     The following routine is necessary to handle DIRSIZ-long entry names.
  323.     Thanks to Richard Todd for pointing this out.
  324. */
  325.  
  326. static int
  327. NameLen( name )                /* return # chars in embedded name */
  328.     char        name[];        /* -> name embedded in struct direct */
  329.     {
  330.     register char    *s;        /* -> name[.] */
  331.     register char    *stop = &name[DIRSIZ];    /* -> past end of name field */
  332.  
  333.     for ( s = &name[1];        /* (empty names are impossible) */
  334.           *s != '\0'        /* not NUL terminator */
  335.        && ++s < stop;        /* < DIRSIZ characters scanned */
  336.         )
  337.         ;
  338.  
  339.     return s - name;        /* # valid characters in name */
  340.     }
  341.  
  342. #else    /* BFS || NFS */
  343.  
  344. extern int    strlen();
  345.  
  346. #define    NameLen( name )    strlen( name )    /* names are always NUL-terminated */
  347.  
  348. #endif
  349.  
  350. #ifdef UNK
  351. static enum    { maybe, no, yes }    state = maybe;
  352.                     /* does _getdents() work? */
  353.  
  354. /*ARGSUSED*/
  355. static void
  356. sig_catch( sig )
  357.     int    sig;            /* must be SIGSYS */
  358.     {
  359.     state = no;            /* attempted _getdents() faulted */
  360.     }
  361. #endif
  362.  
  363. int
  364. getdents( fildes, buf, nbyte )        /* returns # bytes read;
  365.                        0 on EOF, -1 on error */
  366.     int            fildes;    /* directory file descriptor */
  367.     char            *buf;    /* where to put the (struct dirent)s */
  368.     unsigned        nbyte;    /* size of buf[] */
  369.     {
  370.     int            serrno;    /* entry errno */
  371.     off_t            offset;    /* initial directory file offset */
  372.     struct stat        statb;    /* fstat() info */
  373.     union    {
  374.         char        dblk[DIRBLKSIZ];
  375.                     /* directory file block buffer */
  376.         struct direct    dummy;    /* just for alignment */
  377.         }    u;        /* (avoids having to malloc()) */
  378.     register struct direct    *dp;    /* -> u.dblk[.] */
  379.     register struct dirent    *bp;    /* -> buf[.] */
  380.  
  381. #ifdef UNK
  382.     switch ( state )
  383.         {
  384.         void        (*shdlr)();    /* entry SIGSYS handler */
  385.         register int    retval;    /* return from _getdents() if any */
  386.  
  387.     case yes:            /* _getdents() is known to work */
  388.         return _getdents( fildes, buf, nbyte );
  389.  
  390.     case maybe:            /* first time only */
  391.         shdlr = signal( SIGSYS, sig_catch );
  392.         retval = _getdents( fildes, buf, nbyte );    /* try it */
  393.         (void)signal( SIGSYS, shdlr );
  394.  
  395.         if ( state == maybe )    /* SIGSYS did not occur */
  396.             {
  397.             state = yes;    /* so _getdents() must have worked */
  398.             return retval;
  399.             }
  400.         /* else fall through into emulation */
  401.  
  402. /*    case no:    /* fall through into emulation */
  403.         }
  404. #endif
  405.  
  406.     if ( buf == NULL
  407. #ifdef ATT_SPEC
  408.       || (unsigned long)buf % sizeof(long) != 0    /* ugh */
  409. #endif
  410.        )    {
  411.         errno = EFAULT;        /* invalid pointer */
  412.         return -1;
  413.         }
  414.  
  415.     if ( fstat( fildes, &statb ) != 0 )
  416.         return -1;        /* errno set by fstat() */
  417.  
  418.     if ( !S_ISDIR( statb.st_mode ) )
  419.         {
  420.         errno = ENOTDIR;    /* not a directory */
  421.         return -1;
  422.         }
  423.  
  424.     if ( (offset = lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
  425.         return -1;        /* errno set by lseek() */
  426.  
  427. #ifdef BFS                /* no telling what remote hosts do */
  428.     if ( (unsigned long)offset % DIRBLKSIZ != 0 )
  429.         {
  430.         errno = ENOENT;        /* file pointer probably misaligned */
  431.         return -1;
  432.         }
  433. #endif
  434.  
  435.     serrno = errno;            /* save entry errno */
  436.  
  437.     for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
  438.         {            /* convert next directory block */
  439.         int    size;
  440.  
  441.         do    size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
  442.         while ( size == -1 && errno == EINTR );
  443.  
  444.         if ( size <= 0 )
  445.             return size;    /* EOF or error (EBADF) */
  446.  
  447.         for ( dp = (struct direct *)u.dblk;
  448.               (char *)dp < &u.dblk[size];
  449.               dp = (struct direct *)((char *)dp + RecLen( dp ))
  450.             )    {
  451. #ifndef UFS
  452.             if ( dp->d_reclen <= 0 )
  453.                 {
  454.                 errno = EIO;    /* corrupted directory */
  455.                 return -1;
  456.                 }
  457. #endif
  458.  
  459.             if ( dp->d_fileno != 0 )
  460.                 {    /* non-empty; copy to user buffer */
  461.                 register int    reclen =
  462.                     DIRENTSIZ( NameLen( dp->d_name ) );
  463.  
  464.                 if ( (char *)bp + reclen > &buf[nbyte] )
  465.                     {
  466.                     errno = EINVAL;
  467.                     return -1;    /* buf too small */
  468.                     }
  469.  
  470.                 bp->d_ino = dp->d_fileno;
  471.                 bp->d_off = offset + ((char *)dp - u.dblk);
  472.                 bp->d_reclen = reclen;
  473.                 (void)strncpy( bp->d_name, dp->d_name,
  474.                            reclen - DIRENTBASESIZ
  475.                          );    /* adds NUL padding */
  476.  
  477.                 bp = (struct dirent *)((char *)bp + reclen);
  478.                 }
  479.             }
  480.  
  481. #ifndef BFS    /* 4.2BSD screwed up; fixed in 4.3BSD */
  482.         if ( (char *)dp > &u.dblk[size] )
  483.             {
  484.             errno = EIO;    /* corrupted directory */
  485.             return -1;
  486.             }
  487. #endif
  488.         }
  489.  
  490.     errno = serrno;            /* restore entry errno */
  491.     return (char *)bp - buf;    /* return # bytes read */
  492.     }
  493. END OF getdents.c
  494.  
  495. -- 
  496.  
  497. Rich $alz            "Anger is an energy"
  498. Cronus Project, BBN Labs    rsalz@pineapple.bbn.com
  499. Moderator, comp.sources.unix    sources@uunet.uu.net
  500.