/* */ #include "Util.h" #include "File.h" #include "message.h" #include "Randomizer.h" #include "a2netcompat.h" #include "DlAbortEx.h" #include "BitfieldMan.h" #include "DefaultDiskWriter.h" #include "FatalException.h" #include "FileEntry.h" #include #include #include #include #include #include #include #ifndef HAVE_SLEEP # ifdef HAVE_WINSOCK_H # define WIN32_LEAN_AND_MEAN # include # endif // HAVE_WINSOCK_H #endif // HAVE_SLEEP namespace aria2 { std::string Util::trim(const std::string& src, const std::string& trimCharset) { std::string::size_type sp = src.find_first_not_of(trimCharset); std::string::size_type ep = src.find_last_not_of(trimCharset); if(sp == std::string::npos || ep == std::string::npos) { return ""; } else { return src.substr(sp, ep-sp+1); } } void Util::split(std::pair& hp, const std::string& src, char delim) { hp.first = ""; hp.second = ""; std::string::size_type p = src.find(delim); if(p == std::string::npos) { hp.first = src; hp.second = ""; } else { hp.first = trim(src.substr(0, p)); hp.second = trim(src.substr(p+1)); } } std::pair Util::split(const std::string& src, const std::string& delims) { std::pair hp; hp.first = ""; hp.second = ""; std::string::size_type p = src.find_first_of(delims); if(p == std::string::npos) { hp.first = src; hp.second = ""; } else { hp.first = trim(src.substr(0, p)); hp.second = trim(src.substr(p+1)); } return hp; } int64_t Util::difftv(struct timeval tv1, struct timeval tv2) { if((tv1.tv_sec < tv2.tv_sec) || ((tv1.tv_sec == tv2.tv_sec) && (tv1.tv_usec < tv2.tv_usec))) { return 0; } return ((int64_t)(tv1.tv_sec-tv2.tv_sec)*1000000+ tv1.tv_usec-tv2.tv_usec); } int32_t Util::difftvsec(struct timeval tv1, struct timeval tv2) { if(tv1.tv_sec < tv2.tv_sec) { return 0; } return tv1.tv_sec-tv2.tv_sec; } void Util::slice(std::deque& result, const std::string& src, char delim, bool doTrim) { std::string::size_type p = 0; while(1) { std::string::size_type np = src.find(delim, p); if(np == std::string::npos) { std::string term = src.substr(p); if(doTrim) { term = trim(term); } if(term.size()) { result.push_back(term); } break; } std::string term = src.substr(p, np-p); if(doTrim) { term = trim(term); } p = np+1; if(term.size()) { result.push_back(term); } } } bool Util::startsWith(const std::string& target, const std::string& part) { if(target.size() < part.size()) { return false; } if(part == "") { return true; } if(target.find(part) == 0) { return true; } else { return false; } } bool Util::endsWith(const std::string& target, const std::string& part) { if(target.size() < part.size()) { return false; } if(part == "") { return true; } if(target.rfind(part) == target.size()-part.size()) { return true; } else { return false; } } std::string Util::replace(const std::string& target, const std::string& oldstr, const std::string& newstr) { if(target == "" || oldstr == "" ) { return target; } std::string result; std::string::size_type p = 0; std::string::size_type np = target.find(oldstr); while(np != std::string::npos) { result += target.substr(p, np-p)+newstr; p = np+oldstr.size(); np = target.find(oldstr, p); } result += target.substr(p); return result; } bool Util::shouldUrlencode(const char c) { return !(// ALPHA ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || // DIGIT ('0' <= c && c <= '9') || // safe '$' == c || '-' == c || '_' == c || '.' == c || // extra '!' == c || '*' == c || '\'' == c ||'(' == c || ')' == c || ',' == c || // reserved ';' == c || '/' == c || '?' == c || ':' == c || '@' == c || '&' == c || '=' == c || '+' == c); } std::string Util::urlencode(const unsigned char* target, size_t len) { std::string dest; for(size_t i = 0; i < len; i++) { if(shouldUrlencode(target[i])) { char temp[4]; sprintf(temp, "%%%02x", target[i]); temp[sizeof(temp)-1] = '\0'; dest.append(temp); } else { dest += target[i]; } } return dest; } std::string Util::torrentUrlencode(const unsigned char* target, size_t len) { std::string dest; for(size_t i = 0; i < len; i++) { if(('0' <= target[i] && target[i] <= '9') || ('A' <= target[i] && target[i] <= 'Z') || ('a' <= target[i] && target[i] <= 'z')) { dest += target[i]; } else { char temp[4]; sprintf(temp, "%%%02x", target[i]); temp[sizeof(temp)-1] = '\0'; dest.append(temp); } } return dest; } std::string Util::urldecode(const std::string& target) { std::string result; for(std::string::const_iterator itr = target.begin(); itr != target.end(); itr++) { if(*itr == '%') { if(itr+1 != target.end() && itr+2 != target.end() && isxdigit(*(itr+1)) && isxdigit(*(itr+2))) { result += Util::parseInt(std::string(itr+1, itr+3), 16); itr += 2; } else { result += *itr; } } else { result += *itr; } } return result; } std::string Util::toHex(const unsigned char* src, size_t len) { char* temp = new char[len*2+1]; for(size_t i = 0; i < len; i++) { sprintf(temp+i*2, "%02x", src[i]); } temp[len*2] = '\0'; std::string hex = temp; delete [] temp; return hex; } FILE* Util::openFile(const std::string& filename, const std::string& mode) { FILE* file = fopen(filename.c_str(), mode.c_str()); return file; } void Util::fileCopy(const std::string& dest, const std::string& src) { File file(src); rangedFileCopy(dest, src, 0, file.size()); } void Util::rangedFileCopy(const std::string& dest, const std::string& src, off_t srcOffset, uint64_t length) { size_t bufSize = 4096; unsigned char buf[bufSize]; DefaultDiskWriter srcdw; DefaultDiskWriter destdw; srcdw.openExistingFile(src); destdw.initAndOpenFile(dest); lldiv_t res = lldiv(length, bufSize); unsigned int x = res.quot; unsigned int r = res.rem; off_t initialOffset = srcOffset; for(unsigned int i = 0; i < x; ++i) { size_t readLength = 0; while(readLength < bufSize) { ssize_t ret = srcdw.readData(buf, bufSize-readLength, srcOffset); destdw.writeData(buf, ret, srcOffset-initialOffset); srcOffset += ret; readLength += ret; } } if(r > 0) { size_t readLength = 0; while(readLength < r) { ssize_t ret = srcdw.readData(buf, r-readLength, srcOffset); destdw.writeData(buf, ret, srcOffset-initialOffset); srcOffset += ret; readLength += ret; } } } bool Util::isPowerOf(int num, int base) { if(base <= 0) { return false; } if(base == 1) { return true; } while(num%base == 0) { num /= base; if(num == 1) { return true; } } return false; } std::string Util::secfmt(time_t sec) { std::string str; if(sec >= 3600) { str = itos(sec/3600)+"h"; sec %= 3600; } if(sec >= 60) { int min = sec/60; if(min < 10) { str += "0"; } str += itos(min)+"m"; sec %= 60; } if(sec < 10) { str += "0"; } str += itos(sec)+"s"; return str; } size_t Util::expandBuffer(char** pbuf, size_t curLength, size_t newLength) { char* newbuf = new char[newLength]; memcpy(newbuf, *pbuf, curLength); delete [] *pbuf; *pbuf = newbuf; return newLength; } int getNum(const char* buf, int offset, size_t length) { char* temp = new char[length+1]; memcpy(temp, buf+offset, length); temp[length] = '\0'; int x = strtol(temp, 0, 10); delete [] temp; return x; } void unfoldSubRange(const std::string& src, std::deque& range) { if(src.empty()) { return; } std::string::size_type p = src.find_first_of(",-"); if(p == 0) { return; } else if(p == std::string::npos) { range.push_back(atoi(src.c_str())); } else { if(src.at(p) == ',') { int num = getNum(src.c_str(), 0, p); range.push_back(num); unfoldSubRange(src.substr(p+1), range); } else if(src.at(p) == '-') { std::string::size_type rightNumBegin = p+1; std::string::size_type nextDelim = src.find_first_of(",", rightNumBegin); if(nextDelim == std::string::npos) { nextDelim = src.size(); } int left = getNum(src.c_str(), 0, p); int right = getNum(src.c_str(), rightNumBegin, nextDelim-rightNumBegin); for(int i = left; i <= right; i++) { range.push_back(i); } if(src.size() > nextDelim) { unfoldSubRange(src.substr(nextDelim+1), range); } } } } void Util::unfoldRange(const std::string& src, std::deque& range) { unfoldSubRange(src, range); std::sort(range.begin(), range.end()); range.erase(std::unique(range.begin(), range.end()), range.end()); } int32_t Util::parseInt(const std::string& s, int32_t base) { std::string trimed = Util::trim(s); if(trimed.empty()) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, "empty string"); } char* stop; errno = 0; long int v = strtol(trimed.c_str(), &stop, base); if(*stop != '\0') { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } else if((((v == LONG_MIN) || (v == LONG_MAX)) && (errno == ERANGE)) || (v > INT32_MAX) || (v < INT32_MIN)) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } return v; } uint32_t Util::parseUInt(const std::string& s, int base) { std::string trimed = Util::trim(s); if(trimed.empty()) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, "empty string"); } // We don't allow negative number. if(trimed[0] == '-') { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } char* stop; errno = 0; unsigned long int v = strtoul(trimed.c_str(), &stop, base); if(*stop != '\0') { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } else if(((v == ULONG_MAX) && (errno == ERANGE)) || (v > UINT32_MAX)) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } return v; } int64_t Util::parseLLInt(const std::string& s, int32_t base) { std::string trimed = Util::trim(s); if(trimed.empty()) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, "empty string"); } char* stop; errno = 0; int64_t v = strtoll(trimed.c_str(), &stop, base); if(*stop != '\0') { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } else if(((v == INT64_MIN) || (v == INT64_MAX)) && (errno == ERANGE)) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } return v; } uint64_t Util::parseULLInt(const std::string& s, int base) { std::string trimed = Util::trim(s); if(trimed.empty()) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, "empty string"); } // We don't allow negative number. if(trimed[0] == '-') { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } char* stop; errno = 0; uint64_t v = strtoull(trimed.c_str(), &stop, base); if(*stop != '\0') { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } else if((v == ULLONG_MAX) && (errno == ERANGE)) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, trimed.c_str()); } return v; } IntSequence Util::parseIntRange(const std::string& src) { IntSequence::Values values; std::string temp = src; while(temp.size()) { std::pair p = Util::split(temp, ","); temp = p.second; if(p.first.empty()) { continue; } if(p.first.find("-") == std::string::npos) { int32_t v = Util::parseInt(p.first.c_str()); values.push_back(IntSequence::Value(v, v+1)); } else { std::pair vp = Util::split(p.first.c_str(), "-"); if(vp.first.empty() || vp.second.empty()) { throw new DlAbortEx(MSG_INCOMPLETE_RANGE, p.first.c_str()); } int32_t v1 = Util::parseInt(vp.first.c_str()); int32_t v2 = Util::parseInt(vp.second.c_str()); values.push_back(IntSequence::Value(v1, v2+1)); } } return values; } std::string Util::getContentDispositionFilename(const std::string& header) { std::string keyName = "filename="; std::string::size_type attributesp = header.find(keyName); if(attributesp == std::string::npos) { return ""; } std::string::size_type filenamesp = attributesp+strlen(keyName.c_str()); std::string::size_type filenameep; if(filenamesp == header.size()) { return ""; } if(header[filenamesp] == '\'' || header[filenamesp] == '"') { char quoteChar = header[filenamesp]; filenameep = header.find(quoteChar, filenamesp+1); } else { filenameep = header.find(';', filenamesp); } if(filenameep == std::string::npos) { filenameep = header.size(); } return trim(header.substr(filenamesp, filenameep-filenamesp), "\r\n '\""); } static int nbits[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, }; unsigned int Util::countBit(uint32_t n) { return nbits[n&0xffu]+ nbits[(n >> 8)&0xffu]+ nbits[(n >> 16)&0xffu]+ nbits[(n >> 24)&0xffu]; } std::string Util::randomAlpha(size_t length, const RandomizerHandle& randomizer) { static const char *random_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; std::string str; for(size_t i = 0; i < length; i++) { size_t index = randomizer->getRandomNumber(strlen(random_chars)); str += random_chars[index]; } return str; } std::string Util::toUpper(const std::string& src) { std::string temp = src; std::transform(temp.begin(), temp.end(), temp.begin(), ::toupper); return temp; } std::string Util::toLower(const std::string& src) { std::string temp = src; std::transform(temp.begin(), temp.end(), temp.begin(), ::tolower); return temp; } bool Util::isNumbersAndDotsNotation(const std::string& name) { struct sockaddr_in sockaddr; if(inet_aton(name.c_str(), &sockaddr.sin_addr)) { return true; } else { return false; } } void Util::setGlobalSignalHandler(int sig, void (*handler)(int), int flags) { #ifdef HAVE_SIGACTION struct sigaction sigact; sigact.sa_handler = handler; sigact.sa_flags = flags; sigemptyset(&sigact.sa_mask); sigaction(sig, &sigact, NULL); #else signal(sig, handler); #endif // HAVE_SIGACTION } void Util::indexRange(size_t& startIndex, size_t& endIndex, off_t offset, size_t srcLength, size_t destLength) { int64_t _startIndex = offset/destLength; int64_t _endIndex = (offset+srcLength-1)/destLength; assert(_startIndex <= INT32_MAX); assert(_endIndex <= INT32_MAX); startIndex = _startIndex; endIndex = _endIndex; } std::string Util::getHomeDir() { const char* p = getenv("HOME"); if(p) { return p; } else { return ""; } } int64_t Util::getRealSize(const std::string& sizeWithUnit) { std::string::size_type p = sizeWithUnit.find_first_of("KM"); std::string size; int32_t mult = 1; if(p == std::string::npos) { size = sizeWithUnit; } else { if(sizeWithUnit[p] == 'K') { mult = 1024; } else if(sizeWithUnit[p] == 'M') { mult = 1024*1024; } size = sizeWithUnit.substr(0, p); } int64_t v = Util::parseLLInt(size); if(v < 0) { throw new DlAbortEx("Negative value detected: %s", sizeWithUnit.c_str()); } else if(INT64_MAX/mult < v) { throw new DlAbortEx(MSG_STRING_INTEGER_CONVERSION_FAILURE, "overflow/underflow"); } return v*mult; } std::string Util::abbrevSize(int64_t size) { if(size < 1024) { return Util::itos(size, true); } char units[] = { 'K', 'M' }; size_t numUnit = sizeof(units)/sizeof(char); size_t i = 0; int r = size&0x3ff; size >>= 10; for(; i < numUnit-1 && size >= 1024; ++i) { r = size&0x3ff; size >>= 10; } return Util::itos(size, true)+"."+Util::itos(r*10/1024)+units[i]+"i"; } time_t Util::httpGMT(const std::string& httpStdTime) { struct tm tm; memset(&tm, 0, sizeof(tm)); strptime(httpStdTime.c_str(), "%a, %Y-%m-%d %H:%M:%S GMT", &tm); time_t thetime = timegm(&tm); return thetime; } void Util::toStream(std::ostream& os, const FileEntries& fileEntries) { os << _("Files:") << "\n"; os << "idx|path/length" << "\n"; os << "===+===========================================================================" << "\n"; int32_t count = 1; for(FileEntries::const_iterator itr = fileEntries.begin(); itr != fileEntries.end(); count++, itr++) { os << std::setw(3) << count << "|" << (*itr)->getPath() << "\n"; os << " |" << Util::abbrevSize((*itr)->getLength()) << "B" << "\n"; os << "---+---------------------------------------------------------------------------" << "\n"; } } void Util::sleep(long seconds) { #ifdef HAVE_SLEEP ::sleep(seconds); #elif defined(HAVE_USLEEP) ::usleep(seconds * 1000000); #elif defined(HAVE_WINSOCK2_H) ::Sleep(seconds * 1000); #else #error no sleep function is available (nanosleep?) #endif } void Util::usleep(long microseconds) { #ifdef HAVE_USLEEP ::usleep(microseconds); #elif defined(HAVE_WINSOCK2_H) LARGE_INTEGER current, freq, end; static enum {GET_FREQUENCY, GET_MICROSECONDS, SKIP_MICROSECONDS} state = GET_FREQUENCY; if (state == GET_FREQUENCY) { if (QueryPerformanceFrequency(&freq)) state = GET_MICROSECONDS; else state = SKIP_MICROSECONDS; } long msec = microseconds / 1000; microseconds %= 1000; if (state == GET_MICROSECONDS && microseconds) { QueryPerformanceCounter(&end); end.QuadPart += (freq.QuadPart * microseconds) / 1000000; while (QueryPerformanceCounter(¤t) && (current.QuadPart <= end.QuadPart)) /* noop */ ; } if (msec) Sleep(msec); #else #error no usleep function is available (nanosleep?) #endif } bool Util::isNumber(const std::string& what) { if(what.empty()) { return false; } for(uint32_t i = 0; i < what.size(); ++i) { if(!isdigit(what[i])) { return false; } } return true; } bool Util::isLowercase(const std::string& what) { if(what.empty()) { return false; } for(uint32_t i = 0; i < what.size(); ++i) { if(!('a' <= what[i] && what[i] <= 'z')) { return false; } } return true; } bool Util::isUppercase(const std::string& what) { if(what.empty()) { return false; } for(uint32_t i = 0; i < what.size(); ++i) { if(!('A' <= what[i] && what[i] <= 'Z')) { return false; } } return true; } unsigned int Util::alphaToNum(const std::string& alphabets) { if(alphabets.empty()) { return 0; } char base; if(islower(alphabets[0])) { base = 'a'; } else { base = 'A'; } uint64_t num = 0; for(size_t i = 0; i < alphabets.size(); ++i) { unsigned int v = alphabets[i]-base; num = num*26+v; if(num > UINT32_MAX) { return 0; } } return num; } void Util::mkdirs(const std::string& dirpath) { File dir(dirpath); if(dir.isDir()) { // do nothing } else if(dir.exists()) { throw new DlAbortEx(EX_MAKE_DIR, dir.getPath().c_str(), "File already exists."); } else if(!dir.mkdirs()) { throw new DlAbortEx(EX_MAKE_DIR, dir.getPath().c_str(), strerror(errno)); } } void Util::convertBitfield(BitfieldMan* dest, const BitfieldMan* src) { size_t numBlock = dest->countBlock(); for(size_t index = 0; index < numBlock; ++index) { if(src->isBitSetOffsetRange((uint64_t)index*dest->getBlockLength(), dest->getBlockLength())) { dest->setBit(index); } } } std::string Util::toString(const BinaryStreamHandle& binaryStream) { std::stringstream strm; char data[2048]; while(1) { int32_t dataLength = binaryStream->readData((unsigned char*)data, sizeof(data), strm.tellp()); strm.write(data, dataLength); if(dataLength == 0) { break; } } return strm.str(); } #ifdef HAVE_POSIX_MEMALIGN /** * In linux 2.6, alignment and size should be a multiple of 512. */ void* Util::allocateAlignedMemory(size_t alignment, size_t size) { void* buffer; int res; if((res = posix_memalign(&buffer, alignment, size)) != 0) { throw new FatalException("Error in posix_memalign: %s", strerror(res)); } return buffer; } #endif // HAVE_POSIX_MEMALIGN } // namespace aria2