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" };
124126static linenoiseCompletionCallback *completionCallback = NULL ;
125127static linenoiseHintsCallback *hintsCallback = NULL ;
126128static linenoiseFreeHintsCallback *freeHintsCallback = NULL ;
@@ -166,21 +168,58 @@ int linenoiseHistoryAdd(const char *line);
166168#define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both.
167169static 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
172211static 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) {
213252static 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. */
354385static 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. */
393425static 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. */
464494void 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) {
486517struct abuf {
487518 char *b;
488519 int len;
520+
521+ ~abuf () { free (b); }
489522};
490523
491524static 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. */
511540static 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. */
13141341int 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. */
13341362int 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
0 commit comments