root/usr/src/cmd/sgs/ar/common/main.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) 1988 AT&T */
/*        All Rights Reserved   */

/*
 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 * Copyright 2022 Oxide Computer Company
 */

#include "inc.h"
#include "conv.h"

/*
 * Forward declarations
 */
static void setup(int, char **, Cmd_info *);
static void setcom(Cmd_info *, Cmd_func);
static void usage(void);
static void sigexit(int sig);
static int notfound(Cmd_info *);
static void check_swap();

const char *
_ar_msg(Msg mid)
{
        return (gettext(MSG_ORIG(mid)));
}


void
establish_sighandler(void (*handler)())
{
        static const int signum[] = {SIGHUP, SIGINT, SIGQUIT, 0};
        int i;

        if (handler == SIG_IGN) {
                /* Ignore all the specified signals */
                for (i = 0; signum[i]; i++)
                        (void) signal(signum[i], SIG_IGN);

        } else {
                /*
                 * Set any signal that doesn't default to being ignored
                 * to our signal handler.
                 */
                for (i = 0; signum[i]; i++)
                        if (signal(signum[i], SIG_IGN) != SIG_IGN)
                                (void) signal(signum[i], handler);
        }
}

int
main(int argc, char **argv, char *envp[])
{
        int fd;
        Cmd_info *cmd_info;
        int ret;
        char *new = NULL;

#ifndef XPG4
        /*
         * Check for a binary that better fits this architecture.
         */
        (void) conv_check_native(argv, envp);
#endif

        /*
         * Establish locale.
         */
        (void) setlocale(LC_ALL, MSG_ORIG(MSG_STR_EMPTY));
        (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));

        /* Allow a graceful exit up until we start to write an archive */
        establish_sighandler(sigexit);

        /*
         * Initialize cmd_info
         */
        cmd_info = (Cmd_info *)calloc(1, sizeof (Cmd_info));
        if (cmd_info == NULL) {
                int err = errno;
                (void) fprintf(stderr, MSG_INTL(MSG_MALLOC), strerror(err));
                exit(1);
        }

        if (argc < 2)
                usage();

        /*
         * Option handling.
         */
        if (argv[1][0] != '-') {
                new = (char *)malloc(strlen(argv[1]) + 2);
                if (new == NULL) {
                        int err = errno;
                        (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
                            strerror(err));
                        exit(1);
                }
                (void) strcpy(new, MSG_ORIG(MSG_STR_HYPHEN));
                (void) strcat(new, argv[1]);
                argv[1] = new;
        }
        setup(argc, argv, cmd_info);

        /*
         * Check SWAP
         */
        if (cmd_info->opt_flgs & z_FLAG)
                check_swap();

        cmd_info->modified = (cmd_info->opt_flgs & s_FLAG);
        fd = getaf(cmd_info);

        if (fd == -1) {
                boolean_t req_arg = (cmd_info->opt_flgs & (d_FLAG | m_FLAG |
                    p_FLAG | t_FLAG | x_FLAG)) != 0;
                boolean_t req_r = (cmd_info->opt_flgs & r_FLAG) &&
                    (cmd_info->opt_flgs & (a_FLAG | b_FLAG));
                boolean_t req_s = (cmd_info->opt_flgs & s_FLAG) &&
                    (cmd_info->opt_flgs & (r_FLAG | q_FLAG)) == 0;

                if (req_arg || req_r || req_s) {
                        (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_AR),
                            cmd_info->arnam);
                        exit(1);
                }
        }

        (*cmd_info->comfun)(cmd_info);
        if (cmd_info->modified) {
                writefile(cmd_info);
        } else
                (void) close(fd);

        ret = notfound(cmd_info);

        /*
         * Check SWAP
         */
        if (cmd_info->opt_flgs & z_FLAG)
                check_swap();

        free(new);
        free(cmd_info);
        return (ret);

}

/*
 * Option handing function.
 *      Using getopt(), following xcu4 convention.
 */
