1 /**
2  * Compiler implementation of the D programming language
3  * http://dlang.org
4  * This file is not shared with other compilers which use the DMD front-end.
5  *
6  * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
7  *            Some portions copyright (c) 1994-1995 by Symantec
8  * Authors:   Walter Bright, http://www.digitalmars.com
9  * License:   This source file is made available for personal use
10  *            only. The license is in backendlicense.txt
11  *            For any other uses, please contact Digital Mars.
12  * Source:    $(DMDSRC root/_response.d)
13  */
14 
15 module ddmd.root.response;
16 
17 import core.stdc.stdio;
18 import core.stdc.stdlib;
19 import core.stdc..string;
20 import ddmd.root.file;
21 import ddmd.root.filename;
22 
23 /*********************************
24  * #include <stdlib.h>
25  * int response_expand(int *pargc,char ***pargv);
26  *
27  * Expand any response files in command line.
28  * Response files are arguments that look like:
29  *   @NAME
30  * The name is first searched for in the environment. If it is not
31  * there, it is searched for as a file name.
32  * Arguments are separated by spaces, tabs, or newlines. These can be
33  * imbedded within arguments by enclosing the argument in '' or "".
34  * Recursively expands nested response files.
35  *
36  * To use, put the line:
37  *   response_expand(&argc,&argv);
38  * as the first executable statement in main(int argc, char **argv).
39  * argc and argv are adjusted to be the new command line arguments
40  * after response file expansion.
41  *
42  * Digital Mars's MAKE program can be notified that a program can accept
43  * long command lines via environment variables by preceding the rule
44  * line for the program with a *.
45  *
46  * Returns:
47  *   0   success
48  *   !=0   failure (argc, argv unchanged)
49  */
50 bool response_expand(Strings* args)
51 {
52     const(char)* cp;
53     int recurse = 0;
54     for (size_t i = 0; i < args.dim;)
55     {
56         cp = (*args)[i];
57         if (*cp != '@')
58         {
59             ++i;
60             continue;
61         }
62         args.remove(i);
63         char* buffer;
64         char* bufend;
65         cp++;
66         if (auto p = getenv(cp))
67         {
68             buffer = strdup(p);
69             if (!buffer)
70                 goto noexpand;
71             bufend = buffer + strlen(buffer);
72         }
73         else
74         {
75             auto f = File(cp);
76             if (f.read())
77                 goto noexpand;
78             f._ref = 1;
79             buffer = cast(char*)f.buffer;
80             bufend = buffer + f.len;
81         }
82         // The logic of this should match that in setargv()
83         int comment = 0;
84         for (auto p = buffer; p < bufend; p++)
85         {
86             char* d;
87             char c, lastc;
88             ubyte instring;
89             int num_slashes, non_slashes;
90             switch (*p)
91             {
92             case 26:
93                 /* ^Z marks end of file      */
94                 goto L2;
95             case 0xD:
96             case '\n':
97                 if (comment)
98                 {
99                     comment = 0;
100                 }
101                 goto case;
102             case 0:
103             case ' ':
104             case '\t':
105                 continue;
106                 // scan to start of argument
107             case '#':
108                 comment = 1;
109                 continue;
110             case '@':
111                 if (comment)
112                 {
113                     continue;
114                 }
115                 recurse = 1;
116                 goto default;
117             default:
118                 /* start of new argument   */
119                 if (comment)
120                 {
121                     continue;
122                 }
123                 args.insert(i, p);
124                 ++i;
125                 instring = 0;
126                 c = 0;
127                 num_slashes = 0;
128                 for (d = p; 1; p++)
129                 {
130                     lastc = c;
131                     if (p >= bufend)
132                     {
133                         *d = 0;
134                         goto L2;
135                     }
136                     c = *p;
137                     switch (c)
138                     {
139                     case '"':
140                         /*
141                          Yes this looks strange,but this is so that we are
142                          MS Compatible, tests have shown that:
143                          \\\\"foo bar"  gets passed as \\foo bar
144                          \\\\foo  gets passed as \\\\foo
145                          \\\"foo gets passed as \"foo
146                          and \"foo gets passed as "foo in VC!
147                          */
148                         non_slashes = num_slashes % 2;
149                         num_slashes = num_slashes / 2;
150                         for (; num_slashes > 0; num_slashes--)
151                         {
152                             d--;
153                             *d = '\0';
154                         }
155                         if (non_slashes)
156                         {
157                             *(d - 1) = c;
158                         }
159                         else
160                         {
161                             instring ^= 1;
162                         }
163                         break;
164                     case 26:
165                         *d = 0; // terminate argument
166                         goto L2;
167                     case 0xD:
168                         // CR
169                         c = lastc;
170                         continue;
171                         // ignore
172                     case '@':
173                         recurse = 1;
174                         goto Ladd;
175                     case ' ':
176                     case '\t':
177                         if (!instring)
178                         {
179                         case '\n':
180                         case 0:
181                             *d = 0; // terminate argument
182                             goto Lnextarg;
183                         }
184                         goto default;
185                     default:
186                     Ladd:
187                         if (c == '\\')
188                             num_slashes++;
189                         else
190                             num_slashes = 0;
191                         *d++ = c;
192                         break;
193                     }
194                 }
195                 break;
196             }
197         Lnextarg:
198         }
199     L2:
200     }
201     if (recurse)
202     {
203         /* Recursively expand @filename   */
204         if (response_expand(args))
205             goto noexpand;
206     }
207     return false; /* success         */
208 noexpand:
209     /* error         */
210     /* BUG: any file buffers are not free'd   */
211     return true;
212 }