xref: /plan9/sys/src/ape/cmd/pax/paxdir.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1*9a747e4fSDavid du Colombier /*
2*9a747e4fSDavid du Colombier 	opendir -- open a directory stream
3*9a747e4fSDavid du Colombier 
4*9a747e4fSDavid du Colombier 	last edit:	16-Jun-1987	D A Gwyn
5*9a747e4fSDavid du Colombier */
6*9a747e4fSDavid du Colombier 
7*9a747e4fSDavid du Colombier #include	<sys/errno.h>
8*9a747e4fSDavid du Colombier #include	<sys/types.h>
9*9a747e4fSDavid du Colombier #include	<sys/stat.h>
10*9a747e4fSDavid du Colombier #include	"paxdir.h"
11*9a747e4fSDavid du Colombier 
12*9a747e4fSDavid du Colombier #ifdef BSD_SYSV
13*9a747e4fSDavid du Colombier /*
14*9a747e4fSDavid du Colombier 	<sys/_dir.h> -- definitions for 4.2,4.3BSD directories
15*9a747e4fSDavid du Colombier 
16*9a747e4fSDavid du Colombier 	last edit:	25-Apr-1987	D A Gwyn
17*9a747e4fSDavid du Colombier 
18*9a747e4fSDavid du Colombier 	A directory consists of some number of blocks of DIRBLKSIZ bytes each,
19*9a747e4fSDavid du Colombier 	where DIRBLKSIZ is chosen such that it can be transferred to disk in a
20*9a747e4fSDavid du Colombier 	single atomic operation (e.g., 512 bytes on most machines).
21*9a747e4fSDavid du Colombier 
22*9a747e4fSDavid du Colombier 	Each DIRBLKSIZ-byte block contains some number of directory entry
23*9a747e4fSDavid du Colombier 	structures, which are of variable length.  Each directory entry has the
24*9a747e4fSDavid du Colombier 	beginning of a (struct direct) at the front of it, containing its
25*9a747e4fSDavid du Colombier 	filesystem-unique ident number, the length of the entry, and the length
26*9a747e4fSDavid du Colombier 	of the name contained in the entry.  These are followed by the NUL-
27*9a747e4fSDavid du Colombier 	terminated name padded to a (long) boundary with 0 bytes.  The maximum
28*9a747e4fSDavid du Colombier 	length of a name in a directory is MAXNAMELEN.
29*9a747e4fSDavid du Colombier 
30*9a747e4fSDavid du Colombier 	The macro DIRSIZ(dp) gives the amount of space required to represent a
31*9a747e4fSDavid du Colombier 	directory entry.  Free space in a directory is represented by entries
32*9a747e4fSDavid du Colombier 	that have dp->d_reclen > DIRSIZ(dp).  All DIRBLKSIZ bytes in a
33*9a747e4fSDavid du Colombier 	directory block are claimed by the directory entries; this usually
34*9a747e4fSDavid du Colombier 	results in the last entry in a directory having a large dp->d_reclen.
35*9a747e4fSDavid du Colombier 	When entries are deleted from a directory, the space is returned to the
36*9a747e4fSDavid du Colombier 	previous entry in the same directory block by increasing its
37*9a747e4fSDavid du Colombier 	dp->d_reclen.  If the first entry of a directory block is free, then
38*9a747e4fSDavid du Colombier 	its dp->d_fileno is set to 0; entries other than the first in a
39*9a747e4fSDavid du Colombier 	directory do not normally have 	dp->d_fileno set to 0.
40*9a747e4fSDavid du Colombier 
41*9a747e4fSDavid du Colombier 	prerequisite:	<sys/types.h>
42*9a747e4fSDavid du Colombier */
43*9a747e4fSDavid du Colombier 
44*9a747e4fSDavid du Colombier #if defined(accel) || defined(sun) || defined(vax)
45*9a747e4fSDavid du Colombier #define	DIRBLKSIZ	512	/* size of directory block */
46*9a747e4fSDavid du Colombier #else
47*9a747e4fSDavid du Colombier #ifdef alliant
48*9a747e4fSDavid du Colombier #define	DIRBLKSIZ	4096	/* size of directory block */
49*9a747e4fSDavid du Colombier #else
50*9a747e4fSDavid du Colombier #ifdef gould
51*9a747e4fSDavid du Colombier #define	DIRBLKSIZ	1024	/* size of directory block */
52*9a747e4fSDavid du Colombier #else
53*9a747e4fSDavid du Colombier #ifdef ns32000			/* Dynix System V */
54*9a747e4fSDavid du Colombier #define	DIRBLKSIZ	2600	/* size of directory block */
55*9a747e4fSDavid du Colombier #else				/* be conservative; multiple blocks are okay
56*9a747e4fSDavid du Colombier 				 * but fractions are not */
57*9a747e4fSDavid du Colombier #define	DIRBLKSIZ	4096	/* size of directory block */
58*9a747e4fSDavid du Colombier #endif
59*9a747e4fSDavid du Colombier #endif
60*9a747e4fSDavid du Colombier #endif
61*9a747e4fSDavid du Colombier #endif
62*9a747e4fSDavid du Colombier 
63*9a747e4fSDavid du Colombier #define	MAXNAMELEN	255	/* maximum filename length */
64*9a747e4fSDavid du Colombier /* NOTE:  not MAXNAMLEN, which has been preempted by SVR3 <dirent.h> */
65*9a747e4fSDavid du Colombier 
66*9a747e4fSDavid du Colombier struct direct {			/* data from read()/_getdirentries() */
67*9a747e4fSDavid du Colombier     unsigned long   d_fileno;	/* unique ident of entry */
68*9a747e4fSDavid du Colombier     unsigned short  d_reclen;	/* length of this record */
69*9a747e4fSDavid du Colombier     unsigned short  d_namlen;	/* length of string in d_name */
70*9a747e4fSDavid du Colombier     char            d_name[MAXNAMELEN + 1];	/* NUL-terminated filename */
71*9a747e4fSDavid du Colombier };
72*9a747e4fSDavid du Colombier 
73*9a747e4fSDavid du Colombier /*
74*9a747e4fSDavid du Colombier 	The DIRSIZ macro gives the minimum record length which will hold the
75*9a747e4fSDavid du Colombier 	directory entry.  This requires the amount of space in a (struct
76*9a747e4fSDavid du Colombier 	direct) without the d_name field, plus enough space for the name with a
77*9a747e4fSDavid du Colombier 	terminating NUL character, rounded up to a (long) boundary.
78*9a747e4fSDavid du Colombier 
79*9a747e4fSDavid du Colombier 	(Note that Berkeley didn't properly compensate for struct padding,
80*9a747e4fSDavid du Colombier 	but we nevertheless have to use the same size as the actual system.)
81*9a747e4fSDavid du Colombier */
82*9a747e4fSDavid du Colombier 
83*9a747e4fSDavid du Colombier #define	DIRSIZ( dp )	((sizeof(struct direct) - (MAXNAMELEN+1) \
84*9a747e4fSDavid du Colombier 			+ sizeof(long) + (dp)->d_namlen) \
85*9a747e4fSDavid du Colombier 			/ sizeof(long) * sizeof(long))
86*9a747e4fSDavid du Colombier 
87*9a747e4fSDavid du Colombier #else
88*9a747e4fSDavid du Colombier #include	<sys/dir.h>
89*9a747e4fSDavid du Colombier #ifdef SYSV3
90*9a747e4fSDavid du Colombier #undef	MAXNAMLEN		/* avoid conflict with SVR3 */
91*9a747e4fSDavid du Colombier #endif
92*9a747e4fSDavid du Colombier  /* Good thing we don't need to use the DIRSIZ() macro! */
93*9a747e4fSDavid du Colombier #ifdef d_ino			/* 4.3BSD/NFS using d_fileno */
94*9a747e4fSDavid du Colombier #undef	d_ino			/* (not absolutely necessary) */
95*9a747e4fSDavid du Colombier #else
96*9a747e4fSDavid du Colombier #define	d_fileno	d_ino	/* (struct direct) member */
97*9a747e4fSDavid du Colombier #endif
98*9a747e4fSDavid du Colombier #endif
99*9a747e4fSDavid du Colombier #ifdef UNK
100*9a747e4fSDavid du Colombier #ifndef UFS
101*9a747e4fSDavid du Colombier #include "***** ERROR ***** UNK applies only to UFS"
102*9a747e4fSDavid du Colombier /* One could do something similar for getdirentries(), but I didn't bother. */
103*9a747e4fSDavid du Colombier #endif
104*9a747e4fSDavid du Colombier #include	<signal.h>
105*9a747e4fSDavid du Colombier #endif
106*9a747e4fSDavid du Colombier 
107*9a747e4fSDavid du Colombier #if defined(UFS) + defined(BFS) + defined(NFS) != 1	/* sanity check */
108*9a747e4fSDavid du Colombier #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
109*9a747e4fSDavid du Colombier #endif
110*9a747e4fSDavid du Colombier 
111*9a747e4fSDavid du Colombier #ifdef UFS
112*9a747e4fSDavid du Colombier #define	RecLen( dp )	(sizeof(struct direct))	/* fixed-length entries */
113*9a747e4fSDavid du Colombier #else				/* BFS || NFS */
114*9a747e4fSDavid du Colombier #define	RecLen( dp )	((dp)->d_reclen)	/* variable-length entries */
115*9a747e4fSDavid du Colombier #endif
116*9a747e4fSDavid du Colombier 
117*9a747e4fSDavid du Colombier #ifdef NFS
118*9a747e4fSDavid du Colombier #ifdef BSD_SYSV
119*9a747e4fSDavid du Colombier #define	getdirentries	_getdirentries	/* package hides this system call */
120*9a747e4fSDavid du Colombier #endif
121*9a747e4fSDavid du Colombier extern int      getdirentries();
122*9a747e4fSDavid du Colombier static long     dummy;		/* getdirentries() needs basep */
123*9a747e4fSDavid du Colombier #define	GetBlock( fd, buf, n )	getdirentries( fd, buf, (unsigned)n, &dummy )
124*9a747e4fSDavid du Colombier #else				/* UFS || BFS */
125*9a747e4fSDavid du Colombier #ifdef BSD_SYSV
126*9a747e4fSDavid du Colombier #define read	_read		/* avoid emulation overhead */
127*9a747e4fSDavid du Colombier #endif
128*9a747e4fSDavid du Colombier extern int      read();
129*9a747e4fSDavid du Colombier #define	GetBlock( fd, buf, n )	read( fd, buf, (unsigned)n )
130*9a747e4fSDavid du Colombier #endif
131*9a747e4fSDavid du Colombier 
132*9a747e4fSDavid du Colombier #ifdef UNK
133*9a747e4fSDavid du Colombier extern int      _getdents();	/* actual system call */
134*9a747e4fSDavid du Colombier #endif
135*9a747e4fSDavid du Colombier 
136*9a747e4fSDavid du Colombier extern char    *strncpy();
137*9a747e4fSDavid du Colombier extern int      fstat();
138*9a747e4fSDavid du Colombier extern OFFSET   lseek();
139*9a747e4fSDavid du Colombier 
140*9a747e4fSDavid du Colombier extern int      errno;
141*9a747e4fSDavid du Colombier 
142*9a747e4fSDavid du Colombier #ifndef DIRBLKSIZ
143*9a747e4fSDavid du Colombier #define	DIRBLKSIZ	4096	/* directory file read buffer size */
144*9a747e4fSDavid du Colombier #endif
145*9a747e4fSDavid du Colombier 
146*9a747e4fSDavid du Colombier #ifndef NULL
147*9a747e4fSDavid du Colombier #define	NULL	0
148*9a747e4fSDavid du Colombier #endif
149*9a747e4fSDavid du Colombier 
150*9a747e4fSDavid du Colombier #ifndef SEEK_CUR
151*9a747e4fSDavid du Colombier #define	SEEK_CUR	1
152*9a747e4fSDavid du Colombier #endif
153*9a747e4fSDavid du Colombier 
154*9a747e4fSDavid du Colombier #ifndef S_ISDIR			/* macro to test for directory file */
155*9a747e4fSDavid du Colombier #define	S_ISDIR( mode )		(((mode) & S_IFMT) == S_IFDIR)
156*9a747e4fSDavid du Colombier #endif
157*9a747e4fSDavid du Colombier 
158*9a747e4fSDavid du Colombier 
159*9a747e4fSDavid du Colombier #ifndef SEEK_CUR
160*9a747e4fSDavid du Colombier #define	SEEK_CUR	1
161*9a747e4fSDavid du Colombier #endif
162*9a747e4fSDavid du Colombier 
163*9a747e4fSDavid du Colombier #ifdef BSD_SYSV
164*9a747e4fSDavid du Colombier #define open	_open		/* avoid emulation overhead */
165*9a747e4fSDavid du Colombier #endif
166*9a747e4fSDavid du Colombier 
167*9a747e4fSDavid du Colombier extern int      getdents();	/* SVR3 system call, or emulation */
168*9a747e4fSDavid du Colombier 
169*9a747e4fSDavid du Colombier typedef char   *pointer;	/* (void *) if you have it */
170*9a747e4fSDavid du Colombier 
171*9a747e4fSDavid du Colombier extern void     free();
172*9a747e4fSDavid du Colombier extern pointer  malloc();
173*9a747e4fSDavid du Colombier extern int
174*9a747e4fSDavid du Colombier open(), close(), fstat();
175*9a747e4fSDavid du Colombier 
176*9a747e4fSDavid du Colombier extern int      errno;
177*9a747e4fSDavid du Colombier extern OFFSET   lseek();
178*9a747e4fSDavid du Colombier 
179*9a747e4fSDavid du Colombier #ifndef SEEK_SET
180*9a747e4fSDavid du Colombier #define	SEEK_SET	0
181*9a747e4fSDavid du Colombier #endif
182*9a747e4fSDavid du Colombier 
183*9a747e4fSDavid du Colombier typedef int     bool;		/* Boolean data type */
184*9a747e4fSDavid du Colombier #define	false	0
185*9a747e4fSDavid du Colombier #define	true	1
186*9a747e4fSDavid du Colombier 
187*9a747e4fSDavid du Colombier 
188*9a747e4fSDavid du Colombier #ifndef NULL
189*9a747e4fSDavid du Colombier #define	NULL	0
190*9a747e4fSDavid du Colombier #endif
191*9a747e4fSDavid du Colombier 
192*9a747e4fSDavid du Colombier #ifndef O_RDONLY
193*9a747e4fSDavid du Colombier #define	O_RDONLY	0
194*9a747e4fSDavid du Colombier #endif
195*9a747e4fSDavid du Colombier 
196*9a747e4fSDavid du Colombier #ifndef S_ISDIR			/* macro to test for directory file */
197*9a747e4fSDavid du Colombier #define	S_ISDIR( mode )		(((mode) & S_IFMT) == S_IFDIR)
198*9a747e4fSDavid du Colombier #endif
199*9a747e4fSDavid du Colombier 
200*9a747e4fSDavid du Colombier #ifdef __STDC__
201*9a747e4fSDavid du Colombier 
opendir(char * dirname)202*9a747e4fSDavid du Colombier DIR *opendir(char *dirname)
203*9a747e4fSDavid du Colombier 
204*9a747e4fSDavid du Colombier #else
205*9a747e4fSDavid du Colombier 
206*9a747e4fSDavid du Colombier DIR *opendir(dirname)
207*9a747e4fSDavid du Colombier char           *dirname;	/* name of directory */
208*9a747e4fSDavid du Colombier 
209*9a747e4fSDavid du Colombier #endif
210*9a747e4fSDavid du Colombier {
211*9a747e4fSDavid du Colombier     register DIR   *dirp;	/* -> malloc'ed storage */
212*9a747e4fSDavid du Colombier     register int    fd;		/* file descriptor for read */
213*9a747e4fSDavid du Colombier     struct stat     sbuf;	/* result of fstat() */
214*9a747e4fSDavid du Colombier 
215*9a747e4fSDavid du Colombier     if ((fd = open(dirname, O_RDONLY)) < 0)
216*9a747e4fSDavid du Colombier 	return ((DIR *)NULL);		/* errno set by open() */
217*9a747e4fSDavid du Colombier 
218*9a747e4fSDavid du Colombier     if (fstat(fd, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) {
219*9a747e4fSDavid du Colombier 	close(fd);
220*9a747e4fSDavid du Colombier 	errno = ENOTDIR;
221*9a747e4fSDavid du Colombier 	return ((DIR *)NULL);		/* not a directory */
222*9a747e4fSDavid du Colombier     }
223*9a747e4fSDavid du Colombier     if ((dirp = (DIR *) malloc(sizeof(DIR))) == (DIR *)NULL
224*9a747e4fSDavid du Colombier 	|| (dirp->dd_buf = (char *) malloc((unsigned) DIRBUF)) == (char *)NULL
225*9a747e4fSDavid du Colombier 	) {
226*9a747e4fSDavid du Colombier 	register int    serrno = errno;
227*9a747e4fSDavid du Colombier 	/* errno set to ENOMEM by sbrk() */
228*9a747e4fSDavid du Colombier 
229*9a747e4fSDavid du Colombier 	if (dirp != (DIR *)NULL)
230*9a747e4fSDavid du Colombier 	    free((pointer) dirp);
231*9a747e4fSDavid du Colombier 
232*9a747e4fSDavid du Colombier 	close(fd);
233*9a747e4fSDavid du Colombier 	errno = serrno;
234*9a747e4fSDavid du Colombier 	return ((DIR *)NULL);		/* not enough memory */
235*9a747e4fSDavid du Colombier     }
236*9a747e4fSDavid du Colombier     dirp->dd_fd = fd;
237*9a747e4fSDavid du Colombier     dirp->dd_loc = dirp->dd_size = 0;	/* refill needed */
238*9a747e4fSDavid du Colombier 
239*9a747e4fSDavid du Colombier     return dirp;
240*9a747e4fSDavid du Colombier }
241*9a747e4fSDavid du Colombier 
242*9a747e4fSDavid du Colombier 
243*9a747e4fSDavid du Colombier /*
244*9a747e4fSDavid du Colombier  *	closedir -- close a directory stream
245*9a747e4fSDavid du Colombier  *
246*9a747e4fSDavid du Colombier  *	last edit:	11-Nov-1988	D A Gwyn
247*9a747e4fSDavid du Colombier  */
248*9a747e4fSDavid du Colombier 
249*9a747e4fSDavid du Colombier #ifdef __STDC__
250*9a747e4fSDavid du Colombier 
closedir(register DIR * dirp)251*9a747e4fSDavid du Colombier int closedir(register DIR *dirp)
252*9a747e4fSDavid du Colombier 
253*9a747e4fSDavid du Colombier #else
254*9a747e4fSDavid du Colombier 
255*9a747e4fSDavid du Colombier int closedir(dirp)
256*9a747e4fSDavid du Colombier register DIR	*dirp;		/* stream from opendir() */
257*9a747e4fSDavid du Colombier 
258*9a747e4fSDavid du Colombier #endif
259*9a747e4fSDavid du Colombier {
260*9a747e4fSDavid du Colombier     register int	fd;
261*9a747e4fSDavid du Colombier 
262*9a747e4fSDavid du Colombier     if ( dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL ) {
263*9a747e4fSDavid du Colombier 	errno = EFAULT;
264*9a747e4fSDavid du Colombier 	return -1;			/* invalid pointer */
265*9a747e4fSDavid du Colombier     }
266*9a747e4fSDavid du Colombier 
267*9a747e4fSDavid du Colombier     fd = dirp->dd_fd;			/* bug fix thanks to R. Salz */
268*9a747e4fSDavid du Colombier     free( (pointer)dirp->dd_buf );
269*9a747e4fSDavid du Colombier     free( (pointer)dirp );
270*9a747e4fSDavid du Colombier     return close( fd );
271*9a747e4fSDavid du Colombier }
272*9a747e4fSDavid du Colombier 
273*9a747e4fSDavid du Colombier 
274*9a747e4fSDavid du Colombier /*
275*9a747e4fSDavid du Colombier 	readdir -- read next entry from a directory stream
276*9a747e4fSDavid du Colombier 
277*9a747e4fSDavid du Colombier 	last edit:	25-Apr-1987	D A Gwyn
278*9a747e4fSDavid du Colombier */
279*9a747e4fSDavid du Colombier 
280*9a747e4fSDavid du Colombier #ifdef __STDC__
281*9a747e4fSDavid du Colombier 
readdir(register DIR * dirp)282*9a747e4fSDavid du Colombier struct dirent  *readdir(register DIR *dirp)
283*9a747e4fSDavid du Colombier 
284*9a747e4fSDavid du Colombier #else
285*9a747e4fSDavid du Colombier 
286*9a747e4fSDavid du Colombier struct dirent  *readdir(dirp)
287*9a747e4fSDavid du Colombier register DIR   *dirp;		/* stream from opendir() */
288*9a747e4fSDavid du Colombier 
289*9a747e4fSDavid du Colombier #endif
290*9a747e4fSDavid du Colombier {
291*9a747e4fSDavid du Colombier     register struct dirent *dp;	/* -> directory data */
292*9a747e4fSDavid du Colombier 
293*9a747e4fSDavid du Colombier     if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
294*9a747e4fSDavid du Colombier 	errno = EFAULT;
295*9a747e4fSDavid du Colombier 	return (struct dirent *)NULL;		/* invalid pointer */
296*9a747e4fSDavid du Colombier     }
297*9a747e4fSDavid du Colombier     do {
298*9a747e4fSDavid du Colombier 	if (dirp->dd_loc >= dirp->dd_size)	/* empty or obsolete */
299*9a747e4fSDavid du Colombier 	    dirp->dd_loc = dirp->dd_size = 0;
300*9a747e4fSDavid du Colombier 
301*9a747e4fSDavid du Colombier 	if (dirp->dd_size == 0	/* need to refill buffer */
302*9a747e4fSDavid du Colombier 	    && (dirp->dd_size =
303*9a747e4fSDavid du Colombier 		getdents(dirp->dd_fd, dirp->dd_buf, (unsigned) DIRBUF)
304*9a747e4fSDavid du Colombier 		) <= 0
305*9a747e4fSDavid du Colombier 	    )
306*9a747e4fSDavid du Colombier 	    return ((struct dirent *)NULL);	/* EOF or error */
307*9a747e4fSDavid du Colombier 
308*9a747e4fSDavid du Colombier 	dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc];
309*9a747e4fSDavid du Colombier 	dirp->dd_loc += dp->d_reclen;
310*9a747e4fSDavid du Colombier     }
311*9a747e4fSDavid du Colombier     while (dp->d_ino == 0L);	/* don't rely on getdents() */
312*9a747e4fSDavid du Colombier 
313*9a747e4fSDavid du Colombier     return dp;
314*9a747e4fSDavid du Colombier }
315*9a747e4fSDavid du Colombier 
316*9a747e4fSDavid du Colombier 
317*9a747e4fSDavid du Colombier /*
318*9a747e4fSDavid du Colombier 	seekdir -- reposition a directory stream
319*9a747e4fSDavid du Colombier 
320*9a747e4fSDavid du Colombier 	last edit:	24-May-1987	D A Gwyn
321*9a747e4fSDavid du Colombier 
322*9a747e4fSDavid du Colombier 	An unsuccessful seekdir() will in general alter the current
323*9a747e4fSDavid du Colombier 	directory position; beware.
324*9a747e4fSDavid du Colombier 
325*9a747e4fSDavid du Colombier 	NOTE:	4.nBSD directory compaction makes seekdir() & telldir()
326*9a747e4fSDavid du Colombier 		practically impossible to do right.  Avoid using them!
327*9a747e4fSDavid du Colombier */
328*9a747e4fSDavid du Colombier 
329*9a747e4fSDavid du Colombier #ifdef __STDC__
330*9a747e4fSDavid du Colombier 
seekdir(register DIR * dirp,register OFFSET loc)331*9a747e4fSDavid du Colombier void seekdir(register DIR *dirp, register OFFSET loc)
332*9a747e4fSDavid du Colombier 
333*9a747e4fSDavid du Colombier #else
334*9a747e4fSDavid du Colombier 
335*9a747e4fSDavid du Colombier void seekdir(dirp, loc)
336*9a747e4fSDavid du Colombier register DIR   *dirp;		/* stream from opendir() */
337*9a747e4fSDavid du Colombier register OFFSET  loc;		/* position from telldir() */
338*9a747e4fSDavid du Colombier 
339*9a747e4fSDavid du Colombier #endif
340*9a747e4fSDavid du Colombier {
341*9a747e4fSDavid du Colombier     register bool   rewind;	/* "start over when stymied" flag */
342*9a747e4fSDavid du Colombier 
343*9a747e4fSDavid du Colombier     if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
344*9a747e4fSDavid du Colombier 	errno = EFAULT;
345*9a747e4fSDavid du Colombier 	return;			/* invalid pointer */
346*9a747e4fSDavid du Colombier     }
347*9a747e4fSDavid du Colombier     /*
348*9a747e4fSDavid du Colombier      * A (struct dirent)'s d_off is an invented quantity on 4.nBSD
349*9a747e4fSDavid du Colombier      * NFS-supporting systems, so it is not safe to lseek() to it.
350*9a747e4fSDavid du Colombier      */
351*9a747e4fSDavid du Colombier 
352*9a747e4fSDavid du Colombier     /* Monotonicity of d_off is heavily exploited in the following. */
353*9a747e4fSDavid du Colombier 
354*9a747e4fSDavid du Colombier     /*
355*9a747e4fSDavid du Colombier      * This algorithm is tuned for modest directory sizes.  For huge
356*9a747e4fSDavid du Colombier      * directories, it might be more efficient to read blocks until the first
357*9a747e4fSDavid du Colombier      * d_off is too large, then back up one block, or even to use binary
358*9a747e4fSDavid du Colombier      * search on the directory blocks.  I doubt that the extra code for that
359*9a747e4fSDavid du Colombier      * would be worthwhile.
360*9a747e4fSDavid du Colombier      */
361*9a747e4fSDavid du Colombier 
362*9a747e4fSDavid du Colombier     if (dirp->dd_loc >= dirp->dd_size	/* invalid index */
363*9a747e4fSDavid du Colombier 	|| ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off > loc
364*9a747e4fSDavid du Colombier     /* too far along in buffer */
365*9a747e4fSDavid du Colombier 	)
366*9a747e4fSDavid du Colombier 	dirp->dd_loc = 0;	/* reset to beginning of buffer */
367*9a747e4fSDavid du Colombier     /* else save time by starting at current dirp->dd_loc */
368*9a747e4fSDavid du Colombier 
369*9a747e4fSDavid du Colombier     for (rewind = true;;) {
370*9a747e4fSDavid du Colombier 	register struct dirent *dp;
371*9a747e4fSDavid du Colombier 
372*9a747e4fSDavid du Colombier 	/* See whether the matching entry is in the current buffer. */
373*9a747e4fSDavid du Colombier 
374*9a747e4fSDavid du Colombier 	if ((dirp->dd_loc < dirp->dd_size	/* valid index */
375*9a747e4fSDavid du Colombier 	     || readdir(dirp) != (struct dirent *)NULL	/* next buffer read */
376*9a747e4fSDavid du Colombier 	     && (dirp->dd_loc = 0, true)	/* beginning of buffer set */
377*9a747e4fSDavid du Colombier 	     )
378*9a747e4fSDavid du Colombier 	    && (dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off
379*9a747e4fSDavid du Colombier 	    <= loc		/* match possible in this buffer */
380*9a747e4fSDavid du Colombier 	    ) {
381*9a747e4fSDavid du Colombier 	    for ( /* dp initialized above */ ;
382*9a747e4fSDavid du Colombier 		 (char *) dp < &dirp->dd_buf[dirp->dd_size];
383*9a747e4fSDavid du Colombier 		 dp = (struct dirent *) ((char *) dp + dp->d_reclen)
384*9a747e4fSDavid du Colombier 		)
385*9a747e4fSDavid du Colombier 		if (dp->d_off == loc) {	/* found it! */
386*9a747e4fSDavid du Colombier 		    dirp->dd_loc =
387*9a747e4fSDavid du Colombier 			(char *) dp - dirp->dd_buf;
388*9a747e4fSDavid du Colombier 		    return;
389*9a747e4fSDavid du Colombier 		}
390*9a747e4fSDavid du Colombier 	    rewind = false;	/* no point in backing up later */
391*9a747e4fSDavid du Colombier 	    dirp->dd_loc = dirp->dd_size;	/* set end of buffer */
392*9a747e4fSDavid du Colombier 	} else
393*9a747e4fSDavid du Colombier 	 /* whole buffer past matching entry */ if (!rewind) {	/* no point in searching
394*9a747e4fSDavid du Colombier 								 * further */
395*9a747e4fSDavid du Colombier 	    errno = EINVAL;
396*9a747e4fSDavid du Colombier 	    return;		/* no entry at specified loc */
397*9a747e4fSDavid du Colombier 	} else {		/* rewind directory and start over */
398*9a747e4fSDavid du Colombier 	    rewind = false;	/* but only once! */
399*9a747e4fSDavid du Colombier 
400*9a747e4fSDavid du Colombier 	    dirp->dd_loc = dirp->dd_size = 0;
401*9a747e4fSDavid du Colombier 
402*9a747e4fSDavid du Colombier 	    if (lseek(dirp->dd_fd, (OFFSET) 0, SEEK_SET)
403*9a747e4fSDavid du Colombier 		!= 0
404*9a747e4fSDavid du Colombier 		)
405*9a747e4fSDavid du Colombier 		return;		/* errno already set (EBADF) */
406*9a747e4fSDavid du Colombier 
407*9a747e4fSDavid du Colombier 	    if (loc == 0)
408*9a747e4fSDavid du Colombier 		return;		/* save time */
409*9a747e4fSDavid du Colombier 	}
410*9a747e4fSDavid du Colombier     }
411*9a747e4fSDavid du Colombier }
412*9a747e4fSDavid du Colombier 
413*9a747e4fSDavid du Colombier 
414*9a747e4fSDavid du Colombier /* telldir - report directory stream position
415*9a747e4fSDavid du Colombier  *
416*9a747e4fSDavid du Colombier  * DESCRIPTION
417*9a747e4fSDavid du Colombier  *
418*9a747e4fSDavid du Colombier  *	Returns the offset of the next directory entry in the
419*9a747e4fSDavid du Colombier  *	directory associated with dirp.
420*9a747e4fSDavid du Colombier  *
421*9a747e4fSDavid du Colombier  *	NOTE:	4.nBSD directory compaction makes seekdir() & telldir()
422*9a747e4fSDavid du Colombier  *		practically impossible to do right.  Avoid using them!
423*9a747e4fSDavid du Colombier  *
424*9a747e4fSDavid du Colombier  * PARAMETERS
425*9a747e4fSDavid du Colombier  *
426*9a747e4fSDavid du Colombier  *	DIR	*dirp	- stream from opendir()
427*9a747e4fSDavid du Colombier  *
428*9a747e4fSDavid du Colombier  * RETURNS
429*9a747e4fSDavid du Colombier  *
430*9a747e4fSDavid du Colombier  * 	Return offset of next entry
431*9a747e4fSDavid du Colombier  */
432*9a747e4fSDavid du Colombier 
433*9a747e4fSDavid du Colombier 
434*9a747e4fSDavid du Colombier #ifdef __STDC__
435*9a747e4fSDavid du Colombier 
telldir(DIR * dirp)436*9a747e4fSDavid du Colombier OFFSET telldir(DIR *dirp)
437*9a747e4fSDavid du Colombier 
438*9a747e4fSDavid du Colombier #else
439*9a747e4fSDavid du Colombier 
440*9a747e4fSDavid du Colombier OFFSET telldir(dirp)
441*9a747e4fSDavid du Colombier DIR            *dirp;		/* stream from opendir() */
442*9a747e4fSDavid du Colombier 
443*9a747e4fSDavid du Colombier #endif
444*9a747e4fSDavid du Colombier {
445*9a747e4fSDavid du Colombier     if (dirp == (DIR *)NULL || dirp->dd_buf == (char *)NULL) {
446*9a747e4fSDavid du Colombier 	errno = EFAULT;
447*9a747e4fSDavid du Colombier 	return -1;		/* invalid pointer */
448*9a747e4fSDavid du Colombier     }
449*9a747e4fSDavid du Colombier     if (dirp->dd_loc < dirp->dd_size)	/* valid index */
450*9a747e4fSDavid du Colombier 	return ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off;
451*9a747e4fSDavid du Colombier     else			/* beginning of next directory block */
452*9a747e4fSDavid du Colombier 	return lseek(dirp->dd_fd, (OFFSET) 0, SEEK_CUR);
453*9a747e4fSDavid du Colombier }
454*9a747e4fSDavid du Colombier 
455*9a747e4fSDavid du Colombier 
456*9a747e4fSDavid du Colombier #ifdef UFS
457*9a747e4fSDavid du Colombier 
458*9a747e4fSDavid du Colombier /*
459*9a747e4fSDavid du Colombier 	The following routine is necessary to handle DIRSIZ-long entry names.
460*9a747e4fSDavid du Colombier 	Thanks to Richard Todd for pointing this out.
461*9a747e4fSDavid du Colombier */
462*9a747e4fSDavid du Colombier 
463*9a747e4fSDavid du Colombier 
464*9a747e4fSDavid du Colombier /* return # chars in embedded name */
465*9a747e4fSDavid du Colombier 
466*9a747e4fSDavid du Colombier #ifdef __STDC__
467*9a747e4fSDavid du Colombier 
NameLen(char * name)468*9a747e4fSDavid du Colombier static int NameLen(char *name)
469*9a747e4fSDavid du Colombier 
470*9a747e4fSDavid du Colombier #else
471*9a747e4fSDavid du Colombier 
472*9a747e4fSDavid du Colombier static int NameLen(name)
473*9a747e4fSDavid du Colombier char            *name;		/* -> name embedded in struct direct */
474*9a747e4fSDavid du Colombier 
475*9a747e4fSDavid du Colombier #endif
476*9a747e4fSDavid du Colombier {
477*9a747e4fSDavid du Colombier     register char  *s;		/* -> name[.] */
478*9a747e4fSDavid du Colombier     register char  *stop = &name[DIRSIZ];	/* -> past end of name field */
479*9a747e4fSDavid du Colombier 
480*9a747e4fSDavid du Colombier     for (s = &name[1];		/* (empty names are impossible) */
481*9a747e4fSDavid du Colombier 	 *s != '\0'		/* not NUL terminator */
482*9a747e4fSDavid du Colombier 	 && ++s < stop;		/* < DIRSIZ characters scanned */
483*9a747e4fSDavid du Colombier 	);
484*9a747e4fSDavid du Colombier 
485*9a747e4fSDavid du Colombier     return s - name;		/* # valid characters in name */
486*9a747e4fSDavid du Colombier }
487*9a747e4fSDavid du Colombier 
488*9a747e4fSDavid du Colombier #else				/* BFS || NFS */
489*9a747e4fSDavid du Colombier 
490*9a747e4fSDavid du Colombier extern int      strlen();
491*9a747e4fSDavid du Colombier 
492*9a747e4fSDavid du Colombier #define	NameLen( name )	strlen( name )	/* names are always NUL-terminated */
493*9a747e4fSDavid du Colombier 
494*9a747e4fSDavid du Colombier #endif
495*9a747e4fSDavid du Colombier 
496*9a747e4fSDavid du Colombier #ifdef UNK
497*9a747e4fSDavid du Colombier static enum {
498*9a747e4fSDavid du Colombier     maybe, no, yes
499*9a747e4fSDavid du Colombier } state = maybe;
500*9a747e4fSDavid du Colombier 
501*9a747e4fSDavid du Colombier 
502*9a747e4fSDavid du Colombier /* sig_catch - used to catch signals
503*9a747e4fSDavid du Colombier  *
504*9a747e4fSDavid du Colombier  * DESCRIPTION
505*9a747e4fSDavid du Colombier  *
506*9a747e4fSDavid du Colombier  *	Used to catch signals.
507*9a747e4fSDavid du Colombier  */
508*9a747e4fSDavid du Colombier 
509*9a747e4fSDavid du Colombier /*ARGSUSED*/
510*9a747e4fSDavid du Colombier 
511*9a747e4fSDavid du Colombier #ifdef __STDC__
512*9a747e4fSDavid du Colombier 
sig_catch(int sig)513*9a747e4fSDavid du Colombier static void sig_catch(int sig)
514*9a747e4fSDavid du Colombier 
515*9a747e4fSDavid du Colombier #else
516*9a747e4fSDavid du Colombier 
517*9a747e4fSDavid du Colombier static void sig_catch(sig)
518*9a747e4fSDavid du Colombier int             sig;		/* must be SIGSYS */
519*9a747e4fSDavid du Colombier 
520*9a747e4fSDavid du Colombier #endif
521*9a747e4fSDavid du Colombier {
522*9a747e4fSDavid du Colombier     state = no;			/* attempted _getdents() faulted */
523*9a747e4fSDavid du Colombier }
524*9a747e4fSDavid du Colombier #endif
525*9a747e4fSDavid du Colombier 
526*9a747e4fSDavid du Colombier 
527*9a747e4fSDavid du Colombier /* getdents - get directory entries
528*9a747e4fSDavid du Colombier  *
529*9a747e4fSDavid du Colombier  * DESCRIPTION
530*9a747e4fSDavid du Colombier  *
531*9a747e4fSDavid du Colombier  *	Gets directory entries from the filesystem in an implemenation
532*9a747e4fSDavid du Colombier  *	defined way.
533*9a747e4fSDavid du Colombier  *
534*9a747e4fSDavid du Colombier  * PARAMETERS
535*9a747e4fSDavid du Colombier  *
536*9a747e4fSDavid du Colombier  *	int             fildes	- directory file descriptor
537*9a747e4fSDavid du Colombier  *	char           *buf	- where to put the (struct dirent)s
538*9a747e4fSDavid du Colombier  *	unsigned	nbyte	- size of buf[]
539*9a747e4fSDavid du Colombier  *
540*9a747e4fSDavid du Colombier  * RETURNS
541*9a747e4fSDavid du Colombier  *
542*9a747e4fSDavid du Colombier  *	Returns number of bytes read; 0 on EOF, -1 on error
543*9a747e4fSDavid du Colombier  */
544*9a747e4fSDavid du Colombier 
545*9a747e4fSDavid du Colombier #ifdef __STDC__
546*9a747e4fSDavid du Colombier 
getdents(int fildes,char * buf,unsigned nbyte)547*9a747e4fSDavid du Colombier int getdents(int fildes, char *buf, unsigned nbyte)
548*9a747e4fSDavid du Colombier 
549*9a747e4fSDavid du Colombier #else
550*9a747e4fSDavid du Colombier 
551*9a747e4fSDavid du Colombier int getdents(fildes, buf, nbyte)
552*9a747e4fSDavid du Colombier int             fildes;		/* directory file descriptor */
553*9a747e4fSDavid du Colombier char           *buf;		/* where to put the (struct dirent)s */
554*9a747e4fSDavid du Colombier unsigned        nbyte;		/* size of buf[] */
555*9a747e4fSDavid du Colombier 
556*9a747e4fSDavid du Colombier #endif
557*9a747e4fSDavid du Colombier {
558*9a747e4fSDavid du Colombier     int             serrno;	/* entry errno */
559*9a747e4fSDavid du Colombier     OFFSET          offset;	/* initial directory file offset */
560*9a747e4fSDavid du Colombier     struct stat     statb;	/* fstat() info */
561*9a747e4fSDavid du Colombier     union {
562*9a747e4fSDavid du Colombier 	/* directory file block buffer */
563*9a747e4fSDavid du Colombier #ifdef UFS
564*9a747e4fSDavid du Colombier 	char		dblk[DIRBLKSIZ + 1];
565*9a747e4fSDavid du Colombier #else
566*9a747e4fSDavid du Colombier 	char            dblk[DIRBLKSIZ];
567*9a747e4fSDavid du Colombier #endif
568*9a747e4fSDavid du Colombier 	struct direct   dummy;	/* just for alignment */
569*9a747e4fSDavid du Colombier     } u;		/* (avoids having to malloc()) */
570*9a747e4fSDavid du Colombier     register struct direct *dp;	/* -> u.dblk[.] */
571*9a747e4fSDavid du Colombier     register struct dirent *bp;	/* -> buf[.] */
572*9a747e4fSDavid du Colombier 
573*9a747e4fSDavid du Colombier #ifdef UNK
574*9a747e4fSDavid du Colombier     switch (state) {
575*9a747e4fSDavid du Colombier 	SIG_T         (*shdlr)();	/* entry SIGSYS handler */
576*9a747e4fSDavid du Colombier 	register int    retval;		/* return from _getdents() if any */
577*9a747e4fSDavid du Colombier 
578*9a747e4fSDavid du Colombier     case yes:			/* _getdents() is known to work */
579*9a747e4fSDavid du Colombier 	return _getdents(fildes, buf, nbyte);
580*9a747e4fSDavid du Colombier 
581*9a747e4fSDavid du Colombier     case maybe:		/* first time only */
582*9a747e4fSDavid du Colombier 	shdlr = signal(SIGSYS, sig_catch);
583*9a747e4fSDavid du Colombier 	retval = _getdents(fildes, buf, nbyte);	/* try it */
584*9a747e4fSDavid du Colombier 	signal(SIGSYS, shdlr);
585*9a747e4fSDavid du Colombier 
586*9a747e4fSDavid du Colombier 	if (state == maybe) {	/* SIGSYS did not occur */
587*9a747e4fSDavid du Colombier 	    state = yes;	/* so _getdents() must have worked */
588*9a747e4fSDavid du Colombier 	    return retval;
589*9a747e4fSDavid du Colombier 	}
590*9a747e4fSDavid du Colombier 	/* else fall through into emulation */
591*9a747e4fSDavid du Colombier 
592*9a747e4fSDavid du Colombier /*	case no:	/* fall through into emulation */
593*9a747e4fSDavid du Colombier     }
594*9a747e4fSDavid du Colombier #endif
595*9a747e4fSDavid du Colombier 
596*9a747e4fSDavid du Colombier     if (buf == (char *)NULL
597*9a747e4fSDavid du Colombier #ifdef ATT_SPEC
598*9a747e4fSDavid du Colombier 	|| (unsigned long) buf % sizeof(long) != 0	/* ugh */
599*9a747e4fSDavid du Colombier #endif
600*9a747e4fSDavid du Colombier 	) {
601*9a747e4fSDavid du Colombier 	errno = EFAULT;		/* invalid pointer */
602*9a747e4fSDavid du Colombier 	return -1;
603*9a747e4fSDavid du Colombier     }
604*9a747e4fSDavid du Colombier     if (fstat(fildes, &statb) != 0) {
605*9a747e4fSDavid du Colombier 	return -1;		/* errno set by fstat() */
606*9a747e4fSDavid du Colombier     }
607*9a747e4fSDavid du Colombier 
608*9a747e4fSDavid du Colombier     if (!S_ISDIR(statb.st_mode)) {
609*9a747e4fSDavid du Colombier 	errno = ENOTDIR;	/* not a directory */
610*9a747e4fSDavid du Colombier 	return -1;
611*9a747e4fSDavid du Colombier     }
612*9a747e4fSDavid du Colombier     if ((offset = lseek(fildes, (OFFSET) 0, SEEK_CUR)) < 0) {
613*9a747e4fSDavid du Colombier 	return -1;		/* errno set by lseek() */
614*9a747e4fSDavid du Colombier     }
615*9a747e4fSDavid du Colombier 
616*9a747e4fSDavid du Colombier #ifdef BFS			/* no telling what remote hosts do */
617*9a747e4fSDavid du Colombier     if ((unsigned long) offset % DIRBLKSIZ != 0) {
618*9a747e4fSDavid du Colombier 	errno = ENOENT;		/* file pointer probably misaligned */
619*9a747e4fSDavid du Colombier 	return -1;
620*9a747e4fSDavid du Colombier     }
621*9a747e4fSDavid du Colombier #endif
622*9a747e4fSDavid du Colombier 
623*9a747e4fSDavid du Colombier     serrno = errno;		/* save entry errno */
624*9a747e4fSDavid du Colombier 
625*9a747e4fSDavid du Colombier     for (bp = (struct dirent *) buf; bp == (struct dirent *) buf;) {
626*9a747e4fSDavid du Colombier 
627*9a747e4fSDavid du Colombier     	/* convert next directory block */
628*9a747e4fSDavid du Colombier 	int             size;
629*9a747e4fSDavid du Colombier 
630*9a747e4fSDavid du Colombier 	do {
631*9a747e4fSDavid du Colombier 	    size = GetBlock(fildes, u.dblk, DIRBLKSIZ);
632*9a747e4fSDavid du Colombier 	} while (size == -1 && errno == EINTR);
633*9a747e4fSDavid du Colombier 
634*9a747e4fSDavid du Colombier 	if (size <= 0) {
635*9a747e4fSDavid du Colombier 	    return size;	/* EOF or error (EBADF) */
636*9a747e4fSDavid du Colombier 	}
637*9a747e4fSDavid du Colombier 
638*9a747e4fSDavid du Colombier 	for (dp = (struct direct *) u.dblk;
639*9a747e4fSDavid du Colombier 	     (char *) dp < &u.dblk[size];
640*9a747e4fSDavid du Colombier 	     dp = (struct direct *) ((char *) dp + RecLen(dp))
641*9a747e4fSDavid du Colombier 	    ) {
642*9a747e4fSDavid du Colombier #ifndef UFS
643*9a747e4fSDavid du Colombier 	    if (dp->d_reclen <= 0) {
644*9a747e4fSDavid du Colombier 		errno = EIO;	/* corrupted directory */
645*9a747e4fSDavid du Colombier 		return -1;
646*9a747e4fSDavid du Colombier 	    }
647*9a747e4fSDavid du Colombier #endif
648*9a747e4fSDavid du Colombier 
649*9a747e4fSDavid du Colombier 	    if (dp->d_fileno != 0) {	/* non-empty; copy to user buffer */
650*9a747e4fSDavid du Colombier 		register int    reclen =
651*9a747e4fSDavid du Colombier 		DIRENTSIZ(NameLen(dp->d_name));
652*9a747e4fSDavid du Colombier 
653*9a747e4fSDavid du Colombier 		if ((char *) bp + reclen > &buf[nbyte]) {
654*9a747e4fSDavid du Colombier 		    errno = EINVAL;
655*9a747e4fSDavid du Colombier 		    return -1;	/* buf too small */
656*9a747e4fSDavid du Colombier 		}
657*9a747e4fSDavid du Colombier 		bp->d_ino = dp->d_fileno;
658*9a747e4fSDavid du Colombier 		bp->d_off = offset + ((char *) dp - u.dblk);
659*9a747e4fSDavid du Colombier 		bp->d_reclen = reclen;
660*9a747e4fSDavid du Colombier 
661*9a747e4fSDavid du Colombier 		{
662*9a747e4fSDavid du Colombier #ifdef UFS
663*9a747e4fSDavid du Colombier 		    /* Is the following kludge ugly?  You bet. */
664*9a747e4fSDavid du Colombier 
665*9a747e4fSDavid du Colombier 		    register char   save = dp->d_name[DIRSIZ];
666*9a747e4fSDavid du Colombier 		    /* save original data */
667*9a747e4fSDavid du Colombier 
668*9a747e4fSDavid du Colombier 		    dp->d_name[DIRSIZ] = '\0';
669*9a747e4fSDavid du Colombier 		    /* ensure NUL termination */
670*9a747e4fSDavid du Colombier #endif
671*9a747e4fSDavid du Colombier 		    /* adds NUL padding */
672*9a747e4fSDavid du Colombier 		    strncpy(bp->d_name, dp->d_name, reclen - DIRENTBASESIZ);
673*9a747e4fSDavid du Colombier #ifdef UFS
674*9a747e4fSDavid du Colombier 		    dp->d_name[DIRSIZ] = save;
675*9a747e4fSDavid du Colombier 		    /* restore original data */
676*9a747e4fSDavid du Colombier #endif
677*9a747e4fSDavid du Colombier 		}
678*9a747e4fSDavid du Colombier 
679*9a747e4fSDavid du Colombier 		bp = (struct dirent *) ((char *) bp + reclen);
680*9a747e4fSDavid du Colombier 	    }
681*9a747e4fSDavid du Colombier 	}
682*9a747e4fSDavid du Colombier 
683*9a747e4fSDavid du Colombier #ifndef BFS			/* 4.2BSD screwed up; fixed in 4.3BSD */
684*9a747e4fSDavid du Colombier 	if ((char *) dp > &u.dblk[size]) {
685*9a747e4fSDavid du Colombier 	    errno = EIO;	/* corrupted directory */
686*9a747e4fSDavid du Colombier 	    return -1;
687*9a747e4fSDavid du Colombier 	}
688*9a747e4fSDavid du Colombier #endif
689*9a747e4fSDavid du Colombier     }
690*9a747e4fSDavid du Colombier 
691*9a747e4fSDavid du Colombier     errno = serrno;		/* restore entry errno */
692*9a747e4fSDavid du Colombier     return (char *) bp - buf;	/* return # bytes read */
693*9a747e4fSDavid du Colombier }
694