1 /* GNUPLOT - history.c */
2 
3 /*[
4  * Copyright 1986 - 1993, 1999, 2004   Thomas Williams, Colin Kelley
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 #include <stdio.h>
34 
35 #include "gp_hist.h"
36 
37 #include "alloc.h"
38 #include "plot.h"
39 #include "util.h"
40 
41 /* Public variables */
42 
43 int gnuplot_history_size = HISTORY_SIZE;
44 TBOOLEAN history_quiet = FALSE;
45 TBOOLEAN history_full = FALSE;
46 
47 
48 #if defined(READLINE)
49 
50 /* Built-in readline */
51 
52 struct hist *history = NULL;	/* last entry in the history list, no history yet */
53 struct hist *cur_entry = NULL;
54 int history_length = 0;		/* number of entries in history list */
55 int history_base = 1;
56 
57 
58 /* add line to the history */
59 void
add_history(char * line)60 add_history(char *line)
61 {
62     struct hist *entry;
63 
64     entry = (struct hist *) gp_alloc(sizeof(struct hist), "history");
65     entry->line = gp_strdup(line);
66     entry->data = NULL;
67 
68     entry->prev = history;
69     entry->next = NULL;
70     if (history != NULL)
71 	history->next = entry;
72     else
73 	cur_entry = entry;
74     history = entry;
75     history_length++;
76 }
77 
78 
79 /* write history to a file
80  */
81 int
write_history(char * filename)82 write_history(char *filename)
83 {
84     write_history_n(0, filename, "w");
85     return 0;
86 }
87 
88 
89 /* routine to read history entries from a file
90  */
91 void
read_history(char * filename)92 read_history(char *filename)
93 {
94 #ifdef GNUPLOT_HISTORY
95     gp_read_history(filename);
96 #endif
97 }
98 
99 
100 void
using_history(void)101 using_history(void)
102 {
103     /* Nothing to do. */
104 }
105 
106 
107 void
clear_history(void)108 clear_history(void)
109 {
110     HIST_ENTRY * entry = history;
111 
112     while (entry != NULL) {
113 	HIST_ENTRY * prev = entry->prev;
114 	free(entry->line);
115 	free(entry);
116 	entry = prev;
117     }
118 
119     history_length = 0;
120     cur_entry = NULL;
121     history = NULL;
122 }
123 
124 
125 int
where_history(void)126 where_history(void)
127 {
128     struct hist *entry = history;  /* last_entry */
129     int hist_index = history_length;
130 
131     if (entry == NULL)
132 	return 0;		/* no history yet */
133 
134     if (cur_entry == NULL)
135 	return history_length;
136 
137     /* find the current history entry and count backwards */
138     while ((entry->prev != NULL) && (entry != cur_entry)) {
139 	entry = entry->prev;
140 	hist_index--;
141     }
142 
143     if (hist_index > 0)
144 	hist_index--;
145 
146     return hist_index;
147 }
148 
149 
150 int
history_set_pos(int offset)151 history_set_pos(int offset)
152 {
153     struct hist *entry = history;  /* last_entry */
154     int hist_index = history_length - 1;
155 
156     if ((offset < 0) || (offset > history_length) || (history == NULL))
157 	return 0;
158 
159     if (offset == history_length) {
160 	cur_entry = NULL;
161 	return 1;
162     }
163 
164     /* seek backwards */
165     while (entry != NULL) {
166 	if (hist_index == offset) {
167 	    cur_entry = entry;
168 	    return 1;
169 	}
170 	entry = entry->prev;
171 	hist_index--;
172     }
173     return 0;
174 }
175 
176 
177 HIST_ENTRY *
history_get(int offset)178 history_get(int offset)
179 {
180     struct hist *entry = history;  /* last_entry */
181     int hist_index = history_length - 1;
182     int hist_ofs = offset - history_base;
183 
184    if ((hist_ofs < 0) || (hist_ofs >= history_length) || (history == NULL))
185 	return NULL;
186 
187     /* find the current history entry and count backwards */
188     /* seek backwards */
189     while (entry != NULL) {
190 	if (hist_index == hist_ofs)
191 	    return entry;
192 	entry = entry->prev;
193 	hist_index--;
194     }
195 
196     return NULL;
197 }
198 
199 
200 HIST_ENTRY *
current_history(void)201 current_history(void)
202 {
203     return cur_entry;
204 }
205 
206 
207 HIST_ENTRY *
previous_history(void)208 previous_history(void)
209 {
210     if (cur_entry == NULL)
211 	return (cur_entry = history);
212     if ((cur_entry != NULL) && (cur_entry->prev != NULL))
213 	return (cur_entry = cur_entry->prev);
214     else
215 	return NULL;
216 }
217 
218 
219 HIST_ENTRY *
next_history(void)220 next_history(void)
221 {
222     if (cur_entry != NULL)
223 	return (cur_entry = cur_entry->next);
224     else
225 	return NULL;
226 }
227 
228 
229 HIST_ENTRY *
replace_history_entry(int which,const char * line,histdata_t data)230 replace_history_entry(int which, const char *line, histdata_t data)
231 {
232     HIST_ENTRY * entry = history_get(which + 1);
233     HIST_ENTRY * prev_entry;
234 
235     if (entry == NULL)
236 	return NULL;
237 
238     /* save contents: allocate new entry */
239     prev_entry = (HIST_ENTRY *) malloc(sizeof(HIST_ENTRY));
240     if (entry != NULL) {
241 	memset(prev_entry, 0, sizeof(HIST_ENTRY));
242 	prev_entry->line = entry->line;
243 	prev_entry->data = entry->data;
244     }
245 
246     /* set new value */
247     entry->line = gp_strdup(line);
248     entry->data = data;
249 
250     return prev_entry;
251 }
252 
253 
254 HIST_ENTRY *
remove_history(int which)255 remove_history(int which)
256 {
257     HIST_ENTRY * entry;
258 
259     entry = history_get(which + history_base);
260     if (entry == NULL)
261 	return NULL;
262 
263     /* remove entry from chain */
264     if (entry->prev != NULL)
265 	entry->prev->next = entry->next;
266     if (entry->next != NULL)
267 	entry->next->prev = entry->prev;
268     else
269 	history = entry->prev; /* last entry */
270 
271     if (cur_entry == entry)
272 	cur_entry = entry->prev;
273 
274     /* adjust length */
275     history_length--;
276 
277     return entry;
278 }
279 #endif
280 
281 
282 #if defined(READLINE) || defined(HAVE_LIBEDITLINE)
283 histdata_t
free_history_entry(HIST_ENTRY * histent)284 free_history_entry(HIST_ENTRY *histent)
285 {
286     histdata_t data;
287 
288     if (histent == NULL)
289 	return NULL;
290 
291     data = histent->data;
292     free((void *)(histent->line));
293     free(histent);
294     return data;
295 }
296 #endif
297 
298 
299 #if defined(READLINE) || defined(HAVE_WINEDITLINE)
300 int
history_search(const char * string,int direction)301 history_search(const char *string, int direction)
302 {
303     int start;
304     HIST_ENTRY *entry;
305     /* Work-around for WinEditLine: */
306     int once = 1; /* ensure that we try seeking at least one position */
307     char * pos;
308 
309     start = where_history();
310     entry = current_history();
311     while (((entry != NULL) && entry->line != NULL) || once) {
312 	if ((entry != NULL) && (entry->line != NULL) && ((pos = strstr(entry->line, string)) != NULL))
313 	    return (pos - entry->line);
314 	if (direction < 0)
315 	    entry = previous_history();
316 	else
317 	    entry = next_history();
318 	once = 0;
319     }
320     /* not found */
321     history_set_pos(start);
322     return -1;
323 }
324 
325 
326 int
history_search_prefix(const char * string,int direction)327 history_search_prefix(const char *string, int direction)
328 {
329     int start;
330     HIST_ENTRY * entry;
331     /* Work-around for WinEditLine: */
332     int once = 1; /* ensure that we try seeking at least one position */
333     size_t len = strlen(string);
334 
335     start = where_history();
336     entry = current_history();
337     while (((entry != NULL) && entry->line != NULL) || once) {
338 	if ((entry != NULL) && (entry->line != NULL) && (strncmp(entry->line, string, len) == 0))
339 	    return 0;
340 	if (direction < 0)
341 	    entry = previous_history();
342 	else
343 	    entry = next_history();
344 	once = 0;
345     }
346     /* not found */
347     history_set_pos(start);
348     return -1;
349 }
350 #endif
351 
352 #ifdef GNUPLOT_HISTORY
353 
354 /* routine to read history entries from a file,
355  * this complements write_history and is necessary for
356  * saving of history when we are not using libreadline
357  */
358 int
gp_read_history(const char * filename)359 gp_read_history(const char *filename)
360 {
361     FILE *hist_file;
362 
363     if ((hist_file = fopen( filename, "r" ))) {
364     	while (!feof(hist_file)) {
365 	    char line[MAX_LINE_LEN + 1];
366 	    char *pline;
367 
368 	    pline = fgets(line, MAX_LINE_LEN, hist_file);
369 	    if (pline != NULL) {
370 		/* remove trailing linefeed */
371 		if ((pline = strrchr(line, '\n')))
372 		    *pline = '\0';
373 		if ((pline = strrchr(line, '\r')))
374 		    *pline = '\0';
375 
376 		/* skip leading whitespace */
377 		pline = line;
378 		while (isspace((unsigned char) *pline))
379 		    pline++;
380 
381 		/* avoid adding empty lines */
382 		if (*pline)
383 		    add_history(pline);
384 	    }
385 	}
386 	fclose(hist_file);
387 	return 0;
388     } else {
389 	return errno;
390     }
391 }
392 #endif
393 
394 
395 #ifdef USE_READLINE
396 
397 /* Save history to file, or write to stdout or pipe.
398  * For pipes, only "|" works, pipes starting with ">" get a strange
399  * filename like in the non-readline version.
400  *
401  * Peter Weilbacher, 28Jun2004
402  */
403 void
write_history_list(const int num,const char * const filename,const char * mode)404 write_history_list(const int num, const char *const filename, const char *mode)
405 {
406     const HIST_ENTRY *list_entry;
407     FILE *out = stdout;
408     int is_pipe = 0;
409     int is_file = 0;
410     int is_quiet = 0;
411     int i, istart;
412 
413     if (filename && filename[0]) {
414 	/* good filename given and not quiet */
415 #ifdef PIPES
416 	if (filename[0] == '|') {
417 	    restrict_popen();
418 	    out = popen(filename + 1, "w");
419 	    is_pipe = 1;
420 	} else
421 #endif
422 	{
423 	    if (!(out = fopen(filename, mode))) {
424 		int_warn(NO_CARET, "Cannot open file to save history, using standard output.\n");
425 		out = stdout;
426 	    } else {
427 		is_file = 1;
428 	    }
429 	}
430     } else if (filename && !filename[0]) {
431 	is_quiet = 1;
432     }
433 
434     /* Determine starting point and output in loop. */
435     if (num > 0)
436 	istart = history_length - num - 1;
437     else
438 	istart = 0;
439     if (istart < 0 || istart > history_length)
440 	istart = 0;
441 
442     for (i = istart; (list_entry = history_get(i + history_base)); i++) {
443 	/* don't add line numbers when writing to file to make file loadable */
444 	if (!is_file && !is_quiet)
445 	    fprintf(out, "%5i   %s\n", i + history_base, list_entry->line);
446 	else
447 	    fprintf(out, "%s\n", list_entry->line);
448     }
449 
450 #ifdef PIPES
451     if (is_pipe) pclose(out);
452 #endif
453     if (is_file) fclose(out);
454 }
455 
456 
457 /* This is the function getting called in command.c */
458 void
write_history_n(const int n,const char * filename,const char * mode)459 write_history_n(const int n, const char *filename, const char *mode)
460 {
461     write_history_list(n, filename, mode);
462 }
463 #endif
464 
465 
466 #ifdef USE_READLINE
467 
468 /* finds and returns a command from the history list by number */
469 const char *
history_find_by_number(int n)470 history_find_by_number(int n)
471 {
472     if (0 < n && n < history_length)
473 	return history_get(n)->line;
474     else
475 	return NULL;
476 }
477 
478 
479 /* finds and returns a command from the history which starts with <cmd>
480  * Returns NULL if nothing found
481  *
482  * Peter Weilbacher, 28Jun2004
483  */
484 const char *
history_find(char * cmd)485 history_find(char *cmd)
486 {
487     int len;
488 
489     /* remove quotes */
490     if (*cmd == '"')
491 	cmd++;
492     if (!*cmd)
493 	return NULL;
494     len = strlen(cmd);
495     if (cmd[len - 1] == '"')
496 	cmd[--len] = NUL;
497     if (!*cmd)
498 	return NULL;
499 
500     /* Start at latest entry */
501 #if !defined(HAVE_LIBEDITLINE)
502     history_set_pos(history_length);
503 #else
504     while (previous_history());
505 #endif
506 
507     /* Anchored backward search for prefix */
508     if (history_search_prefix(cmd, -1) == 0)
509 	return current_history()->line;
510     return NULL;
511 }
512 
513 
514 /* finds and print all occurencies of commands from the history which
515  * start with <cmd>
516  * Returns the number of found entries on success,
517  * and 0 if no such entry exists
518  *
519  * Peter Weilbacher 28Jun2004
520  */
521 int
history_find_all(char * cmd)522 history_find_all(char *cmd)
523 {
524     int len;
525     int found;
526     int ret;
527     int number = 0; /* each entry found increases this */
528 
529     /* remove quotes */
530     if (*cmd == '"')
531 	cmd++;
532     if (!*cmd)
533 	return 0;
534     len = strlen(cmd);
535     if (cmd[len - 1] == '"')
536 	cmd[--len] = 0;
537     if (!*cmd)
538 	return 0;
539 
540     /* Output matching history entries in chronological order (not backwards
541      * so we have to start at the beginning of the history list.
542      */
543 #if !defined(HAVE_LIBEDITLINE)
544     ret = history_set_pos(0);
545     if (ret == 0) {
546 	fprintf(stderr, "ERROR (history_find_all): could not rewind history\n");
547 	return 0;
548     }
549 #else /* HAVE_LIBEDITLINE */
550     /* libedit's history_set_pos() does not work properly,
551        so we manually go to the oldest entry. Note that directions
552        are reversed. */
553     while (next_history());
554 #endif
555     do {
556 	found = history_search_prefix(cmd, 1); /* Anchored backward search for prefix */
557 	if (found == 0) {
558 	    number++;
559 #if !defined(HAVE_LIBEDITLINE)
560 	    printf("%5i  %s\n", where_history() + history_base, current_history()->line);
561 	    /* Advance one step or we find always the same entry. */
562 	    if (next_history() == NULL)
563 		break; /* finished if stepping didn't work */
564 #else /* HAVE_LIBEDITLINE */
565 	    /* libedit's history indices are reversed wrt GNU readline */
566 	    printf("%5i  %s\n", history_length - where_history() + history_base, current_history()->line);
567 	    /* Advance one step or we find always the same entry. */
568 	    if (!previous_history())
569 		break; /* finished if stepping didn't work */
570 #endif
571 	} /* (found == 0) */
572     } while (found > -1);
573 
574     return number;
575 }
576 
577 #endif /* READLINE */
578