improved memory safety + misc QOL changes
Some checks failed
build_test / build (push) Failing after 2m8s

This commit is contained in:
PoliEcho 2025-04-14 18:53:54 +02:00
parent 2f2ec79575
commit c3074bc8d4
11 changed files with 332 additions and 123 deletions

View File

@ -2,6 +2,7 @@
CPPC = g++
CPPC_FLAGS = -std=c++23 -s -O3 -Wall -Wextra -Wno-write-strings -lncurses -lmenu -lform -lcups
DEBUG_FLAGS = -ggdb -std=c++23 -Wall -Wextra -Wno-write-strings -lncurses -lmenu -lform -lcups
DEBUG_ASANITIZE = -fsanitize=address -ggdb -fno-omit-frame-pointer -std=c++23 -lncurses -lmenu -lcups -Wall -Wextra -Wno-write-strings
SRC_PATH := src
@ -19,6 +20,9 @@ all: make-build-dir $(BIN_PATH)/ParaDocs
debug: CPPC_FLAGS = $(DEBUG_FLAGS)
debug: make-build-dir $(BIN_PATH)/ParaDocs
asan: CPPC_FLAGS = $(DEBUG_ASANITIZE)
asan: make-build-dir $(BIN_PATH)/ParaDocs
make-build-dir:
mkdir -p $(OBJ_PATH)
@ -39,4 +43,4 @@ install:
clean:
rm -fr build
.PHONY: all clean install debug
.PHONY: all clean install debug asan

View File

@ -1,7 +1,20 @@
#include <string_view>
#ifndef NAME
#define NAME "ParaDocs"
#define VERSION "0.0.1"
inline constexpr auto hash_djb2a(const std::string_view sv) {
unsigned long hash{5381};
for (unsigned char c : sv) {
hash = ((hash << 5) + hash) ^ c;
}
return hash;
}
inline constexpr auto operator"" _sh(const char* str, size_t len) {
return hash_djb2a(std::string_view{str, len});
}
#endif

View File

