GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/fs/mkdir.cpp Lines: 22 25 88.0 %
Date: 2021-03-17 Branches: 13 22 59.1 %

Line Branch Exec Source
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
108
/// Create a directory, making leading components first if necessary
109
985
int mkdir_r(const char *const pathname)
110
{
111
985
    if (pathname == nullptr)
112
        return -1;
113
114
985
    const size_t len = CAST_SIZE(strlen(pathname));
115
985
    char *tmp = new char[len + 2];
116
985
    char *p = nullptr;
117
118
985
    strcpy(tmp, pathname);
119
120
    // terminate the pathname with '/'
121
985
    if (tmp[len - 1] != '/')
122
    {
123
985
        tmp[len] = '/';
124
985
        tmp[len + 1] = '\0';
125
    }
126
127
53551
    for (p = tmp; *p != 0; p++)
128
    {
129
26283
        if (*p == '/')
130
        {
131
5300
            *p = '\0';
132
            // ignore a slash at the beginning of a path
133
5300
            if (tmp[0] == 0)
134
            {
135
983
                *p = '/';
136
6275
                continue;
137
            }
138
139
            // check if the name already exists, but not as directory
140
            struct stat statbuf;
141
4317
            if (stat(tmp, &statbuf) == 0)
142
            {
143
4309
                if (S_ISDIR(statbuf.st_mode))
144
                {
145
4309
                    *p = '/';
146
4309
                    continue;
147
                }
148
                else
149
                {
150
                    delete []tmp;
151
                    return -1;
152
                }
153
            }
154
155
8
            if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
156
            {
157
                delete []tmp;
158
                return -1;
159
            }
160
161
8
            *p = '/';
162
        }
163
    }
164
985
    delete []tmp;
165
    return 0;
166
}
167
#endif  // WIN32