2023-03-30 15:56:43 +00:00
|
|
|
#include <imgui/imgui.h>
|
|
|
|
#include <nativefiledialog-extended/src/include/nfd.h>
|
|
|
|
#include <optional>
|
|
|
|
|
|
|
|
#include "../file.h"
|
|
|
|
#include "../logcat_entry.h"
|
|
|
|
|
|
|
|
static inline void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, File& file);
|
|
|
|
static inline void import_logcat_entries_from_clipboard(LogcatEntries& logcat_entries, bool ignore_buffer_lines);
|
|
|
|
static void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, auto cb);
|
|
|
|
static inline std::optional<File> open_file_picker();
|
|
|
|
|
|
|
|
void import_fragment(LogcatEntries& logcat_entries) {
|
|
|
|
static bool ignore_buffer_lines = false;
|
|
|
|
|
|
|
|
ImGui::Checkbox("Ignore buffer lines", &ignore_buffer_lines);
|
|
|
|
|
|
|
|
if (ImGui::Button("Import from File")) {
|
|
|
|
std::optional<File> file = open_file_picker();
|
|
|
|
if (file) {
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
try {
|
|
|
|
import_logcat_entries(logcat_entries, ignore_buffer_lines, *file);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
log(std::string("Failed to import logcat entries: ") + e.what());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Import from Clipboard")) {
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
try {
|
|
|
|
import_logcat_entries_from_clipboard(logcat_entries, ignore_buffer_lines);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
log(std::string("Failed to import logcat entries: ") + e.what());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, File& file) {
|
|
|
|
auto handle_valid_char = [](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length, char c) -> std::optional<bool> {
|
|
|
|
if (c == '\n') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (*line_length == MAX_LOGCAT_LINE_SIZE) {
|
|
|
|
throw std::runtime_error("Received line longer than 512k");
|
|
|
|
}
|
|
|
|
|
|
|
|
line[*line_length] = c;
|
|
|
|
(*line_length)++;
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
};
|
|
|
|
auto handle_char = [&](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length, int c) -> std::optional<bool> {
|
|
|
|
if (c != EOF) {
|
|
|
|
return handle_valid_char(line, line_length, static_cast<char>(c));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (feof(file.get())) {
|
|
|
|
return *line_length != 0;
|
|
|
|
} else if (ferror(file.get())) {
|
|
|
|
throw_system_error("fread()");
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("fgetc() returned EOF without setting anything");
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
};
|
|
|
|
|
|
|
|
import_logcat_entries(logcat_entries, ignore_buffer_lines, [&](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length) {
|
|
|
|
*line_length = 0;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
int c = fgetc(file.get());
|
|
|
|
std::optional<bool> ret = handle_char(line, line_length, c);
|
|
|
|
if (ret) {
|
|
|
|
return *ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void import_logcat_entries_from_clipboard(LogcatEntries& logcat_entries, bool ignore_buffer_lines) {
|
|
|
|
const char* text = ImGui::GetClipboardText();
|
|
|
|
|
|
|
|
import_logcat_entries(logcat_entries, ignore_buffer_lines, [&](char line[MAX_LOGCAT_LINE_SIZE], size_t* line_length) {
|
|
|
|
if (!text) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* newline_offset = strchr(text, '\n');
|
|
|
|
*line_length = newline_offset ? static_cast<size_t>(newline_offset - text) : strlen(text);
|
|
|
|
|
|
|
|
if (*line_length > MAX_LOGCAT_LINE_SIZE) {
|
|
|
|
throw std::runtime_error("Received line longer than 512k");
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(line, text, *line_length);
|
|
|
|
text = newline_offset ? &newline_offset[1] : nullptr;
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static void import_logcat_entries(LogcatEntries& logcat_entries, bool ignore_buffer_lines, auto cb) {
|
|
|
|
char line[MAX_LOGCAT_LINE_SIZE];
|
|
|
|
size_t line_length;
|
|
|
|
Buffer buffer = Buffer::Unknown;
|
|
|
|
|
|
|
|
while (cb(line, &line_length)) {
|
|
|
|
if (line_length == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<LogcatEntry> logcat_entry = try_parse_logcat_entry(line, line_length, buffer);
|
|
|
|
if (logcat_entry) {
|
|
|
|
logcat_entries.push_back(std::move(*logcat_entry));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<Buffer> new_buffer = try_parse_buffer(line, line_length);
|
|
|
|
if (new_buffer) {
|
|
|
|
if (!ignore_buffer_lines) {
|
|
|
|
buffer = *new_buffer;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw std::invalid_argument(std::string("Cannot parse line: ") + std::string(line, line_length));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline std::optional<File> open_file_picker() {
|
|
|
|
nfdchar_t* path;
|
|
|
|
nfdfilteritem_t filters[1] = {{"Log file", "log"}};
|
|
|
|
nfdresult_t res = NFD_OpenDialog(&path, filters, 1, nullptr);
|
|
|
|
|
|
|
|
if (res == NFD_OKAY) {
|
|
|
|
try {
|
|
|
|
File file(path, "r");
|
|
|
|
NFD_FreePath(path);
|
2023-06-21 11:45:08 +00:00
|
|
|
return file;
|
2023-03-30 15:56:43 +00:00
|
|
|
} catch (const std::exception& e) {
|
|
|
|
NFD_FreePath(path);
|
|
|
|
log(std::string("Failed to open file from file picker: ") + e.what());
|
|
|
|
}
|
|
|
|
} else if (res == NFD_CANCEL) {
|
|
|
|
// dialog was canceled, shrug
|
|
|
|
} else {
|
|
|
|
// ignore error when failing to open the file picker
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|