Prevent compiler warning from redefinition of mempcpy.
[kopensolaris-gnu/glibc.git] / sysdeps / posix / getcwd.c
1 /* Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 /* Wants:
20    AC_STDC_HEADERS
21    AC_DIR_HEADER
22    AC_UNISTD_H
23    AC_MEMORY_H
24    AC_CONST
25    AC_ALLOCA
26  */
27
28 /* AIX requires this to be the first thing in the file.  */
29 #if defined _AIX && !defined __GNUC__
30  #pragma alloca
31 #endif
32
33 #ifdef  HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40
41 #ifdef  STDC_HEADERS
42 # include <stddef.h>
43 #endif
44
45 #if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS
46 extern int errno;
47 #endif
48 #ifndef __set_errno
49 # define __set_errno(val) errno = (val)
50 #endif
51
52 #ifndef NULL
53 # define NULL   0
54 #endif
55
56 #if defined USGr3 && !defined DIRENT
57 # define DIRENT
58 #endif /* USGr3 */
59 #if defined Xenix && !defined SYSNDIR
60 # define SYSNDIR
61 #endif /* Xenix */
62
63 #if defined POSIX || defined DIRENT || defined __GNU_LIBRARY__
64 # include <dirent.h>
65 # ifndef __GNU_LIBRARY__
66 #  define D_NAMLEN(d) strlen((d)->d_name)
67 # else
68 #  define HAVE_D_NAMLEN
69 #  define D_NAMLEN(d) ((d)->d_namlen)
70 # endif
71 #else /* not POSIX or DIRENT */
72 # define dirent         direct
73 # define D_NAMLEN(d)    ((d)->d_namlen)
74 # define HAVE_D_NAMLEN
75 # if defined USG && !defined sgi
76 #  if defined SYSNDIR
77 #   include <sys/ndir.h>
78 #  else /* Not SYSNDIR */
79 #   include "ndir.h"
80 #  endif /* SYSNDIR */
81 # else /* not USG */
82 #  include <sys/dir.h>
83 # endif /* USG */
84 #endif /* POSIX or DIRENT or __GNU_LIBRARY__ */
85
86 #if defined HAVE_UNISTD_H || defined __GNU_LIBRARY__
87 # include <unistd.h>
88 #endif
89
90 #if defined STDC_HEADERS || defined __GNU_LIBRARY__ || defined POSIX
91 # include <stdlib.h>
92 # include <string.h>
93 # define ANSI_STRING
94 #else   /* No standard headers.  */
95
96 # ifdef USG
97
98 #  include <string.h>
99 #  ifdef NEED_MEMORY_H
100 #   include <memory.h>
101 #  endif
102 #  define       ANSI_STRING
103
104 # else  /* Not USG.  */
105
106 #  ifdef NeXT
107
108 #   include <string.h>
109
110 #  else /* Not NeXT.  */
111
112 #   include <strings.h>
113
114 #   ifndef bcmp
115 extern int bcmp ();
116 #   endif
117 #   ifndef bzero
118 extern void bzero ();
119 #   endif
120 #   ifndef bcopy
121 extern void bcopy ();
122 #   endif
123
124 #  endif /* NeXT. */
125
126 # endif /* USG.  */
127
128 extern char *malloc (), *realloc ();
129 extern void free ();
130
131 #endif /* Standard headers.  */
132
133 #ifndef ANSI_STRING
134 # define memcpy(d, s, n)        bcopy((s), (d), (n))
135 # define memmove memcpy
136 #endif  /* Not ANSI_STRING.  */
137
138 #ifdef _LIBC
139 # ifndef mempcpy
140 #  define mempcpy __mempcpy
141 # endif
142 # define HAVE_MEMPCPY   1
143 #endif
144
145 #if !defined __alloca && !defined __GNU_LIBRARY__
146
147 # ifdef __GNUC__
148 #  undef alloca
149 #  define alloca(n)     __builtin_alloca (n)
150 # else  /* Not GCC.  */
151 #  if   defined sparc || defined HAVE_ALLOCA_H
152 #   include <alloca.h>
153 #  else /* Not sparc or HAVE_ALLOCA_H.  */
154 #   ifndef _AIX
155 extern char *alloca ();
156 #   endif /* Not _AIX.  */
157 #  endif /* sparc or HAVE_ALLOCA_H.  */
158 # endif /* GCC.  */
159
160 # define __alloca       alloca
161
162 #endif
163
164 #if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__
165 # include <limits.h>
166 #else
167 # include <sys/param.h>
168 #endif
169
170 #ifndef PATH_MAX
171 # ifdef MAXPATHLEN
172 #  define PATH_MAX MAXPATHLEN
173 # else
174 #  define PATH_MAX 1024
175 # endif
176 #endif
177
178 #if !defined STDC_HEADERS && !defined __GNU_LIBRARY__
179 # undef size_t
180 # define size_t unsigned int
181 #endif
182
183 #if !__STDC__ && !defined const
184 # define const
185 #endif
186
187 #ifndef __GNU_LIBRARY__
188 # define __lstat        stat
189 #endif
190 \f
191 #ifndef _LIBC
192 # define __getcwd getcwd
193 #endif
194
195 #ifndef GETCWD_RETURN_TYPE
196 # define GETCWD_RETURN_TYPE char *
197 #endif
198
199 /* Get the pathname of the current working directory, and put it in SIZE
200    bytes of BUF.  Returns NULL if the directory couldn't be determined or
201    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
202    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
203    unless SIZE <= 0, in which case it is as big as necessary.  */
204
205 GETCWD_RETURN_TYPE
206 __getcwd (buf, size)
207      char *buf;
208      size_t size;
209 {
210   static const char dots[]
211     = "../../../../../../../../../../../../../../../../../../../../../../../\
212 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
213 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
214   const char *dotp, *dotlist;
215   size_t dotsize;
216   dev_t rootdev, thisdev;
217   ino_t rootino, thisino;
218   char *path;
219   register char *pathp;
220   struct stat st;
221   int prev_errno = errno;
222
223   if (size == 0)
224     {
225       if (buf != NULL)
226         {
227           __set_errno (EINVAL);
228           return NULL;
229         }
230
231       size = PATH_MAX + 1;
232     }
233
234   if (buf != NULL)
235     path = buf;
236   else
237     {
238       path = malloc (size);
239       if (path == NULL)
240         return NULL;
241     }
242
243   pathp = path + size;
244   *--pathp = '\0';
245
246   if (__lstat (".", &st) < 0)
247     return NULL;
248   thisdev = st.st_dev;
249   thisino = st.st_ino;
250
251   if (__lstat ("/", &st) < 0)
252     return NULL;
253   rootdev = st.st_dev;
254   rootino = st.st_ino;
255
256   dotsize = sizeof (dots) - 1;
257   dotp = &dots[sizeof (dots)];
258   dotlist = dots;
259   while (!(thisdev == rootdev && thisino == rootino))
260     {
261       register DIR *dirstream;
262       struct dirent *d;
263       dev_t dotdev;
264       ino_t dotino;
265       char mount_point;
266
267       /* Look at the parent directory.  */
268       if (dotp == dotlist)
269         {
270           /* My, what a deep directory tree you have, Grandma.  */
271           char *new;
272           if (dotlist == dots)
273             {
274               new = malloc (dotsize * 2 + 1);
275               if (new == NULL)
276                 return NULL;
277 #ifdef HAVE_MEMPCPY
278               dotp = mempcpy (new, dots, dotsize);
279 #else
280               memcpy (new, dots, dotsize);
281               dotp = &new[dotsize];
282 #endif
283             }
284           else
285             {
286               new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
287               if (new == NULL)
288                 goto lose;
289               dotp = &new[dotsize];
290             }
291 #ifdef HAVE_MEMPCPY
292           *((char *) mempcpy ((char *) dotp, new, dotsize)) = '\0';
293           dotsize *= 2;
294 #else
295           memcpy ((char *) dotp, new, dotsize);
296           dotsize *= 2;
297           new[dotsize] = '\0';
298 #endif
299           dotlist = new;
300         }
301
302       dotp -= 3;
303
304       /* Figure out if this directory is a mount point.  */
305       if (__lstat (dotp, &st) < 0)
306         goto lose;
307       dotdev = st.st_dev;
308       dotino = st.st_ino;
309       mount_point = dotdev != thisdev;
310
311       /* Search for the last directory.  */
312       dirstream = __opendir (dotp);
313       if (dirstream == NULL)
314         goto lose;
315       /* Clear errno to distinguish EOF from error if readdir returns
316          NULL.  */
317       __set_errno (0);
318       while ((d = __readdir (dirstream)) != NULL)
319         {
320           if (d->d_name[0] == '.' &&
321               (d->d_name[1] == '\0' ||
322                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
323             continue;
324           if (mount_point || (ino_t) d->d_ino == thisino)
325             {
326               char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
327 #ifdef HAVE_MEMPCPY
328               char *tmp = mempcpy (name, dotp, dotlist + dotsize - dotp);
329               *tmp++ = '/';
330               strcpy (tmp, d->d_name);
331 #else
332               memcpy (name, dotp, dotlist + dotsize - dotp);
333               name[dotlist + dotsize - dotp] = '/';
334               strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
335 #endif
336               if (__lstat (name, &st) < 0)
337                 {
338                   int save = errno;
339                   (void) __closedir (dirstream);
340                   __set_errno (save);
341                   goto lose;
342                 }
343               if (st.st_dev == thisdev && st.st_ino == thisino)
344                 break;
345             }
346         }
347       if (d == NULL)
348         {
349           int save = errno;
350           (void) __closedir (dirstream);
351           if (save == 0)
352             /* EOF on dirstream, which means that the current directory
353                has been removed.  */
354             save = ENOENT;
355           __set_errno (save);
356           goto lose;
357         }
358       else
359         {
360           size_t namlen = _D_EXACT_NAMLEN (d);
361
362           if ((size_t) (pathp - path) <= namlen)
363             {
364               if (buf != NULL)
365                 {
366                   (void) __closedir (dirstream);
367                   __set_errno (ERANGE);
368                   goto lose;
369                 }
370               else
371                 {
372                   size *= 2;
373                   buf = realloc (path, size);
374                   if (buf == NULL)
375                     {
376                       (void) __closedir (dirstream);
377                       free (path);
378                       __set_errno (ENOMEM);/* closedir might have changed it.*/
379                       goto lose;
380                     }
381                   pathp = &buf[pathp - path + size / 2];
382                   path = buf;
383                   /* Move current contents up to the end of the buffer.
384                      This is guaranteed to be non-overlapping.  */
385                   memcpy (pathp, pathp - size / 2, path + size - pathp);
386                 }
387             }
388           pathp -= namlen;
389           (void) memcpy (pathp, d->d_name, namlen);
390           *--pathp = '/';
391           (void) __closedir (dirstream);
392         }
393
394       thisdev = dotdev;
395       thisino = dotino;
396     }
397
398   if (pathp == &path[size - 1])
399     *--pathp = '/';
400
401   if (dotlist != dots)
402     free ((__ptr_t) dotlist);
403
404   memmove (path, pathp, path + size - pathp);
405
406   /* Restore errno on successful return.  */
407   __set_errno (prev_errno);
408
409   return path;
410
411  lose:
412   if (dotlist != dots)
413     free ((__ptr_t) dotlist);
414   return NULL;
415 }
416
417 #if defined _LIBC && !defined __getcwd
418 weak_alias (__getcwd, getcwd)
419 #endif