source: golgotha/src/i4/file/mac/dirent.cc @ 80

Last change on this file since 80 was 80, checked in by Sam Hocevar, 14 years ago
  • Adding the Golgotha source code. Not sure what's going to be interesting in there, but since it's all public domain, there's certainly stuff to pick up.
File size: 19.3 KB
Line 
1/********************************************************************** <BR>
2  This file is part of Crack dot Com's free source code release of
3  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
4  information about compiling & licensing issues visit this URL</a>
5  <PRE> If that doesn't help, contact Jonathan Clark at
6  golgotha_source@usa.net (Subject should have "GOLG" in it)
7***********************************************************************/
8
9//{{{ Dirent for Mac
10/****************************************************************************************
11 *
12 *  Notes:
13 *  From: 'dirent.c' by George T. Talbot
14 *
15 *  Modifications:
16 *
17 *      1) These routines will NOT work under A/UX.
18 *      2) WD = working directory
19 *      3) CD = change directory
20 *      4) FS = file system
21 *      5) Mac filesystems allow spaces as part of pathnames!
22 *      6) All routines which return a path use the default Macintosh path separator,
23 *         a colon (":").
24 *
25 ****************************************************************************************/
26//}}}
27
28#include "dirent.hh"
29//#include <pascal.h>
30#include <string.h>
31
32OSErr dd_errno;             /*  Global errno to check after calls to dirent routines  */
33char  *dd_separator = ":";  /*  If you're feeling brave, change this to "/" */
34int   dd_xform_seps = false;
35
36
37void PtoCstr(unsigned char *st)
38//{{{
39{
40  int len = st[0];
41
42  memcpy(st,st+1,len);
43  st[len] = 0;
44}
45//}}}
46
47
48void CtoPstr(char *st)
49//{{{
50{
51  int len = strlen(st);
52
53  memcpy(st+1,st,len);
54  st[0] = len;
55}
56//}}}
57
58
59DIR *opendir(char *dirname)
60//  This function, given a Macintosh-style pathname, will open a directory to that path.
61//{{{
62/****************************************************************************************
63 *  NOTES:  1)  passing in nil will get you the current directory.
64 *      2)  ".:", "..:" & "Å:" are supported at the beginning of paths ONLY
65 *        by this routine.
66 *      3)  "/" will be turned into ":" by this routine.
67 *
68 *  Calls:      PBHGetVol(), PBHGetCatInfo(), PBHSetVol(), hopendir(), CtoPstr()
69 *  Called By:    <general purpose>
70 *  Globals Used: dd_errno
71 *  Parameters:   pointer to C-string pathname or nil for current directory
72 *  Returns:    pointer to directory management block or nil & dd_errno will be set
73 ****************************************************************************************/
74{
75  WDPBRec     pb;
76  CInfoPBRec    cpb;
77  short     vRefNum;
78  long      dirID;
79  char      *dname;
80  DIR       *temp;
81  char      path_temp[MAXPATHLEN+1];  /*  Temporary area for building pathname  */
82
83  /*  Save the current path */
84  pb.ioCompletion = nil;
85  pb.ioNamePtr  = nil;
86 
87  if (dd_errno = PBHGetVol(&pb, false))
88    return nil;
89
90  vRefNum = pb.ioWDVRefNum;
91  dirID = pb.ioWDDirID;
92
93  /*  dname points to the desired pathname  */
94  dname = dirname;
95
96  /*  If no pathname was passed in, or there are no ".", ".." or "Å" special directory
97   *  names, then handle the pathname as normal.
98   */
99  if (dirname == nil)
100    goto opendir_fallthrough;
101
102  /*  If there's not '.', '..' or 'Å', fall through  */
103  if ((dirname[0] != '.') && (dirname[0] != 'Å'))
104    goto opendir_fallthrough;
105
106  /*  If there's a 'Å', treat it like '..' */
107  if (dirname[0] == 'Å')
108    {
109    dname = &(dirname[1]);
110    goto path_dotdot;
111    }
112
113  /*  If the pathname has "." (current directory) in front of it... */
114  if (dirname[1] != '.')
115    {
116    /*  Skip over the "." and fall through  */
117    dname = &(dirname[1]);
118    goto opendir_fallthrough;
119    }
120
121  /*  Skip over the ".."  */
122  dname = &(dirname[2]);
123
124path_dotdot:
125  /*  If we get here, the directory has ".." in front of it...  */
126 
127  /*  First, get the directory info on the current directory.  We do this so
128   *  that we can get the directory's parent
129   */
130  cpb.dirInfo.ioCompletion  = nil;
131  cpb.dirInfo.ioNamePtr   = (unsigned char *)path_temp;
132                        /* Unused, but must be set because of
133                         * bug in Apple File Sharing.
134                         */
135  cpb.dirInfo.ioVRefNum   = vRefNum;
136  cpb.dirInfo.ioFDirIndex   = -1;
137  cpb.dirInfo.ioDrDirID   = dirID;
138
139  if (dd_errno = PBGetCatInfo(&cpb, false))
140    return nil;
141
142  /*  Temporarily CD to the parent directory  */
143  pb.ioCompletion       = nil;
144  pb.ioNamePtr        = nil;
145  pb.ioVRefNum        = pb.ioWDVRefNum;
146  pb.ioWDDirID        = cpb.dirInfo.ioDrParID;
147 
148  if (dd_errno = PBHSetVol(&pb, false))
149    return nil;
150
151  /*  This is the common code for all three cases above */
152opendir_fallthrough:
153  /*  If the pathname is too long (this is a Macintosh FS constraint), then return  */
154  if (strlen(dname) > MAXPATHLEN)
155    {
156    /*  Set the error */
157    dd_errno  = bdNamErr;
158    temp    = nil;
159   
160    /*  Go to the common exit, where we CD back to the saved WD */
161    goto opendir_exit;
162    }
163
164  /*  If this call was passed a pathname  */
165  if (dname != nil)
166    {
167    /*  Copy the pathname into a temp */
168    strcpy(path_temp, dname);
169   
170    /*  Turn it into a Pascal string for the Mac FS */
171    CtoPstr(path_temp);
172
173    /*  Change any "/" to ":" for the Mac FS  */
174    if (dd_xform_seps)
175    {
176      int i;
177     
178      for (i=1; i<= path_temp[0]; ++i)
179        if (path_temp[i] == '/')
180          path_temp[i] = ':';
181    }
182
183    /*  Try and open the directory  */
184    temp = hopendir(path_temp, 0, 0);
185    }
186  else
187    /*  If this call wasn't passed a pathname, then we call hopendir() with nil to
188     *  tell it to open the current working directory.
189     */
190    temp = hopendir(nil, 0, 0);
191
192  /*  This is the common exit code which restores the current WD  */
193opendir_exit:
194  pb.ioCompletion       = nil;
195  pb.ioNamePtr        = nil;
196  pb.ioVRefNum        = vRefNum;
197  pb.ioWDDirID        = dirID;
198 
199  if (dd_errno = PBHSetVol(&pb, false))
200  {
201    /*  If this call failed, then get rid of the structures created by hopendir() */
202    closedir(temp);
203    return nil;
204  }
205
206  return temp;
207}
208//}}}
209
210
211DIR *hopendir(char *dirname, short vRefNum, long dirID)
212//  This function actually opens the directory.  If you feel brave, you can call it.
213//  If you pass in a dirname, then set vRefNum and dirID to 0.  All named opens are
214//  relative to the current WD.  If you pass in vRefNum and dirID, then don't bother
215//  passing in a name.  This routine WILL CHANGE YOUR CURRENT WORKING DIRECTORY!
216//{{{
217/****************************************************************************************
218 *  Calls:      NewHandle(), PBHGetCatInfo(), PBHSetVol(), PtoCstr(), BlockMove(),
219 *          DisposHandle(), MoveHHi(), HLock(), MemError()
220 *  Called By:    opendir(), and you if you feel brave.
221 *  Globals Used: dd_errno
222 *  Parameters:   pointer to Pascal-string pathname, vRefNum, dirID of desired
223 *          directory.  If you pass in a WDRefNum as the vRefNum, set dirID to 0
224 *  Returns:    pointer to directory management block or nil & dd_errno will be set
225 ****************************************************************************************/
226{
227  DIR       **curh, *cur;
228  CInfoPBRec    cpb;
229  WDPBRec     pb;
230  Str63     name;
231
232  /*  Get memory for the directory structure  */
233  curh  = (DIR **) NewHandle(sizeof(DIR));
234
235  /*  Did we get it?  */
236  if (curh == nil)
237  {
238    dd_errno  = MemError();
239    return nil;
240  }
241
242  /*  Move it high and lock it  */
243  MoveHHi((Handle)curh);
244  HLock((Handle)curh);
245  cur   = *curh;
246
247  /*  If we're supposed to open anything but the current directory, set the current
248   *  working directory to the desired directory.
249   */
250  if ((dirname != nil) || (vRefNum != 0) || (dirID != 0))
251  {
252    pb.ioCompletion       = nil;
253    pb.ioNamePtr        = (unsigned char *)dirname;
254    pb.ioVRefNum        = vRefNum;
255    pb.ioWDDirID        = dirID;
256   
257    if (dd_errno = PBHSetVol(&pb, false))
258      goto failure_exit;
259  }
260
261  cur->dd_buf = nil;
262 
263  /*  Get info on the desired directory (its name, etc.)  */
264  cpb.dirInfo.ioCompletion  = nil;
265  cpb.dirInfo.ioNamePtr   = name;
266  cpb.dirInfo.ioVRefNum   = vRefNum;
267  cpb.dirInfo.ioFDirIndex   = -1;
268  cpb.dirInfo.ioDrDirID   = dirID;
269 
270  if (dd_errno = PBGetCatInfo(&cpb, false))
271    goto failure_exit;
272
273  /*  Save the directory info */
274  cur->dir_fsp.vRefNum  = vRefNum;
275  cur->dd_fd        = cpb.dirInfo.ioDrDirID;
276  cur->dd_parent      = cpb.dirInfo.ioDrParID;
277
278  BlockMove(name, cur->dir_fsp.name, sizeof(Str63));
279
280  /*  Convert the name to a C-style string  */
281  PtoCstr(cur->dir_fsp.name);
282
283  /*  Set up our directory structure to read the first entry  */
284  cur->dd_off       = 1;
285  cur->dd_numents     = cpb.dirInfo.ioDrNmFls;
286  cur->dd_cached      = false;
287
288  return cur;
289
290  /*  This code is branched-to in case of error.  It frees up the memory and returns. */
291  failure_exit:
292  DisposHandle((Handle) curh);
293  return nil;
294}
295//}}}
296
297
298long  telldir(DIR *dirp)
299//  This function returns the index of the directory entry to be next read.
300//{{{
301/****************************************************************************************
302 *  Calls:      nothing
303 *  Called By:    <general purpose>
304 *  Globals Used: none
305 *  Parameters:   pointer to the directory management block
306 *  Returns:    index of the next directory entry to be read.
307 ****************************************************************************************/
308{
309  if (dirp->dd_off > dirp->dd_numents)
310    return -1;
311  else
312    return dirp->dd_off-1;  /* The -1 is because Macs start at 1 & not 0 for dir index,
313                             * and this is a little more POSIX.
314                             */
315}
316//}}}
317
318
319int closedir(DIR *dirp)
320//  This function closes the directory opened with opendir() or hopendir()
321//{{{
322/****************************************************************************************
323 *  Calls:      DisposHandle(), RecoverHandle()
324 *  Called By:    <general purpose>
325 *  Globals Used: none
326 *  Parameters:   pointer to the directory management block
327 *  Returns:    0 (always successful)
328 ****************************************************************************************/
329{
330  struct dirent **cur;
331 
332  /*  Dispose of any directory entries read in. */
333  cur = dirp->dd_buf;
334 
335  dd_errno  = noErr;
336
337  while (cur)
338  {
339    struct dirent **next;
340   
341    next  = (*cur)->next;
342   
343    DisposHandle((Handle) cur);
344   
345    if (dd_errno == noErr)
346      dd_errno  = MemError();
347
348    cur   = next;
349  }
350
351  /*  Dispose of the directory managment block  */
352  DisposHandle(RecoverHandle((Ptr) dirp));
353
354  if (dd_errno == noErr)
355    dd_errno  = MemError();
356
357  return dd_errno?-1:0;
358}
359//}}}
360
361
362void  seekdir(DIR *dirp, long loc)
363//  This function sets the index of the next-read directory entry.  It will also search
364//  the list of read entries so that an entry won't be read from disk more than once.
365//{{{
366/****************************************************************************************
367 *  Calls:      nothing
368 *  Called By:    <general purpose>
369 *  Globals Used: none
370 *  Parameters:   pointer to the directory management block, index of directory
371 *  Returns:    nothing
372 ****************************************************************************************/
373{
374  struct dirent **cur;
375 
376  dirp->dd_off    = loc+1;  /* The +1 is because the Mac indexes directories
377                             * from 1 and not 0 and we want to be a little bit
378                             * POSIX
379                             */
380
381  /*  Search through the entries that we've read already  */
382  cur = dirp->dd_buf;
383 
384  while (cur)
385  {
386    /*  If we find the entry that we've seeked to, set up so that readdir() will
387     *  return this one instead of reading a new one.
388     */
389    if (loc == (*cur)->d_off)
390    {
391      dirp->dd_cached   = true;
392      dirp->dd_cache_hint = cur;
393
394      return;
395    }
396
397    cur = (*cur)->next;
398  }
399
400  /*  If we didn't find it, then tell readdir() to get the entry from the FS  */
401  dirp->dd_cached = false;
402}
403//}}}
404
405
406struct dirent *readdir(DIR *dirp)
407//  This function will read the next directory entry from disk.  It will return nil and
408//  set dd_errno to noErr when the end of the directory is reached.  It will avoid
409//  reading directory entries from disk more than once.
410//{{{
411/****************************************************************************************
412 *  Calls:      nothing
413 *  Called By:    <general purpose>
414 *  Globals Used: none
415 *  Parameters:   pointer to the directory management block
416 *  Returns:    pointer to directory entry or nil if an error occurred and dd_errno
417 *          will be set.  If the last entry has already been read, this will
418 *          return nil and dd_errno will be set to noErr.
419 ****************************************************************************************/
420{
421  CInfoPBRec    cpb;
422  struct dirent **meh, *me;
423 
424  /*  If the entry has been read already, then return the already present entry */
425  if (dirp->dd_cached)
426    me  = *(dirp->dd_cache_hint);
427  else
428    /*  Otherwise, read it from disk... */
429  {
430    /*  Past the end of the directory?  */
431    if (dirp->dd_off > dirp->dd_numents)
432    {
433      dd_errno  = noErr;
434      return nil;
435    }
436
437    /*  Allocate space for a new entry  */
438    meh = (struct dirent **) NewHandle(sizeof(struct dirent));
439   
440    /*  Enough memory?  */
441    if (meh == nil)
442    {
443      dd_errno  = MemError();
444      return nil;
445    }
446
447    /*  Lock the entry  */
448    MoveHHi((Handle) meh);
449    HLock((Handle) meh);
450
451    me  = *meh;
452
453    /*  Get the entry's info from disk  */
454    me->fsp.name[0]       = 0;
455
456    cpb.dirInfo.ioCompletion  = nil;
457    cpb.dirInfo.ioNamePtr   = me->fsp.name;
458    cpb.dirInfo.ioVRefNum   = dirp->dir_fsp.vRefNum;
459    cpb.dirInfo.ioFDirIndex   = dirp->dd_off;
460    cpb.dirInfo.ioDrDirID   = dirp->dd_fd;
461
462    if (dd_errno = PBGetCatInfo(&cpb, false))
463    {
464      DisposHandle((Handle) meh);
465      return nil;
466    }
467 
468    /*  Set up the dirent structure */
469    me->d_off     = dirp->dd_off-1;
470    me->fsp.vRefNum   = cpb.dirInfo.ioVRefNum;
471    me->d_fileno    = cpb.dirInfo.ioDrDirID;
472    me->d_parent    = cpb.dirInfo.ioDrParID;
473   
474    /*  C strings only! */
475    PtoCstr(me->fsp.name);
476
477    /*  Add it to the list for this directory */
478    me->next      = dirp->dd_buf;
479   
480    dirp->dd_buf    = meh;
481  }
482
483  /*  Seek to the next entry  */
484  seekdir(dirp, dirp->dd_off);
485
486  /*  Return what we've found */
487  return me;
488}
489//}}}
490
491OSErr hgetwd(short vRefNum, long startDirID, char *path, int max_path_len, char *sep)
492//  This function will give an absolute pathname to a given directory.
493//{{{
494/****************************************************************************************
495 *  Calls:      NewPtr(), DisposPtr(), PBGetCatInfo()
496 *  Called By:    <general purpose>
497 *  Globals Used: none
498 *  Parameters:   vRefNum and startDirID of desired path, pointer to path name storage,
499 *          length of path name storage, pointer to C-string separator.
500 *  Returns:    bdNamErr if the path would overflow the storage,
501 *          some other error code if something else happened,
502 *          or noErr on success.
503 ****************************************************************************************/
504{
505  long    curDirID;
506  OSErr   err;
507  CInfoPBRec  pb;
508  Str63   name;
509  char    *temp_path;
510
511  /*  Start with an empty path  */
512  path[0] = 0;
513
514  /*  Get memory for a temporary path */
515  temp_path = (char *) NewPtr(max_path_len);
516 
517  if (temp_path == nil)
518    return MemError();
519
520  /*  Start at the given directory  */
521  curDirID  = startDirID;
522
523  do 
524  {
525    /*  Get cat info for the current directory  */
526    name[0] = 0;
527
528    pb.dirInfo.ioCompletion = nil;
529    pb.dirInfo.ioNamePtr  = name;
530    pb.dirInfo.ioVRefNum  = vRefNum;
531    pb.dirInfo.ioFDirIndex  = -1;
532    pb.dirInfo.ioDrDirID  = curDirID;
533   
534    if (err = PBGetCatInfo(&pb, false))
535    {
536      DisposPtr((Ptr) temp_path);
537      return err;
538    }
539
540    /*  Convert name to a C string  */
541    PtoCstr(name);
542
543    /*  Check that we don't overflow storage  */
544    if ((strlen((char*)name) + strlen(path) + strlen(sep)) >= max_path_len)
545    {
546      DisposPtr((Ptr) temp_path);
547      return bdNamErr;
548    }
549
550    /*  Prepend the name and separator  */
551    strcpy(temp_path, path);
552    strcpy(path, (char*)name);
553    strcat(path, sep);
554    strcat(path, temp_path);
555
556    /*  Move "up" one directory */
557    curDirID  = pb.dirInfo.ioDrParID;
558  }
559  /*  Until we hit the root directory */
560  while (pb.dirInfo.ioDrDirID != fsRtDirID);
561
562  /*  Get rid of our temp storage and return  */
563  DisposPtr((Ptr) temp_path);
564
565  return MemError();
566}
567//}}}
568
569
570int chdir(char *path)
571//  This function will change the current working directory.
572//{{{
573/****************************************************************************************
574 *  Calls:      opendir(), closedir(), PBHSetVol()
575 *  Called By:    <general purpose>
576 *  Globals Used: none
577 *  Parameters:   C-string pathname.
578 *  Returns:    -1 on failure, 0 on success.  Sets dd_errno on failure.
579 ****************************************************************************************/
580{
581  DIR   *d;
582  short vRefNum;
583  long  dirID;
584  WDPBRec pb;
585
586  /*  Open the directory  */
587  d = opendir(path);
588 
589  if (d == nil)
590    return -1;
591
592  /*  Get the Mac FS identification for this directory  */
593  vRefNum = d->dd_volume;
594  dirID = d->dd_fd;
595 
596  /*  Close the directory */
597  closedir(d);
598
599  /*  CD to the new directory */
600  pb.ioCompletion = nil;
601  pb.ioNamePtr  = nil;
602  pb.ioVRefNum  = vRefNum;
603  pb.ioWDDirID  = dirID;
604 
605  dd_errno = PBHSetVol(&pb, false);
606 
607  return dd_errno?-1:0;
608}
609//}}}
610
611
612char *getwd(char *path)
613//  This function will get the current working directory's path.
614//{{{
615/****************************************************************************************
616 *  Calls:      PBHGetVol(), hgetwd()
617 *  Called By:    <general purpose>
618 *  Globals Used: none
619 *  Parameters:   pointer to a buffer of MAXPATHLEN bytes.
620 *  Returns:    pointer to the buffer on success, on failure, nil and dd_errno will
621 *          be set.
622 ****************************************************************************************/
623{
624  WDPBRec pb;
625
626  /*  Get the current working directory */
627  pb.ioCompletion = nil;
628  pb.ioNamePtr  = nil;
629 
630  if (dd_errno = PBHGetVol(&pb, false))
631    return nil;
632
633  /*  Transform it into a path  */
634  if (dd_errno = hgetwd(pb.ioWDVRefNum, pb.ioWDDirID, path, MAXPATHLEN-1, dd_separator))
635    return nil;
636
637  return path;
638}
639//}}}
640
641
642char *pathdir(DIR *dirp, char *path)
643//  This function will get the path to a given (already opened) directory.
644//{{{
645/****************************************************************************************
646 *  Calls:      hgetwd()
647 *  Called By:    <general purpose>
648 *  Globals Used: none
649 *  Parameters:   pointer to a buffer of MAXPATHLEN bytes.
650 *  Returns:    pointer to the buffer on success, on failure, nil and dd_errno will
651 *          be set.
652 ****************************************************************************************/
653{
654  if (dd_errno = hgetwd(dirp->dd_volume, dirp->dd_fd, path, MAXPATHLEN-1, dd_separator))
655    return nil;
656
657  return path;
658}
659//}}}
660
661
662//{{{ Emacs Locals
663// Local Variables:
664// folded-file: t
665// End:
666//}}}
Note: See TracBrowser for help on using the repository browser.