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