blob: f835613364a3bd2740f27bc6407ca72f13e40ba2 [file] [log] [blame]
// 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