Skip to content

Instantly share code, notes, and snippets.

@akoskovacs
Last active December 12, 2016 21:14
Show Gist options
  • Save akoskovacs/b3157961d4e039ba53da84e578a5c28f to your computer and use it in GitHub Desktop.
Save akoskovacs/b3157961d4e039ba53da84e578a5c28f to your computer and use it in GitHub Desktop.
Really dumb shell
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define HAVE_READLINE
#ifdef HAVE_READLINE
# include <readline/readline.h>
# include <readline/history.h>
#endif
#define PATH_MAX 256
static const char const *MY_PATH[] = { "/bin/", "/usr/bin/", "/sbin/", NULL };
static const int MAX_ARGS = 10;
static const char *mygetwd(void)
{
static char path[PATH_MAX];
getcwd(path, PATH_MAX-1);
path[PATH_MAX-1] = '\0';
return path;
}
static void dump_argv(char **argv) {
int i;
for (i = 0; argv && argv[i] != NULL; i++) {
printf("%d. - '%s'\n", i, argv[i]);
}
}
static const char *find_exec(const char *command)
{
static char npath[PATH_MAX];
const char **path_p = MY_PATH;
struct stat sb;
if (command == NULL) {
return NULL;
}
/* Absolute and relative paths are easy (no work for us) */
if ((command[0] == '/') // absolute
|| ((command[0] == '.') && command[1] == '/')) { // relative
return command;
}
while (*path_p != NULL) {
strncpy(npath, *path_p, PATH_MAX-1);
npath[PATH_MAX-1] = '\0';
strncat(npath, command, PATH_MAX-1);
npath[PATH_MAX-1] = '\0';
if ((stat(npath, &sb)) == -1) {
if (errno == ENOENT) {
path_p++;
continue;
}
} else {
// Regular file, great
if (S_ISREG(sb.st_mode)) {
if ((sb.st_mode & S_IXUSR) || (sb.st_mode & S_IXGRP)
|| (sb.st_mode & S_IXOTH)) {
// Great, it's executable
return npath;
} else {
fprintf(stderr, "ERROR: The file is not executable!\n");
break;
}
} else {
fprintf(stderr, "ERROR: The file is not regular!\n");
break;
}
}
}
return NULL;
}
static pid_t myexec_argv(char **argv)
{
int status;
extern char **environ;
const char *fullprog = NULL;
fullprog = find_exec(argv[0]);
/* Cannot find executable :( */
if (fullprog == NULL) {
fprintf(stderr, "Cannot find executable '%s'!\n", argv[0]);
return -1;
}
// dump_argv(argv);
pid_t proc = fork();
if (proc == 0) {
/* in child */
if (execve(fullprog, argv, environ) == -1) {
fprintf(stderr, "ERROR: Cannot start program! :(\n");
perror("execve()");
return -1;
}
} else if (proc != -1) {
/* in parent */
if (waitpid(proc, &status, WUNTRACED | WCONTINUED) == -1) {
perror("waitpid()");
return -1;
}
}
return proc;
}
static int myexec_bultin(char **argv)
{
if ((strcmp(argv[0], "cd")) == 0) {
if (argv[1] != NULL) {
if (chdir(argv[1]) == -1) {
fprintf(stderr, "ERROR: Can't change directory!\n");
perror("chdir()");
}
} else {
fprintf(stderr, "ERROR: But, where to go? Need a directory!\n");
}
return 1;
} else if ((strcmp(argv[0], "exit")) == 0) {
exit(0);
}
return 0;
}
static pid_t myexec(char *command)
{
char *saveptr = NULL;
char *argv[MAX_ARGS];
char *nexttok = NULL;
int argv_i = 0;
while ((nexttok = strtok_r(command, " \t\n", &saveptr)) != NULL) {
if (argv_i < MAX_ARGS) {
argv[argv_i++] = nexttok;
} else {
fprintf(stderr, "ERROR: Too many arguments!\n");
return 0;
}
command = NULL;
}
argv[argv_i] = NULL;
if (argv[0] == NULL) {
fprintf(stderr, "ERROR: No program to run.\n");
return 0;
}
if (!myexec_bultin(argv)) {
return myexec_argv(argv);
}
return 0;
}
int main()
{
char command[PATH_MAX];
char prompt[PATH_MAX];
char *cmd = command;
printf("Dumb Shell v0.1\n");
printf("Copyleft (C) Ákos Kovács - 2016\n\n");
while (1) {
snprintf(prompt, PATH_MAX, "[%s] > ", mygetwd());
#ifdef HAVE_READLINE
cmd = readline(prompt);
if (cmd != NULL && cmd[0] != '\0') {
add_history(cmd);
}
#else
printf("%s", prompt);
fgets(command, PATH_MAX-1, stdin);
command[PATH_MAX-1] = '\0';
#endif /* HAVE_READLINE */
myexec(cmd);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment