#define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" // Initialize terminal dimensions void init_terminal_dimensions(void) { struct winsize ws; // Try to get actual terminal size if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0 && ws.ws_row > 0) { set_terminal_dimensions(ws.ws_col, ws.ws_row); } // If ioctl fails, keep the default values (80x24) } // Print centered header with = padding, screen clearing, and optional pause void print_centered_header(const char* text, int pause_before_clear) { if (!text) return; // Phase 1: Pause if requested if (pause_before_clear) { printf("\nPress Enter to continue..."); fflush(stdout); // Wait for Enter key int c; while ((c = getchar()) != '\n' && c != EOF) { // Consume any extra characters until newline } } // Phase 2: Clear screen using terminal height for (int i = 0; i < get_terminal_height(); i++) { printf("\n"); } // Phase 3: Display centered header (existing logic) int text_len = strlen(text); int available_width = get_terminal_width(); // Ensure minimum spacing: at least 1 space on each side int min_required = text_len + 4; // text + " " + text + " " (spaces around text) if (available_width < min_required) { // Terminal too narrow - just print the text with minimal formatting printf("=== %s ===\n", text); return; } // Calculate padding int total_padding = available_width - text_len - 2; // -2 for spaces around text int left_padding = total_padding / 2; int right_padding = total_padding - left_padding; // Print the header for (int i = 0; i < left_padding; i++) { printf("="); } printf(" %s ", text); for (int i = 0; i < right_padding; i++) { printf("="); } printf("\n"); } // Interactive mode main loop int interactive_mode(void) { char input[10]; printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); while (1) { show_main_menu(); if (!fgets(input, sizeof(input), stdin)) { printf("Goodbye!\n"); break; } char choice = toupper(input[0]); switch (choice) { case 'T': handle_text_encrypt(); break; case 'F': handle_file_encrypt(); break; case 'R': handle_directory_encrypt(); break; case 'D': handle_decrypt_menu(); break; case 'P': handle_pads_menu(); break; case 'X': case 'Q': printf("Goodbye!\n"); return 0; default: printf("Invalid choice. Please try again.\n"); break; } } return 0; } void show_main_menu(void) { printf("\n"); char header[64]; snprintf(header, sizeof(header), "Main Menu - OTP %s", OTP_VERSION); print_centered_header(header, 0); printf("\n"); printf(" \033[4mT\033[0mext encrypt\n"); //TEXT ENCRYPT printf(" \033[4mF\033[0mile encrypt\n"); //FILE ENCRYPT printf(" Di\033[4mr\033[0mectory encrypt\n"); //DIRECTORY ENCRYPT printf(" \033[4mD\033[0mecrypt\n"); //DECRYPT printf(" \033[4mP\033[0mads\n"); //PADS printf(" E\033[4mx\033[0mit\n"); //EXIT printf("\nSelect option: "); } int handle_generate_menu(void) { printf("\n"); print_centered_header("Generate New Pad", 0); printf("Enter pad size (examples: 1GB, 5TB, 512MB, 2048): "); char size_input[64]; if (!fgets(size_input, sizeof(size_input), stdin)) { printf("Error: Failed to read input\n"); return 1; } size_input[strcspn(size_input, "\n")] = 0; uint64_t size = parse_size_string(size_input); if (size == 0) { printf("Error: Invalid size format\n"); return 1; } double size_gb = (double)size / (1000.0 * 1000.0 * 1000.0); printf("Generating %.2f GB pad...\n", size_gb); printf("Note: Use 'Add entropy' in Pads menu to enhance randomness after creation.\n"); return generate_pad(size, 1); } int handle_encrypt_menu(void) { printf("\n"); print_centered_header("Encrypt Data", 0); printf("Available pads:\n"); char* selected = select_pad_interactive("Available pads:", "Select pad (or press Enter to continue)", PAD_FILTER_ALL, 0); int pad_count = 1; // Assume at least 1 pad if function returned if (selected) { free(selected); } else { pad_count = 0; } if (pad_count == 0) { printf("No pads available. Generate a pad first.\n"); return 1; } // Ask user to choose between text and file encryption printf("\nSelect encryption type:\n"); printf(" 1. Text message\n"); printf(" 2. File\n"); printf("Enter choice (1-2) or 'esc' to cancel: "); char choice_input[10]; if (!fgets(choice_input, sizeof(choice_input), stdin)) { printf("Error: Failed to read input\n"); return 1; } choice_input[strcspn(choice_input, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(choice_input)) { printf("Returning to main menu...\n"); return 0; } int choice = atoi(choice_input); if (choice == 1) { // Text encryption - use unified pad selection char* selected_pad = select_pad_interactive("Select Pad for Text Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { printf("Text encryption cancelled.\n"); return 1; } int result = encrypt_text(selected_pad, NULL); // NULL for interactive mode free(selected_pad); return result; } else if (choice == 2) { // File encryption printf("\nFile selection options:\n"); printf(" 1. Type file path directly\n"); printf(" 2. Use file manager\n"); printf("Enter choice (1-2) or 'esc' to cancel: "); char file_choice[10]; char input_file[512]; if (!fgets(file_choice, sizeof(file_choice), stdin)) { printf("Error: Failed to read input\n"); return 1; } file_choice[strcspn(file_choice, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(file_choice)) { printf("Returning to main menu...\n"); return 0; } if (atoi(file_choice) == 2) { // Use file manager if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) { printf("Falling back to manual file path entry.\n"); printf("Enter input file path (or 'esc' to cancel): "); if (!fgets(input_file, sizeof(input_file), stdin)) { printf("Error: Failed to read input\n"); return 1; } input_file[strcspn(input_file, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(input_file)) { printf("Returning to main menu...\n"); return 0; } } } else { // Direct file path input printf("Enter input file path (or 'esc' to cancel): "); if (!fgets(input_file, sizeof(input_file), stdin)) { printf("Error: Failed to read input\n"); return 1; } input_file[strcspn(input_file, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(input_file)) { printf("Returning to main menu...\n"); return 0; } } // Check if file exists if (access(input_file, R_OK) != 0) { printf("Error: File '%s' not found or cannot be read\n", input_file); return 1; } // Use unified pad selection char* selected_pad = select_pad_interactive("Select Pad for File Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { printf("File encryption cancelled.\n"); return 1; } // Ask for output format printf("\nSelect output format:\n"); printf(" 1. Binary (.otp) - preserves file permissions\n"); printf(" 2. ASCII (.otp.asc) - text-safe format\n"); printf("Enter choice (1-2) or 'esc' to cancel: "); char format_input[10]; if (!fgets(format_input, sizeof(format_input), stdin)) { printf("Error: Failed to read input\n"); free(selected_pad); return 1; } format_input[strcspn(format_input, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(format_input)) { printf("Returning to main menu...\n"); free(selected_pad); return 0; } int ascii_armor = (atoi(format_input) == 2) ? 1 : 0; // Generate default output filename with files directory and use enhanced input function char default_output[1024]; // Increased size to prevent truncation warnings char temp_default[1024]; // Generate base filename with appropriate extension if (ascii_armor) { snprintf(temp_default, sizeof(temp_default), "%s.otp.asc", input_file); } else { snprintf(temp_default, sizeof(temp_default), "%s.otp", input_file); } // Apply files directory default path get_default_file_path(temp_default, default_output, sizeof(default_output)); char output_file[512]; if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { printf("Error: Failed to read input\n"); return 1; } const char* output_filename = output_file; int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor); free(selected_pad); return result; } else { printf("Invalid choice. Please enter 1 or 2.\n"); return 1; } } int handle_decrypt_menu(void) { printf("\n"); print_centered_header("Smart Decrypt", 0); printf("Enter encrypted data (paste ASCII armor), file path, or press Enter to browse files:\n"); printf("(Type 'esc' or 'q' to return to main menu)\n"); char input_line[MAX_LINE_LENGTH]; if (!fgets(input_line, sizeof(input_line), stdin)) { printf("Error: Failed to read input\n"); return 1; } // Remove newline input_line[strcspn(input_line, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(input_line)) { printf("Returning to main menu...\n"); return 0; } // Trim leading whitespace to handle pasted content better char* trimmed_input = input_line; while (*trimmed_input == ' ' || *trimmed_input == '\t') { trimmed_input++; } // Check for ASCII armor FIRST, before checking for empty input // This handles cases where pasted text starts with the header if (strncmp(trimmed_input, "-----BEGIN OTP MESSAGE-----", 27) == 0) { // Looks like ASCII armor - collect the full message char full_message[MAX_INPUT_SIZE * 4] = {0}; strcat(full_message, input_line); strcat(full_message, "\n"); printf("Continue pasting the message (end with -----END OTP MESSAGE-----):\n"); char line[MAX_LINE_LENGTH]; while (fgets(line, sizeof(line), stdin)) { strncat(full_message, line, sizeof(full_message) - strlen(full_message) - 1); if (strstr(line, "-----END OTP MESSAGE-----")) { break; } } return decrypt_text(NULL, full_message); } else if (strlen(trimmed_input) == 0) { // Empty input - launch file manager to browse for files char selected_file[512]; if (launch_file_manager(get_files_directory(), selected_file, sizeof(selected_file)) != 0) { printf("File browsing cancelled or failed.\n"); printf("Returning to main menu...\n"); return 0; } // Generate smart default output filename with files directory and use enhanced input function char temp_default[512]; char default_output[512]; strncpy(temp_default, selected_file, sizeof(temp_default) - 1); temp_default[sizeof(temp_default) - 1] = '\0'; // Remove common encrypted extensions to get a better default if (strstr(temp_default, ".tar.gz.otp")) { // Directory archive - remove .tar.gz.otp to get original directory name char* ext_pos = strstr(temp_default, ".tar.gz.otp"); *ext_pos = '\0'; } else if (strstr(temp_default, ".tar.otp")) { // Directory archive without compression - remove .tar.otp char* ext_pos = strstr(temp_default, ".tar.otp"); *ext_pos = '\0'; } else if (strstr(temp_default, ".otp.asc")) { // Replace .otp.asc with original extension or no extension char* ext_pos = strstr(temp_default, ".otp.asc"); *ext_pos = '\0'; } else if (strstr(temp_default, ".otp")) { // Replace .otp with original extension or no extension char* ext_pos = strstr(temp_default, ".otp"); *ext_pos = '\0'; } else { // No recognized encrypted extension, add .decrypted suffix strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1); } // Apply files directory default path get_default_file_path(temp_default, default_output, sizeof(default_output)); char output_file[512]; if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { printf("Error: Failed to read input\n"); return 1; } // Check if it's a directory archive if (strstr(selected_file, ".tar.gz.otp") || strstr(selected_file, ".tar.otp")) { // It's a directory archive - extract to directory char extract_dir[512]; strncpy(extract_dir, output_file, sizeof(extract_dir) - 1); extract_dir[sizeof(extract_dir) - 1] = '\0'; // Remove .tar.gz.otp or .tar.otp extension to get directory name char* ext = strstr(extract_dir, ".tar.gz.otp"); if (!ext) ext = strstr(extract_dir, ".tar.otp"); if (ext) *ext = '\0'; printf("Extracting directory archive to: %s/\n", extract_dir); return decrypt_and_extract_directory(selected_file, extract_dir); } else { return decrypt_file(selected_file, output_file); } } else { // Check if it looks like a file path if (access(trimmed_input, R_OK) == 0) { // It's a valid file - decrypt it with enhanced input for output filename char temp_default[512]; char default_output[512]; strncpy(temp_default, trimmed_input, sizeof(temp_default) - 1); temp_default[sizeof(temp_default) - 1] = '\0'; // Remove common encrypted extensions to get a better default if (strstr(temp_default, ".tar.gz.otp")) { // Directory archive - remove .tar.gz.otp to get original directory name char* ext_pos = strstr(temp_default, ".tar.gz.otp"); *ext_pos = '\0'; } else if (strstr(temp_default, ".tar.otp")) { // Directory archive without compression - remove .tar.otp char* ext_pos = strstr(temp_default, ".tar.otp"); *ext_pos = '\0'; } else if (strstr(temp_default, ".otp.asc")) { // Replace .otp.asc with original extension or no extension char* ext_pos = strstr(temp_default, ".otp.asc"); *ext_pos = '\0'; } else if (strstr(temp_default, ".otp")) { // Replace .otp with original extension or no extension char* ext_pos = strstr(temp_default, ".otp"); *ext_pos = '\0'; } else { // No recognized encrypted extension, add .decrypted suffix strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1); } // Apply files directory default path get_default_file_path(temp_default, default_output, sizeof(default_output)); char output_file[512]; if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { printf("Error: Failed to read input\n"); return 1; } // Check if it's a directory archive if (strstr(trimmed_input, ".tar.gz.otp") || strstr(trimmed_input, ".tar.otp")) { // It's a directory archive - extract to directory char extract_dir[512]; strncpy(extract_dir, output_file, sizeof(extract_dir) - 1); extract_dir[sizeof(extract_dir) - 1] = '\0'; // Remove .tar.gz.otp or .tar.otp extension to get directory name char* ext = strstr(extract_dir, ".tar.gz.otp"); if (!ext) ext = strstr(extract_dir, ".tar.otp"); if (ext) *ext = '\0'; printf("Extracting directory archive to: %s/\n", extract_dir); return decrypt_and_extract_directory(trimmed_input, extract_dir); } else { return decrypt_file(trimmed_input, output_file); } } else { printf("Input not recognized as ASCII armor or valid file path.\n"); return 1; } } } int handle_text_encrypt(void) { printf("\n"); print_centered_header("Text Encrypt", 0); // Launch text editor directly char text_buffer[MAX_INPUT_SIZE]; if (launch_text_editor(NULL, text_buffer, sizeof(text_buffer)) != 0) { printf("Error: Could not launch text editor\n"); return 1; } if (strlen(text_buffer) == 0) { printf("No text entered - canceling encryption\n"); return 1; } // Use unified pad selection char* selected_pad = select_pad_interactive("Select Pad for Text Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { printf("Text encryption cancelled.\n"); return 1; } int result = encrypt_text(selected_pad, text_buffer); free(selected_pad); return result; } int handle_file_encrypt(void) { printf("\n"); print_centered_header("File Encrypt", 0); // Launch file manager directly char input_file[512]; if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) { printf("Error: Could not launch file manager\n"); return 1; } // Check if file exists if (access(input_file, R_OK) != 0) { printf("Error: File '%s' not found or cannot be read\n", input_file); return 1; } // Use unified pad selection char* selected_pad = select_pad_interactive("Select Pad for File Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { printf("File encryption cancelled.\n"); return 1; } // Ask for output format printf("\nSelect output format:\n"); printf(" 1. Binary (.otp) - preserves file permissions\n"); printf(" 2. ASCII (.otp.asc) - text-safe format\n"); printf("Enter choice (1-2) or 'esc' to cancel: "); char format_input[10]; if (!fgets(format_input, sizeof(format_input), stdin)) { printf("Error: Failed to read input\n"); free(selected_pad); return 1; } format_input[strcspn(format_input, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(format_input)) { printf("Returning to main menu...\n"); free(selected_pad); return 0; } int ascii_armor = (atoi(format_input) == 2) ? 1 : 0; // Generate default output filename char default_output[1024]; // Increased buffer size to prevent truncation warnings if (ascii_armor) { snprintf(default_output, sizeof(default_output), "%s.otp.asc", input_file); } else { snprintf(default_output, sizeof(default_output), "%s.otp", input_file); } // Use enhanced input function for output filename char output_file[512]; if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { printf("Error: Failed to read input\n"); return 1; } const char* output_filename = output_file; int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor); free(selected_pad); return result; } // Comparison function for qsort (case-insensitive) static int compare_dir_strings(const void *a, const void *b) { return strcasecmp(*(const char**)a, *(const char**)b); } // Function to get list of subdirectories static int get_subdirs(const char *path, char ***subdirs) { DIR *dir = opendir(path); if (!dir) return 0; int count = 0; int capacity = 10; *subdirs = malloc(capacity * sizeof(char*)); struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR) { // Skip . and .. if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; if (count >= capacity) { capacity *= 2; *subdirs = realloc(*subdirs, capacity * sizeof(char*)); } (*subdirs)[count++] = strdup(entry->d_name); } } closedir(dir); // Sort alphabetically if (count > 0) { qsort(*subdirs, count, sizeof(char*), compare_dir_strings); } return count; } // Function to get a single keypress without echo static int getch_nav(void) { struct termios oldt, newt; int ch; tcgetattr(STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); return ch; } // Navigate directories with arrow keys static char* navigate_directory_interactive(void) { char current_path[PATH_MAX]; char original_path[PATH_MAX]; getcwd(original_path, sizeof(original_path)); getcwd(current_path, sizeof(current_path)); char **subdirs = NULL; int subdir_count = 0; int current_index = 0; printf("\nNavigate with arrow keys (UP/DOWN: cycle, RIGHT: enter, LEFT: back, ENTER: select, Q: cancel)\n"); printf("\033[?25l"); // Hide cursor while (1) { // Clear line and show current path printf("\r\033[K%s", current_path); // If we have subdirectories and an index, show current selection if (subdir_count > 0 && current_index < subdir_count) { printf("/%s", subdirs[current_index]); } fflush(stdout); int ch = getch_nav(); // Handle arrow keys (they come as escape sequences) if (ch == 27) { // ESC sequence getch_nav(); // Skip '[' ch = getch_nav(); if (ch == 'A') { // UP arrow // Load subdirs if not loaded if (subdirs == NULL) { subdir_count = get_subdirs(current_path, &subdirs); current_index = 0; } else if (subdir_count > 0) { current_index = (current_index - 1 + subdir_count) % subdir_count; } } else if (ch == 'B') { // DOWN arrow // Load subdirs if not loaded if (subdirs == NULL) { subdir_count = get_subdirs(current_path, &subdirs); current_index = 0; } else if (subdir_count > 0) { current_index = (current_index + 1) % subdir_count; } } else if (ch == 'C') { // RIGHT arrow - go deeper if (subdir_count > 0 && current_index < subdir_count) { // Navigate into selected directory char new_path[PATH_MAX]; snprintf(new_path, sizeof(new_path), "%s/%s", current_path, subdirs[current_index]); if (chdir(new_path) == 0) { getcwd(current_path, sizeof(current_path)); // Free old subdirs for (int i = 0; i < subdir_count; i++) { free(subdirs[i]); } free(subdirs); subdirs = NULL; // Load subdirs of new directory and show first one subdir_count = get_subdirs(current_path, &subdirs); current_index = 0; } } } else if (ch == 'D') { // LEFT arrow - go up if (chdir("..") == 0) { getcwd(current_path, sizeof(current_path)); // Free old subdirs for (int i = 0; i < subdir_count; i++) { free(subdirs[i]); } free(subdirs); subdirs = NULL; subdir_count = 0; current_index = 0; } } } else if (ch == '\n' || ch == '\r') { // ENTER - confirm selection // If a subdirectory is displayed, navigate into it first if (subdir_count > 0 && current_index < subdir_count) { char new_path[PATH_MAX]; snprintf(new_path, sizeof(new_path), "%s/%s", current_path, subdirs[current_index]); if (chdir(new_path) == 0) { getcwd(current_path, sizeof(current_path)); } } break; } else if (ch == 'q' || ch == 'Q') { // Q to quit printf("\033[?25h\n"); // Show cursor // Restore original directory chdir(original_path); // Clean up for (int i = 0; i < subdir_count; i++) { free(subdirs[i]); } free(subdirs); return NULL; } } printf("\033[?25h\n"); // Show cursor // Clean up for (int i = 0; i < subdir_count; i++) { free(subdirs[i]); } free(subdirs); // Restore original directory before returning char* result = strdup(current_path); chdir(original_path); return result; } int handle_directory_encrypt(void) { printf("\n"); print_centered_header("Directory Encrypt", 0); // Directory selection options printf("\nDirectory selection options:\n"); printf(" 1. Type directory path directly\n"); printf(" 2. Navigate with arrow keys\n"); printf(" 3. Use file manager (navigate to directory)\n"); printf("Enter choice (1-3) or 'esc' to cancel: "); char choice_input[10]; char dir_path[512]; if (!fgets(choice_input, sizeof(choice_input), stdin)) { printf("Error: Failed to read input\n"); return 1; } choice_input[strcspn(choice_input, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(choice_input)) { printf("Returning to main menu...\n"); return 0; } int choice = atoi(choice_input); if (choice == 2) { // Use arrow key navigation char* selected = navigate_directory_interactive(); if (!selected) { printf("Directory selection cancelled.\n"); return 0; } strncpy(dir_path, selected, sizeof(dir_path) - 1); dir_path[sizeof(dir_path) - 1] = '\0'; free(selected); printf("Selected: %s\n", dir_path); } else if (choice == 3) { // Use directory manager if (launch_directory_manager(".", dir_path, sizeof(dir_path)) != 0) { printf("Falling back to manual directory path entry.\n"); printf("Enter directory path to encrypt (or 'esc' to cancel): "); if (!fgets(dir_path, sizeof(dir_path), stdin)) { printf("Error: Failed to read input\n"); return 1; } dir_path[strcspn(dir_path, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(dir_path)) { printf("Returning to main menu...\n"); return 0; } } } else { // Direct directory path input printf("Enter directory path to encrypt (or 'esc' to cancel): "); if (!fgets(dir_path, sizeof(dir_path), stdin)) { printf("Error: Failed to read input\n"); return 1; } dir_path[strcspn(dir_path, "\n")] = 0; // Check for ESC/cancel if (is_escape_input(dir_path)) { printf("Returning to main menu...\n"); return 0; } } // Check if directory exists struct stat st; if (stat(dir_path, &st) != 0 || !S_ISDIR(st.st_mode)) { printf("Error: '%s' is not a valid directory\n", dir_path); return 1; } // Select pad char* selected_pad = select_pad_interactive("Select Pad for Directory Encryption", "Select pad (by prefix)", PAD_FILTER_ALL, 1); if (!selected_pad) { printf("Directory encryption cancelled.\n"); return 1; } // Generate default output filename - append .tar.gz.otp to the directory path char default_output[1024]; // Remove trailing slash if present char clean_path[512]; strncpy(clean_path, dir_path, sizeof(clean_path) - 1); clean_path[sizeof(clean_path) - 1] = '\0'; size_t path_len = strlen(clean_path); if (path_len > 0 && clean_path[path_len - 1] == '/') { clean_path[path_len - 1] = '\0'; } snprintf(default_output, sizeof(default_output), "%s.tar.gz.otp", clean_path); // Get output filename char output_file[512]; if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) { printf("Error: Failed to read input\n"); free(selected_pad); return 1; } // Encrypt directory int result = encrypt_directory(dir_path, selected_pad, output_file); free(selected_pad); return result; }