Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/extern/isocline/src/history.c
2727 views
1
/* ----------------------------------------------------------------------------
2
Copyright (c) 2021, Daan Leijen
3
This is free software; you can redistribute it and/or modify it
4
under the terms of the MIT License. A copy of the license can be
5
found in the "LICENSE" file at the root of this distribution.
6
-----------------------------------------------------------------------------*/
7
#include <stdio.h>
8
#include <string.h>
9
#include <sys/stat.h>
10
11
#include "../include/isocline.h"
12
#include "common.h"
13
#include "history.h"
14
#include "stringbuf.h"
15
16
#define IC_MAX_HISTORY (200)
17
18
struct history_s {
19
ssize_t count; // current number of entries in use
20
ssize_t len; // size of elems
21
const char** elems; // history items (up to count)
22
const char* fname; // history file
23
alloc_t* mem;
24
bool allow_duplicates; // allow duplicate entries?
25
};
26
27
ic_private history_t* history_new(alloc_t* mem) {
28
history_t* h = mem_zalloc_tp(mem,history_t);
29
h->mem = mem;
30
return h;
31
}
32
33
ic_private void history_free(history_t* h) {
34
if (h == NULL) return;
35
history_clear(h);
36
if (h->len > 0) {
37
mem_free( h->mem, h->elems );
38
h->elems = NULL;
39
h->len = 0;
40
}
41
mem_free(h->mem, h->fname);
42
h->fname = NULL;
43
mem_free(h->mem, h); // free ourselves
44
}
45
46
ic_private bool history_enable_duplicates( history_t* h, bool enable ) {
47
bool prev = h->allow_duplicates;
48
h->allow_duplicates = enable;
49
return prev;
50
}
51
52
ic_private ssize_t history_count(const history_t* h) {
53
return h->count;
54
}
55
56
//-------------------------------------------------------------
57
// push/clear
58
//-------------------------------------------------------------
59
60
ic_private bool history_update( history_t* h, const char* entry ) {
61
if (entry==NULL) return false;
62
history_remove_last(h);
63
history_push(h,entry);
64
//debug_msg("history: update: with %s; now at %s\n", entry, history_get(h,0));
65
return true;
66
}
67
68
static void history_delete_at( history_t* h, ssize_t idx ) {
69
if (idx < 0 || idx >= h->count) return;
70
mem_free(h->mem, h->elems[idx]);
71
for(ssize_t i = idx+1; i < h->count; i++) {
72
h->elems[i-1] = h->elems[i];
73
}
74
h->count--;
75
}
76
77
ic_private bool history_push( history_t* h, const char* entry ) {
78
if (h->len <= 0 || entry==NULL) return false;
79
// remove any older duplicate
80
if (!h->allow_duplicates) {
81
for( int i = 0; i < h->count; i++) {
82
if (strcmp(h->elems[i],entry) == 0) {
83
history_delete_at(h,i);
84
}
85
}
86
}
87
// insert at front
88
if (h->count == h->len) {
89
// delete oldest entry
90
history_delete_at(h,0);
91
}
92
assert(h->count < h->len);
93
h->elems[h->count] = mem_strdup(h->mem,entry);
94
h->count++;
95
return true;
96
}
97
98
99
static void history_remove_last_n( history_t* h, ssize_t n ) {
100
if (n <= 0) return;
101
if (n > h->count) n = h->count;
102
for( ssize_t i = h->count - n; i < h->count; i++) {
103
mem_free( h->mem, h->elems[i] );
104
}
105
h->count -= n;
106
assert(h->count >= 0);
107
}
108
109
ic_private void history_remove_last(history_t* h) {
110
history_remove_last_n(h,1);
111
}
112
113
ic_private void history_clear(history_t* h) {
114
history_remove_last_n( h, h->count );
115
}
116
117
ic_private const char* history_get( const history_t* h, ssize_t n ) {
118
if (n < 0 || n >= h->count) return NULL;
119
return h->elems[h->count - n - 1];
120
}
121
122
ic_private bool history_search( const history_t* h, ssize_t from /*including*/, const char* search, bool backward, ssize_t* hidx, ssize_t* hpos ) {
123
const char* p = NULL;
124
ssize_t i;
125
if (backward) {
126
for( i = from; i < h->count; i++ ) {
127
p = strstr( history_get(h,i), search);
128
if (p != NULL) break;
129
}
130
}
131
else {
132
for( i = from; i >= 0; i-- ) {
133
p = strstr( history_get(h,i), search);
134
if (p != NULL) break;
135
}
136
}
137
if (p == NULL) return false;
138
if (hidx != NULL) *hidx = i;
139
if (hpos != NULL) *hpos = (p - history_get(h,i));
140
return true;
141
}
142
143
//-------------------------------------------------------------
144
//
145
//-------------------------------------------------------------
146
147
ic_private void history_load_from(history_t* h, const char* fname, long max_entries ) {
148
history_clear(h);
149
h->fname = mem_strdup(h->mem,fname);
150
if (max_entries == 0) {
151
assert(h->elems == NULL);
152
return;
153
}
154
if (max_entries < 0 || max_entries > IC_MAX_HISTORY) max_entries = IC_MAX_HISTORY;
155
h->elems = (const char**)mem_zalloc_tp_n(h->mem, char*, max_entries );
156
if (h->elems == NULL) return;
157
h->len = max_entries;
158
history_load(h);
159
}
160
161
162
163
164
//-------------------------------------------------------------
165
// save/load history to file
166
//-------------------------------------------------------------
167
168
static char from_xdigit( int c ) {
169
if (c >= '0' && c <= '9') return (char)(c - '0');
170
if (c >= 'A' && c <= 'F') return (char)(10 + (c - 'A'));
171
if (c >= 'a' && c <= 'f') return (char)(10 + (c - 'a'));
172
return 0;
173
}
174
175
static char to_xdigit( uint8_t c ) {
176
if (c <= 9) return ((char)c + '0');
177
if (c >= 10 && c <= 15) return ((char)c - 10 + 'A');
178
return '0';
179
}
180
181
static bool ic_isxdigit( int c ) {
182
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'));
183
}
184
185
static bool history_read_entry( history_t* h, FILE* f, stringbuf_t* sbuf ) {
186
sbuf_clear(sbuf);
187
while( !feof(f)) {
188
int c = fgetc(f);
189
if (c == EOF || c == '\n') break;
190
if (c == '\\') {
191
c = fgetc(f);
192
if (c == 'n') { sbuf_append(sbuf,"\n"); }
193
else if (c == 'r') { /* ignore */ } // sbuf_append(sbuf,"\r");
194
else if (c == 't') { sbuf_append(sbuf,"\t"); }
195
else if (c == '\\') { sbuf_append(sbuf,"\\"); }
196
else if (c == 'x') {
197
int c1 = fgetc(f);
198
int c2 = fgetc(f);
199
if (ic_isxdigit(c1) && ic_isxdigit(c2)) {
200
char chr = from_xdigit(c1)*16 + from_xdigit(c2);
201
sbuf_append_char(sbuf,chr);
202
}
203
else return false;
204
}
205
else return false;
206
}
207
else sbuf_append_char(sbuf,(char)c);
208
}
209
if (sbuf_len(sbuf)==0 || sbuf_string(sbuf)[0] == '#') return true;
210
return history_push(h, sbuf_string(sbuf));
211
}
212
213
static bool history_write_entry( const char* entry, FILE* f, stringbuf_t* sbuf ) {
214
sbuf_clear(sbuf);
215
//debug_msg("history: write: %s\n", entry);
216
while( entry != NULL && *entry != 0 ) {
217
char c = *entry++;
218
if (c == '\\') { sbuf_append(sbuf,"\\\\"); }
219
else if (c == '\n') { sbuf_append(sbuf,"\\n"); }
220
else if (c == '\r') { /* ignore */ } // sbuf_append(sbuf,"\\r"); }
221
else if (c == '\t') { sbuf_append(sbuf,"\\t"); }
222
else if (c < ' ' || c > '~' || c == '#') {
223
char c1 = to_xdigit( (uint8_t)c / 16 );
224
char c2 = to_xdigit( (uint8_t)c % 16 );
225
sbuf_append(sbuf,"\\x");
226
sbuf_append_char(sbuf,c1);
227
sbuf_append_char(sbuf,c2);
228
}
229
else sbuf_append_char(sbuf,c);
230
}
231
//debug_msg("history: write buf: %s\n", sbuf_string(sbuf));
232
233
if (sbuf_len(sbuf) > 0) {
234
sbuf_append(sbuf,"\n");
235
fputs(sbuf_string(sbuf),f);
236
}
237
return true;
238
}
239
240
ic_private void history_load( history_t* h ) {
241
if (h->fname == NULL) return;
242
FILE* f = fopen(h->fname, "r");
243
if (f == NULL) return;
244
stringbuf_t* sbuf = sbuf_new(h->mem);
245
if (sbuf != NULL) {
246
while (!feof(f)) {
247
if (!history_read_entry(h,f,sbuf)) break; // error
248
}
249
sbuf_free(sbuf);
250
}
251
fclose(f);
252
}
253
254
ic_private void history_save( const history_t* h ) {
255
if (h->fname == NULL) return;
256
FILE* f = fopen(h->fname, "w");
257
if (f == NULL) return;
258
#ifndef _WIN32
259
chmod(h->fname,S_IRUSR|S_IWUSR);
260
#endif
261
stringbuf_t* sbuf = sbuf_new(h->mem);
262
if (sbuf != NULL) {
263
for( int i = 0; i < h->count; i++ ) {
264
if (!history_write_entry(h->elems[i],f,sbuf)) break; // error
265
}
266
sbuf_free(sbuf);
267
}
268
fclose(f);
269
}
270
271