Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * ============================================== *
- * ==================== HEAD ==================== *
- * ============================================== *
- */
- /*
- * ================ *
- * === Includes === *
- * ================ *
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include "safefork.c"
- #include "stringops.h"
- #include "arrayops.h"
- #include "fileops.h"
- #include "builtins/history.h"
- #ifdef DEBUG
- #include "debug.c"
- #endif
- /*
- * =================== *
- * === Definitions === *
- * =================== *
- */
- #define MAX_USER_INPUT_LENGTH 120
- #define MAX_USER_INPUT_WORDS 20
- // Errors
- #define TOMASHELL_ERROR_TOO_MANY_WORDS 0x01
- /*
- * ============== *
- * === Macros === *
- * ============== *
- */
- /*
- * Shortcut for comparing strings
- */
- #define streq(x,y) strcmp(x,y) == 0
- /*
- * The message that precedes every user command
- */
- #define prompt() printf("%s@%s %d> ",getenv("LOGNAME"),"tomashell",cmd_num++)
- /*
- * =============== *
- * === Globals === *
- * =============== *
- */
- // For debug use
- int ticker = 0;
- // Number of commands given since application started
- int cmd_num = 1;
- /*
- * ================= *
- * === Functions === *
- * ================= *
- */
- /*
- * The main function. All it does is initiate the application loop
- */
- int main();
- /*
- * This is the application loop. It runs until the user chooses to quit
- */
- int tomashell();
- /*
- * This function splits a string up into words. This function must be used
- * instead of only stringops.c:strsplit because the task specification demands
- * a particular behavior regarding the output
- */
- int str_to_words(char* const input, char** const output);
- /*
- * This is the function called when the user tries to run an external command
- */
- int extcommand(char** param, int background);
- /*
- * Something went wrong. Error terminates the application and dumps an error
- * message to the log file
- */
- void error(char* message);
- /*
- * ============================================== *
- * ==================== BODY ==================== *
- * ============================================== *
- */
- int main()
- {
- // Padding newline to start things off
- printf("\n");
- #ifdef DEBUG
- printf("~ Tomashell compiled in DEBUG mode. DEBUG messages will appear on \
- lines starting with \"~\", such as this one.\n\n");
- #endif
- // Run until the user decides to quit. The while loop will continue until
- // tomashell() returns 0
- while(tomashell());
- // Exit message
- puts("\nExiting tomashell");
- // Padding newline to end things
- printf("\n");
- return (0);
- }
- int tomashell()
- {
- int cont = 1; // Whether or not to continue the loop
- int bg = 0; // Whether or not to run the command in the background
- // Declare some space for the user input
- char input[MAX_USER_INPUT_LENGTH];
- // Prompt the user
- prompt();
- // Read the user input
- if(!fgets(input, MAX_USER_INPUT_LENGTH, stdin))
- {
- // No more input
- printf("\n");
- return (0);
- }
- // Trim the input
- strtrim(input);
- #ifdef DEBUG
- // Print out the command and length
- printf("~ Input: %s\n", input);
- printf("~ Input length: %d\n", strlen(input));
- #endif
- // Empty input: move on
- if(!strlen(input)) return 1;
- /*
- * Parse the input
- */
- // For holding the input words
- char* param[21];
- // Before splitting, let's see if there's a trailing ampersand
- if(*(input + (strlen(input) - 1)) == '&')
- {
- #ifdef DEBUG
- printf("~ Background mode set (&)\n");
- #endif
- // If there's a trailing ampersand, flag the current command to be
- // started in the background
- bg = 1;
- // Remove the trailing ampersand from the input string
- strremi(input, strlen(input) - 1);
- }
- // Split input string into words
- if(!str_to_words(input, param))
- {
- // Handle errors
- switch(errno)
- {
- case TOMASHELL_ERROR_TOO_MANY_WORDS:
- printf("You input too many words!\n");
- default:
- error("Unknown error occurred in str_to_words");
- }
- }
- // Save the length of the param array
- int paramlen = arraylen((void**)param);
- #ifdef DEBUG
- printf("~ Number of words in input: %d\n", paramlen);
- #endif
- // Log the command to history
- h_add(param);
- // Builtin commands
- if(streq(param[0], "exit") || streq(param[0], "quit"))
- {
- cont = 0;
- goto end;
- }
- else if(streq(param[0], "h") || streq(param[0], "history"))
- {
- // If h was called with ho additional arguments
- if(paramlen == 1)
- {
- int i; // index
- for(i = 9; i >= 0; i--)
- {
- // Jump ahead if there aren't enough history states
- if(h_len() < i) continue;
- // Fetch the command at this history state
- char* oldcmd = h_cmd_index(i);
- // Print it out
- printf("%d: %s\n", i, oldcmd);
- }
- }
- else printf("Sorry, not implemented yet\n");
- }
- // Try to run the input as an external command
- else if(extcommand(param, bg))
- {
- // Nothing to do in here, really...
- }
- // Command not found
- else
- printf("%s: %s: %s\n", "tomashell", param[0], "Command not found");
- /*
- * This is the end of the function. It's safe to jump here from any point
- * in the function to exit prematurely
- */
- end:
- // Free the param array
- arrayfree((void**)param);
- return (cont);
- }
- int str_to_words(char* const input, char** const output)
- {
- // Split the input string by the space character
- char** words = strsplit(input, ' ');
- int i;
- // Get the array length
- int len = arraylen((void**)words);
- // Validate valid word number
- if(len > MAX_USER_INPUT_WORDS)
- {
- errno = TOMASHELL_ERROR_TOO_MANY_WORDS;
- return (0);
- }
- // Fit into the input array using the defined limits
- for(i = 0; i < len; i++)
- *(output + i) = *(words + i);
- *(output + i) = NULL;
- // Free
- free(words);
- return (1);
- }
- int extcommand(char** const param, int const background)
- {
- // Split path by colons
- char** paths = strsplit(getenv("PATH"), ':');
- // This holds the full path to the command if it exists
- char* cmdpath = NULL;
- // The length of the command
- int cmdlen = strlen(*(param));
- int cmdfound = 0;
- // For various loops
- int i;
- #ifdef DEBUG
- // In debug mode, print out all the paths in PATH
- printf("~ Path:\n");
- for(i = 0; i < arraylen((void**)paths); i++)
- printf("~\t%s\n", *(paths + i));
- #endif
- // Try to find the requested command in PATH
- for(i = 0; i < arraylen((void**)paths); i++)
- {
- // The length of the final path is the length of the path + a dash +
- // the command length and the nullbyte
- int len = strlen(*(paths + i)) + cmdlen + 2;
- // This will hold the concatenated string
- char* c = malloc(sizeof(char) * len);
- // Concatenate the path with the command
- sprintf(c, "%s/%s", *(paths + i), *(param));
- // Check if the file exists in this path
- if(fileexists(c))
- {
- // Set cmdpath to the path and break out of the loop
- cmdpath = c;
- break;
- }
- else
- {
- // Free the temporary variable
- free(c);
- }
- }
- // Check if the command was found
- if(cmdpath)
- {
- // Mark that a command was found
- cmdfound = 1;
- }
- else
- {
- // Mark the no command could be found
- cmdfound = 0;
- // Jump to the end of the function
- goto end;
- }
- /*
- * Fork
- */
- #ifdef DEBUG
- printf("~ Found command at path: %s\n", cmdpath);
- #endif
- // Fork the process using safefork
- pid_t pid = safefork();
- // In case the fork couldn't be completed
- if(pid == -1)
- error("Fork failed");
- // Child process
- else if(pid == 0)
- {
- // Run command
- int result = execve(cmdpath, param, NULL);
- // This shouldn't happen
- printf("Fatal error! execve returned: %d\n", result);
- exit(1);
- }
- // Parent process
- else
- {
- #ifdef DEBUG
- // Print out the PID of the child process in DEBUG mode
- printf("~ Child process created: %d\n", pid);
- #endif
- // To hold the status returned by waitpid
- int status = 0;
- i = 0;
- // Unless the program is set to be launched in the background, wait for
- // the child process to finish
- if(!background)
- {
- while(1)
- {
- // Wait for the child process to finish
- wait(&status);
- #ifdef DEBUG
- printf("~ Child status changed: %d\n", status);
- #endif
- // When the child had stopped, break the loop
- if(status == 0 || status == 256) break;
- }
- }
- // If the program is set for background launch, just print the PID
- else
- {
- printf("%s started at %d\n", param[0], pid);
- }
- }
- /*
- * End of function
- */
- end:
- // Free the array of paths
- arrayfree((void**)paths);
- free(paths);
- // Free the string holding the full path
- if(cmdpath) free(cmdpath);
- // Return 0 if the command couldn't be found, otherwise 1
- return cmdfound;
- }
- void error(char* message)
- {
- #ifdef DEBUG
- printf("%s\n", message);
- #endif
- exit(1);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement