source: abuse/tags/pd/macabuse/imlib/port/mac/dirent.c @ 49

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