1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be 3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file. 4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/ftp/ftp_util.h" 6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <vector> 8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/i18n/char_iterator.h" 10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h" 113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h" 12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_tokenizer.h" 13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h" 14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/time.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h" 16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "unicode/datefmt.h" 17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "unicode/dtfmtsym.h" 18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "unicode/uchar.h" 19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// For examples of Unix<->VMS path conversions, see the unit test file. On VMS 21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// a path looks differently depending on whether it's a file or directory. 22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net { 24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static 26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string FtpUtil::UnixFilePathToVMS(const std::string& unix_path) { 27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (unix_path.empty()) 28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return std::string(); 29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott StringTokenizer tokenizer(unix_path, "/"); 31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::vector<std::string> tokens; 32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott while (tokenizer.GetNext()) 33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott tokens.push_back(tokenizer.token()); 34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (unix_path[0] == '/') { 36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // It's an absolute path. 37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tokens.empty()) { 39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK_EQ(1U, unix_path.length()); 40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return "[]"; 41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tokens.size() == 1) 44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return unix_path.substr(1); // Drop the leading slash. 45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::string result(tokens[0] + ":["); 47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tokens.size() == 2) { 48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Don't ask why, it just works that way on VMS. 49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result.append("000000"); 50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result.append(tokens[1]); 52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott for (size_t i = 2; i < tokens.size() - 1; i++) 53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result.append("." + tokens[i]); 54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result.append("]" + tokens[tokens.size() - 1]); 56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return result; 57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tokens.size() == 1) 60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return unix_path; 61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::string result("["); 63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott for (size_t i = 0; i < tokens.size() - 1; i++) 64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result.append("." + tokens[i]); 65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result.append("]" + tokens[tokens.size() - 1]); 66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return result; 67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static 70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string FtpUtil::UnixDirectoryPathToVMS(const std::string& unix_path) { 71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (unix_path.empty()) 72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return std::string(); 73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::string path(unix_path); 75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (path[path.length() - 1] != '/') 77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott path.append("/"); 78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Reuse logic from UnixFilePathToVMS by appending a fake file name to the 80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // real path and removing it after conversion. 81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott path.append("x"); 82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott path = UnixFilePathToVMS(path); 83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return path.substr(0, path.length() - 1); 84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static 87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string FtpUtil::VMSPathToUnix(const std::string& vms_path) { 88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (vms_path.empty()) 89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return "."; 90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (vms_path == "[]") 92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return "/"; 93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::string result(vms_path); 95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (vms_path[0] == '[') { 96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // It's a relative path. 97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ReplaceFirstSubstringAfterOffset(&result, 0, "[.", ""); 98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // It's an absolute path. 100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result.insert(0, "/"); 101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ReplaceSubstringsAfterOffset(&result, 0, ":[000000]", "/"); 102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ReplaceSubstringsAfterOffset(&result, 0, ":[", "/"); 103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::replace(result.begin(), result.end(), '.', '/'); 105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::replace(result.begin(), result.end(), ']', '/'); 106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Make sure the result doesn't end with a slash. 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (result.length() && result[result.length() - 1] == '/') 109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott result = result.substr(0, result.length() - 1); 110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return result; 112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static 115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool FtpUtil::AbbreviatedMonthToNumber(const string16& text, int* number) { 116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen icu::UnicodeString unicode_text(text.data(), text.size()); 117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int32_t locales_count; 119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const icu::Locale* locales = 120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen icu::DateFormat::getAvailableLocales(locales_count); 121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Some FTP servers localize the date listings. To guess the locale, 123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // we loop over all available ones. 124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (int32_t locale = 0; locale < locales_count; locale++) { 125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UErrorCode status(U_ZERO_ERROR); 126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen icu::DateFormatSymbols format_symbols(locales[locale], status); 128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If we cannot get format symbols for some locale, it's not a fatal error. 130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Just try another one. 131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (U_FAILURE(status)) 132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen continue; 133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int32_t months_count; 135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const icu::UnicodeString* months = 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen format_symbols.getShortMonths(months_count); 137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Loop over all abbreviated month names in given locale. 139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // An alternative solution (to parse |text| in given locale) is more 140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // lenient, and may accept more than we want even with setLenient(false). 141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (int32_t month = 0; month < months_count; month++) { 142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Compare (case-insensitive), but just first three characters. Sometimes 143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // ICU returns longer strings (for example for Russian locale), and in FTP 144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // listings they are abbreviated to just three characters. 145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Note: ICU may also return strings shorter than three characters, 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // and those also should be accepted. 147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (months[month].caseCompare(0, 3, unicode_text, 0) == 0) { 148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen *number = month + 1; 149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return true; 150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return false; 155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static 158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool FtpUtil::LsDateListingToTime(const string16& month, const string16& day, 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const string16& rest, 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const base::Time& current_time, 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::Time* result) { 162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott base::Time::Exploded time_exploded = { 0 }; 163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!AbbreviatedMonthToNumber(month, &time_exploded.month)) 165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return false; 166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 1673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!base::StringToInt(day, &time_exploded.day_of_month)) 168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return false; 169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (time_exploded.day_of_month > 31) 170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return false; 171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!base::StringToInt(rest, &time_exploded.year)) { 1733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Maybe it's time. Does it look like time (HH:MM)? 1743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (rest.length() == 5 && rest[2] == ':') { 175513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!base::StringToInt(rest.begin(), 176513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch rest.begin() + 2, 177513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch &time_exploded.hour)) 1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 180513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!base::StringToInt(rest.begin() + 3, 181513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch rest.begin() + 5, 182513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch &time_exploded.minute)) 1833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 1843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } else if (rest.length() == 4 && rest[1] == ':') { 1853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Sometimes it's just H:MM. 186513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!base::StringToInt(rest.begin(), 187513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch rest.begin() + 1, 188513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch &time_exploded.hour)) 1893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 1903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 191513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!base::StringToInt(rest.begin() + 2, 192513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch rest.begin() + 4, 193513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch &time_exploded.minute)) 1943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 1953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } else { 196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return false; 1973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Guess the year. 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::Time::Exploded current_exploded; 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_time.LocalExplode(¤t_exploded); 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If it's not possible for the parsed date to be in the current year, 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // use the previous year. 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (time_exploded.month > current_exploded.month || 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (time_exploded.month == current_exploded.month && 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch time_exploded.day_of_month > current_exploded.day_of_month)) { 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch time_exploded.year = current_exploded.year - 1; 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch time_exploded.year = current_exploded.year; 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We don't know the time zone of the listing, so just use local time. 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *result = base::Time::FromLocalExploded(time_exploded); 216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return true; 217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static 220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstring16 FtpUtil::GetStringPartAfterColumns(const string16& text, int columns) { 221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen base::i18n::UTF16CharIterator iter(&text); 222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(jshin): Is u_isspace the right function to use here? 224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott for (int i = 0; i < columns; i++) { 225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Skip the leading whitespace. 226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen while (!iter.end() && u_isspace(iter.get())) 227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen iter.Advance(); 228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Skip the actual text of i-th column. 230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen while (!iter.end() && !u_isspace(iter.get())) 231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen iter.Advance(); 232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen string16 result(text.substr(iter.array_pos())); 235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott TrimWhitespace(result, TRIM_ALL, &result); 236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return result; 237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} // namespace 240