merge the formfield patch from ooo-build
[ooovba.git] / dmake / unix / sysvr3 / pwd / getcwd.c
blob5c5b84711a6c89ae5a5b702b2edc44a90bd68ad7
1 /*
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:
9 IEEE Std 1003.1-1988
10 SVID Issue 3
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>
33 #include <sys/stat.h>
35 #ifdef M_XENIX
36 # include <sys/ndir.h>
37 # define dirent direct
38 #else
39 # include <dirent.h>
40 #endif
42 #include <errno.h>
43 #include <string.h>
45 typedef char *pointer; /* (void *) if you have it */
47 extern void free();
48 extern pointer malloc();
49 extern int fstat(), stat();
51 extern int errno; /* normally done by <errno.h> */
53 #ifndef NULL
54 #define NULL 0 /* amorphous null pointer constant */
55 #endif
57 #ifndef NAME_MAX
58 #define NAME_MAX 255 /* maximum directory entry size */
59 #endif
62 char *
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 */
72 struct stat stat1,
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
83 #ifndef XPG2
84 || buf == NULL
85 #endif
86 ) {
87 errno = EINVAL; /* invalid argument */
88 return NULL;
91 buffer = buf;
92 #ifdef XPG2
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 */
96 ) {
97 errno = ENOMEM; /* cannot malloc() specified size */
98 return NULL;
100 #endif
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 */
118 dd = temp;
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 */
129 goto error;
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 */
136 dname = "";
137 goto append;
141 if ( (dir = readdir( dirp )) == NULL )
143 (void)closedir( dirp );
144 errno = ENOENT; /* missing entry */
145 goto error;
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 )];
155 *dname++ = '/';
157 do {
158 if ( (dir = readdir( dirp )) == NULL )
160 (void)closedir( dirp );
161 errno = ENOENT; /* missing entry */
162 goto error;
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
174 dname = dir->d_name;
176 /* append "/" and reversed dname string onto buffer */
177 append:
178 if ( endp != buffer /* avoid trailing / in final name */
179 || dname[0] == '\0' /* but allow "/" when CWD is root */
181 *endp++ = '/';
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 */
193 goto error;
196 while ( app != dname )
197 *endp++ = *--app;
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 )
211 char temp = *endp;
213 *endp = *startp;
214 *startp = temp;
217 errno = serrno; /* restore entry errno */
218 /* XXX -- if buf==NULL, realloc here? */
219 return buffer;
223 errno = ENOMEM; /* actually, algorithm failure */
225 error:
226 if ( buf == NULL )
227 free( (pointer)buffer );
229 return NULL;