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