Replaced response_expand with BSD-licensed reimplementation.

Cleanup to follow later.

GitHub: Fixes #267.
This commit is contained in:
David Nadlinger
2013-05-12 13:34:07 +02:00
parent fc8e0c4c20
commit db270a9a94
3 changed files with 177 additions and 318 deletions

View File

@@ -389,14 +389,10 @@ if (HAVE_SC_ARG_MAX)
add_definitions(-DHAVE_SC_ARG_MAX)
endif()
set_source_files_properties(dmd2/root/response.c dmd2/root/man.c PROPERTIES
LANGUAGE CXX
COMPILE_FLAGS "${DMD_CXXFLAGS}"
)
set_source_files_properties(driver/ldmd.cpp PROPERTIES
COMPILE_FLAGS "${LDC_CXXFLAGS}"
)
add_executable(${LDMD_EXE} dmd2/root/response.c dmd2/root/man.c driver/ldmd.cpp)
add_executable(${LDMD_EXE} dmd2/root/man.c driver/ldmd.cpp driver/response.cpp)
set_target_properties(${LDMD_EXE} PROPERTIES
COMPILE_DEFINITIONS LDC_EXE_NAME="${LDC_EXE_NAME}"
COMPILE_FLAGS "${LLVM_CXXFLAGS}"

View File

@@ -1,313 +0,0 @@
// Copyright (C) 1990-1998 by Symantec
// Copyright (C) 2000-2011 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
* This source file is made available for personal use
* only. The license is in backendlicense.txt
* For any other uses, please contact Digital Mars.
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if _WIN32
#include <tchar.h>
#include <io.h>
#endif
#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <utime.h>
#endif
#if _MSC_VER || defined(__MINGW32__)
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#endif
/*********************************
* #include <stdlib.h>
* int response_expand(int *pargc,char ***pargv);
*
* Expand any response files in command line.
* Response files are arguments that look like:
* @NAME
* The name is first searched for in the environment. If it is not
* there, it is searched for as a file name.
* Arguments are separated by spaces, tabs, or newlines. These can be
* imbedded within arguments by enclosing the argument in '' or "".
* Recursively expands nested response files.
*
* To use, put the line:
* response_expand(&argc,&argv);
* as the first executable statement in main(int argc, char **argv).
* argc and argv are adjusted to be the new command line arguments
* after response file expansion.
*
* Digital Mars's MAKE program can be notified that a program can accept
* long command lines via environment variables by preceding the rule
* line for the program with a *.
*
* Returns:
* 0 success
* !=0 failure (argc, argv unchanged)
*/
struct Narg
{
size_t argc; // arg count
size_t argvmax; // dimension of nargv[]
char **argv;
};
static int addargp(struct Narg *n, char *p)
{
/* The 2 is to always allow room for a NULL argp at the end */
if (n->argc + 2 > n->argvmax)
{
n->argvmax = n->argc + 2;
char **ap = n->argv;
ap = (char **) realloc(ap,n->argvmax * sizeof(char *));
if (!ap)
{ if (n->argv)
free(n->argv);
memset(n, 0, sizeof(*n));
return 1;
}
n->argv = ap;
}
n->argv[n->argc++] = p;
return 0;
}
int response_expand(size_t *pargc, char ***pargv)
{
struct Narg n;
char *cp;
int recurse = 0;
n.argc = 0;
n.argvmax = 0; /* dimension of n.argv[] */
n.argv = NULL;
for (size_t i = 0; i < *pargc; ++i)
{
cp = (*pargv)[i];
if (*cp == '@')
{
char *buffer;
char *bufend;
char *p;
int comment = 0;
cp++;
p = getenv(cp);
if (p)
{
buffer = strdup(p);
if (!buffer)
goto noexpand;
bufend = buffer + strlen(buffer);
}
else
{
long length;
int fd;
int nread;
size_t len;
#if __DMC__
length = filesize(cp);
#else
struct stat statbuf;
if (stat(cp, &statbuf))
goto noexpand;
length = statbuf.st_size;
#endif
if (length & 0xF0000000) /* error or file too big */
goto noexpand;
len = length;
buffer = (char *)malloc(len + 1);
if (!buffer)
goto noexpand;
bufend = &buffer[len];
/* Read file into buffer */
#if _WIN32
fd = _open(cp,O_RDONLY|O_BINARY);
#else
fd = open(cp,O_RDONLY);
#endif
if (fd == -1)
goto noexpand;
nread = read(fd,buffer,len);
close(fd);
if (nread != len)
goto noexpand;
}
// The logic of this should match that in setargv()
for (p = buffer; p < bufend; p++)
{
char *d;
char c,lastc;
unsigned char instring;
int num_slashes,non_slashes;
switch (*p)
{
case 26: /* ^Z marks end of file */
goto L2;
case 0xD:
case '\n':
if (comment)
{
comment = 0;
}
case 0:
case ' ':
case '\t':
continue; // scan to start of argument
case '#':
comment = 1;
continue;
case '@':
if (comment)
{
continue;
}
recurse = 1;
default: /* start of new argument */
if (comment)
{
continue;
}
if (addargp(&n,p))
goto noexpand;
instring = 0;
c = 0;
num_slashes = 0;
for (d = p; 1; p++)
{
lastc = c;
if (p >= bufend)
goto Lend;
c = *p;
switch (c)
{
case '"':
/*
Yes this looks strange,but this is so that we are
MS Compatible, tests have shown that:
\\\\"foo bar" gets passed as \\foo bar
\\\\foo gets passed as \\\\foo
\\\"foo gets passed as \"foo
and \"foo gets passed as "foo in VC!
*/
non_slashes = num_slashes % 2;
num_slashes = num_slashes / 2;
for (; num_slashes > 0; num_slashes--)
{
d--;
*d = '\0';
}
if (non_slashes)
{
*(d-1) = c;
}
else
{
instring ^= 1;
}
break;
case 26:
Lend:
*d = 0; // terminate argument
goto L2;
case 0xD: // CR
c = lastc;
continue; // ignore
case '@':
recurse = 1;
goto Ladd;
case ' ':
case '\t':
if (!instring)
{
case '\n':
case 0:
*d = 0; // terminate argument
goto Lnextarg;
}
default:
Ladd:
if (c == '\\')
num_slashes++;
else
num_slashes = 0;
*d++ = c;
break;
}
#ifdef _MBCS
if (_istlead (c)) {
*d++ = *++p;
if (*(d - 1) == '\0') {
d--;
goto Lnextarg;
}
}
#endif
}
break;
}
Lnextarg:
;
}
L2:
;
}
else if (addargp(&n,(*pargv)[i]))
goto noexpand;
}
if (n.argvmax == 0)
{
n.argvmax = 1;
n.argv = (char **) calloc(n.argvmax, sizeof(char *));
if (!n.argv)
return 1;
}
else
n.argv[n.argc] = NULL;
if (recurse)
{
/* Recursively expand @filename */
if (response_expand(&n.argc,&n.argv))
goto noexpand;
}
*pargc = n.argc;
*pargv = n.argv;
return 0; /* success */
noexpand: /* error */
free(n.argv);
/* BUG: any file buffers are not free'd */
return 1;
}

176
driver/response.cpp Normal file
View File

@@ -0,0 +1,176 @@
//===-- target.cpp --------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// This is an open-source reimplementation of the DMD response_expand function
// (Safety0ff/response_expand on GitHub, see LDC issue #267).
//
//===----------------------------------------------------------------------===//
#include <fstream>
#include <iterator>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
// returns true if the quote is unescaped
bool applyBackslashRule(std::string &arg) {
std::string::reverse_iterator it;
for (it = arg.rbegin(); it != arg.rend() && *it == '\\'; ++it) {}
size_t numbs = std::distance(arg.rbegin(), it);
bool escapedquote = numbs % 2;
size_t numescaped = numbs / 2 + escapedquote;
arg.resize(arg.size() - numescaped);
if (escapedquote)
arg += '"';
return !escapedquote;
}
// returns true if the end of the quote is the end of the argument
bool dealWithQuote(std::istream &is, std::string &arg) {
// first go back and deal with backslashes
if (!applyBackslashRule(arg))
return false;
// keep appending until we find a quote terminator
while (is.good()) {
char c = is.get();
switch (c) {
case '"':
if (applyBackslashRule(arg))
return false;
break;
case EOF: // new line or EOF ends quote and argument
case '\n':
return true;
case '\r': // ignore carriage returns in quotes
break;
default:
arg += c;
}
}
return true; // EOF
}
void dealWithComment(std::istream &is) {
while (is.good()) {
char c = is.get();
if (c == '\n' || c == '\r')
return; // newline, carriage return and EOF end comment
}
}
std::vector<std::string> expand(std::istream &is) {
std::vector<std::string> expanded;
std::string arg;
is >> std::ws;
while (is.good()) {
char c = is.get();
if (c == EOF) {
break;
} else if (std::isspace(c)) {
is >> std::ws;
if (!arg.empty()) {
expanded.push_back(arg);
arg.clear();
}
} else if (c == '"') {
if (dealWithQuote(is, arg) && !arg.empty()) {
expanded.push_back(arg);
arg.clear();
}
} else if (c == '#') {
dealWithComment(is);
} else {
arg += c;
while (is.good()) {
c = is.peek();
if (std::isspace(c) || c == '"' || c == EOF)
break; // N.B. comments can't be placed in the middle of an arg
else
arg += is.get();
}
}
}
if (!arg.empty())
expanded.push_back(arg);
return expanded;
}
int response_expand(size_t *pargc, char ***ppargv) {
// N.B. It is possible to create an infinite loop with response arguments
// in the original implementation, we artificially limmit re-parsing responses
const unsigned reexpand_limit = 32;
std::map<std::string, unsigned> response_args;
// Compile a list of arguments, at the end convert them to the proper C string
// form
std::vector<std::string> processed_args;
std::list<std::string> unprocessed_args;
// fill unprocessed with initial arguments
for (size_t i = 0; i < *pargc; ++i) {
unprocessed_args.push_back((*ppargv)[i]);
}
processed_args.reserve(*pargc);
while (!unprocessed_args.empty()) {
std::string arg = unprocessed_args.front();
unprocessed_args.pop_front();
if (!arg.empty() && arg[0] == '@') {
arg.erase(arg.begin()); // remove leading '@'
if (arg.empty()) {
return 1;
} else {
if (response_args.find(arg) == response_args.end())
response_args[arg] = 1;
else if (++response_args[arg] > reexpand_limit)
return 2; // We might be in an infinite loop
std::vector<std::string> expanded_args;
const char *env = getenv(arg.c_str());
if (env) {
std::string envCppStr(env);
std::istringstream ss(envCppStr);
expanded_args = expand(ss);
} else {
std::ifstream ifs(arg.c_str());
if (ifs.good())
expanded_args = expand(ifs);
else
return 3; // response not found between environment and files
}
unprocessed_args.insert(unprocessed_args.begin(), expanded_args.begin(),
expanded_args.end());
}
} else if (!arg.empty()) {
processed_args.push_back(arg);
}
}
char **pargv = (char **)malloc(sizeof(pargv) * processed_args.size());
for (size_t i = 0; i < processed_args.size(); ++i) {
pargv[i] =
(char *)malloc(sizeof(pargv[i]) * (1 + processed_args[i].length()));
strcpy(pargv[i], processed_args[i].c_str());
}
*pargc = processed_args.size();
*ppargv = pargv;
return 0;
}