]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - cddl/compat/opensolaris/misc/mkdirp.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / cddl / compat / opensolaris / misc / mkdirp.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * $FreeBSD$
23  */
24
25 /*      Copyright (c) 1988 AT&T */
26 /*        All Rights Reserved   */
27
28 /*
29  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
30  * Use is subject to license terms.
31  */
32
33 #pragma ident   "@(#)mkdirp.c   1.15    06/01/04 SMI"
34
35 /*
36  * Creates directory and it's parents if the parents do not
37  * exist yet.
38  *
39  * Returns -1 if fails for reasons other than non-existing
40  * parents.
41  * Does NOT simplify pathnames with . or .. in them.
42  */
43
44 #include <sys/types.h>
45 #include <libgen.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <sys/stat.h>
51
52 static char *simplify(const char *str);
53
54 int
55 mkdirp(const char *d, mode_t mode)
56 {
57         char  *endptr, *ptr, *slash, *str;
58
59         str = simplify(d);
60
61         /* If space couldn't be allocated for the simplified names, return. */
62
63         if (str == NULL)
64                 return (-1);
65
66                 /* Try to make the directory */
67
68         if (mkdir(str, mode) == 0) {
69                 free(str);
70                 return (0);
71         }
72         if (errno != ENOENT) {
73                 free(str);
74                 return (-1);
75         }
76         endptr = strrchr(str, '\0');
77         slash = strrchr(str, '/');
78
79                 /* Search upward for the non-existing parent */
80
81         while (slash != NULL) {
82
83                 ptr = slash;
84                 *ptr = '\0';
85
86                         /* If reached an existing parent, break */
87
88                 if (access(str, F_OK) == 0)
89                         break;
90
91                         /* If non-existing parent */
92
93                 else {
94                         slash = strrchr(str, '/');
95
96                                 /* If under / or current directory, make it. */
97
98                         if (slash == NULL || slash == str) {
99                                 if (mkdir(str, mode) != 0 && errno != EEXIST) {
100                                         free(str);
101                                         return (-1);
102                                 }
103                                 break;
104                         }
105                 }
106         }
107
108         /* Create directories starting from upmost non-existing parent */
109
110         while ((ptr = strchr(str, '\0')) != endptr) {
111                 *ptr = '/';
112                 if (mkdir(str, mode) != 0 && errno != EEXIST) {
113                         /*
114                          *  If the mkdir fails because str already
115                          *  exists (EEXIST), then str has the form
116                          *  "existing-dir/..", and this is really
117                          *  ok. (Remember, this loop is creating the
118                          *  portion of the path that didn't exist)
119                          */
120                         free(str);
121                         return (-1);
122                 }
123         }
124         free(str);
125         return (0);
126 }
127
128 /*
129  *      simplify - given a pathname, simplify that path by removing
130  *                 duplicate contiguous slashes.
131  *
132  *                 A simplified copy of the argument is returned to the
133  *                 caller, or NULL is returned on error.
134  *
135  *                 The caller should handle error reporting based upon the
136  *                 returned vlaue, and should free the returned value,
137  *                 when appropriate.
138  */
139
140 static char *
141 simplify(const char *str)
142 {
143         int i;
144         size_t mbPathlen;       /* length of multi-byte path */
145         size_t wcPathlen;       /* length of wide-character path */
146         wchar_t *wptr;          /* scratch pointer */
147         wchar_t *wcPath;        /* wide-character version of the path */
148         char *mbPath;           /* The copy fo the path to be returned */
149
150         /*
151          *  bail out if there is nothing there.
152          */
153
154         if (!str)
155             return (NULL);
156
157         /*
158          *  Get a copy of the argument.
159          */
160
161         if ((mbPath = strdup(str)) == NULL) {
162                 return (NULL);
163         }
164
165         /*
166          *  convert the multi-byte version of the path to a
167          *  wide-character rendering, for doing our figuring.
168          */
169
170         mbPathlen = strlen(mbPath);
171
172         if ((wcPath = calloc(sizeof (wchar_t), mbPathlen+1)) == NULL) {
173                 free(mbPath);
174                 return (NULL);
175         }
176
177         if ((wcPathlen = mbstowcs(wcPath, mbPath, mbPathlen)) == (size_t)-1) {
178                 free(mbPath);
179                 free(wcPath);
180                 return (NULL);
181         }
182
183         /*
184          *  remove duplicate slashes first ("//../" -> "/")
185          */
186
187         for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
188                 *wptr++ = wcPath[i];
189
190                 if (wcPath[i] == '/') {
191                         i++;
192
193                         while (wcPath[i] == '/') {
194                                 i++;
195                         }
196
197                         i--;
198                 }
199         }
200
201         *wptr = '\0';
202
203         /*
204          *  now convert back to the multi-byte format.
205          */
206
207         if (wcstombs(mbPath, wcPath, mbPathlen) == (size_t)-1) {
208                 free(mbPath);
209                 free(wcPath);
210                 return (NULL);
211         }
212
213         free(wcPath);
214         return (mbPath);
215 }