/* noshmore * an extended version of nosh, * the minimal POSIX C shell. * -> generally: adds some functionality. * * von czjstmax, */ #include #include #include #include #include #include #include #include #include #include #define NOSHMORE_VERSION 2 #define MAX_ALIASES 256 typedef struct { char *dst; char *src; } Alias; Alias aliases[MAX_ALIASES]; int alias_count = 0; void handlecc(int sig); Alias CreateAlias(char *dst, char *src); void FreeAll(); void FreeAliases(); char *line = NULL; wordexp_t p; int main(void) { signal(SIGINT, handlecc); char *USER = getenv("USER"); if (!USER) USER = "anon"; char *HOME = getenv("HOME"); if (!HOME) HOME = "."; while (true) { char prompt[strlen(USER) + strlen("$ ") + 1]; snprintf(prompt, sizeof(prompt), "%s$ ", USER); line = readline(prompt); if (!line) break; if (*line) add_history(line); if (wordexp(line, &p, 0) != 0) { fprintf(stderr, "nsh+! ERROR while parsing\n"); free(line); continue; } // built-ins if (p.we_wordc == 0) { FreeAll(); continue; } if (strcmp(p.we_wordv[0], ":q") == 0 || strcmp(p.we_wordv[0], "exit") == 0) { FreeAll(); FreeAliases(); return 0; } else if (strcmp(p.we_wordv[0], "echo") == 0) { for (size_t i = 1; i < p.we_wordc; i++) printf("%s ", p.we_wordv[i]); if (p.we_wordc >= 2) { if (strcmp(p.we_wordv[1], "-c") != 0) printf("\n"); else continue; } } else if (strcmp(p.we_wordv[0], "alias") == 0) { if (p.we_wordc <= 2) { fprintf(stderr, "nsh+! ERROR: expected 2 args\n"); } else { // overwrite if exists bool found = false; for (int i = 0; i < alias_count; i++) { if (strcmp(aliases[i].dst, p.we_wordv[1]) == 0) { free(aliases[i].src); aliases[i].src = strdup(p.we_wordv[2]); found = true; break; } } if (!found && alias_count < MAX_ALIASES) { aliases[alias_count++] = CreateAlias(p.we_wordv[1], p.we_wordv[2]); } } } else if (strcmp(p.we_wordv[0], "cd") == 0) { if (p.we_wordc <= 1) { if (chdir(HOME)) perror("nsh+! ERROR: cannot retrieve HOME, failed to change directory"); FreeAll(); continue; } if (chdir(p.we_wordv[1])) perror("nsh+! ERROR: failed to change directory"); } else if (strcmp(p.we_wordv[0], "clear") == 0) { printf("\033[2J\033[H\033[3J"); fflush(stdout); } else if (strcmp(p.we_wordv[0], "help") == 0) { printf( "noshmore\tVERSION(%d)\tALIASES(%d)\n" "an extended version of nosh, the minimal POSIX C shell.\n" "\n" "built-ins:\n" "echo -> outputs a string of text, doesn't need \" enclosure.\n" " supports '-c' for output without '\\n'" "clear -> clears screen (and scrollback)\n" "alias -> creates non-persistent command aliases\n" " (i.e. alias v vim)\n" "cd -> change working directory\n" "help -> prints this screen\n" "\ngithub repo: github.com/jstmaxlol/noshmore\n" , NOSHMORE_VERSION, MAX_ALIASES ); } else { for (int i = 0; i < alias_count; i++) { if (strcmp(p.we_wordv[0], aliases[i].dst) == 0) { char new_line[81920] = {0}; strcat(new_line, aliases[i].src); for (size_t j = 1; j < p.we_wordc; j++) { strcat(new_line, " "); strcat(new_line, p.we_wordv[j]); } wordfree(&p); if (wordexp(new_line, &p, 0) != 0) { fprintf(stderr, "nsh+! ERROR: alias expansion failed\n"); break; } break; } } pid_t pid = fork(); char **argv = p.we_wordv; // pray with me now if (pid == 0) { execvp(p.we_wordv[0], argv); perror("nsh+! ERROR: failed to execvp()"); FreeAll(); _exit(1); } else if (pid > 0) { int status; waitpid(pid, &status, 0); } else { fprintf(stderr, "nsh+! ERROR: %s not found\n", p.we_wordv[0]); } } } } void handlecc(int sig) { (void)sig; rl_replace_line("", 0); printf("\n"); rl_on_new_line(); rl_redisplay(); } Alias CreateAlias(char *dst, char *src) { Alias alias; alias.dst = strdup(dst); alias.src = strdup(src); return alias; } void FreeAll() { wordfree(&p); free(line); line = NULL; } void FreeAliases() { for (int i = 0; i < alias_count; i++) { free(aliases[i].dst); free(aliases[i].src); } }