1: // Copyright (C) 1990-1998 by Symantec 2: // Copyright (C) 2000-2011 by Digital Mars 3: // All Rights Reserved 4: // http://www.digitalmars.com 5: // Written by Walter Bright 6: /* 7: * This source file is made available for personal use 8: * only. The license is in /dmd/src/dmd/backendlicense.txt 9: * For any other uses, please contact Digital Mars. 10: */ 11: 12: #include <stdio.h> 13: #include <fcntl.h> 14: #include <stdlib.h> 15: #include <string.h> 16: static char __file__[] = __FILE__; /* for tassert.h */ 17: #include "tassert.h" 18: 19: #if _WIN32 20: #include <tchar.h> 21: #include <io.h> 22: #endif 23: 24: #if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 25: #include <sys/stat.h> 26: #include <sys/types.h> 27: #include <fcntl.h> 28: #include <errno.h> 29: #include <unistd.h> 30: #include <utime.h> 31: #endif 32: 33: #if _MSC_VER 34: #include <sys/stat.h> 35: #endif 36: 37: /********************************* 38: * #include <stdlib.h> 39: * int response_expand(int *pargc,char ***pargv); 40: * 41: * Expand any response files in command line. 42: * Response files are arguments that look like: 43: * @NAME 44: * The name is first searched for in the environment. If it is not 45: * there, it is searched for as a file name. 46: * Arguments are separated by spaces, tabs, or newlines. These can be 47: * imbedded within arguments by enclosing the argument in '' or "". 48: * Recursively expands nested response files. 49: * 50: * To use, put the line: 51: * response_expand(&argc,&argv); 52: * as the first executable statement in main(int argc, char **argv). 53: * argc and argv are adjusted to be the new command line arguments 54: * after response file expansion. 55: * 56: * Digital Mars's MAKE program can be notified that a program can accept 57: * long command lines via environment variables by preceding the rule 58: * line for the program with a *. 59: * 60: * Returns: 61: * 0 success 62: * !=0 failure (argc, argv unchanged) 63: */ 64: 65: struct Narg 66: { 67: int argc; /* arg count */ 68: int argvmax; /* dimension of nargv[] */ 69: char **argv; 70: }; 71: 72: static int addargp(struct Narg *n, char *p) 73: { 74: /* The 2 is to always allow room for a NULL argp at the end */ 75: if (n->argc + 2 > n->argvmax) 76: { 77: n->argvmax = n->argc + 2; 78: n->argv = (char **) realloc(n->argv,n->argvmax * sizeof(char *));warning C6308: 'realloc' might return null pointer: assigning null pointer to 'n->argv', which is passed as an argument to 'realloc', will cause the original memory block to be leaked79: if (!n->argv) 80: return 1; 81: } 82: n->argv[n->argc++] = p; 83: return 0; 84: } 85: 86: int response_expand(int *pargc, char ***pargv) 87: { 88: struct Narg n; 89: int i; 90: char *cp; 91: int recurse = 0; 92: 93: n.argc = 0; 94: n.argvmax = 0; /* dimension of n.argv[] */ 95: n.argv = NULL; 96: for(i=0; i<*pargc; ++i) 97: { 98: cp = (*pargv)[i]; 99: if (*cp == '@') 100: { 101: char *buffer; 102: char *bufend; 103: char *p; 104: 105: cp++; 106: p = getenv(cp);warning C4996: 'getenv': This function or variable may be unsafe. Consider using _dupenv_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\stdlib.h(433) : see declaration of 'getenv'107: if (p) 108: { 109: buffer = strdup(p);warning C4996: 'strdup': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _strdup. See online help for details. c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\string.h(238) : see declaration of 'strdup'110: if (!buffer) 111: goto noexpand; 112: bufend = buffer + strlen(buffer); 113: } 114: else 115: { 116: long length; 117: int fd; 118: int nread; 119: size_t len; 120: 121: #if __DMC__ 122: length = filesize(cp); 123: #else 124: struct stat statbuf; 125: if (stat(cp, &statbuf)) 126: goto noexpand; 127: length = statbuf.st_size; 128: #endif 129: if (length & 0xF0000000) /* error or file too big */ 130: goto noexpand; 131: len = length; 132: buffer = (char *)malloc(len + 1); 133: if (!buffer) 134: goto noexpand; 135: bufend = &buffer[len]; 136: /* Read file into buffer */ 137: #if _WIN32 138: fd = open(cp,O_RDONLY|O_BINARY);warning C4996: 'open': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _open. See online help for details. c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\io.h(312) : see declaration of 'open'139: #else 140: fd = open(cp,O_RDONLY); 141: #endif 142: if (fd == -1) 143: goto noexpand; 144: nread = read(fd,buffer,len);warning C4996: 'read': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _read. See online help for details. c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\io.h(313) : see declaration of 'read'145: close(fd);warning C4996: 'close': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _close. See online help for details. c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\io.h(302) : see declaration of 'close'146: 147: if (nread != len) 148: goto noexpand; 149: } 150: 151: // The logic of this should match that in setargv() 152: 153: for (p = buffer; p < bufend; p++) 154: { 155: char *d; 156: char c,lastc; 157: unsigned char instring; 158: int num_slashes,non_slashes; 159: 160: switch (*p) 161: { 162: case 26: /* ^Z marks end of file */ 163: goto L2; 164: 165: case 0xD: 166: case 0: 167: case ' ': 168: case '\t': 169: case '\n': 170: continue; // scan to start of argument 171: 172: case '@': 173: recurse = 1; 174: default: /* start of new argument */ 175: if (addargp(&n,p)) 176: goto noexpand; 177: instring = 0; 178: c = 0; 179: num_slashes = 0; 180: for (d = p; 1; p++) 181: { 182: lastc = c; 183: if (p >= bufend) 184: goto Lend; 185: c = *p; 186: switch (c) 187: { 188: case '"': 189: /* 190: Yes this looks strange,but this is so that we are 191: MS Compatible, tests have shown that: 192: \\\\"foo bar" gets passed as \\foo bar 193: \\\\foo gets passed as \\\\foo 194: \\\"foo gets passed as \"foo 195: and \"foo gets passed as "foo in VC! 196: */ 197: non_slashes = num_slashes % 2; 198: num_slashes = num_slashes / 2; 199: for (; num_slashes > 0; num_slashes--) 200: { 201: d--; 202: *d = '\0'; 203: } 204: 205: if (non_slashes) 206: { 207: *(d-1) = c; 208: } 209: else 210: { 211: instring ^= 1; 212: } 213: break; 214: case 26: 215: Lend: 216: *d = 0; // terminate argument 217: goto L2; 218: 219: case 0xD: // CR 220: c = lastc; 221: continue; // ignore 222: 223: case '@': 224: recurse = 1; 225: goto Ladd; 226: 227: case ' ': 228: case '\t': 229: if (!instring) 230: { 231: case '\n': 232: case 0: 233: *d = 0; // terminate argument 234: goto Lnextarg; 235: } 236: default: 237: Ladd: 238: if (c == '\\') 239: num_slashes++; 240: else 241: num_slashes = 0; 242: *d++ = c; 243: break; 244: } 245: #ifdef _MBCS 246: if (_istlead (c)) { 247: *d++ = *++p; 248: if (*(d - 1) == '\0') { 249: d--; 250: goto Lnextarg; 251: } 252: } 253: #endif 254: } 255: break; 256: } 257: Lnextarg: 258: ; 259: } 260: L2: 261: ; 262: } 263: else if (addargp(&n,(*pargv)[i])) 264: goto noexpand; 265: } 266: if (n.argvmax == 0) 267: { 268: n.argvmax = 1; 269: n.argv = (char **) calloc(n.argvmax, sizeof(char *)); 270: if (!n.argv) 271: return 1; 272: } 273: else 274: n.argv[n.argc] = NULL; 275: if (recurse) 276: { 277: /* Recursively expand @filename */ 278: if (response_expand(&n.argc,&n.argv)) 279: goto noexpand; 280: } 281: *pargc = n.argc; 282: *pargv = n.argv; 283: return 0; /* success */ 284: 285: noexpand: /* error */ 286: free(n.argv); 287: /* BUG: any file buffers are not free'd */ 288: return 1; 289: } 290: