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