#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"
#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 list_command (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;
struct symtab *current_source_symtab;
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;
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;
for (ofp = object_files; ofp != NULL; ofp = ofp->next)
{
for (s = ofp->symtabs; s; s = s->next)
{
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;
for (ofp = object_files; ofp != NULL; ofp = ofp->next)
{
for (ps = ofp->psymtabs; ps != NULL; ps = ps->next)
{
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;
for (objfile = object_files; objfile != NULL; objfile = objfile->next)
{
for (s = objfile->symtabs; s != NULL; s = s->next)
{
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
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)
{
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 = strchr (name, DIRNAME_SEPARATOR);
char *space = strchr (name, ' ');
char *tab = strchr (name, '\t');
if (separator == 0 && space == 0 && tab == 0)
p = dirname = name + strlen (name);
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);
}
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;
filename = alloca (strlen (string) + 1);
strcpy (filename, string);
fd = open (filename, mode, prot);
if (fd >= 0)
goto done;
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);
fd = open (filename, mode, 0);
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, &s->fullname);
if (result < 0)
{
p = lbasename (s->filename);
if (p != s->filename)
result = openp (path, 0, p, OPEN_MODE, 0, &s->fullname);
}
if (result >= 0)
{
fullname = s->fullname;
s->fullname = mstrsave (s->objfile->md, s->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
list_command (char *arg, int from_tty)
{
struct symtabs_and_lines sals, sals_end;
struct symtab_and_line sal, sal_end;
struct symbol *sym;
char *arg1;
int no_end = 1;
int dummy_end = 0;
int dummy_beg = 0;
int linenum_beg = 0;
char *p;
if (!have_full_symbols () && !have_partial_symbols ())
error ("No symbol table is loaded. Use the \"file\" command.");
if (current_source_symtab == 0 &&
(arg == 0 || arg[0] == '+' || arg[0] == '-'))
select_source_symtab (0);
if (arg == 0 || STREQ (arg, "+"))
{
if (current_source_symtab == 0)
error ("No default source file yet. Do \"help list\".");
print_source_lines (current_source_symtab, current_source_line,
lines_to_list, 0);
return;
}
if (STREQ (arg, "-"))
{
int firstline;
if (current_source_symtab == 0)
error ("No default source file yet. Do \"help list\".");
firstline = max (first_line_listed - lines_to_list, 1);
print_source_lines (current_source_symtab,
firstline, first_line_listed - firstline, 0);
return;
}
arg1 = arg;
if (*arg1 == ',')
dummy_beg = 1;
else
{
sals = decode_line_1 (&arg1, 0, 0, 0, 0);
if (!sals.nelts)
return;
if (sals.nelts > 1)
{
ambiguous_line_spec (&sals);
xfree (sals.sals);
return;
}
sal = sals.sals[0];
xfree (sals.sals);
}
for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++);
linenum_beg = (p == arg1);
while (*arg1 == ' ' || *arg1 == '\t')
arg1++;
if (*arg1 == ',')
{
no_end = 0;
arg1++;
while (*arg1 == ' ' || *arg1 == '\t')
arg1++;
if (*arg1 == 0)
dummy_end = 1;
else
{
if (dummy_beg)
sals_end = decode_line_1 (&arg1, 0, 0, 0, 0);
else
sals_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line, 0);
if (sals_end.nelts == 0)
return;
if (sals_end.nelts > 1)
{
ambiguous_line_spec (&sals_end);
xfree (sals_end.sals);
return;
}
sal_end = sals_end.sals[0];
xfree (sals_end.sals);
}
}
if (*arg1)
error ("Junk at end of line specification.");
if (!no_end && !dummy_beg && !dummy_end
&& sal.symtab != sal_end.symtab)
error ("Specified start and end are in different files.");
if (dummy_beg && dummy_end)
error ("Two empty args do not say what lines to list.");
if (*arg == '*')
{
if (sal.symtab == 0)
error ("No source file for address %s.",
local_hex_string ((unsigned long) sal.pc));
sym = find_pc_function (sal.pc);
if (sym)
{
print_address_numeric (sal.pc, 1, gdb_stdout);
printf_filtered (" is in ");
fputs_filtered (SYMBOL_SOURCE_NAME (sym), gdb_stdout);
printf_filtered (" (%s:%d).\n", sal.symtab->filename, sal.line);
}
else
{
print_address_numeric (sal.pc, 1, gdb_stdout);
printf_filtered (" is at %s:%d.\n",
sal.symtab->filename, sal.line);
}
}
if (!linenum_beg && sal.symtab == 0)
error ("No line number known for %s.", arg);
#if 0
if (from_tty)
#endif
*arg = 0;
if (dummy_beg && sal_end.symtab == 0)
error ("No default source file yet. Do \"help list\".");
if (dummy_beg)
{
int firstline;
firstline = max (sal_end.line - (lines_to_list - 1), 1);
print_source_lines (sal_end.symtab, firstline, (sal_end.line + 1) - firstline, 0);
}
else if (sal.symtab == 0)
error ("No default source file yet. Do \"help list\".");
else if (no_end)
{
int first_line = sal.line - lines_to_list / 2;
if (first_line < 1) first_line = 1;
print_source_lines (sal.symtab, first_line, lines_to_list, 0);
}
else
{
int lastline = (dummy_end ? sal.line + lines_to_list : sal_end.line + 1);
print_source_lines (sal.symtab, sal.line, lastline - sal.line, 0);
}
}
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_com ("list", class_files, list_command,
concat ("List specified function or line.\n\
With no argument, lists ten more lines after or around previous listing.\n\
\"list -\" lists the ten lines before a previous ten-line listing.\n\
One argument specifies a line, and ten lines are listed around that line.\n\
Two arguments with comma between specify starting and ending lines to list.\n\
", "\
Lines can be specified in these ways:\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\
*ADDRESS, to list around the line containing that address.\n\
With two args if one is empty it stands for ten lines away from the other arg.", NULL));
if (!xdb_commands)
add_com_alias ("l", "list", class_files, 1);
else
add_com_alias ("v", "list", class_files, 1);
if (dbx_commands)
add_com_alias ("file", "list", class_files, 1);
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);
}