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