diff options
-rw-r--r-- | net/net.gyp | 3 | ||||
-rw-r--r-- | net/tools/dump_cache/cache_dumper.cc | 190 | ||||
-rw-r--r-- | net/tools/dump_cache/cache_dumper.h | 82 | ||||
-rw-r--r-- | net/tools/dump_cache/dump_cache.cc | 35 | ||||
-rw-r--r-- | net/tools/dump_cache/upgrade.cc | 92 | ||||
-rw-r--r-- | net/tools/dump_cache/url_to_filename_encoder.h | 119 |
6 files changed, 476 insertions, 45 deletions
diff --git a/net/net.gyp b/net/net.gyp index da8f4b5..509f9ec 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -732,9 +732,12 @@ '../base/base.gyp:base', ], 'sources': [ + 'tools/dump_cache/cache_dumper.cc', + 'tools/dump_cache/cache_dumper.h', 'tools/dump_cache/dump_cache.cc', 'tools/dump_cache/dump_files.cc', 'tools/dump_cache/upgrade.cc', + 'tools/dump_cache/url_to_filename_encoder.h', ], }, ], diff --git a/net/tools/dump_cache/cache_dumper.cc b/net/tools/dump_cache/cache_dumper.cc new file mode 100644 index 0000000..29f7be4 --- /dev/null +++ b/net/tools/dump_cache/cache_dumper.cc @@ -0,0 +1,190 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/dump_cache/cache_dumper.h" + +#include "net/base/io_buffer.h" +#include "net/disk_cache/entry_impl.h" +#include "net/http/http_cache.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" +#include "net/tools/dump_cache/url_to_filename_encoder.h" + +bool CacheDumper::CreateEntry(const std::string& key, + disk_cache::Entry** entry) { + return cache_->CreateEntry(key, entry); +} + +bool CacheDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset, + net::IOBuffer* buf, int buf_len) { + int written = entry->WriteData(index, offset, buf, buf_len, NULL, false); + return written == buf_len; +} + +void CacheDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used, + base::Time last_modified) { + if (entry) { + static_cast<disk_cache::EntryImpl*>(entry)->SetTimes(last_used, + last_modified); + entry->Close(); + } +} + +// A version of CreateDirectory which supports lengthy filenames. +// Returns true on success, false on failure. +bool SafeCreateDirectory(const std::wstring& path) { +#ifdef WIN32_LARGE_FILENAME_SUPPORT + // Due to large paths on windows, it can't simply do a + // CreateDirectory("a/b/c"). Instead, create each subdirectory manually. + bool rv = false; + std::wstring::size_type pos(0); + std::wstring backslash(L"\\"); + + // If the path starts with the long file header, skip over that + const std::wstring kLargeFilenamePrefix(L"\\\\?\\"); + std::wstring header(kLargeFilenamePrefix); + if (path.find(header) == 0) + pos = 4; + + // Create the subdirectories individually + while((pos = path.find(backslash, pos)) != std::wstring::npos) { + std::wstring subdir = path.substr(0, pos); + CreateDirectoryW(subdir.c_str(), NULL); + // we keep going even if directory creation failed. + pos++; + } + // Now create the full path + return CreateDirectoryW(path.c_str(), NULL) == TRUE; +#else + return file_util::CreateDirectory(path); +#endif +} + +bool DiskDumper::CreateEntry(const std::string& key, + disk_cache::Entry** entry) { + FilePath path(path_); + entry_path_ = net::UrlToFilenameEncoder::Encode(key, path); + +#ifdef WIN32_LARGE_FILENAME_SUPPORT + // In order for long filenames to work, we'll need to prepend + // the windows magic token. + const std::wstring kLongFilenamePrefix(L"\\\\?\\"); + entry_path_ = FilePath(kLongFilenamePrefix).Append(entry_path_); +#endif + + entry_url_ = key; + + FilePath directory = entry_path_.DirName(); + SafeCreateDirectory(directory.value()); + + std::wstring file = entry_path_.value(); +#ifdef WIN32_LARGE_FILENAME_SUPPORT + entry_ = CreateFileW(file.c_str(), GENERIC_WRITE|GENERIC_READ, 0, 0, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + return entry_ != INVALID_HANDLE_VALUE; +#else + entry_ = file_util::OpenFile(entry_path_, "w+"); + return entry_ != NULL; +#endif +} + +// Utility Function to create a normalized header string from a +// HttpResponseInfo. The output will be formatted exactly +// like so: +// HTTP/<version> <status_code> <status_text>\n +// [<header-name>: <header-values>\n]* +// meaning, each line is \n-terminated, and there is no extra whitespace +// beyond the single space separators shown (of course, values can contain +// whitespace within them). If a given header-name appears more than once +// in the set of headers, they are combined into a single line like so: +// <header-name>: <header-value1>, <header-value2>, ...<header-valueN>\n +// +// DANGER: For some headers (e.g., "Set-Cookie"), the normalized form can be +// a lossy format. This is due to the fact that some servers generate +// Set-Cookie headers that contain unquoted commas (usually as part of the +// value of an "expires" attribute). So, use this function with caution. Do +// not expect to be able to re-parse Set-Cookie headers from this output. +// +// NOTE: Do not make any assumptions about the encoding of this output +// string. It may be non-ASCII, and the encoding used by the server is not +// necessarily known to us. Do not assume that this output is UTF-8! +void GetNormalizedHeaders(const net::HttpResponseInfo& info, + std::string* output) { + // Start with the status line + output->assign(info.headers->GetStatusLine()); + output->append("\r\n"); + + // Enumerate the headers + void* iter = 0; + std::string name, value; + while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) { + output->append(name); + output->append(": "); + output->append(value); + output->append("\r\n"); + } + + // Mark the end of headers + output->append("\r\n"); +} + +bool DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset, + net::IOBuffer* buf, int buf_len) { + if (!entry_) + return false; + + std::string headers; + const char *data; + int len; + if (index == 0) { // Stream 0 is the headers. + net::HttpResponseInfo response_info; + if (!net::HttpCache::ParseResponseInfo(buf->data(), buf_len, + &response_info)) + return false; + + // Remove the size headers. + response_info.headers->RemoveHeader("transfer-encoding"); + response_info.headers->RemoveHeader("content-length"); + response_info.headers->RemoveHeader("x-original-url"); + + // Convert the headers into a string ending with LF. + GetNormalizedHeaders(response_info, &headers); + + // Append a header for the original URL. + std::string url = entry_url_; + // strip off the "XXGET" which may be in the key. + std::string::size_type pos(0); + if ((pos = url.find("http")) != 0) { + if (pos != std::string::npos) + url = url.substr(pos); + } + std::string x_original_url = "X-Original-Url: " + url + "\r\n"; + // we know that the last two bytes are CRLF. + headers.replace(headers.length() - 2, 0, x_original_url); + + data = headers.c_str(); + len = headers.size(); + } else if (index == 1) { // Stream 1 is the data. + data = buf->data(); + len = buf_len; + } +#ifdef WIN32_LARGE_FILENAME_SUPPORT + DWORD bytes; + DWORD rv = WriteFile(entry_, data, len, &bytes, 0); + return rv == TRUE && bytes == len; +#else + int bytes = fwrite(data, 1, len, entry_); + return bytes == len; +#endif +} + +void DiskDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used, + base::Time last_modified) { +#ifdef WIN32_LARGE_FILENAME_SUPPORT + CloseHandle(entry_); +#else + file_util::CloseFile(entry_); +#endif +} + diff --git a/net/tools/dump_cache/cache_dumper.h b/net/tools/dump_cache/cache_dumper.h new file mode 100644 index 0000000..8a11269 --- /dev/null +++ b/net/tools/dump_cache/cache_dumper.h @@ -0,0 +1,82 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TOOLS_DUMP_CACHE_DUMPER_H_ +#define NET_TOOLS_DUMP_CACHE_DUMPER_H_ + +#include <string> +#include "base/file_path.h" +#include "base/file_util.h" +#include "net/disk_cache/backend_impl.h" + +#ifdef WIN32 +// Dumping the cache often creates very large filenames, which are tricky +// on windows. Most API calls don't support large filenames, including +// most of the base library functions. Unfortunately, adding "\\?\" into +// the filename support is tricky. Instead, if WIN32_LARGE_FILENAME_SUPPORT +// is set, we use direct WIN32 APIs for manipulating the files. +#define WIN32_LARGE_FILENAME_SUPPORT +#endif + +// An abstract class for writing cache dump data. +class CacheDumpWriter { + public: + // Creates an entry to be written. + // On success, populates the |entry|. + // Returns true on success, false otherwise. + virtual bool CreateEntry(const std::string& key, + disk_cache::Entry** entry) = 0; + + // Write to the current entry. + // Returns true on success, false otherwise. + virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset, + net::IOBuffer* buf, int buf_len) = 0; + + // Close the current entry. + virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used, + base::Time last_modified) = 0; +}; + +// Writes data to a cache. +class CacheDumper : public CacheDumpWriter { + public: + CacheDumper(disk_cache::BackendImpl* cache) : cache_(cache) {}; + + virtual bool CreateEntry(const std::string& key, disk_cache::Entry** entry); + virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset, + net::IOBuffer* buf, int buf_len); + virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used, + base::Time last_modified); + + private: + disk_cache::BackendImpl* cache_; +}; + +// Writes data to a disk. +class DiskDumper : public CacheDumpWriter { + public: + DiskDumper(const std::wstring& path) : path_(path), entry_(NULL) { + file_util::CreateDirectory(path); + }; + virtual bool CreateEntry(const std::string& key, disk_cache::Entry** entry); + virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset, + net::IOBuffer* buf, int buf_len); + virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used, + base::Time last_modified); + + private: + std::wstring path_; + // This is a bit of a hack. As we get a CreateEntry, we coin the current + // entry_path_ where we write that entry to disk. Subsequent calls to + // WriteEntry() utilize this path for writing to disk. + FilePath entry_path_; + std::string entry_url_; +#ifdef WIN32_LARGE_FILENAME_SUPPORT + HANDLE entry_; +#else + FILE* entry_; +#endif +}; + +#endif // NET_TOOLS_DUMP_CACHE_DUMPER_H_ diff --git a/net/tools/dump_cache/dump_cache.cc b/net/tools/dump_cache/dump_cache.cc index f14c7a9..748bf0f 100644 --- a/net/tools/dump_cache/dump_cache.cc +++ b/net/tools/dump_cache/dump_cache.cc @@ -29,7 +29,7 @@ int GetMajorVersion(const std::wstring& input_path); int DumpContents(const std::wstring& input_path); int DumpHeaders(const std::wstring& input_path); int RunSlave(const std::wstring& input_path, const std::wstring& pipe_number); -int Upgrade(const std::wstring& output_path, HANDLE pipe); +int CopyCache(const std::wstring& output_path, HANDLE pipe, bool copy_to_text); HANDLE CreateServer(std::wstring* pipe_number); const char kUpgradeHelp[] = @@ -50,6 +50,9 @@ const wchar_t kDumpHeaders[] = L"dump-headers"; // Dumps all entries to stdout. const wchar_t kDumpContents[] = L"dump-contents"; +// Convert the cache to files. +const wchar_t kDumpToFiles[] = L"dump-to-files"; + // Upgrade an old version to the current one. const wchar_t kUpgrade[] = L"upgrade"; @@ -63,6 +66,7 @@ int Help() { printf("--dump-headers: display file headers\n"); printf("--dump-contents: display all entries\n"); printf("--upgrade: copy contents to the output path\n"); + printf("--dump-to-files: write the contents of the cache to files\n"); return INVALID_ARGUMENT; } @@ -75,14 +79,21 @@ int LaunchSlave(const CommandLine& command_line, size_t to_remove = hacked_command_line.find(old_exe); hacked_command_line.erase(to_remove, old_exe.size()); - std::wstring new_program = StringPrintf(L"%ls%d.exe", L"dump_cache_", - version); + bool do_upgrade = command_line.HasSwitch(kUpgrade); + bool do_convert_to_text = command_line.HasSwitch(kDumpToFiles); + + std::wstring new_program; + if (do_upgrade) + new_program = StringPrintf(L"%ls%d.exe", L"dump_cache_", version); + else + new_program = StringPrintf(L"dump_cache.exe"); + hacked_command_line.insert(to_remove, new_program); CommandLine new_command_line(L""); new_command_line.ParseFromString(hacked_command_line); - if (command_line.HasSwitch(kUpgrade)) + if (do_upgrade || do_convert_to_text) new_command_line.AppendSwitch(kSlave); new_command_line.AppendSwitchWithValue(kPipe, pipe_number); @@ -110,13 +121,17 @@ int main(int argc, const char* argv[]) { bool upgrade = false; bool slave_required = false; - std::wstring output_path; - if (command_line.HasSwitch(kUpgrade)) { - output_path = command_line.GetSwitchValue(kOutputPath); + bool copy_to_text = false; + std::wstring output_path = command_line.GetSwitchValue(kOutputPath); + if (command_line.HasSwitch(kUpgrade)) + upgrade = true; + if (command_line.HasSwitch(kDumpToFiles)) + copy_to_text = true; + + if (upgrade || copy_to_text) { if (output_path.empty()) return Help(); slave_required = true; - upgrade = true; } int version = GetMajorVersion(input_path); @@ -148,8 +163,8 @@ int main(int argc, const char* argv[]) { return ret; } - if (upgrade) - return Upgrade(output_path, server); + if (upgrade || copy_to_text) + return CopyCache(output_path, server, copy_to_text); if (slave_required) { // Wait until the slave starts dumping data before we quit. Lazy "fix" for a diff --git a/net/tools/dump_cache/upgrade.cc b/net/tools/dump_cache/upgrade.cc index bf27c9d..de0f878 100644 --- a/net/tools/dump_cache/upgrade.cc +++ b/net/tools/dump_cache/upgrade.cc @@ -4,10 +4,16 @@ #include "base/logging.h" #include "base/message_loop.h" +#include "base/scoped_ptr.h" #include "base/string_util.h" +#include "googleurl/src/gurl.h" #include "net/base/io_buffer.h" #include "net/disk_cache/backend_impl.h" #include "net/disk_cache/entry_impl.h" +#include "net/http/http_cache.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" +#include "net/tools/dump_cache/cache_dumper.h" namespace { @@ -102,7 +108,7 @@ enum { class BaseSM : public MessageLoopForIO::IOHandler { public: - BaseSM(disk_cache::BackendImpl* cache, HANDLE channel); + BaseSM(HANDLE channel); virtual ~BaseSM(); protected: @@ -113,7 +119,6 @@ class BaseSM : public MessageLoopForIO::IOHandler { MessageLoopForIO::IOContext in_context_; MessageLoopForIO::IOContext out_context_; - disk_cache::BackendImpl* cache_; disk_cache::EntryImpl* entry_; HANDLE channel_; int state_; @@ -125,9 +130,8 @@ class BaseSM : public MessageLoopForIO::IOHandler { DISALLOW_COPY_AND_ASSIGN(BaseSM); }; -BaseSM::BaseSM(disk_cache::BackendImpl* cache, HANDLE channel) - : cache_(cache), entry_(NULL), channel_(channel), state_(0), - pending_count_(0) { +BaseSM::BaseSM(HANDLE channel) + : entry_(NULL), channel_(channel), state_(0), pending_count_(0) { in_buffer_.reset(new char[kChannelSize]); out_buffer_.reset(new char[kChannelSize]); input_ = reinterpret_cast<IoBuffer*>(in_buffer_.get()); @@ -195,9 +199,12 @@ bool BaseSM::IsPending() { class MasterSM : public BaseSM { public: - MasterSM(disk_cache::BackendImpl* cache, HANDLE channel) - : BaseSM(cache, channel) {} - virtual ~MasterSM() {} + MasterSM(const std::wstring& path, HANDLE channel, bool dump_to_disk) + : BaseSM(channel), path_(path), dump_to_disk_(dump_to_disk) { + } + virtual ~MasterSM() { + delete writer_; + } bool DoInit(); virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, @@ -236,6 +243,10 @@ class MasterSM : public BaseSM { int bytes_remaining_; int offset_; int copied_entries_; + scoped_ptr<disk_cache::BackendImpl> cache_; + CacheDumpWriter* writer_; + const std::wstring& path_; + bool dump_to_disk_; }; void MasterSM::OnIOCompleted(MessageLoopForIO::IOContext* context, @@ -287,6 +298,19 @@ bool MasterSM::DoInit() { DEBUGMSG("Master DoInit\n"); DCHECK(state_ == MASTER_INITIAL); + if (dump_to_disk_) { + writer_ = new DiskDumper(path_); + } else { + cache_.reset(new disk_cache::BackendImpl(path_)); + if (!cache_->Init()) { + printf("Unable to initialize new files\n"); + return false; + } + writer_ = new CacheDumper(cache_.get()); + } + if (!writer_) + return false; + copied_entries_ = 0; remote_entry_ = 0; @@ -343,17 +367,20 @@ void MasterSM::DoGetKey(int bytes_read) { std::string key(input_->buffer); DCHECK(key.size() == input_->msg.buffer_bytes - 1); - if (!cache_->CreateEntry(key, - reinterpret_cast<disk_cache::Entry**>(&entry_))) { + + if (!writer_->CreateEntry(key, + reinterpret_cast<disk_cache::Entry**>(&entry_))) { printf("Skipping entry \"%s\" (name conflict!)\n", key.c_str()); return SendGetPrevEntry(); } - if (key.size() < 60) { - DEBUGMSG("Entry \"%s\" created\n", key.c_str()); - } else { - DEBUGMSG("Entry (long name) created\n", key.c_str()); + if (key.size() >= 64) { + key[60] = '.'; + key[61] = '.'; + key[62] = '.'; + key[63] = '\0'; } + DEBUGMSG("Entry \"%s\" created\n", key.c_str()); state_ = MASTER_GET_USE_TIMES; Message msg; msg.command = GET_USE_TIMES; @@ -403,8 +430,7 @@ void MasterSM::DoGetDataSize() { void MasterSM::CloseEntry() { DEBUGMSG("Master CloseEntry\n"); printf("%c\r", copied_entries_ % 2 ? 'x' : '+'); - entry_->SetTimes(last_used_, last_modified_); - entry_->Close(); + writer_->CloseEntry(entry_, last_used_, last_modified_); entry_ = NULL; copied_entries_++; SendGetPrevEntry(); @@ -447,8 +473,7 @@ void MasterSM::DoReadData(int bytes_read) { scoped_refptr<net::WrappedIOBuffer> buf = new net::WrappedIOBuffer(input_->buffer); - if (read_size != entry_->WriteData(stream_, offset_, buf, read_size, NULL, - false)) + if (!writer_->WriteEntry(entry_, stream_, offset_, buf, read_size)) return Fail(); offset_ += read_size; @@ -482,8 +507,15 @@ void MasterSM::Fail() { class SlaveSM : public BaseSM { public: - SlaveSM(disk_cache::BackendImpl* cache, HANDLE channel) - : BaseSM(cache, channel), iterator_(NULL) {} + SlaveSM(const std::wstring& path, HANDLE channel) + : BaseSM(channel), iterator_(NULL) { + cache_.reset(new disk_cache::BackendImpl(path)); + if (!cache_->Init()) { + printf("Unable to open cache files\n"); + return; + } + cache_->SetUpgradeMode(); + } virtual ~SlaveSM(); bool DoInit(); @@ -509,6 +541,8 @@ class SlaveSM : public BaseSM { void Fail(); void* iterator_; + + scoped_ptr<disk_cache::BackendImpl> cache_; }; SlaveSM::~SlaveSM() { @@ -760,15 +794,10 @@ HANDLE CreateServer(std::wstring* pipe_number) { } // This is the controller process for an upgrade operation. -int Upgrade(const std::wstring& output_path, HANDLE pipe) { +int CopyCache(const std::wstring& output_path, HANDLE pipe, bool copy_to_text) { MessageLoop loop(MessageLoop::TYPE_IO); - disk_cache::BackendImpl cache(output_path); - if (!cache.Init()) { - printf("Unable to initialize new files\n"); - return -1; - } - MasterSM master(&cache, pipe); + MasterSM master(output_path, pipe, copy_to_text); if (!master.DoInit()) { printf("Unable to talk with the helper\n"); return -1; @@ -788,14 +817,7 @@ int RunSlave(const std::wstring& input_path, const std::wstring& pipe_number) { return -1; } - disk_cache::BackendImpl cache(input_path); - if (!cache.Init()) { - printf("Unable to open cache files\n"); - return -1; - } - cache.SetUpgradeMode(); - - SlaveSM slave(&cache, pipe); + SlaveSM slave(input_path, pipe); if (!slave.DoInit()) { printf("Unable to talk with the main process\n"); return -1; diff --git a/net/tools/dump_cache/url_to_filename_encoder.h b/net/tools/dump_cache/url_to_filename_encoder.h new file mode 100644 index 0000000..e71d7a0 --- /dev/null +++ b/net/tools/dump_cache/url_to_filename_encoder.h @@ -0,0 +1,119 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H_ +#define NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H_ + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/string_util.h" +#include "googleurl/src/gurl.h" + +namespace net { + +// Helper class for converting a URL into a filename. +class UrlToFilenameEncoder { + public: + // Given a |url| and a |base_path|, returns a FilePath which represents this + // |url|. + static FilePath Encode(const std::string& url, FilePath base_path) { + std::string clean_url(url); + if (clean_url.length() && clean_url[clean_url.length()-1] == '/') + clean_url.append("index.html"); + + GURL gurl(clean_url); + FilePath filename(base_path); + filename = filename.Append(ASCIIToWide(gurl.host())); + + std::wstring url_filename = ASCIIToWide(gurl.PathForRequest()); + // Strip the leading '/' + if (url_filename[0] == L'/') + url_filename = url_filename.substr(1); + + // replace '/' with '\' + ConvertToSlashes(url_filename); + + // strip double slashes ("\\") + StripDoubleSlashes(url_filename); + + // Save path as filesystem-safe characters + url_filename = Escape(url_filename); + filename = filename.Append(url_filename); + + return filename; + } + + private: + // This is the length at which we chop individual subdirectories. + // Technically, we shouldn't need to do this, but I found that + // even with long-filename support, windows had trouble creating + // long subdirectories, and making them shorter helps. + static const int kMaximumSubdirectoryLength = 128; + + // Escape the given input |path| and chop any individual components + // of the path which are greater than kMaximumSubdirectoryLength characters + // into two chunks. + static std::wstring Escape(const std::wstring& path) { + std::wstring output; + int last_slash = 0; + for (size_t index = 0; index < path.length(); index++) { + wchar_t wide_char = path[index]; + DCHECK((wide_char & 0xff) == wide_char); + char ch = static_cast<char>(wide_char & 0xff); + if (ch == 0x5C) + last_slash = index; + if ((ch == 0x2D) || // hyphen + (ch == 0x5C) || (ch == 0x5F) || // backslash, underscore + ((0x30 <= ch) && (ch <= 0x39)) || // Digits [0-9] + ((0x41 <= ch) && (ch <= 0x5A)) || // Uppercase [A-Z] + ((0x61 <= ch) && (ch <= 0x7A))) { // Lowercase [a-z] + output.append(&path[index],1); + } else { + wchar_t encoded[3]; + encoded[0] = L'x'; + encoded[1] = ch / 16; + encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0'; + encoded[2] = ch % 16; + encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0'; + output.append(encoded, 3); + } + if (index - last_slash > kMaximumSubdirectoryLength) { + wchar_t backslash = L'\\'; + output.append(&backslash, 1); + last_slash = index; + } + } + return output; + } + + // Replace all instances of |from| within |str| as |to|. + static void ReplaceAll(std::wstring& str, const std::wstring& from, + const std::wstring& to) { + std::wstring::size_type pos(0); + while((pos = str.find(from, pos)) != std::wstring::npos) { + str.replace(pos, from.size(), to); + pos += from.size(); + } + } + + // Replace all instances of "/" with "\" in |path|. + static void ConvertToSlashes(std::wstring& path) { + std::wstring slash(L"/"); + std::wstring backslash(L"\\"); + ReplaceAll(path, slash, backslash); + } + + // Replace all instances of "\\" with "%5C%5C" in |path|. + static void StripDoubleSlashes(std::wstring& path) { + std::wstring::size_type pos(0); + std::wstring doubleslash(L"\\\\"); + std::wstring escaped_doubleslash(L"%5C%5C"); + ReplaceAll(path, doubleslash, escaped_doubleslash); + } +}; + +} // namespace net + +#endif // NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H__ + |