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