1041 lines
36 KiB
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 "../include/otp.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;
|
|
} |