From: porkchop@invalid.foo   
      
   /*   
    * mini_repl.c – Mini REPL with persistent history   
    * Michael Sanders - 2025 use as you see fit   
    *   
    * repl commands:   
    * .q quit   
    * .h show short help   
    * .c clear the screen   
    *   
    * Full persistent history (~/.mini_repl_history)   
    * via up/down keys, no history duplicates.   
    *   
    * Build: cc mini_repl.c -o mini_repl   
    *   
    */   
      
   #include    
   #include    
   #include    
   #include    
   #include    
   #include    
   #include    
      
   #define MAXLINE 1024   
   #define HISTORY_MAX 128   
   #define HISTFILE ".mini_repl_history"   
      
   static char *history[HISTORY_MAX];   
   static int hist_count = 0;   
   static int hist_index = -1;   
   static char hist_path[PATH_MAX];   
      
   /* -------- Helpers -------- */   
   static void clear_screen(void) {   
    fputs("\033[2J\033[H", stdout);   
    fflush(stdout);   
   }   
      
   static void load_history(void) {   
    const char *home = getenv("HOME");   
    if (home) snprintf(hist_path, sizeof(hist_path), "%s/%s", home, HISTFILE);   
    else strncpy(hist_path, HISTFILE, sizeof(hist_path) - 1);   
      
    FILE *fp = fopen(hist_path, "r");   
    if (!fp) return;   
      
    char line[MAXLINE];   
    while (fgets(line, sizeof(line), fp) && hist_count < HISTORY_MAX) {   
    line[strcspn(line, "\r\n")] = '\0';   
    if (*line) history[hist_count++] = strdup(line);   
    }   
    fclose(fp);   
   }   
      
   static void save_history(void) {   
    FILE *fp = fopen(hist_path, "w");   
    if (!fp) return;   
    for (int i = 0; i < hist_count; i++) fprintf(fp, "%s\n", history[i]);   
    fclose(fp);   
   }   
      
   static void add_history(const char *line) {   
    if (!line || !*line) return;   
    for (int i = 0; i < hist_count; i++) {   
    if (strcmp(history[i], line) == 0) {   
    free(history[i]);   
    for (int j = i; j < hist_count - 1; j++) history[j] = history[j +   
   1];   
    hist_count--;   
    break;   
    }   
    }   
    if (hist_count == HISTORY_MAX) {   
    free(history[0]);   
    memmove(&history[0], &history[1], sizeof(char*) * (HISTORY_MAX - 1));   
    hist_count--;   
    }   
    history[hist_count++] = strdup(line);   
   }   
      
   /* -------- REPL -------- */   
   int main(void) {   
    load_history();   
      
    struct termios orig, raw;   
    tcgetattr(STDIN_FILENO, &orig);   
    raw = orig;   
    raw.c_lflag &= ~(ICANON | ECHO);   
    tcsetattr(STDIN_FILENO, TCSANOW, &raw);   
      
    printf("Mini REPL (.h help, .c clear, .q quit)\n> ");   
    fflush(stdout);   
      
    char line[MAXLINE];   
    int pos = 0;   
      
    for (;;) {   
    unsigned char c;   
    if (read(STDIN_FILENO, &c, 1) != 1) break;   
      
    if (c == '\n' || c == '\r') {   
    line[pos] = '\0';   
      
    /* BLANK: erase current line and re-prompt */   
    if (pos == 0) {   
    printf("\033[2K\r> ");   
    fflush(stdout);   
    continue;   
    }   
      
    /* ERASE input line before any output */   
    printf("\033[2K\r");   
    fflush(stdout);   
      
    if (strcmp(line, ".q") == 0) {   
    add_history(line);   
    break;   
    } else if (strcmp(line, ".c") == 0) {   
    add_history(line);   
    clear_screen();   
    printf("> ");   
    } else if (strcmp(line, ".h") == 0) {   
    add_history(line);   
    printf("Commands:\n .q Quit\n .c Clear screen\n .h    
   Show help\n\n> ");   
    } else {   
    add_history(line);   
    printf("You typed: %s\n> ", line);   
    }   
      
    fflush(stdout);   
    pos = 0;   
    hist_index = hist_count;   
    continue;   
    }   
      
    if (c == 4) break; /* Ctrl+D */   
      
    if (c == 127 || c == '\b') { /* backspace */   
    if (pos > 0) {   
    pos--;   
    printf("\b \b");   
    fflush(stdout);   
    }   
    continue;   
    }   
      
    if (c == 27) { /* arrows */   
    unsigned char seq[2];   
    if (read(STDIN_FILENO, seq, 2) == 2 && seq[0] == '[') {   
    if (seq[1] == 'A') { /* up */   
    if (hist_index > 0) hist_index--;   
    if (hist_index >= 0 && hist_index < hist_count) {   
    printf("\033[2K\r> %s", history[hist_index]);   
    fflush(stdout);   
    strcpy(line, history[hist_index]);   
    pos = (int)strlen(line);   
    }   
    } else if (seq[1] == 'B') { /* down */   
    if (hist_index < hist_count - 1) hist_index++;   
    else hist_index = hist_count;   
    if (hist_index < hist_count) {   
    printf("\033[2K\r> %s", history[hist_index]);   
    strcpy(line, history[hist_index]);   
    pos = (int)strlen(line);   
    } else {   
    printf("\033[2K\r> ");   
    pos = 0;   
    line[0] = '\0';   
    }   
    fflush(stdout);   
    }   
    }   
    continue;   
    }   
      
    if (isprint(c) && pos < MAXLINE - 1) {   
    line[pos++] = c;   
    putchar(c);   
    fflush(stdout);   
    }   
    }   
      
    tcsetattr(STDIN_FILENO, TCSANOW, &orig);   
    save_history();   
    for (int i = 0; i < hist_count; i++) free(history[i]);   
    return 0;   
   }   
      
   --   
   :wq   
   Mike Sanders   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|