]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | This file is fc.def, from which is created fc.c. |
2 | It implements the builtin "fc" in Bash. | |
3 | ||
74091dd4 | 4 | Copyright (C) 1987-2021 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 fc.c | |
22 | ||
23 | $BUILTIN fc | |
24 | $FUNCTION fc_builtin | |
25 | $DEPENDS_ON HISTORY | |
3185942a JA |
26 | $SHORT_DOC fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command] |
27 | Display or execute commands from the history list. | |
28 | ||
b72432fd | 29 | fc is used to list or edit and re-execute commands from the history list. |
726f6388 JA |
30 | FIRST and LAST can be numbers specifying the range, or FIRST can be a |
31 | string, which means the most recent command beginning with that | |
32 | string. | |
33 | ||
3185942a JA |
34 | Options: |
35 | -e ENAME select which editor to use. Default is FCEDIT, then EDITOR, | |
36 | then vi | |
37 | -l list lines instead of editing | |
38 | -n omit line numbers when listing | |
39 | -r reverse the order of the lines (newest listed first) | |
726f6388 | 40 | |
3185942a | 41 | With the `fc -s [pat=rep ...] [command]' format, COMMAND is |
726f6388 JA |
42 | re-executed after the substitution OLD=NEW is performed. |
43 | ||
44 | A useful alias to use with this is r='fc -s', so that typing `r cc' | |
45 | runs the last command beginning with `cc' and typing `r' re-executes | |
46 | the last command. | |
3185942a JA |
47 | |
48 | Exit Status: | |
49 | Returns success or status of executed command; non-zero if an error occurs. | |
726f6388 JA |
50 | $END |
51 | ||
ccc6cda3 JA |
52 | #include <config.h> |
53 | ||
726f6388 | 54 | #if defined (HISTORY) |
ac50fbac | 55 | #if defined (HAVE_SYS_PARAM_H) |
cce855bc JA |
56 | # include <sys/param.h> |
57 | #endif | |
d166f048 | 58 | #include "../bashtypes.h" |
bb70624e | 59 | #include "posixstat.h" |
b80f6443 | 60 | #if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) |
cce855bc JA |
61 | # include <sys/file.h> |
62 | #endif | |
ccc6cda3 JA |
63 | |
64 | #if defined (HAVE_UNISTD_H) | |
65 | # include <unistd.h> | |
66 | #endif | |
67 | ||
68 | #include <stdio.h> | |
f73dda09 | 69 | #include <chartypes.h> |
ccc6cda3 JA |
70 | |
71 | #include "../bashansi.h" | |
b80f6443 | 72 | #include "../bashintl.h" |
726f6388 | 73 | #include <errno.h> |
ccc6cda3 JA |
74 | |
75 | #include "../shell.h" | |
726f6388 JA |
76 | #include "../builtins.h" |
77 | #include "../flags.h" | |
d233b485 | 78 | #include "../parser.h" |
726f6388 | 79 | #include "../bashhist.h" |
bb70624e | 80 | #include "maxpath.h" |
726f6388 JA |
81 | #include <readline/history.h> |
82 | #include "bashgetopt.h" | |
ccc6cda3 | 83 | #include "common.h" |
726f6388 | 84 | |
726f6388 JA |
85 | #if !defined (errno) |
86 | extern int errno; | |
87 | #endif /* !errno */ | |
88 | ||
8868edaf CR |
89 | #define HIST_INVALID INT_MIN |
90 | #define HIST_ERANGE INT_MIN+1 | |
91 | #define HIST_NOTFOUND INT_MIN+2 | |
726f6388 | 92 | |
8868edaf CR |
93 | /* Values for the flags argument to fc_gethnum */ |
94 | #define HN_LISTING 0x01 | |
95 | #define HN_FIRST 0x02 | |
96 | ||
97 | extern int unlink PARAMS((const char *)); | |
98 | ||
99 | extern FILE *sh_mktmpfp PARAMS((char *, int, char **)); | |
100 | ||
101 | extern int suppress_debug_trap_verbose; | |
d166f048 | 102 | |
726f6388 JA |
103 | /* **************************************************************** */ |
104 | /* */ | |
105 | /* The K*rn shell style fc command (Fix Command) */ | |
106 | /* */ | |
107 | /* **************************************************************** */ | |
108 | ||
109 | /* fc builtin command (fix command) for Bash for those who | |
110 | like K*rn-style history better than csh-style. | |
111 | ||
112 | fc [-e ename] [-nlr] [first] [last] | |
113 | ||
114 | FIRST and LAST can be numbers specifying the range, or FIRST can be | |
115 | a string, which means the most recent command beginning with that | |
116 | string. | |
117 | ||
118 | -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR, | |
119 | then the editor which corresponds to the current readline editing | |
120 | mode, then vi. | |
121 | ||
122 | -l means list lines instead of editing. | |
123 | -n means no line numbers listed. | |
124 | -r means reverse the order of the lines (making it newest listed first). | |
125 | ||
126 | fc -e - [pat=rep ...] [command] | |
127 | fc -s [pat=rep ...] [command] | |
128 | ||
129 | Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's. | |
130 | */ | |
131 | ||
726f6388 JA |
132 | /* Data structure describing a list of global replacements to perform. */ |
133 | typedef struct repl { | |
134 | struct repl *next; | |
135 | char *pat; | |
136 | char *rep; | |
137 | } REPL; | |
138 | ||
726f6388 JA |
139 | /* Accessors for HIST_ENTRY lists that are called HLIST. */ |
140 | #define histline(i) (hlist[(i)]->line) | |
141 | #define histdata(i) (hlist[(i)]->data) | |
142 | ||
143 | #define FREE_RLIST() \ | |
144 | do { \ | |
145 | for (rl = rlist; rl; ) { \ | |
146 | REPL *r; \ | |
147 | r = rl->next; \ | |
148 | if (rl->pat) \ | |
149 | free (rl->pat); \ | |
150 | if (rl->rep) \ | |
151 | free (rl->rep); \ | |
152 | free (rl); \ | |
153 | rl = r; \ | |
154 | } \ | |
155 | } while (0) | |
156 | ||
8868edaf CR |
157 | static char *fc_dosubs PARAMS((char *, REPL *)); |
158 | static char *fc_gethist PARAMS((char *, HIST_ENTRY **, int)); | |
159 | static int fc_gethnum PARAMS((char *, HIST_ENTRY **, int)); | |
160 | static int fc_number PARAMS((WORD_LIST *)); | |
161 | static void fc_replhist PARAMS((char *)); | |
f73dda09 | 162 | #ifdef INCLUDE_UNUSED |
8868edaf CR |
163 | static char *fc_readline PARAMS((FILE *)); |
164 | static void fc_addhist PARAMS((char *)); | |
f73dda09 JA |
165 | #endif |
166 | ||
a0c0a00f CR |
167 | static void |
168 | set_verbose_flag () | |
169 | { | |
170 | echo_input_at_read = verbose_flag; | |
171 | } | |
172 | ||
726f6388 JA |
173 | /* String to execute on a file that we want to edit. */ |
174 | #define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}" | |
95732b49 JA |
175 | #if defined (STRICT_POSIX) |
176 | # define POSIX_FC_EDIT_COMMAND "${FCEDIT:-ed}" | |
177 | #else | |
178 | # define POSIX_FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-ed}}" | |
179 | #endif | |
726f6388 JA |
180 | |
181 | int | |
182 | fc_builtin (list) | |
183 | WORD_LIST *list; | |
184 | { | |
185 | register int i; | |
186 | register char *sep; | |
187 | int numbering, reverse, listing, execute; | |
ac50fbac | 188 | int histbeg, histend, last_hist, retval, opt, rh, real_last; |
726f6388 | 189 | FILE *stream; |
ccc6cda3 | 190 | REPL *rlist, *rl; |
95732b49 | 191 | char *ename, *command, *newcom, *fcedit; |
726f6388 | 192 | HIST_ENTRY **hlist; |
28ef6c31 | 193 | char *fn; |
726f6388 JA |
194 | |
195 | numbering = 1; | |
196 | reverse = listing = execute = 0; | |
ccc6cda3 | 197 | ename = (char *)NULL; |
726f6388 JA |
198 | |
199 | /* Parse out the options and set which of the two forms we're in. */ | |
ccc6cda3 JA |
200 | reset_internal_getopt (); |
201 | lcurrent = list; /* XXX */ | |
202 | while (fc_number (loptend = lcurrent) == 0 && | |
203 | (opt = internal_getopt (list, ":e:lnrs")) != -1) | |
726f6388 | 204 | { |
ccc6cda3 JA |
205 | switch (opt) |
206 | { | |
207 | case 'n': | |
208 | numbering = 0; | |
209 | break; | |
726f6388 | 210 | |
ccc6cda3 | 211 | case 'l': |
8868edaf | 212 | listing = HN_LISTING; /* for fc_gethnum */ |
ccc6cda3 | 213 | break; |
726f6388 | 214 | |
ccc6cda3 JA |
215 | case 'r': |
216 | reverse = 1; | |
217 | break; | |
218 | ||
219 | case 's': | |
220 | execute = 1; | |
221 | break; | |
222 | ||
223 | case 'e': | |
224 | ename = list_optarg; | |
225 | break; | |
226 | ||
a0c0a00f | 227 | CASE_HELPOPT; |
ccc6cda3 JA |
228 | default: |
229 | builtin_usage (); | |
230 | return (EX_USAGE); | |
726f6388 | 231 | } |
726f6388 JA |
232 | } |
233 | ||
ccc6cda3 JA |
234 | list = loptend; |
235 | ||
726f6388 JA |
236 | if (ename && (*ename == '-') && (ename[1] == '\0')) |
237 | execute = 1; | |
238 | ||
239 | /* The "execute" form of the command (re-run, with possible string | |
240 | substitutions). */ | |
241 | if (execute) | |
242 | { | |
ccc6cda3 | 243 | rlist = (REPL *)NULL; |
726f6388 JA |
244 | while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL)) |
245 | { | |
246 | *sep++ = '\0'; | |
247 | rl = (REPL *)xmalloc (sizeof (REPL)); | |
248 | rl->next = (REPL *)NULL; | |
249 | rl->pat = savestring (list->word->word); | |
250 | rl->rep = savestring (sep); | |
251 | ||
252 | if (rlist == NULL) | |
253 | rlist = rl; | |
254 | else | |
255 | { | |
256 | rl->next = rlist; | |
257 | rlist = rl; | |
258 | } | |
259 | list = list->next; | |
260 | } | |
261 | ||
262 | /* If we have a list of substitutions to do, then reverse it | |
263 | to get the replacements in the proper order. */ | |
264 | ||
7117c2d2 | 265 | rlist = REVERSE_LIST (rlist, REPL *); |
726f6388 JA |
266 | |
267 | hlist = history_list (); | |
268 | ||
269 | /* If we still have something in list, it is a command spec. | |
270 | Otherwise, we use the most recent command in time. */ | |
8868edaf | 271 | command = fc_gethist (list ? list->word->word : (char *)NULL, hlist, 0); |
726f6388 JA |
272 | |
273 | if (command == NULL) | |
274 | { | |
b80f6443 | 275 | builtin_error (_("no command found")); |
726f6388 JA |
276 | if (rlist) |
277 | FREE_RLIST (); | |
278 | ||
279 | return (EXECUTION_FAILURE); | |
280 | } | |
281 | ||
282 | if (rlist) | |
283 | { | |
284 | newcom = fc_dosubs (command, rlist); | |
285 | free (command); | |
286 | FREE_RLIST (); | |
287 | command = newcom; | |
288 | } | |
289 | ||
ccc6cda3 JA |
290 | fprintf (stderr, "%s\n", command); |
291 | fc_replhist (command); /* replace `fc -s' with command */ | |
89a92869 CR |
292 | /* Posix says that the re-executed commands should be entered into the |
293 | history. */ | |
d166f048 | 294 | return (parse_and_execute (command, "fc", SEVAL_NOHIST)); |
726f6388 JA |
295 | } |
296 | ||
297 | /* This is the second form of the command (the list-or-edit-and-rerun | |
298 | form). */ | |
299 | hlist = history_list (); | |
300 | if (hlist == 0) | |
301 | return (EXECUTION_SUCCESS); | |
302 | for (i = 0; hlist[i]; i++); | |
303 | ||
304 | /* With the Bash implementation of history, the current command line | |
305 | ("fc blah..." and so on) is already part of the history list by | |
306 | the time we get to this point. This just skips over that command | |
307 | and makes the last command that this deals with be the last command | |
d166f048 JA |
308 | the user entered before the fc. We need to check whether the |
309 | line was actually added (HISTIGNORE may have caused it to not be), | |
310 | so we check hist_last_line_added. */ | |
726f6388 | 311 | |
89a92869 CR |
312 | /* Even though command substitution through parse_and_execute turns off |
313 | remember_on_history, command substitution in a shell when set -o history | |
314 | has been enabled (interactive or not) should use it in the last_hist | |
315 | calculation as if it were on. */ | |
316 | rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list); | |
317 | last_hist = i - rh - hist_last_line_added; | |
726f6388 | 318 | |
ac50fbac CR |
319 | /* Make sure that real_last is calculated the same way here and in |
320 | fc_gethnum. The return value from fc_gethnum is treated specially if | |
321 | it is == real_last and we are listing commands. */ | |
322 | real_last = i; | |
323 | /* back up from the end to the last non-null history entry */ | |
324 | while (hlist[real_last] == 0 && real_last > 0) | |
325 | real_last--; | |
326 | ||
30d188c2 | 327 | /* XXX */ |
7aaa661d | 328 | if (i == last_hist && hlist[last_hist] == 0) |
30d188c2 CR |
329 | while (last_hist >= 0 && hlist[last_hist] == 0) |
330 | last_hist--; | |
331 | if (last_hist < 0) | |
8868edaf | 332 | last_hist = 0; /* per POSIX */ |
30d188c2 | 333 | |
726f6388 JA |
334 | if (list) |
335 | { | |
8868edaf | 336 | histbeg = fc_gethnum (list->word->word, hlist, listing|HN_FIRST); |
726f6388 JA |
337 | list = list->next; |
338 | ||
339 | if (list) | |
8868edaf | 340 | histend = fc_gethnum (list->word->word, hlist, listing); |
ac50fbac CR |
341 | else if (histbeg == real_last) |
342 | histend = listing ? real_last : histbeg; | |
726f6388 | 343 | else |
ccc6cda3 | 344 | histend = listing ? last_hist : histbeg; |
726f6388 JA |
345 | } |
346 | else | |
347 | { | |
348 | /* The default for listing is the last 16 history items. */ | |
349 | if (listing) | |
350 | { | |
351 | histend = last_hist; | |
95732b49 | 352 | histbeg = histend - 16 + 1; /* +1 because loop below uses >= */ |
726f6388 JA |
353 | if (histbeg < 0) |
354 | histbeg = 0; | |
355 | } | |
356 | else | |
ccc6cda3 JA |
357 | /* For editing, it is the last history command. */ |
358 | histbeg = histend = last_hist; | |
726f6388 JA |
359 | } |
360 | ||
8868edaf CR |
361 | if (histbeg == HIST_INVALID || histend == HIST_INVALID) |
362 | { | |
363 | sh_erange ((char *)NULL, _("history specification")); | |
364 | return (EXECUTION_FAILURE); | |
365 | } | |
366 | else if (histbeg == HIST_ERANGE || histend == HIST_ERANGE) | |
367 | { | |
368 | sh_erange ((char *)NULL, _("history specification")); | |
369 | return (EXECUTION_FAILURE); | |
370 | } | |
371 | else if (histbeg == HIST_NOTFOUND || histend == HIST_NOTFOUND) | |
372 | { | |
373 | builtin_error (_("no command found")); | |
374 | return (EXECUTION_FAILURE); | |
375 | } | |
376 | ||
377 | /* We don't throw an error for line specifications out of range, per POSIX */ | |
378 | if (histbeg < 0) | |
379 | histbeg = 0; | |
380 | if (histend < 0) | |
381 | histend = 0; | |
382 | ||
3185942a JA |
383 | /* "When not listing, the fc command that caused the editing shall not be |
384 | entered into the history list." */ | |
385 | if (listing == 0 && hist_last_line_added) | |
386 | { | |
387 | bash_delete_last_history (); | |
388 | /* If we're editing a single command -- the last command in the | |
389 | history -- and we just removed the dummy command added by | |
390 | edit_and_execute_command (), we need to check whether or not we | |
391 | just removed the last command in the history and need to back | |
392 | the pointer up. remember_on_history is off because we're running | |
393 | in parse_and_execute(). */ | |
394 | if (histbeg == histend && histend == last_hist && hlist[last_hist] == 0) | |
395 | last_hist = histbeg = --histend; | |
8868edaf CR |
396 | |
397 | if (hlist[last_hist] == 0) | |
398 | last_hist--; | |
399 | if (histend >= last_hist) | |
400 | histend = last_hist; | |
401 | else if (histbeg >= last_hist) | |
402 | histbeg = last_hist; | |
3185942a JA |
403 | } |
404 | ||
8868edaf CR |
405 | if (histbeg == HIST_INVALID || histend == HIST_INVALID) |
406 | { | |
407 | sh_erange ((char *)NULL, _("history specification")); | |
408 | return (EXECUTION_FAILURE); | |
409 | } | |
410 | else if (histbeg == HIST_ERANGE || histend == HIST_ERANGE) | |
726f6388 | 411 | { |
b80f6443 | 412 | sh_erange ((char *)NULL, _("history specification")); |
726f6388 JA |
413 | return (EXECUTION_FAILURE); |
414 | } | |
8868edaf CR |
415 | else if (histbeg == HIST_NOTFOUND || histend == HIST_NOTFOUND) |
416 | { | |
417 | builtin_error (_("no command found")); | |
418 | return (EXECUTION_FAILURE); | |
419 | } | |
420 | ||
421 | /* We don't throw an error for line specifications out of range, per POSIX */ | |
422 | if (histbeg < 0) | |
423 | histbeg = 0; | |
424 | if (histend < 0) | |
425 | histend = 0; | |
726f6388 JA |
426 | |
427 | if (histend < histbeg) | |
428 | { | |
ccc6cda3 | 429 | i = histend; |
726f6388 | 430 | histend = histbeg; |
ccc6cda3 JA |
431 | histbeg = i; |
432 | ||
726f6388 JA |
433 | reverse = 1; |
434 | } | |
435 | ||
436 | if (listing) | |
437 | stream = stdout; | |
438 | else | |
439 | { | |
440 | numbering = 0; | |
28ef6c31 | 441 | stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn); |
ccc6cda3 | 442 | if (stream == 0) |
726f6388 | 443 | { |
b80f6443 | 444 | builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno)); |
28ef6c31 | 445 | FREE (fn); |
726f6388 JA |
446 | return (EXECUTION_FAILURE); |
447 | } | |
448 | } | |
449 | ||
ccc6cda3 | 450 | for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++) |
726f6388 | 451 | { |
ccc6cda3 | 452 | QUIT; |
74091dd4 CR |
453 | if (hlist[i] == 0) |
454 | continue; | |
ccc6cda3 JA |
455 | if (numbering) |
456 | fprintf (stream, "%d", i + history_base); | |
457 | if (listing) | |
95732b49 JA |
458 | { |
459 | if (posixly_correct) | |
460 | fputs ("\t", stream); | |
461 | else | |
462 | fprintf (stream, "\t%c", histdata (i) ? '*' : ' '); | |
463 | } | |
74091dd4 CR |
464 | if (histline (i)) |
465 | fprintf (stream, "%s\n", histline (i)); | |
726f6388 JA |
466 | } |
467 | ||
468 | if (listing) | |
3185942a | 469 | return (sh_chkwrite (EXECUTION_SUCCESS)); |
726f6388 | 470 | |
3185942a JA |
471 | fflush (stream); |
472 | if (ferror (stream)) | |
473 | { | |
474 | sh_wrerror (); | |
475 | fclose (stream); | |
d233b485 | 476 | FREE (fn); |
3185942a JA |
477 | return (EXECUTION_FAILURE); |
478 | } | |
726f6388 JA |
479 | fclose (stream); |
480 | ||
481 | /* Now edit the file of commands. */ | |
482 | if (ename) | |
483 | { | |
484 | command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2); | |
485 | sprintf (command, "%s %s", ename, fn); | |
486 | } | |
487 | else | |
488 | { | |
95732b49 JA |
489 | fcedit = posixly_correct ? POSIX_FC_EDIT_COMMAND : FC_EDIT_COMMAND; |
490 | command = (char *)xmalloc (3 + strlen (fcedit) + strlen (fn)); | |
491 | sprintf (command, "%s %s", fcedit, fn); | |
726f6388 | 492 | } |
d166f048 | 493 | retval = parse_and_execute (command, "fc", SEVAL_NOHIST); |
ccc6cda3 JA |
494 | if (retval != EXECUTION_SUCCESS) |
495 | { | |
496 | unlink (fn); | |
28ef6c31 | 497 | free (fn); |
ccc6cda3 JA |
498 | return (EXECUTION_FAILURE); |
499 | } | |
726f6388 | 500 | |
a0c0a00f | 501 | #if defined (READLINE) |
8868edaf | 502 | /* If we're executing as part of a dispatched readline command like |
a0c0a00f CR |
503 | {emacs,vi}_edit_and_execute_command, the readline state will indicate it. |
504 | We could remove the partial command from the history, but ksh93 doesn't | |
505 | so we stay compatible. */ | |
506 | #endif | |
507 | ||
d166f048 JA |
508 | /* Make sure parse_and_execute doesn't turn this off, even though a |
509 | call to parse_and_execute farther up the function call stack (e.g., | |
510 | if this is called by vi_edit_and_execute_command) may have already | |
511 | called bash_history_disable. */ | |
512 | remember_on_history = 1; | |
726f6388 | 513 | |
d166f048 | 514 | /* Turn on the `v' flag while fc_execute_file runs so the commands |
726f6388 JA |
515 | will be echoed as they are read by the parser. */ |
516 | begin_unwind_frame ("fc builtin"); | |
d233b485 | 517 | add_unwind_protect (xfree, fn); |
726f6388 | 518 | add_unwind_protect (unlink, fn); |
a0c0a00f | 519 | add_unwind_protect (set_verbose_flag, (char *)NULL); |
8868edaf | 520 | unwind_protect_int (suppress_debug_trap_verbose); |
726f6388 | 521 | echo_input_at_read = 1; |
8868edaf | 522 | suppress_debug_trap_verbose = 1; |
726f6388 | 523 | |
a0c0a00f | 524 | retval = fc_execute_file (fn); |
726f6388 JA |
525 | run_unwind_frame ("fc builtin"); |
526 | ||
527 | return (retval); | |
528 | } | |
529 | ||
ccc6cda3 JA |
530 | /* Return 1 if LIST->word->word is a legal number for fc's use. */ |
531 | static int | |
532 | fc_number (list) | |
533 | WORD_LIST *list; | |
534 | { | |
535 | char *s; | |
536 | ||
537 | if (list == 0) | |
538 | return 0; | |
539 | s = list->word->word; | |
540 | if (*s == '-') | |
541 | s++; | |
7117c2d2 | 542 | return (legal_number (s, (intmax_t *)NULL)); |
ccc6cda3 JA |
543 | } |
544 | ||
726f6388 JA |
545 | /* Return an absolute index into HLIST which corresponds to COMMAND. If |
546 | COMMAND is a number, then it was specified in relative terms. If it | |
8868edaf CR |
547 | is a string, then it is the start of a command line present in HLIST. |
548 | MODE includes HN_LISTING if we are listing commands, and does not if we | |
549 | are executing them. If MODE includes HN_FIRST we are looking for the | |
550 | first history number specification. */ | |
726f6388 | 551 | static int |
8868edaf | 552 | fc_gethnum (command, hlist, mode) |
726f6388 JA |
553 | char *command; |
554 | HIST_ENTRY **hlist; | |
8868edaf | 555 | int mode; |
726f6388 | 556 | { |
89a92869 | 557 | int sign, n, clen, rh; |
8868edaf | 558 | register int i, j, last_hist, real_last, listing; |
726f6388 JA |
559 | register char *s; |
560 | ||
8868edaf | 561 | listing = mode & HN_LISTING; |
3185942a | 562 | sign = 1; |
726f6388 JA |
563 | /* Count history elements. */ |
564 | for (i = 0; hlist[i]; i++); | |
565 | ||
566 | /* With the Bash implementation of history, the current command line | |
567 | ("fc blah..." and so on) is already part of the history list by | |
568 | the time we get to this point. This just skips over that command | |
569 | and makes the last command that this deals with be the last command | |
d166f048 JA |
570 | the user entered before the fc. We need to check whether the |
571 | line was actually added (HISTIGNORE may have caused it to not be), | |
3185942a JA |
572 | so we check hist_last_line_added. This needs to agree with the |
573 | calculation of last_hist in fc_builtin above. */ | |
89a92869 CR |
574 | /* Even though command substitution through parse_and_execute turns off |
575 | remember_on_history, command substitution in a shell when set -o history | |
576 | has been enabled (interactive or not) should use it in the last_hist | |
577 | calculation as if it were on. */ | |
578 | rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list); | |
7aaa661d CR |
579 | last_hist = i - rh - hist_last_line_added; |
580 | ||
581 | if (i == last_hist && hlist[last_hist] == 0) | |
582 | while (last_hist >= 0 && hlist[last_hist] == 0) | |
583 | last_hist--; | |
584 | if (last_hist < 0) | |
585 | return (-1); | |
586 | ||
ac50fbac | 587 | real_last = i; |
7aaa661d | 588 | i = last_hist; |
726f6388 JA |
589 | |
590 | /* No specification defaults to most recent command. */ | |
591 | if (command == NULL) | |
592 | return (i); | |
593 | ||
ac50fbac CR |
594 | /* back up from the end to the last non-null history entry */ |
595 | while (hlist[real_last] == 0 && real_last > 0) | |
596 | real_last--; | |
597 | ||
726f6388 JA |
598 | /* Otherwise, there is a specification. It can be a number relative to |
599 | the current position, or an absolute history number. */ | |
600 | s = command; | |
601 | ||
602 | /* Handle possible leading minus sign. */ | |
603 | if (s && (*s == '-')) | |
604 | { | |
605 | sign = -1; | |
606 | s++; | |
607 | } | |
608 | ||
f73dda09 | 609 | if (s && DIGIT(*s)) |
726f6388 JA |
610 | { |
611 | n = atoi (s); | |
612 | n *= sign; | |
613 | ||
8868edaf CR |
614 | /* We want to return something that is an offset to HISTORY_BASE. */ |
615 | ||
726f6388 JA |
616 | /* If the value is negative or zero, then it is an offset from |
617 | the current history item. */ | |
8868edaf CR |
618 | /* We don't use HN_FIRST here, so we don't return different values |
619 | depending on whether we're looking for the first or last in a | |
620 | pair of range arguments, but nobody else does, either. */ | |
726f6388 | 621 | if (n < 0) |
7117c2d2 JA |
622 | { |
623 | n += i + 1; | |
624 | return (n < 0 ? 0 : n); | |
625 | } | |
726f6388 | 626 | else if (n == 0) |
8868edaf | 627 | return ((sign == -1) ? (listing ? real_last : HIST_INVALID) : i); |
726f6388 | 628 | else |
7117c2d2 | 629 | { |
8868edaf CR |
630 | /* If we're out of range (greater than I (last history entry) or |
631 | less than HISTORY_BASE, we want to return different values | |
632 | based on whether or not we are looking for the first or last | |
633 | value in a desired range of history entries. */ | |
7117c2d2 | 634 | n -= history_base; |
8868edaf CR |
635 | if (n < 0) |
636 | return (mode & HN_FIRST ? 0 : i); | |
637 | else if (n >= i) | |
638 | return (mode & HN_FIRST ? 0 : i); | |
639 | else | |
640 | return n; | |
7117c2d2 | 641 | } |
726f6388 JA |
642 | } |
643 | ||
644 | clen = strlen (command); | |
645 | for (j = i; j >= 0; j--) | |
646 | { | |
647 | if (STREQN (command, histline (j), clen)) | |
648 | return (j); | |
649 | } | |
8868edaf | 650 | return (HIST_NOTFOUND); |
726f6388 JA |
651 | } |
652 | ||
653 | /* Locate the most recent history line which begins with | |
8868edaf CR |
654 | COMMAND in HLIST, and return a malloc()'ed copy of it. |
655 | MODE is 1 if we are listing commands, 0 if we are executing them. */ | |
726f6388 | 656 | static char * |
8868edaf | 657 | fc_gethist (command, hlist, mode) |
726f6388 JA |
658 | char *command; |
659 | HIST_ENTRY **hlist; | |
8868edaf | 660 | int mode; |
726f6388 JA |
661 | { |
662 | int i; | |
663 | ||
95732b49 | 664 | if (hlist == 0) |
726f6388 JA |
665 | return ((char *)NULL); |
666 | ||
8868edaf | 667 | i = fc_gethnum (command, hlist, mode); |
726f6388 JA |
668 | |
669 | if (i >= 0) | |
670 | return (savestring (histline (i))); | |
671 | else | |
672 | return ((char *)NULL); | |
673 | } | |
674 | ||
f73dda09 | 675 | #ifdef INCLUDE_UNUSED |
726f6388 JA |
676 | /* Read the edited history lines from STREAM and return them |
677 | one at a time. This can read unlimited length lines. The | |
678 | caller should free the storage. */ | |
679 | static char * | |
680 | fc_readline (stream) | |
681 | FILE *stream; | |
682 | { | |
683 | register int c; | |
684 | int line_len = 0, lindex = 0; | |
685 | char *line = (char *)NULL; | |
686 | ||
687 | while ((c = getc (stream)) != EOF) | |
688 | { | |
689 | if ((lindex + 2) >= line_len) | |
f73dda09 | 690 | line = (char *)xrealloc (line, (line_len += 128)); |
726f6388 JA |
691 | |
692 | if (c == '\n') | |
693 | { | |
694 | line[lindex++] = '\n'; | |
695 | line[lindex++] = '\0'; | |
696 | return (line); | |
697 | } | |
698 | else | |
699 | line[lindex++] = c; | |
700 | } | |
701 | ||
702 | if (!lindex) | |
703 | { | |
704 | if (line) | |
705 | free (line); | |
706 | ||
707 | return ((char *)NULL); | |
708 | } | |
709 | ||
710 | if (lindex + 2 >= line_len) | |
711 | line = (char *)xrealloc (line, lindex + 3); | |
712 | ||
713 | line[lindex++] = '\n'; /* Finish with newline if none in file */ | |
714 | line[lindex++] = '\0'; | |
715 | return (line); | |
716 | } | |
f73dda09 | 717 | #endif |
726f6388 JA |
718 | |
719 | /* Perform the SUBS on COMMAND. | |
720 | SUBS is a list of substitutions, and COMMAND is a simple string. | |
721 | Return a pointer to a malloc'ed string which contains the substituted | |
722 | command. */ | |
723 | static char * | |
724 | fc_dosubs (command, subs) | |
725 | char *command; | |
726 | REPL *subs; | |
727 | { | |
ccc6cda3 | 728 | register char *new, *t; |
726f6388 JA |
729 | register REPL *r; |
730 | ||
ccc6cda3 | 731 | for (new = savestring (command), r = subs; r; r = r->next) |
726f6388 | 732 | { |
ccc6cda3 | 733 | t = strsub (new, r->pat, r->rep, 1); |
726f6388 JA |
734 | free (new); |
735 | new = t; | |
736 | } | |
737 | return (new); | |
738 | } | |
739 | ||
726f6388 JA |
740 | /* Use `command' to replace the last entry in the history list, which, |
741 | by this time, is `fc blah...'. The intent is that the new command | |
742 | become the history entry, and that `fc' should never appear in the | |
743 | history list. This way you can do `r' to your heart's content. */ | |
744 | static void | |
745 | fc_replhist (command) | |
746 | char *command; | |
747 | { | |
726f6388 JA |
748 | int n; |
749 | ||
ccc6cda3 | 750 | if (command == 0 || *command == '\0') |
726f6388 JA |
751 | return; |
752 | ||
726f6388 | 753 | n = strlen (command); |
726f6388 JA |
754 | if (command[n - 1] == '\n') |
755 | command[n - 1] = '\0'; | |
756 | ||
757 | if (command && *command) | |
758 | { | |
3185942a | 759 | bash_delete_last_history (); |
726f6388 JA |
760 | maybe_add_history (command); /* Obeys HISTCONTROL setting. */ |
761 | } | |
762 | } | |
763 | ||
f73dda09 | 764 | #ifdef INCLUDE_UNUSED |
726f6388 JA |
765 | /* Add LINE to the history, after removing a single trailing newline. */ |
766 | static void | |
767 | fc_addhist (line) | |
768 | char *line; | |
769 | { | |
770 | register int n; | |
771 | ||
95732b49 JA |
772 | if (line == 0 || *line == 0) |
773 | return; | |
774 | ||
726f6388 JA |
775 | n = strlen (line); |
776 | ||
777 | if (line[n - 1] == '\n') | |
778 | line[n - 1] = '\0'; | |
779 | ||
780 | if (line && *line) | |
95732b49 | 781 | maybe_add_history (line); /* Obeys HISTCONTROL setting. */ |
726f6388 | 782 | } |
f73dda09 JA |
783 | #endif |
784 | ||
726f6388 | 785 | #endif /* HISTORY */ |