unistd et al.: Don't assume <unistd.h> exists.
[gnulib.git] / m4 / getcwd-path-max.m4
1 # serial 17
2 # Check for several getcwd bugs with long file names.
3 # If so, arrange to compile the wrapper function.
4
5 # This is necessary for at least GNU libc on linux-2.4.19 and 2.4.20.
6 # I've heard that this is due to a Linux kernel bug, and that it has
7 # been fixed between 2.4.21-pre3 and 2.4.21-pre4.
8
9 # Copyright (C) 2003-2007, 2009-2011 Free Software Foundation, Inc.
10 # This file is free software; the Free Software Foundation
11 # gives unlimited permission to copy and/or distribute it,
12 # with or without modifications, as long as this notice is preserved.
13
14 # From Jim Meyering
15
16 AC_DEFUN([gl_FUNC_GETCWD_PATH_MAX],
17 [
18   AC_CHECK_DECLS_ONCE([getcwd])
19   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
20   AC_CHECK_HEADERS_ONCE([unistd.h])
21   AC_CACHE_CHECK([whether getcwd handles long file names properly],
22     gl_cv_func_getcwd_path_max,
23     [# Arrange for deletion of the temporary directory this test creates.
24      ac_clean_files="$ac_clean_files confdir3"
25      dnl Please keep this in sync with tests/test-getcwd.c.
26      AC_RUN_IFELSE(
27        [AC_LANG_SOURCE(
28           [[
29 #include <errno.h>
30 #include <stdlib.h>
31 #if HAVE_UNISTD_H
32 # include <unistd.h>
33 #else
34 # include <direct.h>
35 #endif
36 #include <string.h>
37 #include <limits.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <fcntl.h>
41
42 #ifndef AT_FDCWD
43 # define AT_FDCWD 0
44 #endif
45 #ifdef ENAMETOOLONG
46 # define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
47 #else
48 # define is_ENAMETOOLONG(x) 0
49 #endif
50
51 /* Don't get link errors because mkdir is redefined to rpl_mkdir.  */
52 #undef mkdir
53
54 #ifndef S_IRWXU
55 # define S_IRWXU 0700
56 #endif
57
58 /* The length of this name must be 8.  */
59 #define DIR_NAME "confdir3"
60 #define DIR_NAME_LEN 8
61 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
62
63 /* The length of "../".  */
64 #define DOTDOTSLASH_LEN 3
65
66 /* Leftover bytes in the buffer, to work around library or OS bugs.  */
67 #define BUF_SLOP 20
68
69 int
70 main ()
71 {
72 #ifndef PATH_MAX
73   /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
74      at least not on a local file system.  And if we were to start worrying
75      about remote file systems, we'd have to enable the wrapper function
76      all of the time, just to be safe.  That's not worth the cost.  */
77   exit (0);
78 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
79         - DIR_NAME_SIZE - BUF_SLOP) \
80        <= PATH_MAX)
81   /* FIXME: Assuming there's a system for which this is true,
82      this should be done in a compile test.  */
83   exit (0);
84 #else
85   char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
86            + DIR_NAME_SIZE + BUF_SLOP];
87   char *cwd = getcwd (buf, PATH_MAX);
88   size_t initial_cwd_len;
89   size_t cwd_len;
90   int fail = 0;
91   size_t n_chdirs = 0;
92
93   if (cwd == NULL)
94     exit (10);
95
96   cwd_len = initial_cwd_len = strlen (cwd);
97
98   while (1)
99     {
100       size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
101       char *c = NULL;
102
103       cwd_len += DIR_NAME_SIZE;
104       /* If mkdir or chdir fails, it could be that this system cannot create
105          any file with an absolute name longer than PATH_MAX, such as cygwin.
106          If so, leave fail as 0, because the current working directory can't
107          be too long for getcwd if it can't even be created.  For other
108          errors, be pessimistic and consider that as a failure, too.  */
109       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
110         {
111           if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
112             fail = 20;
113           break;
114         }
115
116       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
117         {
118           c = getcwd (buf, PATH_MAX);
119           if (!c && errno == ENOENT)
120             {
121               fail = 11;
122               break;
123             }
124           if (c || ! (errno == ERANGE || is_ENAMETOOLONG (errno)))
125             {
126               fail = 21;
127               break;
128             }
129         }
130
131       if (dotdot_max <= cwd_len - initial_cwd_len)
132         {
133           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
134             break;
135           c = getcwd (buf, cwd_len + 1);
136           if (!c)
137             {
138               if (! (errno == ERANGE || errno == ENOENT
139                      || is_ENAMETOOLONG (errno)))
140                 {
141                   fail = 22;
142                   break;
143                 }
144               if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
145                 {
146                   fail = 12;
147                   break;
148                 }
149             }
150         }
151
152       if (c && strlen (c) != cwd_len)
153         {
154           fail = 23;
155           break;
156         }
157       ++n_chdirs;
158     }
159
160   /* Leaving behind such a deep directory is not polite.
161      So clean up here, right away, even though the driving
162      shell script would also clean up.  */
163   {
164     size_t i;
165
166     /* Try rmdir first, in case the chdir failed.  */
167     rmdir (DIR_NAME);
168     for (i = 0; i <= n_chdirs; i++)
169       {
170         if (chdir ("..") < 0)
171           break;
172         if (rmdir (DIR_NAME) != 0)
173           break;
174       }
175   }
176
177   exit (fail);
178 #endif
179 }
180           ]])],
181     [gl_cv_func_getcwd_path_max=yes],
182     [case $? in
183      10|11|12) gl_cv_func_getcwd_path_max='no, but it is partly working';;
184      *) gl_cv_func_getcwd_path_max=no;;
185      esac],
186     [gl_cv_func_getcwd_path_max=no])
187   ])
188   case $gl_cv_func_getcwd_path_max in
189   no,*)
190     AC_DEFINE([HAVE_PARTLY_WORKING_GETCWD], [1],
191       [Define to 1 if getcwd works, except it sometimes fails when it shouldn't,
192        setting errno to ERANGE, ENAMETOOLONG, or ENOENT.]);;
193   esac
194 ])