"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/dmd/dmd/root/response.d" (20 Nov 2020, 10852 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /**
    2  * Parse command line arguments from response files.
    3  *
    4  * This file is not shared with other compilers which use the DMD front-end.
    5  *
    6  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
    7  *              Some portions copyright (c) 1994-1995 by Symantec
    8  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
    9  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
   10  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/response.d, root/_response.d)
   11  * Documentation:  https://dlang.org/phobos/dmd_root_response.html
   12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/response.d
   13  */
   14 
   15 module dmd.root.response;
   16 
   17 import dmd.root.file;
   18 import dmd.root.filename;
   19 
   20 ///
   21 alias responseExpand = responseExpandFrom!lookupInEnvironment;
   22 
   23 /*********************************
   24  * Expand any response files in command line.
   25  * Response files are arguments that look like:
   26  *   @NAME
   27  * The names are resolved by calling the 'lookup' function passed as a template
   28  * parameter. That function is expected to first check the environment and then
   29  * the file system.
   30  * Arguments are separated by spaces, tabs, or newlines. These can be
   31  * imbedded within arguments by enclosing the argument in "".
   32  * Backslashes can be used to escape a ".
   33  * A line comment can be started with #.
   34  * Recursively expands nested response files.
   35  *
   36  * To use, put the arguments in a Strings object and call this on it.
   37  *
   38  * Digital Mars's MAKE program can be notified that a program can accept
   39  * long command lines via environment variables by preceding the rule
   40  * line for the program with a *.
   41  *
   42  * Params:
   43  *     lookup = alias to a function that is called to look up response file
   44  *              arguments in the environment. It is expected to accept a null-
   45  *              terminated string and return a mutable char[] that ends with
   46  *              a null-terminator or null if the response file could not be
   47  *              resolved.
   48  *     args = array containing arguments as null-terminated strings
   49  *
   50  * Returns:
   51  *     true on success, false if a response file could not be expanded.
   52  */
   53 bool responseExpandFrom(alias lookup)(ref Strings args) nothrow
   54 {
   55     const(char)* cp;
   56     bool recurse = false;
   57 
   58     // i is updated by insertArgumentsFromResponse, so no foreach
   59     for (size_t i = 0; i < args.dim;)
   60     {
   61         cp = args[i];
   62         if (cp[0] != '@')
   63         {
   64             ++i;
   65             continue;
   66         }
   67         args.remove(i);
   68         auto buffer = lookup(&cp[1]);
   69         if (!buffer) {
   70             /* error         */
   71             /* BUG: any file buffers are not free'd   */
   72             return false;
   73         }
   74 
   75         recurse = insertArgumentsFromResponse(buffer, args, i) || recurse;
   76     }
   77     if (recurse)
   78     {
   79         /* Recursively expand @filename   */
   80         if (!responseExpandFrom!lookup(args))
   81             /* error         */
   82             /* BUG: any file buffers are not free'd   */
   83             return false;
   84     }
   85     return true; /* success         */
   86 }
   87 
   88 version (unittest)
   89 {
   90     char[] testEnvironment(const(char)* str) nothrow pure
   91         {
   92         import core.stdc.string: strlen;
   93         import dmd.root.string : toDString;
   94         switch (str.toDString())
   95         {
   96         case "Foo":
   97             return "foo @Bar #\0".dup;
   98         case "Bar":
   99             return "bar @Nil\0".dup;
  100         case "Error":
  101             return "@phony\0".dup;
  102         case "Nil":
  103             return "\0".dup;
  104         default:
  105             return null;
  106         }
  107     }
  108 }
  109 
  110 unittest
  111 {
  112     auto args = Strings(4);
  113     args[0] = "first";
  114     args[1] = "@Foo";
  115     args[2] = "@Bar";
  116     args[3] = "last";
  117 
  118     assert(responseExpand!testEnvironment(args));
  119     assert(args.length == 5);
  120     assert(args[0][0 .. 6] == "first\0");
  121     assert(args[1][0 .. 4] == "foo\0");
  122     assert(args[2][0 .. 4] == "bar\0");
  123     assert(args[3][0 .. 4] == "bar\0");
  124     assert(args[4][0 .. 5] == "last\0");
  125 }
  126 
  127 unittest
  128 {
  129     auto args = Strings(2);
  130     args[0] = "@phony";
  131     args[1] = "dummy";
  132     assert(!responseExpand!testEnvironment(args));
  133 }
  134 
  135 unittest
  136 {
  137     auto args = Strings(2);
  138     args[0] = "@Foo";
  139     args[1] = "@Error";
  140     assert(!responseExpand!testEnvironment(args));
  141 }
  142 
  143 /*********************************
  144  * Take the contents of a response-file 'buffer', parse it and put the resulting
  145  * arguments in 'args' at 'argIndex'. 'argIndex' will be updated to point just
  146  * after the inserted arguments.
  147  * The logic of this should match that in setargv()
  148  *
  149  * Params:
  150  *     buffer = mutable string containing the response file
  151  *     args = list of arguments
  152  *     argIndex = position in 'args' where response arguments are inserted
  153  *
  154  * Returns:
  155  *     true if another response argument was found
  156  */
  157 bool insertArgumentsFromResponse(char[] buffer, ref Strings args, ref size_t argIndex) nothrow
  158 {
  159     bool recurse = false;
  160     bool comment = false;
  161 
  162     for (size_t p = 0; p < buffer.length; p++)
  163     {
  164         //char* d;
  165         size_t d = 0;
  166         char c, lastc;
  167         bool instring;
  168         int numSlashes, nonSlashes;
  169         switch (buffer[p])
  170         {
  171         case 26:
  172             /* ^Z marks end of file      */
  173             return recurse;
  174         case '\r':
  175         case '\n':
  176             comment = false;
  177             goto case;
  178         case 0:
  179         case ' ':
  180         case '\t':
  181             continue;
  182             // scan to start of argument
  183         case '#':
  184             comment = true;
  185             continue;
  186         case '@':
  187             if (comment)
  188             {
  189                 continue;
  190             }
  191             recurse = true;
  192             goto default;
  193         default:
  194             /* start of new argument   */
  195             if (comment)
  196             {
  197                 continue;
  198             }
  199             args.insert(argIndex, &buffer[p]);
  200             ++argIndex;
  201             instring = false;
  202             c = 0;
  203             numSlashes = 0;
  204             for (d = p; 1; p++)
  205             {
  206                 lastc = c;
  207                 if (p >= buffer.length)
  208                 {
  209                     buffer[d] = '\0';
  210                     return recurse;
  211                 }
  212                 c = buffer[p];
  213                 switch (c)
  214                 {
  215                 case '"':
  216                     /*
  217                     Yes this looks strange,but this is so that we are
  218                     MS Compatible, tests have shown that:
  219                     \\\\"foo bar"  gets passed as \\foo bar
  220                     \\\\foo  gets passed as \\\\foo
  221                     \\\"foo gets passed as \"foo
  222                     and \"foo gets passed as "foo in VC!
  223                     */
  224                     nonSlashes = numSlashes % 2;
  225                     numSlashes = numSlashes / 2;
  226                     for (; numSlashes > 0; numSlashes--)
  227                     {
  228                         d--;
  229                         buffer[d] = '\0';
  230                     }
  231                     if (nonSlashes)
  232                     {
  233                         buffer[d - 1] = c;
  234                     }
  235                     else
  236                     {
  237                         instring = !instring;
  238                     }
  239                     break;
  240                 case 26:
  241                     buffer[d] = '\0'; // terminate argument
  242                     return recurse;
  243                 case '\r':
  244                     c = lastc;
  245                     continue;
  246                     // ignore
  247                 case ' ':
  248                 case '\t':
  249                     if (!instring)
  250                     {
  251                     case '\n':
  252                     case 0:
  253                         buffer[d] = '\0'; // terminate argument
  254                         goto Lnextarg;
  255                     }
  256                     goto default;
  257                 default:
  258                     if (c == '\\')
  259                         numSlashes++;
  260                     else
  261                         numSlashes = 0;
  262                     buffer[d++] = c;
  263                     break;
  264                 }
  265             }
  266         }
  267     Lnextarg:
  268     }
  269     return recurse;
  270 }
  271 
  272 unittest
  273 {
  274     auto args = Strings(4);
  275     args[0] = "arg0";
  276     args[1] = "arg1";
  277     args[2] = "arg2";
  278 
  279     char[] testData = "".dup;
  280     size_t index = 1;
  281     assert(insertArgumentsFromResponse(testData, args, index) == false);
  282     assert(index == 1);
  283 
  284     testData = (`\\\\"foo bar" \\\\foo \\\"foo \"foo "\"" # @comment`~'\0').dup;
  285     assert(insertArgumentsFromResponse(testData, args, index) == false);
  286     assert(index == 6);
  287 
  288     assert(args[1][0 .. 9] == `\\foo bar`);
  289     assert(args[2][0 .. 7] == `\\\\foo`);
  290     assert(args[3][0 .. 5] == `\"foo`);
  291     assert(args[4][0 .. 4] == `"foo`);
  292     assert(args[5][0 .. 1] == `"`);
  293 
  294     index = 7;
  295     testData = "\t@recurse # comment\r\ntab\t\"@recurse\"\x1A after end\0".dup;
  296     assert(insertArgumentsFromResponse(testData, args, index) == true);
  297     assert(index == 10);
  298     assert(args[7][0 .. 8] == "@recurse");
  299     assert(args[8][0 .. 3] == "tab");
  300     assert(args[9][0 .. 8] == "@recurse");
  301 }
  302 
  303 unittest
  304 {
  305     auto args = Strings(0);
  306 
  307     char[] testData = "\x1A".dup;
  308     size_t index = 0;
  309     assert(insertArgumentsFromResponse(testData, args, index) == false);
  310     assert(index == 0);
  311 
  312     testData = "@\r".dup;
  313     assert(insertArgumentsFromResponse(testData, args, index) == true);
  314     assert(index == 1);
  315     assert(args[0][0 .. 2] == "@\0");
  316 
  317     testData = "ä&#\0".dup;
  318     assert(insertArgumentsFromResponse(testData, args, index) == false);
  319     assert(index == 2);
  320     assert(args[1][0 .. 5] == "ä&#\0");
  321 
  322     testData = "one@\"word \0".dup;
  323     assert(insertArgumentsFromResponse(testData, args, index) == false);
  324     args[0] = "one@\"word";
  325 }
  326 
  327 /*********************************
  328  * Try to resolve the null-terminated string cp to a null-terminated char[].
  329  *
  330  * The name is first searched for in the environment. If it is not
  331  * there, it is searched for as a file name.
  332  *
  333  * Params:
  334  *     cp = null-terminated string to look resolve
  335  *
  336  * Returns:
  337  *     a mutable, manually allocated array containing the contents of the environment
  338  *     variable or file, ending with a null-terminator.
  339  *     The null-terminator is inside the bounds of the array.
  340  *     If cp could not be resolved, null is returned.
  341  */
  342 private char[] lookupInEnvironment(scope const(char)* cp) nothrow {
  343 
  344     import core.stdc.stdlib: getenv;
  345     import core.stdc.string: strlen;
  346     import dmd.root.rmem: mem;
  347 
  348     if (auto p = getenv(cp))
  349     {
  350         char* buffer = mem.xstrdup(p);
  351         return buffer[0 .. strlen(buffer) + 1]; // include null-terminator
  352     }
  353     else
  354     {
  355         auto readResult = File.read(cp);
  356         if (!readResult.success)
  357             return null;
  358         // take ownership of buffer (leaking)
  359         return cast(char[]) readResult.extractDataZ();
  360     }
  361 }