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! 👇

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

Image of PulumiUP 2025

From Cloud to Platforms: What Top Engineers Are Doing Differently

Hear insights from industry leaders about the current state and future of cloud and IaC, platform engineering, and security.

Save Your Spot

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, cherished by the supportive DEV Community. Coders of every background are encouraged to bring their perspectives and bolster our collective wisdom.

A sincere “thank you” often brightens someone’s day—share yours in the comments below!

On DEV, the act of sharing knowledge eases our journey and forges stronger community ties. Found value in this? A quick thank-you to the author can make a world of difference.

Okay