]> git.ipfire.org Git - thirdparty/bash.git/blame - builtins/fc.def
Imported from ../bash-2.05.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
bb70624e 10Software Foundation; either version 2, or (at your option) any later
726f6388
JA
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
bb70624e 20Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
726f6388
JA
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
b72432fd 29fc is used to list or edit and re-execute commands from the history list.
726f6388
JA
30FIRST and LAST can be numbers specifying the range, or FIRST can be a
31string, which means the most recent command beginning with that
32string.
33
34 -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR,
b72432fd 35 then vi.
726f6388
JA
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 55#include "../bashtypes.h"
bb70624e 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"
726f6388 73#include "../bashhist.h"
bb70624e 74#include "maxpath.h"
726f6388
JA
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
28ef6c31 89extern FILE *sh_mktmpfp __P((char *, int, char **));
d166f048 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;
b72432fd 159 int histbeg, histend, last_hist, retval, opt;
726f6388 160 FILE *stream;
ccc6cda3
JA
161 REPL *rlist, *rl;
162 char *ename, *command, *newcom, *line;
726f6388 163 HIST_ENTRY **hlist;
28ef6c31 164 char *fn;
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;
28ef6c31 330 stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn);
ccc6cda3 331 if (stream == 0)
726f6388 332 {
28ef6c31
JA
333 builtin_error ("cannot open temp file %s", fn ? fn : "");
334 FREE (fn);
726f6388
JA
335 return (EXECUTION_FAILURE);
336 }
337 }
338
ccc6cda3 339 for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++)
726f6388 340 {
ccc6cda3
JA
341 QUIT;
342 if (numbering)
343 fprintf (stream, "%d", i + history_base);
344 if (listing)
345 fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
346 fprintf (stream, "%s\n", histline (i));
726f6388
JA
347 }
348
349 if (listing)
350 return (EXECUTION_SUCCESS);
351
352 fclose (stream);
353
354 /* Now edit the file of commands. */
355 if (ename)
356 {
357 command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
358 sprintf (command, "%s %s", ename, fn);
359 }
360 else
361 {
362 command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
363 sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
364 }
d166f048 365 retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
ccc6cda3
JA
366 if (retval != EXECUTION_SUCCESS)
367 {
368 unlink (fn);
28ef6c31 369 free (fn);
ccc6cda3
JA
370 return (EXECUTION_FAILURE);
371 }
726f6388 372
d166f048
JA
373 /* Make sure parse_and_execute doesn't turn this off, even though a
374 call to parse_and_execute farther up the function call stack (e.g.,
375 if this is called by vi_edit_and_execute_command) may have already
376 called bash_history_disable. */
377 remember_on_history = 1;
726f6388 378
d166f048 379 /* Turn on the `v' flag while fc_execute_file runs so the commands
726f6388
JA
380 will be echoed as they are read by the parser. */
381 begin_unwind_frame ("fc builtin");
28ef6c31 382 add_unwind_protect ((Function *)xfree, fn);
726f6388
JA
383 add_unwind_protect (unlink, fn);
384 unwind_protect_int (echo_input_at_read);
385 echo_input_at_read = 1;
386
d166f048 387 retval = fc_execute_file (fn);
726f6388
JA
388
389 run_unwind_frame ("fc builtin");
390
391 return (retval);
392}
393
ccc6cda3
JA
394/* Return 1 if LIST->word->word is a legal number for fc's use. */
395static int
396fc_number (list)
397 WORD_LIST *list;
398{
399 char *s;
400
401 if (list == 0)
402 return 0;
403 s = list->word->word;
404 if (*s == '-')
405 s++;
406 return (legal_number (s, (long *)NULL));
407}
408
726f6388
JA
409/* Return an absolute index into HLIST which corresponds to COMMAND. If
410 COMMAND is a number, then it was specified in relative terms. If it
411 is a string, then it is the start of a command line present in HLIST. */
412static int
413fc_gethnum (command, hlist)
414 char *command;
415 HIST_ENTRY **hlist;
416{
417 int sign = 1, n, clen;
418 register int i, j;
419 register char *s;
420
421 /* Count history elements. */
422 for (i = 0; hlist[i]; i++);
423
424 /* With the Bash implementation of history, the current command line
425 ("fc blah..." and so on) is already part of the history list by
426 the time we get to this point. This just skips over that command
427 and makes the last command that this deals with be the last command
d166f048
JA
428 the user entered before the fc. We need to check whether the
429 line was actually added (HISTIGNORE may have caused it to not be),
430 so we check hist_last_line_added. */
431 i -= 1 + hist_last_line_added;
726f6388
JA
432
433 /* No specification defaults to most recent command. */
434 if (command == NULL)
435 return (i);
436
437 /* Otherwise, there is a specification. It can be a number relative to
438 the current position, or an absolute history number. */
439 s = command;
440
441 /* Handle possible leading minus sign. */
442 if (s && (*s == '-'))
443 {
444 sign = -1;
445 s++;
446 }
447
448 if (s && digit(*s))
449 {
450 n = atoi (s);
451 n *= sign;
452
453 /* Anything specified greater than the last history element that we
454 deal with is an error. */
455 if (n > i + history_base)
456 return (-1);
457
458 /* If the value is negative or zero, then it is an offset from
459 the current history item. */
460 if (n < 0)
461 return (i + n + 1);
462 else if (n == 0)
463 return (i);
464 else
465 return (n - history_base);
466 }
467
468 clen = strlen (command);
469 for (j = i; j >= 0; j--)
470 {
471 if (STREQN (command, histline (j), clen))
472 return (j);
473 }
474 return (-1);
475}
476
477/* Locate the most recent history line which begins with
478 COMMAND in HLIST, and return a malloc()'ed copy of it. */
479static char *
480fc_gethist (command, hlist)
481 char *command;
482 HIST_ENTRY **hlist;
483{
484 int i;
485
486 if (!hlist)
487 return ((char *)NULL);
488
489 i = fc_gethnum (command, hlist);
490
491 if (i >= 0)
492 return (savestring (histline (i)));
493 else
494 return ((char *)NULL);
495}
496
497/* Read the edited history lines from STREAM and return them
498 one at a time. This can read unlimited length lines. The
499 caller should free the storage. */
500static char *
501fc_readline (stream)
502 FILE *stream;
503{
504 register int c;
505 int line_len = 0, lindex = 0;
506 char *line = (char *)NULL;
507
508 while ((c = getc (stream)) != EOF)
509 {
510 if ((lindex + 2) >= line_len)
511 line = (char *) xrealloc (line, (line_len += 128));
512
513 if (c == '\n')
514 {
515 line[lindex++] = '\n';
516 line[lindex++] = '\0';
517 return (line);
518 }
519 else
520 line[lindex++] = c;
521 }
522
523 if (!lindex)
524 {
525 if (line)
526 free (line);
527
528 return ((char *)NULL);
529 }
530
531 if (lindex + 2 >= line_len)
532 line = (char *)xrealloc (line, lindex + 3);
533
534 line[lindex++] = '\n'; /* Finish with newline if none in file */
535 line[lindex++] = '\0';
536 return (line);
537}
538
539/* Perform the SUBS on COMMAND.
540 SUBS is a list of substitutions, and COMMAND is a simple string.
541 Return a pointer to a malloc'ed string which contains the substituted
542 command. */
543static char *
544fc_dosubs (command, subs)
545 char *command;
546 REPL *subs;
547{
ccc6cda3 548 register char *new, *t;
726f6388
JA
549 register REPL *r;
550
ccc6cda3 551 for (new = savestring (command), r = subs; r; r = r->next)
726f6388 552 {
ccc6cda3 553 t = strsub (new, r->pat, r->rep, 1);
726f6388
JA
554 free (new);
555 new = t;
556 }
557 return (new);
558}
559
726f6388
JA
560/* Use `command' to replace the last entry in the history list, which,
561 by this time, is `fc blah...'. The intent is that the new command
562 become the history entry, and that `fc' should never appear in the
563 history list. This way you can do `r' to your heart's content. */
564static void
565fc_replhist (command)
566 char *command;
567{
568 register int i;
569 HIST_ENTRY **hlist, *histent, *discard;
726f6388
JA
570 int n;
571
ccc6cda3 572 if (command == 0 || *command == '\0')
726f6388
JA
573 return;
574
575 hlist = history_list ();
576
577 if (hlist == NULL)
578 return;
579
580 for (i = 0; hlist[i]; i++);
581 i--;
582
583 /* History_get () takes a parameter that should be
584 offset by history_base. */
585
586 histent = history_get (history_base + i); /* Don't free this */
587 if (histent == NULL)
588 return;
589
590 n = strlen (command);
591
592 if (command[n - 1] == '\n')
593 command[n - 1] = '\0';
594
595 if (command && *command)
596 {
597 discard = remove_history (i);
598 if (discard)
599 {
ccc6cda3 600 FREE (discard->line);
726f6388
JA
601 free ((char *) discard);
602 }
603 maybe_add_history (command); /* Obeys HISTCONTROL setting. */
604 }
605}
606
607/* Add LINE to the history, after removing a single trailing newline. */
608static void
609fc_addhist (line)
610 char *line;
611{
612 register int n;
613
614 n = strlen (line);
615
616 if (line[n - 1] == '\n')
617 line[n - 1] = '\0';
618
619 if (line && *line)
620 maybe_add_history (line);
621}
622#endif /* HISTORY */