/** * aliases.c * Implementation of shell alias functionality */ #include "aliases.h" #include "builtins.h" #include #include // Global variables for alias storage AliasEntry *aliases = NULL; int alias_count = 0; int alias_capacity = 0; // Path to the aliases file char aliases_file_path[PATH_MAX]; /** * Initialize the alias system */ void init_aliases(void) { // Set initial capacity alias_capacity = 10; aliases = (AliasEntry*)malloc(alias_capacity * sizeof(AliasEntry)); if (!aliases) { fprintf(stderr, "lsh: allocation error in init_aliases\n"); return; } // Determine aliases file location - in user's home directory char *home_dir = getenv("HOME"); if (home_dir) { snprintf(aliases_file_path, PATH_MAX, "%s/.lsh_aliases", home_dir); } else { // Fallback to current directory if HOME not available strcpy(aliases_file_path, ".lsh_aliases"); } // Load aliases from file load_aliases(); } /** * Clean up the alias system */ void cleanup_aliases(void) { if (!aliases) return; // Free all alias entries for (int i = 0; i < alias_count; i++) { free(aliases[i].name); free(aliases[i].command); } // Free the array free(aliases); aliases = NULL; alias_count = 0; alias_capacity = 0; } /** * Shutdown the alias system */ void shutdown_aliases(void) { // Save aliases to file save_aliases(); // Clean up cleanup_aliases(); } /** * Load aliases from file */ int load_aliases(void) { FILE *fp = fopen(aliases_file_path, "r"); if (!fp) { return 0; // File doesn't exist or can't be opened } char line[LSH_RL_BUFSIZE]; char name[LSH_RL_BUFSIZE / 2]; char command[LSH_RL_BUFSIZE / 2]; while (fgets(line, sizeof(line), fp)) { // Skip comments and empty lines if (line[0] == '#' || line[0] == '\n') { continue; } // Parse alias definitions (name=command) if (sscanf(line, "%s=%s", name, command) == 2) { add_alias(name, command); } } fclose(fp); return 1; } /** * Save aliases to file */ int save_aliases(void) { FILE *fp = fopen(aliases_file_path, "w"); if (!fp) { fprintf(stderr, "lsh: error saving aliases to %s\n", aliases_file_path); return 0; } // Write file header time_t t = time(NULL); struct tm *tm_info = localtime(&t); char timestamp[26]; strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info); fprintf(fp, "# LSH Aliases - Last updated: %s\n\n", timestamp); // Write each alias for (int i = 0; i < alias_count; i++) { fprintf(fp, "%s=%s\n", aliases[i].name, aliases[i].command); } fclose(fp); return 1; } /** * Add a new alias */ int add_alias(const char *name, const char *command) { if (!name || !command) { return 0; } // Check if alias already exists, update it if it does for (int i = 0; i < alias_count; i++) { if (strcmp(aliases[i].name, name) == 0) { // Update existing alias free(aliases[i].command); aliases[i].command = strdup(command); return 1; } } // Check if we need to expand the array if (alias_count >= alias_capacity) { alias_capacity *= 2; AliasEntry *new_aliases = (AliasEntry*)realloc(aliases, alias_capacity * sizeof(AliasEntry)); if (!new_aliases) { fprintf(stderr, "lsh: allocation error in add_alias\n"); return 0; } aliases = new_aliases; } // Add new alias aliases[alias_count].name = strdup(name); aliases[alias_count].command = strdup(command); alias_count++; return 1; } /** * Remove an alias */ int remove_alias(const char *name) { if (!name) { return 0; } for (int i = 0; i < alias_count; i++) { if (strcmp(aliases[i].name, name) == 0) { // Free memory for this alias free(aliases[i].name); free(aliases[i].command); // Shift remaining aliases down for (int j = i; j < alias_count - 1; j++) { aliases[j] = aliases[j + 1]; } alias_count--; return 1; } } return 0; // Alias not found } /** * Find an alias by name */ AliasEntry* find_alias(const char *name) { for (int i = 0; i < alias_count; i++) { if (strcmp(aliases[i].name, name) == 0) { return &aliases[i]; } } return NULL; } /** * Expand a command line by replacing aliases */ char* expand_aliases(const char *command) { if (!command) { return NULL; } char *result = strdup(command); if (!result) return NULL; // Copy the command for tokenization char *cmd_copy = strdup(result); if (!cmd_copy) { free(result); return NULL; } // Extract the first word (command name) char *token = strtok(cmd_copy, " \t"); if (!token) { free(cmd_copy); return result; } // Look up the alias AliasEntry *alias = find_alias(token); if (alias) { // Get the args after the command char *args = strchr(command, ' '); // Allocate new buffer for expanded command free(result); if (args) { // Concatenate alias command and original args result = malloc(strlen(alias->command) + strlen(args) + 1); if (result) { strcpy(result, alias->command); strcat(result, args); } } else { // No args, just use alias command result = strdup(alias->command); } } free(cmd_copy); return result; } /** * Command handler for the "alias" command */ int lsh_alias(char **args) { // No arguments - list all aliases if (args[1] == NULL) { for (int i = 0; i < alias_count; i++) { printf("alias %s='%s'\n", aliases[i].name, aliases[i].command); } return 1; } // Pre-defined aliases if (strcmp(args[1], "vim-mode") == 0) { // Configure aliases for Vim-like operation add_alias("v", "vim"); // Check for neovim first FILE *test_nvim = popen("which nvim 2>/dev/null", "r"); if (test_nvim) { char buffer[128]; if (fgets(buffer, sizeof(buffer), test_nvim) != NULL) { // Neovim found, use it as preferred editor pclose(test_nvim); add_alias("e", "nvim"); printf("Vim mode activated (using Neovim)\n"); return 1; } pclose(test_nvim); // Check for regular vim FILE *test_vim = popen("which vim 2>/dev/null", "r"); if (test_vim) { char buffer[128]; if (fgets(buffer, sizeof(buffer), test_vim) != NULL) { // Vim found pclose(test_vim); add_alias("e", "vim"); printf("Vim mode activated\n"); return 1; } pclose(test_vim); } } // Fallback to vi if neither vim nor neovim is found add_alias("e", "vi"); printf("Vim mode activated (using vi)\n"); return 1; } else if (strcmp(args[1], "emacs-mode") == 0) { // Configure aliases for Emacs-like operation FILE *test_emacs = popen("which emacs 2>/dev/null", "r"); if (test_emacs) { char buffer[128]; if (fgets(buffer, sizeof(buffer), test_emacs) != NULL) { pclose(test_emacs); add_alias("e", "emacs -nw"); printf("Emacs mode activated\n"); return 1; } pclose(test_emacs); } printf("Emacs not found. Emacs mode not activated.\n"); return 1; } // Format: alias name=command char *equals = strchr(args[1], '='); if (equals) { // Split at equals sign *equals = '\0'; char *name = args[1]; char *command = equals + 1; // Remove quotes around command if present if (*command == '\'' || *command == '"') { command++; char *end = command + strlen(command) - 1; if (*end == '\'' || *end == '"') { *end = '\0'; } } add_alias(name, command); printf("Alias added: %s='%s'\n", name, command); } else if (args[2] != NULL) { // Format: alias name command add_alias(args[1], args[2]); printf("Alias added: %s='%s'\n", args[1], args[2]); } else { // Just a name - show this specific alias AliasEntry *alias = find_alias(args[1]); if (alias) { printf("alias %s='%s'\n", alias->name, alias->command); } else { printf("Alias '%s' not found\n", args[1]); } } // Save aliases to file save_aliases(); return 1; } /** * Command handler for the "unalias" command */ int lsh_unalias(char **args) { if (args[1] == NULL) { printf("unalias: missing argument\n"); return 1; } if (remove_alias(args[1])) { printf("Alias '%s' removed\n", args[1]); // Save aliases to file save_aliases(); } else { printf("Alias '%s' not found\n", args[1]); } return 1; } /** * Command handler to show all aliases */ int lsh_aliases(char **args) { for (int i = 0; i < alias_count; i++) { printf("alias %s='%s'\n", aliases[i].name, aliases[i].command); } return 1; } /** * Get alias names for tab completion */ char** get_alias_names(int *count) { if (alias_count == 0 || !count) { *count = 0; return NULL; } char **names = (char**)malloc(alias_count * sizeof(char*)); if (!names) { *count = 0; return NULL; } for (int i = 0; i < alias_count; i++) { names[i] = strdup(aliases[i].name); } *count = alias_count; return names; } /** * Expand alias in args array */ char** expand_alias(char **args) { if (!args || !args[0]) { return NULL; } // Check if the command is an alias AliasEntry *alias = find_alias(args[0]); if (!alias) { return NULL; } // Count tokens in alias command char *alias_copy = strdup(alias->command); if (!alias_copy) { return NULL; } char **alias_tokens = malloc(LSH_TOK_BUFSIZE * sizeof(char*)); if (!alias_tokens) { free(alias_copy); return NULL; } char *token, *rest = alias_copy; int token_count = 0; while ((token = strtok_r(rest, " \t", &rest)) != NULL) { alias_tokens[token_count++] = strdup(token); if (token_count >= LSH_TOK_BUFSIZE) { fprintf(stderr, "lsh: alias expansion: too many tokens\n"); break; } } free(alias_copy); // Count original arguments int arg_count = 0; while (args[arg_count] != NULL) { arg_count++; } // Allocate space for expanded command char **expanded_args = malloc((token_count + arg_count) * sizeof(char*)); if (!expanded_args) { for (int i = 0; i < token_count; i++) { free(alias_tokens[i]); } free(alias_tokens); return NULL; } // Copy alias tokens for (int i = 0; i < token_count; i++) { expanded_args[i] = alias_tokens[i]; } // Copy remaining arguments (skip the command) for (int i = 1; i < arg_count; i++) { expanded_args[token_count + i - 1] = strdup(args[i]); } // Null-terminate the array expanded_args[token_count + arg_count - 1] = NULL; free(alias_tokens); return expanded_args; }