Tcl Extension Architecture (TEA) Sample Extension

Check-in [2c5e0e025e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:command token work startet. Also work on thread save (Ticket [ecf13be4c9])
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | 19630c0c-unload
Files: files | file ages | folders
SHA3-256: 2c5e0e025efd0b9ffd5292e9907782e50505b6b61cb22125917662b2af35b2c0
User & Date: oehhar 2024-12-10 09:12:07
References
2024-12-10
09:14 Ticket [19630c0c49] Add unload procedure to allow deletion of the sample extension dll status still Open with 3 other changes artifact: f6f383b4ac user: oehhar
Context
2024-12-10
16:58
Add assoc data storage (thanks, Emiliano !) check-in: eba8935a06 user: oehhar tags: 19630c0c-unload
09:12
command token work startet. Also work on thread save (Ticket [ecf13be4c9]) check-in: 2c5e0e025e user: oehhar tags: 19630c0c-unload
07:10
Thread save: work in progress check-in: 792cc30232 user: oehhar tags: 19630c0c-unload
Changes
Unified Diff Ignore Whitespace Patch
Changes to generic/tclsample.c.
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 * Create struct with thread local data.
 * This mechanism is called "clientData" in Tcl.
 * We use two of them. One for the sha1 command which holds the context queue.
 * Another for the init command to store the command tolkens to delete them on
 * unload.
 */

struct SHA1ClientData {
    /* State of sha1 command */
    int numcontexts;
    SHA1_CTX *sha1Contexts;
    Tcl_Size *ctxtotalRead;
}

struct CmdClientData {
    /* Tokens of the created commands to delete them on unload */
    Tcl_Command sha1CmdTolken;
    Tcl_Command buildInfoCmdTolken;
}

/* Prototype of the function executed by command "sha1" */

static int Sha1_Cmd(void *clientData, Tcl_Interp *interp,
		int objc, Tcl_Obj *const objv[]);

#define DIGESTSIZE 20







|




|





|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 * Create struct with thread local data.
 * This mechanism is called "clientData" in Tcl.
 * We use two of them. One for the sha1 command which holds the context queue.
 * Another for the init command to store the command tolkens to delete them on
 * unload.
 */

struct Sha1ClientData {
    /* State of sha1 command */
    int numcontexts;
    SHA1_CTX *sha1Contexts;
    Tcl_Size *ctxtotalRead;
};

struct CmdClientData {
    /* Tokens of the created commands to delete them on unload */
    Tcl_Command sha1CmdTolken;
    Tcl_Command buildInfoCmdTolken;
};

/* Prototype of the function executed by command "sha1" */

static int Sha1_Cmd(void *clientData, Tcl_Interp *interp,
		int objc, Tcl_Obj *const objv[]);

#define DIGESTSIZE 20
76
77
78
79
80
81
82






83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
Sha1_Cmd(
    ClientData clientData,	/* Client data with thread local state */
    Tcl_Interp *interp,		/* Current interpreter */
    int objc,			/* Number of arguments */
    Tcl_Obj *const objv[]	/* Argument strings */
    )
{






    /*
     * The default base is hex
     */

    int log2base = 4;
    int a;
    Tcl_Obj *stringObj = NULL;
    Tcl_Channel chan = NULL;
    Tcl_Channel copychan = NULL;
    int mode;
    int contextnum = 0;
#define sha1Context (sha1Contexts[contextnum])
    char *bufPtr;
    Tcl_WideInt maxbytes = 0;
    int doinit = 1;
    int dofinal = 1;
    Tcl_Obj *descriptorObj = NULL;
    Tcl_Size totalRead = 0, n;
    int i, j, mask, bits, offset;
    (void)dummy;

    /*
     * For binary representation + null char
     */

    char buf[129];
    unsigned char digest[DIGESTSIZE];







>
>
>
>
>
>











<







<







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106

107
108
109
110
111
112
113
Sha1_Cmd(
    ClientData clientData,	/* Client data with thread local state */
    Tcl_Interp *interp,		/* Current interpreter */
    int objc,			/* Number of arguments */
    Tcl_Obj *const objv[]	/* Argument strings */
    )
{
    /*
     * Get my thread local memory
     */

    struct Sha1ClientData *sha1ClientDataPtr = clientData;

    /*
     * The default base is hex
     */

    int log2base = 4;
    int a;
    Tcl_Obj *stringObj = NULL;
    Tcl_Channel chan = NULL;
    Tcl_Channel copychan = NULL;
    int mode;
    int contextnum = 0;

    char *bufPtr;
    Tcl_WideInt maxbytes = 0;
    int doinit = 1;
    int dofinal = 1;
    Tcl_Obj *descriptorObj = NULL;
    Tcl_Size totalRead = 0, n;
    int i, j, mask, bits, offset;


    /*
     * For binary representation + null char
     */

    char buf[129];
    unsigned char digest[DIGESTSIZE];
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

147
148

149
150
151
152
153
154
155
156
157
158
159
	 * Everything except -init takes an argument...
	 */
	if ((index != SHAOPT_INIT) && (++a >= objc)) {
	    goto wrongArgs;
	}
	switch ((enum ShaOpts) index) {
	case SHAOPT_INIT:
	    for (contextnum = 1; contextnum < numcontexts; contextnum++) {
		if (ctxtotalRead[contextnum] == -1) {
		    break;
		}
	    }
	    if (contextnum == numcontexts) {
		/*
		 * Allocate a new context.
		 */

		numcontexts++;
		sha1Contexts = (SHA1_CTX *) ckrealloc((void *) sha1Contexts,

			numcontexts * sizeof(SHA1_CTX));
		ctxtotalRead = (Tcl_Size *)ckrealloc(ctxtotalRead,

			numcontexts * sizeof(Tcl_Size));
	    }
	    ctxtotalRead[contextnum] = 0;
	    SHA1Init(&sha1Context);
	    snprintf(buf, sizeof(buf), "sha1%d", contextnum);
	    Tcl_AppendResult(interp, buf, NULL);
	    return TCL_OK;
	case SHAOPT_CHAN:
	    chan = Tcl_GetChannel(interp, Tcl_GetString(objv[a]), &mode);
	    if (chan == NULL) {
		return TCL_ERROR;







|
|



|




|
|
>
|
|
>
|

|
|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
	 * Everything except -init takes an argument...
	 */
	if ((index != SHAOPT_INIT) && (++a >= objc)) {
	    goto wrongArgs;
	}
	switch ((enum ShaOpts) index) {
	case SHAOPT_INIT:
	    for (contextnum = 1; contextnum < sha1ClientDataPtr->numcontexts; contextnum++) {
		if (sha1ClientDataPtr->ctxtotalRead[contextnum] == -1) {
		    break;
		}
	    }
	    if (contextnum == sha1ClientDataPtr->numcontexts) {
		/*
		 * Allocate a new context.
		 */

		sha1ClientDataPtr->numcontexts++;
		sha1ClientDataPtr->sha1Contexts = (SHA1_CTX *) ckrealloc(
			(void *) sha1ClientDataPtr->sha1Contexts,
			sha1ClientDataPtr->numcontexts * sizeof(SHA1_CTX));
		sha1ClientDataPtr->ctxtotalRead = (Tcl_Size *)ckrealloc(
			sha1ClientDataPtr->ctxtotalRead,
			sha1ClientDataPtr->numcontexts * sizeof(Tcl_Size));
	    }
	    sha1ClientDataPtr->ctxtotalRead[contextnum] = 0;
	    SHA1Init(&sha1ClientDataPtr->sha1Contexts[contextnum]);
	    snprintf(buf, sizeof(buf), "sha1%d", contextnum);
	    Tcl_AppendResult(interp, buf, NULL);
	    return TCL_OK;
	case SHAOPT_CHAN:
	    chan = Tcl_GetChannel(interp, Tcl_GetString(objv[a]), &mode);
	    if (chan == NULL) {
		return TCL_ERROR;
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
	    dofinal = 0;
	    continue;
	}
    }

    if (descriptorObj != NULL) {
	if ((sscanf(Tcl_GetString(descriptorObj), "sha1%d",
		&contextnum) != 1) || (contextnum >= numcontexts) ||
		(ctxtotalRead[contextnum] == -1)) {
	    Tcl_AppendResult(interp, "invalid sha1 descriptor \"",
		    Tcl_GetString(descriptorObj), "\"", NULL);
	    return TCL_ERROR;
	}
    }

    if (doinit) {
	SHA1Init(&sha1Context);
    }

    if (stringObj != NULL) {
	char *string;
	if (chan != NULL) {
	    goto wrongArgs;
	}
	string = Tcl_GetStringFromObj(stringObj, &totalRead);

	SHA1Update(&sha1Context, (unsigned char *) string, totalRead);
    } else if (chan != NULL) {
	bufPtr = (char *)ckalloc(TCL_READ_CHUNK_SIZE);
	totalRead = 0;






	while ((n = Tcl_Read(chan, bufPtr,
		maxbytes == 0
		? TCL_READ_CHUNK_SIZE
		: (TCL_READ_CHUNK_SIZE < maxbytes
		? TCL_READ_CHUNK_SIZE
		: maxbytes))) != 0) {
	    if (n == -1) {
		ckfree(bufPtr);
		Tcl_AppendResult(interp, Tcl_GetString(objv[0]), ": ",
			Tcl_GetChannelName(chan), Tcl_PosixError(interp),
			NULL);
		return TCL_ERROR;
	    }

	    totalRead += n;


	    SHA1Update(&sha1Context, (unsigned char *) bufPtr, n);

	    if (copychan != NULL) {
		n = Tcl_Write(copychan, bufPtr, n);
		if (n == -1) {
		    ckfree(bufPtr);
		    Tcl_AppendResult(interp, Tcl_GetString(objv[0]), ": ",
			    Tcl_GetChannelName(copychan),







|
|







|








>
|



>
>
>
>
>
>
















>
|







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
259
260
261
262
263
264
265
266
267
268
269
	    dofinal = 0;
	    continue;
	}
    }

    if (descriptorObj != NULL) {
	if ((sscanf(Tcl_GetString(descriptorObj), "sha1%d",
		&contextnum) != 1) || (contextnum >= sha1ClientDataPtr->numcontexts) ||
		(sha1ClientDataPtr->ctxtotalRead[contextnum] == -1)) {
	    Tcl_AppendResult(interp, "invalid sha1 descriptor \"",
		    Tcl_GetString(descriptorObj), "\"", NULL);
	    return TCL_ERROR;
	}
    }

    if (doinit) {
	SHA1Init(&sha1ClientDataPtr->sha1Contexts[contextnum]);
    }

    if (stringObj != NULL) {
	char *string;
	if (chan != NULL) {
	    goto wrongArgs;
	}
	string = Tcl_GetStringFromObj(stringObj, &totalRead);
	SHA1Update(&sha1ClientDataPtr->sha1Contexts[contextnum],
		(unsigned char *) string, totalRead);
    } else if (chan != NULL) {
	bufPtr = (char *)ckalloc(TCL_READ_CHUNK_SIZE);
	totalRead = 0;
	/*
	 * FIXME: MS-VC 2015 gives the following warning in the next line I
	 * was not able to fix (translated from German):
	 * warning C4244: "Function": Conversion of "Tcl_WideInt" to "int",
	 * possible data loss
	 */
	while ((n = Tcl_Read(chan, bufPtr,
		maxbytes == 0
		? TCL_READ_CHUNK_SIZE
		: (TCL_READ_CHUNK_SIZE < maxbytes
		? TCL_READ_CHUNK_SIZE
		: maxbytes))) != 0) {
	    if (n == -1) {
		ckfree(bufPtr);
		Tcl_AppendResult(interp, Tcl_GetString(objv[0]), ": ",
			Tcl_GetChannelName(chan), Tcl_PosixError(interp),
			NULL);
		return TCL_ERROR;
	    }

	    totalRead += n;

	    SHA1Update(&sha1ClientDataPtr->sha1Contexts[contextnum],
		    (unsigned char *) bufPtr, n);

	    if (copychan != NULL) {
		n = Tcl_Write(copychan, bufPtr, n);
		if (n == -1) {
		    ckfree(bufPtr);
		    Tcl_AppendResult(interp, Tcl_GetString(objv[0]), ": ",
			    Tcl_GetChannelName(copychan),
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
	}
	ckfree(bufPtr);
    } else if (descriptorObj == NULL) {
	goto wrongArgs;
    }

    if (!dofinal) {
	ctxtotalRead[contextnum] += totalRead;
	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(totalRead));
	return TCL_OK;
    }

    if (stringObj == NULL) {
	totalRead += ctxtotalRead[contextnum];
	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(totalRead));
    }

    SHA1Final(&sha1Context, digest);

    /*
     * Take the 20 byte array and print it in the requested base
     * e.g. log2base=1 => binary,  log2base=4 => hex
     */

    n = log2base;







|





|



|







279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
	}
	ckfree(bufPtr);
    } else if (descriptorObj == NULL) {
	goto wrongArgs;
    }

    if (!dofinal) {
	sha1ClientDataPtr->ctxtotalRead[contextnum] += totalRead;
	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(totalRead));
	return TCL_OK;
    }

    if (stringObj == NULL) {
	totalRead += sha1ClientDataPtr->ctxtotalRead[contextnum];
	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(totalRead));
    }

    SHA1Final(&sha1ClientDataPtr->sha1Contexts[contextnum], digest);

    /*
     * Take the 20 byte array and print it in the requested base
     * e.g. log2base=1 => binary,  log2base=4 => hex
     */

    n = log2base;
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
        offset -= n;
        buf[j++] = itoa64f[(bits>>8)&mask];
    }
    buf[j++] = itoa64f[(bits>>8)&mask];
    buf[j++] = '\0';
    Tcl_AppendResult(interp, buf, NULL);
    if (contextnum > 0) {
	ctxtotalRead[contextnum] = -1;
    }
    return TCL_OK;

wrongArgs:
    Tcl_AppendResult(interp, "wrong # args: should be either:\n",
	    "  ",
	    Tcl_GetString(objv[0]),







|







326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
        offset -= n;
        buf[j++] = itoa64f[(bits>>8)&mask];
    }
    buf[j++] = itoa64f[(bits>>8)&mask];
    buf[j++] = '\0';
    Tcl_AppendResult(interp, buf, NULL);
    if (contextnum > 0) {
	sha1ClientDataPtr->ctxtotalRead[contextnum] = -1;
    }
    return TCL_OK;

wrongArgs:
    Tcl_AppendResult(interp, "wrong # args: should be either:\n",
	    "  ",
	    Tcl_GetString(objv[0]),
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
 *
 *----------------------------------------------------------------------
 */

static void
Sha1_CmdDeleteProc(ClientData clientData)
{
    struct SHA1ClientData *sha1ClientDataPointer = ClientData;
    
    /*
     * Release the sha1 queue
     */
    
    ckfree(sha1ClientDataPointer->sha1Contexts);
    ckfree(sha1ClientDataPointer->ctxtotalRead);

    /*
     * Release the procedure client data
     */

    ckfree(sha1ClientDataPointer);
}


/*
 *----------------------------------------------------------------------
 *
 * Sample_Init --







|


|


|
|





|







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
 *
 *----------------------------------------------------------------------
 */

static void
Sha1_CmdDeleteProc(ClientData clientData)
{
    struct Sha1ClientData *sha1ClientDataPtr = clientData;
    
    /*
     * Release the sha1 contextes
     */
    
    ckfree(sha1ClientDataPtr->sha1Contexts);
    ckfree(sha1ClientDataPtr->ctxtotalRead);

    /*
     * Release the procedure client data
     */

    ckfree(sha1ClientDataPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * Sample_Init --
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
458
459
460
461
462
463
464
465
466
467
468
469
470
extern "C" {
#endif  /* __cplusplus */
DLLEXPORT int
Sample_Init(
    Tcl_Interp* interp)		/* Tcl interpreter */
{
    Tcl_CmdInfo info;
    struct CmdClientData *cmdClientDataPointer;
    struct SHA1ClientData *sha1ClientDataPointer;

    /*
     * Require compatible TCL version.
     * Possible version requirement strings:
     * - "8.1-": 8.1 and any higher version
     * - "8.1": 8.1.x to 8.7.x
     * - "8.1 9": allow 8.1.x to 8.7.x and 9.x.x, but not 10.x.x
     * Note that Tcl_InitStubs is a macro, which is replaced by a Tcl version
     * check only, if TCL_STUBS is not defined (e.g. direct link, static build)
     */
    if (Tcl_InitStubs(interp, "8.1-", 0) == NULL) {
	return TCL_ERROR;
    }
    
    /*
     * Create and init my client data
     */
    CmdClientDataPointer = ckalloc(sizeof(struct Sha1ClientData));
    CmdClientDataPointer->sha1CmdTolken = NULL;
    CmdClientDataPointer->buildInfoCmdTolken = NULL;

    /*
     * Init the sha1 context queues
     */
    sha1ClientDataPointer = ckalloc(sizeof(struct Sha1ClientData));
    sha1ClientDataPointer->numcontexts = 1;
    sha1ClientDataPointer->SHA1_CTX = (SHA1_CTX *) ckalloc(sizeof(SHA1_CTX));
    sha1ClientDataPointer->ctxtotalRead = (Tcl_Size *) ckalloc(sizeof(Tcl_Size));

    /*
     * Create the sha1 command.
     * Pass the client data pointer to the procedure, so the que data is available.
     * Also, register a delete proc to clear the sha1 queue on deletion.
     */

    cmdClientDataPointer->sha1CmdTolken = Tcl_CreateObjCommand(
	    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
	    sha1ClientDataPointer, Sha1_CmdDeleteProc);

    /*
     * Create the buildinfo command if tcl supports it
     */

    if (Tcl_GetCommandInfo(interp, "::tcl::build-info", &info)) {
	myClientDataPointer->buildInfoCmdTolken = Tcl_CreateObjCommand(
		interp,
		"::sample::build-info",
		info.objProc, (void *)(
		    PACKAGE_VERSION "+" STRINGIFY(SAMPLE_VERSION_UUID)
#if defined(__clang__) && defined(__clang_major__)
			    ".clang-" STRINGIFY(__clang_major__)
#if __clang_minor__ < 10







|
|

















|
|
|




|
|
|
|







|

|






|







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
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
extern "C" {
#endif  /* __cplusplus */
DLLEXPORT int
Sample_Init(
    Tcl_Interp* interp)		/* Tcl interpreter */
{
    Tcl_CmdInfo info;
    struct CmdClientData *cmdClientDataPtr;
    struct Sha1ClientData *sha1ClientDataPtr;

    /*
     * Require compatible TCL version.
     * Possible version requirement strings:
     * - "8.1-": 8.1 and any higher version
     * - "8.1": 8.1.x to 8.7.x
     * - "8.1 9": allow 8.1.x to 8.7.x and 9.x.x, but not 10.x.x
     * Note that Tcl_InitStubs is a macro, which is replaced by a Tcl version
     * check only, if TCL_STUBS is not defined (e.g. direct link, static build)
     */
    if (Tcl_InitStubs(interp, "8.1-", 0) == NULL) {
	return TCL_ERROR;
    }
    
    /*
     * Create and init my client data
     */
    cmdClientDataPtr = ckalloc(sizeof(struct Sha1ClientData));
    cmdClientDataPtr->sha1CmdTolken = NULL;
    cmdClientDataPtr->buildInfoCmdTolken = NULL;

    /*
     * Init the sha1 context queues
     */
    sha1ClientDataPtr = ckalloc(sizeof(struct Sha1ClientData));
    sha1ClientDataPtr->numcontexts = 1;
    sha1ClientDataPtr->sha1Contexts = (SHA1_CTX *) ckalloc(sizeof(SHA1_CTX));
    sha1ClientDataPtr->ctxtotalRead = (Tcl_Size *) ckalloc(sizeof(Tcl_Size));

    /*
     * Create the sha1 command.
     * Pass the client data pointer to the procedure, so the que data is available.
     * Also, register a delete proc to clear the sha1 queue on deletion.
     */

    cmdClientDataPtr->sha1CmdTolken = Tcl_CreateObjCommand(
	    interp, "sha1", (Tcl_ObjCmdProc *)Sha1_Cmd,
	    sha1ClientDataPtr, Sha1_CmdDeleteProc);

    /*
     * Create the buildinfo command if tcl supports it
     */

    if (Tcl_GetCommandInfo(interp, "::tcl::build-info", &info)) {
	cmdClientDataPtr->buildInfoCmdTolken = Tcl_CreateObjCommand(
		interp,
		"::sample::build-info",
		info.objProc, (void *)(
		    PACKAGE_VERSION "+" STRINGIFY(SAMPLE_VERSION_UUID)
#if defined(__clang__) && defined(__clang_major__)
			    ".clang-" STRINGIFY(__clang_major__)
#if __clang_minor__ < 10
516
517
518
519
520
521
522
523
524





525
526
527
528
529
530
531
			    ".static"
#endif
		), NULL);
    } else {
	/*
	 * No build-info command created. Save a NULL tolken.
	 */
	CmdClientDataPointer->buildInfoCmdTolken = NULL;
    }






    /* Provide the current package */

    if (Tcl_PkgProvideEx(interp, PACKAGE_NAME, PACKAGE_VERSION, NULL) != TCL_OK) {
	return TCL_ERROR;
    }








|

>
>
>
>
>







530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
			    ".static"
#endif
		), NULL);
    } else {
	/*
	 * No build-info command created. Save a NULL tolken.
	 */
	cmdClientDataPtr->buildInfoCmdTolken = NULL;
    }
    
    /*
     * FIXME: Now I have to beam cmdClientDataPtr to the unload procedure below.
     * I have no idea, how to do that. Thanks for any help.
     */

    /* Provide the current package */

    if (Tcl_PkgProvideEx(interp, PACKAGE_NAME, PACKAGE_VERSION, NULL) != TCL_OK) {
	return TCL_ERROR;
    }

558
559
560
561
562
563
564





565

566




567


568
569
570
571
572
extern "C" {
#endif  /* __cplusplus */
DLLEXPORT int
Sample_Unload(
    Tcl_Interp* interp,		/* Tcl interpreter */
    int flags)			/* interpreter or process detach */
{





    /* Remove created commands */

    Tcl_DeleteCommand(interp, "::sample::build-info");




    Tcl_DeleteCommand(interp, "sha1");


    return TCL_OK;
}
#ifdef __cplusplus
}
#endif  /* __cplusplus */







>
>
>
>
>
|
>
|
>
>
>
>
|
>
>





577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
extern "C" {
#endif  /* __cplusplus */
DLLEXPORT int
Sample_Unload(
    Tcl_Interp* interp,		/* Tcl interpreter */
    int flags)			/* interpreter or process detach */
{

    /* CLient data of the DLL */
    /* FIXME: this has to be beamed from the init procedure to this procedure */
    struct CmdClientData *cmdClientDataPtr = NULL;

    /* Remove the sha1 command */
    Tcl_DeleteCommandFromToken(interp, cmdClientDataPtr->sha1CmdTolken); 

    /* if created, also remove the build-info command */
    if (NULL != cmdClientDataPtr->buildInfoCmdTolken) {
	Tcl_DeleteCommandFromToken(interp, cmdClientDataPtr->buildInfoCmdTolken); 
    }
    
    /* free the client data */
    ckfree(cmdClientDataPtr);
    return TCL_OK;
}
#ifdef __cplusplus
}
#endif  /* __cplusplus */