Mercurial > hg > octave-nkf
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 */ |