Pristine Ack-5.5
[Ack-5.5.git] / lang / cem / libcc.ansi / misc / getdents.c
1 /*
2         getdents -- get directory entries in a file system independent format
3                         (SVR3 system call emulation)
4
5         last edit:      06-Jul-1987     D A Gwyn
6
7         This single source file supports several different methods of
8         getting directory entries from the operating system.  Define
9         whichever one of the following describes your system:
10
11         UFS     original UNIX filesystem (14-character name limit)
12         BFS     4.2BSD (also 4.3BSD) native filesystem (long names)
13         NFS     getdirentries() system call
14
15         Also define any of the following that are pertinent:
16
17         ATT_SPEC        check user buffer address for longword alignment
18         BSD_SYSV        BRL UNIX System V emulation environment on 4.nBSD
19         UNK             have _getdents() system call, but kernel may not
20                         support it
21
22         If your C library has a getdents() system call interface, but you
23         can't count on all kernels on which your application binaries may
24         run to support it, change the system call interface name to
25         _getdents() and define "UNK" to enable the system-call validity
26         test in this "wrapper" around _getdents().
27
28         If your system has a getdents() system call that is guaranteed 
29         to always work, you shouldn't be using this source file at all.
30 */
31
32 #include        <errno.h>
33 #include        <stdlib.h>
34 #include        <string.h>
35 #include        <sys/errno.h>
36 #include        <sys/types.h>
37 #ifdef BSD_SYSV
38 #include        <sys/_dir.h>            /* BSD flavor, not System V */
39 #else
40 #if     defined(UFS)
41 #define DIRSIZ          14              /* 14 char filename in Version 7 */
42 #endif
43 #define MAXNAMLEN       255
44 struct  direct {
45         off_t   d_off;                  /* offset of next disk directory entry */
46         u_long  d_fileno;               /* file number of entry */
47         u_short d_reclen;               /* length of this record */
48         u_short d_namlen;               /* length of string in d_name */
49         char    d_name[MAXNAMLEN + 1];  /* name (up to MAXNAMLEN + 1) */
50 };
51 #undef  MAXNAMLEN                       /* avoid conflict with SVR3 */
52
53 #define d_ino   d_fileno                /* compatability */
54
55 #ifdef d_ino                            /* 4.3BSD/NFS using d_fileno */
56 #undef  d_ino                           /* (not absolutely necessary) */
57 #else
58 #define d_fileno        d_ino           /* (struct direct) member */
59 #endif
60 #endif
61 #include        <sys/dirent.h>
62 #include        <sys/stat.h>
63 #ifdef UNK
64 #ifndef UFS
65 #error  UNK applies only to UFS
66 /* One could do something similar for getdirentries(), but I didn't bother. */
67 #endif
68 #include        <signal.h>
69 #endif
70
71 #if     defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
72 #error  exactly one of UFS, BFS, or NFS must be defined
73 #endif
74
75 #ifdef UFS
76 #define RecLen( dp )    (sizeof(struct direct)) /* fixed-length entries */
77 #else   /* BFS || NFS */
78 #define RecLen( dp )    ((dp)->d_reclen)        /* variable-length entries */
79 #endif
80
81 #ifdef NFS
82 #ifdef BSD_SYSV
83 #define getdirentries   _getdirentries  /* package hides this system call */
84 #endif
85 extern int      getdirentries(int fd, char *buf, int nbytes, long *basep);
86 static long     dummy;                  /* getdirentries() needs basep */
87 #define GetBlock( fd, buf, n )  getdirentries( fd, buf, (unsigned)n, &dummy )
88 #else   /* UFS || BFS */
89 #ifdef BSD_SYSV
90 #define read    _read                   /* avoid emulation overhead */
91 #endif
92 extern int      read();
93 #define GetBlock( fd, buf, n )  read( fd, buf, (unsigned)n )
94 #endif
95
96 #ifdef UNK
97 extern int      _getdents();            /* actual system call */
98 #endif
99
100 extern int      _fstat(int fd, struct stat *buf);
101 extern off_t    _lseek(int d, int offset, int whence);
102
103 #ifndef DIRBLKSIZ
104 #define DIRBLKSIZ       4096            /* directory file read buffer size */
105 #endif
106
107 #ifndef NULL
108 #define NULL    0
109 #endif
110
111 #ifndef SEEK_CUR
112 #define SEEK_CUR        1
113 #endif
114
115 #ifndef S_ISDIR                         /* macro to test for directory file */
116 #define S_ISDIR( mode )         (((mode) & S_IFMT) == S_IFDIR)
117 #endif
118
119 #ifdef UFS
120
121 /*
122         The following routine is necessary to handle DIRSIZ-long entry names.
123         Thanks to Richard Todd for pointing this out.
124 */
125
126 static int
127 NameLen( char name[] )                          /* return # chars in embedded name */
128                 /* -> name embedded in struct direct */
129 {
130         register char   *s;             /* -> name[.] */
131         register char   *stop = &name[DIRSIZ];  /* -> past end of name field */
132
133         for ( s = &name[1];             /* (empty names are impossible) */
134               *s != '\0'                /* not NUL terminator */
135            && ++s < stop;               /* < DIRSIZ characters scanned */
136             )
137                 ;
138
139         return s - name;                /* # valid characters in name */
140 }
141
142 #else   /* BFS || NFS */
143
144 #define NameLen( name ) strlen( name )  /* names are always NUL-terminated */
145
146 #endif
147
148 #ifdef UNK
149 static enum     { maybe, no, yes }      state = maybe;
150                                         /* does _getdents() work? */
151
152 /*ARGSUSED*/
153 static void
154 sig_catch(int sig)                      /* sig must be SIGSYS */
155 {
156         state = no;                     /* attempted _getdents() faulted */
157 }
158 #endif
159
160 int
161 getdents(int fildes, char *buf, unsigned nbyte) /* returns # bytes read;
162                                                     0 on EOF, -1 on error */
163 /* fildes == directory file descriptor */
164 /* *buf == where to put the (struct dirent)s */
165 /* nbyte == size of buf[] */
166 {
167         int                     serrno; /* entry errno */
168         off_t                   offset; /* initial directory file offset */
169         struct stat             statb;  /* fstat() info */
170         union   {
171                 char            dblk[DIRBLKSIZ];
172                                         /* directory file block buffer */
173                 struct direct   dummy;  /* just for alignment */
174                 }       u;              /* (avoids having to malloc()) */
175         register struct direct  *dp;    /* -> u.dblk[.] */
176         register struct dirent  *bp;    /* -> buf[.] */
177
178 #ifdef UNK
179         switch ( state )
180                 {
181                 void            (*shdlr)();     /* entry SIGSYS handler */
182                 register int    retval; /* return from _getdents() if any */
183
184         case yes:                       /* _getdents() is known to work */
185                 return _getdents( fildes, buf, nbyte );
186
187         case maybe:                     /* first time only */
188                 shdlr = signal( SIGSYS, sig_catch );
189                 retval = _getdents( fildes, buf, nbyte );       /* try it */
190                 (void)signal( SIGSYS, shdlr );
191
192                 if ( state == maybe )   /* SIGSYS did not occur */
193                         {
194                         state = yes;    /* so _getdents() must have worked */
195                         return retval;
196                         }
197                 /* else fall through into emulation */
198
199 /*      case no:*/      /* fall through into emulation */
200                 }
201 #endif
202
203         if ( buf == NULL
204 #ifdef ATT_SPEC
205           || (unsigned long)buf % sizeof(long) != 0     /* ugh */
206 #endif
207            )    {
208                 errno = EFAULT;         /* invalid pointer */
209                 return -1;
210                 }
211
212         if ( _fstat( fildes, &statb ) != 0 )
213                 return -1;              /* errno set by fstat() */
214
215         if ( !S_ISDIR( statb.st_mode ) )
216                 {
217                 errno = ENOTDIR;        /* not a directory */
218                 return -1;
219                 }
220
221         if ( (offset = _lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
222                 return -1;              /* errno set by lseek() */
223
224 #ifdef BFS                              /* no telling what remote hosts do */
225         if ( (unsigned long)offset % DIRBLKSIZ != 0 )
226                 {
227                 errno = ENOENT;         /* file pointer probably misaligned */
228                 return -1;
229                 }
230 #endif
231
232         serrno = errno;                 /* save entry errno */
233
234         for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
235                 {                       /* convert next directory block */
236                 int     size;
237
238                 do      size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
239                 while ( size == -1 && errno == EINTR );
240
241                 if ( size <= 0 )
242                         return size;    /* EOF or error (EBADF) */
243
244                 for ( dp = (struct direct *)u.dblk;
245                       (char *)dp < &u.dblk[size];
246                       dp = (struct direct *)((char *)dp + RecLen( dp ))
247                     )   {
248 #ifndef UFS
249                         if ( dp->d_reclen <= 0 )
250                                 {
251                                 errno = EIO;    /* corrupted directory */
252                                 return -1;
253                                 }
254 #endif
255
256                         if ( dp->d_fileno != 0 )
257                                 {       /* non-empty; copy to user buffer */
258                                 register int    reclen =
259                                         DIRENTSIZ( NameLen( dp->d_name ) );
260
261                                 if ( (char *)bp + reclen > &buf[nbyte] )
262                                         {
263                                         errno = EINVAL;
264                                         return -1;      /* buf too small */
265                                         }
266
267                                 bp->d_ino = dp->d_fileno;
268                                 bp->d_off = offset + ((char *)dp - u.dblk);
269                                 bp->d_reclen = reclen;
270                                 (void)strncpy( bp->d_name, dp->d_name,
271                                                reclen - DIRENTBASESIZ
272                                              ); /* adds NUL padding */
273
274                                 bp = (struct dirent *)((char *)bp + reclen);
275                                 }
276                         }
277
278 #ifndef BFS     /* 4.2BSD screwed up; fixed in 4.3BSD */
279                 if ( (char *)dp > &u.dblk[size] )
280                         {
281                         errno = EIO;    /* corrupted directory */
282                         return -1;
283                         }
284 #endif
285                 }
286
287         errno = serrno;                 /* restore entry errno */
288         return (char *)bp - buf;        /* return # bytes read */
289 }