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

#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();

	if (cmd_info->comfun == NULL) {
		if ((cmd_info->opt_flgs & (d_FLAG | r_FLAG | q_FLAG |
		    t_FLAG | p_FLAG | m_FLAG | x_FLAG)) == 0) {
			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_01));
			exit(1);
		}
	}

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

	if ((fd == -1) &&
	    (cmd_info->opt_flgs &
	    (d_FLAG | m_FLAG | p_FLAG | t_FLAG | x_FLAG)) ||
	    ((cmd_info->opt_flgs & r_FLAG) &&
	    (cmd_info->opt_flgs & (a_FLAG | b_FLAG)))) {
		(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_02), optopt);
			usage_err++;
			break;
		case '?':
			(void) fprintf(stderr, MSG_INTL(MSG_USAGE_03), 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;
}


/*
 * 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 != 0) {
		(void) fprintf(stderr, MSG_INTL(MSG_USAGE_04));
		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));
}