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