Check-in [357858a659]
Not logged in
Overview
Comment:Made readline an object command to fix a number of memory leaks. Added commands "readline text" to retrieve the current input and "readline update" to redraw the current input.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 357858a65919303bcab6cdceedac43e46af121e4
User & Date: mpatton@jhu.edu on 2000-08-27 13:23:15
Other Links: manifest | tags
Context
2000-08-27
13:25
Updated docs for text and update commands. check-in: 4f73d8af60 user: mpatton@jhu.edu tags: trunk
13:23
Made readline an object command to fix a number of memory leaks. Added commands "readline text" to retrieve the current input and "readline update" to redraw the current input. check-in: 357858a659 user: mpatton@jhu.edu tags: trunk
2000-07-27
13:14
* configure.in: changed a == to = in a sh test (reported by "Daniel O'Connor" <darius@dons.net.au>) check-in: 5096b666f4 user: johannes@zellner.org tags: trunk
Changes

Modified tclreadline.c from [75b74b5625] to [bd4dbca0d1].

67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
/* forward declarations. */
static char* stripleft(char* in);
static char* stripright(char* in);
static char* stripwhite(char* in);
static int TclReadlineLineComplete(void);
static void TclReadlineTerminate(int state);
static char* TclReadlineQuote(char* text, char* quotechars);
static int TclReadlineCmd(ClientData clientData, Tcl_Interp* interp, int argc, char** argv);

static void TclReadlineReadHandler(ClientData clientData, int mask);
static void TclReadlineLineCompleteHandler(char* ptr);
static int TclReadlineInitialize(Tcl_Interp* interp, char* historyfile);
static int blank_line(char* str);
static char** TclReadlineCompletion(char* text, int start, int end);
static char* TclReadline0generator(char* text, int state);
static char* TclReadlineKnownCommands(char* text, int state, int mode);







|
>







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* forward declarations. */
static char* stripleft(char* in);
static char* stripright(char* in);
static char* stripwhite(char* in);
static int TclReadlineLineComplete(void);
static void TclReadlineTerminate(int state);
static char* TclReadlineQuote(char* text, char* quotechars);
static int TclReadlineCmd(ClientData clientData, Tcl_Interp *interp, int objc,
                   Tcl_Obj *CONST objv[]);
static void TclReadlineReadHandler(ClientData clientData, int mask);
static void TclReadlineLineCompleteHandler(char* ptr);
static int TclReadlineInitialize(Tcl_Interp* interp, char* historyfile);
static int blank_line(char* str);
static char** TclReadlineCompletion(char* text, int start, int end);
static char* TclReadline0generator(char* text, int state);
static char* TclReadlineKnownCommands(char* text, int state, int mode);
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
258
	}
	Tcl_DStringAppend(&result, ptr, 1);
    }
    result_c = strdup(Tcl_DStringValue(&result));
    return result_c;
}

