diff --git a/Makefile b/Makefile
index 9e7e92d..1a2307a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
CC = gcc
-CFLAGS = -Wall -Wextra -g
+CFLAGS = -Wextra -g
LIBS = -lm
# Get all .c files
@@ -19,4 +19,4 @@ $(TARGET): $(OBJECTS)
clean:
rm -f $(OBJECTS) $(TARGET)
-.PHONY: all clean
\ No newline at end of file
+.PHONY: all clean
diff --git a/builtins.c b/builtins.c
index 0fbd3c4..77f1a0f 100644
--- a/builtins.c
+++ b/builtins.c
@@ -36,7 +36,7 @@ char *builtin_str[] = {
"bookmark", "bookmarks", "goto", "unbookmark", "focus_timer",
"weather", "grep", "grep-text", "ripgrep", "fzf",
"clip", "echo", "theme", "loc", "git_status",
- "gg"};
+ "gg", "ls",};
// Array of function pointers to built-in command implementations
int (*builtin_func[])(char **) = {
@@ -47,7 +47,7 @@ int (*builtin_func[])(char **) = {
&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_gg, lsh_dir,};
/**
* Set the console text color
@@ -184,142 +184,125 @@ int lsh_exit(char **args) { return 0; }
* 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 count = 0;
- int detailed = 0;
+ DIR *dir;
+ struct dirent *entry;
+ struct stat file_stat;
+ char cwd[PATH_MAX];
+ char file_path[PATH_MAX];
+ int detailed = 0;
- // Check if we should show detailed listing
- if (args[1] != NULL && (strcmp(args[1], "-l") == 0 || strcmp(args[1], "--long") == 0)) {
- detailed = 1;
- }
+ // 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");
- 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");
- return 1;
- }
+ dir = opendir(cwd);
+ if (dir == NULL) {
+ perror("lsh: opendir");
+ free_table(table);
+ return 1;
+ }
- if (detailed) {
- printf("Mode Size Modified Name\n");
- printf("----------------------------------------------------\n");
- }
+ // Collect all entries
+ while ((entry = readdir(dir)) != NULL) {
+ // Skip . and .. entries
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+ continue;
+ }
- // First collect all entries
- typedef struct {
- char name[256];
- struct stat stat;
- int is_dir;
- } DirEntry;
+ 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;
+ }
- DirEntry *entries = malloc(1000 * sizeof(DirEntry)); // Arbitrary limit
- int entry_count = 0;
+ // 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
- while ((entry = readdir(dir)) != NULL && entry_count < 1000) {
- // Skip . and .. entries
- if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
- continue;
- }
+ // 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;
- snprintf(file_path, sizeof(file_path), "%s/%s", cwd, entry->d_name);
- if (stat(file_path, &file_stat) == 0) {
- strcpy(entries[entry_count].name, entry->d_name);
- entries[entry_count].stat = file_stat;
- entries[entry_count].is_dir = S_ISDIR(file_stat.st_mode);
- entry_count++;
- }
- }
+ // 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;
- closedir(dir);
+ // 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;
- // Sort entries - directories first then files
- for (int i = 0; i < entry_count - 1; i++) {
- for (int j = i + 1; j < entry_count; j++) {
- if (entries[i].is_dir < entries[j].is_dir ||
- (entries[i].is_dir == entries[j].is_dir &&
- strcmp(entries[i].name, entries[j].name) > 0)) {
- DirEntry temp = entries[i];
- entries[i] = entries[j];
- entries[j] = temp;
- }
- }
- }
+ // Add row to table
+ add_table_row(table, row);
+ }
+ }
- // Display entries
- for (int i = 0; i < entry_count; i++) {
- if (detailed) {
- // Format mode (simplified)
- char mode[11] = "----------";
- if (entries[i].is_dir) mode[0] = 'd';
- if (entries[i].stat.st_mode & S_IRUSR) mode[1] = 'r';
- if (entries[i].stat.st_mode & S_IWUSR) mode[2] = 'w';
- if (entries[i].stat.st_mode & S_IXUSR) mode[3] = 'x';
- if (entries[i].stat.st_mode & S_IRGRP) mode[4] = 'r';
- if (entries[i].stat.st_mode & S_IWGRP) mode[5] = 'w';
- if (entries[i].stat.st_mode & S_IXGRP) mode[6] = 'x';
- if (entries[i].stat.st_mode & S_IROTH) mode[7] = 'r';
- if (entries[i].stat.st_mode & S_IWOTH) mode[8] = 'w';
- if (entries[i].stat.st_mode & S_IXOTH) mode[9] = 'x';
+ closedir(dir);
- // Format size
- char size_str[20];
- if (entries[i].stat.st_size < 1024) {
- snprintf(size_str, sizeof(size_str), "%d B", (int)entries[i].stat.st_size);
- } else if (entries[i].stat.st_size < 1024 * 1024) {
- snprintf(size_str, sizeof(size_str), "%.1f KB",
- entries[i].stat.st_size / 1024.0);
- } else {
- snprintf(size_str, sizeof(size_str), "%.1f MB",
- entries[i].stat.st_size / (1024.0 * 1024.0));
- }
+ // Print the table
+ print_table(table);
- // Format time
- char time_str[20];
- struct tm *tm_info = localtime(&entries[i].stat.st_mtime);
- strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M", tm_info);
+ // Free the table
+ free_table(table);
- // Print entry with colors
- printf("%s %-10s %-17s ", mode, size_str, time_str);
-
- if (entries[i].is_dir) {
- printf(ANSI_COLOR_BLUE "%s" ANSI_COLOR_RESET "\n", entries[i].name);
- } else if (entries[i].stat.st_mode & S_IXUSR) {
- printf(ANSI_COLOR_GREEN "%s" ANSI_COLOR_RESET "\n", entries[i].name);
- } else {
- printf("%s\n", entries[i].name);
- }
- } else {
- count++;
- if (entries[i].is_dir) {
- printf(ANSI_COLOR_BLUE "%-20s" ANSI_COLOR_RESET, entries[i].name);
- } else if (entries[i].stat.st_mode & S_IXUSR) {
- printf(ANSI_COLOR_GREEN "%-20s" ANSI_COLOR_RESET, entries[i].name);
- } else {
- printf("%-20s", entries[i].name);
- }
-
- if (count % 4 == 0) {
- printf("\n");
- }
- }
- }
+ return 1;
+ }
- free(entries);
-
- if (!detailed && count % 4 != 0) {
- printf("\n");
- }
-
- return 1;
-}
/**
* Built-in command: clear
@@ -819,4 +802,4 @@ int lsh_gg(char **args) {
}
return 1;
-}
\ No newline at end of file
+}
diff --git a/builtins.h b/builtins.h
index 04101ad..23d01fb 100644
--- a/builtins.h
+++ b/builtins.h
@@ -1,5 +1,5 @@
/**
- * builtins.h
+ * builtins.h
* Declarations for all built-in shell commands
*/
@@ -97,4 +97,4 @@ extern int (*builtin_func[])(char **);
*/
char *extract_json_string(const char *json, const char *key);
-#endif // BUILTINS_H
\ No newline at end of file
+#endif // BUILTINS_H
diff --git a/builtins.o b/builtins.o
index 235df55..6ca505d 100644
Binary files a/builtins.o and b/builtins.o differ
diff --git a/shell b/shell
index cec6d47..6ea9e30 100755
Binary files a/shell and b/shell differ
diff --git a/shell.c b/shell.c
index f768e64..19ce6b0 100644
--- a/shell.c
+++ b/shell.c
@@ -22,13 +22,15 @@
#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 WORD g_normal_attributes = 7; // Default white on black
-static WORD g_status_attributes = 12; // Red
-static BOOL g_status_bar_enabled = FALSE; // Flag to track if status bar is enabled
+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
/**
@@ -160,7 +162,7 @@ int init_status_bar(int fd) {
g_console_width = width;
g_status_line = height;
- g_status_bar_enabled = TRUE;
+ g_status_bar_enabled = 1;
// Clear status line
printf(ANSI_SAVE_CURSOR);
@@ -328,6 +330,205 @@ void get_path_display(const char *cwd, char *parent_dir_name,
}
}
+/**
+ * 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 ***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 commands array
+ commands = malloc(cmd_capacity * sizeof(char**));
+ if (!commands) {
+ perror("lsh: allocation error");
+ return NULL;
+ }
+
+ // Split by pipe symbol '|'
+ cmd_str = strtok_r(line, "|", &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);
+ 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
+ return commands;
+}
+
/**
* Launch an external program
*/
@@ -402,6 +603,51 @@ int lsh_execute_piped(char ***commands) {
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]);
@@ -562,7 +808,7 @@ void lsh_loop(void) {
// Check for pipes and parse into multiple commands if present
if (strchr(line, '|') != NULL) {
- commands = lsh_split_piped_line(line);
+ commands = lsh_split_commands(line);
status = lsh_execute_piped(commands);
free_commands(commands);
} else {
@@ -604,4 +850,4 @@ void lsh_loop(void) {
// Restore terminal
restore_terminal(terminal_fd, &g_orig_termios);
-}
+}
\ No newline at end of file
diff --git a/shell.h b/shell.h
index 91c1844..36909c8 100644
--- a/shell.h
+++ b/shell.h
@@ -30,6 +30,22 @@ int lsh_execute(char **args);
*/
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
*
diff --git a/shell.o b/shell.o
index 34f2c7f..636386e 100644
Binary files a/shell.o and b/shell.o differ