blob: d7fc06fbb650cb5b9d8a3863e38a5fc9d1a0e06e [file] [log] [blame]
Dan Pasanenc6e37862014-10-02 14:08:59 -05001/* history.c, created from history.def. */
2#line 22 "./history.def"
3
4#line 57 "./history.def"
5
6#include <config.h>
7
8#if defined (HISTORY)
9#include "../bashtypes.h"
10#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
11# include <sys/file.h>
12#endif
13#include "posixstat.h"
14#include "filecntl.h"
15#include <errno.h>
16#include <stdio.h>
17#if defined (HAVE_UNISTD_H)
18# include <unistd.h>
19#endif
20
21#include "../bashansi.h"
22#include "../bashintl.h"
23
24#include "../shell.h"
25#include "../bashhist.h"
26#include <readline/history.h>
27#include "bashgetopt.h"
28#include "common.h"
29
30#if !defined (errno)
Ricardo Cerqueiraa02fbff2013-07-25 22:35:34 +010031#include <errno.h>
Dan Pasanenc6e37862014-10-02 14:08:59 -050032#endif
33
34extern int current_command_line_count;
35extern int force_append_history; /* shopt -s histappend */
36
37static char *histtime __P((HIST_ENTRY *, const char *));
38static int display_history __P((WORD_LIST *));
39static void push_history __P((WORD_LIST *));
40static int expand_and_print_history __P((WORD_LIST *));
41
42#define AFLAG 0x01
43#define RFLAG 0x02
44#define WFLAG 0x04
45#define NFLAG 0x08
46#define SFLAG 0x10
47#define PFLAG 0x20
48#define CFLAG 0x40
49#define DFLAG 0x80
50
51int
52history_builtin (list)
53 WORD_LIST *list;
54{
55 int flags, opt, result, old_history_lines, obase;
56 char *filename, *delete_arg;
57 intmax_t delete_offset;
58
59 flags = 0;
60 reset_internal_getopt ();
61 while ((opt = internal_getopt (list, "acd:npsrw")) != -1)
62 {
63 switch (opt)
64 {
65 case 'a':
66 flags |= AFLAG;
67 break;
68 case 'c':
69 flags |= CFLAG;
70 break;
71 case 'n':
72 flags |= NFLAG;
73 break;
74 case 'r':
75 flags |= RFLAG;
76 break;
77 case 'w':
78 flags |= WFLAG;
79 break;
80 case 's':
81 flags |= SFLAG;
82 break;
83 case 'd':
84 flags |= DFLAG;
85 delete_arg = list_optarg;
86 break;
87 case 'p':
88#if defined (BANG_HISTORY)
89 flags |= PFLAG;
90#endif
91 break;
92 default:
93 builtin_usage ();
94 return (EX_USAGE);
95 }
96 }
97 list = loptend;
98
99 opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG);
100 if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG)
101 {
102 builtin_error (_("cannot use more than one of -anrw"));
103 return (EXECUTION_FAILURE);
104 }
105
106 /* clear the history, but allow other arguments to add to it again. */
107 if (flags & CFLAG)
108 {
109 bash_clear_history ();
110 if (list == 0)
111 return (EXECUTION_SUCCESS);
112 }
113
114 if (flags & SFLAG)
115 {
116 if (list)
117 push_history (list);
118 return (EXECUTION_SUCCESS);
119 }
120#if defined (BANG_HISTORY)
121 else if (flags & PFLAG)
122 {
123 if (list)
124 return (expand_and_print_history (list));
125 return (sh_chkwrite (EXECUTION_SUCCESS));
126 }
127#endif
128 else if (flags & DFLAG)
129 {
130 if ((legal_number (delete_arg, &delete_offset) == 0)
131 || (delete_offset < history_base)
132 || (delete_offset > (history_base + history_length)))
133 {
134 sh_erange (delete_arg, _("history position"));
135 return (EXECUTION_FAILURE);
136 }
137 opt = delete_offset;
138 result = bash_delete_histent (opt - history_base);
139 /* Since remove_history changes history_length, this can happen if
140 we delete the last history entry. */
141 if (where_history () > history_length)
142 history_set_pos (history_length);
143 return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
144 }
145 else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0)
146 {
147 result = display_history (list);
148 return (sh_chkwrite (result));
149 }
150
151 filename = list ? list->word->word : get_string_value ("HISTFILE");
152 result = EXECUTION_SUCCESS;
153
154 if (flags & AFLAG) /* Append session's history to file. */
155 result = maybe_append_history (filename);
156 else if (flags & WFLAG) /* Write entire history. */
157 result = write_history (filename);
158 else if (flags & RFLAG) /* Read entire file. */
159 result = read_history (filename);
160 else if (flags & NFLAG) /* Read `new' history from file. */
161 {
162 /* Read all of the lines in the file that we haven't already read. */
163 old_history_lines = history_lines_in_file;
164 obase = history_base;
165
166 using_history ();
167 result = read_history_range (filename, history_lines_in_file, -1);
168 using_history ();
169
170 history_lines_in_file = where_history ();
171
172 /* If we're rewriting the history file at shell exit rather than just
173 appending the lines from this session to it, the question is whether
174 we reset history_lines_this_session to 0, losing any history entries
175 we had before we read the new entries from the history file, or
176 whether we count the new entries we just read from the file as
177 history lines added during this session.
178 Right now, we do the latter. This will cause these history entries
179 to be written to the history file along with any intermediate entries
180 we add when we do a `history -a', but the alternative is losing
181 them altogether. */
182 if (force_append_history == 0)
183 history_lines_this_session += history_lines_in_file - old_history_lines +
184 history_base - obase;
185 }
186
187 return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
188}
189
190/* Accessors for HIST_ENTRY lists that are called HLIST. */
191#define histline(i) (hlist[(i)]->line)
192#define histdata(i) (hlist[(i)]->data)
193
194static char *
195histtime (hlist, histtimefmt)
196 HIST_ENTRY *hlist;
197 const char *histtimefmt;
198{
199 static char timestr[128];
200 time_t t;
201
202 t = history_get_time (hlist);
203 if (t)
204 strftime (timestr, sizeof (timestr), histtimefmt, localtime (&t));
205 else
206 strcpy (timestr, "??");
207 return timestr;
208}
209
210static int
211display_history (list)
212 WORD_LIST *list;
213{
214 register int i;
215 intmax_t limit;
216 HIST_ENTRY **hlist;
217 char *histtimefmt, *timestr;
218
219 if (list)
220 {
221 if (get_numeric_arg (list, 0, &limit) == 0)
222 return (EXECUTION_FAILURE);
223
224 if (limit < 0)
225 limit = -limit;
226 }
227 else
228 limit = -1;
229
230 hlist = history_list ();
231
232 if (hlist)
233 {
234 for (i = 0; hlist[i]; i++)
235 ;
236
237 if (0 <= limit && limit < i)
238 i -= limit;
239 else
240 i = 0;
241
242 histtimefmt = get_string_value ("HISTTIMEFORMAT");
243
244 while (hlist[i])
245 {
246 QUIT;
247
248 timestr = (histtimefmt && *histtimefmt) ? histtime (hlist[i], histtimefmt) : (char *)NULL;
249 printf ("%5d%c %s%s\n", i + history_base,
250 histdata(i) ? '*' : ' ',
251 ((timestr && *timestr) ? timestr : ""),
252 histline(i));
253 i++;
254 }
255 }
256
257 return (EXECUTION_SUCCESS);
258}
259
260/* Remove the last entry in the history list and add each argument in
261 LIST to the history. */
262static void
263push_history (list)
264 WORD_LIST *list;
265{
266 char *s;
267
268 /* Delete the last history entry if it was a single entry added to the
269 history list (generally the `history -s' itself), or if `history -s'
270 is being used in a compound command and the compound command was
271 added to the history as a single element (command-oriented history).
272 If you don't want history -s to remove the compound command from the
273 history, change #if 0 to #if 1 below. */
274#if 0
275 if (remember_on_history && hist_last_line_pushed == 0 &&
276 hist_last_line_added && bash_delete_last_history () == 0)
277#else
278 if (remember_on_history && hist_last_line_pushed == 0 &&
279 (hist_last_line_added ||
280 (current_command_line_count > 0 && current_command_first_line_saved && command_oriented_history))
281 && bash_delete_last_history () == 0)
282#endif
283 return;
284
285 s = string_list (list);
286 /* Call check_add_history with FORCE set to 1 to skip the check against
287 current_command_line_count. If history -s is used in a compound
288 command, the above code will delete the compound command's history
289 entry and this call will add the line to the history as a separate
290 entry. Without FORCE=1, if current_command_line_count were > 1, the
291 line would be appended to the entry before the just-deleted entry. */
292 check_add_history (s, 1); /* obeys HISTCONTROL, HISTIGNORE */
293
294 hist_last_line_pushed = 1; /* XXX */
295 free (s);
296}
297
298#if defined (BANG_HISTORY)
299static int
300expand_and_print_history (list)
301 WORD_LIST *list;
302{
303 char *s;
304 int r, result;
305
306 if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0)
307 return EXECUTION_FAILURE;
308 result = EXECUTION_SUCCESS;
309 while (list)
310 {
311 r = history_expand (list->word->word, &s);
312 if (r < 0)
313 {
314 builtin_error (_("%s: history expansion failed"), list->word->word);
315 result = EXECUTION_FAILURE;
316 }
317 else
318 {
319 fputs (s, stdout);
320 putchar ('\n');
321 }
322 FREE (s);
323 list = list->next;
324 }
325 fflush (stdout);
326 return result;
327}
328#endif /* BANG_HISTORY */
329#endif /* HISTORY */