Files
otp/src/util.c

1041 lines
36 KiB
C

#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include <time.h>
#include <ctype.h>
#include <termios.h>
#include <fcntl.h>
#include <math.h>
#include "main.h"
// Global variables for preferences
static char default_pad_path[1024] = "";
void show_progress(uint64_t current, uint64_t total, time_t start_time) {
time_t now = time(NULL);
double elapsed = difftime(now, start_time);
if (elapsed < 1.0) elapsed = 1.0; // Avoid division by zero
double percentage = (double)current / total * 100.0;
double speed = (double)current / elapsed / (1024.0 * 1024.0); // MB/s
uint64_t remaining_bytes = total - current;
double eta = remaining_bytes / (current / elapsed);
printf("\rProgress: %.1f%% (%.1f MB/s, ETA: %.0fs) ", percentage, speed, eta);
fflush(stdout);
}
const char* get_files_directory(void) {
struct stat st = {0};
if (stat(FILES_DIR, &st) == 0 && S_ISDIR(st.st_mode)) {
return FILES_DIR;
}
return "."; // Fall back to current directory
}
void get_default_file_path(const char* filename, char* result_path, size_t result_size) {
const char* files_dir = get_files_directory();
// If filename already has a path (contains '/'), use it as-is
if (strchr(filename, '/') != NULL) {
strncpy(result_path, filename, result_size - 1);
result_path[result_size - 1] = '\0';
return;
}
// Otherwise, prepend the files directory
snprintf(result_path, result_size, "%s/%s", files_dir, filename);
}
char* get_preferred_editor(void) {
// Check EDITOR environment variable first
char* editor = getenv("EDITOR");
if (editor && strlen(editor) > 0) {
// Verify the editor exists
char command[512];
snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", editor);
if (system(command) == 0) {
return strdup(editor);
}
}
// Check VISUAL environment variable
editor = getenv("VISUAL");
if (editor && strlen(editor) > 0) {
char command[512];
snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", editor);
if (system(command) == 0) {
return strdup(editor);
}
}
// Try common editors in order of preference
const char* common_editors[] = {"vim", "vi", "nano", "emacs", "gedit", NULL};
for (int i = 0; common_editors[i] != NULL; i++) {
char command[512];
snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", common_editors[i]);
if (system(command) == 0) {
return strdup(common_editors[i]);
}
}
return NULL; // No editor found
}
int launch_text_editor(const char* initial_content, char* result_buffer, size_t buffer_size) {
char* editor = get_preferred_editor();
if (!editor) {
printf("Error: No text editor found. Set EDITOR environment variable or install vim/nano.\n");
return 1;
}
// Create temporary file
char temp_filename[64];
snprintf(temp_filename, sizeof(temp_filename), "/tmp/otp_edit_%ld.tmp", time(NULL));
// Write initial content to temp file if provided
if (initial_content && strlen(initial_content) > 0) {
FILE* temp_file = fopen(temp_filename, "w");
if (temp_file) {
fputs(initial_content, temp_file);
fclose(temp_file);
}
} else {
// Create empty temp file
FILE* temp_file = fopen(temp_filename, "w");
if (temp_file) {
fclose(temp_file);
}
}
// Launch editor
printf("Opening %s for text editing...\n", editor);
char command[512];
snprintf(command, sizeof(command), "%s %s", editor, temp_filename);
int result = system(command);
if (result == 0) {
// Read the edited content back
FILE* temp_file = fopen(temp_filename, "r");
if (temp_file) {
size_t bytes_read = fread(result_buffer, 1, buffer_size - 1, temp_file);
result_buffer[bytes_read] = '\0';
// Remove trailing newline if present
if (bytes_read > 0 && result_buffer[bytes_read - 1] == '\n') {
result_buffer[bytes_read - 1] = '\0';
}
fclose(temp_file);
} else {
printf("Error: Cannot read edited content\n");
free(editor);
unlink(temp_filename);
return 1;
}
} else {
printf("Editor exited with error or was cancelled\n");
free(editor);
unlink(temp_filename);
return 1;
}
// Clean up
unlink(temp_filename);
free(editor);
return 0;
}
char* get_preferred_file_manager(void) {
// Try file managers in order of preference
const char* file_managers[] = {"ranger", "fzf", "nnn", "lf", NULL};
for (int i = 0; file_managers[i] != NULL; i++) {
char command[512];
snprintf(command, sizeof(command), "which %s >/dev/null 2>&1", file_managers[i]);
if (system(command) == 0) {
return strdup(file_managers[i]);
}
}
return NULL; // No file manager found
}
int launch_file_manager(const char* start_directory, char* selected_file, size_t buffer_size) {
char* fm = get_preferred_file_manager();
if (!fm) {
printf("No file manager found. Please install ranger, fzf, nnn, or lf.\n");
printf("Falling back to manual file path entry.\n");
return 1; // Fall back to manual entry
}
char temp_filename[64];
snprintf(temp_filename, sizeof(temp_filename), "/tmp/otp_file_%ld.tmp", time(NULL));
char command[512];
int result = 1;
printf("Opening %s for file selection...\n", fm);
if (strcmp(fm, "ranger") == 0) {
snprintf(command, sizeof(command), "cd '%s' && ranger --choosefile=%s",
start_directory ? start_directory : ".", temp_filename);
} else if (strcmp(fm, "fzf") == 0) {
snprintf(command, sizeof(command), "cd '%s' && find . -type f | fzf > %s",
start_directory ? start_directory : ".", temp_filename);
} else if (strcmp(fm, "nnn") == 0) {
snprintf(command, sizeof(command), "cd '%s' && nnn -p %s",
start_directory ? start_directory : ".", temp_filename);
} else if (strcmp(fm, "lf") == 0) {
snprintf(command, sizeof(command), "cd '%s' && lf -selection-path=%s",
start_directory ? start_directory : ".", temp_filename);
}
result = system(command);
if (result == 0 || result == 256) { // Some file managers return 256 on success
// Read selected file from temp file
FILE* temp_file = fopen(temp_filename, "r");
if (temp_file) {
if (fgets(selected_file, buffer_size, temp_file)) {
// Remove trailing newline
selected_file[strcspn(selected_file, "\n\r")] = 0;
// For relative paths from fzf, make absolute if needed
if (selected_file[0] == '.' && selected_file[1] == '/') {
char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) {
char abs_path[1024];
snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, selected_file + 2);
strncpy(selected_file, abs_path, buffer_size - 1);
selected_file[buffer_size - 1] = '\0';
}
}
fclose(temp_file);
unlink(temp_filename);
free(fm);
return 0; // Success
}
fclose(temp_file);
}
}
// Clean up and indicate failure
unlink(temp_filename);
free(fm);
return 1; // Fall back to manual entry
}
// Stdin detection functions implementation
int has_stdin_data(void) {
// Check if stdin is a pipe/redirect (not a terminal)
if (!isatty(STDIN_FILENO)) {
return 1;
}
return 0;
}
char* read_stdin_text(void) {
size_t capacity = 4096;
size_t length = 0;
char* buffer = malloc(capacity);
if (!buffer) {
return NULL;
}
char chunk[1024];
while (fgets(chunk, sizeof(chunk), stdin)) {
size_t chunk_len = strlen(chunk);
// Ensure we have enough capacity
while (length + chunk_len >= capacity) {
capacity *= 2;
char* new_buffer = realloc(buffer, capacity);
if (!new_buffer) {
free(buffer);
return NULL;
}
buffer = new_buffer;
}
strcpy(buffer + length, chunk);
length += chunk_len;
}
// Remove trailing newline if present
if (length > 0 && buffer[length - 1] == '\n') {
buffer[length - 1] = '\0';
length--;
}
// If empty, free and return NULL
if (length == 0) {
free(buffer);
return NULL;
}
return buffer;
}
int pipe_mode(int argc, char* argv[], const char* piped_text) {
(void)argc; // Suppress unused parameter warning
(void)argv; // Suppress unused parameter warning
// Check if we have a default pad configured
char* default_pad = get_default_pad_path();
if (default_pad) {
// Verify the default pad exists and extract checksum
if (access(default_pad, R_OK) == 0) {
// Extract checksum from pad filename
char* filename = strrchr(default_pad, '/');
if (!filename) filename = default_pad;
else filename++; // Skip the '/'
// Extract checksum (remove .pad extension)
if (strlen(filename) >= 68 && strstr(filename, ".pad")) {
char pad_checksum[65];
strncpy(pad_checksum, filename, 64);
pad_checksum[64] = '\0';
free(default_pad);
// Encrypt using the default pad (silent mode)
return encrypt_text(pad_checksum, piped_text);
}
}
fprintf(stderr, "Error: Default pad not found or invalid: %s\n", default_pad);
free(default_pad);
return 1;
}
fprintf(stderr, "Error: No default pad configured for pipe mode\n");
fprintf(stderr, "Configure a default pad in ~/.otp/otp.conf\n");
return 1;
}
// Preferences management functions implementation
int load_preferences(void) {
char* home_dir = getenv("HOME");
if (!home_dir) {
return 1; // No home directory
}
char preferences_dir[1024];
char preferences_file[2048]; // Increased buffer size to accommodate longer paths
snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir);
snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir);
FILE* file = fopen(preferences_file, "r");
if (!file) {
// No preferences file exists - create it and set first pad as default
// Create .otp directory if it doesn't exist
struct stat st = {0};
if (stat(preferences_dir, &st) == -1) {
if (mkdir(preferences_dir, 0755) != 0) {
return 1;
}
}
// Find the first available pad to set as default
DIR* dir = opendir(get_current_pads_dir());
if (dir) {
struct dirent* entry;
char first_pad_path[1024];
int found_pad = 0;
while ((entry = readdir(dir)) != NULL && !found_pad) {
if (strstr(entry->d_name, ".pad") && strlen(entry->d_name) == 68) {
// Found a pad file - construct full absolute path
const char* pads_dir = get_current_pads_dir();
if (pads_dir[0] == '/') {
// Already absolute path
int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", pads_dir, entry->d_name);
if (ret >= (int)sizeof(first_pad_path)) {
// Path was truncated, skip this entry
continue;
}
} else {
// Relative path - make it absolute
char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) {
int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s/%s", current_dir, pads_dir, entry->d_name);
if (ret >= (int)sizeof(first_pad_path)) {
// Path was truncated, skip this entry
continue;
}
} else {
// Fallback to relative path
int ret = snprintf(first_pad_path, sizeof(first_pad_path), "%s/%s", pads_dir, entry->d_name);
if (ret >= (int)sizeof(first_pad_path)) {
// Path was truncated, skip this entry
continue;
}
}
}
strncpy(default_pad_path, first_pad_path, sizeof(default_pad_path) - 1);
default_pad_path[sizeof(default_pad_path) - 1] = '\0';
found_pad = 1;
}
}
closedir(dir);
// Create the preferences file with the default pad
if (found_pad) {
save_preferences();
}
}
return 0; // Successfully initialized
}
char line[1024];
while (fgets(line, sizeof(line), file)) {
// Remove newline
line[strcspn(line, "\n")] = 0;
// Skip empty lines and comments
if (strlen(line) == 0 || line[0] == '#') {
continue;
}
// Parse key=value pairs
char* equals = strchr(line, '=');
if (equals) {
*equals = '\0';
char* key = line;
char* value = equals + 1;
// Trim whitespace
while (*key == ' ' || *key == '\t') key++;
while (*value == ' ' || *value == '\t') value++;
if (strcmp(key, "default_pad") == 0) {
strncpy(default_pad_path, value, sizeof(default_pad_path) - 1);
default_pad_path[sizeof(default_pad_path) - 1] = '\0';
}
}
}
fclose(file);
return 0;
}
char* find_pad_by_prefix(const char* prefix) {
DIR* dir = opendir(get_current_pads_dir());
if (!dir) {
printf("Error: Cannot open pads directory %s\n", get_current_pads_dir());
return NULL;
}
struct dirent* entry;
char* matches[100]; // Store up to 100 matches
int match_count = 0;
// Always try hex prefix matching first
size_t prefix_len = strlen(prefix);
while ((entry = readdir(dir)) != NULL && match_count < 100) {
// Skip . and .. entries, and only process .pad files
if (entry->d_name[0] == '.') continue;
if (!strstr(entry->d_name, ".pad")) continue;
if (strlen(entry->d_name) != 68) continue; // 64 char chksum + ".pad"
// Compare prefix with the filename (checksum part)
if (strncmp(entry->d_name, prefix, prefix_len) == 0) {
matches[match_count] = malloc(65);
strncpy(matches[match_count], entry->d_name, 64);
matches[match_count][64] = '\0';
match_count++;
}
}
// Only prefix matching is supported
closedir(dir);
if (match_count == 0) {
printf("No pads found matching '%s'\n", prefix);
printf("Available pads:\n");
char* selected = select_pad_interactive("Available pads:", "Available pads (press Enter to continue)", PAD_FILTER_ALL, 0);
if (selected) {
free(selected);
}
return NULL;
} else if (match_count == 1) {
char* result = matches[0];
return result;
} else {
printf("Multiple matches found for '%s':\n", prefix);
for (int i = 0; i < match_count; i++) {
printf(" %.16s...\n", matches[i]);
}
printf("Please be more specific with your prefix.\n");
// Free all matches and return NULL
for (int i = 0; i < match_count; i++) {
free(matches[i]);
}
return NULL;
}
}
uint64_t parse_size_string(const char* size_str) {
if (!size_str) return 0;
char* endptr;
double value = strtod(size_str, &endptr);
if (value <= 0) return 0;
// Skip whitespace
while (*endptr && isspace(*endptr)) endptr++;
uint64_t multiplier = 1;
if (*endptr) {
char unit[4];
strncpy(unit, endptr, 3);
unit[3] = '\0';
// Convert to uppercase
for (int i = 0; unit[i]; i++) {
unit[i] = toupper(unit[i]);
}
if (strcmp(unit, "K") == 0 || strcmp(unit, "KB") == 0) {
multiplier = 1024ULL;
} else if (strcmp(unit, "M") == 0 || strcmp(unit, "MB") == 0) {
multiplier = 1024ULL * 1024ULL;
} else if (strcmp(unit, "G") == 0 || strcmp(unit, "GB") == 0) {
multiplier = 1024ULL * 1024ULL * 1024ULL;
} else if (strcmp(unit, "T") == 0 || strcmp(unit, "TB") == 0) {
multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL;
} else {
return 0; // Invalid unit
}
} else {
// No unit specified, treat as bytes
multiplier = 1;
}
return (uint64_t)(value * multiplier);
}
void get_directory_display(const char* file_path, char* result, size_t result_size) {
// Extract directory path from full file path
char dir_path[512];
char* last_slash = strrchr(file_path, '/');
if (last_slash) {
size_t dir_len = last_slash - file_path;
if (dir_len >= sizeof(dir_path)) {
dir_len = sizeof(dir_path) - 1;
}
strncpy(dir_path, file_path, dir_len);
dir_path[dir_len] = '\0';
} else {
// No directory separator, assume current directory
strcpy(dir_path, ".");
}
// USB Drive Detection and Smart Shortening
char* home_dir = getenv("HOME");
// Check for USB/removable media mount patterns
if (strstr(dir_path, "/media/") || strstr(dir_path, "/run/media/") || strstr(dir_path, "/mnt/")) {
// Extract USB label/name
char* media_start = NULL;
if (strstr(dir_path, "/media/")) {
media_start = strstr(dir_path, "/media/");
} else if (strstr(dir_path, "/run/media/")) {
media_start = strstr(dir_path, "/run/media/");
} else if (strstr(dir_path, "/mnt/")) {
media_start = strstr(dir_path, "/mnt/");
}
if (media_start) {
// Find the USB label part
char* path_after_media = strchr(media_start + 1, '/');
if (path_after_media) {
path_after_media++; // Skip the slash
// For /media/user/LABEL pattern, skip the username to get to the drive label
if (strstr(media_start, "/media/")) {
char* next_slash = strchr(path_after_media, '/');
if (next_slash) {
path_after_media = next_slash + 1;
}
}
// For /run/media/user/LABEL pattern, skip the username
else if (strstr(media_start, "/run/media/")) {
char* next_slash = strchr(path_after_media, '/');
if (next_slash) {
path_after_media = next_slash + 1;
}
}
// Extract just the USB label (up to next slash or end)
char* label_end = strchr(path_after_media, '/');
char usb_label[32];
if (label_end) {
size_t label_len = label_end - path_after_media;
if (label_len > sizeof(usb_label) - 1) label_len = sizeof(usb_label) - 1;
strncpy(usb_label, path_after_media, label_len);
usb_label[label_len] = '\0';
} else {
// USB label is the last part
strncpy(usb_label, path_after_media, sizeof(usb_label) - 1);
usb_label[sizeof(usb_label) - 1] = '\0';
}
// Format with USB: prefix, limiting total length to fit in result
snprintf(result, result_size, "USB:%s", usb_label);
// Truncate if too long
if (strlen(result) > 11) {
result[11] = '\0';
}
return;
}
}
}
// Home directory shortening
if (home_dir && strncmp(dir_path, home_dir, strlen(home_dir)) == 0) {
if (dir_path[strlen(home_dir)] == '/' || dir_path[strlen(home_dir)] == '\0') {
// Replace home directory with ~
char temp[512];
snprintf(temp, sizeof(temp), "~%s", dir_path + strlen(home_dir));
// If result is too long, truncate intelligently
if (strlen(temp) > 11) {
// Show ~/...end_part
char* last_part = strrchr(temp, '/');
if (last_part && strlen(last_part) < 8) {
snprintf(result, result_size, "~...%s", last_part);
} else {
strncpy(result, temp, 11);
result[11] = '\0';
}
} else {
strncpy(result, temp, result_size - 1);
result[result_size - 1] = '\0';
}
return;
}
}
// Current working directory
if (strcmp(dir_path, ".") == 0 || strcmp(dir_path, get_current_pads_dir()) == 0) {
strncpy(result, "pads", result_size - 1);
result[result_size - 1] = '\0';
return;
}
// System/other paths - smart truncation with ellipsis
if (strlen(dir_path) > 11) {
// Try to show the most meaningful part
char* last_part = strrchr(dir_path, '/');
if (last_part && strlen(last_part) < 9) {
// Show .../last_part
snprintf(result, result_size, "...%s", last_part);
} else {
// Show first part with ellipsis
strncpy(result, dir_path, 8);
strncpy(result + 8, "...", result_size - 8 - 1);
result[result_size - 1] = '\0';
}
} else {
// Short enough, use as-is
strncpy(result, dir_path, result_size - 1);
result[result_size - 1] = '\0';
}
}
int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size) {
// Find the last directory separator
char* last_slash = strrchr(default_path, '/');
char directory[1024] = "";
char filename[512] = "";
if (last_slash) {
// Extract directory path
size_t dir_len = last_slash - default_path + 1; // Include the trailing slash
if (dir_len < sizeof(directory)) {
strncpy(directory, default_path, dir_len);
directory[dir_len] = '\0';
}
// Extract filename
strncpy(filename, last_slash + 1, sizeof(filename) - 1);
filename[sizeof(filename) - 1] = '\0';
} else {
// No directory separator, treat as filename only
strncpy(filename, default_path, sizeof(filename) - 1);
filename[sizeof(filename) - 1] = '\0';
}
// Setup terminal for raw input
struct termios orig_termios;
if (tcgetattr(STDIN_FILENO, &orig_termios) != 0) {
// Fallback to simple input if terminal control fails
printf("\n%s\n%s: ", prompt, directory);
fflush(stdout);
char input_buffer[512];
if (!fgets(input_buffer, sizeof(input_buffer), stdin)) {
return 1;
}
input_buffer[strcspn(input_buffer, "\n")] = 0;
if (strlen(input_buffer) == 0) {
strncpy(result, default_path, result_size - 1);
} else {
if (strlen(directory) > 0) {
snprintf(result, result_size, "%s%s", directory, input_buffer);
} else {
strncpy(result, input_buffer, result_size - 1);
}
}
result[result_size - 1] = '\0';
return 0;
}
// Set up raw terminal mode
struct termios raw_termios = orig_termios;
raw_termios.c_lflag &= ~(ECHO | ICANON);
raw_termios.c_cc[VMIN] = 1;
raw_termios.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_termios) != 0) {
// Fallback if terminal setup fails
printf("\n%s\n%s: ", prompt, directory);
fflush(stdout);
char input_buffer[512];
if (!fgets(input_buffer, sizeof(input_buffer), stdin)) {
return 1;
}
input_buffer[strcspn(input_buffer, "\n")] = 0;
if (strlen(input_buffer) == 0) {
strncpy(result, default_path, result_size - 1);
} else {
if (strlen(directory) > 0) {
snprintf(result, result_size, "%s%s", directory, input_buffer);
} else {
strncpy(result, input_buffer, result_size - 1);
}
}
result[result_size - 1] = '\0';
return 0;
}
// Display prompt and directory
printf("\n%s\n%s", prompt, directory);
fflush(stdout);
// Initialize editing buffer with default filename
char edit_buffer[512];
strncpy(edit_buffer, filename, sizeof(edit_buffer) - 1);
edit_buffer[sizeof(edit_buffer) - 1] = '\0';
int cursor_pos = strlen(edit_buffer);
int buffer_len = cursor_pos;
// Display initial filename
printf("%s", edit_buffer);
fflush(stdout);
// Main editing loop
int c;
while ((c = getchar()) != EOF) {
if (c == '\n' || c == '\r') {
// Enter key - accept input
break;
} else if (c == 127 || c == 8) {
// Backspace/Delete
if (cursor_pos > 0) {
// Move everything after cursor one position left
memmove(&edit_buffer[cursor_pos - 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1);
cursor_pos--;
buffer_len--;
// Redraw line: move cursor to start of filename area, clear to end, redraw buffer
printf("\r%s\033[K%s", directory, edit_buffer);
// Position cursor correctly
if (cursor_pos < buffer_len) {
printf("\033[%dD", buffer_len - cursor_pos);
}
fflush(stdout);
}
} else if (c == 27) {
// Escape sequence (arrow keys, etc.)
int c1 = getchar();
int c2 = getchar();
if (c1 == '[') {
if (c2 == 'C' && cursor_pos < buffer_len) {
// Right arrow
cursor_pos++;
printf("\033[1C");
fflush(stdout);
} else if (c2 == 'D' && cursor_pos > 0) {
// Left arrow
cursor_pos--;
printf("\033[1D");
fflush(stdout);
} else if (c2 == 'H') {
// Home key
printf("\033[%dD", cursor_pos);
cursor_pos = 0;
fflush(stdout);
} else if (c2 == 'F') {
// End key
printf("\033[%dC", buffer_len - cursor_pos);
cursor_pos = buffer_len;
fflush(stdout);
}
}
} else if (c >= 32 && c <= 126) {
// Printable character
if (buffer_len < (int)sizeof(edit_buffer) - 1) {
// Move everything after cursor one position right
memmove(&edit_buffer[cursor_pos + 1], &edit_buffer[cursor_pos], buffer_len - cursor_pos + 1);
edit_buffer[cursor_pos] = c;
cursor_pos++;
buffer_len++;
// Redraw from cursor position
printf("%c", c);
if (cursor_pos < buffer_len) {
// Print remaining characters and move cursor back
printf("%s\033[%dD", &edit_buffer[cursor_pos], buffer_len - cursor_pos);
}
fflush(stdout);
}
}
// Ignore other control characters
}
// Restore terminal
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
printf("\n");
// Construct final result
if (buffer_len == 0) {
// Empty input, use default
strncpy(result, default_path, result_size - 1);
} else {
// Combine directory with edited filename
edit_buffer[buffer_len] = '\0';
if (strlen(directory) > 0) {
snprintf(result, result_size, "%s%s", directory, edit_buffer);
} else {
strncpy(result, edit_buffer, result_size - 1);
}
}
result[result_size - 1] = '\0';
return 0;
}
char* get_preference(const char* key) {
if (strcmp(key, "default_pad") == 0) {
if (strlen(default_pad_path) > 0) {
return strdup(default_pad_path);
}
}
return NULL;
}
int set_preference(const char* key, const char* value) {
if (strcmp(key, "default_pad") == 0) {
if (value) {
strncpy(default_pad_path, value, sizeof(default_pad_path) - 1);
default_pad_path[sizeof(default_pad_path) - 1] = '\0';
} else {
default_pad_path[0] = '\0';
}
return save_preferences();
}
return 1;
}
char* get_default_pad_path(void) {
if (strlen(default_pad_path) > 0) {
return strdup(default_pad_path);
}
return NULL;
}
int set_default_pad_path(const char* pad_path) {
if (!pad_path) {
return set_preference("default_pad", NULL);
}
// Ensure we store the full absolute path
char absolute_path[1024];
if (pad_path[0] == '/') {
// Already absolute path
strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1);
absolute_path[sizeof(absolute_path) - 1] = '\0';
} else {
// Relative path - make it absolute
char current_dir[512];
if (getcwd(current_dir, sizeof(current_dir))) {
snprintf(absolute_path, sizeof(absolute_path), "%s/%s", current_dir, pad_path);
} else {
// Fallback to using the path as-is if getcwd fails
strncpy(absolute_path, pad_path, sizeof(absolute_path) - 1);
absolute_path[sizeof(absolute_path) - 1] = '\0';
}
}
return set_preference("default_pad", absolute_path);
}
// OTP thumb drive detection function implementation
int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size) {
const char* mount_dirs[] = {"/media", "/run/media", "/mnt", NULL};
for (int mount_idx = 0; mount_dirs[mount_idx] != NULL; mount_idx++) {
DIR* mount_dir = opendir(mount_dirs[mount_idx]);
if (!mount_dir) continue;
struct dirent* mount_entry;
while ((mount_entry = readdir(mount_dir)) != NULL) {
if (mount_entry->d_name[0] == '.') continue;
char mount_path[1024]; // Increased buffer size
snprintf(mount_path, sizeof(mount_path), "%s/%s", mount_dirs[mount_idx], mount_entry->d_name);
// For /media, we need to go one level deeper (user directories)
if (strcmp(mount_dirs[mount_idx], "/media") == 0) {
// This is /media/[username] - look inside for drives
DIR* user_dir = opendir(mount_path);
if (!user_dir) continue;
struct dirent* user_entry;
while ((user_entry = readdir(user_dir)) != NULL) {
if (user_entry->d_name[0] == '.') continue;
// Check if drive name starts with "OTP"
if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue;
char user_mount_path[2048]; // Increased buffer size
// Verify buffer has enough space before concatenation
size_t mount_len = strlen(mount_path);
size_t entry_len = strlen(user_entry->d_name);
if (mount_len + entry_len + 2 < sizeof(user_mount_path)) {
snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name);
// Check if this is a readable directory
DIR* drive_dir = opendir(user_mount_path);
if (drive_dir) {
closedir(drive_dir);
strncpy(otp_drive_path, user_mount_path, path_size - 1);
otp_drive_path[path_size - 1] = '\0';
closedir(user_dir);
closedir(mount_dir);
return 1; // Found OTP drive
}
}
}
closedir(user_dir);
} else if (strcmp(mount_dirs[mount_idx], "/run/media") == 0) {
// For /run/media, we need to go one level deeper (skip username)
DIR* user_dir = opendir(mount_path);
if (!user_dir) continue;
struct dirent* user_entry;
while ((user_entry = readdir(user_dir)) != NULL) {
if (user_entry->d_name[0] == '.') continue;
// Check if drive name starts with "OTP"
if (strncmp(user_entry->d_name, "OTP", 3) != 0) continue;
char user_mount_path[2048]; // Increased buffer size
snprintf(user_mount_path, sizeof(user_mount_path), "%s/%s", mount_path, user_entry->d_name);
// Check if this is a readable directory
DIR* drive_dir = opendir(user_mount_path);
if (drive_dir) {
closedir(drive_dir);
strncpy(otp_drive_path, user_mount_path, path_size - 1);
otp_drive_path[path_size - 1] = '\0';
closedir(user_dir);
closedir(mount_dir);
return 1; // Found OTP drive
}
}
closedir(user_dir);
} else {
// Direct mount point (like /mnt/OTP_DRIVE)
// Check if drive name starts with "OTP"
if (strncmp(mount_entry->d_name, "OTP", 3) == 0) {
DIR* drive_dir = opendir(mount_path);
if (drive_dir) {
closedir(drive_dir);
strncpy(otp_drive_path, mount_path, path_size - 1);
otp_drive_path[path_size - 1] = '\0';
closedir(mount_dir);
return 1; // Found OTP drive
}
}
}
}
closedir(mount_dir);
}
return 0; // No OTP drive found
}
int save_preferences(void) {
char* home_dir = getenv("HOME");
if (!home_dir) {
return 1;
}
char preferences_dir[1024];
char preferences_file[2048]; // Increased buffer size to accommodate longer paths
snprintf(preferences_dir, sizeof(preferences_dir), "%s/.otp", home_dir);
snprintf(preferences_file, sizeof(preferences_file), "%s/otp.conf", preferences_dir);
// Create .otp directory if it doesn't exist
struct stat st = {0};
if (stat(preferences_dir, &st) == -1) {
if (mkdir(preferences_dir, 0755) != 0) {
return 1;
}
}
FILE* file = fopen(preferences_file, "w");
if (!file) {
return 1;
}
fprintf(file, "# OTP Preferences File\n");
fprintf(file, "# This file is automatically generated and updated by the OTP program\n\n");
if (strlen(default_pad_path) > 0) {
fprintf(file, "default_pad=%s\n", default_pad_path);
}
fclose(file);
return 0;
}