home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / tcl / tclsrc / c / tclHistory < prev    next >
Text File  |  1996-01-28  |  30KB  |  1,101 lines

  1. /*
  2.  * tclHistory.c --
  3.  *
  4.  *    This module implements history as an optional addition to Tcl.
  5.  *    It can be called to record commands ("events") before they are
  6.  *    executed, and it provides a command that may be used to perform
  7.  *    history substitutions.
  8.  *
  9.  * Copyright (c) 1990-1993 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  */
  15.  
  16. #ifndef lint
  17. static char sccsid[] = "@(#) tclHistory.c 1.37 95/05/04 11:51:16";
  18. #endif /* not lint */
  19.  
  20. #include "tclInt.h"
  21. #ifndef TCL_GENERIC_ONLY /* not for RISCOS*/
  22. #include "tclPort.h"
  23. #endif
  24.  
  25. /*
  26.  * This history stuff is mostly straightforward, except for one thing
  27.  * that makes everything very complicated.  Suppose that the following
  28.  * commands get executed:
  29.  *    echo foo
  30.  *    history redo
  31.  * It's important that the history event recorded for the second command
  32.  * be "echo foo", not "history redo".  Otherwise, if another "history redo"
  33.  * command is typed, it will result in infinite recursions on the
  34.  * "history redo" command.  Thus, the actual recorded history must be
  35.  *    echo foo
  36.  *    echo foo
  37.  * To do this, the history command revises recorded history as part of
  38.  * its execution.  In the example above, when "history redo" starts
  39.  * execution, the current event is "history redo", but the history
  40.  * command arranges for the current event to be changed to "echo foo".
  41.  *
  42.  * There are three additional complications.  The first is that history
  43.  * substitution may only be part of a command, as in the following
  44.  * command sequence:
  45.  *    echo foo bar
  46.  *    echo [history word 3]
  47.  * In this case, the second event should be recorded as "echo bar".  Only
  48.  * part of the recorded event is to be modified.  Fortunately, Tcl_Eval
  49.  * helps with this by recording (in the evalFirst and evalLast fields of
  50.  * the intepreter) the location of the command being executed, so the
  51.  * history module can replace exactly the range of bytes corresponding
  52.  * to the history substitution command.
  53.  *
  54.  * The second complication is that there are two ways to revise history:
  55.  * replace a command, and replace the result of a command.  Consider the
  56.  * two examples below:
  57.  *    format {result is %d} $num       |    format {result is %d} $num
  58.  *    print [history redo]           |    print [history word 3]
  59.  * Recorded history for these two cases should be as follows:
  60.  *    format {result is %d} $num       |    format {result is %d} $num
  61.  *    print [format {result is %d} $num] |    print $num
  62.  * In the left case, the history command was replaced with another command
  63.  * to be executed (the brackets were retained), but in the case on the
  64.  * right the result of executing the history command was replaced (i.e.
  65.  * brackets were replaced too).
  66.  *
  67.  * The third complication is that there could potentially be many
  68.  * history substitutions within a single command, as in:
  69.  *    echo [history word 3] [history word 2]
  70.  * There could even be nested history substitutions, as in:
  71.  *    history subs abc [history word 2]
  72.  * If history revisions were made immediately during each "history" command
  73.  * invocations, it would be very difficult to produce the correct cumulative
  74.  * effect from several substitutions in the same command.  To get around
  75.  * this problem, the actual history revision isn't made during the execution
  76.  * of the "history" command.  Information about the changes is just recorded,
  77.  * in xxx records, and the actual changes are made during the next call to
  78.  * Tcl_RecordHistory (when we know that execution of the previous command
  79.  * has finished).
  80.  */
  81.  
  82. /*
  83.  * Default space allocation for command strings:
  84.  */
  85.  
  86. #define INITIAL_CMD_SIZE 40
  87.  
  88. /*
  89.  * Forward declarations for procedures defined later in this file:
  90.  */
  91.  
  92. static void        DoRevs _ANSI_ARGS_((Interp *iPtr));
  93. static HistoryEvent *    GetEvent _ANSI_ARGS_((Interp *iPtr, char *string));
  94. static char *        GetWords _ANSI_ARGS_((Interp *iPtr, char *command,
  95.                 char *words));
  96. static void        InitHistory _ANSI_ARGS_((Interp *iPtr));
  97. static void        InsertRev _ANSI_ARGS_((Interp *iPtr,
  98.                 HistoryRev *revPtr));
  99. static void        MakeSpace _ANSI_ARGS_((HistoryEvent *hPtr, int size));
  100. static void        RevCommand _ANSI_ARGS_((Interp *iPtr, char *string));
  101. static void        RevResult _ANSI_ARGS_((Interp *iPtr, char *string));
  102. static int        SubsAndEval _ANSI_ARGS_((Interp *iPtr, char *cmd,
  103.                 char *old, char *new));
  104.  
  105. /*
  106.  *----------------------------------------------------------------------
  107.  *
  108.  * InitHistory --
  109.  *
  110.  *    Initialize history-related state in an interpreter.
  111.  *
  112.  * Results:
  113.  *    None.
  114.  *
  115.  * Side effects:
  116.  *    History info is initialized in iPtr.
  117.  *
  118.  *----------------------------------------------------------------------
  119.  */
  120.  
  121. static void
  122. InitHistory(iPtr)
  123.     register Interp *iPtr;        /* Interpreter to initialize. */
  124. {
  125.     int i;
  126.  
  127.     if (iPtr->numEvents != 0) {
  128.     return;
  129.     }
  130.     iPtr->numEvents = 20;
  131.     iPtr->events = (HistoryEvent *)
  132.         ckalloc((unsigned) (iPtr->numEvents * sizeof(HistoryEvent)));
  133.     for (i = 0; i < iPtr->numEvents; i++) {
  134.     iPtr->events[i].command = (char *) ckalloc(INITIAL_CMD_SIZE);
  135.     *iPtr->events[i].command = 0;
  136.     iPtr->events[i].bytesAvl = INITIAL_CMD_SIZE;
  137.     }
  138.     iPtr->curEvent = 0;
  139.     iPtr->curEventNum = 0;
  140. }
  141.  
  142. /*
  143.  *----------------------------------------------------------------------
  144.  *
  145.  * Tcl_RecordAndEval --
  146.  *
  147.  *    This procedure adds its command argument to the current list of
  148.  *    recorded events and then executes the command by calling
  149.  *    Tcl_Eval.
  150.  *
  151.  * Results:
  152.  *    The return value is a standard Tcl return value, the result of
  153.  *    executing cmd.
  154.  *
  155.  * Side effects:
  156.  *    The command is recorded and executed.  In addition, pending history
  157.  *    revisions are carried out, and information is set up to enable
  158.  *    Tcl_Eval to identify history command ranges.  This procedure also
  159.  *    initializes history information for the interpreter, if it hasn't
  160.  *    already been initialized.
  161.  *
  162.  *----------------------------------------------------------------------
  163.  */
  164.  
  165. int
  166. Tcl_RecordAndEval(interp, cmd, flags)
  167.     Tcl_Interp *interp;        /* Token for interpreter in which command
  168.                  * will be executed. */
  169.     char *cmd;            /* Command to record. */
  170.     int flags;            /* Additional flags.  TCL_NO_EVAL means
  171.                  * only record: don't execute command.
  172.                  * TCL_EVAL_GLOBAL means use Tcl_GlobalEval
  173.                  * instead of Tcl_Eval. */
  174. {
  175.     register Interp *iPtr = (Interp *) interp;
  176.     register HistoryEvent *eventPtr;
  177.     int length, result;
  178.  
  179.     if (iPtr->numEvents == 0) {
  180.     InitHistory(iPtr);
  181.     }
  182.     DoRevs(iPtr);
  183.  
  184.     /*
  185.      * Don't record empty commands.
  186.      */
  187.  
  188.     while (isspace(UCHAR(*cmd))) {
  189.     cmd++;
  190.     }
  191.     if (*cmd == '\0') {
  192.     Tcl_ResetResult(interp);
  193.     return TCL_OK;
  194.     }
  195.  
  196.     iPtr->curEventNum++;
  197.     iPtr->curEvent++;
  198.     if (iPtr->curEvent >= iPtr->numEvents) {
  199.     iPtr->curEvent = 0;
  200.     }
  201.     eventPtr = &iPtr->events[iPtr->curEvent];
  202.  
  203.     /*
  204.      * Chop off trailing newlines before recording the command.
  205.      */
  206.  
  207.     length = strlen(cmd);
  208.     while (cmd[length-1] == '\n') {
  209.     length--;
  210.     }
  211.     MakeSpace(eventPtr, length + 1);
  212.     strncpy(eventPtr->command, cmd, (size_t) length);
  213.     eventPtr->command[length] = 0;
  214.  
  215.     /*
  216.      * Execute the command.  Note: history revision isn't possible after
  217.      * a nested call to this procedure, because the event at the top of
  218.      * the history list no longer corresponds to what's going on when
  219.      * a nested call here returns.  Thus, must leave history revision
  220.      * disabled when we return.
  221.      */
  222.  
  223.     result = TCL_OK;
  224.     if (!(flags & TCL_NO_EVAL)) {
  225.     iPtr->historyFirst = cmd;
  226.     iPtr->revDisables = 0;
  227.     iPtr->evalFlags = (flags & ~TCL_EVAL_GLOBAL) | TCL_RECORD_BOUNDS;
  228.     if (flags & TCL_EVAL_GLOBAL) {
  229.         result = Tcl_GlobalEval(interp, cmd);
  230.     } else {
  231.         result = Tcl_Eval(interp, cmd);
  232.     }
  233.     }
  234.     iPtr->revDisables = 1;
  235.     return result;
  236. }
  237.  
  238. /*
  239.  *----------------------------------------------------------------------
  240.  *
  241.  * Tcl_HistoryCmd --
  242.  *
  243.  *    This procedure is invoked to process the "history" Tcl command.
  244.  *    See the user documentation for details on what it does.
  245.  *
  246.  * Results:
  247.  *    A standard Tcl result.
  248.  *
  249.  * Side effects:
  250.  *    See the user documentation.
  251.  *
  252.  *----------------------------------------------------------------------
  253.  */
  254.  
  255.     /* ARGSUSED */
  256. int
  257. Tcl_HistoryCmd(dummy, interp, argc, argv)
  258.     ClientData dummy;            /* Not used. */
  259.     Tcl_Interp *interp;            /* Current interpreter. */
  260.     int argc;                /* Number of arguments. */
  261.     char **argv;            /* Argument strings. */
  262. {
  263.     register Interp *iPtr = (Interp *) interp;
  264.     register HistoryEvent *eventPtr;
  265.     size_t length;
  266.     int c;
  267.  
  268.     if (iPtr->numEvents == 0) {
  269.     InitHistory(iPtr);
  270.     }
  271.  
  272.     /*
  273.      * If no arguments, treat the same as "history info".
  274.      */
  275.  
  276.     if (argc == 1) {
  277.     goto infoCmd;
  278.     }
  279.  
  280.     c = argv[1][0];
  281.     length = strlen(argv[1]);
  282.  
  283.     if ((c == 'a') && (strncmp(argv[1], "add", length)) == 0) {
  284.     if ((argc != 3) && (argc != 4)) {
  285.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  286.             " add event ?exec?\"", (char *) NULL);
  287.         return TCL_ERROR;
  288.     }
  289.     if (argc == 4) {
  290.         if (strncmp(argv[3], "exec", strlen(argv[3])) != 0) {
  291.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  292.             "\": should be \"exec\"", (char *) NULL);
  293.         return TCL_ERROR;
  294.         }
  295.         return Tcl_RecordAndEval(interp, argv[2], 0);
  296.     }
  297.     return Tcl_RecordAndEval(interp, argv[2], TCL_NO_EVAL);
  298.     } else if ((c == 'c') && (strncmp(argv[1], "change", length)) == 0) {
  299.     if ((argc != 3) && (argc != 4)) {
  300.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  301.             " change newValue ?event?\"", (char *) NULL);
  302.         return TCL_ERROR;
  303.     }
  304.     if (argc == 3) {
  305.         eventPtr = &iPtr->events[iPtr->curEvent];
  306.         iPtr->revDisables += 1;
  307.         while (iPtr->revPtr != NULL) {
  308.         HistoryRev *nextPtr;
  309.  
  310.         ckfree(iPtr->revPtr->newBytes);
  311.         nextPtr = iPtr->revPtr->nextPtr;
  312.         ckfree((char *) iPtr->revPtr);
  313.         iPtr->revPtr = nextPtr;
  314.         }
  315.     } else {
  316.         eventPtr = GetEvent(iPtr, argv[3]);
  317.         if (eventPtr == NULL) {
  318.         return TCL_ERROR;
  319.         }
  320.     }
  321.     MakeSpace(eventPtr, (int) strlen(argv[2]) + 1);
  322.     strcpy(eventPtr->command, argv[2]);
  323.     return TCL_OK;
  324.     } else if ((c == 'e') && (strncmp(argv[1], "event", length)) == 0) {
  325.     if (argc > 3) {
  326.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  327.             " event ?event?\"", (char *) NULL);
  328.         return TCL_ERROR;
  329.     }
  330.     eventPtr = GetEvent(iPtr, argc==2 ? "-1" : argv[2]);
  331.     if (eventPtr == NULL) {
  332.         return TCL_ERROR;
  333.     }
  334.     RevResult(iPtr, eventPtr->command);
  335.     Tcl_SetResult(interp, eventPtr->command, TCL_VOLATILE);
  336.     return TCL_OK;
  337.     } else if ((c == 'i') && (strncmp(argv[1], "info", length)) == 0) {
  338.     int count, indx, i;
  339.     char *newline;
  340.  
  341.     if ((argc != 2) && (argc != 3)) {
  342.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  343.             " info ?count?\"", (char *) NULL);
  344.         return TCL_ERROR;
  345.     }
  346.     infoCmd:
  347.     if (argc == 3) {
  348.         if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
  349.         return TCL_ERROR;
  350.         }
  351.         if (count > iPtr->numEvents) {
  352.         count = iPtr->numEvents;
  353.         }
  354.     } else {
  355.         count = iPtr->numEvents;
  356.     }
  357.     newline = "";
  358.     for (i = 0, indx = iPtr->curEvent + 1 + iPtr->numEvents - count;
  359.         i < count; i++, indx++) {
  360.         char *cur, *next, savedChar;
  361.         char serial[20];
  362.  
  363.         if (indx >= iPtr->numEvents) {
  364.         indx -= iPtr->numEvents;
  365.         }
  366.         cur = iPtr->events[indx].command;
  367.         if (*cur == '\0') {
  368.         continue;        /* No command recorded here. */
  369.         }
  370.         sprintf(serial, "%6d  ", iPtr->curEventNum + 1 - (count - i));
  371.         Tcl_AppendResult(interp, newline, serial, (char *) NULL);
  372.         newline = "\n";
  373.  
  374.         /*
  375.          * Tricky formatting here:  for multi-line commands, indent
  376.          * the continuation lines.
  377.          */
  378.  
  379.         while (1) {
  380.         next = strchr(cur, '\n');
  381.         if (next == NULL) {
  382.             break;
  383.         }
  384.         next++;
  385.         savedChar = *next;
  386.         *next = 0;
  387.         Tcl_AppendResult(interp, cur, "\t", (char *) NULL);
  388.         *next = savedChar;
  389.         cur = next;
  390.         }
  391.         Tcl_AppendResult(interp, cur, (char *) NULL);
  392.     }
  393.     return TCL_OK;
  394.     } else if ((c == 'k') && (strncmp(argv[1], "keep", length)) == 0) {
  395.     int count, i, src;
  396.     HistoryEvent *events;
  397.  
  398.     if (argc != 3) {
  399.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  400.             " keep number\"", (char *) NULL);
  401.         return TCL_ERROR;
  402.     }
  403.     if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
  404.         return TCL_ERROR;
  405.     }
  406.     if ((count <= 0) || (count > 1000)) {
  407.         Tcl_AppendResult(interp, "illegal keep count \"", argv[2],
  408.             "\"", (char *) NULL);
  409.         return TCL_ERROR;
  410.     }
  411.  
  412.     /*
  413.      * Create a new history array and copy as much existing history
  414.      * as possible from the old array.
  415.      */
  416.  
  417.     events = (HistoryEvent *)
  418.         ckalloc((unsigned) (count * sizeof(HistoryEvent)));
  419.     if (count < iPtr->numEvents) {
  420.         src = iPtr->curEvent + 1 - count;
  421.         if (src < 0) {
  422.         src += iPtr->numEvents;
  423.         }
  424.     } else {
  425.         src = iPtr->curEvent + 1;
  426.     }
  427.     for (i = 0; i < count; i++, src++) {
  428.         if (src >= iPtr->numEvents) {
  429.         src = 0;
  430.         }
  431.         if (i < iPtr->numEvents) {
  432.         events[i] = iPtr->events[src];
  433.         iPtr->events[src].command = NULL;
  434.         } else {
  435.         events[i].command = (char *) ckalloc(INITIAL_CMD_SIZE);
  436.         events[i].command[0] = 0;
  437.         events[i].bytesAvl = INITIAL_CMD_SIZE;
  438.         }
  439.     }
  440.  
  441.     /*
  442.      * Throw away everything left in the old history array, and
  443.      * substitute the new one for the old one.
  444.      */
  445.  
  446.     for (i = 0; i < iPtr->numEvents; i++) {
  447.         if (iPtr->events[i].command != NULL) {
  448.         ckfree(iPtr->events[i].command);
  449.         }
  450.     }
  451.     ckfree((char *) iPtr->events);
  452.     iPtr->events = events;
  453.     if (count < iPtr->numEvents) {
  454.         iPtr->curEvent = count-1;
  455.     } else {
  456.         iPtr->curEvent = iPtr->numEvents-1;
  457.     }
  458.     iPtr->numEvents = count;
  459.     return TCL_OK;
  460.     } else if ((c == 'n') && (strncmp(argv[1], "nextid", length)) == 0) {
  461.     if (argc != 2) {
  462.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  463.             " nextid\"", (char *) NULL);
  464.         return TCL_ERROR;
  465.     }
  466.     sprintf(iPtr->result, "%d", iPtr->curEventNum+1);
  467.     return TCL_OK;
  468.     } else if ((c == 'r') && (strncmp(argv[1], "redo", length)) == 0) {
  469.     if (argc > 3) {
  470.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  471.             " redo ?event?\"", (char *) NULL);
  472.         return TCL_ERROR;
  473.     }
  474.     eventPtr = GetEvent(iPtr, argc==2 ? "-1" : argv[2]);
  475.     if (eventPtr == NULL) {
  476.         return TCL_ERROR;
  477.     }
  478.     RevCommand(iPtr, eventPtr->command);
  479.     return Tcl_Eval(interp, eventPtr->command);
  480.     } else if ((c == 's') && (strncmp(argv[1], "substitute", length)) == 0) {
  481.     if ((argc > 5) || (argc < 4)) {
  482.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  483.             " substitute old new ?event?\"", (char *) NULL);
  484.         return TCL_ERROR;
  485.     }
  486.     eventPtr = GetEvent(iPtr, argc==4 ? "-1" : argv[4]);
  487.     if (eventPtr == NULL) {
  488.         return TCL_ERROR;
  489.     }
  490.     return SubsAndEval(iPtr, eventPtr->command, argv[2], argv[3]);
  491.     } else if ((c == 'w') && (strncmp(argv[1], "words", length)) == 0) {
  492.     char *words;
  493.  
  494.     if ((argc != 3) && (argc != 4)) {
  495.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  496.             " words num-num/pat ?event?\"", (char *) NULL);
  497.         return TCL_ERROR;
  498.     }
  499.     eventPtr = GetEvent(iPtr, argc==3 ? "-1" : argv[3]);
  500.     if (eventPtr == NULL) {
  501.         return TCL_ERROR;
  502.     }
  503.     words = GetWords(iPtr, eventPtr->command, argv[2]);
  504.     if (words == NULL) {
  505.         return TCL_ERROR;
  506.     }
  507.     RevResult(iPtr, words);
  508.     iPtr->result = words;
  509.     iPtr->freeProc = (Tcl_FreeProc *) free;
  510.     return TCL_OK;
  511.     }
  512.  
  513.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  514.         "\": must be add, change, event, info, keep, nextid, ",
  515.         "redo, substitute, or words", (char *) NULL);
  516.     return TCL_ERROR;
  517. }
  518.  
  519. /*
  520.  *----------------------------------------------------------------------
  521.  *
  522.  * MakeSpace --
  523.  *
  524.  *    Given a history event, make sure it has enough space for
  525.  *    a string of a given length (enlarge the string area if
  526.  *    necessary).
  527.  *
  528.  * Results:
  529.  *    None.
  530.  *
  531.  * Side effects:
  532.  *    More memory may get allocated.
  533.  *
  534.  *----------------------------------------------------------------------
  535.  */
  536.  
  537. static void
  538. MakeSpace(hPtr, size)
  539.     HistoryEvent *hPtr;
  540.     int size;            /* # of bytes needed in hPtr. */
  541. {
  542.     if (hPtr->bytesAvl < size) {
  543.     ckfree(hPtr->command);
  544.     hPtr->command = (char *) ckalloc((unsigned) size);
  545.     hPtr->bytesAvl = size;
  546.     }
  547. }
  548.  
  549. /*
  550.  *----------------------------------------------------------------------
  551.  *
  552.  * InsertRev --
  553.  *
  554.  *    Add a new revision to the list of those pending for iPtr.
  555.  *    Do it in a way that keeps the revision list sorted in
  556.  *    increasing order of firstIndex.  Also, eliminate revisions
  557.  *    that are subsets of other revisions.
  558.  *
  559.  * Results:
  560.  *    None.
  561.  *
  562.  * Side effects:
  563.  *    RevPtr is added to iPtr's revision list.
  564.  *
  565.  *----------------------------------------------------------------------
  566.  */
  567.  
  568. static void
  569. InsertRev(iPtr, revPtr)
  570.     Interp *iPtr;            /* Interpreter to use. */
  571.     register HistoryRev *revPtr;    /* Revision to add to iPtr's list. */
  572. {
  573.     register HistoryRev *curPtr;
  574.     register HistoryRev *prevPtr;
  575.  
  576.     for (curPtr = iPtr->revPtr, prevPtr = NULL; curPtr != NULL;
  577.         prevPtr = curPtr, curPtr = curPtr->nextPtr) {
  578.     /*
  579.      * If this revision includes the new one (or vice versa) then
  580.      * just eliminate the one that is a subset of the other.
  581.      */
  582.  
  583.     if ((revPtr->firstIndex <= curPtr->firstIndex)
  584.         && (revPtr->lastIndex >= curPtr->firstIndex)) {
  585.         curPtr->firstIndex = revPtr->firstIndex;
  586.         curPtr->lastIndex = revPtr->lastIndex;
  587.         curPtr->newSize = revPtr->newSize;
  588.         ckfree(curPtr->newBytes);
  589.         curPtr->newBytes = revPtr->newBytes;
  590.         ckfree((char *) revPtr);
  591.         return;
  592.     }
  593.     if ((revPtr->firstIndex >= curPtr->firstIndex)
  594.         && (revPtr->lastIndex <= curPtr->lastIndex)) {
  595.         ckfree(revPtr->newBytes);
  596.         ckfree((char *) revPtr);
  597.         return;
  598.     }
  599.  
  600.     if (revPtr->firstIndex < curPtr->firstIndex) {
  601.         break;
  602.     }
  603.     }
  604.  
  605.     /*
  606.      * Insert revPtr just after prevPtr.
  607.      */
  608.  
  609.     if (prevPtr == NULL) {
  610.     revPtr->nextPtr = iPtr->revPtr;
  611.     iPtr->revPtr = revPtr;
  612.     } else {
  613.     revPtr->nextPtr = prevPtr->nextPtr;
  614.     prevPtr->nextPtr = revPtr;
  615.     }
  616. }
  617.  
  618. /*
  619.  *----------------------------------------------------------------------
  620.  *
  621.  * RevCommand --
  622.  *
  623.  *    This procedure is invoked by the "history" command to record
  624.  *    a command revision.  See the comments at the beginning of the
  625.  *    file for more information about revisions.
  626.  *
  627.  * Results:
  628.  *    None.
  629.  *
  630.  * Side effects:
  631.  *    Revision information is recorded.
  632.  *
  633.  *----------------------------------------------------------------------
  634.  */
  635.  
  636. static void
  637. RevCommand(iPtr, string)
  638.     register Interp *iPtr;    /* Interpreter in which to perform the
  639.                  * substitution. */
  640.     char *string;        /* String to substitute. */
  641. {
  642.     register HistoryRev *revPtr;
  643.  
  644.     if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) {
  645.     return;
  646.     }
  647.     revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev));
  648.     revPtr->firstIndex = iPtr->evalFirst - iPtr->historyFirst;
  649.     revPtr->lastIndex = iPtr->evalLast - iPtr->historyFirst;
  650.     revPtr->newSize = strlen(string);
  651.     revPtr->newBytes = (char *) ckalloc((unsigned) (revPtr->newSize+1));
  652.     strcpy(revPtr->newBytes, string);
  653.     InsertRev(iPtr, revPtr);
  654. }
  655.  
  656. /*
  657.  *----------------------------------------------------------------------
  658.  *
  659.  * RevResult --
  660.  *
  661.  *    This procedure is invoked by the "history" command to record
  662.  *    a result revision.  See the comments at the beginning of the
  663.  *    file for more information about revisions.
  664.  *
  665.  * Results:
  666.  *    None.
  667.  *
  668.  * Side effects:
  669.  *    Revision information is recorded.
  670.  *
  671.  *----------------------------------------------------------------------
  672.  */
  673.  
  674. static void
  675. RevResult(iPtr, string)
  676.     register Interp *iPtr;    /* Interpreter in which to perform the
  677.                  * substitution. */
  678.     char *string;        /* String to substitute. */
  679. {
  680.     register HistoryRev *revPtr;
  681.     char *evalFirst, *evalLast;
  682.     char *argv[2];
  683.  
  684.     if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) {
  685.     return;
  686.     }
  687.  
  688.     /*
  689.      * Expand the replacement range to include the brackets that surround
  690.      * the command.  If there aren't any brackets (i.e. this command was
  691.      * invoked at top-level) then don't do any revision.  Also, if there
  692.      * are several commands in brackets, of which this is just one,
  693.      * then don't do any revision.
  694.      */
  695.  
  696.     evalFirst = iPtr->evalFirst;
  697.     evalLast = iPtr->evalLast + 1;
  698.     while (1) {
  699.     if (evalFirst == iPtr->historyFirst) {
  700.         return;
  701.     }
  702.     evalFirst--;
  703.     if (*evalFirst == '[') {
  704.         break;
  705.     }
  706.     if (!isspace(UCHAR(*evalFirst))) {
  707.         return;
  708.     }
  709.     }
  710.     if (*evalLast != ']') {
  711.     return;
  712.     }
  713.  
  714.     revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev));
  715.     revPtr->firstIndex = evalFirst - iPtr->historyFirst;
  716.     revPtr->lastIndex = evalLast - iPtr->historyFirst;
  717.     argv[0] = string;
  718.     revPtr->newBytes = Tcl_Merge(1, argv);
  719.     revPtr->newSize = strlen(revPtr->newBytes);
  720.     InsertRev(iPtr, revPtr);
  721. }
  722.  
  723. /*
  724.  *----------------------------------------------------------------------
  725.  *
  726.  * DoRevs --
  727.  *
  728.  *    This procedure is called to apply the history revisions that
  729.  *    have been recorded in iPtr.
  730.  *
  731.  * Results:
  732.  *    None.
  733.  *
  734.  * Side effects:
  735.  *    The most recent entry in the history for iPtr may be modified.
  736.  *
  737.  *----------------------------------------------------------------------
  738.  */
  739.  
  740. static void
  741. DoRevs(iPtr)
  742.     register Interp *iPtr;    /* Interpreter whose history is to
  743.                  * be modified. */
  744. {
  745.     register HistoryRev *revPtr;
  746.     register HistoryEvent *eventPtr;
  747.     char *newCommand, *p;
  748.     unsigned int size;
  749.     int bytesSeen, count;
  750.  
  751.     if (iPtr->revPtr == NULL) {
  752.     return;
  753.     }
  754.  
  755.     /*
  756.      * The revision is done in two passes.  The first pass computes the
  757.      * amount of space needed for the revised event, and the second pass
  758.      * pieces together the new event and frees up the revisions.
  759.      */
  760.  
  761.     eventPtr = &iPtr->events[iPtr->curEvent];
  762.     size = strlen(eventPtr->command) + 1;
  763.     for (revPtr = iPtr->revPtr; revPtr != NULL; revPtr = revPtr->nextPtr) {
  764.     size -= revPtr->lastIndex + 1 - revPtr->firstIndex;
  765.     size += revPtr->newSize;
  766.     }
  767.  
  768.     newCommand = (char *) ckalloc(size);
  769.     p = newCommand;
  770.     bytesSeen = 0;
  771.     for (revPtr = iPtr->revPtr; revPtr != NULL; ) {
  772.     HistoryRev *nextPtr = revPtr->nextPtr;
  773.  
  774.     count = revPtr->firstIndex - bytesSeen;
  775.     if (count > 0) {
  776.         strncpy(p, eventPtr->command + bytesSeen, (size_t) count);
  777.         p += count;
  778.     }
  779.     strncpy(p, revPtr->newBytes, (size_t) revPtr->newSize);
  780.     p += revPtr->newSize;
  781.     bytesSeen = revPtr->lastIndex+1;
  782.     ckfree(revPtr->newBytes);
  783.     ckfree((char *) revPtr);
  784.     revPtr = nextPtr;
  785.     }
  786.     strcpy(p, eventPtr->command + bytesSeen);
  787.  
  788.     /*
  789.      * Replace the command in the event.
  790.      */
  791.  
  792.     ckfree(eventPtr->command);
  793.     eventPtr->command = newCommand;
  794.     eventPtr->bytesAvl = size;
  795.     iPtr->revPtr = NULL;
  796. }
  797.  
  798. /*
  799.  *----------------------------------------------------------------------
  800.  *
  801.  * GetEvent --
  802.  *
  803.  *    Given a textual description of an event (see the manual page
  804.  *    for legal values) find the corresponding event and return its
  805.  *    command string.
  806.  *
  807.  * Results:
  808.  *    The return value is a pointer to the event named by "string".
  809.  *    If no such event exists, then NULL is returned and an error
  810.  *    message is left in iPtr.
  811.  *
  812.  * Side effects:
  813.  *    None.
  814.  *
  815.  *----------------------------------------------------------------------
  816.  */
  817.  
  818. static HistoryEvent *
  819. GetEvent(iPtr, string)
  820.     register Interp *iPtr;    /* Interpreter in which to look. */
  821.     char *string;        /* Description of event. */
  822. {
  823.     int eventNum, index;
  824.     register HistoryEvent *eventPtr;
  825.     int length;
  826.  
  827.     /*
  828.      * First check for a numeric specification of an event.
  829.      */
  830.  
  831.     if (isdigit(UCHAR(*string)) || (*string == '-')) {
  832.     if (Tcl_GetInt((Tcl_Interp *) iPtr, string, &eventNum) != TCL_OK) {
  833.         return NULL;
  834.     }
  835.     if (eventNum < 0) {
  836.         eventNum += iPtr->curEventNum;
  837.         }
  838.     if (eventNum > iPtr->curEventNum) {
  839.         Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string,
  840.             "\" hasn't occurred yet", (char *) NULL);
  841.         return NULL;
  842.     }
  843.     if ((eventNum <= iPtr->curEventNum-iPtr->numEvents)
  844.         || (eventNum <= 0)) {
  845.         Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string,
  846.             "\" is too far in the past", (char *) NULL);
  847.         return NULL;
  848.     }
  849.     index = iPtr->curEvent + (eventNum - iPtr->curEventNum);
  850.     if (index < 0) {
  851.         index += iPtr->numEvents;
  852.     }
  853.     return &iPtr->events[index];
  854.     }
  855.  
  856.     /*
  857.      * Next, check for an event that contains the string as a prefix or
  858.      * that matches the string in the sense of Tcl_StringMatch.
  859.      */
  860.  
  861.     length = strlen(string);
  862.     for (index = iPtr->curEvent - 1; ; index--) {
  863.     if (index < 0) {
  864.         index += iPtr->numEvents;
  865.     }
  866.     if (index == iPtr->curEvent) {
  867.         break;
  868.     }
  869.     eventPtr = &iPtr->events[index];
  870.     if ((strncmp(eventPtr->command, string, (size_t) length) == 0)
  871.         || Tcl_StringMatch(eventPtr->command, string)) {
  872.         return eventPtr;
  873.     }
  874.     }
  875.  
  876.     Tcl_AppendResult((Tcl_Interp *) iPtr, "no event matches \"", string,
  877.         "\"", (char *) NULL);
  878.     return NULL;
  879. }
  880.  
  881. /*
  882.  *----------------------------------------------------------------------
  883.  *
  884.  * SubsAndEval --
  885.  *
  886.  *    Generate a new command by making a textual substitution in
  887.  *    the "cmd" argument.  Then execute the new command.
  888.  *
  889.  * Results:
  890.  *    The return value is a standard Tcl error.
  891.  *
  892.  * Side effects:
  893.  *    History gets revised if the substitution is occurring on
  894.  *    a recorded command line.  Also, the re-executed command
  895.  *    may produce side-effects.
  896.  *
  897.  *----------------------------------------------------------------------
  898.  */
  899.  
  900. static int
  901. SubsAndEval(iPtr, cmd, old, new)
  902.     register Interp *iPtr;    /* Interpreter in which to execute
  903.                  * new command. */
  904.     char *cmd;            /* Command in which to substitute. */
  905.     char *old;            /* String to search for in command. */
  906.     char *new;            /* Replacement string for "old". */
  907. {
  908.     char *src, *dst, *newCmd;
  909.     int count, oldLength, newLength, length, result;
  910.  
  911.     /*
  912.      * Figure out how much space it will take to hold the
  913.      * substituted command (and complain if the old string
  914.      * doesn't appear in the original command).
  915.      */
  916.  
  917.     oldLength = strlen(old);
  918.     newLength = strlen(new);
  919.     src = cmd;
  920.     count = 0;
  921.     while (1) {
  922.     src = strstr(src, old);
  923.     if (src == NULL) {
  924.         break;
  925.     }
  926.     src += oldLength;
  927.     count++;
  928.     }
  929.     if (count == 0) {
  930.     Tcl_AppendResult((Tcl_Interp *) iPtr, "\"", old,
  931.         "\" doesn't appear in event", (char *) NULL);
  932.     return TCL_ERROR;
  933.     }
  934.     length = strlen(cmd) + count*(newLength - oldLength);
  935.  
  936.     /*
  937.      * Generate a substituted command.
  938.      */
  939.  
  940.     newCmd = (char *) ckalloc((unsigned) (length + 1));
  941.     dst = newCmd;
  942.     while (1) {
  943.     src = strstr(cmd, old);
  944.     if (src == NULL) {
  945.         strcpy(dst, cmd);
  946.         break;
  947.     }
  948.     strncpy(dst, cmd, (size_t) (src-cmd));
  949.     dst += src-cmd;
  950.     strcpy(dst, new);
  951.     dst += newLength;
  952.     cmd = src + oldLength;
  953.     }
  954.  
  955.     RevCommand(iPtr, newCmd);
  956.     result = Tcl_Eval((Tcl_Interp *) iPtr, newCmd);
  957.     ckfree(newCmd);
  958.     return result;
  959. }
  960.  
  961. /*
  962.  *----------------------------------------------------------------------
  963.  *
  964.  * GetWords --
  965.  *
  966.  *    Given a command string, return one or more words from the
  967.  *    command string.
  968.  *
  969.  * Results:
  970.  *    The return value is a pointer to a dynamically-allocated
  971.  *    string containing the words of command specified by "words".
  972.  *    If the word specifier has improper syntax then an error
  973.  *    message is placed in iPtr->result and NULL is returned.
  974.  *
  975.  * Side effects:
  976.  *    Memory is allocated.  It is the caller's responsibilty to
  977.  *    free the returned string..
  978.  *
  979.  *----------------------------------------------------------------------
  980.  */
  981.  
  982. static char *
  983. GetWords(iPtr, command, words)
  984.     register Interp *iPtr;    /* Tcl interpreter in which to place
  985.                  * an error message if needed. */
  986.     char *command;        /* Command string. */
  987.     char *words;        /* Description of which words to extract
  988.                  * from the command.  Either num[-num] or
  989.                  * a pattern. */
  990. {
  991.     char *result;
  992.     char *start, *end, *dst;
  993.     register char *next;
  994.     int first;            /* First word desired. -1 means last word
  995.                  * only. */
  996.     int last;            /* Last word desired.  -1 means use everything
  997.                  * up to the end. */
  998.     int index;            /* Index of current word. */
  999.     char *pattern;
  1000.  
  1001.     /*
  1002.      * Figure out whether we're looking for a numerical range or for
  1003.      * a pattern.
  1004.      */
  1005.  
  1006.     pattern = NULL;
  1007.     first = 0;
  1008.     last = -1;
  1009.     if (*words == '$') {
  1010.     if (words[1] != '\0') {
  1011.         goto error;
  1012.     }
  1013.     first = -1;
  1014.     } else if (isdigit(UCHAR(*words))) {
  1015.     first = strtoul(words, &start, 0);
  1016.     if (*start == 0) {
  1017.         last = first;
  1018.     } else if (*start == '-') {
  1019.         start++;
  1020.         if (*start == '$') {
  1021.         start++;
  1022.         } else if (isdigit(UCHAR(*start))) {
  1023.         last = strtoul(start, &start, 0);
  1024.         } else {
  1025.         goto error;
  1026.         }
  1027.         if (*start != 0) {
  1028.         goto error;
  1029.         }
  1030.     }
  1031.     if ((first > last) && (last != -1)) {
  1032.         goto error;
  1033.     }
  1034.     } else {
  1035.     pattern = words;
  1036.     }
  1037.  
  1038.     /*
  1039.      * Scan through the words one at a time, copying those that are
  1040.      * relevant into the result string.  Allocate a result area large
  1041.      * enough to hold all the words if necessary.
  1042.      */
  1043.  
  1044.     result = (char *) ckalloc((unsigned) (strlen(command) + 1));
  1045.     dst = result;
  1046.     for (next = command; isspace(UCHAR(*next)); next++) {
  1047.     /* Empty loop body:  just find start of first word. */
  1048.     }
  1049.     for (index = 0; *next != 0; index++) {
  1050.     start = next;
  1051.     end = TclWordEnd(next, 0, (int *) NULL);
  1052.     if (*end != 0) {
  1053.         end++;
  1054.         for (next = end; isspace(UCHAR(*next)); next++) {
  1055.         /* Empty loop body:  just find start of next word. */
  1056.         }
  1057.     }
  1058.     if ((first > index) || ((first == -1) && (*next != 0))) {
  1059.         continue;
  1060.     }
  1061.     if ((last != -1) && (last < index)) {
  1062.         continue;
  1063.     }
  1064.     if (pattern != NULL) {
  1065.         int match;
  1066.         char savedChar = *end;
  1067.  
  1068.         *end = 0;
  1069.         match = Tcl_StringMatch(start, pattern);
  1070.         *end = savedChar;
  1071.         if (!match) {
  1072.         continue;
  1073.         }
  1074.     }
  1075.     if (dst != result) {
  1076.         *dst = ' ';
  1077.         dst++;
  1078.     }
  1079.     strncpy(dst, start, (size_t) (end-start));
  1080.     dst += end-start;
  1081.     }
  1082.     *dst = 0;
  1083.  
  1084.     /*
  1085.      * Check for an out-of-range argument index.
  1086.      */
  1087.  
  1088.     if ((last >= index) || (first >= index)) {
  1089.     ckfree(result);
  1090.     Tcl_AppendResult((Tcl_Interp *) iPtr, "word selector \"", words,
  1091.         "\" specified non-existent words", (char *) NULL);
  1092.     return NULL;
  1093.     }
  1094.     return result;
  1095.  
  1096.     error:
  1097.     Tcl_AppendResult((Tcl_Interp *) iPtr, "bad word selector \"", words,
  1098.         "\":  should be num-num or pattern", (char *) NULL);
  1099.     return NULL;
  1100. }
  1101.