Skip to content

Commit b98e210

Browse files
committed
linenoise.cpp refactoring
More RAII mainly Signed-off-by: Eric Curtin <[email protected]>
1 parent b9daaff commit b98e210

File tree

2 files changed

+128
-85
lines changed

2 files changed

+128
-85
lines changed

examples/run/linenoise.cpp/linenoise.cpp

Lines changed: 110 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,26 @@
103103
*
104104
*/
105105

106-
#include <termios.h>
107-
#include <unistd.h>
108-
#include <stdlib.h>
109-
#include <stdio.h>
110-
#include <errno.h>
111-
#include <string.h>
112-
#include <stdlib.h>
113-
#include <ctype.h>
114-
#include <sys/stat.h>
115-
#include <sys/types.h>
116-
#include <sys/ioctl.h>
117-
#include <unistd.h>
118-
#include <vector>
119-
#include "linenoise.h"
120-
121-
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
122-
#define LINENOISE_MAX_LINE 4096
123-
static std::vector<const char*> unsupported_term = {"dumb","cons25","emacs",nullptr};
106+
# include "linenoise.h"
107+
108+
# include <ctype.h>
109+
# include <errno.h>
110+
# include <stdio.h>
111+
# include <string.h>
112+
# include <sys/file.h>
113+
# include <sys/ioctl.h>
114+
# include <sys/stat.h>
115+
# include <sys/types.h>
116+
# include <termios.h>
117+
# include <unistd.h>
118+
119+
# include <memory>
120+
# include <string>
121+
# include <vector>
122+
123+
# define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
124+
# define LINENOISE_MAX_LINE 4096
125+
static std::vector<const char *> unsupported_term = { "dumb", "cons25", "emacs" };
124126
static linenoiseCompletionCallback *completionCallback = NULL;
125127
static linenoiseHintsCallback *hintsCallback = NULL;
126128
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
@@ -166,21 +168,58 @@ int linenoiseHistoryAdd(const char *line);
166168
#define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both.
167169
static void refreshLine(struct linenoiseState *l);
168170

171+
class File {
172+
public:
173+
FILE * file = nullptr;
174+
175+
FILE * open(const std::string & filename, const char * mode) {
176+
file = fopen(filename.c_str(), mode);
177+
178+
return file;
179+
}
180+
181+
int lock() {
182+
if (file) {
183+
fd = fileno(file);
184+
if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
185+
fd = -1;
186+
187+
return 1;
188+
}
189+
}
190+
191+
return 0;
192+
}
193+
194+
~File() {
195+
if (fd >= 0) {
196+
flock(fd, LOCK_UN);
197+
}
198+
199+
if (file) {
200+
fclose(file);
201+
}
202+
}
203+
204+
private:
205+
int fd = -1;
206+
};
207+
169208
__attribute__((format(printf, 1, 2)))
170209
/* Debugging function. */
171210
#if 0
172211
static void lndebug(const char *fmt, ...) {
173-
static FILE *lndebug_fp = NULL;
174-
if (lndebug_fp == NULL) {
175-
lndebug_fp = fopen("/tmp/lndebug.txt", "a");
212+
static File file;
213+
if (file.file == nullptr) {
214+
file.open("/tmp/lndebug.txt", "a");
176215
}
177216

178-
if (lndebug_fp != NULL) {
217+
if (file.file != nullptr) {
179218
va_list args;
180219
va_start(args, fmt);
181-
vfprintf(lndebug_fp, fmt, args);
220+
vfprintf(file.file, fmt, args);
182221
va_end(args);
183-
fflush(lndebug_fp);
222+
fflush(file.file);
184223
}
185224
}
186225
#else
@@ -213,8 +252,11 @@ void linenoiseSetMultiLine(int ml) {
213252
static int isUnsupportedTerm(void) {
214253
char *term = getenv("TERM");
215254
if (term == NULL) return 0;
216-
for (int j = 0; unsupported_term[j]; ++j)
217-
if (!strcasecmp(term, unsupported_term[j])) return 1;
255+
for (size_t j = 0; j < unsupported_term.size(); ++j) {
256+
if (!strcasecmp(term, unsupported_term[j])) {
257+
return 1;
258+
}
259+
}
218260
return 0;
219261
}
220262

@@ -334,17 +376,6 @@ static void linenoiseBeep(void) {
334376
fflush(stderr);
335377
}
336378

337-
/* ============================== Completion ================================ */
338-
339-
/* Free a list of completion option populated by linenoiseAddCompletion(). */
340-
static void freeCompletions(linenoiseCompletions *lc) {
341-
size_t i;
342-
for (i = 0; i < lc->len; i++)
343-
free(lc->cvec[i]);
344-
if (lc->cvec != NULL)
345-
free(lc->cvec);
346-
}
347-
348379
/* Called by completeLine() and linenoiseShow() to render the current
349380
* edited line with the proposed completion. If the current completion table
350381
* is already available, it is passed as second argument, otherwise the
@@ -353,9 +384,9 @@ static void freeCompletions(linenoiseCompletions *lc) {
353384
* Flags are the same as refreshLine*(), that is REFRESH_* macros. */
354385
static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseCompletions *lc, int flags) {
355386
/* Obtain the table of completions if the caller didn't provide one. */
356-
linenoiseCompletions ctable = { 0, NULL };
387+
linenoiseCompletions ctable;
357388
if (lc == NULL) {
358-
completionCallback(ls->buf,&ctable);
389+
completionCallback(ls->buf, &ctable);
359390
lc = &ctable;
360391
}
361392

@@ -364,16 +395,17 @@ static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseComple
364395
struct linenoiseState saved = *ls;
365396
ls->len = ls->pos = strlen(lc->cvec[ls->completion_idx]);
366397
ls->buf = lc->cvec[ls->completion_idx];
367-
refreshLineWithFlags(ls,flags);
398+
refreshLineWithFlags(ls, flags);
368399
ls->len = saved.len;
369400
ls->pos = saved.pos;
370401
ls->buf = saved.buf;
371402
} else {
372-
refreshLineWithFlags(ls,flags);
403+
refreshLineWithFlags(ls, flags);
373404
}
374405

375-
/* Free the completions table if needed. */
376-
if (lc != &ctable) freeCompletions(&ctable);
406+
if (lc == &ctable) {
407+
ctable.to_free = false;
408+
}
377409
}
378410

379411
/* This is an helper function for linenoiseEdit*() and is called when the
@@ -391,11 +423,11 @@ static void refreshLineWithCompletion(struct linenoiseState *ls, linenoiseComple
391423
* possible completions, and the caller should read for the next characters
392424
* from stdin. */
393425
static int completeLine(struct linenoiseState *ls, int keypressed) {
394-
linenoiseCompletions lc = { 0, NULL };
426+
linenoiseCompletions lc;
395427
int nwritten;
396428
char c = keypressed;
397429

398-
completionCallback(ls->buf,&lc);
430+
completionCallback(ls->buf, &lc);
399431
if (lc.len == 0) {
400432
linenoiseBeep();
401433
ls->in_completion = 0;
@@ -406,7 +438,7 @@ static int completeLine(struct linenoiseState *ls, int keypressed) {
406438
ls->in_completion = 1;
407439
ls->completion_idx = 0;
408440
} else {
409-
ls->completion_idx = (ls->completion_idx+1) % (lc.len+1);
441+
ls->completion_idx = (ls->completion_idx + 1) % (lc.len + 1);
410442
if (ls->completion_idx == lc.len) linenoiseBeep();
411443
}
412444
c = 0;
@@ -420,8 +452,7 @@ static int completeLine(struct linenoiseState *ls, int keypressed) {
420452
default:
421453
/* Update buffer and return */
422454
if (ls->completion_idx < lc.len) {
423-
nwritten = snprintf(ls->buf,ls->buflen,"%s",
424-
lc.cvec[ls->completion_idx]);
455+
nwritten = snprintf(ls->buf, ls->buflen, "%s", lc.cvec[ls->completion_idx]);
425456
ls->len = ls->pos = nwritten;
426457
}
427458
ls->in_completion = 0;
@@ -430,13 +461,12 @@ static int completeLine(struct linenoiseState *ls, int keypressed) {
430461

431462
/* Show completion or original buffer */
432463
if (ls->in_completion && ls->completion_idx < lc.len) {
433-
refreshLineWithCompletion(ls,&lc,REFRESH_ALL);
464+
refreshLineWithCompletion(ls, &lc, REFRESH_ALL);
434465
} else {
435466
refreshLine(ls);
436467
}
437468
}
438469

439-
freeCompletions(&lc);
440470
return c; /* Return last read character */
441471
}
442472

@@ -462,19 +492,20 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
462492
* user typed <tab>. See the example.c source code for a very easy to
463493
* understand example. */
464494
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
465-
size_t len = strlen(str);
466-
char *copy, **cvec;
467-
468-
copy = (char*) malloc(len + 1);
469-
if (copy == NULL) return;
470-
memcpy(copy,str,len+1);
471-
cvec = (char**) realloc(lc->cvec,sizeof(char*)*(lc->len+1));
472-
if (cvec == NULL) {
473-
free(copy);
495+
const size_t len = strlen(str);
496+
auto copy = std::make_unique<char[]>(len + 1);
497+
if (!copy) {
474498
return;
475499
}
500+
501+
memcpy(copy.get(), str, len + 1);
502+
char ** cvec = static_cast<char **>(std::realloc(lc->cvec, sizeof(char *) * (lc->len + 1)));
503+
if (cvec == nullptr) {
504+
return;
505+
}
506+
476507
lc->cvec = cvec;
477-
lc->cvec[lc->len++] = copy;
508+
lc->cvec[lc->len++] = copy.release();
478509
}
479510

480511
/* =========================== Line editing ================================= */
@@ -486,6 +517,8 @@ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
486517
struct abuf {
487518
char *b;
488519
int len;
520+
521+
~abuf() { free(b); }
489522
};
490523

491524
static void abInit(struct abuf *ab) {
@@ -502,10 +535,6 @@ static void abAppend(struct abuf *ab, const char *s, int len) {
502535
ab->len += len;
503536
}
504537

505-
static void abFree(struct abuf *ab) {
506-
free(ab->b);
507-
}
508-
509538
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
510539
* to the right of the prompt. */
511540
static void refreshShowHints(struct abuf * ab, struct linenoiseState * l, int plen) {
@@ -584,8 +613,7 @@ static void refreshSingleLine(struct linenoiseState *l, int flags) {
584613
abAppend(&ab,seq,strlen(seq));
585614
}
586615

587-
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
588-
abFree(&ab);
616+
(void) !write(fd, ab.b, ab.len); /* Can't recover from write error. */
589617
}
590618

591619
/* Multi line low level line refresh.
@@ -685,8 +713,7 @@ static void refreshMultiLine(struct linenoiseState *l, int flags) {
685713
lndebug("\n");
686714
l->oldpos = l->pos;
687715

688-
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
689-
abFree(&ab);
716+
(void) !write(fd, ab.b, ab.len); /* Can't recover from write error. */
690717
}
691718

692719
/* Calls the two low level functions refreshSingleLine() or
@@ -1313,16 +1340,17 @@ int linenoiseHistorySetMaxLen(int len) {
13131340
* otherwise -1 is returned. */
13141341
int linenoiseHistorySave(const char *filename) {
13151342
mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
1316-
FILE *fp;
1317-
int j;
1318-
1319-
fp = fopen(filename,"w");
1343+
File file;
1344+
file.open(filename, "w");
13201345
umask(old_umask);
1321-
if (fp == NULL) return -1;
1346+
if (file.file == NULL) {
1347+
return -1;
1348+
}
13221349
chmod(filename,S_IRUSR|S_IWUSR);
1323-
for (j = 0; j < history_len; j++)
1324-
fprintf(fp,"%s\n",history[j]);
1325-
fclose(fp);
1350+
for (int j = 0; j < history_len; ++j) {
1351+
fprintf(file.file, "%s\n", history[j]);
1352+
}
1353+
13261354
return 0;
13271355
}
13281356

@@ -1332,20 +1360,21 @@ int linenoiseHistorySave(const char *filename) {
13321360
* If the file exists and the operation succeeded 0 is returned, otherwise
13331361
* on error -1 is returned. */
13341362
int linenoiseHistoryLoad(const char *filename) {
1335-
FILE *fp = fopen(filename,"r");
1363+
File file;
1364+
file.open(filename, "r");
13361365
char buf[LINENOISE_MAX_LINE];
1366+
if (file.file == NULL) {
1367+
return -1;
1368+
}
13371369

1338-
if (fp == NULL) return -1;
1339-
1340-
while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
1370+
while (fgets(buf, LINENOISE_MAX_LINE, file.file) != NULL) {
13411371
char *p;
13421372

13431373
p = strchr(buf,'\r');
13441374
if (!p) p = strchr(buf,'\n');
13451375
if (p) *p = '\0';
13461376
linenoiseHistoryAdd(buf);
13471377
}
1348-
fclose(fp);
13491378
return 0;
13501379
}
13511380
#endif

examples/run/linenoise.cpp/linenoise.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ extern "C" {
4545
#endif
4646

4747
#include <stddef.h> /* For size_t. */
48+
#include <stdlib.h>
4849

4950
extern const char *linenoiseEditMore;
5051

@@ -69,10 +70,23 @@ struct linenoiseState {
6970
int history_index; /* The history index we are currently editing. */
7071
};
7172

72-
typedef struct linenoiseCompletions {
73-
size_t len;
74-
char **cvec;
75-
} linenoiseCompletions;
73+
struct linenoiseCompletions {
74+
size_t len = 0;
75+
char ** cvec = nullptr;
76+
bool to_free = true;
77+
78+
~linenoiseCompletions() {
79+
if (!to_free) {
80+
return;
81+
}
82+
83+
for (size_t i = 0; i < len; ++i) {
84+
free(cvec[i]);
85+
}
86+
87+
free(cvec);
88+
}
89+
};
7690

7791
/* Non blocking API. */
7892
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt);

0 commit comments

Comments
 (0)