2 getcwd -- get pathname of current working directory
4 public-domain implementation
6 last edit: 03-Nov-1990 Gwyn@BRL.MIL
8 complies with the following standards:
11 X/Open Portability Guide Issue 2 (when "XPG2" is defined)
12 X/Open Portability Guide Issue 3
14 This implementation of getcwd() can be used to replace the UNIX
15 System V library routine (which uses popen() to capture the output of
16 the "pwd" command). Once that is done, "pwd" can be reimplemented as
17 just puts(getcwd((char*)0,0)), assuming "XPG2" is defined below.
19 This implementation depends on every directory having entries for
20 "." and "..". It also depends on the internals of the <dirent.h>
21 data structures to some degree.
23 I considered using chdir() to ascend the hierarchy, followed by a
24 final chdir() to the path being returned by getcwd() to restore the
25 location, but decided that error recovery was too difficult that way.
26 The algorithm I settled on was inspired by my rewrite of the "pwd"
27 utility, combined with the dotdots[] array trick from the SVR2 shell.
29 #define XPG2 /* define to support obsolete XPG2-mandated feature */
32 #include <sys/types.h>
36 # include <sys/ndir.h>
37 # define dirent direct
45 typedef char *pointer
; /* (void *) if you have it */
48 extern pointer
malloc();
49 extern int fstat(), stat();
51 extern int errno
; /* normally done by <errno.h> */
54 #define NULL 0 /* amorphous null pointer constant */
58 #define NAME_MAX 255 /* maximum directory entry size */
63 getcwd( buf
, size
) /* returns pointer to CWD pathname */
64 char *buf
; /* where to put name (NULL to malloc) */
65 int size
; /* size of buf[] or malloc()ed memory */
67 static char dotdots
[] =
68 "../../../../../../../../../../../../../../../../../../../../../../../../../..";
69 char *dotdot
; /* -> dotdots[.], right to left */
70 DIR *dirp
; /* -> parent directory stream */
71 struct dirent
*dir
; /* -> directory entry */
73 stat2
; /* info from stat() */
74 struct stat
*d
= &stat1
; /* -> info about "." */
75 struct stat
*dd
= &stat2
; /* -> info about ".." */
76 register char *buffer
; /* local copy of buf, or malloc()ed */
77 char *bufend
; /* -> buffer[size] */
78 register char *endp
; /* -> end of reversed string */
79 register char *dname
; /* entry name ("" for root) */
80 int serrno
= errno
; /* save entry errno */
82 if ( buf
!= NULL
&& size
<= 0
87 errno
= EINVAL
; /* invalid argument */
93 if ( buf
== NULL
/* wants us to malloc() the string */
94 && (buffer
= (char *) malloc( (unsigned) size
)) == NULL
95 /* XXX -- actually should probably not pay attention to "size" arg */
97 errno
= ENOMEM
; /* cannot malloc() specified size */
102 if ( stat( ".", dd
) != 0 ) /* prime the pump */
103 goto error
; /* errno already set */
105 endp
= buffer
; /* initially, empty string */
106 bufend
= &buffer
[size
];
108 for ( dotdot
= &dotdots
[sizeof dotdots
]; dotdot
!= dotdots
; )
110 dotdot
-= 3; /* include one more "/.." section */
111 /* (first time is actually "..") */
113 /* swap stat() info buffers */
115 register struct stat
*temp
= d
;
117 d
= dd
; /* new current dir is old parent dir */
121 if ( (dirp
= opendir( dotdot
)) == NULL
) /* new parent */
122 goto error
; /* errno already set */
124 if ( fstat( dirp
->dd_fd
, dd
) != 0 )
126 serrno
= errno
; /* set by fstat() */
127 (void)closedir( dirp
);
128 errno
= serrno
; /* in case closedir() clobbered it */
132 if ( d
->st_dev
== dd
->st_dev
)
133 { /* not crossing a mount point */
134 if ( d
->st_ino
== dd
->st_ino
)
135 { /* root directory */
141 if ( (dir
= readdir( dirp
)) == NULL
)
143 (void)closedir( dirp
);
144 errno
= ENOENT
; /* missing entry */
147 while ( dir
->d_ino
!= d
->st_ino
);
149 else { /* crossing a mount point */
150 struct stat t
; /* info re. test entry */
151 char name
[sizeof dotdots
+ 1 + NAME_MAX
];
153 (void)strcpy( name
, dotdot
);
154 dname
= &name
[strlen( name
)];
158 if ( (dir
= readdir( dirp
)) == NULL
)
160 (void)closedir( dirp
);
161 errno
= ENOENT
; /* missing entry */
165 (void)strcpy( dname
, dir
->d_name
);
166 /* must fit if NAME_MAX is not a lie */
168 while ( stat( name
, &t
) != 0
169 || t
.st_ino
!= d
->st_ino
170 || t
.st_dev
!= d
->st_dev
176 /* append "/" and reversed dname string onto buffer */
178 if ( endp
!= buffer
/* avoid trailing / in final name */
179 || dname
[0] == '\0' /* but allow "/" when CWD is root */
184 register char *app
; /* traverses dname string */
186 for ( app
= dname
; *app
!= '\0'; ++app
)
189 if ( app
- dname
>= bufend
- endp
)
191 (void)closedir( dirp
);
192 errno
= ERANGE
; /* won't fit allotted space */
196 while ( app
!= dname
)
200 (void)closedir( dirp
);
202 if ( dname
[0] == '\0' ) /* reached root; wrap it up */
204 register char *startp
; /* -> buffer[.] */
206 *endp
= '\0'; /* plant null terminator */
208 /* straighten out reversed pathname string */
209 for ( startp
= buffer
; --endp
> startp
; ++startp
)
217 errno
= serrno
; /* restore entry errno */
218 /* XXX -- if buf==NULL, realloc here? */
223 errno
= ENOMEM
; /* actually, algorithm failure */
227 free( (pointer
)buffer
);