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