.
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CC=gcc
2 | TARGET_EXEC := pbook
3 | BUILD_DIR := ./build
4 | INSTALL_DIR := /usr/local/bin
5 | SRC_DIRS := ./src
6 | SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
7 | OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
8 | DEPS := $(OBJS:.o=.d)
9 | INC_DIRS := $(shell find $(SRC_DIRS) -type d) ./
10 | INC_FLAGS := $(addprefix -I,$(INC_DIRS))
11 | CFLAGS := $(INC_FLAGS) -MMD -MP -Wall --debug -Wextra -pedantic -std=c99
12 | LDFLAGS := $(shell pkg-config --cflags --libs ncurses form menu)
13 | $(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
14 | $(CC) $(OBJS) -o $@ $(LDFLAGS)
15 |
16 | $(BUILD_DIR)/%.c.o: %.c
17 | mkdir -p $(dir $@)
18 | $(CC) $(CFLAGS) $(CFLAGS) -c $< -o $@
19 |
20 | .PHONY: clean install uninstall
21 |
22 | clean:
23 | rm -r $(BUILD_DIR)
24 |
25 | install: $(BUILD_DIR)/$(TARGET_EXEC)
26 | sudo install -m 755 $< $(INSTALL_DIR)/$(TARGET_EXEC)
27 |
28 | uninstall: $(INSTALL_DIR)/$(TARGET_EXEC)
29 | rm $(INSTALL_DIR)/$(TARGET_EXEC)
30 |
31 |
32 | -include $(DEPS)
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | pbook
6 |
7 |
8 | A phonebook for TUI lovers!
9 |
10 |
11 | pbook is a simple, easy to use phonebook manager,
12 | that stores it's database in a simple csv like file.
13 |
14 |
15 | ### Preview🙉:
16 | 
17 |
18 | ### How to install⬇️:
19 |
20 | 0. Be sure to have `pkg-config`, `ncurses`, `make`, `C compiler` Installed( They should be present in all debian based distros).
21 |
22 | 1. Clone this repo.
23 | ```shell
24 | git clone https://github.com/proh14/pbook.git
25 | cd pbook
26 | ```
27 | 2. Run `make` inside this repo's main directory.
28 | ```shell
29 | make
30 | ```
31 |
32 | 3. go to build directory
33 | ```shell
34 | cd build
35 | ```
36 | 4. run pbook!
37 | ```shell
38 | ./pbook
39 | ```
40 |
41 | OR to install you may run the command
42 | ```shell
43 | make install
44 | ```
45 | then restart your shell and run
46 | ```shell
47 | pbook
48 | ```
49 | to start the program
50 |
51 | ### Features⌨️:
52 | 1. Add's and Remove's contacts to it's database.
53 | 2. Edit's an existing contact.
54 | 3. Can store: name,number,email,birthday,Addres1,Addres2
55 |
56 | ### Work in progress⚒️:
57 | I am spending my free time trying to optimize, fix bugs, add new features to this program.
58 | you can see the [TODO](./TODO.md) file for a list of things I want to do.
59 |
60 | ### Thank you Stargazers⭐:
61 | [](https://github.com/proh14/pbook/stargazers)
62 |
63 | ### Thank you Forkers🍴:
64 | [](https://github.com/proh14/pbook/network/members)
65 |
66 | ### A huge thanks to🙏:
67 | 1. [Ncurses programming HOWTO](https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/)
68 | 2. [Ncurses manpages](https://linux.die.net/man/3/ncurses)
69 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | - [ ] Make the program more memory efficent.
2 | - [ ] Add a config file for the user.
3 | - [ ] Allow for custom fields.
4 | - [ ] Allow sorting the phonebook.
5 | - [ ] Make the program more efficent.
6 | - [ ] Add gpg encryption.
7 | - [ ] Support multiply file formats like: plain text, csv, xml, mutt.
8 | - [x] Fix the usage of ',' in forms.
9 | - [ ] Allow for duplicate names.
10 | - [ ] Check for screen boudries.
11 | - [ ] Add support for vCards.
12 |
--------------------------------------------------------------------------------
/compile_flags.txt:
--------------------------------------------------------------------------------
1 | -Wall
2 | --debug
3 | -Wextra
4 | -pedantic
5 | -std=c99
6 | -I./src/include
7 | -I./
8 | -MMD
9 | -MP
10 |
--------------------------------------------------------------------------------
/config.def.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #ifndef _CONFIG_H_
3 | #define _CONFIG_H_
4 |
5 | // USAGE: Enable the usage of XDG paths if defined as 1 (in
6 | // ~/.local/share/pbook/contacts.pbook)
7 | #define USE_XDG 0
8 |
9 | // USAGE: Defines the name and path of the detabase file.
10 | // NOTE: Do not use ~ or $HOME in the path it will be added itself
11 | // defenition.
12 | #define DBFILE "/.contacts.pbook"
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proh14/pbook/c16d5904f9b32ecfaeb02fd8f140af7dde22f3df/images/logo.png
--------------------------------------------------------------------------------
/images/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/proh14/pbook/c16d5904f9b32ecfaeb02fd8f140af7dde22f3df/images/preview.gif
--------------------------------------------------------------------------------
/src/contacts.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | char *FILENAME;
10 |
11 | bool contactMatch(person *p1, person *p2) {
12 | if (strcmp(p1->name, p2->name) != 0)
13 | return false;
14 | if (strcmp(p1->email, p2->email) != 0)
15 | return false;
16 | if (strcmp(p1->numbers, p2->numbers) != 0)
17 | return false;
18 | if (strcmp(p1->address, p2->address) != 0)
19 | return false;
20 | if (strcmp(p1->address2, p2->address2) != 0)
21 | return false;
22 | if (strcmp(p1->birthday, p2->birthday) != 0)
23 | return false;
24 | return true;
25 | }
26 |
27 | void addContact(person *p) {
28 | FILE *fp = fopen(FILENAME, "a");
29 | fprintf(fp, "%s,%s,%s,%s,%s,%s\n", p->name, p->numbers, p->address,
30 | p->address2, p->email, p->birthday);
31 | fclose(fp);
32 | }
33 |
34 | void fillContact(person *p) {
35 | strcpy(p->name, "none");
36 | strcpy(p->numbers, "none");
37 | strcpy(p->email, "none");
38 | strcpy(p->birthday, "none");
39 | strcpy(p->address, "none");
40 | strcpy(p->address2, "none");
41 | }
42 |
43 | int readContact(namepair *n, FILE *fp) {
44 | char line[sizeof(person)];
45 | void *ret = fgets(line, sizeof(line), fp);
46 |
47 | if (!ret) {
48 | return EOF;
49 | }
50 |
51 | char *token = strtok(line, ",");
52 | strcpy(n->name, token);
53 | token = strtok(NULL, ",");
54 | strcpy(n->numbers, token);
55 |
56 | return 1;
57 | }
58 |
59 | void editContact(person *o, person *p) {
60 | FILE *fp = fopen(FILENAME, "r");
61 | FILE *fp2 = fopen("temp.txt", "w");
62 | char line[sizeof(person)];
63 | while (fgets(line, sizeof(line), fp)) {
64 | char *token = strtok(line, ",");
65 | if (strcmp(token, o->name) == 0) {
66 | fprintf(fp2, "%s,%s,%s,%s,%s,%s\n", p->name, p->numbers, p->address,
67 | p->address2, p->email, p->birthday);
68 | } else {
69 | line[strlen(token)] = ',';
70 | fprintf(fp2, "%s", line);
71 | }
72 | }
73 | fclose(fp);
74 | fclose(fp2);
75 | remove(FILENAME);
76 | rename("temp.txt", FILENAME);
77 | }
78 |
79 | void deleteContact(person *p) {
80 | FILE *fp = fopen(FILENAME, "r");
81 | FILE *fp2 = fopen("temp.txt", "w");
82 | char line[sizeof(person)];
83 | while (fgets(line, sizeof(line), fp)) {
84 | char *token = strtok(line, ",");
85 | if (strcmp(token, p->name) != 0) {
86 | line[strlen(token)] = ',';
87 | fprintf(fp2, "%s", line);
88 | }
89 | }
90 | fclose(fp);
91 | fclose(fp2);
92 | remove(FILENAME);
93 | rename("temp.txt", FILENAME);
94 | }
95 |
96 | person *searchContact(const char *name) {
97 | FILE *fp = fopen(FILENAME, "r");
98 | char line[sizeof(person)];
99 | while (fgets(line, sizeof(line), fp) != NULL) {
100 | char *token = strtok(line, ",");
101 | if (strcmp(token, name) == 0) {
102 | person *p = malloc(sizeof(person));
103 | strcpy(p->name, token);
104 | token = strtok(NULL, ",");
105 | strcpy(p->numbers, token);
106 | token = strtok(NULL, ",");
107 | strcpy(p->address, token);
108 | token = strtok(NULL, ",");
109 | strcpy(p->address2, token);
110 | token = strtok(NULL, ",");
111 | strcpy(p->email, token);
112 | token = strtok(NULL, ",");
113 | strcpy(p->birthday, token);
114 | fclose(fp);
115 | return p;
116 | }
117 | }
118 | fclose(fp);
119 | return NULL;
120 | }
121 |
122 | void initContact(void) {
123 | int file_size = 15;
124 | char *home = getenv("HOME");
125 | if (home == NULL) {
126 | printf("HOME not set.\n");
127 | exit(1);
128 | }
129 |
130 | #if USE_XDG == 0
131 | const char *path = "/.contacts.pbook";
132 | #else
133 | const char *path = "/.local/share/pbook/contacts.pbook";
134 | file_size = 14;
135 | #endif
136 | FILENAME = malloc(strlen(home) + strlen(path) + 1);
137 | strcpy(FILENAME, home);
138 | strcat(FILENAME, path);
139 | FILENAME[strlen(path) + strlen(home) - file_size] = '\0';
140 | struct stat st = {0};
141 | if (stat(FILENAME, &st) == -1) {
142 | mkdir(FILENAME, 0700);
143 | }
144 | FILENAME[strlen(path) + strlen(home) - file_size] =
145 | (file_size == 15) ? '.' : 'c';
146 | FILE *fp = fopen(FILENAME, "ab+");
147 | fclose(fp);
148 | }
149 |
150 | void endContact(void) { free(FILENAME); }
151 |
--------------------------------------------------------------------------------
/src/form_handler.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | struct field_settings {
9 | int max;
10 | char *name;
11 | };
12 |
13 | const struct field_settings opts[] = {
14 | {MAX_NAME, "Name: "}, {MAX_NUMBERS, "Phone number: "},
15 | {MAX_EMAIL, "Email: "}, {MAX_BIRTHDAY, "Birthday: "},
16 | {MAX_ADDRESS, "Adress 1: "}, {MAX_ADDRESS, "Adress 2: "},
17 | };
18 |
19 | void init_fields(FIELD *fields[], int num, int w_lines) {
20 | int i = 0;
21 | int y = 2;
22 | while (i < num - 1) {
23 | fields[i] = new_field(1, 20, y, 15, 0, 0);
24 | field_opts_off(fields[i], O_STATIC);
25 | set_max_field(fields[i], opts[i].max);
26 | set_field_back(fields[i], A_UNDERLINE);
27 | field_opts_off(fields[i], O_AUTOSKIP);
28 | y += (w_lines) / num;
29 | i++;
30 | }
31 | }
32 |
33 | void set_field_buffers(person *p, FIELD *fields[]) {
34 | int i = 0;
35 | char *buffers[] = {
36 | p->name, p->numbers, p->email, p->birthday, p->address, p->address2,
37 | };
38 | while (fields[i] != NULL) {
39 | set_field_buffer(fields[i], 0, buffers[i]);
40 | i++;
41 | }
42 | }
43 |
44 | void free_fields(FIELD *fields[]) {
45 | int i;
46 | for (i = 0; fields[i] != NULL; i++) {
47 | free_field(fields[i]);
48 | }
49 | }
50 |
51 | void draw_field_names(WINDOW *subwindow, int num, int w_lines) {
52 | int i;
53 | int y = 2;
54 | for (i = 0; i < num - 1; i++) {
55 | mvwprintw(subwindow, y, 0, "%s", opts[i].name);
56 | y += (w_lines) / num;
57 | }
58 | }
59 |
60 | void set_person_from_field(FIELD *fields[], person *p) {
61 | char *buffers[] = {
62 | p->name, p->numbers, p->email, p->birthday, p->address, p->address2,
63 | };
64 | int i = 0;
65 | while (fields[i] != NULL) {
66 | strcpy(buffers[i], rcoms(rtrim(field_buffer(fields[i], 0))));
67 | i++;
68 | }
69 | }
70 |
71 | void init_form(FORM *form, WINDOW *win, WINDOW *subwindow) {
72 | set_form_win(form, win);
73 | set_form_sub(form, subwindow);
74 | }
75 |
76 | void do_form_key(FORM *form, int c) {
77 | switch (c) {
78 | case KEY_UP:
79 | form_driver(form, REQ_PREV_FIELD);
80 | form_driver(form, REQ_END_LINE);
81 | break;
82 | case KEY_DOWN:
83 | case 10:
84 | form_driver(form, REQ_NEXT_FIELD);
85 | form_driver(form, REQ_END_LINE);
86 | break;
87 | case KEY_RIGHT:
88 | form_driver(form, REQ_NEXT_CHAR);
89 | break;
90 | case KEY_LEFT:
91 | form_driver(form, REQ_PREV_CHAR);
92 | break;
93 | case KEY_BACKSPACE:
94 | case 127:
95 | form_driver(form, REQ_PREV_CHAR);
96 | form_driver(form, REQ_DEL_CHAR);
97 | break;
98 | case KEY_DC:
99 | form_driver(form, REQ_DEL_CHAR);
100 | break;
101 | default:
102 | form_driver(form, c);
103 | break;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/include/contacts.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #ifndef _CONTACTS_H_
4 | #define _CONTACTS_H_
5 | #include
6 |
7 | extern char *FILENAME;
8 |
9 | #define MAX_NAME 30
10 | #define MAX_NUMBERS 30
11 | #define MAX_BIRTHDAY 20
12 | #define MAX_ADDRESS 50
13 | #define MAX_EMAIL 20
14 |
15 | typedef struct person {
16 | char name[MAX_NAME];
17 | char numbers[MAX_NUMBERS];
18 | char email[MAX_EMAIL];
19 | char birthday[MAX_BIRTHDAY];
20 | char address[MAX_ADDRESS];
21 | char address2[MAX_ADDRESS];
22 | } person;
23 |
24 | typedef struct namepair {
25 | char name[MAX_NAME];
26 | char numbers[MAX_NUMBERS];
27 | } namepair;
28 |
29 | void addContact(person *p);
30 | void deleteContact(person *p);
31 | person *searchContact(const char *name);
32 | void editContact(person *o, person *n);
33 | int readContact(namepair *p, FILE *fp);
34 | void initContact(void);
35 | void endContact(void);
36 | void fillContact(person *p);
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/src/include/form_handler.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #ifndef _FORM_HANDLER_H_
3 | #define _FORM_HANDLER_H_
4 |
5 | #include
6 | #include
7 |
8 | void init_fields(FIELD *fields[], int num, int w_lines);
9 | void set_field_buffers(person *p, FIELD *fields[]);
10 | void free_fields(FIELD *fields[]);
11 | void draw_field_names(WINDOW *subwindow, int num, int w_lines);
12 | void set_person_from_field(FIELD *fields[], person *p);
13 | void init_form(FORM *form, WINDOW *win, WINDOW *subwindow);
14 | void do_form_key(FORM *form, int c);
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------
/src/include/menu_handler.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #ifndef _MENU_HANDLER_H_
3 | #define _MENU_HANDLER_H_
4 |
5 | #include
6 | #include
7 |
8 | namepair *init_items(ITEM ***items);
9 | void free_items(ITEM ***items);
10 | void init_menu(MENU *menu, WINDOW *win, WINDOW *subwindow, int w_lines);
11 | void update_menu(MENU *menu, ITEM ***itmes, namepair **np);
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/src/include/ui.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #ifndef _UI_H_
3 | #define _UI_H_
4 |
5 | #include
6 |
7 | void init_ui(void);
8 | void end_ui(void);
9 |
10 | void draw_menu(void);
11 |
12 | void draw_form(person *p);
13 |
14 | void draw_title(void);
15 |
16 | void draw_help(void);
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/src/include/utils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #ifndef _UTILS_H_
3 | #define _UTILS_H_
4 |
5 | int lineNumber(char *filename);
6 | char *rtrim(char *s);
7 | char *rcoms(char *s);
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/src/include/window_handler.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #ifndef _WINDOW_HANDLER_H_
3 | #define _WINDOW_HANDLER_H_
4 |
5 | #include
6 |
7 | void init_window(WINDOW **win, WINDOW **subwin, int *rows, int *cols);
8 | void delete_window(WINDOW *win, WINDOW *subwin);
9 | void reformat(WINDOW *win, int w_cols);
10 | void middlePrint(WINDOW *win, int starty, int startx, int width,
11 | const char *string);
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/src/menu_handler.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | namepair *init_items(ITEM ***items) {
8 | int lines = lineNumber(FILENAME);
9 |
10 | *items = malloc(sizeof(ITEM *) * (lines + 1));
11 |
12 | int i = 0;
13 | for (i = 0; i < lines + 1; i++) {
14 | (*items)[i] = NULL;
15 | }
16 |
17 | FILE *fp = fopen(FILENAME, "r");
18 | namepair *np = malloc(sizeof(namepair) * lines);
19 |
20 | i = 0;
21 | while (readContact(&np[i], fp) != EOF) {
22 | (*items)[i] = new_item(np[i].name, np[i].numbers);
23 | i++;
24 | }
25 | fclose(fp);
26 | return np;
27 | }
28 |
29 | void free_items(ITEM ***items) {
30 | int i;
31 | for (i = 0; (*items)[i] != NULL; i++) {
32 | free_item((*items)[i]);
33 | }
34 | free_item((*items)[i]);
35 | free(*items);
36 | }
37 |
38 | void init_menu(MENU *menu, WINDOW *win, WINDOW *subwindow, int w_lines) {
39 | set_menu_win(menu, win);
40 | set_menu_sub(menu, subwindow);
41 | set_menu_format(menu, w_lines - 4, 1);
42 |
43 | set_menu_mark(menu, "* ");
44 | }
45 |
46 | void update_menu(MENU *menu, ITEM ***items, namepair **np) {
47 | set_menu_items(menu, NULL);
48 | free_items(items);
49 | free(*np);
50 | *np = init_items(items);
51 | set_menu_items(menu, *items);
52 | }
53 |
--------------------------------------------------------------------------------
/src/pbook.c:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the pbook distribution
3 | * (https://github.com/proh14/pbook).
4 | *
5 | * Copyright (c) 2024 Hoorad Farrokh.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, version 3.
10 | *
11 | * This program is distributed in the hope that it will be useful, but
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | int main(void) {
25 | initContact();
26 | init_ui();
27 | draw_title();
28 | draw_help();
29 | draw_menu();
30 | end_ui();
31 | endContact();
32 | return 0;
33 | }
34 |
--------------------------------------------------------------------------------
/src/ui.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | static WINDOW *win;
13 | static WINDOW *subwindow;
14 | static WINDOW *helpwin;
15 | static int w_lines;
16 | static int w_cols;
17 |
18 | #define CTRL(k) ((k)&0x1f)
19 |
20 | void init_ui(void) {
21 | initscr();
22 | cbreak();
23 | noecho();
24 | raw();
25 | keypad(stdscr, TRUE);
26 | init_window(&win, &subwindow, &w_lines, &w_cols);
27 | helpwin = newwin(LINES - 1, COLS / 2, 1, w_cols);
28 | refresh();
29 | curs_set(0);
30 | }
31 |
32 | void draw_form(person *p) {
33 | curs_set(1);
34 | FORM *form;
35 | const int MAX_FIELDS = 7;
36 | FIELD *fields[MAX_FIELDS];
37 | for (int i = 0; i < MAX_FIELDS; i++) {
38 | fields[i] = NULL;
39 | }
40 | init_fields(fields, MAX_FIELDS, w_lines);
41 | set_field_buffers(p, fields);
42 |
43 | form = new_form(fields);
44 | init_form(form, win, subwindow);
45 |
46 | post_form(form);
47 | reformat(win, w_cols);
48 | draw_field_names(subwindow, MAX_FIELDS, w_lines);
49 |
50 | int c = 0;
51 |
52 | form_driver(form, REQ_END_LINE);
53 | while ((c = wgetch(win)) != CTRL('q')) {
54 | do_form_key(form, c);
55 | }
56 |
57 | set_person_from_field(fields, p);
58 |
59 | unpost_form(form);
60 |
61 | free_form(form);
62 | free_fields(fields);
63 | refresh();
64 | curs_set(0);
65 | }
66 |
67 | void draw_menu(void) {
68 | ITEM **items = {NULL};
69 | MENU *menu;
70 | namepair *np = init_items(&items);
71 |
72 | person *p;
73 | person cp;
74 |
75 | menu = new_menu(items);
76 | init_menu(menu, win, subwindow, w_lines);
77 |
78 | reformat(win, w_cols);
79 | middlePrint(win, 1, 0, w_cols, "CONTACTS");
80 |
81 | post_menu(menu);
82 |
83 | int c = 0;
84 |
85 | while (tolower((c = wgetch(win))) != CTRL('q')) {
86 | switch (c) {
87 | case KEY_DOWN:
88 | menu_driver(menu, REQ_DOWN_ITEM);
89 | break;
90 | case KEY_UP:
91 | menu_driver(menu, REQ_UP_ITEM);
92 | break;
93 | case 10:
94 | unpost_menu(menu);
95 | p = searchContact(item_name(current_item(menu)));
96 | if (p == NULL) {
97 | break;
98 | }
99 | cp = *p;
100 | draw_form(p);
101 | editContact(&cp, p);
102 | update_menu(menu, &items, &np);
103 | post_menu(menu);
104 | reformat(win, w_cols);
105 | free(p);
106 | break;
107 | case 'd':
108 | unpost_menu(menu);
109 | p = searchContact(item_name(current_item(menu)));
110 | if (p == NULL) {
111 | break;
112 | }
113 | deleteContact(p);
114 | update_menu(menu, &items, &np);
115 | reformat(win, w_cols);
116 | post_menu(menu);
117 | free(p);
118 | break;
119 | case 'a':
120 | unpost_menu(menu);
121 | p = malloc(sizeof(person));
122 | fillContact(p);
123 | draw_form(p);
124 | person *t = searchContact(p->name);
125 | if (t == NULL) {
126 | addContact(p);
127 | }
128 | free(t);
129 | update_menu(menu, &items, &np);
130 | reformat(win, w_cols);
131 | post_menu(menu);
132 | free(p);
133 | break;
134 | }
135 | wrefresh(win);
136 | }
137 |
138 | unpost_menu(menu);
139 | free_menu(menu);
140 | free_items(&items);
141 | free(np);
142 | }
143 |
144 | void draw_title(void) {
145 | const char *title = "pbook - a simple phonebook for TUI lovers";
146 | attron(A_REVERSE);
147 | mvprintw(0, 0, "%s", title);
148 | for (int i = strlen(title); i < COLS; i++) {
149 | mvprintw(0, i, " ");
150 | }
151 | attron(A_REVERSE);
152 | refresh();
153 | }
154 |
155 | void draw_help(void) {
156 | reformat(helpwin, COLS / 2);
157 | middlePrint(helpwin, 1, 1, COLS / 2, "HELP");
158 | mvwprintw(helpwin, 3, 1, "In menu:");
159 | mvwprintw(helpwin, 5, 1, " Use arrow keys to move aroud.");
160 | mvwprintw(helpwin, 6, 1, " Use 'Enter' to select/edit a contact");
161 | mvwprintw(helpwin, 7, 1, " Use 'a' to add a contact");
162 | mvwprintw(helpwin, 8, 1, " Use 'd' to remove a contact");
163 | mvwprintw(helpwin, 9, 1, " Use CTRL + q to exit");
164 |
165 | mvwprintw(helpwin, 12, 1, "In edit form:");
166 | mvwprintw(helpwin, 14, 1, " Use arrow keys to move aroud.");
167 | mvwprintw(helpwin, 15, 1, " Use 'Enter' to save that field a contact");
168 | mvwprintw(helpwin, 16, 1, " Use typical keys to delete a character");
169 | mvwprintw(helpwin, 17, 1, " Use CTRL + q to exit");
170 |
171 | mvwprintw(helpwin, 20, 1, "Database file: %s", FILENAME);
172 |
173 | wrefresh(helpwin);
174 | }
175 |
176 | void end_ui(void) {
177 | delete_window(win, subwindow);
178 | delwin(helpwin);
179 | endwin();
180 | }
181 |
--------------------------------------------------------------------------------
/src/utils.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | int lineNumber(char *filename) {
7 | FILE *fp = fopen(filename, "r");
8 | int lines = 0;
9 | int ch = 0;
10 | while ((ch = fgetc(fp)) != EOF) {
11 | if (ch == '\n') {
12 | lines++;
13 | }
14 | }
15 |
16 | fclose(fp);
17 | return lines;
18 | }
19 |
20 | char *rtrim(char *s) {
21 | for (int i = strlen(s) - 1; i > 0; i--) {
22 | if (!isspace(s[i])) {
23 | break;
24 | }
25 | s[i] = '\0';
26 | }
27 | return s;
28 | }
29 |
30 | char *rcoms(char *s) {
31 | for (int i = 0; s[i] != '\0'; i++) {
32 | if (s[i] == ',') {
33 | s[i] = '-';
34 | break;
35 | }
36 | }
37 | return s;
38 | }
39 |
--------------------------------------------------------------------------------
/src/window_handler.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | void init_window(WINDOW **win, WINDOW **subwindow, int *rows, int *cols) {
6 | *cols = COLS / 2;
7 | *rows = LINES - 1;
8 |
9 | *win = newwin(*rows, *cols, 1, 0);
10 | keypad(*win, TRUE);
11 |
12 | *subwindow = derwin(*win, *rows - 3, *cols - 1, 3, 1);
13 | }
14 |
15 | void delete_window(WINDOW *win, WINDOW *subwindow) {
16 | delwin(subwindow);
17 | delwin(win);
18 | }
19 |
20 | void reformat(WINDOW *win, int w_cols) {
21 | box(win, 0, 0);
22 | mvwaddch(win, 2, 0, ACS_LTEE);
23 | mvwaddch(win, 2, w_cols - 1, ACS_RTEE);
24 | mvwhline(win, 2, 1, ACS_HLINE, w_cols - 2);
25 | wrefresh(win);
26 | }
27 |
28 | // https://tldp.org/HOWTO/NCURSES-Programming-HOWTO
29 | // with modification
30 | void middlePrint(WINDOW *win, int starty, int startx, int width,
31 | const char *string) {
32 | int length, x, y;
33 | int temp;
34 |
35 | if (win == NULL)
36 | win = stdscr;
37 | getyx(win, y, x);
38 | if (startx != 0)
39 | x = startx;
40 | if (starty != 0)
41 | y = starty;
42 | if (width == 0)
43 | width = 80;
44 |
45 | length = strlen(string);
46 | temp = (width - length) / 2;
47 | x = startx + (int)temp;
48 | mvwprintw(win, y, x, "%s", string);
49 | refresh();
50 | }
51 |
--------------------------------------------------------------------------------