Icons are changed
[gnuplot] / src / history.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: history.c,v 1.21.2.4 2009/03/26 22:02:02 mikulik Exp $"); }
3 #endif
4
5 /* GNUPLOT - history.c */
6
7 /*[
8  * Copyright 1986 - 1993, 1999, 2004   Thomas Williams, Colin Kelley
9  *
10  * Permission to use, copy, and distribute this software and its
11  * documentation for any purpose with or without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and
13  * that both that copyright notice and this permission notice appear
14  * in supporting documentation.
15  *
16  * Permission to modify the software is granted, but not the right to
17  * distribute the complete modified source code.  Modifications are to
18  * be distributed as patches to the released version.  Permission to
19  * distribute binaries produced by compiling modified sources is granted,
20  * provided you
21  *   1. distribute the corresponding source modifications from the
22  *    released version in the form of a patch file along with the binaries,
23  *   2. add special version identification to distinguish your version
24  *    in addition to the base release version number,
25  *   3. provide your name and address as the primary contact for the
26  *    support of your modified version, and
27  *   4. retain our contact information in regard to use of the base
28  *    software.
29  * Permission to distribute the released version of the source code along
30  * with corresponding source modifications in the form of a patch file is
31  * granted with same provisions 2 through 4 for binary distributions.
32  *
33  * This software is provided "as is" without express or implied warranty
34  * to the extent permitted by applicable law.
35 ]*/
36
37 #include <stdio.h>
38
39 #include "gp_hist.h"
40
41 #include "alloc.h"
42 #include "util.h"
43
44
45 /* moved here from plot.c */
46 #ifdef GNUPLOT_HISTORY
47 # ifndef HISTORY_SIZE
48 /* Can be overriden with the environment variable 'GNUPLOT_HISTORY_SIZE' */
49 #  define HISTORY_SIZE 666
50 # endif
51 long int gnuplot_history_size = HISTORY_SIZE;
52 #endif
53
54
55 #if defined(READLINE) && !defined(HAVE_LIBREADLINE) && !defined(HAVE_LIBEDITLINE)
56
57 struct hist *history = NULL;     /* no history yet */
58 struct hist *cur_entry = NULL;
59
60 /* add line to the history */
61 void
62 add_history(char *line)
63 {
64     static struct hist *first_entry = NULL; 
65         /* this points to first entry in history list, 
66             whereas "history" points to last entry     */
67     static long int hist_count = 0;
68         /* number of entries in history list */
69     struct hist *entry;
70
71     entry = history;
72     while (entry != NULL) {
73         /* Don't store duplicate entries */
74         if (!strcmp(entry->line, line)) {
75             /* cmd lines are equal, relink entry that was found last */
76             if (entry->next == NULL) {
77                 /* previous command repeated, no change */
78                 return;
79             }
80             if (entry->prev == NULL) {
81                 /* current cmd line equals the first in the history */
82                 (entry->next)->prev = NULL;
83                 first_entry = entry->next;
84                 history->next = entry;
85                 entry->prev = history;
86                 entry->next = NULL;
87                 history = entry;
88                 return;
89             }
90             /* bridge over entry's vacancy, then move it to the end */
91             (entry->prev)->next = entry->next;
92             (entry->next)->prev = entry->prev;
93             entry->prev = history;
94             history->next = entry;
95             entry->next = NULL;
96             history = entry;
97             return;
98         }
99         entry = entry->prev;
100     }                           /* end of not-storing duplicated entries */
101
102 #ifdef GNUPLOT_HISTORY
103     /* limit size of history list to "gnuplot_history_size" */
104     if (gnuplot_history_size != -1) {
105         while ((hist_count >= gnuplot_history_size) && (first_entry != NULL)) {
106     
107             entry = first_entry;
108
109             /* remove first entry from chain */
110             first_entry = first_entry->next;
111             if (first_entry) {
112                 first_entry->prev = NULL;
113             } 
114             hist_count--;
115
116             /* remove references */
117             if (cur_entry == entry)
118                 cur_entry = first_entry;
119             if (history == entry) {
120                 cur_entry = history = NULL;
121                 hist_count = 0;
122             }
123
124             free( entry->line );
125             free( entry );           
126         }
127     }
128 #endif
129
130     entry = (struct hist *) gp_alloc(sizeof(struct hist), "history");
131     entry->line = gp_strdup(line);
132
133     entry->prev = history;
134     entry->next = NULL;
135     if (history != NULL) {
136         history->next = entry;
137     } else {
138         first_entry = entry;
139     } 
140     history = entry;
141     hist_count++;
142 }
143
144
145 /*
146  * New functions for browsing the history. They are called from command.c
147  * when the user runs the 'history' command
148  */
149
150 /* write <n> last entries of the history to the file <filename>
151  * Input parameters:
152  *    n > 0 ... write only <n> last entries; otherwise all entries
153  *    filename == NULL ... write to stdout; otherwise to the filename
154  *    filename == "" ... write to stdout, but without entry numbers
155  *    mode ... should be "w" or "a" to select write or append for file,
156  *             ignored if history is written to a pipe
157 */
158 void
159 write_history_n(const int n, const char *filename, const char *mode)
160 {
161     struct hist *entry = history, *start = NULL;
162     FILE *out = stdout;
163 #ifdef PIPES
164     int is_pipe = 0; /* not filename but pipe to an external program */
165 #endif
166     int hist_entries = 0;
167     int hist_index = 1;
168
169     if (entry == NULL)
170         return;                 /* no history yet */
171
172     /* find the beginning of the history and count nb of entries */
173     while (entry->prev != NULL) {
174         entry = entry->prev;
175         hist_entries++;
176         if (n <= 0 || hist_entries <= n)
177             start = entry;      /* listing will start from this entry */
178     }
179     entry = start;
180     hist_index = (n > 0) ? GPMAX(hist_entries - n, 0) + 1 : 1;
181
182     /* now write the history */
183     if (filename != NULL && filename[0]) {
184 #ifdef PIPES
185         if (filename[0]=='|') {
186             out = popen(filename+1, "w");
187             is_pipe = 1;
188         } else
189 #endif
190         out = fopen(filename, mode);
191     }
192     if (!out) {
193         /* cannot use int_error() because we are just exiting gnuplot:
194            int_error(NO_CARET, "cannot open file for saving the history");
195         */
196         fprintf(stderr, "Warning: cannot open file %s for saving the history.", filename);
197     } else {
198         while (entry != NULL) {
199             /* don't add line numbers when writing to file
200             * to make file loadable */
201             if (filename) {
202                 if (filename[0]==0) fputs(" ", out);
203                 fprintf(out, "%s\n", entry->line);
204             } else
205                 fprintf(out, "%5i  %s\n", hist_index++, entry->line);
206             entry = entry->next;
207         }
208         if (filename != NULL && filename[0]) {
209 #ifdef PIPES
210             if (is_pipe)
211                 pclose(out);
212             else
213 #endif
214             fclose(out);
215         }
216     }
217 }
218
219
220 /* obviously the same routine as in GNU readline, according to code from
221  * plot.c:#if defined(HAVE_LIBREADLINE) && defined(GNUPLOT_HISTORY)
222  */
223 void
224 write_history(char *filename)
225 {
226     write_history_n(0, filename, "w");
227 }
228
229
230 /* routine to read history entries from a file,
231  * this complements write_history and is necessary for
232  * saving of history when we are not using libreadline
233  */
234 void
235 read_history(char *filename)
236 {
237     FILE *hist_file;
238     
239     if ((hist_file = fopen( filename, "r" ))) {
240         while (!feof(hist_file)) {
241             char *pline, line[MAX_LINE_LEN+1];
242             pline = fgets(line, MAX_LINE_LEN, hist_file);
243             if (pline) {
244                 /* remove trailing linefeed */
245                 if ((pline = strrchr(line, '\n')))
246                     *pline = '\0';
247                 if ((pline = strrchr(line, '\r')))
248                     *pline = '\0';
249
250                 add_history(line);
251             }
252         }
253         fclose(hist_file);
254     }
255 }
256
257
258 /* finds and returns a command from the history which starts with <cmd>
259  * (ignores leading spaces in <cmd>)
260  * Returns NULL if nothing found
261  */
262 const char *
263 history_find(char *cmd)
264 {
265     struct hist *entry = history;
266     size_t len;
267     char *line;
268
269     if (entry == NULL)
270         return NULL;            /* no history yet */
271     if (*cmd == '"')
272         cmd++;                  /* remove surrounding quotes */
273     if (!*cmd)
274         return NULL;
275
276     len = strlen(cmd);
277
278     if (cmd[len - 1] == '"')
279         cmd[--len] = 0;
280     if (!*cmd)
281         return NULL;
282
283     /* search through the history */
284     while (entry != NULL) {
285         line = entry->line;
286         while (isspace((unsigned char) *line))
287             line++;             /* skip leading spaces */
288         if (!strncmp(cmd, line, len))   /* entry found */
289             return line;
290         entry = entry->prev;
291     }
292     return NULL;
293 }
294
295
296 /* finds and print all occurencies of commands from the history which
297  * start with <cmd>
298  * (ignores leading spaces in <cmd>)
299  * Returns 1 on success, 0 if no such entry exists
300  */
301 int
302 history_find_all(char *cmd)
303 {
304     struct hist *entry = history;
305     int hist_index = 1;
306     char res = 0;
307     int len;
308     char *line;
309
310     if (entry == NULL)
311         return 0;               /* no history yet */
312     if (*cmd == '"')
313         cmd++;                  /* remove surrounding quotes */
314     if (!*cmd)
315         return 0;
316     len = strlen(cmd);
317     if (cmd[len - 1] == '"')
318         cmd[--len] = 0;
319     if (!*cmd)
320         return 0;
321     /* find the beginning of the history */
322     while (entry->prev != NULL)
323         entry = entry->prev;
324     /* search through the history */
325     while (entry != NULL) {
326         line = entry->line;
327         while (isspace((unsigned char) *line))
328             line++;             /* skip leading spaces */
329         if (!strncmp(cmd, line, len)) { /* entry found */
330             printf("%5i  %s\n", hist_index, line);
331             res = 1;
332         }
333         entry = entry->next;
334         hist_index++;
335     }
336     return res;
337 }
338
339 #elif defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDITLINE)
340
341 /* Save history to file, or write to stdout or pipe.
342  * For pipes, only "|" works, pipes starting with ">" get a strange 
343  * filename like in the non-readline version.
344  *
345  * Peter Weilbacher, 28Jun2004
346  */
347
348 void
349 write_history_list(num, filename, mode)
350 const int num;
351 const char *const filename;
352 const char *mode;
353 {
354 #ifdef HAVE_LIBREADLINE
355     HIST_ENTRY **the_list = history_list();
356 #endif
357     const HIST_ENTRY *list_entry;
358     FILE *out = stdout;
359     int is_pipe = 0;
360     int is_file = 0;
361     int is_quiet = 0;
362     int i, istart;
363
364     if (filename && filename[0] ) {
365         /* good filename given and not quiet */
366 #ifdef PIPES
367         if (filename[0]=='|') {
368             out = popen(filename+1, "w");
369             is_pipe = 1;
370         } else {
371 #endif
372             if (! (out = fopen(filename, mode) ) ) {
373                 /* Fall back to 'stdout' */
374                 int_warn(NO_CARET, "Cannot open file to save history, using standard output.\n");
375                 out = stdout;
376             } else is_file = 1;
377 #ifdef PIPES
378         }
379 #endif
380     } else if (filename && !filename[0])
381         is_quiet = 1;
382
383     /* Determine starting point and output in loop.
384      * For some reason the readline functions append_history() 
385      * and write_history() do not work they way I thought they did...
386      */
387     if (num > 0) {
388         istart = history_length - num;
389         if (istart <= 0 || istart > history_length)
390             istart = 1;
391     } else istart = 1;
392 #ifdef HAVE_LIBREADLINE
393     if (the_list)
394 #endif
395         for (i = istart; list_entry = history_get(i); i++) {
396             /* don't add line numbers when writing to file to make file loadable */
397             if (is_file)
398                 fprintf(out, "%s\n", list_entry->line);
399             else {
400                 if (!is_quiet) fprintf(out, "%5i", i + history_base - 1);
401                 fprintf(out, "  %s\n", list_entry->line);
402             }
403         }
404     /* close if something was opened */
405 #ifdef PIPES
406     if (is_pipe) pclose(out);
407 #endif
408     if (is_file) fclose(out);
409 }
410
411 /* This is the function getting called in command.c */
412 void
413 write_history_n(n, filename, mode)
414 const int n;
415 const char *filename;
416 const char *mode;
417 {
418     write_history_list(n, filename, mode);
419 }
420
421 /* finds and returns a command from the history which starts with <cmd>
422  * Returns NULL if nothing found
423  *
424  * Peter Weilbacher, 28Jun2004
425  */
426 const char *
427 history_find(cmd)
428 char *cmd;
429 {
430     int len;
431
432     /* quote removal, copied from non-readline version */
433     if (*cmd == '"') cmd++;
434     if (!*cmd) return 0;
435     len = strlen(cmd);
436     if (cmd[len - 1] == '"') cmd[--len] = 0;
437     if (!*cmd) return 0;
438     /* printf ("searching for '%s'\n", cmd); */
439
440     /* Anchored backward search for prefix */
441     if (history_search_prefix(cmd, -1) == 0)
442         return current_history()->line;
443     return NULL;
444 }
445
446 /* finds and print all occurencies of commands from the history which
447  * start with <cmd>
448  * Returns the number of found entries on success,
449  * and 0 if no such entry exists
450  *
451  * Peter Weilbacher 28Jun2004
452  */
453 int
454 history_find_all(cmd)
455 char *cmd;
456 {
457     int len;
458     int found;
459     int number = 0; /* each found entry increases this */
460
461     /* quote removal, copied from non-readline version */
462     if (*cmd == '"') cmd++;
463     if (!*cmd) return 0;
464     len = strlen(cmd);
465     if (cmd[len - 1] == '"') cmd[--len] = 0;
466     if (!*cmd) return 0;
467     /* printf ("searching for all occurrences of '%s'\n", cmd); */
468
469     /* Output matching history entries in chronological order (not backwards
470      * so we have to start at the beginning of the history list.
471      */
472     history_set_pos(0);
473     do {
474         found = history_search_prefix(cmd, 1); /* Anchored backward search for prefix */
475         if (found == 0) {
476             number++;
477             printf("%5i  %s\n", where_history() + history_base, current_history()->line);
478             /* go one step back or you find always the same entry. */
479             if (!history_set_pos(where_history() + 1))
480                 break; /* finished if stepping didn't work */
481         } /* (found == 0) */
482     } while (found > -1);
483
484     return number;
485 }
486
487 #endif /* READLINE && !HAVE_LIBREADLINE && !HAVE_LIBEDITLINE */