dict @ d102e028aee6571c0fd9dfd4074cfb3c15f4594e

ref: Refactor newer folder structure

Create a lib dict and importer project.

* dict: holds the main application
* importer: code to read from source to a common database.
* lib: shared code
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f253be6cd6bd7109239ee826e9c19e0ce59e03b2..a88fc556f01258b3ea3de17980acb4881a84de81 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,12 +2,6 @@ cmake_minimum_required(VERSION 3.26)
 
 project(dict VERSION 0.1 LANGUAGES C)
 
-file(GLOB src CONFIGURE_DEPENDS "*.h" "*.c")
-add_executable(dict ${src})
-
-target_compile_options(dict PRIVATE -Wall -Wextra -Wpedantic -Werror)
-target_include_directories(dict PUBLIC "${PROJECT_BINARY_DIR}")
-target_link_libraries(dict sqlite3 ncursesw m c)
-
-
 add_subdirectory(ext)
+add_subdirectory(importer)
+add_subdirectory(lib)
diff --git a/data.c b/lib/data.c
rename from data.c
rename to lib/data.c
index 2c3f3bb0bbc811c9a8c3552e84d60f8c50714892..7ebf597ca8ab317c76ac1960882f19669994d18e 100644
--- a/data.c
+++ b/lib/data.c
@@ -1,10 +1,11 @@
+#include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
 
 #include "data.h"
 
 const char *insert_into = "INSERT INTO words (LINE) VALUES($VVV);";
-const char *select_words = "SELECT Id, Line FROM words LIMIT 10;";
+const char *select_words = "SELECT Id, Line FROM words WHERE line like $VVV LIMIT 10;";
 const char *create_table =  "CREATE TABLE IF NOT EXISTS words (ID INTEGER PRIMARY KEY AUTOINCREMENT, LINE TEXT NOT NULL);";
 
 Data* new_data(const char* con) {
@@ -42,7 +43,6 @@         printf("\n");
         return;
     }
 
-    // binds the paremets to the statement, in this case the line;
     sqlite3_bind_text(stmt, 1, line, len, NULL);
 
     int c = sqlite3_step(stmt);
@@ -76,7 +76,7 @@
     sqlite3_finalize(stmt);
 }
 
