]> git.ipfire.org Git - thirdparty/bash.git/blame - builtins/fc.def
Imported from ../bash-2.01.1.tar.gz.
[thirdparty/bash.git] / builtins / fc.def
CommitLineData
726f6388
JA
1This file is fc.def, from which is created fc.c.
2It implements the builtin "fc" in Bash.
3
4Copyright (C) 1987, 1989, 1991 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 it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 1, or (at your option) any later
11version.
12
13Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with Bash; see the file COPYING. If not, write to the Free Software
20Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22$PRODUCES fc.c
23
24$BUILTIN fc
25$FUNCTION fc_builtin
26$DEPENDS_ON HISTORY
27$SHORT_DOC fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [cmd]
28
29FIRST and LAST can be numbers specifying the range, or FIRST can be a
30string, which means the most recent command beginning with that
31string.
32
33 -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR,
34 then the editor which corresponds to the current readline editing
35 mode, then vi.
36
37 -l means list lines instead of editing.
38 -n means no line numbers listed.
39 -r means reverse the order of the lines (making it newest listed first).
40
41With the `fc -s [pat=rep ...] [command]' format, the command is
42re-executed after the substitution OLD=NEW is performed.
43
44A useful alias to use with this is r='fc -s', so that typing `r cc'
45runs the last command beginning with `cc' and typing `r' re-executes
46the last command.
47$END
48
ccc6cda3
JA
49#include <config.h>
50
726f6388
JA
51#if defined (HISTORY)
52#include <sys/param.h>
d166f048
JA
53#include "../bashtypes.h"
54#include "../posixstat.h"
726f6388 55#include <sys/file.h>
ccc6cda3
JA
56
57#if defined (HAVE_UNISTD_H)
58# include <unistd.h>
59#endif
60
61#include <stdio.h>
62
63#include "../bashansi.h"
726f6388 64#include <errno.h>
ccc6cda3
JA
65
66#include "../shell.h"
726f6388
JA
67#include "../builtins.h"
68#include "../flags.h"
69#include "../maxpath.h"
70#include "../bashhist.h"
71#include <readline/history.h>
72#include "bashgetopt.h"
ccc6cda3 73#include "common.h"
726f6388 74
726f6388
JA
75#if !defined (errno)
76extern int errno;
77#endif /* !errno */
78
79extern int echo_input_at_read;
d166f048
JA
80extern int current_command_line_count;
81extern int literal_history;
726f6388
JA
82
83extern int unlink ();
84
d166f048
JA
85extern int fc_execute_file ();
86
726f6388
JA
87/* **************************************************************** */
88/* */
89/* The K*rn shell style fc command (Fix Command) */
90/* */
91/* **************************************************************** */
92
93/* fc builtin command (fix command) for Bash for those who
94 like K*rn-style history better than csh-style.
95
96 fc [-e ename] [-nlr] [first] [last]
97
98 FIRST and LAST can be numbers specifying the range, or FIRST can be
99 a string, which means the most recent command beginning with that
100 string.
101
102 -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR,
103 then the editor which corresponds to the current readline editing
104 mode, then vi.
105
106 -l means list lines instead of editing.
107 -n means no line numbers listed.
108 -r means reverse the order of the lines (making it newest listed first).
109
110 fc -e - [pat=rep ...] [command]
111 fc -s [pat=rep ...] [command]
112
113 Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
114*/
115
ccc6cda3
JA
116static char *fc_dosubs (), *fc_gethist (), *fc_readline ();
117static int fc_gethnum (), fc_number ();
726f6388
JA
118static void fc_replhist (), fc_addhist ();
119
120/* Data structure describing a list of global replacements to perform. */
121typedef struct repl {
122 struct repl *next;
123 char *pat;
124 char *rep;
125} REPL;
126
726f6388
JA
127/* Accessors for HIST_ENTRY lists that are called HLIST. */
128#define histline(i) (hlist[(i)]->line)
129#define histdata(i) (hlist[(i)]->data)
130
131#define FREE_RLIST() \
132 do { \
133 for (rl = rlist; rl; ) { \
134 REPL *r; \
135 r = rl->next; \
136 if (rl->pat) \
137 free (rl->pat); \
138 if (rl->rep) \
139 free (rl->rep); \
140 free (rl); \
141 rl = r; \
142 } \
143 } while (0)
144
145/* String to execute on a file that we want to edit. */
146#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
147
148int
149fc_builtin (list)
150 WORD_LIST *list;
151{
152 register int i;
153 register char *sep;
154 int numbering, reverse, listing, execute;
155 int histbeg, histend, last_hist, retval, first, opt;
156 FILE *stream;
ccc6cda3
JA
157 REPL *rlist, *rl;
158 char *ename, *command, *newcom, *line;
726f6388 159 HIST_ENTRY **hlist;
ccc6cda3 160 char fn[64];
726f6388
JA
161
162 numbering = 1;
163 reverse = listing = execute = 0;
ccc6cda3 164 ename = (char *)NULL;
726f6388
JA
165
166 /* Parse out the options and set which of the two forms we're in. */
ccc6cda3
JA
167 reset_internal_getopt ();
168 lcurrent = list; /* XXX */
169 while (fc_number (loptend = lcurrent) == 0 &&
170 (opt = internal_getopt (list, ":e:lnrs")) != -1)
726f6388 171 {
ccc6cda3
JA
172 switch (opt)
173 {
174 case 'n':
175 numbering = 0;
176 break;
726f6388 177
ccc6cda3
JA
178 case 'l':
179 listing = 1;
180 break;
726f6388 181
ccc6cda3
JA
182 case 'r':
183 reverse = 1;
184 break;
185
186 case 's':
187 execute = 1;
188 break;
189
190 case 'e':
191 ename = list_optarg;
192 break;
193
194 default:
195 builtin_usage ();
196 return (EX_USAGE);
726f6388 197 }
726f6388
JA
198 }
199
ccc6cda3
JA
200 list = loptend;
201
726f6388
JA
202 if (ename && (*ename == '-') && (ename[1] == '\0'))
203 execute = 1;
204
205 /* The "execute" form of the command (re-run, with possible string
206 substitutions). */
207 if (execute)
208 {
ccc6cda3 209 rlist = (REPL *)NULL;
726f6388
JA
210 while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL))
211 {
212 *sep++ = '\0';
213 rl = (REPL *)xmalloc (sizeof (REPL));
214 rl->next = (REPL *)NULL;
215 rl->pat = savestring (list->word->word);
216 rl->rep = savestring (sep);
217
218 if (rlist == NULL)
219 rlist = rl;
220 else
221 {
222 rl->next = rlist;
223 rlist = rl;
224 }
225 list = list->next;
226 }
227
228 /* If we have a list of substitutions to do, then reverse it
229 to get the replacements in the proper order. */
230
231 if (rlist && rlist->next)
ccc6cda3 232 rlist = (REPL *)reverse_list ((GENERIC_LIST *) rlist);
726f6388
JA
233
234 hlist = history_list ();
235
236 /* If we still have something in list, it is a command spec.
237 Otherwise, we use the most recent command in time. */
ccc6cda3 238 command = fc_gethist (list ? list->word->word : (char *)NULL, hlist);
726f6388
JA
239
240 if (command == NULL)
241 {
242 builtin_error ("no command found");
243 if (rlist)
244 FREE_RLIST ();
245
246 return (EXECUTION_FAILURE);
247 }
248
249 if (rlist)
250 {
251 newcom = fc_dosubs (command, rlist);
252 free (command);
253 FREE_RLIST ();
254 command = newcom;
255 }
256
ccc6cda3
JA
257 fprintf (stderr, "%s\n", command);
258 fc_replhist (command); /* replace `fc -s' with command */
d166f048 259 return (parse_and_execute (command, "fc", SEVAL_NOHIST));
726f6388
JA
260 }
261
262 /* This is the second form of the command (the list-or-edit-and-rerun
263 form). */
264 hlist = history_list ();
265 if (hlist == 0)
266 return (EXECUTION_SUCCESS);
267 for (i = 0; hlist[i]; i++);
268
269 /* With the Bash implementation of history, the current command line
270 ("fc blah..." and so on) is already part of the history list by
271 the time we get to this point. This just skips over that command
272 and makes the last command that this deals with be the last command
d166f048
JA
273 the user entered before the fc. We need to check whether the
274 line was actually added (HISTIGNORE may have caused it to not be),
275 so we check hist_last_line_added. */
726f6388 276
d166f048 277 last_hist = i - 1 - hist_last_line_added;
726f6388
JA
278
279 if (list)
280 {
281 histbeg = fc_gethnum (list->word->word, hlist);
282 list = list->next;
283
284 if (list)
285 histend = fc_gethnum (list->word->word, hlist);
286 else
ccc6cda3 287 histend = listing ? last_hist : histbeg;
726f6388
JA
288 }
289 else
290 {
291 /* The default for listing is the last 16 history items. */
292 if (listing)
293 {
294 histend = last_hist;
295 histbeg = histend - 16;
296 if (histbeg < 0)
297 histbeg = 0;
298 }
299 else
ccc6cda3
JA
300 /* For editing, it is the last history command. */
301 histbeg = histend = last_hist;
726f6388
JA
302 }
303
304 /* We print error messages for line specifications out of range. */
305 if ((histbeg < 0) || (histend < 0) ||
306 (histbeg > last_hist) || (histend > last_hist))
307 {
308 builtin_error ("history specification out of range");
309 return (EXECUTION_FAILURE);
310 }
311
312 if (histend < histbeg)
313 {
ccc6cda3 314 i = histend;
726f6388 315 histend = histbeg;
ccc6cda3
JA
316 histbeg = i;
317
726f6388
JA
318 reverse = 1;
319 }
320
321 if (listing)
322 stream = stdout;
323 else
324 {
325 numbering = 0;
d166f048 326 /* XXX - this is raceable */
ccc6cda3 327 sprintf (fn, "/tmp/bash%d", (int)time ((time_t *) 0) + (int)getpid ());
726f6388
JA
328
329 stream = fopen (fn, "w");
330
ccc6cda3 331 if (stream == 0)
726f6388
JA
332 {
333 builtin_error ("cannot open temp file %s", fn);
334 return (EXECUTION_FAILURE);
335 }
336 }
337
ccc6cda3 338 for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++)
726f6388 339 {
ccc6cda3
JA
340 QUIT;
341 if (numbering)
342 fprintf (stream, "%d", i + history_base);
343 if (listing)
344 fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
345 fprintf (stream, "%s\n", histline (i));
726f6388
JA
346 }
347
348 if (listing)
349 return (EXECUTION_SUCCESS);
350
351 fclose (stream);
352
353 /* Now edit the file of commands. */
354 if (ename)
355 {
356 command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
357 sprintf (command, "%s %s", ename, fn);
358 }
359 else
360 {
361 command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
362 sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
363 }
d166f048 364 retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
ccc6cda3
JA
365 if (retval != EXECUTION_SUCCESS)
366 {
367 unlink (fn);
368 return (EXECUTION_FAILURE);
369 }
726f6388
JA
370
371 /* Now reopen the file and execute the edited commands. */
372
373 stream = fopen (fn, "r");
374
375 if (stream == NULL)
376 {
377 builtin_error ("cannot reopen temp file %s", fn);
378 unlink (fn);
379 return (EXECUTION_FAILURE);
380 }
381
382 retval = EXECUTION_SUCCESS;
383 first = 1;
384
d166f048
JA
385#if 1
386 /* Make sure parse_and_execute doesn't turn this off, even though a
387 call to parse_and_execute farther up the function call stack (e.g.,
388 if this is called by vi_edit_and_execute_command) may have already
389 called bash_history_disable. */
390 remember_on_history = 1;
391#else
726f6388
JA
392 /* First, write the commands to the history file. This will not happen
393 when we call parse_and_execute, since parse_and_execute disables
394 the command line history while it executes. */
d166f048
JA
395
396 opt = current_command_line_count;
726f6388
JA
397 while ((line = fc_readline (stream)) != NULL)
398 {
399 if (line[0] == '\n')
400 {
401 free (line);
402 continue; /* Skip blank lines. */
403 }
404
405 if (first)
406 {
407 first = 0;
d166f048
JA
408 /* If we retrieved only one command from the history file, but we
409 read multiple lines from the edited file, and literal_history
410 has been set by `shopt', we assume that it was a compound
411 command stored with embedded newlines. In this case, we want
412 the history code to store it as one command again. */
413 if (literal_history && histbeg == histend)
414 current_command_line_count = 1;
726f6388
JA
415 fc_replhist (line);
416 }
417 else
d166f048
JA
418 {
419 if (literal_history && histbeg == histend)
420 current_command_line_count++;
421 fc_addhist (line);
422 }
726f6388
JA
423
424 free (line);
425 }
426 fclose (stream);
d166f048
JA
427 current_command_line_count = opt;
428#endif
726f6388 429
d166f048 430 /* Turn on the `v' flag while fc_execute_file runs so the commands
726f6388
JA
431 will be echoed as they are read by the parser. */
432 begin_unwind_frame ("fc builtin");
433 add_unwind_protect (unlink, fn);
434 unwind_protect_int (echo_input_at_read);
435 echo_input_at_read = 1;
436
d166f048 437 retval = fc_execute_file (fn);
726f6388
JA
438
439 run_unwind_frame ("fc builtin");
440
441 return (retval);
442}
443
ccc6cda3
JA
444/* Return 1 if LIST->word->word is a legal number for fc's use. */
445static int
446fc_number (list)
447 WORD_LIST *list;
448{
449 char *s;
450
451 if (list == 0)
452 return 0;
453 s = list->word->word;
454 if (*s == '-')
455 s++;
456 return (legal_number (s, (long *)NULL));
457}
458
726f6388
JA
459/* Return an absolute index into HLIST which corresponds to COMMAND. If
460 COMMAND is a number, then it was specified in relative terms. If it
461 is a string, then it is the start of a command line present in HLIST. */
462static int
463fc_gethnum (command, hlist)
464 char *command;
465 HIST_ENTRY **hlist;
466{
467 int sign = 1, n, clen;
468 register int i, j;
469 register char *s;
470
471 /* Count history elements. */
472 for (i = 0; hlist[i]; i++);
473
474 /* With the Bash implementation of history, the current command line
475 ("fc blah..." and so on) is already part of the history list by
476 the time we get to this point. This just skips over that command
477 and makes the last command that this deals with be the last command
d166f048
JA
478 the user entered before the fc. We need to check whether the
479 line was actually added (HISTIGNORE may have caused it to not be),
480 so we check hist_last_line_added. */
481 i -= 1 + hist_last_line_added;
726f6388
JA
482
483 /* No specification defaults to most recent command. */
484 if (command == NULL)
485 return (i);
486
487 /* Otherwise, there is a specification. It can be a number relative to
488 the current position, or an absolute history number. */
489 s = command;
490
491 /* Handle possible leading minus sign. */
492 if (s && (*s == '-'))
493 {
494 sign = -1;
495 s++;
496 }
497
498 if (s && digit(*s))
499 {
500 n = atoi (s);
501 n *= sign;
502
503 /* Anything specified greater than the last history element that we
504 deal with is an error. */
505 if (n > i + history_base)
506 return (-1);
507
508 /* If the value is negative or zero, then it is an offset from
509 the current history item. */
510 if (n < 0)
511 return (i + n + 1);
512 else if (n == 0)
513 return (i);
514 else
515 return (n - history_base);
516 }
517
518 clen = strlen (command);
519 for (j = i; j >= 0; j--)
520 {
521 if (STREQN (command, histline (j), clen))
522 return (j);
523 }
524 return (-1);
525}
526
527/* Locate the most recent history line which begins with
528 COMMAND in HLIST, and return a malloc()'ed copy of it. */
529static char *
530fc_gethist (command, hlist)
531 char *command;
532 HIST_ENTRY **hlist;
533{
534 int i;
535
536 if (!hlist)
537 return ((char *)NULL);
538
539 i = fc_gethnum (command, hlist);
540
541 if (i >= 0)
542 return (savestring (histline (i)));
543 else
544 return ((char *)NULL);
545}
546
547/* Read the edited history lines from STREAM and return them
548 one at a time. This can read unlimited length lines. The
549 caller should free the storage. */
550static char *
551fc_readline (stream)
552 FILE *stream;
553{
554 register int c;
555 int line_len = 0, lindex = 0;
556 char *line = (char *)NULL;
557
558 while ((c = getc (stream)) != EOF)
559 {
560 if ((lindex + 2) >= line_len)
561 line = (char *) xrealloc (line, (line_len += 128));
562
563 if (c == '\n')
564 {
565 line[lindex++] = '\n';
566 line[lindex++] = '\0';
567 return (line);
568 }
569 else
570 line[lindex++] = c;
571 }
572
573 if (!lindex)
574 {
575 if (line)
576 free (line);
577
578 return ((char *)NULL);
579 }
580
581 if (lindex + 2 >= line_len)
582 line = (char *)xrealloc (line, lindex + 3);
583
584 line[lindex++] = '\n'; /* Finish with newline if none in file */
585 line[lindex++] = '\0';
586 return (line);
587}
588
589/* Perform the SUBS on COMMAND.
590 SUBS is a list of substitutions, and COMMAND is a simple string.
591 Return a pointer to a malloc'ed string which contains the substituted
592 command. */
593static char *
594fc_dosubs (command, subs)
595 char *command;
596 REPL *subs;
597{
ccc6cda3 598 register char *new, *t;
726f6388
JA
599 register REPL *r;
600
ccc6cda3 601 for (new = savestring (command), r = subs; r; r = r->next)
726f6388 602 {
ccc6cda3 603 t = strsub (new, r->pat, r->rep, 1);
726f6388
JA
604 free (new);
605 new = t;
606 }
607 return (new);
608}
609
726f6388
JA
610/* Use `command' to replace the last entry in the history list, which,
611 by this time, is `fc blah...'. The intent is that the new command
612 become the history entry, and that `fc' should never appear in the
613 history list. This way you can do `r' to your heart's content. */
614static void
615fc_replhist (command)
616 char *command;
617{
618 register int i;
619 HIST_ENTRY **hlist, *histent, *discard;
726f6388
JA
620 int n;
621
ccc6cda3 622 if (command == 0 || *command == '\0')
726f6388
JA
623 return;
624
625 hlist = history_list ();
626
627 if (hlist == NULL)
628 return;
629
630 for (i = 0; hlist[i]; i++);
631 i--;
632
633 /* History_get () takes a parameter that should be
634 offset by history_base. */
635
636 histent = history_get (history_base + i); /* Don't free this */
637 if (histent == NULL)
638 return;
639
640 n = strlen (command);
641
642 if (command[n - 1] == '\n')
643 command[n - 1] = '\0';
644
645 if (command && *command)
646 {
647 discard = remove_history (i);
648 if (discard)
649 {
ccc6cda3 650 FREE (discard->line);
726f6388
JA
651 free ((char *) discard);
652 }
653 maybe_add_history (command); /* Obeys HISTCONTROL setting. */
654 }
655}
656
657/* Add LINE to the history, after removing a single trailing newline. */
658static void
659fc_addhist (line)
660 char *line;
661{
662 register int n;
663
664 n = strlen (line);
665
666 if (line[n - 1] == '\n')
667 line[n - 1] = '\0';
668
669 if (line && *line)
670 maybe_add_history (line);
671}
672#endif /* HISTORY */