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