mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-02-21 14:13:20 +01:00
Replaced response_expand with BSD-licensed reimplementation.
Cleanup to follow later. GitHub: Fixes #267.
This commit is contained in:
@@ -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}"
|
||||
|
||||
@@ -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
176
driver/response.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user