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