Call this initial commit...

master
Chris Dorman 2020-11-24 06:45:28 +08:00
commit 55513bcad0
8 changed files with 462 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
lsh

23
Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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);

5
test.sh Normal file
View File

@ -0,0 +1,5 @@
#!/bin/sh
# This is a comment to skip
# skip this one too
exit