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