1
/**
2
 * Parses compiler settings from a .ini file.
3
 *
4
 * Copyright:   Copyright (C) 1994-1998 by Symantec
5
 *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
6
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dinifile.d, _dinifile.d)
9
 * Documentation:  https://dlang.org/phobos/dmd_dinifile.html
10
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dinifile.d
11
 */
12

13
module dmd.dinifile;
14

15
import core.stdc.ctype;
16
import core.stdc.string;
17
import core.sys.posix.stdlib;
18
import core.sys.windows.winbase;
19
import core.sys.windows.windef;
20

21
import dmd.env;
22
import dmd.errors;
23
import dmd.globals;
24
import dmd.root.rmem;
25
import dmd.root.filename;
26
import dmd.root.outbuffer;
27
import dmd.root.port;
28
import dmd.root.string;
29
import dmd.root.stringtable;
30

31
private enum LOG = false;
32

33
/*****************************
34
 * Find the config file
35
 * Params:
36
 *      argv0 = program name (argv[0])
37
 *      inifile = .ini file name
38
 * Returns:
39
 *      file path of the config file or NULL
40
 *      Note: this is a memory leak
41
 */
42
const(char)[] findConfFile(const(char)[] argv0, const(char)[] inifile)
43
{
44
    static if (LOG)
45
    {
46
        printf("findinifile(argv0 = '%.*s', inifile = '%.*s')\n",
47
               cast(int)argv0.length, argv0.ptr, cast(int)inifile.length, inifile.ptr);
48
    }
49 1
    if (FileName.absolute(inifile))
50 0
        return inifile;
51 1
    if (FileName.exists(inifile))
52 0
        return inifile;
53
    /* Look for inifile in the following sequence of places:
54
     *      o current directory
55
     *      o home directory
56
     *      o exe directory (windows)
57
     *      o directory off of argv0
58
     *      o SYSCONFDIR=/etc (non-windows)
59
     */
60 1
    auto filename = FileName.combine(getenv("HOME").toDString, inifile);
61 1
    if (FileName.exists(filename))
62 0
        return filename;
63
    version (Windows)
64
    {
65
        // This fix by Tim Matthews
66
        char[MAX_PATH + 1] resolved_name;
67
        const len = GetModuleFileNameA(null, resolved_name.ptr, MAX_PATH + 1);
68
        if (len && FileName.exists(resolved_name[0 .. len]))
69
        {
70
            filename = FileName.replaceName(resolved_name[0 .. len], inifile);
71
            if (FileName.exists(filename))
72
                return filename;
73
        }
74
    }
75 1
    filename = FileName.replaceName(argv0, inifile);
76 1
    if (FileName.exists(filename))
77 1
        return filename;
78
    version (Posix)
79
    {
80
        // Search PATH for argv0
81 0
        const p = getenv("PATH");
82
        static if (LOG)
83
        {
84
            printf("\tPATH='%s'\n", p);
85
        }
86 0
        auto abspath = FileName.searchPath(p, argv0, false);
87 0
        if (abspath)
88
        {
89 0
            auto absname = FileName.replaceName(abspath, inifile);
90 0
            if (FileName.exists(absname))
91 0
                return absname;
92
        }
93
        // Resolve symbolic links
94 0
        filename = FileName.canonicalName(abspath ? abspath : argv0);
95 0
        if (filename)
96
        {
97 0
            filename = FileName.replaceName(filename, inifile);
98 0
            if (FileName.exists(filename))
99 0
                return filename;
100
        }
101
        // Search SYSCONFDIR=/etc for inifile
102 0
        filename = FileName.combine(import("SYSCONFDIR.imp"), inifile);
103
    }
104 0
    return filename;
105
}
106

107
/**********************************
108
 * Read from environment, looking for cached value first.
109
 * Params:
110
 *      environment = cached copy of the environment
111
 *      name = name to look for
112
 * Returns:
113
 *      environment value corresponding to name
114
 */
