Skip to content

Instantly share code, notes, and snippets.

@SibTiger SibTiger/SeaShell.c
Created Nov 13, 2017

Embed
What would you like to do?
Operating Systems: Simulation of a Shell Environment [without Pipes]
// =============================================================================
// -----------------------------------------------------------------------------
// =============================================================================
// Programmer: Nicholas Gautier
// Class: CS3513; Operating Systems
// Assignment #: 1
// Due Date: 19.September.2017
// Instructor: Dr. Zhao
// Description: This program allows the user to interact with a basic shell
// environment and executes external commands at the user's
// request. This shell environment is very basic and offers
// very limited functionality.
// NOTES: This program was originally developed under the Windows
// environment, however due to limitations - this was
// expeditiously converted to work with *nix environment.
// The reason for this change-over was how CreateProcess() and
// fork() works. fork() copies the program and furthers the
// execution as if nothing really changed (visually),
// CreateProcess() however starts a brand new process without
// copying the main instance source and furthering the execution.
// Hacks can be made to emulate this behavior, but this may not
// be suitable for class material. Thus, we will have to shift
// to work with Linux and expeditiously get this program to work
// given that it took several days already....
// Credits:
// Dr. Zhao for resolving how the PATH data is stored internally within the
// program.
// Kyle Ressel for helping to resolve how PATH data is stored by the
// further revisements of improvements, also helping to get the
// execl() statement to work dynamically.
// Return Codes:
// 0 = Successful
// 1 = %PATH% Environment System Variable not obtained
// =============================================================================
// -----------------------------------------------------------------------------
// =============================================================================
#pragma region Online Resources - HELP ME
// printf special formatting:
// https://www.codingunit.com/printf-format-specifiers-format-conversions-and-formatted-output
// Passing and returning char's:
// https://www.quora.com/How-can-I-pass-a-string-as-an-argument-to-a-function-and-return-a-string-in-C
// strtok example:
// http://www.cplusplus.com/forum/general/13058/
// strtok_s [Microsoft's thread-safe version]:
// https://msdn.microsoft.com/en-us/library/ftsafwz3.aspx
// Time features
// https://www.tutorialspoint.com/c_standard_library/time_h.htm
// fgets [user input]
// https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
// _stricmp() [Windows]
// https://stackoverflow.com/questions/17899993/compare-strings-after-converting-to-lower-case-in-c
// https://msdn.microsoft.com/en-us/library/k59z8dwe(v=vs.80).aspx
// Page 118 - OS book
#pragma endregion
#pragma region Libraries
#include <stdio.h>
#include <stdlib.h> // Holds malloc (if we ever need it), system(),
#include <stddef.h> // Holds are NULLPTR
#include <string.h> // for functions: strtok(), strtok_s() - MICROSOFT's 'THREAD SAFE' version, strcmp()
#include <time.h> // Used for the tm struct functionality; for prompt
#include <ctype.h> // used for tolower()
#include <unistd.h> // Used for fork() and pid_t
#include <stdbool.h> // Because I was spoiled in C[++ | #]; give me bool datatypes!
#pragma endregion
#pragma region Macros
#define _NAME_ "Sea Shell" // Program name
#define _VERSION_ "1.0" // Program version
#define _INPUT_MAX_LENGTH_ 255 // Entire input limit
#pragma endregion
#pragma region Function Prototypes
char *FetchEnvironmentVariable(char *[]); // Retrieve the System Environment variables
void BootUpScreen(); // Display bootup message
void WelcomeScreen(); // Display the Welcome Screen
void ExitingScreen(); // Display the terminating message
void PromptMSG(); // Display the Prompt message before STDIN is provided.
char *PromptSTDIN(); // Allow the end-user to type a command into the shell
#pragma endregion
// Main - Entry Point
// -------
// This function is the starting or entry-point of the program.
// Help for Main Arguments: https://stackoverflow.com/a/4176337
int main(int argc, char **argv)
{
// Declarations and Initializations
char *pathRaw = FetchEnvironmentVariable("PATH"); // Fetch %PATH% (or $PATH) and store it directly to the pointer of chars
char *pathToken = NULL; // Used for parsing through the PATH system environment variable
char storedToken[1000][100]; // Stores the entire tokens in an array for easy access.
// This will allow the program to access each directory easily when invoking
char *execCMD; // This variable will allow the end-user to execute the specific command.
char *execArgs; // This allows the user to pass arguments to the array of char's
// in which allows the user to issue arguments to perform special
// operations that could not be normally performed without.
// the execl() or other means (or actions).
char tokenDelim[] = ":;"; // Specifies the delimiters for the tokens
// Delim: : = Linux's $PATH, ; = Windows's %PATH% or $PATH
// This will cover both fields as an 'or'. Thus : OR ; but never both
int parsingCounter = 0; // This will allow the program to pass through iterations and record storing.
// Record storing will be for caching the value held within this variable to
// another variable, such as 'parsingCounter_Result'.
int parsingCounter_Result = 0; // This variable can help to determine the maximum indexes used in the
// variable 'parsingCounter'.
char *userSTDIN; // This pointer of char's variable will be used for capturing user input.
char path[_INPUT_MAX_LENGTH_]; // This variable is dedicated to the execl() for capturing the path
// and the binary.
pid_t processID; // Retains the process id of the child process that is duplicated.
// ----------
// Provide the user with a bootup message
BootUpScreen();
// Check to make sure that pathRaw contains data we need; else - terminate this program abruptly.
if (!strcmp(pathRaw, "ERROR"))
{
printf("<!> OPERATION HALTED <!>\n");
printf("===============================\n\n");
printf("UNABLE TO SUCCESSFULLY OBTAIN SYSTEM ENVIRONMENT %PATH%!\nPlease complain to the developer of this program to make better software.\n\n");
return 1; // Failure to obtain %PATH% - KILL PROGRAM
} // if: %PATH% not retrieved successfully
else
{
pathToken = strtok(pathRaw, tokenDelim); // Immediately capture the first token and have strtok() cache the initial string.
// NOTE: This is NOT thread safe and stores data in a static pointer.
while(pathToken != NULL)
{
strcpy(storedToken[parsingCounter], pathToken); // Duplicate the token to an array for access for later
// As required for this assignment, provide output in the following way:
// Index - of the array we are going to store into.
// Path - Store data within based on the index of the array
// Token - Token that has been generated from the PATH system environment.
printf("Path Index: %d\n\t\tPath Directory: %s\n\t\tGenerated Token: %s\n", parsingCounter, storedToken[parsingCounter], pathToken);
parsingCounter++; // Update the parsingCounter to the next index.
pathToken = strtok(NULL, tokenDelim); // Shift to the next token
} // Scan through the %PATH% and generate tokens
printf("\n"); // Visual purposes only
// Save the final index for later
parsingCounter_Result = parsingCounter;
} // else
// Provide the user with the basic information about this shell
WelcomeScreen();
// Never-Ending loop
do
{
//Provide the prompt message
PromptMSG();
// Get user-feedback
userSTDIN = PromptSTDIN();
// As requested for this assignment, provide what the user entered into the program
printf("\nRequested Command: %s\n\n", userSTDIN);
// Terminate the program by request?
if ((strcasecmp(userSTDIN, "exit") == 0) ||
(strcasecmp(userSTDIN, "quit") == 0))
break; // Leave the loop and start the termination process
else
{
processID = fork(); // Create a new child process
if (processID < 0)
{ // Were we able to fork a new process? Check child state
fprintf(stderr, "Unable To Generate Child; Fork Failure!");
return 2;
} // if: Fork() status
else if (processID == 0)
{ // Child Process:
parsingCounter = 0; // Reset the parser counter as we will use this to access
// the path array index.
// Fetch the initial command that the user requested for
execCMD = strtok(userSTDIN, " ");
// Try to retrieve parameters issued by the end-user - - if any was provided
execArgs = strtok(NULL, "");
do
{
strcpy(path, storedToken[parsingCounter]); // Capture path into a cache variable
strcat(path, "/"); // Add the UNIX filesystem '/' directory char.
strcat(path, userSTDIN); // Add the user's requested command to the cache variable
// ----
printf("%s\n", path); // Display the path before execution
// ----
// Execute the command as requested
execl (path, execCMD, execArgs, (char *)NULL);
parsingCounter++; // Update to the next index
} while (parsingCounter <= parsingCounter_Result); // Loop until we reach the max index limit
printf("Invalid External or Internal command!\n");
exit(0);
} // if: Child Process
else // Parent process; wait for child (fork()) to finish process.
wait(NULL);
} // else: Run extCMD
} while (1);
// Terminating process
ExitingScreen();
// Terminate the software
return 0;
} // main()
// Fetch Environment Variable
// This function is designed to retrieve a variable from the host system
// and return the data as requested.
// -------
// Parameters:
// envVar[] - Char
// This string will state what environment variable is being requested.
// -------
// Output:
// String (char[])
// Returns the results from the system environment variable as a string (or char[]).
// If failure, the return will be 'ERROR'. Which in this case, the program is unable to work.
// -------
char *FetchEnvironmentVariable(char *envVar[])
{
// Declaration and Initializations
// ----------------
char *buffer = NULL; // Capture the data into the buffer-pointer
size_t sizeNum = 0; // Retains the number of elements
// ----------------
buffer = getenv(envVar); // Capture the system environment variable data
if (buffer != NULL) // Check to see if the data is valid (failed or passed)
return buffer;
else
return "ERROR";
} // FetchPathEnvironment()
// Welcome Screen
// -------
// This function will provide basic information to the user about the program. Adjacent to the Command Shell (and PowerShell)
void WelcomeScreen()
{
printf("%s [version: %s]\nOn the C shore\n\n", _NAME_, _VERSION_);
} // WelcomeScreen()
// Boot Up Screen
// -------
// This function will merely display the bootup message on the terminal screen.
void BootUpScreen()
{
printf("\nStarting up %s. . .\n\n", _NAME_);
} // BootUpScreen()
// Exiting Screen
// -------
// This function will display a message in regards to the shell terminating
void ExitingScreen()
{
printf("\n\n%s is closing. . .\n", _NAME_);
} // ExitingScreen()
// Prompt Message
// -------
// With this function, this can provide the user basic information that would matter most.
void PromptMSG()
{
// Declarations and Initializations
struct tm * timeDateObj; // Create an instance of the 'tm' object
char timeDateBuffer[100]; // Declare a buffer for the time\date formatting
char *programWorkingDirectory; // Create a new variable for holding the program's WD
// Gather information as needed
// ===========================
// Program's Working Directory
programWorkingDirectory = getcwd(NULL, 0); // Cache the program's working directory.
if (programWorkingDirectory == NULL) // Did 'getcwd()' failed?
programWorkingDirectory = "<ERROR_GET_WD_FAILED>"; // Provide an error message, but DON'T kill the program.
// ===========================
// Date and Time
// I wasted over an hour on this, if something breaks - just nuke this feature.
// https://stackoverflow.com/questions/23024862/localtime-s-in-c-using-microsoft-visual-studio-2013
// https://stackoverflow.com/questions/13658756/example-of-tm-use
// http://www.cplusplus.com/reference/ctime/strftime/
// https://www.tutorialspoint.com/c_standard_library/time_h.htm <-- USING TM DIRECTLY VIA OBJECT
time_t rawTime; // We will need this var. for the next step
time(&rawTime); // Capture current date in seconds as since 1970's
timeDateObj = localtime(&rawTime); // Populate the time\date data in 'tm'
strftime(timeDateBuffer, 100, "[%a.%e.%b.%Y %H.%M:%S]", timeDateObj); // Perform special formatting to time and date scheme.
// ===========================
// Display the Prompt Message: Date and Time
printf("%s ", timeDateBuffer);
// Display the Prompt Message: WD
printf("[%s]\n", programWorkingDirectory);
} // PromptMSG()
// Prompt STDIN [keyboard]
// -------
// This function allows the end-user to type a command into the shell for evaluation and execution.
// -------
// Output
// char*
// Command requested by the end user
char *PromptSTDIN()
{
char *interactionSTDIN = (char *) malloc(sizeof(char) * _INPUT_MAX_LENGTH_); // Used for storing user input
printf(">>>>> "); // This should help indicate that we are waiting for user interaction
fgets(interactionSTDIN, _INPUT_MAX_LENGTH_, stdin); // Capture user input
interactionSTDIN[strlen(interactionSTDIN) -1] = '\0';
// Report back the requested command
return interactionSTDIN;
} // PromptSTDIN()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.