trace.c revision d522f475
1/* $XTermId: trace.c,v 1.83 2007/12/31 20:58:29 tom Exp $ */
2
3/************************************************************
4
5Copyright 1997-2006,2007 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    if (hints->flags & PAspect)
442	TRACE(("   min aspect %d/%d\n", hints->min_aspect.y, hints->min_aspect.y));
443    if (hints->flags & PAspect)
444	TRACE(("   max aspect %d/%d\n", hints->max_aspect.y, hints->max_aspect.y));
445    if (hints->flags & PBaseSize)
446	TRACE(("   base       %d,%d\n", hints->base_height, hints->base_width));
447    if (hints->flags & PWinGravity)
448	TRACE(("   gravity    %d\n", hints->win_gravity));
449}
450
451void
452TraceWMSizeHints(XtermWidget xw)
453{
454    XSizeHints sizehints = xw->hints;
455
456    getXtermSizeHints(xw);
457    TraceSizeHints(&xw->hints);
458    xw->hints = sizehints;
459}
460
461/*
462 * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
463 * our own error-handler.
464 */
465static int
466no_error(Display * dpy GCC_UNUSED, XErrorEvent * event GCC_UNUSED)
467{
468    return 1;
469}
470
471void
472TraceTranslations(const char *name, Widget w)
473{
474    String result;
475    XErrorHandler save = XSetErrorHandler(no_error);
476    XtTranslations xlations;
477    Widget xcelerat;
478
479    TRACE(("TraceTranslations for %s (widget %#lx)\n", name, (long) w));
480    if (w) {
481	XtVaGetValues(w,
482		      XtNtranslations, &xlations,
483		      XtNaccelerators, &xcelerat,
484		      (XtPointer) 0);
485	TRACE(("... xlations %#08lx\n", (long) xlations));
486	TRACE(("... xcelerat %#08lx\n", (long) xcelerat));
487	result = _XtPrintXlations(w, xlations, xcelerat, True);
488	TRACE(("%s\n", result != 0 ? result : "(null)"));
489	if (result)
490	    XFree(result);
491    } else {
492	TRACE(("none (widget is null)\n"));
493    }
494    XSetErrorHandler(save);
495}
496
497#define XRES_S(name) Trace(#name " = %s\n", NonNull(resp->name))
498#define XRES_B(name) Trace(#name " = %s\n", BtoS(resp->name))
499#define XRES_I(name) Trace(#name " = %d\n", resp->name)
500
501void
502TraceXtermResources(void)
503{
504    XTERM_RESOURCE *resp = &resource;
505
506    Trace("XTERM_RESOURCE settings:\n");
507    XRES_S(xterm_name);
508    XRES_S(icon_geometry);
509    XRES_S(title);
510    XRES_S(icon_name);
511    XRES_S(term_name);
512    XRES_S(tty_modes);
513    XRES_B(hold_screen);
514    XRES_B(utmpInhibit);
515    XRES_B(utmpDisplayId);
516    XRES_B(messages);
517#if OPT_SUNPC_KBD
518    XRES_B(sunKeyboard);
519#endif
520#if OPT_HP_FUNC_KEYS
521    XRES_B(hpFunctionKeys);
522#endif
523#if OPT_SCO_FUNC_KEYS
524    XRES_B(scoFunctionKeys);
525#endif
526#if OPT_SUN_FUNC_KEYS
527    XRES_B(sunFunctionKeys);
528#endif
529#if OPT_INITIAL_ERASE
530    XRES_B(ptyInitialErase);
531    XRES_B(backarrow_is_erase);
532#endif
533    XRES_B(useInsertMode);
534#if OPT_ZICONBEEP
535    XRES_I(zIconBeep);
536#endif
537#if OPT_PTY_HANDSHAKE
538    XRES_B(wait_for_map);
539    XRES_B(ptyHandshake);
540    XRES_B(ptySttySize);
541#endif
542#if OPT_SAME_NAME
543    XRES_B(sameName);
544#endif
545#if OPT_SESSION_MGT
546    XRES_B(sessionMgt);
547#endif
548}
549
550void
551TraceArgv(const char *tag, char **argv)
552{
553    int n = 0;
554
555    TRACE(("%s:\n", tag));
556    while (*argv != 0) {
557	TRACE(("  %d:%s\n", n++, *argv++));
558    }
559}
560
561static char *
562parse_option(char *dst, char *src, int first)
563{
564    char *s;
565
566    if (!strncmp(src, "-/+", 3)) {
567	dst[0] = first;
568	strcpy(dst + 1, src + 3);
569    } else {
570	strcpy(dst, src);
571    }
572    for (s = dst; *s != '\0'; s++) {
573	if (*s == '#' || *s == '%' || *s == 'S') {
574	    s[1] = '\0';
575	} else if (*s == ' ') {
576	    *s = '\0';
577	    break;
578	}
579    }
580    return dst;
581}
582
583static Bool
584same_option(OptionHelp * opt, XrmOptionDescRec * res)
585{
586    char temp[BUFSIZ];
587    return !strcmp(parse_option(temp, opt->opt, res->option[0]), res->option);
588}
589
590static Bool
591standard_option(char *opt)
592{
593    static const char *table[] =
594    {
595	"+rv",
596	"+synchronous",
597	"-background",
598	"-bd",
599	"-bg",
600	"-bordercolor",
601	"-borderwidth",
602	"-bw",
603	"-display",
604	"-fg",
605	"-fn",
606	"-font",
607	"-foreground",
608	"-geometry",
609	"-iconic",
610	"-name",
611	"-reverse",
612	"-rv",
613	"-selectionTimeout",
614	"-synchronous",
615	"-title",
616	"-xnllanguage",
617	"-xrm",
618	"-xtsessionID",
619    };
620    Cardinal n;
621    char temp[BUFSIZ];
622
623    opt = parse_option(temp, opt, '-');
624    for (n = 0; n < XtNumber(table); n++) {
625	if (!strcmp(opt, table[n]))
626	    return True;
627    }
628    return False;
629}
630
631/*
632 * Analyse the options/help messages for inconsistencies.
633 */
634void
635TraceOptions(OptionHelp * options, XrmOptionDescRec * resources, Cardinal res_count)
636{
637    OptionHelp *opt_array = sortedOpts(options, resources, res_count);
638    size_t j, k;
639    XrmOptionDescRec *res_array = sortedOptDescs(resources, res_count);
640    Bool first, found;
641
642    TRACE(("Checking options-tables for inconsistencies:\n"));
643
644#if 0
645    TRACE(("Options listed in help-message:\n"));
646    for (j = 0; options[j].opt != 0; j++)
647	TRACE(("%5d %-28s %s\n", j, opt_array[j].opt, opt_array[j].desc));
648    TRACE(("Options listed in resource-table:\n"));
649    for (j = 0; j < res_count; j++)
650	TRACE(("%5d %-28s %s\n", j, res_array[j].option, res_array[j].specifier));
651#endif
652
653    /* list all options[] not found in resources[] */
654    for (j = 0, first = True; options[j].opt != 0; j++) {
655	found = False;
656	for (k = 0; k < res_count; k++) {
657	    if (same_option(&opt_array[j], &res_array[k])) {
658		found = True;
659		break;
660	    }
661	}
662	if (!found) {
663	    if (first) {
664		TRACE(("Options listed in help, not found in resource list:\n"));
665		first = False;
666	    }
667	    TRACE(("  %-28s%s\n", opt_array[j].opt,
668		   standard_option(opt_array[j].opt) ? " (standard)" : ""));
669	}
670    }
671
672    /* list all resources[] not found in options[] */
673    for (j = 0, first = True; j < res_count; j++) {
674	found = False;
675	for (k = 0; options[k].opt != 0; k++) {
676	    if (same_option(&opt_array[k], &res_array[j])) {
677		found = True;
678		break;
679	    }
680	}
681	if (!found) {
682	    if (first) {
683		TRACE(("Resource list items not found in options-help:\n"));
684		first = False;
685	    }
686	    TRACE(("  %s\n", res_array[j].option));
687	}
688    }
689
690    TRACE(("Resource list items that will be ignored by XtOpenApplication:\n"));
691    for (j = 0; j < res_count; j++) {
692	switch (res_array[j].argKind) {
693	case XrmoptionSkipArg:
694	    TRACE(("  %-28s {param}\n", res_array[j].option));
695	    break;
696	case XrmoptionSkipNArgs:
697	    TRACE(("  %-28s {%ld params}\n", res_array[j].option, (long)
698		   res_array[j].value));
699	    break;
700	case XrmoptionSkipLine:
701	    TRACE(("  %-28s {remainder of line}\n", res_array[j].option));
702	    break;
703	case XrmoptionIsArg:
704	case XrmoptionNoArg:
705	case XrmoptionResArg:
706	case XrmoptionSepArg:
707	case XrmoptionStickyArg:
708	default:
709	    break;
710	}
711    }
712}
713