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