binutils-gdb/gdb/utils.c
2012-06-03 15:36:32 +01:00

999 lines
21 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* General utility routines for GDB, the GNU debugger.
Copyright (C) 1986, 1989 Free Software Foundation, Inc.
This file is part of GDB.
GDB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
GDB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GDB; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <pwd.h>
#include "defs.h"
#include "param.h"
#ifdef HAVE_TERMIO
#include <termio.h>
#endif
/* If this definition isn't overridden by the header files, assume
that isatty and fileno exist on this system. */
#ifndef ISATTY
#define ISATTY(FP) (isatty (fileno (FP)))
#endif
void error ();
void fatal ();
/* Chain of cleanup actions established with make_cleanup,
to be executed if an error happens. */
static struct cleanup *cleanup_chain;
/* Nonzero means a quit has been requested. */
int quit_flag;
/* Nonzero means quit immediately if Control-C is typed now,
rather than waiting until QUIT is executed. */
int immediate_quit;
/* Add a new cleanup to the cleanup_chain,
and return the previous chain pointer
to be passed later to do_cleanups or discard_cleanups.
Args are FUNCTION to clean up with, and ARG to pass to it. */
struct cleanup *
make_cleanup (function, arg)
void (*function) ();
int arg;
{
register struct cleanup *new
= (struct cleanup *) xmalloc (sizeof (struct cleanup));
register struct cleanup *old_chain = cleanup_chain;
new->next = cleanup_chain;
new->function = function;
new->arg = arg;
cleanup_chain = new;
return old_chain;
}
/* Discard cleanups and do the actions they describe
until we get back to the point OLD_CHAIN in the cleanup_chain. */
void
do_cleanups (old_chain)
register struct cleanup *old_chain;
{
register struct cleanup *ptr;
while ((ptr = cleanup_chain) != old_chain)
{
(*ptr->function) (ptr->arg);
cleanup_chain = ptr->next;
free (ptr);
}
}
/* Discard cleanups, not doing the actions they describe,
until we get back to the point OLD_CHAIN in the cleanup_chain. */
void
discard_cleanups (old_chain)
register struct cleanup *old_chain;
{
register struct cleanup *ptr;
while ((ptr = cleanup_chain) != old_chain)
{
cleanup_chain = ptr->next;
free (ptr);
}
}
/* Set the cleanup_chain to 0, and return the old cleanup chain. */
struct cleanup *
save_cleanups ()
{
struct cleanup *old_chain = cleanup_chain;
cleanup_chain = 0;
return old_chain;
}
/* Restore the cleanup chain from a previously saved chain. */
void
restore_cleanups (chain)
struct cleanup *chain;
{
cleanup_chain = chain;
}
/* This function is useful for cleanups.
Do
foo = xmalloc (...);
old_chain = make_cleanup (free_current_contents, &foo);
to arrange to free the object thus allocated. */
void
free_current_contents (location)
char **location;
{
free (*location);
}
/* Generally useful subroutines used throughout the program. */
/* Like malloc but get error if no storage available. */
char *
xmalloc (size)
long size;
{
register char *val = (char *) malloc (size);
if (!val)
fatal ("virtual memory exhausted.", 0);
return val;
}
/* Like realloc but get error if no storage available. */
char *
xrealloc (ptr, size)
char *ptr;
long size;
{
register char *val = (char *) realloc (ptr, size);
if (!val)
fatal ("virtual memory exhausted.", 0);
return val;
}
/* Print the system error message for errno, and also mention STRING
as the file name for which the error was encountered.
Then return to command level. */
void
perror_with_name (string)
char *string;
{
extern int sys_nerr;
extern char *sys_errlist[];
extern int errno;
char *err;
char *combined;
if (errno < sys_nerr)
err = sys_errlist[errno];
else
err = "unknown error";
combined = (char *) alloca (strlen (err) + strlen (string) + 3);
strcpy (combined, string);
strcat (combined, ": ");
strcat (combined, err);
error ("%s.", combined);
}
/* Print the system error message for ERRCODE, and also mention STRING
as the file name for which the error was encountered. */
void
print_sys_errmsg (string, errcode)
char *string;
int errcode;
{
extern int sys_nerr;
extern char *sys_errlist[];
char *err;
char *combined;
if (errcode < sys_nerr)
err = sys_errlist[errcode];
else
err = "unknown error";
combined = (char *) alloca (strlen (err) + strlen (string) + 3);
strcpy (combined, string);
strcat (combined, ": ");
strcat (combined, err);
printf ("%s.\n", combined);
}
void
quit ()
{
#ifdef HAVE_TERMIO
ioctl (fileno (stdout), TCFLSH, 1);
#else /* not HAVE_TERMIO */
ioctl (fileno (stdout), TIOCFLUSH, 0);
#endif /* not HAVE_TERMIO */
#ifdef TIOCGPGRP
error ("Quit");
#else
error ("Quit (expect signal %d when inferior is resumed)", SIGINT);
#endif /* TIOCGPGRP */
}
/* Control C comes here */
void
request_quit ()
{
quit_flag = 1;
#ifdef USG
/* Restore the signal handler. */
signal (SIGINT, request_quit);
#endif
if (immediate_quit)
quit ();
}
/* Print an error message and return to command level.
STRING is the error message, used as a fprintf string,
and ARG is passed as an argument to it. */
void
error (string, arg1, arg2, arg3)
char *string;
int arg1, arg2, arg3;
{
terminal_ours (); /* Should be ok even if no inf. */
fflush (stdout);
fprintf (stderr, string, arg1, arg2, arg3);
fprintf (stderr, "\n");
return_to_top_level ();
}
/* Print an error message and exit reporting failure.
This is for a error that we cannot continue from.
STRING and ARG are passed to fprintf. */
void
fatal (string, arg)
char *string;
int arg;
{
fprintf (stderr, "gdb: ");
fprintf (stderr, string, arg);
fprintf (stderr, "\n");
exit (1);
}
/* Print an error message and exit, dumping core.
STRING is a printf-style control string, and ARG is a corresponding
argument. */
void
fatal_dump_core (string, arg)
char *string;
int arg;
{
fprintf (stderr, "gdb: ");
fprintf (stderr, string, arg);
fprintf (stderr, "\n");
signal (SIGQUIT, SIG_DFL);
kill (getpid (), SIGQUIT);
/* We should never get here, but just in case... */
exit (1);
}
/* Make a copy of the string at PTR with SIZE characters
(and add a null character at the end in the copy).
Uses malloc to get the space. Returns the address of the copy. */
char *
savestring (ptr, size)
char *ptr;
int size;
{
register char *p = (char *) xmalloc (size + 1);
bcopy (ptr, p, size);
p[size] = 0;
return p;
}
char *
concat (s1, s2, s3)
char *s1, *s2, *s3;
{
register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1;
register char *val = (char *) xmalloc (len);
strcpy (val, s1);
strcat (val, s2);
strcat (val, s3);
return val;
}
void
print_spaces (n, file)
register int n;
register FILE *file;
{
while (n-- > 0)
fputc (' ', file);
}
/* Ask user a y-or-n question and return 1 iff answer is yes.
Takes three args which are given to printf to print the question.
The first, a control string, should end in "? ".
It should not say how to answer, because we do that. */
int
query (ctlstr, arg1, arg2)
char *ctlstr;
{
register int answer;
/* Automatically answer "yes" if input is not from a terminal. */
if (!input_from_terminal_p ())
return 1;
while (1)
{
printf (ctlstr, arg1, arg2);
printf ("(y or n) ");
fflush (stdout);
answer = fgetc (stdin);
clearerr (stdin); /* in case of C-d */
if (answer != '\n')
while (fgetc (stdin) != '\n') clearerr (stdin);
if (answer >= 'a')
answer -= 040;
if (answer == 'Y')
return 1;
if (answer == 'N')
return 0;
printf ("Please answer y or n.\n");
}
}
/* Parse a C escape sequence. STRING_PTR points to a variable
containing a pointer to the string to parse. That pointer
is updated past the characters we use. The value of the
escape sequence is returned.
A negative value means the sequence \ newline was seen,
which is supposed to be equivalent to nothing at all.
If \ is followed by a null character, we return a negative
value and leave the string pointer pointing at the null character.
If \ is followed by 000, we return 0 and leave the string pointer
after the zeros. A value of 0 does not mean end of string. */
int
parse_escape (string_ptr)
char **string_ptr;
{
register int c = *(*string_ptr)++;
switch (c)
{
case 'a':
return '\a';
case 'b':
return '\b';
case 'e':
return 033;
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'v':
return '\v';
case '\n':
return -2;
case 0:
(*string_ptr)--;
return 0;
case '^':
c = *(*string_ptr)++;
if (c == '\\')
c = parse_escape (string_ptr);
if (c == '?')
return 0177;
return (c & 0200) | (c & 037);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
register int i = c - '0';
register int count = 0;
while (++count < 3)
{
if ((c = *(*string_ptr)++) >= '0' && c <= '7')
{
i *= 8;
i += c - '0';
}
else
{
(*string_ptr)--;
break;
}
}
return i;
}
default:
return c;
}
}
/* Print the character CH on STREAM as part of the contents
of a literal string whose delimiter is QUOTER. */
void
printchar (ch, stream, quoter)
unsigned char ch;
FILE *stream;
int quoter;
{
register int c = ch;
if (c < 040 || c >= 0177)
switch (c)
{
case '\n':
fputs_filtered ("\\n", stream);
break;
case '\b':
fputs_filtered ("\\b", stream);
break;
case '\t':
fputs_filtered ("\\t", stream);
break;
case '\f':
fputs_filtered ("\\f", stream);
break;
case '\r':
fputs_filtered ("\\r", stream);
break;
case '\033':
fputs_filtered ("\\e", stream);
break;
case '\007':
fputs_filtered ("\\a", stream);
break;
default:
fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
break;
}
else
{
if (c == '\\' || c == quoter)
fputs_filtered ("\\", stream);
fprintf_filtered (stream, "%c", c);
}
}
static int lines_per_page, lines_printed, chars_per_line, chars_printed;
/* Set values of page and line size. */
static void
set_screensize_command (arg, from_tty)
char *arg;
int from_tty;
{
char *p = arg;
char *p1;
int tolinesize = lines_per_page;
int tocharsize = chars_per_line;
if (p == 0)
error_no_arg ("set screensize");
while (*p >= '0' && *p <= '9')
p++;
if (*p && *p != ' ' && *p != '\t')
error ("Non-integral argument given to \"set screensize\".");
tolinesize = atoi (arg);
while (*p == ' ' || *p == '\t')
p++;
if (*p)
{
p1 = p;
while (*p1 >= '0' && *p1 <= '9')
p1++;
if (*p1)
error ("Non-integral second argument given to \"set screensize\".");
tocharsize = atoi (p);
}
lines_per_page = tolinesize;
chars_per_line = tocharsize;
}
static void
prompt_for_continue ()
{
immediate_quit++;
gdb_readline ("---Type <return> to continue---", 0);
chars_printed = lines_printed = 0;
immediate_quit--;
}
/* Reinitialize filter; ie. tell it to reset to original values. */
void
reinitialize_more_filter ()
{
lines_printed = 0;
chars_printed = 0;
}
static void
screensize_info (arg, from_tty)
char *arg;
int from_tty;
{
if (arg)
error ("\"info screensize\" does not take any arguments.");
if (!lines_per_page)
printf ("Output more filtering is disabled.\n");
else
{
printf ("Output more filtering is enabled with\n");
printf ("%d lines per page and %d characters per line.\n",
lines_per_page, chars_per_line);
}
}
/* Like fputs but pause after every screenful.
Unlike fputs, fputs_filtered does not return a value.
It is OK for LINEBUFFER to be NULL, in which case just don't print
anything.
Note that a longjmp to top level may occur in this routine
(since prompt_for_continue may do so) so this routine should not be
called when cleanups are not in place. */
void
fputs_filtered (linebuffer, stream)
char *linebuffer;
FILE *stream;
{
char *lineptr;
if (linebuffer == 0)
return;
/* Don't do any filtering if it is disabled. */
if (stream != stdout || !ISATTY(stdout) || lines_per_page == 0)
{
fputs (linebuffer, stream);
return;
}
/* Go through and output each character. Show line extension
when this is necessary; prompt user for new page when this is
necessary. */
lineptr = linebuffer;
while (*lineptr)
{
/* Possible new page. */
if (lines_printed >= lines_per_page - 1)
prompt_for_continue ();
while (*lineptr && *lineptr != '\n')
{
/* Print a single line. */
if (*lineptr == '\t')
{
putc ('\t', stream);
/* Shifting right by 3 produces the number of tab stops
we have already passed, and then adding one and
shifting left 3 advances to the next tab stop. */
chars_printed = ((chars_printed >> 3) + 1) << 3;
lineptr++;
}
else
{
putc (*lineptr, stream);
chars_printed++;
lineptr++;
}
if (chars_printed >= chars_per_line)
{
chars_printed = 0;
lines_printed++;
/* Possible new page. */
if (lines_printed >= lines_per_page - 1)
prompt_for_continue ();
}
}
if (*lineptr == '\n')
{
lines_printed++;
putc ('\n', stream);
lineptr++;
chars_printed = 0;
}
}
}
/* Print ARG1, ARG2, and ARG3 on stdout using format FORMAT. If this
information is going to put the amount written since the last call
to INIIALIZE_MORE_FILTER or the last page break over the page size,
print out a pause message and do a gdb_readline to get the users
permision to continue.
Unlike fprintf, this function does not return a value.
Note that this routine has a restriction that the length of the
final output line must be less than 255 characters *or* it must be
less than twice the size of the format string. This is a very
arbitrary restriction, but it is an internal restriction, so I'll
put it in. This means that the %s format specifier is almost
useless; unless the caller can GUARANTEE that the string is short
enough, fputs_filtered should be used instead.
Note also that a longjmp to top level may occur in this routine
(since prompt_for_continue may do so) so this routine should not be
called when cleanups are not in place. */
void
fprintf_filtered (stream, format, arg1, arg2, arg3, arg4, arg5, arg6)
FILE *stream;
char *format;
int arg1, arg2, arg3, arg4, arg5, arg6;
{
static char *linebuffer = (char *) 0;
static int line_size;
int format_length = strlen (format);
int numchars;
/* Allocated linebuffer for the first time. */
if (!linebuffer)
{
linebuffer = (char *) xmalloc (255);
line_size = 255;
}
/* Reallocate buffer to a larger size if this is necessary. */
if (format_length * 2 > line_size)
{
line_size = format_length * 2;
/* You don't have to copy. */
free (linebuffer);
linebuffer = (char *) xmalloc (line_size);
}
/* This won't blow up if the restrictions described above are
followed. */
(void) sprintf (linebuffer, format, arg1, arg2, arg3);
fputs_filtered (linebuffer, stream);
}
void
printf_filtered (format, arg1, arg2, arg3, arg4, arg5, arg6)
char *format;
int arg1, arg2, arg3, arg4, arg5, arg6;
{
fprintf_filtered (stdout, format, arg1, arg2, arg3, arg4, arg5, arg6);
}
/* Print N spaces. */
void
print_spaces_filtered (n, stream)
int n;
FILE *stream;
{
register char *s = (char *) alloca (n + 1);
register char *t = s;
while (n--)
*t++ = ' ';
*t = '\0';
fputs_filtered (s, stream);
}
#ifdef USG
bcopy (from, to, count)
char *from, *to;
{
memcpy (to, from, count);
}
bcmp (from, to, count)
{
return (memcmp (to, from, count));
}
bzero (to, count)
char *to;
{
while (count--)
*to++ = 0;
}
getwd (buf)
char *buf;
{
getcwd (buf, MAXPATHLEN);
}
char *
index (s, c)
char *s;
{
char *strchr ();
return strchr (s, c);
}
char *
rindex (s, c)
char *s;
{
char *strrchr ();
return strrchr (s, c);
}
#ifndef USG
char *sys_siglist[32] = {
"SIG0",
"SIGHUP",
"SIGINT",
"SIGQUIT",
"SIGILL",
"SIGTRAP",
"SIGIOT",
"SIGEMT",
"SIGFPE",
"SIGKILL",
"SIGBUS",
"SIGSEGV",
"SIGSYS",
"SIGPIPE",
"SIGALRM",
"SIGTERM",
"SIGUSR1",
"SIGUSR2",
"SIGCLD",
"SIGPWR",
"SIGWIND",
"SIGPHONE",
"SIGPOLL",
};
#endif
/* Queue routines */
struct queue {
struct queue *forw;
struct queue *back;
};
insque (item, after)
struct queue *item;
struct queue *after;
{
item->forw = after->forw;
after->forw->back = item;
item->back = after;
after->forw = item;
}
remque (item)
struct queue *item;
{
item->forw->back = item->back;
item->back->forw = item->forw;
}
#endif /* USG */
#ifdef USG
/* There is too much variation in Sys V signal numbers and names, so
we must initialize them at runtime. */
static char undoc[] = "(undocumented)";
char *sys_siglist[NSIG];
#endif /* USG */
extern struct cmd_list_element *setlist;
void
_initialize_utils ()
{
int i;
add_cmd ("screensize", class_support, set_screensize_command,
"Change gdb's notion of the size of the output screen.\n\
The first argument is the number of lines on a page.\n\
The second argument (optional) is the number of characters on a line.",
&setlist);
add_info ("screensize", screensize_info,
"Show gdb's current notion of the size of the output screen.");
/* These defaults will be used if we are unable to get the correct
values from termcap. */
lines_per_page = 24;
chars_per_line = 80;
/* Initialize the screen height and width from termcap. */
{
int termtype = getenv ("TERM");
/* Positive means success, nonpositive means failure. */
int status;
/* 2048 is large enough for all known terminals, according to the
GNU termcap manual. */
char term_buffer[2048];
if (termtype)
{
status = tgetent (term_buffer, termtype);
if (status > 0)
{
int val;
val = tgetnum ("li");
if (val >= 0)
lines_per_page = val;
else
/* The number of lines per page is not mentioned
in the terminal description. This probably means
that paging is not useful (e.g. emacs shell window),
so disable paging. */
lines_per_page = 0;
val = tgetnum ("co");
if (val >= 0)
chars_per_line = val;
}
}
}
#ifdef USG
/* Initialize signal names. */
for (i = 0; i < NSIG; i++)
sys_siglist[i] = undoc;
#ifdef SIGHUP
sys_siglist[SIGHUP ] = "SIGHUP";
#endif
#ifdef SIGINT
sys_siglist[SIGINT ] = "SIGINT";
#endif
#ifdef SIGQUIT
sys_siglist[SIGQUIT ] = "SIGQUIT";
#endif
#ifdef SIGILL
sys_siglist[SIGILL ] = "SIGILL";
#endif
#ifdef SIGTRAP
sys_siglist[SIGTRAP ] = "SIGTRAP";
#endif
#ifdef SIGIOT
sys_siglist[SIGIOT ] = "SIGIOT";
#endif
#ifdef SIGEMT
sys_siglist[SIGEMT ] = "SIGEMT";
#endif
#ifdef SIGFPE
sys_siglist[SIGFPE ] = "SIGFPE";
#endif
#ifdef SIGKILL
sys_siglist[SIGKILL ] = "SIGKILL";
#endif
#ifdef SIGBUS
sys_siglist[SIGBUS ] = "SIGBUS";
#endif
#ifdef SIGSEGV
sys_siglist[SIGSEGV ] = "SIGSEGV";
#endif
#ifdef SIGSYS
sys_siglist[SIGSYS ] = "SIGSYS";
#endif
#ifdef SIGPIPE
sys_siglist[SIGPIPE ] = "SIGPIPE";
#endif
#ifdef SIGALRM
sys_siglist[SIGALRM ] = "SIGALRM";
#endif
#ifdef SIGTERM
sys_siglist[SIGTERM ] = "SIGTERM";
#endif
#ifdef SIGUSR1
sys_siglist[SIGUSR1 ] = "SIGUSR1";
#endif
#ifdef SIGUSR2
sys_siglist[SIGUSR2 ] = "SIGUSR2";
#endif
#ifdef SIGCLD
sys_siglist[SIGCLD ] = "SIGCLD";
#endif
#ifdef SIGCHLD
sys_siglist[SIGCHLD ] = "SIGCHLD";
#endif
#ifdef SIGPWR
sys_siglist[SIGPWR ] = "SIGPWR";
#endif
#ifdef SIGTSTP
sys_siglist[SIGTSTP ] = "SIGTSTP";
#endif
#ifdef SIGTTIN
sys_siglist[SIGTTIN ] = "SIGTTIN";
#endif
#ifdef SIGTTOU
sys_siglist[SIGTTOU ] = "SIGTTOU";
#endif
#ifdef SIGSTOP
sys_siglist[SIGSTOP ] = "SIGSTOP";
#endif
#ifdef SIGXCPU
sys_siglist[SIGXCPU ] = "SIGXCPU";
#endif
#ifdef SIGXFSZ
sys_siglist[SIGXFSZ ] = "SIGXFSZ";
#endif
#ifdef SIGVTALRM
sys_siglist[SIGVTALRM ] = "SIGVTALRM";
#endif
#ifdef SIGPROF
sys_siglist[SIGPROF ] = "SIGPROF";
#endif
#ifdef SIGWINCH
sys_siglist[SIGWINCH ] = "SIGWINCH";
#endif
#ifdef SIGCONT
sys_siglist[SIGCONT ] = "SIGCONT";
#endif
#ifdef SIGURG
sys_siglist[SIGURG ] = "SIGURG";
#endif
#ifdef SIGIO
sys_siglist[SIGIO ] = "SIGIO";
#endif
#ifdef SIGWIND
sys_siglist[SIGWIND ] = "SIGWIND";
#endif
#ifdef SIGPHONE
sys_siglist[SIGPHONE ] = "SIGPHONE";
#endif
#ifdef SIGPOLL
sys_siglist[SIGPOLL ] = "SIGPOLL";
#endif
#endif /* USG */
}