Skip to content

Instantly share code, notes, and snippets.

@offlinehacker
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save offlinehacker/57589067d7a3d89c9a80 to your computer and use it in GitHub Desktop.
Save offlinehacker/57589067d7a3d89c9a80 to your computer and use it in GitHub Desktop.
My shell
/* Compile with: g++ -Wall –Werror -o shell shell.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
/* The array below will hold the arguments: args[0] is the command. */
static char* args[512];
char *name[100];
int status = 0;
#define READ 0
#define WRITE 1
typedef struct {
int pipes[2];
pid_t pid;
int status;
} pdesc_t;
#define IN_CMD 0x1
#define MIDDLE_CMD 0x2
#define OUT_CMD 0x4
#define RUN_FG 0x8
typedef int (*command_t)(char*, char**);
#define DO_FORK 0x1
typedef struct {
char name[20];
command_t func;
int opts;
} command_desc_t;
pdesc_t *run_command(char **args, int input, int output, int opts, command_t command)
{
int status;
pdesc_t *proc = (pdesc_t*)malloc(sizeof(pdesc_t));
fflush(stdout);
// No worries about memory after fork, it gets copied
if ( pipe(proc->pipes) == -1 ) goto error;
if ( (proc->pid = fork()) == -1 ) goto error;
if ( proc->pid == 0 ) {
if ( opts & IN_CMD && input == 0 ) {
// First command
if ( dup2(input, STDIN_FILENO) == -1 ) goto child_error;
if ( dup2(proc->pipes[WRITE], STDOUT_FILENO) == -1 ) goto child_error;
} else if ( opts & MIDDLE_CMD && input != 0 ) {
// Middle command
if ( dup2(input, STDIN_FILENO) == -1 ) goto child_error;
if ( dup2(proc->pipes[WRITE], STDOUT_FILENO) == -1 ) goto child_error;
} else {
// Last command
if ( dup2(input, STDIN_FILENO) == -1 ) goto child_error;
if ( dup2(output, STDOUT_FILENO) == -1 ) goto child_error;
}
if ( (status = command( args[0], args )) == -1 ) goto child_error;
exit(status);
}
// Nothing more needs to be written
if ( close(proc->pipes[WRITE]) == -1 ) goto error;
// If it's the last command, nothing more needs to be read
if ( opts & OUT_CMD ) {
if ( close(proc->pipes[READ]) == -1 ) goto error;
}
return proc;
child_error:
exit(EXIT_FAILURE);
error:
perror("Error");
return -1;
}
/* Final cleanup, 'wait' for processes to terminate.
* n : Number of times 'command' was invoked.
*/
static int cleanup(int n)
{
int i;
int status;
for (i = 0; i < n; ++i) wait(&status);
return status;
}
static int run(char* cmd, int input, int first, int last);
const char *trimwhitespace(char *str);
static int split(char* cmd, char **dest);
static char line[1024];
int main( int argc, char *argv[] )
{
pdesc_t *proc;
FILE *input = stdin;
int interactive = 1;
if ( isatty(STDIN_FILENO) ) {
printf("♪┏(°.°)┛┗(°.°)┓┗(°.°)┛┏(°.°)┓ ♪\n");
printf("MYsH: Type 'exit' or send EOF to exit.\n");
} else {
interactive = 0;
}
FILE *output_fd = NULL;
FILE *input_fd = NULL;
strcpy(name, "mysh");
while (1) {
if (input_fd) close(input_fd);
if (output_fd) close(output_fd);
/* Print the command prompt */
if (interactive) printf("%s> ", name);
fflush(NULL);
/* Read a command line */
if (!fgets(line, 1024, stdin)) return 0;
int input = STDIN_FILENO;
int output = STDOUT_FILENO;
int first = 1;
char* cmd = trimwhitespace(line);
size_t len = strlen(cmd);
char * redirect_to = memchr(cmd, '>', len);
if (redirect_to != NULL) *(redirect_to++) = '\0';
char * redirect_from = memchr(cmd, '<', len);
if (redirect_from != NULL) *(redirect_from++) = '\0';
char *bg = memchr(cmd, '&', len);
if (bg != NULL) *(bg++) = '\0';
if ( redirect_to != NULL ) {
if ( (output_fd = fopen(trimwhitespace(redirect_to), "w")) == NULL ) {
perror("Error");
continue;
}
output = fileno(output_fd);
} else output = STDOUT_FILENO;
if ( redirect_from != NULL ) {
if ( (input_fd = fopen(trimwhitespace(redirect_from), "r")) == NULL ) {
perror("Error");
continue;
}
input = fileno(input_fd);
} else input = STDIN_FILENO;
cmd = trimwhitespace(cmd);
int count = 1; // Number of forks
char *tmp = strstr(cmd, "pipes");
if (!(tmp && tmp==cmd)) {
char* next = strchr(cmd, '|'); /* Find first '|' */
while (next != NULL) {
/* 'next' points to '|' */
*next = '\0';
if ( first ) { proc = run(cmd, input, output, IN_CMD); }
else { proc = run(cmd, input, output, MIDDLE_CMD);}
if ( proc == -1 ) goto exc_error;
cmd = trimwhitespace(next + 1);
next = strchr(cmd, '|'); /* Find next '|' */
first = 0;
if ( proc->pid == -1 ) { input = 0; break; }
input = proc->pipes[READ];
count ++;
free(proc);
}
proc = run(cmd, input, output, OUT_CMD);
if ( proc == -1 ) goto exc_error;
} else {
int x;
static char* pipes[512];
int len = split(cmd, pipes);
for( x=1; x<len; x++ ) {
if (x>1) { free(proc); }
if ( x == len-1 ) { proc = run(pipes[x], input, output, OUT_CMD); }
else if ( x == 1 ) { proc = run(pipes[x], input, output, IN_CMD); }
else { proc = run(pipes[x], input, output, MIDDLE_CMD);}
if ( proc == -1 ) goto exc_error;
if ( proc->pid == -1 ) { input = 0; break; }
input = proc->pipes[READ];
count++;
}
}
if ( proc && proc->pid == -1 ) { status = proc->status; free(proc); }
else if ( bg == NULL ) { status = cleanup(count); }
}
return 0;
exc_error:
fprintf(stderr, "Excution error!");
exit(EXIT_FAILURE);
}
int to_int(char *str) {
char *endptr;
long val;
val = strtol(str, &endptr, 10);
/* Check for various possible errors */
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
|| (errno != 0 && val == 0)) {
perror("Error: ");
return -1;
}
if (endptr == str) {
return -1;
}
return (int)val;
}
static int cmd_echo(char *cmd, char **args) {
args++;
while (args[0]) { printf("%s ", args[0]); args++; }
printf("\n");
return 0;
}
static int cmd_print(char *cmd, char **args) {
args++;
while (args[0]) { printf("%s ", args[0]); args++; }
return 0;
}
static int cmd_status(char *cmd, char **args) {
printf("%d\n", status);
return status;
}
static int cmd_pid(char *cmd, char **args) {
printf("%d\n", getpid());
return 0;
}
static int cmd_ppid(char *cmd, char **args) {
printf("%d\n", getppid());
return 0;
}
static int cmd_exit(char *cmd, char **args) {
int exit_code;
if ( args[1] != NULL ) {
if( (exit_code = to_int(args[1])) == -1) {
fprintf(stderr, "Error: Error code invalid\n");
return -1;
};
exit(exit_code);
} else {
exit(EXIT_SUCCESS);
}
}
static int cmd_name(char *cmd, char **args) {
if (!args[1]) {
printf("%s\n", name);
return 0;
}
strcpy(name, args[1]);
return 0;
}
static int cmd_dir(char *cmd, char **args) {
int ret;
if (!args[1]) {
return chdir("/");
}
if ( (ret = chdir(args[1])) == -1 ) perror("Error");
return ret;
}
static int cmd_dirwhere(char *cmd, char **args) {
char *cwd = get_current_dir_name();
printf("%s\n", cwd);
free(cwd);
return 0;
}
static int cmd_dirmake(char *cmd, char **args) {
int ret;
if (!args[1]) {
fprintf(stderr, "Error: Directory name not specified\n");
return -1;
}
if ( (ret = mkdir(args[1], 0755)) == -1 ) perror("Error");
return ret;
}
static int cmd_dirremove(char *cmd, char **args) {
int ret;
if (!args[1]) {
fprintf(stderr, "Error: Directory name not specified\n");
return -1;
}
if ( (ret = rmdir(args[1])) == -1 ) perror("Error");
return ret;
}
static int cmd_dirlist(char *cmd, char **args) {
DIR *dirp;
struct dirent *entry;
struct stat stat_buf;
struct stat *statp = &stat_buf;
if (!args[1]) args[1] = ".";
dirp = opendir(args[1]);
if (!dirp) goto error;
/* Reset errno on each iteration to detect readdir() errors */
while (errno = 0, entry = readdir(dirp)){
if (lstat(entry->d_name, statp) == -1) goto error;
printf("%s ", entry->d_name);
}
if (errno) goto error;
closedir(dirp);
printf("\n");
return 0;
error:
perror("Error");
return -1;
}
static int cmd_linkhard(char *cmd, char **args) {
int ret;
if (!args[1]) {
fprintf(stderr, "Error: Source file not specified\n");
return -1;
}
if (!args[2]) {
fprintf(stderr, "Error: Link name not specified\n");
return -1;
}
if ( (ret = link(args[1], args[2])) == -1 ) perror("Error");
return ret;
}
static int cmd_linksoft(char *cmd, char **args) {
int ret;
if (!args[1]) {
fprintf(stderr, "Error: Source file not specified\n");
return -1;
}
if (!args[2]) {
fprintf(stderr, "Error: Link name not specified\n");
return -1;
}
if( (ret = symlink(args[1], args[2])) == -1 ) perror("Error");
return ret;
}
static int cmd_linkread(char *cmd, char **args) {
int ret;
char path[PATH_MAX];
if (!args[1]) {
fprintf(stderr, "Error: Link name not specified\n");
return -1;
}
if( (ret = readlink(args[1], path, PATH_MAX)) == -1 ) perror("Error");
else printf("%s\n", path);
return ret;
}
static int cmd_unlink(char *cmd, char **args) {
int ret;
char path[PATH_MAX];
if (!args[1]) {
fprintf(stderr, "Error: File name not specified\n");
return -1;
}
if( (ret = unlink(args[1])) == -1 ) perror("Error");
return ret;
}
static int cmd_rename(char *cmd, char **args) {
int ret;
if (!args[1]) {
fprintf(stderr, "Error: Source file not specified\n");
return -1;
}
if (!args[2]) {
fprintf(stderr, "Error: Destination file not specified\n");
return -1;
}
if( (ret = rename(args[1], args[2])) == -1 ) perror("Error");
return ret;
}
int cpcat(int fd_to, int fd_from)
{
char buf[4096];
ssize_t nread;
while (nread = read(fd_from, buf, sizeof buf), nread > 0) {
char *out_ptr = buf;
ssize_t nwritten;
do {
nwritten = write(fd_to, out_ptr, nread);
if (nwritten >= 0) {
nread -= nwritten;
out_ptr += nwritten;
}
else if (errno != EINTR) return -1;
} while (nread > 0);
}
if (nread == 0) {
if (close(fd_to) < 0) return -1;
return 0;
}
}
static int cmd_cpcat(char *cmd, char **args) {
int ret, fd_to, fd_from;
char path[PATH_MAX];
if (!args[1]) {
fd_from = STDIN_FILENO;
fd_to = STDOUT_FILENO;
} else {
if ( (fd_from = open(args[1], O_RDONLY)) == -1 ) goto out_error;
if (!args[2]) fd_to = STDOUT_FILENO;
else {
if ( (fd_to = open(args[2], O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1 ) return -1;
}
}
if( (ret = cpcat(fd_to, fd_from)) == -1 ) goto out_error;
return ret;
out_error:
perror("Error");
if (fd_from != STDIN_FILENO) close(fd_from);
if (fd_to != STDOUT_FILENO) close(fd_to);
return -1;
}
static int cmd_listhard(char *cmd, char **args) {
DIR *dirp;
struct dirent *entry;
struct stat stat_buf;
struct stat *statp = &stat_buf;
ino_t inode;
if (!args[1]) {
fprintf(stderr, "Error: Link file not specified\n");
return -1;
}
if (stat(args[1], statp) == -1) goto error;
inode = statp->st_ino;
dirp = opendir(".");
if (!dirp) goto error;
/* Reset errno on each iteration to detect readdir() errors */
while (errno = 0, entry = readdir(dirp)){
if (lstat(entry->d_name, statp) == -1) goto error;
if (inode == statp->st_ino) printf("%s ", entry->d_name);
}
if (errno) goto error;
closedir(dirp);
printf("\n");
return 0;
error:
perror("Error");
return -1;
}
static int cmd_help(char *cmd, char **args);
command_desc_t commands[] = {
{"help", (command_t)cmd_help, DO_FORK},
{"echo", (command_t)cmd_echo, DO_FORK},
{"print", (command_t)cmd_print, DO_FORK},
{"status", (command_t)cmd_status, DO_FORK},
{"dirwhere", (command_t)cmd_dirwhere, DO_FORK},
{"dirmake", (command_t)cmd_dirmake, DO_FORK},
{"dirremove", (command_t)cmd_dirremove, DO_FORK},
{"dirlist", (command_t)cmd_dirlist, DO_FORK},
{"linkhard", (command_t)cmd_linkhard, DO_FORK},
{"linklist", (command_t)cmd_listhard, DO_FORK},
{"linksoft", (command_t)cmd_linksoft, DO_FORK},
{"linkread", (command_t)cmd_linkread, DO_FORK},
{"unlink", (command_t)cmd_unlink, DO_FORK},
{"rename", (command_t)cmd_rename, DO_FORK},
{"cpcat", (command_t)cmd_cpcat, DO_FORK},
{"dir", (command_t)cmd_dir, 0},
{"name", (command_t)cmd_name, 0},
{"exit", (command_t)cmd_exit, 0},
{"pid", (command_t)cmd_pid, 0},
{"ppid", (command_t)cmd_ppid, 0}
};
static int cmd_help(char *cmd, char **args) {
int x;
for( x=0; x<sizeof(commands)/sizeof(command_desc_t); x++)
printf("%s\n", commands[x].name);
return 0;
}
static int cmd_exec(char *cmd, char **args) {
int ret;
if ( (ret = execvp(cmd, args)) == -1 ) { perror("Error"); }
return ret;
}
static int run(char* cmd, int input, int output, int opts) {
int x;
pdesc_t *proc = (pdesc_t*)malloc(sizeof(pdesc_t));
split(cmd, args);
// Skip comments or blank commands
if ( args[0] == NULL || args[0][0] == '#' || args[0][0] == '\n' ) {
proc = (pdesc_t*)malloc(sizeof(pdesc_t));
proc->status = status;
proc->pid = -1;
return proc;
}
// Run internal commands
for( x=0; x<sizeof(commands)/sizeof(command_desc_t); x++) {
if ( strcmp(commands[x].name, args[0]) == 0 ) {
if ( commands[x].opts & DO_FORK ) {
return run_command(args, input, output, opts, commands[x].func);
} else {
proc->pid = -1;
proc->status = commands[x].func(args[0], args);
return proc;
}
}
}
// Run external command
free(proc);
return run_command(args, input, output, opts, (command_t)cmd_exec);
}
const char* skipwhite(char* s)
{
while (isspace(*s)) ++s;
return s;
}
const char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace(*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && (isspace(*end) || *end == '\n')) end--;
// Write new null terminator
*(end+1) = 0;
return str;
}
const char * strip(char* s) {
int len = strlen(s);
if (s[0] == '"')
if (s[len-1] == '"')
(s++)[len-1] = '\0';
return s;
}
static int split(char* cmd, char **dest)
{
char tok;
if (cmd[0] == '"') {tok = '"'; cmd++;} else tok = ' ';
char* next = strchr(cmd, tok);
int i = 0;
while(next != NULL) {
next[0] = '\0';
dest[i] = cmd;
++i;
cmd = skipwhite(next + 1);
if (cmd[0] == '"') {tok = '"'; cmd++;} else tok = ' ';
next = strchr(cmd, tok);
}
if (cmd[0] != '\0') args[i] = strip(cmd);
else args[i] = NULL;
dest[++i] = NULL;
return i-1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment