2828static const unsigned char itoa64f [] =
2929 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_," ;
3030
31- static int numcontexts = 0 ;
32- static SHA1_CTX * sha1Contexts = NULL ;
33- static Tcl_Size * ctxtotalRead = NULL ;
31+ #define DIGESTSIZE 20
32+
33+ /*
34+ * The procedure needs interpreter local state. This is called
35+ * "Command client data" in TCL. Typically, a struct is allocated and
36+ * the pointer to it is made available on each operation by TCL.
37+ * Here is the struct for the sha1 procedure.
38+ */
39+
40+ struct Sha1ClientData {
41+ int numcontexts ;
42+ SHA1_CTX * sha1Contexts ;
43+ Tcl_Size * ctxtotalRead ;
44+ };
3445
3546static int Sha1_Cmd (void * clientData , Tcl_Interp * interp ,
3647 int objc , Tcl_Obj * const objv []);
37-
38- #define DIGESTSIZE 20
3948
4049/*
4150 *----------------------------------------------------------------------
@@ -55,12 +64,18 @@ static int Sha1_Cmd(void *clientData, Tcl_Interp *interp,
5564
5665static int
5766Sha1_Cmd (
58- void * dummy , /* Not used. */
67+ ClientData clientData , /* Client data with thread local state */
5968 Tcl_Interp * interp , /* Current interpreter */
6069 int objc , /* Number of arguments */
6170 Tcl_Obj * const objv [] /* Argument strings */
6271 )
6372{
73+ /*
74+ * Get my thread local memory
75+ */
76+
77+ struct Sha1ClientData * sha1ClientDataPtr = clientData ;
78+
6479 /*
6580 * The default base is hex
6681 */
@@ -72,15 +87,13 @@ Sha1_Cmd(
7287 Tcl_Channel copychan = NULL ;
7388 int mode ;
7489 int contextnum = 0 ;
75- #define sha1Context (sha1Contexts[contextnum])
7690 char * bufPtr ;
7791 Tcl_WideInt maxbytes = 0 ;
7892 int doinit = 1 ;
7993 int dofinal = 1 ;
8094 Tcl_Obj * descriptorObj = NULL ;
8195 Tcl_Size totalRead = 0 , n ;
8296 int i , j , mask , bits , offset ;
83- (void )dummy ;
8497
8598 /*
8699 * For binary representation + null char
@@ -113,24 +126,26 @@ Sha1_Cmd(
113126 }
114127 switch ((enum ShaOpts ) index ) {
115128 case SHAOPT_INIT :
116- for (contextnum = 1 ; contextnum < numcontexts ; contextnum ++ ) {
117- if (ctxtotalRead [contextnum ] == -1 ) {
129+ for (contextnum = 1 ; contextnum < sha1ClientDataPtr -> numcontexts ; contextnum ++ ) {
130+ if (sha1ClientDataPtr -> ctxtotalRead [contextnum ] == -1 ) {
118131 break ;
119132 }
120133 }
121- if (contextnum == numcontexts ) {
134+ if (contextnum == sha1ClientDataPtr -> numcontexts ) {
122135 /*
123136 * Allocate a new context.
124137 */
125138
126- numcontexts ++ ;
127- sha1Contexts = (SHA1_CTX * ) ckrealloc ((void * ) sha1Contexts ,
128- numcontexts * sizeof (SHA1_CTX ));
129- ctxtotalRead = (Tcl_Size * )ckrealloc (ctxtotalRead ,
130- numcontexts * sizeof (Tcl_Size ));
139+ sha1ClientDataPtr -> numcontexts ++ ;
140+ sha1ClientDataPtr -> sha1Contexts = (SHA1_CTX * ) ckrealloc (
141+ (void * ) sha1ClientDataPtr -> sha1Contexts ,
142+ sha1ClientDataPtr -> numcontexts * sizeof (SHA1_CTX ));
143+ sha1ClientDataPtr -> ctxtotalRead = (Tcl_Size * )ckrealloc (
144+ sha1ClientDataPtr -> ctxtotalRead ,
145+ sha1ClientDataPtr -> numcontexts * sizeof (Tcl_Size ));
131146 }
132- ctxtotalRead [contextnum ] = 0 ;
133- SHA1Init (& sha1Context );
147+ sha1ClientDataPtr -> ctxtotalRead [contextnum ] = 0 ;
148+ SHA1Init (& sha1ClientDataPtr -> sha1Contexts [ contextnum ] );
134149 snprintf (buf , sizeof (buf ), "sha1%d" , contextnum );
135150 Tcl_AppendResult (interp , buf , NULL );
136151 return TCL_OK ;
@@ -188,16 +203,16 @@ Sha1_Cmd(
188203
189204 if (descriptorObj != NULL ) {
190205 if ((sscanf (Tcl_GetString (descriptorObj ), "sha1%d" ,
191- & contextnum ) != 1 ) || (contextnum >= numcontexts ) ||
192- (ctxtotalRead [contextnum ] == -1 )) {
206+ & contextnum ) != 1 ) || (contextnum >= sha1ClientDataPtr -> numcontexts ) ||
207+ (sha1ClientDataPtr -> ctxtotalRead [contextnum ] == -1 )) {
193208 Tcl_AppendResult (interp , "invalid sha1 descriptor \"" ,
194209 Tcl_GetString (descriptorObj ), "\"" , NULL );
195210 return TCL_ERROR ;
196211 }
197212 }
198213
199214 if (doinit ) {
200- SHA1Init (& sha1Context );
215+ SHA1Init (& sha1ClientDataPtr -> sha1Contexts [ contextnum ] );
201216 }
202217
203218 if (stringObj != NULL ) {
@@ -206,10 +221,17 @@ Sha1_Cmd(
206221 goto wrongArgs ;
207222 }
208223 string = Tcl_GetStringFromObj (stringObj , & totalRead );
209- SHA1Update (& sha1Context , (unsigned char * ) string , totalRead );
224+ SHA1Update (& sha1ClientDataPtr -> sha1Contexts [contextnum ],
225+ (unsigned char * ) string , totalRead );
210226 } else if (chan != NULL ) {
211227 bufPtr = (char * )ckalloc (TCL_READ_CHUNK_SIZE );
212228 totalRead = 0 ;
229+ /*
230+ * FIXME: MS-VC 2015 gives the following warning in the next line I
231+ * was not able to fix (translated from German):
232+ * warning C4244: "Function": Conversion of "Tcl_WideInt" to "int",
233+ * possible data loss
234+ */
213235 while ((n = Tcl_Read (chan , bufPtr ,
214236 maxbytes == 0
215237 ? TCL_READ_CHUNK_SIZE
@@ -226,7 +248,8 @@ Sha1_Cmd(
226248
227249 totalRead += n ;
228250
229- SHA1Update (& sha1Context , (unsigned char * ) bufPtr , n );
251+ SHA1Update (& sha1ClientDataPtr -> sha1Contexts [contextnum ],
252+ (unsigned char * ) bufPtr , n );
230253
231254 if (copychan != NULL ) {
232255 n = Tcl_Write (copychan , bufPtr , n );
@@ -250,17 +273,17 @@ Sha1_Cmd(
250273 }
251274
252275 if (!dofinal ) {
253- ctxtotalRead [contextnum ] += totalRead ;
276+ sha1ClientDataPtr -> ctxtotalRead [contextnum ] += totalRead ;
254277 Tcl_SetObjResult (interp , Tcl_NewWideIntObj (totalRead ));
255278 return TCL_OK ;
256279 }
257280
258281 if (stringObj == NULL ) {
259- totalRead += ctxtotalRead [contextnum ];
282+ totalRead += sha1ClientDataPtr -> ctxtotalRead [contextnum ];
260283 Tcl_SetObjResult (interp , Tcl_NewWideIntObj (totalRead ));
261284 }
262285
263- SHA1Final (& sha1Context , digest );
286+ SHA1Final (& sha1ClientDataPtr -> sha1Contexts [ contextnum ] , digest );
264287
265288 /*
266289 * Take the 20 byte array and print it in the requested base
@@ -297,7 +320,7 @@ Sha1_Cmd(
297320 buf [j ++ ] = '\0' ;
298321 Tcl_AppendResult (interp , buf , NULL );
299322 if (contextnum > 0 ) {
300- ctxtotalRead [contextnum ] = -1 ;
323+ sha1ClientDataPtr -> ctxtotalRead [contextnum ] = -1 ;
301324 }
302325 return TCL_OK ;
303326
@@ -325,6 +348,42 @@ Sha1_Cmd(
325348 NULL );
326349 return TCL_ERROR ;
327350}
351+
352+
353+ /*
354+ *----------------------------------------------------------------------
355+ *
356+ * Sha1_CmdDeleteProc --
357+ *
358+ * Clear all thread data of the Sha1 command.
359+ *
360+ * Results:
361+ * No result
362+ *
363+ * Side effects:
364+ * None.
365+ *
366+ *----------------------------------------------------------------------
367+ */
368+
369+ static void
370+ Sha1_CmdDeleteProc (ClientData clientData )
371+ {
372+ struct Sha1ClientData * sha1ClientDataPtr = clientData ;
373+
374+ /*
375+ * Release the sha1 contextes
376+ */
377+
378+ ckfree (sha1ClientDataPtr -> sha1Contexts );
379+ ckfree (sha1ClientDataPtr -> ctxtotalRead );
380+
381+ /*
382+ * Release the procedure client data
383+ */
384+
385+ ckfree (sha1ClientDataPtr );
386+ }
328387
329388/*
330389 *----------------------------------------------------------------------
@@ -358,6 +417,7 @@ Sample_Init(
358417 Tcl_Interp * interp ) /* Tcl interpreter */
359418{
360419 Tcl_CmdInfo info ;
420+ struct Sha1ClientData * sha1ClientDataPtr ;
361421
362422 /*
363423 * Require compatible TCL version.
@@ -368,10 +428,35 @@ Sample_Init(
368428 * Note that Tcl_InitStubs is a macro, which is replaced by a Tcl version
369429 * check only, if TCL_STUBS is not defined (e.g. direct link, static build)
370430 */
431+
371432 if (Tcl_InitStubs (interp , "8.1-" , 0 ) == NULL ) {
372433 return TCL_ERROR ;
373434 }
374435
436+ /*
437+ * Init the sha1 context queues
438+ */
439+
440+ sha1ClientDataPtr = ckalloc (sizeof (struct Sha1ClientData ));
441+ sha1ClientDataPtr -> numcontexts = 1 ;
442+ sha1ClientDataPtr -> sha1Contexts = (SHA1_CTX * ) ckalloc (sizeof (SHA1_CTX ));
443+ sha1ClientDataPtr -> ctxtotalRead = (Tcl_Size * ) ckalloc (sizeof (Tcl_Size ));
444+
445+ /*
446+ * Create the sha1 command.
447+ * Pass the client data pointer to the procedure, so the queue data is
448+ * available.
449+ * Also, register a delete proc to clear the sha1 queue on deletion.
450+ */
451+
452+ Tcl_CreateObjCommand (
453+ interp , "sha1" , (Tcl_ObjCmdProc * )Sha1_Cmd ,
454+ sha1ClientDataPtr , Sha1_CmdDeleteProc );
455+
456+ /*
457+ * Create the buildinfo command if tcl supports it
458+ */
459+
375460 if (Tcl_GetCommandInfo (interp , "::tcl::build-info" , & info )) {
376461 Tcl_CreateObjCommand (interp , "::sample::build-info" ,
377462 info .objProc , (void * )(
@@ -434,13 +519,6 @@ Sample_Init(
434519 if (Tcl_PkgProvideEx (interp , PACKAGE_NAME , PACKAGE_VERSION , NULL ) != TCL_OK ) {
435520 return TCL_ERROR ;
436521 }
437- Tcl_CreateObjCommand (interp , "sha1" , (Tcl_ObjCmdProc * )Sha1_Cmd ,
438- NULL , NULL );
439-
440- numcontexts = 1 ;
441- sha1Contexts = (SHA1_CTX * ) ckalloc (sizeof (SHA1_CTX ));
442- ctxtotalRead = (Tcl_Size * ) ckalloc (sizeof (Tcl_Size ));
443- ctxtotalRead [0 ] = 0 ;
444522
445523 return TCL_OK ;
446524}
0 commit comments