Call this initial commit...
This commit is contained in:
commit
55513bcad0
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
lsh
|
23
Makefile
Normal file
23
Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
# Makefile - (C) Chris Dorman, 2020 <cddo@riseup.net>
|
||||
|
||||
PREFIX = /freon
|
||||
CC = cc
|
||||
|
||||
CFLAGS = -O2
|
||||
LDFLAGS =
|
||||
|
||||
BIN = cdsh
|
||||
OBJECTS = src/parse.o src/main.o
|
||||
|
||||
all: main
|
||||
|
||||
fresh: clean all
|
||||
|
||||
main: $(OBJECTS)
|
||||
$(CC) $(OBJECTS) -o $(BIN) $(LDFLAGS) $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) $(BIN)
|
||||
|
||||
install:
|
||||
cp $(BIN) $(PREFIX)/bin/$(BIN)
|
35
README.md
Normal file
35
README.md
Normal file
@ -0,0 +1,35 @@
|
||||
CDSH
|
||||
===
|
||||
|
||||
CDSH is a simple implementation of a shell in C, and Since its purpose
|
||||
is demonstration (not feature completeness or even fitness for casual use),
|
||||
it has many limitations, including:
|
||||
|
||||
* Commands must be on a single line.
|
||||
* Arguments must be separated by whitespace.
|
||||
* No quoting arguments or escaping whitespace.
|
||||
* No piping or redirection.
|
||||
* Only builtins are: `cd`, `help`, `exit`.
|
||||
|
||||
As of forking this open domain source, I wanted to give it more functionality.
|
||||
I do plan to give this the ability to be scripted and further as well has
|
||||
allowing some customization to the prompt, and per user file. I also plan to
|
||||
form processing of for, while, and if statements.
|
||||
|
||||
Compiling & Running
|
||||
-------
|
||||
|
||||
make; make install
|
||||
|
||||
cdsh &
|
||||
|
||||
Go from there!
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
CDSH is relicensed under LGPLv2 - Chris Dorman maintained!
|
||||
|
||||
This code is in the public domain (see [UNLICENSE](UNLICENSE) for more details).
|
||||
This means you can use, modify, and distribute it without any restriction. I
|
||||
appreciate, but don't require, acknowledgement in derivative work.
|
281
src/main.c
Normal file
281
src/main.c
Normal file
@ -0,0 +1,281 @@
|
||||
// @file main.c ~ main source file for in
|
||||
// @author Original: Stephen Brennan ~ Maintained and customized
|
||||
// my Chris Dorman, 2020+ LGPLv2
|
||||
// @desc CDSH, Chris Dorman's SHell! Simplistic, but functional
|
||||
// posix unix shell.
|
||||
|
||||
#include "main.h"
|
||||
#include "parse.h"
|
||||
|
||||
/*
|
||||
List of builtin commands, followed by their corresponding functions.
|
||||
*/
|
||||
char *builtin_str[] = {
|
||||
"cd",
|
||||
"help",
|
||||
"exit"
|
||||
};
|
||||
|
||||
int (*builtin_func[]) (char **) = {
|
||||
&cdsh_cd,
|
||||
&cdsh_help,
|
||||
&cdsh_exit
|
||||
};
|
||||
|
||||
int cdsh_num_builtins() {
|
||||
return sizeof(builtin_str) / sizeof(char *);
|
||||
}
|
||||
|
||||
/*
|
||||
Builtin function implementations.
|
||||
*/
|
||||
|
||||
/*
|
||||
@brief Bultin command: change directory.
|
||||
@param args List of args. args[0] is "cd". args[1] is the directory.
|
||||
@return Always returns 1, to continue executing.
|
||||
*/
|
||||
int cdsh_cd(char **args)
|
||||
{
|
||||
if (args[1] == NULL) {
|
||||
fprintf(stderr, "cdsh: expected argument to \"cd\"\n");
|
||||
} else {
|
||||
if (chdir(args[1]) != 0) {
|
||||
perror("cdsh");
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Builtin command: print help.
|
||||
@param args List of args. Not examined.
|
||||
@return Always returns 1, to continue executing.
|
||||
*/
|
||||
int cdsh_help(char **args)
|
||||
{
|
||||
int i;
|
||||
printf("Chris Dorman's SHell\n");
|
||||
printf("Type program names and arguments, and hit enter.\n");
|
||||
printf("The following are built in:\n");
|
||||
|
||||
for (i = 0; i < cdsh_num_builtins(); i++) {
|
||||
printf("=> %s\n", builtin_str[i]);
|
||||
}
|
||||
|
||||
printf("Use the man command for information on other programs.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Builtin command: exit.
|
||||
@param args List of args. Not examined.
|
||||
@return Always returns 0, to terminate execution.
|
||||
*/
|
||||
int cdsh_exit(char **args)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Launch a program and wait for it to terminate.
|
||||
@param args Null terminated list of arguments (including program).
|
||||
@return Always returns 1, to continue execution.
|
||||
*/
|
||||
int cdsh_launch(char **args)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
// Child process
|
||||
if (execvp(args[0], args) == -1) {
|
||||
perror("cdsh");
|
||||
}
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
} else if (pid < 0) {
|
||||
// Error forking
|
||||
perror("cdsh");
|
||||
} else {
|
||||
// Parent process
|
||||
do {
|
||||
waitpid(pid, &status, WUNTRACED);
|
||||
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Execute shell built-in or launch program.
|
||||
@param args Null terminated list of arguments.
|
||||
@return 1 if the shell should continue running, 0 if it should terminate
|
||||
*/
|
||||
int cdsh_execute(char **args)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (args[0] == NULL) {
|
||||
// An empty command was entered.
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < cdsh_num_builtins(); i++) {
|
||||
if (strcmp(args[0], builtin_str[i]) == 0) {
|
||||
return (*builtin_func[i])(args);
|
||||
}
|
||||
}
|
||||
|
||||
return cdsh_launch(args);
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Read a line of input from stdin.
|
||||
@return The line from stdin.
|
||||
*/
|
||||
char *cdsh_read_line(void)
|
||||
{
|
||||
#ifdef CDSH_USE_STD_GETLINE
|
||||
char *line = NULL;
|
||||
ssize_t bufsize = 0; // have getline allocate a buffer for us
|
||||
if (getline(&line, &bufsize, stdin) == -1) {
|
||||
if (feof(stdin)) {
|
||||
exit(EXIT_SUCCESS); // We recieved an EOF
|
||||
} else {
|
||||
perror("cdsh: getline\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
#else
|
||||
#define CDSH_RL_BUFSIZE 1024
|
||||
int bufsize = CDSH_RL_BUFSIZE;
|
||||
int position = 0;
|
||||
char *buffer = malloc(sizeof(char) * bufsize);
|
||||
int c;
|
||||
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "cdsh: allocation error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
// Read a character
|
||||
c = getchar();
|
||||
|
||||
if (c == EOF) {
|
||||
exit(EXIT_SUCCESS);
|
||||
} else if (c == '\n') {
|
||||
buffer[position] = '\0';
|
||||
return buffer;
|
||||
} else {
|
||||
buffer[position] = c;
|
||||
}
|
||||
|
||||
position++;
|
||||
|
||||
// If we have exceeded the buffer, reallocate.
|
||||
if (position >= bufsize) {
|
||||
bufsize += CDSH_RL_BUFSIZE;
|
||||
buffer = realloc(buffer, bufsize);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "cdsh: allocation error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define CDSH_TOK_BUFSIZE 64
|
||||
#define CDSH_TOK_DELIM " \t\r\n\a"
|
||||
/*
|
||||
@brief Split a line into tokens (very naively).
|
||||
@param line The line.
|
||||
@return Null-terminated array of tokens.
|
||||
*/
|
||||
|
||||
char **cdsh_split_line(char *line)
|
||||
{
|
||||
int bufsize = CDSH_TOK_BUFSIZE, position = 0;
|
||||
char **tokens = malloc(bufsize * sizeof(char*));
|
||||
char *token, **tokens_backup;
|
||||
|
||||
if (!tokens) {
|
||||
fprintf(stderr, "cdsh: allocation error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
token = strtok(line, CDSH_TOK_DELIM);
|
||||
while (token != NULL) {
|
||||
tokens[position] = token;
|
||||
position++;
|
||||
|
||||
if (position >= bufsize) {
|
||||
bufsize += CDSH_TOK_BUFSIZE;
|
||||
tokens_backup = tokens;
|
||||
tokens = realloc(tokens, bufsize * sizeof(char*));
|
||||
if (!tokens) {
|
||||
free(tokens_backup);
|
||||
fprintf(stderr, "cdsh: allocation error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
token = strtok(NULL, CDSH_TOK_DELIM);
|
||||
}
|
||||
tokens[position] = NULL;
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/*
|
||||
@brief Loop getting input and executing it.
|
||||
*/
|
||||
void cdsh_loop(void)
|
||||
{
|
||||
char *line;
|
||||
char **args;
|
||||
int status;
|
||||
|
||||
do {
|
||||
printf("> ");
|
||||
line = cdsh_read_line();
|
||||
args = cdsh_split_line(line);
|
||||
status = cdsh_execute(args);
|
||||
|
||||
free(line);
|
||||
free(args);
|
||||
} while (status);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Main entry point.
|
||||
@param argc Argument count.
|
||||
@param argv Argument vector.
|
||||
@return status code
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Load config files, if any.
|
||||
if(argc < 2 || !strcmp(argv[1], "-?") || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
|
||||
printf("usage: cdsh [file location]\n"
|
||||
"Example: cdsh /path/to/script.sh \n");
|
||||
cdsh_exit(0); // exit
|
||||
} else if (argc == 2) {
|
||||
|
||||
parse(argv[1]);
|
||||
|
||||
cdsh_exit(0);
|
||||
}
|
||||
|
||||
// Run command loop.
|
||||
cdsh_loop();
|
||||
|
||||
// Perform any shutdown/cleanup.
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
17
src/main.h
Normal file
17
src/main.h
Normal file
@ -0,0 +1,17 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h> // For directory listings
|
||||
#include <libgen.h> // for dirname
|
||||
|
||||
/*
|
||||
Function Declarations for builtin shell commands:
|
||||
*/
|
||||
int cdsh_cd(char **args);
|
||||
int cdsh_help(char **args);
|
||||
int cdsh_exit(char **args);
|
||||
int cdsh_execute(char **args);
|
87
src/parse.c
Normal file
87
src/parse.c
Normal file
@ -0,0 +1,87 @@
|
||||
#include "main.h"
|
||||
#include "parse.h"
|
||||
|
||||
const char *newline = "\n";
|
||||
const char *equal = "=";
|
||||
const char *space = " ";
|
||||
|
||||
// Check if a file exists (index.html)
|
||||
int file_exists(char *fname) {
|
||||
struct stat buffer;
|
||||
if (stat (fname, &buffer) == 0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if the path is a directory (darklight)
|
||||
int is_dir(char* p) {
|
||||
char * stripslash;
|
||||
struct stat st;
|
||||
stripslash = p + 1; // strip the first forward 'slash' from the string
|
||||
if (stat(stripslash, &st) == 0 && (st.st_mode & S_IFDIR)) {
|
||||
return 1;
|
||||
}
|
||||
else if (stat(stripslash, &st) == 0 && (st.st_mode & S_IFREG)) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// write to struct
|
||||
void parse(char *filename)
|
||||
{
|
||||
// open config as readable
|
||||
FILE *file = fopen (filename, "r");
|
||||
|
||||
printf("Parsing %s\n", filename);
|
||||
|
||||
// if file is null, end
|
||||
if (file != NULL)
|
||||
{
|
||||
// line buffer for config
|
||||
char line[CONFBUF];
|
||||
// int used to track config line
|
||||
//int i = 0;
|
||||
|
||||
// config while loop, loops fgets until end of file
|
||||
while(fgets(line, sizeof(line), file) != NULL)
|
||||
{
|
||||
|
||||
char *cfline; // setup string
|
||||
cfline = strtok(line, newline);
|
||||
|
||||
// If its the crunchbang, note for now
|
||||
if (strncmp("#!",line,2)==0) {
|
||||
printf("cdsh: found the crunchbang\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// if line is commented out, skip
|
||||
if (strncmp("#",line,1)==0) {
|
||||
printf("cdsh: skipping comment line in file...\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(newline,line,1)==0) {
|
||||
printf("cdsh: skipping empty line in file...\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp("exit",cfline,4)==0 || strncmp("EXIT",cfline,4)==0) {
|
||||
cdsh_exit(0);
|
||||
} /*else {
|
||||
int x;
|
||||
char *command;
|
||||
for(x = 0; x++; x>=strlen(cfline)) {
|
||||
strncat(command, &cfline[x], 1);
|
||||
}
|
||||
|
||||
cdsh_execute(&command);
|
||||
} */
|
||||
|
||||
} // End while
|
||||
} // End if file
|
||||
|
||||
fclose(file);
|
||||
}
|
13
src/parse.h
Normal file
13
src/parse.h
Normal file
@ -0,0 +1,13 @@
|
||||
#define CONFBUF 2048
|
||||
|
||||
struct parser
|
||||
{
|
||||
char parseline[CONFBUF];
|
||||
char status[CONFBUF];
|
||||
char varname[CONFBUF];
|
||||
char vardata[CONFBUF];
|
||||
};
|
||||
|
||||
void parse(char *filename);
|
||||
int file_exists(char *fname);
|
||||
int is_dir(char* p);
|
Loading…
x
Reference in New Issue
Block a user