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