gdb-2.8
[binutils-gdb.git] / gdb / source.c
1 /* List lines of source files for GDB, the GNU debugger.
2 Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc.
3
4 GDB is distributed in the hope that it will be useful, but WITHOUT ANY
5 WARRANTY. No author or distributor accepts responsibility to anyone
6 for the consequences of using it or for whether it serves any
7 particular purpose or works at all, unless he says so in writing.
8 Refer to the GDB General Public License for full details.
9
10 Everyone is granted permission to copy, modify and redistribute GDB,
11 but only under the conditions described in the GDB General Public
12 License. A copy of this license is supposed to have been given to you
13 along with GDB so you can know your rights and responsibilities. It
14 should be in a file named COPYING. Among other things, the copyright
15 notice and this notice must be preserved on all copies.
16
17 In other words, go ahead and share GDB, but don't try to stop
18 anyone else from sharing it farther. Help stamp out software hoarding!
19 */
20
21 #include <stdio.h>
22 #include <sys/param.h>
23 #include <sys/stat.h>
24 #include <sys/file.h>
25 #include "defs.h"
26 #include "initialize.h"
27 #include "symtab.h"
28
29 /* Path of directories to search for source files.
30 Same format as the PATH environment variable's value. */
31
32 static char *source_path;
33
34 /* Symtab of default file for listing lines of. */
35
36 struct symtab *current_source_symtab;
37
38 /* Default next line to list. */
39
40 int current_source_line;
41
42 /* Line number of last line printed. Default for various commands.
43 current_source_line is usually, but not always, the same as this. */
44
45 static int last_line_listed;
46
47 /* First line number listed by last listing command. */
48
49 static int first_line_listed;
50
51 START_FILE
52 \f
53 /* Set the source file default for the "list" command,
54 specifying a symtab. */
55
56 void
57 select_source_symtab (s)
58 register struct symtab *s;
59 {
60 if (s)
61 {
62 struct symtab_and_line sal;
63
64 /* Make the default place to list be the function `main'
65 if one exists. */
66 if (lookup_symbol ("main", 0, VAR_NAMESPACE))
67 {
68 sal = decode_line_spec ("main", 1);
69 current_source_symtab = sal.symtab;
70 current_source_line = sal.line - 9;
71 return;
72 }
73
74 /* If there is no `main', use the last symtab in the list,
75 which is actually the first found in the file's symbol table.
76 But ignore .h files. */
77 do
78 {
79 char *name = s->filename;
80 int len = strlen (name);
81 if (! (len > 2 && !strcmp (&name[len - 2], ".h")))
82 current_source_symtab = s;
83 s = s->next;
84 }
85 while (s);
86 current_source_line = 1;
87 }
88 }
89 \f
90 static void
91 directories_info ()
92 {
93 printf ("Source directories searched: %s\n", source_path);
94 }
95
96 void
97 init_source_path ()
98 {
99 register struct symtab *s;
100
101 source_path = savestring (current_directory, strlen (current_directory));
102
103 /* Forget what we learned about line positions in source files;
104 must check again now since files may be found in
105 a different directory now. */
106 for (s = symtab_list; s; s = s->next)
107 if (s->line_charpos != 0)
108 {
109 free (s->line_charpos);
110 s->line_charpos = 0;
111 }
112 }
113
114 void
115 directory_command (dirname, from_tty)
116 char *dirname;
117 int from_tty;
118 {
119 char *old = source_path;
120
121 if (dirname == 0)
122 {
123 if (query ("Reinitialize source path to %s? ", current_directory))
124 {
125 init_source_path ();
126 free (old);
127 }
128 }
129 else
130 {
131 struct stat st;
132 register int len = strlen (dirname);
133 register char *tem;
134 extern char *index ();
135
136 if (index (dirname, ':'))
137 error ("Please add one directory at a time to the source path.");
138 if (dirname[len - 1] == '/')
139 /* Sigh. "foo/" => "foo" */
140 dirname[--len] == '\0';
141
142 while (dirname[len - 1] == '.')
143 {
144 if (len == 1)
145 {
146 /* "." => getwd () */
147 dirname = current_directory;
148 goto append;
149 }
150 else if (dirname[len - 2] == '/')
151 {
152 if (len == 2)
153 {
154 /* "/." => "/" */
155 dirname[--len] = '\0';
156 goto append;
157 }
158 else
159 {
160 /* "...foo/." => "...foo" */
161 dirname[len -= 2] = '\0';
162 continue;
163 }
164 }
165 break;
166 }
167
168 if (dirname[0] != '/')
169 dirname = concat (current_directory, "/", dirname);
170 else
171 dirname = savestring (dirname, len);
172 make_cleanup (free, dirname);
173
174 if (stat (dirname, &st) < 0)
175 perror_with_name (dirname);
176 if ((st.st_mode & S_IFMT) != S_IFDIR)
177 error ("%s is not a directory.", dirname);
178
179 append:
180 len = strlen (dirname);
181 tem = source_path;
182 while (1)
183 {
184 if (!strncmp (tem, dirname, len)
185 && (tem[len] == '\0' || tem[len] == ':'))
186 {
187 printf ("\"%s\" is already in the source path.\n",
188 dirname);
189 break;
190 }
191 tem = index (tem, ':');
192 if (tem)
193 tem++;
194 else
195 {
196 source_path = concat (old, ":", dirname);
197 free (old);
198 break;
199 }
200 }
201 if (from_tty)
202 directories_info ();
203 }
204 }
205 \f
206 /* Open a file named STRING, searching path PATH (dir names sep by colons)
207 using mode MODE and protection bits PROT in the calls to open.
208 If TRY_CWD_FIRST, try to open ./STRING before searching PATH.
209 (ie pretend the first element of PATH is ".")
210 If FILENAMED_OPENED is non-null, set it to a newly allocated string naming
211 the actual file opened (this string will always start with a "/"
212
213 If a file is found, return the descriptor.
214 Otherwise, return -1, with errno set for the last name we tried to open. */
215
216 /* >>>> This should only allow files of certain types,
217 >>>> eg executable, non-directory */
218 int
219 openp (path, try_cwd_first, string, mode, prot, filename_opened)
220 char *path;
221 int try_cwd_first;
222 char *string;
223 int mode;
224 int prot;
225 char **filename_opened;
226 {
227 register int fd;
228 register char *filename;
229 register char *p, *p1;
230 register int len;
231
232 /* ./foo => foo */
233 while (string[0] == '.' && string[1] == '/')
234 string += 2;
235
236 if (try_cwd_first || string[0] == '/')
237 {
238 filename = string;
239 fd = open (filename, mode, prot);
240 if (fd >= 0 || string[0] == '/')
241 goto done;
242 }
243
244 filename = (char *) alloca (strlen (path) + strlen (string) + 2);
245 fd = -1;
246 for (p = path; p; p = p1 ? p1 + 1 : 0)
247 {
248 p1 = (char *) index (p, ':');
249 if (p1)
250 len = p1 - p;
251 else
252 len = strlen (p);
253
254 strncpy (filename, p, len);
255 filename[len] = 0;
256 strcat (filename, "/");
257 strcat (filename, string);
258
259 fd = open (filename, mode, prot);
260 if (fd >= 0) break;
261 }
262
263 done:
264 if (filename_opened)
265 if (fd < 0)
266 *filename_opened = (char *) 0;
267 else if (filename[0] == '/')
268 *filename_opened = savestring (filename, strlen (filename));
269 else
270 {
271 *filename_opened = concat (current_directory, "/", filename);
272 }
273
274 return fd;
275 }
276 \f
277 /* Create and initialize the table S->line_charpos that records
278 the positions of the lines in the source file, which is assumed
279 to be open on descriptor DESC.
280 All set S->nlines to the number of such lines. */
281
282 static void
283 find_source_lines (s, desc)
284 struct symtab *s;
285 int desc;
286 {
287 struct stat st;
288 register char *data, *p, *end;
289 int nlines = 0;
290 int lines_allocated = 1000;
291 int *line_charpos = (int *) xmalloc (lines_allocated * sizeof (int));
292 extern int exec_mtime;
293
294 fstat (desc, &st);
295 if (get_exec_file (0) != 0 && exec_mtime < st.st_mtime)
296 printf ("Source file is more recent than executable.\n");
297
298 data = (char *) alloca (st.st_size);
299 myread (desc, data, st.st_size);
300 end = data + st.st_size;
301 p = data;
302 line_charpos[0] = 0;
303 nlines = 1;
304 while (p != end)
305 {
306 if (*p++ == '\n'
307 /* A newline at the end does not start a new line. */
308 && p != end)
309 {
310 if (nlines == lines_allocated)
311 {
312 lines_allocated *= 2;
313 line_charpos = (int *) xrealloc (line_charpos,
314 sizeof (int) * lines_allocated);
315 }
316 line_charpos[nlines++] = p - data;
317 }
318 }
319 s->nlines = nlines;
320 s->line_charpos = (int *) xrealloc (line_charpos, nlines * sizeof (int));
321 }
322
323 /* Return the character position of a line LINE in symtab S.
324 Return 0 if anything is invalid. */
325
326 int
327 source_line_charpos (s, line)
328 struct symtab *s;
329 int line;
330 {
331 if (!s) return 0;
332 if (!s->line_charpos || line <= 0) return 0;
333 if (line > s->nlines)
334 line = s->nlines;
335 return s->line_charpos[line - 1];
336 }
337
338 /* Return the line number of character position POS in symtab S. */
339
340 int
341 source_charpos_line (s, chr)
342 register struct symtab *s;
343 register int chr;
344 {
345 register int line = 0;
346 register int *lnp;
347
348 if (s == 0 || s->line_charpos == 0) return 0;
349 lnp = s->line_charpos;
350 /* Files are usually short, so sequential search is Ok */
351 while (line < s->nlines && *lnp <= chr)
352 {
353 line++;
354 lnp++;
355 }
356 if (line >= s->nlines)
357 line = s->nlines;
358 return line;
359 }
360 \f
361 /* Get full pathname and line number positions for a symtab.
362 Return nonzero if line numbers may have changed.
363 Set *FULLNAME to actual name of the file as found by `openp',
364 or to 0 if the file is not found. */
365
366 int
367 get_filename_and_charpos (s, line, fullname)
368 struct symtab *s;
369 int line;
370 char **fullname;
371 {
372 register int desc, linenums_changed = 0;
373
374 desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname);
375 if (desc < 0)
376 {
377 if (fullname)
378 *fullname = NULL;
379 return 0;
380 }
381 if (fullname)
382 *fullname = s->fullname;
383 if (s->line_charpos == 0) linenums_changed = 1;
384 if (linenums_changed) find_source_lines (s, desc);
385 close (desc);
386 return linenums_changed;
387 }
388
389 /* Print text describing the full name of the source file S
390 and the line number LINE and its corresponding character position.
391 The text starts with two Ctrl-z so that the Emacs-GDB interface
392 can easily find it.
393
394 MID_STATEMENT is nonzero if the PC is not at the beginning of that line.
395
396 Return 1 if successful, 0 if could not find the file. */
397
398 int
399 identify_source_line (s, line, mid_statement)
400 struct symtab *s;
401 int line;
402 int mid_statement;
403 {
404 if (s->line_charpos == 0)
405 get_filename_and_charpos (s, line, 0);
406 if (s->fullname == 0)
407 return 0;
408 printf ("\032\032%s:%d:%d:%s\n", s->fullname,
409 line, s->line_charpos[line - 1],
410 mid_statement ? "middle" : "beg");
411 current_source_line = line;
412 first_line_listed = line;
413 last_line_listed = line;
414 current_source_symtab = s;
415 return 1;
416 }
417 \f
418 /* Print source lines from the file of symtab S,
419 starting with line number LINE and stopping before line number STOPLINE. */
420
421 void
422 print_source_lines (s, line, stopline, noerror)
423 struct symtab *s;
424 int line, stopline;
425 int noerror;
426 {
427 register int c;
428 register int desc;
429 register FILE *stream;
430 int nlines = stopline - line;
431
432 desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname);
433 if (desc < 0)
434 {
435 extern int errno;
436 if (! noerror)
437 perror_with_name (s->filename);
438 print_sys_errmsg (s->filename, errno);
439 return;
440 }
441
442 if (s->line_charpos == 0)
443 find_source_lines (s, desc);
444
445 if (line < 1 || line > s->nlines)
446 {
447 close (desc);
448 error ("Line number out of range; %s has %d lines.",
449 s->filename, s->nlines);
450 }
451
452 if (lseek (desc, s->line_charpos[line - 1], 0) < 0)
453 {
454 close (desc);
455 perror_with_name (s->filename);
456 }
457
458 current_source_symtab = s;
459 current_source_line = line;
460 first_line_listed = line;
461
462 stream = fdopen (desc, "r");
463 clearerr (stream);
464
465 while (nlines-- > 0)
466 {
467 c = fgetc (stream);
468 if (c == EOF) break;
469 last_line_listed = current_source_line;
470 printf ("%d\t", current_source_line++);
471 do
472 {
473 if (c < 040 && c != '\t' && c != '\n')
474 {
475 fputc ('^', stdout);
476 fputc (c + 0100, stdout);
477 }
478 else if (c == 0177)
479 printf ("^?");
480 else
481 fputc (c, stdout);
482 } while (c != '\n' && (c = fgetc (stream)) >= 0);
483 }
484
485 fclose (stream);
486 }
487 \f
488 static void
489 list_command (arg, from_tty)
490 char *arg;
491 int from_tty;
492 {
493 struct symtab_and_line sal, sal_end;
494 struct symbol *sym;
495 char *arg1;
496 int no_end = 1;
497 int dummy_end = 0;
498 int dummy_beg = 0;
499 int linenum_beg = 0;
500 char *p;
501
502 if (symtab_list == 0)
503 error ("Listing source lines requires symbols.");
504
505 /* "l" or "l +" lists next ten lines. */
506
507 if (arg == 0 || !strcmp (arg, "+"))
508 {
509 if (current_source_symtab == 0)
510 error ("No default source file yet. Do \"help list\".");
511 print_source_lines (current_source_symtab, current_source_line,
512 current_source_line + 10, 0);
513 return;
514 }
515
516 /* "l -" lists previous ten lines, the ones before the ten just listed. */
517 if (!strcmp (arg, "-"))
518 {
519 if (current_source_symtab == 0)
520 error ("No default source file yet. Do \"help list\".");
521 print_source_lines (current_source_symtab,
522 max (first_line_listed - 10, 1),
523 first_line_listed, 0);
524 return;
525 }
526
527 /* Now if there is only one argument, decode it in SAL
528 and set NO_END.
529 If there are two arguments, decode them in SAL and SAL_END
530 and clear NO_END; however, if one of the arguments is blank,
531 set DUMMY_BEG or DUMMY_END to record that fact. */
532
533 arg1 = arg;
534 if (*arg1 == ',')
535 dummy_beg = 1;
536 else
537 sal = decode_line_1 (&arg1, 0, 0, 0);
538
539 /* Record whether the BEG arg is all digits. */
540
541 for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++);
542 linenum_beg = (p == arg1);
543
544 while (*arg1 == ' ' || *arg1 == '\t')
545 arg1++;
546 if (*arg1 == ',')
547 {
548 no_end = 0;
549 arg1++;
550 while (*arg1 == ' ' || *arg1 == '\t')
551 arg1++;
552 if (*arg1 == 0)
553 dummy_end = 1;
554 else if (dummy_beg)
555 sal_end = decode_line_1 (&arg1, 0, 0, 0);
556 else
557 sal_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line);
558 }
559
560 if (*arg1)
561 error ("Junk at end of line specification.");
562
563 if (!no_end && !dummy_beg && !dummy_end
564 && sal.symtab != sal_end.symtab)
565 error ("Specified start and end are in different files.");
566 if (dummy_beg && dummy_end)
567 error ("Two empty args do not say what lines to list.");
568
569 /* if line was specified by address,
570 first print exactly which line, and which file.
571 In this case, sal.symtab == 0 means address is outside
572 of all known source files, not that user failed to give a filename. */
573 if (*arg == '*')
574 {
575 if (sal.symtab == 0)
576 error ("No source file for address 0x%x.", sal.pc);
577 sym = find_pc_function (sal.pc);
578 if (sym)
579 printf ("0x%x is in %s (%s, line %d).\n",
580 sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line);
581 else
582 printf ("0x%x is in %s, line %d.\n",
583 sal.pc, sal.symtab->filename, sal.line);
584 }
585
586 /* If line was not specified by just a line number,
587 and it does not imply a symtab, it must be an undebuggable symbol
588 which means no source code. */
589
590 if (! linenum_beg && sal.symtab == 0)
591 error ("No line number known for %s.", arg);
592
593 /* If this command is repeated with RET,
594 turn it into the no-arg variant. */
595
596 if (from_tty)
597 *arg = 0;
598
599 if (dummy_beg && sal_end.symtab == 0)
600 error ("No default source file yet. Do \"help list\".");
601 if (dummy_beg)
602 print_source_lines (sal_end.symtab, max (sal_end.line - 9, 1),
603 sal_end.line + 1, 0);
604 else if (sal.symtab == 0)
605 error ("No default source file yet. Do \"help list\".");
606 else if (no_end)
607 print_source_lines (sal.symtab, max (sal.line - 5, 1), sal.line + 5, 0);
608 else
609 print_source_lines (sal.symtab, sal.line,
610 dummy_end ? sal.line + 10 : sal_end.line + 1,
611 0);
612 }
613 \f
614 /* Print info on range of pc's in a specified line. */
615
616 static void
617 line_info (arg, from_tty)
618 char *arg;
619 int from_tty;
620 {
621 struct symtab_and_line sal;
622 int start_pc, end_pc;
623
624 if (arg == 0)
625 {
626 sal.symtab = current_source_symtab;
627 sal.line = last_line_listed;
628 }
629 else
630 {
631 sal = decode_line_spec (arg, 0);
632
633 /* If this command is repeated with RET,
634 turn it into the no-arg variant. */
635
636 if (from_tty)
637 *arg = 0;
638 }
639
640 if (sal.symtab == 0)
641 error ("No source file specified.");
642 if (sal.line > 0
643 && find_line_pc_range (sal.symtab, sal.line, &start_pc, &end_pc))
644 {
645 if (start_pc == end_pc)
646 printf ("Line %d of \"%s\" is at pc 0x%x but contains no code.\n",
647 sal.line, sal.symtab->filename, start_pc);
648 else
649 printf ("Line %d of \"%s\" starts at pc 0x%x and ends at 0x%x.\n",
650 sal.line, sal.symtab->filename, start_pc, end_pc);
651 /* x/i should display this line's code. */
652 set_next_address (start_pc);
653 /* Repeating "info line" should do the following line. */
654 last_line_listed = sal.line + 1;
655 }
656 else
657 printf ("Line number %d is out of range for \"%s\".\n",
658 sal.line, sal.symtab->filename);
659 }
660 \f
661 /* Commands to search the source file for a regexp. */
662
663 static void
664 forward_search_command (regex, from_tty)
665 char *regex;
666 {
667 register int c;
668 register int desc;
669 register FILE *stream;
670 int line = last_line_listed + 1;
671 char *msg;
672
673 msg = (char *) re_comp (regex);
674 if (msg)
675 error (msg);
676
677 if (current_source_symtab == 0)
678 error ("No default source file yet. Do \"help list\".");
679
680 /* Search from last_line_listed+1 in current_source_symtab */
681
682 desc = openp (source_path, 0, current_source_symtab->filename,
683 O_RDONLY, 0, &current_source_symtab->fullname);
684 if (desc < 0)
685 perror_with_name (current_source_symtab->filename);
686
687 if (current_source_symtab->line_charpos == 0)
688 find_source_lines (current_source_symtab, desc);
689
690 if (line < 1 || line > current_source_symtab->nlines)
691 {
692 close (desc);
693 error ("Expression not found");
694 }
695
696 if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
697 {
698 close (desc);
699 perror_with_name (current_source_symtab->filename);
700 }
701
702 stream = fdopen (desc, "r");
703 clearerr (stream);
704 while (1) {
705 char buf[4096]; /* Should be reasonable??? */
706 register char *p = buf;
707
708 c = fgetc (stream);
709 if (c == EOF)
710 break;
711 do {
712 *p++ = c;
713 } while (c != '\n' && (c = fgetc (stream)) >= 0);
714
715 /* we now have a source line in buf, null terminate and match */
716 *p = 0;
717 if (re_exec (buf) > 0)
718 {
719 /* Match! */
720 fclose (stream);
721 print_source_lines (current_source_symtab,
722 line, line+1, 0);
723 current_source_line = max (line - 5, 1);
724 return;
725 }
726 line++;
727 }
728
729 printf ("Expression not found\n");
730 fclose (stream);
731 }
732
733 static void
734 reverse_search_command (regex, from_tty)
735 char *regex;
736 {
737 register int c;
738 register int desc;
739 register FILE *stream;
740 int line = last_line_listed - 1;
741 char *msg;
742
743 msg = (char *) re_comp (regex);
744 if (msg)
745 error (msg);
746
747 if (current_source_symtab == 0)
748 error ("No default source file yet. Do \"help list\".");
749
750 /* Search from last_line_listed-1 in current_source_symtab */
751
752 desc = openp (source_path, 0, current_source_symtab->filename,
753 O_RDONLY, 0, &current_source_symtab->fullname);
754 if (desc < 0)
755 perror_with_name (current_source_symtab->filename);
756
757 if (current_source_symtab->line_charpos == 0)
758 find_source_lines (current_source_symtab, desc);
759
760 if (line < 1 || line > current_source_symtab->nlines)
761 {
762 close (desc);
763 error ("Expression not found");
764 }
765
766 if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
767 {
768 close (desc);
769 perror_with_name (current_source_symtab->filename);
770 }
771
772 stream = fdopen (desc, "r");
773 clearerr (stream);
774 while (1)
775 {
776 char buf[4096]; /* Should be reasonable??? */
777 register char *p = buf;
778
779 c = fgetc (stream);
780 if (c == EOF)
781 break;
782 do {
783 *p++ = c;
784 } while (c != '\n' && (c = fgetc (stream)) >= 0);
785
786 /* We now have a source line in buf; null terminate and match. */
787 *p = 0;
788 if (re_exec (buf) > 0)
789 {
790 /* Match! */
791 fclose (stream);
792 print_source_lines (current_source_symtab,
793 line, line+1, 0);
794 current_source_line = max (line - 5, 1);
795 return;
796 }
797 line--;
798 if (fseek (stream, current_source_symtab->line_charpos[line - 1], 0) < 0)
799 {
800 fclose (stream);
801 perror_with_name (current_source_symtab->filename);
802 }
803 }
804
805 printf ("Expression not found\n");
806 fclose (stream);
807 return;
808 }
809 \f
810 static
811 initialize ()
812 {
813 current_source_symtab = 0;
814 init_source_path ();
815
816 add_com ("directory", class_files, directory_command,
817 "Add directory DIR to end of search path for source files.\n\
818 With no argument, reset the search path to just the working directory\n\
819 and forget cached info on line positions in source files.");
820
821 add_info ("directories", directories_info,
822 "Current search path for finding source files.");
823
824 add_info ("line", line_info,
825 "Core addresses of the code for a source line.\n\
826 Line can be specified as\n\
827 LINENUM, to list around that line in current file,\n\
828 FILE:LINENUM, to list around that line in that file,\n\
829 FUNCTION, to list around beginning of that function,\n\
830 FILE:FUNCTION, to distinguish among like-named static functions.\n\
831 Default is to describe the last source line that was listed.\n\n\
832 This sets the default address for \"x\" to the line's first instruction\n\
833 so that \"x/i\" suffices to start examining the machine code.\n\
834 The address is also stored as the value of \"$_\".");
835
836 add_com ("forward-search", class_files, forward_search_command,
837 "Search for regular expression (see regex(3)) from last line listed.");
838 add_com_alias ("search", "forward-search", class_files, 0);
839
840 add_com ("reverse-search", class_files, reverse_search_command,
841 "Search backward for regular expression (see regex(3)) from last line listed.");
842
843 add_com ("list", class_files, list_command,
844 "List specified function or line.\n\
845 With no argument, lists ten more lines after or around previous listing.\n\
846 \"list -\" lists the ten lines before a previous ten-line listing.\n\
847 One argument specifies a line, and ten lines are listed around that line.\n\
848 Two arguments with comma between specify starting and ending lines to list.\n\
849 Lines can be specified in these ways:\n\
850 LINENUM, to list around that line in current file,\n\
851 FILE:LINENUM, to list around that line in that file,\n\
852 FUNCTION, to list around beginning of that function,\n\
853 FILE:FUNCTION, to distinguish among like-named static functions.\n\
854 *ADDRESS, to list around the line containing that address.\n\
855 With two args if one is empty it stands for ten lines away from the other arg.");
856 }
857
858 END_FILE