static int
TclReadlineCmd(
    ClientData  clientData,
    Tcl_Interp* interp,     /* Current interpreter */
    int         argc,       /* Number of arguments */
    char**      argv        /* Argument strings    */
	      )
{
    int i, obj_idx, status;
    Tcl_Obj** objv = (Tcl_Obj**) MALLOC((argc + 1) * sizeof(Tcl_Obj *));

    static char *subCmds[] = {
	"read", "initialize", "write", "add", "complete",
	"customcompleter", "builtincompleter", "eofchar",
	"reset-terminal", "bell",
	(char *) NULL
    };
    enum SubCmdIdx {
	TCLRL_READ, TCLRL_INITIALIZE, TCLRL_WRITE, TCLRL_ADD, TCLRL_COMPLETE,
	TCLRL_CUSTOMCOMPLETER, TCLRL_BUILTINCOMPLETER, TCLRL_EOFCHAR,
	TCLRL_RESET_TERMINAL, TCLRL_BELL
    };


    Tcl_ResetResult(interp); /* clear the result space */

    for (i = 0;  i < argc;  i++) {
	Tcl_Obj* objPtr = Tcl_NewStringObj(argv[i], -1);
	Tcl_IncrRefCount(objPtr);
	objv[i] = objPtr;
    }
    objv[argc] = 0; /* terminate */

    if (argc < 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
	return TCL_ERROR;
    }

    status = Tcl_GetIndexFromObj
    (interp, objv[1], subCmds, "option", 0, (int *) &obj_idx);

    if (status != TCL_OK) {
	FREE(objv)
	return status;
    }

    switch (obj_idx) {

	case TCLRL_READ:

	    rl_callback_handler_install(argc == 3 ? argv[2] : "%",

		TclReadlineLineCompleteHandler);

	    Tcl_CreateFileHandler(0, TCL_READABLE,
		TclReadlineReadHandler, (ClientData) NULL);

	    /**
	     * Main Loop.
	     * XXX each modification of the global variables







|
|
<
<
<
<
<


<




|





|

<



<
<
<
<
<
<
<
|








<







|
>
|







194
195
196
197
198
199
200
201
202





203
204

205
206
207
208
209
210
211
212
213
214
215
216

217
218
219







220
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
	}
	Tcl_DStringAppend(&result, ptr, 1);
    }
    result_c = strdup(Tcl_DStringValue(&result));
    return result_c;
}

static int TclReadlineCmd(ClientData clientData, Tcl_Interp *interp, int objc,
			  Tcl_Obj *CONST objv[])





{
    int i, obj_idx, status;


    static char *subCmds[] = {
	"read", "initialize", "write", "add", "complete",
	"customcompleter", "builtincompleter", "eofchar",
	"reset-terminal", "bell", "text", "update",
	(char *) NULL
    };
    enum SubCmdIdx {
	TCLRL_READ, TCLRL_INITIALIZE, TCLRL_WRITE, TCLRL_ADD, TCLRL_COMPLETE,
	TCLRL_CUSTOMCOMPLETER, TCLRL_BUILTINCOMPLETER, TCLRL_EOFCHAR,
	TCLRL_RESET_TERMINAL, TCLRL_BELL, TCLRL_TEXT, TCLRL_UPDATE
    };


    Tcl_ResetResult(interp); /* clear the result space */








    if (objc < 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
	return TCL_ERROR;
    }

    status = Tcl_GetIndexFromObj
    (interp, objv[1], subCmds, "option", 0, (int *) &obj_idx);

    if (status != TCL_OK) {

	return status;
    }

    switch (obj_idx) {

	case TCLRL_READ:

	    rl_callback_handler_install(
			       objc == 3 ? Tcl_GetStringFromObj(objv[2], 0)
			       : "%", TclReadlineLineCompleteHandler);

	    Tcl_CreateFileHandler(0, TCL_READABLE,
		TclReadlineReadHandler, (ClientData) NULL);

	    /**
	     * Main Loop.
	     * XXX each modification of the global variables
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
319
320
321
322
323

324
325
326
327
328
329
330
331
332
333


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

359
360
361
362
363
364
365
366
367
368
369


370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416






417
418
419
420

421
422


























423
424
425
426
427
428
429
		    return tclrl_state;
		    /* NOTREACHED */
		    break;
	    }
	    break;

	case TCLRL_INITIALIZE:
	    if (3 != argc) {
		Tcl_WrongNumArgs(interp, 2, objv, "historyfile");
		return TCL_ERROR;
	    } else {
		return TclReadlineInitialize(interp, argv[2]);

	    }
	    break;

	case TCLRL_WRITE:
	    if (3 != argc) {
		Tcl_WrongNumArgs(interp, 2, objv, "historyfile");
		return TCL_ERROR;
	    }  else if (write_history(argv[2])) {
		Tcl_AppendResult(interp, "unable to write history to `",
		    argv[2], "'\n", (char*) NULL);
		return TCL_ERROR;
	    }
	    if (tclrl_history_length >= 0) {

		history_truncate_file(argv[2], tclrl_history_length);
	    }
	    return TCL_OK;
	    break;

	case TCLRL_ADD:
	    if (3 != argc) {
		Tcl_WrongNumArgs(interp, 2, objv, "completerLine");
		return TCL_ERROR;
	    } else if (TclReadlineKnownCommands(argv[2], (int) 0, _CMD_SET)) {


		Tcl_AppendResult(interp, "unable to add command \"",
		    argv[2], "\"\n", (char*) NULL);
	    }
	    break;

	case TCLRL_COMPLETE:
	    if (3 != argc) {
		Tcl_WrongNumArgs(interp, 2, objv, "line");
		return TCL_ERROR;
	    } else if (Tcl_CommandComplete(argv[2])) {
		Tcl_AppendResult(interp, "1", (char*) NULL);
	    } else {
		Tcl_AppendResult(interp, "0", (char*) NULL);
	    }
	    break;

	case TCLRL_CUSTOMCOMPLETER:
	    if (argc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?scriptCompleter?");
		return TCL_ERROR;
	    } else if (3 == argc) {
		if (tclrl_custom_completer)
		    FREE(tclrl_custom_completer);
		if (!blank_line(argv[2]))
		    tclrl_custom_completer = stripwhite(strdup(argv[2]));

	    }
	    Tcl_AppendResult(interp, tclrl_custom_completer, (char*) NULL);
	    break;

	case TCLRL_BUILTINCOMPLETER:
	    if (argc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?boolean?");
		return TCL_ERROR;
	    } else if (3 == argc) {
		int bool = tclrl_use_builtin_completer;
		if (TCL_OK != Tcl_GetBoolean(interp, argv[2], &bool)) {


		    Tcl_AppendResult(interp,
			"wrong # args: should be a boolean value.",
			(char*) NULL);
		    return TCL_ERROR;
		} else {
		    tclrl_use_builtin_completer = bool;
		}
	    }
	    Tcl_AppendResult(interp, tclrl_use_builtin_completer ? "1" : "0",
		(char*) NULL);
	    break;

	case TCLRL_EOFCHAR:
	    if (argc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?script?");
		return TCL_ERROR;
	    } else if (3 == argc) {
		if (tclrl_eof_string)
		    FREE(tclrl_eof_string);
		if (!blank_line(argv[2]))
		    tclrl_eof_string = stripwhite(strdup(argv[2]));

	    }
	    Tcl_AppendResult(interp, tclrl_eof_string, (char*) NULL);
	    break;

	case TCLRL_RESET_TERMINAL:
	    /* TODO: add this to the completer */
	    if (argc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?terminal-name?");
		return TCL_ERROR;
	    }
	    if (3 == argc) {
		/*
		 * - tcl8.0 doesn't have Tcl_GetString()
		 * - rl_reset_terminal() might be defined
		 *   to take no arguments. This might produce
		 *   a compiler warning.
		 */
		rl_reset_terminal(Tcl_GetStringFromObj(objv[2], (int*) NULL));
#ifdef CLEANUP_AFER_SIGNAL
	    } else {
		rl_cleanup_after_signal();
#endif
	    }
	    break;

	case TCLRL_BELL:






	    /*
	     * ring the terminal bell obeying the current
	     * settings -- audible or visible.
	     */

	    ding();
	    break;



























	default:
	    goto BAD_COMMAND;
	    /* NOTREACHED */
	    break;
    }








|



|
>




|


|

|



>
|





|


|
>
>

|




|


|







|


|


|
|
>





|


|

|
>
>













|


|


|
|
>






|



|

|




|








>
>
>
>
>
>




>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
		    return tclrl_state;
		    /* NOTREACHED */
		    break;
	    }
	    break;

	case TCLRL_INITIALIZE:
	    if (3 != objc) {
		Tcl_WrongNumArgs(interp, 2, objv, "historyfile");
		return TCL_ERROR;
	    } else {
		return TclReadlineInitialize(interp,
					     Tcl_GetStringFromObj(objv[2], 0));
	    }
	    break;

	case TCLRL_WRITE:
	    if (3 != objc) {
		Tcl_WrongNumArgs(interp, 2, objv, "historyfile");
		return TCL_ERROR;
	    }  else if (write_history(Tcl_GetStringFromObj(objv[2], 0))) {
		Tcl_AppendResult(interp, "unable to write history to `",
		    Tcl_GetStringFromObj(objv[2], 0), "'\n", (char*) NULL);
		return TCL_ERROR;
	    }
	    if (tclrl_history_length >= 0) {
		history_truncate_file(Tcl_GetStringFromObj(objv[2], 0),
				      tclrl_history_length);
	    }
	    return TCL_OK;
	    break;

	case TCLRL_ADD:
	    if (3 != objc) {
		Tcl_WrongNumArgs(interp, 2, objv, "completerLine");
		return TCL_ERROR;
	    } else if (TclReadlineKnownCommands(
				     Tcl_GetStringFromObj(objv[2], 0),
				     (int) 0, _CMD_SET)) {
		Tcl_AppendResult(interp, "unable to add command \"",
		    Tcl_GetStringFromObj(objv[2], 0), "\"\n", (char*) NULL);
	    }
	    break;

	case TCLRL_COMPLETE:
	    if (3 != objc) {
		Tcl_WrongNumArgs(interp, 2, objv, "line");
		return TCL_ERROR;
	    } else if (Tcl_CommandComplete(Tcl_GetStringFromObj(objv[2], 0))) {
		Tcl_AppendResult(interp, "1", (char*) NULL);
	    } else {
		Tcl_AppendResult(interp, "0", (char*) NULL);
	    }
	    break;

	case TCLRL_CUSTOMCOMPLETER:
	    if (objc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?scriptCompleter?");
		return TCL_ERROR;
	    } else if (3 == objc) {
		if (tclrl_custom_completer)
		    FREE(tclrl_custom_completer);
		if (!blank_line(Tcl_GetStringFromObj(objv[2], 0)))
		    tclrl_custom_completer =
		         stripwhite(strdup(Tcl_GetStringFromObj(objv[2], 0)));
	    }
	    Tcl_AppendResult(interp, tclrl_custom_completer, (char*) NULL);
	    break;

	case TCLRL_BUILTINCOMPLETER:
	    if (objc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?boolean?");
		return TCL_ERROR;
	    } else if (3 == objc) {
		int bool = tclrl_use_builtin_completer;
		if (TCL_OK != Tcl_GetBoolean(interp,
					     Tcl_GetStringFromObj(objv[2], 0),
					     &bool)) {
		    Tcl_AppendResult(interp,
			"wrong # args: should be a boolean value.",
			(char*) NULL);
		    return TCL_ERROR;
		} else {
		    tclrl_use_builtin_completer = bool;
		}
	    }
	    Tcl_AppendResult(interp, tclrl_use_builtin_completer ? "1" : "0",
		(char*) NULL);
	    break;

	case TCLRL_EOFCHAR:
	    if (objc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?script?");
		return TCL_ERROR;
	    } else if (3 == objc) {
		if (tclrl_eof_string)
		    FREE(tclrl_eof_string);
		if (!blank_line(Tcl_GetStringFromObj(objv[2], 0)))
		    tclrl_eof_string = 
		        stripwhite(strdup(Tcl_GetStringFromObj(objv[2], 0)));
	    }
	    Tcl_AppendResult(interp, tclrl_eof_string, (char*) NULL);
	    break;

	case TCLRL_RESET_TERMINAL:
	    /* TODO: add this to the completer */
	    if (objc > 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?terminal-name?");
		return TCL_ERROR;
	    }
	    if (3 == objc) {
		/*
		 * - tcl8.0 doesn't have Tcl_GetStringFromObj()
		 * - rl_reset_terminal() might be defined
		 *   to take no arguments. This might produce
		 *   a compiler warning.
		 */
		rl_reset_terminal(Tcl_GetStringFromObj(objv[2], 0));
#ifdef CLEANUP_AFER_SIGNAL
	    } else {
		rl_cleanup_after_signal();
#endif
	    }
	    break;

	case TCLRL_BELL:
	    if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return TCL_ERROR;
	    }


	    /*
	     * ring the terminal bell obeying the current
	     * settings -- audible or visible.
	     */

	    ding();
	    break;

        case TCLRL_UPDATE:
	    if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return TCL_ERROR;
	    }

	    /* Update the input line */

	    if (rl_line_buffer) {
	        rl_forced_update_display();
	    }

	    break;


        case TCLRL_TEXT:
	    if (objc != 2) {
		Tcl_WrongNumArgs(interp, 2, objv, "");
		return TCL_ERROR;
	    }

	    /* Return the current input line */
	    Tcl_SetObjResult(interp,
		   Tcl_NewStringObj(rl_line_buffer ? rl_line_buffer : "", -1));
	    break;

	default:
	    goto BAD_COMMAND;
	    /* NOTREACHED */
	    break;
    }

528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
    return Tclreadline_Init(interp);
}

int
Tclreadline_Init(Tcl_Interp *interp)
{
    int status;
    Tcl_CreateCommand(interp, "::tclreadline::readline", TclReadlineCmd,
	(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    tclrl_interp = interp;
    if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::historyLength",
		(char*) &tclrl_history_length, TCL_LINK_INT)))
	return status;

    if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::library",







|







556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
    return Tclreadline_Init(interp);
}

int
Tclreadline_Init(Tcl_Interp *interp)
{
    int status;
    Tcl_CreateObjCommand(interp, "::tclreadline::readline", TclReadlineCmd,
	(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    tclrl_interp = interp;
    if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::historyLength",
		(char*) &tclrl_history_length, TCL_LINK_INT)))
	return status;

    if (TCL_OK != (status = Tcl_LinkVar(interp, "::tclreadline::library",