Commit | Line | Data |
---|---|---|
d44e3c4f | 1 | /****************************************************************************** |
2 | * Copyright (c) 2000-2016 Ericsson Telecom AB | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * Balasko, Jeno | |
10 | * Forstner, Matyas | |
11 | * Raduly, Csaba | |
12 | * Szabo, Bence Janos | |
13 | * Szabo, Janos Zoltan – initial implementation | |
14 | * | |
15 | ******************************************************************************/ | |
970ed795 EL |
16 | #include <string.h> |
17 | #include <errno.h> | |
18 | #include <sys/stat.h> | |
19 | #include <unistd.h> | |
20 | ||
21 | #include "memory.h" | |
22 | #include "path.h" | |
23 | ||
24 | /* Initial buffer size for getcwd */ | |
25 | #define BUFSIZE 1024 | |
26 | ||
27 | expstring_t get_working_dir(void) | |
28 | { | |
29 | expstring_t ret_val = NULL; | |
30 | char buf[BUFSIZE]; | |
31 | const char *buf_ptr; | |
32 | buf_ptr = getcwd(buf, sizeof(buf)); | |
33 | if (buf_ptr != NULL) ret_val = mcopystr(buf_ptr); | |
34 | else if (errno == ERANGE) { | |
35 | /* the initial buffer size is not enough */ | |
36 | size_t size; | |
37 | for (size = 2 * BUFSIZE; ; size *= 2) { | |
38 | char *tmp = (char*)Malloc(size); | |
39 | buf_ptr = getcwd(tmp, size); | |
40 | if (buf_ptr != NULL) ret_val = mcopystr(buf_ptr); | |
41 | Free(tmp); | |
42 | if (buf_ptr != NULL || errno != ERANGE) break; | |
43 | } | |
44 | } | |
45 | if (ret_val == NULL) { | |
46 | /* an error occurred */ | |
47 | path_error("Getting the current working directory failed: %s", | |
48 | strerror(errno)); | |
49 | } | |
50 | /* clear the possible error codes */ | |
51 | errno = 0; | |
52 | return ret_val; | |
53 | } | |
54 | ||
55 | int set_working_dir(const char *new_dir) | |
56 | { | |
57 | if (new_dir == NULL) { | |
58 | /* invalid argument */ | |
59 | return 1; | |
60 | } else if (chdir(new_dir)) { | |
61 | /* an error occurred */ | |
62 | path_error("Setting the current working directory to `%s' failed: %s", | |
63 | new_dir, strerror(errno)); | |
64 | errno = 0; | |
65 | return 1; | |
66 | } else { | |
67 | /* everything is OK */ | |
68 | return 0; | |
69 | } | |
70 | } | |
71 | ||
72 | enum path_status_t get_path_status(const char *path_name) | |
73 | { | |
74 | struct stat buf; | |
75 | if (stat(path_name, &buf)) { | |
76 | if (errno != ENOENT) { | |
77 | path_error("system call stat() failed on `%s': %s", path_name, | |
78 | strerror(errno)); | |
79 | } | |
80 | errno = 0; | |
81 | return PS_NONEXISTENT; | |
82 | } | |
83 | if (S_ISDIR(buf.st_mode)) return PS_DIRECTORY; | |
84 | else return PS_FILE; | |
85 | } | |
86 | ||
87 | expstring_t get_dir_from_path(const char *path_name) | |
88 | { | |
89 | size_t last_slash_index = (size_t)-1; | |
90 | size_t i; | |
91 | for (i = 0; path_name[i] != '\0'; i++) | |
92 | if (path_name[i] == '/') last_slash_index = i; | |
93 | if (last_slash_index == (size_t)-1) { | |
94 | /* path_name does not contain any slash */ | |
95 | return NULL; | |
96 | } else if (last_slash_index == 0) { | |
97 | /* path_name has the format "/filename": return "/" */ | |
98 | return mcopystr("/"); | |
99 | } else { | |
100 | /* path_name has the format "<something>/filename": | |
101 | return "<something>" */ | |
102 | expstring_t ret_val = mcopystr(path_name); | |
103 | ret_val = mtruncstr(ret_val, last_slash_index); | |
104 | return ret_val; | |
105 | } | |
106 | } | |
107 | ||
108 | expstring_t get_file_from_path(const char *path_name) | |
109 | { | |
110 | size_t last_slash_index = (size_t)-1; | |
111 | size_t i; | |
112 | for (i = 0; path_name[i] != '\0'; i++) | |
113 | if (path_name[i] == '/') last_slash_index = i; | |
114 | if (last_slash_index == (size_t)-1) { | |
115 | /* path_name does not contain any slash: return the entire input */ | |
116 | return mcopystr(path_name); | |
117 | } else { | |
118 | /* path_name has the format "<something>/filename": return "filename" */ | |
119 | return mcopystr(path_name + last_slash_index + 1); | |
120 | } | |
121 | } | |
122 | ||
123 | expstring_t compose_path_name(const char *dir_name, | |
124 | const char *file_name) | |
125 | { | |
126 | if (dir_name != NULL && dir_name[0] != '\0') { | |
127 | expstring_t ret_val = mcopystr(dir_name); | |
128 | if (file_name != NULL && file_name[0] != '\0') { | |
129 | /* neither dir_name nor file_name are empty */ | |
130 | size_t dir_name_len = strlen(dir_name); | |
131 | /* do not add the separator slash if dir_name ends with a slash */ | |
132 | if (dir_name[dir_name_len - 1] != '/') | |
133 | ret_val = mputc(ret_val, '/'); | |
134 | ret_val = mputstr(ret_val, file_name); | |
135 | } | |
136 | return ret_val; | |
137 | } else return mcopystr(file_name); | |
138 | } | |
139 | ||
feade998 | 140 | expstring_t get_absolute_dir(const char *dir_name, const char *base_dir, const int with_error) |
970ed795 EL |
141 | { |
142 | expstring_t ret_val; | |
143 | /* save the working directory */ | |
144 | expstring_t initial_dir = get_working_dir(); | |
145 | if (base_dir != NULL && (dir_name == NULL || dir_name[0] != '/')) { | |
146 | /* go to base_dir first if it is given and dir_name is not an | |
147 | absolute path */ | |
148 | if (set_working_dir(base_dir)) { | |
149 | Free(initial_dir); | |
150 | return NULL; | |
151 | } | |
152 | } | |
feade998 | 153 | if (dir_name != NULL && with_error && set_working_dir(dir_name)) { |
970ed795 EL |
154 | /* there was an error: go back to initial_dir */ |
155 | set_working_dir(initial_dir); | |
156 | Free(initial_dir); | |
157 | return NULL; | |
158 | } | |
feade998 | 159 | if (dir_name != NULL && !with_error && chdir(dir_name)) { |
160 | //No error sign | |
161 | errno = 0; | |
162 | Free(initial_dir); | |
163 | return NULL; | |
164 | } | |
970ed795 EL |
165 | ret_val = get_working_dir(); |
166 | /* restore the working directory */ | |
167 | set_working_dir(initial_dir); | |
168 | Free(initial_dir); | |
169 | if (ret_val != NULL && | |
170 | #if defined WIN32 && defined MINGW | |
171 | /* On native Windows the absolute path name shall begin with | |
172 | * a drive letter, colon and backslash */ | |
173 | (((ret_val[0] < 'A' || ret_val[0] > 'Z') && | |
174 | (ret_val[0] < 'a' || ret_val[0] > 'z')) || | |
175 | ret_val[1] != ':' || ret_val[2] != '\\') | |
176 | #else | |
177 | /* On UNIX-like systems the absolute path name shall begin with | |
178 | * a slash */ | |
179 | ret_val[0] != '/' | |
180 | #endif | |
181 | ) | |
182 | path_error("Internal error: `%s' is not a valid absolute pathname.", | |
183 | ret_val); | |
184 | return ret_val; | |
185 | } | |
186 | ||
187 | expstring_t get_relative_dir(const char *dir_name, const char *base_dir) | |
188 | { | |
189 | expstring_t ret_val = NULL; | |
190 | /* canonize dir_name and the base directory */ | |
feade998 | 191 | expstring_t canonized_dir_name = get_absolute_dir(dir_name, base_dir, 1); |
970ed795 | 192 | expstring_t canonized_base_dir = base_dir != NULL ? |
feade998 | 193 | get_absolute_dir(base_dir, NULL, 1) : get_working_dir(); |
970ed795 EL |
194 | size_t i, last_slash = 0; |
195 | if (canonized_dir_name == NULL || canonized_base_dir == NULL) { | |
196 | /* an error occurred */ | |
197 | Free(canonized_dir_name); | |
198 | Free(canonized_base_dir); | |
199 | return NULL; | |
200 | } | |
201 | /* skip over the common leading directory part of canonized_dir_name and | |
202 | canonized_base_dir */ | |
203 | for (i = 1; ; i++) { | |
204 | char dir_c = canonized_dir_name[i]; | |
205 | char base_c = canonized_base_dir[i]; | |
206 | if (dir_c == '\0') { | |
207 | /* we must update last_slash if dir_name is a parent of base_dir */ | |
208 | if (base_c == '/') last_slash = i; | |
209 | /* we must stop anyway */ | |
210 | break; | |
211 | } else if (dir_c == '/') { | |
212 | if (base_c == '/' || base_c == '\0') last_slash = i; | |
213 | if (base_c != '/') break; | |
214 | } else { | |
215 | if (dir_c != base_c) break; | |
216 | } | |
217 | } | |
218 | if (canonized_dir_name[i] == '\0' && canonized_base_dir[i] == '\0') { | |
219 | /* canonized_dir_name and canonized_base_dir are the same */ | |
220 | ret_val = mcopystr("."); | |
221 | } else { | |
222 | if (canonized_base_dir[last_slash] == '/' && | |
223 | canonized_base_dir[last_slash + 1] != '\0') { | |
224 | /* canonized_base_dir has some own additional components | |
225 | (i.e. it is not the parent of canonized_dir_name) */ | |
226 | for (i = last_slash; canonized_base_dir[i] != '\0'; i++) { | |
227 | if (canonized_base_dir[i] == '/') { | |
228 | /* go up one level in the relative path */ | |
229 | if (ret_val != NULL) ret_val = mputc(ret_val, '/'); | |
230 | ret_val = mputstr(ret_val, ".."); | |
231 | } | |
232 | } | |
233 | } | |
234 | if (canonized_dir_name[last_slash] == '/' && | |
235 | canonized_dir_name[last_slash + 1] != '\0') { | |
236 | /* canonized_dir_name has some own additional components | |
237 | (i.e. it is not the parent of canonized_base_dir) */ | |
238 | /* append the remaining parts of canonized_dir_name to the result */ | |
239 | if (ret_val != NULL) ret_val = mputc(ret_val, '/'); | |
240 | ret_val = mputstr(ret_val, canonized_dir_name + last_slash + 1); | |
241 | } | |
242 | } | |
243 | Free(canonized_dir_name); | |
244 | Free(canonized_base_dir); | |
245 | return ret_val; | |
246 | } |