1This file is history.def, from which is created history.c.
2It implements the builtin "history" in Bash.
3
4Copyright (C) 1987-2009 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 offset OFFSET.
35
36  -a	append history lines from this session to the history file
37  -n	read all history lines not already read from the history file
38  -r	read the history file and append the contents to the history
39	list
40  -w	write the current history to the history file
41	and append them to the history list
42
43  -p	perform history expansion on each ARG and display the result
44	without storing it in the history list
45  -s	append the ARGs to the history list as a single entry
46
47If FILENAME is given, it is used as the history file.  Otherwise,
48if $HISTFILE has a value, that is used, else ~/.bash_history.
49
50If the $HISTTIMEFORMAT variable is set and not null, its value is used
51as a format string for strftime(3) to print the time stamp associated
52with each displayed history entry.  No time stamps are printed otherwise.
53
54Exit Status:
55Returns success unless an invalid option is given or an error occurs.
56$END
57
58#include <config.h>
59
60#if defined (HISTORY)
61#include "../bashtypes.h"
62#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
63#  include <sys/file.h>
64#endif
65#include "posixstat.h"
66#include "filecntl.h"
67#include <errno.h>
68#include <stdio.h>
69#if defined (HAVE_UNISTD_H)
70#  include <unistd.h>
71#endif
72
73#include "../bashansi.h"
74#include "../bashintl.h"
75
76#include "../shell.h"
77#include "../bashhist.h"
78#include <readline/history.h>
79#include "bashgetopt.h"
80#include "common.h"
81
82#if !defined (errno)
83extern int errno;
84#endif
85
86extern int current_command_line_count;
87extern int force_append_history;	/* shopt -s histappend */
88
89static char *histtime __P((HIST_ENTRY *, const char *));
90static int display_history __P((WORD_LIST *));
91static void push_history __P((WORD_LIST *));
92static int expand_and_print_history __P((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;
108  char *filename, *delete_arg;
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	default:
145	  builtin_usage ();
146	  return (EX_USAGE);
147	}
148    }
149  list = loptend;
150
151  opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG);
152  if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG)
153    {
154      builtin_error (_("cannot use more than one of -anrw"));
155      return (EXECUTION_FAILURE);
156    }
157
158  /* clear the history, but allow other arguments to add to it again. */
159  if (flags & CFLAG)
160    {
161      bash_clear_history ();
162      if (list == 0)
163	return (EXECUTION_SUCCESS);
164    }
165
166  if (flags & SFLAG)
167    {
168      if (list)
169	push_history (list);
170      return (EXECUTION_SUCCESS);
171    }
172#if defined (BANG_HISTORY)
173  else if (flags & PFLAG)
174    {
175      if (list)
176	return (expand_and_print_history (list));
177      return (sh_chkwrite (EXECUTION_SUCCESS));
178    }
179#endif
180  else if (flags & DFLAG)
181    {
182      if ((legal_number (delete_arg, &delete_offset) == 0)
183	  || (delete_offset < history_base)
184	  || (delete_offset > (history_base + history_length)))
185	{
186	  sh_erange (delete_arg, _("history position"));
187	  return (EXECUTION_FAILURE);
188	}
189      opt = delete_offset;
190      result = bash_delete_histent (opt - history_base);
191      /* Since remove_history changes history_length, this can happen if
192	 we delete the last history entry. */
193      if (where_history () > history_length)
194	history_set_pos (history_length);
195      return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
196    }
197  else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0)
198    {
199      result = display_history (list);
200      return (sh_chkwrite (result));
201    }
202
203  filename = list ? list->word->word : get_string_value ("HISTFILE");
204  result = EXECUTION_SUCCESS;
205
206  if (flags & AFLAG)		/* Append session's history to file. */
207    result = maybe_append_history (filename);
208  else if (flags & WFLAG)	/* Write entire history. */
209    result = write_history (filename);
210  else if (flags & RFLAG)	/* Read entire file. */
211    result = read_history (filename);
212  else if (flags & NFLAG)	/* Read `new' history from file. */
213    {
214      /* Read all of the lines in the file that we haven't already read. */
215      old_history_lines = history_lines_in_file;
216      obase = history_base;
217
218      using_history ();
219      result = read_history_range (filename, history_lines_in_file, -1);
220      using_history ();
221
222      history_lines_in_file = where_history ();
223
224      /* If we're rewriting the history file at shell exit rather than just
225	 appending the lines from this session to it, the question is whether
226	 we reset history_lines_this_session to 0, losing any history entries
227	 we had before we read the new entries from the history file, or
228	 whether we count the new entries we just read from the file as
229	 history lines added during this session.
230	 Right now, we do the latter.  This will cause these history entries
231	 to be written to the history file along with any intermediate entries
232	 we add when we do a `history -a', but the alternative is losing
233	 them altogether. */
234      if (force_append_history == 0)
235	history_lines_this_session += history_lines_in_file - old_history_lines +
236				    history_base - obase;
237    }
238
239  return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
240}
241
242/* Accessors for HIST_ENTRY lists that are called HLIST. */
243#define histline(i) (hlist[(i)]->line)
244#define histdata(i) (hlist[(i)]->data)
245
246static char *
247histtime (hlist, histtimefmt)
248     HIST_ENTRY *hlist;
249     const char *histtimefmt;
250{
251  static char timestr[128];
252  time_t t;
253
254  t = history_get_time (hlist);
255  if (t)
256    strftime (timestr, sizeof (timestr), histtimefmt, localtime (&t));
257  else
258    strcpy (timestr, "??");
259  return timestr;
260}
261
262static int
263display_history (list)
264     WORD_LIST *list;
265{
266  register int i;
267  intmax_t limit;
268  HIST_ENTRY **hlist;
269  char *histtimefmt, *timestr;
270
271  if (list)
272    {
273      if (get_numeric_arg (list, 0, &limit) == 0)
274	return (EXECUTION_FAILURE);
275
276      if (limit < 0)
277	limit = -limit;
278    }
279  else
280    limit = -1;
281
282  hlist = history_list ();
283
284  if (hlist)
285    {
286      for (i = 0;  hlist[i]; i++)
287	;
288
289      if (0 <= limit && limit < i)
290	i -= limit;
291      else
292	i = 0;
293
294      histtimefmt = get_string_value ("HISTTIMEFORMAT");
295
296      while (hlist[i])
297	{
298	  QUIT;
299
300	  timestr = (histtimefmt && *histtimefmt) ? histtime (hlist[i], histtimefmt) : (char *)NULL;
301	  printf ("%5d%c %s%s\n", i + history_base,
302		  histdata(i) ? '*' : ' ',
303		  ((timestr && *timestr) ? timestr : ""),
304		  histline(i));
305	  i++;
306	}
307    }
308
309  return (EXECUTION_SUCCESS);
310}
311
312/* Remove the last entry in the history list and add each argument in
313   LIST to the history. */
314static void
315push_history (list)
316     WORD_LIST *list;
317{
318  char *s;
319
320  /* Delete the last history entry if it was a single entry added to the
321     history list (generally the `history -s' itself), or if `history -s'
322     is being used in a compound command and the compound command was
323     added to the history as a single element (command-oriented history).
324     If you don't want history -s to remove the compound command from the
325     history, change #if 0 to #if 1 below. */
326#if 0
327  if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0)
328#else
329  if (hist_last_line_pushed == 0 &&
330	(hist_last_line_added ||
331	  (current_command_line_count > 0 && current_command_first_line_saved && command_oriented_history))
332      && bash_delete_last_history () == 0)
333#endif
334      return;
335
336  s = string_list (list);
337  /* Call check_add_history with FORCE set to 1 to skip the check against
338     current_command_line_count.  If history -s is used in a compound
339     command, the above code will delete the compound command's history
340     entry and this call will add the line to the history as a separate
341     entry.  Without FORCE=1, if current_command_line_count were > 1, the
342     line would be appended to the entry before the just-deleted entry. */
343  check_add_history (s, 1);	/* obeys HISTCONTROL, HISTIGNORE */
344
345  hist_last_line_pushed = 1;	/* XXX */
346  free (s);
347}
348
349#if defined (BANG_HISTORY)
350static int
351expand_and_print_history (list)
352     WORD_LIST *list;
353{
354  char *s;
355  int r, result;
356
357  if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0)
358    return EXECUTION_FAILURE;
359  result = EXECUTION_SUCCESS;
360  while (list)
361    {
362      r = history_expand (list->word->word, &s);
363      if (r < 0)
364	{
365	  builtin_error (_("%s: history expansion failed"), list->word->word);
366	  result = EXECUTION_FAILURE;
367	}
368      else
369	{
370	  fputs (s, stdout);
371	  putchar ('\n');
372	}
373      FREE (s);
374      list = list->next;
375    }
376  fflush (stdout);
377  return result;
378}
379#endif /* BANG_HISTORY */
380#endif /* HISTORY */
381