| 1 | /////////////////////////////////////////////////////////////////////////////// |
| 2 | // Copyright (c) 2000-2015 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 | } |