home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume25 / tcl / part21 < prev    next >
Encoding:
Text File  |  1991-11-15  |  33.0 KB  |  1,152 lines

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