115
const(char)* readFromEnv(const ref StringTable!(char*) environment, const(char)* name)
116
{
117 1
    const len = strlen(name);
118 1
    const sv = environment.lookup(name, len);
119 1
    if (sv && sv.value)
120 1
        return sv.value; // get cached value
121 1
    return getenv(name);
122
}
123

124
/*********************************
125
 * Write to our copy of the environment, not the real environment
126
 */
127
private bool writeToEnv(ref StringTable!(char*) environment, char* nameEqValue)
128
{
129 1
    auto p = strchr(nameEqValue, '=');
130 1
    if (!p)
131 0
        return false;
132 1
    auto sv = environment.update(nameEqValue, p - nameEqValue);
133 1
    sv.value = p + 1;
134 1
    return true;
135
}
136

137
/************************************
138
 * Update real environment with our copy.
139
 * Params:
140
 *      environment = our copy of the environment
141
 */
142
void updateRealEnvironment(ref StringTable!(char*) environment)
143
{
144 1
    foreach (sv; environment)
145
    {
146 1
        const name = sv.toDchars();
147 1
        const value = sv.value;
148 1
        if (!value) // deleted?
149
            continue;
150 1
        if (putenvRestorable(name.toDString, value.toDString))
151 0
            assert(0);
152
    }
153
}
154

155
/*****************************
156
 * Read and analyze .ini file.
157
 * Write the entries into environment as
158
 * well as any entries in one of the specified section(s).
159
 *
160
 * Params:
161
 *      environment = our own cache of the program environment
162
 *      filename = name of the file being parsed
163
 *      path = what @P will expand to
164
 *      buffer = contents of configuration file
165
 *      sections = section names
166
 */
167
void parseConfFile(ref StringTable!(char*) environment, const(char)[] filename, const(char)[] path, const(ubyte)[] buffer, const(Strings)* sections)
168
{
169
    /********************
170
     * Skip spaces.
171
     */
172
    static inout(char)* skipspace(inout(char)* p)
173
    {
174 1
        while (isspace(*p))
175 0
            p++;
176 1
        return p;
177
    }
178

179
    // Parse into lines
180 1
    bool envsection = true; // default is to read
181 1
    OutBuffer buf;
182 1
    bool eof = false;
183 1
    int lineNum = 0;
184 1
    for (size_t i = 0; i < buffer.length && !eof; i++)
185
    {
186
    Lstart:
187 1
        const linestart = i;
188 1
        for (; i < buffer.length; i++)
189
        {
190 1
            switch (buffer[i])
191
            {
192 0
            case '\r':
193 0
                break;
194 1
            case '\n':
195
                // Skip if it was preceded by '\r'
196 1
                if (i && buffer[i - 1] == '\r')
197
                {
198 0
                    i++;
199 0
                    goto Lstart;
200
                }
201 1
                break;
202 0
            case 0:
203 0
            case 0x1A:
204 0
                eof = true;
205 0
                break;
206 1
            default:
207 1
                continue;
208
            }
209 1
            break;
210
        }
211 1
        ++lineNum;
212 1
        buf.setsize(0);
213
        // First, expand the macros.
214
        // Macros are bracketed by % characters.
215
    Kloop:
216 1
        for (size_t k = 0; k < i - linestart; ++k)
217
        {
218
            // The line is buffer[linestart..i]
219 1
            const line = cast(const char*)&buffer[linestart];
220 1
            if (line[k] == '%')
221
            {
222 1
                foreach (size_t j; k + 1 .. i - linestart)
223
                {
224 1
                    if (line[j] != '%')
225 1
                        continue;
226 1
                    if (j - k == 3 && Port.memicmp(&line[k + 1], "@P", 2) == 0)
227
                    {
228
                        // %@P% is special meaning the path to the .ini file
229 1
                        auto p = path;
230 1
                        if (!p.length)
231 0
                            p = ".";
232 1
                        buf.writestring(p);
233
                    }
234
                    else
235
                    {
236 0
                        auto len2 = j - k;
237 0
                        auto p = cast(char*)Mem.check(malloc(len2));
238 0
                        len2--;
239 0
                        memcpy(p, &line[k + 1], len2);
240 0
                        p[len2] = 0;
241 0
                        Port.strupr(p);
242 0
                        const penv = readFromEnv(environment, p);
243 0
                        if (penv)
244 0
                            buf.writestring(penv);
245 0
                        free(p);
246
                    }
247 1
                    k = j;
248 1
                    continue Kloop;
249
                }
250
            }
251 1
            buf.writeByte(line[k]);
252
        }
253

254
        // Remove trailing spaces
255 1
        const slice = buf[];
256 1
        auto slicelen = slice.length;
257 1
        while (slicelen && isspace(slice[slicelen - 1]))
258 0
            --slicelen;
259 1
        buf.setsize(slicelen);
260

261 1
        auto p = buf.peekChars();
262
        // The expanded line is in p.
263
        // Now parse it for meaning.
264 1
        p = skipspace(p);
265 1
        switch (*p)
266
        {
267 0
        case ';':
268
            // comment
269 1
        case 0:
270
            // blank
271 1
            break;
272 1
        case '[':
273
            // look for [Environment]
274 1
            p = skipspace(p + 1);
275 1
            char* pn;
276 1
            for (pn = p; isalnum(*pn); pn++)
277
            {
278
            }
279 1
            if (*skipspace(pn) != ']')
280
            {
281
                // malformed [sectionname], so just say we're not in a section
282 0
                envsection = false;
283 0
                break;
284
            }
285
            /* Search sectionnamev[] for p..pn and set envsection to true if it's there
286
             */
287 1
            for (size_t j = 0; 1; ++j)
288
            {
289 1
                if (j == sections.dim)
290
                {
291
                    // Didn't find it
292 1
                    envsection = false;
293 1
                    break;
294
                }
295 1
                const sectionname = (*sections)[j];
296 1
                const len = strlen(sectionname);
297 1
                if (pn - p == len && Port.memicmp(p, sectionname, len) == 0)
298
                {
299 1
                    envsection = true;
300 1
                    break;
301
                }
302
            }
303 1
            break;
304 1
        default:
305 1
            if (envsection)
306
            {
307 1
                auto pn = p;
308
                // Convert name to upper case;
309
                // remove spaces bracketing =
310 1
                for (; *p; p++)
311
                {
312 1
                    if (islower(*p))
313 0
                        *p &= ~0x20;
314 1
                    else if (isspace(*p))
315
                    {
316 0
                        memmove(p, p + 1, strlen(p));
317 0
                        p--;
318
                    }
319 1
                    else if (p[0] == '?' && p[1] == '=')
320
                    {
321 0
                        *p = '\0';
322 0
                        if (readFromEnv(environment, pn))
323
                        {
324 0
                            pn = null;
325 0
                            break;
326
                        }
327
                        // remove the '?' and resume parsing starting from
328
                        // '=' again so the regular variable format is
329
                        // parsed
330 0
                        memmove(p, p + 1, strlen(p + 1) + 1);
331 0
                        p--;
332
                    }
333 1
                    else if (*p == '=')
334
                    {
335 1
                        p++;
336 1
                        while (isspace(*p))
337 0
                            memmove(p, p + 1, strlen(p));
338 1
                        break;
339
                    }
340
                }
341 1
                if (pn)
342
                {
343 1
                    auto pns = cast(char*)Mem.check(strdup(pn));
344 1
                    if (!writeToEnv(environment, pns))
345
                    {
346 0
                        error(Loc(filename.xarraydup.ptr, lineNum, 0), "Use `NAME=value` syntax, not `%s`", pn);
347 0
                        fatal();
348
                    }
349
                    static if (LOG)
350
                    {
351
                        printf("\tputenv('%s')\n", pn);
352
                        //printf("getenv(\"TEST\") = '%s'\n",getenv("TEST"));
353
                    }
354
                }
355
            }
356 1
            break;
357
        }
358
    }
359
}

Read our documentation on viewing source code .

Loading