xref: /freebsd/contrib/dialog/trace.c (revision a96ef4501919d7ac08e94e98dc34b0bdd744802b)
1 /*
2  *  $Id: trace.c,v 1.33 2020/11/23 23:32:43 tom Exp $
3  *
4  *  trace.c -- implements screen-dump and keystroke-logging
5  *
6  *  Copyright 2007-2019,2020	Thomas E. Dickey
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License, version 2.1
10  *  as published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful, but
13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this program; if not, write to
19  *	Free Software Foundation, Inc.
20  *	51 Franklin St., Fifth Floor
21  *	Boston, MA 02110, USA.
22  */
23 
24 #include <dialog.h>
25 
26 #ifdef HAVE_DLG_TRACE
27 
28 #ifdef NEED_WCHAR_H
29 #include <wchar.h>
30 #endif
31 
32 #include <dlg_keys.h>
33 #include <time.h>
34 
35 #define myFP dialog_state.trace_output
36 
37 static void
dlg_trace_time(const char * tag)38 dlg_trace_time(const char *tag)
39 {
40     time_t now = time((time_t *) 0);
41     fprintf(myFP, "%s %s", tag, ctime(&now));
42 }
43 
44 void
dlg_trace_msg(const char * fmt,...)45 dlg_trace_msg(const char *fmt, ...)
46 {
47     if (myFP != 0) {
48 	va_list ap;
49 	va_start(ap, fmt);
50 	vfprintf(myFP, fmt, ap);
51 	va_end(ap);
52 	fflush(myFP);
53     }
54 }
55 
56 void
dlg_trace_va_msg(const char * fmt,va_list ap)57 dlg_trace_va_msg(const char *fmt, va_list ap)
58 {
59     if (myFP != 0) {
60 	vfprintf(myFP, fmt, ap);
61 	fflush(myFP);
62     }
63 }
64 
65 void
dlg_trace_2s(const char * name,const char * value)66 dlg_trace_2s(const char *name, const char *value)
67 {
68     bool first = TRUE;
69     int left, right = 0;
70 
71     if (value == 0)
72 	value = "<NULL>";
73 
74     while (value[right] != '\0') {
75 	const char *next;
76 
77 	value += right;
78 	if ((next = strchr(value, '\n')) != 0) {
79 	    left = (int) (next - value);
80 	    right = left + 1;
81 	} else {
82 	    left = (int) strlen(value);
83 	    right = left;
84 	}
85 	if (first) {
86 	    first = FALSE;
87 	    dlg_trace_msg("#%14s = %.*s\n", name, left, value);
88 	} else {
89 	    dlg_trace_msg("#+%13s%.*s\n", " ", left, value);
90 	}
91     }
92 }
93 
94 void
dlg_trace_2n(const char * name,int value)95 dlg_trace_2n(const char *name, int value)
96 {
97     dlg_trace_msg("#%14s = %d\n", name, value);
98 }
99 
100 void
dlg_trace_win(WINDOW * win)101 dlg_trace_win(WINDOW *win)
102 {
103     if (myFP != 0) {
104 	WINDOW *top = wgetparent(win);
105 
106 	while (top != 0 && top != stdscr) {
107 	    win = top;
108 	    top = wgetparent(win);
109 	}
110 
111 	if (win != 0) {
112 	    int rc = getmaxy(win);
113 	    int cc = getmaxx(win);
114 	    chtype ch, c2;
115 	    int y, x;
116 	    int j, k;
117 
118 	    fprintf(myFP, "window %dx%d at %d,%d\n",
119 		    rc, cc, getbegy(win), getbegx(win));
120 
121 	    getyx(win, y, x);
122 	    for (j = 0; j < rc; ++j) {
123 		fprintf(myFP, "%3d:", j);
124 		for (k = 0; k < cc; ++k) {
125 #ifdef USE_WIDE_CURSES
126 		    char buffer[80];
127 
128 		    ch = mvwinch(win, j, k) & (A_CHARTEXT | A_ALTCHARSET);
129 		    if (ch & A_ALTCHARSET) {
130 			c2 = dlg_asciibox(ch);
131 			if (c2 != 0) {
132 			    ch = c2;
133 			}
134 			buffer[0] = (char) ch;
135 			buffer[1] = '\0';
136 		    } else {
137 			cchar_t cch;
138 			const wchar_t *uc;
139 
140 			if (win_wch(win, &cch) == ERR
141 			    || (uc = wunctrl((&cch))) == 0
142 			    || uc[1] != 0
143 			    || wcwidth(uc[0]) <= 0) {
144 			    buffer[0] = '.';
145 			    buffer[1] = '\0';
146 			} else {
147 			    mbstate_t state;
148 			    const wchar_t *ucp = uc;
149 
150 			    memset(&state, 0, sizeof(state));
151 			    wcsrtombs(buffer, &ucp, sizeof(buffer), &state);
152 			    k += wcwidth(uc[0]) - 1;
153 			}
154 		    }
155 		    fputs(buffer, myFP);
156 #else
157 		    ch = mvwinch(win, j, k) & (A_CHARTEXT | A_ALTCHARSET);
158 		    c2 = dlg_asciibox(ch);
159 		    if (c2 != 0) {
160 			ch = c2;
161 		    } else if (unctrl(ch) == 0 || strlen(unctrl(ch)) > 1) {
162 			ch = '.';
163 		    }
164 		    fputc((int) (ch & 0xff), myFP);
165 #endif
166 		}
167 		fputc('\n', myFP);
168 	    }
169 	    wmove(win, y, x);
170 	    fflush(myFP);
171 	}
172     }
173 }
174 
175 void
dlg_trace_chr(int ch,int fkey)176 dlg_trace_chr(int ch, int fkey)
177 {
178     static int last_err = 0;
179 
180     /*
181      * Do not bother to trace ERR's indefinitely, since those are usually due
182      * to relatively short polling timeouts.
183      */
184     if (last_err && !fkey && ch == ERR) {
185 	++last_err;
186     } else if (myFP != 0) {
187 	const char *fkey_name = "?";
188 
189 	if (last_err) {
190 	    fprintf(myFP, "skipped %d ERR's\n", last_err);
191 	    last_err = 0;
192 	}
193 
194 	if (fkey) {
195 	    if (fkey > KEY_MAX || (fkey_name = keyname(fkey)) == 0) {
196 #define CASE(name) case name: fkey_name = #name; break
197 		switch ((DLG_KEYS_ENUM) fkey) {
198 		    CASE(DLGK_MIN);
199 		    CASE(DLGK_OK);
200 		    CASE(DLGK_CANCEL);
201 		    CASE(DLGK_EXTRA);
202 		    CASE(DLGK_HELP);
203 		    CASE(DLGK_ESC);
204 		    CASE(DLGK_PAGE_FIRST);
205 		    CASE(DLGK_PAGE_LAST);
206 		    CASE(DLGK_PAGE_NEXT);
207 		    CASE(DLGK_PAGE_PREV);
208 		    CASE(DLGK_ITEM_FIRST);
209 		    CASE(DLGK_ITEM_LAST);
210 		    CASE(DLGK_ITEM_NEXT);
211 		    CASE(DLGK_ITEM_PREV);
212 		    CASE(DLGK_FIELD_FIRST);
213 		    CASE(DLGK_FIELD_LAST);
214 		    CASE(DLGK_FIELD_NEXT);
215 		    CASE(DLGK_FIELD_PREV);
216 		    CASE(DLGK_FORM_FIRST);
217 		    CASE(DLGK_FORM_LAST);
218 		    CASE(DLGK_FORM_NEXT);
219 		    CASE(DLGK_FORM_PREV);
220 		    CASE(DLGK_GRID_UP);
221 		    CASE(DLGK_GRID_DOWN);
222 		    CASE(DLGK_GRID_LEFT);
223 		    CASE(DLGK_GRID_RIGHT);
224 		    CASE(DLGK_DELETE_LEFT);
225 		    CASE(DLGK_DELETE_RIGHT);
226 		    CASE(DLGK_DELETE_ALL);
227 		    CASE(DLGK_ENTER);
228 		    CASE(DLGK_BEGIN);
229 		    CASE(DLGK_FINAL);
230 		    CASE(DLGK_SELECT);
231 		    CASE(DLGK_HELPFILE);
232 		    CASE(DLGK_TRACE);
233 		    CASE(DLGK_TOGGLE);
234 		    CASE(DLGK_LEAVE);
235 		}
236 	    }
237 	} else if (ch == ERR) {
238 	    fkey_name = "ERR";
239 	    last_err = 1;
240 	} else {
241 	    fkey_name = unctrl((chtype) ch);
242 	    if (fkey_name == 0)
243 		fkey_name = "UNKNOWN";
244 	}
245 	if (ch >= 0) {
246 	    fprintf(myFP, "chr %s (ch=%#x, fkey=%d)\n", fkey_name, ch, fkey);
247 	} else {
248 	    fprintf(myFP, "chr %s (ch=%d, fkey=%d)\n", fkey_name, ch, fkey);
249 	}
250 	fflush(myFP);
251     }
252 }
253 
254 void
dlg_trace(const char * fname)255 dlg_trace(const char *fname)
256 {
257     if (fname != 0) {
258 	if (myFP == 0) {
259 	    myFP = fopen(fname, "a");
260 	    if (myFP != 0) {
261 		dlg_trace_time("## opened at");
262 		DLG_TRACE(("## dialog %s\n", dialog_version()));
263 		DLG_TRACE(("## vile: confmode\n"));
264 	    }
265 	}
266     } else if (myFP != 0) {
267 	dlg_trace_time("## closed at");
268 	fclose(myFP);
269 	myFP = 0;
270     }
271 }
272 #else
273 #undef dlg_trace
274 extern void dlg_trace(const char *);
275 void
dlg_trace(const char * fname)276 dlg_trace(const char *fname)
277 {
278     (void) fname;
279 }
280 #endif
281