trace.c revision 2eaa94a1
1/* $XTermId: trace.c,v 1.85 2008/06/03 20:52:34 tom Exp $ */
2
3/************************************************************
4
5Copyright 1997-2007,2008 by Thomas E. Dickey
6
7                        All Rights Reserved
8
9Permission to use, copy, modify, and distribute this software and its
10documentation for any purpose and without fee is hereby granted,
11provided that the above copyright notice appear in all copies and that
12both that copyright notice and this permission notice appear in
13supporting documentation, and that the name of the above listed
14copyright holder(s) not be used in advertising or publicity pertaining
15to distribution of the software without specific, written prior
16permission.
17
18THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD
19TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
20AND FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE
21LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25
26********************************************************/
27
28/*
29 * debugging support via TRACE macro.
30 */
31
32#include <xterm.h>		/* for definition of GCC_UNUSED */
33#include <data.h>
34#include <trace.h>
35
36#include <time.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include <sys/types.h>
40#include <stdio.h>
41#include <stdarg.h>
42#include <assert.h>
43
44#ifdef HAVE_X11_TRANSLATEI_H
45#include <X11/TranslateI.h>
46#else
47#ifdef __cplusplus
48extern "C" {
49#endif
50
51    extern String _XtPrintXlations(Widget w,
52				   XtTranslations xlations,
53				   Widget accelWidget,
54				   _XtBoolean includeRHS);
55#ifdef __cplusplus
56}
57#endif
58#endif
59char *trace_who = "parent";
60
61void
62Trace(const char *fmt,...)
63{
64    static FILE *fp;
65    static char *trace_out;
66    va_list ap;
67
68    if (fp != 0
69	&& trace_who != trace_out) {
70	fclose(fp);
71	fp = 0;
72    }
73    trace_out = trace_who;
74
75    if (!fp) {
76	char name[BUFSIZ];
77#if 0				/* usually I do not want unique names */
78	int unique;
79	for (unique = 0;; ++unique) {
80	    if (unique)
81		sprintf(name, "Trace-%s.out-%d", trace_who, unique);
82	    else
83		sprintf(name, "Trace-%s.out", trace_who);
84	    if ((fp = fopen(name, "r")) == 0) {
85		break;
86	    }
87	    fclose(fp);
88	}
89#else
90	sprintf(name, "Trace-%s.out", trace_who);
91#endif
92	fp = fopen(name, "w");
93	if (fp != 0) {
94	    fprintf(fp, "%s\n", xtermVersion());
95	    TraceIds(NULL, 0);
96	}
97    }
98    if (!fp)
99	abort();
100
101    va_start(ap, fmt);
102    if (fmt != 0) {
103	vfprintf(fp, fmt, ap);
104	(void) fflush(fp);
105    } else {
106	(void) fclose(fp);
107	(void) fflush(stdout);
108	(void) fflush(stderr);
109    }
110    va_end(ap);
111}
112
113void
114TraceIds(const char *fname, int lnum)
115{
116    Trace("process %d ", (int) getpid());
117#ifdef HAVE_UNISTD_H
118    Trace("real (%u/%u) effective (%u/%u)",
119	  (unsigned) getuid(), (unsigned) getgid(),
120	  (unsigned) geteuid(), (unsigned) getegid());
121#endif
122    if (fname != 0) {
123	Trace(" (%s@%d)\n", fname, lnum);
124    } else {
125	time_t now = time((time_t *) 0);
126	Trace("-- %s", ctime(&now));
127    }
128}
129
130static void
131formatAscii(char *dst, unsigned value)
132{
133    switch (value) {
134    case '\\':
135	sprintf(dst, "\\\\");
136	break;
137    case '\b':
138	sprintf(dst, "\\b");
139	break;
140    case '\n':
141	sprintf(dst, "\\n");
142	break;
143    case '\r':
144	sprintf(dst, "\\r");
145	break;
146    case '\t':
147	sprintf(dst, "\\t");
148	break;
149    default:
150	if (E2A(value) < 32 || (E2A(value) >= 127 && E2A(value) < 160))
151	    sprintf(dst, "\\%03o", value);
152	else
153	    sprintf(dst, "%c", CharOf(value));
154	break;
155    }
156}
157
158#if OPT_DEC_CHRSET
159
160const char *
161visibleChrsetName(int chrset)
162{
163    const char *result = "?";
164    switch (chrset) {
165    case CSET_SWL:
166	result = "CSET_SWL";
167	break;
168    case CSET_DHL_TOP:
169	result = "CSET_DHL_TOP";
170	break;
171    case CSET_DHL_BOT:
172	result = "CSET_DHL_BOT";
173	break;
174    case CSET_DWL:
175	result = "CSET_DWL";
176	break;
177    }
178    return result;
179}
180#endif
181
182char *
183visibleChars(PAIRED_CHARS(Char * buf, Char * buf2), unsigned len)
184{
185    static char *result;
186    static unsigned used;
187    unsigned limit = ((len + 1) * 8) + 1;
188    char *dst;
189
190    if (limit > used) {
191	used = limit;
192	result = XtRealloc(result, used);
193    }
194    dst = result;
195    *dst = '\0';
196    while (len--) {
197	unsigned value = *buf++;
198#if OPT_WIDE_CHARS
199	if (buf2 != 0) {
200	    value |= (*buf2 << 8);
201	    buf2++;
202	}
203	if (value > 255)
204	    sprintf(dst, "\\u+%04X", value);
205	else
206#endif
207	    formatAscii(dst, value);
208	dst += strlen(dst);
209    }
210    return result;
211}
212
213char *
214visibleIChar(IChar * buf, unsigned len)
215{
216    static char *result;
217    static unsigned used;
218    unsigned limit = ((len + 1) * 6) + 1;
219    char *dst;
220
221    if (limit > used) {
222	used = limit;
223	result = XtRealloc(result, used);
224    }
225    dst = result;
226    while (len--) {
227	unsigned value = *buf++;
228#if OPT_WIDE_CHARS
229	if (value > 255)
230	    sprintf(dst, "\\u+%04X", value);
231	else
232#endif
233	    formatAscii(dst, value);
234	dst += strlen(dst);
235    }
236    return result;
237}
238
239#define CASETYPE(name) case name: result = #name; break;
240
241const char *
242visibleKeyboardType(xtermKeyboardType type)
243{
244    const char *result = "?";
245    switch (type) {
246	CASETYPE(keyboardIsLegacy);	/* bogus vt220 codes for F1-F4, etc. */
247	CASETYPE(keyboardIsDefault);
248	CASETYPE(keyboardIsHP);
249	CASETYPE(keyboardIsSCO);
250	CASETYPE(keyboardIsSun);
251	CASETYPE(keyboardIsTermcap);
252	CASETYPE(keyboardIsVT220);
253    }
254    return result;
255}
256
257const char *
258visibleEventType(int type)
259{
260    const char *result = "?";
261    switch (type) {
262	CASETYPE(KeyPress);
263	CASETYPE(KeyRelease);
264	CASETYPE(ButtonPress);
265	CASETYPE(ButtonRelease);
266	CASETYPE(MotionNotify);
267	CASETYPE(EnterNotify);
268	CASETYPE(LeaveNotify);
269	CASETYPE(FocusIn);
270	CASETYPE(FocusOut);
271	CASETYPE(KeymapNotify);
272	CASETYPE(Expose);
273	CASETYPE(GraphicsExpose);
274	CASETYPE(NoExpose);
275	CASETYPE(VisibilityNotify);
276	CASETYPE(CreateNotify);
277	CASETYPE(DestroyNotify);
278	CASETYPE(UnmapNotify);
279	CASETYPE(MapNotify);
280	CASETYPE(MapRequest);
281	CASETYPE(ReparentNotify);
282	CASETYPE(ConfigureNotify);
283	CASETYPE(ConfigureRequest);
284	CASETYPE(GravityNotify);
285	CASETYPE(ResizeRequest);
286	CASETYPE(CirculateNotify);
287	CASETYPE(CirculateRequest);
288	CASETYPE(PropertyNotify);
289	CASETYPE(SelectionClear);
290	CASETYPE(SelectionRequest);
291	CASETYPE(SelectionNotify);
292	CASETYPE(ColormapNotify);
293	CASETYPE(ClientMessage);
294	CASETYPE(MappingNotify);
295    }
296    return result;
297}
298
299const char *
300visibleXError(int code)
301{
302    static char temp[80];
303    const char *result = "?";
304    switch (code) {
305	CASETYPE(Success);
306	CASETYPE(BadRequest);
307	CASETYPE(BadValue);
308	CASETYPE(BadWindow);
309	CASETYPE(BadPixmap);
310	CASETYPE(BadAtom);
311	CASETYPE(BadCursor);
312	CASETYPE(BadFont);
313	CASETYPE(BadMatch);
314	CASETYPE(BadDrawable);
315	CASETYPE(BadAccess);
316	CASETYPE(BadAlloc);
317	CASETYPE(BadColor);
318	CASETYPE(BadGC);
319	CASETYPE(BadIDChoice);
320	CASETYPE(BadName);
321	CASETYPE(BadLength);
322	CASETYPE(BadImplementation);
323    default:
324	sprintf(temp, "%d", code);
325	result = temp;
326	break;
327    }
328    return result;
329}
330
331#if OPT_TRACE_FLAGS
332#define isScrnFlag(flag) ((flag) == LINEWRAPPED)
333
334static char *
335ScrnText(TScreen * screen, int row)
336{
337    Char *chars = SCRN_BUF_CHARS(screen, row);
338#if OPT_WIDE_CHARS
339    Char *widec = 0;
340#endif
341
342    if_OPT_WIDE_CHARS(screen, {
343	widec = SCRN_BUF_WIDEC(screen, row);
344    });
345    return visibleChars(PAIRED_CHARS(chars, widec), screen->max_col + 1);
346}
347
348#if OPT_TRACE_FLAGS > 1
349#define DETAILED_FLAGS(name) \
350    Trace("TEST " #name " %d [%d..%d] top %d chars %p (%d)\n", \
351    	  row, \
352	  -screen->savedlines, \
353	  screen->max_row, \
354	  screen->topline, \
355	  SCRN_BUF_CHARS(screen, row), \
356	  (&(SCRN_BUF_FLAGS(screen, row)) - screen->visbuf) / MAX_PTRS)
357#else
358#define DETAILED_FLAGS(name)	/* nothing */
359#endif
360
361#define SHOW_BAD_ROW(name, screen, row) \
362	Trace("OOPS " #name " bad row %d [%d..%d]\n", \
363	      row, -(screen->savedlines), screen->max_row)
364
365#define SHOW_SCRN_FLAG(name,code) \
366	Trace(#name " {%d, top=%d, saved=%d}%05d%s:%s\n", \
367	      row, screen->topline, screen->savedlines, \
368	      ROW2ABS(screen, row), \
369	      code ? "*" : "", \
370	      ScrnText(screen, row))
371
372void
373ScrnClrFlag(TScreen * screen, int row, int flag)
374{
375    DETAILED_FLAGS(ScrnClrFlag);
376    if (!okScrnRow(screen, row)) {
377	SHOW_BAD_ROW(ScrnClrFlag, screen, row);
378	assert(0);
379    } else if (isScrnFlag(flag)) {
380	SHOW_SCRN_FLAG(ScrnClrFlag, 0);
381    }
382
383    SCRN_BUF_FLAGS(screen, row) =
384	(Char *) ((long) SCRN_BUF_FLAGS(screen, row) & ~(flag));
385}
386
387void
388ScrnSetFlag(TScreen * screen, int row, int flag)
389{
390    DETAILED_FLAGS(ScrnSetFlag);
391    if (!okScrnRow(screen, row)) {
392	SHOW_BAD_ROW(ScrnSetFlag, screen, row);
393	assert(0);
394    } else if (isScrnFlag(flag)) {
395	SHOW_SCRN_FLAG(ScrnSetFlag, 1);
396    }
397
398    SCRN_BUF_FLAGS(screen, row) =
399	(Char *) (((long) SCRN_BUF_FLAGS(screen, row) | (flag)));
400}
401
402int
403ScrnTstFlag(TScreen * screen, int row, int flag)
404{
405    int code = 0;
406    if (!okScrnRow(screen, row)) {
407	SHOW_BAD_ROW(ScrnTstFlag, screen, row);
408    } else {
409	code = ((long) SCRN_BUF_FLAGS(screen, row) & (flag)) != 0;
410
411	DETAILED_FLAGS(ScrnTstFlag);
412	if (!okScrnRow(screen, row)) {
413	    SHOW_BAD_ROW(ScrnSetFlag, screen, row);
414	    assert(0);
415	} else if (isScrnFlag(flag)) {
416	    SHOW_SCRN_FLAG(ScrnTstFlag, code);
417	}
418    }
419    return code;
420}
421#endif /* OPT_TRACE_FLAGS */
422
423void
424TraceSizeHints(XSizeHints * hints)
425{
426    TRACE(("size hints:\n"));
427    if (hints->flags & (USPosition | PPosition))
428	TRACE(("   position   %d,%d%s%s\n", hints->y, hints->x,
429	       hints->flags & USPosition ? " user" : "",
430	       hints->flags & PPosition ? " prog" : ""));
431    if (hints->flags & (USSize | PSize))
432	TRACE(("   size       %d,%d%s%s\n", hints->height, hints->width,
433	       hints->flags & USSize ? " user" : "",
434	       hints->flags & PSize ? " prog" : ""));
435    if (hints->flags & PMinSize)
436	TRACE(("   min        %d,%d\n", hints->min_height, hints->min_width));
437    if (hints->flags & PMaxSize)
438	TRACE(("   max        %d,%d\n", hints->max_height, hints->max_width));
439    if (hints->flags & PResizeInc)
440	TRACE(("   inc        %d,%d\n", hints->height_inc, hints->width_inc));
441    else
442	TRACE(("   inc        NONE!\n"));
443    if (hints->flags & PAspect)
444	TRACE(("   min aspect %d/%d\n", hints->min_aspect.y, hints->min_aspect.y));
445    if (hints->flags & PAspect)
446	TRACE(("   max aspect %d/%d\n", hints->max_aspect.y, hints->max_aspect.y));
447    if (hints->flags & PBaseSize)
448	TRACE(("   base       %d,%d\n", hints->base_height, hints->base_width));
449    if (hints->flags & PWinGravity)
450	TRACE(("   gravity    %d\n", hints->win_gravity));
451}
452
453void
454TraceWMSizeHints(XtermWidget xw)
455{
456    XSizeHints sizehints = xw->hints;
457
458    getXtermSizeHints(xw);
459    TraceSizeHints(&xw->hints);
460    xw->hints = sizehints;
461}
462
463/*
464 * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
465 * our own error-handler.
466 */
467static int
468no_error(Display * dpy GCC_UNUSED, XErrorEvent * event GCC_UNUSED)
469{
470    return 1;
471}
472
473void
474TraceTranslations(const char *name, Widget w)
475{
476    String result;
477    XErrorHandler save = XSetErrorHandler(no_error);
478    XtTranslations xlations;
479    Widget xcelerat;
480
481    TRACE(("TraceTranslations for %s (widget %#lx)\n", name, (long) w));
482    if (w) {
483	XtVaGetValues(w,
484		      XtNtranslations, &xlations,
485		      XtNaccelerators, &xcelerat,
486		      (XtPointer) 0);
487	TRACE(("... xlations %#08lx\n", (long) xlations));
488	TRACE(("... xcelerat %#08lx\n", (long) xcelerat));
489	result = _XtPrintXlations(w, xlations, xcelerat, True);
490	TRACE(("%s\n", result != 0 ? result : "(null)"));
491	if (result)
492	    XFree(result);
493    } else {
494	TRACE(("none (widget is null)\n"));
495    }
496    XSetErrorHandler(save);
497}
498
499int
500TraceResizeRequest(const char *fn, int ln, Widget w,
501		   Dimension reqwide,
502		   Dimension reqhigh,
503		   Dimension * gotwide,
504		   Dimension * gothigh)
505{
506    int rc;
507
508    TRACE(("%s@%d ResizeRequest %dx%d\n", fn, ln, reqhigh, reqwide));
509    rc = XtMakeResizeRequest((Widget) w, reqwide, reqhigh, gotwide, gothigh);
510    TRACE(("... ResizeRequest -> "));
511    if (gothigh && gotwide)
512	TRACE(("%dx%d ", *gothigh, *gotwide));
513    TRACE(("(%d)\n", rc));
514    return rc;
515}
516
517#define XRES_S(name) Trace(#name " = %s\n", NonNull(resp->name))
518#define XRES_B(name) Trace(#name " = %s\n", BtoS(resp->name))
519#define XRES_I(name) Trace(#name " = %d\n", resp->name)
520
521void
522TraceXtermResources(void)
523{
524    XTERM_RESOURCE *resp = &resource;
525
526    Trace("XTERM_RESOURCE settings:\n");
527    XRES_S(xterm_name);
528    XRES_S(icon_geometry);
529    XRES_S(title);
530    XRES_S(icon_name);
531    XRES_S(term_name);
532    XRES_S(tty_modes);
533    XRES_B(hold_screen);
534    XRES_B(utmpInhibit);
535    XRES_B(utmpDisplayId);
536    XRES_B(messages);
537#if OPT_SUNPC_KBD
538    XRES_B(sunKeyboard);
539#endif
540#if OPT_HP_FUNC_KEYS
541    XRES_B(hpFunctionKeys);
542#endif
543#if OPT_SCO_FUNC_KEYS
544    XRES_B(scoFunctionKeys);
545#endif
546#if OPT_SUN_FUNC_KEYS
547    XRES_B(sunFunctionKeys);
548#endif
549#if OPT_INITIAL_ERASE
550    XRES_B(ptyInitialErase);
551    XRES_B(backarrow_is_erase);
552#endif
553    XRES_B(useInsertMode);
554#if OPT_ZICONBEEP
555    XRES_I(zIconBeep);
556#endif
557#if OPT_PTY_HANDSHAKE
558    XRES_B(wait_for_map);
559    XRES_B(ptyHandshake);
560    XRES_B(ptySttySize);
561#endif
562#if OPT_SAME_NAME
563    XRES_B(sameName);
564#endif
565#if OPT_SESSION_MGT
566    XRES_B(sessionMgt);
567#endif
568}
569
570void
571TraceArgv(const char *tag, char **argv)
572{
573    int n = 0;
574
575    TRACE(("%s:\n", tag));
576    while (*argv != 0) {
577	TRACE(("  %d:%s\n", n++, *argv++));
578    }
579}
580
581static char *
582parse_option(char *dst, char *src, int first)
583{
584    char *s;
585
586    if (!strncmp(src, "-/+", 3)) {
587	dst[0] = first;
588	strcpy(dst + 1, src + 3);
589    } else {
590	strcpy(dst, src);
591    }
592    for (s = dst; *s != '\0'; s++) {
593	if (*s == '#' || *s == '%' || *s == 'S') {
594	    s[1] = '\0';
595	} else if (*s == ' ') {
596	    *s = '\0';
597	    break;
598	}
599    }
600    return dst;
601}
602
603static Bool
604same_option(OptionHelp * opt, XrmOptionDescRec * res)
605{
606    char temp[BUFSIZ];
607    return !strcmp(parse_option(temp, opt->opt, res->option[0]), res->option);
608}
609
610static Bool
611standard_option(char *opt)
612{
613    static const char *table[] =
614    {
615	"+rv",
616	"+synchronous",
617	"-background",
618	"-bd",
619	"-bg",
620	"-bordercolor",
621	"-borderwidth",
622	"-bw",
623	"-display",
624	"-fg",
625	"-fn",
626	"-font",
627	"-foreground",
628	"-geometry",
629	"-iconic",
630	"-name",
631	"-reverse",
632	"-rv",
633	"-selectionTimeout",
634	"-synchronous",
635	"-title",
636	"-xnllanguage",
637	"-xrm",
638	"-xtsessionID",
639    };
640    Cardinal n;
641    char temp[BUFSIZ];
642
643    opt = parse_option(temp, opt, '-');
644    for (n = 0; n < XtNumber(table); n++) {
645	if (!strcmp(opt, table[n]))
646	    return True;
647    }
648    return False;
649}
650
651/*
652 * Analyse the options/help messages for inconsistencies.
653 */
654void
655TraceOptions(OptionHelp * options, XrmOptionDescRec * resources, Cardinal res_count)
656{
657    OptionHelp *opt_array = sortedOpts(options, resources, res_count);
658    size_t j, k;
659    XrmOptionDescRec *res_array = sortedOptDescs(resources, res_count);
660    Bool first, found;
661
662    TRACE(("Checking options-tables for inconsistencies:\n"));
663
664#if 0
665    TRACE(("Options listed in help-message:\n"));
666    for (j = 0; options[j].opt != 0; j++)
667	TRACE(("%5d %-28s %s\n", j, opt_array[j].opt, opt_array[j].desc));
668    TRACE(("Options listed in resource-table:\n"));
669    for (j = 0; j < res_count; j++)
670	TRACE(("%5d %-28s %s\n", j, res_array[j].option, res_array[j].specifier));
671#endif
672
673    /* list all options[] not found in resources[] */
674    for (j = 0, first = True; options[j].opt != 0; j++) {
675	found = False;
676	for (k = 0; k < res_count; k++) {
677	    if (same_option(&opt_array[j], &res_array[k])) {
678		found = True;
679		break;
680	    }
681	}
682	if (!found) {
683	    if (first) {
684		TRACE(("Options listed in help, not found in resource list:\n"));
685		first = False;
686	    }
687	    TRACE(("  %-28s%s\n", opt_array[j].opt,
688		   standard_option(opt_array[j].opt) ? " (standard)" : ""));
689	}
690    }
691
692    /* list all resources[] not found in options[] */
693    for (j = 0, first = True; j < res_count; j++) {
694	found = False;
695	for (k = 0; options[k].opt != 0; k++) {
696	    if (same_option(&opt_array[k], &res_array[j])) {
697		found = True;
698		break;
699	    }
700	}
701	if (!found) {
702	    if (first) {
703		TRACE(("Resource list items not found in options-help:\n"));
704		first = False;
705	    }
706	    TRACE(("  %s\n", res_array[j].option));
707	}
708    }
709
710    TRACE(("Resource list items that will be ignored by XtOpenApplication:\n"));
711    for (j = 0; j < res_count; j++) {
712	switch (res_array[j].argKind) {
713	case XrmoptionSkipArg:
714	    TRACE(("  %-28s {param}\n", res_array[j].option));
715	    break;
716	case XrmoptionSkipNArgs:
717	    TRACE(("  %-28s {%ld params}\n", res_array[j].option, (long)
718		   res_array[j].value));
719	    break;
720	case XrmoptionSkipLine:
721	    TRACE(("  %-28s {remainder of line}\n", res_array[j].option));
722	    break;
723	case XrmoptionIsArg:
724	case XrmoptionNoArg:
725	case XrmoptionResArg:
726	case XrmoptionSepArg:
727	case XrmoptionStickyArg:
728	default:
729	    break;
730	}
731    }
732}
733