//===-- 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 #include #include #include #include #include #include #include #include #include #include // 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 expand(std::istream &is) { std::vector 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 response_args; // Compile a list of arguments, at the end convert them to the proper C string // form std::vector processed_args; std::list 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 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(malloc(sizeof(char *) * processed_args.size())); for (size_t i = 0; i < processed_args.size(); ++i) { pargv[i] = reinterpret_cast( 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; }