mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-09 04:49:10 +00:00
269 lines
7.5 KiB
C
269 lines
7.5 KiB
C
/* ----------------------------------------------------------------------------
|
|
Copyright (c) 2021, Daan Leijen
|
|
This is free software; you can redistribute it and/or modify it
|
|
under the terms of the MIT License. A copy of the license can be
|
|
found in the "LICENSE" file at the root of this distribution.
|
|
-----------------------------------------------------------------------------*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "../include/isocline.h"
|
|
#include "common.h"
|
|
#include "history.h"
|
|
#include "stringbuf.h"
|
|
|
|
#define IC_MAX_HISTORY (200)
|
|
|
|
struct history_s {
|
|
ssize_t count; // current number of entries in use
|
|
ssize_t len; // size of elems
|
|
const char** elems; // history items (up to count)
|
|
const char* fname; // history file
|
|
alloc_t* mem;
|
|
bool allow_duplicates; // allow duplicate entries?
|
|
};
|
|
|
|
ic_private history_t* history_new(alloc_t* mem) {
|
|
history_t* h = mem_zalloc_tp(mem,history_t);
|
|
h->mem = mem;
|
|
return h;
|
|
}
|
|
|
|
ic_private void history_free(history_t* h) {
|
|
if (h == NULL) return;
|
|
history_clear(h);
|
|
if (h->len > 0) {
|
|
mem_free( h->mem, h->elems );
|
|
h->elems = NULL;
|
|
h->len = 0;
|
|
}
|
|
mem_free(h->mem, h->fname);
|
|
h->fname = NULL;
|
|
mem_free(h->mem, h); // free ourselves
|
|
}
|
|
|
|
ic_private bool history_enable_duplicates( history_t* h, bool enable ) {
|
|
bool prev = h->allow_duplicates;
|
|
h->allow_duplicates = enable;
|
|
return prev;
|
|
}
|
|
|
|
ic_private ssize_t history_count(const history_t* h) {
|
|
return h->count;
|
|
}
|
|
|
|
//-------------------------------------------------------------
|
|
// push/clear
|
|
//-------------------------------------------------------------
|
|
|
|
ic_private bool history_update( history_t* h, const char* entry ) {
|
|
if (entry==NULL) return false;
|
|
history_remove_last(h);
|
|
history_push(h,entry);
|
|
//debug_msg("history: update: with %s; now at %s\n", entry, history_get(h,0));
|
|
return true;
|
|
}
|
|
|
|
static void history_delete_at( history_t* h, ssize_t idx ) {
|
|
if (idx < 0 || idx >= h->count) return;
|
|
mem_free(h->mem, h->elems[idx]);
|
|
for(ssize_t i = idx+1; i < h->count; i++) {
|
|
h->elems[i-1] = h->elems[i];
|
|
}
|
|
h->count--;
|
|
}
|
|
|
|
ic_private bool history_push( history_t* h, const char* entry ) {
|
|
if (h->len <= 0 || entry==NULL) return false;
|
|
// remove any older duplicate
|
|
if (!h->allow_duplicates) {
|
|
for( int i = 0; i < h->count; i++) {
|
|
if (strcmp(h->elems[i],entry) == 0) {
|
|
history_delete_at(h,i);
|
|
}
|
|
}
|
|
}
|
|
// insert at front
|
|
if (h->count == h->len) {
|
|
// delete oldest entry
|
|
history_delete_at(h,0);
|
|
}
|
|
assert(h->count < h->len);
|
|
h->elems[h->count] = mem_strdup(h->mem,entry);
|
|
h->count++;
|
|
return true;
|
|
}
|
|
|
|
|
|
static void history_remove_last_n( history_t* h, ssize_t n ) {
|
|
if (n <= 0) return;
|
|
if (n > h->count) n = h->count;
|
|
for( ssize_t i = h->count - n; i < h->count; i++) {
|
|
mem_free( h->mem, h->elems[i] );
|
|
}
|
|
h->count -= n;
|
|
assert(h->count >= 0);
|
|
}
|
|
|
|
ic_private void history_remove_last(history_t* h) {
|
|
history_remove_last_n(h,1);
|
|
}
|
|
|
|
ic_private void history_clear(history_t* h) {
|
|
history_remove_last_n( h, h->count );
|
|
}
|
|
|
|
ic_private const char* history_get( const history_t* h, ssize_t n ) {
|
|
if (n < 0 || n >= h->count) return NULL;
|
|
return h->elems[h->count - n - 1];
|
|
}
|
|
|
|
ic_private bool history_search( const history_t* h, ssize_t from /*including*/, const char* search, bool backward, ssize_t* hidx, ssize_t* hpos ) {
|
|
const char* p = NULL;
|
|
ssize_t i;
|
|
if (backward) {
|
|
for( i = from; i < h->count; i++ ) {
|
|
p = strstr( history_get(h,i), search);
|
|
if (p != NULL) break;
|
|
}
|
|
}
|
|
else {
|
|
for( i = from; i >= 0; i-- ) {
|
|
p = strstr( history_get(h,i), search);
|
|
if (p != NULL) break;
|
|
}
|
|
}
|
|
if (p == NULL) return false;
|
|
if (hidx != NULL) *hidx = i;
|
|
if (hpos != NULL) *hpos = (p - history_get(h,i));
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------
|
|
//
|
|
//-------------------------------------------------------------
|
|
|
|
ic_private void history_load_from(history_t* h, const char* fname, long max_entries ) {
|
|
history_clear(h);
|
|
h->fname = mem_strdup(h->mem,fname);
|
|
if (max_entries == 0) {
|
|
assert(h->elems == NULL);
|
|
return;
|
|
}
|
|
if (max_entries < 0 || max_entries > IC_MAX_HISTORY) max_entries = IC_MAX_HISTORY;
|
|
h->elems = (const char**)mem_zalloc_tp_n(h->mem, char*, max_entries );
|
|
if (h->elems == NULL) return;
|
|
h->len = max_entries;
|
|
history_load(h);
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// save/load history to file
|
|
//-------------------------------------------------------------
|
|
|
|
static char from_xdigit( int c ) {
|
|
if (c >= '0' && c <= '9') return (char)(c - '0');
|
|
if (c >= 'A' && c <= 'F') return (char)(10 + (c - 'A'));
|
|
if (c >= 'a' && c <= 'f') return (char)(10 + (c - 'a'));
|
|
return 0;
|
|
}
|
|
|
|
static char to_xdigit( uint8_t c ) {
|
|
if (c <= 9) return ((char)c + '0');
|
|
if (c >= 10 && c <= 15) return ((char)c - 10 + 'A');
|
|
return '0';
|
|
}
|
|
|
|
static bool ic_isxdigit( int c ) {
|
|
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'));
|
|
}
|
|
|
|
static bool history_read_entry( history_t* h, FILE* f, stringbuf_t* sbuf ) {
|
|
sbuf_clear(sbuf);
|
|
while( !feof(f)) {
|
|
int c = fgetc(f);
|
|
if (c == EOF || c == '\n') break;
|
|
if (c == '\\') {
|
|
c = fgetc(f);
|
|
if (c == 'n') { sbuf_append(sbuf,"\n"); }
|
|
else if (c == 'r') { /* ignore */ } // sbuf_append(sbuf,"\r");
|
|
else if (c == 't') { sbuf_append(sbuf,"\t"); }
|
|
else if (c == '\\') { sbuf_append(sbuf,"\\"); }
|
|
else if (c == 'x') {
|
|
int c1 = fgetc(f);
|
|
int c2 = fgetc(f);
|
|
if (ic_isxdigit(c1) && ic_isxdigit(c2)) {
|
|
char chr = from_xdigit(c1)*16 + from_xdigit(c2);
|
|
sbuf_append_char(sbuf,chr);
|
|
}
|
|
else return false;
|
|
}
|
|
else return false;
|
|
}
|
|
else sbuf_append_char(sbuf,(char)c);
|
|
}
|
|
if (sbuf_len(sbuf)==0 || sbuf_string(sbuf)[0] == '#') return true;
|
|
return history_push(h, sbuf_string(sbuf));
|
|
}
|
|
|
|
static bool history_write_entry( const char* entry, FILE* f, stringbuf_t* sbuf ) {
|
|
sbuf_clear(sbuf);
|
|
//debug_msg("history: write: %s\n", entry);
|
|
while( entry != NULL && *entry != 0 ) {
|
|
char c = *entry++;
|
|
if (c == '\\') { sbuf_append(sbuf,"\\\\"); }
|
|
else if (c == '\n') { sbuf_append(sbuf,"\\n"); }
|
|
else if (c == '\r') { /* ignore */ } // sbuf_append(sbuf,"\\r"); }
|
|
else if (c == '\t') { sbuf_append(sbuf,"\\t"); }
|
|
else if (c < ' ' || c > '~' || c == '#') {
|
|
char c1 = to_xdigit( (uint8_t)c / 16 );
|
|
char c2 = to_xdigit( (uint8_t)c % 16 );
|
|
sbuf_append(sbuf,"\\x");
|
|
sbuf_append_char(sbuf,c1);
|
|
sbuf_append_char(sbuf,c2);
|
|
}
|
|
else sbuf_append_char(sbuf,c);
|
|
}
|
|
//debug_msg("history: write buf: %s\n", sbuf_string(sbuf));
|
|
|
|
if (sbuf_len(sbuf) > 0) {
|
|
sbuf_append(sbuf,"\n");
|
|
fputs(sbuf_string(sbuf),f);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ic_private void history_load( history_t* h ) {
|
|
if (h->fname == NULL) return;
|
|
FILE* f = fopen(h->fname, "r");
|
|
if (f == NULL) return;
|
|
stringbuf_t* sbuf = sbuf_new(h->mem);
|
|
if (sbuf != NULL) {
|
|
while (!feof(f)) {
|
|
if (!history_read_entry(h,f,sbuf)) break; // error
|
|
}
|
|
sbuf_free(sbuf);
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
ic_private void history_save( const history_t* h ) {
|
|
if (h->fname == NULL) return;
|
|
FILE* f = fopen(h->fname, "w");
|
|
if (f == NULL) return;
|
|
#ifndef _WIN32
|
|
chmod(h->fname,S_IRUSR|S_IWUSR);
|
|
#endif
|
|
stringbuf_t* sbuf = sbuf_new(h->mem);
|
|
if (sbuf != NULL) {
|
|
for( int i = 0; i < h->count; i++ ) {
|
|
if (!history_write_entry(h->elems[i],f,sbuf)) break; // error
|
|
}
|
|
sbuf_free(sbuf);
|
|
}
|
|
fclose(f);
|
|
}
|