1/* $XTermId: trace.c,v 1.244 2024/12/01 20:23:01 tom Exp $ */
2
3/*
4 * Copyright 1997-2023,2024 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 */
32
33/*
34 * debugging support via TRACE macro.
35 */
36
37#include <xterm.h>		/* for definition of GCC_UNUSED */
38#include <xstrings.h>
39#include <wcwidth.h>
40#include <version.h>
41
42#if OPT_TRACE
43
44#include <data.h>
45#include <trace.h>
46
47#include <time.h>
48#include <stdlib.h>
49#include <unistd.h>
50#include <sys/types.h>
51#include <sys/stat.h>
52#include <stdio.h>
53#include <assert.h>
54
55#include <X11/Xatom.h>
56#include <X11/Xmu/Atoms.h>
57#include <X11/Xmu/Error.h>
58
59#ifdef HAVE_X11_TRANSLATEI_H
60#include <X11/ConvertI.h>
61#include <X11/TranslateI.h>
62#else
63#ifdef __cplusplus
64extern "C" {
65#endif
66
67    extern String _XtPrintXlations(Widget w,
68				   XtTranslations xlations,
69				   Widget accelWidget,
70				   _XtBoolean includeRHS);
71#ifdef __cplusplus
72}
73#endif
74#endif
75const char *trace_who = "parent";
76
77static FILE *trace_fp;
78
79static FILE *
80TraceOpen(void)
81{
82    static const char *trace_out;
83
84    if (trace_fp != NULL
85	&& trace_who != trace_out) {
86	fclose(trace_fp);
87	trace_fp = NULL;
88    }
89    trace_out = trace_who;
90
91    if (!trace_fp) {
92	static char dot[] = ".";
93	mode_t oldmask = umask(077);
94	char *home;
95	char *name;
96	/*
97	 * Put the trace-file in user's home-directory if the current
98	 * directory is not writable.
99	 */
100	home = ((access(dot, R_OK | W_OK) == 0)
101		? dot
102		: getenv("HOME"));
103	if (home != NULL &&
104	    (name = malloc(strlen(home) + strlen(trace_who) + 50)) != NULL) {
105#if OPT_TRACE_UNIQUE		/* usually I do not want unique names */
106	    int unique;
107	    for (unique = 0;; ++unique) {
108		if (unique)
109		    sprintf(name, "%s/Trace-%s.out-%d", home, trace_who, unique);
110		else
111		    sprintf(name, "%s/Trace-%s.out", home, trace_who);
112		if ((trace_fp = fopen(name, "r")) == 0) {
113		    break;
114		}
115		fclose(trace_fp);
116	    }
117#else
118	    sprintf(name, "%s/Trace-%s.out", home, trace_who);
119#endif
120	    trace_fp = fopen(name, "w");
121	    if (trace_fp != NULL) {
122		fprintf(trace_fp, "%s\n", xtermVersion());
123		TraceIds(NULL, 0);
124	    }
125	    if (!trace_fp) {
126		xtermWarning("Cannot open \"%s\"\n", name);
127		exit(EXIT_FAILURE);
128	    }
129	    (void) umask(oldmask);
130	    free(name);
131	}
132    }
133    return trace_fp;
134}
135
136void
137Trace(const char *fmt, ...)
138{
139    va_list ap;
140    FILE *fp = TraceOpen();
141
142    va_start(ap, fmt);
143    vfprintf(fp, fmt, ap);
144    (void) fflush(fp);
145    va_end(ap);
146}
147
148void
149TraceVA(const char *fmt, va_list ap)
150{
151    FILE *fp = TraceOpen();
152
153    vfprintf(fp, fmt, ap);
154    (void) fflush(fp);
155}
156
157void
158TraceClose(void)
159{
160    if (trace_fp != NULL) {
161	(void) fclose(trace_fp);
162	(void) fflush(stdout);
163	(void) fflush(stderr);
164	(void) visibleChars(NULL, 0);
165	(void) visibleIChars(NULL, 0);
166	trace_fp = NULL;
167    }
168}
169
170void
171TraceXError(Display *d, XErrorEvent *ev)
172{
173    FILE *fp = TraceOpen();
174    (void) XmuPrintDefaultErrorMessage(d, ev, fp);
175    (void) fflush(fp);
176}
177
178void
179TraceIds(const char *fname, int lnum)
180{
181    Trace("process %d ", (int) getpid());
182#ifdef HAVE_UNISTD_H
183    Trace("real (%u/%u) effective (%u/%u)",
184	  (unsigned) getuid(), (unsigned) getgid(),
185	  (unsigned) geteuid(), (unsigned) getegid());
186#endif
187    if (fname != NULL) {
188	Trace(" (%s@%d)\n", fname, lnum);
189    } else {
190	time_t now = time((time_t *) 0);
191	Trace("-- %s", ctime(&now));
192    }
193}
194
195void
196TraceTime(const char *fname, int lnum)
197{
198    time_t now;
199    if (fname != NULL) {
200	Trace("datetime (%s@%d) ", fname, lnum);
201    }
202    now = time((time_t *) 0);
203    Trace("-- %s", ctime(&now));
204}
205
206static void
207formatAscii(char *dst, unsigned value)
208{
209    switch (value) {
210    case '\\':
211	sprintf(dst, "\\\\");
212	break;
213    case '\b':
214	sprintf(dst, "\\b");
215	break;
216    case '\n':
217	sprintf(dst, "\\n");
218	break;
219    case '\r':
220	sprintf(dst, "\\r");
221	break;
222    case '\t':
223	sprintf(dst, "\\t");
224	break;
225    default:
226	if (value < 32 || (value >= 127 && value < 160))
227	    sprintf(dst, "\\%03o", value & 0xff);
228	else
229	    sprintf(dst, "%c", CharOf(value));
230	break;
231    }
232}
233
234#if OPT_DEC_CHRSET
235
236const char *
237visibleDblChrset(unsigned chrset)
238{
239    const char *result = "?";
240    switch (chrset) {
241    case CSET_SWL:
242	result = "CSET_SWL";
243	break;
244    case CSET_DHL_TOP:
245	result = "CSET_DHL_TOP";
246	break;
247    case CSET_DHL_BOT:
248	result = "CSET_DHL_BOT";
249	break;
250    case CSET_DWL:
251	result = "CSET_DWL";
252	break;
253    }
254    return result;
255}
256#endif
257
258const char *
259visibleScsCode(DECNRCM_codes chrset)
260{
261#define MAP(to,from) case from: result = to ":" #from; break
262    const char *result = "<ERR>";
263    switch ((DECNRCM_codes) chrset) {
264	MAP("B", nrc_ASCII);
265	MAP("A", nrc_British);
266	MAP("A", nrc_British_Latin_1);
267	MAP("&4", nrc_DEC_Cyrillic);
268	MAP("0", nrc_DEC_Spec_Graphic);
269	MAP("1", nrc_DEC_Alt_Chars);
270	MAP("2", nrc_DEC_Alt_Graphics);
271	MAP("<", nrc_DEC_Supp);
272	MAP("<", nrc_DEC_UPSS);
273	MAP("%5", nrc_DEC_Supp_Graphic);
274	MAP(">", nrc_DEC_Technical);
275	MAP("4", nrc_Dutch);
276	MAP("5", nrc_Finnish);
277	MAP("C", nrc_Finnish2);
278	MAP("R", nrc_French);
279	MAP("f", nrc_French2);
280	MAP("Q", nrc_French_Canadian);
281	MAP("9", nrc_French_Canadian2);
282	MAP("K", nrc_German);
283	MAP("\"?", nrc_DEC_Greek_Supp);
284	MAP("\">", nrc_Greek);
285	MAP("F", nrc_ISO_Greek_Supp);
286	MAP("\"4", nrc_DEC_Hebrew_Supp);
287	MAP("%=", nrc_Hebrew);
288	MAP("H", nrc_ISO_Hebrew_Supp);
289	MAP("Y", nrc_Italian);
290	MAP("A", nrc_ISO_Latin_1_Supp);
291	MAP("B", nrc_ISO_Latin_2_Supp);
292	MAP("I", nrc_JIS_Katakana);
293	MAP("J", nrc_JIS_Roman);
294	MAP("M", nrc_ISO_Latin_5_Supp);
295	MAP("L", nrc_ISO_Latin_Cyrillic);
296	MAP("`", nrc_Norwegian_Danish);
297	MAP("E", nrc_Norwegian_Danish2);
298	MAP("6", nrc_Norwegian_Danish3);
299	MAP("%6", nrc_Portugese);
300	MAP("&5", nrc_Russian);
301	MAP("%3", nrc_SCS_NRCS);
302	MAP("Z", nrc_Spanish);
303	MAP("7", nrc_Swedish);
304	MAP("H", nrc_Swedish2);
305	MAP("=", nrc_Swiss);
306	MAP("%2", nrc_Turkish);
307	MAP("%0", nrc_DEC_Turkish_Supp);
308	MAP("<UNK>", nrc_Unknown);
309    }
310#undef MAP
311    return result;
312}
313
314const char *
315visibleChars(const Char *buf, size_t len)
316{
317    static char *result;
318    static size_t used;
319
320    if (buf != NULL) {
321	size_t limit = ((len + 1) * 8) + 1;
322
323	if (limit > used) {
324	    used = limit;
325	    result = realloc(result, used);
326	}
327	if (result != NULL) {
328	    char *dst = result;
329	    *dst = '\0';
330	    while (len--) {
331		unsigned value = *buf++;
332		formatAscii(dst, value);
333		dst += strlen(dst);
334	    }
335	}
336    } else {
337	FreeAndNull(result);
338	used = 0;
339    }
340    return NonNull(result);
341}
342
343const char *
344visibleEventMode(EventMode value)
345{
346    const char *result;
347    switch (value) {
348    case NORMAL:
349	result = "NORMAL";
350	break;
351    case LEFTEXTENSION:
352	result = "LEFTEXTENSION";
353	break;
354    case RIGHTEXTENSION:
355	result = "RIGHTEXTENSION";
356	break;
357    default:
358	result = "?";
359	break;
360    }
361    return result;
362}
363
364const char *
365visibleIChars(const IChar *buf, size_t len)
366{
367    static char *result;
368    static size_t used;
369
370    if (buf != NULL) {
371	size_t limit = ((len + 1) * 12) + 1;
372
373	if (limit > used) {
374	    used = limit;
375	    result = realloc(result, used);
376	}
377	if (result != NULL) {
378	    char *dst = result;
379	    *dst = '\0';
380	    while (len--) {
381		unsigned value = *buf++;
382#if OPT_WIDE_CHARS
383		if (value > 255)
384		    sprintf(dst, "\\U+%04X", value);
385		else
386#endif
387		    formatAscii(dst, value);
388		dst += strlen(dst);
389	    }
390	}
391    } else {
392	FreeAndNull(result);
393	used = 0;
394    }
395    return NonNull(result);
396}
397
398const char *
399visibleUChar(unsigned chr)
400{
401    IChar buf[1];
402    buf[0] = (IChar) chr;
403    return visibleIChars(buf, 1);
404}
405
406const char *
407visibleEventType(int type)
408{
409    const char *result = "?";
410    switch (type) {
411	CASETYPE(KeyPress);
412	CASETYPE(KeyRelease);
413	CASETYPE(ButtonPress);
414	CASETYPE(ButtonRelease);
415	CASETYPE(MotionNotify);
416	CASETYPE(EnterNotify);
417	CASETYPE(LeaveNotify);
418	CASETYPE(FocusIn);
419	CASETYPE(FocusOut);
420	CASETYPE(KeymapNotify);
421	CASETYPE(Expose);
422	CASETYPE(GraphicsExpose);
423	CASETYPE(NoExpose);
424	CASETYPE(VisibilityNotify);
425	CASETYPE(CreateNotify);
426	CASETYPE(DestroyNotify);
427	CASETYPE(UnmapNotify);
428	CASETYPE(MapNotify);
429	CASETYPE(MapRequest);
430	CASETYPE(ReparentNotify);
431	CASETYPE(ConfigureNotify);
432	CASETYPE(ConfigureRequest);
433	CASETYPE(GravityNotify);
434	CASETYPE(ResizeRequest);
435	CASETYPE(CirculateNotify);
436	CASETYPE(CirculateRequest);
437	CASETYPE(PropertyNotify);
438	CASETYPE(SelectionClear);
439	CASETYPE(SelectionRequest);
440	CASETYPE(SelectionNotify);
441	CASETYPE(ColormapNotify);
442	CASETYPE(ClientMessage);
443	CASETYPE(MappingNotify);
444    }
445    return result;
446}
447
448const char *
449visibleMappingMode(int code)
450{
451    const char *result = "?";
452    switch (code) {
453	CASETYPE(MappingModifier);
454	CASETYPE(MappingKeyboard);
455	CASETYPE(MappingPointer);
456    }
457    return result;
458}
459
460const char *
461visibleNotifyMode(int code)
462{
463    const char *result = "?";
464    switch (code) {
465	CASETYPE(NotifyNormal);
466	CASETYPE(NotifyGrab);
467	CASETYPE(NotifyUngrab);
468	CASETYPE(NotifyWhileGrabbed);
469    }
470    return result;
471}
472
473const char *
474visibleNotifyDetail(int code)
475{
476    const char *result = "?";
477    switch (code) {
478	CASETYPE(NotifyAncestor);
479	CASETYPE(NotifyVirtual);
480	CASETYPE(NotifyInferior);
481	CASETYPE(NotifyNonlinear);
482	CASETYPE(NotifyNonlinearVirtual);
483	CASETYPE(NotifyPointer);
484	CASETYPE(NotifyPointerRoot);
485	CASETYPE(NotifyDetailNone);
486    }
487    return result;
488}
489
490const char *
491visibleSelectionTarget(Display *d, Atom a)
492{
493    const char *result = "?";
494
495    if (a == XA_STRING) {
496	result = "XA_STRING";
497    } else if (a == XA_TEXT(d)) {
498	result = "XA_TEXT()";
499    } else if (a == XA_COMPOUND_TEXT(d)) {
500	result = "XA_COMPOUND_TEXT()";
501    } else if (a == XA_UTF8_STRING(d)) {
502	result = "XA_UTF8_STRING()";
503    } else if (a == XA_TARGETS(d)) {
504	result = "XA_TARGETS()";
505    }
506
507    return result;
508}
509
510#if OPT_TEK4014
511const char *
512visibleTekparse(int code)
513{
514    static const struct {
515	int code;
516	const char *name;
517    } table[] = {
518#include "Tekparse.cin"
519    };
520    const char *result = "?";
521    Cardinal n;
522    for (n = 0; n < XtNumber(table); ++n) {
523	if (table[n].code == code) {
524	    result = table[n].name;
525	    break;
526	}
527    }
528    return result;
529}
530#endif
531
532const char *
533visibleVTparse(int code)
534{
535    static const struct {
536	int code;
537	const char *name;
538    } table[] = {
539#include "VTparse.cin"
540    };
541    const char *result = "?";
542    Cardinal n;
543    for (n = 0; n < XtNumber(table); ++n) {
544	if (table[n].code == code) {
545	    result = table[n].name;
546	    break;
547	}
548    }
549    return result;
550}
551
552const char *
553visibleXError(int code)
554{
555    static char temp[80];
556    const char *result = "?";
557    switch (code) {
558	CASETYPE(Success);
559	CASETYPE(BadRequest);
560	CASETYPE(BadValue);
561	CASETYPE(BadWindow);
562	CASETYPE(BadPixmap);
563	CASETYPE(BadAtom);
564	CASETYPE(BadCursor);
565	CASETYPE(BadFont);
566	CASETYPE(BadMatch);
567	CASETYPE(BadDrawable);
568	CASETYPE(BadAccess);
569	CASETYPE(BadAlloc);
570	CASETYPE(BadColor);
571	CASETYPE(BadGC);
572	CASETYPE(BadIDChoice);
573	CASETYPE(BadName);
574	CASETYPE(BadLength);
575	CASETYPE(BadImplementation);
576    default:
577	sprintf(temp, "%d", code);
578	result = temp;
579	break;
580    }
581    return result;
582}
583
584#if OPT_TRACE_FLAGS
585#define isScrnFlag(flag) ((flag) == LINEWRAPPED)
586
587static char *
588ScrnText(LineData *ld)
589{
590    return visibleIChars(ld->charData, ld->lineSize);
591}
592
593#define SHOW_BAD_LINE(name, ld) \
594	Trace("OOPS " #name " bad row\n")
595
596#define SHOW_SCRN_FLAG(name,code) \
597	Trace(#name " %s:%s\n", \
598	      code ? "*" : "", \
599	      ScrnText(ld))
600
601void
602LineClrFlag(LineData *ld, int flag)
603{
604    if (ld == 0) {
605	SHOW_BAD_LINE(LineClrFlag, ld);
606	assert(0);
607    } else if (isScrnFlag(flag)) {
608	SHOW_SCRN_FLAG(LineClrFlag, 0);
609    }
610
611    LineFlags(ld) &= ~flag;
612}
613
614void
615LineSetFlag(LineData *ld, int flag)
616{
617    if (ld == 0) {
618	SHOW_BAD_LINE(LineSetFlag, ld);
619	assert(0);
620    } else if (isScrnFlag(flag)) {
621	SHOW_SCRN_FLAG(LineSetFlag, 1);
622    }
623
624    LineFlags(ld) |= flag;
625}
626
627int
628LineTstFlag(LineData ld, int flag)
629{
630    int code = 0;
631    if (ld == 0) {
632	SHOW_BAD_LINE(LineTstFlag, ld);
633    } else {
634	code = LineFlags(ld);
635
636	if (isScrnFlag(flag)) {
637	    SHOW_SCRN_FLAG(LineTstFlag, code);
638	}
639    }
640    return code;
641}
642#endif /* OPT_TRACE_FLAGS */
643
644const char *
645TraceAtomName(Display *dpy, Atom atom)
646{
647    static char *result;
648    free(result);
649    if (atom != 0) {
650	result = XGetAtomName(dpy, atom);
651    } else {
652	result = x_strdup("NONE");
653    }
654    return result;
655}
656
657/*
658 * Trace the normal or alternate screen, showing color values up to 16, e.g.,
659 * for debugging with vttest.
660 */
661void
662TraceScreen(XtermWidget xw, int whichBuf)
663{
664    TScreen *screen = TScreenOf(xw);
665
666    if (screen->editBuf_index[whichBuf]) {
667	int row;
668
669	TRACE(("TraceScreen %d:\n", whichBuf));
670	for (row = 0; row <= LastRowNumber(screen); ++row) {
671	    LineData *ld = getLineData(screen, row);
672
673	    TRACE((" %3d:", row));
674	    if (ld != NULL) {
675		int col;
676
677		for (col = 0; col < ld->lineSize; ++col) {
678		    int ch = (int) ld->charData[col];
679		    if (ch < ' ')
680			ch = ' ';
681		    if (ch >= 127)
682			ch = '#';
683		    TRACE(("%c", ch));
684		}
685		TRACE((":\n"));
686
687#if 0
688		TRACE(("  xx:"));
689		for (col = 0; col < ld->lineSize; ++col) {
690		    unsigned attrs = ld->attribs[col];
691		    char ch;
692		    if (attrs & PROTECTED) {
693			ch = '*';
694		    } else if (attrs & BLINK) {
695			ch = 'B';
696		    } else if (attrs & CHARDRAWN) {
697			ch = '+';
698		    } else {
699			ch = ' ';
700		    }
701		    TRACE(("%c", ch));
702		}
703		TRACE((":\n"));
704#endif
705
706#if 0
707		TRACE(("  fg:"));
708		for (col = 0; col < ld->lineSize; ++col) {
709		    unsigned fg = extract_fg(xw, ld->color[col], ld->attribs[col]);
710		    if (fg > 15)
711			fg = 15;
712		    TRACE(("%1x", fg));
713		}
714		TRACE((":\n"));
715
716		TRACE(("  bg:"));
717		for (col = 0; col < ld->lineSize; ++col) {
718		    unsigned bg = extract_bg(xw, ld->color[col], ld->attribs[col]);
719		    if (bg > 15)
720			bg = 15;
721		    TRACE(("%1x", bg));
722		}
723		TRACE((":\n"));
724#endif
725	    } else {
726		TRACE(("null lineData\n"));
727	    }
728	}
729    } else {
730	TRACE(("TraceScreen %d is nil\n", whichBuf));
731    }
732}
733
734static char *
735formatEventMask(char *target, unsigned source, Boolean buttons)
736{
737#define DATA(name) { name ## Mask, #name }
738    static struct {
739	unsigned mask;
740	const char *name;
741    } table[] = {
742	DATA(Shift),
743	    DATA(Lock),
744	    DATA(Control),
745	    DATA(Mod1),
746	    DATA(Mod2),
747	    DATA(Mod3),
748	    DATA(Mod4),
749	    DATA(Mod5),
750	    DATA(Button1),
751	    DATA(Button2),
752	    DATA(Button3),
753	    DATA(Button4),
754	    DATA(Button5),
755    };
756#undef DATA
757    Cardinal n;
758    char marker = L_CURL;
759    char *base = target;
760
761    for (n = 0; n < XtNumber(table); ++n) {
762	if (!buttons && (table[n].mask >= Button1Mask))
763	    continue;
764	if ((table[n].mask & source)) {
765	    UIntClr(source, table[n].mask);
766	    sprintf(target, "%c%s", marker, table[n].name);
767	    target += strlen(target);
768	    marker = '|';
769	}
770    }
771
772    if (source != 0) {
773	sprintf(target, "%c?%#x", marker, source);
774	target += strlen(target);
775	marker = '|';
776    }
777
778    if (marker == L_CURL)
779	*target++ = L_CURL;
780    *target++ = R_CURL;
781
782    *target = '\0';
783    return base;
784}
785
786void
787TraceEvent(const char *tag, XEvent *ev, String *params, const Cardinal *num_params)
788{
789    char mask_buffer[160];
790
791    TRACE(("Event #%lu %s: %#lx %s",
792	   ev->xany.serial,
793	   tag,
794	   ev->xany.window,
795	   visibleEventType(ev->type)));
796
797    switch (ev->type) {
798    case KeyPress:
799	/* FALLTHRU */
800    case KeyRelease:
801	TRACE((" keycode 0x%04X %s",
802	       ev->xkey.keycode,
803	       formatEventMask(mask_buffer, ev->xkey.state, False)));
804	break;
805    case ButtonPress:
806	/* FALLTHRU */
807    case ButtonRelease:
808	TRACE((" button %u state %#x %s",
809	       ev->xbutton.button,
810	       ev->xbutton.state,
811	       formatEventMask(mask_buffer, ev->xbutton.state, True)));
812	break;
813    case MotionNotify:
814	TRACE((" (%d,%d) state %#x %s",
815	       ev->xmotion.y_root,
816	       ev->xmotion.x_root,
817	       ev->xmotion.state,
818	       formatEventMask(mask_buffer, ev->xmotion.state, True)));
819	break;
820    case EnterNotify:
821    case LeaveNotify:
822	TRACE((" at %d,%d root %d,%d %s %s",
823	       ev->xcrossing.y,
824	       ev->xcrossing.x,
825	       ev->xcrossing.y_root,
826	       ev->xcrossing.x_root,
827	       visibleNotifyMode(ev->xcrossing.mode),
828	       visibleNotifyDetail(ev->xcrossing.detail)));
829	break;
830    case FocusIn:
831    case FocusOut:
832	TRACE((" %s %s",
833	       visibleNotifyMode(ev->xfocus.mode),
834	       visibleNotifyDetail(ev->xfocus.detail)));
835	break;
836    case MapNotify:
837	TRACE((" event %#lx %s",
838	       ev->xmap.event,
839	       ev->xmap.override_redirect ? "override" : ""));
840	break;
841    case UnmapNotify:
842	TRACE((" event %#lx %s",
843	       ev->xunmap.event,
844	       ev->xunmap.from_configure ? "configure" : ""));
845	break;
846    case ReparentNotify:
847	TRACE((" at %d,%d event %#lx parent %#lx %s",
848	       ev->xreparent.y,
849	       ev->xreparent.x,
850	       ev->xreparent.event,
851	       ev->xreparent.parent,
852	       ev->xreparent.override_redirect ? "override" : ""));
853	break;
854    case ConfigureNotify:
855	TRACE((" at %d,%d size %dx%d bd %d above %#lx",
856	       ev->xconfigure.y,
857	       ev->xconfigure.x,
858	       ev->xconfigure.height,
859	       ev->xconfigure.width,
860	       ev->xconfigure.border_width,
861	       ev->xconfigure.above));
862	break;
863    case CreateNotify:
864	TRACE((" at %d,%d size %dx%d bd %d",
865	       ev->xcreatewindow.y,
866	       ev->xcreatewindow.x,
867	       ev->xcreatewindow.height,
868	       ev->xcreatewindow.width,
869	       ev->xcreatewindow.border_width));
870	break;
871    case ResizeRequest:
872	TRACE((" size %dx%d",
873	       ev->xresizerequest.height,
874	       ev->xresizerequest.width));
875	break;
876    case PropertyNotify:
877	TRACE((" %s %s",
878	       TraceAtomName(XtDisplay(toplevel), ev->xproperty.atom),
879	       ((ev->xproperty.state == PropertyNewValue)
880		? "NewValue"
881		: ((ev->xproperty.state == PropertyDelete)
882		   ? "Delete"
883		   : "?"))));
884
885	break;
886    case Expose:
887	TRACE((" at %d,%d size %dx%d count %d",
888	       ev->xexpose.y,
889	       ev->xexpose.x,
890	       ev->xexpose.height,
891	       ev->xexpose.width,
892	       ev->xexpose.count));
893	break;
894    case MappingNotify:
895	TRACE((" %s first_keycode %d count %d",
896	       visibleMappingMode(ev->xmapping.request),
897	       ev->xmapping.first_keycode,
898	       ev->xmapping.count));
899	break;
900    case VisibilityNotify:
901	TRACE((" state %d",
902	       ev->xvisibility.state));
903	break;
904    case KeymapNotify:
905	{
906	    Cardinal j;
907	    for (j = 0; j < XtNumber(ev->xkeymap.key_vector); ++j) {
908		if (ev->xkeymap.key_vector[j] != 0) {
909		    Cardinal k;
910		    for (k = 0; k < CHAR_BIT; ++k) {
911			if (ev->xkeymap.key_vector[j] & (1 << k)) {
912			    TRACE((" key%u", (j * CHAR_BIT) + k));
913			}
914		    }
915		}
916	    }
917	}
918	break;
919    case NoExpose:
920	TRACE((" send_event:%d display %p major:%d minor:%d",
921	       ev->xnoexpose.send_event,
922	       (void *) ev->xnoexpose.display,
923	       ev->xnoexpose.major_code,
924	       ev->xnoexpose.minor_code));
925	break;
926    case GraphicsExpose:
927	TRACE((" send_event:%d display %p major:%d minor:%d",
928	       ev->xgraphicsexpose.send_event,
929	       (void *) ev->xgraphicsexpose.display,
930	       ev->xgraphicsexpose.major_code,
931	       ev->xgraphicsexpose.minor_code));
932	break;
933    case SelectionClear:
934	TRACE((" selection:%s",
935	       TraceAtomName(ev->xselectionclear.display,
936			     ev->xselectionclear.selection)));
937	break;
938    case SelectionRequest:
939	TRACE((" owner:%#lx requestor:%#lx",
940	       ev->xselectionrequest.owner,
941	       ev->xselectionrequest.requestor));
942	TRACE((" selection:%s",
943	       TraceAtomName(ev->xselectionrequest.display,
944			     ev->xselectionrequest.selection)));
945	TRACE((" target:%s",
946	       TraceAtomName(ev->xselectionrequest.display,
947			     ev->xselectionrequest.target)));
948	TRACE((" property:%s",
949	       TraceAtomName(ev->xselectionrequest.display,
950			     ev->xselectionrequest.property)));
951	break;
952    default:
953	TRACE((":FIXME"));
954	break;
955    }
956    TRACE(("\n"));
957    if (params != NULL && *num_params != 0) {
958	Cardinal n;
959	for (n = 0; n < *num_params; ++n) {
960	    TRACE(("  param[%d] = %s\n", n, params[n]));
961	}
962    }
963}
964
965#if OPT_RENDERFONT && OPT_WIDE_CHARS
966void
967TraceFallback(XtermWidget xw, const char *tag, unsigned wc, int n, XftFont *font)
968{
969    TScreen *screen = TScreenOf(xw);
970    XGlyphInfo gi;
971    int expect = my_wcwidth((wchar_t) wc);
972    int hijack = mk_wcwidth_cjk((wchar_t) wc);
973    int actual;
974
975    XftTextExtents32(screen->display, font, &wc, 1, &gi);
976    actual = ((gi.xOff + FontWidth(screen) - 1)
977	      / FontWidth(screen));
978
979    TRACE(("FALLBACK #%d %s U+%04X %d,%d pos,"
980	   " %d,%d off," " %dx%d size,"
981	   " %d/%d next," " %d vs %d/%d cells%s\n",
982	   n + 1, tag, wc,
983	   gi.y, gi.x,
984	   gi.yOff, gi.xOff,
985	   gi.height, gi.width,
986	   font->max_advance_width,
987	   FontWidth(screen),
988	   actual, expect, hijack,
989	   ((actual != expect)
990	    ? ((actual == hijack)
991	       ? " OOPS"
992	       : " oops")
993	    : "")));
994}
995#endif /* OPT_RENDERFONT */
996
997void
998TraceFocus(Widget w, XEvent *ev)
999{
1000    TRACE(("trace_focus event type %d:%s\n",
1001	   ev->type, visibleEventType(ev->type)));
1002    switch (ev->type) {
1003    case FocusIn:
1004    case FocusOut:
1005	{
1006	    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1007	    TRACE(("\tdetail: %s\n", visibleNotifyDetail(event->detail)));
1008	    TRACE(("\tmode:   %s\n", visibleNotifyMode(event->mode)));
1009	    TRACE(("\twindow: %#lx\n", event->window));
1010	}
1011	break;
1012    case EnterNotify:
1013    case LeaveNotify:
1014	{
1015	    XCrossingEvent *event = (XCrossingEvent *) ev;
1016	    TRACE(("\tdetail:    %s\n", visibleNotifyDetail(event->detail)));
1017	    TRACE(("\tmode:      %s\n", visibleNotifyMode(event->mode)));
1018	    TRACE(("\twindow:    %#lx\n", event->window));
1019	    TRACE(("\tfocus:     %d\n", event->focus));
1020	    TRACE(("\troot:      %#lx\n", event->root));
1021	    TRACE(("\tsubwindow: %#lx\n", event->subwindow));
1022	}
1023	break;
1024    }
1025    while (w != NULL) {
1026	TRACE(("w %p -> %#lx\n", (void *) w, XtWindow(w)));
1027	w = XtParent(w);
1028    }
1029}
1030
1031void
1032TraceSizeHints(XSizeHints * hints)
1033{
1034    TRACE(("size hints:\n"));
1035    if (hints->flags & (USPosition | PPosition)) {
1036	TRACE(("   position   %d,%d%s%s\n", hints->y, hints->x,
1037	       (hints->flags & USPosition) ? " user" : "",
1038	       (hints->flags & PPosition) ? " prog" : ""));
1039    }
1040    if (hints->flags & (USSize | PSize)) {
1041	TRACE(("   size       %d,%d%s%s\n", hints->height, hints->width,
1042	       (hints->flags & USSize) ? " user" : "",
1043	       (hints->flags & PSize) ? " prog" : ""));
1044    }
1045    if (hints->flags & PMinSize) {
1046	TRACE(("   min        %d,%d\n", hints->min_height, hints->min_width));
1047    }
1048    if (hints->flags & PMaxSize) {
1049	TRACE(("   max        %d,%d\n", hints->max_height, hints->max_width));
1050    }
1051    if (hints->flags & PResizeInc) {
1052	TRACE(("   inc        %d,%d\n", hints->height_inc, hints->width_inc));
1053    } else {
1054	TRACE(("   inc        NONE!\n"));
1055    }
1056    if (hints->flags & PAspect) {
1057	TRACE(("   min aspect %d/%d\n", hints->min_aspect.y, hints->min_aspect.y));
1058	TRACE(("   max aspect %d/%d\n", hints->max_aspect.y, hints->max_aspect.y));
1059    }
1060    if (hints->flags & PBaseSize) {
1061	TRACE(("   base       %d,%d\n", hints->base_height, hints->base_width));
1062    }
1063    if (hints->flags & PWinGravity) {
1064	TRACE(("   gravity    %d\n", hints->win_gravity));
1065    }
1066}
1067
1068static void
1069TraceEventMask(const char *tag, long mask)
1070{
1071#define DATA(name) { name##Mask, #name }
1072    /* *INDENT-OFF* */
1073    static struct {
1074	long mask;
1075	const char *name;
1076    } table[] = {
1077	DATA(KeyPress),
1078	DATA(KeyRelease),
1079	DATA(ButtonPress),
1080	DATA(ButtonRelease),
1081	DATA(EnterWindow),
1082	DATA(LeaveWindow),
1083	DATA(PointerMotion),
1084	DATA(PointerMotionHint),
1085	DATA(Button1Motion),
1086	DATA(Button2Motion),
1087	DATA(Button3Motion),
1088	DATA(Button4Motion),
1089	DATA(Button5Motion),
1090	DATA(ButtonMotion),
1091	DATA(KeymapState),
1092	DATA(Exposure),
1093	DATA(VisibilityChange),
1094	DATA(StructureNotify),
1095	DATA(ResizeRedirect),
1096	DATA(SubstructureNotify),
1097	DATA(SubstructureRedirect),
1098	DATA(FocusChange),
1099	DATA(PropertyChange),
1100	DATA(ColormapChange),
1101	DATA(OwnerGrabButton),
1102    };
1103#undef DATA
1104    Cardinal n;
1105    /* *INDENT-ON* */
1106
1107    for (n = 0; n < XtNumber(table); ++n) {
1108	if (table[n].mask & mask) {
1109	    TRACE(("%s %s\n", tag, table[n].name));
1110	}
1111    }
1112}
1113
1114void
1115TraceWindowAttributes(XWindowAttributes * attrs)
1116{
1117    TRACE(("window attributes:\n"));
1118    TRACE(("   position     %d,%d\n", attrs->y, attrs->x));
1119    TRACE(("   size         %dx%d\n", attrs->height, attrs->width));
1120    TRACE(("   border       %d\n", attrs->border_width));
1121    TRACE(("   depth        %d\n", attrs->depth));
1122    TRACE(("   bit_gravity  %d\n", attrs->bit_gravity));
1123    TRACE(("   win_gravity  %d\n", attrs->win_gravity));
1124    TRACE(("   root         %#lx\n", (long) attrs->root));
1125    TRACE(("   class        %s\n", ((attrs->class == InputOutput)
1126				    ? "InputOutput"
1127				    : ((attrs->class == InputOnly)
1128				       ? "InputOnly"
1129				       : "unknown"))));
1130    TRACE(("   map_state    %s\n", ((attrs->map_state == IsUnmapped)
1131				    ? "IsUnmapped"
1132				    : ((attrs->map_state == IsUnviewable)
1133				       ? "IsUnviewable"
1134				       : ((attrs->map_state == IsViewable)
1135					  ? "IsViewable"
1136					  : "unknown")))));
1137    TRACE(("   all_events\n"));
1138    TraceEventMask("        ", attrs->all_event_masks);
1139    TRACE(("   your_events\n"));
1140    TraceEventMask("        ", attrs->your_event_mask);
1141    TRACE(("   no_propagate\n"));
1142    TraceEventMask("        ", attrs->do_not_propagate_mask);
1143}
1144
1145void
1146TraceWMSizeHints(XtermWidget xw)
1147{
1148    XSizeHints sizehints = xw->hints;
1149
1150    getXtermSizeHints(xw);
1151    TraceSizeHints(&xw->hints);
1152    xw->hints = sizehints;
1153}
1154
1155const char *
1156ModifierName(unsigned modifier)
1157{
1158    const char *s = "";
1159    if (modifier & ShiftMask)
1160	s = " Shift";
1161    else if (modifier & LockMask)
1162	s = " Lock";
1163    else if (modifier & ControlMask)
1164	s = " Control";
1165    else if (modifier & Mod1Mask)
1166	s = " Mod1";
1167    else if (modifier & Mod2Mask)
1168	s = " Mod2";
1169    else if (modifier & Mod3Mask)
1170	s = " Mod3";
1171    else if (modifier & Mod4Mask)
1172	s = " Mod4";
1173    else if (modifier & Mod5Mask)
1174	s = " Mod5";
1175    return s;
1176}
1177
1178void
1179TraceTranslations(const char *name, Widget w)
1180{
1181    String result;
1182    XErrorHandler save = XSetErrorHandler(ignore_x11_error);
1183    XtTranslations xlations;
1184    Widget xcelerat;
1185
1186    TRACE(("TraceTranslations for %s (widget %#lx) " TRACE_L "\n",
1187	   name, (long) w));
1188    if (w) {
1189	XtVaGetValues(w,
1190		      XtNtranslations, &xlations,
1191		      XtNaccelerators, &xcelerat,
1192		      (XtPointer) 0);
1193	TRACE(("... xlations %#08lx\n", (long) xlations));
1194	TRACE(("... xcelerat %#08lx\n", (long) xcelerat));
1195	result = _XtPrintXlations(w, xlations, xcelerat, True);
1196	TRACE(("%s\n", NonNull(result)));
1197	if (result)
1198	    XFree((char *) result);
1199    } else {
1200	TRACE(("none (widget is null)\n"));
1201    }
1202    TRACE((TRACE_R "\n"));
1203    XSetErrorHandler(save);
1204}
1205
1206XtGeometryResult
1207TraceResizeRequest(const char *fn, int ln, Widget w,
1208		   unsigned reqwide,
1209		   unsigned reqhigh,
1210		   Dimension *gotwide,
1211		   Dimension *gothigh)
1212{
1213    XtGeometryResult rc;
1214
1215    TRACE(("%s@%d ResizeRequest %ux%u\n", fn, ln, reqhigh, reqwide));
1216    rc = XtMakeResizeRequest((Widget) w,
1217			     (Dimension) reqwide,
1218			     (Dimension) reqhigh,
1219			     gotwide, gothigh);
1220    TRACE(("... ResizeRequest -> "));
1221    if (gothigh && gotwide)
1222	TRACE(("%dx%d ", *gothigh, *gotwide));
1223    TRACE(("(%d)\n", rc));
1224    return rc;
1225}
1226
1227#define XRES_S(name) Trace(#name " = %s\n", NonNull(resp->name))
1228#define XRES_B(name) Trace(#name " = %s\n", MtoS(resp->name))
1229#define XRES_I(name) Trace(#name " = %d\n", resp->name)
1230
1231void
1232TraceXtermResources(void)
1233{
1234    XTERM_RESOURCE *resp = &resource;
1235
1236    Trace("XTERM_RESOURCE settings:\n");
1237    XRES_S(icon_geometry);
1238    XRES_S(title);
1239    XRES_S(icon_hint);
1240    XRES_S(icon_name);
1241    XRES_S(term_name);
1242    XRES_S(tty_modes);
1243    XRES_I(minBufSize);
1244    XRES_I(maxBufSize);
1245    XRES_B(hold_screen);
1246    XRES_B(utmpInhibit);
1247    XRES_B(utmpDisplayId);
1248    XRES_B(messages);
1249    XRES_S(menuLocale);
1250    XRES_S(omitTranslation);
1251    XRES_S(keyboardType);
1252#ifdef HAVE_LIB_XCURSOR
1253    XRES_S(cursorTheme);
1254#endif
1255#if OPT_PRINT_ON_EXIT
1256    XRES_I(printModeNow);
1257    XRES_I(printModeOnXError);
1258    XRES_I(printOptsNow);
1259    XRES_I(printOptsOnXError);
1260    XRES_S(printFileNow);
1261    XRES_S(printFileOnXError);
1262#endif
1263#if OPT_SUNPC_KBD
1264    XRES_B(sunKeyboard);
1265#endif
1266#if OPT_HP_FUNC_KEYS
1267    XRES_B(hpFunctionKeys);
1268#endif
1269#if OPT_SCO_FUNC_KEYS
1270    XRES_B(scoFunctionKeys);
1271#endif
1272#if OPT_SUN_FUNC_KEYS
1273    XRES_B(sunFunctionKeys);
1274#endif
1275#if OPT_INITIAL_ERASE
1276    XRES_B(ptyInitialErase);
1277    XRES_B(backarrow_is_erase);
1278#endif
1279    XRES_B(useInsertMode);
1280#if OPT_ZICONBEEP
1281    XRES_I(zIconBeep);
1282    XRES_S(zIconFormat);
1283#endif
1284#if OPT_PTY_HANDSHAKE
1285    XRES_B(wait_for_map);
1286    XRES_B(ptyHandshake);
1287    XRES_B(ptySttySize);
1288#endif
1289#if OPT_REPORT_CCLASS
1290    XRES_B(reportCClass);
1291#endif
1292#if OPT_REPORT_COLORS
1293    XRES_B(reportColors);
1294#endif
1295#if OPT_REPORT_FONTS
1296    XRES_B(reportFonts);
1297#endif
1298#if OPT_REPORT_ICONS
1299    XRES_B(reportIcons);
1300#endif
1301#if OPT_SAME_NAME
1302    XRES_B(sameName);
1303#endif
1304#if OPT_SESSION_MGT
1305    XRES_B(sessionMgt);
1306#endif
1307#if OPT_TOOLBAR
1308    XRES_B(toolBar);
1309#endif
1310#if OPT_MAXIMIZE
1311    XRES_B(maximized);
1312    XRES_S(fullscreen_s);
1313#endif
1314#if USE_DOUBLE_BUFFER
1315    XRES_B(buffered);
1316    XRES_I(buffered_fps);
1317#endif
1318}
1319
1320void
1321TraceArgv(const char *tag, char **argv)
1322{
1323    TRACE(("%s:\n", tag));
1324    if (argv != NULL) {
1325	int n = 0;
1326
1327	while (*argv != NULL) {
1328	    TRACE(("  %d:%s\n", n++, *argv++));
1329	}
1330    }
1331}
1332
1333static char *
1334parse_option(char *dst, String src, int first)
1335{
1336    char *s;
1337
1338    if (!strncmp(src, "-/+", (size_t) 3)) {
1339	dst[0] = (char) first;
1340	strcpy(dst + 1, src + 3);
1341    } else {
1342	strcpy(dst, src);
1343    }
1344    for (s = dst; *s != '\0'; s++) {
1345	if (*s == '#' || *s == '%' || *s == 'S') {
1346	    s[1] = '\0';
1347	} else if (*s == ' ') {
1348	    *s = '\0';
1349	    break;
1350	}
1351    }
1352    return dst;
1353}
1354
1355static Bool
1356same_option(OptionHelp * opt, XrmOptionDescRec * res)
1357{
1358    char temp[BUFSIZ];
1359    return !strcmp(parse_option(temp, opt->opt, res->option[0]), res->option);
1360}
1361
1362static Bool
1363standard_option(String opt)
1364{
1365    static const char *table[] =
1366    {
1367	"+rv",
1368	"+synchronous",
1369	"-background",
1370	"-bd",
1371	"-bg",
1372	"-bordercolor",
1373	"-borderwidth",
1374	"-bw",
1375	"-display",
1376	"-fg",
1377	"-fn",
1378	"-font",
1379	"-foreground",
1380	"-geometry",
1381	"-iconic",
1382	"-name",
1383	"-reverse",
1384	"-rv",
1385	"-selectionTimeout",
1386	"-synchronous",
1387	"-title",
1388	"-xnllanguage",
1389	"-xrm",
1390	"-xtsessionID",
1391    };
1392    Cardinal n;
1393    char temp[BUFSIZ];
1394
1395    opt = parse_option(temp, opt, '-');
1396    for (n = 0; n < XtNumber(table); n++) {
1397	if (!strcmp(opt, table[n]))
1398	    return True;
1399    }
1400    return False;
1401}
1402
1403/*
1404 * Analyse the options/help messages for inconsistencies.
1405 */
1406void
1407TraceOptions(OptionHelp * options, XrmOptionDescRec * resources, Cardinal res_count)
1408{
1409    OptionHelp *opt_array = sortedOpts(options, resources, res_count);
1410    size_t j, k;
1411    XrmOptionDescRec *res_array = sortedOptDescs(resources, res_count);
1412    Bool first, found;
1413
1414    TRACE(("Checking options-tables for inconsistencies:\n"));
1415
1416#if 0
1417    TRACE(("Options listed in help-message:\n"));
1418    for (j = 0; options[j].opt != 0; j++)
1419	TRACE(("%5d %-28s %s\n", j, opt_array[j].opt, opt_array[j].desc));
1420    TRACE(("Options listed in resource-table:\n"));
1421    for (j = 0; j < res_count; j++)
1422	TRACE(("%5d %-28s %s\n", j, res_array[j].option, res_array[j].specifier));
1423#endif
1424
1425    /* list all options[] not found in resources[] */
1426    for (j = 0, first = True; options[j].opt != NULL; j++) {
1427	found = False;
1428	for (k = 0; k < res_count; k++) {
1429	    if (same_option(&opt_array[j], &res_array[k])) {
1430		found = True;
1431		break;
1432	    }
1433	}
1434	if (!found) {
1435	    if (first) {
1436		TRACE(("Options listed in help, not found in resource list:\n"));
1437		first = False;
1438	    }
1439	    TRACE(("  %-28s%s\n", opt_array[j].opt,
1440		   standard_option(opt_array[j].opt) ? " (standard)" : ""));
1441	}
1442    }
1443
1444    /* list all resources[] not found in options[] */
1445    for (j = 0, first = True; j < res_count; j++) {
1446	found = False;
1447	for (k = 0; options[k].opt != NULL; k++) {
1448	    if (same_option(&opt_array[k], &res_array[j])) {
1449		found = True;
1450		break;
1451	    }
1452	}
1453	if (!found) {
1454	    if (first) {
1455		TRACE(("Resource list items not found in options-help:\n"));
1456		first = False;
1457	    }
1458	    TRACE(("  %s\n", res_array[j].option));
1459	}
1460    }
1461
1462    TRACE(("Resource list items that will be ignored by XtOpenApplication:\n"));
1463    for (j = 0; j < res_count; j++) {
1464	switch (res_array[j].argKind) {
1465	case XrmoptionSkipArg:
1466	    TRACE(("  %-28s {param}\n", res_array[j].option));
1467	    break;
1468	case XrmoptionSkipNArgs:
1469	    TRACE(("  %-28s {%ld params}\n", res_array[j].option, (long)
1470		   res_array[j].value));
1471	    break;
1472	case XrmoptionSkipLine:
1473	    TRACE(("  %-28s {remainder of line}\n", res_array[j].option));
1474	    break;
1475	case XrmoptionIsArg:
1476	case XrmoptionNoArg:
1477	case XrmoptionResArg:
1478	case XrmoptionSepArg:
1479	case XrmoptionStickyArg:
1480	default:
1481	    break;
1482	}
1483    }
1484}
1485#else
1486extern void empty_trace(void);
1487void
1488empty_trace(void)
1489{
1490}
1491#endif
1492