xref: /illumos-gate/usr/src/cmd/file/file.c (revision 287247a826fa2ab8d01f6c8f276d405eb08420f8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22 /*	  All Rights Reserved  	*/
23 
24 
25 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
26 /*	  All Rights Reserved	*/
27 
28 /*
29  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
30  * Use is subject to license terms.
31  */
32 
33 #define	_LARGEFILE64_SOURCE
34 
35 /* Get definitions for the relocation types supported. */
36 #define	ELF_TARGET_ALL
37 
38 #include <ctype.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <libelf.h>
44 #include <stdlib.h>
45 #include <limits.h>
46 #include <locale.h>
47 #include <wctype.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <door.h>
51 #include <sys/param.h>
52 #include <sys/types.h>
53 #include <sys/mkdev.h>
54 #include <sys/stat.h>
55 #include <sys/elf.h>
56 #include <procfs.h>
57 #include <sys/core.h>
58 #include <sys/dumphdr.h>
59 #include <netinet/in.h>
60 #include <gelf.h>
61 #include <elfcap.h>
62 #include <sgsrtcid.h>
63 #include "file.h"
64 #include "elf_read.h"
65 
66 /*
67  *	Misc
68  */
69 
70 #define	FBSZ		512
71 #define	MLIST_SZ	12
72 
73 /*
74  * The 0x8FCA0102 magic string was used in crash dumps generated by releases
75  * prior to Solaris 7.
76  */
77 #define	OLD_DUMP_MAGIC	0x8FCA0102
78 
79 #if defined(__sparc)
80 #define	NATIVE_ISA	"SPARC"
81 #define	OTHER_ISA	"Intel"
82 #else
83 #define	NATIVE_ISA	"Intel"
84 #define	OTHER_ISA	"SPARC"
85 #endif
86 
87 /* Assembly language comment char */
88 #ifdef pdp11
89 #define	ASCOMCHAR '/'
90 #else
91 #define	ASCOMCHAR '!'
92 #endif
93 
94 #pragma	align	16(fbuf)
95 static char	fbuf[FBSZ];
96 
97 /*
98  * Magic file variables
99  */
100 static intmax_t maxmagicoffset;
101 static intmax_t tmpmax;
102 static char	*magicbuf;
103 
104 static char	*dfile;
105 static char	*troff[] = {	/* new troff intermediate lang */
106 		"x", "T", "res", "init", "font", "202", "V0", "p1", 0};
107 
108 static char	*fort[] = {			/* FORTRAN */
109 		"function", "subroutine", "common", "dimension", "block",
110 		"integer", "real", "data", "double",
111 		"FUNCTION", "SUBROUTINE", "COMMON", "DIMENSION", "BLOCK",
112 		"INTEGER", "REAL", "DATA", "DOUBLE", 0};
113 
114 static char	*asc[] = {		/* Assembler Commands */
115 		"sys", "mov", "tst", "clr", "jmp", "cmp", "set", "inc",
116 		"dec", 0};
117 
118 static char	*c[] = {			/* C Language */
119 		"int", "char", "float", "double", "short", "long", "unsigned",
120 		"register", "static", "struct", "extern", 0};
121 
122 static char	*as[] = {	/* Assembler Pseudo Ops, prepended with '.' */
123 		"globl", "global", "ident", "file", "byte", "even",
124 		"text", "data", "bss", "comm", 0};
125 
126 /*
127  * The line and debug section names are used by the strip command.
128  * Any changes in the strip implementation need to be reflected here.
129  */
130 static char	*debug_sections[] = { /* Debug sections in a ELF file */
131 		".debug", ".stab", ".dwarf", ".line", NULL};
132 
133 /* start for MB env */
134 static wchar_t	wchar;
135 static int	length;
136 static int	IS_ascii;
137 static int	Max;
138 /* end for MB env */
139 static int	i;	/* global index into first 'fbsz' bytes of file */
140 static int	fbsz;
141 static int	ifd = -1;
142 static int	elffd = -1;
143 static int	tret;
144 static int	hflg;
145 static int	dflg;
146 static int	mflg;
147 static int	M_flg;
148 static int	iflg;
149 static struct stat64	mbuf;
150 
151 static char	**mlist1;	/* 1st ordered list of magic files */
152 static char	**mlist2;	/* 2nd ordered list of magic files */
153 static size_t	mlist1_sz;	/* number of ptrs allocated for mlist1 */
154 static size_t	mlist2_sz;	/* number of ptrs allocated for mlist2 */
155 static char	**mlist1p;	/* next entry in mlist1 */
156 static char	**mlist2p;	/* next entry in mlist2 */
157 
158 static ssize_t	mread;
159 
160 static void ar_coff_or_aout(int ifd);
161 static int type(char *file);
162 static int def_position_tests(char *file);
163 static void def_context_tests(void);
164 static int troffint(char *bp, int n);
165 static int lookup(char **tab);
166 static int ccom(void);
167 static int ascom(void);
168 static int sccs(void);
169 static int english(char *bp, int n);
170 static int shellscript(char buf[], struct stat64 *sb);
171 static int elf_check(char *file);
172 static int get_door_target(char *, char *, size_t);
173 static int zipfile(char *, int);
174 static int is_crash_dump(const char *, int);
175 static void print_dumphdr(const int, const dumphdr_t *, uint32_t (*)(uint32_t),
176     const char *);
177 static uint32_t swap_uint32(uint32_t);
178 static uint32_t return_uint32(uint32_t);
179 static void usage(void);
180 static void default_magic(void);
181 static void add_to_mlist(char *, int);
182 static void fd_cleanup(void);
183 static int is_rtld_config(void);
184 
185 /* from elf_read.c */
186 int elf_read32(int elffd, Elf_Info *EInfo);
187 int elf_read64(int elffd, Elf_Info *EInfo);
188 
189 #ifdef XPG4
190 	/* SUSv3 requires a single <space> after the colon */
191 #define	prf(x)	(void) printf("%s: ", x);
192 #else	/* !XPG4 */
193 #define	prf(x)	(void) printf("%s:%s", x, (int)strlen(x) > 6 ? "\t" : "\t\t");
194 #endif	/* XPG4 */
195 
196 /*
197  * Static program identifier - used to prevent localization of the name "file"
198  * within individual error messages.
199  */
200 const char *File = "file";
201 
202 int
203 main(int argc, char **argv)
204 {
205 	char	*p;
206 	int	ch;
207 	FILE	*fl;
208 	int	bflg = 0;
209 	int	cflg = 0;
210 	int	eflg = 0;
211 	int	fflg = 0;
212 	char	*ap = NULL;
213 	int	pathlen;
214 	char	**filep;
215 
216 	(void) setlocale(LC_ALL, "");
217 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
218 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
219 #endif
220 	(void) textdomain(TEXT_DOMAIN);
221 
222 	while ((ch = getopt(argc, argv, "M:bcdf:him:")) != EOF) {
223 		switch (ch) {
224 
225 		case 'M':
226 			add_to_mlist(optarg, !dflg);
227 			M_flg++;
228 			break;
229 
230 		case 'b':
231 			bflg++;
232 			break;
233 
234 		case 'c':
235 			cflg++;
236 			break;
237 
238 		case 'd':
239 			if (!dflg) {
240 				default_magic();
241 				add_to_mlist(dfile, 0);
242 				dflg++;
243 			}
244 			break;
245 
246 		case 'f':
247 			fflg++;
248 			errno = 0;
249 			if ((fl = fopen(optarg, "r")) == NULL) {
250 				int err = errno;
251 				(void) fprintf(stderr, gettext("%s: cannot "
252 				    "open file %s: %s\n"), File, optarg,
253 				    err ? strerror(err) : "");
254 				usage();
255 			}
256 			pathlen = pathconf("/", _PC_PATH_MAX);
257 			if (pathlen == -1) {
258 				int err = errno;
259 				(void) fprintf(stderr, gettext("%s: cannot "
260 				    "determine maximum path length: %s\n"),
261 				    File, strerror(err));
262 				exit(1);
263 			}
264 			pathlen += 2; /* for null and newline in fgets */
265 			if ((ap = malloc(pathlen * sizeof (char))) == NULL) {
266 				int err = errno;
267 				(void) fprintf(stderr, gettext("%s: malloc "
268 				    "failed: %s\n"), File, strerror(err));
269 				exit(2);
270 			}
271 			break;
272 
273 		case 'h':
274 			hflg++;
275 			break;
276 
277 		case 'i':
278 			iflg++;
279 			break;
280 
281 		case 'm':
282 			add_to_mlist(optarg, !dflg);
283 			mflg++;
284 			break;
285 
286 		case '?':
287 			eflg++;
288 			break;
289 		}
290 	}
291 	if (!cflg && !fflg && (eflg || optind == argc))
292 		usage();
293 	if (iflg && (dflg || mflg || M_flg)) {
294 		usage();
295 	}
296 	if ((iflg && cflg) || (cflg && bflg)) {
297 		usage();
298 	}
299 
300 	if (!dflg && !mflg && !M_flg && !iflg) {
301 	/* no -d, -m, nor -M option; also -i option doesn't need magic  */
302 		default_magic();
303 		if (f_mkmtab(dfile, cflg, 0) == -1) {
304 			exit(2);
305 		}
306 	}
307 
308 	else if (mflg && !M_flg && !dflg) {
309 	/* -m specified without -d nor -M */
310 
311 #ifdef XPG4	/* For SUSv3 only */
312 
313 		/*
314 		 * The default position-dependent magic file tests
315 		 * in /etc/magic will follow all the -m magic tests.
316 		 */
317 
318 		for (filep = mlist1; filep < mlist1p; filep++) {
319 			if (f_mkmtab(*filep, cflg, 1) == -1) {
320 				exit(2);
321 			}
322 		}
323 		default_magic();
324 		if (f_mkmtab(dfile, cflg, 0) == -1) {
325 			exit(2);
326 		}
327 #else	/* !XPG4 */
328 		/*
329 		 * Retain Solaris file behavior for -m before SUSv3,
330 		 * when the new -d and -M options are not specified.
331 		 * Use the -m file specified in place of the default
332 		 * /etc/magic file.  Solaris file will
333 		 * now allow more than one magic file to be specified
334 		 * with multiple -m options, for consistency with
335 		 * other behavior.
336 		 *
337 		 * Put the magic table(s) specified by -m into
338 		 * the second magic table instead of the first
339 		 * (as indicated by the last argument to f_mkmtab()),
340 		 * since they replace the /etc/magic tests and
341 		 * must be executed alongside the default
342 		 * position-sensitive tests.
343 		 */
344 
345 		for (filep = mlist1; filep < mlist1p; filep++) {
346 			if (f_mkmtab(*filep, cflg, 0) == -1) {
347 				exit(2);
348 			}
349 		}
350 #endif /* XPG4 */
351 	} else {
352 		/*
353 		 * For any other combination of -d, -m, and -M,
354 		 * use the magic files in command-line order.
355 		 * Store the entries from the two separate lists of magic
356 		 * files, if any, into two separate magic file tables.
357 		 * mlist1: magic tests executed before default magic tests
358 		 * mlist2: default magic tests and after
359 		 */
360 		for (filep = mlist1; filep && (filep < mlist1p); filep++) {
361 			if (f_mkmtab(*filep, cflg, 1) == -1) {
362 				exit(2);
363 			}
364 		}
365 		for (filep = mlist2; filep && (filep < mlist2p); filep++) {
366 			if (f_mkmtab(*filep, cflg, 0) == -1) {
367 				exit(2);
368 			}
369 		}
370 	}
371 
372 	/* Initialize the magic file variables; check both magic tables */
373 	tmpmax = f_getmaxoffset(1);
374 	maxmagicoffset = f_getmaxoffset(0);
375 	if (maxmagicoffset < tmpmax) {
376 		maxmagicoffset = tmpmax;
377 	}
378 	if (maxmagicoffset < (intmax_t)FBSZ)
379 		maxmagicoffset = (intmax_t)FBSZ;
380 	if ((magicbuf = malloc(maxmagicoffset)) == NULL) {
381 		int err = errno;
382 		(void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
383 		    File, strerror(err));
384 		exit(2);
385 	}
386 
387 	if (cflg) {
388 		f_prtmtab();
389 		if (ferror(stdout) != 0) {
390 			(void) fprintf(stderr, gettext("%s: error writing to "
391 			    "stdout\n"), File);
392 			exit(1);
393 		}
394 		if (fclose(stdout) != 0) {
395 			int err = errno;
396 			(void) fprintf(stderr, gettext("%s: fclose "
397 			    "failed: %s\n"), File, strerror(err));
398 			exit(1);
399 		}
400 		exit(0);
401 	}
402 
403 	for (; fflg || optind < argc; optind += !fflg) {
404 		register int	l;
405 
406 		if (fflg) {
407 			if ((p = fgets(ap, pathlen, fl)) == NULL) {
408 				fflg = 0;
409 				optind--;
410 				continue;
411 			}
412 			l = strlen(p);
413 			if (l > 0)
414 				p[l - 1] = '\0';
415 		} else
416 			p = argv[optind];
417 
418 		if (!bflg)
419 			prf(p);		/* print "file_name:<tab>" */
420 
421 		if (type(p))
422 			tret = 1;
423 	}
424 	if (ap != NULL)
425 		free(ap);
426 	if (tret != 0)
427 		exit(tret);
428 
429 	if (ferror(stdout) != 0) {
430 		(void) fprintf(stderr, gettext("%s: error writing to "
431 		    "stdout\n"), File);
432 		exit(1);
433 	}
434 	if (fclose(stdout) != 0) {
435 		int err = errno;
436 		(void) fprintf(stderr, gettext("%s: fclose failed: %s\n"),
437 		    File, strerror(err));
438 		exit(1);
439 	}
440 	return (0);
441 }
442 
443 static int
444 type(char *file)
445 {
446 	int	cc;
447 	char	buf[BUFSIZ];
448 	int	(*statf)() = hflg ? lstat64 : stat64;
449 
450 	i = 0;		/* reset index to beginning of file */
451 	ifd = -1;
452 	if ((*statf)(file, &mbuf) < 0) {
453 		if (statf == lstat64 || lstat64(file, &mbuf) < 0) {
454 			int err = errno;
455 			(void) printf(gettext("cannot open: %s\n"),
456 			    strerror(err));
457 			return (0);		/* POSIX.2 */
458 		}
459 	}
460 	switch (mbuf.st_mode & S_IFMT) {
461 	case S_IFREG:
462 		if (iflg) {
463 			(void) printf(gettext("regular file\n"));
464 			return (0);
465 		}
466 		break;
467 	case S_IFCHR:
468 		(void) printf(gettext("character"));
469 		goto spcl;
470 
471 	case S_IFDIR:
472 		(void) printf(gettext("directory\n"));
473 		return (0);
474 
475 	case S_IFIFO:
476 		(void) printf(gettext("fifo\n"));
477 		return (0);
478 
479 	case S_IFLNK:
480 		if ((cc = readlink(file, buf, BUFSIZ)) < 0) {
481 			int err = errno;
482 			(void) printf(gettext("readlink error: %s\n"),
483 			    strerror(err));
484 			return (1);
485 		}
486 		buf[cc] = '\0';
487 		(void) printf(gettext("symbolic link to %s\n"), buf);
488 		return (0);
489 
490 	case S_IFBLK:
491 		(void) printf(gettext("block"));
492 					/* major and minor, see sys/mkdev.h */
493 spcl:
494 		(void) printf(gettext(" special (%d/%d)\n"),
495 		    major(mbuf.st_rdev), minor(mbuf.st_rdev));
496 		return (0);
497 
498 	case S_IFSOCK:
499 		(void) printf("socket\n");
500 		/* FIXME, should open and try to getsockname. */
501 		return (0);
502 
503 	case S_IFDOOR:
504 		if (get_door_target(file, buf, sizeof (buf)) == 0)
505 			(void) printf(gettext("door to %s\n"), buf);
506 		else
507 			(void) printf(gettext("door\n"));
508 		return (0);
509 
510 	}
511 
512 	if (elf_version(EV_CURRENT) == EV_NONE) {
513 		(void) printf(gettext("libelf is out of date\n"));
514 		return (1);
515 	}
516 
517 	ifd = open64(file, O_RDONLY);
518 	if (ifd < 0) {
519 		int err = errno;
520 		(void) printf(gettext("cannot open: %s\n"), strerror(err));
521 		return (0);			/* POSIX.2 */
522 	}
523 
524 	/* need another fd for elf, since we might want to read the file too */
525 	elffd = open64(file, O_RDONLY);
526 	if (elffd < 0) {
527 		int err = errno;
528 		(void) printf(gettext("cannot open: %s\n"), strerror(err));
529 		(void) close(ifd);
530 		ifd = -1;
531 		return (0);			/* POSIX.2 */
532 	}
533 	if ((fbsz = read(ifd, fbuf, FBSZ)) == -1) {
534 		int err = errno;
535 		(void) printf(gettext("cannot read: %s\n"), strerror(err));
536 		(void) close(ifd);
537 		ifd = -1;
538 		return (0);			/* POSIX.2 */
539 	}
540 	if (fbsz == 0) {
541 		(void) printf(gettext("empty file\n"));
542 		fd_cleanup();
543 		return (0);
544 	}
545 
546 	/*
547 	 * First try user-specified position-dependent magic tests, if any,
548 	 * which need to execute before the default tests.
549 	 */
550 	if ((mread = pread(ifd, (void*)magicbuf, (size_t)maxmagicoffset,
551 	    (off_t)0)) == -1) {
552 		int err = errno;
553 		(void) printf(gettext("cannot read: %s\n"), strerror(err));
554 		fd_cleanup();
555 		return (0);
556 	}
557 
558 	/*
559 	 * ChecK against Magic Table entries.
560 	 * Check first magic table for magic tests to be applied
561 	 * before default tests.
562 	 * If no default tests are to be applied, all magic tests
563 	 * should occur in this magic table.
564 	 */
565 	switch (f_ckmtab(magicbuf, mread, 1)) {
566 		case -1:	/* Error */
567 			exit(2);
568 			break;
569 		case 0:		/* Not magic */
570 			break;
571 		default:	/* Switch is magic index */
572 			(void) putchar('\n');
573 			fd_cleanup();
574 			return (0);
575 			/* NOTREACHED */
576 			break;
577 	}
578 
579 	if (dflg || !M_flg) {
580 		/*
581 		 * default position-dependent tests,
582 		 * plus non-default magic tests, if any
583 		 */
584 		switch (def_position_tests(file)) {
585 			case -1:	/* error */
586 				fd_cleanup();
587 				return (1);
588 			case 1:	/* matching type found */
589 				fd_cleanup();
590 				return (0);
591 				/* NOTREACHED */
592 				break;
593 			case 0:		/* no matching type found */
594 				break;
595 		}
596 		/* default context-sensitive tests */
597 		def_context_tests();
598 	} else {
599 		/* no more tests to apply; no match was found */
600 		(void) printf(gettext("data\n"));
601 	}
602 	fd_cleanup();
603 	return (0);
604 }
605 
606 /*
607  * def_position_tests() - applies default position-sensitive tests,
608  *	looking for values in specific positions in the file.
609  *	These are followed by default (followed by possibly some
610  *	non-default) magic file tests.
611  *
612  *	All position-sensitive tests, default or otherwise, must
613  *	be applied before context-sensitive tests, to avoid
614  *	false context-sensitive matches.
615  *
616  * 	Returns -1 on error which should result in error (non-zero)
617  *	exit status for the file utility.
618  *	Returns 0 if no matching file type found.
619  *	Returns 1 if matching file type found.
620  */
621 
622 static int
623 def_position_tests(char *file)
624 {
625 	if (sccs()) {	/* look for "1hddddd" where d is a digit */
626 		(void) printf("sccs \n");
627 		return (1);
628 	}
629 	if (fbuf[0] == '#' && fbuf[1] == '!' && shellscript(fbuf+2, &mbuf))
630 		return (1);
631 
632 	if (elf_check(file) == 0) {
633 		(void) putchar('\n');
634 		return (1);
635 	/* LINTED: pointer cast may result in improper alignment */
636 	} else if (*(int *)fbuf == CORE_MAGIC) {
637 		/* LINTED: pointer cast may result in improper alignment */
638 		struct core *corep = (struct core *)fbuf;
639 
640 		(void) printf("a.out core file");
641 
642 		if (*(corep->c_cmdname) != '\0')
643 			(void) printf(" from '%s'", corep->c_cmdname);
644 		(void) putchar('\n');
645 		return (1);
646 	}
647 
648 	/*
649 	 * Runtime linker (ld.so.1) configuration file.
650 	 */
651 	if (is_rtld_config())
652 		return (1);
653 
654 	/*
655 	 * ZIP files, JAR files, and Java executables
656 	 */
657 	if (zipfile(fbuf, ifd))
658 		return (1);
659 
660 	if (is_crash_dump(fbuf, ifd))
661 		return (1);
662 
663 	/*
664 	 * ChecK against Magic Table entries.
665 	 * The magic entries checked here always start with default
666 	 * magic tests and may be followed by other, non-default magic
667 	 * tests.  If no default tests are to be executed, all the
668 	 * magic tests should have been in the first magic table.
669 	 */
670 	switch (f_ckmtab(magicbuf, mread, 0)) {
671 		case -1:	/* Error */
672 			exit(2);
673 			break;
674 		case 0:		/* Not magic */
675 			return (0);
676 			/* NOTREACHED */
677 			break;
678 		default:	/* Switch is magic index */
679 
680 			/*
681 			 * f_ckmtab recognizes file type,
682 			 * check if it is PostScript.
683 			 * if not, check if elf or a.out
684 			 */
685 			if (magicbuf[0] == '%' && magicbuf[1] == '!') {
686 				(void) putchar('\n');
687 			} else {
688 
689 				/*
690 				 * Check that the file is executable (dynamic
691 				 * objects must be executable to be exec'ed,
692 				 * shared objects need not be, but by convention
693 				 * should be executable).
694 				 *
695 				 * Note that we should already have processed
696 				 * the file if it was an ELF file.
697 				 */
698 				ar_coff_or_aout(elffd);
699 				(void) putchar('\n');
700 			}
701 			return (1);
702 			/* NOTREACHED */
703 			break;
704 	}
705 
706 	return (0);	/* file was not identified */
707 }
708 
709 /*
710  * def_context_tests() - default context-sensitive tests.
711  *	These are the last tests to be applied.
712  *	If no match is found, prints out "data".
713  */
714 
715 static void
716 def_context_tests(void)
717 {
718 	int	j;
719 	int	nl;
720 	char	ch;
721 	int	len;
722 
723 	if (ccom() == 0)
724 		goto notc;
725 	while (fbuf[i] == '#') {
726 		j = i;
727 		while (fbuf[i++] != '\n') {
728 			if (i - j > 255) {
729 				(void) printf(gettext("data\n"));
730 				return;
731 			}
732 			if (i >= fbsz)
733 				goto notc;
734 		}
735 		if (ccom() == 0)
736 			goto notc;
737 	}
738 check:
739 	if (lookup(c) == 1) {
740 		while ((ch = fbuf[i]) != ';' && ch != '{') {
741 			if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
742 				len = 1;
743 			i += len;
744 			if (i >= fbsz)
745 				goto notc;
746 		}
747 		(void) printf(gettext("c program text"));
748 		goto outa;
749 	}
750 	nl = 0;
751 	while (fbuf[i] != '(') {
752 		if (fbuf[i] <= 0)
753 			goto notas;
754 		if (fbuf[i] == ';') {
755 			i++;
756 			goto check;
757 		}
758 		if (fbuf[i++] == '\n')
759 			if (nl++ > 6)
760 				goto notc;
761 		if (i >= fbsz)
762 			goto notc;
763 	}
764 	while (fbuf[i] != ')') {
765 		if (fbuf[i++] == '\n')
766 			if (nl++ > 6)
767 				goto notc;
768 		if (i >= fbsz)
769 			goto notc;
770 	}
771 	while (fbuf[i] != '{') {
772 		if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
773 			len = 1;
774 		if (fbuf[i] == '\n')
775 			if (nl++ > 6)
776 				goto notc;
777 		i += len;
778 		if (i >= fbsz)
779 			goto notc;
780 	}
781 	(void) printf(gettext("c program text"));
782 	goto outa;
783 notc:
784 	i = 0;			/* reset to begining of file again */
785 	while (fbuf[i] == 'c' || fbuf[i] == 'C'|| fbuf[i] == '!' ||
786 	    fbuf[i] == '*' || fbuf[i] == '\n') {
787 		while (fbuf[i++] != '\n')
788 			if (i >= fbsz)
789 				goto notfort;
790 	}
791 	if (lookup(fort) == 1) {
792 		(void) printf(gettext("fortran program text"));
793 		goto outa;
794 	}
795 notfort:			/* looking for assembler program */
796 	i = 0;			/* reset to beginning of file again */
797 	if (ccom() == 0)	/* assembler programs may contain */
798 				/* c-style comments */
799 		goto notas;
800 	if (ascom() == 0)
801 		goto notas;
802 	j = i - 1;
803 	if (fbuf[i] == '.') {
804 		i++;
805 		if (lookup(as) == 1) {
806 			(void) printf(gettext("assembler program text"));
807 			goto outa;
808 		} else if (j != -1 && fbuf[j] == '\n' && isalpha(fbuf[j + 2])) {
809 			(void) printf(
810 			    gettext("[nt]roff, tbl, or eqn input text"));
811 			goto outa;
812 		}
813 	}
814 	while (lookup(asc) == 0) {
815 		if (ccom() == 0)
816 			goto notas;
817 		if (ascom() == 0)
818 			goto notas;
819 		while (fbuf[i] != '\n' && fbuf[i++] != ':') {
820 			if (i >= fbsz)
821 				goto notas;
822 		}
823 		while (fbuf[i] == '\n' || fbuf[i] == ' ' || fbuf[i] == '\t')
824 			if (i++ >= fbsz)
825 				goto notas;
826 		j = i - 1;
827 		if (fbuf[i] == '.') {
828 			i++;
829 			if (lookup(as) == 1) {
830 				(void) printf(
831 				    gettext("assembler program text"));
832 				goto outa;
833 			} else if (fbuf[j] == '\n' && isalpha(fbuf[j+2])) {
834 				(void) printf(
835 				    gettext("[nt]roff, tbl, or eqn input "
836 				    "text"));
837 				goto outa;
838 			}
839 		}
840 	}
841 	(void) printf(gettext("assembler program text"));
842 	goto outa;
843 notas:
844 	/* start modification for multibyte env */
845 	IS_ascii = 1;
846 	if (fbsz < FBSZ)
847 		Max = fbsz;
848 	else
849 		Max = FBSZ - MB_LEN_MAX; /* prevent cut of wchar read */
850 	/* end modification for multibyte env */
851 
852 	for (i = 0; i < Max; /* null */)
853 		if (fbuf[i] & 0200) {
854 			IS_ascii = 0;
855 			if (fbuf[0] == '\100' && fbuf[1] == '\357') {
856 				(void) printf(gettext("troff output\n"));
857 				return;
858 			}
859 		/* start modification for multibyte env */
860 			if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
861 			    <= 0 || !iswprint(wchar)) {
862 				(void) printf(gettext("data\n"));
863 				return;
864 			}
865 			i += length;
866 		}
867 		else
868 			i++;
869 	i = fbsz;
870 		/* end modification for multibyte env */
871 	if (mbuf.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH))
872 		(void) printf(gettext("commands text"));
873 	else if (troffint(fbuf, fbsz))
874 		(void) printf(gettext("troff intermediate output text"));
875 	else if (english(fbuf, fbsz))
876 		(void) printf(gettext("English text"));
877 	else if (IS_ascii)
878 		(void) printf(gettext("ascii text"));
879 	else
880 		(void) printf(gettext("text")); /* for multibyte env */
881 outa:
882 	/*
883 	 * This code is to make sure that no MB char is cut in half
884 	 * while still being used.
885 	 */
886 	fbsz = (fbsz < FBSZ ? fbsz : fbsz - MB_CUR_MAX + 1);
887 	while (i < fbsz) {
888 		if (isascii(fbuf[i])) {
889 			i++;
890 			continue;
891 		} else {
892 			if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
893 			    <= 0 || !iswprint(wchar)) {
894 				(void) printf(gettext(" with garbage\n"));
895 				return;
896 			}
897 			i = i + length;
898 		}
899 	}
900 	(void) printf("\n");
901 }
902 
903 static int
904 troffint(char *bp, int n)
905 {
906 	int k;
907 
908 	i = 0;
909 	for (k = 0; k < 6; k++) {
910 		if (lookup(troff) == 0)
911 			return (0);
912 		if (lookup(troff) == 0)
913 			return (0);
914 		while (i < n && bp[i] != '\n')
915 			i++;
916 		if (i++ >= n)
917 			return (0);
918 	}
919 	return (1);
920 }
921 
922 static void
923 ar_coff_or_aout(int elffd)
924 {
925 	Elf *elf;
926 
927 	/*
928 	 * Get the files elf descriptor and process it as an elf or
929 	 * a.out (4.x) file.
930 	 */
931 
932 	elf = elf_begin(elffd, ELF_C_READ, (Elf *)0);
933 	switch (elf_kind(elf)) {
934 		case ELF_K_AR :
935 			(void) printf(gettext(", not a dynamic executable "
936 			    "or shared object"));
937 			break;
938 		case ELF_K_COFF:
939 			(void) printf(gettext(", unsupported or unknown "
940 			    "file type"));
941 			break;
942 		default:
943 			/*
944 			 * This is either an unknown file or an aout format
945 			 * At this time, we don't print dynamic/stripped
946 			 * info. on a.out or non-Elf binaries.
947 			 */
948 			break;
949 	}
950 	(void) elf_end(elf);
951 }
952 
953 
954 static void
955 print_elf_type(Elf_Info EI)
956 {
957 	switch (EI.type) {
958 	case ET_NONE:
959 		(void) printf(" %s", gettext("unknown type"));
960 		break;
961 	case ET_REL:
962 		(void) printf(" %s", gettext("relocatable"));
963 		break;
964 	case ET_EXEC:
965 		(void) printf(" %s", gettext("executable"));
966 		break;
967 	case ET_DYN:
968 		(void) printf(" %s", gettext("dynamic lib"));
969 		break;
970 	default:
971 		break;
972 	}
973 }
974 
975 static void
976 print_elf_machine(int machine)
977 {
978 	/*
979 	 * This table must be kept in sync with the EM_ constants
980 	 * in /usr/include/sys/elf.h.
981 	 */
982 	static const char *mach_str[EM_NUM] = {
983 		"unknown machine",		/* 0 - EM_NONE */
984 		"WE32100",			/* 1 - EM_M32 */
985 		"SPARC",			/* 2 - EM_SPARC */
986 		"80386",			/* 3 - EM_386 */
987 		"M68000",			/* 4 - EM_68K */
988 		"M88000",			/* 5 - EM_88K */
989 		"80486",			/* 6 - EM_486 */
990 		"i860",				/* 7 - EM_860 */
991 		"MIPS RS3000 Big-Endian",	/* 8 - EM_MIPS */
992 		"S/370",			/* 9 - EM_S370 */
993 		"MIPS RS3000 Little-Endian",	/* 10 - EM_MIPS_RS3_LE */
994 		"MIPS RS6000",			/* 11 - EM_RS6000 */
995 		NULL,				/* 12 - EM_UNKNOWN12 */
996 		NULL,				/* 13 - EM_UNKNOWN13 */
997 		NULL,				/* 14 - EM_UNKNOWN14 */
998 		"PA-RISC",			/* 15 - EM_PA_RISC */
999 		"nCUBE",			/* 16 - EM_nCUBE */
1000 		"VPP500",			/* 17 - EM_VPP500 */
1001 		"SPARC32PLUS",			/* 18 - EM_SPARC32PLUS */
1002 		"i960",				/* 19 - EM_960 */
1003 		"PowerPC",			/* 20 - EM_PPC */
1004 		"PowerPC64",			/* 21 - EM_PPC64 */
1005 		"S/390",			/* 22 - EM_S390 */
1006 		NULL,				/* 23 - EM_UNKNOWN23 */
1007 		NULL,				/* 24 - EM_UNKNOWN24 */
1008 		NULL,				/* 25 - EM_UNKNOWN25 */
1009 		NULL,				/* 26 - EM_UNKNOWN26 */
1010 		NULL,				/* 27 - EM_UNKNOWN27 */
1011 		NULL,				/* 28 - EM_UNKNOWN28 */
1012 		NULL,				/* 29 - EM_UNKNOWN29 */
1013 		NULL,				/* 30 - EM_UNKNOWN30 */
1014 		NULL,				/* 31 - EM_UNKNOWN31 */
1015 		NULL,				/* 32 - EM_UNKNOWN32 */
1016 		NULL,				/* 33 - EM_UNKNOWN33 */
1017 		NULL,				/* 34 - EM_UNKNOWN34 */
1018 		NULL,				/* 35 - EM_UNKNOWN35 */
1019 		"V800",				/* 36 - EM_V800 */
1020 		"FR20",				/* 37 - EM_FR20 */
1021 		"RH32",				/* 38 - EM_RH32 */
1022 		"RCE",				/* 39 - EM_RCE */
1023 		"ARM",				/* 40 - EM_ARM */
1024 		"Alpha",			/* 41 - EM_ALPHA */
1025 		"S/390",			/* 42 - EM_SH */
1026 		"SPARCV9",			/* 43 - EM_SPARCV9 */
1027 		"Tricore",			/* 44 - EM_TRICORE */
1028 		"ARC",				/* 45 - EM_ARC */
1029 		"H8/300",			/* 46 - EM_H8_300 */
1030 		"H8/300H",			/* 47 - EM_H8_300H */
1031 		"H8S",				/* 48 - EM_H8S */
1032 		"H8/500",			/* 49 - EM_H8_500 */
1033 		"IA64",				/* 50 - EM_IA_64 */
1034 		"MIPS-X",			/* 51 - EM_MIPS_X */
1035 		"Coldfire",			/* 52 - EM_COLDFIRE */
1036 		"M68HC12",			/* 53 - EM_68HC12 */
1037 		"MMA",				/* 54 - EM_MMA */
1038 		"PCP",				/* 55 - EM_PCP */
1039 		"nCPU",				/* 56 - EM_NCPU */
1040 		"NDR1",				/* 57 - EM_NDR1 */
1041 		"Starcore",			/* 58 - EM_STARCORE */
1042 		"ME16",				/* 59 - EM_ME16 */
1043 		"ST100",			/* 60 - EM_ST100 */
1044 		"TINYJ",			/* 61 - EM_TINYJ */
1045 		"AMD64",			/* 62 - EM_AMD64 */
1046 		"PDSP",				/* 63 - EM_PDSP */
1047 		NULL,				/* 64 - EM_UNKNOWN64 */
1048 		NULL,				/* 65 - EM_UNKNOWN65 */
1049 		"FX66",				/* 66 - EM_FX66 */
1050 		"ST9 PLUS",			/* 67 - EM_ST9PLUS */
1051 		"ST7",				/* 68 - EM_ST7 */
1052 		"68HC16",			/* 69 - EM_68HC16 */
1053 		"68HC11",			/* 70 - EM_68HC11 */
1054 		"68H08",			/* 71 - EM_68HC08 */
1055 		"68HC05",			/* 72 - EM_68HC05 */
1056 		"SVX",				/* 73 - EM_SVX */
1057 		"ST19",				/* 74 - EM_ST19 */
1058 		"VAX",				/* 75 - EM_VAX */
1059 		"CRIS",				/* 76 - EM_CRIS */
1060 		"Javelin",			/* 77 - EM_JAVELIN */
1061 		"Firepath",			/* 78 - EM_FIREPATH */
1062 		"ZSP",				/* 79 - EM_ZSP */
1063 		"MMIX",				/* 80 - EM_MMIX */
1064 		"HUANY",			/* 81 - EM_HUANY */
1065 		"Prism",			/* 82 - EM_PRISM */
1066 		"AVR",				/* 83 - EM_AVR */
1067 		"FR30",				/* 84 - EM_FR30 */
1068 		"D10V",				/* 85 - EM_D10V */
1069 		"D30V",				/* 86 - EM_D30V */
1070 		"V850",				/* 87 - EM_V850 */
1071 		"M32R",				/* 88 - EM_M32R */
1072 		"MN10300",			/* 89 - EM_MN10300 */
1073 		"MN10200",			/* 90 - EM_MN10200 */
1074 		"picoJava",			/* 91 - EM_PJ */
1075 		"OpenRISC",			/* 92 - EM_OPENRISC */
1076 		"Tangent-A5",			/* 93 - EM_ARC_A5 */
1077 		"Xtensa"			/* 94 - EM_XTENSA */
1078 	};
1079 	/* If new machine is added, refuse to compile until we're updated */
1080 #if EM_NUM != 95
1081 #error "Number of known ELF machine constants has changed"
1082 #endif
1083 
1084 	const char *str;
1085 
1086 	if ((machine < EM_NONE) || (machine >= EM_NUM))
1087 		machine = EM_NONE;
1088 
1089 	str = mach_str[machine];
1090 	if (str)
1091 		(void) printf(" %s", str);
1092 }
1093 
1094 static void
1095 print_elf_datatype(int datatype)
1096 {
1097 	switch (datatype) {
1098 	case ELFDATA2LSB:
1099 		(void) printf(" LSB");
1100 		break;
1101 	case ELFDATA2MSB:
1102 		(void) printf(" MSB");
1103 		break;
1104 	default:
1105 		break;
1106 	}
1107 }
1108 
1109 static void
1110 print_elf_class(int class)
1111 {
1112 	switch (class) {
1113 	case ELFCLASS32:
1114 		(void) printf(" %s", gettext("32-bit"));
1115 		break;
1116 	case ELFCLASS64:
1117 		(void) printf(" %s", gettext("64-bit"));
1118 		break;
1119 	default:
1120 		break;
1121 	}
1122 }
1123 
1124 static void
1125 print_elf_flags(Elf_Info EI)
1126 {
1127 	unsigned int flags;
1128 
1129 	flags = EI.flags;
1130 	switch (EI.machine) {
1131 	case EM_SPARCV9:
1132 		if (flags & EF_SPARC_EXT_MASK) {
1133 			if (flags & EF_SPARC_SUN_US3) {
1134 				(void) printf("%s", gettext(
1135 				    ", UltraSPARC3 Extensions Required"));
1136 			} else if (flags & EF_SPARC_SUN_US1) {
1137 				(void) printf("%s", gettext(
1138 				    ", UltraSPARC1 Extensions Required"));
1139 			}
1140 			if (flags & EF_SPARC_HAL_R1)
1141 				(void) printf("%s", gettext(
1142 				    ", HaL R1 Extensions Required"));
1143 		}
1144 		break;
1145 	case EM_SPARC32PLUS:
1146 		if (flags & EF_SPARC_32PLUS)
1147 			(void) printf("%s", gettext(", V8+ Required"));
1148 		if (flags & EF_SPARC_SUN_US3) {
1149 			(void) printf("%s",
1150 			    gettext(", UltraSPARC3 Extensions Required"));
1151 		} else if (flags & EF_SPARC_SUN_US1) {
1152 			(void) printf("%s",
1153 			    gettext(", UltraSPARC1 Extensions Required"));
1154 		}
1155 		if (flags & EF_SPARC_HAL_R1)
1156 			(void) printf("%s",
1157 			    gettext(", HaL R1 Extensions Required"));
1158 		break;
1159 	default:
1160 		break;
1161 	}
1162 }
1163 
1164 /*
1165  * check_ident:	checks the ident field of the presumeably
1166  *		elf file. If check fails, this is not an
1167  *		elf file.
1168  */
1169 static int
1170 check_ident(unsigned char *ident, int fd)
1171 {
1172 	int class;
1173 	if (pread64(fd, ident, EI_NIDENT, 0) != EI_NIDENT)
1174 		return (ELF_READ_FAIL);
1175 	class = ident[EI_CLASS];
1176 	if (class != ELFCLASS32 && class != ELFCLASS64)
1177 		return (ELF_READ_FAIL);
1178 	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
1179 	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3)
1180 		return (ELF_READ_FAIL);
1181 
1182 	return (ELF_READ_OKAY);
1183 }
1184 
1185 static int
1186 elf_check(char *file)
1187 {
1188 	Elf_Info EInfo;
1189 	int class, version, format;
1190 	unsigned char ident[EI_NIDENT];
1191 
1192 	(void) memset(&EInfo, 0, sizeof (Elf_Info));
1193 	EInfo.file = file;
1194 
1195 	/*
1196 	 * Verify information in file indentifier.
1197 	 * Return quietly if not elf; Different type of file.
1198 	 */
1199 	if (check_ident(ident, elffd) == ELF_READ_FAIL)
1200 		return (1);
1201 
1202 	/*
1203 	 * Read the elf headers for processing and get the
1204 	 * get the needed information in Elf_Info struct.
1205 	 */
1206 	class = ident[EI_CLASS];
1207 	if (class == ELFCLASS32) {
1208 		if (elf_read32(elffd, &EInfo) == ELF_READ_FAIL) {
1209 			(void) fprintf(stderr, gettext("%s: %s: can't "
1210 			    "read ELF header\n"), File, file);
1211 			return (1);
1212 		}
1213 	} else if (class == ELFCLASS64) {
1214 		if (elf_read64(elffd, &EInfo) == ELF_READ_FAIL) {
1215 			(void) fprintf(stderr, gettext("%s: %s: can't "
1216 			    "read ELF header\n"), File, file);
1217 			return (1);
1218 		}
1219 	} else {
1220 		/* something wrong */
1221 		return (1);
1222 	}
1223 
1224 	/* version not in ident then 1 */
1225 	version = ident[EI_VERSION] ? ident[EI_VERSION] : 1;
1226 
1227 	format = ident[EI_DATA];
1228 	(void) printf("%s", gettext("ELF"));
1229 	print_elf_class(class);
1230 	print_elf_datatype(format);
1231 	print_elf_type(EInfo);
1232 
1233 	if (EInfo.core_type != EC_NOTCORE) {
1234 		/* Print what kind of core is this */
1235 		if (EInfo.core_type == EC_OLDCORE)
1236 			(void) printf(" %s", gettext("pre-2.6 core file"));
1237 		else
1238 			(void) printf(" %s", gettext("core file"));
1239 	}
1240 
1241 	/* Print machine info */
1242 	print_elf_machine(EInfo.machine);
1243 
1244 	/* Print Version */
1245 	if (version == 1)
1246 		(void) printf(" %s %d", gettext("Version"), version);
1247 
1248 	/* Print Flags */
1249 	print_elf_flags(EInfo);
1250 
1251 	/* Last bit, if it is a core */
1252 	if (EInfo.core_type != EC_NOTCORE) {
1253 		/* Print the program name that dumped this core */
1254 		(void) printf(gettext(", from '%s'"), EInfo.fname);
1255 		return (0);
1256 	}
1257 
1258 	/* Print Capabilities */
1259 	if (EInfo.cap_str[0] != '\0')
1260 		(void) printf(" [%s]", EInfo.cap_str);
1261 
1262 	if ((EInfo.type != ET_EXEC) && (EInfo.type != ET_DYN))
1263 		return (0);
1264 
1265 	/* Print if it is dynamically linked */
1266 	if (EInfo.dynamic)
1267 		(void) printf(gettext(", dynamically linked"));
1268 	else
1269 		(void) printf(gettext(", statically linked"));
1270 
1271 	/* Printf it it is stripped */
1272 	if (EInfo.stripped & E_SYMTAB) {
1273 		(void) printf(gettext(", not stripped"));
1274 		if (!(EInfo.stripped & E_DBGINF)) {
1275 			(void) printf(gettext(
1276 			    ", no debugging information available"));
1277 		}
1278 	} else {
1279 		(void) printf(gettext(", stripped"));
1280 	}
1281 
1282 	return (0);
1283 }
1284 
1285 /*
1286  * is_rtld_config - If file is a runtime linker config file, prints
1287  * the description and returns True (1). Otherwise, silently returns
1288  * False (0).
1289  */
1290 int
1291 is_rtld_config(void)
1292 {
1293 	Rtc_id *id;
1294 
1295 	if ((fbsz >= sizeof (*id)) && RTC_ID_TEST(fbuf)) {
1296 		(void) printf(gettext("Runtime Linking Configuration"));
1297 		id = (Rtc_id *) fbuf;
1298 		print_elf_class(id->id_class);
1299 		print_elf_datatype(id->id_data);
1300 		print_elf_machine(id->id_machine);
1301 		(void) printf("\n");
1302 		return (1);
1303 	}
1304 
1305 	return (0);
1306 }
1307 
1308 /*
1309  * lookup -
1310  * Attempts to match one of the strings from a list, 'tab',
1311  * with what is in the file, starting at the current index position 'i'.
1312  * Looks past any initial whitespace and expects whitespace or other
1313  * delimiting characters to follow the matched string.
1314  * A match identifies the file as being 'assembler', 'fortran', 'c', etc.
1315  * Returns 1 for a successful match, 0 otherwise.
1316  */
1317 static int
1318 lookup(char **tab)
1319 {
1320 	register char	r;
1321 	register int	k, j, l;
1322 
1323 	while (fbuf[i] == ' ' || fbuf[i] == '\t' || fbuf[i] == '\n')
1324 		i++;
1325 	for (j = 0; tab[j] != 0; j++) {
1326 		l = 0;
1327 		for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++)
1328 			;
1329 		if (r == '\0')
1330 			if (fbuf[k] == ' ' || fbuf[k] == '\n' ||
1331 			    fbuf[k] == '\t' || fbuf[k] == '{' ||
1332 			    fbuf[k] == '/') {
1333 				i = k;
1334 				return (1);
1335 			}
1336 	}
1337 	return (0);
1338 }
1339 
1340 /*
1341  * ccom -
1342  * Increments the current index 'i' into the file buffer 'fbuf' past any
1343  * whitespace lines and C-style comments found, starting at the current
1344  * position of 'i'.  Returns 1 as long as we don't increment i past the
1345  * size of fbuf (fbsz).  Otherwise, returns 0.
1346  */
1347 
1348 static int
1349 ccom(void)
1350 {
1351 	register char	cc;
1352 	int		len;
1353 
1354 	while ((cc = fbuf[i]) == ' ' || cc == '\t' || cc == '\n')
1355 		if (i++ >= fbsz)
1356 			return (0);
1357 	if (fbuf[i] == '/' && fbuf[i+1] == '*') {
1358 		i += 2;
1359 		while (fbuf[i] != '*' || fbuf[i+1] != '/') {
1360 			if (fbuf[i] == '\\')
1361 				i++;
1362 			if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
1363 				len = 1;
1364 			i += len;
1365 			if (i >= fbsz)
1366 				return (0);
1367 		}
1368 		if ((i += 2) >= fbsz)
1369 			return (0);
1370 	}
1371 	if (fbuf[i] == '\n')
1372 		if (ccom() == 0)
1373 			return (0);
1374 	return (1);
1375 }
1376 
1377 /*
1378  * ascom -
1379  * Increments the current index 'i' into the file buffer 'fbuf' past
1380  * consecutive assembler program comment lines starting with ASCOMCHAR,
1381  * starting at the current position of 'i'.
1382  * Returns 1 as long as we don't increment i past the
1383  * size of fbuf (fbsz).  Otherwise returns 0.
1384  */
1385 
1386 static int
1387 ascom(void)
1388 {
1389 	while (fbuf[i] == ASCOMCHAR) {
1390 		i++;
1391 		while (fbuf[i++] != '\n')
1392 			if (i >= fbsz)
1393 				return (0);
1394 		while (fbuf[i] == '\n')
1395 			if (i++ >= fbsz)
1396 				return (0);
1397 	}
1398 	return (1);
1399 }
1400 
1401 static int
1402 sccs(void)
1403 {				/* look for "1hddddd" where d is a digit */
1404 	register int j;
1405 
1406 	if (fbuf[0] == 1 && fbuf[1] == 'h') {
1407 		for (j = 2; j <= 6; j++) {
1408 			if (isdigit(fbuf[j]))
1409 				continue;
1410 			else
1411 				return (0);
1412 		}
1413 	} else {
1414 		return (0);
1415 	}
1416 	return (1);
1417 }
1418 
1419 static int
1420 english(char *bp, int n)
1421 {
1422 #define	NASC 128		/* number of ascii char ?? */
1423 	register int	j, vow, freq, rare, len;
1424 	register int	badpun = 0, punct = 0;
1425 	int	ct[NASC];
1426 
1427 	if (n < 50)
1428 		return (0); /* no point in statistics on squibs */
1429 	for (j = 0; j < NASC; j++)
1430 		ct[j] = 0;
1431 	for (j = 0; j < n; j += len) {
1432 		if ((unsigned char)bp[j] < NASC)
1433 			ct[bp[j]|040]++;
1434 		switch (bp[j]) {
1435 		case '.':
1436 		case ',':
1437 		case ')':
1438 		case '%':
1439 		case ';':
1440 		case ':':
1441 		case '?':
1442 			punct++;
1443 			if (j < n-1 && bp[j+1] != ' ' && bp[j+1] != '\n')
1444 				badpun++;
1445 		}
1446 		if ((len = mblen(&bp[j], MB_CUR_MAX)) <= 0)
1447 			len = 1;
1448 	}
1449 	if (badpun*5 > punct)
1450 		return (0);
1451 	vow = ct['a'] + ct['e'] + ct['i'] + ct['o'] + ct['u'];
1452 	freq = ct['e'] + ct['t'] + ct['a'] + ct['i'] + ct['o'] + ct['n'];
1453 	rare = ct['v'] + ct['j'] + ct['k'] + ct['q'] + ct['x'] + ct['z'];
1454 	if (2*ct[';'] > ct['e'])
1455 		return (0);
1456 	if ((ct['>'] + ct['<'] + ct['/']) > ct['e'])
1457 		return (0);	/* shell file test */
1458 	return (vow * 5 >= n - ct[' '] && freq >= 10 * rare);
1459 }
1460 
1461 
1462 static int
1463 shellscript(char buf[], struct stat64 *sb)
1464 {
1465 	char *tp, *cp, *xp, *up, *gp;
1466 
1467 	cp = strchr(buf, '\n');
1468 	if (cp == NULL || cp - fbuf > fbsz)
1469 		return (0);
1470 	for (tp = buf; tp != cp && isspace((unsigned char)*tp); tp++)
1471 		if (!isascii(*tp))
1472 			return (0);
1473 	for (xp = tp; tp != cp && !isspace((unsigned char)*tp); tp++)
1474 		if (!isascii(*tp))
1475 			return (0);
1476 	if (tp == xp)
1477 		return (0);
1478 	if (sb->st_mode & S_ISUID)
1479 		up = gettext("set-uid ");
1480 	else
1481 		up = "";
1482 
1483 	if (sb->st_mode & S_ISGID)
1484 		gp = gettext("set-gid ");
1485 	else
1486 		gp = "";
1487 
1488 	if (strncmp(xp, "/bin/sh", tp - xp) == 0)
1489 		xp = gettext("shell");
1490 	else if (strncmp(xp, "/bin/csh", tp - xp) == 0)
1491 		xp = gettext("c-shell");
1492 	else if (strncmp(xp, "/usr/sbin/dtrace", tp - xp) == 0)
1493 		xp = gettext("DTrace");
1494 	else
1495 		*tp = '\0';
1496 	/*
1497 	 * TRANSLATION_NOTE
1498 	 * This message is printed by file command for shell scripts.
1499 	 * The first %s is for the translation for "set-uid " (if the script
1500 	 *   has the set-uid bit set), or is for an empty string (if the
1501 	 *   script does not have the set-uid bit set).
1502 	 * Similarly, the second %s is for the translation for "set-gid ",
1503 	 *   or is for an empty string.
1504 	 * The third %s is for the translation for either: "shell", "c-shell",
1505 	 *   or "DTrace", or is for the pathname of the program the script
1506 	 *   executes.
1507 	 */
1508 	(void) printf(gettext("%s%sexecutable %s script\n"), up, gp, xp);
1509 	return (1);
1510 }
1511 
1512 static int
1513 get_door_target(char *file, char *buf, size_t bufsize)
1514 {
1515 	int fd;
1516 	door_info_t di;
1517 	psinfo_t psinfo;
1518 
1519 	if ((fd = open64(file, O_RDONLY)) < 0 ||
1520 	    door_info(fd, &di) != 0) {
1521 		if (fd >= 0)
1522 			(void) close(fd);
1523 		return (-1);
1524 	}
1525 	(void) close(fd);
1526 
1527 	(void) sprintf(buf, "/proc/%ld/psinfo", di.di_target);
1528 	if ((fd = open64(buf, O_RDONLY)) < 0 ||
1529 	    read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
1530 		if (fd >= 0)
1531 			(void) close(fd);
1532 		return (-1);
1533 	}
1534 	(void) close(fd);
1535 
1536 	(void) snprintf(buf, bufsize, "%s[%ld]", psinfo.pr_fname, di.di_target);
1537 	return (0);
1538 }
1539 
1540 /*
1541  * ZIP file header information
1542  */
1543 #define	SIGSIZ		4
1544 #define	LOCSIG		"PK\003\004"
1545 #define	LOCHDRSIZ	30
1546 
1547 #define	CH(b, n)	(((unsigned char *)(b))[n])
1548 #define	SH(b, n)	(CH(b, n) | (CH(b, n+1) << 8))
1549 #define	LG(b, n)	(SH(b, n) | (SH(b, n+2) << 16))
1550 
1551 #define	LOCNAM(b)	(SH(b, 26))	/* filename size */
1552 #define	LOCEXT(b)	(SH(b, 28))	/* extra field size */
1553 
1554 #define	XFHSIZ		4		/* header id, data size */
1555 #define	XFHID(b)	(SH(b, 0))	/* extract field header id */
1556 #define	XFDATASIZ(b)	(SH(b, 2))	/* extract field data size */
1557 #define	XFJAVASIG	0xcafe		/* java executables */
1558 
1559 static int
1560 zipfile(char *fbuf, int fd)
1561 {
1562 	off_t xoff, xoff_end;
1563 
1564 	if (strncmp(fbuf, LOCSIG, SIGSIZ) != 0)
1565 		return (0);
1566 
1567 	xoff = LOCHDRSIZ + LOCNAM(fbuf);
1568 	xoff_end = xoff + LOCEXT(fbuf);
1569 
1570 	while (xoff < xoff_end) {
1571 		char xfhdr[XFHSIZ];
1572 
1573 		if (pread(fd, xfhdr, XFHSIZ, xoff) != XFHSIZ)
1574 			break;
1575 
1576 		if (XFHID(xfhdr) == XFJAVASIG) {
1577 			(void) printf("%s\n", gettext("java archive file"));
1578 			return (1);
1579 		}
1580 		xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr);
1581 	}
1582 
1583 	/*
1584 	 * We could just print "ZIP archive" here.
1585 	 *
1586 	 * However, customers may be using their own entries in
1587 	 * /etc/magic to distinguish one kind of ZIP file from another, so
1588 	 * let's defer the printing of "ZIP archive" to there.
1589 	 */
1590 	return (0);
1591 }
1592 
1593 static int
1594 is_crash_dump(const char *buf, int fd)
1595 {
1596 	/* LINTED: pointer cast may result in improper alignment */
1597 	const dumphdr_t *dhp = (const dumphdr_t *)buf;
1598 
1599 	/*
1600 	 * The current DUMP_MAGIC string covers Solaris 7 and later releases.
1601 	 * The utsname struct is only present in dumphdr_t's with dump_version
1602 	 * greater than or equal to 9.
1603 	 */
1604 	if (dhp->dump_magic == DUMP_MAGIC) {
1605 		print_dumphdr(fd, dhp, return_uint32, NATIVE_ISA);
1606 
1607 	} else if (dhp->dump_magic == swap_uint32(DUMP_MAGIC)) {
1608 		print_dumphdr(fd, dhp, swap_uint32, OTHER_ISA);
1609 
1610 	} else if (dhp->dump_magic == OLD_DUMP_MAGIC ||
1611 	    dhp->dump_magic == swap_uint32(OLD_DUMP_MAGIC)) {
1612 		char *isa = (dhp->dump_magic == OLD_DUMP_MAGIC ?
1613 		    NATIVE_ISA : OTHER_ISA);
1614 		(void) printf(gettext("SunOS 32-bit %s crash dump\n"), isa);
1615 
1616 	} else {
1617 		return (0);
1618 	}
1619 
1620 	return (1);
1621 }
1622 
1623 static void
1624 print_dumphdr(const int fd, const dumphdr_t *dhp, uint32_t (*swap)(uint32_t),
1625     const char *isa)
1626 {
1627 	dumphdr_t dh;
1628 
1629 	/*
1630 	 * A dumphdr_t is bigger than FBSZ, so we have to manually read the
1631 	 * rest of it.
1632 	 */
1633 	if (swap(dhp->dump_version) > 8 && pread(fd, &dh, sizeof (dumphdr_t),
1634 	    (off_t)0) == sizeof (dumphdr_t)) {
1635 		const char *c = swap(dh.dump_flags) & DF_COMPRESSED ?
1636 		    "compressed " : "";
1637 		const char *l = swap(dh.dump_flags) & DF_LIVE ?
1638 		    "live" : "crash";
1639 
1640 		(void) printf(gettext(
1641 		    "%s %s %s %u-bit %s %s%s dump from '%s'\n"),
1642 		    dh.dump_utsname.sysname, dh.dump_utsname.release,
1643 		    dh.dump_utsname.version, swap(dh.dump_wordsize), isa,
1644 		    c, l, dh.dump_utsname.nodename);
1645 	} else {
1646 		(void) printf(gettext("SunOS %u-bit %s crash dump\n"),
1647 		    swap(dhp->dump_wordsize), isa);
1648 	}
1649 }
1650 
1651 static void
1652 usage(void)
1653 {
1654 	(void) fprintf(stderr, gettext(
1655 	    "usage: file [-bdh] [-M mfile] [-m mfile] [-f ffile] file ...\n"
1656 	    "       file [-bdh] [-M mfile] [-m mfile] -f ffile\n"
1657 	    "       file -i [-bh] [-f ffile] file ...\n"
1658 	    "       file -i [-bh] -f ffile\n"
1659 	    "       file -c [-d] [-M mfile] [-m mfile]\n"));
1660 	exit(2);
1661 }
1662 
1663 static uint32_t
1664 swap_uint32(uint32_t in)
1665 {
1666 	uint32_t out;
1667 
1668 	out = (in & 0x000000ff) << 24;
1669 	out |= (in & 0x0000ff00) << 8; /* >> 8 << 16 */
1670 	out |= (in & 0x00ff0000) >> 8; /* >> 16 << 8 */
1671 	out |= (in & 0xff000000) >> 24;
1672 
1673 	return (out);
1674 }
1675 
1676 static uint32_t
1677 return_uint32(uint32_t in)
1678 {
1679 	return (in);
1680 }
1681 
1682 /*
1683  * Check if str is in the string list str_list.
1684  */
1685 int
1686 is_in_list(char *str)
1687 {
1688 	int i;
1689 
1690 	/*
1691 	 * Only need to compare the strlen(str_list[i]) bytes.
1692 	 * That way .stab will match on .stab* sections, and
1693 	 * .debug will match on .debug* sections.
1694 	 */
1695 	for (i = 0; debug_sections[i] != NULL; i++) {
1696 		if (strncmp(debug_sections[i], str,
1697 		    strlen(debug_sections[i])) == 0) {
1698 			return (1);
1699 		}
1700 	}
1701 	return (0);
1702 }
1703 
1704 /*
1705  * default_magic -
1706  *	allocate space for and create the default magic file
1707  *	name string.
1708  */
1709 
1710 static void
1711 default_magic(void)
1712 {
1713 	const char *msg_locale = setlocale(LC_MESSAGES, NULL);
1714 	struct stat	statbuf;
1715 
1716 	if ((dfile = malloc(strlen(msg_locale) + 35)) == NULL) {
1717 		int err = errno;
1718 		(void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
1719 		    File, strerror(err));
1720 		exit(2);
1721 	}
1722 	(void) snprintf(dfile, strlen(msg_locale) + 35,
1723 	    "/usr/lib/locale/%s/LC_MESSAGES/magic", msg_locale);
1724 	if (stat(dfile, &statbuf) != 0) {
1725 		(void) strcpy(dfile, "/etc/magic");
1726 	}
1727 }
1728 
1729 /*
1730  * add_to_mlist -
1731  *	Add the given magic_file filename string to the list of magic
1732  *	files (mlist).  This list of files will later be examined, and
1733  *	each magic file's entries will be added in order to
1734  *	the mtab table.
1735  *
1736  *	The first flag is set to 1 to add to the first list, mlist1.
1737  *	The first flag is set to 0 to add to the second list, mlist2.
1738  */
1739 
1740 static void
1741 add_to_mlist(char *magic_file, int first)
1742 {
1743 	char	**mlist;	/* ordered list of magic files */
1744 	size_t	mlist_sz;	/* number of pointers allocated  for mlist */
1745 	char	**mlistp;	/* next entry in mlist */
1746 	size_t mlistp_off;
1747 
1748 	if (first) {
1749 		mlist = mlist1;
1750 		mlist_sz = mlist1_sz;
1751 		mlistp = mlist1p;
1752 	} else {
1753 		mlist = mlist2;
1754 		mlist_sz = mlist2_sz;
1755 		mlistp = mlist2p;
1756 	}
1757 
1758 	if (mlist == NULL) {	/* initial mlist allocation */
1759 		if ((mlist = calloc(MLIST_SZ, sizeof (char *))) == NULL) {
1760 			int err = errno;
1761 			(void) fprintf(stderr, gettext("%s: malloc "
1762 			    "failed: %s\n"), File, strerror(err));
1763 			exit(2);
1764 		}
1765 		mlist_sz = MLIST_SZ;
1766 		mlistp = mlist;
1767 	}
1768 	if ((mlistp - mlist) >= mlist_sz) {
1769 		mlistp_off = mlistp - mlist;
1770 		mlist_sz *= 2;
1771 		if ((mlist = realloc(mlist,
1772 		    mlist_sz * sizeof (char *))) == NULL) {
1773 			int err = errno;
1774 			(void) fprintf(stderr, gettext("%s: malloc "
1775 			    "failed: %s\n"), File, strerror(err));
1776 			exit(2);
1777 		}
1778 		mlistp = mlist + mlistp_off;
1779 	}
1780 	/*
1781 	 * now allocate memory for and copy the
1782 	 * magic file name string
1783 	 */
1784 	if ((*mlistp = malloc(strlen(magic_file) + 1)) == NULL) {
1785 		int err = errno;
1786 		(void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
1787 		    File, strerror(err));
1788 		exit(2);
1789 	}
1790 	(void) strlcpy(*mlistp, magic_file, strlen(magic_file) + 1);
1791 	mlistp++;
1792 
1793 	if (first) {
1794 		mlist1 = mlist;
1795 		mlist1_sz = mlist_sz;
1796 		mlist1p = mlistp;
1797 	} else {
1798 		mlist2 = mlist;
1799 		mlist2_sz = mlist_sz;
1800 		mlist2p = mlistp;
1801 	}
1802 }
1803 
1804 static void
1805 fd_cleanup(void)
1806 {
1807 	if (ifd != -1) {
1808 		(void) close(ifd);
1809 		ifd = -1;
1810 	}
1811 	if (elffd != -1) {
1812 		(void) close(elffd);
1813 		elffd = -1;
1814 	}
1815 }
1816