1This file is history.def, from which is created history.c.
2It implements the builtin "history" in Bash.
3
4Copyright (C) 1987-2020 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
12
13Bash is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with Bash.  If not, see <http://www.gnu.org/licenses/>.
20
21$PRODUCES history.c
22
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...]
27Display or manipulate the history list.
28
29Display the history list with line numbers, prefixing each modified
30entry with a `*'.  An argument of N lists only the last N entries.
31
32Options:
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
36
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
43
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
47
48If FILENAME is given, it is used as the history file.  Otherwise,
49if HISTFILE has a value, that is used, else ~/.bash_history.
50
51If the HISTTIMEFORMAT variable is set and not null, its value is used
52as a format string for strftime(3) to print the time stamp associated
53with each displayed history entry.  No time stamps are printed otherwise.
54
55Exit Status:
56Returns success unless an invalid option is given or an error occurs.
57$END
58
59#include <config.h>
60
61#if defined (HISTORY)
62#include "../bashtypes.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
73
74#include "../bashansi.h"
75#include "../bashintl.h"
76
77#include "../shell.h"
78#include "../flags.h"
79#include "../parser.h"
80#include "../bashhist.h"
81#include <readline/history.h>
82#include "bashgetopt.h"
83#include "common.h"
84
85#if !defined (errno)
86extern int errno;
87#endif
88
89static char *histtime PARAMS((HIST_ENTRY *, const char *));
90static int display_history PARAMS((WORD_LIST *));
91static void push_history PARAMS((WORD_LIST *));
92static int expand_and_print_history PARAMS((WORD_LIST *));
93
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
102
103int
104history_builtin (list)
105     WORD_LIST *list;
106{
107  int flags, opt, result, old_history_lines, obase, ind;
108  char *filename, *delete_arg, *range;
109  intmax_t delete_offset;
110
111  flags = 0;
112  reset_internal_getopt ();
113  while ((opt = internal_getopt (list, "acd:npsrw")) != -1)
114    {
115      switch (opt)
116	{
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);
148	}
149    }
150  list = loptend;
151
152  opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG);
153  if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG)
154    {
155      builtin_error (_("cannot use more than one of -anrw"));
156      return (EXECUTION_FAILURE);
157    }
158
159  /* clear the history, but allow other arguments to add to it again. */
160  if (flags & CFLAG)
161    {
162      bash_clear_history ();
163      if (list == 0)
164	return (EXECUTION_SUCCESS);
165    }
166
167  if (flags & SFLAG)
168    {
169      if (list)
170	push_history (list);
171      return (EXECUTION_SUCCESS);
172    }
173#if defined (BANG_HISTORY)
174  else if (flags & PFLAG)
175    {
176      if (list)
177	return (expand_and_print_history (list));
178      return (sh_chkwrite (EXECUTION_SUCCESS));
179    }
180#endif
181  else if ((flags & DFLAG) && (range = strchr ((delete_arg[0] == '-') ? delete_arg + 1 : delete_arg, '-')))
182    {
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)
186	{
187	  range[-1] = '-';
188	  sh_erange (delete_arg, _("history position"));
189	  return (EXECUTION_FAILURE);
190	}
191      if (delete_arg[0] == '-' && delete_start < 0)
192        {
193	  /* the_history[history_length] == 0x0, so this is correct */
194          delete_start += history_length;
195	  if (delete_start < history_base)
196	    {
197start_error:
198	      sh_erange (delete_arg, _("history position"));
199	      return (EXECUTION_FAILURE);
200	    }
201        }
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)
208        {
209          delete_end += history_length;
210	  if (delete_end < history_base)
211	    {
212range_error:
213	      sh_erange (range, _("history position"));
214	      return (EXECUTION_FAILURE);
215	    }
216        }
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 = bash_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);
225    }
226  else if (flags & DFLAG)
227    {
228      if (legal_number (delete_arg, &delete_offset) == 0)
229	{
230	  sh_erange (delete_arg, _("history position"));
231	  return (EXECUTION_FAILURE);
232	}
233      /* check for negative offsets, count back from end of list */
234      if (delete_arg[0] == '-' && delete_offset < 0)
235	{
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)
241	    {
242	      sh_erange (delete_arg, _("history position"));
243	      return (EXECUTION_FAILURE);
244	    }
245	  opt = ind + history_base;	/* compensate for opt - history_base below */
246	}
247      else if ((delete_offset < history_base) || (delete_offset >= (history_base + history_length)))
248	{
249	  sh_erange (delete_arg, _("history position"));
250	  return (EXECUTION_FAILURE);
251	}
252      else
253	opt = delete_offset;
254
255      /* Positive arguments from numbers as displayed by display_history need
256	 to be offset by history_base */
257      result = bash_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);
263    }
264  else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0)
265    {
266      result = display_history (list);
267      return (sh_chkwrite (result));
268    }
269
270  filename = list ? list->word->word : get_string_value ("HISTFILE");
271  result = EXECUTION_SUCCESS;
272
273#if defined (RESTRICTED_SHELL)
274  if (restricted && strchr (filename, '/'))
275    {
276      sh_restricted (filename);
277      return (EXECUTION_FAILURE);
278    }
279#endif
280
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. */
286    {
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; */
290    }
291  else if (flags & NFLAG)	/* Read `new' history from file. */
292    {
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;
296
297      using_history ();
298      result = read_history_range (filename, history_lines_in_file, -1);
299      using_history ();
300
301      history_lines_in_file = history_lines_read_from_file;
302      /* history_lines_in_file = where_history () + history_base - 1; */
303
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;
317    }
318
319  return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
320}
321
322/* Accessors for HIST_ENTRY lists that are called HLIST. */
323#define histline(i) (hlist[(i)]->line)
324#define histdata(i) (hlist[(i)]->data)
325
326static char *
327histtime (hlist, histtimefmt)
328     HIST_ENTRY *hlist;
329     const char *histtimefmt;
330{
331  static char timestr[128];
332  time_t t;
333  struct tm *tm;
334
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;
345}
346
347static int
348display_history (list)
349     WORD_LIST *list;
350{
351  register int i;
352  intmax_t limit;
353  HIST_ENTRY **hlist;
354  char *histtimefmt, *timestr;
355
356  if (list)
357    {
358      if (get_numeric_arg (list, 0, &limit) == 0)
359	return (EXECUTION_FAILURE);
360
361      if (limit < 0)
362	limit = -limit;
363    }
364  else
365    limit = -1;
366
367  hlist = history_list ();
368
369  if (hlist)
370    {
371      for (i = 0;  hlist[i]; i++)
372	;
373
374      if (0 <= limit && limit < i)
375	i -= limit;
376      else
377	i = 0;
378
379      histtimefmt = get_string_value ("HISTTIMEFORMAT");
380
381      while (hlist[i])
382	{
383	  QUIT;
384
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++;
391	}
392    }
393
394  return (EXECUTION_SUCCESS);
395}
396
397/* Remove the last entry in the history list and add each argument in
398   LIST to the history. */
399static void
400push_history (list)
401     WORD_LIST *list;
402{
403  char *s;
404
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 && bash_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      && bash_delete_last_history () == 0)
419#endif
420      return;
421
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 */
430
431  hist_last_line_pushed = 1;	/* XXX */
432  free (s);
433}
434
435#if defined (BANG_HISTORY)
436static int
437expand_and_print_history (list)
438     WORD_LIST *list;
439{
440  char *s;
441  int r, result;
442
443  if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0)
444    return EXECUTION_FAILURE;
445  result = EXECUTION_SUCCESS;
446  while (list)
447    {
448      r = history_expand (list->word->word, &s);
449      if (r < 0)
450	{
451	  builtin_error (_("%s: history expansion failed"), list->word->word);
452	  result = EXECUTION_FAILURE;
453	}
454      else
455	{
456	  fputs (s, stdout);
457	  putchar ('\n');
458	}
459      FREE (s);
460      list = list->next;
461    }
462  fflush (stdout);
463  return result;
464}
465#endif /* BANG_HISTORY */
466#endif /* HISTORY */
467