static void
setup(int argc, char *argv[], Cmd_info *cmd_info)
{
        int Vflag = 0;
        int c;
        int usage_err = 0;

        while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != -1) {
                switch (c) {
                case 'a': /* position after named archive member file */
                        cmd_info->opt_flgs |= a_FLAG;
                        cmd_info->ponam = trim(optarg);
                        break;
                case 'b': /* position before named archive member file */
                case 'i': /* position before named archive member: same as b */
                        cmd_info->opt_flgs |= b_FLAG;
                        cmd_info->ponam = trim(optarg);
                        break;
                case 'c': /* supress messages */
                        cmd_info->opt_flgs |= c_FLAG;
                        break;
                case 'd':
                        /*
                         * key operation:
                         * delete files from the archive
                         */
                        setcom(cmd_info, dcmd);
                        cmd_info->opt_flgs |= d_FLAG;
                        break;
                case 'l': /* ignored */
                        break;
                case 'm':
                        /*
                         * key operation:
                         * move files to end of the archive
                         * or as indicated by position flag
                         */
                        setcom(cmd_info, mcmd);
                        cmd_info->opt_flgs |= m_FLAG;
                        break;
                case 'p':
                        /*
                         * key operation:
                         * print files in the archive
                         */
                        setcom(cmd_info, pcmd);
                        cmd_info->opt_flgs |= p_FLAG;
                        break;
                case 'q':
                        /*
                         * key operation:
                         * quickly append files to end of the archive
                         */
                        setcom(cmd_info, qcmd);
                        cmd_info->opt_flgs |= q_FLAG;
                        break;
                case 'r':
                        /*
                         * key operation:
                         * replace or add files to the archive
                         */
                        setcom(cmd_info, rcmd);
                        cmd_info->opt_flgs |= r_FLAG;
                        break;
                case 's': /* force symbol table regeneration */
                        cmd_info->opt_flgs |= s_FLAG;
                        break;
                case 'S': /* Build SYM64 symbol table */
                        cmd_info->opt_flgs |= S_FLAG;
                        break;
                case 't':
                        /*
                         * key operation:
                         * print table of contents
                         */
                        setcom(cmd_info, tcmd);
                        cmd_info->opt_flgs |= t_FLAG;
                        break;
                case 'u': /* update: change archive dependent on file dates */
                        cmd_info->opt_flgs |= u_FLAG;
                        break;
                case 'v': /* verbose */
                        cmd_info->opt_flgs |= v_FLAG;
                        break;
                case 'x':
                        /*
                         * key operation:
                         * extract files from the archive
                         */
                        setcom(cmd_info, xcmd);
                        cmd_info->opt_flgs |= x_FLAG;
                        break;
                case 'z':
                        cmd_info->opt_flgs |= z_FLAG;
                        break;
                case 'V':
                        /*
                         * print version information.
                         * adjust command line access accounting
                         */
                        if (Vflag == 0) {
                                (void) fprintf(stderr,
                                    MSG_ORIG(MSG_FMT_VERSION),
                                    (const char *)SGU_PKG,
                                    (const char *)SGU_REL);
                                Vflag++;
                        }
                        break;
                case 'C':
                        cmd_info->opt_flgs |= C_FLAG;
                        break;
                case 'M':
                        /*
                         * -M was an original undocumented AT&T feature that
                         * would force the use of mmap() instead of read()
                         * for pulling file data into the process before
                         * writing it to the archive. Ignored.
                         */
                        break;
                case 'T':
                        cmd_info->opt_flgs |= T_FLAG;
                        break;
                case ':':
                        (void) fprintf(stderr, MSG_INTL(MSG_USAGE_OPERAND),
                            optopt);
                        usage_err++;
                        break;
                case '?':
                        (void) fprintf(stderr, MSG_INTL(MSG_USAGE_OPTION),
                            optopt);
                        usage_err++;
                        break;
                }
        }

        if (usage_err || argc - optind < 1)
                usage();

        cmd_info->arnam = argv[optind];
        cmd_info->namv = &argv[optind+1];
        cmd_info->namc = argc - optind - 1;

        /*
         * GNU ar popularized the use of -s on its own which previously used to
         * require another command function. As such, we don't set a command
         * function when we encounter the -s flag because that might otherwise
         * clobber an existing one being set and would interrupt the detection
         * of multiple flags being used that way.
         *
         * If after processing everything, we find there's no command function
         * set and the -s flag has been set, then we can finally set a command
         * function. The command function for -t 'tcmd' is used in this case. It
         * knows to only print out data if -t has been specified.
         *
         * While ar has not traditionally been very stringent about using flags
         * in circumstances they aren't called for, we go ahead and check for
         * that now for this newer option.
         */
        if (cmd_info->comfun == NULL) {
                if ((cmd_info->opt_flgs & s_FLAG) != 0) {
                        if ((cmd_info->opt_flgs & ~(s_FLAG | v_FLAG)) != 0) {
                                (void) fprintf(stderr,
                                    MSG_INTL(MSG_USAGE_S_BAD_ARG));
                                exit(1);
                        }

                        if (cmd_info->namc > 0) {
                                (void) fprintf(stderr,
                                    MSG_INTL(MSG_USAGE_S_EXTRA_AR));
                                exit(1);
                        }

                        setcom(cmd_info, tcmd);
                } else if ((cmd_info->opt_flgs & (d_FLAG | r_FLAG | q_FLAG |
                    s_FLAG | t_FLAG | p_FLAG | m_FLAG | x_FLAG)) == 0) {
                        (void) fprintf(stderr, MSG_INTL(MSG_USAGE_REQ_FLAG));
                        exit(1);
                }
        }
}


/*
 * Set the function to be called to do the key operation.
 * Check that only one key is indicated.
 */
static void
setcom(Cmd_info *cmd_info, Cmd_func *fun)
{
        if (cmd_info->comfun != NULL) {
                (void) fprintf(stderr, MSG_INTL(MSG_USAGE_TOO_MANY));
                exit(1);
        }
        cmd_info->comfun = fun;
}

static void
usage(void)
{
        (void) fprintf(stderr, MSG_INTL(MSG_USAGE));
        exit(1);
}

/*ARGSUSED0*/
static void
sigexit(int sig)
{
        exit(100);
}

/* tells the user which of the listed files were not found in the archive */

static int
notfound(Cmd_info *cmd_info)
{
        int i, n;

        n = 0;
        for (i = 0; i < cmd_info->namc; i++)
                if (cmd_info->namv[i]) {
                        (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_FILE),
                            cmd_info->namv[i]);
                        n++;
                }
        return (n);
}

/*
 * Debugging info
 */
static void
check_swap(void)
{
        (void) system(MSG_ORIG(MSG_CMD_SWAP));
}