1 /* exp_log.c - logging routines and other things common to both Expect
2 program and library. Note that this file must NOT have any
3 references to Tcl except for including tclInt.h
4 */
5
6 #include "expect_cf.h"
7 #include <stdio.h>
8 /*#include <varargs.h> tclInt.h drags in varargs.h. Since Pyramid */
9 /* objects to including varargs.h twice, just */
10 /* omit this one. */
11 #include "tclInt.h"
12 #ifdef NO_STDLIB_H
13 #include "../compat/stdlib.h"
14 #else
15 #include <stdlib.h> /* for malloc */
16 #endif
17 #include <ctype.h>
18
19 #include "expect_comm.h"
20 #include "exp_int.h"
21 #include "exp_rename.h"
22 #include "exp_command.h"
23 #include "exp_log.h"
24
25 typedef struct ThreadSpecificData {
26 Tcl_Channel diagChannel;
27 Tcl_DString diagFilename;
28 int diagToStderr;
29
30 Tcl_Channel logChannel;
31 Tcl_DString logFilename; /* if no name, then it came from -open or -leaveopen */
32 int logAppend;
33 int logLeaveOpen;
34 int logAll; /* if TRUE, write log of all interactions
35 * despite value of logUser - i.e., even if
36 * user is not seeing it (via stdout)
37 */
38 int logUser; /* TRUE if user sees interactions on stdout */
39 } ThreadSpecificData;
40
41 static Tcl_ThreadDataKey dataKey;
42
43 /*
44 * create a reasonably large buffer for the bulk of the output routines
45 * that are not too large
46 */
47 static char bigbuf[2000];
48
49 static void expDiagWriteCharsUni _ANSI_ARGS_((Tcl_UniChar *str,int len));
50
51 /*
52 * Following this are several functions that log the conversation. Some
53 * general notes on all of them:
54 */
55
56 /*
57 * ignore sprintf return value ("character count") because it's not
58 * defined in terms of UTF so it would be misinterpreted if we passed
59 * it on.
60 */
61
62 /*
63 * if necessary, they could be made more efficient by skipping vsprintf based
64 * on booleans
65 */
66
67 /* Most of them have multiple calls to printf-style functions. */
68 /* At first glance, it seems stupid to reformat the same arguments again */
69 /* but we have no way of telling how long the formatted output will be */
70 /* and hence cannot allocate a buffer to do so. */
71 /* Fortunately, in production code, most of the duplicate reformatting */
72 /* will be skipped, since it is due to handling errors and debugging. */
73
74 /*
75 * Name: expWriteBytesAndLogIfTtyU
76 *
77 * Output to channel (and log if channel is stdout or devtty)
78 *
79 * Returns: TCL_OK or TCL_ERROR;
80 */
81
82 int
expWriteBytesAndLogIfTtyU(esPtr,buf,lenChars)83 expWriteBytesAndLogIfTtyU(esPtr,buf,lenChars)
84 ExpState *esPtr;
85 Tcl_UniChar *buf;
86 int lenChars;
87 {
88 int wc;
89 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
90
91 if (esPtr->valid)
92 wc = expWriteCharsUni(esPtr,buf,lenChars);
93
94 if (tsdPtr->logChannel && ((esPtr->fdout == 1) || expDevttyIs(esPtr))) {
95 Tcl_DString ds;
96 Tcl_DStringInit (&ds);
97 Tcl_UniCharToUtfDString (buf,lenChars,&ds);
98 Tcl_WriteChars(tsdPtr->logChannel,Tcl_DStringValue (&ds), Tcl_DStringLength (&ds));
99 Tcl_DStringFree (&ds);
100 }
101 return wc;
102 }
103
104 /*
105 * Name: expLogDiagU
106 *
107 * Send to the Log (and Diag if open). This is for writing to the log.
108 * (In contrast, expDiagLog... is for writing diagnostics.)
109 */
110
111 void
expLogDiagU(buf)112 expLogDiagU(buf)
113 char *buf;
114 {
115 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
116
117 expDiagWriteChars(buf,-1);
118 if (tsdPtr->logChannel) {
119 Tcl_WriteChars(tsdPtr->logChannel, buf, -1);
120 }
121 }
122
123 /*
124 * Name: expLogInteractionU
125 *
126 * Show chars to user if they've requested it, UNLESS they're seeing it
127 * already because they're typing it and tty driver is echoing it.
128 * Also send to Diag and Log if appropriate.
129 */
130 void
expLogInteractionU(esPtr,buf,buflen)131 expLogInteractionU(esPtr,buf,buflen)
132 ExpState *esPtr;
133 Tcl_UniChar *buf;
134 int buflen;
135 {
136 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
137
138 if (tsdPtr->logAll || (tsdPtr->logUser && tsdPtr->logChannel)) {
139 Tcl_DString ds;
140 Tcl_DStringInit (&ds);
141 Tcl_UniCharToUtfDString (buf,buflen,&ds);
142 Tcl_WriteChars(tsdPtr->logChannel,Tcl_DStringValue (&ds), Tcl_DStringLength (&ds));
143 Tcl_DStringFree (&ds);
144 }
145
146 /* hmm.... if stdout is closed such as by disconnect, loguser
147 should be forced FALSE */
148
149 /* don't write to user if they're seeing it already, i.e., typing it! */
150 if (tsdPtr->logUser && (!expStdinoutIs(esPtr)) && (!expDevttyIs(esPtr))) {
151 ExpState *stdinout = expStdinoutGet();
152 if (stdinout->valid) {
153 (void) expWriteCharsUni(stdinout,buf,buflen);
154 }
155 }
156 expDiagWriteCharsUni(buf,buflen);
157 }
158
159 /* send to log if open */
160 /* send to stderr if debugging enabled */
161 /* use this for logging everything but the parent/child conversation */
162 /* (this turns out to be almost nothing) */
163 /* uppercase L differentiates if from math function of same name */
164 #define LOGUSER (tsdPtr->logUser || force_stdout)
165 /*VARARGS*/
166 void
TCL_VARARGS_DEF(int,arg1)167 expStdoutLog TCL_VARARGS_DEF(int,arg1)
168 {
169 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
170 int force_stdout;
171 char *fmt;
172 va_list args;
173
174 force_stdout = TCL_VARARGS_START(int,arg1,args);
175 fmt = va_arg(args,char *);
176
177 if ((!tsdPtr->logUser) && (!force_stdout) && (!tsdPtr->logAll)) return;
178
179 (void) vsprintf(bigbuf,fmt,args);
180 expDiagWriteBytes(bigbuf,-1);
181 if (tsdPtr->logAll || (LOGUSER && tsdPtr->logChannel)) Tcl_WriteChars(tsdPtr->logChannel,bigbuf,-1);
182 if (LOGUSER) fprintf(stdout,"%s",bigbuf);
183 va_end(args);
184 }
185
186 /* just like log but does no formatting */
187 /* send to log if open */
188 /* use this function for logging the parent/child conversation */
189 void
expStdoutLogU(buf,force_stdout)190 expStdoutLogU(buf,force_stdout)
191 char *buf;
192 int force_stdout; /* override value of logUser */
193 {
194 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
195 int length;
196
197 if ((!tsdPtr->logUser) && (!force_stdout) && (!tsdPtr->logAll)) return;
198
199 length = strlen(buf);
200 expDiagWriteBytes(buf,length);
201 if (tsdPtr->logAll || (LOGUSER && tsdPtr->logChannel)) Tcl_WriteChars(tsdPtr->logChannel,buf,-1);
202 if (LOGUSER) {
203 #if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1))
204 Tcl_WriteChars (Tcl_GetStdChannel (TCL_STDOUT), buf, length);
205 Tcl_Flush (Tcl_GetStdChannel (TCL_STDOUT));
206 #else
207 fwrite(buf,1,length,stdout);
208 #endif
209 }
210 }
211
212 /* send to log if open */
213 /* send to stderr */
214 /* use this function for error conditions */
215 /*VARARGS*/
216 void
TCL_VARARGS_DEF(char *,arg1)217 expErrorLog TCL_VARARGS_DEF(char *,arg1)
218 {
219 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
220
221 char *fmt;
222 va_list args;
223
224 fmt = TCL_VARARGS_START(char *,arg1,args);
225 (void) vsprintf(bigbuf,fmt,args);
226
227 expDiagWriteChars(bigbuf,-1);
228 fprintf(stderr,"%s",bigbuf);
229 if (tsdPtr->logChannel) Tcl_WriteChars(tsdPtr->logChannel,bigbuf,-1);
230
231 va_end(args);
232 }
233
234 /* just like errorlog but does no formatting */
235 /* send to log if open */
236 /* use this function for logging the parent/child conversation */
237 /*ARGSUSED*/
238 void
expErrorLogU(buf)239 expErrorLogU(buf)
240 char *buf;
241 {
242 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
243
244 int length = strlen(buf);
245 fwrite(buf,1,length,stderr);
246 expDiagWriteChars(buf,-1);
247 if (tsdPtr->logChannel) Tcl_WriteChars(tsdPtr->logChannel,buf,-1);
248 }
249
250
251
252 /* send diagnostics to Diag, Log, and stderr */
253 /* use this function for recording unusual things in the log */
254 /*VARARGS*/
255 void
TCL_VARARGS_DEF(char *,arg1)256 expDiagLog TCL_VARARGS_DEF(char *,arg1)
257 {
258 char *fmt;
259 va_list args;
260
261 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
262
263 if ((tsdPtr->diagToStderr == 0) && (tsdPtr->diagChannel == 0)) return;
264
265 fmt = TCL_VARARGS_START(char *,arg1,args);
266
267 (void) vsprintf(bigbuf,fmt,args);
268
269 expDiagWriteBytes(bigbuf,-1);
270 if (tsdPtr->diagToStderr) {
271 fprintf(stderr,"%s",bigbuf);
272 if (tsdPtr->logChannel) Tcl_WriteChars(tsdPtr->logChannel,bigbuf,-1);
273 }
274
275 va_end(args);
276 }
277
278
279 /* expDiagLog for unformatted strings
280 this also takes care of arbitrary large strings */
281 void
expDiagLogU(str)282 expDiagLogU(str)
283 char *str;
284 {
285 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
286
287 if ((tsdPtr->diagToStderr == 0) && (tsdPtr->diagChannel == 0)) return;
288
289 expDiagWriteBytes(str,-1);
290
291 if (tsdPtr->diagToStderr) {
292 fprintf(stderr,"%s",str);
293 if (tsdPtr->logChannel) Tcl_WriteChars(tsdPtr->logChannel,str,-1);
294 }
295 }
296
297 /* expPrintf prints to stderr. It's just a utility for making
298 debugging easier. */
299
300 /*VARARGS*/
301 void
TCL_VARARGS_DEF(char *,arg1)302 expPrintf TCL_VARARGS_DEF(char *,arg1)
303 {
304 char *fmt;
305 va_list args;
306 char bigbuf[2000];
307 int len, rc;
308
309 fmt = TCL_VARARGS_START(char *,arg1,args);
310 len = vsprintf(bigbuf,arg1,args);
311 retry:
312 rc = write(2,bigbuf,len);
313 if ((rc == -1) && (errno == EAGAIN)) goto retry;
314
315 va_end(args);
316 }
317
318
319 void
expDiagToStderrSet(val)320 expDiagToStderrSet(val)
321 int val;
322 {
323 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
324
325 tsdPtr->diagToStderr = val;
326 }
327
328
329 int
expDiagToStderrGet()330 expDiagToStderrGet() {
331 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
332 return tsdPtr->diagToStderr;
333 }
334
335 Tcl_Channel
expDiagChannelGet()336 expDiagChannelGet()
337 {
338 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
339 return tsdPtr->diagChannel;
340 }
341
342 void
expDiagChannelClose(interp)343 expDiagChannelClose(interp)
344 Tcl_Interp *interp;
345 {
346 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
347
348 if (!tsdPtr->diagChannel) return;
349 Tcl_UnregisterChannel(interp,tsdPtr->diagChannel);
350 Tcl_DStringFree(&tsdPtr->diagFilename);
351 tsdPtr->diagChannel = 0;
352 }
353
354 /* currently this registers the channel, however the exp_internal
355 command doesn't currently give the channel name to the user so
356 this is kind of useless - but we might change this someday */
357 int
expDiagChannelOpen(interp,filename)358 expDiagChannelOpen(interp,filename)
359 Tcl_Interp *interp;
360 char *filename;
361 {
362 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
363 char *newfilename;
364
365 Tcl_ResetResult(interp);
366 newfilename = Tcl_TranslateFileName(interp,filename,&tsdPtr->diagFilename);
367 if (!newfilename) return TCL_ERROR;
368
369 /* Tcl_TildeSubst doesn't store into dstring */
370 /* if no ~, so force string into dstring */
371 /* this is only needed so that next time around */
372 /* we can get dstring for -info if necessary */
373 if (Tcl_DStringValue(&tsdPtr->diagFilename)[0] == '\0') {
374 Tcl_DStringAppend(&tsdPtr->diagFilename,filename,-1);
375 }
376
377 tsdPtr->diagChannel = Tcl_OpenFileChannel(interp,newfilename,"a",0777);
378 if (!tsdPtr->diagChannel) {
379 Tcl_DStringFree(&tsdPtr->diagFilename);
380 return TCL_ERROR;
381 }
382 Tcl_RegisterChannel(interp,tsdPtr->diagChannel);
383 Tcl_SetChannelOption(interp,tsdPtr->diagChannel,"-buffering","none");
384 return TCL_OK;
385 }
386
387 void
expDiagWriteObj(obj)388 expDiagWriteObj(obj)
389 Tcl_Obj *obj;
390 {
391 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
392
393 if (!tsdPtr->diagChannel) return;
394
395 Tcl_WriteObj(tsdPtr->diagChannel,obj);
396 }
397
398 /* write 8-bit bytes */
399 void
expDiagWriteBytes(str,len)400 expDiagWriteBytes(str,len)
401 char *str;
402 int len;
403 {
404 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
405
406 if (!tsdPtr->diagChannel) return;
407
408 Tcl_Write(tsdPtr->diagChannel,str,len);
409 }
410
411 /* write UTF chars */
412 void
expDiagWriteChars(str,len)413 expDiagWriteChars(str,len)
414 char *str;
415 int len;
416 {
417 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
418
419 if (!tsdPtr->diagChannel) return;
420
421 Tcl_WriteChars(tsdPtr->diagChannel,str,len);
422 }
423
424 /* write Unicode chars */
425 static void
expDiagWriteCharsUni(str,len)426 expDiagWriteCharsUni(str,len)
427 Tcl_UniChar *str;
428 int len;
429 {
430 Tcl_DString ds;
431 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
432
433 if (!tsdPtr->diagChannel) return;
434
435 Tcl_DStringInit (&ds);
436 Tcl_UniCharToUtfDString (str,len,&ds);
437 Tcl_WriteChars(tsdPtr->diagChannel,Tcl_DStringValue (&ds), Tcl_DStringLength (&ds));
438 Tcl_DStringFree (&ds);
439 }
440
441 char *
expDiagFilename()442 expDiagFilename()
443 {
444 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
445
446 return Tcl_DStringValue(&tsdPtr->diagFilename);
447 }
448
449 void
expLogChannelClose(interp)450 expLogChannelClose(interp)
451 Tcl_Interp *interp;
452 {
453 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
454
455 if (!tsdPtr->logChannel) return;
456
457 if (Tcl_DStringLength(&tsdPtr->logFilename)) {
458 /* it's a channel that we created */
459 Tcl_UnregisterChannel(interp,tsdPtr->logChannel);
460 Tcl_DStringFree(&tsdPtr->logFilename);
461 } else {
462 /* it's a channel that tcl::open created */
463 if (!tsdPtr->logLeaveOpen) {
464 Tcl_UnregisterChannel(interp,tsdPtr->logChannel);
465 }
466 }
467 tsdPtr->logChannel = 0;
468 tsdPtr->logAll = 0; /* can't write to log if none open! */
469 }
470
471 /* currently this registers the channel, however the exp_log_file
472 command doesn't currently give the channel name to the user so
473 this is kind of useless - but we might change this someday */
474 int
expLogChannelOpen(interp,filename,append)475 expLogChannelOpen(interp,filename,append)
476 Tcl_Interp *interp;
477 char *filename;
478 int append;
479 {
480 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
481 char *newfilename;
482 char mode[2];
483
484 if (append) {
485 strcpy(mode,"a");
486 } else {
487 strcpy(mode,"w");
488 }
489
490 Tcl_ResetResult(interp);
491 newfilename = Tcl_TranslateFileName(interp,filename,&tsdPtr->logFilename);
492 if (!newfilename) return TCL_ERROR;
493
494 /* Tcl_TildeSubst doesn't store into dstring */
495 /* if no ~, so force string into dstring */
496 /* this is only needed so that next time around */
497 /* we can get dstring for -info if necessary */
498 if (Tcl_DStringValue(&tsdPtr->logFilename)[0] == '\0') {
499 Tcl_DStringAppend(&tsdPtr->logFilename,filename,-1);
500 }
501
502 tsdPtr->logChannel = Tcl_OpenFileChannel(interp,newfilename,mode,0777);
503 if (!tsdPtr->logChannel) {
504 Tcl_DStringFree(&tsdPtr->logFilename);
505 return TCL_ERROR;
506 }
507 Tcl_RegisterChannel(interp,tsdPtr->logChannel);
508 Tcl_SetChannelOption(interp,tsdPtr->logChannel,"-buffering","none");
509 expLogAppendSet(append);
510 return TCL_OK;
511 }
512
513 int
expLogAppendGet()514 expLogAppendGet()
515 {
516 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
517 return tsdPtr->logAppend;
518 }
519
520 void
expLogAppendSet(app)521 expLogAppendSet(app)
522 int app;
523 {
524 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
525 tsdPtr->logAppend = app;
526 }
527
528 int
expLogAllGet()529 expLogAllGet()
530 {
531 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
532 return tsdPtr->logAll;
533 }
534
535 void
expLogAllSet(app)536 expLogAllSet(app)
537 int app;
538 {
539 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
540 tsdPtr->logAll = app;
541 /* should probably confirm logChannel != 0 */
542 }
543
544 int
expLogToStdoutGet()545 expLogToStdoutGet()
546 {
547 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
548 return tsdPtr->logUser;
549 }
550
551 void
expLogToStdoutSet(app)552 expLogToStdoutSet(app)
553 int app;
554 {
555 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
556 tsdPtr->logUser = app;
557 }
558
559 int
expLogLeaveOpenGet()560 expLogLeaveOpenGet()
561 {
562 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
563 return tsdPtr->logLeaveOpen;
564 }
565
566 void
expLogLeaveOpenSet(app)567 expLogLeaveOpenSet(app)
568 int app;
569 {
570 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
571 tsdPtr->logLeaveOpen = app;
572 }
573
574 Tcl_Channel
expLogChannelGet()575 expLogChannelGet()
576 {
577 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
578 return tsdPtr->logChannel;
579 }
580
581 /* to set to a pre-opened channel (presumably by tcl::open) */
582 int
expLogChannelSet(interp,name)583 expLogChannelSet(interp,name)
584 Tcl_Interp *interp;
585 char *name;
586 {
587 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
588
589 int mode;
590
591 if (0 == (tsdPtr->logChannel = Tcl_GetChannel(interp,name,&mode))) {
592 return TCL_ERROR;
593 }
594 if (!(mode & TCL_WRITABLE)) {
595 tsdPtr->logChannel = 0;
596 Tcl_SetResult(interp,"channel is not writable",TCL_VOLATILE);
597 return TCL_ERROR;
598 }
599 return TCL_OK;
600 }
601
602 char *
expLogFilenameGet()603 expLogFilenameGet()
604 {
605 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
606
607 return Tcl_DStringValue(&tsdPtr->logFilename);
608 }
609
610 int
expLogUserGet()611 expLogUserGet()
612 {
613 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
614
615 return tsdPtr->logUser;
616 }
617
618 void
expLogUserSet(logUser)619 expLogUserSet(logUser)
620 int logUser;
621 {
622 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
623
624 tsdPtr->logUser = logUser;
625 }
626
627
628
629 /* generate printable versions of random ASCII strings. Primarily used */
630 /* in diagnostic mode, "expect -d" */
631 static char *
expPrintifyReal(s)632 expPrintifyReal(s)
633 char *s;
634 {
635 static int destlen = 0;
636 static char *dest = 0;
637 char *d; /* ptr into dest */
638 unsigned int need;
639 Tcl_UniChar ch;
640
641 if (s == 0) return("<null>");
642
643 /* worst case is every character takes 4 to printify */
644 need = strlen(s)*6 + 1;
645 if (need > destlen) {
646 if (dest) ckfree(dest);
647 dest = ckalloc(need);
648 destlen = need;
649 }
650
651 for (d = dest;*s;) {
652 s += Tcl_UtfToUniChar(s, &ch);
653 if (ch == '\r') {
654 strcpy(d,"\\r"); d += 2;
655 } else if (ch == '\n') {
656 strcpy(d,"\\n"); d += 2;
657 } else if (ch == '\t') {
658 strcpy(d,"\\t"); d += 2;
659 } else if ((ch < 0x80) && isprint(UCHAR(ch))) {
660 *d = (char)ch; d += 1;
661 } else {
662 sprintf(d,"\\u%04x",ch); d += 6;
663 }
664 }
665 *d = '\0';
666 return(dest);
667 }
668
669 /* generate printable versions of random ASCII strings. Primarily used */
670 /* in diagnostic mode, "expect -d" */
671 static char *
expPrintifyRealUni(s,numchars)672 expPrintifyRealUni(s,numchars)
673 Tcl_UniChar *s;
674 int numchars;
675 {
676 static int destlen = 0;
677 static char *dest = 0;
678 char *d; /* ptr into dest */
679 unsigned int need;
680 Tcl_UniChar ch;
681
682 if (s == 0) return("<null>");
683 if (numchars == 0) return("");
684
685 /* worst case is every character takes 6 to printify */
686 need = numchars*6 + 1;
687 if (need > destlen) {
688 if (dest) ckfree(dest);
689 dest = ckalloc(need);
690 destlen = need;
691 }
692
693 for (d = dest;numchars > 0;numchars--) {
694 ch = *s; s++;
695
696 if (ch == '\r') {
697 strcpy(d,"\\r"); d += 2;
698 } else if (ch == '\n') {
699 strcpy(d,"\\n"); d += 2;
700 } else if (ch == '\t') {
701 strcpy(d,"\\t"); d += 2;
702 } else if ((ch < 0x80) && isprint(UCHAR(ch))) {
703 *d = (char)ch; d += 1;
704 } else {
705 sprintf(d,"\\u%04x",ch); d += 6;
706 }
707 }
708 *d = '\0';
709 return(dest);
710 }
711
712 char *
expPrintifyObj(obj)713 expPrintifyObj(obj)
714 Tcl_Obj *obj;
715 {
716 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
717
718 /* don't bother writing into bigbuf if we're not going to ever use it */
719 if ((!tsdPtr->diagToStderr) && (!tsdPtr->diagChannel)) return((char *)0);
720
721 return expPrintifyReal(Tcl_GetString(obj));
722 }
723
724 char *
expPrintify(s)725 expPrintify(s) /* INTL */
726 char *s;
727 {
728 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
729
730 /* don't bother writing into bigbuf if we're not going to ever use it */
731 if ((!tsdPtr->diagToStderr) && (!tsdPtr->diagChannel)) return((char *)0);
732
733 return expPrintifyReal(s);
734 }
735
736 char *
expPrintifyUni(s,numchars)737 expPrintifyUni(s,numchars) /* INTL */
738 Tcl_UniChar *s;
739 int numchars;
740 {
741 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
742
743 /* don't bother writing into bigbuf if we're not going to ever use it */
744 if ((!tsdPtr->diagToStderr) && (!tsdPtr->diagChannel)) return((char *)0);
745
746 return expPrintifyRealUni(s,numchars);
747 }
748
749 void
expDiagInit()750 expDiagInit()
751 {
752 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
753
754 Tcl_DStringInit(&tsdPtr->diagFilename);
755 tsdPtr->diagChannel = 0;
756 tsdPtr->diagToStderr = 0;
757 }
758
759 void
expLogInit()760 expLogInit()
761 {
762 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
763
764 Tcl_DStringInit(&tsdPtr->logFilename);
765 tsdPtr->logChannel = 0;
766 tsdPtr->logAll = FALSE;
767 tsdPtr->logUser = TRUE;
768 }
769