summaryrefslogtreecommitdiff
path: root/usr/src/cmd/file/file.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/file/file.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/file/file.c')
-rw-r--r--usr/src/cmd/file/file.c1966
1 files changed, 1966 insertions, 0 deletions
diff --git a/usr/src/cmd/file/file.c b/usr/src/cmd/file/file.c
new file mode 100644
index 0000000000..8c48682c3d
--- /dev/null
+++ b/usr/src/cmd/file/file.c
@@ -0,0 +1,1966 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/* Copyright (c) 1987, 1988 Microsoft Corporation */
+/* All Rights Reserved */
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define _LARGEFILE64_SOURCE
+
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <libelf.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <locale.h>
+#include <wctype.h>
+#include <string.h>
+#include <errno.h>
+#include <door.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <sys/elf.h>
+#include <sys/elf_M32.h>
+#include <sys/elf_SPARC.h>
+#include <procfs.h>
+#include <sys/core.h>
+#include <sys/dumphdr.h>
+#include <netinet/in.h>
+#include <gelf.h>
+#include <elfcap.h>
+#include "file.h"
+
+typedef Elf64_Nhdr GElf_Nhdr;
+
+/*
+ * Misc
+ */
+
+#define FBSZ 512
+#define MLIST_SZ 12
+
+/*
+ * The 0x8FCA0102 magic string was used in crash dumps generated by releases
+ * prior to Solaris 7.
+ */
+#define OLD_DUMP_MAGIC 0x8FCA0102
+
+#if defined(__sparc)
+#define NATIVE_ISA "SPARC"
+#define OTHER_ISA "Intel"
+#else
+#define NATIVE_ISA "Intel"
+#define OTHER_ISA "SPARC"
+#endif
+
+/* Assembly language comment char */
+#ifdef pdp11
+#define ASCOMCHAR '/'
+#else
+#define ASCOMCHAR '!'
+#endif
+
+#pragma align 16(fbuf)
+static char fbuf[FBSZ];
+
+/*
+ * Magic file variables
+ */
+static intmax_t maxmagicoffset;
+static intmax_t tmpmax;
+static char *magicbuf;
+
+static char *dfile;
+static char *troff[] = { /* new troff intermediate lang */
+ "x", "T", "res", "init", "font", "202", "V0", "p1", 0};
+
+static char *fort[] = { /* FORTRAN */
+ "function", "subroutine", "common", "dimension", "block",
+ "integer", "real", "data", "double",
+ "FUNCTION", "SUBROUTINE", "COMMON", "DIMENSION", "BLOCK",
+ "INTEGER", "REAL", "DATA", "DOUBLE", 0};
+
+static char *asc[] = { /* Assembler Commands */
+ "sys", "mov", "tst", "clr", "jmp", "cmp", "set", "inc",
+ "dec", 0};
+
+static char *c[] = { /* C Language */
+ "int", "char", "float", "double", "short", "long", "unsigned",
+ "register", "static", "struct", "extern", 0};
+
+static char *as[] = { /* Assembler Pseudo Ops, prepended with '.' */
+ "globl", "global", "ident", "file", "byte", "even",
+ "text", "data", "bss", "comm", 0};
+
+/*
+ * The line and debug section names are used by the strip command.
+ * Any changes in the strip implementation need to be reflected here.
+ */
+static char *debug_sections[] = { /* Debug sections in a ELF file */
+ ".debug", ".stab", ".dwarf", ".line", NULL};
+
+
+/* start for MB env */
+static wchar_t wchar;
+static int length;
+static int IS_ascii;
+static int Max;
+/* end for MB env */
+static int i; /* global index into first 'fbsz' bytes of file */
+static int fbsz;
+static int ifd = -1;
+static int elffd = -1;
+static int tret;
+static int hflg;
+static int dflg;
+static int mflg;
+static int M_flg;
+static int iflg;
+static struct stat64 mbuf;
+
+static char **mlist1; /* 1st ordered list of magic files */
+static char **mlist2; /* 2nd ordered list of magic files */
+static size_t mlist1_sz; /* number of ptrs allocated for mlist1 */
+static size_t mlist2_sz; /* number of ptrs allocated for mlist2 */
+static char **mlist1p; /* next entry in mlist1 */
+static char **mlist2p; /* next entry in mlist2 */
+
+static ssize_t mread;
+
+static void is_stripped(Elf *elf);
+static Elf *is_elf_file(int elffd);
+static void ar_coff_or_aout(int ifd);
+static int type(char *file);
+static int def_position_tests(void);
+static void def_context_tests(void);
+static int troffint(char *bp, int n);
+static int lookup(char **tab);
+static int ccom(void);
+static int ascom(void);
+static int sccs(void);
+static int english(char *bp, int n);
+static int old_core(Elf *elf, GElf_Ehdr *ehdr, int format);
+static int core(Elf *elf, GElf_Ehdr *ehdr, int format);
+static int shellscript(char buf[], struct stat64 *sb);
+static int elf_check(Elf *elf);
+static int get_door_target(char *, char *, size_t);
+static int zipfile(char *, int);
+static int is_crash_dump(const char *, int);
+static void print_dumphdr(const int, const dumphdr_t *, uint32_t (*)(uint32_t),
+ const char *);
+static uint32_t swap_uint32(uint32_t);
+static uint32_t return_uint32(uint32_t);
+static int is_in_list(char *[], char *);
+static void usage(void);
+static void default_magic(void);
+static void add_to_mlist(char *, int);
+static void fd_cleanup(void);
+
+#ifdef XPG4
+ /* SUSv3 requires a single <space> after the colon */
+#define prf(x) (void) printf("%s: ", x);
+#else /* !XPG4 */
+#define prf(x) (void) printf("%s:%s", x, (int)strlen(x) > 6 ? "\t" : "\t\t");
+#endif /* XPG4 */
+
+int
+main(int argc, char **argv)
+{
+ char *p;
+ int ch;
+ FILE *fl;
+ int cflg = 0;
+ int eflg = 0;
+ int fflg = 0;
+ char *ap = NULL;
+ int pathlen;
+ char **filep;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((ch = getopt(argc, argv, "M:cdf:him:")) != EOF) {
+ switch (ch) {
+
+ case 'M':
+ add_to_mlist(optarg, !dflg);
+ M_flg++;
+ break;
+
+ case 'c':
+ cflg++;
+ break;
+
+ case 'd':
+ if (!dflg) {
+ default_magic();
+ add_to_mlist(dfile, 0);
+ dflg++;
+ }
+ break;
+
+ case 'f':
+ fflg++;
+ if ((fl = fopen(optarg, "r")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("cannot open %s\n"), optarg);
+ usage();
+ }
+ pathlen = pathconf("/", _PC_PATH_MAX);
+ if (pathlen == -1) {
+ (void) fprintf(stderr,
+ gettext("pathconf: cannot determine "
+ "maximum path length\n"));
+ exit(1);
+ }
+ pathlen += 2; /* for null and newline in fgets */
+ ap = malloc(pathlen * sizeof (char));
+ if (ap == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+ break;
+
+ case 'h':
+ hflg++;
+ break;
+
+ case 'i':
+ iflg++;
+ break;
+
+ case 'm':
+ add_to_mlist(optarg, !dflg);
+ mflg++;
+ break;
+
+ case '?':
+ eflg++;
+ break;
+ }
+ }
+ if (!cflg && !fflg && (eflg || optind == argc))
+ usage();
+ if (iflg && (dflg || mflg || M_flg)) {
+ usage();
+ }
+ if (iflg && cflg) {
+ usage();
+ }
+
+ if (!dflg && !mflg && !M_flg && !iflg) {
+ /* no -d, -m, nor -M option; also -i option doesn't need magic */
+ default_magic();
+ if (f_mkmtab(dfile, cflg, 0) == -1) {
+ exit(2);
+ }
+ }
+
+ else if (mflg && !M_flg && !dflg) {
+ /* -m specified without -d nor -M */
+
+#ifdef XPG4 /* For SUSv3 only */
+
+ /*
+ * The default position-dependent magic file tests
+ * in /etc/magic will follow all the -m magic tests.
+ */
+
+ for (filep = mlist1; filep < mlist1p; filep++) {
+ if (f_mkmtab(*filep, cflg, 1) == -1) {
+ exit(2);
+ }
+ }
+ default_magic();
+ if (f_mkmtab(dfile, cflg, 0) == -1) {
+ exit(2);
+ }
+#else /* !XPG4 */
+ /*
+ * Retain Solaris file behavior for -m before SUSv3,
+ * when the new -d and -M options are not specified.
+ * Use the -m file specified in place of the default
+ * /etc/magic file. Solaris file will
+ * now allow more than one magic file to be specified
+ * with multiple -m options, for consistency with
+ * other behavior.
+ *
+ * Put the magic table(s) specified by -m into
+ * the second magic table instead of the first
+ * (as indicated by the last argument to f_mkmtab()),
+ * since they replace the /etc/magic tests and
+ * must be executed alongside the default
+ * position-sensitive tests.
+ */
+
+ for (filep = mlist1; filep < mlist1p; filep++) {
+ if (f_mkmtab(*filep, cflg, 0) == -1) {
+ exit(2);
+ }
+ }
+#endif /* XPG4 */
+ } else {
+ /*
+ * For any other combination of -d, -m, and -M,
+ * use the magic files in command-line order.
+ * Store the entries from the two separate lists of magic
+ * files, if any, into two separate magic file tables.
+ * mlist1: magic tests executed before default magic tests
+ * mlist2: default magic tests and after
+ */
+ for (filep = mlist1; filep && (filep < mlist1p); filep++) {
+ if (f_mkmtab(*filep, cflg, 1) == -1) {
+ exit(2);
+ }
+ }
+ for (filep = mlist2; filep && (filep < mlist2p); filep++) {
+ if (f_mkmtab(*filep, cflg, 0) == -1) {
+ exit(2);
+ }
+ }
+ }
+
+ /* Initialize the magic file variables; check both magic tables */
+ tmpmax = f_getmaxoffset(1);
+ maxmagicoffset = f_getmaxoffset(0);
+ if (maxmagicoffset < tmpmax) {
+ maxmagicoffset = tmpmax;
+ }
+ if (maxmagicoffset < (intmax_t)FBSZ)
+ maxmagicoffset = (intmax_t)FBSZ;
+ if ((magicbuf = (char *)malloc(maxmagicoffset)) == NULL) {
+ (void) fprintf(stderr, gettext("malloc failed\n"));
+ exit(2);
+ }
+
+ if (cflg) {
+ f_prtmtab();
+ if (ferror(stdout) != 0) {
+ (void) fprintf(stderr, gettext("file: error writing to "
+ "stdout\n"));
+ exit(1);
+ }
+ if (fclose(stdout) != 0) {
+ perror(gettext("file: fclose failed"));
+ exit(1);
+ }
+ exit(0);
+ }
+ for (; fflg || optind < argc; optind += !fflg) {
+ register int l;
+
+ if (fflg) {
+ if ((p = fgets(ap, pathlen, fl)) == NULL) {
+ fflg = 0;
+ optind--;
+ continue;
+ }
+ l = strlen(p);
+ if (l > 0)
+ p[l - 1] = '\0';
+ } else
+ p = argv[optind];
+ prf(p); /* print "file_name:<tab>" */
+
+ if (type(p))
+ tret = 1;
+ }
+ if (ap != NULL)
+ free(ap);
+ if (tret != 0) {
+ exit(tret);
+ }
+ if (ferror(stdout) != 0) {
+ (void) fprintf(stderr, gettext("file: error writing to "
+ "stdout\n"));
+ exit(1);
+ }
+ if (fclose(stdout) != 0) {
+ perror(gettext("file: fclose failed"));
+ exit(1);
+ }
+ return (0);
+}
+
+static int
+type(char *file)
+{
+ int cc;
+ char buf[BUFSIZ];
+ int (*statf)() = hflg ? lstat64 : stat64;
+
+ i = 0; /* reset index to beginning of file */
+ ifd = -1;
+ if ((*statf)(file, &mbuf) < 0) {
+ if (statf == lstat64 || lstat64(file, &mbuf) < 0) {
+ (void) printf(gettext("cannot open: %s\n"),
+ strerror(errno));
+ return (0); /* POSIX.2 */
+ }
+ }
+ switch (mbuf.st_mode & S_IFMT) {
+ case S_IFREG:
+ if (iflg) {
+ (void) printf(gettext("regular file\n"));
+ return (0);
+ }
+ break;
+ case S_IFCHR:
+ (void) printf(gettext("character"));
+ goto spcl;
+
+ case S_IFDIR:
+ (void) printf(gettext("directory\n"));
+ return (0);
+
+ case S_IFIFO:
+ (void) printf(gettext("fifo\n"));
+ return (0);
+
+ case S_IFLNK:
+ if ((cc = readlink(file, buf, BUFSIZ)) < 0) {
+ (void) printf(gettext("readlink error: %s\n"),
+ strerror(errno));
+ return (1);
+ }
+ buf[cc] = '\0';
+ (void) printf(gettext("symbolic link to %s\n"), buf);
+ return (0);
+
+ case S_IFBLK:
+ (void) printf(gettext("block"));
+ /* major and minor, see sys/mkdev.h */
+spcl:
+ (void) printf(gettext(" special (%d/%d)\n"),
+ major(mbuf.st_rdev), minor(mbuf.st_rdev));
+ return (0);
+
+ case S_IFSOCK:
+ (void) printf("socket\n");
+ /* FIXME, should open and try to getsockname. */
+ return (0);
+
+ case S_IFDOOR:
+ if (get_door_target(file, buf, sizeof (buf)) == 0)
+ (void) printf(gettext("door to %s\n"), buf);
+ else
+ (void) printf(gettext("door\n"));
+ return (0);
+
+ }
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ (void) printf(gettext("libelf is out of date\n"));
+ return (1);
+ }
+
+ ifd = open64(file, O_RDONLY);
+ if (ifd < 0) {
+ (void) printf(gettext("cannot open: %s\n"), strerror(errno));
+ return (0); /* POSIX.2 */
+ }
+
+ /* need another fd for elf, since we might want to read the file too */
+ elffd = open64(file, O_RDONLY);
+ if (elffd < 0) {
+ (void) printf(gettext("cannot open: %s\n"), strerror(errno));
+ (void) close(ifd);
+ ifd = -1;
+ return (0); /* POSIX.2 */
+ }
+ if ((fbsz = read(ifd, fbuf, FBSZ)) == -1) {
+ (void) printf(gettext("cannot read: %s\n"), strerror(errno));
+ (void) close(ifd);
+ ifd = -1;
+ return (0); /* POSIX.2 */
+ }
+ if (fbsz == 0) {
+ (void) printf(gettext("empty file\n"));
+ fd_cleanup();
+ return (0);
+ }
+
+ /*
+ * First try user-specified position-dependent magic tests, if any,
+ * which need to execute before the default tests.
+ */
+ if ((mread = pread(ifd, (void*)magicbuf, (size_t)maxmagicoffset,
+ (off_t)0)) == -1) {
+ (void) printf(gettext("cannot read: %s\n"), strerror(errno));
+ fd_cleanup();
+ return (0);
+ }
+
+ /*
+ * ChecK against Magic Table entries.
+ * Check first magic table for magic tests to be applied
+ * before default tests.
+ * If no default tests are to be applied, all magic tests
+ * should occur in this magic table.
+ */
+ switch (f_ckmtab(magicbuf, mread, 1)) {
+ case -1: /* Error */
+ exit(2);
+ break;
+ case 0: /* Not magic */
+ break;
+ default: /* Switch is magic index */
+ (void) putchar('\n');
+ fd_cleanup();
+ return (0);
+ /* NOTREACHED */
+ break;
+ }
+
+ if (dflg || !M_flg) {
+ /*
+ * default position-dependent tests,
+ * plus non-default magic tests, if any
+ */
+ switch (def_position_tests()) {
+ case -1: /* error */
+ fd_cleanup();
+ return (1);
+ case 1: /* matching type found */
+ fd_cleanup();
+ return (0);
+ /* NOTREACHED */
+ break;
+ case 0: /* no matching type found */
+ break;
+ }
+ /* default context-sensitive tests */
+ def_context_tests();
+ } else {
+ /* no more tests to apply; no match was found */
+ (void) printf(gettext("data\n"));
+ }
+ fd_cleanup();
+ return (0);
+}
+
+/*
+ * def_position_tests() - applies default position-sensitive tests,
+ * looking for values in specific positions in the file.
+ * These are followed by default (followed by possibly some
+ * non-default) magic file tests.
+ *
+ * All position-sensitive tests, default or otherwise, must
+ * be applied before context-sensitive tests, to avoid
+ * false context-sensitive matches.
+ *
+ * Returns -1 on error which should result in error (non-zero)
+ * exit status for the file utility.
+ * Returns 0 if no matching file type found.
+ * Returns 1 if matching file type found.
+ */
+
+static int
+def_position_tests(void)
+{
+ Elf *elf;
+
+ if (sccs()) { /* look for "1hddddd" where d is a digit */
+ (void) printf("sccs \n");
+ return (1);
+ }
+ if (fbuf[0] == '#' && fbuf[1] == '!' && shellscript(fbuf+2, &mbuf))
+ return (1);
+ if ((elf = is_elf_file(elffd)) != NULL) {
+ (void) elf_check(elf);
+ (void) elf_end(elf);
+ (void) putchar('\n');
+ return (1);
+
+ /* LINTED: pointer cast may result in improper alignment */
+ } else if (*(int *)fbuf == CORE_MAGIC) {
+ /* LINTED: pointer cast may result in improper alignment */
+ struct core *corep = (struct core *)fbuf;
+
+ (void) printf("a.out core file");
+
+ if (*(corep->c_cmdname) != '\0')
+ (void) printf(" from '%s'", corep->c_cmdname);
+ (void) putchar('\n');
+ return (1);
+ }
+
+ /*
+ * ZIP files, JAR files, and Java executables
+ */
+ if (zipfile(fbuf, ifd))
+ return (1);
+
+ if (is_crash_dump(fbuf, ifd))
+ return (1);
+
+ /*
+ * ChecK against Magic Table entries.
+ * The magic entries checked here always start with default
+ * magic tests and may be followed by other, non-default magic
+ * tests. If no default tests are to be executed, all the
+ * magic tests should have been in the first magic table.
+ */
+ switch (f_ckmtab(magicbuf, mread, 0)) {
+ case -1: /* Error */
+ exit(2);
+ break;
+ case 0: /* Not magic */
+ return (0);
+ /* NOTREACHED */
+ break;
+ default: /* Switch is magic index */
+
+ /*
+ * f_ckmtab recognizes file type,
+ * check if it is PostScript.
+ * if not, check if elf or a.out
+ */
+ if (magicbuf[0] == '%' && magicbuf[1] == '!') {
+ (void) putchar('\n');
+ } else {
+
+ /*
+ * Check that the file is executable (dynamic
+ * objects must be executable to be exec'ed,
+ * shared objects need not be, but by convention
+ * should be executable).
+ *
+ * Note that we should already have processed
+ * the file if it was an ELF file.
+ */
+ ar_coff_or_aout(elffd);
+ (void) putchar('\n');
+ }
+ return (1);
+ /* NOTREACHED */
+ break;
+ }
+
+ return (0); /* file was not identified */
+}
+
+/*
+ * def_context_tests() - default context-sensitive tests.
+ * These are the last tests to be applied.
+ * If no match is found, prints out "data".
+ */
+
+static void
+def_context_tests(void)
+{
+ int j;
+ int nl;
+ char ch;
+ int len;
+
+ if (ccom() == 0)
+ goto notc;
+ while (fbuf[i] == '#') {
+ j = i;
+ while (fbuf[i++] != '\n') {
+ if (i - j > 255) {
+ (void) printf(gettext("data\n"));
+ return;
+ }
+ if (i >= fbsz)
+ goto notc;
+ }
+ if (ccom() == 0)
+ goto notc;
+ }
+check:
+ if (lookup(c) == 1) {
+ while ((ch = fbuf[i]) != ';' && ch != '{') {
+ if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
+ len = 1;
+ i += len;
+ if (i >= fbsz)
+ goto notc;
+ }
+ (void) printf(gettext("c program text"));
+ goto outa;
+ }
+ nl = 0;
+ while (fbuf[i] != '(') {
+ if (fbuf[i] <= 0)
+ goto notas;
+ if (fbuf[i] == ';') {
+ i++;
+ goto check;
+ }
+ if (fbuf[i++] == '\n')
+ if (nl++ > 6)
+ goto notc;
+ if (i >= fbsz)
+ goto notc;
+ }
+ while (fbuf[i] != ')') {
+ if (fbuf[i++] == '\n')
+ if (nl++ > 6)
+ goto notc;
+ if (i >= fbsz)
+ goto notc;
+ }
+ while (fbuf[i] != '{') {
+ if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
+ len = 1;
+ if (fbuf[i] == '\n')
+ if (nl++ > 6)
+ goto notc;
+ i += len;
+ if (i >= fbsz)
+ goto notc;
+ }
+ (void) printf(gettext("c program text"));
+ goto outa;
+notc:
+ i = 0; /* reset to begining of file again */
+ while (fbuf[i] == 'c' || fbuf[i] == 'C'|| fbuf[i] == '!' ||
+ fbuf[i] == '*' || fbuf[i] == '\n') {
+ while (fbuf[i++] != '\n')
+ if (i >= fbsz)
+ goto notfort;
+ }
+ if (lookup(fort) == 1) {
+ (void) printf(gettext("fortran program text"));
+ goto outa;
+ }
+notfort: /* looking for assembler program */
+ i = 0; /* reset to beginning of file again */
+ if (ccom() == 0) /* assembler programs may contain */
+ /* c-style comments */
+ goto notas;
+ if (ascom() == 0)
+ goto notas;
+ j = i - 1;
+ if (fbuf[i] == '.') {
+ i++;
+ if (lookup(as) == 1) {
+ (void) printf(gettext("assembler program text"));
+ goto outa;
+ } else if (j != -1 && fbuf[j] == '\n' && isalpha(fbuf[j + 2])) {
+ (void) printf(
+ gettext("[nt]roff, tbl, or eqn input text"));
+ goto outa;
+ }
+ }
+ while (lookup(asc) == 0) {
+ if (ccom() == 0)
+ goto notas;
+ if (ascom() == 0)
+ goto notas;
+ while (fbuf[i] != '\n' && fbuf[i++] != ':') {
+ if (i >= fbsz)
+ goto notas;
+ }
+ while (fbuf[i] == '\n' || fbuf[i] == ' ' || fbuf[i] == '\t')
+ if (i++ >= fbsz)
+ goto notas;
+ j = i - 1;
+ if (fbuf[i] == '.') {
+ i++;
+ if (lookup(as) == 1) {
+ (void) printf(
+ gettext("assembler program text"));
+ goto outa;
+ } else if (fbuf[j] == '\n' && isalpha(fbuf[j+2])) {
+ (void) printf(
+ gettext("[nt]roff, tbl, or eqn input "
+ "text"));
+ goto outa;
+ }
+ }
+ }
+ (void) printf(gettext("assembler program text"));
+ goto outa;
+notas:
+ /* start modification for multibyte env */
+ IS_ascii = 1;
+ if (fbsz < FBSZ)
+ Max = fbsz;
+ else
+ Max = FBSZ - MB_LEN_MAX; /* prevent cut of wchar read */
+ /* end modification for multibyte env */
+
+ for (i = 0; i < Max; /* null */)
+ if (fbuf[i] & 0200) {
+ IS_ascii = 0;
+ if (fbuf[0] == '\100' && fbuf[1] == '\357') {
+ (void) printf(gettext("troff output\n"));
+ return;
+ }
+ /* start modification for multibyte env */
+ if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
+ <= 0 || !iswprint(wchar)) {
+ (void) printf(gettext("data\n"));
+ return;
+ }
+ i += length;
+ }
+ else
+ i++;
+ i = fbsz;
+ /* end modification for multibyte env */
+ if (mbuf.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH))
+ (void) printf(gettext("commands text"));
+ else if (troffint(fbuf, fbsz))
+ (void) printf(gettext("troff intermediate output text"));
+ else if (english(fbuf, fbsz))
+ (void) printf(gettext("English text"));
+ else if (IS_ascii)
+ (void) printf(gettext("ascii text"));
+ else
+ (void) printf(gettext("text")); /* for multibyte env */
+outa:
+ /*
+ * This code is to make sure that no MB char is cut in half
+ * while still being used.
+ */
+ fbsz = (fbsz < FBSZ ? fbsz : fbsz - MB_CUR_MAX + 1);
+ while (i < fbsz) {
+ if (isascii(fbuf[i])) {
+ i++;
+ continue;
+ } else {
+ if ((length = mbtowc(&wchar, &fbuf[i], MB_CUR_MAX))
+ <= 0 || !iswprint(wchar)) {
+ (void) printf(gettext(" with garbage\n"));
+ return;
+ }
+ i = i + length;
+ }
+ }
+ (void) printf("\n");
+}
+
+static int
+troffint(char *bp, int n)
+{
+ int k;
+
+ i = 0;
+ for (k = 0; k < 6; k++) {
+ if (lookup(troff) == 0)
+ return (0);
+ if (lookup(troff) == 0)
+ return (0);
+ while (i < n && bp[i] != '\n')
+ i++;
+ if (i++ >= n)
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Determine if the passed descriptor describes an ELF file.
+ * If so, return the Elf handle.
+ */
+static Elf *
+is_elf_file(int elffd)
+{
+ Elf *elf;
+
+ elf = elf_begin(elffd, ELF_C_READ, (Elf *)0);
+ switch (elf_kind(elf)) {
+ case ELF_K_ELF:
+ break;
+ default:
+ (void) elf_end(elf);
+ elf = NULL;
+ break;
+ }
+ return (elf);
+}
+
+static void
+ar_coff_or_aout(int elffd)
+{
+ Elf *elf;
+
+ /*
+ * Get the files elf descriptor and process it as an elf or
+ * a.out (4.x) file.
+ */
+
+ elf = elf_begin(elffd, ELF_C_READ, (Elf *)0);
+ switch (elf_kind(elf)) {
+ case ELF_K_AR :
+ (void) printf(gettext(", not a dynamic executable "
+ "or shared object"));
+ break;
+ case ELF_K_COFF:
+ (void) printf(gettext(", unsupported or unknown "
+ "file type"));
+ break;
+ default:
+ /*
+ * This is either an unknown file or an aout format
+ * At this time, we don't print dynamic/stripped
+ * info. on a.out or non-Elf binaries.
+ */
+ break;
+ }
+ (void) elf_end(elf);
+}
+
+
+static void
+print_elf_type(Elf *elf, GElf_Ehdr *ehdr, int format)
+{
+ switch (ehdr->e_type) {
+ case ET_NONE:
+ (void) printf(" %s", gettext("unknown type"));
+ break;
+ case ET_REL:
+ (void) printf(" %s", gettext("relocatable"));
+ break;
+ case ET_EXEC:
+ (void) printf(" %s", gettext("executable"));
+ break;
+ case ET_DYN:
+ (void) printf(" %s", gettext("dynamic lib"));
+ break;
+ case ET_CORE:
+ if (old_core(elf, ehdr, format))
+ (void) printf(" %s", gettext("pre-2.6 core file"));
+ else
+ (void) printf(" %s", gettext("core file"));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+print_elf_machine(int machine)
+{
+ switch (machine) {
+ case EM_NONE:
+ (void) printf(" %s", gettext("unknown machine"));
+ break;
+ case EM_M32:
+ (void) printf(" %s", gettext("WE32100"));
+ break;
+ case EM_SPARC:
+ (void) printf(" %s", gettext("SPARC"));
+ break;
+ case EM_386:
+ (void) printf(" %s", gettext("80386"));
+ break;
+ case EM_68K:
+ (void) printf(" %s", gettext("M68000"));
+ break;
+ case EM_88K:
+ (void) printf(" %s", gettext("M88000"));
+ break;
+ case EM_486:
+ (void) printf(" %s", gettext("80486"));
+ break;
+ case EM_860:
+ (void) printf(" %s", gettext("i860"));
+ break;
+ case EM_MIPS:
+ (void) printf(" %s", gettext("MIPS RS3000 Big-Endian"));
+ break;
+ case EM_MIPS_RS3_LE:
+ (void) printf(" %s", gettext("MIPS RS3000 Little-Endian"));
+ break;
+ case EM_RS6000:
+ (void) printf(" %s", gettext("MIPS RS6000"));
+ break;
+ case EM_PA_RISC:
+ (void) printf(" %s", gettext("PA-RISC"));
+ break;
+ case EM_nCUBE:
+ (void) printf(" %s", gettext("nCUBE"));
+ break;
+ case EM_VPP500:
+ (void) printf(" %s", gettext("VPP500"));
+ break;
+ case EM_SPARC32PLUS:
+ (void) printf(" %s", gettext("SPARC32PLUS"));
+ break;
+ case EM_PPC:
+ (void) printf(" %s", gettext("PowerPC"));
+ break;
+ case EM_SPARCV9:
+ (void) printf(" %s", gettext("SPARCV9"));
+ break;
+ case EM_IA_64:
+ (void) printf(" %s", gettext("IA64"));
+ break;
+ case EM_AMD64:
+ (void) printf(" %s", gettext("AMD64"));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+print_elf_datatype(int datatype)
+{
+ switch (datatype) {
+ case ELFDATA2LSB:
+ (void) printf(" %s", gettext("LSB"));
+ break;
+ case ELFDATA2MSB:
+ (void) printf(" %s", gettext("MSB"));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+print_elf_class(int class)
+{
+ switch (class) {
+ case ELFCLASS32:
+ (void) printf(" %s", gettext("32-bit"));
+ break;
+ case ELFCLASS64:
+ (void) printf(" %s", gettext("64-bit"));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+print_elf_flags(int machine, unsigned int flags)
+{
+ switch (machine) {
+ case EM_M32:
+ if (flags & EF_M32_MAU)
+ (void) printf("%s", gettext(", MAU Required"));
+ break;
+ case EM_SPARCV9:
+ if (flags & EF_SPARC_EXT_MASK) {
+ if (flags & EF_SPARC_SUN_US3) {
+ (void) printf("%s", gettext(
+ ", UltraSPARC3 Extensions Required"));
+ } else if (flags & EF_SPARC_SUN_US1) {
+ (void) printf("%s", gettext(
+ ", UltraSPARC1 Extensions Required"));
+ }
+ if (flags & EF_SPARC_HAL_R1)
+ (void) printf("%s", gettext(
+ ", HaL R1 Extensions Required"));
+ }
+ break;
+ case EM_SPARC32PLUS:
+ if (flags & EF_SPARC_32PLUS)
+ (void) printf("%s", gettext(", V8+ Required"));
+ if (flags & EF_SPARC_SUN_US3) {
+ (void) printf("%s",
+ gettext(", UltraSPARC3 Extensions Required"));
+ } else if (flags & EF_SPARC_SUN_US1) {
+ (void) printf("%s",
+ gettext(", UltraSPARC1 Extensions Required"));
+ }
+ if (flags & EF_SPARC_HAL_R1)
+ (void) printf("%s",
+ gettext(", HaL R1 Extensions Required"));
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+print_cap(Elf *elf, GElf_Ehdr *ehdr)
+{
+ Elf_Scn *scn = 0;
+
+ /*
+ * Traverse the files sections to see if any software/hardware
+ * capabilities are available.
+ */
+ while ((scn = elf_nextscn(elf, scn)) != 0) {
+ GElf_Word ndx, capn;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+
+ if (gelf_getshdr(scn, &shdr) == 0) {
+ (void) fprintf(stderr,
+ gettext("can't read ELF section header\n"));
+ return (1);
+ }
+ if (shdr.sh_type != SHT_SUNW_cap)
+ continue;
+
+ /*
+ * Get the data associated with the .cap section.
+ */
+ if ((data = elf_getdata(scn, 0)) == 0) {
+ (void) fprintf(stderr,
+ gettext("can't read ELF section data\n"));
+ return (1);
+ }
+
+ capn = (GElf_Word)(shdr.sh_size / shdr.sh_entsize);
+ for (ndx = 0; ndx < capn; ndx++) {
+ char str[100];
+ GElf_Cap cap;
+
+ if (gelf_getcap(data, ndx, &cap) == NULL) {
+ (void) fprintf(stderr,
+ gettext("can't read capabilities data\n"));
+ return (1);
+ }
+ if (cap.c_tag != CA_SUNW_NULL) {
+ (void) cap_val2str(cap.c_tag, cap.c_un.c_val,
+ str, sizeof (str), 0, ehdr->e_machine);
+ (void) printf(" [%s]", str);
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+elf_check(Elf *elf)
+{
+ GElf_Ehdr ehdr;
+ GElf_Phdr phdr;
+ int dynamic, cnt;
+ char *ident;
+ size_t size;
+
+ /*
+ * verify information in file header
+ */
+ if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *)0) {
+ (void) fprintf(stderr, gettext("can't read ELF header\n"));
+ return (1);
+ }
+ ident = elf_getident(elf, &size);
+ (void) printf("%s", gettext("ELF"));
+ print_elf_class(ident[EI_CLASS]);
+ print_elf_datatype(ident[EI_DATA]);
+ print_elf_type(elf, &ehdr, ident[EI_DATA]);
+ print_elf_machine(ehdr.e_machine);
+ if (ehdr.e_version == 1)
+ (void) printf(" %s %d",
+ gettext("Version"), (int)ehdr.e_version);
+ print_elf_flags(ehdr.e_machine, ehdr.e_flags);
+
+ if (core(elf, &ehdr, ident[EI_DATA])) /* check for core file */
+ return (0);
+
+ if (print_cap(elf, &ehdr))
+ return (1);
+
+ /*
+ * check type
+ */
+ if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN))
+ return (1);
+
+ /*
+ * read program header and check for dynamic section
+ */
+ if (ehdr.e_phnum == 0) {
+ (void) fprintf(stderr, gettext("can't read program header\n"));
+ return (1);
+ }
+
+ for (dynamic = 0, cnt = 0; cnt < (int)ehdr.e_phnum; cnt++) {
+ if (gelf_getphdr(elf, cnt, &phdr) == NULL) {
+ (void) fprintf(stderr,
+ gettext("can't read program header\n"));
+ return (1);
+ }
+ if (phdr.p_type == PT_DYNAMIC) {
+ dynamic = 1;
+ break;
+ }
+ }
+ if (dynamic)
+ (void) printf(gettext(", dynamically linked"));
+ else
+ (void) printf(gettext(", statically linked"));
+
+ is_stripped(elf);
+ return (0);
+}
+
+/*
+ * is_stripped prints information on whether the executable has
+ * been stripped.
+ */
+static void
+is_stripped(Elf *elf)
+{
+ GElf_Shdr shdr;
+ GElf_Ehdr ehdr;
+ Elf_Scn *scn, *nextscn;
+ char *section_name;
+ int symtab = 0;
+ int debuginfo = 0;
+
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ return;
+ }
+
+ /*
+ * Definition time:
+ * - "not stripped" means that an executable file
+ * contains a Symbol Table (.symtab)
+ * - "stripped" means that an executable file
+ * does not contain a Symbol Table.
+ * When strip -l or strip -x is run, it strips the
+ * debugging information (.line section name (strip -l),
+ * .line, .debug*, .stabs*, .dwarf* section names
+ * and SHT_SUNW_DEBUGSTR and SHT_SUNW_DEBUG
+ * section types (strip -x), however the Symbol
+ * Table will still be present.
+ * Therefore, if
+ * - No Symbol Table present, then report
+ * "stripped"
+ * - Symbol Table present with debugging
+ * information (line number or debug section names,
+ * or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
+ * types) then report:
+ * "not stripped"
+ * - Symbol Table present with no debugging
+ * information (line number or debug section names,
+ * or SHT_SUNW_DEBUGSTR or SHT_SUNW_DEBUG section
+ * types) then report:
+ * "not stripped, no debugging information
+ * available"
+ */
+ scn = NULL;
+ while ((nextscn = elf_nextscn(elf, scn)) != NULL) {
+ if (symtab && debuginfo) {
+ break;
+ }
+
+ scn = nextscn;
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ continue;
+ }
+
+ if (!symtab && (shdr.sh_type == SHT_SYMTAB)) {
+ symtab++;
+ continue;
+ }
+
+ if (!debuginfo &&
+ ((shdr.sh_type == SHT_SUNW_DEBUG) ||
+ (shdr.sh_type == SHT_SUNW_DEBUGSTR) ||
+ (((section_name = elf_strptr(elf, ehdr.e_shstrndx,
+ (size_t)shdr.sh_name)) != NULL) &&
+ (is_in_list(debug_sections, section_name))))) {
+ debuginfo++;
+ }
+ }
+
+ /*
+ * Now that we've scanned all sections, print out the appropriate
+ * diagnostic.
+ */
+ if (symtab) {
+ (void) printf(gettext(", not stripped"));
+ if (!debuginfo) {
+ (void) printf(gettext(
+ ", no debugging information available"));
+ }
+ } else {
+ (void) printf(gettext(", stripped"));
+ }
+}
+
+/*
+ * lookup -
+ * Attempts to match one of the strings from a list, 'tab',
+ * with what is in the file, starting at the current index position 'i'.
+ * Looks past any initial whitespace and expects whitespace or other
+ * delimiting characters to follow the matched string.
+ * A match identifies the file as being 'assembler', 'fortran', 'c', etc.
+ * Returns 1 for a successful match, 0 otherwise.
+ */
+static int
+lookup(char **tab)
+{
+ register char r;
+ register int k, j, l;
+
+ while (fbuf[i] == ' ' || fbuf[i] == '\t' || fbuf[i] == '\n')
+ i++;
+ for (j = 0; tab[j] != 0; j++) {
+ l = 0;
+ for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++);
+ if (r == '\0')
+ if (fbuf[k] == ' ' || fbuf[k] == '\n' ||
+ fbuf[k] == '\t' || fbuf[k] == '{' ||
+ fbuf[k] == '/') {
+ i = k;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * ccom -
+ * Increments the current index 'i' into the file buffer 'fbuf' past any
+ * whitespace lines and C-style comments found, starting at the current
+ * position of 'i'. Returns 1 as long as we don't increment i past the
+ * size of fbuf (fbsz). Otherwise, returns 0.
+ */
+
+static int
+ccom(void)
+{
+ register char cc;
+ int len;
+
+ while ((cc = fbuf[i]) == ' ' || cc == '\t' || cc == '\n')
+ if (i++ >= fbsz)
+ return (0);
+ if (fbuf[i] == '/' && fbuf[i+1] == '*') {
+ i += 2;
+ while (fbuf[i] != '*' || fbuf[i+1] != '/') {
+ if (fbuf[i] == '\\')
+ i++;
+ if ((len = mblen(&fbuf[i], MB_CUR_MAX)) <= 0)
+ len = 1;
+ i += len;
+ if (i >= fbsz)
+ return (0);
+ }
+ if ((i += 2) >= fbsz)
+ return (0);
+ }
+ if (fbuf[i] == '\n')
+ if (ccom() == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * ascom -
+ * Increments the current index 'i' into the file buffer 'fbuf' past
+ * consecutive assembler program comment lines starting with ASCOMCHAR,
+ * starting at the current position of 'i'.
+ * Returns 1 as long as we don't increment i past the
+ * size of fbuf (fbsz). Otherwise returns 0.
+ */
+
+static int
+ascom(void)
+{
+ while (fbuf[i] == ASCOMCHAR) {
+ i++;
+ while (fbuf[i++] != '\n')
+ if (i >= fbsz)
+ return (0);
+ while (fbuf[i] == '\n')
+ if (i++ >= fbsz)
+ return (0);
+ }
+ return (1);
+}
+
+static int
+sccs(void)
+{ /* look for "1hddddd" where d is a digit */
+ register int j;
+
+ if (fbuf[0] == 1 && fbuf[1] == 'h') {
+ for (j = 2; j <= 6; j++) {
+ if (isdigit(fbuf[j]))
+ continue;
+ else
+ return (0);
+ }
+ } else {
+ return (0);
+ }
+ return (1);
+}
+
+static int
+english(char *bp, int n)
+{
+#define NASC 128 /* number of ascii char ?? */
+ register int j, vow, freq, rare, len;
+ register int badpun = 0, punct = 0;
+ int ct[NASC];
+
+ if (n < 50)
+ return (0); /* no point in statistics on squibs */
+ for (j = 0; j < NASC; j++)
+ ct[j] = 0;
+ for (j = 0; j < n; j += len) {
+ if ((unsigned char)bp[j] < NASC)
+ ct[bp[j]|040]++;
+ switch (bp[j]) {
+ case '.':
+ case ',':
+ case ')':
+ case '%':
+ case ';':
+ case ':':
+ case '?':
+ punct++;
+ if (j < n-1 && bp[j+1] != ' ' && bp[j+1] != '\n')
+ badpun++;
+ }
+ if ((len = mblen(&bp[j], MB_CUR_MAX)) <= 0)
+ len = 1;
+ }
+ if (badpun*5 > punct)
+ return (0);
+ vow = ct['a'] + ct['e'] + ct['i'] + ct['o'] + ct['u'];
+ freq = ct['e'] + ct['t'] + ct['a'] + ct['i'] + ct['o'] + ct['n'];
+ rare = ct['v'] + ct['j'] + ct['k'] + ct['q'] + ct['x'] + ct['z'];
+ if (2*ct[';'] > ct['e'])
+ return (0);
+ if ((ct['>'] + ct['<'] + ct['/']) > ct['e'])
+ return (0); /* shell file test */
+ return (vow * 5 >= n - ct[' '] && freq >= 10 * rare);
+}
+
+/*
+ * Convert a word from an elf file to native format.
+ * This is needed because there's no elf routine to
+ * get and decode a Note section header.
+ */
+static void
+convert_gelf_word(Elf *elf, GElf_Word *data, int version, int format)
+{
+ Elf_Data src, dst;
+
+ dst.d_buf = data;
+ dst.d_version = version;
+ dst.d_size = sizeof (GElf_Word);
+ dst.d_type = ELF_T_WORD;
+ src.d_buf = data;
+ src.d_version = version;
+ src.d_size = sizeof (GElf_Word);
+ src.d_type = ELF_T_WORD;
+ (void) gelf_xlatetom(elf, &dst, &src, format);
+}
+
+static void
+convert_gelf_nhdr(Elf *elf, GElf_Nhdr *nhdr, GElf_Word version, int format)
+{
+ convert_gelf_word(elf, &nhdr->n_namesz, version, format);
+ convert_gelf_word(elf, &nhdr->n_descsz, version, format);
+ convert_gelf_word(elf, &nhdr->n_type, version, format);
+}
+
+/*
+ * Return true if it is an old (pre-restructured /proc) core file.
+ */
+static int
+old_core(Elf *elf, GElf_Ehdr *ehdr, int format)
+{
+ register int inx;
+ GElf_Phdr phdr;
+ GElf_Phdr nphdr;
+ GElf_Nhdr nhdr;
+ off_t offset;
+
+ if (ehdr->e_type != ET_CORE)
+ return (0);
+ for (inx = 0; inx < (int)ehdr->e_phnum; inx++) {
+ if (gelf_getphdr(elf, inx, &phdr) == NULL) {
+ return (0);
+ }
+ if (phdr.p_type == PT_NOTE) {
+ /*
+ * If the next segment is also a note, use it instead.
+ */
+ if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) {
+ return (0);
+ }
+ if (nphdr.p_type == PT_NOTE)
+ phdr = nphdr;
+ offset = (off_t)phdr.p_offset;
+ (void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset);
+ convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format);
+ /*
+ * Old core files have type NT_PRPSINFO.
+ */
+ if (nhdr.n_type == NT_PRPSINFO)
+ return (1);
+ return (0);
+ }
+ }
+ return (0);
+}
+
+/*
+ * If it's a core file, print out the name of the file that dumped core.
+ */
+static int
+core(Elf *elf, GElf_Ehdr *ehdr, int format)
+{
+ register int inx;
+ char *psinfo;
+ GElf_Phdr phdr;
+ GElf_Phdr nphdr;
+ GElf_Nhdr nhdr;
+ off_t offset;
+
+ if (ehdr->e_type != ET_CORE)
+ return (0);
+ for (inx = 0; inx < (int)ehdr->e_phnum; inx++) {
+ if (gelf_getphdr(elf, inx, &phdr) == NULL) {
+ (void) fprintf(stderr,
+ gettext("can't read program header\n"));
+ return (0);
+ }
+ if (phdr.p_type == PT_NOTE) {
+ char *fname;
+ size_t size;
+ /*
+ * If the next segment is also a note, use it instead.
+ */
+ if (gelf_getphdr(elf, inx+1, &nphdr) == NULL) {
+ (void) fprintf(stderr,
+ gettext("can't read program header\n"));
+ return (0);
+ }
+ if (nphdr.p_type == PT_NOTE)
+ phdr = nphdr;
+ offset = (off_t)phdr.p_offset;
+ (void) pread(ifd, &nhdr, sizeof (GElf_Nhdr), offset);
+ convert_gelf_nhdr(elf, &nhdr, ehdr->e_version, format);
+ /*
+ * Note: the ABI states that n_namesz must
+ * be rounded up to a 4 byte boundary.
+ */
+ offset += sizeof (GElf_Nhdr) +
+ ((nhdr.n_namesz + 0x03) & ~0x3);
+ size = nhdr.n_descsz;
+ psinfo = malloc(size);
+ (void) pread(ifd, psinfo, size, offset);
+ /*
+ * We want to print the string contained
+ * in psinfo->pr_fname[], where 'psinfo'
+ * is either an old NT_PRPSINFO structure
+ * or a new NT_PSINFO structure.
+ *
+ * Old core files have only type NT_PRPSINFO.
+ * New core files have type NT_PSINFO.
+ *
+ * These structures are also different by
+ * virtue of being contained in a core file
+ * of either 32-bit or 64-bit type.
+ *
+ * To further complicate matters, we ourself
+ * might be compiled either 32-bit or 64-bit.
+ *
+ * For these reason, we just *know* the offsets of
+ * pr_fname[] into the four different structures
+ * here, regardless of how we are compiled.
+ */
+ if (gelf_getclass(elf) == ELFCLASS32) {
+ /* 32-bit core file, 32-bit structures */
+ if (nhdr.n_type == NT_PSINFO)
+ fname = psinfo + 88;
+ else /* old: NT_PRPSINFO */
+ fname = psinfo + 84;
+ } else if (gelf_getclass(elf) == ELFCLASS64) {
+ /* 64-bit core file, 64-bit structures */
+ if (nhdr.n_type == NT_PSINFO)
+ fname = psinfo + 136;
+ else /* old: NT_PRPSINFO */
+ fname = psinfo + 120;
+ } else {
+ free(psinfo);
+ break;
+ }
+ (void) printf(gettext(", from '%s'"), fname);
+ free(psinfo);
+ break;
+ }
+ }
+ return (1);
+}
+
+static int
+shellscript(char buf[], struct stat64 *sb)
+{
+ char *tp, *cp, *xp, *up, *gp;
+
+ cp = strchr(buf, '\n');
+ if (cp == NULL || cp - fbuf > fbsz)
+ return (0);
+ for (tp = buf; tp != cp && isspace((unsigned char)*tp); tp++)
+ if (!isascii(*tp))
+ return (0);
+ for (xp = tp; tp != cp && !isspace((unsigned char)*tp); tp++)
+ if (!isascii(*tp))
+ return (0);
+ if (tp == xp)
+ return (0);
+ if (sb->st_mode & S_ISUID)
+ up = gettext("set-uid ");
+ else
+ up = "";
+
+ if (sb->st_mode & S_ISGID)
+ gp = gettext("set-gid ");
+ else
+ gp = "";
+
+ if (strncmp(xp, "/bin/sh", tp - xp) == 0)
+ xp = gettext("shell");
+ else if (strncmp(xp, "/bin/csh", tp - xp) == 0)
+ xp = gettext("c-shell");
+ else if (strncmp(xp, "/usr/sbin/dtrace", tp - xp) == 0)
+ xp = gettext("DTrace");
+ else
+ *tp = '\0';
+ /*
+ * TRANSLATION_NOTE
+ * This message is printed by file command for shell scripts.
+ * The first %s is for the translation for "set-uid " (if the script
+ * has the set-uid bit set), or is for an empty string (if the
+ * script does not have the set-uid bit set).
+ * Similarly, the second %s is for the translation for "set-gid ",
+ * or is for an empty string.
+ * The third %s is for the translation for either: "shell", "c-shell",
+ * or "DTrace", or is for the pathname of the program the script
+ * executes.
+ */
+ (void) printf(gettext("%s%sexecutable %s script\n"), up, gp, xp);
+ return (1);
+}
+
+static int
+get_door_target(char *file, char *buf, size_t bufsize)
+{
+ int fd;
+ door_info_t di;
+ psinfo_t psinfo;
+
+ if ((fd = open64(file, O_RDONLY)) < 0 ||
+ door_info(fd, &di) != 0) {
+ if (fd >= 0)
+ (void) close(fd);
+ return (-1);
+ }
+ (void) close(fd);
+
+ (void) sprintf(buf, "/proc/%ld/psinfo", di.di_target);
+ if ((fd = open64(buf, O_RDONLY)) < 0 ||
+ read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
+ if (fd >= 0)
+ (void) close(fd);
+ return (-1);
+ }
+ (void) close(fd);
+
+ (void) snprintf(buf, bufsize, "%s[%ld]", psinfo.pr_fname, di.di_target);
+ return (0);
+}
+
+/*
+ * ZIP file header information
+ */
+#define SIGSIZ 4
+#define LOCSIG "PK\003\004"
+#define LOCHDRSIZ 30
+
+#define CH(b, n) (((unsigned char *)(b))[n])
+#define SH(b, n) (CH(b, n) | (CH(b, n+1) << 8))
+#define LG(b, n) (SH(b, n) | (SH(b, n+2) << 16))
+
+#define LOCNAM(b) (SH(b, 26)) /* filename size */
+#define LOCEXT(b) (SH(b, 28)) /* extra field size */
+
+#define XFHSIZ 4 /* header id, data size */
+#define XFHID(b) (SH(b, 0)) /* extract field header id */
+#define XFDATASIZ(b) (SH(b, 2)) /* extract field data size */
+#define XFJAVASIG 0xcafe /* java executables */
+
+static int
+zipfile(char *fbuf, int fd)
+{
+ off_t xoff, xoff_end;
+
+ if (strncmp(fbuf, LOCSIG, SIGSIZ) != 0)
+ return (0);
+
+ xoff = LOCHDRSIZ + LOCNAM(fbuf);
+ xoff_end = xoff + LOCEXT(fbuf);
+
+ while (xoff < xoff_end) {
+ char xfhdr[XFHSIZ];
+
+ if (pread(fd, xfhdr, XFHSIZ, xoff) != XFHSIZ)
+ break;
+
+ if (XFHID(xfhdr) == XFJAVASIG) {
+ (void) printf("%s\n", gettext("java program"));
+ return (1);
+ }
+ xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr);
+ }
+
+ /*
+ * We could just print "ZIP archive" here.
+ *
+ * However, customers may be using their own entries in
+ * /etc/magic to distinguish one kind of ZIP file from another, so
+ * let's defer the printing of "ZIP archive" to there.
+ */
+ return (0);
+}
+
+static int
+is_crash_dump(const char *buf, int fd)
+{
+ /* LINTED: pointer cast may result in improper alignment */
+ const dumphdr_t *dhp = (const dumphdr_t *)buf;
+
+ /*
+ * The current DUMP_MAGIC string covers Solaris 7 and later releases.
+ * The utsname struct is only present in dumphdr_t's with dump_version
+ * greater than or equal to 9.
+ */
+ if (dhp->dump_magic == DUMP_MAGIC) {
+ print_dumphdr(fd, dhp, return_uint32, NATIVE_ISA);
+
+ } else if (dhp->dump_magic == swap_uint32(DUMP_MAGIC)) {
+ print_dumphdr(fd, dhp, swap_uint32, OTHER_ISA);
+
+ } else if (dhp->dump_magic == OLD_DUMP_MAGIC ||
+ dhp->dump_magic == swap_uint32(OLD_DUMP_MAGIC)) {
+ char *isa = (dhp->dump_magic == OLD_DUMP_MAGIC ?
+ NATIVE_ISA : OTHER_ISA);
+ (void) printf(gettext("SunOS 32-bit %s crash dump\n"), isa);
+
+ } else {
+ return (0);
+ }
+
+ return (1);
+}
+
+static void
+print_dumphdr(const int fd, const dumphdr_t *dhp, uint32_t (*swap)(uint32_t),
+ const char *isa)
+{
+ dumphdr_t dh;
+
+ /*
+ * A dumphdr_t is bigger than FBSZ, so we have to manually read the
+ * rest of it.
+ */
+ if (swap(dhp->dump_version) > 8 && pread(fd, &dh, sizeof (dumphdr_t),
+ (off_t)0) == sizeof (dumphdr_t)) {
+ (void) printf(gettext(
+ "%s %s %s %u-bit %s crash dump from '%s'\n"),
+ dh.dump_utsname.sysname, dh.dump_utsname.release,
+ dh.dump_utsname.version, swap(dh.dump_wordsize), isa,
+ dh.dump_utsname.nodename);
+ } else {
+ (void) printf(gettext("SunOS %u-bit %s crash dump\n"),
+ swap(dhp->dump_wordsize), isa);
+ }
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n"
+ " file [-dh] [-M mfile] [-m mfile] -f ffile\n"
+ " file -i [-h] [-f ffile] file ...\n"
+ " file -i [-h] -f ffile\n"
+ " file -c [-d] [-M mfile] [-m mfile]\n"));
+ exit(2);
+}
+
+static uint32_t
+swap_uint32(uint32_t in)
+{
+ uint32_t out;
+
+ out = (in & 0x000000ff) << 24;
+ out |= (in & 0x0000ff00) << 8; /* >> 8 << 16 */
+ out |= (in & 0x00ff0000) >> 8; /* >> 16 << 8 */
+ out |= (in & 0xff000000) >> 24;
+
+ return (out);
+}
+
+static uint32_t
+return_uint32(uint32_t in)
+{
+ return (in);
+}
+
+/*
+ * Check if str is in the string list str_list.
+ */
+static int
+is_in_list(char *str_list[], char *str)
+{
+ int i;
+
+ /*
+ * Only need to compare the strlen(str_list[i]) bytes.
+ * That way .stab will match on .stab* sections, and
+ * .debug will match on .debug* sections.
+ */
+ for (i = 0; str_list[i] != NULL; i++) {
+ if (strncmp(str_list[i], str, strlen(str_list[i])) == 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * default_magic -
+ * allocate space for and create the default magic file
+ * name string.
+ */
+
+static void
+default_magic(void)
+{
+ const char *msg_locale = setlocale(LC_MESSAGES, NULL);
+ struct stat statbuf;
+
+ if ((dfile = (char *)malloc(strlen(msg_locale) + 35)) == NULL) {
+ perror("file");
+ exit(2);
+ }
+ (void) snprintf(dfile, strlen(msg_locale) + 35,
+ "/usr/lib/locale/%s/LC_MESSAGES/magic", msg_locale);
+ if (stat(dfile, &statbuf) != 0) {
+ (void) strcpy(dfile, "/etc/magic");
+ }
+}
+
+/*
+ * add_to_mlist -
+ * Add the given magic_file filename string to the list of magic
+ * files (mlist). This list of files will later be examined, and
+ * each magic file's entries will be added in order to
+ * the mtab table.
+ *
+ * The first flag is set to 1 to add to the first list, mlist1.
+ * The first flag is set to 0 to add to the second list, mlist2.
+ */
+
+static void
+add_to_mlist(char *magic_file, int first)
+{
+ char **mlist; /* ordered list of magic files */
+ size_t mlist_sz; /* number of pointers allocated for mlist */
+ char **mlistp; /* next entry in mlist */
+ size_t mlistp_off;
+
+ if (first) {
+ mlist = mlist1;
+ mlist_sz = mlist1_sz;
+ mlistp = mlist1p;
+ } else {
+ mlist = mlist2;
+ mlist_sz = mlist2_sz;
+ mlistp = mlist2p;
+ }
+
+ if (mlist == NULL) { /* initial mlist allocation */
+ if ((mlist = (char **)calloc(MLIST_SZ, sizeof (char *)))
+ == NULL) {
+ perror("file");
+ exit(2);
+ }
+ mlist_sz = MLIST_SZ;
+ mlistp = mlist;
+ }
+ if ((mlistp - mlist) >= mlist_sz) {
+ mlistp_off = mlistp - mlist;
+ mlist_sz *= 2;
+ if ((mlist = (char **)realloc(mlist,
+ mlist_sz * sizeof (char *))) == NULL) {
+ perror("file");
+ exit(2);
+ }
+ mlistp = mlist + mlistp_off;
+ }
+ /*
+ * now allocate memory for and copy the
+ * magic file name string
+ */
+ if ((*mlistp = malloc(strlen(magic_file) + 1)) == NULL) {
+ perror("file");
+ exit(2);
+ }
+ (void) strlcpy(*mlistp, magic_file, strlen(magic_file) + 1);
+ mlistp++;
+
+ if (first) {
+ mlist1 = mlist;
+ mlist1_sz = mlist_sz;
+ mlist1p = mlistp;
+ } else {
+ mlist2 = mlist;
+ mlist2_sz = mlist_sz;
+ mlist2p = mlistp;
+ }
+}
+
+static void
+fd_cleanup(void)
+{
+ if (ifd != -1) {
+ (void) close(ifd);
+ ifd = -1;
+ }
+ if (elffd != -1) {
+ (void) close(elffd);
+ elffd = -1;
+ }
+}