ld64 with ppc
[darwin-xtools/darwin-xtools-svp.git] / cctools / misc / strings.c
blobe8d62a74da46d5d29c6eeebc3661ec096f530ed4
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 /* $OpenBSD: strings.c,v 1.2 1996/06/26 05:39:30 deraadt Exp $ */
24 /* $NetBSD: strings.c,v 1.7 1995/02/15 15:49:19 jtc Exp $ */
27 * Copyright (c) 1980, 1987, 1993
28 * The Regents of the University of California. All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
59 * The NeXT Computer, Inc. strings(1) program that handles fat files, archives
60 * and Mach-O objects files (no BSD a.out files). Some lines of code were
61 * taken and adapted from the BSD release.
63 * CHANGES FROM THE BSD VERSION OF strings(1):
64 * Object files are no longer recognized as objects if read from standard input
65 * but must be a command line argument to be treated as an object file. The
66 * result is if an object is read from standard input the entire file is
67 * searched for strings not just the appropate sections. With the handling of
68 * fat files any file that contains objects has it contents processed as objects
69 * which includes the objects in archives which the 4.3bsd strings(1) did not
70 * process as object files. Object files may be of the form "libx.a(x.o)"
71 * which refer to an archive member.
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <ctype.h>
76 #include <limits.h>
77 #include <sys/types.h>
78 #include <sys/stat.h>
79 #include "stuff/bool.h"
80 #include "stuff/ofile.h"
81 #include "stuff/errors.h"
82 #include "stuff/allocate.h"
84 char *progname = NULL;
86 struct flags {
87 enum bool treat_as_data;
88 enum bool print_offsets;
89 char *offset_format;
90 enum bool all_sections;
91 uint32_t minimum_length;
94 static void usage(
95 void);
96 static void ofile_processor(
97 struct ofile *ofile,
98 char *arch_name,
99 void *cookie);
100 static void ofile_find(
101 char *addr,
102 uint32_t size,
103 uint32_t offset,
104 struct flags *flags);
105 static void find(
106 uint32_t cnt,
107 struct flags *flags);
108 static enum bool dirt(
109 int c);
111 /* apple_version is created by the libstuff/Makefile */
112 extern char apple_version[];
113 char *version = apple_version;
115 /* likewise xtools_version and support_url. */
116 extern char xtools_version[];
117 extern char package_version[];
118 extern char support_url[];
121 main(
122 int argc,
123 char **argv,
124 char **envp)
126 struct flags flags;
127 int i;
128 uint32_t j, nfiles;
129 char *endp, *pnam;
130 struct arch_flag *arch_flags;
131 uint32_t narch_flags;
132 enum bool all_archs, rest_args_files, use_member_syntax;
133 struct stat stat_buf;
135 progname = argv[0];
136 pnam = strrchr(progname, '/');
137 pnam = (pnam)?pnam+1:progname;
139 nfiles = 0;
140 arch_flags = NULL;
141 narch_flags = 0;
142 all_archs = FALSE;
144 flags.treat_as_data = FALSE;
145 flags.print_offsets = FALSE;
146 flags.offset_format = NULL;
147 flags.all_sections = FALSE;
148 flags.minimum_length = 4;
150 rest_args_files = FALSE;
151 for(i = 1; i < argc; i++){
152 if(strcmp(argv[i], "--version") == 0){
153 /* Implement a gnu-style --version. */
154 fprintf(stdout, "xtools-%s %s %s\nBased on Apple Inc. %s\n",
155 xtools_version, pnam, package_version, apple_version);
156 exit(0);
157 } else if(strcmp(argv[i], "--help") == 0){
158 fprintf(stdout, "Usage: %s [-] [-a] [-o] [-t format] [-number] "
159 "[-n number] [[-arch <arch_flag>] ...] [--] [file ...]\n",
160 pnam);
161 fprintf(stdout, "Please report bugs to %s\n", support_url);
162 exit(0);
164 if(rest_args_files == FALSE && argv[i][0] == '-'){
165 if(argv[i][1] == '\0')
166 flags.treat_as_data = TRUE;
167 else if(strcmp(argv[i], "--") == 0)
168 rest_args_files = TRUE;
169 else if(strcmp(argv[i], "-arch") == 0){
170 if(i + 1 == argc){
171 error("missing argument(s) to %s option", argv[i]);
172 usage();
174 if(strcmp("all", argv[i+1]) == 0){
175 all_archs = TRUE;
177 else{
178 arch_flags = reallocate(arch_flags,
179 (narch_flags + 1) * sizeof(struct arch_flag));
180 if(get_arch_from_flag(argv[i+1],
181 arch_flags + narch_flags) == 0){
182 error("unknown architecture specification flag: "
183 "%s %s", argv[i], argv[i+1]);
184 arch_usage();
185 usage();
187 narch_flags++;
189 i++;
191 else if(strcmp(argv[i], "-n") == 0){
192 if(i + 1 == argc){
193 error("missing argument to %s option", argv[i]);
194 usage();
196 flags.minimum_length = strtoul(argv[i+1], &endp, 10);
197 if(*endp != '\0'){
198 error("invalid decimal number in option: %s %s",
199 argv[i], argv[i+1]);
200 usage();
202 i++;
204 else if(strcmp(argv[i], "-t") == 0){
205 if(i + 1 == argc){
206 error("missing argument to %s option", argv[i]);
207 usage();
209 if(argv[i+1][1] != '\0'){
210 error("invalid argument to option: %s %s",
211 argv[i], argv[i+1]);
212 usage();
214 switch(argv[i+1][0]){
215 case 'd':
216 flags.print_offsets = TRUE;
217 flags.offset_format = "%d";
218 break;
219 case 'o':
220 flags.print_offsets = TRUE;
221 flags.offset_format = "%o";
222 break;
223 case 'x':
224 flags.print_offsets = TRUE;
225 flags.offset_format = "%x";
226 break;
227 default:
228 error("invalid argument to option: %s %s",
229 argv[i], argv[i+1]);
230 usage();
232 i++;
234 else{
235 endp = NULL;
236 for(j = 1; argv[i][j] != '\0' && endp == NULL; j++){
237 switch(argv[i][j]){
238 case 'o':
239 flags.print_offsets = TRUE;
240 flags.offset_format = "%7lu";
241 break;
242 case 'a':
243 flags.all_sections = TRUE;
244 break;
245 default:
246 if(!isdigit(argv[i][j])){
247 error("unknown flag: %s", argv[i]);
248 usage();
250 flags.minimum_length = strtoul(argv[i]+j,&endp,10);
251 if(*endp != '\0'){
252 error("invalid decimal number in flag: %s",
253 argv[i]);
254 usage();
260 else{
261 nfiles++;
266 * Process the file or stdin if there are no files.
268 rest_args_files = FALSE;
269 if(nfiles != 0){
270 for(i = 1; i < argc; i++){
271 if(argv[i][0] != '-' || rest_args_files == TRUE){
272 if(flags.treat_as_data == TRUE){
273 if(freopen(argv[i], "r", stdin) == NULL)
274 system_error("can't open: %s", argv[i]);
275 rewind(stdin);
276 find(UINT_MAX, &flags);
278 else{
280 * If there's a filename that's an exact match then use
281 * that, else fall back to the member syntax.
283 if(stat(argv[i], &stat_buf) == 0)
284 use_member_syntax = FALSE;
285 else
286 use_member_syntax = TRUE;
287 ofile_process(argv[i], arch_flags, narch_flags,
288 all_archs, TRUE, TRUE, use_member_syntax,
289 ofile_processor,&flags);
292 else if(strcmp(argv[i], "-arch") == 0 ||
293 strcmp(argv[i], "-n") == 0 ||
294 strcmp(argv[i], "-t") == 0)
295 i++;
296 else if(strcmp(argv[i], "--") == 0)
297 rest_args_files = TRUE;
300 else{
301 find(UINT_MAX, &flags);
303 if(errors == 0)
304 return(EXIT_SUCCESS);
305 else
306 return(EXIT_FAILURE);
310 * usage() prints the current usage message and exits indicating failure.
312 static
313 void
314 usage(
315 void)
317 fprintf(stderr, "Usage: %s [-] [-a] [-o] [-t format] [-number] "
318 "[-n number] [[-arch <arch_flag>] ...] [--] [file ...]\n",
319 progname);
320 exit(EXIT_FAILURE);
324 * ofile_processor() is called by ofile_process() for each ofile to process.
325 * All ofiles that are object files are process by section non-object files
326 * have their logical contents processed entirely. The locical contents may
327 * be a single archive member in an archive, if the name "libx.a(x.o)" was
328 * used or a specific architecture if "-arch <arch_flag> fatfile" was used
329 * which is not the entire physical file. If the entire physical file is
330 * wanted to be searched then the "-" option is used and this routine is not
331 * used.
333 static
334 void
335 ofile_processor(
336 struct ofile *ofile,
337 char *arch_name,
338 void *cookie)
340 char *addr;
341 uint64_t offset, size;
342 uint32_t i, j;
343 uint32_t ncmds;
344 struct flags *flags;
345 struct load_command *lc;
346 struct segment_command *sg;
347 struct segment_command_64 *sg64;
348 struct section *s;
349 struct section_64 *s64;
351 flags = (struct flags *)cookie;
354 * If the ofile is not an object file then process it without reguard
355 * to sections.
357 if(ofile->object_addr == NULL || ofile->member_type == OFILE_LLVM_BITCODE){
358 if(ofile->file_type == OFILE_FAT && ofile->arch_flag.cputype != 0){
359 if(ofile->fat_header->magic == FAT_MAGIC_64){
360 addr = ofile->file_addr +
361 ofile->fat_archs64[ofile->narch].offset;
362 size = ofile->fat_archs64[ofile->narch].size;
363 offset = ofile->fat_archs64[ofile->narch].offset;
365 else{
366 addr = ofile->file_addr +
367 ofile->fat_archs[ofile->narch].offset;
368 size = ofile->fat_archs[ofile->narch].size;
369 offset = ofile->fat_archs[ofile->narch].offset;
372 else{
373 addr = ofile->file_addr;
374 size = ofile->file_size;
375 offset = 0;
377 if(ofile->member_ar_hdr != NULL) {
378 addr = addr + ofile->member_offset;
379 size = strtoul(ofile->member_ar_hdr->ar_size, NULL, 10);
380 offset = offset + ofile->member_offset;
382 if(offset >= ofile->file_size)
383 size = 0;
384 else if(offset + size > ofile->file_size)
385 size = ofile->file_size - offset;
386 ofile_find(addr, size, offset, flags);
387 return;
391 * The ofile is an object file so process with reguard to it's sections.
393 lc = ofile->load_commands;
394 if(ofile->mh != NULL)
395 ncmds = ofile->mh->ncmds;
396 else
397 ncmds = ofile->mh64->ncmds;
398 for(i = 0; i < ncmds; i++){
399 if(lc->cmd == LC_SEGMENT){
400 sg = (struct segment_command *)lc;
401 s = (struct section *)((char *)sg +
402 sizeof(struct segment_command));
403 for(j = 0; j < sg->nsects; j++){
404 if(flags->all_sections){
405 if((s->flags & S_ZEROFILL) != S_ZEROFILL &&
406 (s->flags & S_THREAD_LOCAL_ZEROFILL) !=
407 S_THREAD_LOCAL_ZEROFILL){
408 addr = ofile->object_addr + s->offset;
409 offset = s->offset;
410 size = s->size;
411 if(offset >= ofile->file_size)
412 size = 0;
413 else if(offset + size > ofile->file_size)
414 size = ofile->file_size - offset;
415 ofile_find(addr, size, offset, flags);
418 else{
419 if((s->flags & S_ZEROFILL) != S_ZEROFILL &&
420 (s->flags & S_THREAD_LOCAL_ZEROFILL) !=
421 S_THREAD_LOCAL_ZEROFILL &&
422 (strcmp(s->sectname, SECT_TEXT) != 0 ||
423 strcmp(s->segname, SEG_TEXT) != 0)){
424 addr = ofile->object_addr + s->offset;
425 offset = s->offset;
426 size = s->size;
427 if(offset >= ofile->file_size)
428 size = 0;
429 else if(offset + size > ofile->file_size)
430 size = ofile->file_size - offset;
431 ofile_find(addr, size, offset, flags);
434 s++;
437 else if(lc->cmd == LC_SEGMENT_64){
438 sg64 = (struct segment_command_64 *)lc;
439 s64 = (struct section_64 *)((char *)sg64 +
440 sizeof(struct segment_command_64));
441 for(j = 0; j < sg64->nsects; j++){
442 if(flags->all_sections){
443 if((s64->flags & S_ZEROFILL) != S_ZEROFILL &&
444 (s64->flags & S_THREAD_LOCAL_ZEROFILL) !=
445 S_THREAD_LOCAL_ZEROFILL){
446 addr = ofile->object_addr + s64->offset;
447 offset = s64->offset;
448 size = s64->size;
449 if(offset >= ofile->file_size)
450 size = 0;
451 else if(offset + size > ofile->file_size)
452 size = ofile->file_size - offset;
453 ofile_find(addr, size, offset, flags);
456 else{
457 if((s64->flags & S_ZEROFILL) != S_ZEROFILL &&
458 (s64->flags & S_THREAD_LOCAL_ZEROFILL) !=
459 S_THREAD_LOCAL_ZEROFILL &&
460 (strcmp(s64->sectname, SECT_TEXT) != 0 ||
461 strcmp(s64->segname, SEG_TEXT) != 0)){
462 addr = ofile->object_addr + s64->offset;
463 offset = s64->offset;
464 size = s64->size;
465 if(offset >= ofile->file_size)
466 size = 0;
467 else if(offset + size > ofile->file_size)
468 size = ofile->file_size - offset;
469 ofile_find(addr, size, offset, flags);
472 s64++;
475 lc = (struct load_command *)((char *)lc + lc->cmdsize);
480 * ofile_find is used by ofile_processor() to find strings in part of a ofile
481 * that is memory at addr for size. offset is the offset in the file to this
482 * data for use when printing offsets.
484 static
485 void
486 ofile_find(
487 char *addr,
488 uint32_t size,
489 uint32_t offset,
490 struct flags *flags)
492 uint32_t i, string_length;
493 char c, *string;
495 string = addr;
496 string_length = 0;
497 for(i = 0; i < size; i++){
498 c = addr[i];
499 if(c == '\n' || dirt(c) || i == size - 1){
500 if(string_length >= flags->minimum_length){
501 if(flags->print_offsets){
502 printf(flags->offset_format, offset + (string - addr));
503 printf(" ");
505 if(i == size - 1 && c != '\n')
506 printf("%.*s\n", (int)string_length + 1, string);
507 else
508 printf("%.*s\n", (int)string_length, string);
510 string = addr + i + 1;
511 string_length = 0;
513 else{
514 string_length++;
520 * find() is the original 4.3bsd code that uses the stdin stream. It searches
521 * for strings through a count of cnt bytes.
523 static
524 void
525 find(
526 uint32_t cnt,
527 struct flags *flags)
529 static char buf[BUFSIZ];
530 register char *cp;
531 register int c, cc;
533 cp = buf, cc = 0;
534 for (; cnt != 0; cnt--) {
535 c = getc(stdin);
536 if (c == '\n' || dirt(c) || cnt == 0) {
537 if (cp > buf && cp[-1] == '\n')
538 --cp;
539 *cp++ = 0;
540 if (cp > &buf[flags->minimum_length]) {
541 if (flags->print_offsets == TRUE){
542 printf(flags->offset_format,
543 ftell(stdin) - cc - 1);
544 printf(" ");
546 printf("%s\n", buf);
548 cp = buf, cc = 0;
549 } else {
550 if (cp < &buf[sizeof buf - 2])
551 *cp++ = c;
552 cc++;
554 if (ferror(stdin) || feof(stdin))
555 break;
560 * dirt() is the original 4.3bsd code that returns TRUE or FALSE if the
561 * character passed to it could be in a part of a printable string.
563 static
564 enum bool
565 dirt(
566 int c)
568 switch(c){
569 case '\n':
570 case '\f':
571 return(FALSE);
572 case 0177:
573 return(TRUE);
574 default:
575 if(c > 0200 || c < ' ')
576 return(TRUE);
577 else
578 return(FALSE);