📄 tclhistory.c
字号:
* * Side effects: * RevPtr is added to iPtr's revision list. * *---------------------------------------------------------------------- */static voidInsertRev(iPtr, revPtr) Interp *iPtr; /* Interpreter to use. */ register HistoryRev *revPtr; /* Revision to add to iPtr's list. */{ register HistoryRev *curPtr; register HistoryRev *prevPtr; for (curPtr = iPtr->revPtr, prevPtr = NULL; curPtr != NULL; prevPtr = curPtr, curPtr = curPtr->nextPtr) { /* * If this revision includes the new one (or vice versa) then * just eliminate the one that is a subset of the other. */ if ((revPtr->firstIndex <= curPtr->firstIndex) && (revPtr->lastIndex >= curPtr->firstIndex)) { curPtr->firstIndex = revPtr->firstIndex; curPtr->lastIndex = revPtr->lastIndex; curPtr->newSize = revPtr->newSize; ckfree(curPtr->newBytes); curPtr->newBytes = revPtr->newBytes; ckfree((char *) revPtr); return; } if ((revPtr->firstIndex >= curPtr->firstIndex) && (revPtr->lastIndex <= curPtr->lastIndex)) { ckfree(revPtr->newBytes); ckfree((char *) revPtr); return; } if (revPtr->firstIndex < curPtr->firstIndex) { break; } } /* * Insert revPtr just after prevPtr. */ if (prevPtr == NULL) { revPtr->nextPtr = iPtr->revPtr; iPtr->revPtr = revPtr; } else { revPtr->nextPtr = prevPtr->nextPtr; prevPtr->nextPtr = revPtr; }}/* *---------------------------------------------------------------------- * * RevCommand -- * * This procedure is invoked by the "history" command to record * a command revision. See the comments at the beginning of the * file for more information about revisions. * * Results: * None. * * Side effects: * Revision information is recorded. * *---------------------------------------------------------------------- */static voidRevCommand(iPtr, string) register Interp *iPtr; /* Interpreter in which to perform the * substitution. */ char *string; /* String to substitute. */{ register HistoryRev *revPtr; if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) { return; } revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev)); revPtr->firstIndex = iPtr->evalFirst - iPtr->historyFirst; revPtr->lastIndex = iPtr->evalLast - iPtr->historyFirst; revPtr->newSize = strlen(string); revPtr->newBytes = (char *) ckalloc((unsigned) (revPtr->newSize+1)); strcpy(revPtr->newBytes, string); InsertRev(iPtr, revPtr);}/* *---------------------------------------------------------------------- * * RevResult -- * * This procedure is invoked by the "history" command to record * a result revision. See the comments at the beginning of the * file for more information about revisions. * * Results: * None. * * Side effects: * Revision information is recorded. * *---------------------------------------------------------------------- */static voidRevResult(iPtr, string) register Interp *iPtr; /* Interpreter in which to perform the * substitution. */ char *string; /* String to substitute. */{ register HistoryRev *revPtr; char *evalFirst, *evalLast; char *argv[2]; if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) { return; } /* * Expand the replacement range to include the brackets that surround * the command. If there aren't any brackets (i.e. this command was * invoked at top-level) then don't do any revision. Also, if there * are several commands in brackets, of which this is just one, * then don't do any revision. */ evalFirst = iPtr->evalFirst; evalLast = iPtr->evalLast + 1; while (1) { if (evalFirst == iPtr->historyFirst) { return; } evalFirst--; if (*evalFirst == '[') { break; } if (!isspace(*evalFirst)) { return; } } if (*evalLast != ']') { return; } revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev)); revPtr->firstIndex = evalFirst - iPtr->historyFirst; revPtr->lastIndex = evalLast - iPtr->historyFirst; argv[0] = string; revPtr->newBytes = Tcl_Merge(1, argv); revPtr->newSize = strlen(revPtr->newBytes); InsertRev(iPtr, revPtr);}/* *---------------------------------------------------------------------- * * DoRevs -- * * This procedure is called to apply the history revisions that * have been recorded in iPtr. * * Results: * None. * * Side effects: * The most recent entry in the history for iPtr may be modified. * *---------------------------------------------------------------------- */static voidDoRevs(iPtr) register Interp *iPtr; /* Interpreter whose history is to * be modified. */{ register HistoryRev *revPtr; register HistoryEvent *eventPtr; char *newCommand, *p; unsigned int size; int bytesSeen, count; if (iPtr->revPtr == NULL) { return; } /* * The revision is done in two passes. The first pass computes the * amount of space needed for the revised event, and the second pass * pieces together the new event and frees up the revisions. */ eventPtr = &iPtr->events[iPtr->curEvent]; size = strlen(eventPtr->command) + 1; for (revPtr = iPtr->revPtr; revPtr != NULL; revPtr = revPtr->nextPtr) { size -= revPtr->lastIndex + 1 - revPtr->firstIndex; size += revPtr->newSize; } newCommand = (char *) ckalloc(size); p = newCommand; bytesSeen = 0; for (revPtr = iPtr->revPtr; revPtr != NULL; ) { HistoryRev *nextPtr = revPtr->nextPtr; count = revPtr->firstIndex - bytesSeen; if (count > 0) { strncpy(p, eventPtr->command + bytesSeen, count); p += count; } strncpy(p, revPtr->newBytes, revPtr->newSize); p += revPtr->newSize; bytesSeen = revPtr->lastIndex+1; ckfree(revPtr->newBytes); ckfree((char *) revPtr); revPtr = nextPtr; } if (&p[strlen(&eventPtr->command[bytesSeen]) + 1] > &newCommand[size]) { tracef("Assertion failed!\n"); } strcpy(p, eventPtr->command + bytesSeen); /* * Replace the command in the event. */ ckfree(eventPtr->command); eventPtr->command = newCommand; eventPtr->bytesAvl = size; iPtr->revPtr = NULL;}/* *---------------------------------------------------------------------- * * GetEvent -- * * Given a textual description of an event (see the manual page * for legal values) find the corresponding event and return its * command string. * * Results: * The return value is a pointer to the event named by "string". * If no such event exists, then NULL is returned and an error * message is left in iPtr. * * Side effects: * None. * *---------------------------------------------------------------------- */static HistoryEvent *GetEvent(iPtr, string) register Interp *iPtr; /* Interpreter in which to look. */ char *string; /* Description of event. */{ long eventNum; int index, length; register HistoryEvent *eventPtr; /* * First check for a numeric specification of an event. */ if (isdigit(*string) || (*string == '-')) { if (Tcl_GetInt((Tcl_Interp *) iPtr, string, &eventNum) != TCL_OK) { return NULL; } if (eventNum < 0) { eventNum += iPtr->curEventNum; } if (eventNum > iPtr->curEventNum) { Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string, "\" hasn't occurred yet", (char *) NULL); return NULL; } if ((eventNum <= iPtr->curEventNum-iPtr->numEvents) || (eventNum <= 0)) { Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string, "\" is too far in the past", (char *) NULL); return NULL; } index = iPtr->curEvent + (eventNum - iPtr->curEventNum); if (index < 0) { index += iPtr->numEvents; } return &iPtr->events[index]; } /* * Next, check for an event that contains the string as a prefix or * that matches the string in the sense of Tcl_StringMatch. */ length = strlen(string); for (index = iPtr->curEvent - 1; ; index--) { if (index < 0) { index += iPtr->numEvents; } if (index == iPtr->curEvent) { break; } eventPtr = &iPtr->events[index]; if ((strncmp(eventPtr->command, string, length) == 0) || Tcl_StringMatch(eventPtr->command, string)) { return eventPtr; } } Tcl_AppendResult((Tcl_Interp *) iPtr, "no event matches \"", string, "\"", (char *) NULL); return NULL;}/* *---------------------------------------------------------------------- * * SubsAndEval -- * * Generate a new command by making a textual substitution in * the "cmd" argument. Then execute the new command. * * Results: * The return value is a standard Tcl error. * * Side effects: * History gets revised if the substitution is occurring on * a recorded command line. Also, the re-executed command * may produce side-effects. * *---------------------------------------------------------------------- */static intSubsAndEval(iPtr, cmd, old, new) register Interp *iPtr; /* Interpreter in which to execute * new command. */ char *cmd; /* Command in which to substitute. */ char *old; /* String to search for in command. */ char *new; /* Replacement string for "old". */{ char *src, *dst, *newCmd; int count, oldLength, newLength, length, result; /* * Figure out how much space it will take to hold the * substituted command (and complain if the old string * doesn't appear in the original command). */ oldLength = strlen(old); newLength = strlen(new); src = cmd; count = 0; while (1) { src = strstr(src, old); if (src == NULL) { break; } src += oldLength; count++; } if (count == 0) { Tcl_AppendResult((Tcl_Interp *) iPtr, "\"", old, "\" doesn't appear in event", (char *) NULL); return TCL_ERROR; } length = strlen(cmd) + count*(newLength - oldLength); /* * Generate a substituted command. */ newCmd = (char *) ckalloc((unsigned) (length + 1)); dst = newCmd; while (1) { src = strstr(cmd, old); if (src == NULL) { strcpy(dst, cmd); break; } strncpy(dst, cmd, src-cmd); dst += src-cmd; strcpy(dst, new); dst += newLength; cmd = src + oldLength; } RevCommand(iPtr, newCmd); result = Tcl_Eval((Tcl_Interp *) iPtr, newCmd, 0, (char **) NULL); ckfree(newCmd); return result;}/* *---------------------------------------------------------------------- * * GetWords -- * * Given a command string, return one or more words from the * command string. * * Results: * The return value is a pointer to a dynamically-allocated * string containing the words of command specified by "words". * If the word specifier has improper syntax then an error * message is placed in iPtr->result and NULL is returned. * * Side effects: * Memory is allocated. It is the caller's responsibilty to * free the returned string.. * *---------------------------------------------------------------------- */static char *GetWords(iPtr, command, words) register Interp *iPtr; /* Tcl interpreter in which to place * an error message if needed. */ char *command; /* Command string. */ char *words; /* Description of which words to extract * from the command. Either num[-num] or * a pattern. */{ char *result; char *start, *end, *dst; register char *next; int first; /* First word desired. -1 means last word * only. */ int last; /* Last word desired. -1 means use everything * up to the end. */ int index; /* Index of current word. */ char *pattern; /* * Figure out whether we're looking for a numerical range or for * a pattern. */ pattern = NULL; first = 0; last = -1; if (*words == '$') { if (words[1] != '\0') { goto error; } first = -1; } else if (isdigit(*words)) { first = strtoul(words, &start, 0); if (*start == 0) { last = first; } else if (*start == '-') { start++; if (*start == '$') { start++; } else if (isdigit(*start)) { last = strtoul(start, &start, 0); } else { goto error; } if (*start != 0) { goto error; } } if ((first > last) && (last != -1)) { goto error; } } else { pattern = words; } /* * Scan through the words one at a time, copying those that are * relevant into the result string. Allocate a result area large * enough to hold all the words if necessary. */ result = (char *) ckalloc((unsigned) (strlen(command) + 1)); dst = result; for (next = command; isspace(*next); next++) { /* Empty loop body: just find start of first word. */ } for (index = 0; *next != 0; index++) { start = next; end = TclWordEnd(next, 0); if (*end != 0) { end++; for (next = end; isspace(*next); next++) { /* Empty loop body: just find start of next word. */ } } if ((first > index) || ((first == -1) && (*next != 0))) { continue; } if ((last != -1) && (last < index)) { continue; } if (pattern != NULL) { int match; char savedChar = *end; *end = 0; match = Tcl_StringMatch(start, pattern); *end = savedChar; if (!match) { continue; } } if (dst != result) { *dst = ' '; dst++; } strncpy(dst, start, (end-start)); dst += end-start; } *dst = 0; /* * Check for an out-of-range argument index. */ if ((last >= index) || (first >= index)) { ckfree(result); Tcl_AppendResult((Tcl_Interp *) iPtr, "word selector \"", words, "\" specified non-existent words", (char *) NULL); return NULL; } return result; error: Tcl_AppendResult((Tcl_Interp *) iPtr, "bad word selector \"", words, "\": should be num-num or pattern", (char *) NULL); return NULL;}#elsestatic const char file_name[] = "tclHistory.c";#endif /* EXCLUDE_TCL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -