You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
4.2 KiB
181 lines
4.2 KiB
/* |
|
* Copyright (C) 2012-2018 Daniel Scharrer |
|
* |
|
* This software is provided 'as-is', without any express or implied |
|
* warranty. In no event will the author(s) be held liable for any damages |
|
* arising from the use of this software. |
|
* |
|
* Permission is granted to anyone to use this software for any purpose, |
|
* including commercial applications, and to alter it and redistribute it |
|
* freely, subject to the following restrictions: |
|
* |
|
* 1. The origin of this software must not be misrepresented; you must not |
|
* claim that you wrote the original software. If you use this software |
|
* in a product, an acknowledgment in the product documentation would be |
|
* appreciated but is not required. |
|
* 2. Altered source versions must be plainly marked as such, and must not be |
|
* misrepresented as being the original software. |
|
* 3. This notice may not be removed or altered from any source distribution. |
|
*/ |
|
|
|
#include "setup/filename.hpp" |
|
|
|
#include <stddef.h> |
|
#include <algorithm> |
|
#include <cctype> |
|
|
|
namespace setup { |
|
|
|
namespace { |
|
|
|
//! Check for separators in input paths. |
|
struct is_path_separator { |
|
bool operator()(char c) { |
|
return (c == '\\' || c == '/'); |
|
} |
|
}; |
|
|
|
struct is_unsafe_path_char { |
|
bool operator()(char c) { |
|
if(c < 32) { |
|
return true; |
|
} |
|
switch(c) { |
|
case '<': return true; |
|
case '>': return true; |
|
case ':': return true; |
|
case '"': return true; |
|
case '|': return true; |
|
case '?': return true; |
|
case '*': return true; |
|
default: return false; |
|
} |
|
} |
|
}; |
|
|
|
std::string replace_unsafe_chars(const std::string & str) { |
|
std::string result; |
|
result.resize(str.size()); |
|
std::replace_copy_if(str.begin(), str.end(), result.begin(), is_unsafe_path_char(), '$'); |
|
return result; |
|
} |
|
|
|
} // anonymous namespace |
|
|
|
std::string filename_map::lookup(const std::string & key) const { |
|
std::map<std::string, std::string>::const_iterator i = find(key); |
|
return (i == end()) ? replace_unsafe_chars(key) : i->second; |
|
} |
|
|
|
std::string filename_map::expand_variables(it & begin, it end, bool close) const { |
|
|
|
std::string result; |
|
result.reserve(size_t(end - begin)); |
|
|
|
while(begin != end) { |
|
|
|
// Flush everything before the next bracket |
|
it pos = begin; |
|
while(pos != end && *pos != '{' && *pos != '}') { |
|
++pos; |
|
} |
|
result.append(begin, pos); |
|
begin = pos; |
|
|
|
if(pos == end) { |
|
// No more variables or escape sequences |
|
break; |
|
} |
|
|
|
begin++; |
|
|
|
if(close && *pos == '}') { |
|
// Current context closed |
|
break; |
|
} |
|
|
|
if(!close && *pos == '}') { |
|
// literal '}' character |
|
result.push_back('}'); |
|
continue; |
|
} |
|
|
|
// '{{' escape sequence |
|
if(begin != end && *begin == '{') { |
|
result.push_back('{'); |
|
begin++; |
|
continue; |
|
} |
|
|
|
// Recursively expand variables until we reach the end of this context |
|
result.append(lookup(expand_variables(begin, end, true))); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
std::string filename_map::shorten_path(const std::string & path) const { |
|
|
|
std::string result; |
|
result.reserve(path.size()); |
|
|
|
it begin = path.begin(); |
|
it end = path.end(); |
|
while(begin != end) { |
|
|
|
it s_begin = begin; |
|
it s_end = std::find_if(begin, end, is_path_separator()); |
|
begin = (s_end == end) ? end : (s_end + 1); |
|
|
|
size_t segment_length = size_t(s_end - s_begin); |
|
|
|
// Empty segment - ignore |
|
if(segment_length == 0) { |
|
continue; |
|
} |
|
|
|
// '.' segment - ignore |
|
if(segment_length == 1 && *s_begin == '.') { |
|
continue; |
|
} |
|
|
|
// '..' segment - backtrace in result path |
|
if(segment_length == 2 && *s_begin == '.' && *(s_begin + 1) == '.') { |
|
size_t last_sep = result.find_last_of(path_sep); |
|
if(last_sep == std::string::npos) { |
|
last_sep = 0; |
|
} |
|
result.resize(last_sep); |
|
continue; |
|
} |
|
|
|
// Normal segment - append to the result path |
|
if(!result.empty()) { |
|
result.push_back(path_sep); |
|
} |
|
result.append(s_begin, s_end); |
|
|
|
} |
|
|
|
return result; |
|
} |
|
|
|
std::string filename_map::convert(std::string input) const { |
|
|
|
// Convert paths to lower-case if requested |
|
if(lowercase) { |
|
std::transform(input.begin(), input.end(), input.begin(), ::tolower); |
|
} |
|
|
|
// Don't expand variables if requested |
|
if(!expand) { |
|
return input; |
|
} |
|
|
|
it begin = input.begin(); |
|
std::string expanded = expand_variables(begin, input.end()); |
|
|
|
return shorten_path(expanded); |
|
} |
|
|
|
} // namespace setup
|
|
|