]>
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 | ||
4 | Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. | |
5 | ||
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
8 | Bash is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 1, or (at your option) any later | |
11 | version. | |
12 | ||
13 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along | |
19 | with Bash; see the file COPYING. If not, write to the Free Software | |
20 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | ||
22 | $PRODUCES history.c | |
23 | ||
24 | $BUILTIN history | |
25 | $FUNCTION history_builtin | |
26 | $DEPENDS_ON HISTORY | |
ccc6cda3 | 27 | $SHORT_DOC history [-c] [n] or history -awrn [filename] or history -ps arg [arg...] |
726f6388 JA |
28 | Display the history list with line numbers. Lines listed with |
29 | with a `*' have been modified. Argument of N says to list only | |
ccc6cda3 JA |
30 | the last N lines. The -c option causes the history list to be |
31 | cleared by deleting all of the entries. The `-w' option writes out the | |
32 | current history to the history file; `-r' means to read the file and | |
33 | append the contents to the history list instead. `-a' means | |
726f6388 JA |
34 | to append history lines from this session to the history file. |
35 | Argument `-n' means to read all history lines not already read | |
ccc6cda3 JA |
36 | from the history file and append them to the history list. If |
37 | FILENAME is given, then that is used as the history file else | |
38 | if $HISTFILE has a value, that is used, else ~/.bash_history. | |
39 | If the -s option is supplied, the non-option ARGs are appended to | |
40 | the history list as a single entry. The -p option means to perform | |
41 | history expansion on each ARG and display the result, without storing | |
42 | anything in the history list. | |
726f6388 JA |
43 | $END |
44 | ||
ccc6cda3 JA |
45 | #include <config.h> |
46 | ||
726f6388 | 47 | #if defined (HISTORY) |
d166f048 | 48 | #include "../bashtypes.h" |
cce855bc JA |
49 | #ifndef _MINIX |
50 | # include <sys/file.h> | |
51 | #endif | |
726f6388 | 52 | #include "../posixstat.h" |
ccc6cda3 JA |
53 | #include "../filecntl.h" |
54 | #include <errno.h> | |
55 | #include <stdio.h> | |
56 | #if defined (HAVE_UNISTD_H) | |
57 | # include <unistd.h> | |
58 | #endif | |
59 | ||
60 | #include "../bashansi.h" | |
61 | ||
62 | #include "../shell.h" | |
726f6388 JA |
63 | #include "../bashhist.h" |
64 | #include <readline/history.h> | |
ccc6cda3 JA |
65 | #include "bashgetopt.h" |
66 | #include "common.h" | |
67 | ||
68 | #if !defined (errno) | |
69 | extern int errno; | |
70 | #endif | |
726f6388 | 71 | |
ccc6cda3 JA |
72 | static void display_history (); |
73 | static void push_history (); | |
74 | static int expand_and_print_history (); | |
726f6388 | 75 | |
ccc6cda3 JA |
76 | #define AFLAG 0x01 |
77 | #define RFLAG 0x02 | |
78 | #define WFLAG 0x04 | |
79 | #define NFLAG 0x08 | |
80 | #define SFLAG 0x10 | |
81 | #define PFLAG 0x20 | |
82 | #define CFLAG 0x40 | |
83 | ||
84 | int | |
726f6388 JA |
85 | history_builtin (list) |
86 | WORD_LIST *list; | |
87 | { | |
ccc6cda3 JA |
88 | int flags, opt, result; |
89 | char *filename; | |
726f6388 | 90 | |
ccc6cda3 JA |
91 | flags = 0; |
92 | reset_internal_getopt (); | |
93 | while ((opt = internal_getopt (list, "acnpsrw")) != -1) | |
726f6388 | 94 | { |
ccc6cda3 | 95 | switch (opt) |
726f6388 | 96 | { |
ccc6cda3 JA |
97 | case 'a': |
98 | flags |= AFLAG; | |
726f6388 | 99 | break; |
ccc6cda3 JA |
100 | case 'c': |
101 | flags |= CFLAG; | |
102 | break; | |
103 | case 'n': | |
104 | flags |= NFLAG; | |
105 | break; | |
106 | case 'r': | |
107 | flags |= RFLAG; | |
108 | break; | |
109 | case 'w': | |
110 | flags |= WFLAG; | |
111 | break; | |
112 | case 's': | |
113 | flags |= SFLAG; | |
114 | break; | |
115 | case 'p': | |
116 | #if defined (BANG_HISTORY) | |
117 | flags |= PFLAG; | |
118 | #endif | |
119 | break; | |
120 | default: | |
121 | builtin_usage (); | |
726f6388 JA |
122 | return (EX_USAGE); |
123 | } | |
ccc6cda3 JA |
124 | } |
125 | list = loptend; | |
126 | ||
127 | opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG); | |
128 | if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG) | |
129 | { | |
130 | builtin_error ("cannot use more than one of -anrw"); | |
131 | return (EXECUTION_FAILURE); | |
132 | } | |
133 | ||
134 | /* clear the history, but allow other arguments to add to it again. */ | |
135 | if (flags & CFLAG) | |
136 | { | |
137 | clear_history (); | |
138 | if (list == 0) | |
139 | return (EXECUTION_SUCCESS); | |
726f6388 JA |
140 | } |
141 | ||
ccc6cda3 JA |
142 | if (flags & SFLAG) |
143 | { | |
144 | if (list) | |
145 | push_history (list); | |
146 | return (EXECUTION_SUCCESS); | |
147 | } | |
148 | #if defined (BANG_HISTORY) | |
149 | else if (flags & PFLAG) | |
150 | { | |
151 | if (list) | |
152 | return (expand_and_print_history (list)); | |
153 | return (EXECUTION_SUCCESS); | |
154 | } | |
155 | #endif | |
156 | else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0) | |
157 | { | |
158 | display_history (list); | |
159 | return (EXECUTION_SUCCESS); | |
160 | } | |
161 | ||
162 | filename = list ? list->word->word : get_string_value ("HISTFILE"); | |
163 | result = EXECUTION_SUCCESS; | |
164 | ||
165 | if (flags & AFLAG) /* Append session's history to file. */ | |
166 | result = maybe_append_history (filename); | |
167 | else if (flags & WFLAG) /* Write entire history. */ | |
168 | result = write_history (filename); | |
169 | else if (flags & RFLAG) /* Read entire file. */ | |
170 | result = read_history (filename); | |
171 | else if (flags & NFLAG) /* Read `new' history from file. */ | |
172 | { | |
173 | /* Read all of the lines in the file that we haven't already read. */ | |
174 | using_history (); | |
175 | result = read_history_range (filename, history_lines_in_file, -1); | |
176 | using_history (); | |
177 | history_lines_in_file = where_history (); | |
178 | } | |
179 | ||
180 | return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS); | |
181 | } | |
182 | ||
183 | /* Accessors for HIST_ENTRY lists that are called HLIST. */ | |
184 | #define histline(i) (hlist[(i)]->line) | |
185 | #define histdata(i) (hlist[(i)]->data) | |
186 | ||
187 | static void | |
188 | display_history (list) | |
189 | WORD_LIST *list; | |
190 | { | |
191 | register int i; | |
192 | int limited, limit; | |
193 | HIST_ENTRY **hlist; | |
194 | ||
726f6388 JA |
195 | if (list) |
196 | { | |
197 | limited = 1; | |
d166f048 | 198 | limit = get_numeric_arg (list, 0); |
726f6388 | 199 | } |
ccc6cda3 JA |
200 | else |
201 | limited = limit = 0; | |
726f6388 JA |
202 | |
203 | hlist = history_list (); | |
204 | ||
205 | if (hlist) | |
206 | { | |
ccc6cda3 JA |
207 | for (i = 0; hlist[i]; i++) |
208 | ; | |
726f6388 JA |
209 | |
210 | if (limit < 0) | |
211 | limit = -limit; | |
212 | ||
ccc6cda3 | 213 | if ((limited == 0) || ((i -= limit) < 0)) |
726f6388 | 214 | i = 0; |
726f6388 JA |
215 | |
216 | while (hlist[i]) | |
217 | { | |
218 | QUIT; | |
219 | printf ("%5d%c %s\n", i + history_base, | |
ccc6cda3 JA |
220 | histdata(i) ? '*' : ' ', |
221 | histline(i)); | |
726f6388 JA |
222 | i++; |
223 | } | |
224 | } | |
726f6388 | 225 | } |
ccc6cda3 JA |
226 | |
227 | static int | |
228 | delete_last_history () | |
229 | { | |
230 | register int i; | |
231 | HIST_ENTRY **hlist, *histent, *discard; | |
232 | ||
233 | hlist = history_list (); | |
234 | if (hlist == NULL) | |
235 | return 0; | |
236 | ||
237 | for (i = 0; hlist[i]; i++) | |
238 | ; | |
239 | i--; | |
240 | ||
241 | /* History_get () takes a parameter that must be offset by history_base. */ | |
242 | histent = history_get (history_base + i); /* Don't free this */ | |
243 | if (histent == NULL) | |
244 | return 0; | |
245 | ||
246 | discard = remove_history (i); | |
247 | if (discard) | |
248 | { | |
249 | if (discard->line) | |
250 | free (discard->line); | |
251 | free ((char *) discard); | |
252 | } | |
253 | return (1); | |
254 | } | |
255 | ||
256 | /* Remove the last entry in the history list and add each argument in | |
257 | LIST to the history. */ | |
258 | static void | |
259 | push_history (list) | |
260 | WORD_LIST *list; | |
261 | { | |
262 | char *s; | |
263 | ||
d166f048 | 264 | if (hist_last_line_added && delete_last_history () == 0) |
ccc6cda3 JA |
265 | return; |
266 | s = string_list (list); | |
267 | maybe_add_history (s); /* Obeys HISTCONTROL setting. */ | |
268 | free (s); | |
269 | } | |
270 | ||
271 | #if defined (BANG_HISTORY) | |
272 | static int | |
273 | expand_and_print_history (list) | |
274 | WORD_LIST *list; | |
275 | { | |
276 | char *s; | |
277 | int r, result; | |
278 | ||
d166f048 | 279 | if (hist_last_line_added && delete_last_history () == 0) |
ccc6cda3 JA |
280 | return EXECUTION_FAILURE; |
281 | result = EXECUTION_SUCCESS; | |
282 | while (list) | |
283 | { | |
284 | r = history_expand (list->word->word, &s); | |
285 | if (r < 0) | |
286 | { | |
287 | builtin_error ("%s: history expansion failed", list->word->word); | |
288 | result = EXECUTION_FAILURE; | |
289 | } | |
290 | else | |
291 | { | |
292 | fputs (s, stdout); | |
293 | putchar ('\n'); | |
294 | } | |
295 | FREE (s); | |
296 | list = list->next; | |
297 | } | |
298 | fflush (stdout); | |
299 | return result; | |
300 | } | |
301 | #endif /* BANG_HISTORY */ | |
726f6388 | 302 | #endif /* HISTORY */ |