#include "defs.h"
#include "symtab.h"
#include "expression.h"
#include "language.h"
#include "command.h"
#include "source.h"
#include "gdbcmd.h"
#include "frame.h"
#include "value.h"
#include "top.h"
#include <sys/types.h>
#include "gdb_string.h"
#include "gdb_stat.h"
#include <fcntl.h>
#include "gdbcore.h"
#include "gdb_regex.h"
#include "symfile.h"
#include "objfiles.h"
#include "annotate.h"
#include "gdbtypes.h"
#include "linespec.h"
#include "filenames.h"
#include "completer.h"
#include "ui-out.h"
#include "readline/readline.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
#define OPEN_MODE (O_RDONLY | O_BINARY)
#define FDOPEN_MODE FOPEN_RB
void _initialize_source (void);
static int get_filename_and_charpos (struct symtab *, char **);
static void reverse_search_command (char *, int);
static void forward_search_command (char *, int);
static void line_info (char *, int);
static void source_info (char *, int);
static void show_directories (char *, int);
char *source_path;
static struct symtab *current_source_symtab;
static int current_source_line;
static char *pathname_substitutions = NULL;
static char **pathname_substitutions_argv = NULL;
int lines_to_list = 10;
static void
show_lines_to_list (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
fprintf_filtered (file, _("\
Number of source lines gdb will list by default is %s.\n"),
value);
}
static int last_line_listed;
static int first_line_listed;
static struct symtab *last_source_visited = NULL;
static int last_source_error = 0;
int
get_first_line_listed (void)
{
return first_line_listed;
}
int
get_lines_to_list (void)
{
return lines_to_list;
}
struct symtab_and_line
get_current_source_symtab_and_line (void)
{
struct symtab_and_line cursal = { };
cursal.symtab = current_source_symtab;
cursal.line = current_source_line;
cursal.pc = 0;
cursal.end = 0;
return cursal;
}
void
set_default_source_symtab_and_line (void)
{
if (!have_full_symbols () && !have_partial_symbols ())
error (_("No symbol table is loaded. Use the \"file\" command."));
if (current_source_symtab == 0)
select_source_symtab (0);
}
struct symtab_and_line
set_current_source_symtab_and_line (const struct symtab_and_line *sal)
{
struct symtab_and_line cursal = { };
cursal.symtab = current_source_symtab;
cursal.line = current_source_line;
current_source_symtab = sal->symtab;
current_source_line = sal->line;
cursal.pc = 0;
cursal.end = 0;
return cursal;
}
void
clear_current_source_symtab_and_line (void)
{
current_source_symtab = 0;
current_source_line = 0;
}
void
select_source_symtab (struct symtab *s)
{
struct symtabs_and_lines sals;
struct symtab_and_line sal;
struct partial_symtab *ps;
struct partial_symtab *cs_pst = 0;
struct objfile *ofp;
if (s)
{
current_source_symtab = s;
current_source_line = 1;
return;
}
if (current_source_symtab)
return;
if (lookup_symbol (main_name (), 0, VAR_DOMAIN, 0, NULL))
{
sals = decode_line_spec (main_name (), 1);
sal = sals.sals[0];
xfree (sals.sals);
current_source_symtab = sal.symtab;
current_source_line = max (sal.line - (lines_to_list - 1), 1);
if (current_source_symtab)
return;
}
current_source_line = 1;
ALL_OBJFILES (ofp)
{
ALL_SYMTABS (ofp, s)
{
char *name = s->filename;
int len = strlen (name);
if (!(len > 2 && (DEPRECATED_STREQ (&name[len - 2], ".h"))))
{
current_source_symtab = s;
}
}
}
if (current_source_symtab)
return;
ALL_OBJFILES (ofp)
{
ALL_OBJFILE_PSYMTABS (ofp, ps)
{
char *name = ps->filename;
int len = strlen (name);
if (!(len > 2 && (DEPRECATED_STREQ (&name[len - 2], ".h"))))
{
cs_pst = ps;
}
}
}
if (cs_pst)
{
if (cs_pst->readin)
{
internal_error (__FILE__, __LINE__,
_("select_source_symtab: "
"readin pst found and no symtabs."));
}
else
{
current_source_symtab = PSYMTAB_TO_SYMTAB (cs_pst);
}
}
if (current_source_symtab)
return;
error (_("Can't find a default source file"));
}
static void
show_directories (char *ignore, int from_tty)
{
puts_filtered ("Source directories searched: ");
puts_filtered (source_path);
puts_filtered ("\n");
}
void
forget_cached_source_info (void)
{
struct symtab *s;
struct objfile *objfile;
struct partial_symtab *pst;
ALL_OBJFILES (objfile)
{
ALL_OBJFILE_SYMTABS (objfile, s)
{
if (s->line_charpos != NULL)
{
xfree (s->line_charpos);
s->line_charpos = NULL;
}
if (s->fullname != NULL)
{
xfree (s->fullname);
s->fullname = NULL;
}
}
ALL_OBJFILE_PSYMTABS (objfile, pst)
{
if (pst->fullname != NULL)
{
xfree (pst->fullname);
pst->fullname = NULL;
}
}
}
}
void
init_source_path (void)
{
char buf[20];
sprintf (buf, "$cdir%c$cwd", DIRNAME_SEPARATOR);
source_path = xstrdup (buf);
forget_cached_source_info ();
}
void
init_last_source_visited (void)
{
last_source_visited = NULL;
}
void
directory_command (char *dirname, int from_tty)
{
dont_repeat ();
if (dirname == 0)
{
if (from_tty && query (_("Reinitialize source path to empty? ")))
{
xfree (source_path);
init_source_path ();
}
}
else
{
mod_path (dirname, &source_path);
last_source_visited = NULL;
}
if (from_tty)
show_directories ((char *) 0, from_tty);
forget_cached_source_info ();
}
void
mod_path (char *dirname, char **which_path)
{
add_path (dirname, which_path, 1);
}
void
add_path (char *dirname, char **which_path, int parse_separators)
{
char *old = *which_path;
int prefix = 0;
if (dirname == 0)
return;
dirname = xstrdup (dirname);
make_cleanup (xfree, dirname);
do
{
char *name = dirname;
char *p;
struct stat st;
{
char *separator = NULL;
char *space = NULL;
char *tab = NULL;
if (parse_separators)
{
separator = strchr (name, DIRNAME_SEPARATOR);
space = strchr (name, ' ');
tab = strchr (name, '\t');
}
if (separator == 0 && space == 0 && tab == 0)
p = dirname = name + strlen (name);
else if (parse_separators && *name == '"')
{
p = name + 1;
while (*p != '\0')
{
if (p[0] == '"' && p[-1] != '\\')
{
dirname = p + 1;
name += 1;
break;
}
p++;
}
if (*p == '\0')
error ("Unmatched separators in dir pathname");
while (*dirname == DIRNAME_SEPARATOR
|| *dirname == ' '
|| *dirname == '\t')
++dirname;
}
else
{
p = 0;
if (separator != 0 && (p == 0 || separator < p))
p = separator;
if (space != 0 && (p == 0 || space < p))
p = space;
if (tab != 0 && (p == 0 || tab < p))
p = tab;
dirname = p + 1;
while (*dirname == DIRNAME_SEPARATOR
|| *dirname == ' '
|| *dirname == '\t')
++dirname;
}
}
if (!(IS_DIR_SEPARATOR (*name) && p <= name + 1)
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
&& !(p == name + 3 && name[1] == ':')
#endif
&& IS_DIR_SEPARATOR (p[-1]))
--p;
*p = '\0';
while (p > name && p[-1] == '.')
{
if (p - name == 1)
{
name = current_directory;
goto append;
}
else if (p > name + 1 && IS_DIR_SEPARATOR (p[-2]))
{
if (p - name == 2)
{
*--p = '\0';
goto append;
}
else
{
p -= 2;
*p = '\0';
continue;
}
}
else
break;
}
if (name[0] == '~')
name = tilde_expand (name);
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
else if (IS_ABSOLUTE_PATH (name) && p == name + 2)
name = concat (name, ".", (char *)NULL);
#endif
else if (!IS_ABSOLUTE_PATH (name) && name[0] != '$')
name = concat (current_directory, SLASH_STRING, name, (char *)NULL);
else
name = savestring (name, p - name);
make_cleanup (xfree, name);
if (name[0] != '$')
{
if (stat (name, &st) < 0)
{
int save_errno = errno;
fprintf_unfiltered (gdb_stderr, "Warning: ");
print_sys_errmsg (name, save_errno);
}
else if ((st.st_mode & S_IFMT) != S_IFDIR)
warning (_("%s is not a directory."), name);
}
append:
{
unsigned int len = strlen (name);
p = *which_path;
while (1)
{
if (!strncmp (p, name, len)
&& (p[len] == '\0' || p[len] == DIRNAME_SEPARATOR))
{
if (p > *which_path)
p--;
if (prefix > p - *which_path)
goto skip_dup;
strcpy (p, &p[len + 1]);
}
p = strchr (p, DIRNAME_SEPARATOR);
if (p != 0)
++p;
else
break;
}
if (p == 0)
{
char tinybuf[2];
tinybuf[0] = DIRNAME_SEPARATOR;
tinybuf[1] = '\0';
if (prefix)
{
char *temp, c;
c = old[prefix];
old[prefix] = '\0';
temp = concat (old, tinybuf, name, (char *)NULL);
old[prefix] = c;
*which_path = concat (temp, "", &old[prefix], (char *)NULL);
prefix = strlen (temp);
xfree (temp);
}
else
{
*which_path = concat (name, (old[0] ? tinybuf : old),
old, (char *)NULL);
prefix = strlen (name);
}
xfree (old);
old = *which_path;
}
}
skip_dup:;
}
while (*dirname != '\0');
}
static void
source_info (char *ignore, int from_tty)
{
struct symtab *s = current_source_symtab;
if (!s)
{
printf_filtered (_("No current source file.\n"));
return;
}
printf_filtered (_("Current source file is %s\n"), s->filename);
if (s->dirname)
printf_filtered (_("Compilation directory is %s\n"), s->dirname);
if (s->fullname)
printf_filtered (_("Located in %s\n"), s->fullname);
if (s->nlines)
printf_filtered (_("Contains %d line%s.\n"), s->nlines,
s->nlines == 1 ? "" : "s");
printf_filtered (_("Source language is %s.\n"), language_str (s->language));
printf_filtered (_("Compiled with %s debugging format.\n"), s->debugformat);
printf_filtered (_("%s preprocessor macro info.\n"),
s->macro_table ? "Includes" : "Does not include");
}
static int
is_regular_file (const char *name)
{
struct stat st;
const int status = stat (name, &st);
if (status != 0)
return (errno != ENOENT);
return S_ISREG (st.st_mode);
}
int
openp (const char *path, int opts, const char *string,
int mode, int prot,
char **filename_opened)
{
int fd;
char *filename;
const char *p;
const char *p1;
int len;
int alloclen;
if (!path)
path = ".";
mode |= O_BINARY;
if ((opts & OPF_TRY_CWD_FIRST) || IS_ABSOLUTE_PATH (string))
{
int i;
if (is_regular_file (string))
{
filename = alloca (strlen (string) + 1);
strcpy (filename, string);
fd = open (filename, mode, prot);
if (fd >= 0)
goto done;
if (file_exists_p (filename) && fd < 0)
goto done;
}
else
{
filename = NULL;
fd = -1;
}
if (!(opts & OPF_SEARCH_IN_PATH))
for (i = 0; string[i]; i++)
if (IS_DIR_SEPARATOR (string[i]))
goto done;
}
while (IS_DIR_SEPARATOR(string[0]))
string++;
while (string[0] == '.' && IS_DIR_SEPARATOR (string[1]))
string += 2;
alloclen = strlen (path) + strlen (string) + 2;
filename = alloca (alloclen);
fd = -1;
for (p = path; p; p = p1 ? p1 + 1 : 0)
{
p1 = strchr (p, DIRNAME_SEPARATOR);
if (p1)
len = p1 - p;
else
len = strlen (p);
if (len == 4 && p[0] == '$' && p[1] == 'c'
&& p[2] == 'w' && p[3] == 'd')
{
int newlen;
len = strlen (current_directory);
newlen = len + strlen (string) + 2;
if (newlen > alloclen)
{
alloclen = newlen;
filename = alloca (alloclen);
}
strcpy (filename, current_directory);
}
else
{
strncpy (filename, p, len);
filename[len] = 0;
}
while (len > 0 && IS_DIR_SEPARATOR (filename[len - 1]))
filename[--len] = 0;
strcat (filename + len, SLASH_STRING);
strcat (filename, string);
if (is_regular_file (filename))
{
fd = open (filename, mode);
if (fd >= 0)
break;
}
}
done:
if (filename_opened)
{
if (fd < 0)
*filename_opened = NULL;
else if (IS_ABSOLUTE_PATH (filename))
*filename_opened = xfullpath (filename);
else
{
char *f = concat (current_directory,
IS_DIR_SEPARATOR (current_directory[strlen (current_directory) - 1])
? "" : SLASH_STRING,
filename, (char *)NULL);
*filename_opened = xfullpath (f);
xfree (f);
}
}
return fd;
}
int
source_full_path_of (char *filename, char **full_pathname)
{
int fd;
fd = openp (source_path, OPF_TRY_CWD_FIRST | OPF_SEARCH_IN_PATH, filename,
O_RDONLY, 0, full_pathname);
if (fd < 0)
{
*full_pathname = NULL;
return 0;
}
close (fd);
return 1;
}
int
open_source_file_fullpath (const char *dirname, const char *filename,
char **fullname)
{
char *path = NULL;
char *ss = NULL;
char **psubs = pathname_substitutions_argv;
char **tsub = NULL;
int nsubs = 0;
int csub = 0;
int result;
CHECK_FATAL (filename != NULL);
if (dirname != NULL)
path = (char *) alloca (strlen (dirname) + strlen (filename) + 2);
else
path = (char *) alloca (strlen (filename) + 2);
if (filename[0] == '/' || dirname == NULL)
sprintf (path, "%s", filename);
else
sprintf (path, "%s/%s", dirname, filename);
for (;;)
{
ss = strstr (path, "//");
if (ss == NULL)
break;
memmove (ss, ss + 1, strlen (ss + 1) + 1);
}
for (;;)
{
ss = strstr (path, "/./");
if (ss == NULL)
break;
memmove (ss, ss + 2, strlen (ss + 2) + 1);
}
nsubs = 0;
if (psubs != NULL)
for (tsub = psubs; *tsub != NULL; tsub++)
nsubs++;
if ((nsubs % 2) != 0)
error ("unable to parse pathname-substitutions");
nsubs /= 2;
for (csub = 0; csub < nsubs; csub++)
{
char *from = psubs[csub * 2];
char *to = psubs[(csub * 2) + 1];
if (strncmp (path, from, strlen (from)) == 0)
{
char *remainder = path + strlen (from);
char *npath = (char *) alloca (strlen (to) + strlen (remainder) + 1);
sprintf (npath, "%s%s", to, remainder);
result = openp ("", 0, npath, OPEN_MODE, 0, fullname);
if (result >= 0)
return result;
}
}
return -1;
}
static void
set_pathname_substitution (char *args, int from_tty, struct cmd_list_element * c)
{
forget_cached_source_info ();
last_source_error = 0;
int success = 0;
if (pathname_substitutions_argv != NULL)
{
freeargv (pathname_substitutions_argv);
pathname_substitutions_argv = NULL;
}
if (pathname_substitutions && pathname_substitutions[0])
{
pathname_substitutions_argv = buildargv (pathname_substitutions);
if (pathname_substitutions_argv && pathname_substitutions_argv[0] != NULL)
{
int i;
success = 1;
for (i = 0; success && pathname_substitutions_argv[i] != NULL; i += 2)
{
success = pathname_substitutions_argv[i+1] != NULL;
}
}
if (!success)
{
warning ("An even number of paths must be given, surround paths "
"constaining spaces with single or double quotes.");
freeargv (pathname_substitutions_argv);
pathname_substitutions_argv = NULL;
}
}
}
void
add_one_pathname_substitution (const char *old, const char *new)
{
int arrsize = 0;
if (pathname_substitutions_argv == NULL)
{
pathname_substitutions_argv = (char **) xmalloc (sizeof (char *) * 3);
pathname_substitutions_argv[0] = xstrdup (old);
pathname_substitutions_argv[1] = xstrdup (new);
pathname_substitutions_argv[2] = NULL;
return;
}
while (pathname_substitutions_argv[arrsize] != NULL)
{
if (arrsize % 2 == 0)
if (strcmp (pathname_substitutions_argv[arrsize], old) == 0)
return;
arrsize++;
}
arrsize++;
pathname_substitutions_argv = (char **) xrealloc
(pathname_substitutions_argv,
sizeof (char *) * (arrsize + 2));
pathname_substitutions_argv[arrsize - 1] = xstrdup (old);
pathname_substitutions_argv[arrsize] = xstrdup (new);
pathname_substitutions_argv[arrsize + 1] = NULL;
}
static void
show_pathname_substitutions (struct ui_file *file, int from_tty,
struct cmd_list_element *c,
const char *value)
{
char *from;
char *to;
int i = 0;
if (pathname_substitutions_argv)
{
fprintf_filtered (file, _("Current source pathname substitions:\n"));
for (i = 0; pathname_substitutions_argv[i] != NULL; i += 2)
{
from = pathname_substitutions_argv[i];
to = pathname_substitutions_argv[i+1];
fprintf_filtered (file, _(" [%i] '%s' -> '%s'\n"), i/2 + 1, from, to);
}
}
else
fprintf_filtered (file, _("No source path substitions are currently set.\n"));
if (pathname_substitutions)
fprintf_filtered (file, _("\nLast source pathname substition command was:\n"
"set pathname-substitutions%s%s\n"),
pathname_substitutions[0] ? " " : "",
pathname_substitutions);
}
int
find_and_open_source (struct objfile *objfile,
const char *filename,
const char *dirname,
char **fullname)
{
char *path = source_path;
const char *p;
int result;
if (*fullname)
{
result = open (*fullname, OPEN_MODE);
if (result >= 0)
return result;
xfree (*fullname);
*fullname = NULL;
}
if (dirname != NULL)
{
#define cdir_len 5
p = (char *) strstr (source_path, "$cdir");
if (p && (p == path || p[-1] == DIRNAME_SEPARATOR)
&& (p[cdir_len] == DIRNAME_SEPARATOR || p[cdir_len] == '\0'))
{
int len;
path = (char *)
alloca (strlen (source_path) + 1 + strlen (dirname) + 1);
len = p - source_path;
strncpy (path, source_path, len);
strcpy (path + len, dirname);
strcat (path + len, source_path + len + cdir_len);
}
}
result = open_source_file_fullpath (dirname, filename, fullname);
if (result < 0)
result = openp (path, OPF_SEARCH_IN_PATH, filename, OPEN_MODE, 0, fullname);
if (result < 0)
{
p = lbasename (filename);
if (p != filename)
result = openp (path, OPF_SEARCH_IN_PATH, p, OPEN_MODE, 0, fullname);
}
if (result >= 0)
{
char *tmp_fullname;
tmp_fullname = *fullname;
*fullname = xstrdup (tmp_fullname);
xfree (tmp_fullname);
}
return result;
}
int
open_source_file (struct symtab *s)
{
if (!s)
return -1;
return find_and_open_source (s->objfile, s->filename, s->dirname,
&s->fullname);
}
char *
symtab_to_fullname (struct symtab *s)
{
int r;
if (!s)
return NULL;
if (s->fullname)
return s->fullname;
r = find_and_open_source (s->objfile, s->filename, s->dirname,
&s->fullname);
if (r)
{
close (r);
return s->fullname;
}
return NULL;
}
char *
psymtab_to_fullname (struct partial_symtab *ps)
{
int r;
if (!ps)
return NULL;
if (ps->fullname)
return ps->fullname;
r = find_and_open_source (ps->objfile, ps->filename, ps->dirname,
&ps->fullname);
if (r)
{
close (r);
return ps->fullname;
}
return NULL;
}
void
find_source_lines (struct symtab *s, int desc)
{
struct stat st;
int nlines = 0;
int lines_allocated = 1000;
int *line_charpos;
long mtime = 0;
int size;
line_charpos = (int *) xmalloc (lines_allocated * sizeof (int));
if (fstat (desc, &st) < 0)
perror_with_name (s->filename);
if (s && s->objfile && s->objfile->obfd)
mtime = bfd_get_mtime (s->objfile->obfd);
else if (exec_bfd)
mtime = bfd_get_mtime (exec_bfd);
if (mtime && mtime < st.st_mtime)
warning (_("Source file is more recent than executable."));
#ifdef LSEEK_NOT_LINEAR
{
char c, oldc;
int eol = 0;
line_charpos[0] = lseek (desc, 0, SEEK_CUR);
nlines = 1;
while (myread (desc, &c, 1) > 0)
{
if (eol && ((c != '\n') || (oldc != '\r')))
{
eol = 0;
if (nlines == lines_allocated)
{
lines_allocated *= 2;
line_charpos =
(int *) xrealloc ((char *) line_charpos,
sizeof (int) * lines_allocated);
}
line_charpos[nlines++] = lseek (desc, 0, SEEK_CUR) - 1;
}
if ((c == '\n') || (c == '\r'))
{
eol = 1;
oldc = c;
}
}
}
#else
{
struct cleanup *old_cleanups;
int eol = 0;
char oldc;
register char *data, *p, *end;
size = (int) st.st_size;
data = (char *) xmalloc (size);
old_cleanups = make_cleanup (xfree, data);
size = myread (desc, data, size);
if (size < 0)
perror_with_name (s->filename);
end = data + size;
p = data;
line_charpos[0] = 0;
nlines = 1;
while (p != end)
{
char c = *p++;
if (eol && ((c != '\n') || (oldc != '\r')))
{
eol = 0;
if (nlines == lines_allocated)
{
lines_allocated *= 2;
line_charpos =
(int *) xrealloc ((char *) line_charpos,
sizeof (int) * lines_allocated);
}
line_charpos[nlines++] = p - data - 1;
}
if ((c == '\n') || (c == '\r'))
{
eol = 1;
oldc = c;
}
}
do_cleanups (old_cleanups);
}
#endif
s->nlines = nlines;
s->line_charpos =
(int *) xrealloc ((char *) line_charpos, nlines * sizeof (int));
}
#if 0
int
source_line_charpos (struct symtab *s, int line)
{
if (!s)
return 0;
if (!s->line_charpos || line <= 0)
return 0;
if (line > s->nlines)
line = s->nlines;
return s->line_charpos[line - 1];
}
int
source_charpos_line (struct symtab *s, int chr)
{
int line = 0;
int *lnp;
if (s == 0 || s->line_charpos == 0)
return 0;
lnp = s->line_charpos;
while (line < s->nlines && *lnp <= chr)
{
line++;
lnp++;
}
if (line >= s->nlines)
line = s->nlines;
return line;
}
#endif
static int
get_filename_and_charpos (struct symtab *s, char **fullname)
{
int desc, linenums_changed = 0;
desc = open_source_file (s);
if (desc < 0)
{
if (fullname)
*fullname = NULL;
return 0;
}
if (fullname)
*fullname = s->fullname;
if (s->line_charpos == 0)
linenums_changed = 1;
if (linenums_changed)
find_source_lines (s, desc);
close (desc);
return linenums_changed;
}
int
identify_source_line (struct symtab *s, int line, int mid_statement,
CORE_ADDR pc)
{
if (s->line_charpos == 0)
get_filename_and_charpos (s, (char **) NULL);
if (s->fullname == 0)
return 0;
if (line > s->nlines)
return 0;
annotate_source (s->fullname, line, s->line_charpos[line - 1],
mid_statement, pc);
current_source_line = line;
first_line_listed = line;
last_line_listed = line;
current_source_symtab = s;
return 1;
}
static void print_source_lines_base (struct symtab *s, int line, int nlines,
int noerror);
static void
print_source_lines_base (struct symtab *s, int line, int nlines, int noerror)
{
int desc;
FILE *stream;
int stopline = line + nlines;
int c, oldc;
int eol;
int just_kidding_about_error = 0;
current_source_symtab = s;
current_source_line = line;
first_line_listed = line;
if (ui_out_test_flags (uiout, ui_source_list))
{
if ((s != last_source_visited) || (!last_source_error))
{
last_source_visited = s;
desc = open_source_file (s);
}
else
{
desc = last_source_error;
noerror = 1;
}
}
else
{
desc = -1;
noerror = 1;
just_kidding_about_error = 1;
}
if (desc < 0)
{
if (!just_kidding_about_error)
last_source_error = desc;
if (!noerror)
{
char *name = alloca (strlen (s->filename) + 100);
sprintf (name, "%d\t%s", line, s->filename);
print_sys_errmsg (name, errno);
}
else
ui_out_field_int (uiout, "line", line);
ui_out_text (uiout, "\tin ");
ui_out_field_string (uiout, "file", s->filename);
ui_out_text (uiout, "\n");
return;
}
last_source_error = 0;
if (print_source_lines_hook)
{
int local_stopline;
if (stopline == line)
local_stopline = line;
else
local_stopline = stopline - 1;
close (desc);
print_source_lines_hook (s, line, line - local_stopline);
last_line_listed = local_stopline;
return;
}
if (s->line_charpos == 0)
find_source_lines (s, desc);
if (line < 1 || line > s->nlines)
{
close (desc);
error (_("Line number %d out of range; %s has %d lines."),
line, s->filename, s->nlines);
}
if (lseek (desc, s->line_charpos[line - 1], 0) < 0)
{
close (desc);
perror_with_name (s->filename);
}
stream = fdopen (desc, FDOPEN_MODE);
clearerr (stream);
c = fgetc (stream);
eol = 0;
while (nlines-- > 0)
{
if (c == EOF)
break;
last_line_listed = current_source_line;
ui_out_text_fmt (uiout, "%d\t", current_source_line++);
for (;;)
{
if (eol && ((c != '\n') || (oldc != '\r')))
{
eol = 0;
break;
}
else if (c < 040 && c != '\t' && c != '\n' && c != '\r')
{
ui_out_text_fmt (uiout, "^%c", c + 0100);
}
else if (c == 0177)
ui_out_text (uiout, "^?");
else if ((c == '\r') || (c == '\n'))
{
eol = 1;
oldc = c;
}
else
{
ui_out_text_fmt (uiout, "%c", c);
}
c = fgetc (stream);
if (c == EOF)
break;
}
ui_out_text (uiout, "\n");
}
fclose (stream);
}
void
print_source_lines (struct symtab *s, int line, int nlines, int noerror)
{
print_source_lines_base (s, line, nlines, noerror);
}
static void
line_info (char *arg, int from_tty)
{
struct symtabs_and_lines sals;
struct symtab_and_line sal;
CORE_ADDR start_pc, end_pc;
int i;
init_sal (&sal);
if (arg == 0)
{
sal.symtab = current_source_symtab;
sal.line = last_line_listed;
sals.nelts = 1;
sals.sals = (struct symtab_and_line *)
xmalloc (sizeof (struct symtab_and_line));
sals.sals[0] = sal;
}
else
{
sals = decode_line_spec_1 (arg, 0);
dont_repeat ();
}
for (i = 0; i < sals.nelts; i++)
{
sal = sals.sals[i];
if (sal.symtab == 0)
{
printf_filtered (_("No line number information available"));
if (sal.pc != 0)
{
printf_filtered (" for address ");
wrap_here (" ");
print_address (sal.pc, gdb_stdout);
}
else
printf_filtered (".");
printf_filtered ("\n");
}
else if (sal.line > 0
&& find_line_pc_range (sal, &start_pc, &end_pc))
{
if (start_pc == end_pc)
{
printf_filtered ("Line %d of \"%s\"",
sal.line, sal.symtab->filename);
wrap_here (" ");
printf_filtered (" is at address ");
print_address (start_pc, gdb_stdout);
wrap_here (" ");
printf_filtered (" but contains no code.\n");
}
else
{
printf_filtered ("Line %d of \"%s\"",
sal.line, sal.symtab->filename);
wrap_here (" ");
printf_filtered (" starts at address ");
print_address (start_pc, gdb_stdout);
wrap_here (" ");
printf_filtered (" and ends at ");
print_address (end_pc, gdb_stdout);
printf_filtered (".\n");
}
set_next_address (start_pc);
last_line_listed = sal.line + 1;
if (annotation_level && sals.nelts == 1)
identify_source_line (sal.symtab, sal.line, 0, start_pc);
}
else
printf_filtered (_("Line number %d is out of range for \"%s\".\n"),
sal.line, sal.symtab->filename);
}
xfree (sals.sals);
}
void convert_sal (struct symtab_and_line *sal)
{
struct symtab *symtab = NULL;
struct linetable *linetable = NULL;
unsigned int i;
int fd;
symtab = sal->symtab;
if (symtab == NULL)
return;
linetable = LINETABLE (symtab);
if (linetable == NULL)
return;
if (! linetable->lines_are_chars)
return;
if (symtab->line_charpos == 0)
{
fd = open_source_file (symtab);
if (fd < 0)
return;
find_source_lines (symtab, fd);
close (fd);
}
for (i = 1; i < symtab->nlines; i++)
{
if ((symtab->line_charpos[i] >= (sal->line + 1))
&& (symtab->line_charpos[i - 1] <= (sal->line + 1)))
{
sal->line = i;
break;
}
}
}
static void
forward_search_command (char *regex, int from_tty)
{
int c;
int desc;
FILE *stream;
int line;
char *msg;
line = last_line_listed + 1;
msg = (char *) re_comp (regex);
if (msg)
error (("%s"), msg);
if (current_source_symtab == 0)
select_source_symtab (0);
desc = open_source_file (current_source_symtab);
if (desc < 0)
perror_with_name (current_source_symtab->filename);
if (current_source_symtab->line_charpos == 0)
find_source_lines (current_source_symtab, desc);
if (line < 1 || line > current_source_symtab->nlines)
{
close (desc);
error (_("Expression not found"));
}
if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
{
close (desc);
perror_with_name (current_source_symtab->filename);
}
stream = fdopen (desc, FDOPEN_MODE);
clearerr (stream);
while (1)
{
static char *buf = NULL;
char *p;
int cursize, newsize;
cursize = 256;
buf = xmalloc (cursize);
p = buf;
c = getc (stream);
if (c == EOF)
break;
do
{
*p++ = c;
if (p - buf == cursize)
{
newsize = cursize + cursize / 2;
buf = xrealloc (buf, newsize);
p = buf + cursize;
cursize = newsize;
}
}
while (c != '\n' && (c = getc (stream)) >= 0);
if (p - buf > 1 && p[-2] == '\r')
{
p--;
p[-1] = '\n';
}
*p = 0;
if (re_exec (buf) > 0)
{
fclose (stream);
print_source_lines (current_source_symtab, line, 1, 0);
set_internalvar (lookup_internalvar ("_"),
value_from_longest (builtin_type_int,
(LONGEST) line));
current_source_line = max (line - lines_to_list / 2, 1);
return;
}
line++;
}
printf_filtered (_("Expression not found\n"));
fclose (stream);
}
static void
reverse_search_command (char *regex, int from_tty)
{
int c;
int desc;
FILE *stream;
int line;
char *msg;
line = last_line_listed - 1;
msg = (char *) re_comp (regex);
if (msg)
error (("%s"), msg);
if (current_source_symtab == 0)
select_source_symtab (0);
desc = open_source_file (current_source_symtab);
if (desc < 0)
perror_with_name (current_source_symtab->filename);
if (current_source_symtab->line_charpos == 0)
find_source_lines (current_source_symtab, desc);
if (line < 1 || line > current_source_symtab->nlines)
{
close (desc);
error (_("Expression not found"));
}
if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
{
close (desc);
perror_with_name (current_source_symtab->filename);
}
stream = fdopen (desc, FDOPEN_MODE);
clearerr (stream);
while (line > 1)
{
char buf[4096];
char *p = buf;
c = getc (stream);
if (c == EOF)
break;
do
{
*p++ = c;
}
while (c != '\n' && (c = getc (stream)) >= 0);
if (p - buf > 1 && p[-2] == '\r')
{
p--;
p[-1] = '\n';
}
*p = 0;
if (re_exec (buf) > 0)
{
fclose (stream);
print_source_lines (current_source_symtab, line, 1, 0);
set_internalvar (lookup_internalvar ("_"),
value_from_longest (builtin_type_int,
(LONGEST) line));
current_source_line = max (line - lines_to_list / 2, 1);
return;
}
line--;
if (fseek (stream, current_source_symtab->line_charpos[line - 1], 0) < 0)
{
fclose (stream);
perror_with_name (current_source_symtab->filename);
}
}
printf_filtered (_("Expression not found\n"));
fclose (stream);
return;
}
void
_initialize_source (void)
{
struct cmd_list_element *c;
current_source_symtab = 0;
init_source_path ();
re_set_syntax (RE_SYNTAX_GREP);
c = add_cmd ("directory", class_files, directory_command, _("\
Add directory DIR to beginning of search path for source files.\n\
Forget cached info on source file locations and line positions.\n\
DIR can also be $cwd for the current working directory, or $cdir for the\n\
directory in which the source file was compiled into object code.\n\
With no argument, reset the search path to $cdir:$cwd, the default."),
&cmdlist);
if (dbx_commands)
add_com_alias ("use", "directory", class_files, 0);
set_cmd_completer (c, filename_completer);
add_cmd ("directories", no_class, show_directories, _("\
Current search path for finding source files.\n\
$cwd in the path means the current working directory.\n\
$cdir in the path means the compilation directory of the source file."),
&showlist);
if (xdb_commands)
{
add_com_alias ("D", "directory", class_files, 0);
add_cmd ("ld", no_class, show_directories, _("\
Current search path for finding source files.\n\
$cwd in the path means the current working directory.\n\
$cdir in the path means the compilation directory of the source file."),
&cmdlist);
}
add_info ("source", source_info,
_("Information about the current source file."));
add_info ("line", line_info, _("\
Core addresses of the code for a source line.\n\
Line can be specified as\n\
LINENUM, to list around that line in current file,\n\
FILE:LINENUM, to list around that line in that file,\n\
FUNCTION, to list around beginning of that function,\n\
FILE:FUNCTION, to distinguish among like-named static functions.\n\
Default is to describe the last source line that was listed.\n\n\
This sets the default address for \"x\" to the line's first instruction\n\
so that \"x/i\" suffices to start examining the machine code.\n\
The address is also stored as the value of \"$_\"."));
add_com ("forward-search", class_files, forward_search_command, _("\
Search for regular expression (see regex(3)) from last line listed.\n\
The matching line number is also stored as the value of \"$_\"."));
add_com_alias ("search", "forward-search", class_files, 0);
add_com ("reverse-search", class_files, reverse_search_command, _("\
Search backward for regular expression (see regex(3)) from last line listed.\n\
The matching line number is also stored as the value of \"$_\"."));
if (xdb_commands)
{
add_com_alias ("/", "forward-search", class_files, 0);
add_com_alias ("?", "reverse-search", class_files, 0);
}
add_setshow_integer_cmd ("listsize", class_support, &lines_to_list, _("\
Set number of source lines gdb will list by default."), _("\
Show number of source lines gdb will list by default."), NULL,
NULL,
show_lines_to_list,
&setlist, &showlist);
add_setshow_string_cmd ("pathname-substitutions", class_support,
&pathname_substitutions, _("\
Set string substitutions to be used when searching for source files."), _("\
Show string substitutions to be used when searching for source files."), _("\
The string substitutions are space separated pairs of paths where each\n\
string can be surrounded by quotes if a path contains spaces.\n\
\n\
Example:\n\
pathname-substitutions /path1/from /new/path1/to '/path2/with space/from' /path2/to"),
set_pathname_substitution,
show_pathname_substitutions,
&setlist, &showlist);
}