* Fix a couple of typos.
[external/binutils.git] / binutils / strings.c
1 /* strings -- print the strings of printable characters in files
2    Copyright (C) 1993 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17 \f
18 /* Usage: strings [options] file...
19
20    Options:
21    --all
22    -a
23    -            Do not scan only the initialized data section of object files.
24
25    --print-file-name
26    -f           Print the name of the file before each string.
27
28    --bytes=min-len
29    -n min-len
30    -min-len     Print graphic char sequences, MIN-LEN or more bytes long,
31                 that are followed by a NUL or a newline.  Default is 4.
32
33    --radix={o,x,d}
34    -t {o,x,d}   Print the offset within the file before each string,
35                 in octal/hex/decimal.
36
37    -o           Like -to.  (Some other implementations have -o like -to,
38                 others like -td.  We chose one arbitrarily.)
39
40    --help
41    -h           Print the usage message on the standard output.
42
43    --version
44    -v           Print the program version number.
45
46    Written by Richard Stallman <rms@gnu.ai.mit.edu>
47    and David MacKenzie <djm@gnu.ai.mit.edu>.  */
48
49 #include <stdio.h>
50 #include <getopt.h>
51 #include <ctype.h>
52 #include <errno.h>
53 #include <bfd.h>
54
55 #ifdef isascii
56 #define isgraphic(c) (isascii (c) && isprint (c))
57 #else
58 #define isgraphic(c) (isprint (c))
59 #endif
60
61 #ifndef errno
62 extern int errno;
63 #endif
64
65 /* The BFD section flags that identify an initialized data section.  */
66 #define DATA_FLAGS (SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_HAS_CONTENTS)
67
68 /* Radix for printing addresses (must be 8, 10 or 16).  */
69 static int address_radix;
70
71 /* Minimum length of sequence of graphic chars to trigger output.  */
72 static int string_min;
73
74 /* true means print address within file for each string.  */
75 static boolean print_addresses;
76
77 /* true means print filename for each string.  */
78 static boolean print_filenames;
79
80 /* true means for object files scan only the data section.  */
81 static boolean datasection_only;
82
83 /* true if we found an initialized data section in the current file.  */
84 static boolean got_a_section;
85
86 /* Opened to /dev/null for reading from a BFD.  */
87 static FILE *devnull;
88
89 extern char *program_name;
90 extern char *program_version;
91
92 static struct option long_options[] =
93 {
94   {"all", no_argument, NULL, 'a'},
95   {"print-file-name", no_argument, NULL, 'f'},
96   {"bytes", required_argument, NULL, 'n'},
97   {"radix", required_argument, NULL, 't'},
98   {"help", no_argument, NULL, 'h'},
99   {"version", no_argument, NULL, 'v'},
100   {NULL, 0, NULL, 0}
101 };
102
103 char *xmalloc ();
104 char *xrealloc ();
105
106 static boolean strings_file ();
107 static int integer_arg ();
108 static void dump_strings ();
109 static void usage ();
110 \f
111 void
112 main (argc, argv)
113      int argc;
114      char **argv;
115 {
116   int optc;
117   int exit_status = 0;
118   boolean files_given = false;
119
120   program_name = argv[0];
121   string_min = -1;
122   print_addresses = false;
123   print_filenames = false;
124   datasection_only = true;
125
126   while ((optc = getopt_long (argc, argv, "afhn:ot:v0123456789",
127                               long_options, (int *) 0)) != EOF)
128     {
129       switch (optc)
130         {
131         case 'a':
132           datasection_only = false;
133           break;
134
135         case 'f':
136           print_filenames = true;
137           break;
138
139         case 'h':
140           usage (stdout);
141           exit (0);
142
143         case 'n':
144           string_min = integer_arg (optarg);
145           if (string_min < 1)
146             {
147               fprintf (stderr, "%s: invalid number %s\n",
148                        program_name, optarg);
149               exit (1);
150             }
151           break;
152
153         case 'o':
154           print_addresses = true;
155           address_radix = 8;
156           break;
157
158         case 't':
159           print_addresses = true;
160           if (optarg[1] != '\0')
161             usage ();
162           switch (optarg[0])
163             {
164             case 'o':
165               address_radix = 8;
166               break;
167
168             case 'd':
169               address_radix = 10;
170               break;
171
172             case 'x':
173               address_radix = 16;
174               break;
175
176             default:
177               usage ();
178             }
179           break;
180
181         case 'v':
182           printf ("%s version %s\n", program_name, program_version);
183           exit (0);
184
185         case '?':
186           usage (stderr);
187
188         default:
189           if (string_min < 0)
190             string_min = optc;
191           else
192             string_min = string_min * 10 + optc - '0';
193           break;
194         }
195     }
196
197   if (string_min < 0)
198     string_min = 4;
199
200   bfd_init ();
201   devnull = fopen ("/dev/null", "r");
202   if (devnull == NULL)
203     {
204       fprintf (stderr, "%s: ", program_name);
205       perror ("/dev/null");
206       exit (1);
207     }
208
209   for (; optind < argc; ++optind)
210     {
211       if (!strcmp (argv[optind], "-"))
212         datasection_only = false;
213       else
214         {
215           files_given = true;
216           exit_status |= (strings_file (argv[optind]) == false);
217         }
218     }
219
220   if (files_given == false)
221     usage (stderr);
222
223   exit (exit_status);
224 }
225 \f
226 /* Scan the sections of the file ABFD, whose printable name is FILE.
227    If any of them contain initialized data,
228    set `got_a_section' and print the strings in them.  */
229
230 static void
231 strings_a_section (abfd, sect, file)
232      bfd *abfd;
233      asection *sect;
234      PTR file;
235 {
236   if ((sect->flags & DATA_FLAGS) == DATA_FLAGS)
237     {
238       bfd_size_type sz = bfd_get_section_size_before_reloc (sect);
239       PTR mem = xmalloc (sz);
240       if (bfd_get_section_contents (abfd, sect, mem, (file_ptr) 0, sz))
241         {
242           got_a_section = true;
243           dump_strings (file, devnull, sect->filepos, 0, sz, mem);
244         }
245       free (mem);
246     }
247 }
248
249 /* Print the strings in the initialized data section of FILE.
250    Return true if successful,
251    false if not (such as if FILE is not an object file).  */
252
253 static boolean
254 strings_object_file (file)
255      char *file;
256 {
257   bfd *abfd = bfd_openr (file, NULL);
258
259   if (abfd == NULL)
260     {
261       if (bfd_error != system_call_error)
262         {
263           /* Out of memory, or an invalid target is specified by the
264              GNUTARGET environment variable.  */
265           fprintf (stderr, "%s: ", program_name);
266           bfd_perror (file);
267         }
268       return false;
269     }
270
271   /* For some reason, without this call, the BFD has no sections.
272      This call is only for the side effect of reading in the sections.  */
273   bfd_check_format (abfd, bfd_object);
274
275   got_a_section = false;
276   bfd_map_over_sections (abfd, strings_a_section, file);
277
278   if (!bfd_close (abfd))
279     {
280       fprintf (stderr, "%s: ", program_name);
281       bfd_perror (file);
282       return false;
283     }
284
285   return got_a_section;
286 }
287
288 /* Print the strings in FILE.  Return true if ok, false if an error occurs.  */
289
290 static boolean
291 strings_file (file)
292      char *file;
293 {
294   /* If we weren't told to scan the whole file,
295      try to open it as an object file and only look at
296      initialized data sections.  If that fails, fall back to the
297      whole file.  */
298   if (!datasection_only || !strings_object_file (file))
299     {
300       FILE *stream;
301
302       stream = fopen (file, "r");
303       if (stream == NULL)
304         {
305           fprintf (stderr, "%s: ", program_name);
306           perror (file);
307           return false;
308         }
309
310       dump_strings (file, stream, (file_ptr) 0, 0, 0, (char *) 0);
311
312       if (fclose (stream) == EOF)
313         {
314           fprintf (stderr, "%s: ", program_name);
315           perror (file);
316           return false;
317         }
318     }
319
320   return true;
321 }
322 \f
323 /* Find the strings in file FILENAME, read from STREAM.
324    Assume that STREAM is positioned so that the next byte read
325    is at address ADDRESS in the file.
326    Stop reading at address STOP_POINT in the file, if nonzero.
327
328    Optionally the caller can supply a buffer of characters
329    to be processed before the data in STREAM.
330    MAGIC is the address of the buffer and
331    MAGICCOUNT is how many characters are in it.
332    Those characters come at address ADDRESS and the data in STREAM follow.  */
333
334 static void
335 dump_strings (filename, stream, address, stop_point, magiccount, magic)
336      char *filename;
337      FILE *stream;
338      file_ptr address;
339      int stop_point;
340      int magiccount;
341      char *magic;
342 {
343   int bufsize = 100;
344   char *buf = (char *) xmalloc (bufsize);
345
346   while (1)
347     {
348       int i;
349       int c;
350
351       /* See if the next `string_min' chars are all graphic chars.  */
352     tryline:
353       if (stop_point && address >= stop_point)
354         break;
355       for (i = 0; i < string_min; i++)
356         {
357           if (magiccount)
358             {
359               magiccount--;
360               c = *magic++;
361             }
362           else
363             {
364               c = getc (stream);
365               if (c < 0)
366                 return;
367             }
368           address++;
369           if (!isgraphic (c))
370             /* Found a non-graphic.  Try again starting with next char.  */
371             goto tryline;
372           buf[i] = c;
373         }
374
375       /* We found a run of `string_min' graphic characters.
376          Now see if it is terminated with a NUL byte or a newline.   */
377       while (1)
378         {
379           if (i == bufsize)
380             {
381               bufsize *= 2;
382               buf = (char *) xrealloc (buf, bufsize);
383             }
384           if (magiccount)
385             {
386               magiccount--;
387               c = *magic++;
388             }
389           else
390             {
391               c = getc (stream);
392               if (c < 0)
393                 return;
394             }
395           address++;
396           if (c == '\0' || c == '\n')
397             break;              /* It is; print this string.  */
398           if (!isgraphic (c))
399             goto tryline;       /* It isn't; give up on this string.  */
400           buf[i++] = c;         /* The string continues; store it all.  */
401         }
402
403       /* If we get here, the string is all graphics and properly terminated,
404          so print it.  It is all in `buf' and `i' is its length.  */
405       buf[i] = '\0';
406       if (print_filenames)
407         printf ("%s: ", filename);
408       if (print_addresses)
409         switch (address_radix)
410           {
411           case 8:
412             printf ("%7lo ", address - i - 1);
413             break;
414
415           case 10:
416             printf ("%7ld ", address - i - 1);
417             break;
418
419           case 16:
420             printf ("%7lx ", address - i - 1);
421             break;
422           }
423
424       for (i = 0; (c = buf[i]) != '\0'; i++)
425         switch (c)
426           {
427           case '\n':
428             printf ("\\n");
429             break;
430           case '\t':
431             printf ("\\t");
432             break;
433           case '\f':
434             printf ("\\f");
435             break;
436           case '\b':
437             printf ("\\b");
438             break;
439           case '\r':
440             printf ("\\r");
441             break;
442           default:
443             putchar (c);
444           }
445       putchar ('\n');
446     }
447 }
448 \f
449 /* Parse string S as an integer, using decimal radix by default,
450    but allowing octal and hex numbers as in C.  */
451
452 static int
453 integer_arg (s)
454      char *s;
455 {
456   int value;
457   int radix = 10;
458   char *p = s;
459   int c;
460
461   if (*p != '0')
462     radix = 10;
463   else if (*++p == 'x')
464     {
465       radix = 16;
466       p++;
467     }
468   else
469     radix = 8;
470
471   value = 0;
472   while (((c = *p++) >= '0' && c <= '9')
473          || (radix == 16 && (c & ~40) >= 'A' && (c & ~40) <= 'Z'))
474     {
475       value *= radix;
476       if (c >= '0' && c <= '9')
477         value += c - '0';
478       else
479         value += (c & ~40) - 'A';
480     }
481
482   if (c == 'b')
483     value *= 512;
484   else if (c == 'B')
485     value *= 1024;
486   else
487     p--;
488
489   if (*p)
490     {
491       fprintf (stderr, "%s: invalid integer argument %s\n", program_name, s);
492       exit (1);
493     }
494   return value;
495 }
496
497 static void
498 usage (stream)
499      FILE *stream;
500 {
501   fprintf (stream, "\
502 Usage: %s [-afhov] [-n min-len] [-min-len] [-t {o,x,d}] [-]\n\
503        [--all] [--print-file-name] [--bytes=min-len] [--radix={o,x,d}]\n\
504        [--help] [--version] file...\n",
505            program_name);
506   exit (1);
507 }