1 /******************************************************************************
2  *									      *
3  *	                           N O T I C E				      *
4  *									      *
5  *	              Copyright Abandoned, 1987, Fred Fish		      *
6  *									      *
7  *									      *
8  *	This previously copyrighted work has been placed into the  public     *
9  *	domain  by  the  author  and  may be freely used for any purpose,     *
10  *	private or commercial.						      *
11  *									      *
12  *	Because of the number of inquiries I was receiving about the  use     *
13  *	of this product in commercially developed works I have decided to     *
14  *	simply make it public domain to further its unrestricted use.   I     *
15  *	specifically  would  be  most happy to see this material become a     *
16  *	part of the standard Unix distributions by AT&T and the  Berkeley     *
17  *	Computer  Science  Research Group, and a standard part of the GNU     *
18  *	system from the Free Software Foundation.			      *
19  *									      *
20  *	I would appreciate it, as a courtesy, if this notice is  left  in     *
21  *	all copies and derivative works.  Thank you.			      *
22  *									      *
23  *	The author makes no warranty of any kind  with  respect  to  this     *
24  *	product  and  explicitly disclaims any implied warranties of mer-     *
25  *	chantability or fitness for any particular purpose.		      *
26  *									      *
27  ******************************************************************************
28  */
29 
30 
31 /*
32  *  FILE
33  *
34  *	dbug.c   runtime support routines for dbug package
35  *
36  *  SCCS
37  *
38  *	@(#)dbug.c	1.19 9/5/87
39  *
40  *  DESCRIPTION
41  *
42  *	These are the runtime support routines for the dbug package.
43  *	The dbug package has two main components; the user include
44  *	file containing various macro definitions, and the runtime
45  *	support routines which are called from the macro expansions.
46  *
47  *	Externally visible functions in the runtime support module
48  *	use the naming convention pattern "_db_xx...xx_", thus
49  *	they are unlikely to collide with user defined function names.
50  *
51  *  AUTHOR(S)
52  *
53  *	Fred Fish		(base code)
54  *	(Currently at Motorola Computer Division, Tempe, Az.)
55  *	hao!noao!mcdsun!fnf
56  *	(602) 438-3614
57  *
58  *	Binayak Banerjee	(profiling enhancements)
59  *	seismo!bpa!sjuvax!bbanerje
60  */
61 
62 
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #ifdef amiga
67 #define AMIGA
68 #endif
69 
70 #ifdef AMIGA
71 #define HZ (50)			/* Probably in some header somewhere */
72 #endif
73 
74 /*
75  *	Manifest constants that should not require any changes.
76  */
77 
78 #define FALSE		0	/* Boolean FALSE */
79 #define TRUE		1	/* Boolean TRUE */
80 #define EOS		'\000'	/* End Of String marker */
81 
82 /*
83  *	Manifest constants which may be "tuned" if desired.
84  */
85 
86 #define PRINTBUF	1024	/* Print buffer size */
87 #define INDENT		4	/* Indentation per trace level */
88 #define MAXDEPTH	200	/* Maximum trace depth default */
89 
90 /*
91  *	The following flags are used to determine which
92  *	capabilities the user has enabled with the state
93  *	push macro.
94  */
95 
96 #define TRACE_ON	000001	/* Trace enabled */
97 #define DEBUG_ON	000002	/* Debug enabled */
98 #define FILE_ON 	000004	/* File name print enabled */
99 #define LINE_ON		000010	/* Line number print enabled */
100 #define DEPTH_ON	000020	/* Function nest level print enabled */
101 #define PROCESS_ON	000040	/* Process name print enabled */
102 #define NUMBER_ON	000100	/* Number each line of output */
103 #define PROFILE_ON	000200	/* Print out profiling code */
104 
105 #define TRACING (stack -> flags & TRACE_ON)
106 #define DEBUGGING (stack -> flags & DEBUG_ON)
107 #define PROFILING (stack -> flags & PROFILE_ON)
108 #define STREQ(a,b) (strcmp(a,b) == 0)
109 
110 /*
111  *	Typedefs to make things more obvious.
112  */
113 
114 #define VOID void		/* Can't use typedef for most compilers */
115 typedef int BOOLEAN;
116 
117 /*
118  *	Make it easy to change storage classes if necessary.
119  */
120 
121 #define LOCAL static		/* Names not needed by outside world */
122 #define IMPORT extern		/* Names defined externally */
123 #define EXPORT			/* Allocated here, available globally */
124 #define AUTO auto		/* Names to be allocated on stack */
125 #define REGISTER register	/* Names to be placed in registers */
126 
127 /*
128  *	The following define is for the variable arguments kluge, see
129  *	the comments in _db_doprnt_().
130  *
131  *	Also note that the longer this list, the less prone to failing
132  *	on long argument lists, but the more stuff that must be moved
133  *	around for each call to the runtime support routines.  The
134  *	length may really be critical if the machine convention is
135  *	to pass arguments in registers.
136  *
137  *	Note that the default define allows up to 16 integral arguments,
138  *	or 8 floating point arguments (doubles), on most machines.
139  *
140  *	Someday this may be replaced with true varargs support, when
141  *	ANSI C has had time to take root.
142  */
143 
144 #define ARGLIST a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15
145 
146 /*
147  * The default file for profiling.  Could also add another flag
148  * (G?) which allowed the user to specify this.
149  */
150 
151 #define PROF_FILE	"dbugmon.out"
152 
153 /*
154  *	Variables which are available externally but should only
155  *	be accessed via the macro package facilities.
156  */
157 
158 EXPORT FILE *_db_fp_ = (FILE *)0;	/* Output stream, default is set to
159 					 * stderr later */
160 EXPORT FILE *_db_pfp_ = (FILE *)0;	/* Profile stream, 'dbugmon.out' */
161 EXPORT char *_db_process_ = "dbug";	/* Pointer to process name; argv[0] */
162 EXPORT BOOLEAN _db_on_ = FALSE;		/* TRUE if debugging currently on */
163 EXPORT BOOLEAN _db_pon_ = FALSE;	/* TRUE if debugging currently on */
164 
165 /*
166  *	Externally supplied functions.
167  */
168 
169 /* Disable the manual definitions, if something is missing use #include's! */
170 #if 0
171 
172 #ifdef unix			/* Only needed for unix */
173 IMPORT VOID perror ();		/* Print system/library error */
174 IMPORT int chown ();		/* Change owner of a file */
175 IMPORT int getgid ();		/* Get real group id */
176 IMPORT int getuid ();		/* Get real user id */
177 IMPORT int access ();		/* Test file for access */
178 #else
179 #if !(AMIGA || LATTICE || __TURBOC__)
180 LOCAL VOID perror ();		/* Fake system/library error print routine */
181 #endif
182 #endif
183 
184 # if BSD4_3 || sun
185 IMPORT int getrusage ();
186 #endif
187 
188 IMPORT int atoi ();		/* Convert ascii to integer */
189 IMPORT VOID exit ();		/* Terminate execution */
190 IMPORT int fclose ();		/* Close a stream */
191 IMPORT FILE *fopen ();		/* Open a stream */
192 #if !defined(__BORLANDC__)
193 IMPORT int fprintf ();		/* Formatted print on file */
194 #endif
195 IMPORT VOID free ();
196 IMPORT char *malloc ();		/* Allocate memory */
197 IMPORT int strcmp ();		/* Compare strings */
198 IMPORT char *strcpy ();		/* Copy strings around */
199 IMPORT int strlen ();		/* Find length of string */
200 
201 #ifndef fflush			/* This is sometimes a macro */
202 IMPORT int fflush ();		/* Flush output for stream */
203 #endif
204 
205 #endif
206 
207 
208 /*
209  *	The user may specify a list of functions to trace or
210  *	debug.  These lists are kept in a linear linked list,
211  *	a very simple implementation.
212  */
213 
214 struct link {
215     char *string;		/* Pointer to link's contents */
216     struct link *next_link;	/* Pointer to the next link */
217 };
218 
219 
220 /*
221  *	Debugging states can be pushed or popped off of a
222  *	stack which is implemented as a linked list.  Note
223  *	that the head of the list is the current state and the
224  *	stack is pushed by adding a new state to the head of the
225  *	list or popped by removing the first link.
226  */
227 
228 struct state {
229     int flags;				/* Current state flags */
230     int maxdepth;			/* Current maximum trace depth */
231     unsigned int delay;			/* Delay after each output line */
232     int level;				/* Current function nesting level */
233     FILE *out_file;			/* Current output stream */
234     FILE *prof_file;			/* Current profiling stream */
235     struct link *functions;		/* List of functions */
236     struct link *p_functions;		/* List of profiled functions */
237     struct link *keywords;		/* List of debug keywords */
238     struct link *processes;		/* List of process names */
239     struct state *next_state;		/* Next state in the list */
240 };
241 
242 LOCAL struct state *stack = NULL;	/* Linked list of stacked states */
243 
244 /*
245  *	Local variables not seen by user.
246  */
247 
248 LOCAL int lineno = 0;		/* Current debugger output line number */
249 LOCAL char *func = "?func";	/* Name of current user function */
250 LOCAL char *file = "?file";	/* Name of current user file */
251 LOCAL BOOLEAN init_done = FALSE;/* Set to TRUE when initialization done */
252 
253 /*#if unix || AMIGA || M_I86*/
254 LOCAL int jmplevel;		/* Remember nesting level at setjmp () */
255 LOCAL char *jmpfunc;		/* Remember current function for setjmp */
256 LOCAL char *jmpfile;		/* Remember current file for setjmp */
257 /*#endif*/
258 
259 LOCAL struct link *ListParse ();/* Parse a debug command string */
260 LOCAL char *StrDup ();		/* Make a fresh copy of a string */
261 LOCAL VOID OpenFile ();		/* Open debug output stream */
262 LOCAL VOID OpenProfile ();	/* Open profile output stream */
263 LOCAL VOID CloseFile ();	/* Close debug output stream */
264 LOCAL VOID PushState ();	/* Push current debug state */
265 LOCAL VOID ChangeOwner ();	/* Change file owner and group */
266 LOCAL BOOLEAN DoTrace ();	/* Test for tracing enabled */
267 LOCAL BOOLEAN Writable ();	/* Test to see if file is writable */
268 LOCAL unsigned long Clock ();	/* Return current user time (ms) */
269 LOCAL char *DbugMalloc ();	/* Allocate memory for runtime support */
270 LOCAL char *BaseName ();	/* Remove leading pathname components */
271 LOCAL VOID DoPrefix ();		/* Print debugger line prefix */
272 LOCAL VOID FreeList ();		/* Free memory from linked list */
273 LOCAL VOID Indent ();		/* Indent line to specified indent */
274 LOCAL int DelayArg (int value); /* Convert D flag argument */
275 LOCAL BOOLEAN DoProfile ();	/* Check if profiling is enabled */
276 
277 				/* Supplied in Sys V runtime environ */
278 LOCAL char *Strtok ();		/* Break string into tokens */
279 LOCAL char *Strrchr ();		/* Find last occurrence of char */
280 
281 /*
282  *	The following local variables are used to hold the state information
283  *	between the call to _db_pargs_() and _db_doprnt_(), during
284  *	expansion of the DBUG_PRINT macro.  This is the only macro
285  *	that currently uses these variables.  The DBUG_PRINT macro
286  *	and the new _db_doprnt_() routine replace the older DBUG_N macros
287  *	and their corresponding runtime support routine _db_printf_().
288  *
289  *	These variables are currently used only by _db_pargs_() and
290  *	_db_doprnt_().
291  */
292 
293 LOCAL int u_line = 0;		/* User source code line number */
294 LOCAL char *u_keyword = "?";	/* Keyword for current macro */
295 
296 /*
297  *	Miscellaneous printf format strings.
298  */
299 
300 #define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
301 #define ERR_OPEN "%s: can't open debug output stream \"%s\": "
302 #define ERR_CLOSE "%s: can't close debug file: "
303 #define ERR_ABORT "%s: debugger aborting because %s\n"
304 #define ERR_CHOWN "%s: can't change owner/group of \"%s\": "
305 #define ERR_PRINTF "%s: obsolete object file for '%s', please recompile!\n"
306 
307 /*
308  *	Macros and defines for testing file accessibility under UNIX.
309  */
310 
311 #ifdef unix
312 #  define A_EXISTS	00		/* Test for file existance */
313 #  define A_EXECUTE	01		/* Test for execute permission */
314 #  define A_WRITE	02		/* Test for write access */
315 #  define A_READ	03		/* Test for read access */
316 #  define EXISTS(pathname) (access (pathname, A_EXISTS) == 0)
317 #  define WRITABLE(pathname) (access (pathname, A_WRITE) == 0)
318 #else
319 #  define EXISTS(pathname) (FALSE)	/* Assume no existance */
320 #endif
321 
322 /*
323  *	Translate some calls among different systems.
324  */
325 
326 #ifdef unix
327 # define XDelay sleep
328 IMPORT unsigned int sleep ();	/* Pause for given number of seconds */
329 #endif
330 
331 #ifdef AMIGA
332 IMPORT int XDelay ();		/* Pause for given number of ticks */
333 #endif
334 
335 
336 /*
337  *  FUNCTION
338  *
339  *	_db_push_    push current debugger state and set up new one
340  *
341  *  SYNOPSIS
342  *
343  *	VOID _db_push_ (control)
344  *	char *control;
345  *
346  *  DESCRIPTION
347  *
348  *	Given pointer to a debug control string in "control", pushes
349  *	the current debug state, parses the control string, and sets
350  *	up a new debug state.
351  *
352  *	The only attribute of the new state inherited from the previous
353  *	state is the current function nesting level.  This can be
354  *	overridden by using the "r" flag in the control string.
355  *
356  *	The debug control string is a sequence of colon separated fields
357  *	as follows:
358  *
359  *		<field_1>:<field_2>:...:<field_N>
360  *
361  *	Each field consists of a mandatory flag character followed by
362  *	an optional "," and comma separated list of modifiers:
363  *
364  *		flag[,modifier,modifier,...,modifier]
365  *
366  *	The currently recognized flag characters are:
367  *
368  *		d	Enable output from DBUG_<N> macros for
369  *			for the current state.  May be followed
370  *			by a list of keywords which selects output
371  *			only for the DBUG macros with that keyword.
372  *			A null list of keywords implies output for
373  *			all macros.
374  *
375  *		D	Delay after each debugger output line.
376  *			The argument is the number of tenths of seconds
377  *			to delay, subject to machine capabilities.
378  *			I.E.  -#D,20 is delay two seconds.
379  *
380  *		f	Limit debugging and/or tracing, and profiling to the
381  *			list of named functions.  Note that a null list will
382  *			disable all functions.  The appropriate "d" or "t"
383  *			flags must still be given, this flag only limits their
384  *			actions if they are enabled.
385  *
386  *		F	Identify the source file name for each
387  *			line of debug or trace output.
388  *
389  *		g	Enable profiling.  Create a file called 'dbugmon.out'
390  *			containing information that can be used to profile
391  *			the program.  May be followed by a list of keywords
392  *			that select profiling only for the functions in that
393  *			list.  A null list implies that all functions are
394  *			considered.
395  *
396  *		L	Identify the source file line number for
397  *			each line of debug or trace output.
398  *
399  *		n	Print the current function nesting depth for
400  *			each line of debug or trace output.
401  *
402  *		N	Number each line of dbug output.
403  *
404  *		p	Limit debugger actions to specified processes.
405  *			A process must be identified with the
406  *			DBUG_PROCESS macro and match one in the list
407  *			for debugger actions to occur.
408  *
409  *		P	Print the current process name for each
410  *			line of debug or trace output.
411  *
412  *		r	When pushing a new state, do not inherit
413  *			the previous state's function nesting level.
414  *			Useful when the output is to start at the
415  *			left margin.
416  *
417  *		t	Enable function call/exit trace lines.
418  *			May be followed by a list (containing only
419  *			one modifier) giving a numeric maximum
420  *			trace level, beyond which no output will
421  *			occur for either debugging or tracing
422  *			macros.  The default is a compile time
423  *			option.
424  *
425  *	Some examples of debug control strings which might appear
426  *	on a shell command line (the "-#" is typically used to
427  *	introduce a control string to an application program) are:
428  *
429  *		-#d:t
430  *		-#d:f,main,subr1:F:L:t,20
431  *		-#d,input,output,files:n
432  *
433  *	For convenience, any leading "-#" is stripped off.
434  *
435  */
436 
437 
_db_push_(control)438 VOID _db_push_ (control)
439 char *control;
440 {
441     REGISTER char *scan;
442     REGISTER struct link *temp;
443 
444     if (!_db_fp_)
445 	_db_fp_ = stderr;	/* Output stream, default stderr */
446 
447     if (control && *control == '-') {
448 	if (*++control == '#') {
449 	    control++;
450 	}
451     }
452     control = StrDup (control);
453     PushState ();
454     scan = Strtok (control, ":");
455     for (; scan != NULL; scan = Strtok ((char *)NULL, ":")) {
456 	switch (*scan++) {
457 	    case 'd':
458 		_db_on_ = TRUE;
459 		stack -> flags |= DEBUG_ON;
460 		if (*scan++ == ',') {
461 		    stack -> keywords = ListParse (scan);
462 		}
463 	    	break;
464 	    case 'D':
465 		stack -> delay = 0;
466 		if (*scan++ == ',') {
467 		    temp = ListParse (scan);
468 		    stack -> delay = DelayArg (atoi (temp -> string));
469 		    FreeList (temp);
470 		}
471 		break;
472 	    case 'f':
473 		if (*scan++ == ',') {
474 		    stack -> functions = ListParse (scan);
475 		}
476 		break;
477 	    case 'F':
478 		stack -> flags |= FILE_ON;
479 		break;
480 	    case 'g':
481 		_db_pon_ = TRUE;
482 		OpenProfile(PROF_FILE);
483 		stack -> flags |= PROFILE_ON;
484 		if (*scan++ == ',') {
485 		    stack -> p_functions = ListParse (scan);
486 		}
487 		break;
488 	    case 'L':
489 		stack -> flags |= LINE_ON;
490 		break;
491 	    case 'n':
492 		stack -> flags |= DEPTH_ON;
493 		break;
494 	    case 'N':
495 		stack -> flags |= NUMBER_ON;
496 		break;
497 	    case 'o':
498 		if (*scan++ == ',') {
499 		    temp = ListParse (scan);
500 		    OpenFile (temp -> string);
501 		    FreeList (temp);
502 		} else {
503 		    OpenFile ("-");
504 		}
505 		break;
506 	    case 'p':
507 		if (*scan++ == ',') {
508 		    stack -> processes = ListParse (scan);
509 		}
510 		break;
511 	    case 'P':
512 		stack -> flags |= PROCESS_ON;
513 		break;
514 	    case 'r':
515 		stack -> level = 0;
516 		break;
517 	    case 't':
518 		stack -> flags |= TRACE_ON;
519 		if (*scan++ == ',') {
520 		    temp = ListParse (scan);
521 		    stack -> maxdepth = atoi (temp -> string);
522 		    FreeList (temp);
523 		}
524 		break;
525 	}
526     }
527     free (control);
528 }
529 
530 
531 
532 /*
533  *  FUNCTION
534  *
535  *	_db_pop_    pop the debug stack
536  *
537  *  DESCRIPTION
538  *
539  *	Pops the debug stack, returning the debug state to its
540  *	condition prior to the most recent _db_push_ invocation.
541  *	Note that the pop will fail if it would remove the last
542  *	valid state from the stack.  This prevents user errors
543  *	in the push/pop sequence from screwing up the debugger.
544  *	Maybe there should be some kind of warning printed if the
545  *	user tries to pop too many states.
546  *
547  */
548 
_db_pop_()549 VOID _db_pop_ ()
550 {
551     REGISTER struct state *discard;
552 
553     discard = stack;
554     if (discard != NULL && discard -> next_state != NULL) {
555 	stack = discard -> next_state;
556 	_db_fp_ = stack -> out_file;
557 	_db_pfp_ = stack -> prof_file;
558 	if (discard -> keywords != NULL) {
559 	    FreeList (discard -> keywords);
560 	}
561 	if (discard -> functions != NULL) {
562 	    FreeList (discard -> functions);
563 	}
564 	if (discard -> processes != NULL) {
565 	    FreeList (discard -> processes);
566 	}
567 	if (discard -> p_functions != NULL) {
568 	    FreeList (discard -> p_functions);
569 	}
570 	CloseFile (discard -> out_file);
571 	CloseFile (discard -> prof_file);
572 	free ((char *) discard);
573     }
574 }
575 
576 
577 /*
578  *  FUNCTION
579  *
580  *	_db_enter_    process entry point to user function
581  *
582  *  SYNOPSIS
583  *
584  *	VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_)
585  *	char *_func_;		points to current function name
586  *	char *_file_;		points to current file name
587  *	int _line_;		called from source line number
588  *	char **_sfunc_;		save previous _func_
589  *	char **_sfile_;		save previous _file_
590  *	int *_slevel_;		save previous nesting level
591  *
592  *  DESCRIPTION
593  *
594  *	Called at the beginning of each user function to tell
595  *	the debugger that a new function has been entered.
596  *	Note that the pointers to the previous user function
597  *	name and previous user file name are stored on the
598  *	caller's stack (this is why the ENTER macro must be
599  *	the first "executable" code in a function, since it
600  *	allocates these storage locations).  The previous nesting
601  *	level is also stored on the callers stack for internal
602  *	self consistency checks.
603  *
604  *	Also prints a trace line if tracing is enabled and
605  *	increments the current function nesting depth.
606  *
607  *	Note that this mechanism allows the debugger to know
608  *	what the current user function is at all times, without
609  *	maintaining an internal stack for the function names.
610  *
611  */
612 
_db_enter_(_func_,_file_,_line_,_sfunc_,_sfile_,_slevel_)613 VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_)
614 char *_func_;
615 char *_file_;
616 int _line_;
617 char **_sfunc_;
618 char **_sfile_;
619 int *_slevel_;
620 {
621     if (!init_done) {
622 	_db_push_ ("");
623     }
624     *_sfunc_ = func;
625     *_sfile_ = file;
626     func = _func_;
627     file = BaseName (_file_);
628     stack -> level++;
629     *_slevel_ = stack -> level;
630     if (DoProfile ()) {
631 	(VOID) fprintf (_db_pfp_, "%s\tE\t%ld\n",func, Clock());
632 	(VOID) fflush (_db_pfp_);
633     }
634     if (DoTrace ()) {
635 	DoPrefix (_line_);
636 	Indent (stack -> level);
637 	(VOID) fprintf (_db_fp_, ">%s\n", func);
638 	(VOID) fflush (_db_fp_);
639 	(VOID) XDelay (stack -> delay);
640     }
641 }
642 
643 
644 /*
645  *  FUNCTION
646  *
647  *	_db_return_    process exit from user function
648  *
649  *  SYNOPSIS
650  *
651  *	VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
652  *	int _line_;		current source line number
653  *	char **_sfunc_;		where previous _func_ is to be retrieved
654  *	char **_sfile_;		where previous _file_ is to be retrieved
655  *	int *_slevel_;		where previous level was stashed
656  *
657  *  DESCRIPTION
658  *
659  *	Called just before user function executes an explicit or implicit
660  *	return.  Prints a trace line if trace is enabled, decrements
661  *	the current nesting level, and restores the current function and
662  *	file names from the defunct function's stack.
663  *
664  */
665 
_db_return_(_line_,_sfunc_,_sfile_,_slevel_)666 VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
667 int _line_;
668 char **_sfunc_;
669 char **_sfile_;
670 int *_slevel_;
671 {
672     if (!init_done) {
673 	_db_push_ ("");
674     }
675     if (stack -> level != *_slevel_ && (TRACING || DEBUGGING || PROFILING)) {
676 	(VOID) fprintf (_db_fp_, ERR_MISSING_RETURN, _db_process_, func);
677         (VOID) XDelay (stack -> delay);
678     } else if (DoProfile ()) {
679 	(VOID) fprintf (_db_pfp_, "%s\tX\t%ld\n", func, Clock());
680         (VOID) XDelay (stack -> delay);
681     } else if (DoTrace ()) {
682 	DoPrefix (_line_);
683 	Indent (stack -> level);
684 	(VOID) fprintf (_db_fp_, "<%s\n", func);
685         (VOID) XDelay (stack -> delay);
686     }
687     (VOID) fflush (_db_fp_);
688     stack -> level = *_slevel_ - 1;
689     func = *_sfunc_;
690     file = *_sfile_;
691 }
692 
693 
694 /*
695  *  FUNCTION
696  *
697  *	_db_pargs_    log arguments for subsequent use by _db_doprnt_()
698  *
699  *  SYNOPSIS
700  *
701  *	VOID _db_pargs_ (_line_, keyword)
702  *	int _line_;
703  *	char *keyword;
704  *
705  *  DESCRIPTION
706  *
707  *	The new universal printing macro DBUG_PRINT, which replaces
708  *	all forms of the DBUG_N macros, needs two calls to runtime
709  *	support routines.  The first, this function, remembers arguments
710  *	that are used by the subsequent call to _db_doprnt_().
711 *
712  */
713 
_db_pargs_(_line_,keyword)714 VOID _db_pargs_ (_line_, keyword)
715 int _line_;
716 char *keyword;
717 {
718     u_line = _line_;
719     u_keyword = keyword;
720 }
721 
722 
723 /*
724  *  FUNCTION
725  *
726  *	_db_doprnt_    handle print of debug lines
727  *
728  *  SYNOPSIS
729  *
730  *	VOID _db_doprnt_ (format, ARGLIST)
731  *	char *format;
732  *	long ARGLIST;
733  *
734  *  DESCRIPTION
735  *
736  *	When invoked via one of the DBUG macros, tests the current keyword
737  *	set by calling _db_pargs_() to see if that macro has been selected
738  *	for processing via the debugger control string, and if so, handles
739  *	printing of the arguments via the format string.  The line number
740  *	of the DBUG macro in the source is found in u_line.
741  *
742  *	Note that the format string SHOULD NOT include a terminating
743  *	newline, this is supplied automatically.
744  *
745  *  NOTES
746  *
747  *	This runtime support routine replaces the older _db_printf_()
748  *	routine which is temporarily kept around for compatibility.
749  *
750  *	The rather ugly argument declaration is to handle some
751  *	magic with respect to the number of arguments passed
752  *	via the DBUG macros.  The current maximum is 3 arguments
753  *	(not including the keyword and format strings).
754  *
755  *	The new <varargs.h> facility is not yet common enough to
756  *	convert to it quite yet...
757  *
758  */
759 
760 /*VARARGS1*/
_db_doprnt_(format,ARGLIST)761 VOID _db_doprnt_ (format, ARGLIST)
762 char *format;
763 long ARGLIST;
764 {
765     if (_db_keyword_ (u_keyword)) {
766 	DoPrefix (u_line);
767 	if (TRACING) {
768 	    Indent (stack -> level + 1);
769 	} else {
770 	    (VOID) fprintf (_db_fp_, "%s: ", func);
771 	}
772 	(VOID) fprintf (_db_fp_, "%s: ", u_keyword);
773 	(VOID) fprintf (_db_fp_, format, ARGLIST);
774 	(VOID) fprintf (_db_fp_, "\n");
775 	(VOID) fflush (_db_fp_);
776 	(VOID) XDelay (stack -> delay);
777     }
778 }
779 
780 /*
781  *	The following routine is kept around temporarily for compatibility
782  *	with older objects that were compiled with the DBUG_N macro form
783  *	of the print routine.  It will print a warning message on first
784  *	usage.  It will go away in subsequent releases...
785  */
786 
787 /*VARARGS3*/
_db_printf_(_line_,keyword,format,ARGLIST)788 VOID _db_printf_ (_line_, keyword, format, ARGLIST)
789 int _line_;
790 char *keyword,  *format;
791 long ARGLIST;
792 {
793     static BOOLEAN firsttime = TRUE;
794 
795     if (firsttime) {
796 	(VOID) fprintf (stderr, ERR_PRINTF, _db_process_, file);
797 	firsttime = FALSE;
798     }
799     _db_pargs_ (_line_, keyword);
800     _db_doprnt_ (format, ARGLIST);
801 }
802 
803 
804 /*
805  *  FUNCTION
806  *
807  *	ListParse    parse list of modifiers in debug control string
808  *
809  *  SYNOPSIS
810  *
811  *	LOCAL struct link *ListParse (ctlp)
812  *	char *ctlp;
813  *
814  *  DESCRIPTION
815  *
816  *	Given pointer to a comma separated list of strings in "cltp",
817  *	parses the list, building a list and returning a pointer to it.
818  *	The original comma separated list is destroyed in the process of
819  *	building the linked list, thus it had better be a duplicate
820  *	if it is important.
821  *
822  *	Note that since each link is added at the head of the list,
823  *	the final list will be in "reverse order", which is not
824  *	significant for our usage here.
825  *
826  */
827 
ListParse(ctlp)828 LOCAL struct link *ListParse (ctlp)
829 char *ctlp;
830 {
831     REGISTER char *start;
832     REGISTER struct link *new;
833     REGISTER struct link *head;
834 
835     head = NULL;
836     while (*ctlp != EOS) {
837 	start = ctlp;
838 	while (*ctlp != EOS && *ctlp != ',') {
839 	    ctlp++;
840 	}
841 	if (*ctlp == ',') {
842 	    *ctlp++ = EOS;
843 	}
844 	new = (struct link *) DbugMalloc (sizeof (struct link));
845 	new -> string = StrDup (start);
846 	new -> next_link = head;
847 	head = new;
848     }
849     return (head);
850 }
851 
852 
853 /*
854  *  FUNCTION
855  *
856  *	InList    test a given string for member of a given list
857  *
858  *  SYNOPSIS
859  *
860  *	LOCAL BOOLEAN InList (linkp, cp)
861  *	struct link *linkp;
862  *	char *cp;
863  *
864  *  DESCRIPTION
865  *
866  *	Tests the string pointed to by "cp" to determine if it is in
867  *	the list pointed to by "linkp".  Linkp points to the first
868  *	link in the list.  If linkp is NULL then the string is treated
869  *	as if it is in the list (I.E all strings are in the null list).
870  *	This may seem rather strange at first but leads to the desired
871  *	operation if no list is given.  The net effect is that all
872  *	strings will be accepted when there is no list, and when there
873  *	is a list, only those strings in the list will be accepted.
874  *
875  */
876 
InList(linkp,cp)877 LOCAL BOOLEAN InList (linkp, cp)
878 struct link *linkp;
879 char *cp;
880 {
881     REGISTER struct link *scan;
882     REGISTER BOOLEAN accept;
883 
884     if (linkp == NULL) {
885 	accept = TRUE;
886     } else {
887 	accept = FALSE;
888 	for (scan = linkp; scan != NULL; scan = scan -> next_link) {
889 	    if (STREQ (scan -> string, cp)) {
890 		accept = TRUE;
891 		break;
892 	    }
893 	}
894     }
895     return (accept);
896 }
897 
898 
899 /*
900  *  FUNCTION
901  *
902  *	PushState    push current state onto stack and set up new one
903  *
904  *  SYNOPSIS
905  *
906  *	LOCAL VOID PushState ()
907  *
908  *  DESCRIPTION
909  *
910  *	Pushes the current state on the state stack, and initializes
911  *	a new state.  The only parameter inherited from the previous
912  *	state is the function nesting level.  This action can be
913  *	inhibited if desired, via the "r" flag.
914  *
915  *	The state stack is a linked list of states, with the new
916  *	state added at the head.  This allows the stack to grow
917  *	to the limits of memory if necessary.
918  *
919  */
920 
PushState()921 LOCAL VOID PushState ()
922 {
923     REGISTER struct state *new;
924 
925     new = (struct state *) DbugMalloc (sizeof (struct state));
926     new -> flags = 0;
927     new -> delay = 0;
928     new -> maxdepth = MAXDEPTH;
929     if (stack != NULL) {
930 	new -> level = stack -> level;
931     } else {
932 	new -> level = 0;
933     }
934     new -> out_file = stderr;
935     new -> functions = NULL;
936     new -> p_functions = NULL;
937     new -> keywords = NULL;
938     new -> processes = NULL;
939     new -> next_state = stack;
940     stack = new;
941     init_done = TRUE;
942 }
943 
944 
945 /*
946  *  FUNCTION
947  *
948  *	DoTrace    check to see if tracing is current enabled
949  *
950  *  SYNOPSIS
951  *
952  *	LOCAL BOOLEAN DoTrace ()
953  *
954  *  DESCRIPTION
955  *
956  *	Checks to see if tracing is enabled based on whether the
957  *	user has specified tracing, the maximum trace depth has
958  *	not yet been reached, the current function is selected,
959  *	and the current process is selected.  Returns TRUE if
960  *	tracing is enabled, FALSE otherwise.
961  *
962  */
963 
DoTrace()964 LOCAL BOOLEAN DoTrace ()
965 {
966     REGISTER BOOLEAN trace;
967 
968     trace = FALSE;
969     if (TRACING) {
970 	if (stack -> level <= stack -> maxdepth) {
971 	    if (InList (stack -> functions, func)) {
972 		if (InList (stack -> processes, _db_process_)) {
973 		    trace = TRUE;
974 		}
975 	    }
976 	}
977     }
978     return (trace);
979 }
980 
981 
982 /*
983  *  FUNCTION
984  *
985  *	DoProfile    check to see if profiling is current enabled
986  *
987  *  SYNOPSIS
988  *
989  *	LOCAL BOOLEAN DoProfile ()
990  *
991  *  DESCRIPTION
992  *
993  *	Checks to see if profiling is enabled based on whether the
994  *	user has specified profiling, the maximum trace depth has
995  *	not yet been reached, the current function is selected,
996  *	and the current process is selected.  Returns TRUE if
997  *	profiling is enabled, FALSE otherwise.
998  *
999  */
1000 
DoProfile()1001 LOCAL BOOLEAN DoProfile ()
1002 {
1003     REGISTER BOOLEAN profile;
1004 
1005     profile = FALSE;
1006     if (PROFILING) {
1007 	if (stack -> level <= stack -> maxdepth) {
1008 	    if (InList (stack -> p_functions, func)) {
1009 		if (InList (stack -> processes, _db_process_)) {
1010 		    profile = TRUE;
1011 		}
1012 	    }
1013 	}
1014     }
1015     return (profile);
1016 }
1017 
1018 
1019 /*
1020  *  FUNCTION
1021  *
1022  *	_db_keyword_    test keyword for member of keyword list
1023  *
1024  *  SYNOPSIS
1025  *
1026  *	BOOLEAN _db_keyword_ (keyword)
1027  *	char *keyword;
1028  *
1029  *  DESCRIPTION
1030  *
1031  *	Test a keyword to determine if it is in the currently active
1032  *	keyword list.  As with the function list, a keyword is accepted
1033  *	if the list is null, otherwise it must match one of the list
1034  *	members.  When debugging is not on, no keywords are accepted.
1035  *	After the maximum trace level is exceeded, no keywords are
1036  *	accepted (this behavior subject to change).  Additionally,
1037  *	the current function and process must be accepted based on
1038  *	their respective lists.
1039  *
1040  *	Returns TRUE if keyword accepted, FALSE otherwise.
1041  *
1042  */
1043 
_db_keyword_(keyword)1044 BOOLEAN _db_keyword_ (keyword)
1045 char *keyword;
1046 {
1047     REGISTER BOOLEAN accept;
1048 
1049     if (!init_done) {
1050 	_db_push_ ("");
1051     }
1052     accept = FALSE;
1053     if (DEBUGGING) {
1054 	if (stack -> level <= stack -> maxdepth) {
1055 	    if (InList (stack -> functions, func)) {
1056 		if (InList (stack -> keywords, keyword)) {
1057 		    if (InList (stack -> processes, _db_process_)) {
1058 			accept = TRUE;
1059 		    }
1060 		}
1061 	    }
1062 	}
1063     }
1064     return (accept);
1065 }
1066 
1067 
1068 /*
1069  *  FUNCTION
1070  *
1071  *	Indent    indent a line to the given indentation level
1072  *
1073  *  SYNOPSIS
1074  *
1075  *	LOCAL VOID Indent (indent)
1076  *	int indent;
1077  *
1078  *  DESCRIPTION
1079  *
1080  *	Indent a line to the given level.  Note that this is
1081  *	a simple minded but portable implementation.
1082  *	There are better ways.
1083  *
1084  *	Also, the indent must be scaled by the compile time option
1085  *	of character positions per nesting level.
1086  *
1087  */
1088 
Indent(indent)1089 LOCAL VOID Indent (indent)
1090 int indent;
1091 {
1092     REGISTER int count;
1093     AUTO char buffer[PRINTBUF];
1094 
1095     indent *= INDENT;
1096     for (count = 0; (count < (indent - INDENT)) && (count < (PRINTBUF - 1)); count++) {
1097 	if ((count % INDENT) == 0) {
1098 	    buffer[count] = '|';
1099 	} else {
1100 	    buffer[count] = ' ';
1101 	}
1102     }
1103     buffer[count] = EOS;
1104     (VOID) fprintf (_db_fp_, buffer);
1105     (VOID) fflush (_db_fp_);
1106 }
1107 
1108 
1109 /*
1110  *  FUNCTION
1111  *
1112  *	FreeList    free all memory associated with a linked list
1113  *
1114  *  SYNOPSIS
1115  *
1116  *	LOCAL VOID FreeList (linkp)
1117  *	struct link *linkp;
1118  *
1119  *  DESCRIPTION
1120  *
1121  *	Given pointer to the head of a linked list, frees all
1122  *	memory held by the list and the members of the list.
1123  *
1124  */
1125 
FreeList(linkp)1126 LOCAL VOID FreeList (linkp)
1127 struct link *linkp;
1128 {
1129     REGISTER struct link *old;
1130 
1131     while (linkp != NULL) {
1132 	old = linkp;
1133 	linkp = linkp -> next_link;
1134 	if (old -> string != NULL) {
1135 	    free (old -> string);
1136 	}
1137 	free ((char *) old);
1138     }
1139 }
1140 
1141 
1142 /*
1143  *  FUNCTION
1144  *
1145  *	StrDup   make a duplicate of a string in new memory
1146  *
1147  *  SYNOPSIS
1148  *
1149  *	LOCAL char *StrDup (string)
1150  *	char *string;
1151  *
1152  *  DESCRIPTION
1153  *
1154  *	Given pointer to a string, allocates sufficient memory to make
1155  *	a duplicate copy, and copies the string to the newly allocated
1156  *	memory.  Failure to allocated sufficient memory is immediately
1157  *	fatal.
1158  *
1159  */
1160 
1161 
StrDup(string)1162 LOCAL char *StrDup (string)
1163 char *string;
1164 {
1165     REGISTER char *new;
1166 
1167     new = DbugMalloc (strlen (string) + 1);
1168     (VOID) strcpy (new, string);
1169     return (new);
1170 }
1171 
1172 
1173 /*
1174  *  FUNCTION
1175  *
1176  *	DoPrefix    print debugger line prefix prior to indentation
1177  *
1178  *  SYNOPSIS
1179  *
1180  *	LOCAL VOID DoPrefix (_line_)
1181  *	int _line_;
1182  *
1183  *  DESCRIPTION
1184  *
1185  *	Print prefix common to all debugger output lines, prior to
1186  *	doing indentation if necessary.  Print such information as
1187  *	current process name, current source file name and line number,
1188  *	and current function nesting depth.
1189  *
1190  */
1191 
1192 
DoPrefix(_line_)1193 LOCAL VOID DoPrefix (_line_)
1194 int _line_;
1195 {
1196     lineno++;
1197     if (stack -> flags & NUMBER_ON) {
1198 	(VOID) fprintf (_db_fp_, "%5d: ", lineno);
1199     }
1200     if (stack -> flags & PROCESS_ON) {
1201 	(VOID) fprintf (_db_fp_, "%s: ", _db_process_);
1202     }
1203     if (stack -> flags & FILE_ON) {
1204 	(VOID) fprintf (_db_fp_, "%14s: ", file);
1205     }
1206     if (stack -> flags & LINE_ON) {
1207 	(VOID) fprintf (_db_fp_, "%5d: ", _line_);
1208     }
1209     if (stack -> flags & DEPTH_ON) {
1210 	(VOID) fprintf (_db_fp_, "%4d: ", stack -> level);
1211     }
1212     (VOID) fflush (_db_fp_);
1213 }
1214 
1215 
1216 /*
1217  *  FUNCTION
1218  *
1219  *	OpenFile    open new output stream for debugger output
1220  *
1221  *  SYNOPSIS
1222  *
1223  *	LOCAL VOID OpenFile (name)
1224  *	char *name;
1225  *
1226  *  DESCRIPTION
1227  *
1228  *	Given name of a new file (or "-" for stdout) opens the file
1229  *	and sets the output stream to the new file.
1230  *
1231  */
1232 
OpenFile(name)1233 LOCAL VOID OpenFile (name)
1234 char *name;
1235 {
1236     REGISTER FILE *fp;
1237     REGISTER BOOLEAN newfile;
1238 
1239     if (name != NULL) {
1240 	if (strcmp (name, "-") == 0) {
1241 	    _db_fp_ = stdout;
1242 	    stack -> out_file = _db_fp_;
1243 	} else {
1244 	    if (!Writable (name)) {
1245 		(VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1246 		perror ("");
1247 		(VOID) fflush (_db_fp_);
1248 		(VOID) XDelay (stack -> delay);
1249 	    } else {
1250 		if (EXISTS (name)) {
1251 		    newfile = FALSE;
1252 		} else {
1253 		    newfile = TRUE;
1254 		}
1255 		fp = fopen (name, "a");
1256 		if (fp == NULL) {
1257  		    (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1258 		    perror ("");
1259 		    (VOID) fflush (_db_fp_);
1260 		    (VOID) XDelay (stack -> delay);
1261 		} else {
1262 		    _db_fp_ = fp;
1263 		    stack -> out_file = fp;
1264 		    if (newfile) {
1265 			ChangeOwner (name);
1266 		    }
1267 		}
1268 	    }
1269 	}
1270     }
1271 }
1272 
1273 
1274 /*
1275  *  FUNCTION
1276  *
1277  *	OpenProfile    open new output stream for profiler output
1278  *
1279  *  SYNOPSIS
1280  *
1281  *	LOCAL VOID OpenProfile (name)
1282  *	char *name;
1283  *
1284  *  DESCRIPTION
1285  *
1286  *	Given name of a new file, opens the file
1287  *	and sets the profiler output stream to the new file.
1288  *
1289  *	It is currently unclear whether the prefered behavior is
1290  *	to truncate any existing file, or simply append to it.
1291  *	The latter behavior would be desirable for collecting
1292  *	accumulated runtime history over a number of separate
1293  *	runs.  It might take some changes to the analyzer program
1294  *	though, and the notes that Binayak sent with the profiling
1295  *	diffs indicated that append was the normal mode, but this
1296  *	does not appear to agree with the actual code. I haven't
1297  *	investigated at this time [fnf; 24-Jul-87].
1298  */
1299 
OpenProfile(name)1300 LOCAL VOID OpenProfile (name)
1301 char *name;
1302 {
1303     REGISTER FILE *fp;
1304     REGISTER BOOLEAN newfile;
1305 
1306     if (name != NULL) {
1307 	if (!Writable (name)) {
1308 	    (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1309 	    perror ("");
1310 	    (VOID) fflush (_db_fp_);
1311 	    (VOID) XDelay (stack -> delay);
1312 	} else {
1313 	    if (EXISTS (name)) {
1314 		newfile = FALSE;
1315 	    } else {
1316 		newfile = TRUE;
1317 	    }
1318 	    fp = fopen (name, "w");
1319 	    if (fp == NULL) {
1320 		(VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
1321 		perror ("");
1322 		(VOID) fflush (_db_fp_);
1323 		(VOID) XDelay (stack -> delay);
1324 	    } else {
1325 		_db_pfp_ = fp;
1326 		stack -> prof_file = fp;
1327 		if (newfile) {
1328 		    ChangeOwner (name);
1329 		}
1330 	    }
1331 	}
1332     }
1333 }
1334 
1335 
1336 /*
1337  *  FUNCTION
1338  *
1339  *	CloseFile    close the debug output stream
1340  *
1341  *  SYNOPSIS
1342  *
1343  *	LOCAL VOID CloseFile (fp)
1344  *	FILE *fp;
1345  *
1346  *  DESCRIPTION
1347  *
1348  *	Closes the debug output stream unless it is standard output
1349  *	or standard error.
1350  *
1351  */
1352 
CloseFile(fp)1353 LOCAL VOID CloseFile (fp)
1354 FILE *fp;
1355 {
1356     if (fp != stderr && fp != stdout) {
1357 	if (fclose (fp) == EOF) {
1358 	    (VOID) fprintf (stderr, ERR_CLOSE, _db_process_);
1359 	    perror ("");
1360 	    (VOID) fflush (stderr);
1361 	    (VOID) XDelay (stack -> delay);
1362 	}
1363     }
1364 }
1365 
1366 
1367 /*
1368  *  FUNCTION
1369  *
1370  *	DbugExit    print error message and exit
1371  *
1372  *  SYNOPSIS
1373  *
1374  *	LOCAL VOID DbugExit (why)
1375  *	char *why;
1376  *
1377  *  DESCRIPTION
1378  *
1379  *	Prints error message using current process name, the reason for
1380  *	aborting (typically out of memory), and exits with status 1.
1381  *	This should probably be changed to use a status code
1382  *	defined in the user's debugger include file.
1383  *
1384  */
1385 
DbugExit(why)1386 LOCAL VOID DbugExit (why)
1387 char *why;
1388 {
1389     (VOID) fprintf (stderr, ERR_ABORT, _db_process_, why);
1390     (VOID) fflush (stderr);
1391     (VOID) XDelay (stack -> delay);
1392     exit (1);
1393 }
1394 
1395 
1396 /*
1397  *  FUNCTION
1398  *
1399  *	DbugMalloc    allocate memory for debugger runtime support
1400  *
1401  *  SYNOPSIS
1402  *
1403  *	LOCAL char *DbugMalloc (size)
1404  *	int size;
1405  *
1406  *  DESCRIPTION
1407  *
1408  *	Allocate more memory for debugger runtime support functions.
1409  *	Failure to to allocate the requested number of bytes is
1410  *	immediately fatal to the current process.  This may be
1411  *	rather unfriendly behavior.  It might be better to simply
1412  *	print a warning message, freeze the current debugger state,
1413  *	and continue execution.
1414  *
1415  */
1416 
DbugMalloc(size)1417 LOCAL char *DbugMalloc (size)
1418 int size;
1419 {
1420     register char *new;
1421 
1422     new = malloc ( size );
1423     if (new == NULL) {
1424 	DbugExit ("out of memory");
1425     }
1426     return (new);
1427 }
1428 
1429 
1430 /*
1431  *	This function may be eliminated when strtok is available
1432  *	in the runtime environment (missing from BSD4.1).
1433  */
1434 
Strtok(s1,s2)1435 LOCAL char *Strtok (s1, s2)
1436 char *s1, *s2;
1437 {
1438     static char *end = NULL;
1439     REGISTER char *rtnval;
1440 
1441     rtnval = NULL;
1442     if (s2 != NULL) {
1443 	if (s1 != NULL) {
1444 	    end = s1;
1445 	    rtnval = Strtok ((char *) NULL, s2);
1446 	} else if (end != NULL) {
1447 	    if (*end != EOS) {
1448 		rtnval = end;
1449 		while (*end != *s2 && *end != EOS) {end++;}
1450 		if (*end != EOS) {
1451 		    *end++ = EOS;
1452 		}
1453 	    }
1454 	}
1455     }
1456     return (rtnval);
1457 }
1458 
1459 
1460 /*
1461  *  FUNCTION
1462  *
1463  *	BaseName    strip leading pathname components from name
1464  *
1465  *  SYNOPSIS
1466  *
1467  *	LOCAL char *BaseName (pathname)
1468  *	char *pathname;
1469  *
1470  *  DESCRIPTION
1471  *
1472  *	Given pointer to a complete pathname, locates the base file
1473  *	name at the end of the pathname and returns a pointer to
1474  *	it.
1475  *
1476  */
1477 
BaseName(pathname)1478 LOCAL char *BaseName (pathname)
1479 char *pathname;
1480 {
1481     register char *base;
1482 
1483     base = Strrchr (pathname, '/');
1484     if (base++ == NULL) {
1485 	base = pathname;
1486     }
1487     return (base);
1488 }
1489 
1490 
1491 /*
1492  *  FUNCTION
1493  *
1494  *	Writable    test to see if a pathname is writable/creatable
1495  *
1496  *  SYNOPSIS
1497  *
1498  *	LOCAL BOOLEAN Writable (pathname)
1499  *	char *pathname;
1500  *
1501  *  DESCRIPTION
1502  *
1503  *	Because the debugger might be linked in with a program that
1504  *	runs with the set-uid-bit (suid) set, we have to be careful
1505  *	about opening a user named file for debug output.  This consists
1506  *	of checking the file for write access with the real user id,
1507  *	or checking the directory where the file will be created.
1508  *
1509  *	Returns TRUE if the user would normally be allowed write or
1510  *	create access to the named file.  Returns FALSE otherwise.
1511  *
1512  */
1513 
Writable(pathname)1514 LOCAL BOOLEAN Writable (pathname)
1515 char *pathname;
1516 {
1517     REGISTER BOOLEAN granted;
1518 #ifdef unix
1519     REGISTER char *lastslash;
1520 #endif
1521 
1522 #ifndef unix
1523     granted = TRUE;
1524 #else
1525     granted = FALSE;
1526     if (EXISTS (pathname)) {
1527 	if (WRITABLE (pathname)) {
1528 	    granted = TRUE;
1529 	}
1530     } else {
1531 	lastslash = Strrchr (pathname, '/');
1532 	if (lastslash != NULL) {
1533 	    *lastslash = EOS;
1534 	} else {
1535 	    pathname = ".";
1536 	}
1537 	if (WRITABLE (pathname)) {
1538 	    granted = TRUE;
1539 	}
1540 	if (lastslash != NULL) {
1541 	    *lastslash = '/';
1542 	}
1543     }
1544 #endif
1545     return (granted);
1546 }
1547 
1548 
1549 /*
1550  *	This function may be eliminated when strrchr is available
1551  *	in the runtime environment (missing from BSD4.1).
1552  *	Alternately, you can use rindex() on BSD systems.
1553  */
1554 
Strrchr(s,c)1555 LOCAL char *Strrchr (s, c)
1556 char *s;
1557 char c;
1558 {
1559     REGISTER char *scan;
1560 
1561     for (scan = s; *scan != EOS; scan++) {;}
1562     while (scan > s && *--scan != c) {;}
1563     if (*scan != c) {
1564 	scan = NULL;
1565     }
1566     return (scan);
1567 }
1568 
1569 
1570 /*
1571  *  FUNCTION
1572  *
1573  *	ChangeOwner    change owner to real user for suid programs
1574  *
1575  *  SYNOPSIS
1576  *
1577  *	LOCAL VOID ChangeOwner (pathname)
1578  *
1579  *  DESCRIPTION
1580  *
1581  *	For unix systems, change the owner of the newly created debug
1582  *	file to the real owner.  This is strictly for the benefit of
1583  *	programs that are running with the set-user-id bit set.
1584  *
1585  *	Note that at this point, the fact that pathname represents
1586  *	a newly created file has already been established.  If the
1587  *	program that the debugger is linked to is not running with
1588  *	the suid bit set, then this operation is redundant (but
1589  *	harmless).
1590  *
1591  */
1592 
ChangeOwner(pathname)1593 LOCAL VOID ChangeOwner (pathname)
1594 char *pathname;
1595 {
1596 #ifdef unix
1597     if (chown (pathname, getuid (), getgid ()) == -1) {
1598 	(VOID) fprintf (stderr, ERR_CHOWN, _db_process_, pathname);
1599 	perror ("");
1600 	(VOID) fflush (stderr);
1601 	(VOID) XDelay (stack -> delay);
1602     }
1603 #endif
1604 }
1605 
1606 
1607 /*
1608  *  FUNCTION
1609  *
1610  *	_db_setjmp_    save debugger environment
1611  *
1612  *  SYNOPSIS
1613  *
1614  *	VOID _db_setjmp_ ()
1615  *
1616  *  DESCRIPTION
1617  *
1618  *	Invoked as part of the user's DBUG_SETJMP macro to save
1619  *	the debugger environment in parallel with saving the user's
1620  *	environment.
1621  *
1622  */
1623 
_db_setjmp_()1624 VOID _db_setjmp_ ()
1625 {
1626    jmplevel = stack -> level;
1627    jmpfunc = func;
1628    jmpfile = file;
1629 }
1630 
1631 
1632 /*
1633  *  FUNCTION
1634  *
1635  *	_db_longjmp_    restore previously saved debugger environment
1636  *
1637  *  SYNOPSIS
1638  *
1639  *	VOID _db_longjmp_ ()
1640  *
1641  *  DESCRIPTION
1642  *
1643  *	Invoked as part of the user's DBUG_LONGJMP macro to restore
1644  *	the debugger environment in parallel with restoring the user's
1645  *	previously saved environment.
1646  *
1647  */
1648 
_db_longjmp_()1649 VOID _db_longjmp_ ()
1650 {
1651     stack -> level = jmplevel;
1652     if (jmpfunc) {
1653 	func = jmpfunc;
1654     }
1655     if (jmpfile) {
1656 	file = jmpfile;
1657     }
1658 }
1659 
1660 
1661 /*
1662  *  FUNCTION
1663  *
1664  *	DelayArg   convert D flag argument to appropriate value
1665  *
1666  *  SYNOPSIS
1667  *
1668  *	LOCAL int DelayArg (value)
1669  *	int value;
1670  *
1671  *  DESCRIPTION
1672  *
1673  *	Converts delay argument, given in tenths of a second, to the
1674  *	appropriate numerical argument used by the system to delay
1675  *	that that many tenths of a second.  For example, on the
1676  *	AMIGA, there is a system call "Delay()" which takes an
1677  *	argument in ticks (50 per second).  On unix, the sleep
1678  *	command takes seconds.  Thus a value of "10", for one
1679  *	second of delay, gets converted to 50 on the amiga, and 1
1680  *	on unix.  Other systems will need to use a timing loop.
1681  *
1682  */
1683 
DelayArg(value)1684 LOCAL int DelayArg (value)
1685 int value;
1686 {
1687     int delayarg = 0;
1688 
1689 #ifdef unix
1690     delayarg = value / 10;		/* Delay is in seconds for sleep () */
1691 #endif
1692 #ifdef AMIGA
1693     delayarg = (HZ * value) / 10;	/* Delay in ticks for XDelay () */
1694 #endif
1695     return (delayarg);
1696 }
1697 
1698 
1699 /*
1700  *	A dummy delay stub for systems that do not support delays.
1701  *	With a little work, this can be turned into a timing loop.
1702  */
1703 
1704 #ifndef unix
1705 #ifndef AMIGA
XDelay()1706 XDelay ()
1707 {
1708 }
1709 #endif
1710 #endif
1711 
1712 
1713 /*
1714  *  FUNCTION
1715  *
1716  *	perror    perror simulation for systems that don't have it
1717  *
1718  *  SYNOPSIS
1719  *
1720  *	LOCAL VOID perror (s)
1721  *	char *s;
1722  *
1723  *  DESCRIPTION
1724  *
1725  *	Perror produces a message on the standard error stream which
1726  *	provides more information about the library or system error
1727  *	just encountered.  The argument string s is printed, followed
1728  *	by a ':', a blank, and then a message and a newline.
1729  *
1730  *	An undocumented feature of the unix perror is that if the string
1731  *	's' is a null string (NOT a NULL pointer!), then the ':' and
1732  *	blank are not printed.
1733  *
1734  *	This version just complains about an "unknown system error".
1735  *
1736  */
1737 
1738 #if !unix && !(AMIGA || LATTICE || __TURBOC__ )
perror(s)1739 LOCAL VOID perror (s)
1740 #if __STDC__
1741 const char *s;
1742 #else
1743 char *s;
1744 #endif
1745 {
1746     if (s && *s != EOS) {
1747 	(VOID) fprintf (stderr, "%s: ", s);
1748     }
1749     (VOID) fprintf (stderr, "<unknown system error>\n");
1750 }
1751 #endif	/* !unix && !(AMIGA && LATTICE) */
1752 
1753 /*
1754  * Here we need the definitions of the clock routine.  Add your
1755  * own for whatever system that you have.
1756  */
1757 
1758 #if unix
1759 
1760 # include <sys/param.h>
1761 # if !defined(Solaris) && (BSD4_3 || sun)
1762 
1763 /*
1764  * Definition of the Clock() routine for 4.3 BSD.
1765  */
1766 
1767 #include <sys/time.h>
1768 #include <sys/resource.h>
1769 
1770 
1771 /*
1772  * Returns the user time in milliseconds used by this process so
1773  * far.
1774  */
1775 
Clock()1776 LOCAL unsigned long Clock ()
1777 {
1778     struct rusage ru;
1779 
1780     (VOID) getrusage (RUSAGE_SELF, &ru);
1781     return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
1782 }
1783 
1784 #else
1785 
Clock()1786 LOCAL unsigned long Clock ()
1787 {
1788     return (0);
1789 }
1790 
1791 # endif
1792 
1793 #else
1794 
1795 #if AMIGA
1796 
1797 struct DateStamp {		/* Yes, this is a hack, but doing it right */
1798 	long ds_Days;		/* is incredibly ugly without splitting this */
1799 	long ds_Minute;		/* off into a separate file */
1800 	long ds_Tick;
1801 };
1802 
1803 static int first_clock = TRUE;
1804 static struct DateStamp begin;
1805 static struct DateStamp elapsed;
1806 
Clock()1807 LOCAL unsigned long Clock ()
1808 {
1809     register struct DateStamp *now;
1810     register unsigned long millisec = 0;
1811     extern VOID *AllocMem ();
1812 
1813     now = (struct DateStamp *) AllocMem ((long) sizeof (struct DateStamp), 0L);
1814     if (now != NULL) {
1815 	if (first_clock == TRUE) {
1816 	    first_clock = FALSE;
1817 	    (VOID) DateStamp (now);
1818 	    begin = *now;
1819 	}
1820 	(VOID) DateStamp (now);
1821 	millisec = 24 * 3600 * (1000 / HZ) * (now -> ds_Days - begin.ds_Days);
1822 	millisec += 60 * (1000 / HZ) * (now -> ds_Minute - begin.ds_Minute);
1823 	millisec += (1000 / HZ) * (now -> ds_Tick - begin.ds_Tick);
1824 	(VOID) FreeMem (now, (long) sizeof (struct DateStamp));
1825     }
1826     return (millisec);
1827 }
1828 
1829 #else
1830 
Clock()1831 LOCAL unsigned long Clock ()
1832 {
1833     return (0);
1834 }
1835 
1836 #endif	/* AMIGA */
1837 
1838 #endif	/* unix */
1839 
1840 #ifdef AMIGA
XDelay(x)1841 XDelay(x)
1842 int x;
1843 {
1844 	if (x) Delay(x);	/* fix Delay bug in AmigaDOS */
1845 }
1846 #endif
1847 
1848