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 }