1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.haxx.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | #include "tool_setup.h" |
23 | |
24 | #include <sys/stat.h> |
25 | |
26 | #ifdef WIN32 |
27 | # include <direct.h> |
28 | #endif |
29 | |
30 | #define ENABLE_CURLX_PRINTF |
31 | /* use our own printf() functions */ |
32 | #include "curlx.h" |
33 | |
34 | #include "tool_dirhie.h" |
35 | |
36 | #include "memdebug.h" /* keep this as LAST include */ |
37 | |
38 | #ifdef NETWARE |
39 | # ifndef __NOVELL_LIBC__ |
40 | # define mkdir mkdir_510 |
41 | # endif |
42 | #endif |
43 | |
44 | #if defined(WIN32) || (defined(MSDOS) && !defined(__DJGPP__)) |
45 | # define mkdir(x,y) (mkdir)((x)) |
46 | # ifndef F_OK |
47 | # define F_OK 0 |
48 | # endif |
49 | #endif |
50 | |
51 | static void show_dir_errno(FILE *errors, const char *name) |
52 | { |
53 | switch(errno) { |
54 | #ifdef EACCES |
55 | case EACCES: |
56 | fprintf(errors, "You don't have permission to create %s.\n" , name); |
57 | break; |
58 | #endif |
59 | #ifdef ENAMETOOLONG |
60 | case ENAMETOOLONG: |
61 | fprintf(errors, "The directory name %s is too long.\n" , name); |
62 | break; |
63 | #endif |
64 | #ifdef EROFS |
65 | case EROFS: |
66 | fprintf(errors, "%s resides on a read-only file system.\n" , name); |
67 | break; |
68 | #endif |
69 | #ifdef ENOSPC |
70 | case ENOSPC: |
71 | fprintf(errors, "No space left on the file system that will " |
72 | "contain the directory %s.\n" , name); |
73 | break; |
74 | #endif |
75 | #ifdef EDQUOT |
76 | case EDQUOT: |
77 | fprintf(errors, "Cannot create directory %s because you " |
78 | "exceeded your quota.\n" , name); |
79 | break; |
80 | #endif |
81 | default : |
82 | fprintf(errors, "Error creating directory %s.\n" , name); |
83 | break; |
84 | } |
85 | } |
86 | |
87 | /* |
88 | * Create the needed directory hierarchy recursively in order to save |
89 | * multi-GETs in file output, ie: |
90 | * curl "http://my.site/dir[1-5]/file[1-5].txt" -o "dir#1/file#2.txt" |
91 | * should create all the dir* automagically |
92 | */ |
93 | |
94 | #if defined(WIN32) || defined(__DJGPP__) |
95 | /* systems that may use either or when specifying a path */ |
96 | #define PATH_DELIMITERS "\\/" |
97 | #else |
98 | #define PATH_DELIMITERS DIR_CHAR |
99 | #endif |
100 | |
101 | |
102 | CURLcode create_dir_hierarchy(const char *outfile, FILE *errors) |
103 | { |
104 | char *tempdir; |
105 | char *tempdir2; |
106 | char *outdup; |
107 | char *dirbuildup; |
108 | CURLcode result = CURLE_OK; |
109 | size_t outlen; |
110 | |
111 | outlen = strlen(outfile); |
112 | outdup = strdup(outfile); |
113 | if(!outdup) |
114 | return CURLE_OUT_OF_MEMORY; |
115 | |
116 | dirbuildup = malloc(outlen + 1); |
117 | if(!dirbuildup) { |
118 | Curl_safefree(outdup); |
119 | return CURLE_OUT_OF_MEMORY; |
120 | } |
121 | dirbuildup[0] = '\0'; |
122 | |
123 | /* Allow strtok() here since this isn't used threaded */ |
124 | /* !checksrc! disable BANNEDFUNC 2 */ |
125 | tempdir = strtok(outdup, PATH_DELIMITERS); |
126 | |
127 | while(tempdir != NULL) { |
128 | tempdir2 = strtok(NULL, PATH_DELIMITERS); |
129 | /* since strtok returns a token for the last word even |
130 | if not ending with DIR_CHAR, we need to prune it */ |
131 | if(tempdir2 != NULL) { |
132 | size_t dlen = strlen(dirbuildup); |
133 | if(dlen) |
134 | msnprintf(&dirbuildup[dlen], outlen - dlen, "%s%s" , DIR_CHAR, tempdir); |
135 | else { |
136 | if(outdup == tempdir) |
137 | /* the output string doesn't start with a separator */ |
138 | strcpy(dirbuildup, tempdir); |
139 | else |
140 | msnprintf(dirbuildup, outlen, "%s%s" , DIR_CHAR, tempdir); |
141 | } |
142 | if((-1 == mkdir(dirbuildup, (mode_t)0000750)) && (errno != EEXIST)) { |
143 | show_dir_errno(errors, dirbuildup); |
144 | result = CURLE_WRITE_ERROR; |
145 | break; /* get out of loop */ |
146 | } |
147 | } |
148 | tempdir = tempdir2; |
149 | } |
150 | |
151 | Curl_safefree(dirbuildup); |
152 | Curl_safefree(outdup); |
153 | |
154 | return result; |
155 | } |
156 | |