Search code examples
cdata-structures

Implementation of a terminal history functionality in a tiny toy project


I'm trying to emulate a specific feature of terminal emulators, the ability to parse through previous commands.

My real naive approach was to try and create a cyclical buffer and navigate through it with a next and prev command where it essentially changes view to the current line in the history buffer.

typedef struct {
    char buffer[GT_SCQUEUE_SIZE][GT_SCQUEUE_STRING_SIZE];
    int front;
    int rear;
    int view;
} GT_SCQueue;

GT_SCQueue scq = {{{0}}, -1, -1, 0};

void GT_SCQueue_Push(GT_SCQueue* scq, const char* string) {
    if (++scq->rear == GT_SCQUEUE_SIZE) {
        scq->rear = 0;
    }

    if (scq->front == scq->rear) {
        scq->front = (scq->rear + 1) % GT_SCQUEUE_SIZE;
    }

    if (scq->front == -1) {
        scq->front = 0;
    }

    strncpy(scq->buffer[scq->rear], string, GT_SCQUEUE_STRING_SIZE - 1);
    scq->buffer[scq->rear][GT_SCQUEUE_STRING_SIZE - 1] = '\0';
}

Not at all workable, my code has gotten messy and I'm about to do a rewrite, what are some ways of implementing this feature? is a cyclical buffer the correct way to do this? I wasn't sure how to place the constraints on the functions that traverse up and down the buffer. I'm more so looking for ideas and simple resources/implementations of the desired feature.


Solution

  • As requested, here is how you can implement a stack solution in your code. I've not found use for structure members front, rear, view but I left them intact in the structure. Perhaps my addition of global variable stack_top replaces the intent of one or two of those structure members?

    I'm not a fan of globals, but a stack pointer (stack_top) needs to be shared by two functions, so here we are. You can certainly code it differently if a global is too horrible. It can certainly be added as a member of GT_SCQueue, and that is an easy mod, yes?

    Tested, runnable code is here: https://godbolt.org/z/bfr4bfr5o

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    
    #define GT_SCQUEUE_SIZE 1000
    
    /* Only one modification to the structure ... */
    typedef struct {
        char *buffer[GT_SCQUEUE_SIZE]; /* ... here. */
        int front;  /* unused */
        int rear;   /* unused */
        int view;   /* unused */
    } GT_SCQueue;
    
    
    GT_SCQueue scq = {{0}, -1, -1, 0};
    int stack_top = -1;
    
    /*---------------------------------------------------------------------------------------------
        GT_SCQueue_Push()
    
        Places the item in param string onto the stack.
    *---------------------------------------------------------------------------------------------*/
    int GT_SCQueue_Push(GT_SCQueue* scq, const char* string) 
    {
        if(stack_top < GT_SCQUEUE_SIZE-1)
        {
            stack_top++;
        }
        else 
        {
            printf("Stack Overflow items: %d  Max: %d .... moving 1st item out of stack.\n", stack_top+1, GT_SCQUEUE_SIZE);
            return 0;
        }
        /*Add new item on the right */
        scq->buffer[stack_top] = strdup(string);
    
        if(!scq->buffer[stack_top])
        {
            printf("GT_SCQueue_Push() memory error!\n");
            return 0;
        }
        printf("GT_SCQueue_Push() added element [%s].  Elements on the stack: %d  Available: %d\n", 
                string, stack_top+1, GT_SCQUEUE_SIZE-(stack_top+1) );
    
        /* Return success */
        return 1;
    }
    
    
    /*---------------------------------------------------------------------------------------------
        GT_SCQueue_Pop()
    
        Pops an item off the stack and into param string.
    *---------------------------------------------------------------------------------------------*/
    int GT_SCQueue_Pop(GT_SCQueue* scq, char *string) 
    {
        if(stack_top < 0)
        {
            printf("UNDO Stack Underflow -- index: %d", stack_top);
            return 0;
        }
        strcpy(string, scq->buffer[stack_top] );
        free(scq->buffer[stack_top]);
        scq->buffer[stack_top] = NULL;
        printf("GT_SCQueue_Pop() popped element index: %d of string [%s]\n", stack_top, string);
        stack_top--;
        return 1;
    }
    
    int main() 
    {
    char b1[20];
    
        GT_SCQueue_Push(&scq, "Hello You!");
        GT_SCQueue_Pop(&scq, b1);
        printf("Report from main() -- Popped [%s]\n", b1);
    }
    

    Output:

        T_SCQueue_Push() added element [Hello You!].  Elements on the stack: 1  Available: 999
        GT_SCQueue_Pop() popped element index: 0 of string [Hello You!]
        Report from main() -- Popped [Hello You!]