summaryrefslogtreecommitdiff
path: root/m4/getcwd-path-max.m4
blob: 9069969b32730c78b9428429ddee542d4ed59231 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#serial 2
# Check whether getcwd has the bug that it succeeds for a working directory
# longer than PATH_MAX, yet returns a truncated directory name.
# If so, arrange to compile the wrapper function.

# This is necessary for at least GNU libc on linux-2.4.19 and 2.4.20.
# I've heard that this is due to a Linux kernel bug, and that it has
# been fixed between 2.4.21-pre3 and 2.4.21-pre4.  */

# From Jim Meyering

AC_DEFUN([GL_FUNC_GETCWD_PATH_MAX],
[
  AC_CACHE_CHECK([whether getcwd properly handles paths longer than PATH_MAX],
                 gl_cv_func_getcwd_vs_path_max,
  [
  AC_CHECK_DECLS([getcwd])
  # Arrange for deletion of the temporary directory this test creates.
  ac_clean_files="$ac_clean_files confdir3"
  AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>

/* Don't get link errors because mkdir is redefined to rpl_mkdir.  */
#undef mkdir

#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

/* The extra casts work around common compiler bugs.  */
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
   It is necessary at least when t == time_t.  */
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
			      ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))

#ifndef INT_MAX
# define INT_MAX TYPE_MAXIMUM (int)
#endif

#ifndef PATH_MAX
/* There might be a better way to handle this case, but note:
   - the value shouldn't be anywhere near INT_MAX, and
   - the value shouldn't be so big that the local declaration, below,
   blows the stack.  */
# define PATH_MAX 40000
#endif

/* The length of this name must be 8.  */
#define DIR_NAME "confdir3"

int
main ()
{
  /* The '9' comes from strlen (DIR_NAME) + 1.  */
#if INT_MAX - 9 <= PATH_MAX
  /* FIXME: Assuming there's a system for which this is true -- Hurd?,
     this should be done in a compile test.  */
  exit (0);
#else
  char buf[PATH_MAX + 20];
  char *cwd = getcwd (buf, PATH_MAX);
  size_t cwd_len;
  int fail = 0;
  size_t n_chdirs = 0;

  if (cwd == NULL)
    exit (1);

  cwd_len = strlen (cwd);

  while (1)
    {
      char *c;
      size_t len;

      cwd_len += 1 + strlen (DIR_NAME);
      /* If mkdir or chdir fails, be pessimistic and consider that
	 as a failure, too.  */
      if (mkdir (DIR_NAME, 0700) < 0 || chdir (DIR_NAME) < 0)
	{
	  fail = 1;
	  break;
	}
      if ((c = getcwd (buf, PATH_MAX)) == NULL)
        {
	  /* This allows any failure to indicate there is no bug.
	     FIXME: check errno?  */
	  break;
	}
      if ((len = strlen (c)) != cwd_len)
	{
	  fail = 1;
	  break;
	}
      ++n_chdirs;
      if (PATH_MAX < len)
	break;
    }

  /* Leaving behind such a deep directory is not polite.
     So clean up here, right away, even though the driving
     shell script would also clean up.  */
  {
    size_t i;

    /* Unlink first, in case the chdir failed.  */
    unlink (DIR_NAME);
    for (i = 0; i <= n_chdirs; i++)
      {
	if (chdir ("..") < 0)
	  break;
	rmdir (DIR_NAME);
      }
  }

  exit (fail);
#endif
}
  ]])],
       [gl_cv_func_getcwd_vs_path_max=yes],
       [gl_cv_func_getcwd_vs_path_max=no],
       [gl_cv_func_getcwd_vs_path_max=no])])

  if test $gl_cv_func_getcwd_vs_path_max = yes; then
    AC_LIBOBJ(getcwd)
    AC_DEFINE(getcwd, rpl_getcwd,
      [Define to rpl_getcwd if the wrapper function should be used.])
  fi
])