How I Built a Simple Shell in C – A Beginner's Guide to System Programming (1/3)

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5175

    #1

    How I Built a Simple Shell in C – A Beginner's Guide to System Programming (1/3)

    Part 1: Reading User Input in a Custom Shell (C Programming)

    In this part of my custom shell project, I will explain how to read user input dynamically in C. Handling user input properly is crucial in a shell environment, as commands can vary in length. Instead of using fixed-size buffers, I implemented a dynamic memory allocation approach for better flexibility.





    Understanding the read_command Function

    The function read_command() is responsible for:

    ✅ Reading input from the user dynamically

    ✅ Handling memory allocation & reallocation to avoid buffer overflow

    ✅ Ensuring proper termination of the input string


    Breaking Down the Code






    #ifndef READ_COMMAND_H
    #define READ_COMMAND_H

    #include
    #include
    #include

    #define INT_BUFFER_SIZE 32 // Initial buffer size

    char *read_command();

    #endif
    • Header Guard (#ifndef READ_COMMAND_H): Prevents multiple inclusions.
    • Constant Definition (INT_BUFFER_SIZE): Sets an initial buffer size for input storage.





    Implementation of read_command()





    char *read_command()
    {
    char *command = malloc(INT_BUFFER_SIZE * sizeof(char));
    if (!command)
    {
    perror("Memory allocation failed");
    exit(EXIT_FAILURE);
    }
    • Memory Allocation (malloc): Initially allocates INT_BUFFER_SIZE bytes for storing user input.
    • Error Handling: If malloc fails, the program prints an error and exits.




    int size = INT_BUFFER_SIZE;
    int length = 0;
    int c;
    • size: Keeps track of the current buffer size.
    • length: Tracks the actual number of characters read.
    • c: Stores the input character retrieved from getchar().





    Handling Dynamic Input Growth





    while ((c = getchar()) != '\n' && c != EOF)
    {
    if (length >= (size - 1))
    {
    size *= 2; // Double the buffer size when needed
    char *new_command = realloc(command, (size + 1));
    if (!new_command)
    {
    free(command);
    perror("Memory reallocation failed");
    exit(EXIT_FAILURE);
    }
    command = new_command;
    }
    command[length++] = c;
    }






    1. Expanding Memory Dynamically (realloc)
      • If input exceeds the allocated size, the buffer is doubled (size *= 2).
      • realloc attempts to resize the buffer; if it fails, memory is freed, and an error is displayed.
    2. Storing Characters
      • Each character from getchar() is stored sequentially in command[length++]





    Finalizing the Input





    command[length] = '\0'; // Null-terminate the string
    return command;
    • Null-Termination (\0): Ensures that the string is properly terminated so it can be processed as a valid C string.
    • **Returning the Input: **The function returns the dynamically allocated string for further use.





    Why This Approach?

    📌 Avoids Buffer Overflow: Unlike scanf or gets, this method expands dynamically as needed.

    📌 Efficient Memory Management: realloc optimizes memory allocation instead of pre-allocating a large chunk.

    📌 Better Flexibility: Can handle long commands without arbitrary limitations.





    Next Part: Parsing Command 🚀

    In the next part, I will explain how the shell parses user input and executes commands using system functions. Stay tuned! 😊


    📂 GitHub: https://github.com/Ertugrulmutlu/shell_of_mine


    What do you think about this approach? Would you have handled input differently? Let me know in the comments! 👇




    More...
Working...