ManaPlus
mkdir.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2010 The Mana Developers
4  * Copyright (C) 2011-2019 The ManaPlus Developers
5  * Copyright (C) 2019-2021 Andrei Karas
6  *
7  * This file is part of The ManaPlus Client.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "fs/mkdir.h"
24 
25 #include "utils/cast.h"
26 
27 #if defined WIN32
28 #include <limits.h>
29 #include <windows.h>
30 #endif // defined WIN32
31 
32 #include <sys/stat.h>
33 
34 #include "debug.h"
35 
36 #if defined WIN32
37 int mkdir_r(const char *const pathname)
38 {
39  if (!pathname)
40  return -1;
41 
42  char tmp[PATH_MAX];
43  char tmp2[PATH_MAX];
44  char *p;
45 
46  if (strlen(pathname) >= PATH_MAX - 2)
47  return -1;
48 
49  strncpy(tmp, pathname, sizeof(tmp) - 1);
50  tmp[PATH_MAX - 1] = '\0';
51 
52  const int len = CAST_S32(strlen(tmp));
53 
54  if (len < 1 || len >= INT_MAX)
55  return -1;
56 
57  // terminate the pathname with '/'
58  if (tmp[len - 1] != '/')
59  {
60  tmp[len] = '/';
61  tmp[len + 1] = '\0';
62  }
63 
64  for (p = tmp; *p; p++)
65  {
66  if (*p == '/' || *p == '\\')
67  {
68  *p = '\0';
69  // ignore a slash at the beginning of a path
70  if (tmp[0] == 0)
71  {
72  *p = '/';
73  continue;
74  }
75 
76  strcpy(tmp2, tmp);
77  char *p2 = tmp2 + strlen(tmp2) - 1;
78  if (*p2 == '/' || *p2 == '\\')
79  *p2 = 0;
80  // check if the name already exists, but not as directory
81  struct stat statbuf;
82  if (!stat(tmp2, &statbuf))
83  {
84  if (S_ISDIR(statbuf.st_mode))
85  {
86  *p = '/';
87  continue;
88  }
89  else
90  return -1;
91  }
92 
93  if (!CreateDirectory(tmp2, nullptr))
94  {
95  // hack, hack. just assume that x: might be a drive
96  // letter, and try again
97  if (!(strlen(tmp2) == 2 && !strcmp(tmp2 + 1, ":")))
98  return -1;
99  }
100 
101  *p = '/';
102  }
103  }
104  return 0;
105 }
106 #else // WIN32
107 
109 int mkdir_r(const char *const pathname)
110 {
111  if (pathname == nullptr)
112  return -1;
113 
114  const size_t len = CAST_SIZE(strlen(pathname));
115  char *tmp = new char[len + 2];
116  char *p = nullptr;
117 
118  strcpy(tmp, pathname);
119 
120  // terminate the pathname with '/'
121  if (tmp[len - 1] != '/')
122  {
123  tmp[len] = '/';
124  tmp[len + 1] = '\0';
125  }
126 
127  for (p = tmp; *p != 0; p++)
128  {
129  if (*p == '/')
130  {
131  *p = '\0';
132  // ignore a slash at the beginning of a path
133  if (tmp[0] == 0)
134  {
135  *p = '/';
136  continue;
137  }
138 
139  // check if the name already exists, but not as directory
140  struct stat statbuf;
141  if (stat(tmp, &statbuf) == 0)
142  {
143  if (S_ISDIR(statbuf.st_mode))
144  {
145  *p = '/';
146  continue;
147  }
148  else
149  {
150  delete []tmp;
151  return -1;
152  }
153  }
154 
155  if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
156  {
157  delete []tmp;
158  return -1;
159  }
160 
161  *p = '/';
162  }
163  }
164  delete []tmp;
165  return 0;
166 }
167 #endif // WIN32
#define CAST_S32
Definition: cast.h:30
#define CAST_SIZE
Definition: cast.h:34
int mkdir_r(const char *const pathname)
Create a directory, making leading components first if necessary.
Definition: mkdir.cpp:109
bool mkdir(const std::string &dirname)
Definition: fs.cpp:775