* strings.c, strings.1: New files.
[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;  /* false if any files were given.  */
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", program_name, optarg);
148               exit (1);
149             }
150           break;
151
152         case 'o':
153           print_addresses = true;
154           address_radix = 8;
155           break;
156
157         case 't':
158           print_addresses = true;
159           if (optarg[1] != '\0')
160             usage ();
161           switch (optarg[0])
162             {
163             case 'o':
164               address_radix = 8;
165               break;
166
167             case 'd':
168               address_radix = 10;
169               break;
170
171             case 'x':
172               address_radix = 16;
173               break;
174
175             default:
176               usage ();
177             }
178           break;
179
180         case 'v':
181           printf ("%s version %s\n", program_name, program_version);
182           exit (0);
183
184         case '?':
185           usage (stderr);
186
187         default:
188           if (string_min < 0)
189             string_min = optc;
190           else
191             string_min = string_min * 10 + optc - '0';
192           break;
193         }
194     }
195
196   if (string_min < 0)
197     string_min = 4;
198
199   bfd_init ();
200   devnull = fopen ("/dev/null", "r");
201   if (devnull == NULL)
202     {
203       fprintf (stderr, "%s: ", program_name);
204       perror ("/dev/null");
205       exit (1);
206     }
207
208   for (; optind < argc; ++optind)
209     {
210       if (!strcmp (argv[optind], "-"))
211         datasection_only = false;
212       else
213         {
214           files_given = true;
215           exit_status |= (strings_file (argv[optind]) == false);
216         }
217     }
218
219   if (files_given == false)
220     usage (stderr);
221
222   exit (exit_status);
223 }
224 \f
225 /* Scan the sections of the file ABFD, whose printable name is FILE.
226    If any of them contain initialized data,
227    set `got_a_section' and print the strings in them.  */
228
229 static void
230 strings_a_section (abfd, sect, file)
231      bfd *abfd;
232      asection *sect;
233      PTR file;
234 {
235   if ((sect->flags & DATA_FLAGS) == DATA_FLAGS)
236     {
237       bfd_size_type sz = bfd_get_section_size_before_reloc (sect);
238       PTR mem = xmalloc (sz);
239       if (bfd_get_section_contents (abfd, sect, mem, (file_ptr) 0, sz))
240         {
241           got_a_section = true;
242           dump_strings (file, devnull, sect->filepos, 0, sz, mem);
243         }
244       free (mem);
245     }
246 }
247
248 /* Print the strings in the initialized data section of FILE.
249    Return true if successful,
250    false if not (such as if FILE is not an object file).  */
251
252 static boolean
253 strings_object_file (file)
254      char *file;
255 {
256   bfd *abfd = bfd_openr (file, NULL);
257
258   if (abfd == NULL)
259     {
260       if (bfd_error != system_call_error)
261         {
262           /* Out of memory, or an invalid target is specified by the
263              GNUTARGET environment variable.  */
264           fprintf (stderr, "%s: ", program_name);
265           bfd_perror (file);
266         }
267       return false;
268     }
269
270   /* For some reason, without this call, the BFD has no sections.
271      This call is only for the side effect of reading in the sections.  */
272   bfd_check_format (abfd, bfd_object);
273
274   got_a_section = false;
275   bfd_map_over_sections (abfd, strings_a_section, file);
276
277   if (!bfd_close (abfd))
278     {
279       fprintf (stderr, "%s: ", program_name);
280       bfd_perror (file);
281       return false;
282     }
283
284   return got_a_section;
285 }
286
287 /* Print the strings in FILE.  Return true if ok, false if an error occurs.  */
288
289 static boolean
290 strings_file (file)
291      char *file;
292 {
293   /* If we weren't told to scan the whole file,
294      try to open it as an object file and only look at
295      initialized data sections.  If that fails, fall back to the
296      whole file.  */
297   if (!datasection_only || !strings_object_file (file))
298     {
299       FILE *stream;
300
301       stream = fopen (file, "r");
302       if (stream == NULL)
303         {
304           fprintf (stderr, "%s: ", program_name);
305           perror (file);
306           return false;
307         }
308
309       dump_strings (file, stream, (file_ptr) 0, 0, 0, (char *) 0);
310
311       if (fclose (stream) == EOF)
312         {
313           fprintf (stderr, "%s: ", program_name);
314           perror (file);
315           return false;
316         }
317     }
318
319   return true;
320 }
321 \f
322 /* Find the strings in file FILENAME, read from STREAM.
323    Assume that STREAM is positioned so that the next byte read
324    is at address ADDRESS in the file.
325    Stop reading at address STOP_POINT in the file, if nonzero.
326
327    Optionally the caller can supply a buffer of characters
328    to be processed before the data in STREAM.
329    MAGIC is the address of the buffer and
330    MAGICCOUNT is how many characters are in it.
331    Those characters come at address ADDRESS and the data in STREAM follow.  */
332
333 static void
334 dump_strings (filename, stream, address, stop_point, magiccount, magic)
335      char *filename;
336      FILE *stream;
337      file_ptr address;
338      int stop_point;
339      int magiccount;
340      char *magic;
341 {
342   int bufsize = 100;
343   char *buf = (char *) xmalloc (bufsize);
344
345   while (1)
346     {
347       int i;
348       int c;
349
350       /* See if the next `string_min' chars are all graphic chars.  */
351     tryline:
352       if (stop_point && address >= stop_point)
353         break;
354       for (i = 0; i < string_min; i++)
355         {
356           if (magiccount)
357             {
358               magiccount--;
359               c = *magic++;
360             }
361           else
362             {
363               c = getc (stream);
364               if (c < 0)
365                 return;
366             }
367           address++;
368           if (!isgraphic (c))
369             /* Found a non-graphic.  Try again starting with next char.  */
370             goto tryline;
371           buf[i] = c;
372         }
373
374       /* We found a run of `string_min' graphic characters.
375          Now see if it is terminated with a NUL byte or a newline.   */
376       while (1)
377         {
378           if (i == bufsize)
379             {
380               bufsize *= 2;
381               buf = (char *) xrealloc (buf, bufsize);
382             }
383           if (magiccount)
384             {
385               magiccount--;
386               c = *magic++;
387             }
388           else
389             {
390               c = getc (stream);
391               if (c < 0)
392                 return;
393             }
394           address++;
395           if (c == '\0' || c == '\n')
396             break;              /* It is; print this string.  */
397           if (!isgraphic (c))
398             goto tryline;       /* It isn't; give up on this string.  */
399           buf[i++] = c;         /* The string continues; store it all.  */
400         }
401
402       /* If we get here, the string is all graphics and properly terminated,
403          so print it.  It is all in `buf' and `i' is its length.  */
404       buf[i] = '\0';
405       if (print_filenames)
406         printf ("%s: ", filename);
407       if (print_addresses)
408         switch (address_radix)
409           {
410           case 8:
411             printf ("%7lo ", address - i - 1);
412             break;
413
414           case 10:
415             printf ("%7ld ", address - i - 1);
416             break;
417
418           case 16:
419             printf ("%7lx ", address - i - 1);
420             break;
421           }
422
423       for (i = 0; (c = buf[i]) != '\0'; i++)
424         switch (c)
425           {
426           case '\n':
427             printf ("\\n");
428             break;
429           case '\t':
430             printf ("\\t");
431             break;
432           case '\f':
433             printf ("\\f");
434             break;
435           case '\b':
436             printf ("\\b");
437             break;
438           case '\r':
439             printf ("\\r");
440             break;
441           default:
442             putchar (c);
443           }
444       putchar ('\n');
445     }
446 }
447 \f
448 /* Parse string S as an integer, using decimal radix by default,
449    but allowing octal and hex numbers as in C.  */
450
451 static int
452 integer_arg (s)
453      char *s;
454 {
455   int value;
456   int radix = 10;
457   char *p = s;
458   int c;
459
460   if (*p != '0')
461     radix = 10;
462   else if (*++p == 'x')
463     {
464       radix = 16;
465       p++;
466     }
467   else
468     radix = 8;
469
470   value = 0;
471   while (((c = *p++) >= '0' && c <= '9')
472          || (radix == 16 && (c & ~40) >= 'A' && (c & ~40) <= 'Z'))
473     {
474       value *= radix;
475       if (c >= '0' && c <= '9')
476         value += c - '0';
477       else
478         value += (c & ~40) - 'A';
479     }
480
481   if (c == 'b')
482     value *= 512;
483   else if (c == 'B')
484     value *= 1024;
485   else
486     p--;
487
488   if (*p)
489     {
490       fprintf (stderr, "%s: invalid integer argument %s\n", program_name, s);
491       exit (1);
492     }
493   return value;
494 }
495
496 static void
497 usage (stream)
498      FILE *stream;
499 {
500   fprintf (stream, "\
501 Usage: %s [-afhov] [-n min-len] [-min-len] [-t {o,x,d}] [-]\n\
502        [--all] [--print-file-name] [--bytes=min-len] [--radix={o,x,d}]\n\
503        [--help] [--version] file...\n",
504            program_name);
505   exit (1);
506 }