home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume9 / dtree / getcwd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-12-20  |  5.1 KB  |  204 lines

  1. /*
  2.     getcwd -- get current working directory name (POSIX and SVID compatible)
  3.  
  4.     last edit:    21-Sep-1987    D A Gwyn
  5.  
  6.     This public-domain getcwd() routine can be used to replace the UNIX
  7.     System V library routine (which uses popen() to capture the output of
  8.     the "pwd" command).  Once that is done, "pwd" can be reimplemented as
  9.     just puts(getcwd()).
  10.  
  11.     This implementation depends on every directory having entries for
  12.     "." and "..".  It also depends on the internals of the <dirent.h>
  13.     data structures to some degree.
  14.  
  15.     I considered using chdir() to ascend the hierarchy, followed by a
  16.     final chdir() to the path being returned by getcwd() to restore the
  17.     location, but decided that error recovery was too difficult that way.
  18.     The algorithm I settled on was inspired by my rewrite of the "pwd"
  19.     utility, combined with the dotdots[] array trick from the SVR2 shell.
  20. */
  21. #include    <sys/types.h>
  22. #include    <sys/stat.h>
  23. #include    <string.h>
  24. #include    <dirent.h>
  25. #include    <errno.h>
  26.  
  27. typedef char    *pointer;        /* (void *) if you have it */
  28.  
  29. extern void    free();
  30. extern pointer    malloc();
  31. extern int    fstat(), stat();
  32.  
  33. extern int    errno;            /* normally done by <errno.h> */
  34.  
  35. #ifndef NULL
  36. #define    NULL    0            /* amorphous null pointer constant */
  37. #endif
  38.  
  39. #ifndef NAME_MAX
  40. #define    NAME_MAX    255        /* maximum directory entry size */
  41. #endif
  42.  
  43. char    *
  44. getcwd( buf, size )            /* returns pointer to CWD pathname */
  45.     char        *buf;        /* where to put name (NULL to malloc) */
  46.     int        size;        /* size of buf[] or malloc()ed memory */
  47.     {
  48.     static char    dotdots[] =
  49. "../../../../../../../../../../../../../../../../../../../../../../../../../..";
  50.     char         *dotdot;    /* -> dotdots[.], right to left */
  51.     DIR        *dirp;        /* -> parent directory stream */
  52.     struct dirent    *dir;        /* -> directory entry */
  53.     struct stat    stat1, stat2;    /* info from stat() */
  54.     struct stat    *d = &stat1;    /* -> info about "." */
  55.     struct stat    *dd = &stat2;    /* -> info about ".." */
  56.     register char    *buffer;    /* local copy of buf, or malloc()ed */
  57.     char        *bufend;    /* -> buffer[size] */
  58.     register char    *endp;        /* -> end of reversed string */
  59.     register char    *dname;        /* entry name ("" for root) */
  60.     int        serrno = errno;    /* save entry errno */
  61.  
  62.     if ( size == 0 )
  63.         {
  64.         errno = EINVAL;        /* invalid argument */
  65.         return NULL;
  66.         }
  67.  
  68.     if ( (buffer = buf) == NULL    /* wants us to malloc() the string */
  69.       && (buffer = (char *)malloc( (unsigned)size )) == NULL
  70.        )    {
  71.         errno = ENOMEM;        /* cannot malloc() specified size */
  72.         return NULL;
  73.         }
  74.  
  75.     if ( stat( ".", dd ) != 0 )    /* prime the pump */
  76.         goto error;        /* errno already set */
  77.  
  78.     endp = buffer;            /* initially, empty string */
  79.     bufend = &buffer[size];
  80.  
  81.     for ( dotdot = &dotdots[sizeof(dotdots)]; dotdot != dotdots; )
  82.         {
  83.         dotdot -= 3;        /* include one more "/.." section */
  84.                     /* (first time is actually "..") */
  85.  
  86.         /* swap stat() info buffers */
  87.         {
  88.         register struct stat    *temp = d;
  89.  
  90.         d = dd;            /* new current dir is old parent dir */
  91.         dd = temp;
  92.         }
  93.  
  94.         if ( (dirp = opendir( dotdot )) == NULL )    /* new parent */
  95.             goto error;    /* errno already set */
  96.  
  97.         if ( fstat( dirp->dd_fd, dd ) != 0 )
  98.             {
  99.             serrno = errno;    /* set by fstat() */
  100.             (void)closedir( dirp );
  101.             errno = serrno;    /* in case closedir() clobbered it */
  102.             goto error;
  103.             }
  104.  
  105.         if ( d->st_dev == dd->st_dev )
  106.             {        /* not crossing a mount point */
  107.             if ( d->st_ino == dd->st_ino )
  108.                 {    /* root directory */
  109.                 dname = "";
  110.                 goto append;
  111.                 }
  112.  
  113.             do
  114.                 if ( (dir = readdir( dirp )) == NULL )
  115.                     {
  116.                     (void)closedir( dirp );
  117.                     errno = ENOENT;    /* missing entry */
  118.                     goto error;
  119.                     }
  120.             while ( dir->d_ino != d->st_ino );
  121.             }
  122.         else    {        /* crossing a mount point */
  123.             struct stat    t;    /* info re. test entry */
  124.             char        name[sizeof(dotdots) + 1 + NAME_MAX];
  125.  
  126.             (void)strcpy( name, dotdot );
  127.             dname = &name[strlen( name )];
  128.             *dname++ = '/';
  129.  
  130.             do    {
  131.                 if ( (dir = readdir( dirp )) == NULL )
  132.                     {
  133.                     (void)closedir( dirp );
  134.                     errno = ENOENT;    /* missing entry */
  135.                     goto error;
  136.                     }
  137.  
  138.                 (void)strcpy( dname, dir->d_name );
  139.                 /* must fit if NAME_MAX is not a lie */
  140.                 }
  141.             while ( stat( name, &t ) != 0
  142.                  || t.st_ino != d->st_ino
  143.                  || t.st_dev != d->st_dev
  144.                   );
  145.             }
  146.  
  147.         dname = dir->d_name;
  148.  
  149.         /* append "/" and reversed dname string onto buffer */
  150.     append:
  151.         if ( endp != buffer    /* avoid trailing / in final name */
  152.           || dname[0] == '\0'    /* but allow "/" when CWD is root */
  153.            )
  154.             *endp++ = '/';
  155.  
  156.         {
  157.         register char    *app;    /* traverses dname string */
  158.  
  159.         for ( app = dname; *app != '\0'; ++app )
  160.             ;
  161.  
  162.         if ( app - dname >= bufend - endp )
  163.             {
  164.             (void)closedir( dirp );
  165.             errno = ERANGE;    /* won't fit allotted space */
  166.             goto error;
  167.             }
  168.  
  169.         while ( app != dname )
  170.             *endp++ = *--app;
  171.         }
  172.  
  173.         (void)closedir( dirp );
  174.  
  175.         if ( dname[0] == '\0' )    /* reached root; wrap it up */
  176.             {
  177.             register char    *startp;    /* -> buffer[.] */
  178.  
  179.             *endp = '\0';    /* plant null terminator */
  180.  
  181.             /* straighten out reversed pathname string */
  182.             for ( startp = buffer; --endp > startp; ++startp )
  183.                 {
  184.                 char    temp = *endp;
  185.  
  186.                 *endp = *startp;
  187.                 *startp = temp;
  188.                 }
  189.  
  190.             errno = serrno;    /* restore entry errno */
  191.             return buffer;
  192.             }
  193.         }
  194.  
  195.     errno = ENOMEM;            /* actually, algorithm failure */
  196.  
  197.     error:
  198.     if ( buf == NULL )
  199.         free( (pointer)buffer );
  200.  
  201.     return NULL;
  202.     }
  203.  
  204.