-LIST* data_select(Data* data) {
+LIST* data_select(Data* data, char *sch, int len) {
     sqlite3_stmt *stmt; 
     int r = sqlite3_prepare_v2(data->db, select_words, -1, &stmt, NULL);
 
@@ -89,15 +89,19 @@     }
 
     LIST *list = NULL;
 
+    sqlite3_bind_text(stmt, 1, sch, len, NULL);
+
     int m =  sqlite3_step(stmt);
     while(m == SQLITE_ROW) {
         Word *word = (Word*)malloc(sizeof(Word));
 
         int id = sqlite3_column_int(stmt, 0);
         const unsigned char *line = sqlite3_column_text(stmt, 1);
+        unsigned char *line2 = malloc(sizeof(char*)+strlen((char*)line));
+        memcpy(line2, line, strlen((char*)line));
 
         word->Id = id;
-        word->Line = line;
+        word->Line = line2;
         list = list_add(list, word);
 
         m = sqlite3_step(stmt);
diff --git a/data.h b/lib/data.h
rename from data.h
rename to lib/data.h
index 8bc30e9396446df274a6dd39022d5e32f5d42033..56edd34841971064ac48a0a3acae93ce5d7690aa 100644
--- a/data.h
+++ b/lib/data.h
@@ -39,7 +39,7 @@
 /*
  * Select all words.
  */
-LIST* data_select(Data*);
+LIST* data_select(Data*, char*, int);
 
 /*
  * Print result code from sqlite.
diff --git a/dict/CMakeLists.txt b/dict/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..051635bf187ec677b57b1a26edb60050bc63b50a
--- /dev/null
+++ b/dict/CMakeLists.txt
@@ -0,0 +1,6 @@
+file(GLOB src CONFIGURE_DEPENDS "*.c")
+add_executable(dict ${src})
+
+target_compile_options(dict PRIVATE -Wall -Wextra -Wpedantic -Werror)
+target_include_directories(dict PUBLIC "${PROJECT_BINARY_DIR}")
+target_link_libraries(dict sqlite3 ncursesw m c lib)
diff --git a/dict/main.c b/dict/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..8240b756f55aa57482b2a2fa13a0cbb00fa7f58d
--- /dev/null
+++ b/dict/main.c
@@ -0,0 +1,111 @@
+#include <unistd.h>
+#include <string.h>
+#include <locale.h>
+#include <stdio.h>
+#include <sqlite3.h>
+#include <ncurses.h>
+
+#include "../lib/data.h"
+#include "../lib/ui.h"
+#include "../lib/util.h"
+
+Data *data;
+
+void search(char*, int);
+int run(const char*, const char*);
+
+int main(int argc, char** argv) {
+    int opt;
+    char* txt = NULL;
+    char* db = NULL;
+
+    while ((opt = getopt(argc, argv, "t:d:h")) != -1) {
+        switch(opt) {
+            case 't':
+                txt = copy_achar(optarg);
+                break;
+            case 'd':
+                db = copy_achar(optarg);
+                break;
+            case 'h':
+                  // fall through
+            default:
+                printf("Usage: %s", argv[0]);
+                goto end;
+        }
+    }
+
+    int r = run(db, txt);
+
+end:
+    if (txt != NULL)
+        free(txt);
+    if (db != NULL)
+        free(db);
+
+    return r;
+}
+
+int run(const char *db, const char *txt) {
+    data = new_data(db);
+    bootstrap(data);
+
+    setlocale(LC_ALL, "");
+    initscr(); 
+    noecho();
+    cbreak();
+    keypad(stdscr, TRUE);
+
+    FILE *f = fopen(txt, "r");
+    unsigned int lines = count_file_lines(f);
+    fseek(f, 0, SEEK_SET);
+
+    char * line = NULL;
+    size_t len = 0;
+    ssize_t read;
+    PROGRESS_BAR *bar = new_progress_bar(stdscr, lines);
+    while ((read = getline(&line, &len, f)) != -1) {
+        if (line[0] == '#' || line[0] == '\n')
+            continue;
+
+        insert(data, line, read-1);
+        bar_step(bar, 1);
+    }
+
+    move(2,0);
+    printw("Saving db...");
+    refresh();
+    load_or_save_db(data->db, "backup.db", 1);
+
+    clear();
+    refresh();
+
+    TEXT_BOX *box = new_text_box(stdscr, 100);
+    get_char(box, search);
+
+    clear();
+    refresh();
+
+    endwin();
+
+    free_data(data);
+    return 0;
+}
+
+void search(char *sch, int len) {
+    char s[len+2];
+
+    sprintf(s, "%%%*s%%", len, sch);
+
+    LIST* l = data_select(data, s, len+2);
+
+    for (int y = 1; y < 20; y++) {
+        move(y, 0);
+        Word *item = (Word*)list_get(l, y);
+        if (item != NULL)
+            printw("%s", item->Line);
+    }
+    refresh();
+}
+
+
diff --git a/importer/CMakeLists.txt b/importer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..587952ece1ff225046dfb50ce6c0a9f896f50cd7
--- /dev/null
+++ b/importer/CMakeLists.txt
@@ -0,0 +1,9 @@
+project(dict_importer VERSION 0.1 LANGUAGES C)
+
+file(GLOB src CONFIGURE_DEPENDS "*.c")
+add_executable(dict_importer ${src})
+
+target_compile_options(dict_importer PRIVATE -Wall -Wextra -Wpedantic -Werror)
+target_include_directories(dict_importer PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}")
+target_link_libraries(dict_importer sqlite3 lib)
+
diff --git a/importer/main.c b/importer/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..a1850f830b62b9e0166f1a72735883e56f338003
--- /dev/null
+++ b/importer/main.c
@@ -0,0 +1,71 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "../lib/util.h"
+#include "../lib/data.h"
+
+int run(const char *db, const char *txt);
+
+int main(int argc, char** argv) {
+    int opt;
+    char* txt = NULL;
+    char* db = NULL;
+
+    while ((opt = getopt(argc, argv, "t:d:h")) != -1) {
+        switch(opt) {
+            case 't':
+                txt = copy_achar(optarg);
+                break;
+            case 'd':
+                db = copy_achar(optarg);
+                break;
+            case 'h':
+                  // fall through
+            default:
+                printf("Usage: %s", argv[0]);
+                goto end;
+        }
+    }
+
+
+    int r = run(db, txt);
+
+end:
+    if (txt != NULL)
+        free(txt);
+    if (db != NULL)
+        free(db);
+
+    return r;
+}
+
+int run(const char *db, const char *txt) {
+    char * line = NULL;
+    size_t len = 0;
+    int count = 0;
+    ssize_t read;
+    Data *data;
+    FILE *f;
+    int total;
+
+    data = new_data(":memory:");
+    f = fopen(txt, "r");
+
+    bootstrap(data);
+
+    total = count_file_lines(f);
+    fseek(f, 0, SEEK_SET);
+
+    while ((read = getline(&line, &len, f)) != -1) {
+        if (line[0] == '#' || line[0] == '\n')
+            continue;
+
+        insert(data, line, read-1);
+
+        float t = ((float)count/(float)total)*100;
+        printf("\rLoading data [%03.0f%%] %d/%d", t, count, total);
+    }
+
+    return load_or_save_db(data->db, db, 1);
+}
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..023cdf1338199643645914f83bd5af6dfb3297b3
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,5 @@
+file(GLOB lib CONFIGURE_DEPENDS "*.h" "*.c")
+
+add_library(lib ${lib})
+target_compile_options(lib PRIVATE -Wall -Wextra -Wpedantic -Werror)
+target_link_libraries(lib sqlite3 ncursesw m c)
diff --git a/lib/util.c b/lib/util.c
new file mode 100644
index 0000000000000000000000000000000000000000..6720082ac9e4f530f85d26e47416487702fac414
--- /dev/null
+++ b/lib/util.c
@@ -0,0 +1,61 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "util.h"
+
+#define BUF_SIZE 100
+
+char* copy_achar(const char* src) {
+    int len = strlen(src) + 1;
+    char* dest = (char*)malloc(sizeof(char)*len);
+    strcpy(dest, src);
+
+    return dest;
+}
+
+
+int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave){
+    int rc;                   /* Function return code */
+    sqlite3 *pFile;           /* Database connection opened on zFilename */
+    sqlite3_backup *pBackup;  /* Backup object used to copy data */
+    sqlite3 *pTo;             /* Database to copy to (pFile or pInMemory) */
+    sqlite3 *pFrom;           /* Database to copy from (pFile or pInMemory) */
+
+    rc = sqlite3_open(zFilename, &pFile);
+    if( rc==SQLITE_OK ){
+        pFrom = (isSave ? pInMemory : pFile);
+        pTo   = (isSave ? pFile     : pInMemory);
+
+        pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
+        if( pBackup ){
+            (void)sqlite3_backup_step(pBackup, -1);
+            (void)sqlite3_backup_finish(pBackup);
+        }
+        rc = sqlite3_errcode(pTo);
+    }
+
+    (void)sqlite3_close(pFile);
+    return rc;
+}
+
+unsigned int count_file_lines(FILE *file) {
+    char buf[BUF_SIZE];
+    unsigned int counter = 0;
+    for(;;)
+    {
+        size_t res = fread(buf, 1, BUF_SIZE, file);
+        if (ferror(file))
+            return -1;
+
+        size_t i;
+        for(i = 0; i < res; i++)
+            if (buf[i] == '\n')
+                counter++;
+
+        if (feof(file))
+            break;
+    }
+
+    return counter;
+}
diff --git a/lib/util.h b/lib/util.h
new file mode 100644
index 0000000000000000000000000000000000000000..c03dbae7654b1ac748acd438ed6ab0f4975a2427
--- /dev/null
+++ b/lib/util.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <sqlite3.h>
+#include <stdio.h>
+
+/*
+ * Copy of a char to a newly created string of the same size.
+ */
+char* copy_achar(const char*);
+
+
+int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave);
+
+
+unsigned int count_file_lines(FILE *file);
diff --git a/list.c b/lib/list.c
rename from list.c
rename to lib/list.c
index a40dd576db2dcc385a4bb2f4fb8f6f4e99102793..fc0fddfd3e82dc239d36563fd7953e5522aa1ff0 100644
--- a/list.c
+++ b/lib/list.c
@@ -42,3 +42,14 @@
     free(list->list);
     free(list);
 }
