comparison src/oct-hist.cc @ 1:78fd87e624cb

[project @ 1993-08-08 01:13:40 by jwe] Initial revision
author jwe
date Sun, 08 Aug 1993 01:13:40 +0000
parents
children bd04d91a7a4a
comparison
equal deleted inserted replaced
0:22412e3a4641 1:78fd87e624cb
1 // octave-hist.cc -*- C++ -*-
2 /*
3
4 Copyright (C) 1992, 1993 John W. Eaton
5
6 This file is part of Octave.
7
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any
11 later version.
12
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, write to the Free
20 Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 The functions listed below were adapted from similar functions from
23 GNU Bash, the Bourne Again SHell, copyright (C) 1987, 1989, 1991 Free
24 Software Foundation, Inc.
25
26 do_history edit_history_readline
27 do_edit_history edit_history_add_hist
28
29 */
30
31 #ifdef __GNUG__
32 #pragma implementation
33 #endif
34
35 #include <sys/types.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <signal.h>
44 #include <fstream.h>
45 #include <strstream.h>
46
47 #include "statdefs.h"
48 #include "utils.h"
49 #include "error.h"
50 #include "input.h"
51 #include "octave.h"
52 #include "unwind-prot.h"
53 #include "octave-hist.h"
54 #include "sighandlers.h"
55
56 extern "C"
57 {
58 #include <readline/history.h>
59 }
60
61 // Nonzero means we are saving history lines.
62 int saving_history = 1;
63
64 // The number of lines to save in the history file.
65 static int octave_hist_size = 1024;
66
67 // The name of the history file.
68 static char *octave_hist_file;
69
70 // The number of hisory lines we read from the history file.
71 static int history_lines_in_file = 0;
72
73 // The number of history lines we've saved so far.
74 static int history_lines_this_session = 0;
75
76 /*
77 * Get some default values, possibly reading them from the
78 * environment.
79 */
80 static int
81 default_history_size (void)
82 {
83 int size = 1024;
84 char *env_size = getenv ("OCTAVE_HISTSIZE");
85 if (env_size != (char *) NULL)
86 {
87 int val;
88 if (sscanf (env_size, "%d", &val) == 1)
89 size = val > 0 ? val : 0;
90 }
91 return size;
92 }
93
94 static char *
95 default_history_file (void)
96 {
97 char *file = (char *) NULL;;
98
99 char *env_file = getenv ("OCTAVE_HISTFILE");
100 if (env_file != (char *) NULL)
101 {
102 fstream f (env_file, (ios::in | ios::out));
103 if (f != 0)
104 {
105 file = strsave (env_file);
106 f.close ();
107 }
108 }
109
110 if (file == (char *) NULL)
111 {
112 if (home_directory != NULL)
113 file = strconcat (home_directory, "/.octave_hist");
114 }
115
116 return file;
117 }
118
119 /*
120 * Prime the history list.
121 */
122 void
123 initialize_history (void)
124 {
125 octave_hist_file = default_history_file ();
126 octave_hist_size = default_history_size ();
127
128 read_history (octave_hist_file);
129 using_history ();
130 history_lines_in_file = where_history ();
131 }
132
133 void
134 clean_up_history (void)
135 {
136 stifle_history (octave_hist_size);
137 write_history (octave_hist_file);
138 }
139
140 void
141 maybe_save_history (char *s)
142 {
143 if (saving_history)
144 {
145 add_history (s);
146 history_lines_this_session++;
147 }
148 }
149
150 /*
151 * Display, save, or load history. Stolen and modified from bash.
152 *
153 * Arg of -w FILENAME means write file, arg of -r FILENAME
154 * means read file, arg of -q means don't number lines. Arg of N
155 * means only display that many items.
156 */
157 void
158 do_history (int argc, char **argv)
159 {
160 HIST_ENTRY **hlist;
161
162 int numbered_output = 1;
163
164 while (--argc > 0)
165 {
166 argv++;
167
168 if (*argv[0] == '-' && strlen (*argv) == 2
169 && ((*argv)[1] == 'r' || (*argv)[1] == 'w'
170 || (*argv)[1] == 'a' || (*argv)[1] == 'n'))
171 {
172 char *file;
173 int result = 0;
174
175 if (argc > 1)
176 file = *(argv+1);
177 else
178 file = octave_hist_file;
179
180 switch ((*argv)[1])
181 {
182 case 'a': // Append `new' lines to file.
183 {
184 if (history_lines_this_session)
185 {
186 if (history_lines_this_session < where_history ())
187 {
188 // If the filename was supplied, then create it if it doesn't already
189 // exist.
190 if (file)
191 {
192 struct stat buf;
193
194 if (stat (file, &buf) == -1)
195 {
196 int tem;
197
198 tem = open (file, O_CREAT, 0666);
199 close (tem);
200 }
201 }
202
203 result =
204 append_history (history_lines_this_session, file);
205 history_lines_in_file += history_lines_this_session;
206 history_lines_this_session = 0;
207 }
208 }
209 }
210 break;
211 case 'w': // Write entire history.
212 result = write_history (file);
213 break;
214 case 'r': // Read entire file.
215 result = read_history (file);
216 break;
217 case 'n': // Read `new' history from file.
218 // Read all of the lines in the file that we haven't already read.
219 using_history ();
220 result = read_history_range (file, history_lines_in_file, -1);
221 using_history ();
222 history_lines_in_file = where_history ();
223 break;
224 }
225 return;
226 }
227 else if (strcmp (*argv, "-q") == 0)
228 numbered_output = 0;
229 else if (strcmp (*argv, "--") == 0)
230 {
231 argc--;
232 argv++;
233 break;
234 }
235 else
236 break;
237 }
238
239 int limited = 0;
240 int limit = 0;
241
242 if (argc > 0)
243 {
244 limited = 1;
245 if (sscanf (*argv, "%d", &limit) != 1)
246 {
247 if (*argv[0] == '-')
248 message ("history", "unrecognized option `%s'", *argv);
249 else
250 message ("history", "bad non-numeric arg `%s'", *argv);
251 return;
252 }
253 }
254
255 hlist = history_list ();
256
257 if (hlist)
258 {
259 for (int i = 0; hlist[i] != (HIST_ENTRY *) NULL; i++)
260 ; // Do nothing.
261
262 if (limit < 0)
263 limit = -limit;
264
265 if (!limited)
266 i = 0;
267 else
268 if ((i -= limit) < 0)
269 i = 0;
270
271 while (hlist[i])
272 {
273 // QUIT; // in bash: (interrupt_state) throw_to_top_level ();
274
275 if (numbered_output)
276 cerr.form ("%5d%c", i + history_base, hlist[i]->data ? '*' : ' ');
277 cerr << hlist[i]->line << "\n";
278 i++;
279 }
280 }
281 }
282
283 /*
284 * Read the edited history lines from STREAM and return them
285 * one at a time. This can read unlimited length lines. The
286 * caller should free the storage.
287 */
288 static char *
289 edit_history_readline (fstream& stream)
290 {
291 char c;
292 int line_len = 128;
293 int lindex = 0;
294 char *line = new char [line_len];
295 line[0] = '\0';
296
297 while (stream.get (c))
298 {
299 if (lindex + 2 >= line_len)
300 {
301 char *tmp_line = new char [line_len += 128];
302 strcpy (tmp_line, line);
303 delete [] line;
304 line = tmp_line;
305 }
306
307 if (c == '\n')
308 {
309 line[lindex++] = '\n';
310 line[lindex++] = '\0';
311 return line;
312 }
313 else
314 line[lindex++] = c;
315 }
316
317 if (! lindex)
318 {
319 delete [] line;
320 return (char *) NULL;
321 }
322
323 if (lindex + 2 >= line_len)
324 {
325 char *tmp_line = new char [lindex+3];
326 strcpy (tmp_line, line);
327 delete [] line;
328 line = tmp_line;
329 }
330
331 // Finish with newline if none in file.
332
333 line[lindex++] = '\n';
334 line[lindex++] = '\0';
335 return line;
336 }
337
338 static void
339 edit_history_add_hist (char *line)
340 {
341 if (line != (char *) NULL)
342 {
343 int len = strlen (line);
344 if (len > 0 && line[len-1] == '\n')
345 line[len-1] = '\0';
346
347 if (line[0] != '\0')
348 add_history (line);
349 }
350 }
351
352 #define histline(i) (hlist[(i)]->line)
353
354 #define EDIT_COMMAND "${EDITOR:-vi}"
355
356 void
357 do_edit_history (int argc, char **argv)
358 {
359 HIST_ENTRY **hlist;
360
361 hlist = history_list ();
362
363 int hist_count = 0;
364
365 while (hlist[hist_count++] != (HIST_ENTRY *) NULL)
366 ; // Find the number of items in the history list.
367
368 // The current command line is already part of the history list by the
369 // time we get to this point. Delete it from the list.
370
371 hist_count -= 2;
372 remove_history (hist_count);
373 hist_count--;
374
375 // If no numbers have been specified, the default is to edit the last
376 // command in the history list.
377
378 int hist_end = hist_count;
379 int hist_beg = hist_count;
380 int reverse = 0;
381
382 // Process options
383
384 int usage_error = 0;
385 if (argc == 3)
386 {
387 argv++;
388 if (sscanf (*argv++, "%d", &hist_beg) != 1
389 || sscanf (*argv, "%d", &hist_end) != 1)
390 usage_error = 1;
391 else
392 {
393 hist_beg--;
394 hist_end--;
395 }
396 }
397 else if (argc == 2)
398 {
399 argv++;
400 if (sscanf (*argv++, "%d", &hist_beg) != 1)
401 usage_error = 1;
402 else
403 {
404 hist_beg--;
405 hist_end = hist_beg;
406 }
407 }
408
409 if (hist_beg < 0 || hist_end < 0 || hist_beg > hist_count
410 || hist_end > hist_count)
411 {
412 error ("history specification out of range");
413 return;
414 }
415
416 if (usage_error)
417 {
418 usage ("edit_history [first] [last]");
419 return;
420 }
421
422 if (hist_end < hist_beg)
423 {
424 int t = hist_end;
425 hist_end = hist_beg;
426 hist_beg = t;
427 reverse = 1;
428 }
429
430 char *name = tmpnam ((char *) NULL);
431
432 fstream file (name, ios::out);
433 if (! file)
434 {
435 error ("edit_history: couldn't open temporary file `%s'", name);
436 return;
437 }
438
439 if (reverse)
440 {
441 for (int i = hist_end; i >= hist_beg; i--)
442 file << histline (i) << "\n";
443 }
444 else
445 {
446 for (int i = hist_beg; i <= hist_end; i++)
447 file << histline (i) << "\n";
448 }
449
450 file.close ();
451
452 // Call up our favorite editor on the file of commands.
453
454 ostrstream buf;
455 buf << EDIT_COMMAND << " " << name << ends;
456 char *cmd = buf.str ();
457
458 // Ignore interrupts while we are off editing commands. Should we
459 // maybe avoid using system()? There still seems to be a problem with
460 // properly waiting for emacsclient.
461
462 volatile sig_handler *saved_sigint_handler = signal (SIGINT, SIG_IGN);
463 system (cmd);
464 signal (SIGINT, saved_sigint_handler);
465
466 // Write the commands to the history file since parse_and_execute
467 // disables command line history while it executes.
468
469 file.open (name, ios::in);
470
471 char *line;
472 while ((line = edit_history_readline (file)) != NULL)
473 {
474
475 // Skip blank lines
476
477 if (line[0] == '\n')
478 {
479 delete [] line;
480 continue;
481 }
482
483 edit_history_add_hist (line);
484 }
485
486 file.close ();
487
488 // Turn on command echo, so the output from this will make better sense.
489
490 begin_unwind_frame ("do_edit_history");
491 unwind_protect_int (echo_input);
492 echo_input = 1;
493
494 parse_and_execute (name, 1);
495
496 run_unwind_frame ("do_edit_history");
497
498 // Delete the temporary file. Should probably be done with an
499 // unwind_protect.
500
501 unlink (name);
502 }
503
504 int
505 current_history_number (void)
506 {
507 using_history ();
508
509 if (octave_hist_size > 0)
510 return history_base + where_history ();
511 else
512 return -1;
513
514 }
515
516 /*
517 ;;; Local Variables: ***
518 ;;; mode: C++ ***
519 ;;; page-delimiter: "^/\\*" ***
520 ;;; End: ***
521 */