1 /*_ filespec.h Fri Jul 8 1988 Modified by: bright */ 2 /* Copyright (C) 1986-1987 by Northwest Software */ 3 /* All Rights Reserved */ 4 /* Written by Walter Bright */ 5 module dmd.backend.filespec; 6 7 import core.stdc.ctype; 8 import core.stdc.stdio; 9 import core.stdc.stdlib; 10 import core.stdc.string; 11 12 import dmd.backend.mem; 13 14 15 nothrow: 16 @safe: 17 18 /********************************* 19 * String compare of filenames. 20 */ 21 22 version (Windows) 23 { 24 extern (C) 25 { 26 int stricmp(const(char)*, const(char)*) pure nothrow @nogc; 27 int memicmp(const(void)*, const(void)*, size_t) pure nothrow @nogc; 28 } 29 30 alias filespeccmp = stricmp; 31 alias filespecmemcmp = memicmp; 32 33 enum DIRCHAR = '\\'; 34 35 bool ispathdelim(char c) { return c == DIRCHAR || c == ':' || c == '/'; } 36 } 37 else 38 { 39 import core.stdc.string : strcmp, memcmp; 40 alias filespeccmp = strcmp; 41 alias filespecmemcmp = memcmp; 42 43 enum DIRCHAR = '/'; 44 45 bool ispathdelim(char c) { return c == DIRCHAR; } 46 } 47 48 /**************************** 49 * Combine path and filename to form a filespec. 50 * Input: 51 * path Path, with or without trailing / 52 * (can be NULL) 53 * filename Cannot be NULL 54 * Returns: 55 * filespec mem_malloc'd file specification 56 * NULL Out of memory 57 */ 58 @trusted 59 char *filespecaddpath(const(char)* path, const(char)* filename) 60 { 61 char* filespec; 62 size_t pathlen; 63 64 if (!path || (pathlen = strlen(path)) == 0) 65 filespec = mem_strdup(filename); 66 else 67 { 68 filespec = cast(char*) mem_malloc(pathlen + 1 + strlen(filename) + 1); 69 if (filespec) 70 { 71 strcpy(filespec,path); 72 version (Windows) 73 { 74 if (!ispathdelim(filespec[pathlen - 1])) 75 strcat(filespec,"\\"); 76 } 77 else 78 { 79 if (!ispathdelim(filespec[pathlen - 1])) 80 strcat(filespec,"/"); 81 } 82 strcat(filespec,filename); 83 } 84 } 85 return filespec; 86 } 87 88 /******************************* filespecrootpath ************************** 89 * Purpose: To expand a relative path into an absolute path. 90 * 91 * Side Effects: mem_frees input string. 92 * 93 * Returns: mem_malloced string with absolute path. 94 * NULL if some failure. 95 */ 96 97 version (Windows) 98 extern (C) char* getcwd(char*, size_t); 99 else 100 { 101 import core.sys.posix.unistd: getcwd; 102 } 103 104 @trusted 105 char *filespecrootpath(char* filespec) 106 { 107 char *cwd; 108 char *cwd_t; 109 char *p; 110 char *p2; 111 112 if (!filespec) 113 return filespec; 114 version (Windows) 115 { 116 // if already absolute (with \ or drive:) ... 117 if (*filespec == DIRCHAR || (isalpha(*filespec) && *(filespec+1) == ':')) 118 return filespec; // ... return input string 119 } 120 else 121 { 122 if (*filespec == DIRCHAR) // already absolute ... 123 return filespec; // ... return input string 124 } 125 126 // get current working directory path 127 version (Windows) 128 { 129 char[132] cwd_d = void; 130 if (getcwd(cwd_d.ptr, cwd_d.length)) 131 cwd_t = cwd_d.ptr; 132 else 133 cwd_t = null; 134 } 135 else 136 { 137 cwd_t = cast(char *)getcwd(null, 256); 138 } 139 140 if (cwd_t == null) 141 { 142 mem_free(filespec); 143 return null; // error - path too long (more than 256 chars !) 144 } 145 cwd = mem_strdup(cwd_t); // convert cwd to mem package 146 version (Windows) 147 { 148 } 149 else 150 { 151 free(cwd_t); 152 } 153 p = filespec; 154 while (p != null) 155 { 156 p2 = cast(char*)strchr(p, DIRCHAR); 157 if (p2 != null) 158 { 159 *p2 = '\0'; 160 if (strcmp(p, "..") == 0) // move up cwd 161 // remove last directory from cwd 162 *(cast(char *)strrchr(cwd, DIRCHAR)) = '\0'; 163 else if (strcmp(p, ".") != 0) // not current directory 164 { 165 cwd_t = cwd; 166 const cwdlen = strlen(cwd_t) + 1 + strlen(p) + 1; 167 cwd = cast(char *)mem_calloc(cwdlen); 168 snprintf(cwd, cwdlen, "%s%c%s", cwd_t, DIRCHAR, p); // add relative directory 169 mem_free(cwd_t); 170 } 171 // else if ".", then ignore - it means current directory 172 *p2 = DIRCHAR; 173 p2++; 174 } 175 else if (strcmp(p,"..") == 0) // move up cwd 176 { 177 // remove last directory from cwd 178 *(cast(char *)strrchr(cwd, DIRCHAR)) = '\0'; 179 } 180 else if (strcmp(p,".") != 0) // no more subdirectories ... 181 { // ... save remaining string 182 cwd_t = cwd; 183 const cwdlen = strlen(cwd_t) + 1 + strlen(p) + 1; 184 cwd = cast(char *)mem_calloc(cwdlen); 185 snprintf(cwd, cwdlen, "%s%c%s", cwd_t, DIRCHAR, p); // add relative directory 186 mem_free(cwd_t); 187 } 188 p = p2; 189 } 190 mem_free(filespec); 191 192 return cwd; 193 } 194 195 /***************************** 196 * Add extension onto filespec, if one isn't already there. 197 * Input: 198 * filespec Cannot be NULL 199 * ext Extension (without the .) 200 * Returns: 201 * mem_malloc'ed string (NULL if error) 202 */ 203 @trusted 204 char *filespecdefaultext(const(char)* filespec, const(char)* ext) 205 { 206 char *p; 207 208 const(char)* pext = filespecdotext(filespec); 209 if (*pext == '.') /* if already got an extension */ 210 { 211 p = mem_strdup(filespec); 212 } 213 else 214 { 215 const n = pext - filespec; 216 p = cast(char *) mem_malloc(n + 1 + strlen(ext) + 1); 217 if (p) 218 { 219 memcpy(p,filespec,n); 220 p[n] = '.'; 221 strcpy(&p[n + 1],ext); 222 } 223 } 224 return p; 225 } 226 227 /********************** 228 * Return string that is the dot and extension. 229 * The string returned is NOT mem_malloc'ed. 230 * Return pointer to the 0 at the end of filespec if dot isn't found. 231 * Return NULL if filespec is NULL. 232 */ 233 @trusted 234 char *filespecdotext(const(char)* filespec) 235 { 236 auto p = filespec; 237 if (p) 238 { 239 const len = strlen(p); 240 p += len; 241 while (1) 242 { 243 if (*p == '.') 244 break; 245 if (p <= filespec || ispathdelim(*p)) 246 { p = filespec + len; 247 break; 248 } 249 p--; 250 } 251 } 252 return cast(char*)p; 253 } 254 255 /***************************** 256 * Force extension onto filespec. 257 * Input: 258 * filespec String that may or may not contain an extension 259 * ext Extension that doesn't contain a . 260 * Returns: 261 * mem_malloc'ed string (NULL if error) 262 * NULL if filespec is NULL 263 * If ext is NULL, return mem_strdup(filespec) 264 */ 265 @trusted 266 char *filespecforceext(const(char)* filespec, const(char)* ext) 267 { 268 char* p; 269 270 if (ext && *ext == '.') 271 ext++; 272 if ((p = cast(char *)filespec) != null) 273 { 274 const(char)* pext = filespecdotext(filespec); 275 if (ext) 276 { 277 size_t n = pext - filespec; 278 p = cast(char*) mem_malloc(n + 1 + strlen(ext) + 1); 279 if (p) 280 { 281 memcpy(p, filespec, n); 282 p[n] = '.'; 283 strcpy(&p[n + 1],ext); 284 } 285 } 286 else 287 p = mem_strdup(filespec); 288 } 289 return p; 290 } 291 292 /*********************** 293 * Get root name of file name. 294 * That is, return a mem_strdup()'d version of the filename without 295 * the .ext. 296 */ 297 298 char *filespecgetroot(const(char)* name) 299 { 300 char* p = filespecdotext(name); 301 const c = *p; 302 *p = 0; 303 char* root = mem_strdup(name); 304 *p = c; 305 return root; 306 } 307 308 /********************** 309 * Return string that is the filename plus dot and extension. 310 * The string returned is NOT mem_malloc'ed. 311 */ 312 313 @trusted 314 char *filespecname(const(char)* filespec) 315 { 316 const(char)* p; 317 318 /* Start at end of string and back up till we find the beginning 319 * of the filename or a path 320 */ 321 for (p = filespec + strlen(filespec); 322 p != filespec && !ispathdelim(*(p - 1)); 323 p-- 324 ) 325 { } 326 return cast(char *)p; 327 } 328 329 330 /***************************** 331 * Convert filespec into a backup filename appropriate for the 332 * operating system. For instance, under MS-DOS path\filename.ext will 333 * be converted to path\filename.bak. 334 * Input: 335 * filespec String that may or may not contain an extension 336 * Returns: 337 * mem_malloc'ed string (NULL if error) 338 * NULL if filespec is NULL 339 */ 340 341 @trusted 342 char *filespecbackup(const(char)* filespec) 343 { 344 version (Windows) 345 { 346 return filespecforceext(filespec,"BAK"); 347 } 348 else 349 { 350 char* p; 351 char* f; 352 353 // Prepend .B to file name, if it isn't already there 354 if (!filespec) 355 return cast(char *)filespec; 356 p = filespecname(filespec); 357 if (p[0] == '.' && p[1] == 'B') 358 return mem_strdup(filespec); 359 f = cast(char *) mem_malloc(strlen(filespec) + 2 + 1); 360 if (f) 361 { strcpy(f,filespec); 362 strcpy(&f[p - filespec],".B"); 363 strcat(f,p); 364 } 365 return f; 366 } 367 }