/src/binutils-gdb/gas/listing.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* listing.c - maintain assembly listings |
2 | | Copyright (C) 1991-2025 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | /* Contributed by Steve Chamberlain <sac@cygnus.com> |
22 | | |
23 | | A listing page looks like: |
24 | | |
25 | | LISTING_HEADER sourcefilename pagenumber |
26 | | TITLE LINE |
27 | | SUBTITLE LINE |
28 | | linenumber address data source |
29 | | linenumber address data source |
30 | | linenumber address data source |
31 | | linenumber address data source |
32 | | |
33 | | If not overridden, the listing commands are: |
34 | | |
35 | | .title "stuff" |
36 | | Put "stuff" onto the title line |
37 | | .sbttl "stuff" |
38 | | Put stuff onto the subtitle line |
39 | | |
40 | | If these commands come within 10 lines of the top of the page, they |
41 | | will affect the page they are on, as well as any subsequent page |
42 | | |
43 | | .eject |
44 | | Throw a page |
45 | | .list |
46 | | Increment the enable listing counter |
47 | | .nolist |
48 | | Decrement the enable listing counter |
49 | | |
50 | | .psize Y[,X] |
51 | | Set the paper size to X wide and Y high. Setting a psize Y of |
52 | | zero will suppress form feeds except where demanded by .eject |
53 | | |
54 | | If the counter goes below zero, listing is suppressed. |
55 | | |
56 | | Listings are a maintained by read calling various listing_<foo> |
57 | | functions. What happens most is that the macro NO_LISTING is not |
58 | | defined (from the Makefile), then the macro LISTING_NEWLINE expands |
59 | | into a call to listing_newline. The call is done from read.c, every |
60 | | time it sees a newline, and -l is on the command line. |
61 | | |
62 | | The function listing_newline remembers the frag associated with the |
63 | | newline, and creates a new frag - note that this is wasteful, but not |
64 | | a big deal, since listing slows things down a lot anyway. The |
65 | | function also remembers when the filename changes. |
66 | | |
67 | | When all the input has finished, and gas has had a chance to settle |
68 | | down, the listing is output. This is done by running down the list of |
69 | | frag/source file records, and opening the files as needed and printing |
70 | | out the bytes and chars associated with them. |
71 | | |
72 | | The only things which the architecture can change about the listing |
73 | | are defined in these macros: |
74 | | |
75 | | LISTING_HEADER The name of the architecture |
76 | | LISTING_WORD_SIZE The make of the number of bytes in a word, this determines |
77 | | the clumping of the output data. eg a value of |
78 | | 2 makes words look like 1234 5678, whilst 1 |
79 | | would make the same value look like 12 34 56 |
80 | | 78 |
81 | | LISTING_LHS_WIDTH Number of words of above size for the lhs |
82 | | |
83 | | LISTING_LHS_WIDTH_SECOND Number of words for the data on the lhs |
84 | | for the second line |
85 | | |
86 | | LISTING_LHS_CONT_LINES Max number of lines to use up for a continuation |
87 | | LISTING_RHS_WIDTH Number of chars from the input file to print |
88 | | on a line. */ |
89 | | |
90 | | #include "as.h" |
91 | | #include "filenames.h" |
92 | | #include "safe-ctype.h" |
93 | | #include "input-file.h" |
94 | | #include "subsegs.h" |
95 | | #include "bfdver.h" |
96 | | #include <time.h> |
97 | | #include <stdarg.h> |
98 | | |
99 | | #ifndef NO_LISTING |
100 | | |
101 | | #ifndef LISTING_HEADER |
102 | 0 | #define LISTING_HEADER "GAS LISTING" |
103 | | #endif |
104 | | #ifndef LISTING_WORD_SIZE |
105 | 0 | #define LISTING_WORD_SIZE 4 |
106 | | #endif |
107 | | #ifndef LISTING_LHS_WIDTH |
108 | | #define LISTING_LHS_WIDTH ((LISTING_WORD_SIZE) > 4 ? 1 : 4 / (LISTING_WORD_SIZE)) |
109 | | #endif |
110 | | #ifndef LISTING_LHS_WIDTH_SECOND |
111 | | #define LISTING_LHS_WIDTH_SECOND LISTING_LHS_WIDTH |
112 | | #endif |
113 | | #ifndef LISTING_RHS_WIDTH |
114 | | #define LISTING_RHS_WIDTH 100 |
115 | | #endif |
116 | | #ifndef LISTING_LHS_CONT_LINES |
117 | | #define LISTING_LHS_CONT_LINES 4 |
118 | | #endif |
119 | 0 | #define MAX_DATELEN 30 |
120 | | |
121 | | /* This structure remembers which .s were used. */ |
122 | | typedef struct file_info_struct |
123 | | { |
124 | | struct file_info_struct * next; |
125 | | char * filename; |
126 | | long pos; |
127 | | unsigned int linenum; |
128 | | int at_end; |
129 | | } file_info_type; |
130 | | |
131 | | enum edict_enum |
132 | | { |
133 | | EDICT_NONE, |
134 | | EDICT_SBTTL, |
135 | | EDICT_TITLE, |
136 | | EDICT_NOLIST, |
137 | | EDICT_LIST, |
138 | | EDICT_NOLIST_NEXT, |
139 | | EDICT_EJECT |
140 | | }; |
141 | | |
142 | | |
143 | | struct list_message |
144 | | { |
145 | | char *message; |
146 | | struct list_message *next; |
147 | | }; |
148 | | |
149 | | /* This structure remembers which line from which file goes into which |
150 | | frag. */ |
151 | | struct list_info_struct |
152 | | { |
153 | | /* Frag which this line of source is nearest to. */ |
154 | | fragS *frag; |
155 | | |
156 | | /* The actual line in the source file. */ |
157 | | unsigned int line; |
158 | | |
159 | | /* Pointer to the file info struct for the file which this line |
160 | | belongs to. */ |
161 | | file_info_type *file; |
162 | | |
163 | | /* The expanded text of any macro that may have been executing. */ |
164 | | char *line_contents; |
165 | | |
166 | | /* Next in list. */ |
167 | | struct list_info_struct *next; |
168 | | |
169 | | /* Pointer to the file info struct for the high level language |
170 | | source line that belongs here. */ |
171 | | file_info_type *hll_file; |
172 | | |
173 | | /* High level language source line. */ |
174 | | unsigned int hll_line; |
175 | | |
176 | | /* Pointers to linked list of messages associated with this line. */ |
177 | | struct list_message *messages, *last_message; |
178 | | |
179 | | #ifdef OBJ_ELF |
180 | | /* Nonzero if this line is to be omitted because it contains |
181 | | debugging information. This can become a flags field if we come |
182 | | up with more information to store here. */ |
183 | | bool debugging; |
184 | | #endif |
185 | | |
186 | | enum edict_enum edict; |
187 | | char *edict_arg; |
188 | | |
189 | | }; |
190 | | |
191 | | typedef struct list_info_struct list_info_type; |
192 | | |
193 | | unsigned int listing_lhs_width = LISTING_LHS_WIDTH; |
194 | | unsigned int listing_lhs_width_second = LISTING_LHS_WIDTH_SECOND; |
195 | | unsigned int listing_lhs_cont_lines = LISTING_LHS_CONT_LINES; |
196 | | unsigned int listing_rhs_width = LISTING_RHS_WIDTH; |
197 | | |
198 | | struct list_info_struct * listing_tail; |
199 | | |
200 | | static file_info_type * file_info_head; |
201 | | static file_info_type * last_open_file_info; |
202 | | static FILE * last_open_file; |
203 | | static struct list_info_struct * head; |
204 | | static unsigned int paper_width = 200; |
205 | | static unsigned int paper_height = 60; |
206 | | |
207 | | extern int listing; |
208 | | |
209 | | /* File to output listings to. */ |
210 | | static FILE *list_file; |
211 | | |
212 | | /* This static array is used to keep the text of data to be printed |
213 | | before the start of the line. */ |
214 | | |
215 | | #define MAX_BYTES \ |
216 | 0 | (((LISTING_WORD_SIZE * 2) + 1) * listing_lhs_width \ |
217 | 0 | + ((((LISTING_WORD_SIZE * 2) + 1) * listing_lhs_width_second) \ |
218 | 0 | * listing_lhs_cont_lines) \ |
219 | 0 | + 20) |
220 | | |
221 | | static char *data_buffer; |
222 | | |
223 | | static void |
224 | | listing_message (const char *name, const char *message) |
225 | 988k | { |
226 | 988k | if (listing_tail != (list_info_type *) NULL) |
227 | 0 | { |
228 | 0 | char *n = concat (name, message, (char *) NULL); |
229 | 0 | struct list_message *lm = XNEW (struct list_message); |
230 | 0 | lm->message = n; |
231 | 0 | lm->next = NULL; |
232 | |
|
233 | 0 | if (listing_tail->last_message) |
234 | 0 | listing_tail->last_message->next = lm; |
235 | 0 | else |
236 | 0 | listing_tail->messages = lm; |
237 | 0 | listing_tail->last_message = lm; |
238 | 0 | } |
239 | 988k | } |
240 | | |
241 | | void |
242 | | listing_warning (const char *message) |
243 | 43.7k | { |
244 | 43.7k | listing_message (_("Warning: "), message); |
245 | 43.7k | } |
246 | | |
247 | | void |
248 | | listing_error (const char *message) |
249 | 944k | { |
250 | 944k | listing_message (_("Error: "), message); |
251 | 944k | } |
252 | | |
253 | | static file_info_type * |
254 | | file_info (const char *file_name) |
255 | 0 | { |
256 | | /* Find an entry with this file name. */ |
257 | 0 | file_info_type *p = file_info_head; |
258 | |
|
259 | 0 | while (p != (file_info_type *) NULL) |
260 | 0 | { |
261 | 0 | if (filename_cmp (p->filename, file_name) == 0) |
262 | 0 | return p; |
263 | 0 | p = p->next; |
264 | 0 | } |
265 | | |
266 | | /* Make new entry. */ |
267 | 0 | p = XNEW (file_info_type); |
268 | 0 | p->next = file_info_head; |
269 | 0 | file_info_head = p; |
270 | 0 | p->filename = xstrdup (file_name); |
271 | 0 | p->pos = 0; |
272 | 0 | p->linenum = 0; |
273 | 0 | p->at_end = 0; |
274 | |
|
275 | 0 | return p; |
276 | 0 | } |
277 | | |
278 | | static void |
279 | | new_frag (void) |
280 | 0 | { |
281 | 0 | frag_wane (frag_now); |
282 | 0 | frag_new (0); |
283 | 0 | } |
284 | | |
285 | | void |
286 | | listing_newline (char *ps) |
287 | 28 | { |
288 | 28 | const char *file; |
289 | 28 | unsigned int line; |
290 | 28 | static unsigned int last_line = 0xffff; |
291 | 28 | static const char *last_file = NULL; |
292 | 28 | list_info_type *new_i = NULL; |
293 | | |
294 | 28 | if (listing == 0) |
295 | 28 | return; |
296 | | |
297 | 0 | if (now_seg == absolute_section) |
298 | 0 | return; |
299 | | |
300 | 0 | #ifdef OBJ_ELF |
301 | | /* In ELF, anything in a section beginning with .debug or .line is |
302 | | considered to be debugging information. This includes the |
303 | | statement which switches us into the debugging section, which we |
304 | | can only set after we are already in the debugging section. */ |
305 | 0 | if (IS_ELF |
306 | 0 | && (listing & LISTING_NODEBUG) != 0 |
307 | 0 | && listing_tail != NULL |
308 | 0 | && ! listing_tail->debugging) |
309 | 0 | { |
310 | 0 | const char *segname; |
311 | |
|
312 | 0 | segname = segment_name (now_seg); |
313 | 0 | if (startswith (segname, ".debug") |
314 | 0 | || startswith (segname, ".line")) |
315 | 0 | listing_tail->debugging = true; |
316 | 0 | } |
317 | 0 | #endif |
318 | | |
319 | | /* PR 21977 - use the physical file name not the logical one unless high |
320 | | level source files are being included in the listing. */ |
321 | 0 | if (listing & LISTING_HLL) |
322 | 0 | file = as_where (&line); |
323 | 0 | else |
324 | 0 | file = as_where_physical (&line); |
325 | |
|
326 | 0 | if (ps == NULL) |
327 | 0 | { |
328 | 0 | if (line == last_line |
329 | 0 | && !(last_file && file && filename_cmp (file, last_file))) |
330 | 0 | return; |
331 | | |
332 | 0 | new_i = XNEW (list_info_type); |
333 | | |
334 | | /* Detect if we are reading from stdin by examining the file |
335 | | name returned by as_where(). |
336 | | |
337 | | [FIXME: We rely upon the name in the strcmp below being the |
338 | | same as the one used by input_scrub_new_file(), if that is |
339 | | not true, then this code will fail]. |
340 | | |
341 | | If we are reading from stdin, then we need to save each input |
342 | | line here (assuming of course that we actually have a line of |
343 | | input to read), so that it can be displayed in the listing |
344 | | that is produced at the end of the assembly. */ |
345 | 0 | if (strcmp (file, _("{standard input}")) == 0 |
346 | 0 | && input_line_pointer != NULL) |
347 | 0 | { |
348 | 0 | char *copy, *src, *dest; |
349 | 0 | int len; |
350 | 0 | int seen_quote = 0; |
351 | 0 | int seen_slash = 0; |
352 | |
|
353 | 0 | for (copy = input_line_pointer; |
354 | 0 | seen_quote ? *copy : !is_end_of_line (*copy); |
355 | 0 | copy++) |
356 | 0 | { |
357 | 0 | if (seen_slash) |
358 | 0 | seen_slash = 0; |
359 | 0 | else if (*copy == '\\') |
360 | 0 | seen_slash = 1; |
361 | 0 | else if (*copy == '"') |
362 | 0 | seen_quote = !seen_quote; |
363 | 0 | } |
364 | |
|
365 | 0 | len = copy - input_line_pointer + 1; |
366 | |
|
367 | 0 | copy = XNEWVEC (char, len); |
368 | |
|
369 | 0 | src = input_line_pointer; |
370 | 0 | dest = copy; |
371 | |
|
372 | 0 | while (--len) |
373 | 0 | { |
374 | 0 | unsigned char c = *src++; |
375 | | |
376 | | /* Omit control characters in the listing. */ |
377 | 0 | if (!ISCNTRL (c)) |
378 | 0 | *dest++ = c; |
379 | 0 | } |
380 | |
|
381 | 0 | *dest = 0; |
382 | |
|
383 | 0 | new_i->line_contents = copy; |
384 | 0 | } |
385 | 0 | else |
386 | 0 | new_i->line_contents = NULL; |
387 | 0 | } |
388 | 0 | else |
389 | 0 | { |
390 | 0 | new_i = XNEW (list_info_type); |
391 | 0 | new_i->line_contents = ps; |
392 | 0 | } |
393 | | |
394 | 0 | last_line = line; |
395 | 0 | last_file = file; |
396 | |
|
397 | 0 | new_frag (); |
398 | |
|
399 | 0 | if (listing_tail) |
400 | 0 | listing_tail->next = new_i; |
401 | 0 | else |
402 | 0 | head = new_i; |
403 | |
|
404 | 0 | listing_tail = new_i; |
405 | |
|
406 | 0 | new_i->frag = frag_now; |
407 | 0 | new_i->line = line; |
408 | 0 | new_i->file = file_info (file); |
409 | 0 | new_i->next = (list_info_type *) NULL; |
410 | 0 | new_i->messages = NULL; |
411 | 0 | new_i->last_message = NULL; |
412 | 0 | new_i->edict = EDICT_NONE; |
413 | 0 | new_i->hll_file = (file_info_type *) NULL; |
414 | 0 | new_i->hll_line = 0; |
415 | |
|
416 | 0 | new_frag (); |
417 | |
|
418 | 0 | #ifdef OBJ_ELF |
419 | | /* In ELF, anything in a section beginning with .debug or .line is |
420 | | considered to be debugging information. */ |
421 | 0 | new_i->debugging = false; |
422 | 0 | if ((listing & LISTING_NODEBUG) != 0) |
423 | 0 | { |
424 | 0 | const char *segname; |
425 | |
|
426 | 0 | segname = segment_name (now_seg); |
427 | 0 | if (startswith (segname, ".debug") |
428 | 0 | || startswith (segname, ".line")) |
429 | 0 | new_i->debugging = true; |
430 | 0 | } |
431 | 0 | #endif |
432 | 0 | } |
433 | | |
434 | | /* Set listing context back to where it was when input was parsed, to allow |
435 | | associating late code/data emission to segments with their origin. */ |
436 | | |
437 | | struct list_info_struct *listing_override_tail (struct list_info_struct *info) |
438 | 0 | { |
439 | 0 | struct list_info_struct *prev = listing_tail; |
440 | 0 | const fragS *frag; |
441 | |
|
442 | 0 | if (!info) |
443 | 0 | return NULL; |
444 | | |
445 | 0 | listing_tail = info; |
446 | | |
447 | | /* The first frag created by listing_newline() is still associated with the |
448 | | earlier line. For the adjustment done below this property doesn't hold, |
449 | | though. */ |
450 | 0 | frag = info->frag; |
451 | 0 | if (frag->line != info) |
452 | 0 | frag = frag->fr_next; |
453 | | |
454 | | /* Check whether there's any other output data already for this line. Replace |
455 | | info->frag only if there's none. This is to cover for contributions to |
456 | | multiple sections from a single line not being properly represented in the |
457 | | listing, at the time of writing. Prefer the listing to show any "ordinary" |
458 | | code/data contributions over any .eh_frame ones. (This way multiple .cfi_* |
459 | | on a single line will also have all their contributions listed, rather |
460 | | than just those from the last such directive.) */ |
461 | 0 | for (; frag; frag = frag->fr_next) |
462 | 0 | if (frag->line != info |
463 | 0 | || (frag->fr_type != rs_dummy |
464 | 0 | && (frag->fr_type != rs_fill |
465 | 0 | || frag->fr_fix |
466 | 0 | || (frag->fr_var && frag->fr_offset)))) |
467 | 0 | break; |
468 | |
|
469 | 0 | if (!frag || frag->line != info) |
470 | 0 | { |
471 | 0 | new_frag (); |
472 | 0 | info->frag = frag_now; |
473 | 0 | new_frag (); |
474 | 0 | } |
475 | |
|
476 | 0 | return prev; |
477 | 0 | } |
478 | | |
479 | | /* Attach all current frags to the previous line instead of the |
480 | | current line. This is called by the MIPS backend when it discovers |
481 | | that it needs to add some NOP instructions; the added NOP |
482 | | instructions should go with the instruction that has the delay, not |
483 | | with the new instruction. */ |
484 | | |
485 | | void |
486 | | listing_prev_line (void) |
487 | 0 | { |
488 | 0 | list_info_type *l; |
489 | 0 | fragS *f; |
490 | |
|
491 | 0 | if (head == (list_info_type *) NULL |
492 | 0 | || head == listing_tail) |
493 | 0 | return; |
494 | | |
495 | 0 | new_frag (); |
496 | |
|
497 | 0 | for (l = head; l->next != listing_tail; l = l->next) |
498 | 0 | ; |
499 | |
|
500 | 0 | for (f = frchain_now->frch_root; f != (fragS *) NULL; f = f->fr_next) |
501 | 0 | if (f->line == listing_tail) |
502 | 0 | f->line = l; |
503 | |
|
504 | 0 | listing_tail->frag = frag_now; |
505 | 0 | new_frag (); |
506 | 0 | } |
507 | | |
508 | | /* This function returns the next source line from the file supplied, |
509 | | truncated to size. It appends a fake line to the end of each input |
510 | | file to make using the returned buffer simpler. */ |
511 | | |
512 | | static const char * |
513 | | buffer_line (file_info_type *file, char *line, unsigned int size) |
514 | 0 | { |
515 | 0 | unsigned int count = 0; |
516 | 0 | int c; |
517 | 0 | char *p = line; |
518 | | |
519 | | /* If we couldn't open the file, return an empty line. */ |
520 | 0 | if (file->at_end) |
521 | 0 | return ""; |
522 | | |
523 | | /* Check the cache and see if we last used this file. */ |
524 | 0 | if (!last_open_file_info || file != last_open_file_info) |
525 | 0 | { |
526 | 0 | if (last_open_file) |
527 | 0 | { |
528 | 0 | last_open_file_info->pos = ftell (last_open_file); |
529 | 0 | fclose (last_open_file); |
530 | 0 | } |
531 | | |
532 | | /* Open the file in the binary mode so that ftell above can |
533 | | return a reliable value that we can feed to fseek below. */ |
534 | 0 | last_open_file_info = file; |
535 | 0 | last_open_file = fopen (file->filename, FOPEN_RB); |
536 | 0 | if (last_open_file == NULL) |
537 | 0 | { |
538 | 0 | file->at_end = 1; |
539 | 0 | return ""; |
540 | 0 | } |
541 | | |
542 | | /* Seek to where we were last time this file was open. */ |
543 | 0 | if (file->pos) |
544 | 0 | fseek (last_open_file, file->pos, SEEK_SET); |
545 | 0 | } |
546 | | |
547 | 0 | c = fgetc (last_open_file); |
548 | |
|
549 | 0 | while (c != EOF && c != '\n' && c != '\r') |
550 | 0 | { |
551 | 0 | if (++count < size) |
552 | 0 | *p++ = c; |
553 | 0 | c = fgetc (last_open_file); |
554 | 0 | } |
555 | | |
556 | | /* If '\r' is followed by '\n', swallow that. Likewise, if '\n' |
557 | | is followed by '\r', swallow that as well. */ |
558 | 0 | if (c == '\r' || c == '\n') |
559 | 0 | { |
560 | 0 | int next = fgetc (last_open_file); |
561 | |
|
562 | 0 | if ((c == '\r' && next != '\n') |
563 | 0 | || (c == '\n' && next != '\r')) |
564 | 0 | ungetc (next, last_open_file); |
565 | 0 | } |
566 | |
|
567 | 0 | if (c == EOF) |
568 | 0 | { |
569 | 0 | file->at_end = 1; |
570 | 0 | if (count + 3 < size) |
571 | 0 | { |
572 | 0 | *p++ = '.'; |
573 | 0 | *p++ = '.'; |
574 | 0 | *p++ = '.'; |
575 | 0 | } |
576 | 0 | } |
577 | 0 | file->linenum++; |
578 | 0 | *p++ = 0; |
579 | 0 | return line; |
580 | 0 | } |
581 | | |
582 | | |
583 | | /* This function rewinds the requested file back to the line requested, |
584 | | reads it in again into the buffer provided and then restores the file |
585 | | back to its original location. */ |
586 | | |
587 | | static void |
588 | | rebuffer_line (file_info_type * file, |
589 | | unsigned int linenum, |
590 | | char * buffer, |
591 | | unsigned int size) |
592 | 0 | { |
593 | 0 | unsigned int count = 0; |
594 | 0 | unsigned int current_line; |
595 | 0 | char * p = buffer; |
596 | 0 | long pos; |
597 | 0 | long pos2; |
598 | 0 | int c; |
599 | 0 | bool found = false; |
600 | | |
601 | | /* Sanity checks. */ |
602 | 0 | if (file == NULL || buffer == NULL || size <= 1 || file->linenum <= linenum) |
603 | 0 | return; |
604 | | |
605 | | /* Check the cache and see if we last used this file. */ |
606 | 0 | if (last_open_file_info == NULL || file != last_open_file_info) |
607 | 0 | { |
608 | 0 | if (last_open_file) |
609 | 0 | { |
610 | 0 | last_open_file_info->pos = ftell (last_open_file); |
611 | 0 | fclose (last_open_file); |
612 | 0 | } |
613 | | |
614 | | /* Open the file in the binary mode so that ftell above can |
615 | | return a reliable value that we can feed to fseek below. */ |
616 | 0 | last_open_file_info = file; |
617 | 0 | last_open_file = fopen (file->filename, FOPEN_RB); |
618 | 0 | if (last_open_file == NULL) |
619 | 0 | { |
620 | 0 | file->at_end = 1; |
621 | 0 | return; |
622 | 0 | } |
623 | | |
624 | | /* Seek to where we were last time this file was open. */ |
625 | 0 | if (file->pos) |
626 | 0 | fseek (last_open_file, file->pos, SEEK_SET); |
627 | 0 | } |
628 | | |
629 | | /* Remember where we are in the current file. */ |
630 | 0 | pos2 = pos = ftell (last_open_file); |
631 | 0 | if (pos < 3) |
632 | 0 | return; |
633 | 0 | current_line = file->linenum; |
634 | | |
635 | | /* Leave room for the nul at the end of the buffer. */ |
636 | 0 | size -= 1; |
637 | 0 | buffer[size] = 0; |
638 | | |
639 | | /* Increment the current line count by one. |
640 | | This is to allow for the fact that we are searching for the |
641 | | start of a previous line, but we do this by detecting end-of-line |
642 | | character(s) not start-of-line characters. */ |
643 | 0 | ++ current_line; |
644 | |
|
645 | 0 | while (pos2 > 0 && ! found) |
646 | 0 | { |
647 | 0 | char * ptr; |
648 | | |
649 | | /* Move backwards through the file, looking for earlier lines. */ |
650 | 0 | pos2 = (long) size > pos2 ? 0 : pos2 - size; |
651 | 0 | fseek (last_open_file, pos2, SEEK_SET); |
652 | | |
653 | | /* Our caller has kindly provided us with a buffer, so we use it. */ |
654 | 0 | if (fread (buffer, 1, size, last_open_file) != size) |
655 | 0 | { |
656 | 0 | as_warn (_("unable to rebuffer file: %s\n"), file->filename); |
657 | 0 | return; |
658 | 0 | } |
659 | | |
660 | 0 | for (ptr = buffer + size; ptr >= buffer; -- ptr) |
661 | 0 | { |
662 | 0 | if (*ptr == '\n') |
663 | 0 | { |
664 | 0 | -- current_line; |
665 | |
|
666 | 0 | if (current_line == linenum) |
667 | 0 | { |
668 | | /* We have found the start of the line we seek. */ |
669 | 0 | found = true; |
670 | | |
671 | | /* FIXME: We could skip the read-in-the-line code |
672 | | below if we know that we already have the whole |
673 | | line in the buffer. */ |
674 | | |
675 | | /* Advance pos2 to the newline character we have just located. */ |
676 | 0 | pos2 += (ptr - buffer); |
677 | | |
678 | | /* Skip the newline and, if present, the carriage return. */ |
679 | 0 | if (ptr + 1 == buffer + size) |
680 | 0 | { |
681 | 0 | ++pos2; |
682 | 0 | if (fgetc (last_open_file) == '\r') |
683 | 0 | ++ pos2; |
684 | 0 | } |
685 | 0 | else |
686 | 0 | pos2 += (ptr[1] == '\r' ? 2 : 1); |
687 | | |
688 | | /* Move the file pointer to this location. */ |
689 | 0 | fseek (last_open_file, pos2, SEEK_SET); |
690 | 0 | break; |
691 | 0 | } |
692 | 0 | } |
693 | 0 | } |
694 | 0 | } |
695 | | |
696 | | /* Read in the line. */ |
697 | 0 | c = fgetc (last_open_file); |
698 | |
|
699 | 0 | while (c != EOF && c != '\n' && c != '\r') |
700 | 0 | { |
701 | 0 | if (count < size) |
702 | 0 | *p++ = c; |
703 | 0 | count++; |
704 | |
|
705 | 0 | c = fgetc (last_open_file); |
706 | 0 | } |
707 | | |
708 | | /* If '\r' is followed by '\n', swallow that. Likewise, if '\n' |
709 | | is followed by '\r', swallow that as well. */ |
710 | 0 | if (c == '\r' || c == '\n') |
711 | 0 | { |
712 | 0 | int next = fgetc (last_open_file); |
713 | |
|
714 | 0 | if ((c == '\r' && next != '\n') |
715 | 0 | || (c == '\n' && next != '\r')) |
716 | 0 | ungetc (next, last_open_file); |
717 | 0 | } |
718 | | |
719 | | /* Terminate the line. */ |
720 | 0 | *p++ = 0; |
721 | | |
722 | | /* Reset the file position. */ |
723 | 0 | fseek (last_open_file, pos, SEEK_SET); |
724 | 0 | } |
725 | | |
726 | | static const char *fn; |
727 | | static unsigned int eject; /* Eject pending. */ |
728 | | static unsigned int page; /* Current page number. */ |
729 | | static const char *title; /* Current title. */ |
730 | | static const char *subtitle; /* Current subtitle. */ |
731 | | static unsigned int on_page; /* Number of lines printed on current page. */ |
732 | | |
733 | | static void |
734 | | listing_page (list_info_type *list) |
735 | 0 | { |
736 | | /* Grope around, see if we can see a title or subtitle edict coming up |
737 | | soon. (we look down 10 lines of the page and see if it's there) */ |
738 | 0 | if ((eject || (on_page >= paper_height)) |
739 | 0 | && paper_height != 0) |
740 | 0 | { |
741 | 0 | unsigned int c = 10; |
742 | 0 | int had_title = 0; |
743 | 0 | int had_subtitle = 0; |
744 | |
|
745 | 0 | page++; |
746 | |
|
747 | 0 | while (c != 0 && list) |
748 | 0 | { |
749 | 0 | if (list->edict == EDICT_SBTTL && !had_subtitle) |
750 | 0 | { |
751 | 0 | had_subtitle = 1; |
752 | 0 | subtitle = list->edict_arg; |
753 | 0 | } |
754 | 0 | if (list->edict == EDICT_TITLE && !had_title) |
755 | 0 | { |
756 | 0 | had_title = 1; |
757 | 0 | title = list->edict_arg; |
758 | 0 | } |
759 | 0 | list = list->next; |
760 | 0 | c--; |
761 | 0 | } |
762 | |
|
763 | 0 | if (page > 1) |
764 | 0 | { |
765 | 0 | fprintf (list_file, "\f"); |
766 | 0 | } |
767 | |
|
768 | 0 | fprintf (list_file, "%s %s \t\t\tpage %d\n", LISTING_HEADER, fn, page); |
769 | 0 | fprintf (list_file, "%s\n", title); |
770 | 0 | fprintf (list_file, "%s\n", subtitle); |
771 | 0 | on_page = 3; |
772 | 0 | eject = 0; |
773 | 0 | } |
774 | 0 | } |
775 | | |
776 | | /* Print a line into the list_file. Update the line count |
777 | | and if necessary start a new page. */ |
778 | | |
779 | | static void |
780 | | emit_line (list_info_type * list, const char * format, ...) |
781 | 0 | { |
782 | 0 | va_list args; |
783 | |
|
784 | 0 | va_start (args, format); |
785 | |
|
786 | 0 | vfprintf (list_file, format, args); |
787 | 0 | on_page++; |
788 | 0 | listing_page (list); |
789 | |
|
790 | 0 | va_end (args); |
791 | 0 | } |
792 | | |
793 | | static unsigned int |
794 | | calc_hex (list_info_type *list) |
795 | 0 | { |
796 | 0 | size_t data_buffer_size; |
797 | 0 | list_info_type *first = list; |
798 | 0 | unsigned int address = ~(unsigned int) 0; |
799 | 0 | fragS *frag; |
800 | 0 | fragS *frag_ptr; |
801 | 0 | unsigned int octet_in_frag; |
802 | | |
803 | | /* Find first frag which says it belongs to this line. */ |
804 | 0 | frag = list->frag; |
805 | 0 | while (frag && frag->line != list) |
806 | 0 | frag = frag->fr_next; |
807 | |
|
808 | 0 | frag_ptr = frag; |
809 | |
|
810 | 0 | data_buffer_size = 0; |
811 | | |
812 | | /* Dump all the frags which belong to this line. */ |
813 | 0 | while (frag_ptr != (fragS *) NULL && frag_ptr->line == first) |
814 | 0 | { |
815 | | /* Print as many bytes from the fixed part as is sensible. */ |
816 | 0 | octet_in_frag = 0; |
817 | 0 | while (octet_in_frag < frag_ptr->fr_fix |
818 | 0 | && data_buffer_size < MAX_BYTES - 3) |
819 | 0 | { |
820 | 0 | if (address == ~(unsigned int) 0) |
821 | 0 | address = frag_ptr->fr_address / OCTETS_PER_BYTE; |
822 | |
|
823 | 0 | sprintf (data_buffer + data_buffer_size, |
824 | 0 | "%02X", |
825 | 0 | (frag_ptr->fr_literal[octet_in_frag]) & 0xff); |
826 | 0 | data_buffer_size += 2; |
827 | 0 | octet_in_frag++; |
828 | 0 | } |
829 | 0 | if (frag_ptr->fr_type == rs_fill || frag_ptr->fr_type == rs_fill_nop) |
830 | 0 | { |
831 | 0 | unsigned int var_rep_max = octet_in_frag; |
832 | 0 | unsigned int var_rep_idx = octet_in_frag; |
833 | | |
834 | | /* Print as many bytes from the variable part as is sensible. */ |
835 | 0 | while ((octet_in_frag |
836 | 0 | < frag_ptr->fr_fix + frag_ptr->fr_var * frag_ptr->fr_offset) |
837 | 0 | && data_buffer_size < MAX_BYTES - 3) |
838 | 0 | { |
839 | 0 | if (address == ~(unsigned int) 0) |
840 | 0 | address = frag_ptr->fr_address / OCTETS_PER_BYTE; |
841 | |
|
842 | 0 | sprintf (data_buffer + data_buffer_size, |
843 | 0 | "%02X", |
844 | 0 | (frag_ptr->fr_literal[var_rep_idx]) & 0xff); |
845 | 0 | data_buffer_size += 2; |
846 | |
|
847 | 0 | var_rep_idx++; |
848 | 0 | octet_in_frag++; |
849 | |
|
850 | 0 | if (var_rep_idx >= frag_ptr->fr_fix + frag_ptr->fr_var) |
851 | 0 | var_rep_idx = var_rep_max; |
852 | 0 | } |
853 | 0 | } |
854 | |
|
855 | 0 | frag_ptr = frag_ptr->fr_next; |
856 | 0 | } |
857 | 0 | data_buffer[data_buffer_size] = '\0'; |
858 | 0 | return address; |
859 | 0 | } |
860 | | |
861 | | static void |
862 | | print_lines (list_info_type *list, unsigned int lineno, |
863 | | const char *string, unsigned int address) |
864 | 0 | { |
865 | 0 | unsigned int idx; |
866 | 0 | unsigned int nchars; |
867 | 0 | unsigned int lines; |
868 | 0 | unsigned int octet_in_word = 0; |
869 | 0 | char *src = data_buffer; |
870 | 0 | int cur; |
871 | 0 | struct list_message *msg; |
872 | | |
873 | | /* Print the stuff on the first line. */ |
874 | 0 | listing_page (list); |
875 | 0 | nchars = (LISTING_WORD_SIZE * 2 + 1) * listing_lhs_width; |
876 | | |
877 | | /* Print the hex for the first line. */ |
878 | 0 | if (address == ~(unsigned int) 0) |
879 | 0 | { |
880 | 0 | fprintf (list_file, "% 4d ", lineno); |
881 | 0 | for (idx = 0; idx < nchars; idx++) |
882 | 0 | fprintf (list_file, " "); |
883 | |
|
884 | 0 | emit_line (NULL, "\t%s\n", string ? string : ""); |
885 | 0 | return; |
886 | 0 | } |
887 | | |
888 | 0 | if (had_errors ()) |
889 | 0 | fprintf (list_file, "% 4d ???? ", lineno); |
890 | 0 | else |
891 | 0 | fprintf (list_file, "% 4d %04x ", lineno, address); |
892 | | |
893 | | /* And the data to go along with it. */ |
894 | 0 | idx = 0; |
895 | 0 | cur = 0; |
896 | 0 | while (src[cur] && idx < nchars) |
897 | 0 | { |
898 | 0 | int offset; |
899 | 0 | offset = cur; |
900 | 0 | fprintf (list_file, "%c%c", src[offset], src[offset + 1]); |
901 | 0 | cur += 2; |
902 | 0 | octet_in_word++; |
903 | |
|
904 | 0 | if (octet_in_word == LISTING_WORD_SIZE) |
905 | 0 | { |
906 | 0 | fprintf (list_file, " "); |
907 | 0 | idx++; |
908 | 0 | octet_in_word = 0; |
909 | 0 | } |
910 | |
|
911 | 0 | idx += 2; |
912 | 0 | } |
913 | |
|
914 | 0 | for (; idx < nchars; idx++) |
915 | 0 | fprintf (list_file, " "); |
916 | |
|
917 | 0 | emit_line (list, "\t%s\n", string ? string : ""); |
918 | |
|
919 | 0 | for (msg = list->messages; msg; msg = msg->next) |
920 | 0 | emit_line (list, "**** %s\n", msg->message); |
921 | |
|
922 | 0 | for (lines = 0; |
923 | 0 | lines < listing_lhs_cont_lines |
924 | 0 | && src[cur]; |
925 | 0 | lines++) |
926 | 0 | { |
927 | 0 | nchars = ((LISTING_WORD_SIZE * 2) + 1) * listing_lhs_width_second - 1; |
928 | 0 | idx = 0; |
929 | | |
930 | | /* Print any more lines of data, but more compactly. */ |
931 | 0 | fprintf (list_file, "% 4d ", lineno); |
932 | |
|
933 | 0 | while (src[cur] && idx < nchars) |
934 | 0 | { |
935 | 0 | int offset; |
936 | 0 | offset = cur; |
937 | 0 | fprintf (list_file, "%c%c", src[offset], src[offset + 1]); |
938 | 0 | cur += 2; |
939 | 0 | idx += 2; |
940 | 0 | octet_in_word++; |
941 | |
|
942 | 0 | if (octet_in_word == LISTING_WORD_SIZE) |
943 | 0 | { |
944 | 0 | fprintf (list_file, " "); |
945 | 0 | idx++; |
946 | 0 | octet_in_word = 0; |
947 | 0 | } |
948 | 0 | } |
949 | |
|
950 | 0 | emit_line (list, "\n"); |
951 | 0 | } |
952 | 0 | } |
953 | | |
954 | | static void |
955 | | list_symbol_table (void) |
956 | 0 | { |
957 | 0 | extern symbolS *symbol_rootP; |
958 | 0 | int got_some = 0; |
959 | |
|
960 | 0 | symbolS *ptr; |
961 | 0 | eject = 1; |
962 | 0 | listing_page (NULL); |
963 | |
|
964 | 0 | for (ptr = symbol_rootP; ptr != (symbolS *) NULL; ptr = symbol_next (ptr)) |
965 | 0 | { |
966 | 0 | if (SEG_NORMAL (S_GET_SEGMENT (ptr)) |
967 | 0 | || S_GET_SEGMENT (ptr) == absolute_section) |
968 | 0 | { |
969 | | /* Don't report section symbols. They are not interesting. */ |
970 | 0 | if (symbol_section_p (ptr)) |
971 | 0 | continue; |
972 | | |
973 | 0 | if (S_GET_NAME (ptr)) |
974 | 0 | { |
975 | 0 | char buf[30]; |
976 | 0 | valueT val = S_GET_VALUE (ptr); |
977 | |
|
978 | 0 | bfd_sprintf_vma (stdoutput, buf, val); |
979 | 0 | if (!got_some) |
980 | 0 | { |
981 | 0 | fprintf (list_file, "DEFINED SYMBOLS\n"); |
982 | 0 | on_page++; |
983 | 0 | got_some = 1; |
984 | 0 | } |
985 | |
|
986 | 0 | if (symbol_get_frag (ptr) && symbol_get_frag (ptr)->line) |
987 | 0 | { |
988 | 0 | fprintf (list_file, "%20s:%-5d %s:%s %s\n", |
989 | 0 | symbol_get_frag (ptr)->line->file->filename, |
990 | 0 | symbol_get_frag (ptr)->line->line, |
991 | 0 | segment_name (S_GET_SEGMENT (ptr)), |
992 | 0 | buf, S_GET_NAME (ptr)); |
993 | 0 | } |
994 | 0 | else |
995 | 0 | { |
996 | 0 | fprintf (list_file, "%33s:%s %s\n", |
997 | 0 | segment_name (S_GET_SEGMENT (ptr)), |
998 | 0 | buf, S_GET_NAME (ptr)); |
999 | 0 | } |
1000 | |
|
1001 | 0 | on_page++; |
1002 | 0 | listing_page (NULL); |
1003 | 0 | } |
1004 | 0 | } |
1005 | |
|
1006 | 0 | } |
1007 | 0 | if (!got_some) |
1008 | 0 | { |
1009 | 0 | fprintf (list_file, "NO DEFINED SYMBOLS\n"); |
1010 | 0 | on_page++; |
1011 | 0 | } |
1012 | 0 | emit_line (NULL, "\n"); |
1013 | |
|
1014 | 0 | got_some = 0; |
1015 | |
|
1016 | 0 | for (ptr = symbol_rootP; ptr != (symbolS *) NULL; ptr = symbol_next (ptr)) |
1017 | 0 | { |
1018 | 0 | if (S_GET_NAME (ptr) && strlen (S_GET_NAME (ptr)) != 0) |
1019 | 0 | { |
1020 | 0 | if (S_GET_SEGMENT (ptr) == undefined_section) |
1021 | 0 | { |
1022 | 0 | if (!got_some) |
1023 | 0 | { |
1024 | 0 | got_some = 1; |
1025 | |
|
1026 | 0 | emit_line (NULL, "UNDEFINED SYMBOLS\n"); |
1027 | 0 | } |
1028 | |
|
1029 | 0 | emit_line (NULL, "%s\n", S_GET_NAME (ptr)); |
1030 | 0 | } |
1031 | 0 | } |
1032 | 0 | } |
1033 | |
|
1034 | 0 | if (!got_some) |
1035 | 0 | emit_line (NULL, "NO UNDEFINED SYMBOLS\n"); |
1036 | 0 | } |
1037 | | |
1038 | | typedef struct cached_line |
1039 | | { |
1040 | | file_info_type *file; |
1041 | | unsigned int line; |
1042 | | unsigned int bufsize; |
1043 | | char *buffer; |
1044 | | } cached_line; |
1045 | | |
1046 | | static void |
1047 | | alloc_cache (cached_line *cache, unsigned int width) |
1048 | 0 | { |
1049 | 0 | if (cache->bufsize < width) |
1050 | 0 | { |
1051 | 0 | cache->bufsize = width; |
1052 | 0 | free (cache->buffer); |
1053 | 0 | cache->buffer = xmalloc (width); |
1054 | 0 | } |
1055 | 0 | cache->buffer[0] = 0; |
1056 | 0 | } |
1057 | | |
1058 | | static void |
1059 | | print_source (file_info_type * current_file, |
1060 | | list_info_type * list, |
1061 | | unsigned int width) |
1062 | 0 | { |
1063 | 0 | #define NUM_CACHE_LINES 3 |
1064 | 0 | static cached_line cached_lines[NUM_CACHE_LINES]; |
1065 | 0 | static int next_free_line = 0; |
1066 | 0 | cached_line * cache = NULL; |
1067 | |
|
1068 | 0 | if (current_file->linenum > list->hll_line |
1069 | 0 | && list->hll_line > 0) |
1070 | 0 | { |
1071 | | /* This can happen with modern optimizing compilers. The source |
1072 | | lines from the high level language input program are split up |
1073 | | and interleaved, meaning the line number we want to display |
1074 | | (list->hll_line) can have already been displayed. We have |
1075 | | three choices: |
1076 | | |
1077 | | a. Do nothing, since we have already displayed the source |
1078 | | line. This was the old behaviour. |
1079 | | |
1080 | | b. Display the particular line requested again, but only |
1081 | | that line. This is the new behaviour. |
1082 | | |
1083 | | c. Display the particular line requested again and reset |
1084 | | the current_file->line_num value so that we redisplay |
1085 | | all the following lines as well the next time we |
1086 | | encounter a larger line number. */ |
1087 | 0 | int i; |
1088 | | |
1089 | | /* Check the cache, maybe we already have the line saved. */ |
1090 | 0 | for (i = 0; i < NUM_CACHE_LINES; i++) |
1091 | 0 | if (cached_lines[i].file == current_file |
1092 | 0 | && cached_lines[i].line == list->hll_line) |
1093 | 0 | { |
1094 | 0 | cache = cached_lines + i; |
1095 | 0 | break; |
1096 | 0 | } |
1097 | |
|
1098 | 0 | if (i == NUM_CACHE_LINES) |
1099 | 0 | { |
1100 | 0 | cache = cached_lines + next_free_line; |
1101 | 0 | next_free_line ++; |
1102 | 0 | if (next_free_line == NUM_CACHE_LINES) |
1103 | 0 | next_free_line = 0; |
1104 | |
|
1105 | 0 | cache->file = current_file; |
1106 | 0 | cache->line = list->hll_line; |
1107 | 0 | alloc_cache (cache, width); |
1108 | 0 | rebuffer_line (current_file, cache->line, cache->buffer, width); |
1109 | 0 | } |
1110 | |
|
1111 | 0 | emit_line (list, "%4u:%-13s **** %s\n", |
1112 | 0 | cache->line, cache->file->filename, cache->buffer); |
1113 | 0 | return; |
1114 | 0 | } |
1115 | | |
1116 | 0 | if (!current_file->at_end) |
1117 | 0 | { |
1118 | 0 | int num_lines_shown = 0; |
1119 | |
|
1120 | 0 | while (current_file->linenum < list->hll_line |
1121 | 0 | && !current_file->at_end) |
1122 | 0 | { |
1123 | 0 | const char *p; |
1124 | |
|
1125 | 0 | cache = cached_lines + next_free_line; |
1126 | 0 | cache->file = current_file; |
1127 | 0 | cache->line = current_file->linenum + 1; |
1128 | 0 | alloc_cache (cache, width); |
1129 | 0 | p = buffer_line (current_file, cache->buffer, width); |
1130 | | |
1131 | | /* Cache optimization: If printing a group of lines |
1132 | | cache the first and last lines in the group. */ |
1133 | 0 | if (num_lines_shown == 0) |
1134 | 0 | { |
1135 | 0 | next_free_line ++; |
1136 | 0 | if (next_free_line == NUM_CACHE_LINES) |
1137 | 0 | next_free_line = 0; |
1138 | 0 | } |
1139 | |
|
1140 | 0 | emit_line (list, "%4u:%-13s **** %s\n", |
1141 | 0 | cache->line, cache->file->filename, p); |
1142 | 0 | num_lines_shown ++; |
1143 | 0 | } |
1144 | 0 | } |
1145 | 0 | } |
1146 | | |
1147 | | /* Sometimes the user doesn't want to be bothered by the debugging |
1148 | | records inserted by the compiler, see if the line is suspicious. */ |
1149 | | |
1150 | | static bool |
1151 | | debugging_pseudo (list_info_type *list ATTRIBUTE_UNUSED, const char *line) |
1152 | 0 | { |
1153 | 0 | #ifdef OBJ_ELF |
1154 | 0 | static bool in_debug; |
1155 | 0 | bool was_debug; |
1156 | |
|
1157 | 0 | if (list->debugging) |
1158 | 0 | { |
1159 | 0 | in_debug = true; |
1160 | 0 | return true; |
1161 | 0 | } |
1162 | 0 | was_debug = in_debug; |
1163 | 0 | in_debug = false; |
1164 | 0 | #endif |
1165 | |
|
1166 | 0 | while (is_whitespace (*line)) |
1167 | 0 | line++; |
1168 | |
|
1169 | 0 | if (*line != '.') |
1170 | 0 | { |
1171 | 0 | #ifdef OBJ_ELF |
1172 | | /* The ELF compiler sometimes emits blank lines after switching |
1173 | | out of a debugging section. If the next line drops us back |
1174 | | into debugging information, then don't print the blank line. |
1175 | | This is a hack for a particular compiler behaviour, not a |
1176 | | general case. */ |
1177 | 0 | if (was_debug |
1178 | 0 | && *line == '\0' |
1179 | 0 | && list->next != NULL |
1180 | 0 | && list->next->debugging) |
1181 | 0 | { |
1182 | 0 | in_debug = true; |
1183 | 0 | return true; |
1184 | 0 | } |
1185 | 0 | #endif |
1186 | | |
1187 | 0 | return false; |
1188 | 0 | } |
1189 | | |
1190 | 0 | line++; |
1191 | |
|
1192 | 0 | if (startswith (line, "def")) |
1193 | 0 | return true; |
1194 | 0 | if (startswith (line, "val")) |
1195 | 0 | return true; |
1196 | 0 | if (startswith (line, "scl")) |
1197 | 0 | return true; |
1198 | 0 | if (startswith (line, "line")) |
1199 | 0 | return true; |
1200 | 0 | if (startswith (line, "endef")) |
1201 | 0 | return true; |
1202 | 0 | if (startswith (line, "ln")) |
1203 | 0 | return true; |
1204 | 0 | if (startswith (line, "type")) |
1205 | 0 | return true; |
1206 | 0 | if (startswith (line, "size")) |
1207 | 0 | return true; |
1208 | 0 | if (startswith (line, "dim")) |
1209 | 0 | return true; |
1210 | 0 | if (startswith (line, "tag")) |
1211 | 0 | return true; |
1212 | 0 | if (startswith (line, "stabs")) |
1213 | 0 | return true; |
1214 | 0 | if (startswith (line, "stabn")) |
1215 | 0 | return true; |
1216 | | |
1217 | 0 | return false; |
1218 | 0 | } |
1219 | | |
1220 | | static void |
1221 | | listing_listing (char *name ATTRIBUTE_UNUSED) |
1222 | 0 | { |
1223 | 0 | list_info_type *list = head; |
1224 | 0 | file_info_type *current_hll_file = (file_info_type *) NULL; |
1225 | 0 | char *buffer; |
1226 | 0 | const char *p; |
1227 | 0 | int show_listing = 1; |
1228 | 0 | unsigned int width; |
1229 | |
|
1230 | 0 | buffer = XNEWVEC (char, listing_rhs_width); |
1231 | 0 | data_buffer = XNEWVEC (char, MAX_BYTES); |
1232 | 0 | eject = 1; |
1233 | 0 | list = head->next; |
1234 | |
|
1235 | 0 | while (list) |
1236 | 0 | { |
1237 | 0 | unsigned int list_line; |
1238 | |
|
1239 | 0 | width = listing_rhs_width > paper_width ? paper_width : |
1240 | 0 | listing_rhs_width; |
1241 | |
|
1242 | 0 | list_line = list->line; |
1243 | 0 | switch (list->edict) |
1244 | 0 | { |
1245 | 0 | case EDICT_LIST: |
1246 | | /* Skip all lines up to the current. */ |
1247 | 0 | list_line--; |
1248 | 0 | break; |
1249 | 0 | case EDICT_NOLIST: |
1250 | 0 | show_listing--; |
1251 | 0 | break; |
1252 | 0 | case EDICT_NOLIST_NEXT: |
1253 | 0 | if (show_listing == 0) |
1254 | 0 | list_line--; |
1255 | 0 | break; |
1256 | 0 | case EDICT_EJECT: |
1257 | 0 | break; |
1258 | 0 | case EDICT_NONE: |
1259 | 0 | break; |
1260 | 0 | case EDICT_TITLE: |
1261 | 0 | title = list->edict_arg; |
1262 | 0 | break; |
1263 | 0 | case EDICT_SBTTL: |
1264 | 0 | subtitle = list->edict_arg; |
1265 | 0 | break; |
1266 | 0 | default: |
1267 | 0 | abort (); |
1268 | 0 | } |
1269 | | |
1270 | 0 | if (show_listing <= 0) |
1271 | 0 | { |
1272 | 0 | while (list->file->linenum < list_line |
1273 | 0 | && !list->file->at_end) |
1274 | 0 | p = buffer_line (list->file, buffer, width); |
1275 | 0 | } |
1276 | |
|
1277 | 0 | if (list->edict == EDICT_LIST |
1278 | 0 | || (list->edict == EDICT_NOLIST_NEXT && show_listing == 0)) |
1279 | 0 | { |
1280 | | /* Enable listing for the single line that caused the enable. */ |
1281 | 0 | list_line++; |
1282 | 0 | show_listing++; |
1283 | 0 | } |
1284 | |
|
1285 | 0 | if (show_listing > 0) |
1286 | 0 | { |
1287 | | /* Scan down the list and print all the stuff which can be done |
1288 | | with this line (or lines). */ |
1289 | 0 | if (list->hll_file) |
1290 | 0 | current_hll_file = list->hll_file; |
1291 | |
|
1292 | 0 | if (current_hll_file && list->hll_line && (listing & LISTING_HLL)) |
1293 | 0 | print_source (current_hll_file, list, width); |
1294 | |
|
1295 | 0 | if (!list->line_contents || list->file->linenum) |
1296 | 0 | { |
1297 | 0 | while (list->file->linenum < list_line |
1298 | 0 | && !list->file->at_end) |
1299 | 0 | { |
1300 | 0 | unsigned int address; |
1301 | |
|
1302 | 0 | p = buffer_line (list->file, buffer, width); |
1303 | |
|
1304 | 0 | if (list->file->linenum < list_line) |
1305 | 0 | address = ~(unsigned int) 0; |
1306 | 0 | else |
1307 | 0 | address = calc_hex (list); |
1308 | |
|
1309 | 0 | if (!((listing & LISTING_NODEBUG) |
1310 | 0 | && debugging_pseudo (list, p))) |
1311 | 0 | print_lines (list, list->file->linenum, p, address); |
1312 | 0 | } |
1313 | 0 | } |
1314 | |
|
1315 | 0 | if (list->line_contents) |
1316 | 0 | { |
1317 | 0 | if (!((listing & LISTING_NODEBUG) |
1318 | 0 | && debugging_pseudo (list, list->line_contents))) |
1319 | 0 | print_lines (list, list->line, list->line_contents, |
1320 | 0 | calc_hex (list)); |
1321 | |
|
1322 | 0 | free (list->line_contents); |
1323 | 0 | list->line_contents = NULL; |
1324 | 0 | } |
1325 | |
|
1326 | 0 | if (list->edict == EDICT_EJECT) |
1327 | 0 | eject = 1; |
1328 | 0 | } |
1329 | |
|
1330 | 0 | if (list->edict == EDICT_NOLIST_NEXT && show_listing == 1) |
1331 | 0 | --show_listing; |
1332 | |
|
1333 | 0 | list = list->next; |
1334 | 0 | } |
1335 | | |
1336 | 0 | free (buffer); |
1337 | 0 | free (data_buffer); |
1338 | 0 | data_buffer = NULL; |
1339 | 0 | } |
1340 | | |
1341 | | /* Print time stamp in ISO format: yyyy-mm-ddThh:mm:ss.ss+/-zzzz. */ |
1342 | | |
1343 | | static void |
1344 | | print_timestamp (void) |
1345 | 0 | { |
1346 | 0 | const time_t now = time (NULL); |
1347 | 0 | struct tm * timestamp; |
1348 | 0 | char stampstr[MAX_DATELEN]; |
1349 | | |
1350 | | /* Any portable way to obtain subsecond values??? */ |
1351 | 0 | timestamp = localtime (&now); |
1352 | 0 | strftime (stampstr, MAX_DATELEN, "%Y-%m-%dT%H:%M:%S.000%z", timestamp); |
1353 | 0 | fprintf (list_file, _("\n time stamp \t: %s\n\n"), stampstr); |
1354 | 0 | } |
1355 | | |
1356 | | static void |
1357 | | print_single_option (char * opt, int *pos) |
1358 | 0 | { |
1359 | 0 | size_t opt_len = strlen (opt); |
1360 | |
|
1361 | 0 | if ((*pos + opt_len) < paper_width) |
1362 | 0 | { |
1363 | 0 | fprintf (list_file, _("%s "), opt); |
1364 | 0 | *pos = *pos + opt_len; |
1365 | 0 | } |
1366 | 0 | else |
1367 | 0 | { |
1368 | 0 | fprintf (list_file, _("\n\t%s "), opt); |
1369 | 0 | *pos = opt_len; |
1370 | 0 | } |
1371 | 0 | } |
1372 | | |
1373 | | /* Print options passed to as. */ |
1374 | | |
1375 | | static void |
1376 | | print_options (char ** argv) |
1377 | 0 | { |
1378 | 0 | const char *field_name = _("\n options passed\t: "); |
1379 | 0 | int pos = strlen (field_name); |
1380 | 0 | char **p; |
1381 | |
|
1382 | 0 | fputs (field_name, list_file); |
1383 | 0 | for (p = &argv[1]; *p != NULL; p++) |
1384 | 0 | if (**p == '-') |
1385 | 0 | { |
1386 | | /* Ignore these. */ |
1387 | 0 | if (strcmp (*p, "-o") == 0) |
1388 | 0 | { |
1389 | 0 | if (p[1] != NULL) |
1390 | 0 | p++; |
1391 | 0 | continue; |
1392 | 0 | } |
1393 | 0 | if (strcmp (*p, "-v") == 0) |
1394 | 0 | continue; |
1395 | | |
1396 | 0 | print_single_option (*p, &pos); |
1397 | 0 | } |
1398 | 0 | } |
1399 | | |
1400 | | /* Print a first section with basic info like file names, as version, |
1401 | | options passed, target, and timestamp. |
1402 | | The format of this section is as follows: |
1403 | | |
1404 | | AS VERSION |
1405 | | |
1406 | | fieldname TAB ':' fieldcontents |
1407 | | { TAB fieldcontents-cont } */ |
1408 | | |
1409 | | static void |
1410 | | listing_general_info (char ** argv) |
1411 | 0 | { |
1412 | | /* Print the stuff on the first line. */ |
1413 | 0 | eject = 1; |
1414 | 0 | listing_page (NULL); |
1415 | |
|
1416 | 0 | fprintf (list_file, |
1417 | 0 | _(" GNU assembler version %s (%s)\n\t using BFD version %s."), |
1418 | 0 | VERSION, TARGET_ALIAS, BFD_VERSION_STRING); |
1419 | 0 | print_options (argv); |
1420 | 0 | fprintf (list_file, _("\n input file \t: %s"), fn); |
1421 | 0 | fprintf (list_file, _("\n output file \t: %s"), out_file_name); |
1422 | 0 | fprintf (list_file, _("\n target \t: %s"), TARGET_CANONICAL); |
1423 | 0 | print_timestamp (); |
1424 | 0 | } |
1425 | | |
1426 | | void |
1427 | | listing_print (char *name, char **argv) |
1428 | 0 | { |
1429 | 0 | int using_stdout; |
1430 | |
|
1431 | 0 | title = ""; |
1432 | 0 | subtitle = ""; |
1433 | |
|
1434 | 0 | if (name == NULL) |
1435 | 0 | { |
1436 | 0 | list_file = stdout; |
1437 | 0 | using_stdout = 1; |
1438 | 0 | } |
1439 | 0 | else |
1440 | 0 | { |
1441 | 0 | list_file = fopen (name, FOPEN_WT); |
1442 | 0 | if (list_file != NULL) |
1443 | 0 | using_stdout = 0; |
1444 | 0 | else |
1445 | 0 | { |
1446 | 0 | as_warn (_("can't open %s: %s"), name, xstrerror (errno)); |
1447 | 0 | list_file = stdout; |
1448 | 0 | using_stdout = 1; |
1449 | 0 | } |
1450 | 0 | } |
1451 | |
|
1452 | 0 | if (listing & LISTING_NOFORM) |
1453 | 0 | paper_height = 0; |
1454 | |
|
1455 | 0 | if (listing & LISTING_GENERAL) |
1456 | 0 | listing_general_info (argv); |
1457 | |
|
1458 | 0 | if (listing & LISTING_LISTING) |
1459 | 0 | listing_listing (name); |
1460 | |
|
1461 | 0 | if (listing & LISTING_SYMBOLS) |
1462 | 0 | list_symbol_table (); |
1463 | |
|
1464 | 0 | if (! using_stdout) |
1465 | 0 | { |
1466 | 0 | if (fclose (list_file) == EOF) |
1467 | 0 | as_warn (_("can't close %s: %s"), name, xstrerror (errno)); |
1468 | 0 | } |
1469 | |
|
1470 | 0 | if (last_open_file) |
1471 | 0 | fclose (last_open_file); |
1472 | 0 | } |
1473 | | |
1474 | | void |
1475 | | listing_file (const char *name) |
1476 | 28 | { |
1477 | 28 | fn = name; |
1478 | 28 | } |
1479 | | |
1480 | | void |
1481 | | listing_eject (int ignore ATTRIBUTE_UNUSED) |
1482 | 0 | { |
1483 | 0 | if (listing) |
1484 | 0 | listing_tail->edict = EDICT_EJECT; |
1485 | 0 | } |
1486 | | |
1487 | | /* Turn listing on or off. An argument of 0 means to turn off |
1488 | | listing. An argument of 1 means to turn on listing. An argument |
1489 | | of 2 means to turn off listing, but as of the next line; that is, |
1490 | | the current line should be listed, but the next line should not. */ |
1491 | | |
1492 | | void |
1493 | | listing_list (int on) |
1494 | 0 | { |
1495 | 0 | if (listing) |
1496 | 0 | { |
1497 | 0 | switch (on) |
1498 | 0 | { |
1499 | 0 | case 0: |
1500 | 0 | if (listing_tail->edict == EDICT_LIST) |
1501 | 0 | listing_tail->edict = EDICT_NONE; |
1502 | 0 | else |
1503 | 0 | listing_tail->edict = EDICT_NOLIST; |
1504 | 0 | break; |
1505 | 0 | case 1: |
1506 | 0 | if (listing_tail->edict == EDICT_NOLIST |
1507 | 0 | || listing_tail->edict == EDICT_NOLIST_NEXT) |
1508 | 0 | listing_tail->edict = EDICT_NONE; |
1509 | 0 | else |
1510 | 0 | listing_tail->edict = EDICT_LIST; |
1511 | 0 | break; |
1512 | 0 | case 2: |
1513 | 0 | listing_tail->edict = EDICT_NOLIST_NEXT; |
1514 | 0 | break; |
1515 | 0 | default: |
1516 | 0 | abort (); |
1517 | 0 | } |
1518 | 0 | } |
1519 | 0 | } |
1520 | | |
1521 | | void |
1522 | | listing_psize (int width_only) |
1523 | 13 | { |
1524 | 13 | if (! width_only) |
1525 | 0 | { |
1526 | 0 | paper_height = get_absolute_expression (); |
1527 | |
|
1528 | 0 | if (paper_height > 1000) |
1529 | 0 | { |
1530 | 0 | paper_height = 0; |
1531 | 0 | as_warn (_("strange paper height, set to no form")); |
1532 | 0 | } |
1533 | |
|
1534 | 0 | if (*input_line_pointer != ',') |
1535 | 0 | { |
1536 | 0 | demand_empty_rest_of_line (); |
1537 | 0 | return; |
1538 | 0 | } |
1539 | | |
1540 | 0 | ++input_line_pointer; |
1541 | 0 | } |
1542 | | |
1543 | 13 | { |
1544 | 13 | expressionS exp; |
1545 | | |
1546 | 13 | (void) expression_and_evaluate (& exp); |
1547 | | |
1548 | 13 | if (exp.X_op == O_constant) |
1549 | 13 | { |
1550 | 13 | offsetT new_width = exp.X_add_number; |
1551 | | |
1552 | 13 | if (new_width > 7) |
1553 | 3 | paper_width = new_width; |
1554 | 10 | else |
1555 | 10 | as_bad (_("new paper width is too small")); |
1556 | 13 | } |
1557 | 0 | else if (exp.X_op != O_absent) |
1558 | 0 | as_bad (_("bad or irreducible expression for paper width")); |
1559 | 0 | else |
1560 | 0 | as_bad (_("missing expression for paper width")); |
1561 | 13 | } |
1562 | | |
1563 | 13 | demand_empty_rest_of_line (); |
1564 | 13 | } |
1565 | | |
1566 | | void |
1567 | | listing_nopage (int ignore ATTRIBUTE_UNUSED) |
1568 | 0 | { |
1569 | 0 | paper_height = 0; |
1570 | 0 | } |
1571 | | |
1572 | | void |
1573 | | listing_title (int depth) |
1574 | 34 | { |
1575 | 34 | int quoted; |
1576 | 34 | char *start; |
1577 | 34 | char *ttl; |
1578 | 34 | unsigned int length; |
1579 | | |
1580 | 34 | SKIP_WHITESPACE (); |
1581 | 34 | if (*input_line_pointer != '\"') |
1582 | 34 | quoted = 0; |
1583 | 0 | else |
1584 | 0 | { |
1585 | 0 | quoted = 1; |
1586 | 0 | ++input_line_pointer; |
1587 | 0 | } |
1588 | | |
1589 | 34 | start = input_line_pointer; |
1590 | | |
1591 | 740 | while (*input_line_pointer) |
1592 | 720 | { |
1593 | 720 | if (quoted |
1594 | 720 | ? *input_line_pointer == '\"' |
1595 | 720 | : is_end_of_stmt (*input_line_pointer)) |
1596 | 14 | { |
1597 | 14 | if (listing) |
1598 | 0 | { |
1599 | 0 | length = input_line_pointer - start; |
1600 | 0 | ttl = xmemdup0 (start, length); |
1601 | 0 | listing_tail->edict = depth ? EDICT_SBTTL : EDICT_TITLE; |
1602 | 0 | listing_tail->edict_arg = ttl; |
1603 | 0 | } |
1604 | 14 | if (quoted) |
1605 | 0 | input_line_pointer++; |
1606 | 14 | demand_empty_rest_of_line (); |
1607 | 14 | return; |
1608 | 14 | } |
1609 | 706 | else if (*input_line_pointer == '\n') |
1610 | 0 | { |
1611 | 0 | as_bad (_("new line in title")); |
1612 | 0 | demand_empty_rest_of_line (); |
1613 | 0 | return; |
1614 | 0 | } |
1615 | 706 | else |
1616 | 706 | { |
1617 | 706 | input_line_pointer++; |
1618 | 706 | } |
1619 | 720 | } |
1620 | 34 | } |
1621 | | |
1622 | | void |
1623 | | listing_source_line (unsigned int line) |
1624 | 0 | { |
1625 | 0 | if (listing) |
1626 | 0 | { |
1627 | 0 | new_frag (); |
1628 | 0 | listing_tail->hll_line = line; |
1629 | 0 | new_frag (); |
1630 | 0 | } |
1631 | 0 | } |
1632 | | |
1633 | | void |
1634 | | listing_source_file (const char *file) |
1635 | 0 | { |
1636 | 0 | if (listing) |
1637 | 0 | listing_tail->hll_file = file_info (file); |
1638 | 0 | } |
1639 | | |
1640 | | #endif |