@ -5,22 +5,16 @@
#include <fstream>
#include <string>
#include <vector>
#include "memory.h"
#include "types.h"
std::vector<allocation> cups_allocated;
/**
* Displays a menu of available printers and prints content to the selected one
* @param content The text content to print
* @return true if printing was successful, false otherwise
*/
bool printDocument(const std::string& content) {
current_allocated = &cups_allocated;
// Initialize variables
bool success = false;
cups_dest_t* dests = nullptr;
int num_dests = 0;
ITEM** menu_items = nullptr;
MENU* menu = nullptr;
WINDOW* menu_win = nullptr;
std::vector<char*> item_names;
std::vector<char*> item_descriptions;
complete_menu printer_menu = {nullptr, nullptr, 0, nullptr};
// Write content to a temporary file
std::string tempFileName = "/tmp/print_temp_ParaDocs.txt";
@ -32,12 +26,20 @@ bool printDocument(const std::string& content) {
tempFile.close();
// Get printer destinations from CUPS
num_dests = cupsGetDests(&dests);
cups_dest_t* dests = nullptr;
int num_dests = cupsGetDests(&dests);
if (num_dests <= 0) {
remove(tempFileName.c_str());
return false; // No printers available
}
char** item_names = new char*[num_dests];
cups_allocated.push_back(
{CHAR_PTR_ARRAY, item_names, static_cast<size_t>(num_dests)});
char** item_descriptions = new char*[num_dests];
cups_allocated.push_back(
{CHAR_PTR_ARRAY, item_descriptions, static_cast<size_t>(num_dests)});
// Initialize ncurses
initscr();
start_color();
@ -47,7 +49,7 @@ bool printDocument(const std::string& content) {
try {
// Allocate memory for menu items
menu_items = new ITEM*[num_dests + 1];
printer_menu.items = new ITEM*[num_dests + 1];
// Prepare menu items for each printer
for (int i = 0; i < num_dests; i++) {
@ -62,7 +64,7 @@ bool printDocument(const std::string& content) {
// Allocate memory for name string
char* name_str = new char[display_name.length() + 1];
std::strcpy(name_str, display_name.c_str());
item_names.push_back(name_str);
item_names[i] = name_str;
// Get description if available
const char* desc =
@ -72,54 +74,55 @@ bool printDocument(const std::string& content) {
// Allocate memory for description string
char* desc_str = new char[description.length() + 1];
std::strcpy(desc_str, description.c_str());
item_descriptions.push_back(desc_str);
item_descriptions[i] = desc_str;
// Create menu item
menu_items[i] = new_item(name_str, desc_str);
if (!menu_items[i]) {
printer_menu.items[i] = new_item(name_str, desc_str);
if (!printer_menu.items[i]) {
throw std::runtime_error("Failed to create menu item");
}
}
menu_items[num_dests] = nullptr; // NULL-terminate the array
printer_menu.items[num_dests] = nullptr; // NULL-terminate the array
// Create the menu
menu = new_menu(menu_items);
if (!menu) {
printer_menu.menu = new_menu(printer_menu.items);
if (!printer_menu.menu) {
throw std::runtime_error("Failed to create menu");
}
// Set up menu window
int rows, cols;
getmaxyx(stdscr, rows, cols);
menu_win = newwin(rows - 4, cols - 4, 2, 2);
keypad(menu_win, TRUE);
printer_menu.win = newwin(rows - 4, cols - 4, 2, 2);
keypad(printer_menu.win, TRUE);
// Set menu window and sub window
set_menu_win(menu, menu_win);
set_menu_sub(menu, derwin(menu_win, rows - 6, cols - 6, 1, 1));
set_menu_format(menu, rows - 8, 1);
set_menu_mark(menu, " * ");
set_menu_win(printer_menu.menu, printer_menu.win);
set_menu_sub(printer_menu.menu,
derwin(printer_menu.win, rows - 6, cols - 6, 1, 1));
set_menu_format(printer_menu.menu, rows - 8, 1);
set_menu_mark(printer_menu.menu, " * ");
// Post the menu with instructions
mvprintw(0, 0, "Select a printer and press Enter:");
mvprintw(rows - 2, 0, "Press q to cancel");
refresh();
post_menu(menu);
wrefresh(menu_win);
post_menu(printer_menu.menu);
wrefresh(printer_menu.win);
// Menu interaction loop
int c;
while ((c = wgetch(menu_win)) != 'q') {
while ((c = wgetch(printer_menu.win)) != 'q') {
switch (c) {
case KEY_DOWN:
menu_driver(menu, REQ_DOWN_ITEM);
menu_driver(printer_menu.menu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(menu, REQ_UP_ITEM);
menu_driver(printer_menu.menu, REQ_UP_ITEM);
break;
case 10: // Enter key
{
ITEM* cur = current_item(menu);
ITEM* cur = current_item(printer_menu.menu);
int index = item_index(cur);
// Print the document to the selected printer
@ -142,7 +145,7 @@ bool printDocument(const std::string& content) {
break;
}
}
wrefresh(menu_win);
wrefresh(printer_menu.win);
}
} catch (const std::exception& e) {
mvprintw(0, 0, "Error: %s", e.what());
@ -151,39 +154,16 @@ bool printDocument(const std::string& content) {
}
exit_menu:
// Clean up resources
if (menu) {
unpost_menu(menu);
free_menu(menu);
}
if (menu_items) {
for (int i = 0; i < num_dests && menu_items[i]; i++) {
free_item(menu_items[i]);
}
delete[] menu_items;
}
// Free allocated strings
for (char* str : item_names) {
delete[] str;
}
for (char* str : item_descriptions) {
delete[] str;
}
if (menu_win) {
delwin(menu_win);
}
// End ncurses
endwin();
unpost_menu(printer_menu.menu);
wclear(printer_menu.win);
clear();
// Remove temp file
remove(tempFileName.c_str());
// Free CUPS destinations
cupsFreeDests(num_dests, dests);
delete_all(&cups_allocated);
return success;
}

View File

@ -12,7 +12,9 @@
#include <iostream>
#include <string>
#include <thread>
#include "memory.h"
/*
size_t spawn_menu(uint16_t begin_y, uint16_t begin_x, const char** choices,
size_t n_choices) {
ITEM** sp_items;
@ -22,26 +24,26 @@ size_t spawn_menu(uint16_t begin_y, uint16_t begin_x, const char** choices,
int i;
sp_items = new ITEM*[n_choices];
/* Create items */
// Create items
for (i = 0; i < n_choices; ++i) {
sp_items[i] = new_item(choices[i], choices[i]);
}
/* Crate menu */
// Crate menu
sp_menu = new_menu(sp_items);
/* Create the window to be associated with the menu */
// Create the window to be associated with the menu
sp_menu_win = newwin(10, 40, begin_y, begin_x);
keypad(sp_menu_win, TRUE);
/* Set main window and sub window */
// Set main window and sub window
set_menu_win(sp_menu, sp_menu_win);
set_menu_sub(sp_menu, derwin(sp_menu_win, 6, 38, begin_y - 1, begin_x - 3));
/* Set menu mark to the string " * " */
// Set menu mark to the string " * "
set_menu_mark(sp_menu, " * ");
/* Print a border around the main window and print a title */
// Print a border around the main window and print a title
box(sp_menu_win, 0, 0);
print_in_middle(sp_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));
mvwaddch(sp_menu_win, 2, 0, ACS_LTEE);
@ -50,7 +52,7 @@ size_t spawn_menu(uint16_t begin_y, uint16_t begin_x, const char** choices,
mvprintw(LINES - 2, 0, "F1 to exit");
refresh();
/* Post the menu */
// Post the menu
post_menu(sp_menu);
wrefresh(sp_menu_win);
@ -67,7 +69,7 @@ size_t spawn_menu(uint16_t begin_y, uint16_t begin_x, const char** choices,
}
size_t selected = item_index(current_item(sp_menu));
/* Unpost and free all the memory taken up */
// Unpost and free all the memory taken up
unpost_menu(sp_menu);
free_menu(sp_menu);
for (i = 0; i < n_choices; ++i)
@ -76,6 +78,7 @@ size_t spawn_menu(uint16_t begin_y, uint16_t begin_x, const char** choices,
return selected;
}
*/
void print_in_middle(WINDOW* win, int starty, int startx, int width,
char* string, chtype color) {
@ -157,9 +160,17 @@ std::string spawncmd() {
return std::string(cmd);
}
void async_clock(WINDOW* win) {
std::this_thread::sleep_for(std::chrono::milliseconds(150));
void async_clock(WINDOW* win, WINDOW* text_win) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
bool prev_echo_state;
uint16_t prev_y, prev_x;
while (true) {
prev_echo_state = is_echo();
getyx(stdscr, prev_y, prev_x);
noecho();
wborder(text_win, 0, ' ', 0, 0, 0, ACS_HLINE, 0, ACS_HLINE);
mvwprintw(text_win, 1, 1, "správný čas na podání trestního oznámení je");
wrefresh(text_win);
auto now =
std::chrono::current_zone()->to_local(std::chrono::system_clock::now());
std::string time_str;
@ -176,37 +187,30 @@ void async_clock(WINDOW* win) {
time_str = "Error";
}
// Clear window content but preserve border
werase(win);
wborder(win, 0, 0, 0, 0, ACS_TTEE, 0, ACS_BTEE, 0);
// Print the time in the window
mvwprintw(win, 1, 1, "%s", time_str.c_str());
wrefresh(win);
if (prev_echo_state) {
echo();
}
move(prev_y, prev_x);
refresh();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void show_clock_text(WINDOW* text_win) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
wborder(text_win, 0, ' ', 0, 0, 0, ACS_HLINE, 0, ACS_HLINE);
mvwprintw(text_win, 1, 1, "správný čas na podání trestního oznámení je");
wrefresh(text_win);
}
void async_clock_init() {
// program only launches sometimes i think there is race condition
//memory leak
WINDOW* win = newwin(3, 10, LINES - 3, COLS - 10);
box(win, 0, 0);
wrefresh(win);
std::thread clock_thread(async_clock, win);
clock_thread.detach();
current_allocated->push_back({WINDOW_TYPE, win, 1});
//memory leak
WINDOW* text_win = newwin(3, 52, LINES - 3, COLS - 10 - 44);
current_allocated->push_back({WINDOW_TYPE, text_win, 1});
std::thread text_thread(show_clock_text, text_win);
text_thread.detach();
std::thread clock_thread(async_clock, win, text_win);
clock_thread.detach();
}

View File

@ -1,9 +1,11 @@
#include <unistd.h>
#include <csignal>
#include <cstdlib>
#include <iostream>
#include "color.h"
#include "const.h"
#include "menu.h"
#include "signal.h"
void PrintHelp() {
std::cout << RED R"( ____ ____
@ -26,6 +28,14 @@ void PrintVersion() {
}
int main(int argc, char* argv[]) {
// signal handlers
std::signal(SIGTERM, safe_exit);
std::signal(SIGINT, safe_exit);
std::signal(SIGQUIT, safe_exit);
std::signal(SIGHUP, safe_exit);
// error signal handlers
signal(SIGSEGV, safe_exit);
int opt;
while ((opt = getopt(argc, argv, "hV")) != -1) {

99
src/memory.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "memory.h"
#include <curses.h>
#include <menu.h>
#include <ncurses.h>
// #include <panel.h> UNCOMENT IF IF WE USE PANELs
#include <iostream>
#include "color.h"
#include "types.h"
std::vector<allocation>* current_allocated;
template <typename T>
struct NcursesDeleter {
static void delete_element(T obj);
};
template <>
void NcursesDeleter<WINDOW*>::delete_element(WINDOW* win) {
delwin(win);
}
/*template <>
void NcursesDeleter<PANEL*>::delete_element(PANEL* pan) {
del_panel(pan);
} UNCOMENT IF WE USE PANELs*/
template <>
void NcursesDeleter<ITEM*>::delete_element(ITEM* item) {
free_item(item);
}
template <typename T>
void delete_ncurses_arrays(void* ptr, std::size_t size) {
T* array = static_cast<T*>(ptr);
for (std::size_t j = 0; j < size; ++j) {
NcursesDeleter<T>::delete_element(array[j]);
}
delete[] array;
}
void delete_all(std::vector<allocation>* allocated) {
if (allocated == nullptr) {
return;
}
for (long long i = allocated->size() - 1; i >= 0; i--) {
switch (allocated->at(i).type) {
case WINDOW_ARRAY: {
delete_ncurses_arrays<WINDOW*>(allocated->at(i).ptr,
allocated->at(i).size);
break;
}
/* case PANEL_ARRAY: {
delete_ncurses_arrays<PANEL*>(allocated->at(i).ptr,
allocated->at(i).size);
break;
} UNCOMENT IF WE USE PANELs*/
case ITEM_ARRAY: {
delete_ncurses_arrays<ITEM*>(allocated->at(i).ptr,
allocated->at(i).size);
break;
}
case CHAR_PTR_ARRAY: {
char** array = static_cast<char**>(allocated->at(i).ptr);
for (std::size_t j = 0; j < allocated->at(i).size; ++j) {
delete[] array[j];
}
delete[] array;
break;
}
case GENERIC_ARRAY:
delete[] static_cast<char*>(allocated->at(i).ptr);
break;
case WINDOW_TYPE:
delwin(static_cast<WINDOW*>(allocated->at(i).ptr));
break;
/*case PANEL_TYPE:
del_panel(static_cast<PANEL*>(allocated->at(i).ptr));
break; UNCOMENT IF WE USE PANELs */
case MENU_TYPE:
free_menu(static_cast<MENU*>(allocated->at(i).ptr));
break;
case COMPLETE_MENU_TYPE: {
free_menu(static_cast<complete_menu*>(allocated->at(i).ptr)->menu);
delwin(static_cast<complete_menu*>(allocated->at(i).ptr)->win);
delete_ncurses_arrays<ITEM*>(
static_cast<complete_menu*>(allocated->at(i).ptr)->items,
static_cast<complete_menu*>(allocated->at(i).ptr)->items_size);
break;
}
case GENERIC_TYPE:
delete static_cast<char*>(allocated->at(i).ptr);
break;
default:
std::cerr << RED "[!!CRITICAL!!]" << RESET " Unknown allocation type"
<< "\n";
break;
}
allocated->pop_back();
}
}

30
src/memory.h Normal file
View File

@ -0,0 +1,30 @@
// header guard
#ifndef PARADOCS_MEMORY_H_
#define PARADOCS_MEMORY_H_
#include <menu.h>
#include <cstddef>
#include <vector>
enum AllocationType {
WINDOW_ARRAY,
// PANEL_ARRAY, UNCOMENT IF WE USE PANELs
ITEM_ARRAY,
CHAR_PTR_ARRAY,
GENERIC_ARRAY,
WINDOW_TYPE,
// PANEL_TYPE, UNCOMENT IF WE USE PANELs
MENU_TYPE,
COMPLETE_MENU_TYPE,
GENERIC_TYPE
};
struct allocation {
AllocationType type;
void* ptr;
std::size_t size;
};
extern std::vector<allocation>* current_allocated;
void delete_all(std::vector<allocation>* allocated);
#endif

View File

@ -1,23 +1,24 @@
#include <curses.h>
#include <menu.h>
#include <ncurses.h>
#include <clocale>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include "const.h"
#include "cups.h"
#include "gameske_funkce.h"
#include "memory.h"
#include "types.h"
#define CTRLD 4
std::vector<allocation> main_menu_allocated;
const char* choices[] = {"paradox", "kompromis", "Stereotyp", "Žena"};
const char* date[] = {"2023-10-01", "2023-10-02", "2023-10-03", "2023-10-04"};
const char* choices2[] = {"PRINT", "EDIT", "DELETE", "Žena"};
void menu() {
ITEM** my_items;
int c;
MENU* my_menu;
WINDOW* my_menu_win;
int n_choices, i;
current_allocated = &main_menu_allocated;
/* Initialize curses */
setlocale(LC_ALL, "");
initscr();
@ -32,61 +33,75 @@ void menu() {
async_clock_init();
/* Create items */
n_choices = ARRAY_SIZE(choices);
my_items = new ITEM*[ARRAY_SIZE(choices)];
for (i = 0; i < n_choices; ++i)
my_items[i] = new_item(choices[i], date[i]);
size_t n_choices = ARRAY_SIZE(choices);
complete_menu main_menu = {nullptr, nullptr, 0, nullptr};
main_menu_allocated.push_back({COMPLETE_MENU_TYPE, &main_menu, 1});
main_menu.items = new ITEM*[ARRAY_SIZE(choices) + 1];
main_menu.items_size = ARRAY_SIZE(choices) + 1;
for (size_t i = 0; i < n_choices; ++i) {
main_menu.items[i] = new_item(choices[i], date[i]);
}
main_menu.items[n_choices] = nullptr;
/* Crate menu */
my_menu = new_menu(my_items);
main_menu.menu = new_menu(main_menu.items);
/* Create the window to be associated with the menu */
my_menu_win = newwin(10, 40, 4, 4);
keypad(my_menu_win, TRUE);
main_menu.win = newwin(10, 40, 4, 4);
keypad(main_menu.win, TRUE);
/* Set main window and sub window */
set_menu_win(my_menu, my_menu_win);
set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));
set_menu_win(main_menu.menu, main_menu.win);
set_menu_sub(main_menu.menu, derwin(main_menu.win, 6, 38, 3, 1));
/* Set menu mark to the string " * " */
set_menu_mark(my_menu, " * ");
set_menu_mark(main_menu.menu, " * ");
/* Print a border around the main window and print a title */
box(my_menu_win, 0, 0);
print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));
mvwaddch(my_menu_win, 2, 0, ACS_LTEE);
mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);
mvwaddch(my_menu_win, 2, 39, ACS_RTEE);
box(main_menu.win, 0, 0);
print_in_middle(main_menu.win, 1, 0, 40, "My Menu", COLOR_PAIR(1));
mvwaddch(main_menu.win, 2, 0, ACS_LTEE);
mvwhline(main_menu.win, 2, 1, ACS_HLINE, 38);
mvwaddch(main_menu.win, 2, 39, ACS_RTEE);
mvprintw(LINES - 2, 0, "F1 to exit");
refresh();
/* Post the menu */
post_menu(my_menu);
wrefresh(my_menu_win);
post_menu(main_menu.menu);
wrefresh(main_menu.win);
while ((c = wgetch(my_menu_win)) != KEY_F(1)) {
int c;
while ((c = wgetch(main_menu.win)) != KEY_F(1)) {
switch (c) {
case KEY_DOWN:
menu_driver(my_menu, REQ_DOWN_ITEM);
menu_driver(main_menu.menu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(my_menu, REQ_UP_ITEM);
menu_driver(main_menu.menu, REQ_UP_ITEM);
break;
case 10:
refresh();
break;
case ':':
spawncmd();
switch (hash_djb2a(spawncmd())) {
case "print"_sh:
case "p"_sh:
// DONT FORGET TO PRINT ACTUAL DOCUMENT
printDocument("test");
current_allocated = &main_menu_allocated;
break;
default:
print_in_middle(main_menu.win, 10, 0, 40, "Unknown command",
COLOR_PAIR(1));
break;
}
}
wrefresh(my_menu_win);
wrefresh(main_menu.win);
}
/* Unpost and free all the memory taken up */
unpost_menu(my_menu);
free_menu(my_menu);
for (i = 0; i < n_choices; ++i)
free_item(my_items[i]);
delete[] my_items;
unpost_menu(main_menu.menu);
delete_all(&main_menu_allocated);
endwin();
}

33
src/signal.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <curses.h>
#include <csignal>
#include <iostream>
#include "memory.h"
void safe_exit(int code) {
switch (code) {
case SIGTERM:
std::cerr << "\nreceived SIGTERM exiting...\n";
break;
case SIGINT:
std::cerr << "\nreceived SIGINT exiting...\n";
break;
case SIGQUIT:
std::cerr << "\nreceived SIGQUIT exiting...\n";
break;
case SIGHUP:
std::cerr << "\nreceived SIGHUP exiting...\n";
break;
case SIGSEGV:
std::cerr << "\nreceived SIGSEGV(segmentaiton fault) exiting...\nIf this "
"repeats please report it as a bug\n";
break;
default:
break;
}
delete_all(current_allocated);
echo();
endwin();
exit(code);
}

7
src/signal.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef PARADOCS_SIGNAL_H_
#define PARADOCS_SIGNAL_H_
void safe_exit(int code);
#endif

14
src/types.h Normal file
View File

@ -0,0 +1,14 @@
#include <menu.h>
#include <ncurses.h>
#ifndef PARADOCS_TYPES_H_
#define PARADOCS_TYPES_H_
struct complete_menu {
WINDOW* win;
ITEM** items;
size_t items_size;
MENU* menu;
};
#endif