+
+
+void *list_get(LIST *list, unsigned int index) {
+    if (list == NULL)
+        return NULL;
+
+    if (index < list->size) 
+        return list->list[index];
+
+    return NULL;
+}
diff --git a/list.h b/lib/list.h
rename from list.h
rename to lib/list.h
index e33bf017753ff3c93db620c320e024cd2b18ec8a..dd28722a34ace46877267b9eea10edb154148bdf 100644
--- a/list.h
+++ b/lib/list.h
@@ -22,6 +22,8 @@ * Remove an item from a given list
 * @list: array list structure.
 * @pos: position of item to be removed.
 */
-LIST* list_remove(LIST* list, unsigned int pos);
+LIST *list_remove(LIST *list, unsigned int pos);
 
 void list_free(LIST* list);
+
+void *list_get(LIST *list, unsigned int index);
diff --git a/main.c b/main.c
deleted file mode 100644
index 026b4b5e244137c725d6f3a6e21f789a7f9bf88f..0000000000000000000000000000000000000000
--- a/main.c
+++ /dev/null
@@ -1,104 +0,0 @@
-#include <locale.h>
-#include <stdio.h>
-#include <sqlite3.h>
-#include <ncurses.h>
-#include "data.h"
-#include "ui.h"
-
-#define BUF_SIZE 100
-
-unsigned int count_lines(FILE* file);
-int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave);
-
-int main() {
-    Data *data = new_data(":memory:");
-    bootstrap(data);
-
-    setlocale(LC_ALL, "");
-    noecho();
-    cbreak();
-	nonl();
-    keypad(stdscr, TRUE);
-    initscr(); 
-
-    FILE *f = fopen("dict.txt", "r");
-    unsigned int lines = count_lines(f);
-    fseek(f, 0, SEEK_SET);
-
-    char * line = NULL;
-    size_t len = 0;
-    ssize_t read;
-    PROGRESS_BAR *bar = new_progress_bar(stdscr, lines);
-    while ((read = getline(&line, &len, f)) != -1) {
-        if (line[0] == '#' || line[0] == '\n')
-            continue;
-
-        insert(data, line, read-1);
-        bar_step(bar, 1);
-    }
-
-    move(2,0);
-    printw("Saving db...");
-    refresh();
-    load_or_save_db(data->db, "backup.db", 1);
-
-    clear();
-    refresh();
-
-    TEXT_BOX *box = new_text_box(stdscr, 10);
-    get_char(box);
-
-    clear();
-    refresh();
-
-    endwin();
-
-    free_data(data);
-    return 0;
-}
-
-int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave){
-  int rc;                   /* Function return code */
-  sqlite3 *pFile;           /* Database connection opened on zFilename */
-  sqlite3_backup *pBackup;  /* Backup object used to copy data */
-  sqlite3 *pTo;             /* Database to copy to (pFile or pInMemory) */
-  sqlite3 *pFrom;           /* Database to copy from (pFile or pInMemory) */
-
-  rc = sqlite3_open(zFilename, &pFile);
-  if( rc==SQLITE_OK ){
-    pFrom = (isSave ? pInMemory : pFile);
-    pTo   = (isSave ? pFile     : pInMemory);
-
-    pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
-    if( pBackup ){
-      (void)sqlite3_backup_step(pBackup, -1);
-      (void)sqlite3_backup_finish(pBackup);
-    }
-    rc = sqlite3_errcode(pTo);
-  }
-
-  (void)sqlite3_close(pFile);
-  return rc;
-}
-
-unsigned int count_lines(FILE* file)
-{
-    char buf[BUF_SIZE];
-    unsigned int counter = 0;
-    for(;;)
-    {
-        size_t res = fread(buf, 1, BUF_SIZE, file);
-        if (ferror(file))
-            return -1;
-
-        size_t i;
-        for(i = 0; i < res; i++)
-            if (buf[i] == '\n')
-                counter++;
-
-        if (feof(file))
-            break;
-    }
-
-    return counter;
-}
diff --git a/ui.c b/lib/ui.c
rename from ui.c
rename to lib/ui.c
index 9762859cc6dadb17f0c06acc2387f0027477af5f..1a285a00e407566b783917c1541883301ab838c7 100644
--- a/ui.c
+++ b/lib/ui.c
@@ -58,7 +58,7 @@     memset(text->text, '\0', length);
     return text;
 }
 
