150276Speter/****************************************************************************
2178866Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32166124Srafan *     and: Thomas E. Dickey                        1996-on                 *
3350276Speter ****************************************************************************/
3450276Speter
3550276Speter/*
3650276Speter *	lib_trace.c - Tracing/Debugging routines
37166124Srafan *
38166124Srafan * The _tracef() function is originally from pcurses (by Pavel Curtis) in 1982.
39166124Srafan * pcurses allowed one to enable/disable tracing using traceon() and traceoff()
40166124Srafan * functions.  ncurses provides a trace() function which allows one to
41166124Srafan * selectively enable or disable several tracing features.
4250276Speter */
4350276Speter
4450276Speter#include <curses.priv.h>
4550276Speter#include <tic.h>
4650276Speter
4750276Speter#include <ctype.h>
4850276Speter
49184989SrafanMODULE_ID("$Id: lib_trace.c,v 1.71 2008/08/23 18:04:29 tom Exp $")
5062449Speter
51166124SrafanNCURSES_EXPORT_VAR(unsigned) _nc_tracing = 0; /* always define this */
5250276Speter
5350276Speter#ifdef TRACE
54174993Srafan
55174993Srafan#if USE_REENTRANT
56174993SrafanNCURSES_EXPORT(const char *)
57174993SrafanNCURSES_PUBLIC_VAR(_nc_tputs_trace) (void)
58174993Srafan{
59174993Srafan    return SP ? SP->_tputs_trace : _nc_prescreen._tputs_trace;
60174993Srafan}
61174993SrafanNCURSES_EXPORT(long)
62174993SrafanNCURSES_PUBLIC_VAR(_nc_outchars) (void)
63174993Srafan{
64174993Srafan    return SP ? SP->_outchars : _nc_prescreen._outchars;
65174993Srafan}
66174993SrafanNCURSES_EXPORT(void)
67174993Srafan_nc_set_tputs_trace(const char *s)
68174993Srafan{
69174993Srafan    if (SP)
70174993Srafan	SP->_tputs_trace = s;
71174993Srafan    else
72174993Srafan	_nc_prescreen._tputs_trace = s;
73174993Srafan}
74174993SrafanNCURSES_EXPORT(void)
75174993Srafan_nc_count_outchars(long increment)
76174993Srafan{
77174993Srafan    if (SP)
78174993Srafan	SP->_outchars += increment;
79174993Srafan    else
80174993Srafan	_nc_prescreen._outchars += increment;
81174993Srafan}
82174993Srafan#else
83166124SrafanNCURSES_EXPORT_VAR(const char *) _nc_tputs_trace = "";
84166124SrafanNCURSES_EXPORT_VAR(long) _nc_outchars = 0;
85174993Srafan#endif
8650276Speter
87174993Srafan#define TraceFP		_nc_globals.trace_fp
88174993Srafan#define TracePath	_nc_globals.trace_fname
89174993Srafan#define TraceLevel	_nc_globals.trace_level
9050276Speter
9176726SpeterNCURSES_EXPORT(void)
92166124Srafantrace(const unsigned int tracelevel)
9350276Speter{
94174993Srafan    if ((TraceFP == 0) && tracelevel) {
95174993Srafan	const char *mode = _nc_globals.init_trace ? "ab" : "wb";
9650276Speter
97174993Srafan	if (TracePath[0] == '\0') {
98184989Srafan	    int size = sizeof(TracePath) - 12;
99184989Srafan	    if (getcwd(TracePath, size) == 0) {
100166124Srafan		perror("curses: Can't get working directory");
101166124Srafan		exit(EXIT_FAILURE);
102166124Srafan	    }
103184989Srafan	    TracePath[size] = '\0';
104184989Srafan	    assert(strlen(TracePath) <= size);
105174993Srafan	    strcat(TracePath, "/trace");
106174993Srafan	    if (_nc_is_dir_path(TracePath)) {
107174993Srafan		strcat(TracePath, ".log");
108174993Srafan	    }
109166124Srafan	}
110166124Srafan
111174993Srafan	_nc_globals.init_trace = TRUE;
11297049Speter	_nc_tracing = tracelevel;
113174993Srafan	if (_nc_access(TracePath, W_OK) < 0
114174993Srafan	    || (TraceFP = fopen(TracePath, mode)) == 0) {
115166124Srafan	    perror("curses: Can't open 'trace' file");
11662449Speter	    exit(EXIT_FAILURE);
11750276Speter	}
11862449Speter	/* Try to set line-buffered mode, or (failing that) unbuffered,
11962449Speter	 * so that the trace-output gets flushed automatically at the
12062449Speter	 * end of each line.  This is useful in case the program dies.
12162449Speter	 */
12262449Speter#if HAVE_SETVBUF		/* ANSI */
123174993Srafan	(void) setvbuf(TraceFP, (char *) 0, _IOLBF, 0);
12462449Speter#elif HAVE_SETBUF		/* POSIX */
125174993Srafan	(void) setbuffer(TraceFP, (char *) 0);
12650276Speter#endif
127166124Srafan	_tracef("TRACING NCURSES version %s.%d (tracelevel=%#x)",
128166124Srafan		NCURSES_VERSION,
129166124Srafan		NCURSES_VERSION_PATCH,
130166124Srafan		tracelevel);
131166124Srafan    } else if (tracelevel == 0) {
132174993Srafan	if (TraceFP != 0) {
133174993Srafan	    fclose(TraceFP);
134174993Srafan	    TraceFP = 0;
135166124Srafan	}
136166124Srafan	_nc_tracing = tracelevel;
13797049Speter    } else if (_nc_tracing != tracelevel) {
13897049Speter	_nc_tracing = tracelevel;
13997049Speter	_tracef("tracelevel=%#x", tracelevel);
14062449Speter    }
14150276Speter}
14250276Speter
143174993Srafanstatic void
144174993Srafan_nc_va_tracef(const char *fmt, va_list ap)
14550276Speter{
14662449Speter    static const char Called[] = T_CALLED("");
14762449Speter    static const char Return[] = T_RETURN("");
148174993Srafan
14962449Speter    bool before = FALSE;
15062449Speter    bool after = FALSE;
151166124Srafan    unsigned doit = _nc_tracing;
15262449Speter    int save_err = errno;
15350276Speter
15462449Speter    if (strlen(fmt) >= sizeof(Called) - 1) {
15562449Speter	if (!strncmp(fmt, Called, sizeof(Called) - 1)) {
15662449Speter	    before = TRUE;
157174993Srafan	    TraceLevel++;
15862449Speter	} else if (!strncmp(fmt, Return, sizeof(Return) - 1)) {
15962449Speter	    after = TRUE;
16050276Speter	}
16162449Speter	if (before || after) {
162174993Srafan	    if ((TraceLevel <= 1)
16362449Speter		|| (doit & TRACE_ICALLS) != 0)
16462449Speter		doit &= (TRACE_CALLS | TRACE_CCALLS);
16562449Speter	    else
16662449Speter		doit = 0;
16762449Speter	}
16862449Speter    }
16950276Speter
17062449Speter    if (doit != 0) {
171174993Srafan	if (TraceFP == 0)
172174993Srafan	    TraceFP = stderr;
173178866Srafan#ifdef USE_PTHREADS
174178866Srafan	/*
175178866Srafan	 * TRACE_ICALLS is "really" needed to show normal use with threaded
176178866Srafan	 * applications, since anything can be running during a napms(),
177178866Srafan	 * making it appear in the hierarchical trace as it other functions
178178866Srafan	 * are being called.
179178866Srafan	 *
180178866Srafan	 * Rather than add the complication of a per-thread stack, just
181178866Srafan	 * show the thread-id in each line of the trace.
182178866Srafan	 */
183184989Srafan# if USE_WEAK_SYMBOLS
184184989Srafan	if ((pthread_self))
185184989Srafan# endif
186184989Srafan	    fprintf(TraceFP, "%#lx:", (long) (void *) pthread_self());
187178866Srafan#endif
18862449Speter	if (before || after) {
18962449Speter	    int n;
190174993Srafan	    for (n = 1; n < TraceLevel; n++)
191174993Srafan		fputs("+ ", TraceFP);
19250276Speter	}
193174993Srafan	vfprintf(TraceFP, fmt, ap);
194174993Srafan	fputc('\n', TraceFP);
195174993Srafan	fflush(TraceFP);
19662449Speter    }
19750276Speter
198174993Srafan    if (after && TraceLevel)
199174993Srafan	TraceLevel--;
200174993Srafan
20162449Speter    errno = save_err;
20250276Speter}
20350276Speter
204174993SrafanNCURSES_EXPORT(void)
205174993Srafan_tracef(const char *fmt,...)
206174993Srafan{
207174993Srafan    va_list ap;
208174993Srafan
209174993Srafan    va_start(ap, fmt);
210174993Srafan    _nc_va_tracef(fmt, ap);
211174993Srafan    va_end(ap);
212174993Srafan}
213174993Srafan
214166124Srafan/* Trace 'bool' return-values */
215166124SrafanNCURSES_EXPORT(NCURSES_BOOL)
216166124Srafan_nc_retrace_bool(NCURSES_BOOL code)
217166124Srafan{
218166124Srafan    T((T_RETURN("%s"), code ? "TRUE" : "FALSE"));
219166124Srafan    return code;
220166124Srafan}
221166124Srafan
22250276Speter/* Trace 'int' return-values */
22376726SpeterNCURSES_EXPORT(int)
22462449Speter_nc_retrace_int(int code)
22550276Speter{
22662449Speter    T((T_RETURN("%d"), code));
22762449Speter    return code;
22850276Speter}
22950276Speter
230166124Srafan/* Trace 'unsigned' return-values */
231166124SrafanNCURSES_EXPORT(unsigned)
232166124Srafan_nc_retrace_unsigned(unsigned code)
233166124Srafan{
234166124Srafan    T((T_RETURN("%#x"), code));
235166124Srafan    return code;
236166124Srafan}
237166124Srafan
23850276Speter/* Trace 'char*' return-values */
23976726SpeterNCURSES_EXPORT(char *)
24062449Speter_nc_retrace_ptr(char *code)
24150276Speter{
24262449Speter    T((T_RETURN("%s"), _nc_visbuf(code)));
24362449Speter    return code;
24450276Speter}
24550276Speter
246166124Srafan/* Trace 'const char*' return-values */
247166124SrafanNCURSES_EXPORT(const char *)
248166124Srafan_nc_retrace_cptr(const char *code)
249166124Srafan{
250166124Srafan    T((T_RETURN("%s"), _nc_visbuf(code)));
251166124Srafan    return code;
252166124Srafan}
253166124Srafan
254166124Srafan/* Trace 'NCURSES_CONST void*' return-values */
255166124SrafanNCURSES_EXPORT(NCURSES_CONST void *)
256166124Srafan_nc_retrace_cvoid_ptr(NCURSES_CONST void *code)
257166124Srafan{
258166124Srafan    T((T_RETURN("%p"), code));
259166124Srafan    return code;
260166124Srafan}
261166124Srafan
262166124Srafan/* Trace 'void*' return-values */
263166124SrafanNCURSES_EXPORT(void *)
264166124Srafan_nc_retrace_void_ptr(void *code)
265166124Srafan{
266166124Srafan    T((T_RETURN("%p"), code));
267166124Srafan    return code;
268166124Srafan}
269166124Srafan
27097049Speter/* Trace 'SCREEN *' return-values */
27197049SpeterNCURSES_EXPORT(SCREEN *)
272166124Srafan_nc_retrace_sp(SCREEN *code)
27397049Speter{
27497049Speter    T((T_RETURN("%p"), code));
27597049Speter    return code;
27697049Speter}
27797049Speter
27850276Speter/* Trace 'WINDOW *' return-values */
27976726SpeterNCURSES_EXPORT(WINDOW *)
28062449Speter_nc_retrace_win(WINDOW *code)
28150276Speter{
28262449Speter    T((T_RETURN("%p"), code));
28362449Speter    return code;
28450276Speter}
285174993Srafan
286174993Srafan#if USE_REENTRANT
287174993Srafan/*
288174993Srafan * Check if the given trace-mask is enabled.
289174993Srafan *
290174993Srafan * This function may be called from within one of the functions that fills
291174993Srafan * in parameters for _tracef(), but in that case we do not want to lock the
292174993Srafan * mutex, since it is already locked.
293174993Srafan */
294174993SrafanNCURSES_EXPORT(int)
295174993Srafan_nc_use_tracef(unsigned mask)
296174993Srafan{
297174993Srafan    bool result = FALSE;
298174993Srafan
299174993Srafan    _nc_lock_global(tst_tracef);
300174993Srafan    if (!_nc_globals.nested_tracef++) {
301184989Srafan	if ((result = (_nc_tracing & (mask))) != 0
302184989Srafan	    && _nc_try_global(tracef) == 0) {
303174993Srafan	    /* we will call _nc_locked_tracef(), no nesting so far */
304174993Srafan	} else {
305174993Srafan	    /* we will not call _nc_locked_tracef() */
306174993Srafan	    _nc_globals.nested_tracef = 0;
307174993Srafan	}
308174993Srafan    } else {
309174993Srafan	/* we may call _nc_locked_tracef(), but with nested_tracef > 0 */
310174993Srafan	result = (_nc_tracing & (mask));
311174993Srafan    }
312174993Srafan    _nc_unlock_global(tst_tracef);
313174993Srafan    return result;
314174993Srafan}
315174993Srafan
316174993Srafan/*
317174993Srafan * We call this if _nc_use_tracef() returns true, which means we must unlock
318174993Srafan * the tracef mutex.
319174993Srafan */
320174993SrafanNCURSES_EXPORT(void)
321174993Srafan_nc_locked_tracef(const char *fmt,...)
322174993Srafan{
323174993Srafan    va_list ap;
324174993Srafan
325174993Srafan    va_start(ap, fmt);
326174993Srafan    _nc_va_tracef(fmt, ap);
327174993Srafan    va_end(ap);
328174993Srafan
329174993Srafan    if (--(_nc_globals.nested_tracef) == 0)
330174993Srafan	_nc_unlock_global(tracef);
331174993Srafan}
332174993Srafan#endif /* USE_REENTRANT */
333174993Srafan
33450276Speter#endif /* TRACE */
335