A custom shell implementation for FogOS with command history, built-in commands, and script execution support.
- Command History: Maintains a history of up to 100 commands with support for:
- Viewing full history with
historycommand - Accessing commands by number
- Prefix-based history search (via helper functions)
- Viewing full history with
- Built-in Commands:
exit- Exits the shell immediatelycd <directory>- Changes the current working directory (with error handling)history- Displays command history with numbered entries
- Command Execution: Executes external programs via
fork()andexec()- Tracks exit status of executed commands
- Waits for child processes to complete
- Script Support: Can read and execute commands from a file (passed as command-line argument)
- Reads commands line-by-line from the file
- Automatically closes file when done
- Comment Handling:
- Lines starting with
#are treated as comments and ignored - Inline comments (text after
#on a line) are also stripped
- Lines starting with
- Custom Prompt: Displays exit status and command number in the format
[exit_status]-[cmd_num]─[cwd]$- Only shown in interactive mode (not when reading from file)
- Shows the exit status of the previous command
# Interactive mode
$ crash
# Execute commands from a file
$ crash script.txt- Uses custom tokenization functions (
strspn,strcspn,next_token) for parsing commands - Maintains command history in a circular buffer (max 100 entries)
- Tracks exit status of executed commands and displays in prompt
- Supports both interactive input (via
gets()) and file-based input (viaread()) - Handles empty lines and whitespace trimming
- Custom string utility functions for tokenization without standard library dependencies
A custom memory allocator implementation with multiple allocation strategies, block management, and debugging features.
- Multiple Allocation Algorithms:
- First Fit (FIRST_FIT=0): Allocates from the first free block that fits
- Best Fit (BEST_FIT=1): Allocates from the smallest free block that fits
- Worst Fit (WORST_FIT=2): Allocates from the largest free block
- Algorithm can be switched at runtime via
malloc_setfsm()
- Memory Management:
- Block-based allocation with metadata headers (
mem_blockstruct) - 16-byte alignment for all user allocations
- Page-aligned (4096 bytes) allocations from OS via
sbrk() - Automatic block splitting when allocating from larger free blocks
- Splits only if remaining size >= minimum split size (requested size + block header + 16 bytes)
- Automatic coalescing (merging) of adjacent free blocks on
free() - Memory release back to OS when tail blocks are free and >= 4096 bytes
- Automatically releases memory at the end of the heap
- Block-based allocation with metadata headers (
- Standard Functions:
malloc(size)- Allocate memory (returns NULL on failure or zero size)free(ptr)- Free allocated memory (safe to call with NULL)calloc(nmemb, size)- Allocate and zero-initialize memoryrealloc(ptr, size)- Resize existing allocation- In-place expansion when next block is free and large enough
- In-place shrinking when new size is smaller
- Allocates new block and copies data if in-place resize not possible
- Debug Features:
malloc_print()- Print current memory state (all blocks) and free listmalloc_name(ptr, name)- Name allocations for debugging (max 7 chars, null-terminated)malloc_setfsm(algorithm)- Switch allocation algorithm at runtime- Optional debug logging when compiled with
-DDEBUG=1
- Data Structures:
- Doubly-linked block list maintained in address order (for coalescing)
- Separate free list (singly-linked) for efficient allocation searches
- Free bit stored in the least significant bit of the size field (bit manipulation)
- Block metadata includes: name (8 bytes), size (with free bit), next/prev pointers
- Free list nodes stored immediately after block header
- Allocation Strategy:
- Try to reuse a free block using selected algorithm (First/Best/Worst Fit)
- If no suitable free block, request new memory from OS via
sbrk() - Split blocks when allocating from larger free blocks
- Return pointer to user data (after block header)
- Deallocation Strategy:
- Mark block as free
- Add to free list
- Merge with adjacent free blocks (both next and previous)
- Release memory to OS if tail block is free and >= 4096 bytes
- Reallocation Strategy:
- Shrink in-place if new size is smaller (split off excess)
- Expand in-place if next block is free and combined size is sufficient
- Otherwise, allocate new block, copy data, and free old block
// Set allocation algorithm
malloc_setfsm(FIRST_FIT); // or BEST_FIT, WORST_FIT
// Allocate memory
void *ptr = malloc(100);
malloc_name(ptr, "buffer");
// Resize allocation
ptr = realloc(ptr, 200);
// Debug: print memory state
malloc_print();
// Free memory
free(ptr);