| // Copyright 2019 The Fuchsia 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 "src/developer/cmd/autocomplete.h" |
| |
| #include <dirent.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <zircon/assert.h> |
| |
| #include <utility> |
| |
| #include "src/developer/cmd/command.h" |
| |
| namespace cmd { |
| namespace { |
| |
| void GetNextToken(const std::string& line, size_t pos, size_t* start, size_t* end) { |
| *start = line.find_first_not_of(Command::kWhitespace, pos); |
| *end = line.find_first_of(Command::kWhitespace, *start); |
| } |
| |
| void SearchDirectory(const std::string& directory, const std::string& entry_prefix, |
| fit::function<void(const char*)> callback) { |
| DIR* dir = opendir(directory.c_str()); |
| if (dir == NULL) { |
| return; |
| } |
| |
| struct dirent* entry = nullptr; |
| while ((entry = readdir(dir)) != NULL) { |
| if (strncmp(entry_prefix.c_str(), entry->d_name, entry_prefix.length())) { |
| continue; |
| } |
| if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { |
| continue; |
| } |
| // TODO: Consider returning only executable files. |
| callback(entry->d_name); |
| } |
| |
| closedir(dir); |
| } |
| |
| }; // namespace |
| |
| Autocomplete::Autocomplete(const std::string& line) { |
| size_t pos = 0u; |
| while (pos < line.size()) { |
| size_t start = 0u; |
| size_t end = 0u; |
| GetNextToken(line, pos, &start, &end); |
| if (start != std::string::npos && end != std::string::npos) { |
| tokens_.push_back(line.substr(start, end - start)); |
| pos = end; |
| } else { |
| fragment_prefix_ = line.substr(0, start); |
| if (start != std::string::npos) { |
| fragment_ = line.substr(start, end - start); |
| } |
| break; |
| } |
| } |
| } |
| |
| Autocomplete::~Autocomplete() = default; |
| |
| void Autocomplete::AddCompletion(const std::string& completion) { |
| completions_.push_back(fragment_prefix_ + completion); |
| } |
| |
| void Autocomplete::CompleteAsPath() { |
| size_t split = fragment_.rfind('/'); |
| if (split == std::string::npos) { |
| CompleteAsDirectoryEntry("."); |
| } else { |
| std::string dirname = fragment_.substr(0, split + 1); |
| std::string entry_prefix = fragment_.substr(split + 1); |
| SearchDirectory(dirname, entry_prefix, |
| [this, &dirname](const char* entry) { AddCompletion(dirname + entry); }); |
| } |
| } |
| |
| void Autocomplete::CompleteAsDirectoryEntry(const std::string& directory) { |
| SearchDirectory(directory, fragment_, [this](const char* entry) { AddCompletion(entry); }); |
| } |
| |
| void Autocomplete::CompleteAsEnvironmentVariable() { |
| if (fragment_.find('=') != std::string::npos) { |
| return; |
| } |
| for (char** variable = environ; *variable; variable++) { |
| if (!strncmp(fragment_.c_str(), *variable, fragment_.length())) { |
| AddCompletion(std::string(*variable, strchr(*variable, '=') - *variable)); |
| } |
| } |
| } |
| |
| std::vector<std::string> Autocomplete::TakeCompletions() { return std::move(completions_); } |
| |
| } // namespace cmd |