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 }