-void get_char(TEXT_BOX* text) {
+void get_char(TEXT_BOX* text, void (*sch)(char*, int)) {
     while(1){
         wchar_t c;
         get_wch((wint_t*)&c);
@@ -75,6 +75,11 @@                     text->text[text->current] = c;
                     text->text[++text->current] = '\0';
                 }
         }
+
+        char str[text->length];
+        wcstombs(str, text->text, sizeof(text->text));
+        sch(str, (int)strlen(str));
+
         move(0,0);
         wrefresh(text->scr);
         wprintw(text->scr, "%*ls", text->current,text->text);
diff --git a/ui.h b/lib/ui.h
rename from ui.h
rename to lib/ui.h
index cdc0539b61f5d717ee843d9ac164d6c18c54ca7a..90b352ff9f0cd7dcc853c3e6695fb9ce74d2b10a 100644
--- a/ui.h
+++ b/lib/ui.h
@@ -1,3 +1,4 @@
+#pragma once
 #include <ncurses.h>
 
 typedef struct progress_bar {
@@ -18,5 +19,5 @@     WINDOW *scr;
 } TEXT_BOX;
 
 TEXT_BOX* new_text_box(WINDOW*, int);
-void get_char(TEXT_BOX* text);
+void get_char(TEXT_BOX* text, void (*sch)(char*, int));