1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
//===-- response.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 "driver/args.h"
#include <fstream>
#include <iterator>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include <cctype>
// 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()) {
auto 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()) {
auto 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;
}
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;
std::string env = env::get(arg.c_str());
if (!env.empty()) {
std::istringstream ss(env);
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 =
reinterpret_cast<char **>(malloc(sizeof(char *) * processed_args.size()));
for (size_t i = 0; i < processed_args.size(); ++i) {
pargv[i] = reinterpret_cast<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;
}
|