1diff --git a/CMakeLists.txt b/CMakeLists.txt
2index f253be6cd6bd7109239ee826e9c19e0ce59e03b2..a88fc556f01258b3ea3de17980acb4881a84de81 100644
3--- a/CMakeLists.txt
4+++ b/CMakeLists.txt
5@@ -2,12 +2,6 @@ cmake_minimum_required(VERSION 3.26)
6
7 project(dict VERSION 0.1 LANGUAGES C)
8
9-file(GLOB src CONFIGURE_DEPENDS "*.h" "*.c")
10-add_executable(dict ${src})
11-
12-target_compile_options(dict PRIVATE -Wall -Wextra -Wpedantic -Werror)
13-target_include_directories(dict PUBLIC "${PROJECT_BINARY_DIR}")
14-target_link_libraries(dict sqlite3 ncursesw m c)
15-
16-
17 add_subdirectory(ext)
18+add_subdirectory(importer)
19+add_subdirectory(lib)
20diff --git a/data.c b/lib/data.c
21rename from data.c
22rename to lib/data.c
23index 2c3f3bb0bbc811c9a8c3552e84d60f8c50714892..7ebf597ca8ab317c76ac1960882f19669994d18e 100644
24--- a/data.c
25+++ b/lib/data.c
26@@ -1,10 +1,11 @@
27+#include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30
31 #include "data.h"
32
33 const char *insert_into = "INSERT INTO words (LINE) VALUES($VVV);";
34-const char *select_words = "SELECT Id, Line FROM words LIMIT 10;";
35+const char *select_words = "SELECT Id, Line FROM words WHERE line like $VVV LIMIT 10;";
36 const char *create_table = "CREATE TABLE IF NOT EXISTS words (ID INTEGER PRIMARY KEY AUTOINCREMENT, LINE TEXT NOT NULL);";
37
38 Data* new_data(const char* con) {
39@@ -42,7 +43,6 @@ printf("\n");
40 return;
41 }
42
43- // binds the paremets to the statement, in this case the line;
44 sqlite3_bind_text(stmt, 1, line, len, NULL);
45
46 int c = sqlite3_step(stmt);
47@@ -76,7 +76,7 @@
48 sqlite3_finalize(stmt);
49 }
50
51-LIST* data_select(Data* data) {
52+LIST* data_select(Data* data, char *sch, int len) {
53 sqlite3_stmt *stmt;
54 int r = sqlite3_prepare_v2(data->db, select_words, -1, &stmt, NULL);
55
56@@ -89,15 +89,19 @@ }
57
58 LIST *list = NULL;
59
60+ sqlite3_bind_text(stmt, 1, sch, len, NULL);
61+
62 int m = sqlite3_step(stmt);
63 while(m == SQLITE_ROW) {
64 Word *word = (Word*)malloc(sizeof(Word));
65
66 int id = sqlite3_column_int(stmt, 0);
67 const unsigned char *line = sqlite3_column_text(stmt, 1);
68+ unsigned char *line2 = malloc(sizeof(char*)+strlen((char*)line));
69+ memcpy(line2, line, strlen((char*)line));
70
71 word->Id = id;
72- word->Line = line;
73+ word->Line = line2;
74 list = list_add(list, word);
75
76 m = sqlite3_step(stmt);
77diff --git a/data.h b/lib/data.h
78rename from data.h
79rename to lib/data.h
80index 8bc30e9396446df274a6dd39022d5e32f5d42033..56edd34841971064ac48a0a3acae93ce5d7690aa 100644
81--- a/data.h
82+++ b/lib/data.h
83@@ -39,7 +39,7 @@
84 /*
85 * Select all words.
86 */
87-LIST* data_select(Data*);
88+LIST* data_select(Data*, char*, int);
89
90 /*
91 * Print result code from sqlite.
92diff --git a/dict/CMakeLists.txt b/dict/CMakeLists.txt
93new file mode 100644
94index 0000000000000000000000000000000000000000..051635bf187ec677b57b1a26edb60050bc63b50a
95--- /dev/null
96+++ b/dict/CMakeLists.txt
97@@ -0,0 +1,6 @@
98+file(GLOB src CONFIGURE_DEPENDS "*.c")
99+add_executable(dict ${src})
100+
101+target_compile_options(dict PRIVATE -Wall -Wextra -Wpedantic -Werror)
102+target_include_directories(dict PUBLIC "${PROJECT_BINARY_DIR}")
103+target_link_libraries(dict sqlite3 ncursesw m c lib)
104diff --git a/dict/main.c b/dict/main.c
105new file mode 100644
106index 0000000000000000000000000000000000000000..8240b756f55aa57482b2a2fa13a0cbb00fa7f58d
107--- /dev/null
108+++ b/dict/main.c
109@@ -0,0 +1,111 @@
110+#include <unistd.h>
111+#include <string.h>
112+#include <locale.h>
113+#include <stdio.h>
114+#include <sqlite3.h>
115+#include <ncurses.h>
116+
117+#include "../lib/data.h"
118+#include "../lib/ui.h"
119+#include "../lib/util.h"
120+
121+Data *data;
122+
123+void search(char*, int);
124+int run(const char*, const char*);
125+
126+int main(int argc, char** argv) {
127+ int opt;
128+ char* txt = NULL;
129+ char* db = NULL;
130+
131+ while ((opt = getopt(argc, argv, "t:d:h")) != -1) {
132+ switch(opt) {
133+ case 't':
134+ txt = copy_achar(optarg);
135+ break;
136+ case 'd':
137+ db = copy_achar(optarg);
138+ break;
139+ case 'h':
140+ // fall through
141+ default:
142+ printf("Usage: %s", argv[0]);
143+ goto end;
144+ }
145+ }
146+
147+ int r = run(db, txt);
148+
149+end:
150+ if (txt != NULL)
151+ free(txt);
152+ if (db != NULL)
153+ free(db);
154+
155+ return r;
156+}
157+
158+int run(const char *db, const char *txt) {
159+ data = new_data(db);
160+ bootstrap(data);
161+
162+ setlocale(LC_ALL, "");
163+ initscr();
164+ noecho();
165+ cbreak();
166+ keypad(stdscr, TRUE);
167+
168+ FILE *f = fopen(txt, "r");
169+ unsigned int lines = count_file_lines(f);
170+ fseek(f, 0, SEEK_SET);
171+
172+ char * line = NULL;
173+ size_t len = 0;
174+ ssize_t read;
175+ PROGRESS_BAR *bar = new_progress_bar(stdscr, lines);
176+ while ((read = getline(&line, &len, f)) != -1) {
177+ if (line[0] == '#' || line[0] == '\n')
178+ continue;
179+
180+ insert(data, line, read-1);
181+ bar_step(bar, 1);
182+ }
183+
184+ move(2,0);
185+ printw("Saving db...");
186+ refresh();
187+ load_or_save_db(data->db, "backup.db", 1);
188+
189+ clear();
190+ refresh();
191+
192+ TEXT_BOX *box = new_text_box(stdscr, 100);
193+ get_char(box, search);
194+
195+ clear();
196+ refresh();
197+
198+ endwin();
199+
200+ free_data(data);
201+ return 0;
202+}
203+
204+void search(char *sch, int len) {
205+ char s[len+2];
206+
207+ sprintf(s, "%%%*s%%", len, sch);
208+
209+ LIST* l = data_select(data, s, len+2);
210+
211+ for (int y = 1; y < 20; y++) {
212+ move(y, 0);
213+ Word *item = (Word*)list_get(l, y);
214+ if (item != NULL)
215+ printw("%s", item->Line);
216+ }
217+ refresh();
218+}
219+
220+
221diff --git a/importer/CMakeLists.txt b/importer/CMakeLists.txt
222new file mode 100644
223index 0000000000000000000000000000000000000000..587952ece1ff225046dfb50ce6c0a9f896f50cd7
224--- /dev/null
225+++ b/importer/CMakeLists.txt
226@@ -0,0 +1,9 @@
227+project(dict_importer VERSION 0.1 LANGUAGES C)
228+
229+file(GLOB src CONFIGURE_DEPENDS "*.c")
230+add_executable(dict_importer ${src})
231+
232+target_compile_options(dict_importer PRIVATE -Wall -Wextra -Wpedantic -Werror)
233+target_include_directories(dict_importer PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}")
234+target_link_libraries(dict_importer sqlite3 lib)
235+
236diff --git a/importer/main.c b/importer/main.c
237new file mode 100644
238index 0000000000000000000000000000000000000000..a1850f830b62b9e0166f1a72735883e56f338003
239--- /dev/null
240+++ b/importer/main.c
241@@ -0,0 +1,71 @@
242+#include <stdlib.h>
243+#include <stdio.h>
244+#include <unistd.h>
245+
246+#include "../lib/util.h"
247+#include "../lib/data.h"
248+
249+int run(const char *db, const char *txt);
250+
251+int main(int argc, char** argv) {
252+ int opt;
253+ char* txt = NULL;
254+ char* db = NULL;
255+
256+ while ((opt = getopt(argc, argv, "t:d:h")) != -1) {
257+ switch(opt) {
258+ case 't':
259+ txt = copy_achar(optarg);
260+ break;
261+ case 'd':
262+ db = copy_achar(optarg);
263+ break;
264+ case 'h':
265+ // fall through
266+ default:
267+ printf("Usage: %s", argv[0]);
268+ goto end;
269+ }
270+ }
271+
272+
273+ int r = run(db, txt);
274+
275+end:
276+ if (txt != NULL)
277+ free(txt);
278+ if (db != NULL)
279+ free(db);
280+
281+ return r;
282+}
283+
284+int run(const char *db, const char *txt) {
285+ char * line = NULL;
286+ size_t len = 0;
287+ int count = 0;
288+ ssize_t read;
289+ Data *data;
290+ FILE *f;
291+ int total;
292+
293+ data = new_data(":memory:");
294+ f = fopen(txt, "r");
295+
296+ bootstrap(data);
297+
298+ total = count_file_lines(f);
299+ fseek(f, 0, SEEK_SET);
300+
301+ while ((read = getline(&line, &len, f)) != -1) {
302+ if (line[0] == '#' || line[0] == '\n')
303+ continue;
304+
305+ insert(data, line, read-1);
306+
307+ float t = ((float)count/(float)total)*100;
308+ printf("\rLoading data [%03.0f%%] %d/%d", t, count, total);
309+ }
310+
311+ return load_or_save_db(data->db, db, 1);
312+}
313diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
314new file mode 100644
315index 0000000000000000000000000000000000000000..023cdf1338199643645914f83bd5af6dfb3297b3
316--- /dev/null
317+++ b/lib/CMakeLists.txt
318@@ -0,0 +1,5 @@
319+file(GLOB lib CONFIGURE_DEPENDS "*.h" "*.c")
320+
321+add_library(lib ${lib})
322+target_compile_options(lib PRIVATE -Wall -Wextra -Wpedantic -Werror)
323+target_link_libraries(lib sqlite3 ncursesw m c)
324diff --git a/lib/util.c b/lib/util.c
325new file mode 100644
326index 0000000000000000000000000000000000000000..6720082ac9e4f530f85d26e47416487702fac414
327--- /dev/null
328+++ b/lib/util.c
329@@ -0,0 +1,61 @@
330+#include <stdlib.h>
331+#include <stdio.h>
332+#include <string.h>
333+
334+#include "util.h"
335+
336+#define BUF_SIZE 100
337+
338+char* copy_achar(const char* src) {
339+ int len = strlen(src) + 1;
340+ char* dest = (char*)malloc(sizeof(char)*len);
341+ strcpy(dest, src);
342+
343+ return dest;
344+}
345+
346+
347+int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave){
348+ int rc; /* Function return code */
349+ sqlite3 *pFile; /* Database connection opened on zFilename */
350+ sqlite3_backup *pBackup; /* Backup object used to copy data */
351+ sqlite3 *pTo; /* Database to copy to (pFile or pInMemory) */
352+ sqlite3 *pFrom; /* Database to copy from (pFile or pInMemory) */
353+
354+ rc = sqlite3_open(zFilename, &pFile);
355+ if( rc==SQLITE_OK ){
356+ pFrom = (isSave ? pInMemory : pFile);
357+ pTo = (isSave ? pFile : pInMemory);
358+
359+ pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
360+ if( pBackup ){
361+ (void)sqlite3_backup_step(pBackup, -1);
362+ (void)sqlite3_backup_finish(pBackup);
363+ }
364+ rc = sqlite3_errcode(pTo);
365+ }
366+
367+ (void)sqlite3_close(pFile);
368+ return rc;
369+}
370+
371+unsigned int count_file_lines(FILE *file) {
372+ char buf[BUF_SIZE];
373+ unsigned int counter = 0;
374+ for(;;)
375+ {
376+ size_t res = fread(buf, 1, BUF_SIZE, file);
377+ if (ferror(file))
378+ return -1;
379+
380+ size_t i;
381+ for(i = 0; i < res; i++)
382+ if (buf[i] == '\n')
383+ counter++;
384+
385+ if (feof(file))
386+ break;
387+ }
388+
389+ return counter;
390+}
391diff --git a/lib/util.h b/lib/util.h
392new file mode 100644
393index 0000000000000000000000000000000000000000..c03dbae7654b1ac748acd438ed6ab0f4975a2427
394--- /dev/null
395+++ b/lib/util.h
396@@ -0,0 +1,15 @@
397+#pragma once
398+
399+#include <sqlite3.h>
400+#include <stdio.h>
401+
402+/*
403+ * Copy of a char to a newly created string of the same size.
404+ */
405+char* copy_achar(const char*);
406+
407+
408+int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave);
409+
410+
411+unsigned int count_file_lines(FILE *file);
412diff --git a/list.c b/lib/list.c
413rename from list.c
414rename to lib/list.c
415index a40dd576db2dcc385a4bb2f4fb8f6f4e99102793..fc0fddfd3e82dc239d36563fd7953e5522aa1ff0 100644
416--- a/list.c
417+++ b/lib/list.c
418@@ -42,3 +42,14 @@
419 free(list->list);
420 free(list);
421 }
422+
423+
424+void *list_get(LIST *list, unsigned int index) {
425+ if (list == NULL)
426+ return NULL;
427+
428+ if (index < list->size)
429+ return list->list[index];
430+
431+ return NULL;
432+}
433diff --git a/list.h b/lib/list.h
434rename from list.h
435rename to lib/list.h
436index e33bf017753ff3c93db620c320e024cd2b18ec8a..dd28722a34ace46877267b9eea10edb154148bdf 100644
437--- a/list.h
438+++ b/lib/list.h
439@@ -22,6 +22,8 @@ * Remove an item from a given list
440 * @list: array list structure.
441 * @pos: position of item to be removed.
442 */
443-LIST* list_remove(LIST* list, unsigned int pos);
444+LIST *list_remove(LIST *list, unsigned int pos);
445
446 void list_free(LIST* list);
447+
448+void *list_get(LIST *list, unsigned int index);
449diff --git a/main.c b/main.c
450deleted file mode 100644
451index 026b4b5e244137c725d6f3a6e21f789a7f9bf88f..0000000000000000000000000000000000000000
452--- a/main.c
453+++ /dev/null
454@@ -1,104 +0,0 @@
455-#include <locale.h>
456-#include <stdio.h>
457-#include <sqlite3.h>
458-#include <ncurses.h>
459-#include "data.h"
460-#include "ui.h"
461-
462-#define BUF_SIZE 100
463-
464-unsigned int count_lines(FILE* file);
465-int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave);
466-
467-int main() {
468- Data *data = new_data(":memory:");
469- bootstrap(data);
470-
471- setlocale(LC_ALL, "");
472- noecho();
473- cbreak();
474- nonl();
475- keypad(stdscr, TRUE);
476- initscr();
477-
478- FILE *f = fopen("dict.txt", "r");
479- unsigned int lines = count_lines(f);
480- fseek(f, 0, SEEK_SET);
481-
482- char * line = NULL;
483- size_t len = 0;
484- ssize_t read;
485- PROGRESS_BAR *bar = new_progress_bar(stdscr, lines);
486- while ((read = getline(&line, &len, f)) != -1) {
487- if (line[0] == '#' || line[0] == '\n')
488- continue;
489-
490- insert(data, line, read-1);
491- bar_step(bar, 1);
492- }
493-
494- move(2,0);
495- printw("Saving db...");
496- refresh();
497- load_or_save_db(data->db, "backup.db", 1);
498-
499- clear();
500- refresh();
501-
502- TEXT_BOX *box = new_text_box(stdscr, 10);
503- get_char(box);
504-
505- clear();
506- refresh();
507-
508- endwin();
509-
510- free_data(data);
511- return 0;
512-}
513-
514-int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave){
515- int rc; /* Function return code */
516- sqlite3 *pFile; /* Database connection opened on zFilename */
517- sqlite3_backup *pBackup; /* Backup object used to copy data */
518- sqlite3 *pTo; /* Database to copy to (pFile or pInMemory) */
519- sqlite3 *pFrom; /* Database to copy from (pFile or pInMemory) */
520-
521- rc = sqlite3_open(zFilename, &pFile);
522- if( rc==SQLITE_OK ){
523- pFrom = (isSave ? pInMemory : pFile);
524- pTo = (isSave ? pFile : pInMemory);
525-
526- pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
527- if( pBackup ){
528- (void)sqlite3_backup_step(pBackup, -1);
529- (void)sqlite3_backup_finish(pBackup);
530- }
531- rc = sqlite3_errcode(pTo);
532- }
533-
534- (void)sqlite3_close(pFile);
535- return rc;
536-}
537-
538-unsigned int count_lines(FILE* file)
539-{
540- char buf[BUF_SIZE];
541- unsigned int counter = 0;
542- for(;;)
543- {
544- size_t res = fread(buf, 1, BUF_SIZE, file);
545- if (ferror(file))
546- return -1;
547-
548- size_t i;
549- for(i = 0; i < res; i++)
550- if (buf[i] == '\n')
551- counter++;
552-
553- if (feof(file))
554- break;
555- }
556-
557- return counter;
558-}
559diff --git a/ui.c b/lib/ui.c
560rename from ui.c
561rename to lib/ui.c
562index 9762859cc6dadb17f0c06acc2387f0027477af5f..1a285a00e407566b783917c1541883301ab838c7 100644
563--- a/ui.c
564+++ b/lib/ui.c
565@@ -58,7 +58,7 @@ memset(text->text, '\0', length);
566 return text;
567 }
568
569-void get_char(TEXT_BOX* text) {
570+void get_char(TEXT_BOX* text, void (*sch)(char*, int)) {
571 while(1){
572 wchar_t c;
573 get_wch((wint_t*)&c);
574@@ -75,6 +75,11 @@ text->text[text->current] = c;
575 text->text[++text->current] = '\0';
576 }
577 }
578+
579+ char str[text->length];
580+ wcstombs(str, text->text, sizeof(text->text));
581+ sch(str, (int)strlen(str));
582+
583 move(0,0);
584 wrefresh(text->scr);
585 wprintw(text->scr, "%*ls", text->current,text->text);
586diff --git a/ui.h b/lib/ui.h
587rename from ui.h
588rename to lib/ui.h
589index cdc0539b61f5d717ee843d9ac164d6c18c54ca7a..90b352ff9f0cd7dcc853c3e6695fb9ce74d2b10a 100644
590--- a/ui.h
591+++ b/lib/ui.h
592@@ -1,3 +1,4 @@
593+#pragma once
594 #include <ncurses.h>
595
596 typedef struct progress_bar {
597@@ -18,5 +19,5 @@ WINDOW *scr;
598 } TEXT_BOX;
599
600 TEXT_BOX* new_text_box(WINDOW*, int);
601-void get_char(TEXT_BOX* text);
602+void get_char(TEXT_BOX* text, void (*sch)(char*, int));
603