DEV Community

Ertugrul
Ertugrul

Posted on

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 <stdio.h> #include <stdlib.h> #include <string.h> #define INT_BUFFER_SIZE 32 // Initial buffer size char *read_command(); #endif 
Enter fullscreen mode Exit fullscreen mode
  • 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); } 
Enter fullscreen mode Exit fullscreen mode
  • 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; 
Enter fullscreen mode Exit fullscreen mode
  • 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; } 
Enter fullscreen mode Exit fullscreen mode
  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; 
Enter fullscreen mode Exit fullscreen mode
  • 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! 👇

Top comments (0)