File: dirent.c

package info (click to toggle)
xlispstat 3.52.14-1
  • links: PTS
  • area: main
  • in suites: potato
  • size: 7,560 kB
  • ctags: 12,676
  • sloc: ansic: 91,357; lisp: 21,759; sh: 1,525; makefile: 521; csh: 1
file content (648 lines) | stat: -rw-r--r-- 17,430 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
/****************************************************************************************
 *
 *	File:		dirent.c
 *	Created:	7/3/93		By:	George T. Talbot
 *	Purpose:	Implements UNIX-like directory reading for the Macintosh.
 *
 *	Modifications:
 *
 *	Notes:
 *			1) These routines will NOT work under A/UX.
 *			2) WD = working directory
 *			3) CD = change directory
 *			4) FS = file system
 *			5) Mac filesystems allow spaces as part of pathnames!
 *			6) All routines which return a path use the default Macintosh path separator,
 *			   a colon (":").
 *
 ****************************************************************************************/

#include "dirent.h"
#ifdef THINK_C
# include <pascal.h>
#endif /* THINK_C */
#ifdef applec
# include <Strings.h>
#endif /* applec */
#include <string.h>

OSErr	dd_errno;				/*	Global errno to check after calls to dirent routines	*/
char	*dd_separator = ":";	/*	If you're feeling brave, change this to "/"	*/
int		dd_xform_seps = false;

/****************************************************************************************
 *
 *	This function, given a Macintosh-style pathname, will open a directory to that path.
 *	NOTES:	1)	passing in nil will get you the current directory.
 *			2)	".:", "..:" & ":" are supported at the beginning of paths ONLY
 *				by this routine.
 *			3)	"/" will be turned into ":" by this routine.
 *
 *	Calls:			PBHGetVol(), PBHGetCatInfo(), PBHSetVol(), hopendir(), CtoPstr()
 *	Called By:		<general purpose>
 *	Globals Used:	dd_errno
 *	Parameters:		pointer to C-string pathname or nil for current directory
 *	Returns:		pointer to directory management block or nil & dd_errno will be set
 *
 ****************************************************************************************/

