summaryrefslogtreecommitdiff
path: root/lib/mkdir.c
blob: 34cab6c9a6b7aebb7e1b6fcaea9b57460f60a42b (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
/* On some systems, mkdir ("foo/", 0700) fails because of the trailing
   slash.  On those systems, this wrapper removes the trailing slash.

   Copyright (C) 2001, 2003, 2006, 2008, 2009 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* written by Jim Meyering */

#include <config.h>

/* Specification.  */
#include <sys/stat.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dirname.h"
#include "xalloc.h"

/* Disable the definition of mkdir to rpl_mkdir (from the <sys/stat.h>
   substitute) in this file.  Otherwise, we'd get an endless recursion.  */
#undef mkdir

/* mingw's _mkdir() function has 1 argument, but we pass 2 arguments.
   Additionally, it declares _mkdir (and depending on compile flags, an
   alias mkdir), only in the nonstandard io.h.  */
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
# define mkdir(name,mode) _mkdir (name)
# define maybe_unused _GL_UNUSED
#else
# define maybe_unused /* empty */
#endif

/* This function is required at least for NetBSD 1.5.2.  */

int
rpl_mkdir (char const *dir, mode_t mode maybe_unused)
{
  int ret_val;
  char *tmp_dir;
  size_t len = strlen (dir);

  if (len && dir[len - 1] == '/')
    {
      tmp_dir = strdup (dir);
      if (!tmp_dir)
        {
          /* Rather than rely on strdup-posix, we set errno ourselves.  */
          errno = ENOMEM;
          return -1;
        }
      strip_trailing_slashes (tmp_dir);
    }
  else
    {
      tmp_dir = (char *) dir;
    }
#if FUNC_MKDIR_DOT_BUG
  /* Additionally, cygwin 1.5 mistakenly creates a directory "d/./".  */
  {
    char *last = last_component (tmp_dir);
    if (*last == '.' && (last[1] == '\0'
                         || (last[1] == '.' && last[2] == '\0')))
      {
        struct stat st;
        if (stat (tmp_dir, &st) == 0)
          errno = EEXIST;
        return -1;
      }
  }
#endif /* FUNC_MKDIR_DOT_BUG */

  ret_val = mkdir (tmp_dir, mode);

  if (tmp_dir != dir)
    free (tmp_dir);

  return ret_val;
}