Yesterday, I completed 22 of 43 stages. I planned to redo the autocompletion part from scratch, so I paused. Today I updated my code, but the redirection stage is failing. After some research, I found that formatting differs across OSs — on my MacBook Air, I got the desired results.
Could my code be picking up the wrong ls? Logs are attached below.
[tester::#UN3] Running tests for Stage #UN3 (Redirection - Append stderr)
[tester::#UN3] [setup] export PATH=/tmp/blueberry/apple/blueberry:$PATH
[tester::#UN3] Running ./your_program.sh
[your-program] $ ls -1 nonexistent >> /tmp/qux/bar.md
[your-program] ls: cannot access 'nonexistent': No such file or directory
[tester::#UN3] ^ Line does not match expected value.
[tester::#UN3] Expected: "ls: nonexistent: No such file or directory"
[tester::#UN3] Received: "ls: cannot access 'nonexistent': No such file or directory"
[your-program] $
[tester::#UN3] Assertion failed.
[tester::#UN3] Test failed (try setting 'debug: true' in your codecrafters.yml to see more details)
Here’s the code which handles non-builtin commands:
void exec_external(char **argv, struct exec_info **execs, int n_exc) {
struct exec_info *ei = find_exec(argv[0], execs, n_exc);
if (strcmp(argv[0], "ls") == 0) {
printf("%s\n", ei->e_path);
}
if (ei == NULL) {
printf("%s: command not found\n", argv[0]);
return;
}
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
} else if (pid == 0) {
execv(ei->e_path, argv);
} else {
if (waitpid(pid, NULL, 0) != pid) {
perror("waitpid");
exit(1);
}
}
}
The parser remains unchanged. My current idea is to, before entering the REPL loop, traverse all the paths, locate all executables, and store them in a structure like this:
struct exec_info {
char *e_name;
int e_len;
char *e_path;
};
Each entry will be dynamically allocated — e_name for the executable name, e_len for its length, and e_path for the absolute path. I’ll then perform a binary search to quickly retrieve the associated data and work with it as needed.
#include "exec.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #include <unistd.h> int exec_compare_sort(const void *a, const void *b) { const struct exec_info *ea = *((const struct exec_info**)a); const struct exec_info *eb = *((const struct exec_info**)b); return strcmp(ea->e_name, eb->e_name); } int exec_compare_bin_search(const void *key, const void *elem) { const struct exec_info *e_key = (const struct exec_info*)key; const struct exec_info *e_elem = *((const struct exec_info**)elem); return strcmp(e_key->e_name, e_elem->e_name); } struct exec_info *find_exec(char *name, struct exec_info **execs, int n_exc) { struct exec_info key = { .e_name = name, .e_len = strlen(name), .e_path = NULL }; struct exec_info **res = bsearch(&key, execs, n_exc, sizeof(struct exec_info*), exec_compare_bin_search); return res ? *res : NULL; } struct exec_info **build_exec_list(char **paths, int n_paths, int *n_exc, int *mx_len) { int cap = 16, len = 0, tmp_len = 0; struct exec_info **exc_list = malloc(cap * sizeof(struct exec_info*)); char *built_in[] = {"echo", "pwd", "cd", "exit", "type"}; for (int i = 0; i < 5; i++) { exc_list[len] = malloc(sizeof(struct exec_info)); exc_list[len]->e_name = strdup(built_in[i]); exc_list[len]->e_len = strlen(built_in[i]); exc_list[len++]->e_path = strdup("-"); } for (int i = 0; i < n_paths; i++) { DIR *d = opendir(paths[i]); if (!d) continue; struct dirent *di; while ((di = readdir(d)) != NULL) { if (di->d_name[0] == '.' || di->d_type == DT_DIR) continue; int okay = 1; for (int i = 0; i < 5; i++) { if (strcmp(built_in[i], di->d_name) == 0) { okay = 0; break; } } if (!okay) continue; char full[256]; sprintf(full, "%s/%s", paths[i], di->d_name); if (access(full, F_OK | X_OK) == 0) { if (len == cap) { cap *= 2; exc_list = realloc(exc_list, cap * sizeof(struct exec_info*)); } int l = strlen(di->d_name); if (l > tmp_len) { tmp_len = l; } exc_list[len] = malloc(sizeof(struct exec_info)); exc_list[len]->e_name = strdup(di->d_name); exc_list[len]->e_len = l; exc_list[len++]->e_path = strdup(full); } } closedir(d); } *mx_len = tmp_len; *n_exc = len; qsort(exc_list, len, sizeof(struct exec_info*), exec_compare_sort);//sort for auto-completion return exc_list; }}