DIR	*opendir(char *dirname)
	{
	WDPBRec			pb;
	CInfoPBRec		cpb;
	short			vRefNum;
	long			dirID;
	char			*dname;
	DIR				*temp;
	char			path_temp[MAXPATHLEN+1];	/*	Temporary area for building pathname	*/

	/*	Save the current path	*/
	pb.ioCompletion	= nil;
	pb.ioNamePtr	= nil;
	
	if (dd_errno = PBHGetVolSync(&pb))
		return nil;

	vRefNum	= pb.ioWDVRefNum;
	dirID	= pb.ioWDDirID;

	/*	dname points to the desired pathname	*/
	dname	= dirname;

	/*	If no pathname was passed in, or there are no ".", ".." or "" special directory
	 *	names, then handle the pathname as normal.
	 */
	if (dirname == nil)
		goto opendir_fallthrough;

	/*	If there's not '.', '..' or '', fall through	*/
	if ((dirname[0] != '.') && (dirname[0] != ''))
		goto opendir_fallthrough;

	/*	If there's a '', treat it like '..'	*/
	if (dirname[0] == '')
		{
		dname = &(dirname[1]);
		goto path_dotdot;
		}

	/*	If the pathname has "." (current directory) in front of it...	*/
	if (dirname[1] != '.')
		{
		/*	Skip over the "." and fall through	*/
		dname	= &(dirname[1]);
		goto opendir_fallthrough;
		}

	/*	Skip over the ".."	*/
	dname	= &(dirname[2]);

path_dotdot:
	/*	If we get here, the directory has ".." in front of it...	*/
	
	/*	First, get the directory info on the current directory.  We do this so
	 *	that we can get the directory's parent
	 */
	cpb.dirInfo.ioCompletion	= nil;
	cpb.dirInfo.ioNamePtr		= (StringPtr) path_temp;
												/* Unused, but must be set because of
												 * bug in Apple File Sharing.
												 */
	cpb.dirInfo.ioVRefNum		= vRefNum;
	cpb.dirInfo.ioFDirIndex		= -1;
	cpb.dirInfo.ioDrDirID		= dirID;

	if (dd_errno = PBGetCatInfoSync(&cpb))
		return nil;

	/*	Temporarily CD to the parent directory	*/
	pb.ioCompletion				= nil;
	pb.ioNamePtr				= nil;
	pb.ioVRefNum				= pb.ioWDVRefNum;
	pb.ioWDDirID				= cpb.dirInfo.ioDrParID;
	
	if (dd_errno = PBHSetVolSync(&pb))
		return nil;

	/*	This is the common code for all three cases above	*/
opendir_fallthrough:
	/*	If the pathname is too long (this is a Macintosh FS constraint), then return	*/
	if (strlen(dname) > MAXPATHLEN)
		{
		/*	Set the error	*/
		dd_errno	= bdNamErr;
		temp		= nil;
		
		/*	Go to the common exit, where we CD back to the saved WD	*/
		goto opendir_exit;
		}

	/*	If this call was passed a pathname	*/
	if (dname != nil)
		{
		/*	Copy the pathname into a temp	*/
		strcpy(path_temp, dname);
		
		/*	Turn it into a Pascal string for the Mac FS	*/
		CtoPstr(path_temp);

		/*	Change any "/" to ":" for the Mac FS	*/
		if (dd_xform_seps)
			{
			int i;
			
			for (i=1; i<= path_temp[0]; ++i)
				if (path_temp[i] == '/')
					path_temp[i] = ':';
			}

		/*	Try and open the directory	*/
		temp = hopendir(path_temp, 0, 0);
		}
	else
		/*	If this call wasn't passed a pathname, then we call hopendir() with nil to
		 *	tell it to open the current working directory.
		 */
		temp = hopendir(nil, 0, 0);

	/*	This is the common exit code which restores the current WD	*/
opendir_exit:
	pb.ioCompletion				= nil;
	pb.ioNamePtr				= nil;
	pb.ioVRefNum				= vRefNum;
	pb.ioWDDirID				= dirID;
	
	if (dd_errno = PBHSetVolSync(&pb))
		{
		/*	If this call failed, then get rid of the structures created by hopendir()	*/
		closedir(temp);
		return nil;
		}

	return temp;
	}

/****************************************************************************************
 *
 *	This function actually opens the directory.  If you feel brave, you can call it.
 *	If you pass in a dirname, then set vRefNum and dirID to 0.  All named opens are
 *	relative to the current WD.  If you pass in vRefNum and dirID, then don't bother
 *	passing in a name.  This routine WILL CHANGE YOUR CURRENT WORKING DIRECTORY!
 *
 *	Calls:			NewHandle(), PBHGetCatInfo(), PBHSetVol(), PtoCstr(), BlockMove(),
 *					DisposHandle(), MoveHHi(), HLock(), MemError()
 *	Called By:		opendir(), and you if you feel brave.
 *	Globals Used:	dd_errno
 *	Parameters:		pointer to Pascal-string pathname, vRefNum, dirID of desired
 *					directory.  If you pass in a WDRefNum as the vRefNum, set dirID to 0
 *	Returns:		pointer to directory management block or nil & dd_errno will be set
 *
 ****************************************************************************************/

DIR	*hopendir(char *dirname, short vRefNum, long dirID)
	{
	DIR				**curh, *cur;
	CInfoPBRec		cpb;
	WDPBRec			pb;
	Str63			name;

	/*	Get memory for the directory structure	*/
	curh	= (DIR **) NewHandle(sizeof(DIR));

	/*	Did we get it?	*/
	if (curh == nil)
		{
		dd_errno	= MemError();
		return nil;
		}

	/*	Move it high and lock it	*/
	MoveHHi((Handle) curh);
	HLock((Handle) curh);
	cur		= *curh;

	/*	If we're supposed to open anything but the current directory, set the current
	 *	working directory to the desired directory.
	 */
	if ((dirname != nil) || (vRefNum != 0) || (dirID != 0))
		{
		pb.ioCompletion				= nil;
		pb.ioNamePtr				= (StringPtr) dirname;
		pb.ioVRefNum				= vRefNum;
		pb.ioWDDirID				= dirID;
		
		if (dd_errno = PBHSetVolSync(&pb))
			goto failure_exit;
		}

	cur->dd_buf	= nil;
	
	/*	Get info on the desired directory (its name, etc.)	*/
	cpb.dirInfo.ioCompletion	= nil;
	cpb.dirInfo.ioNamePtr		= name;
	cpb.dirInfo.ioVRefNum		= vRefNum;
	cpb.dirInfo.ioFDirIndex		= -1;
	cpb.dirInfo.ioDrDirID		= dirID;
	
	if (dd_errno = PBGetCatInfoSync(&cpb))
		goto failure_exit;

	/*	Save the directory info	*/
	cur->dir_fsp.vRefNum	= vRefNum;
	cur->dd_fd				= cpb.dirInfo.ioDrDirID;
	cur->dd_parent			= cpb.dirInfo.ioDrParID;

	BlockMove(name, cur->dir_fsp.name, sizeof(Str63));

	/*	Convert the name to a C-style string	*/
	PtoCstr(cur->dir_fsp.name);

	/*	Set up our directory structure to read the first entry	*/
	cur->dd_off				= 1;
	cur->dd_numents			= cpb.dirInfo.ioDrNmFls;
	cur->dd_cached			= false;

	return cur;

	/*	This code is branched-to in case of error.  It frees up the memory and returns.	*/
failure_exit:
	DisposeHandle((Handle) curh);
	return nil;
	}

/****************************************************************************************
 *
 *	This function returns the index of the directory entry to be next read.
 *
 *	Calls:			nothing
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		pointer to the directory management block
 *	Returns:		index of the next directory entry to be read.
 *
 ****************************************************************************************/

long	telldir(DIR *dirp)
	{
	if (dirp->dd_off > dirp->dd_numents)
		return -1;
	else
		return dirp->dd_off-1;	/* The -1 is because Macs start at 1 & not 0 for dir index,
								 * and this is a little more POSIX.
								 */
	}

/****************************************************************************************
 *
 *	This function closes the directory opened with opendir() or hopendir()
 *
 *	Calls:			DisposHandle(), RecoverHandle()
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		pointer to the directory management block
 *	Returns:		0 (always successful)
 *
 ****************************************************************************************/

int	closedir(DIR *dirp)
	{
	struct dirent	**cur;
	
	/*	Dispose of any directory entries read in.	*/
	cur	= dirp->dd_buf;
	
	dd_errno	= noErr;

	while (cur)
		{
		struct dirent	**next;
		
		next	= (*cur)->next;
		
		DisposeHandle((Handle) cur);
		
		if (dd_errno == noErr)
			dd_errno	= MemError();

		cur		= next;
		}

	/*	Dispose of the directory managment block	*/
	DisposeHandle(RecoverHandle((Ptr) dirp));

	if (dd_errno == noErr)
		dd_errno	= MemError();

	return dd_errno?-1:0;
	}

/****************************************************************************************
 *
 *	This function sets the index of the next-read directory entry.  It will also search
 *	the list of read entries so that an entry won't be read from disk more than once.
 *
 *	Calls:			nothing
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		pointer to the directory management block, index of directory
 *	Returns:		nothing
 *
 ****************************************************************************************/

void	seekdir(DIR *dirp, long loc)
	{
	struct dirent	**cur;
	
	dirp->dd_off		= loc+1;	/* The +1 is because the Mac indexes directories
									 * from 1 and not 0 and we want to be a little bit
									 * POSIX
									 */

	/*	Search through the entries that we've read already	*/
	cur	= dirp->dd_buf;
	
	while (cur)
		{
		/*	If we find the entry that we've seeked to, set up so that readdir() will
		 *	return this one instead of reading a new one.
		 */
		if (loc == (*cur)->d_off)
			{
			dirp->dd_cached		= true;
			dirp->dd_cache_hint	= cur;

			return;
			}

		cur	= (*cur)->next;
		}

	/*	If we didn't find it, then tell readdir() to get the entry from the FS	*/
	dirp->dd_cached	= false;
	}

/****************************************************************************************
 *
 *	This function will read the next directory entry from disk.  It will return nil and
 *	set dd_errno to noErr when the end of the directory is reached.  It will avoid
 *	reading directory entries from disk more than once.
 *
 *	Calls:			nothing
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		pointer to the directory management block
 *	Returns:		pointer to directory entry or nil if an error occurred and dd_errno
 *					will be set.  If the last entry has already been read, this will
 *					return nil and dd_errno will be set to noErr.
 *
 ****************************************************************************************/

struct dirent	*readdir(DIR *dirp)
	{
	CInfoPBRec		cpb;
	struct dirent	**meh, *me;
	
	/*	If the entry has been read already, then return the already present entry	*/
	if (dirp->dd_cached)
		me	= *(dirp->dd_cache_hint);
	else
		/*	Otherwise, read it from disk...	*/
		{
		/*	Past the end of the directory?	*/
		if (dirp->dd_off > dirp->dd_numents)
			{
			dd_errno	= noErr;
			return nil;
			}

		/*	Allocate space for a new entry	*/
		meh	= (struct dirent **) NewHandle(sizeof(struct dirent));
		
		/*	Enough memory?	*/
		if (meh == nil)
			{
			dd_errno	= MemError();
			return nil;
			}

		/*	Lock the entry	*/
		MoveHHi((Handle) meh);
		HLock((Handle) meh);

		me	= *meh;

		/*	Get the entry's info from disk	*/
		me->fsp.name[0]				= 0;

		cpb.dirInfo.ioCompletion	= nil;
		cpb.dirInfo.ioNamePtr		= me->fsp.name;
		cpb.dirInfo.ioVRefNum		= dirp->dir_fsp.vRefNum;
		cpb.dirInfo.ioFDirIndex		= dirp->dd_off;
		cpb.dirInfo.ioDrDirID		= dirp->dd_fd;

		if (dd_errno = PBGetCatInfoSync(&cpb))
			{
			DisposeHandle((Handle) meh);
			return nil;
			}
	
		/*	Set up the dirent structure	*/
		me->d_off			= dirp->dd_off-1;
		me->fsp.vRefNum		= cpb.dirInfo.ioVRefNum;
		me->d_fileno		= cpb.dirInfo.ioDrDirID;
		me->d_parent		= cpb.dirInfo.ioDrParID;
		
		/*	C strings only!	*/
		PtoCstr(me->fsp.name);

		/*	Add it to the list for this directory	*/
		me->next			= dirp->dd_buf;
		
		dirp->dd_buf		= meh;
		}

	/*	Seek to the next entry	*/
	seekdir(dirp, dirp->dd_off);

	/*	Return what we've found	*/
	return me;
	}

/****************************************************************************************
 *
 *	This function will give an absolute pathname to a given directory.
 *
 *	Calls:			NewPtr(), DisposPtr(), PBGetCatInfo()
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		vRefNum and startDirID of desired path, pointer to path name storage,
 *					length of path name storage, pointer to C-string separator.
 *	Returns:		bdNamErr if the path would overflow the storage,
 *					some other error code if something else happened,
 *					or noErr on success.
 *
 ****************************************************************************************/

