xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/dmd/common/string.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /**
2  * Common string functions including filename manipulation.
3  *
4  * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
5  * Authors:   Walter Bright, https://www.digitalmars.com
6  * License:   $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/string.d, common/_string.d)
8  * Documentation: https://dlang.org/phobos/dmd_common_string.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/string.d
10  */
11 module dmd.common.string;
12 
13 nothrow:
14 
15 /**
16 Defines a temporary array using a fixed-length buffer as back store. If the length
17 of the buffer suffices, it is readily used. Otherwise, `malloc` is used to
18 allocate memory for the array and `free` is used for deallocation in the
19 destructor.
20 
21 This type is meant to use exclusively as an automatic variable. It is not
22 default constructible or copyable.
23 */
SmallBuffer(T)24 struct SmallBuffer(T)
25 {
26     import core.stdc.stdlib : malloc, free;
27 
28     private T[] _extent;
29     private bool needsFree;
30 
31   nothrow:
32 
33     @disable this(); // no default ctor
34     @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable
35 
36     this(size_t len, T[] buffer)
37     {
38         if (len <= buffer.length)
39         {
40             _extent = buffer[0 .. len];
41         }
42         else
43         {
44             _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len];
45             _extent.ptr || assert(0, "Out of memory.");
46             needsFree = true;
47         }
48         assert(this.length == len);
49     }
50 
51     ~this()
52     {
53         if (needsFree)
54             free(_extent.ptr);
55     }
56 
57     void create(size_t len)
58     {
59         if (len <= _extent.length)
60         {
61             _extent = _extent[0 .. len];
62         }
63         else
64         {
65             __dtor();
66             _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len];
67             _extent.ptr || assert(0, "Out of memory.");
68             needsFree = true;
69         }
70         assert(this.length == len);
71     }
72 
73     // Force accesses to extent to be scoped.
74     scope inout extent()
75     {
76         return _extent;
77     }
78 
79     alias extent this;
80 }
81 
82 /// ditto
83 unittest
84 {
85     char[230] buf = void;
86     auto a = SmallBuffer!char(10, buf);
87     assert(a[] is buf[0 .. 10]);
88     auto b = SmallBuffer!char(1000, buf);
89     assert(b[] !is buf[]);
90     b.create(1000);
91     assert(b.length == 1000);
92     assert(b[] !is buf[]);
93 }
94 
95 /**
96 Converts a zero-terminated C string to a D slice. Takes linear time and allocates no memory.
97 
98 Params:
99 stringz = the C string to be converted
100 
101 Returns:
102 a slice comprehending the string. The terminating 0 is not part of the slice.
103 */
asDString(C)104 auto asDString(C)(C* stringz) pure @nogc nothrow
105 {
106     import core.stdc.string : strlen;
107     return stringz[0 .. strlen(stringz)];
108 }
109 
110 ///
111 unittest
112 {
113     const char* p = "123".ptr;
114     assert(p.asDString == "123");
115 }
116 
117 /**
118 (Windows only) Converts a narrow string to a wide string using `buffer` as strorage. Returns a slice managed by
119 `buffer` containing the converted string. The terminating zero is not part of the returned slice,
120 but is guaranteed to follow it.
121 */
version(Windows)122 version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow
123 {
124     import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar;
125     // assume filenames encoded in system default Windows ANSI code page
126     enum CodePage = CP_ACP;
127 
128     if (narrow is null)
129         return null;
130 
131     const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
132     if (requiredLength < cast(int) buffer.length)
133     {
134         buffer[requiredLength] = 0;
135         return buffer[0 .. requiredLength];
136     }
137 
138     buffer.create(requiredLength + 1);
139     const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, requiredLength);
140     assert(length == requiredLength);
141     buffer[length] = 0;
142     return buffer[0 .. length];
143 }
144 
145 /**************************************
146 * Converts a path to one suitable to be passed to Win32 API
147 * functions that can deal with paths longer than 248
148 * characters then calls the supplied function on it.
149 *
150 * Params:
151 *  path = The Path to call F on.
152 *
153 * Returns:
154 *  The result of calling F on path.
155 *
156 * References:
157 *  https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
158 */
version(Windows)159 version(Windows) auto extendedPathThen(alias F)(const(char)[] path)
160 {
161     import core.sys.windows.winbase;
162     import core.sys.windows.winnt;
163 
164     if (!path.length)
165         return F((wchar[]).init);
166 
167     wchar[1024] buf = void;
168     auto store = SmallBuffer!wchar(buf.length, buf);
169     auto wpath = toWStringz(path, store);
170 
171     // GetFullPathNameW expects a sized buffer to store the result in. Since we don't
172     // know how large it has to be, we pass in null and get the needed buffer length
173     // as the return code.
174     const pathLength = GetFullPathNameW(&wpath[0],
175                                         0 /*length8*/,
176                                         null /*output buffer*/,
177                                         null /*filePartBuffer*/);
178     if (pathLength == 0)
179     {
180         return F((wchar[]).init);
181     }
182 
183     // wpath is the UTF16 version of path, but to be able to use
184     // extended paths, we need to prefix with `\\?\` and the absolute
185     // path.
186     static immutable prefix = `\\?\`w;
187 
188     // prefix only needed for long names and non-UNC names
189     const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\');
190     const prefixLength = needsPrefix ? prefix.length : 0;
191 
192     // +1 for the null terminator
193     const bufferLength = pathLength + prefixLength + 1;
194 
195     wchar[1024] absBuf = void;
196     auto absPath = SmallBuffer!wchar(bufferLength, absBuf);
197 
198     absPath[0 .. prefixLength] = prefix[0 .. prefixLength];
199 
200     const absPathRet = GetFullPathNameW(&wpath[0],
201         cast(uint)(absPath.length - prefixLength - 1),
202         &absPath[prefixLength],
203         null /*filePartBuffer*/);
204 
205     if (absPathRet == 0 || absPathRet > absPath.length - prefixLength)
206     {
207         return F((wchar[]).init);
208     }
209 
210     absPath[$ - 1] = '\0';
211     // Strip null terminator from the slice
212     return F(absPath[0 .. $ - 1]);
213 }
214