improve of cmpl.
[bush.git] / builtins / history.def
blob67c56e25aa6cf26f9542c8eb7c291546fb7cdd05
1 This file is history.def, from which is created history.c.
2 It implements the builtin "history" in Bush.
4 Copyright (C) 1987-2020 Free Software Foundation, Inc.
6 This file is part of GNU Bush, the Bourne Again SHell.
8 Bush is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 Bush is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Bush. If not, see <http://www.gnu.org/licenses/>.
21 $PRODUCES history.c
23 $BUILTIN history
24 $FUNCTION history_builtin
25 $DEPENDS_ON HISTORY
26 $SHORT_DOC history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...]
27 Display or manipulate the history list.
29 Display the history list with line numbers, prefixing each modified
30 entry with a `*'. An argument of N lists only the last N entries.
32 Options:
33 -c clear the history list by deleting all of the entries
34 -d offset delete the history entry at position OFFSET. Negative
35 offsets count back from the end of the history list
37 -a append history lines from this session to the history file
38 -n read all history lines not already read from the history file
39 and append them to the history list
40 -r read the history file and append the contents to the history
41 list
42 -w write the current history to the history file
44 -p perform history expansion on each ARG and display the result
45 without storing it in the history list
46 -s append the ARGs to the history list as a single entry
48 If FILENAME is given, it is used as the history file. Otherwise,
49 if HISTFILE has a value, that is used, else ~/.bush_history.
51 If the HISTTIMEFORMAT variable is set and not null, its value is used
52 as a format string for strftime(3) to print the time stamp associated
53 with each displayed history entry. No time stamps are printed otherwise.
55 Exit Status:
56 Returns success unless an invalid option is given or an error occurs.
57 $END
59 #include <config.h>
61 #if defined (HISTORY)
62 #include "../src/bushtypes.h"
63 #if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
64 # include <sys/file.h>
65 #endif
66 #include "posixstat.h"
67 #include "filecntl.h"
68 #include <errno.h>
69 #include <stdio.h>
70 #if defined (HAVE_UNISTD_H)
71 # include <unistd.h>
72 #endif
74 #include "../src/bushansi.h"
75 #include "../src/bushintl.h"
77 #include "../src/shell.h"
78 #include "../src/flags.h"
79 #include "../src/lxrgmr/parser.h"
80 #include "../src/bushhist.h"
81 #include <readline/history.h>
82 #include "bushgetopt.h"
83 #include "common.h"
85 #if !defined (errno)
86 extern int errno;
87 #endif
89 static char *histtime PARAMS((HIST_ENTRY *, const char *));
90 static int display_history PARAMS((WORD_LIST *));
91 static void push_history PARAMS((WORD_LIST *));
92 static int expand_and_print_history PARAMS((WORD_LIST *));
94 #define AFLAG 0x01
95 #define RFLAG 0x02
96 #define WFLAG 0x04
97 #define NFLAG 0x08
98 #define SFLAG 0x10
99 #define PFLAG 0x20
100 #define CFLAG 0x40
101 #define DFLAG 0x80
104 history_builtin (list)
105 WORD_LIST *list;
107 int flags, opt, result, old_history_lines, obase, ind;
108 char *filename, *delete_arg, *range;
109 intmax_t delete_offset;
111 flags = 0;
112 reset_internal_getopt ();
113 while ((opt = internal_getopt (list, "acd:npsrw")) != -1)
115 switch (opt)
117 case 'a':
118 flags |= AFLAG;
119 break;
120 case 'c':
121 flags |= CFLAG;
122 break;
123 case 'n':
124 flags |= NFLAG;
125 break;
126 case 'r':
127 flags |= RFLAG;
128 break;
129 case 'w':
130 flags |= WFLAG;
131 break;
132 case 's':
133 flags |= SFLAG;
134 break;
135 case 'd':
136 flags |= DFLAG;
137 delete_arg = list_optarg;
138 break;
139 case 'p':
140 #if defined (BANG_HISTORY)
141 flags |= PFLAG;
142 #endif
143 break;
144 CASE_HELPOPT;
145 default:
146 builtin_usage ();
147 return (EX_USAGE);
150 list = loptend;
152 opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG);
153 if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG)
155 builtin_error (_("cannot use more than one of -anrw"));
156 return (EXECUTION_FAILURE);
159 /* clear the history, but allow other arguments to add to it again. */
160 if (flags & CFLAG)
162 bush_clear_history ();
163 if (list == 0)
164 return (EXECUTION_SUCCESS);
167 if (flags & SFLAG)
169 if (list)
170 push_history (list);
171 return (EXECUTION_SUCCESS);
173 #if defined (BANG_HISTORY)
174 else if (flags & PFLAG)
176 if (list)
177 return (expand_and_print_history (list));
178 return (sh_chkwrite (EXECUTION_SUCCESS));
180 #endif
181 else if ((flags & DFLAG) && (range = strchr ((delete_arg[0] == '-') ? delete_arg + 1 : delete_arg, '-')))
183 intmax_t delete_start, delete_end;
184 *range++ = '\0';
185 if (legal_number (delete_arg, &delete_start) == 0 || legal_number (range, &delete_end) == 0)
187 range[-1] = '-';
188 sh_erange (delete_arg, _("history position"));
189 return (EXECUTION_FAILURE);
191 if (delete_arg[0] == '-' && delete_start < 0)
193 /* the_history[history_length] == 0x0, so this is correct */
194 delete_start += history_length;
195 if (delete_start < history_base)
197 start_error:
198 sh_erange (delete_arg, _("history position"));
199 return (EXECUTION_FAILURE);
202 /* numbers as displayed by display_history are offset by history_base */
203 else if (delete_start > 0)
204 delete_start -= history_base;
205 if (delete_start < 0 || delete_start >= history_length)
206 goto start_error;
207 if (range[0] == '-' && delete_end < 0)
209 delete_end += history_length;
210 if (delete_end < history_base)
212 range_error:
213 sh_erange (range, _("history position"));
214 return (EXECUTION_FAILURE);
217 else if (delete_end > 0)
218 delete_end -= history_base;
219 if (delete_end < 0 || delete_end >= history_length)
220 goto range_error;
221 result = bush_delete_history_range (delete_start, delete_end);
222 if (where_history () > history_length)
223 history_set_pos (history_length);
224 return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
226 else if (flags & DFLAG)
228 if (legal_number (delete_arg, &delete_offset) == 0)
230 sh_erange (delete_arg, _("history position"));
231 return (EXECUTION_FAILURE);
233 /* check for negative offsets, count back from end of list */
234 if (delete_arg[0] == '-' && delete_offset < 0)
236 /* since the_history[history_length] == 0x0, this calculation means
237 that history -d -1 will delete the last history entry, which at
238 this point is the history -d -1 we just added. */
239 ind = history_length + delete_offset;
240 if (ind < history_base)
242 sh_erange (delete_arg, _("history position"));
243 return (EXECUTION_FAILURE);
245 opt = ind + history_base; /* compensate for opt - history_base below */
247 else if ((delete_offset < history_base) || (delete_offset >= (history_base + history_length)))
249 sh_erange (delete_arg, _("history position"));
250 return (EXECUTION_FAILURE);
252 else
253 opt = delete_offset;
255 /* Positive arguments from numbers as displayed by display_history need
256 to be offset by history_base */
257 result = bush_delete_histent (opt - history_base);
258 /* Since remove_history changes history_length, this can happen if
259 we delete the last history entry. */
260 if (where_history () > history_length)
261 history_set_pos (history_length);
262 return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
264 else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0)
266 result = display_history (list);
267 return (sh_chkwrite (result));
270 filename = list ? list->word->word : get_string_value ("HISTFILE");
271 result = EXECUTION_SUCCESS;
273 #if defined (RESTRICTED_SHELL)
274 if (restricted && strchr (filename, '/'))
276 sh_restricted (filename);
277 return (EXECUTION_FAILURE);
279 #endif
281 if (flags & AFLAG) /* Append session's history to file. */
282 result = maybe_append_history (filename);
283 else if (flags & WFLAG) /* Write entire history. */
284 result = write_history (filename);
285 else if (flags & RFLAG) /* Read entire file. */
287 result = read_history (filename);
288 history_lines_in_file = history_lines_read_from_file;
289 /* history_lines_in_file = where_history () + history_base - 1; */
291 else if (flags & NFLAG) /* Read `new' history from file. */
293 /* Read all of the lines in the file that we haven't already read. */
294 old_history_lines = history_lines_in_file;
295 obase = history_base;
297 using_history ();
298 result = read_history_range (filename, history_lines_in_file, -1);
299 using_history ();
301 history_lines_in_file = history_lines_read_from_file;
302 /* history_lines_in_file = where_history () + history_base - 1; */
304 /* If we're rewriting the history file at shell exit rather than just
305 appending the lines from this session to it, the question is whether
306 we reset history_lines_this_session to 0, losing any history entries
307 we had before we read the new entries from the history file, or
308 whether we count the new entries we just read from the file as
309 history lines added during this session.
310 Right now, we do the latter. This will cause these history entries
311 to be written to the history file along with any intermediate entries
312 we add when we do a `history -a', but the alternative is losing
313 them altogether. */
314 if (force_append_history == 0)
315 history_lines_this_session += history_lines_in_file - old_history_lines +
316 history_base - obase;
319 return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
322 /* Accessors for HIST_ENTRY lists that are called HLIST. */
323 #define histline(i) (hlist[(i)]->line)
324 #define histdata(i) (hlist[(i)]->data)
326 static char *
327 histtime (hlist, histtimefmt)
328 HIST_ENTRY *hlist;
329 const char *histtimefmt;
331 static char timestr[128];
332 time_t t;
333 struct tm *tm;
335 t = history_get_time (hlist);
336 tm = t ? localtime (&t) : 0;
337 if (t && tm)
338 strftime (timestr, sizeof (timestr), histtimefmt, tm);
339 else if (hlist->timestamp && hlist->timestamp[0])
340 snprintf (timestr, sizeof (timestr), _("%s: invalid timestamp"),
341 (hlist->timestamp[0] == '#') ? hlist->timestamp + 1: hlist->timestamp);
342 else
343 strcpy (timestr, "??");
344 return timestr;
347 static int
348 display_history (list)
349 WORD_LIST *list;
351 register int i;
352 intmax_t limit;
353 HIST_ENTRY **hlist;
354 char *histtimefmt, *timestr;
356 if (list)
358 if (get_numeric_arg (list, 0, &limit) == 0)
359 return (EXECUTION_FAILURE);
361 if (limit < 0)
362 limit = -limit;
364 else
365 limit = -1;
367 hlist = history_list ();
369 if (hlist)
371 for (i = 0; hlist[i]; i++)
374 if (0 <= limit && limit < i)
375 i -= limit;
376 else
377 i = 0;
379 histtimefmt = get_string_value ("HISTTIMEFORMAT");
381 while (hlist[i])
383 QUIT;
385 timestr = (histtimefmt && *histtimefmt) ? histtime (hlist[i], histtimefmt) : (char *)NULL;
386 printf ("%5d%c %s%s\n", i + history_base,
387 histdata(i) ? '*' : ' ',
388 ((timestr && *timestr) ? timestr : ""),
389 histline(i));
390 i++;
394 return (EXECUTION_SUCCESS);
397 /* Remove the last entry in the history list and add each argument in
398 LIST to the history. */
399 static void
400 push_history (list)
401 WORD_LIST *list;
403 char *s;
405 /* Delete the last history entry if it was a single entry added to the
406 history list (generally the `history -s' itself), or if `history -s'
407 is being used in a compound command and the compound command was
408 added to the history as a single element (command-oriented history).
409 If you don't want history -s to remove the compound command from the
410 history, change #if 0 to #if 1 below. */
411 #if 0
412 if (remember_on_history && hist_last_line_pushed == 0 &&
413 hist_last_line_added && bush_delete_last_history () == 0)
414 #else
415 if (remember_on_history && hist_last_line_pushed == 0 &&
416 (hist_last_line_added ||
417 (current_command_line_count > 0 && current_command_first_line_saved && command_oriented_history))
418 && bush_delete_last_history () == 0)
419 #endif
420 return;
422 s = string_list (list);
423 /* Call check_add_history with FORCE set to 1 to skip the check against
424 current_command_line_count. If history -s is used in a compound
425 command, the above code will delete the compound command's history
426 entry and this call will add the line to the history as a separate
427 entry. Without FORCE=1, if current_command_line_count were > 1, the
428 line would be appended to the entry before the just-deleted entry. */
429 check_add_history (s, 1); /* obeys HISTCONTROL, HISTIGNORE */
431 hist_last_line_pushed = 1; /* XXX */
432 free (s);
435 #if defined (BANG_HISTORY)
436 static int
437 expand_and_print_history (list)
438 WORD_LIST *list;
440 char *s;
441 int r, result;
443 if (hist_last_line_pushed == 0 && hist_last_line_added && bush_delete_last_history () == 0)
444 return EXECUTION_FAILURE;
445 result = EXECUTION_SUCCESS;
446 while (list)
448 r = history_expand (list->word->word, &s);
449 if (r < 0)
451 builtin_error (_("%s: history expansion failed"), list->word->word);
452 result = EXECUTION_FAILURE;
454 else
456 fputs (s, stdout);
457 putchar ('\n');
459 FREE (s);
460 list = list->next;
462 fflush (stdout);
463 return result;
465 #endif /* BANG_HISTORY */
466 #endif /* HISTORY */