/* */ #ifndef D_PARAMED_STRING_H #define D_PARAMED_STRING_H #include "common.h" #include #include #include #include "util.h" #include "DlAbortEx.h" #include "fmt.h" namespace aria2 { namespace paramed_string { template InputIterator expandChoice (std::vector& res, InputIterator first, InputIterator last) { ++first; InputIterator i = std::find(first, last, '}'); if(i == last) { throw DL_ABORT_EX("Missing '}' in the parameterized string."); } std::vector choices; util::splitIter(first, i, std::back_inserter(choices), ',', true, false); std::vector res2; res2.reserve(res.size()*choices.size()); for(std::vector::const_iterator i = res.begin(), eoi = res.end(); i != eoi; ++i) { for(std::vector::const_iterator j = choices.begin(), eoj = choices.end(); j != eoj; ++j) { res2.push_back(*i); res2.back().append((*j).first, (*j).second); } } res.swap(res2); return i+1; } template int32_t fromBase26(InputIterator first, InputIterator last, char zero) { int32_t res = 0; for(; first != last; ++first) { res *= 26; res += *first-zero; if(res > static_cast(UINT16_MAX)) { throw DL_ABORT_EX("Loop range overflow."); } } return res; } std::string toBase26(int32_t n, char zero, size_t width); template InputIterator expandLoop (std::vector& res, InputIterator first, InputIterator last) { ++first; InputIterator i = std::find(first, last, ']'); if(i == last) { throw DL_ABORT_EX("Missing ']' in the parameterized string."); } InputIterator colon = std::find(first, i, ':'); uint32_t step; if(colon == i) { step = 1; } else { if(!util::parseUIntNoThrow(step, std::string(colon+1, i))) { throw DL_ABORT_EX("A step count must be a positive number."); } if(step > UINT16_MAX) { throw DL_ABORT_EX("Loop step overflow."); } } InputIterator minus = std::find(first, colon, '-'); if(minus == colon) { throw DL_ABORT_EX("Loop range missing."); } if(util::isNumber(first, minus) && util::isNumber(minus+1, colon)) { uint32_t start, end; if(!util::parseUIntNoThrow(start, std::string(first, minus)) || !util::parseUIntNoThrow(end, std::string(minus+1, colon))) { throw DL_ABORT_EX("Loop range missing."); } if(start > UINT16_MAX || end > UINT16_MAX) { throw DL_ABORT_EX("Loop range overflow."); } if(start <= end) { std::string format; if(minus-first == colon-minus-1) { format = fmt("%%0%lud", static_cast(minus-first)); } else { format = "%d"; } std::vector res2; res2.reserve(res.size()*((end+1-start)/step)); for(std::vector::const_iterator i = res.begin(), eoi = res.end(); i != eoi; ++i) { for(uint32_t j = start; j <= end; j += step) { res2.push_back(*i); res2.back() += fmt(format.c_str(), j); } } res.swap(res2); } } else if((util::isLowercase(first, minus) && util::isLowercase(minus+1, colon)) || (util::isUppercase(first, minus) && util::isUppercase(minus+1, colon))) { char zero = ('a' <= *first && *first <= 'z' ? 'a' : 'A'); int32_t start, end; start = fromBase26(first, minus, zero); end = fromBase26(minus+1, colon, zero); if(start <= end) { size_t width; if(minus-first == colon-minus-1) { width = minus-first; } else { width = 0; } std::vector res2; res2.reserve(res.size()*((end+1-start)/step)); for(std::vector::const_iterator i = res.begin(), eoi = res.end(); i != eoi; ++i) { for(int32_t j = start; j <= end; j += step) { res2.push_back(*i); res2.back() += toBase26(j, zero, width); } } res.swap(res2); } } else { throw DL_ABORT_EX("Invalid loop range."); } return i+1; } // Expand parameterized string. // The available parameters are loop [] and choice {}. // // Loop: [START-END:STEP] // // A is arbitrary string. START and END must satisfy one of following // condition: // // * both are decimal digits and START <= END. // // * both are composed of 'a' to 'z' letter and START <= END // lexicographically. // // * both are composed of 'A' to 'Z' letter and START <= END // lexicographically. // // Leading zeros in START and END are kep preserved if the length of // START and END in string representation is equal. // // When loop through START to END, we include both START and END. // // STEP is dicimal number and it is used as loop step. STEP can be // omitted. If omitted, preceding ':' also must be omitted. // // START, END and STEP must be less than or equal to 65535 in decimal. // // Examples: // "alpha:[1-2]:bravo" -> ["alpha:1:bravo", "alpha:2:bravo"] // "alpha:[05-10:5]" -> ["alpha:05:bravo", "alpha:10:bravo"] // // Choice: {C1,C2,...,Cn} // // C1 to Cn are arbitrary string but they cannot contain ','. // // Examples: // "alpha:[foo,bar]:bravo" -> ["alpha:foo:bravo", "alpha:bar:bravo"] template void expand (InputIterator first, InputIterator last, OutputIterator out) { std::vector res; res.push_back(""); while(first != last) { InputIterator i = first; for(; i != last && *i != '{' && *i != '['; ++i); for(auto & re : res) { re.append(first, i); } first = i; if(first == last) { break; } if(*first == '{') { first = expandChoice(res, first, last); } else if(*first == '[') { first = expandLoop(res, first, last); } } if(res.size() != 1 || !res[0].empty()) { std::copy(res.begin(), res.end(), out); } } } // namespace paramed_string } // namespace aria2 #endif // D_PARAMED_STRING_H