|
|
1.1 root 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:
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:
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:
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:
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:
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:
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:
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:
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:
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, ¤t_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, ¤t_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:
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.