OSErr	hgetwd(short vRefNum, long startDirID, char *path, int max_path_len, char *sep)
	{
	long		curDirID;
	OSErr		err;
	CInfoPBRec	pb;
	Str63		name;
	char		*temp_path;

	/*	Start with an empty path	*/
	path[0]	= 0;

	/*	Get memory for a temporary path	*/
	temp_path	= (char *) NewPtr(max_path_len);
	
	if (temp_path == nil)
		return MemError();

	/*	Start at the given directory	*/
	curDirID	= startDirID;

	do	{
		/*	Get cat info for the current directory	*/
		name[0]	= 0;

		pb.dirInfo.ioCompletion	= nil;
		pb.dirInfo.ioNamePtr	= name;
		pb.dirInfo.ioVRefNum	= vRefNum;
		pb.dirInfo.ioFDirIndex	= -1;
		pb.dirInfo.ioDrDirID	= curDirID;
		
		if (err = PBGetCatInfoSync(&pb))
			{
			DisposePtr((Ptr) temp_path);
			return err;
			}

		/*	Convert name to a C string	*/
		PtoCstr(name);

		/*	Check that we don't overflow storage	*/
		if ((strlen((char *) name) + strlen(path) + strlen(sep)) >= max_path_len)
			{
			DisposePtr((Ptr) temp_path);
			return bdNamErr;
			}

		/*	Prepend the name and separator	*/
		strcpy(temp_path, path);
		strcpy(path, (char *) name);
		strcat(path, sep);
		strcat(path, temp_path);

		/*	Move "up" one directory	*/
		curDirID	= pb.dirInfo.ioDrParID;
		}
	/*	Until we hit the root directory	*/
	while (pb.dirInfo.ioDrDirID != fsRtDirID);

	/*	Get rid of our temp storage and return	*/
	DisposePtr((Ptr) temp_path);

	return MemError();
	}

/****************************************************************************************
 *
 *	This function will change the current working directory.
 *
 *	Calls:			opendir(), closedir(), PBHSetVol()
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		C-string pathname.
 *	Returns:		-1 on failure, 0 on success.  Sets dd_errno on failure.
 *
 ****************************************************************************************/

#include <LowMem.h>	/* sdm7g */

int	chdir(char *path)
	{
	DIR		*d;
	short	vRefNum;
	long	dirID;
	WDPBRec	pb;
	  	
	/*	Open the directory	*/
	d	= opendir(path);
	
	if (d == nil)
		return -1;

	/*	Get the Mac FS identification for this directory	*/
	vRefNum	= d->dd_volume;
	dirID	= d->dd_fd;
	
	/*	Close the directory	*/
	closedir(d);

	/*	CD to the new directory	*/
	pb.ioCompletion	= nil;
	pb.ioNamePtr	= nil;
	pb.ioVRefNum	= vRefNum;
	pb.ioWDDirID	= dirID;
	
	dd_errno = PBHSetVolSync(&pb);
 
   /*	LMSetSFSaveDisk( -vRefNum );	/* sdm7g */
  	LMSetCurDirStore( dirID );	/* sdm7g */
	
	return dd_errno?-1:0;
	}

/****************************************************************************************
 *
 *	This function will get the current working directory's path.
 *
 *	Calls:			PBHGetVol(), hgetwd()
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		pointer to a buffer of MAXPATHLEN bytes.
 *	Returns:		pointer to the buffer on success, on failure, nil and dd_errno will
 *					be set.
 *
 ****************************************************************************************/

char *getwd(char *path)
	{
	WDPBRec	pb;

	/*	Get the current working directory	*/
	pb.ioCompletion	= nil;
	pb.ioNamePtr	= nil;
	
	if (dd_errno = PBHGetVolSync(&pb))
		return nil;

	/*	Transform it into a path	*/
	if (dd_errno = hgetwd(pb.ioWDVRefNum, pb.ioWDDirID, path, MAXPATHLEN-1, dd_separator))
		return nil;

	return path;
	}

/****************************************************************************************
 *
 *	This function will get the path to a given (already opened) directory.
 *
 *	Calls:			hgetwd()
 *	Called By:		<general purpose>
 *	Globals Used:	none
 *	Parameters:		pointer to a buffer of MAXPATHLEN bytes.
 *	Returns:		pointer to the buffer on success, on failure, nil and dd_errno will
 *					be set.
 *
 ****************************************************************************************/

char *pathdir(DIR *dirp, char *path)
	{
	if (dd_errno = hgetwd(dirp->dd_volume, dirp->dd_fd, path, MAXPATHLEN-1, dd_separator))
		return nil;

	return path;
	}