#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>
#ifdef CRLF_SOURCE_FILES
#ifndef O_BINARY
#define O_BINARY 0
#endif
#define OPEN_MODE (O_RDONLY | O_BINARY)
#define FDOPEN_MODE FOPEN_RB
#else
#define OPEN_MODE O_RDONLY
#define FDOPEN_MODE FOPEN_RT
#endif
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 ambiguous_line_spec (struct symtabs_and_lines *);
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;
char *pathname_substitutions = NULL;
int lines_to_list = 10;
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)
{
struct symtab_and_line cursal;
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 (register 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_NAMESPACE, 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 && (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 && (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)
{
register struct symtab *s;
register struct objfile *objfile;
struct partial_symtab *pst;
ALL_OBJFILES (objfile)
{
ALL_OBJFILE_SYMTABS (objfile, s)
{
if (s->line_charpos != NULL)
{
xmfree (objfile->md, s->line_charpos);
s->line_charpos = NULL;
}
if (s->fullname != NULL)
{
xmfree (objfile->md, 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;
register 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, ".", NULL);
#endif
else if (!IS_ABSOLUTE_PATH (name) && name[0] != '$')
name = concat (current_directory, SLASH_STRING, name, 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:
{
register 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, NULL);
old[prefix] = c;
*which_path = concat (temp, "", &old[prefix], NULL);
prefix = strlen (temp);
xfree (temp);
}
else
{
*which_path = concat (name, (old[0] ? tinybuf : old), old, NULL);
prefix = strlen (name);
}
xfree (old);
old = *which_path;
}
}
skip_dup:;
}
while (*dirname != '\0');
}
static void
source_info (char *ignore, int from_tty)
{
register 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 try_cwd_first, const char *string,
int mode, int prot,
char **filename_opened)
{
register int fd;
register char *filename;
const char *p;
const char *p1;
register int len;
int alloclen;
if (!path)
path = ".";
#if defined(_WIN32) || defined(__CYGWIN__)
mode |= O_BINARY;
#endif
if (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;
}
else
{
filename = NULL;
fd = -1;
}
for (i = 0; string[i]; i++)
if (IS_DIR_SEPARATOR (string[i]))
goto done;
}
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, 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, 1, filename, O_RDONLY, 0, full_pathname);
if (fd < 0)
{
*full_pathname = NULL;
return 0;
}
close (fd);
return 1;
}
int
open_source_file_fullpath (s)
struct symtab *s;
{
char *path = NULL;
char *ss = NULL;
char **psubs = NULL;
char **tsub = NULL;
int nsubs = 0;
int csub = 0;
int result;
CHECK_FATAL (s->filename != NULL);
if (s->dirname != NULL) {
path = (char *) alloca (strlen (s->dirname) + strlen (s->filename) + 2);
} else {
path = (char *) alloca (strlen (s->filename) + 2);
}
if ((s->filename[0] == '/') || (s->dirname == NULL)) {
sprintf (path, "%s", s->filename);
} else {
sprintf (path, "%s/%s", s->dirname, s->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);
}
if (pathname_substitutions != NULL) {
psubs = buildargv (pathname_substitutions);
if (psubs == NULL) {
error ("unable to parse pathname-substitutions");
}
}
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, &s->fullname);
if (result >= 0) {
return result;
}
}
}
return -1;
}
int
open_source_file (struct symtab *s)
{
char *path = source_path;
const char *p;
int result;
char *fullname;
if (s->fullname)
{
result = open (s->fullname, OPEN_MODE, 0);
if (result >= 0)
return result;
xmfree (s->objfile->md, s->fullname);
s->fullname = NULL;
}
if (s->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 (s->dirname) + 1);
len = p - source_path;
strncpy (path, source_path, len);
strcpy (path + len, s->dirname);
strcat (path + len, source_path + len + cdir_len);
}
}
result = open_source_file_fullpath (s);
if (result < 0)
result = openp (path, 0, s->filename, OPEN_MODE, 0, &fullname);
if (result < 0)
{
p = lbasename (s->filename);
if (p != s->filename)
result = openp (path, 0, p, OPEN_MODE, 0, &fullname);
}
if (result >= 0)
{
s->fullname = mstrsave (s->objfile->md, fullname);
xfree (fullname);
}
return result;
}
char *
symtab_to_filename (struct symtab *s)
{
int fd;
if (!s)
return NULL;
if (s->fullname)
return s->fullname;
fd = open_source_file (s);
if (fd < 0)
return s->filename;
close (fd);
return s->fullname;
}
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 *) xmmalloc (s->objfile->md,
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.\n");
}
#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 *) xmrealloc (s->objfile->md, (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 c, 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 *) xmrealloc (s->objfile->md, (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 *) xmrealloc (s->objfile->md, (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 (register struct symtab *s, register int chr)
{
register int line = 0;
register 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)
{
register 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 stopline,
int noerror);
static void
print_source_lines_base (struct symtab *s, int line, int nlines, int noerror)
{
register int desc;
register FILE *stream;
int stopline = line + nlines;
int c, oldc;
int eol;
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;
}
if (desc < 0)
{
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);
}
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
ambiguous_line_spec (struct symtabs_and_lines *sals)
{
int i;
for (i = 0; i < sals->nelts; ++i)
if (sals->sals[i].symtab != 0)
printf_filtered ("file: \"%s\", line number: %d\n",
sals->sals[i].symtab->filename, sals->sals[i].line);
else
printf_filtered ("No file and line information.\n");
}
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);
}
static void
forward_search_command (char *regex, int from_tty)
{
register int c;
register int desc;
register FILE *stream;
int line;
char *msg;
line = last_line_listed + 1;
msg = (char *) re_comp (regex);
if (msg)
error (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;
register 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);
#ifdef CRLF_SOURCE_FILES
if (p - buf > 1 && p[-2] == '\r')
{
p--;
p[-1] = '\n';
}
#endif
*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)
{
register int c;
register int desc;
register FILE *stream;
int line;
char *msg;
line = last_line_listed - 1;
msg = (char *) re_comp (regex);
if (msg)
error (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];
register char *p = buf;
c = getc (stream);
if (c == EOF)
break;
do
{
*p++ = c;
}
while (c != '\n' && (c = getc (stream)) >= 0);
#ifdef CRLF_SOURCE_FILES
if (p - buf > 1 && p[-2] == '\r')
{
p--;
p[-1] = '\n';
}
#endif
*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,
concat ("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 \"$_\".", NULL));
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_show_from_set
(add_set_cmd ("listsize", class_support, var_uinteger,
(char *) &lines_to_list,
"Set number of source lines gdb will list by default.",
&setlist),
&showlist);
add_show_from_set
(add_set_cmd ("pathname-substitutions", class_support, var_string,
(char *) &pathname_substitutions,
"Set string substitutions to be used when searching for source files.",
&setlist),
&showlist);
}