Advertisement
codemonkey

tomashell.c

Oct 13th, 2011
441
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.77 KB | None | 0 0
  1. /*
  2.  * ============================================== *
  3.  * ==================== HEAD ==================== *
  4.  * ============================================== *
  5.  */
  6.  
  7. /*
  8.  * ================ *
  9.  * === Includes === *
  10.  * ================ *
  11.  */
  12.  
  13. #include <stdlib.h>
  14. #include <stdio.h>
  15. #include <unistd.h>
  16. #include <sys/types.h>
  17.  
  18. #include "safefork.c"
  19. #include "stringops.h"
  20. #include "arrayops.h"
  21. #include "fileops.h"
  22. #include "builtins/history.h"
  23.  
  24. #ifdef DEBUG
  25. #include "debug.c"
  26. #endif
  27.  
  28. /*
  29.  * =================== *
  30.  * === Definitions === *
  31.  * =================== *
  32.  */
  33.  
  34. #define MAX_USER_INPUT_LENGTH 120
  35. #define MAX_USER_INPUT_WORDS 20
  36.  
  37. // Errors
  38. #define TOMASHELL_ERROR_TOO_MANY_WORDS 0x01
  39.  
  40. /*
  41.  * ============== *
  42.  * === Macros === *
  43.  * ============== *
  44.  */
  45.  
  46. /*
  47.  * Shortcut for comparing strings
  48.  */
  49. #define streq(x,y) strcmp(x,y) == 0
  50.  
  51. /*
  52.  * The message that precedes every user command
  53.  */
  54. #define prompt() printf("%s@%s %d> ",getenv("LOGNAME"),"tomashell",cmd_num++)
  55.  
  56. /*
  57.  * =============== *
  58.  * === Globals === *
  59.  * =============== *
  60.  */
  61.  
  62. // For debug use
  63. int ticker = 0;
  64.  
  65. // Number of commands given since application started
  66. int cmd_num = 1;
  67.  
  68. /*
  69.  * ================= *
  70.  * === Functions === *
  71.  * ================= *
  72.  */
  73.  
  74. /*
  75.  * The main function. All it does is initiate the application loop
  76.  */
  77. int main();
  78.  
  79. /*
  80.  * This is the application loop. It runs until the user chooses to quit
  81.  */
  82. int tomashell();
  83.  
  84. /*
  85.  * This function splits a string up into words. This function must be used
  86.  * instead of only stringops.c:strsplit because the task specification demands
  87.  * a particular behavior regarding the output
  88.  */
  89. int str_to_words(char* const input, char** const output);
  90.  
  91. /*
  92.  * This is the function called when the user tries to run an external command
  93.  */
  94. int extcommand(char** param, int background);
  95.  
  96. /*
  97.  * Something went wrong. Error terminates the application and dumps an error
  98.  * message to the log file
  99.  */
  100. void error(char* message);
  101.  
  102. /*
  103.  * ============================================== *
  104.  * ==================== BODY ==================== *
  105.  * ============================================== *
  106.  */
  107.  
  108. int main()
  109. {
  110.     // Padding newline to start things off
  111.     printf("\n");
  112.  
  113. #ifdef DEBUG
  114.     printf("~ Tomashell compiled in DEBUG mode. DEBUG messages will appear on \
  115. lines starting with \"~\", such as this one.\n\n");
  116. #endif
  117.  
  118.     // Run until the user decides to quit. The while loop will continue until
  119.     // tomashell() returns 0
  120.     while(tomashell());
  121.  
  122.     // Exit message
  123.     puts("\nExiting tomashell");
  124.  
  125.     // Padding newline to end things
  126.     printf("\n");
  127.  
  128.     return (0);
  129. }
  130.  
  131. int tomashell()
  132. {
  133.     int cont = 1; // Whether or not to continue the loop
  134.     int bg = 0; // Whether or not to run the command in the background
  135.  
  136.     // Declare some space for the user input
  137.     char input[MAX_USER_INPUT_LENGTH];
  138.  
  139.     // Prompt the user
  140.     prompt();
  141.  
  142.     // Read the user input
  143.     if(!fgets(input, MAX_USER_INPUT_LENGTH, stdin))
  144.     {
  145.         // No more input
  146.         printf("\n");
  147.         return (0);
  148.     }
  149.  
  150.     // Trim the input
  151.     strtrim(input);
  152.  
  153. #ifdef DEBUG
  154.     // Print out the command and length
  155.     printf("~ Input: %s\n", input);
  156.     printf("~ Input length: %d\n", strlen(input));
  157. #endif
  158.  
  159.     // Empty input: move on
  160.     if(!strlen(input)) return 1;
  161.  
  162.     /*
  163.      * Parse the input
  164.      */
  165.  
  166.     // For holding the input words
  167.     char* param[21];
  168.  
  169.     // Before splitting, let's see if there's a trailing ampersand
  170.     if(*(input + (strlen(input) - 1)) == '&')
  171.     {
  172. #ifdef DEBUG
  173.         printf("~ Background mode set (&)\n");
  174. #endif
  175.        
  176.         // If there's a trailing ampersand, flag the current command to be
  177.         // started in the background
  178.         bg = 1;
  179.  
  180.         // Remove the trailing ampersand from the input string
  181.         strremi(input, strlen(input) - 1);
  182.     }
  183.  
  184.     // Split input string into words
  185.     if(!str_to_words(input, param))
  186.     {
  187.         // Handle errors
  188.         switch(errno)
  189.         {
  190.             case TOMASHELL_ERROR_TOO_MANY_WORDS:
  191.                 printf("You input too many words!\n");
  192.             default:
  193.                 error("Unknown error occurred in str_to_words");
  194.         }
  195.     }
  196.  
  197.     // Save the length of the param array
  198.     int paramlen = arraylen((void**)param);
  199.    
  200. #ifdef DEBUG
  201.     printf("~ Number of words in input: %d\n", paramlen);
  202. #endif
  203.  
  204.     // Log the command to history
  205.     h_add(param);
  206.  
  207.     // Builtin commands
  208.     if(streq(param[0], "exit") || streq(param[0], "quit"))
  209.     {
  210.         cont = 0;
  211.         goto end;
  212.     }
  213.     else if(streq(param[0], "h") || streq(param[0], "history"))
  214.     {
  215.         // If h was called with ho additional arguments
  216.         if(paramlen == 1)
  217.         {
  218.             int i; // index
  219.  
  220.             for(i = 9; i >= 0; i--)
  221.             {
  222.                 // Jump ahead if there aren't enough history states
  223.                 if(h_len() < i) continue;
  224.  
  225.                 // Fetch the command at this history state
  226.                 char* oldcmd = h_cmd_index(i);
  227.  
  228.                 // Print it out
  229.                 printf("%d: %s\n", i, oldcmd);
  230.             }
  231.         }
  232.         else printf("Sorry, not implemented yet\n");
  233.     }
  234.  
  235.     // Try to run the input as an external command
  236.     else if(extcommand(param, bg))
  237.     {
  238.         // Nothing to do in here, really...
  239.     }
  240.  
  241.     // Command not found
  242.     else
  243.         printf("%s: %s: %s\n", "tomashell", param[0], "Command not found");
  244.  
  245.     /*
  246.      * This is the end of the function. It's safe to jump here from any point
  247.      * in the function to exit prematurely
  248.      */
  249.     end:
  250.  
  251.     // Free the param array
  252.     arrayfree((void**)param);
  253.  
  254.     return (cont);
  255. }
  256.  
  257. int str_to_words(char* const input, char** const output)
  258. {
  259.     // Split the input string by the space character
  260.     char** words = strsplit(input, ' ');
  261.     int i;
  262.  
  263.     // Get the array length
  264.     int len = arraylen((void**)words);
  265.  
  266.     // Validate valid word number
  267.     if(len > MAX_USER_INPUT_WORDS)
  268.     {
  269.         errno = TOMASHELL_ERROR_TOO_MANY_WORDS;
  270.         return (0);
  271.     }
  272.  
  273.     // Fit into the input array using the defined limits
  274.     for(i = 0; i < len; i++)
  275.         *(output + i) = *(words + i);
  276.  
  277.     *(output + i) = NULL;
  278.  
  279.     // Free
  280.     free(words);
  281.  
  282.     return (1);
  283. }
  284.  
  285. int extcommand(char** const param, int const background)
  286. {
  287.     // Split path by colons
  288.     char** paths = strsplit(getenv("PATH"), ':');
  289.  
  290.     // This holds the full path to the command if it exists
  291.     char* cmdpath = NULL;
  292.  
  293.     // The length of the command
  294.     int cmdlen = strlen(*(param));
  295.     int cmdfound = 0;
  296.  
  297.     // For various loops
  298.     int i;
  299.  
  300. #ifdef DEBUG
  301.     // In debug mode, print out all the paths in PATH
  302.     printf("~ Path:\n");
  303.     for(i = 0; i < arraylen((void**)paths); i++)
  304.         printf("~\t%s\n", *(paths + i));
  305. #endif
  306.  
  307.     // Try to find the requested command in PATH
  308.     for(i = 0; i < arraylen((void**)paths); i++)
  309.     {
  310.         // The length of the final path is the length of the path + a dash +
  311.         // the command length and the nullbyte
  312.         int len = strlen(*(paths + i)) + cmdlen + 2;
  313.  
  314.         // This will hold the concatenated string
  315.         char* c = malloc(sizeof(char) * len);
  316.  
  317.         // Concatenate the path with the command
  318.         sprintf(c, "%s/%s", *(paths + i), *(param));
  319.  
  320.         // Check if the file exists in this path
  321.         if(fileexists(c))
  322.         {
  323.             // Set cmdpath to the path and break out of the loop
  324.             cmdpath = c;
  325.             break;
  326.         }
  327.         else
  328.         {
  329.             // Free the temporary variable
  330.             free(c);
  331.         }
  332.     }
  333.  
  334.     // Check if the command was found
  335.     if(cmdpath)
  336.     {
  337.         // Mark that a command was found
  338.         cmdfound = 1;
  339.     }
  340.     else
  341.     {
  342.         // Mark the no command could be found
  343.         cmdfound = 0;
  344.  
  345.         // Jump to the end of the function
  346.         goto end;
  347.     }
  348.  
  349.     /*
  350.      * Fork
  351.      */
  352.  
  353. #ifdef DEBUG
  354.     printf("~ Found command at path: %s\n", cmdpath);
  355. #endif
  356.    
  357.     // Fork the process using safefork
  358.     pid_t pid = safefork();
  359.  
  360.     // In case the fork couldn't be completed
  361.     if(pid == -1)
  362.         error("Fork failed");
  363.  
  364.     // Child process
  365.     else if(pid == 0)
  366.     {
  367.         // Run command
  368.         int result = execve(cmdpath, param, NULL);
  369.  
  370.         // This shouldn't happen
  371.         printf("Fatal error! execve returned: %d\n", result);
  372.         exit(1);
  373.     }
  374.  
  375.     // Parent process
  376.     else
  377.     {
  378. #ifdef DEBUG
  379.         // Print out the PID of the child process in DEBUG mode
  380.         printf("~ Child process created: %d\n", pid);
  381. #endif
  382.  
  383.         // To hold the status returned by waitpid
  384.         int status = 0;
  385.         i = 0;
  386.  
  387.         // Unless the program is set to be launched in the background, wait for
  388.         // the child process to finish
  389.         if(!background)
  390.         {
  391.             while(1)
  392.             {
  393.                 // Wait for the child process to finish
  394.                 wait(&status);
  395.  
  396. #ifdef DEBUG
  397.                 printf("~ Child status changed: %d\n", status);
  398. #endif
  399.  
  400.                 // When the child had stopped, break the loop
  401.                 if(status == 0 || status == 256) break;
  402.             }
  403.         }
  404.         // If the program is set for background launch, just print the PID
  405.         else
  406.         {
  407.             printf("%s started at %d\n", param[0], pid);
  408.         }
  409.     }
  410.  
  411.     /*
  412.      * End of function
  413.      */
  414.     end:
  415.     // Free the array of paths
  416.     arrayfree((void**)paths);
  417.     free(paths);
  418.  
  419.     // Free the string holding the full path
  420.     if(cmdpath) free(cmdpath);
  421.  
  422.     // Return 0 if the command couldn't be found, otherwise 1
  423.     return cmdfound;
  424. }
  425.  
  426. void error(char* message)
  427. {
  428. #ifdef DEBUG
  429.     printf("%s\n", message);
  430. #endif
  431.  
  432.     exit(1);
  433. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement