Optimize a bit by using mempcpy.
[kopensolaris-gnu/glibc.git] / sysdeps / posix / getcwd.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 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 #if !defined __alloca && !defined __GNU_LIBRARY__
139
140 # ifdef __GNUC__
141 #  undef alloca
142 #  define alloca(n)     __builtin_alloca (n)
143 # else  /* Not GCC.  */
144 #  if   defined sparc || defined HAVE_ALLOCA_H
145 #   include <alloca.h>
146 #  else /* Not sparc or HAVE_ALLOCA_H.  */
147 #   ifndef _AIX
148 extern char *alloca ();
149 #   endif /* Not _AIX.  */
150 #  endif /* sparc or HAVE_ALLOCA_H.  */
151 # endif /* GCC.  */
152
153 # define __alloca       alloca
154
155 #endif
156
157 #if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__
158 # include <limits.h>
159 #else
160 # include <sys/param.h>
161 #endif
162
163 #ifndef PATH_MAX
164 # ifdef MAXPATHLEN
165 #  define PATH_MAX MAXPATHLEN
166 # else
167 #  define PATH_MAX 1024
168 # endif
169 #endif
170
171 #if !defined STDC_HEADERS && !defined __GNU_LIBRARY__
172 # undef size_t
173 # define size_t unsigned int
174 #endif
175
176 #if !__STDC__ && !defined const
177 # define const
178 #endif
179
180 #ifndef __GNU_LIBRARY__
181 # define __lstat        stat
182 #endif
183 \f
184 #ifndef _LIBC
185 # define __getcwd getcwd
186 #endif
187
188 /* Get the pathname of the current working directory, and put it in SIZE
189    bytes of BUF.  Returns NULL if the directory couldn't be determined or
190    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
191    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
192    unless SIZE <= 0, in which case it is as big as necessary.  */
193
194 char *
195 __getcwd (buf, size)
196      char *buf;
197      size_t size;
198 {
199   static const char dots[]
200     = "../../../../../../../../../../../../../../../../../../../../../../../\
201 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
202 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
203   const char *dotp, *dotlist;
204   size_t dotsize;
205   dev_t rootdev, thisdev;
206   ino_t rootino, thisino;
207   char *path;
208   register char *pathp;
209   struct stat st;
210
211   if (size == 0)
212     {
213       if (buf != NULL)
214         {
215           __set_errno (EINVAL);
216           return NULL;
217         }
218
219       size = PATH_MAX + 1;
220     }
221
222   if (buf != NULL)
223     path = buf;
224   else
225     {
226       path = malloc (size);
227       if (path == NULL)
228         return NULL;
229     }
230
231   pathp = path + size;
232   *--pathp = '\0';
233
234   if (__lstat (".", &st) < 0)
235     return NULL;
236   thisdev = st.st_dev;
237   thisino = st.st_ino;
238
239   if (__lstat ("/", &st) < 0)
240     return NULL;
241   rootdev = st.st_dev;
242   rootino = st.st_ino;
243
244   dotsize = sizeof (dots) - 1;
245   dotp = &dots[sizeof (dots)];
246   dotlist = dots;
247   while (!(thisdev == rootdev && thisino == rootino))
248     {
249       register DIR *dirstream;
250       struct dirent *d;
251       dev_t dotdev;
252       ino_t dotino;
253       char mount_point;
254
255       /* Look at the parent directory.  */
256       if (dotp == dotlist)
257         {
258           /* My, what a deep directory tree you have, Grandma.  */
259           char *new;
260           if (dotlist == dots)
261             {
262               new = malloc (dotsize * 2 + 1);
263               if (new == NULL)
264                 return NULL;
265 #ifdef HAVE_MEMPCPY
266               dotp = mempcpy (new, dots, dotsize);
267 #else
268               memcpy (new, dots, dotsize);
269               dotp = &new[dotsize];
270 #endif
271             }
272           else
273             {
274               new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
275               if (new == NULL)
276                 goto lose;
277               dotp = &new[dotsize];
278             }
279 #ifdef HAVE_MEMPCPY
280           *((char *) mempcpy (dotp, new, dotsize)) = '\0';
281           dotsize *= 2;
282 #else
283           memcpy (dotp, new, dotsize);
284           dotsize *= 2;
285           new[dotsize] = '\0';
286 #endif
287           dotlist = new;
288         }
289
290       dotp -= 3;
291
292       /* Figure out if this directory is a mount point.  */
293       if (__lstat (dotp, &st) < 0)
294         goto lose;
295       dotdev = st.st_dev;
296       dotino = st.st_ino;
297       mount_point = dotdev != thisdev;
298
299       /* Search for the last directory.  */
300       dirstream = __opendir (dotp);
301       if (dirstream == NULL)
302         goto lose;
303       while ((d = __readdir (dirstream)) != NULL)
304         {
305           if (d->d_name[0] == '.' &&
306               (d->d_name[1] == '\0' ||
307                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
308             continue;
309           if (mount_point || (ino_t) d->d_ino == thisino)
310             {
311               char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
312 #ifdef HAVE_MEMPCPY
313               char *tmp = mempcpy (name, dotp, dotlist + dotsize - dotp);
314               *tmp++ = '/';
315               strcpy (tmp, d->d_name);
316 #else
317               memcpy (name, dotp, dotlist + dotsize - dotp);
318               name[dotlist + dotsize - dotp] = '/';
319               strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
320 #endif
321               if (__lstat (name, &st) < 0)
322                 {
323                   int save = errno;
324                   (void) __closedir (dirstream);
325                   __set_errno (save);
326                   goto lose;
327                 }
328               if (st.st_dev == thisdev && st.st_ino == thisino)
329                 break;
330             }
331         }
332       if (d == NULL)
333         {
334           int save = errno;
335           (void) __closedir (dirstream);
336           __set_errno (save);
337           goto lose;
338         }
339       else
340         {
341           size_t namlen = _D_EXACT_NAMLEN (d);
342
343           if ((size_t) (pathp - path) <= namlen)
344             {
345               if (buf != NULL)
346                 {
347                   (void) __closedir (dirstream);
348                   __set_errno (ERANGE);
349                   goto lose;
350                 }
351               else
352                 {
353                   size *= 2;
354                   buf = realloc (path, size);
355                   if (buf == NULL)
356                     {
357                       (void) __closedir (dirstream);
358                       free (path);
359                       __set_errno (ENOMEM);/* closedir might have changed it.*/
360                       goto lose;
361                     }
362                   pathp = &buf[pathp - path + size / 2];
363                   path = buf;
364                   /* Move current contents up to the end of the buffer.
365                      This is guaranteed to be non-overlapping.  */
366                   memcpy (pathp, pathp - size / 2, path + size - pathp);
367                 }
368             }
369           pathp -= namlen;
370           (void) memcpy (pathp, d->d_name, namlen);
371           *--pathp = '/';
372           (void) __closedir (dirstream);
373         }
374
375       thisdev = dotdev;
376       thisino = dotino;
377     }
378
379   if (pathp == &path[size - 1])
380     *--pathp = '/';
381
382   if (dotlist != dots)
383     free ((__ptr_t) dotlist);
384
385   memmove (path, pathp, path + size - pathp);
386   return path;
387
388  lose:
389   if (dotlist != dots)
390     free ((__ptr_t) dotlist);
391   return NULL;
392 }
393
394 #ifdef _LIBC
395 weak_alias (__getcwd, getcwd)
396 #endif