From a3685d46c883c96e122b12bfebc6975705962e07 Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Sun, 3 Mar 2024 14:29:17 +0100 Subject: v2 initial commit --- src/array.cpp | 186 ++++++++++++++++++++++ src/array.h | 29 ++++ src/definitions.h | 16 ++ src/main.cpp | 416 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main_windows.cpp | 372 ++++++++++++++++++++++++++++++++++++++++++++ src/memory_bucket.cpp | 68 +++++++++ src/memory_bucket.h | 28 ++++ src/mutex.cpp | 102 +++++++++++++ src/mutex.h | 42 +++++ src/platform.h | 35 +++++ src/search.cpp | 237 ++++++++++++++++++++++++++++ src/search.h | 64 ++++++++ 12 files changed, 1595 insertions(+) create mode 100644 src/array.cpp create mode 100644 src/array.h create mode 100644 src/definitions.h create mode 100644 src/main.cpp create mode 100644 src/main_windows.cpp create mode 100644 src/memory_bucket.cpp create mode 100644 src/memory_bucket.h create mode 100644 src/mutex.cpp create mode 100644 src/mutex.h create mode 100644 src/platform.h create mode 100644 src/search.cpp create mode 100644 src/search.h (limited to 'src') diff --git a/src/array.cpp b/src/array.cpp new file mode 100644 index 0000000..b6e0a9b --- /dev/null +++ b/src/array.cpp @@ -0,0 +1,186 @@ +#include "array.h" + +array array_create(int entry_size) +{ + array new_array; + new_array.length = 0; + new_array.reserved_length = 0; + new_array.entry_size = entry_size; + new_array.data = 0; + new_array.reserve_jump = 1; + new_array.mutex = mutex_create_recursive(); + + return new_array; +} + +int array_push(array *array, void *data) +{ + ASSERT(array); + ASSERT(data); + ASSERT(array->reserve_jump >= 1); + + mutex_lock(&array->mutex); + array->length++; + + if (!array->data) + { + array->data = malloc(array->entry_size * array->reserve_jump); + array->reserved_length = array->reserve_jump; + } + + if (array->reserved_length < array->length) + { + array->reserved_length += array->reserve_jump; + array->data = realloc(array->data, (array->reserved_length*array->entry_size)); + } + + memcpy((char*)array->data + ((array->length-1) * array->entry_size), + data, array->entry_size); + + int result = array->length -1; + mutex_unlock(&array->mutex); + return result; +} + +int array_push_size(array *array, void *data, int data_size) +{ + ASSERT(array); + ASSERT(data); + ASSERT(array->reserve_jump >= 1); + + mutex_lock(&array->mutex); + array->length++; + + if (!array->data) + { + array->data = malloc(array->entry_size * array->reserve_jump); + array->reserved_length = array->reserve_jump; + } + + if (array->reserved_length < array->length) + { + array->reserved_length += array->reserve_jump; + array->data = realloc(array->data, (array->reserved_length*array->entry_size)); + } + + memcpy((char*)array->data + ((array->length-1) * array->entry_size), + data, data_size); + + // fill remaining space with 0 + if (array->entry_size > data_size) + { + int remaining = array->entry_size - data_size; + memset((char*)array->data + ((array->length-1) * array->entry_size) + data_size, + 0, remaining); + } + + int result = array->length -1; + mutex_unlock(&array->mutex); + return result; +} + +void array_reserve(array *array, int reserve_count) +{ + ASSERT(array); + + mutex_lock(&array->mutex); + int reserved_count = array->reserved_length - array->length; + reserve_count -= reserved_count; + + if (reserve_count > 0) + { + array->reserved_length += reserve_count; + + if (array->data) + { + array->data = realloc(array->data, (array->reserved_length*array->entry_size)); + } + else + { + array->data = malloc(array->reserved_length*array->entry_size); + } + } + mutex_unlock(&array->mutex); +} + +void array_remove_at(array *array, int at) +{ + ASSERT(array); + ASSERT(at >= 0); + ASSERT(at < array->length); + + mutex_lock(&array->mutex); + if (array->length > 1) + { + int offset = at * array->entry_size; + int size = (array->length - at - 1) * array->entry_size; + memcpy((char*)array->data + offset, + (char*)array->data + offset + array->entry_size, + size); + + //array->data = realloc(array->data, array->length * array->entry_size); + } + + array->length--; + mutex_unlock(&array->mutex); +} + +void array_remove(array *array, void *ptr) +{ + mutex_lock(&array->mutex); + int offset = (char*)ptr - (char*)array->data; + int at = offset / array->entry_size; + array_remove_at(array, at); + mutex_unlock(&array->mutex); +} + +void array_remove_by(array *array, void *data) +{ + ASSERT(array); + + mutex_lock(&array->mutex); + for (int i = 0; i < array->length; i++) + { + void *d = array_at(array, i); + if (memcmp(d, data, array->entry_size) == 0) + { + array_remove_at(array, i); + return; + } + } + mutex_unlock(&array->mutex); +} + +void *array_at(array *array, int at) +{ + mutex_lock(&array->mutex); + ASSERT(array); + ASSERT(at >= 0); + ASSERT(at < array->length); + + void *result = (char*)array->data + (at * array->entry_size); + mutex_unlock(&array->mutex); + return result; +} + +void array_destroy(array *array) +{ + ASSERT(array); + free(array->data); + mutex_destroy(&array->mutex); +} + +array array_copy(array *arr) +{ + array new_array; + new_array.length = arr->length; + new_array.reserved_length = arr->reserved_length; + new_array.entry_size = arr->entry_size; + new_array.data = malloc(new_array.entry_size*new_array.reserved_length); + new_array.mutex = mutex_create(); + + mutex_lock(&arr->mutex); + memcpy(new_array.data, arr->data, new_array.entry_size*new_array.reserved_length); + mutex_unlock(&arr->mutex); + return new_array; +} \ No newline at end of file diff --git a/src/array.h b/src/array.h new file mode 100644 index 0000000..f00c220 --- /dev/null +++ b/src/array.h @@ -0,0 +1,29 @@ +#ifndef INCLUDE_ARRAY +#define INCLUDE_ARRAY + +#define ASSERT(e_) {if(!(e_)){*(int*)0=0;}} + +#include "mutex.h" + +typedef struct t_array +{ + int length; + int reserved_length; + int entry_size; + int reserve_jump; + void *data; + mutex mutex; +} array; + +array array_create(int entry_size); +int array_push(array *array, void *data); +int array_push_size(array *array, void *data, int data_size); +void array_remove_at(array *array, int at); +void array_remove(array *array, void *ptr); +void array_remove_by(array *array, void *data); +void *array_at(array *array, int at); +void array_destroy(array *array); +void array_reserve(array *array, int reserve_count); +array array_copy(array *array); + +#endif \ No newline at end of file diff --git a/src/definitions.h b/src/definitions.h new file mode 100644 index 0000000..2bf70ae --- /dev/null +++ b/src/definitions.h @@ -0,0 +1,16 @@ +#ifndef INCLUDE_EMBEDDED_RESOURCES +#define INCLUDE_EMBEDDED_RESOURCES + +#define TS_VERSION "v1.0.0" + +// Find these with dumpbin [objfile] /SYMBOLS +extern "C" +{ +extern unsigned char _binary_LICENSE_start[]; +extern unsigned char _binary_LICENSE_end[]; + +extern unsigned char _binary_misc_logo_64_png_start[]; +extern unsigned char _binary_misc_logo_64_png_end[]; +} + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..df1dfe2 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,416 @@ +#include "../imgui/imgui.h" +#include "../imgui/imgui_spectrum.h" +#include "../imgui/imgui_impl_opengl3_loader.h" +#include "definitions.h" +#include "search.h" +#include "platform.h" +#include "../utf8.h" +#include + +typedef struct t_ts_image { + GLuint id; + int width; + int height; +} ts_image; + +ts_image img_logo; + +#define SEARCH_BUFFER_SIZE 2048 + +search_result* current_search_result = nullptr; + +char path_buffer[SEARCH_BUFFER_SIZE]; +char filter_buffer[SEARCH_BUFFER_SIZE]; +char query_buffer[SEARCH_BUFFER_SIZE]; + +bool open_settings_window = false; +bool open_about_window = false; + +int thread_count = 4; +int current_locale_index = 0; +int locales_count = 2; +char* locales[] = { + "English", + "Dutch" +}; + +#define STB_IMAGE_IMPLEMENTATION +#include "../stb_image.h" + +// Simple helper function to load an image into a OpenGL texture with common settings +bool LoadTexture(unsigned char* data, unsigned long size, GLuint* out_texture, int* out_width, int* out_height) +{ + // Load from file + int image_width = 0; + int image_height = 0; + unsigned char* image_data = stbi_load_from_memory(data, size, &image_width, &image_height, NULL, 4); + if (image_data == NULL) { + printf("Failed to load %s\n", stbi_failure_reason()); + return false; + } + + // Create a OpenGL texture identifier + GLuint image_texture; + glGenTextures(1, &image_texture); + glBindTexture(GL_TEXTURE_2D, image_texture); + + // Setup filtering parameters for display + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Upload pixels into texture +#if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); + stbi_image_free(image_data); + + *out_texture = image_texture; + *out_width = image_width; + *out_height = image_height; + + return true; +} + +static ts_image _ts_load_image(unsigned char* data, unsigned long size) { + int w = 0; + int h = 0; + GLuint id = 0; + bool ret = LoadTexture(data, size, &id, &w, &h); + + return ts_image {id, w, h}; +} + +static void _ts_search_file(found_file* ref, file_content content, search_result* result) { + if (content.content && !content.file_error) + { + array text_matches = array_create(sizeof(text_match)); + int search_len = strlen(result->search_text); + if (string_contains_ex((char*)content.content, result->search_text, &text_matches)) + { + mutex_lock(&result->matches.mutex); + for (int i = 0; i < text_matches.length; i++) + { + text_match *m = (text_match *)array_at(&text_matches, i); + + file_match file_match; + file_match.file = ref; + file_match.line_nr = m->line_nr; + file_match.word_match_offset = m->word_offset; + file_match.word_match_length = m->word_match_len; + file_match.line_info = (char*)malloc(MAX_INPUT_LENGTH); + + int text_pad_lr = 25; + if (file_match.word_match_offset > text_pad_lr) { + m->line_start += file_match.word_match_offset - text_pad_lr; + file_match.word_match_offset = text_pad_lr; + } + int total_len = text_pad_lr + search_len + text_pad_lr; + + snprintf(file_match.line_info, MAX_INPUT_LENGTH, "%.*s", total_len, m->line_start); + for (int i = 0; i < total_len; i++) { + if (file_match.line_info[i] == '\n') file_match.line_info[i] = ' '; + if (file_match.line_info[i] == '\t') file_match.line_info[i] = ' '; + if (file_match.line_info[i] == '\r') file_match.line_info[i] = ' '; + if (file_match.line_info[i] == '\x0B') file_match.line_info[i] = ' '; + } + + array_push_size(&result->matches, &file_match, sizeof(file_match)); + ref->match_count++; + result->match_count = result->matches.length; + } + mutex_unlock(&result->matches.mutex); + } + + array_destroy(&text_matches); + } +} + +static void* _ts_search_thread(void* args) { + search_result* new_result = (search_result *)args; + + keep_going:; + while (new_result->file_list_read_cursor < new_result->files.length) + { + mutex_lock(&new_result->files.mutex); + int read_cursor = new_result->file_list_read_cursor++; + new_result->file_count++; + mutex_unlock(&new_result->files.mutex); + + if (read_cursor >= new_result->files.length) continue; + + found_file* f = (found_file*)array_at(&new_result->files, read_cursor); + file_content content = platform_read_file(f->path, "rb"); + + _ts_search_file(f, content, new_result); + + free(content.content); + } + + if (!new_result->done_finding_files) + goto keep_going; + + new_result->completed_match_threads++; + + return 0; +} + +static void _ts_start_search() { + search_result* new_result = create_empty_search_result(); + snprintf(new_result->directory_to_search, MAX_INPUT_LENGTH, "%s", path_buffer); + snprintf(new_result->search_text, MAX_INPUT_LENGTH, "%s", query_buffer); + + + platform_list_files(new_result); + //new_result->max_thread_count + for (int i = 0; i < 1; i++) { + thread thr = thread_start(_ts_search_thread, new_result); + thread_detach(&thr); + } + + current_search_result = new_result; +} + +static void _ts_create_popups() { + ImGuiIO& io = ImGui::GetIO(); + if (open_settings_window) { + ImGui::OpenPopup("Text-Search settings"); + ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), ImGuiCond_Always, ImVec2(0.5f,0.5f)); + } + + // Settings window + if (ImGui::BeginPopupModal("Text-Search settings", NULL, ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) { + ImGui::SetWindowSize({300, 0}); + ImGui::DragInt("Threads", &thread_count, 1.0f, 1, 64); + ImGui::Combo("Language", ¤t_locale_index, locales, locales_count); + + ImGui::Dummy({0, 70}); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + if (ImGui::Button("Close")) { + open_settings_window = false; + ImGui::CloseCurrentPopup(); + } + ImGui::PopStyleVar(); + + ImGui::EndPopup(); + } + + if (open_about_window) { + ImGui::OpenPopup("About Text-Search"); + ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), ImGuiCond_Always, ImVec2(0.5f,0.5f)); + } + + // About window + if (ImGui::BeginPopupModal("About Text-Search", NULL, ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) { + ImGui::SetWindowSize({600, 0}); + + //ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 64 - 10); + //ImGui::Image((void*)(intptr_t)img_logo.id, {64, 64}); + + char* license = (char*)_binary_LICENSE_start; + int license_length = _binary_LICENSE_end - _binary_LICENSE_start; + ImGui::Text("%.*s", license_length, license); + + ImGui::Dummy({0, 70}); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + if (ImGui::Button("Close")) { + open_about_window = false; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::CalcTextSize(TS_VERSION).x - 15); + ImGui::Text(TS_VERSION); + ImGui::PopStyleVar(); + + ImGui::EndPopup(); + } +} + +static int _ts_create_menu() { + int menu_bar_h = 0; + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGui::Spectrum::Color(0xDDDDDD)); + ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 1.0f); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ImGui::MenuItem("Open", "CTRL+O"); + ImGui::MenuItem("Save", "CTRL+S"); + ImGui::Separator(); + ImGui::MenuItem("Exit", "CTRL+Q"); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Program")) + { + if (ImGui::MenuItem("Settings")) { + open_settings_window = true; + } + if (ImGui::MenuItem("About")) { + open_about_window = true; + } + + ImGui::EndMenu(); + } + + menu_bar_h = 27; + ImGui::EndMenuBar(); + } + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + _ts_create_popups(); + + return menu_bar_h; +} + +void ts_load_images() { + snprintf(path_buffer, MAX_INPUT_LENGTH, "%s", "C:\\Users\\aldri\\Desktop\\Vault\\Projects\\allegro5"); + snprintf(filter_buffer, MAX_INPUT_LENGTH, "%s", "*.h"); + snprintf(query_buffer, MAX_INPUT_LENGTH, "%s", "test"); + + int size = _binary_misc_logo_64_png_end - _binary_misc_logo_64_png_start; + unsigned char* data = (unsigned char *)_binary_misc_logo_64_png_start; + img_logo = _ts_load_image(data, size); +} + +void ts_create_gui(int window_w, int window_h) { + static float f = 0.0f; + static int counter = 0; + int window_pad = 50; + int textbox_area_height = 80; + int statusbar_area_height = 30; + int result_area_height = window_h - textbox_area_height - statusbar_area_height - window_pad; + + ImGui::SetNextWindowSize({(float)window_w, (float)window_h}); + ImGui::SetNextWindowPos({0, 0}); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(5, 5)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0.f, 0.f)); + ImGui::Begin("text-search", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoScrollbar| + ImGuiWindowFlags_MenuBar); + ImGui::PopStyleVar(); + + float menu_bar_h = _ts_create_menu(); + + float pos_y = 0; + + pos_y += menu_bar_h + 15; + + { // Search boxes + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(5, 5)); + + float offset = 15.0f; + float separator_w = 10.0f; + float frame_w = window_w/2.5f - offset - separator_w/2.0f; + ImGui::SetNextWindowPos({offset, pos_y}); + ImGui::BeginChild("search-boxes", ImVec2(frame_w, textbox_area_height), false); + { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushItemWidth(-1); + ImGui::InputTextWithHint("path-ti", "Path", path_buffer, 4000); + ImGui::PopItemWidth(); + + ImGui::PushItemWidth(-1); + ImGui::InputTextWithHint("query", "Query", query_buffer, 4000); + ImGui::PopItemWidth(); + ImGui::PopStyleVar(); + } + ImGui::EndChild(); + + ImGui::SetNextWindowPos({offset + frame_w + separator_w, pos_y}); + ImGui::BeginChild("search-boxes2", ImVec2(frame_w, textbox_area_height), false); + { + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushItemWidth(-1); + ImGui::InputTextWithHint("filter-ti", "Filter", filter_buffer, 4000); + ImGui::PopItemWidth(); + ImGui::PopStyleVar(); + + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + if (ImGui::Button("Search")) { + _ts_start_search(); + } + ImGui::PopStyleVar(); + } + ImGui::EndChild(); + + ImGui::PopStyleVar(); + } + pos_y += textbox_area_height + 7; + + { // Results + ImGui::SetNextWindowPos({5, pos_y}); + + if (ImGui::BeginTable("results-table", 3, ImGuiTableFlags_BordersH|ImGuiTableFlags_ScrollY|ImGuiTableFlags_RowBg|ImGuiTableFlags_SizingFixedFit, + {(float)window_w-7.0f, (float)result_area_height})) + { + int nr_w = 50; + int line_w = 120; + int file_w = ImGui::GetWindowWidth() - line_w - nr_w; + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoHeaderLabel, nr_w); + ImGui::TableSetupColumn("File", 0, file_w); + ImGui::TableSetupColumn("Match", 0, line_w); + ImGui::TableHeadersRow(); + + int itemcount = current_search_result == 0 ? 0 : current_search_result->matches.length; + found_file* prev_file = nullptr; + for (int item = 0; item < itemcount; item++) + { + file_match *file = (file_match *)array_at(¤t_search_result->matches, item); + + if (prev_file != file->file) { + prev_file = file->file; + char match_info_txt[20]; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TableHeader(""); + + ImGui::TableNextColumn(); + ImGui::TableHeader(file->file->path); + + ImGui::TableNextColumn(); + snprintf(match_info_txt, 20, "%d match(es)", file->file->match_count); + ImGui::TableHeader(match_info_txt); + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("#%d", item+1); + + ImGui::TableNextColumn(); + ImGui::Text("%.*s", file->word_match_offset, file->line_info); + ImGui::SameLine(); + ImGui::TextColored({255,0,0,255}, "%.*s", file->word_match_length, file->line_info + file->word_match_offset); + ImGui::SameLine(); + ImGui::TextUnformatted(file->line_info + file->word_match_offset + file->word_match_length); + + ImGui::TableNextColumn(); + ImGui::Text("line %d", file->line_nr); + } + ImGui::EndTable(); + } + } + pos_y += result_area_height; + + { // Statusbar + ImGui::SetNextWindowPos({0, pos_y}); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 6)); + ImGui::BeginChild("search-statusbar", ImVec2(window_w, statusbar_area_height), ImGuiChildFlags_None, ImGuiWindowFlags_None); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 7.0f); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.0f); + if (current_search_result) ImGui::Text("Found %d matches in %d files", current_search_result->match_count, current_search_result->file_count); + else ImGui::Text("No search completed"); + + ImGui::SameLine(); + + ImGui::SetCursorPosX(window_w - 10.0f - ImGui::CalcTextSize("no search completed").x); + ImGui::Text("no search completed"); + ImGui::EndChild(); + ImGui::PopStyleVar(); + } + + ImGui::PopStyleVar(); + + ImGui::End(); +} \ No newline at end of file diff --git a/src/main_windows.cpp b/src/main_windows.cpp new file mode 100644 index 0000000..3a1d2e8 --- /dev/null +++ b/src/main_windows.cpp @@ -0,0 +1,372 @@ +// Dear ImGui: standalone example application for Win32 + OpenGL 3 + +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// This is provided for completeness, however it is strongly recommended you use OpenGL with SDL or GLFW. + +#include + +#include "../imgui/imgui.h" +#include "../imgui/imgui_spectrum.h" +#include "../imgui/imgui_impl_opengl3.h" +#include "../imgui/imgui_impl_win32.h" +#include "../utf8.h" +#include "platform.h" +#include "mutex.h" +#include "array.h" +#include "memory_bucket.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + +#define IDI_LOGO 123 + +void ts_create_gui(int window_w, int window_h); +void ts_load_images(); + +// Data stored per platform window +struct WGL_WindowData { HDC hDC; }; + +// Data +static HGLRC g_hRC; +static WGL_WindowData g_MainWindow; +static int g_Width; +static int g_Height; + +// Forward declarations of helper functions +bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data); +void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data); +void ResetDeviceWGL(); +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Main code +int main(int, char**) +{ + // Create application window + //ImGui_ImplWin32_EnableDpiAwareness(); + WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr }; + ::RegisterClassExW(&wc); + HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Text-Search", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr); + + // Initialize OpenGL + if (!CreateDeviceWGL(hwnd, &g_MainWindow)) + { + CleanupDeviceWGL(hwnd, &g_MainWindow); + ::DestroyWindow(hwnd); + ::UnregisterClassW(wc.lpszClassName, wc.hInstance); + return 1; + } + wglMakeCurrent(g_MainWindow.hDC, g_hRC); + + // Show the window + ::ShowWindow(hwnd, SW_SHOWDEFAULT); + ::UpdateWindow(hwnd); + + HICON hicoCaption = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_LOGO)); + SendMessage(hwnd, WM_SETICON, ICON_BIG, + reinterpret_cast(hicoCaption)); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.IniFilename = NULL; + + // Setup Dear ImGui style + ImGui::Spectrum::StyleColorsSpectrum(); + ImGui::Spectrum::LoadFont(18.0f); + //ImGui::StyleColorsLight(); + + // Setup Platform/Renderer backends + ImGui_ImplWin32_InitForOpenGL(hwnd); + ImGui_ImplOpenGL3_Init(); + + ts_load_images(); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. + // - Read 'docs/FONTS.md' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != nullptr); + + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + bool done = false; + while (!done) + { + MSG msg; + while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + if (msg.message == WM_QUIT) + done = true; + } + if (done) + break; + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + ts_create_gui(g_Width, g_Height); + + // Rendering + ImGui::Render(); + glViewport(0, 0, g_Width, g_Height); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Present + ::SwapBuffers(g_MainWindow.hDC); + } + + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + + CleanupDeviceWGL(hwnd, &g_MainWindow); + wglDeleteContext(g_hRC); + ::DestroyWindow(hwnd); + ::UnregisterClassW(wc.lpszClassName, wc.hInstance); + + return 0; +} + +// Helper functions +bool CreateDeviceWGL(HWND hWnd, WGL_WindowData* data) +{ + HDC hDc = ::GetDC(hWnd); + PIXELFORMATDESCRIPTOR pfd = { 0 }; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + + const int pf = ::ChoosePixelFormat(hDc, &pfd); + if (pf == 0) + return false; + if (::SetPixelFormat(hDc, pf, &pfd) == FALSE) + return false; + ::ReleaseDC(hWnd, hDc); + + data->hDC = ::GetDC(hWnd); + if (!g_hRC) + g_hRC = wglCreateContext(data->hDC); + return true; +} + +void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data) +{ + wglMakeCurrent(nullptr, nullptr); + ::ReleaseDC(hWnd, data->hDC); +} + +// Forward declare message handler from imgui_impl_win32.cpp +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +// Win32 message handler +// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. +// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. +// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. +// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. +LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) + return true; + + switch (msg) + { + case WM_GETMINMAXINFO: + { + MINMAXINFO *minmax = (MINMAXINFO *)lParam; + minmax->ptMinTrackSize.x = 800; + minmax->ptMinTrackSize.y = 600; + break; + } + case WM_SIZE: + if (wParam != SIZE_MINIMIZED) + { + g_Width = LOWORD(lParam); + g_Height = HIWORD(lParam); + } + return 0; + case WM_SYSCOMMAND: + if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu + return 0; + break; + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + return ::DefWindowProcW(hWnd, msg, wParam, lParam); +} + +file_content platform_read_file(char *path, const char *mode) +{ + file_content result; + result.content = 0; + result.content_length = 0; + result.file_error = 0; + + FILE *file = fopen(path, mode); + if (!file) + { + if (errno == EMFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS; + else if (errno == ENFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM; + else if (errno == EACCES) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == EPERM) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == ENOENT) + result.file_error = FILE_ERROR_NOT_FOUND; + else if (errno == ECONNABORTED) + result.file_error = FILE_ERROR_CONNECTION_ABORTED; + else if (errno == ECONNREFUSED) + result.file_error = FILE_ERROR_CONNECTION_REFUSED; + else if (errno == ENETDOWN) + result.file_error = FILE_ERROR_NETWORK_DOWN; + else + { + result.file_error = FILE_ERROR_GENERIC; + printf("ERROR: %d\n", errno); + } + + goto done_failure; + } + + fseek(file, 0 , SEEK_END); + int length = ftell(file); + fseek(file, 0, SEEK_SET); + + int length_to_alloc = length+1; + + result.content = malloc(length_to_alloc); + if (!result.content) goto done; + + memset(result.content, 0, length); + int read_result = fread(result.content, 1, length, file); + if (read_result == 0 && length != 0) + { + free(result.content); + result.content = 0; + return result; + } + + result.content_length = read_result; + + ((char*)result.content)[length] = 0; + + done: + fclose(file); + done_failure: + return result; +} + +static void *_list_files_thread(void *args) +{ + search_result *info = (search_result *)args; + platform_list_files_block(info); + info->done_finding_files = true; + return 0; +} + +void platform_list_files(search_result* result) +{ + thread thr = thread_start(_list_files_thread, (void*)result); + thread_detach(&thr); +} + +void platform_list_files_block(search_result* result, wchar_t* start_dir) +{ + // Utf8 to wchar str + wchar_t* search_dir = (wchar_t*)malloc(MAX_INPUT_LENGTH); + if (start_dir == nullptr) { + MultiByteToWideChar(CP_UTF8, 0, result->directory_to_search, strlen(result->directory_to_search), search_dir, MAX_INPUT_LENGTH); + } + else { + wcscpy(search_dir, start_dir); + } + + // Append wildcard + wchar_t* search_dir_fix = (wchar_t*)malloc(MAX_INPUT_LENGTH); + wcscpy(search_dir_fix, search_dir); + wcscat(search_dir_fix, L"\\*"); + + WIN32_FIND_DATAW file_info; + HANDLE handle = FindFirstFileW(search_dir_fix, &file_info); + + if (handle == INVALID_HANDLE_VALUE) + { + return; + } + + do + { + //if (*is_cancelled) break; + const wchar_t *name = (const wchar_t *)file_info.cFileName; + + // symbolic link is not allowed.. + if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + continue; + + + if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + if ((wcscmp(name, L".") == 0) || (wcscmp(name, L"..") == 0)) + continue; + + wchar_t* subdir_buffer_path = (wchar_t*)malloc(MAX_INPUT_LENGTH); + wcscpy(subdir_buffer_path, search_dir); + wcscat(subdir_buffer_path, L"\\"); + wcscat(subdir_buffer_path, name); + platform_list_files_block(result, subdir_buffer_path); + } + else if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) || + (file_info.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)) + { + wchar_t complete_file_path[MAX_INPUT_LENGTH]; + wcscpy(complete_file_path, search_dir); + wcscat(complete_file_path, L"\\"); + wcscat(complete_file_path, name); + + found_file f; + f.path = (utf8_int8_t*)malloc(MAX_INPUT_LENGTH); + f.match_count = 0; + WideCharToMultiByte(CP_UTF8,0,complete_file_path,-1,(LPSTR)f.path,MAX_INPUT_LENGTH, NULL, NULL); + + mutex_lock(&result->files.mutex); + array_push_size(&result->files, &f, sizeof(found_file)); + mutex_unlock(&result->files.mutex); + + } + } + while (FindNextFile(handle, (LPWIN32_FIND_DATAW)&file_info) != 0); + + FindClose(handle); +} \ No newline at end of file diff --git a/src/memory_bucket.cpp b/src/memory_bucket.cpp new file mode 100644 index 0000000..71cc79a --- /dev/null +++ b/src/memory_bucket.cpp @@ -0,0 +1,68 @@ +#include "memory_bucket.h" + +memory_bucket memory_bucket_init(int bucket_size) +{ + memory_bucket collection; + collection.bucket_mutex = mutex_create(); + collection.buckets = array_create(sizeof(memory_bucket_entry)); + + memory_bucket_entry bucket; + bucket.data = (char*)malloc(bucket_size); + bucket.length = bucket_size; + bucket.cursor = 0; + array_push(&collection.buckets, &bucket); + return collection; +} + +void* memory_bucket_reserve(memory_bucket *bucket, int reserve_length) +{ + mutex_lock(&bucket->bucket_mutex); + memory_bucket_entry *bucket_entry = 0; + for (int i = 0; i < bucket->buckets.length; i++) + { + bucket_entry = (memory_bucket_entry *)array_at(&bucket->buckets, i); + + if (bucket_entry->length - bucket_entry->cursor < reserve_length) continue; + + void *space = bucket_entry->data+bucket_entry->cursor; + bucket_entry->cursor += reserve_length; + mutex_unlock(&bucket->bucket_mutex); + + return space; + } + + // failed to find suitable space, allocate new bucket + memory_bucket_entry new_bucket; + new_bucket.data = (char*)malloc(bucket_entry->length); + new_bucket.length = bucket_entry->length; + new_bucket.cursor = 0; + array_push(&bucket->buckets, &new_bucket); + mutex_unlock(&bucket->bucket_mutex); + + return new_bucket.data; +} + +inline void memory_bucket_reset(memory_bucket *bucket) +{ + mutex_lock(&bucket->bucket_mutex); + for (int i = 0; i < bucket->buckets.length; i++) + { + memory_bucket_entry *bucket_entry = (memory_bucket_entry *)array_at(&bucket->buckets, i); + bucket_entry->cursor = 0; + } + mutex_unlock(&bucket->bucket_mutex); +} + +inline void memory_bucket_destroy(memory_bucket *bucket) +{ + mutex_lock(&bucket->bucket_mutex); + for (int i = 0; i < bucket->buckets.length; i++) + { + memory_bucket_entry *bucket_entry = (memory_bucket_entry *)array_at(&bucket->buckets, i); + free(bucket_entry->data); + } + array_destroy(&bucket->buckets); + mutex_unlock(&bucket->bucket_mutex); + + mutex_destroy(&bucket->bucket_mutex); +} \ No newline at end of file diff --git a/src/memory_bucket.h b/src/memory_bucket.h new file mode 100644 index 0000000..f203804 --- /dev/null +++ b/src/memory_bucket.h @@ -0,0 +1,28 @@ +#ifndef INCLUDE_MEMORY_BUCKET +#define INCLUDE_MEMORY_BUCKET + +#define kilobytes(num) num*1000 +#define megabytes(num) kilobytes(num*1000) + +#include "mutex.h" +#include "array.h" + +typedef struct t_memory_bucket_entry +{ + char *data; + int length; + int cursor; +} memory_bucket_entry; + +typedef struct t_memory_bucket +{ + mutex bucket_mutex; + array buckets; +} memory_bucket; + +memory_bucket memory_bucket_init(int bucket_size); +void* memory_bucket_reserve(memory_bucket *bucket, int reserve_length); +void memory_bucket_reset(memory_bucket *bucket); +void memory_bucket_destroy(memory_bucket *bucket); + +#endif \ No newline at end of file diff --git a/src/mutex.cpp b/src/mutex.cpp new file mode 100644 index 0000000..089e5bd --- /dev/null +++ b/src/mutex.cpp @@ -0,0 +1,102 @@ +#include "mutex.h" + +mutex mutex_create() +{ + mutex result; + result.mutex = CreateMutex( + NULL, // default security attributes + FALSE, // initially not owned + NULL); // unnamed mutex + + return result; +} + +mutex mutex_create_recursive() +{ + return mutex_create(); +} + +void mutex_lock(mutex *mutex) +{ + WaitForSingleObject( + mutex->mutex, // handle to mutex + INFINITE); // no time-out interval +} + +int mutex_trylock(mutex *mutex) +{ + return WaitForSingleObject(mutex->mutex, 1) == WAIT_OBJECT_0; +} + +void mutex_unlock(mutex *mutex) +{ + ReleaseMutex(mutex->mutex); +} + +void mutex_destroy(mutex *mutex) +{ + CloseHandle(mutex->mutex); +} + + +thread thread_start(void *(*start_routine) (void *), void *arg) +{ + thread result; + result.valid = 0; + + result.thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, + arg, 0, NULL); + result.valid = 1; + + return result; +} + +void thread_join(thread *thread) +{ + if (thread->valid) + { + WaitForSingleObject(thread->thread, INFINITE); + CloseHandle(thread->thread); + } +} + +int thread_tryjoin(thread *thread) +{ + if (thread->valid) + { + int result = WaitForSingleObject(thread->thread, 0); + return result == WAIT_OBJECT_0; + } + return 0; +} + +void thread_detach(thread *thread) +{ + if (thread->valid) + { + CloseHandle(thread->thread); + } +} + +void thread_stop(thread *thread) +{ + if (thread->valid) + { + SuspendThread(thread->thread); + } +} + +int thread_get_id() +{ + return GetCurrentThreadId(); +} + +void thread_exit() +{ + ExitThread(0); +} + +void thread_sleep(int microseconds) +{ + Sleep(microseconds/1000); +} \ No newline at end of file diff --git a/src/mutex.h b/src/mutex.h new file mode 100644 index 0000000..a061c2e --- /dev/null +++ b/src/mutex.h @@ -0,0 +1,42 @@ +#ifndef INCLUDE_MUTEX +#define INCLUDE_MUTEX + +#ifdef _WIN32 +#include +#include /* _beginthread, _endthread */ +#include +#include +#include +#include + +typedef struct t_mutex +{ + HANDLE mutex; +} mutex; + +typedef struct t_thread +{ + HANDLE thread; + int valid; +} thread; +#endif + +thread thread_start(void *(*start_routine) (void *), void *arg); +void thread_join(thread *thread); +int thread_tryjoin(thread *thread); +void thread_detach(thread *thread); +void thread_stop(thread *thread); +int thread_get_id(); +void thread_sleep(int microseconds); +void thread_exit(); + + +mutex mutex_create_recursive(); +mutex mutex_create(); +void mutex_lock(mutex *mutex); +int mutex_trylock(mutex *mutex); +void mutex_unlock(mutex *mutex); +void mutex_destroy(mutex *mutex); + + +#endif \ No newline at end of file diff --git a/src/platform.h b/src/platform.h new file mode 100644 index 0000000..66cdcb1 --- /dev/null +++ b/src/platform.h @@ -0,0 +1,35 @@ +#ifndef INCLUDE_PLATFORM +#define INCLUDE_PLATFORM + +#include "array.h" +#include "memory_bucket.h" +#include "search.h" +#include "../utf8.h" + +typedef struct t_file_content +{ + int content_length; + void *content; + int file_error; +} file_content; + +typedef enum t_file_open_error +{ + FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS = 1, + FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM = 2, + FILE_ERROR_NO_ACCESS = 3, + FILE_ERROR_NOT_FOUND = 4, + FILE_ERROR_CONNECTION_ABORTED = 5, + FILE_ERROR_CONNECTION_REFUSED = 6, + FILE_ERROR_NETWORK_DOWN = 7, + FILE_ERROR_REMOTE_IO_ERROR = 8, + FILE_ERROR_STALE = 9, // NFS server file is removed/renamed + FILE_ERROR_GENERIC = 10, + FILE_ERROR_TOO_BIG = 11, +} file_open_error; + +file_content platform_read_file(char *path, const char *mode); +void platform_list_files_block(search_result* result, wchar_t* start_dir = nullptr); +void platform_list_files(search_result* result); + +#endif \ No newline at end of file diff --git a/src/search.cpp b/src/search.cpp new file mode 100644 index 0000000..c764d71 --- /dev/null +++ b/src/search.cpp @@ -0,0 +1,237 @@ +#include "search.h" +#include "platform.h" + +array get_filters(char *pattern) +{ + array result = array_create(MAX_INPUT_LENGTH); + + char current_filter[MAX_INPUT_LENGTH]; + int filter_len = 0; + while(*pattern) + { + char ch = *pattern; + + if (ch == ',') + { + current_filter[filter_len] = 0; + array_push(&result, current_filter); + filter_len = 0; + } + else + { + if(filter_len < MAX_INPUT_LENGTH-1) + { + current_filter[filter_len++] = ch; + } + else + { + current_filter[filter_len] = ch; + } + } + + pattern++; + } + current_filter[filter_len] = 0; + array_push(&result, current_filter); + + return result; +} + +int string_match(char *first, char *second) +{ + // If we reach at the end of both strings, we are done + if (*first == '\0' && *second == '\0') + return 1; + + // Make sure that the characters after '*' are present + // in second string. This function assumes that the first + // string will not contain two consecutive '*' + if (*first == '*' && *(first+1) != '\0' && *second == '\0') + return 0; + + // If the first string contains '?', or current characters + // of both strings string_match + if (*first == '?' || *first == *second) + return string_match(first+1, second+1); + + // If there is *, then there are two possibilities + // a) We consider current character of second string + // b) We ignore current character of second string. + if (*first == '*') + return string_match(first+1, second) || string_match(first, second+1); + return 0; +} + + +int filter_matches(array *filters, char *string, char **matched_filter) +{ + for (int i = 0; i < filters->length; i++) + { + char *filter = (char *)array_at(filters, i); + if (string_match(filter, string)) + { + *matched_filter = filter; + return strlen(filter); + } + } + return -1; +} + +search_result *create_empty_search_result() +{ + search_result *new_result_buffer = (search_result *)malloc(sizeof(search_result)); + new_result_buffer->completed_match_threads = 0; + new_result_buffer->mutex = mutex_create(); + new_result_buffer->done_finding_files = false; + new_result_buffer->file_list_read_cursor = 0; + new_result_buffer->max_thread_count = 4; + new_result_buffer->match_count = 0; + new_result_buffer->file_count = 0; + new_result_buffer->max_file_size = megabytes(1000); + + new_result_buffer->files = array_create(sizeof(found_file)); + new_result_buffer->files.reserve_jump = FILE_RESERVE_COUNT; + array_reserve(&new_result_buffer->files, FILE_RESERVE_COUNT); + + new_result_buffer->matches = array_create(sizeof(file_match)); + new_result_buffer->matches.reserve_jump = FILE_RESERVE_COUNT; + array_reserve(&new_result_buffer->matches, FILE_RESERVE_COUNT); + + // filter buffers + new_result_buffer->directory_to_search = (char*)malloc(MAX_INPUT_LENGTH); + new_result_buffer->search_text = (char*)malloc(MAX_INPUT_LENGTH); + + return new_result_buffer; +} + +bool string_is_asteriks(char *text) +{ + utf8_int32_t ch; + while((text = utf8codepoint(text, &ch)) && ch) + { + if (ch != '*') return false; + } + return true; +} + +bool string_contains_ex(char *text_to_search, utf8_int8_t *text_to_find, array *text_matches) +{ + bool final_result = false; + bool is_asteriks_only = false; + + // * wildcard at the start of text to find is not needed + if (string_is_asteriks(text_to_find)) + { + is_asteriks_only = true; + text_to_find += strlen(text_to_find); + } + + // remove all asteriks from start + utf8_int32_t br; + while(utf8codepoint(text_to_find, &br) && br == '*') + { + text_to_find = utf8codepoint(text_to_find, &br); + } + + char *text_to_find_original = text_to_find; + bool save_info = (text_matches != 0); + + utf8_int32_t text_to_search_ch = 0; + utf8_int32_t text_to_find_ch = 0; + size_t text_to_find_char_len = utf8len(text_to_find); + + int line_nr_val = 1; + int word_offset_val = 0; + int word_match_len_val = 0; + char* line_start_ptr = text_to_search; + + int index = 0; + while((text_to_search = utf8codepoint(text_to_search, &text_to_search_ch)) + && text_to_search_ch) + { + word_offset_val++; + if (text_to_search_ch == '\n') + { + line_nr_val++; + word_offset_val = 0; + line_start_ptr = text_to_search; + } + + utf8_int8_t *text_to_search_current_attempt = text_to_search; + utf8_int32_t text_to_search_current_attempt_ch = text_to_search_ch; + + bool in_wildcard = false; + + text_to_find = utf8codepoint(text_to_find, &text_to_find_ch); + //text_to_search_current_attempt = utf8codepoint(text_to_search_current_attempt, + //&text_to_search_current_attempt_ch); + + word_match_len_val = 0; + while(text_to_search_current_attempt_ch) + { + // wildcard, accept any character in text to search + if (text_to_find_ch == '?') + goto continue_search; + + // character matches, + if (text_to_find_ch == text_to_search_current_attempt_ch && in_wildcard) + in_wildcard = false; + + // wildcard, accept any characters in text to search untill next char is found + if (text_to_find_ch == '*') + { + text_to_find = utf8codepoint(text_to_find, &text_to_find_ch); + in_wildcard = true; + } + + // text to find has reached 0byte, word has been found + if (text_to_find_ch == 0) + { + done: + if (save_info) + { + text_match new_match; + new_match.line_nr = line_nr_val; + new_match.word_offset = word_offset_val-1; + new_match.word_match_len = word_match_len_val; + new_match.line_start = line_start_ptr; + new_match.line_info = 0; + array_push(text_matches, &new_match); + } + + final_result = true; + + if (is_asteriks_only) + { + return final_result; + } + + break; + } + + // character does not match, continue search + if (text_to_find_ch != text_to_search_current_attempt_ch && !in_wildcard) + break; + + continue_search: + if (!in_wildcard) + text_to_find = utf8codepoint(text_to_find, &text_to_find_ch); + + text_to_search_current_attempt = utf8codepoint( + text_to_search_current_attempt, + &text_to_search_current_attempt_ch); + + if (!text_to_search_current_attempt_ch && !text_to_find_ch) goto done; + + word_match_len_val++; + } + + text_to_find = text_to_find_original; + index++; + } + + return final_result; + + set_info_and_return_failure: + return false; +} \ No newline at end of file diff --git a/src/search.h b/src/search.h new file mode 100644 index 0000000..ebb98f3 --- /dev/null +++ b/src/search.h @@ -0,0 +1,64 @@ +#ifndef INCLUDE_SEARCH +#define INCLUDE_SEARCH + +#define MAX_INPUT_LENGTH 4096 +#define MAX_ERROR_MESSAGE_LENGTH (MAX_INPUT_LENGTH) +#define FILE_RESERVE_COUNT 100000 +#define ERROR_RESERVE_COUNT 100 + +#include "array.h" +#include "memory_bucket.h" +#include "../utf8.h" + +typedef struct t_found_file +{ + utf8_int8_t *path; + int match_count; +} found_file; + +typedef struct t_search_result +{ + // data + array files; + array matches; + int match_count; + int file_count; + + // thread syncing + mutex mutex; + int completed_match_threads; + int done_finding_files; + int file_list_read_cursor; + + // search query + utf8_int8_t *directory_to_search; + utf8_int8_t *search_text; + int max_thread_count; + int max_file_size; +} search_result; + +typedef struct t_file_match +{ + found_file* file; + int line_nr; + int word_match_offset; + int word_match_length; + utf8_int8_t *line_info; // will be null when no match is found +} file_match; + +typedef struct t_text_match +{ + int line_nr; + int word_offset; + int word_match_len; + char *line_start; + char *line_info; +} text_match; + +array get_filters(char *pattern); +int filter_matches(array *filters, char *string, char **matched_filter); +int string_match(char *first, char *second); +search_result *create_empty_search_result(); +bool string_contains_ex(char *text_to_search, char *text_to_find, array *text_matches); + +#endif \ No newline at end of file -- cgit v1.2.3-70-g09d2