diff --git a/Makefile b/Makefile index 7344348..a3942e0 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -Wextra -g -Iinclude +CFLAGS = -Wextra -g -Iinclude -Iinclude/core -Iinclude/input -Iinclude/history -Iinclude/search -Iinclude/ui -Iinclude/data -Iinclude/git -Iinclude/system -Iinclude/utils LIBS = -lm -lncurses # Directories @@ -7,9 +7,9 @@ SRC_DIR = src INC_DIR = include BUILD_DIR = build -# Get all .c files from src directory -SOURCES = $(wildcard $(SRC_DIR)/*.c) -# Generate object file names in build directory +# Get all .c files recursively from src directory and subdirectories +SOURCES = $(shell find $(SRC_DIR) -name "*.c") +# Generate object file names in build directory, preserving subdirectory structure OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o) TARGET = shell @@ -21,7 +21,9 @@ $(BUILD_DIR): $(TARGET): $(OBJECTS) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) +# Create build subdirectories and compile $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c + @mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ clean: diff --git a/build/aliases.o b/build/aliases.o deleted file mode 100644 index 711f278..0000000 Binary files a/build/aliases.o and /dev/null differ diff --git a/build/autocorrect.o b/build/autocorrect.o deleted file mode 100644 index 5ee3484..0000000 Binary files a/build/autocorrect.o and /dev/null differ diff --git a/build/bookmarks.o b/build/bookmarks.o deleted file mode 100644 index 4111330..0000000 Binary files a/build/bookmarks.o and /dev/null differ diff --git a/build/builtins.o b/build/builtins.o deleted file mode 100644 index a8e16a2..0000000 Binary files a/build/builtins.o and /dev/null differ diff --git a/build/countdown_timer.o b/build/countdown_timer.o deleted file mode 100644 index 479bf6e..0000000 Binary files a/build/countdown_timer.o and /dev/null differ diff --git a/build/diff_viewer.o b/build/diff_viewer.o deleted file mode 100644 index 0966e63..0000000 Binary files a/build/diff_viewer.o and /dev/null differ diff --git a/build/favorite_cities.o b/build/favorite_cities.o deleted file mode 100644 index 4fa007c..0000000 Binary files a/build/favorite_cities.o and /dev/null differ diff --git a/build/filters.o b/build/filters.o deleted file mode 100644 index 3d9aa16..0000000 Binary files a/build/filters.o and /dev/null differ diff --git a/build/fzf_native.o b/build/fzf_native.o deleted file mode 100644 index 4424154..0000000 Binary files a/build/fzf_native.o and /dev/null differ diff --git a/build/git_integration.o b/build/git_integration.o deleted file mode 100644 index 80a8a8e..0000000 Binary files a/build/git_integration.o and /dev/null differ diff --git a/build/grep.o b/build/grep.o deleted file mode 100644 index 476d2ed..0000000 Binary files a/build/grep.o and /dev/null differ diff --git a/build/line_reader.o b/build/line_reader.o deleted file mode 100644 index c76f8a5..0000000 Binary files a/build/line_reader.o and /dev/null differ diff --git a/build/main.o b/build/main.o deleted file mode 100644 index 217749e..0000000 Binary files a/build/main.o and /dev/null differ diff --git a/build/ncurses_diff_viewer.o b/build/ncurses_diff_viewer.o deleted file mode 100644 index e7728d9..0000000 Binary files a/build/ncurses_diff_viewer.o and /dev/null differ diff --git a/build/persistent_history.o b/build/persistent_history.o deleted file mode 100644 index c57add7..0000000 Binary files a/build/persistent_history.o and /dev/null differ diff --git a/build/ps_command.o b/build/ps_command.o deleted file mode 100644 index 71a7a54..0000000 Binary files a/build/ps_command.o and /dev/null differ diff --git a/build/ripgrep.o b/build/ripgrep.o deleted file mode 100644 index aa620e5..0000000 Binary files a/build/ripgrep.o and /dev/null differ diff --git a/build/shell.o b/build/shell.o deleted file mode 100644 index 7afb611..0000000 Binary files a/build/shell.o and /dev/null differ diff --git a/build/structured_data.o b/build/structured_data.o deleted file mode 100644 index f3259fe..0000000 Binary files a/build/structured_data.o and /dev/null differ diff --git a/build/system_monitor.o b/build/system_monitor.o deleted file mode 100644 index dd10651..0000000 Binary files a/build/system_monitor.o and /dev/null differ diff --git a/build/tab_complete.o b/build/tab_complete.o deleted file mode 100644 index 72ee4de..0000000 Binary files a/build/tab_complete.o and /dev/null differ diff --git a/build/themes.o b/build/themes.o deleted file mode 100644 index ffa412b..0000000 Binary files a/build/themes.o and /dev/null differ diff --git a/build/timer.o b/build/timer.o deleted file mode 100644 index 6e8c954..0000000 Binary files a/build/timer.o and /dev/null differ diff --git a/build/weather.o b/build/weather.o deleted file mode 100644 index 06b26ce..0000000 Binary files a/build/weather.o and /dev/null differ diff --git a/include/builtins.h b/include/builtins.h deleted file mode 100644 index 291e6f1..0000000 --- a/include/builtins.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * builtins.h - * Declarations for all built-in shell commands - */ - -#ifndef BUILTINS_H -#define BUILTINS_H - -#include "common.h" -#include "system_monitor.h" -#include - -// Syntax highlighting definitions using ANSI colors -#define COLOR_DEFAULT ANSI_COLOR_RESET -#define COLOR_KEYWORD ANSI_COLOR_CYAN -#define COLOR_STRING ANSI_COLOR_GREEN -#define COLOR_COMMENT "\x1b[90m" // Dark gray -#define COLOR_NUMBER ANSI_COLOR_MAGENTA -#define COLOR_PREPROCESSOR ANSI_COLOR_YELLOW -#define COLOR_IDENTIFIER "\x1b[97m" // Bright white - -// Add declarations for color functions -void set_color(int color); -void reset_color(); - -// History command implementation -#define HISTORY_SIZE 10 - -// New struct to hold history entries with timestamps -typedef struct { - char *command; - time_t timestamp; -} HistoryEntry; - -// History variables - made extern to be accessible from line_reader.c -extern HistoryEntry command_history[HISTORY_SIZE]; -extern int history_count; -extern int history_index; - -// Built-in command declarations -int lsh_cd(char **args); -int lsh_help(char **args); -int lsh_exit(char **args); -int lsh_dir(char **args); -int lsh_clear(char **args); -int lsh_mkdir(char **args); -int lsh_rmdir(char **args); -int lsh_del(char **args); -int lsh_touch(char **args); -int lsh_pwd(char **args); -int lsh_cat(char **args); -int lsh_history(char **args); -int lsh_copy(char **args); -int lsh_paste(char **args); -int lsh_move(char **args); -int lsh_ps(char **args); -int lsh_news(char **args); -// Alias command declarations - added for alias support -int lsh_alias(char **args); -int lsh_unalias(char **args); -int lsh_aliases(char **args); // New command to list all aliases -// Bookmark command declarations - added for bookmark support -int lsh_bookmark(char **args); -int lsh_bookmarks(char **args); -int lsh_goto(char **args); -int lsh_unbookmark(char **args); -int lsh_focus_timer(char **args); -int lsh_weather(char **args); -// Text search command -int lsh_grep(char **args); -int lsh_actual_grep(char **args); -int lsh_ripgrep(char **args); -int lsh_fzf_native(char **args); -int lsh_clip(char **args); -int lsh_echo(char **args); -int lsh_self_destruct(); -int lsh_theme(char **args); -int lsh_loc(char **args); -int lsh_git_status(char **args); -int lsh_gg(char **args); -int lsh_stats(char **args); - -// Add command to history -void lsh_add_to_history(const char *command); - -// Get number of builtin commands -int lsh_num_builtins(void); - -// Expose the builtin command strings and function pointers -extern char *builtin_str[]; -extern int (*builtin_func[])(char **); - -/** - * Extract a string value from a JSON object - * - * @param json The JSON string to parse - * @param key The key to find - * @return Allocated string containing the value (caller must free) - */ -char *extract_json_string(const char *json, const char *key); - -#endif // BUILTINS_H diff --git a/include/common.h b/include/common.h deleted file mode 100644 index 47609b0..0000000 --- a/include/common.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * common.h - * Common includes, defines and macros used throughout the shell - */ - -#ifndef COMMON_H -#define COMMON_H - -// Standard library includes -#include -#include -#include -#include // For isprint -#include // For file control -#include // For POSIX API (replaces direct.h) -#include -#include // For waitpid -#include // For stat (replaces fileapi.h) -#include // For terminal control -#include // For console dimensions -#include // For signal handling -#include // For time functions -#include // For PATH_MAX - -// Define PATH_MAX if not defined -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -// Common defines -#define LSH_RL_BUFSIZE 1024 -#define LSH_TOK_BUFSIZE 64 -#define LSH_TOK_DELIM " \t\r\n\a" - -// Terminal control and color codes (ANSI sequences) -#define ANSI_COLOR_RESET "\x1b[0m" -#define ANSI_COLOR_BLACK "\x1b[30m" -#define ANSI_COLOR_RED "\x1b[31m" -#define ANSI_COLOR_GREEN "\x1b[32m" -#define ANSI_COLOR_YELLOW "\x1b[33m" -#define ANSI_COLOR_BLUE "\x1b[34m" -#define ANSI_COLOR_MAGENTA "\x1b[35m" -#define ANSI_COLOR_CYAN "\x1b[36m" -#define ANSI_COLOR_WHITE "\x1b[37m" - -// Background colors -#define ANSI_BG_BLACK "\x1b[40m" -#define ANSI_BG_RED "\x1b[41m" -#define ANSI_BG_GREEN "\x1b[42m" -#define ANSI_BG_YELLOW "\x1b[43m" -#define ANSI_BG_BLUE "\x1b[44m" -#define ANSI_BG_MAGENTA "\x1b[45m" -#define ANSI_BG_CYAN "\x1b[46m" -#define ANSI_BG_WHITE "\x1b[47m" - -// Cursor movement and screen control -#define ANSI_CLEAR_SCREEN "\x1b[2J" -#define ANSI_CURSOR_HOME "\x1b[H" -#define ANSI_SAVE_CURSOR "\x1b[s" -#define ANSI_RESTORE_CURSOR "\x1b[u" - -// Key code definitions used by the line reader -#define KEY_BACKSPACE 127 // Different on Linux -#define KEY_TAB 9 -#define KEY_ENTER 10 // Linux: 10 (LF) or 13 (CR) -#define KEY_ESCAPE 27 - -// Special keys (Linux terminal uses escape sequences) -// These will need handling in the line reader -#define KEY_UP 1000 -#define KEY_DOWN 1001 -#define KEY_LEFT 1002 -#define KEY_RIGHT 1003 -#define KEY_SHIFT_ENTER 1010 -#define KEY_SHIFT_TAB 1011 - -// Typedefs for compatibility -typedef unsigned int UINT; -typedef unsigned short WORD; -typedef int BOOL; -typedef int HANDLE; -typedef int DWORD; - -// Define Windows constants -#define FALSE 0 -#define TRUE 1 - -// Struct to replace COORD -typedef struct { - short X; - short Y; -} COORD; - -// Struct to replace SMALL_RECT -typedef struct { - short Left; - short Top; - short Right; - short Bottom; -} SMALL_RECT; - -// Struct to replace CONSOLE_SCREEN_BUFFER_INFO -typedef struct { - COORD dwSize; - COORD dwCursorPosition; - WORD wAttributes; - SMALL_RECT srWindow; -} CONSOLE_SCREEN_BUFFER_INFO; - -// Struct to replace CONSOLE_CURSOR_INFO -typedef struct { - DWORD dwSize; - BOOL bVisible; -} CONSOLE_CURSOR_INFO; - -#endif // COMMON_H diff --git a/include/aliases.h b/include/data/aliases.h similarity index 100% rename from include/aliases.h rename to include/data/aliases.h diff --git a/include/bookmarks.h b/include/data/bookmarks.h similarity index 100% rename from include/bookmarks.h rename to include/data/bookmarks.h diff --git a/include/favorite_cities.h b/include/data/favorite_cities.h similarity index 100% rename from include/favorite_cities.h rename to include/data/favorite_cities.h diff --git a/include/structured_data.h b/include/data/structured_data.h similarity index 100% rename from include/structured_data.h rename to include/data/structured_data.h diff --git a/include/git_integration.h b/include/git/git_integration.h similarity index 100% rename from include/git_integration.h rename to include/git/git_integration.h diff --git a/include/persistent_history.h b/include/history/persistent_history.h similarity index 100% rename from include/persistent_history.h rename to include/history/persistent_history.h diff --git a/include/autocorrect.h b/include/input/autocorrect.h similarity index 100% rename from include/autocorrect.h rename to include/input/autocorrect.h diff --git a/include/line_reader.h b/include/input/line_reader.h similarity index 100% rename from include/line_reader.h rename to include/input/line_reader.h diff --git a/include/tab_complete.h b/include/input/tab_complete.h similarity index 100% rename from include/tab_complete.h rename to include/input/tab_complete.h diff --git a/include/filters.h b/include/search/filters.h similarity index 100% rename from include/filters.h rename to include/search/filters.h diff --git a/include/fzf_native.h b/include/search/fzf_native.h similarity index 100% rename from include/fzf_native.h rename to include/search/fzf_native.h diff --git a/include/grep.h b/include/search/grep.h similarity index 100% rename from include/grep.h rename to include/search/grep.h diff --git a/include/ripgrep.h b/include/search/ripgrep.h similarity index 100% rename from include/ripgrep.h rename to include/search/ripgrep.h diff --git a/include/shell.h b/include/shell.h deleted file mode 100644 index 36909c8..0000000 --- a/include/shell.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * shell.h - * Core shell functionality - */ - -#ifndef SHELL_H -#define SHELL_H - -#include "common.h" -#include "structured_data.h" - -/** - * Display a welcome banner with BBQ sauce invention time - */ -void display_welcome_banner(void); - -/** - * Execute a command - * - * @param args Null-terminated array of command arguments - * @return 1 to continue the shell, 0 to exit - */ -int lsh_execute(char **args); - -/** - * Execute a pipeline of commands - * - * @param commands Null-terminated array of command arrays - * @return 1 to continue the shell, 0 to exit - */ -int lsh_execute_piped(char ***commands); - -/** - * Create a TableData structure from an ls command - * - * @param args Command arguments - * @return TableData pointer (must be freed by caller) or NULL on error - */ -TableData* create_ls_table(char **args); - -/** - * Parse input line into an array of commands (for pipelines) - * - * @param line Input command line - * @return Null-terminated array of command arrays - */ -char*** lsh_split_commands(char *line); - -/** - * Launch an external program - * - * @param args Null-terminated array of command arguments - * @return Always returns 1 (continue the shell) - */ -int lsh_launch(char **args); - -/** - * Main shell loop - */ -void lsh_loop(void); - -/** - * Free memory for a command array from lsh_split_commands - */ -void free_commands(char ***commands); - -/** - * Initialize the status bar at the bottom of the screen - * - * @param fd File descriptor for terminal (STDOUT_FILENO) - * @return 1 if successful, 0 on failure - */ -int init_status_bar(int fd); - -/** - * Temporarily hide the status bar before command execution - * - * @param fd File descriptor for terminal (STDOUT_FILENO) - */ -void hide_status_bar(int fd); - -/** - * Ensures there's space for the status bar by scrolling if needed - * - * @param fd File descriptor for terminal (STDOUT_FILENO) - */ -void ensure_status_bar_space(int fd); - -/** - * Check for terminal window resize and update status bar position - * - * @param fd File descriptor for terminal (STDOUT_FILENO) - */ -void check_console_resize(int fd); - -/** - * Update the status bar with Git information - * - * @param fd File descriptor for terminal (STDOUT_FILENO) - * @param git_info The Git information to display in the status bar - */ -void update_status_bar(int fd, const char *git_info); - -/** - * Get the name of the parent and current directories from a path - * - * @param cwd The current working directory path - * @param parent_dir_name Buffer to store the parent directory name - * @param current_dir_name Buffer to store the current directory name - * @param buf_size Size of the buffers - */ -void get_path_display(const char *cwd, char *parent_dir_name, - char *current_dir_name, size_t buf_size); - -/** - * Initialize the terminal for raw mode - * - * @param orig_termios Original terminal settings to restore later - * @return File descriptor for terminal - */ -int init_terminal(struct termios *orig_termios); - -/** - * Restore terminal to original settings - * - * @param fd File descriptor for terminal - * @param orig_termios Original terminal settings - */ -void restore_terminal(int fd, struct termios *orig_termios); - -/** - * Get console dimensions - * - * @param fd File descriptor for terminal - * @param width Pointer to store width - * @param height Pointer to store height - * @return 1 on success, 0 on failure - */ -int get_console_dimensions(int fd, int *width, int *height); - -#endif // SHELL_H \ No newline at end of file diff --git a/include/system_monitor.h b/include/system/system_monitor.h similarity index 100% rename from include/system_monitor.h rename to include/system/system_monitor.h diff --git a/include/diff_viewer.h b/include/ui/diff_viewer.h similarity index 100% rename from include/diff_viewer.h rename to include/ui/diff_viewer.h diff --git a/include/ncurses_diff_viewer.h b/include/ui/ncurses_diff_viewer.h similarity index 100% rename from include/ncurses_diff_viewer.h rename to include/ui/ncurses_diff_viewer.h diff --git a/include/themes.h b/include/ui/themes.h similarity index 100% rename from include/themes.h rename to include/ui/themes.h diff --git a/include/countdown_timer.h b/include/utils/countdown_timer.h similarity index 100% rename from include/countdown_timer.h rename to include/utils/countdown_timer.h diff --git a/include/timer.h b/include/utils/timer.h similarity index 100% rename from include/timer.h rename to include/utils/timer.h diff --git a/include/weather.h b/include/utils/weather.h similarity index 100% rename from include/weather.h rename to include/utils/weather.h diff --git a/shell b/shell index 4b082ac..a206703 100755 Binary files a/shell and b/shell differ diff --git a/src/builtins.c b/src/builtins.c deleted file mode 100644 index 84496e4..0000000 --- a/src/builtins.c +++ /dev/null @@ -1,1444 +0,0 @@ -/** - * builtins.c - * Implementation of all built-in shell commands - */ - -#include "builtins.h" -#include "common.h" -#include "diff_viewer.h" -#include "ncurses_diff_viewer.h" -#include "filters.h" -#include "fzf_native.h" -#include "git_integration.h" -#include "grep.h" -#include "persistent_history.h" -#include "structured_data.h" -#include "themes.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// History variables -HistoryEntry command_history[HISTORY_SIZE]; -int history_count = 0; -int history_index = 0; - -// String array of built-in command names -char *builtin_str[] = { - "cd", "help", "exit", "dir", "clear", - "mkdir", "rmdir", "del", "touch", "pwd", - "cat", "history", "copy", "move", "paste", - "ps", "news", "alias", "unalias", "aliases", - "bookmark", "bookmarks", "goto", "unbookmark", "focus_timer", - "weather", "grep", "grep-text", "ripgrep", "fzf", - "clip", "echo", "theme", "loc", "git_status", - "gg", "ls", "stats", "monitor", -}; - -// Array of function pointers to built-in command implementations -int (*builtin_func[])(char **) = { - &lsh_cd, &lsh_help, &lsh_exit, &lsh_dir, - &lsh_clear, &lsh_mkdir, &lsh_rmdir, &lsh_del, - &lsh_touch, &lsh_pwd, &lsh_cat, &lsh_history, - &lsh_copy, &lsh_move, &lsh_paste, &lsh_ps, - &lsh_news, &lsh_alias, &lsh_unalias, &lsh_aliases, - &lsh_bookmark, &lsh_bookmarks, &lsh_goto, &lsh_unbookmark, - &lsh_focus_timer, &lsh_weather, &lsh_grep, &lsh_actual_grep, - &lsh_ripgrep, &lsh_fzf_native, &lsh_clip, &lsh_echo, - &lsh_theme, &lsh_loc, &lsh_git_status, &lsh_gg, - lsh_dir, - &lsh_stats, &builtin_monitor, -}; - -/** - * Set the console text color - */ -void set_color(int color) { - switch (color) { - case 0: - printf(ANSI_COLOR_RESET); - break; - case 1: - printf(ANSI_COLOR_RED); - break; - case 2: - printf(ANSI_COLOR_GREEN); - break; - case 3: - printf(ANSI_COLOR_YELLOW); - break; - case 4: - printf(ANSI_COLOR_BLUE); - break; - case 5: - printf(ANSI_COLOR_MAGENTA); - break; - case 6: - printf(ANSI_COLOR_CYAN); - break; - case 7: - printf(ANSI_COLOR_WHITE); - break; - default: - printf(ANSI_COLOR_RESET); - } -} - -/** - * Reset console color to default - */ -void reset_color() { printf(ANSI_COLOR_RESET); } - -/** - * Get the number of built-in commands - */ -int lsh_num_builtins() { return sizeof(builtin_str) / sizeof(char *); } - -/** - * Add a command to the history - */ -void lsh_add_to_history(const char *command) { - // Don't add empty commands or duplicates of the last command - if (!command || *command == '\0' || - (history_count > 0 && - strcmp(command_history[history_index - 1].command, command) == 0)) { - return; - } - - // Free the oldest entry if we're overwriting it - if (history_count == HISTORY_SIZE) { - free(command_history[history_index].command); - } else { - history_count++; - } - - // Add the new command - command_history[history_index].command = strdup(command); - command_history[history_index].timestamp = time(NULL); - - // Update the index - history_index = (history_index + 1) % HISTORY_SIZE; -} - -/** - * Built-in command: cd - * Changes the current directory - */ -int lsh_cd(char **args) { - if (args[1] == NULL) { - // No argument provided, change to home directory - char *home_dir = getenv("HOME"); - if (home_dir == NULL) { - fprintf(stderr, "lsh: HOME environment variable not set\n"); - return 1; - } - if (chdir(home_dir) != 0) { - perror("lsh: cd"); - } - } else { - if (chdir(args[1]) != 0) { - perror("lsh: cd"); - } - } - return 1; -} - -/** - * Built-in command: help - * Displays help information - */ -int lsh_help(char **args) { - if (args[1] != NULL) { - // Command-specific help - if (strcmp(args[1], "cd") == 0) { - printf("cd - Change directory\n"); - printf("Usage: cd [directory]\n"); - printf(" cd - change to home directory\n"); - printf(" cd - change to specified directory\n"); - } else if (strcmp(args[1], "cat") == 0) { - printf("cat - Display file contents\n"); - printf("Usage: cat \n"); - printf(" Displays the contents of the specified file\n"); - } else if (strcmp(args[1], "grep") == 0) { - printf("grep - Search for text patterns in files\n"); - printf("Usage: grep \n"); - printf(" Searches for the specified pattern in the given file\n"); - } else if (strcmp(args[1], "gg") == 0) { - printf("gg - Git command shortcuts\n"); - printf("Usage: gg \n"); - printf("Available commands:\n"); - printf(" s - status (enhanced git status)\n"); - printf(" c - commit\n"); - printf(" p - pull\n"); - printf(" ps - push\n"); - printf(" a - add .\n"); - printf(" l - log\n"); - printf(" d - diff\n"); - printf(" dd - ncurses diff viewer\n"); - printf(" b - branch\n"); - printf(" ch - checkout\n"); - printf(" o - open repository in browser\n"); - } else if (strcmp(args[1], "weather") == 0) { - printf("weather - Shows weather information\n"); - printf("Usage:\n"); - printf(" weather - shows weather for your current location\n"); - printf(" weather - shows weather for a specific city\n"); - printf("Examples:\n"); - printf(" weather\n"); - printf(" weather London\n"); - printf(" weather New York\n"); - } else if (strcmp(args[1], "dir") == 0 || strcmp(args[1], "ls") == 0) { - printf("dir/ls - List directory contents\n"); - printf("Usage: dir\n"); - printf(" Lists files and directories in the current directory\n"); - printf(" Shows file sizes, types, and modification dates in a table format\n"); - } else if (strcmp(args[1], "mkdir") == 0) { - printf("mkdir - Create directory\n"); - printf("Usage: mkdir \n"); - printf(" Creates a new directory with the specified name\n"); - } else if (strcmp(args[1], "rmdir") == 0) { - printf("rmdir - Remove directory\n"); - printf("Usage: rmdir \n"); - printf(" Removes an empty directory\n"); - } else if (strcmp(args[1], "del") == 0) { - printf("del - Delete file\n"); - printf("Usage: del \n"); - printf(" Deletes the specified file\n"); - } else if (strcmp(args[1], "touch") == 0) { - printf("touch - Create file or update timestamp\n"); - printf("Usage: touch \n"); - printf(" Creates a new empty file or updates the timestamp of an existing file\n"); - } else if (strcmp(args[1], "pwd") == 0) { - printf("pwd - Print working directory\n"); - printf("Usage: pwd\n"); - printf(" Displays the current working directory path\n"); - } else if (strcmp(args[1], "history") == 0) { - printf("history - Show command history\n"); - printf("Usage: history\n"); - printf(" Displays the list of previously executed commands with timestamps\n"); - } else if (strcmp(args[1], "copy") == 0) { - printf("copy - Copy file\n"); - printf("Usage: copy \n"); - printf(" Copies a file from source to destination\n"); - } else if (strcmp(args[1], "move") == 0) { - printf("move - Move/rename file\n"); - printf("Usage: move \n"); - printf(" Moves or renames a file from source to destination\n"); - } else if (strcmp(args[1], "clear") == 0) { - printf("clear - Clear screen\n"); - printf("Usage: clear\n"); - printf(" Clears the terminal screen\n"); - } else if (strcmp(args[1], "echo") == 0) { - printf("echo - Display text\n"); - printf("Usage: echo [text...]\n"); - printf(" Displays the specified text to the terminal\n"); - } else if (strcmp(args[1], "alias") == 0) { - printf("alias - Create command alias\n"); - printf("Usage: alias \n"); - printf(" Creates a shortcut alias for a command\n"); - } else if (strcmp(args[1], "bookmark") == 0) { - printf("bookmark - Bookmark current directory\n"); - printf("Usage: bookmark \n"); - printf(" Saves the current directory with a bookmark name\n"); - } else if (strcmp(args[1], "goto") == 0) { - printf("goto - Go to bookmarked directory\n"); - printf("Usage: goto \n"); - printf(" Changes to a previously bookmarked directory\n"); - } else if (strcmp(args[1], "theme") == 0) { - printf("theme - Change shell theme\n"); - printf("Usage: theme \n"); - printf(" Changes the visual appearance of the shell\n"); - } else if (strcmp(args[1], "loc") == 0) { - printf("loc - Count lines of code\n"); - printf("Usage: loc \n"); - printf(" Counts total lines, code lines, comments, and blank lines in a file\n"); - } else if (strcmp(args[1], "monitor") == 0) { - printf("monitor - System monitor\n"); - printf("Usage: monitor\n"); - printf(" Displays real-time system information including CPU, memory, and disk usage\n"); - } else if (strcmp(args[1], "stats") == 0) { - printf("stats - Command usage statistics\n"); - printf("Usage: stats\n"); - printf(" Shows statistics about your most frequently used commands\n"); - } else if (strcmp(args[1], "help") == 0) { - printf("help - Display help information\n"); - printf("Usage:\n"); - printf(" help - show all available commands\n"); - printf(" help - show help for a specific command\n"); - } else if (strcmp(args[1], "exit") == 0) { - printf("exit - Exit the shell\n"); - printf("Usage: exit\n"); - printf(" Terminates the shell session\n"); - } else if (strcmp(args[1], "ps") == 0) { - printf("ps - List running processes\n"); - printf("Usage: ps\n"); - printf(" Displays a list of all running processes on the system\n"); - } else if (strcmp(args[1], "news") == 0) { - printf("news - Show latest repository updates\n"); - printf("Usage: news\n"); - printf(" Fetches and displays the latest commit information from the GitHub repository\n"); - } else if (strcmp(args[1], "unalias") == 0) { - printf("unalias - Remove command alias\n"); - printf("Usage: unalias \n"); - printf(" Removes a previously created command alias\n"); - } else if (strcmp(args[1], "aliases") == 0) { - printf("aliases - List all aliases\n"); - printf("Usage: aliases\n"); - printf(" Displays all currently defined command aliases\n"); - } else if (strcmp(args[1], "bookmarks") == 0) { - printf("bookmarks - List all bookmarks\n"); - printf("Usage: bookmarks\n"); - printf(" Displays all saved directory bookmarks\n"); - } else if (strcmp(args[1], "unbookmark") == 0) { - printf("unbookmark - Remove bookmark\n"); - printf("Usage: unbookmark \n"); - printf(" Removes a previously saved directory bookmark\n"); - } else if (strcmp(args[1], "focus_timer") == 0) { - printf("focus_timer - Productivity timer\n"); - printf("Usage: focus_timer [minutes]\n"); - printf(" Starts a focus/pomodoro timer for productivity sessions\n"); - } else if (strcmp(args[1], "grep-text") == 0) { - printf("grep-text - Alternative text search\n"); - printf("Usage: grep-text \n"); - printf(" Alternative implementation for searching text patterns in files\n"); - } else if (strcmp(args[1], "ripgrep") == 0) { - printf("ripgrep - Fast text search\n"); - printf("Usage: ripgrep [path]\n"); - printf(" Fast recursive text search using ripgrep-like functionality\n"); - } else if (strcmp(args[1], "fzf") == 0) { - printf("fzf - Fuzzy file finder\n"); - printf("Usage: fzf\n"); - printf(" Interactive fuzzy file finder for quick file selection\n"); - } else if (strcmp(args[1], "clip") == 0) { - printf("clip - Clipboard operations\n"); - printf("Usage: clip\n"); - printf(" Clipboard functionality (currently not implemented)\n"); - } else if (strcmp(args[1], "git_status") == 0) { - printf("git_status - Git repository status\n"); - printf("Usage: git_status\n"); - printf(" Shows the current Git repository status\n"); - } else if (strcmp(args[1], "paste") == 0) { - printf("paste - Paste clipboard content\n"); - printf("Usage: paste\n"); - printf(" Paste functionality (currently not implemented)\n"); - } else { - printf("No help available for '%s'\n", args[1]); - printf("Type 'help' to see all available commands\n"); - } - return 1; - } - - // General help - show all commands - printf("LSH Shell - A lightweight shell with modern features\n"); - printf("Type a command and press Enter to execute it.\n"); - printf("The following built-in commands are available:\n\n"); - - // Sort commands alphabetically - char *sorted_commands[lsh_num_builtins()]; - for (int i = 0; i < lsh_num_builtins(); i++) { - sorted_commands[i] = builtin_str[i]; - } - - for (int i = 0; i < lsh_num_builtins() - 1; i++) { - for (int j = i + 1; j < lsh_num_builtins(); j++) { - if (strcmp(sorted_commands[i], sorted_commands[j]) > 0) { - char *temp = sorted_commands[i]; - sorted_commands[i] = sorted_commands[j]; - sorted_commands[j] = temp; - } - } - } - - // Print commands in columns - int columns = 4; - int rows = (lsh_num_builtins() + columns - 1) / columns; - for (int i = 0; i < rows; i++) { - for (int j = 0; j < columns; j++) { - int index = j * rows + i; - if (index < lsh_num_builtins()) { - printf("%-15s", sorted_commands[index]); - } - } - printf("\n"); - } - - printf( - "\nFor more information on specific commands, type 'help '\n"); - printf("Use tab completion for commands and file paths\n"); - printf("Use arrow keys to navigate command history\n"); - printf("Type a partial command followed by '?' for suggestions\n"); - - return 1; -} - -/** - * Built-in command: exit - * Exits the shell - */ -int lsh_exit(char **args) { return 0; } - -/** - * Built-in command: dir - * Lists files in the current directory - */ -int lsh_dir(char **args) { - DIR *dir; - struct dirent *entry; - struct stat file_stat; - char cwd[PATH_MAX]; - char file_path[PATH_MAX]; - int detailed = 0; - - // Create table headers - char *headers[] = {"Name", "Size", "Type", "Modified"}; - TableData *table = create_table(headers, 4); - if (!table) { - fprintf(stderr, "lsh: failed to create table\n"); - return 1; - } - - if (getcwd(cwd, sizeof(cwd)) == NULL) { - perror("lsh: getcwd"); - free_table(table); - return 1; - } - - dir = opendir(cwd); - if (dir == NULL) { - perror("lsh: opendir"); - free_table(table); - return 1; - } - - // Collect all entries - while ((entry = readdir(dir)) != NULL) { - // Skip . and .. entries - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { - continue; - } - - snprintf(file_path, sizeof(file_path), "%s/%s", cwd, entry->d_name); - if (stat(file_path, &file_stat) == 0) { - // Create a new row - DataValue *row = (DataValue *)malloc(4 * sizeof(DataValue)); - if (!row) { - fprintf(stderr, "lsh: allocation error\n"); - continue; - } - - // Name column - row[0].type = TYPE_STRING; - row[0].value.str_val = strdup(entry->d_name); - row[0].is_highlighted = - S_ISDIR(file_stat.st_mode); // Highlight directories - - // Size column - row[1].type = TYPE_SIZE; - row[1].value.str_val = malloc(32); - if (S_ISDIR(file_stat.st_mode)) { - strcpy(row[1].value.str_val, ""); - } else if (file_stat.st_size < 1024) { - snprintf(row[1].value.str_val, 32, "%d B", (int)file_stat.st_size); - } else if (file_stat.st_size < 1024 * 1024) { - snprintf(row[1].value.str_val, 32, "%.1f KB", - file_stat.st_size / 1024.0); - } else { - snprintf(row[1].value.str_val, 32, "%.1f MB", - file_stat.st_size / (1024.0 * 1024.0)); - } - row[1].is_highlighted = 0; - - // Type column - row[2].type = TYPE_STRING; - if (S_ISDIR(file_stat.st_mode)) { - row[2].value.str_val = strdup("Directory"); - } else if (S_ISREG(file_stat.st_mode)) { - // Try to determine file type by extension - char *ext = strrchr(entry->d_name, '.'); - if (ext != NULL) { - ext++; // Skip the dot - if (strcasecmp(ext, "c") == 0 || strcasecmp(ext, "cpp") == 0 || - strcasecmp(ext, "h") == 0 || strcasecmp(ext, "hpp") == 0) { - row[2].value.str_val = strdup("Source"); - } else if (strcasecmp(ext, "exe") == 0 || - strcasecmp(ext, "bat") == 0 || - strcasecmp(ext, "sh") == 0 || - strcasecmp(ext, "com") == 0) { - row[2].value.str_val = strdup("Executable"); - } else if (strcasecmp(ext, "txt") == 0 || - strcasecmp(ext, "md") == 0 || - strcasecmp(ext, "log") == 0) { - row[2].value.str_val = strdup("Text"); - } else if (strcasecmp(ext, "jpg") == 0 || - strcasecmp(ext, "png") == 0 || - strcasecmp(ext, "gif") == 0 || - strcasecmp(ext, "bmp") == 0) { - row[2].value.str_val = strdup("Image"); - } else { - row[2].value.str_val = strdup("File"); - } - } else { - row[2].value.str_val = strdup("File"); - } - } else { - row[2].value.str_val = strdup("Special"); - } - row[2].is_highlighted = 0; - - // Modified date column - row[3].type = TYPE_STRING; - row[3].value.str_val = malloc(32); - struct tm *tm_info = localtime(&file_stat.st_mtime); - strftime(row[3].value.str_val, 32, "%Y-%m-%d %H:%M", tm_info); - row[3].is_highlighted = 0; - - // Add row to table - add_table_row(table, row); - } - } - - closedir(dir); - - // Print the table - print_table(table); - - // Free the table - free_table(table); - - return 1; -} - -/** - * Built-in command: clear - * Clears the screen - */ -int lsh_clear(char **args) { - printf(ANSI_CLEAR_SCREEN); - printf(ANSI_CURSOR_HOME); - return 1; -} - -/** - * Built-in command: mkdir - * Creates a new directory - */ -int lsh_mkdir(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "lsh: expected argument to \"mkdir\"\n"); - } else { - if (mkdir(args[1], 0755) != 0) { - perror("lsh: mkdir"); - } - } - return 1; -} - -/** - * Built-in command: rmdir - * Removes a directory - */ -int lsh_rmdir(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "lsh: expected argument to \"rmdir\"\n"); - } else { - if (rmdir(args[1]) != 0) { - perror("lsh: rmdir"); - } - } - return 1; -} - -/** - * Built-in command: del - * Deletes a file - */ -int lsh_del(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "lsh: expected argument to \"del\"\n"); - } else { - if (unlink(args[1]) != 0) { - perror("lsh: del"); - } - } - return 1; -} - -/** - * Built-in command: touch - * Creates a new file or updates timestamp of existing file - */ -int lsh_touch(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "lsh: expected argument to \"touch\"\n"); - } else { - int fd = open(args[1], O_CREAT | O_WRONLY | O_APPEND, 0644); - if (fd == -1) { - perror("lsh: touch"); - } else { - close(fd); - } - } - return 1; -} - -/** - * Built-in command: pwd - * Prints the current working directory - */ -int lsh_pwd(char **args) { - char cwd[PATH_MAX]; - if (getcwd(cwd, sizeof(cwd)) != NULL) { - printf("%s\n", cwd); - } else { - perror("lsh: getcwd"); - } - return 1; -} - -/** - * Built-in command: cat - * Displays the contents of a file - */ -int lsh_cat(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "lsh: expected argument to \"cat\"\n"); - return 1; - } - - FILE *fp = fopen(args[1], "r"); - if (fp == NULL) { - perror("lsh: cat"); - return 1; - } - - char buffer[1024]; - while (fgets(buffer, sizeof(buffer), fp) != NULL) { - printf("%s", buffer); - } - - fclose(fp); - return 1; -} - -/** - * Built-in command: history - * Displays command history - */ -int lsh_history(char **args) { - char time_str[20]; - struct tm *tm_info; - - printf("Command History:\n"); - printf("----------------\n"); - - // Display history in chronological order - for (int i = 0; i < history_count; i++) { - int idx = (history_index - history_count + i + HISTORY_SIZE) % HISTORY_SIZE; - tm_info = localtime(&command_history[idx].timestamp); - strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); - printf("%3d: [%s] %s\n", i + 1, time_str, command_history[idx].command); - } - - return 1; -} - -/** - * Built-in command: copy - * Copies a file - */ -int lsh_copy(char **args) { - if (args[1] == NULL || args[2] == NULL) { - fprintf(stderr, - "lsh: expected source and destination arguments to \"copy\"\n"); - return 1; - } - - FILE *source = fopen(args[1], "rb"); - if (source == NULL) { - perror("lsh: copy (source)"); - return 1; - } - - FILE *dest = fopen(args[2], "wb"); - if (dest == NULL) { - fclose(source); - perror("lsh: copy (destination)"); - return 1; - } - - char buffer[4096]; - size_t bytes_read; - while ((bytes_read = fread(buffer, 1, sizeof(buffer), source)) > 0) { - fwrite(buffer, 1, bytes_read, dest); - } - - fclose(source); - fclose(dest); - printf("Copied %s to %s\n", args[1], args[2]); - return 1; -} - -/** - * Built-in command: move - * Moves a file - */ -int lsh_move(char **args) { - if (args[1] == NULL || args[2] == NULL) { - fprintf(stderr, - "lsh: expected source and destination arguments to \"move\"\n"); - return 1; - } - - if (rename(args[1], args[2]) != 0) { - perror("lsh: move"); - return 1; - } - - printf("Moved %s to %s\n", args[1], args[2]); - return 1; -} - -/** - * Built-in command: paste - * Placeholder for paste functionality - */ -int lsh_paste(char **args) { - printf("Paste functionality not implemented yet\n"); - return 1; -} - -/** - * Built-in command: ps - * Lists running processes - */ -int lsh_ps(char **args) { - FILE *fp = popen("ps -ef", "r"); - if (fp == NULL) { - perror("lsh: ps"); - return 1; - } - - char buffer[1024]; - while (fgets(buffer, sizeof(buffer), fp) != NULL) { - printf("%s", buffer); - } - - pclose(fp); - return 1; -} - -char *unescape_json_string(const char *str) { - if (!str) - return NULL; - - size_t len = strlen(str); - char *result = malloc(len + 1); - if (!result) - return NULL; - - size_t j = 0; - for (size_t i = 0; i < len; i++) { - if (str[i] == '\\' && i + 1 < len) { - switch (str[i + 1]) { - case 'n': - result[j++] = '\n'; - i++; // Skip the next character - break; - case 't': - result[j++] = '\t'; - i++; - break; - case 'r': - result[j++] = '\r'; - i++; - break; - case '\\': - result[j++] = '\\'; - i++; - break; - case '"': - result[j++] = '"'; - i++; - break; - default: - // Keep the backslash if it's not a recognized escape sequence - result[j++] = str[i]; - } - } else { - result[j++] = str[i]; - } - } - result[j] = '\0'; - - return result; -} - -/** - * Built-in command: news - * Displays information about the last push to the shelltest GitHub repository - */ -int lsh_news(char **args) { - printf("Fetching latest updates from shelltestLinux repository...\n\n"); - - // Use GitHub API to get the latest commit from the main branch - const char *api_url = - "https://api.github.com/repos/marcusDenslow/shelltestLinux/commits/main"; - char command[1024]; - snprintf(command, sizeof(command), - "curl -s -H \"Accept: application/vnd.github.v3+json\" \"%s\"", - api_url); - - FILE *fp = popen(command, "r"); - if (!fp) { - fprintf( - stderr, - "Failed to fetch updates. Please check your internet connection.\n"); - return 1; - } - - // Read the entire response - char response[8192] = ""; - size_t total_read = 0; - size_t chunk_size; - - while ((chunk_size = fread(response + total_read, 1, - sizeof(response) - total_read - 1, fp)) > 0) { - total_read += chunk_size; - if (total_read >= sizeof(response) - 1) - break; - } - response[total_read] = '\0'; - pclose(fp); - - // Check if we got an error response - if (strstr(response, "\"message\":") && - strstr(response, "\"documentation_url\":")) { - fprintf(stderr, "Failed to fetch updates from GitHub. The repository might " - "be private or there might be a connection issue.\n"); - return 1; - } - - // Extract commit information from JSON - char *sha = extract_json_string(response, "sha"); - char *message_raw = extract_json_string(response, "message"); - char *message = NULL; - if (message_raw) { - message = unescape_json_string(message_raw); - free(message_raw); - } - - char *author_name = NULL; - char *author_email = NULL; - char *date = NULL; - - // Find author object - char *author_start = strstr(response, "\"author\":"); - if (author_start) { - author_name = extract_json_string(author_start, "name"); - author_email = extract_json_string(author_start, "email"); - date = extract_json_string(author_start, "date"); - } - - // Format the date if we have it - char formatted_date[64] = "Unknown date"; - if (date) { - // Parse the ISO date (e.g., "2024-01-20T15:30:45Z") - struct tm tm_info = {0}; - if (sscanf(date, "%d-%d-%dT%d:%d:%d", &tm_info.tm_year, &tm_info.tm_mon, - &tm_info.tm_mday, &tm_info.tm_hour, &tm_info.tm_min, - &tm_info.tm_sec) == 6) { - tm_info.tm_year -= 1900; // tm_year is years since 1900 - tm_info.tm_mon -= 1; // tm_mon is 0-based - - time_t commit_time = mktime(&tm_info); - time_t now = time(NULL); - double diff_seconds = difftime(now, commit_time); - - // Format as relative time - if (diff_seconds < 60) { - snprintf(formatted_date, sizeof(formatted_date), "just now"); - } else if (diff_seconds < 3600) { - int minutes = (int)(diff_seconds / 60); - snprintf(formatted_date, sizeof(formatted_date), "%d minute%s ago", - minutes, minutes == 1 ? "" : "s"); - } else if (diff_seconds < 86400) { - int hours = (int)(diff_seconds / 3600); - snprintf(formatted_date, sizeof(formatted_date), "%d hour%s ago", hours, - hours == 1 ? "" : "s"); - } else if (diff_seconds < 604800) { - int days = (int)(diff_seconds / 86400); - snprintf(formatted_date, sizeof(formatted_date), "%d day%s ago", days, - days == 1 ? "" : "s"); - } else { - // Format as absolute date - strftime(formatted_date, sizeof(formatted_date), "%Y-%m-%d %H:%M", - &tm_info); - } - } - } - - // Display the information - printf(ANSI_COLOR_CYAN "Latest Update - shelltestLinux\n" ANSI_COLOR_RESET); - printf("════════════════════════════════════════════════════════════\n\n"); - - if (sha && message) { - // Shorten SHA to first 7 characters - char short_sha[8] = ""; - strncpy(short_sha, sha, 7); - short_sha[7] = '\0'; - - printf(ANSI_COLOR_GREEN "Commit:" ANSI_COLOR_RESET " %s\n", short_sha); - printf(ANSI_COLOR_GREEN "Date:" ANSI_COLOR_RESET " %s\n", formatted_date); - - if (author_name) { - printf(ANSI_COLOR_GREEN "Author:" ANSI_COLOR_RESET " %s", author_name); - if (author_email) { - printf(" <%s>", author_email); - } - printf("\n"); - } - - printf("\n" ANSI_COLOR_YELLOW "Message:" ANSI_COLOR_RESET "\n"); - - // Split the message into title and description - char *first_newline = strchr(message, '\n'); - if (first_newline) { - // We have a title and description - *first_newline = '\0'; // Temporarily terminate the title - printf(" " ANSI_COLOR_CYAN "%s" ANSI_COLOR_RESET "\n\n", message); - - // Print the description - char *description = first_newline + 1; - // Skip any leading newlines in the description - while (*description == '\n') - description++; - - if (*description) { - printf(" %s\n", description); - } - } else { - // Just a title, no description - printf(" " ANSI_COLOR_CYAN "%s" ANSI_COLOR_RESET "\n", message); - } - - printf("\n────────────────────────────────────────────────────────────\n"); - printf("Repository: " ANSI_COLOR_CYAN - "https://github.com/marcusDenslow/shelltestLinux" ANSI_COLOR_RESET - "\n"); - printf("View commit: " - "https://github.com/marcusDenslow/shelltestLinux/commit/%s\n", - short_sha); - - } else { - fprintf(stderr, "Failed to parse update information. The GitHub API " - "response format might have changed.\n"); - } - - // Free allocated strings - if (sha) - free(sha); - if (message) - free(message); - if (author_name) - free(author_name); - if (author_email) - free(author_email); - if (date) - free(date); - - return 1; -} - -/** - * Built-in command: clip - * Placeholder for clipboard functionality - */ -int lsh_clip(char **args) { - printf("Clipboard functionality not implemented yet\n"); - return 1; -} - -/** - * Built-in command: echo - * Echoes arguments to stdout - */ -int lsh_echo(char **args) { - if (args[1] == NULL) { - printf("\n"); - return 1; - } - - for (int i = 1; args[i] != NULL; i++) { - printf("%s", args[i]); - if (args[i + 1] != NULL) { - printf(" "); - } - } - printf("\n"); - return 1; -} - -/** - * Built-in command: loc - * Counts lines of code - */ -int lsh_loc(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "lsh: expected file or directory argument to \"loc\"\n"); - return 1; - } - - struct stat st; - if (stat(args[1], &st) != 0) { - perror("lsh: loc"); - return 1; - } - - if (S_ISREG(st.st_mode)) { - // Count lines in a single file - FILE *file = fopen(args[1], "r"); - if (!file) { - perror("lsh: loc"); - return 1; - } - - int lines = 0, code_lines = 0, blank_lines = 0, comment_lines = 0; - char line[1024]; - int in_comment_block = 0; - - while (fgets(line, sizeof(line), file)) { - lines++; - char *trimmed = line; - while (*trimmed && isspace(*trimmed)) - trimmed++; - - if (*trimmed == '\0') { - blank_lines++; - } else if (strncmp(trimmed, "//", 2) == 0) { - comment_lines++; - } else if (strncmp(trimmed, "/*", 2) == 0) { - comment_lines++; - in_comment_block = 1; - if (strstr(trimmed, "*/")) { - in_comment_block = 0; - } - } else if (in_comment_block) { - comment_lines++; - if (strstr(trimmed, "*/")) { - in_comment_block = 0; - } - } else { - code_lines++; - } - } - - fclose(file); - - printf("File: %s\n", args[1]); - printf("Total lines: %d\n", lines); - printf("Code lines: %d\n", code_lines); - printf("Comment lines: %d\n", comment_lines); - printf("Blank lines: %d\n", blank_lines); - - } else if (S_ISDIR(st.st_mode)) { - printf("Directory LOC counting not implemented yet\n"); - } else { - fprintf(stderr, "lsh: %s is not a file or directory\n", args[1]); - } - - return 1; -} - -/** - * Helper function to extract a string value from a JSON response - */ -char *extract_json_string(const char *json, const char *key) { - char search_key[100]; - sprintf(search_key, "\"%s\":", key); - - char *key_pos = strstr(json, search_key); - if (!key_pos) - return NULL; - - key_pos += strlen(search_key); - - // Skip whitespace - while (*key_pos && isspace(*key_pos)) - key_pos++; - - if (*key_pos != '"') - return NULL; - - key_pos++; // Skip opening quote - - // Find closing quote - char *end = key_pos; - while (*end && *end != '"') { - if (*end == '\\' && *(end + 1)) { - end += 2; // Skip escaped character - } else { - end++; - } - } - - if (!*end) - return NULL; - - // Allocate and copy the string value - int len = end - key_pos; - char *result = malloc(len + 1); - if (!result) - return NULL; - - strncpy(result, key_pos, len); - result[len] = '\0'; - - return result; -} - -/** - * Built-in command: git_status - * Displays the Git status of the current repo - */ -int lsh_git_status(char **args) { - // Show current Git status - char *git_status = get_git_status(); - if (git_status) { - printf("Git Status: %s\n", git_status); - free(git_status); - } else { - printf("Not in a Git repository or Git not available\n"); - } - return 1; -} - -/** - * Built-in command: gg - * Quick access to Git commands - */ -int lsh_gg(char **args) { - if (args[1] == NULL) { - printf("Usage: gg \n"); - printf("Available commands:\n"); - printf(" s - status\n"); - printf(" c - commit\n"); - printf(" p - pull\n"); - printf(" ps - push\n"); - printf(" a - add .\n"); - printf(" l - log\n"); - printf(" d - diff\n"); - printf(" dd - ncurses diff viewer\n"); - printf(" b - branch\n"); - printf(" ch - checkout\n"); - printf(" o - open in GitHub browser\n"); - return 1; - } - - // Execute the appropriate git command based on shorthand - if (strcmp(args[1], "s") == 0) { - // Enhanced git status with more information - char branch_name[100] = ""; - char repo_name[100] = ""; - char last_commit_title[256] = ""; - char last_commit_hash[32] = ""; - char recent_commits[2][256]; - char repo_url[512] = ""; - int is_dirty = 0; - - // Check if we're in a git repo - if (!get_git_branch(branch_name, sizeof(branch_name), &is_dirty)) { - printf("Not in a Git repository\n"); - return 1; - } - - // Get all the information - get_git_repo_name(repo_name, sizeof(repo_name)); - get_last_commit(last_commit_title, sizeof(last_commit_title), - last_commit_hash, sizeof(last_commit_hash)); - int commit_count = get_recent_commit(recent_commits, 2); - get_repo_url(repo_url, sizeof(repo_url)); - - // Display enhanced status - printf("\n" ANSI_COLOR_CYAN "Git Repository Status" ANSI_COLOR_RESET "\n"); - printf("══════════════════════════════════════════════════\n\n"); - - // Repository info - printf(ANSI_COLOR_GREEN "Repo:" ANSI_COLOR_RESET " %s\n", - strlen(repo_name) > 0 ? repo_name : "Unknown"); - - // Branch info with clickable link - if (strlen(repo_url) > 0) { - printf(ANSI_COLOR_GREEN "Branch:" ANSI_COLOR_RESET " \033]8;;%s/tree/%s\033\\%s\033]8;;\033\\%s\n", - repo_url, branch_name, branch_name, is_dirty ? " *" : ""); - } else { - printf(ANSI_COLOR_GREEN "Branch:" ANSI_COLOR_RESET " %s%s\n", - branch_name, is_dirty ? " *" : ""); - } - - // Last commit info - if (strlen(last_commit_hash) > 0 && strlen(last_commit_title) > 0) { - if (strlen(repo_url) > 0) { - printf(ANSI_COLOR_GREEN "Last commit:" ANSI_COLOR_RESET " \033]8;;%s/commit/%s\033\\%s\033]8;;\033\\ - %s\n", - repo_url, last_commit_hash, last_commit_hash, last_commit_title); - } else { - printf(ANSI_COLOR_GREEN "Last commit:" ANSI_COLOR_RESET " %s - %s\n", - last_commit_hash, last_commit_title); - } - } - - // Recent commits - if (commit_count > 0) { - printf(ANSI_COLOR_GREEN "Last %d commits:" ANSI_COLOR_RESET "\n", commit_count); - for (int i = 0; i < commit_count; i++) { - printf(" %d. %s\n", i + 1, recent_commits[i]); - } - } - - // Repository link - if (strlen(repo_url) > 0) { - printf(ANSI_COLOR_GREEN "Repository:" ANSI_COLOR_RESET " \033]8;;%s\033\\%s\033]8;;\033\\\n", - repo_url, repo_url); - } - - printf("\n"); - - // Show regular git status output - printf(ANSI_COLOR_YELLOW "Working Directory Status:" ANSI_COLOR_RESET "\n"); - system("git status --short"); - - if (!is_dirty) { - printf(ANSI_COLOR_GREEN "Working tree clean" ANSI_COLOR_RESET "\n"); - } - - printf("\n"); - } else if (strcmp(args[1], "b") == 0) { - system("git branch"); - } else if (strcmp(args[1], "o") == 0) { - // Get the remote URL - FILE *fp; - char remote_url[1024] = {0}; - fp = popen("git config --get remote.origin.url 2>/dev/null", "r"); - if (fp) { - fgets(remote_url, sizeof(remote_url), fp); - pclose(fp); - - // Trim trailing newline - char *newline = strchr(remote_url, '\n'); - if (newline) - *newline = '\0'; - - if (strlen(remote_url) > 0) { - // Convert SSH URLs to HTTPS - char https_url[1024] = {0}; - if (strstr(remote_url, "git@github.com:")) { - // Convert ssh format (git@github.com:user/repo.git) to https - char *repo_path = strchr(remote_url, ':'); - if (repo_path) { - repo_path++; // Skip the colon - // Remove .git suffix if present - char *git_suffix = strstr(repo_path, ".git"); - if (git_suffix) - *git_suffix = '\0'; - - sprintf(https_url, "https://github.com/%s", repo_path); - } - } else if (strstr(remote_url, "https://github.com/")) { - // Already HTTPS format - strcpy(https_url, remote_url); - // Remove .git suffix if present - char *git_suffix = strstr(https_url, ".git"); - if (git_suffix) - *git_suffix = '\0'; - } - - if (strlen(https_url) > 0) { - // Open the URL in the default browser - char command[1100]; - // Use xdg-open on Linux - sprintf(command, "xdg-open %s >/dev/null 2>&1", https_url); - int result = system(command); - if (result == 0) { - printf("Opening %s in browser\n", https_url); - } else { - printf("Failed to open browser. URL: %s\n", https_url); - } - } else { - printf("Could not parse GitHub URL from: %s\n", remote_url); - } - } else { - printf("No remote URL found. Is this a Git repository with a GitHub " - "remote?\n"); - } - } else { - printf("Not in a Git repository or no remote configured\n"); - } - } else if (strcmp(args[1], "c") == 0) { - if (args[2] != NULL) { - char command[1024]; - sprintf(command, "git commit -m \"%s\"", args[2]); - system(command); - } else { - system("git commit"); - } - } else if (strcmp(args[1], "p") == 0) { - system("git pull"); - } else if (strcmp(args[1], "ps") == 0) { - // Try push without credentials first - force git to fail without prompting - int result = system("GIT_ASKPASS=/bin/false GIT_TERMINAL_PROMPT=0 git push /dev/null"); - - // If push failed, try with credentials - if (result != 0) { - char username[256] = ""; - char token[256] = ""; - - printf("Push failed. Authentication required.\n"); - printf("GitHub Username: "); - fflush(stdout); - - if (fgets(username, sizeof(username), stdin)) { - // Remove newline - username[strcspn(username, "\n")] = 0; - - printf("Personal Access Token: "); - fflush(stdout); - - // Hide token input - system("stty -echo"); - if (fgets(token, sizeof(token), stdin)) { - token[strcspn(token, "\n")] = 0; - system("stty echo"); - printf("\n"); - - printf("Attempting authenticated push...\n"); - result = execute_git_with_auth("git push", username, token); - - // Clear credentials from memory for security - memset(username, 0, sizeof(username)); - memset(token, 0, sizeof(token)); - - if (result == 0) { - printf("Push successful!\n"); - } else { - printf("Push failed. Please check your credentials and try again.\n"); - } - } else { - system("stty echo"); - printf("\nAuthentication cancelled.\n"); - } - } else { - printf("Authentication cancelled.\n"); - } - } else { - printf("Push successful!\n"); - } - } else if (strcmp(args[1], "a") == 0) { - system("git add ."); - } else if (strcmp(args[1], "l") == 0) { - system("git log --oneline -10"); - } else if (strcmp(args[1], "d") == 0) { - run_diff_viewer(); - } else if (strcmp(args[1], "dd") == 0) { - run_ncurses_diff_viewer(); - } else if (strcmp(args[1], "b") == 0) { - system("git branch"); - } else if (strcmp(args[1], "ch") == 0) { - if (args[2] != NULL) { - char command[1024]; - sprintf(command, "git checkout %s", args[2]); - system(command); - } else { - printf("Please specify a branch to checkout\n"); - } - } else if (strcmp(args[1], "debug") == 0) { - printf("=== Git Debug Log ===\n"); - if (access("/tmp/git_debug.log", F_OK) == 0) { - system("cat /tmp/git_debug.log"); - } else { - printf("No debug log found. Try pushing to generate debug info.\n"); - } - printf("\n=== End Debug Log ===\n"); - } else if (strcmp(args[1], "debug-clear") == 0) { - system("rm -f /tmp/git_debug.log"); - printf("Debug log cleared.\n"); - } else { - printf("Unknown git command shorthand: %s\n", args[1]); - } - - return 1; -} - -int lsh_stats(char **args) { - printf("Command Statistics\n"); - printf("=================\n\n"); - - if (frequency_count == 0) { - printf("No command history available\n"); - return 1; - } - - typedef struct { - char *command; - int count; - double score; - } CommandStat; - - CommandStat *stats = malloc(frequency_count * sizeof(CommandStat)); - if (!stats) - return 1; - - time_t current_time = time(NULL); - for (int i = 0; i < frequency_count; i++) { - stats[i].command = command_frequencies[i].command; - stats[i].count = command_frequencies[i].count; - - time_t most_recent = 0; - for (int j = history_size - 1; j >= 0; j--) { - if (strcmp(history_entries[j].command, stats[i].command) == 0) { - most_recent = history_entries[j].timestamp; - break; - } - } - - double hours_ago = (current_time - most_recent) / 3600.0; - double recency_weight = hours_ago > 0 ? 1.0 / (1.0 + hours_ago * 0.1) : 1.0; - - stats[i].score = stats[i].count * recency_weight; - - } - - for (int i = 0; i < frequency_count - 1; i++) { - for (int j = i + 1; j < frequency_count; j++) { - if (stats[j].score > stats[i].score) { - CommandStat temp = stats[i]; - stats[i] = stats[j]; - stats[j] = temp; - } - } - } - - printf("Top Commands (by frequency + recency):\n"); - printf("Rank Command Count Score\n"); - printf("---- -------------------- ----- -----\n"); - - int display_count = frequency_count < 10 ? frequency_count : 10; - for (int i = 0; i < display_count; i++) { - printf("%-4d %-20s %-5d %.2f\n", i + 1, stats[i].command, - stats[i].count, stats[i].score); - } - - printf("\nNext Command Prediction: \n"); - if (frequency_count > 0) { - printf("Most likely: %s (score: %.2f)\n", stats[0].command, stats[0].score); - } - free(stats); - return 1; -} diff --git a/src/aliases.c b/src/data/aliases.c similarity index 100% rename from src/aliases.c rename to src/data/aliases.c diff --git a/src/bookmarks.c b/src/data/bookmarks.c similarity index 100% rename from src/bookmarks.c rename to src/data/bookmarks.c diff --git a/src/favorite_cities.c b/src/data/favorite_cities.c similarity index 100% rename from src/favorite_cities.c rename to src/data/favorite_cities.c diff --git a/src/structured_data.c b/src/data/structured_data.c similarity index 100% rename from src/structured_data.c rename to src/data/structured_data.c diff --git a/src/git_integration.c b/src/git/git_integration.c similarity index 100% rename from src/git_integration.c rename to src/git/git_integration.c diff --git a/src/persistent_history.c b/src/history/persistent_history.c similarity index 100% rename from src/persistent_history.c rename to src/history/persistent_history.c diff --git a/src/autocorrect.c b/src/input/autocorrect.c similarity index 100% rename from src/autocorrect.c rename to src/input/autocorrect.c diff --git a/src/line_reader.c b/src/input/line_reader.c similarity index 100% rename from src/line_reader.c rename to src/input/line_reader.c diff --git a/src/tab_complete.c b/src/input/tab_complete.c similarity index 100% rename from src/tab_complete.c rename to src/input/tab_complete.c diff --git a/src/main.c b/src/main.c deleted file mode 100644 index efc6a5c..0000000 --- a/src/main.c +++ /dev/null @@ -1,26 +0,0 @@ -/** - * main.c - * Entry point for the LSH shell program - */ - -#include "common.h" -#include "shell.h" -#include - -/** - * Main entry point - */ - -int main(int argc, char **argv) { - // Set locale to support UTF-8 - setlocale(LC_ALL, "en_US.UTF-8"); - - // Setup terminal for proper UTF-8 handling - // This is Linux equivalent of SetConsoleOutputCP(65001) - printf("\033%%G"); // Tell terminal to use UTF-8 - - // Start the shell loop - lsh_loop(); - - return EXIT_SUCCESS; -} diff --git a/src/filters.c b/src/search/filters.c similarity index 100% rename from src/filters.c rename to src/search/filters.c diff --git a/src/fzf_native.c b/src/search/fzf_native.c similarity index 100% rename from src/fzf_native.c rename to src/search/fzf_native.c diff --git a/src/grep.c b/src/search/grep.c similarity index 100% rename from src/grep.c rename to src/search/grep.c diff --git a/src/ripgrep.c b/src/search/ripgrep.c similarity index 100% rename from src/ripgrep.c rename to src/search/ripgrep.c diff --git a/src/shell.c b/src/shell.c deleted file mode 100644 index 6fd93d3..0000000 --- a/src/shell.c +++ /dev/null @@ -1,1068 +0,0 @@ -/** - * shell.c - * Implementation of core shell functionality - */ - -#include "shell.h" -#include "aliases.h" // Added for alias support -#include "autocorrect.h" -#include "bookmarks.h" // Added for bookmark support -#include "builtins.h" -#include "countdown_timer.h" -#include "favorite_cities.h" -#include "filters.h" -#include "git_integration.h" // Added for Git repository detection -#include "line_reader.h" -#include "persistent_history.h" -#include "structured_data.h" -#include "tab_complete.h" // Added for tab completion support -#include "themes.h" -#include -#include // Added for time functions -#include -#include -#include -#include // For DIR, opendir, readdir -#include // For stat - -// Global variables for status bar -static int g_console_width = 80; -static int g_status_line = 0; -static int g_normal_attributes = 7; // Default white on black -static int g_status_attributes = 12; // Red -static int g_status_bar_enabled = 0; // Flag to track if status bar is enabled -static struct termios g_orig_termios; // Original terminal settings - -/** - * Initialize terminal for raw mode - */ -int init_terminal(struct termios *orig_termios) { - int fd = STDIN_FILENO; - - if (!isatty(fd)) { - fprintf(stderr, "Not running in a terminal\n"); - return -1; - } - - // Get current terminal settings - if (tcgetattr(fd, orig_termios) == -1) { - perror("tcgetattr"); - return -1; - } - - // Create raw terminal settings (make a copy first) - struct termios raw = *orig_termios; - - // Disable canonical mode and echo, but preserve some control characters - raw.c_iflag &= ~(ICRNL | IXON); // Disable CTRL-S and CTRL-Q and CR to NL translation - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); // Disable echo, canonical mode, and signal keys - raw.c_cc[VMIN] = 1; // Wait for at least one character - raw.c_cc[VTIME] = 0; // No timeout - - // Apply modified terminal settings - if (tcsetattr(fd, TCSAFLUSH, &raw) == -1) { - perror("tcsetattr"); - return -1; - } - - printf("\033[?25h"); // Ensure cursor is visible - - return fd; -} - -/** - * Restore terminal to original settings - */ -void restore_terminal(int fd, struct termios *orig_termios) { - // Restore cursor and terminal settings - printf("\033[?25h"); // Ensure cursor is visible - printf("\033c"); // Reset terminal state - - if (tcsetattr(fd, TCSAFLUSH, orig_termios) == -1) { - perror("tcsetattr"); - } -} - -/** - * Get console dimensions - */ -int get_console_dimensions(int fd, int *width, int *height) { - struct winsize ws; - - if (ioctl(fd, TIOCGWINSZ, &ws) == -1) { - perror("ioctl"); - return 0; - } - - *width = ws.ws_col; - *height = ws.ws_row; - - return 1; -} - -/** - * Temporarily hide the status bar before command execution - */ -void hide_status_bar(int fd) { - // Skip if status bar is not enabled - if (!g_status_bar_enabled) - return; - - int width, height; - if (!get_console_dimensions(fd, &width, &height)) { - return; - } - - // Save cursor position - printf(ANSI_SAVE_CURSOR); - - // Move to status bar line and clear it - printf("\033[%d;1H", height); - printf("\033[2K"); // Clear entire line - - // Restore cursor position - printf(ANSI_RESTORE_CURSOR); - fflush(stdout); -} - -/** - * This function scrolls the console buffer up one line to make room for the - * status bar when we're at the bottom of the screen. - */ -void ensure_status_bar_space(int fd) { - int width, height; - if (!get_console_dimensions(fd, &width, &height)) { - return; - } - - // First clear the status bar if it exists - printf(ANSI_SAVE_CURSOR); - - // Move to status bar line and clear it - printf("\033[%d;1H", height); - printf("\033[2K"); // Clear entire line - - // Add a blank line above status bar for spacing - printf("\033[%d;1H", height - 1); - printf("\033[2K"); // Clear entire line - - // Restore cursor position - printf(ANSI_RESTORE_CURSOR); - fflush(stdout); -} - -/** - * Initialize the status bar at the bottom of the screen - */ -int init_status_bar(int fd) { - int width, height; - if (!get_console_dimensions(fd, &width, &height)) { - return 0; - } - - g_console_width = width; - g_status_line = height; - g_status_bar_enabled = 1; - - // Clear status line - printf(ANSI_SAVE_CURSOR); - printf("\033[%d;1H", height); - printf("\033[2K"); // Clear entire line - printf(ANSI_RESTORE_CURSOR); - fflush(stdout); - - return 1; -} - -/** - * Check for console window resize and update status bar position - */ -void check_console_resize(int fd) { - if (!g_status_bar_enabled) - return; - - int width, height; - if (!get_console_dimensions(fd, &width, &height)) { - return; - } - - // Check if dimensions have changed - if (width != g_console_width || height != g_status_line) { - g_console_width = width; - g_status_line = height; - - // Redraw status bar at new position - hide_status_bar(fd); - } -} - -/** - * Update the status bar with Git information - */ -void update_status_bar(int fd, const char *git_info) { - if (!g_status_bar_enabled) - return; - - int width, height; - if (!get_console_dimensions(fd, &width, &height)) { - return; - } - - g_console_width = width; - g_status_line = height; - - // Get current time for status bar - time_t rawtime; - struct tm *timeinfo; - char time_buffer[10]; - - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(time_buffer, sizeof(time_buffer), "%H:%M:%S", timeinfo); - - // Get current directory for display - char cwd[LSH_RL_BUFSIZE]; - char parent_dir[LSH_RL_BUFSIZE / 2]; - char current_dir[LSH_RL_BUFSIZE / 2]; - - if (getcwd(cwd, sizeof(cwd)) != NULL) { - get_path_display(cwd, parent_dir, current_dir, LSH_RL_BUFSIZE / 2); - } else { - strcpy(parent_dir, "unknown"); - strcpy(current_dir, "dir"); - } - - printf(ANSI_SAVE_CURSOR); - - // Move cursor to status line - printf("\033[%d;1H", height); - - // Clear the status line - printf("\033[2K"); - - // Set status bar color (cyan background) - printf(ANSI_BG_CYAN ANSI_COLOR_BLACK); - - // Format: [time] parent_dir/current_dir [git_info] - printf(" %s ", time_buffer); - printf(" %s/%s ", parent_dir, current_dir); - - if (git_info != NULL && strlen(git_info) > 0) { - printf(" %s ", git_info); - } - - // Fill the rest of line with space to color the entire bar - printf("%*s", width - (int)strlen(time_buffer) - - (int)strlen(parent_dir) - (int)strlen(current_dir) - - (git_info ? (int)strlen(git_info) + 3 : 0) - 8, ""); - - // Reset color - printf(ANSI_COLOR_RESET); - - // Restore cursor position - printf(ANSI_RESTORE_CURSOR); - fflush(stdout); -} - -/** - * Get the name of the parent and current directories from a path - */ - -void get_path_display(const char *cwd, char *parent_dir_name, - char *current_dir_name, size_t buf_size) { - char path_copy[PATH_MAX]; - strncpy(path_copy, cwd, PATH_MAX - 1); - path_copy[PATH_MAX - 1] = '\0'; - - // Default values if we can't parse the path - strncpy(parent_dir_name, ".", buf_size - 1); - strncpy(current_dir_name, "unknown", buf_size - 1); - parent_dir_name[buf_size - 1] = '\0'; - current_dir_name[buf_size - 1] = '\0'; - - // Handle root directory special case - if (strcmp(path_copy, "/") == 0) { - strncpy(parent_dir_name, "/", buf_size - 1); - strncpy(current_dir_name, "", buf_size - 1); - return; - } - - // Remove trailing slash if present - size_t len = strlen(path_copy); - if (len > 1 && path_copy[len - 1] == '/') { - path_copy[len - 1] = '\0'; - } - - // Find the last component (current directory) - char *last_slash = strrchr(path_copy, '/'); - if (!last_slash) { - // No slash found, must be a relative path with no slashes - strncpy(current_dir_name, path_copy, buf_size - 1); - return; - } - - // Extract current directory name - strncpy(current_dir_name, last_slash + 1, buf_size - 1); - - // Handle paths directly under root - if (last_slash == path_copy) { - strncpy(parent_dir_name, "/", buf_size - 1); - return; - } - - // Temporarily terminate the string at the last slash - *last_slash = '\0'; - - // Find the previous slash to identify parent directory - char *prev_slash = strrchr(path_copy, '/'); - - if (prev_slash) { - // Extract just the parent directory name - strncpy(parent_dir_name, prev_slash + 1, buf_size - 1); - - // Special case: parent is root - if (prev_slash == path_copy) { - strncpy(parent_dir_name, "/", buf_size - 1); - } - } else { - // No previous slash, parent is the remaining part - strncpy(parent_dir_name, path_copy, buf_size - 1); - } -} - -/** - * Create a TableData structure from an ls command - */ -TableData* create_ls_table(char **args) { - DIR *dir; - struct dirent *entry; - struct stat file_stat; - char cwd[PATH_MAX]; - char file_path[PATH_MAX]; - - // Create table headers - char *headers[] = {"Name", "Size", "Type", "Modified"}; - TableData *table = create_table(headers, 4); - if (!table) { - fprintf(stderr, "lsh: failed to create table\n"); - return NULL; - } - - if (getcwd(cwd, sizeof(cwd)) == NULL) { - perror("lsh: getcwd"); - free_table(table); - return NULL; - } - - dir = opendir(cwd); - if (dir == NULL) { - perror("lsh: opendir"); - free_table(table); - return NULL; - } - - // Collect all entries - while ((entry = readdir(dir)) != NULL) { - // Skip . and .. entries - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { - continue; - } - - snprintf(file_path, sizeof(file_path), "%s/%s", cwd, entry->d_name); - if (stat(file_path, &file_stat) == 0) { - // Create a new row - DataValue *row = (DataValue*)malloc(4 * sizeof(DataValue)); - if (!row) { - fprintf(stderr, "lsh: allocation error\n"); - continue; - } - - // Name column - row[0].type = TYPE_STRING; - row[0].value.str_val = strdup(entry->d_name); - row[0].is_highlighted = S_ISDIR(file_stat.st_mode); // Highlight directories - - // Size column - row[1].type = TYPE_SIZE; - row[1].value.str_val = malloc(32); - if (S_ISDIR(file_stat.st_mode)) { - strcpy(row[1].value.str_val, ""); - } else if (file_stat.st_size < 1024) { - snprintf(row[1].value.str_val, 32, "%d B", (int)file_stat.st_size); - } else if (file_stat.st_size < 1024 * 1024) { - snprintf(row[1].value.str_val, 32, "%.1f KB", file_stat.st_size / 1024.0); - } else { - snprintf(row[1].value.str_val, 32, "%.1f MB", file_stat.st_size / (1024.0 * 1024.0)); - } - row[1].is_highlighted = 0; - - // Type column - row[2].type = TYPE_STRING; - if (S_ISDIR(file_stat.st_mode)) { - row[2].value.str_val = strdup("Directory"); - } else if (S_ISREG(file_stat.st_mode)) { - // Try to determine file type by extension - char *ext = strrchr(entry->d_name, '.'); - if (ext != NULL) { - ext++; // Skip the dot - if (strcasecmp(ext, "c") == 0 || strcasecmp(ext, "cpp") == 0 || - strcasecmp(ext, "h") == 0 || strcasecmp(ext, "hpp") == 0) { - row[2].value.str_val = strdup("Source"); - } else if (strcasecmp(ext, "exe") == 0 || strcasecmp(ext, "bat") == 0 || - strcasecmp(ext, "sh") == 0 || strcasecmp(ext, "com") == 0) { - row[2].value.str_val = strdup("Executable"); - } else if (strcasecmp(ext, "txt") == 0 || strcasecmp(ext, "md") == 0 || - strcasecmp(ext, "log") == 0) { - row[2].value.str_val = strdup("Text"); - } else if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "png") == 0 || - strcasecmp(ext, "gif") == 0 || strcasecmp(ext, "bmp") == 0) { - row[2].value.str_val = strdup("Image"); - } else { - row[2].value.str_val = strdup("File"); - } - } else { - row[2].value.str_val = strdup("File"); - } - } else { - row[2].value.str_val = strdup("Special"); - } - row[2].is_highlighted = 0; - - // Modified date column - row[3].type = TYPE_STRING; - row[3].value.str_val = malloc(32); - struct tm *tm_info = localtime(&file_stat.st_mtime); - strftime(row[3].value.str_val, 32, "%Y-%m-%d %H:%M", tm_info); - row[3].is_highlighted = 0; - - // Add row to table - add_table_row(table, row); - } - } - - closedir(dir); - return table; -} - -/** - * Parse input line into an array of commands (for pipelines) - */ -char*** lsh_split_commands(char *line) { - char **cmd_groups = NULL; - char *cmd_group, *saveptr0; - int group_count = 0, group_capacity = 10; - char ***commands = NULL; - char **command = NULL; - char *cmd_str, *token, *saveptr1, *saveptr2; - int cmd_count = 0, cmd_capacity = 10; - int token_count = 0, token_capacity = 10; - - - // Allocate initial command groups array - cmd_groups = malloc(group_capacity * sizeof(char*)); - if (!cmd_groups) { - perror("lsh: allocation error"); - return NULL; - } - - // Split by && symbol - cmd_group = strtok_r(line, "&&", &saveptr0); - while (cmd_group != NULL) { - // Trim whitespace - while (*cmd_group && isspace(*cmd_group)) cmd_group++; - cmd_groups[group_count] = strdup(cmd_group); - group_count++; - - if (group_count >= group_capacity - 1) { - group_capacity *= 2; - cmd_groups = realloc(cmd_groups, group_capacity * sizeof(char*)); - if (!cmd_groups) { - perror("lsh: allocation error"); - return NULL; - } - } - cmd_group = strtok_r(NULL, "&&", &saveptr0); - } - cmd_groups[group_count] = NULL; - - char *current_cmd = cmd_groups[0]; - - // Allocate initial commands array - commands = malloc(cmd_capacity * sizeof(char**)); - if (!commands) { - perror("lsh: allocation error"); - for (int i = 0; i < group_count; i++) { - free(cmd_groups[i]); - } - free(cmd_groups); - return NULL; - } - - // Split by pipe symbol '|' - cmd_str = strtok_r(current_cmd, "|", &saveptr1); - while (cmd_str != NULL) { - // Trim whitespace - while (*cmd_str && isspace(*cmd_str)) cmd_str++; - - // Allocate command token array - command = malloc(token_capacity * sizeof(char*)); - if (!command) { - perror("lsh: allocation error"); - // Free previous allocations - for (int i = 0; i < cmd_count; i++) { - for (int j = 0; commands[i][j] != NULL; j++) { - free(commands[i][j]); - } - free(commands[i]); - } - free(commands); - for (int i = 0; i < group_count; i++) { - free(cmd_groups[i]); - } - free(cmd_groups); - return NULL; - } - - // Split command into tokens - token_count = 0; - token = strtok_r(cmd_str, " \t\r\n\a", &saveptr2); - while (token != NULL) { - // Check if we need to resize tokens array - if (token_count >= token_capacity - 1) { - token_capacity *= 2; - command = realloc(command, token_capacity * sizeof(char*)); - if (!command) { - perror("lsh: allocation error"); - // Free previous allocations - for (int i = 0; i < cmd_count; i++) { - for (int j = 0; commands[i][j] != NULL; j++) { - free(commands[i][j]); - } - free(commands[i]); - } - free(commands); - return NULL; - } - } - - command[token_count] = strdup(token); - token_count++; - token = strtok_r(NULL, " \t\r\n\a", &saveptr2); - } - command[token_count] = NULL; // Null-terminate the tokens array - - // Check if we need to resize commands array - if (cmd_count >= cmd_capacity - 1) { - cmd_capacity *= 2; - commands = realloc(commands, cmd_capacity * sizeof(char**)); - if (!commands) { - perror("lsh: allocation error"); - return NULL; - } - } - - commands[cmd_count] = command; - cmd_count++; - cmd_str = strtok_r(NULL, "|", &saveptr1); - } - - commands[cmd_count] = NULL; // Null-terminate the commands array - - // Store the remaining command groups in the last position (if any) - if (group_count > 1) { - // Create a special command array for the command groups marker - char **marker = malloc(sizeof(char*) * 2); - if (!marker) { - perror("lsh: allocation error"); - for (int i = 0; i < group_count; i++) { - free(cmd_groups[i]); - } - free(cmd_groups); - return NULL; - } - marker[0] = strdup("&&_COMMAND_GROUPS"); - marker[1] = NULL; - - // Create a special command array for the remaining groups - char **cmd_group_array = malloc(sizeof(char*) * group_count); - if (!cmd_group_array) { - perror("lsh: allocation error"); - free(marker[0]); - free(marker); - for (int i = 0; i < group_count; i++) { - free(cmd_groups[i]); - } - free(cmd_groups); - return NULL; - } - - // Copy all remaining command groups - for (int i = 1; i < group_count; i++) { - cmd_group_array[i-1] = strdup(cmd_groups[i]); - } - cmd_group_array[group_count-1] = NULL; - - // Allocate space for cmd_count + 2 commands - // (original commands + cmd_group_array + marker) - char ***new_commands = malloc((cmd_count + 3) * sizeof(char**)); - if (!new_commands) { - perror("lsh: allocation error"); - for (int i = 0; cmd_group_array[i] != NULL; i++) { - free(cmd_group_array[i]); - } - free(cmd_group_array); - free(marker[0]); - free(marker); - for (int i = 0; i < group_count; i++) { - free(cmd_groups[i]); - } - free(cmd_groups); - return NULL; - } - - // Copy original commands - for (int i = 0; i < cmd_count; i++) { - new_commands[i] = commands[i]; - } - - // Add the command groups and marker - new_commands[cmd_count] = cmd_group_array; - new_commands[cmd_count+1] = marker; - new_commands[cmd_count+2] = NULL; - - // Free the old commands array but not its contents - free(commands); - commands = new_commands; - } - - // Free the command groups array - for (int i = 0; i < group_count; i++) { - free(cmd_groups[i]); - } - free(cmd_groups); - - return commands; -} - -/** - * Launch an external program - */ -int lsh_launch(char **args) { - pid_t pid, wpid; - int status; - - // Fork a child process - pid = fork(); - - if (pid == 0) { - // Child process - if (execvp(args[0], args) == -1) { - perror("lsh"); - } - exit(EXIT_FAILURE); - } else if (pid < 0) { - // Error forking - perror("lsh"); - } else { - // Parent process - do { - wpid = waitpid(pid, &status, WUNTRACED); - } while (!WIFEXITED(status) && !WIFSIGNALED(status)); - } - - return 1; -} - -/** - * Execute a command - */ -int lsh_execute(char **args) { - if (args[0] == NULL) { - // An empty command was entered - return 1; - } - - // Check if it's a built-in command - for (int i = 0; i < lsh_num_builtins(); i++) { - if (strcmp(args[0], builtin_str[i]) == 0) { - return (*builtin_func[i])(args); - } - } - - // If not a built-in, check aliases and launch external - char **alias_expansion = expand_alias(args); - if (alias_expansion != NULL) { - int result = lsh_execute(alias_expansion); - // Free the memory allocated for alias expansion - for (int i = 0; alias_expansion[i] != NULL; i++) { - free(alias_expansion[i]); - } - free(alias_expansion); - return result; - } - - return lsh_launch(args); -} - -/** - * Execute a pipeline of commands - */ -int lsh_execute_piped(char ***commands) { - // Count the number of commands in the pipeline - int cmd_count = 0; - while (commands[cmd_count] != NULL) { - cmd_count++; - } - - if (cmd_count == 0) { - return 1; // Nothing to execute - } - - // Check if this is a table operation starting with ls/dir - if (strcmp(commands[0][0], "ls") == 0 || strcmp(commands[0][0], "dir") == 0) { - // Create a table from ls command - TableData *table = create_ls_table(commands[0]); - if (!table) { - return 1; // Error already printed - } - - // Apply filters from the pipeline - for (int i = 1; commands[i] != NULL; i++) { - // Find the filter command - char *filter_cmd = commands[i][0]; - int filter_idx = -1; - - for (int j = 0; j < filter_count; j++) { - if (strcmp(filter_cmd, filter_str[j]) == 0) { - filter_idx = j; - break; - } - } - - if (filter_idx == -1) { - fprintf(stderr, "lsh: unknown filter command: %s\n", filter_cmd); - free_table(table); - return 1; - } - - // Apply the filter - TableData *filtered_table = filter_func[filter_idx](table, &commands[i][1]); - free_table(table); // Free the old table - - if (!filtered_table) { - return 1; // Error already printed - } - - table = filtered_table; - } - - // Print the final table - print_table(table); - free_table(table); - - return 1; - } - - if (cmd_count == 1) { - // No piping needed - return lsh_execute(commands[0]); - } - - // Create pipes - int pipes[cmd_count - 1][2]; - for (int i = 0; i < cmd_count - 1; i++) { - if (pipe(pipes[i]) == -1) { - perror("pipe"); - return 1; - } - } - - // Create processes - pid_t pids[cmd_count]; - for (int i = 0; i < cmd_count; i++) { - pids[i] = fork(); - - if (pids[i] == -1) { - perror("fork"); - return 1; - } else if (pids[i] == 0) { - // Child process - - // Set up input from previous pipe - if (i > 0) { - if (dup2(pipes[i - 1][0], STDIN_FILENO) == -1) { - perror("dup2"); - exit(EXIT_FAILURE); - } - } - - // Set up output to next pipe - if (i < cmd_count - 1) { - if (dup2(pipes[i][1], STDOUT_FILENO) == -1) { - perror("dup2"); - exit(EXIT_FAILURE); - } - } - - // Close all pipe fds - for (int j = 0; j < cmd_count - 1; j++) { - close(pipes[j][0]); - close(pipes[j][1]); - } - - // Execute the command - if (execvp(commands[i][0], commands[i]) == -1) { - perror("execvp"); - exit(EXIT_FAILURE); - } - } - } - - // Parent process closes all pipe fds - for (int i = 0; i < cmd_count - 1; i++) { - close(pipes[i][0]); - close(pipes[i][1]); - } - - // Wait for all children - for (int i = 0; i < cmd_count; i++) { - int status; - waitpid(pids[i], &status, 0); - } - - return 1; -} - -/** - * Free memory for a command array from lsh_split_commands - */ -void free_commands(char ***commands) { - for (int i = 0; commands[i] != NULL; i++) { - for (int j = 0; commands[i][j] != NULL; j++) { - free(commands[i][j]); - } - free(commands[i]); - } - free(commands); -} - -/** - * Display a welcome banner - */ -void display_welcome_banner(void) { - printf(ANSI_COLOR_CYAN); - printf("╔════════════════════════════════════════════════════════════╗\n"); - printf("║ Welcome to the LSH Shell (Linux) ║\n"); - printf("║ ║\n"); - printf("║ Type 'help' to see available commands ║\n"); - printf("╚════════════════════════════════════════════════════════════╝\n"); - printf(ANSI_COLOR_RESET); -} - -/** - * Main shell loop - */ -void lsh_loop(void) { - char *line; - char **args; - char ***commands = NULL; - int status = 1; - char git_info[LSH_RL_BUFSIZE] = {0}; - int terminal_fd; - - // Initialize terminal for raw mode - terminal_fd = init_terminal(&g_orig_termios); - - // Initialize the status bar - //init_status_bar(STDOUT_FILENO); - - // Initialize subsystems - init_aliases(); - init_bookmarks(); - init_tab_completion(); - init_persistent_history(); - init_favorite_cities(); - init_themes(); - init_autocorrect(); - init_git_integration(); - - // Display the welcome banner - display_welcome_banner(); - - do { - // Check for console resize - check_console_resize(STDOUT_FILENO); - - // Get Git status for current directory - // If git_status() returns null, git_info[0] will remain 0 - char *git_status_info = get_git_status(); - if (git_status_info != NULL) { - strncpy(git_info, git_status_info, LSH_RL_BUFSIZE - 1); - git_info[LSH_RL_BUFSIZE - 1] = '\0'; - free(git_status_info); - } else { - git_info[0] = '\0'; - } - - // Update status bar with Git information - update_status_bar(STDOUT_FILENO, git_info); - - // Get input from the user - line = lsh_read_line(); - - // Empty line check - if (line == NULL || line[0] == '\0') { - if (line != NULL) { - free(line); - } - continue; - } - - // Add line to persistent history - add_to_history(line); - - // Check for pipes or && and parse into multiple commands if present - if (strchr(line, '|') != NULL || strchr(line, '&') != NULL) { - commands = lsh_split_commands(line); - - // Find if there are command groups (&&) - int has_cmd_groups = 0; - char **remaining_cmd_groups = NULL; - char **marker = NULL; - int cmd_count = 0; - - // Count commands and check for the special command groups marker - while (commands[cmd_count] != NULL) { - cmd_count++; - } - - // We need at least 2 positions for command groups: - // 1. The groups themselves - // 2. The marker with "&&_COMMAND_GROUPS" - if (cmd_count > 1) { - // Get the last non-null command - marker = commands[cmd_count-1]; - - // Check if it's the marker - if (marker != NULL && marker[0] != NULL && - strcmp(marker[0], "&&_COMMAND_GROUPS") == 0) { - - has_cmd_groups = 1; - - // Get the command groups from the previous position - remaining_cmd_groups = commands[cmd_count-2]; - - // Set these positions to NULL so they don't get processed - // as regular commands - commands[cmd_count-1] = NULL; - commands[cmd_count-2] = NULL; - } - } - - // Execute the first command or pipeline - status = lsh_execute_piped(commands); - - // If successful and we have command groups, execute them sequentially - if (status && has_cmd_groups && remaining_cmd_groups != NULL) { - for (int i = 0; remaining_cmd_groups[i] != NULL; i++) { - char *cmd_group = remaining_cmd_groups[i]; - - // Parse this command group - char *cmd_copy = strdup(cmd_group); - - // Check if it contains pipes - if (strchr(cmd_copy, '|') != NULL) { - char ***cmd_commands = lsh_split_commands(cmd_copy); - if (cmd_commands) { - status = lsh_execute_piped(cmd_commands); - free_commands(cmd_commands); - } - } else { - // Simple command without pipes - char **args = lsh_split_line(cmd_copy); - - // Check for corrections before executing - char **corrected_args = check_for_corrections(args); - if (corrected_args != NULL) { - for (int j = 0; args[j] != NULL; j++) { - free(args[j]); - } - free(args); - args = corrected_args; - } - - // Execute command - status = lsh_execute(args); - - // Free allocated memory - for (int j = 0; args[j] != NULL; j++) { - free(args[j]); - } - free(args); - } - - free(cmd_copy); - - // If a command failed, stop execution - if (!status) { - break; - } - } - } - - // Clean up marker and command groups if they exist - if (has_cmd_groups && marker) { - // Free marker - if (marker[0]) free(marker[0]); - free(marker); - - // Free command groups - if (remaining_cmd_groups) { - for (int i = 0; remaining_cmd_groups[i] != NULL; i++) { - free(remaining_cmd_groups[i]); - } - free(remaining_cmd_groups); - } - } - - free_commands(commands); - } else { - // Normal command parsing - args = lsh_split_line(line); - - // Check for corrections before executing - char **corrected_args = check_for_corrections(args); - if (corrected_args != NULL) { - // Free the original args - for (int i = 0; args[i] != NULL; i++) { - free(args[i]); - } - free(args); - args = corrected_args; - } - - // Execute command - status = lsh_execute(args); - - // Free allocated memory - for (int i = 0; args[i] != NULL; i++) { - free(args[i]); - } - free(args); - } - - free(line); - } while (status); - - // Shutdown subsystems - shutdown_aliases(); - shutdown_bookmarks(); - shutdown_tab_completion(); - shutdown_persistent_history(); - shutdown_favorite_cities(); - shutdown_themes(); - shutdown_autocorrect(); - - // Restore terminal - restore_terminal(terminal_fd, &g_orig_termios); -} diff --git a/src/ps_command.c b/src/system/ps_command.c similarity index 100% rename from src/ps_command.c rename to src/system/ps_command.c diff --git a/src/system_monitor.c b/src/system/system_monitor.c similarity index 100% rename from src/system_monitor.c rename to src/system/system_monitor.c diff --git a/src/diff_viewer.c b/src/ui/diff_viewer.c similarity index 100% rename from src/diff_viewer.c rename to src/ui/diff_viewer.c diff --git a/src/ncurses_diff_viewer.c b/src/ui/ncurses_diff_viewer.c similarity index 100% rename from src/ncurses_diff_viewer.c rename to src/ui/ncurses_diff_viewer.c diff --git a/src/themes.c b/src/ui/themes.c similarity index 100% rename from src/themes.c rename to src/ui/themes.c diff --git a/src/countdown_timer.c b/src/utils/countdown_timer.c similarity index 100% rename from src/countdown_timer.c rename to src/utils/countdown_timer.c diff --git a/src/timer.c b/src/utils/timer.c similarity index 100% rename from src/timer.c rename to src/utils/timer.c diff --git a/src/weather.c b/src/utils/weather.c similarity index 100% rename from src/weather.c rename to src/utils/weather.c