]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | This file is history.def, from which is created history.c. |
2 | It implements the builtin "history" in Bash. | |
3 | ||
3185942a | 4 | Copyright (C) 1987-2009 Free Software Foundation, Inc. |
726f6388 JA |
5 | |
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
3185942a JA |
8 | Bash 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. | |
726f6388 | 12 | |
3185942a JA |
13 | Bash 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. | |
726f6388 | 17 | |
3185942a JA |
18 | You should have received a copy of the GNU General Public License |
19 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
726f6388 JA |
20 | |
21 | $PRODUCES history.c | |
22 | ||
23 | $BUILTIN history | |
24 | $FUNCTION history_builtin | |
25 | $DEPENDS_ON HISTORY | |
3185942a JA |
26 | $SHORT_DOC history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...] |
27 | Display or manipulate the history list. | |
28 | ||
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. | |
31 | ||
32 | Options: | |
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 | ||
47 | If FILENAME is given, it is used as the history file. Otherwise, | |
ccc6cda3 | 48 | if $HISTFILE has a value, that is used, else ~/.bash_history. |
b80f6443 JA |
49 | |
50 | If the $HISTTIMEFORMAT variable is set and not null, its value is used | |
51 | as a format string for strftime(3) to print the time stamp associated | |
52 | with each displayed history entry. No time stamps are printed otherwise. | |
3185942a JA |
53 | |
54 | Exit Status: | |
55 | Returns success unless an invalid option is given or an error occurs. | |
726f6388 JA |
56 | $END |
57 | ||
ccc6cda3 JA |
58 | #include <config.h> |
59 | ||
726f6388 | 60 | #if defined (HISTORY) |
d166f048 | 61 | #include "../bashtypes.h" |
b80f6443 | 62 | #if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) |
cce855bc JA |
63 | # include <sys/file.h> |
64 | #endif | |
bb70624e JA |
65 | #include "posixstat.h" |
66 | #include "filecntl.h" | |
ccc6cda3 JA |
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" | |
b80f6443 | 74 | #include "../bashintl.h" |
ccc6cda3 JA |
75 | |
76 | #include "../shell.h" | |
726f6388 JA |
77 | #include "../bashhist.h" |
78 | #include <readline/history.h> | |
ccc6cda3 JA |
79 | #include "bashgetopt.h" |
80 | #include "common.h" | |
81 | ||
82 | #if !defined (errno) | |
83 | extern int errno; | |
84 | #endif | |
726f6388 | 85 | |
7117c2d2 | 86 | extern int current_command_line_count; |
95732b49 JA |
87 | extern int force_append_history; /* shopt -s histappend */ |
88 | ||
b80f6443 | 89 | static char *histtime __P((HIST_ENTRY *, const char *)); |
3185942a | 90 | static int display_history __P((WORD_LIST *)); |
f73dda09 JA |
91 | static void push_history __P((WORD_LIST *)); |
92 | static int expand_and_print_history __P((WORD_LIST *)); | |
726f6388 | 93 | |
ccc6cda3 JA |
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 | |
bb70624e | 101 | #define DFLAG 0x80 |
ccc6cda3 JA |
102 | |
103 | int | |
726f6388 JA |
104 | history_builtin (list) |
105 | WORD_LIST *list; | |
106 | { | |
b80f6443 | 107 | int flags, opt, result, old_history_lines, obase; |
bb70624e | 108 | char *filename, *delete_arg; |
7117c2d2 | 109 | intmax_t delete_offset; |
726f6388 | 110 | |
ccc6cda3 JA |
111 | flags = 0; |
112 | reset_internal_getopt (); | |
bb70624e | 113 | while ((opt = internal_getopt (list, "acd:npsrw")) != -1) |
726f6388 | 114 | { |
ccc6cda3 | 115 | switch (opt) |
726f6388 | 116 | { |
ccc6cda3 JA |
117 | case 'a': |
118 | flags |= AFLAG; | |
726f6388 | 119 | break; |
ccc6cda3 JA |
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; | |
bb70624e JA |
135 | case 'd': |
136 | flags |= DFLAG; | |
137 | delete_arg = list_optarg; | |
138 | break; | |
ccc6cda3 JA |
139 | case 'p': |
140 | #if defined (BANG_HISTORY) | |
141 | flags |= PFLAG; | |
142 | #endif | |
143 | break; | |
144 | default: | |
145 | builtin_usage (); | |
726f6388 JA |
146 | return (EX_USAGE); |
147 | } | |
ccc6cda3 JA |
148 | } |
149 | list = loptend; | |
150 | ||
151 | opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG); | |
152 | if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG) | |
153 | { | |
b80f6443 | 154 | builtin_error (_("cannot use more than one of -anrw")); |
ccc6cda3 JA |
155 | return (EXECUTION_FAILURE); |
156 | } | |
157 | ||
158 | /* clear the history, but allow other arguments to add to it again. */ | |
159 | if (flags & CFLAG) | |
160 | { | |
3185942a | 161 | bash_clear_history (); |
ccc6cda3 JA |
162 | if (list == 0) |
163 | return (EXECUTION_SUCCESS); | |
726f6388 JA |
164 | } |
165 | ||
ccc6cda3 JA |
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) | |
28ef6c31 | 176 | return (expand_and_print_history (list)); |
3185942a | 177 | return (sh_chkwrite (EXECUTION_SUCCESS)); |
ccc6cda3 JA |
178 | } |
179 | #endif | |
bb70624e JA |
180 | else if (flags & DFLAG) |
181 | { | |
f73dda09 JA |
182 | if ((legal_number (delete_arg, &delete_offset) == 0) |
183 | || (delete_offset < history_base) | |
184 | || (delete_offset > (history_base + history_length))) | |
bb70624e | 185 | { |
b80f6443 | 186 | sh_erange (delete_arg, _("history position")); |
bb70624e JA |
187 | return (EXECUTION_FAILURE); |
188 | } | |
189 | opt = delete_offset; | |
3185942a | 190 | result = bash_delete_histent (opt - history_base); |
bb70624e JA |
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 | } | |
ccc6cda3 JA |
197 | else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0) |
198 | { | |
3185942a JA |
199 | result = display_history (list); |
200 | return (sh_chkwrite (result)); | |
ccc6cda3 JA |
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. */ | |
7117c2d2 | 215 | old_history_lines = history_lines_in_file; |
b80f6443 JA |
216 | obase = history_base; |
217 | ||
ccc6cda3 JA |
218 | using_history (); |
219 | result = read_history_range (filename, history_lines_in_file, -1); | |
220 | using_history (); | |
b80f6443 | 221 | |
ccc6cda3 | 222 | history_lines_in_file = where_history (); |
95732b49 JA |
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. | |
b80f6443 JA |
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. */ | |
95732b49 JA |
234 | if (force_append_history == 0) |
235 | history_lines_this_session += history_lines_in_file - old_history_lines + | |
b80f6443 | 236 | history_base - obase; |
ccc6cda3 JA |
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 | ||
b80f6443 JA |
246 | static char * |
247 | histtime (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 | ||
3185942a | 262 | static int |
ccc6cda3 JA |
263 | display_history (list) |
264 | WORD_LIST *list; | |
265 | { | |
266 | register int i; | |
7117c2d2 | 267 | intmax_t limit; |
ccc6cda3 | 268 | HIST_ENTRY **hlist; |
b80f6443 | 269 | char *histtimefmt, *timestr; |
ccc6cda3 | 270 | |
726f6388 JA |
271 | if (list) |
272 | { | |
3185942a JA |
273 | if (get_numeric_arg (list, 0, &limit) == 0) |
274 | return (EXECUTION_FAILURE); | |
275 | ||
f73dda09 JA |
276 | if (limit < 0) |
277 | limit = -limit; | |
726f6388 | 278 | } |
ccc6cda3 | 279 | else |
f73dda09 | 280 | limit = -1; |
726f6388 JA |
281 | |
282 | hlist = history_list (); | |
283 | ||
284 | if (hlist) | |
285 | { | |
ccc6cda3 JA |
286 | for (i = 0; hlist[i]; i++) |
287 | ; | |
726f6388 | 288 | |
f73dda09 JA |
289 | if (0 <= limit && limit < i) |
290 | i -= limit; | |
291 | else | |
726f6388 | 292 | i = 0; |
726f6388 | 293 | |
b80f6443 JA |
294 | histtimefmt = get_string_value ("HISTTIMEFORMAT"); |
295 | ||
726f6388 JA |
296 | while (hlist[i]) |
297 | { | |
298 | QUIT; | |
b80f6443 JA |
299 | |
300 | timestr = (histtimefmt && *histtimefmt) ? histtime (hlist[i], histtimefmt) : (char *)NULL; | |
301 | printf ("%5d%c %s%s\n", i + history_base, | |
ccc6cda3 | 302 | histdata(i) ? '*' : ' ', |
b80f6443 | 303 | ((timestr && *timestr) ? timestr : ""), |
ccc6cda3 | 304 | histline(i)); |
726f6388 JA |
305 | i++; |
306 | } | |
307 | } | |
b80f6443 | 308 | |
3185942a | 309 | return (EXECUTION_SUCCESS); |
ccc6cda3 JA |
310 | } |
311 | ||
312 | /* Remove the last entry in the history list and add each argument in | |
313 | LIST to the history. */ | |
314 | static void | |
315 | push_history (list) | |
316 | WORD_LIST *list; | |
317 | { | |
318 | char *s; | |
319 | ||
7117c2d2 JA |
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 | |
3185942a | 327 | if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0) |
7117c2d2 | 328 | #else |
95732b49 JA |
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)) | |
3185942a | 332 | && bash_delete_last_history () == 0) |
7117c2d2 JA |
333 | #endif |
334 | return; | |
335 | ||
ccc6cda3 | 336 | s = string_list (list); |
7117c2d2 JA |
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 */ | |
95732b49 JA |
344 | |
345 | hist_last_line_pushed = 1; /* XXX */ | |
ccc6cda3 JA |
346 | free (s); |
347 | } | |
348 | ||
349 | #if defined (BANG_HISTORY) | |
350 | static int | |
351 | expand_and_print_history (list) | |
352 | WORD_LIST *list; | |
353 | { | |
354 | char *s; | |
355 | int r, result; | |
356 | ||
3185942a | 357 | if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0) |
ccc6cda3 JA |
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 | { | |
b80f6443 | 365 | builtin_error (_("%s: history expansion failed"), list->word->word); |
ccc6cda3 JA |
366 | result = EXECUTION_FAILURE; |
367 | } | |
368 | else | |
28ef6c31 | 369 | { |
ccc6cda3 JA |
370 | fputs (s, stdout); |
371 | putchar ('\n'); | |
28ef6c31 | 372 | } |
ccc6cda3 JA |
373 | FREE (s); |
374 | list = list->next; | |
375 | } | |
376 | fflush (stdout); | |
377 | return result; | |
378 | } | |
379 | #endif /* BANG_HISTORY */ | |
726f6388 | 380 | #endif /* HISTORY */ |