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