root/usr/src/cmd/file/file.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright (c) 2018, Joyent, Inc.
 */

#define _LARGEFILE64_SOURCE

/* Get definitions for the relocation types supported. */
#define ELF_TARGET_ALL

#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 <procfs.h>
#include <sys/core.h>
#include <sys/dumphdr.h>
#include <netinet/in.h>
#include <gelf.h>
#include <elfcap.h>
#include <sgsrtcid.h>
#include "file.h"
#include "elf_read.h"

/*
 *      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 ar_coff_or_aout(int ifd);
static int type(char *file);
static int def_position_tests(char *file);
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 shellscript(char buf[], struct stat64 *sb);
static int elf_check(char *file);
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 void usage(void);
static void default_magic(void);
static void add_to_mlist(char *, int);
static void fd_cleanup(void);
static int is_rtld_config(void);

/* from elf_read.c */
int elf_read32(int elffd, Elf_Info *EInfo);
int elf_read64(int elffd, Elf_Info *EInfo);

#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 */

/*
 * Static program identifier - used to prevent localization of the name "file"
 * within individual error messages.
 */
const char *File = "file";

int
main(int argc, char **argv)
{
        char    *p;
        int     ch;
        FILE    *fl;
        int     bflg = 0;
        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:bcdf:him:")) != EOF) {
                switch (ch) {

                case 'M':
                        add_to_mlist(optarg, !dflg);
                        M_flg++;
                        break;

                case 'b':
                        bflg++;
                        break;

                case 'c':
                        cflg++;
                        break;

                case 'd':
                        if (!dflg) {
                                default_magic();
                                add_to_mlist(dfile, 0);
                                dflg++;
                        }
                        break;

                case 'f':
                        fflg++;
                        errno = 0;
                        if ((fl = fopen(optarg, "r")) == NULL) {
                                int err = errno;
                                (void) fprintf(stderr, gettext("%s: cannot "
                                    "open file %s: %s\n"), File, optarg,
                                    err ? strerror(err) : "");
                                usage();
                        }
                        pathlen = pathconf("/", _PC_PATH_MAX);
                        if (pathlen == -1) {
                                int err = errno;
                                (void) fprintf(stderr, gettext("%s: cannot "
                                    "determine maximum path length: %s\n"),
                                    File, strerror(err));
                                exit(1);
                        }
                        pathlen += 2; /* for null and newline in fgets */
                        if ((ap = malloc(pathlen * sizeof (char))) == NULL) {
                                int err = errno;
                                (void) fprintf(stderr, gettext("%s: malloc "
                                    "failed: %s\n"), File, strerror(err));
                                exit(2);
                        }
                        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) || (cflg && bflg)) {
                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 = malloc(maxmagicoffset)) == NULL) {
                int err = errno;
                (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
                    File, strerror(err));
                exit(2);
        }

        if (cflg) {
                f_prtmtab();
                if (ferror(stdout) != 0) {
                        (void) fprintf(stderr, gettext("%s: error writing to "
                            "stdout\n"), File);
                        exit(1);
                }
                if (fclose(stdout) != 0) {
                        int err = errno;
                        (void) fprintf(stderr, gettext("%s: fclose "
                            "failed: %s\n"), File, strerror(err));
                        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];

                if (!bflg)
                        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("%s: error writing to "
                    "stdout\n"), File);
                exit(1);
        }
        if (fclose(stdout) != 0) {
                int err = errno;
                (void) fprintf(stderr, gettext("%s: fclose failed: %s\n"),
                    File, strerror(err));
                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) {
                        int err = errno;
                        (void) printf(gettext("cannot open: %s\n"),
                            strerror(err));
                        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) {
                        int err = errno;
                        (void) printf(gettext("readlink error: %s\n"),
                            strerror(err));
                        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) {
                int err = errno;
                (void) printf(gettext("cannot open: %s\n"), strerror(err));
                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) {
                int err = errno;
                (void) printf(gettext("cannot open: %s\n"), strerror(err));
                (void) close(ifd);
                ifd = -1;
                return (0);                     /* POSIX.2 */
        }
        if ((fbsz = read(ifd, fbuf, FBSZ)) == -1) {
                int err = errno;
                (void) printf(gettext("cannot read: %s\n"), strerror(err));
                (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) {
                int err = errno;
                (void) printf(gettext("cannot read: %s\n"), strerror(err));
                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(file)) {
                        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(char *file)
{
        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_check(file) == 0) {
                (void) putchar('\n');
                return (1);
        } else if (*(int *)fbuf == CORE_MAGIC) {
#if !defined(_LP64)
                struct core *corep = (struct core *)fbuf;
#endif

                (void) printf("a.out core file");

#if !defined(_LP64)
                if (*(corep->c_cmdname) != '\0')
                        (void) printf(" from '%s'", corep->c_cmdname);
#endif
                (void) putchar('\n');
                return (1);
        }

        /*
         * Runtime linker (ld.so.1) configuration file.
         */
        if (is_rtld_config())
                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') &&
                            ((uchar_t)fbuf[1] == (uchar_t)'\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);
}

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_Info EI)
{
        switch (EI.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;
        default:
                break;
        }
}

static void
print_elf_machine(int machine)
{
        /*
         * This table must be kept in sync with the EM_ constants
         * in /usr/include/sys/elf.h.
         */
        static const char *mach_str[EM_NUM] = {
                [EM_NONE] = "unknown machine",
                [EM_M32] = "WE32100",
                [EM_SPARC] = "SPARC",
                [EM_386] = "80386",
                [EM_68K] = "M68000",
                [EM_88K] = "M88000",
                [EM_486] = "80486",
                [EM_860] = "i860",
                [EM_MIPS] = "MIPS RS3000 Big-Endian",
                [EM_S370] = "S/370",
                [EM_MIPS_RS3_LE] = "MIPS RS3000 Little-Endian",
                [EM_RS6000] = "MIPS RS6000",
                [EM_PA_RISC] = "PA-RISC",
                [EM_nCUBE] = "nCUBE",
                [EM_VPP500] = "VPP500",
                [EM_SPARC32PLUS] = "SPARC32PLUS",
                [EM_960] = "i960",
                [EM_PPC] = "PowerPC",
                [EM_PPC64] = "PowerPC64",
                [EM_S390] = "S/390",
                [EM_V800] = "V800",
                [EM_FR20] = "FR20",
                [EM_RH32] = "RH32",
                [EM_RCE] = "RCE",
                [EM_ARM] = "ARM",
                [EM_ALPHA] = "Alpha",
                [EM_SH] = "S/390",
                [EM_SPARCV9] = "SPARCV9",
                [EM_TRICORE] = "Tricore",
                [EM_ARC] = "ARC",
                [EM_H8_300] = "H8/300",
                [EM_H8_300H] = "H8/300H",
                [EM_H8S] = "H8S",
                [EM_H8_500] = "H8/500",
                [EM_IA_64] = "IA64",
                [EM_MIPS_X] = "MIPS-X",
                [EM_COLDFIRE] = "Coldfire",
                [EM_68HC12] = "M68HC12",
                [EM_MMA] = "MMA",
                [EM_PCP] = "PCP",
                [EM_NCPU] = "nCPU",
                [EM_NDR1] = "NDR1",
                [EM_STARCORE] = "Starcore",
                [EM_ME16] = "ME16",
                [EM_ST100] = "ST100",
                [EM_TINYJ] = "TINYJ",
                [EM_AMD64] = "AMD64",
                [EM_PDSP] = "PDSP",
                [EM_FX66] = "FX66",
                [EM_ST9PLUS] = "ST9 PLUS",
                [EM_ST7] = "ST7",
                [EM_68HC16] = "68HC16",
                [EM_68HC11] = "68HC11",
                [EM_68HC08] = "68H08",
                [EM_68HC05] = "68HC05",
                [EM_SVX] = "SVX",
                [EM_ST19] = "ST19",
                [EM_VAX] = "VAX",
                [EM_CRIS] = "CRIS",
                [EM_JAVELIN] = "Javelin",
                [EM_FIREPATH] = "Firepath",
                [EM_ZSP] = "ZSP",
                [EM_MMIX] = "MMIX",
                [EM_HUANY] = "HUANY",
                [EM_PRISM] = "Prism",
                [EM_AVR] = "AVR",
                [EM_FR30] = "FR30",
                [EM_D10V] = "D10V",
                [EM_D30V] = "D30V",
                [EM_V850] = "V850",
                [EM_M32R] = "M32R",
                [EM_MN10300] = "MN10300",
                [EM_MN10200] = "MN10200",
                [EM_PJ] = "picoJava",
                [EM_OPENRISC] = "OpenRISC",
                [EM_ARC_A5] = "Tangent-A5",
                [EM_XTENSA] = "Xtensa",

                [EM_VIDEOCORE] = "Videocore",
                [EM_TMM_GPP] = "TMM_GPP",
                [EM_NS32K] = "NS32K",
                [EM_TPC] = "TPC",
                [EM_SNP1K] = "SNP1K",
                [EM_ST200] = "ST200",
                [EM_IP2K] = "IP2K",
                [EM_MAX] = "MAX",
                [EM_CR] = "CompactRISC",
                [EM_F2MC16] = "F2MC16",
                [EM_MSP430] = "MSP430",
                [EM_BLACKFIN] = "Blackfin",
                [EM_SE_C33] = "S1C33",
                [EM_SEP] = "SEP",
                [EM_ARCA] = "Arca",
                [EM_UNICORE] = "Unicore",
                [EM_EXCESS] = "eXcess",
                [EM_DXP] = "DXP",
                [EM_ALTERA_NIOS2] = "Nios 2",
                [EM_CRX] = "CompactRISC CRX",
                [EM_XGATE] = "XGATE",
                [EM_C166] = "C16x/XC16x",
                [EM_M16C] = "M16C",
                [EM_DSPIC30F] = "dsPIC30F",
                [EM_CE] = "CE RISC",
                [EM_M32C] = "M32C",
                [EM_TSK3000] = "TSK3000",
                [EM_RS08] = "RS08",
                [EM_SHARC] = "SHARC",
                [EM_ECOG2] = "eCOG2",
                [EM_SCORE7] = "SCORE7",
                [EM_DSP24] = "DSP24",
                [EM_VIDEOCORE3] = "Videocore III",
                [EM_LATTICEMICO32] = "LATTICEMICO32",
                [EM_SE_C17] = "SE_C17",
                [EM_TI_C6000] = "TMS320C6000",
                [EM_TI_C2000] = "TMS320C2000",
                [EM_TI_C5500] = "TMS320C55x",
                [EM_TI_ARP32] = "ASRP32",
                [EM_TI_PRU] = "TI_PRU",
                [EM_MMDSP_PLUS] = "MMDSP_PLUS",
                [EM_CYPRESS_M8C] = "M8C",
                [EM_R32C] = "R32C",
                [EM_TRIMEDIA] = "TriMedia",
                [EM_QDSP6] = "QDSP6",
                [EM_8051] = "8051",
                [EM_STXP7X] = "STxP7x",
                [EM_NDS32] = "NDS32",
                [EM_ECOG1] = "eCOG1X",
                [EM_MAXQ30] = "MAXQ30",
                [EM_XIMO16] = "XIMO16",
                [EM_MANIK] = "M2000",
                [EM_CRAYNV2] = "CRAYNV2",
                [EM_RX] = "RX",
                [EM_METAG] = "METAG",
                [EM_MCST_ELBRUS] = "Elbrus",
                [EM_ECOG16] = "eCOG16",
                [EM_CR16] = "CR16",
                [EM_ETPU] = "ETPU",
                [EM_SLE9X] = "SLE9X",
                [EM_L10M] = "L10M",
                [EM_K10M] = "K10M",

                [EM_AARCH64] = "aarch64",

                [EM_AVR32] = "AVR32",
                [EM_STM8] = "STM8",
                [EM_TILE64] = "TILE64",
                [EM_TILEPRO] = "TILEPRO",
                [EM_MICROBLAZE] = "MicroBlaze",
                [EM_CUDA] = "CUDA",
                [EM_TILEGX] = "TILE-Gx",
                [EM_CLOUDSHIELD] = "CloudShield",
                [EM_COREA_1ST] = "CORE-A 1st",
                [EM_COREA_2ND] = "CORE-A 2nd",
                [EM_ARC_COMPACT2] = "ARCompact V2",
                [EM_OPEN8] = "Open8",
                [EM_RL78] = "RL78",
                [EM_VIDEOCORE5] = "VideoCore V",
                [EM_78KOR] = "78KOR",
                [EM_56800EX] = "56800EX",
                [EM_BA1] = "BA1",
                [EM_BA2] = "BA2",
                [EM_XCORE] = "xCORE",
                [EM_MCHP_PIC] = "MCHP_PIC",
                [EM_KM32] = "KM32",
                [EM_KMX32] = "KMX32",
                [EM_KMX16] = "KMX16",
                [EM_KMX8] = "KMX8",
                [EM_KVARC] = "KVARC",
                [EM_CDP] = "CDP",
                [EM_COGE] = "COGE",
                [EM_COOL] = "CoolEngine",
                [EM_NORC] = "NORC",
                [EM_CSR_KALIMBA] = "Kalimba",
                [EM_Z80] = "Zilog Z80",
                [EM_VISIUM] = "VISIUMcore",
                [EM_FT32] = "FT32",
                [EM_MOXIE] = "Moxie",
                [EM_AMDGPU] = "AMD GPU",
                [EM_RISCV] = "RISC-V",
                [EM_LANAI] = "Lanai",
                [EM_CEVA] = "CEVA",
                [EM_CEVA_X2] = "CEVA X2",
                [EM_BPF] = "Linux BPF",
                [EM_GRAPHCORE_IPU] = "Graphcore IPU",
                [EM_IMG1] = "Imagination Technologies",
                [EM_NFP] = "Netronome Flow Processor",
                [EM_VE] = "NEC Vector Engine",
                [EM_CSKY] = "C-SKY",
                [EM_ARC_COMPACT3_64] = "ARCv2.3 (64-bit)",
                [EM_MCS6502] = "MCS6502",
                [EM_ARC_COMPACT3] = "ARCv2.3 (32-bit)",
                [EM_KVX] = "Kalray KVX",
                [EM_65816] = "WDC 65816/65C816",
                [EM_LOONGARCH] = "Loongarch",
                [EM_KF32] = "KungFu32",
                [EM_U16_U8CORE] = "nX-U16/U8",
                [EM_TACHYUM] = "Tachyum",
                [EM_56800EF] = "NXP 56800EF",
                [EM_SBF] = "Solana Bytecode",
                [EM_AIENGINE] = "AMD/Xilinx AIEngine",
                [EM_SIMA_MLA] = "SiMa MLA",
                [EM_BANG] = "BANG",
                [EM_LOONGGPU] = "LoongGPU",
                [EM_SW64] = "SW64",
                [EM_AIECTRLCODE] = "AMD/Xilinx AIEngine ctrlcode",
        };
        /* If new machine is added, refuse to compile until we're updated */
#if EM_NUM != (EM_AIECTRLCODE + 1)
#error "Number of known ELF machine constants has changed"
#endif

        const char *str;

        if ((machine < EM_NONE) || (machine >= EM_NUM))
                machine = EM_NONE;

        str = mach_str[machine];
        if (str)
                (void) printf(" %s", str);
}

static void
print_elf_datatype(int datatype)
{
        switch (datatype) {
        case ELFDATA2LSB:
                (void) printf(" LSB");
                break;
        case ELFDATA2MSB:
                (void) printf(" 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(Elf_Info EI)
{
        unsigned int flags;

        flags = EI.flags;
        switch (EI.machine) {
        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;
        }
}

/*
 * check_ident: checks the ident field of the presumeably
 *              elf file. If check fails, this is not an
 *              elf file.
 */
static int
check_ident(unsigned char *ident, int fd)
{
        int class;
        if (pread64(fd, ident, EI_NIDENT, 0) != EI_NIDENT)
                return (ELF_READ_FAIL);
        class = ident[EI_CLASS];
        if (class != ELFCLASS32 && class != ELFCLASS64)
                return (ELF_READ_FAIL);
        if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
            ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3)
                return (ELF_READ_FAIL);

        return (ELF_READ_OKAY);
}

static int
elf_check(char *file)
{
        Elf_Info EInfo;
        int class, version, format;
        unsigned char ident[EI_NIDENT];

        (void) memset(&EInfo, 0, sizeof (Elf_Info));
        EInfo.file = file;

        /*
         * Verify information in file indentifier.
         * Return quietly if not elf; Different type of file.
         */
        if (check_ident(ident, elffd) == ELF_READ_FAIL)
                return (1);

        /*
         * Read the elf headers for processing and get the
         * get the needed information in Elf_Info struct.
         */
        class = ident[EI_CLASS];
        if (class == ELFCLASS32) {
                if (elf_read32(elffd, &EInfo) == ELF_READ_FAIL) {
                        (void) fprintf(stderr, gettext("%s: %s: can't "
                            "read ELF header\n"), File, file);
                        return (1);
                }
        } else if (class == ELFCLASS64) {
                if (elf_read64(elffd, &EInfo) == ELF_READ_FAIL) {
                        (void) fprintf(stderr, gettext("%s: %s: can't "
                            "read ELF header\n"), File, file);
                        return (1);
                }
        } else {
                /* something wrong */
                return (1);
        }

        /* version not in ident then 1 */
        version = ident[EI_VERSION] ? ident[EI_VERSION] : 1;

        format = ident[EI_DATA];
        (void) printf("%s", gettext("ELF"));
        print_elf_class(class);
        print_elf_datatype(format);
        print_elf_type(EInfo);

        if (EInfo.core_type != EC_NOTCORE) {
                /* Print what kind of core is this */
                if (EInfo.core_type == EC_OLDCORE)
                        (void) printf(" %s", gettext("pre-2.6 core file"));
                else
                        (void) printf(" %s", gettext("core file"));
        }

        /* Print machine info */
        print_elf_machine(EInfo.machine);

        /* Print Version */
        if (version == 1)
                (void) printf(" %s %d", gettext("Version"), version);

        if (EInfo.kmod) {
                (void) printf(", %s", gettext("kernel module"));
        }

        /* Print Flags */
        print_elf_flags(EInfo);

        /* Last bit, if it is a core */
        if (EInfo.core_type != EC_NOTCORE) {
                /* Print the program name that dumped this core */
                (void) printf(gettext(", from '%s'"), EInfo.fname);
                return (0);
        }

        /* Print Capabilities */
        if (EInfo.cap_str[0] != '\0')
                (void) printf(" [%s]", EInfo.cap_str);

        if ((EInfo.type != ET_EXEC) && (EInfo.type != ET_DYN))
                return (0);

        /* Print if it is dynamically linked */
        if (EInfo.dynamic)
                (void) printf(gettext(", dynamically linked"));
        else
                (void) printf(gettext(", statically linked"));

        /* Printf it it is stripped */
        if (EInfo.stripped & E_SYMTAB) {
                (void) printf(gettext(", not stripped"));
                if (!(EInfo.stripped & E_DBGINF)) {
                        (void) printf(gettext(
                            ", no debugging information available"));
                }
        } else {
                (void) printf(gettext(", stripped"));
        }

        return (0);
}

/*
 * is_rtld_config - If file is a runtime linker config file, prints
 * the description and returns True (1). Otherwise, silently returns
 * False (0).
 */
int
is_rtld_config(void)
{
        Rtc_id *id;

        if ((fbsz >= sizeof (*id)) && RTC_ID_TEST(fbuf)) {
                (void) printf(gettext("Runtime Linking Configuration"));
                id = (Rtc_id *) fbuf;
                print_elf_class(id->id_class);
                print_elf_datatype(id->id_data);
                print_elf_machine(id->id_machine);
                (void) printf("\n");
                return (1);
        }

        return (0);
}

/*
 * 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);
}

/* look for "1hddddd" where d is a digit */
static int
sccs(void)
{
        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);
}


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 archive file"));
                        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)) {
                const char *c = swap(dh.dump_flags) & DF_COMPRESSED ?
                    "compressed " : "";
                const char *l = swap(dh.dump_flags) & DF_LIVE ?
                    "live" : "crash";

                (void) printf(gettext(
                    "%s %s %s %u-bit %s %s%s dump from '%s'\n"),
                    dh.dump_utsname.sysname, dh.dump_utsname.release,
                    dh.dump_utsname.version, swap(dh.dump_wordsize), isa,
                    c, l, 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 [-bdh] [-M mfile] [-m mfile] [-f ffile] file ...\n"
            "       file [-bdh] [-M mfile] [-m mfile] -f ffile\n"
            "       file -i [-bh] [-f ffile] file ...\n"
            "       file -i [-bh] -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.
 */
int
is_in_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; debug_sections[i] != NULL; i++) {
                if (strncmp(debug_sections[i], str,
                    strlen(debug_sections[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 = malloc(strlen(msg_locale) + 35)) == NULL) {
                int err = errno;
                (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
                    File, strerror(err));
                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 = calloc(MLIST_SZ, sizeof (char *))) == NULL) {
                        int err = errno;
                        (void) fprintf(stderr, gettext("%s: malloc "
                            "failed: %s\n"), File, strerror(err));
                        exit(2);
                }
                mlist_sz = MLIST_SZ;
                mlistp = mlist;
        }
        if ((mlistp - mlist) >= mlist_sz) {
                mlistp_off = mlistp - mlist;
                mlist_sz *= 2;
                if ((mlist = realloc(mlist,
                    mlist_sz * sizeof (char *))) == NULL) {
                        int err = errno;
                        (void) fprintf(stderr, gettext("%s: malloc "
                            "failed: %s\n"), File, strerror(err));
                        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) {
                int err = errno;
                (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
                    File, strerror(err));
                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;
        }
}