xref: /onnv-gate/usr/src/cmd/sgs/elfedit/common/elfedit.c (revision 6635:8de60a34b2e5)
15088Sab196087 /*
25088Sab196087  * CDDL HEADER START
35088Sab196087  *
45088Sab196087  * The contents of this file are subject to the terms of the
55088Sab196087  * Common Development and Distribution License (the "License").
65088Sab196087  * You may not use this file except in compliance with the License.
75088Sab196087  *
85088Sab196087  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95088Sab196087  * or http://www.opensolaris.org/os/licensing.
105088Sab196087  * See the License for the specific language governing permissions
115088Sab196087  * and limitations under the License.
125088Sab196087  *
135088Sab196087  * When distributing Covered Code, include this CDDL HEADER in each
145088Sab196087  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155088Sab196087  * If applicable, add the following below this CDDL HEADER, with the
165088Sab196087  * fields enclosed by brackets "[]" replaced with your own identifying
175088Sab196087  * information: Portions Copyright [yyyy] [name of copyright owner]
185088Sab196087  *
195088Sab196087  * CDDL HEADER END
205088Sab196087  */
215088Sab196087 
225088Sab196087 /*
235892Sab196087  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
245088Sab196087  * Use is subject to license terms.
255088Sab196087  */
265088Sab196087 #pragma ident	"%Z%%M%	%I%	%E% SMI"
275088Sab196087 
285088Sab196087 #include	<sys/types.h>
295088Sab196087 #include	<sys/stat.h>
305088Sab196087 #include	<sys/wait.h>
315088Sab196087 #include	<stdarg.h>
325088Sab196087 #include	<fcntl.h>
335088Sab196087 #include	<stdlib.h>
345088Sab196087 #include	<stdio.h>
355088Sab196087 #include	<signal.h>
365088Sab196087 #include	<dirent.h>
375088Sab196087 #include	<libelf.h>
385088Sab196087 #include	<gelf.h>
395088Sab196087 #include	<conv.h>
405088Sab196087 #include	<dlfcn.h>
415088Sab196087 #include	<link.h>
425088Sab196087 #include	<stdarg.h>
435088Sab196087 #include	<libgen.h>
445088Sab196087 #include	<libintl.h>
455088Sab196087 #include	<locale.h>
465088Sab196087 #include	<unistd.h>
475088Sab196087 #include	<errno.h>
485088Sab196087 #include	<ctype.h>
495088Sab196087 #include	<limits.h>
505088Sab196087 #include	<strings.h>
515088Sab196087 #include	<sgs.h>
525088Sab196087 #include	"msg.h"
535088Sab196087 #include	"_elfedit.h"
545088Sab196087 #include	<debug.h>	/* liblddb */
555088Sab196087 
565088Sab196087 
575088Sab196087 
585088Sab196087 /*
595088Sab196087  * Column at which elfedit_format_command_usage() will wrap the
605088Sab196087  * generated usage string if the wrap argument is True (1).
615088Sab196087  */
625088Sab196087 #define	USAGE_WRAP_COL 55
635088Sab196087 
645088Sab196087 
655088Sab196087 
665088Sab196087 
675088Sab196087 /*
685088Sab196087  * Type used to represent a string buffer that can grow as needed
695088Sab196087  * to hold strings of arbitrary length. The user should declare
705088Sab196087  * variables of this type sa static. The strbuf_ensure_size() function
715088Sab196087  * is used to ensure that it has a minimum desired size.
725088Sab196087  */
735088Sab196087 typedef struct {
745088Sab196087 	char *buf;		/* String buffer */
755088Sab196087 	size_t n;		/* Size of buffer */
765088Sab196087 } STRBUF;
775088Sab196087 
785088Sab196087 
795088Sab196087 
805088Sab196087 
815088Sab196087 /*
825088Sab196087  * Types used by tokenize_user_cmd() to represent the result of
835088Sab196087  * spliting a user command into individual tokens.
845088Sab196087  */
855088Sab196087 typedef struct {
865088Sab196087 	char	*tok_str;	/* Token string */
875088Sab196087 	size_t	tok_len;	/* strlen(str) */
885088Sab196087 	size_t	tok_line_off;	/* Token offset in original string */
895088Sab196087 } TOK_ELT;
905088Sab196087 typedef struct {
915088Sab196087 	size_t	tokst_cmd_len;	/* Length of original user command, without */
925088Sab196087 				/*	newline or NULL termination chars */
935088Sab196087 	size_t	tokst_str_size;	/* Space needed to hold all the resulting */
945088Sab196087 				/*	tokens, including terminating NULL */
955088Sab196087 	TOK_ELT	*tokst_buf;	/* The array of tokens */
965088Sab196087 	size_t	tokst_cnt;	/* # of tokens in array */
975088Sab196087 	size_t	tokst_bufsize;	/* capacity of array */
985088Sab196087 } TOK_STATE;
995088Sab196087 
1005088Sab196087 
1015088Sab196087 
1025088Sab196087 
1035088Sab196087 /* State block used by gettok_init() and gettok() */
1045088Sab196087 typedef struct {
1055088Sab196087 	const char	*gtok_buf;	/* Addr of buffer containing string */
1065088Sab196087 	char		*gtok_cur_buf;	/* Addr withing buffer for next token */
1075088Sab196087 	int		gtok_inc_null_final; /* True if final NULL token used */
1085088Sab196087 	int		gtok_null_seen;	/* True when NULL byte seen */
1095088Sab196087 	TOK_ELT		gtok_last_token; /* Last token parsed */
1105088Sab196087 
1115088Sab196087 } GETTOK_STATE;
1125088Sab196087 
1135088Sab196087 
1145088Sab196087 
1155088Sab196087 
1165088Sab196087 /*
1175088Sab196087  * The elfedit_cpl_*() functions are used for command line completion.
1185088Sab196087  * Currently this uses the tecla library, but to allow for changing the
1195088Sab196087  * library used, we hide all tecla interfaces from our modules. Instead,
1205088Sab196087  * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
1215088Sab196087  * address of that struct as an opaque handle to the modules. Since the
1225088Sab196087  * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
1235088Sab196087  * as necessary.
1245088Sab196087  */
1255088Sab196087 typedef struct {
1265088Sab196087 	WordCompletion	*ecpl_cpl;		/* tecla handle */
1275088Sab196087 	const char	*ecpl_line;		/* raw input line */
1285088Sab196087 	int		ecpl_word_start;	/* start offset within line */
1295088Sab196087 	int		ecpl_word_end;		/* offset just past token */
1305088Sab196087 	/*
1315088Sab196087 	 * ecpl_add_mod_colon is a secret handshake between
1325088Sab196087 	 * elfedit_cpl_command() and  elfedit_cpl_add_match(). It adds
1335088Sab196087 	 * ':' to end of matched modules.
1345088Sab196087 	 */
1355088Sab196087 	int		ecpl_add_mod_colon;
1365088Sab196087 	const char	*ecpl_token_str;	/* token being completed */
1375088Sab196087 	size_t		ecpl_token_len;		/* strlen(ecpl_token_str) */
1385088Sab196087 } ELFEDIT_CPL_STATE;
1395088Sab196087 
1405088Sab196087 
1415088Sab196087 
1425088Sab196087 
1435088Sab196087 /* This structure maintains elfedit global state */
1445088Sab196087 STATE_T state;
1455088Sab196087 
1465088Sab196087 
1475088Sab196087 
1485088Sab196087 /*
1495088Sab196087  * Define a pair of static global variables that contain the
1505088Sab196087  * ISA strings that correspond to %i and %I tokens in module search
1515088Sab196087  * paths.
1525088Sab196087  *
1535088Sab196087  *	isa_i_str - The ISA string for the currently running program
1545088Sab196087  *	isa_I_str - For 64-bit programs, the same as isa_i_str. For
1555088Sab196087  *		32-bit programs, an empty string.
1565088Sab196087  */
1575088Sab196087 #ifdef __sparc
1585088Sab196087 #ifdef __sparcv9
1595088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64);
1605088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64);
1615088Sab196087 #else
1625088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32);
1635088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
1645088Sab196087 #endif
1655088Sab196087 #endif
1665088Sab196087 
1675088Sab196087 #ifdef __i386
1685088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32);
1695088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
1705088Sab196087 #endif
1715088Sab196087 #ifdef __amd64
1725088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64);
1735088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64);
1745088Sab196087 #endif
1755088Sab196087 
1765088Sab196087 
1775088Sab196087 
1785088Sab196087 /* Forward declarations */
1795088Sab196087 static void free_user_cmds(void);
1805088Sab196087 static void elfedit_pager_cleanup(void);
1815088Sab196087 
1825088Sab196087 
1835088Sab196087 
1845088Sab196087 /*
1855088Sab196087  * We supply this function for the msg module
1865088Sab196087  */
1875088Sab196087 const char *
1885088Sab196087 _elfedit_msg(Msg mid)
1895088Sab196087 {
1905088Sab196087 	return (gettext(MSG_ORIG(mid)));
1915088Sab196087 }
1925088Sab196087 
1935088Sab196087 
1945088Sab196087 /*
1955088Sab196087  * Copy at most min(cpsize, dstsize-1) bytes from src into dst,
1965088Sab196087  * truncating src if necessary.  The  result is always null-terminated.
1975088Sab196087  *
1985088Sab196087  * entry:
1995088Sab196087  *	dst - Destination buffer
2005088Sab196087  *	src - Source string
2015088Sab196087  *	dstsize - sizeof(dst)
2025088Sab196087  *
2035088Sab196087  * note:
2045088Sab196087  *	This is similar to strncpy(), but with two modifications:
2055088Sab196087  *	1) You specify the number of characters to copy, not just
2065088Sab196087  *		the size of the destination. Hence, you can copy non-NULL
2075088Sab196087  *		terminated strings.
2085088Sab196087  *	2) The destination is guaranteed to be NULL terminated. strncpy()
2095088Sab196087  *		does not terminate a completely full buffer.
2105088Sab196087  */
2115088Sab196087 static void
2125088Sab196087 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize)
2135088Sab196087 {
2145088Sab196087 	if (cpsize >= dstsize)
2155088Sab196087 		cpsize = dstsize - 1;
2165088Sab196087 	if (cpsize > 0)
2175088Sab196087 		(void) strncpy(dst, src, cpsize + 1);
2185088Sab196087 	dst[cpsize] = '\0';
2195088Sab196087 }
2205088Sab196087 
2215088Sab196087 
2225088Sab196087 /*
2235088Sab196087  * Calls exit() on behalf of elfedit.
2245088Sab196087  */
2255088Sab196087 void
2265088Sab196087 elfedit_exit(int status)
2275088Sab196087 {
2285088Sab196087 	if (state.file.present) {
2295088Sab196087 		/* Exiting with unflushed changes pending? Issue debug notice */
2305088Sab196087 		if (state.file.dirty)
2315088Sab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
2325088Sab196087 			    MSG_INTL(MSG_DEBUG_DIRTYEXIT));
2335088Sab196087 
2345088Sab196087 		/*
2355088Sab196087 		 * If the edit file is marked for unlink on exit, then
2365088Sab196087 		 * take care of it here.
2375088Sab196087 		 */
2385088Sab196087 		if (state.file.unlink_on_exit) {
2395088Sab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
2405088Sab196087 			    MSG_INTL(MSG_DEBUG_UNLINKFILE),
2415088Sab196087 			    state.file.outfile);
2425088Sab196087 			(void) unlink(state.file.outfile);
2435088Sab196087 		}
2445088Sab196087 	}
2455088Sab196087 
2465088Sab196087 	exit(status);
2475088Sab196087 }
2485088Sab196087 
2495088Sab196087 
2505088Sab196087 /*
2515088Sab196087  * Standard message function for elfedit. All user visible
2525088Sab196087  * output, for error or informational reasons, should go through
2535088Sab196087  * this function.
2545088Sab196087  *
2555088Sab196087  * entry:
2565088Sab196087  *	type - Type of message. One of the ELFEDIT_MSG_* values.
2575088Sab196087  *	format, ... - As per the printf() family
2585088Sab196087  *
2595088Sab196087  * exit:
2605088Sab196087  *	The desired message has been output. For informational
2615088Sab196087  *	messages, control returns to the caller. For errors,
2625088Sab196087  *	this routine will terminate execution or strip the execution
2635088Sab196087  *	stack and return control directly to the outer control loop.
2645088Sab196087  *	In either case, the caller will not receive control.
2655088Sab196087  */
2665088Sab196087 /*PRINTFLIKE2*/
2675088Sab196087 void
2685088Sab196087 elfedit_msg(elfedit_msg_t type, const char *format, ...)
2695088Sab196087 {
2705088Sab196087 	typedef enum {			/* What to do after finished */
2715088Sab196087 		DISP_RET = 0,		/* Return to caller */
2725088Sab196087 		DISP_JMP = 1, 		/* if (interactive) longjmp else exit */
2735088Sab196087 		DISP_EXIT = 2		/* exit under all circumstances */
2745088Sab196087 	} DISP;
2755088Sab196087 
2765088Sab196087 	va_list args;
2775088Sab196087 	FILE *stream = stderr;
2785088Sab196087 	DISP disp = DISP_RET;
2795088Sab196087 	int do_output = 1;
2805088Sab196087 	int need_prefix = 1;
2815088Sab196087 
2825088Sab196087 	va_start(args, format);
2835088Sab196087 
2845088Sab196087 	switch (type) {
2855088Sab196087 	case ELFEDIT_MSG_ERR:
2865088Sab196087 	case ELFEDIT_MSG_CMDUSAGE:
2875088Sab196087 		disp = DISP_JMP;
2885088Sab196087 		break;
2895088Sab196087 	case ELFEDIT_MSG_FATAL:
2905088Sab196087 		disp = DISP_EXIT;
2915088Sab196087 		break;
2925088Sab196087 	case ELFEDIT_MSG_USAGE:
2935088Sab196087 		need_prefix = 0;
2945088Sab196087 		break;
2955088Sab196087 	case ELFEDIT_MSG_DEBUG:
2965088Sab196087 		if (!(state.flags & ELFEDIT_F_DEBUG))
2975088Sab196087 			return;
2985088Sab196087 		stream = stdout;
2995088Sab196087 		break;
3005088Sab196087 	case ELFEDIT_MSG_QUIET:
3015088Sab196087 		do_output = 0;
3025088Sab196087 		disp = DISP_JMP;
3035088Sab196087 		break;
3045088Sab196087 	}
3055088Sab196087 
3065088Sab196087 
3075088Sab196087 	/*
3085088Sab196087 	 * If there is a pager process running, we are returning to the
3095088Sab196087 	 * caller, and the output is going to stdout, then let the
3105088Sab196087 	 * pager handle it instead of writing it directly from this process.
3115088Sab196087 	 * That way, the output gets paged along with everything else.
3125088Sab196087 	 *
3135088Sab196087 	 * If there is a pager process running, and we are not returning
3145088Sab196087 	 * to the caller, then end the pager process now, before we generate
3155088Sab196087 	 * any new output. This allows for any text buffered in the pager
3165088Sab196087 	 * pipe to be output before the new stuff.
3175088Sab196087 	 */
3185088Sab196087 	if (state.pager.fptr != NULL) {
3195088Sab196087 		if (disp == DISP_RET) {
3205088Sab196087 			if (stream == stdout)
3215088Sab196087 				stream = state.pager.fptr;
3225088Sab196087 		} else {
3235088Sab196087 			elfedit_pager_cleanup();
3245088Sab196087 		}
3255088Sab196087 	}
3265088Sab196087 
3275088Sab196087 	/*
3285088Sab196087 	 * If this message is coming from within the libtecla command
3295088Sab196087 	 * completion code, call gl_normal_io() to give the library notice.
3305088Sab196087 	 * That function sets the tty back to cooked mode and advances
3315088Sab196087 	 * the cursor to the beginning of the next line so that our output
3325088Sab196087 	 * will appear properly. When we return to the command completion code,
3335088Sab196087 	 * tecla will re-enter raw mode and redraw the current command line.
3345088Sab196087 	 */
3355088Sab196087 	if (state.input.in_tecla)
3365088Sab196087 		(void) gl_normal_io(state.input.gl);
3375088Sab196087 
3385088Sab196087 	if (do_output) {
3395088Sab196087 		if (need_prefix)
3405088Sab196087 			(void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT));
3415088Sab196087 		(void) vfprintf(stream, format, args);
3425088Sab196087 		(void) fflush(stream);
3435088Sab196087 	}
3445088Sab196087 	va_end(args);
3455088Sab196087 
3465088Sab196087 	/*
3475088Sab196087 	 * If this is an error, then we do not return to the caller.
3485088Sab196087 	 * The action taken depends on whether the outer loop has registered
3495088Sab196087 	 * a jump buffer for us or not.
3505088Sab196087 	 */
3515088Sab196087 	if (disp != DISP_RET) {
3525088Sab196087 		if (state.msg_jbuf.active && (disp == DISP_JMP)) {
3535088Sab196087 			/* Free the user command list */
3545088Sab196087 			free_user_cmds();
3555088Sab196087 
3565088Sab196087 			/* Clean up to reflect effect of non-local goto */
3575088Sab196087 			state.input.in_tecla = FALSE;
3585088Sab196087 
3595088Sab196087 			/* Jump to the outer loop to resume */
3605088Sab196087 			siglongjmp(state.msg_jbuf.env, 1);
3615088Sab196087 		} else {
3625088Sab196087 			elfedit_exit(1);
3635088Sab196087 		}
3645088Sab196087 	}
3655088Sab196087 }
3665088Sab196087 
3675088Sab196087 
3685088Sab196087 /*
3695088Sab196087  * Wrapper on elfedit_msg() that issues an error that results from
3705088Sab196087  * a call to libelf.
3715088Sab196087  *
3725088Sab196087  * entry:
3735088Sab196087  *	file - Name of ELF object
3745088Sab196087  *	libelf_rtn_name - Name of routine that was called
3755088Sab196087  *
3765088Sab196087  * exit:
3775088Sab196087  *	An error has been issued that shows the routine called
3785088Sab196087  *	and the libelf error string for it from elf_errmsg().
3795088Sab196087  *	This routine does not return to the caller.
3805088Sab196087  */
3815088Sab196087 void
3825088Sab196087 elfedit_elferr(const char *file, const char *libelf_rtn_name)
3835088Sab196087 {
3845088Sab196087 	const char *errstr = elf_errmsg(elf_errno());
3855088Sab196087 
3865088Sab196087 	elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file,
3875088Sab196087 	    libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN));
3885088Sab196087 }
3895088Sab196087 
3905088Sab196087 
3915088Sab196087 /*
3925088Sab196087  * Start an output pager process for elfedit_printf()/elfedit_write() to use.
3935088Sab196087  *
3945088Sab196087  * note:
3955088Sab196087  *	If this elfedit session is not interactive, then no pager is
3965088Sab196087  *	started. Paging is only intended for interactive use. The caller
3975088Sab196087  *	is not supposed to worry about this point, but simply to use
3985088Sab196087  *	this function to flag situations in which paging might be needed.
3995088Sab196087  */
4005088Sab196087 void
4015088Sab196087 elfedit_pager_init(void)
4025088Sab196087 {
4035088Sab196087 	const char	*errstr;
4045088Sab196087 	const char	*cmd;
4055088Sab196087 	int		err;
4065088Sab196087 
4075088Sab196087 	/*
4085088Sab196087 	 * If there is no pager process running, start one.
4095088Sab196087 	 * Only do this for interactive sessions --- elfedit_pager()
4105088Sab196087 	 * won't use a pager in batch mode.
4115088Sab196087 	 */
4125088Sab196087 	if (state.msg_jbuf.active && state.input.full_tty &&
4135088Sab196087 	    (state.pager.fptr == NULL)) {
4145088Sab196087 		/*
4155088Sab196087 		 * If the user has the PAGER environment variable set,
4165088Sab196087 		 * then we will use that program. Otherwise we default
4175088Sab196087 		 * to /bin/more.
4185088Sab196087 		 */
4195088Sab196087 		cmd = getenv(MSG_ORIG(MSG_STR_PAGER));
4205088Sab196087 		if ((cmd == NULL) || (*cmd == '\0'))
4215088Sab196087 			cmd = MSG_ORIG(MSG_STR_BINMORE);
4225088Sab196087 
4235088Sab196087 		/*
4245088Sab196087 		 * The popen() manpage says that on failure, it "may set errno",
4255088Sab196087 		 * which is somewhat ambiguous. We explicitly zero it here, and
4265088Sab196087 		 * assume that any change is due to popen() failing.
4275088Sab196087 		 */
4285088Sab196087 		errno = 0;
4295088Sab196087 		state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W));
4305088Sab196087 		if (state.pager.fptr == NULL) {
4315088Sab196087 			err = errno;
4325088Sab196087 			errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) :
4335088Sab196087 			    strerror(err);
4345088Sab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC),
4355088Sab196087 			    MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr);
4365088Sab196087 		}
4375088Sab196087 	}
4385088Sab196087 }
4395088Sab196087 
4405088Sab196087 
4415088Sab196087 /*
4425088Sab196087  * If there is a pager process present, close it out.
4435088Sab196087  *
4445088Sab196087  * note:
4455088Sab196087  *	This function is called from within elfedit_msg(), and as
4465088Sab196087  *	such, must not use elfedit_msg() to report errors. Furthermore,
4475088Sab196087  *	any such errors are not a sufficient reason to terminate the process
4485088Sab196087  *	or to longjmp(). This is a rare case where errors are written
4495088Sab196087  *	directly to stderr.
4505088Sab196087  */
4515088Sab196087 static void
4525088Sab196087 elfedit_pager_cleanup(void)
4535088Sab196087 {
4545088Sab196087 	if (state.pager.fptr != NULL) {
4555088Sab196087 		if (pclose(state.pager.fptr) == -1)
4565088Sab196087 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI));
4575088Sab196087 
4585088Sab196087 		state.pager.fptr = NULL;
4595088Sab196087 	}
4605088Sab196087 }
4615088Sab196087 
4625088Sab196087 
4635088Sab196087 /*
4645088Sab196087  * Print general formtted text for the user, using printf()-style
4655088Sab196087  * formatting. Uses the pager process if one has been started, or
4665088Sab196087  * stdout otherwise.
4675088Sab196087  */
4685088Sab196087 void
4695088Sab196087 elfedit_printf(const char *format, ...)
4705088Sab196087 {
4715088Sab196087 	va_list	args;
4725088Sab196087 	int	err;
4735088Sab196087 	FILE	*fptr;
4745088Sab196087 	int	pager;
4755088Sab196087 	int	broken_pipe = 0;
4765088Sab196087 
4775088Sab196087 	/*
4785088Sab196087 	 * If there is a pager process, then use it. Otherwise write
4795088Sab196087 	 * directly to stdout.
4805088Sab196087 	 */
4815088Sab196087 	pager = (state.pager.fptr != NULL);
4825088Sab196087 	fptr = pager ? state.pager.fptr : stdout;
4835088Sab196087 
4845088Sab196087 	va_start(args, format);
4855088Sab196087 	errno = 0;
4865088Sab196087 	err = vfprintf(fptr, format, args);
4875088Sab196087 
4885088Sab196087 	/* Did we fail because a child pager process has exited? */
4895088Sab196087 	broken_pipe = pager && (err < 0) && (errno == EPIPE);
4905088Sab196087 
4915088Sab196087 	va_end(args);
4925088Sab196087 
4935088Sab196087 	/*
4945088Sab196087 	 * On error, we simply issue the error without cleaning up
4955088Sab196087 	 * the pager process. The message code handles that as a standard
4965088Sab196087 	 * part of error processing.
4975088Sab196087 	 *
4985088Sab196087 	 * We handle failure due to an exited pager process differently
4995088Sab196087 	 * than a normal error, because it is usually due to the user
5005088Sab196087 	 * intentionally telling it to.
5015088Sab196087 	 */
5025088Sab196087 	if (err < 0) {
5035088Sab196087 		if (broken_pipe)
5045088Sab196087 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
5055088Sab196087 		else
5065088Sab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
5075088Sab196087 	}
5085088Sab196087 }
5095088Sab196087 
5105088Sab196087 
5115088Sab196087 /*
5125088Sab196087  * Some our modules use liblddb routines to format ELF output.
5135088Sab196087  * In order to ensure that such output is sent to the pager pipe
5145088Sab196087  * when there is one, and stdout otherwise, we redefine the dbg_print()
5155088Sab196087  * function here.
5165088Sab196087  *
5175088Sab196087  * This item should be defined NODIRECT.
5185088Sab196087  */
5195088Sab196087 /* PRINTFLIKE2 */
5205088Sab196087 void
5215088Sab196087 dbg_print(Lm_list *lml, const char *format, ...)
5225088Sab196087 {
5235088Sab196087 	va_list	ap;
5245088Sab196087 	int	err;
5255088Sab196087 	FILE	*fptr;
5265088Sab196087 	int	pager;
5275088Sab196087 	int	broken_pipe = 0;
5285088Sab196087 
5295088Sab196087 #if	defined(lint)
5305088Sab196087 	/*
5315088Sab196087 	 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
5325088Sab196087 	 * Supress the lint error by making a dummy assignment.
5335088Sab196087 	 */
5345088Sab196087 	lml = 0;
5355088Sab196087 #endif
5365088Sab196087 
5375088Sab196087 	/*
5385088Sab196087 	 * If there is a pager process, then use it. Otherwise write
5395088Sab196087 	 * directly to stdout.
5405088Sab196087 	 */
5415088Sab196087 	pager = (state.pager.fptr != NULL);
5425088Sab196087 	fptr = pager ? state.pager.fptr : stdout;
5435088Sab196087 
5445088Sab196087 	va_start(ap, format);
5455088Sab196087 	errno = 0;
5465088Sab196087 	err = vfprintf(fptr, format, ap);
5475088Sab196087 	if (err >= 0)
5485088Sab196087 		err = fprintf(fptr, MSG_ORIG(MSG_STR_NL));
5495088Sab196087 
5505088Sab196087 	/* Did we fail because a child pager process has exited? */
5515088Sab196087 	broken_pipe = (err < 0) && pager && (errno == EPIPE);
5525088Sab196087 
5535088Sab196087 	va_end(ap);
5545088Sab196087 
5555088Sab196087 	/*
5565088Sab196087 	 * On error, we simply issue the error without cleaning up
5575088Sab196087 	 * the pager process. The message code handles that as a standard
5585088Sab196087 	 * part of error processing.
5595088Sab196087 	 *
5605088Sab196087 	 * We handle failure due to an exited pager process differently
5615088Sab196087 	 * than a normal error, because it is usually due to the user
5625088Sab196087 	 * intentionally telling it to.
5635088Sab196087 	 */
5645088Sab196087 	if (err < 0) {
5655088Sab196087 		if (broken_pipe)
5665088Sab196087 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
5675088Sab196087 		else
5685088Sab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
5695088Sab196087 	}
5705088Sab196087 }
5715088Sab196087 
5725088Sab196087 
5735088Sab196087 /*
5745088Sab196087  * Write raw bytes of text in a manner similar to fwrite().
5755088Sab196087  * Uses the pager process if one has been started, or
5765088Sab196087  * stdout otherwise.
5775088Sab196087  */
5785088Sab196087 void
5795088Sab196087 elfedit_write(const void *ptr, size_t size)
5805088Sab196087 {
5815088Sab196087 	FILE	*fptr;
5825088Sab196087 	int	err;
5835088Sab196087 
5845088Sab196087 	/*
5855088Sab196087 	 * If there is a pager process, then use it. Otherwise write
5865088Sab196087 	 * directly to stdout.
5875088Sab196087 	 */
5885088Sab196087 	fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr;
5895088Sab196087 
5905088Sab196087 	if (fwrite(ptr, 1, size, fptr) != size) {
5915088Sab196087 		err = errno;
5925088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE),
5935088Sab196087 		    strerror(err));
5945088Sab196087 	}
5955088Sab196087 }
5965088Sab196087 
5975088Sab196087 
5985088Sab196087 /*
5995892Sab196087  * Convert the NULL terminated string to the form used by the C
600*6635Sab196087  * language to represent literal strings. See conv_str_to_c_literal()
601*6635Sab196087  * for details.
602*6635Sab196087  *
603*6635Sab196087  * This routine differs from conv_str_to_c_literal() in two ways:
604*6635Sab196087  *	1) String is NULL terminated instead of counted
605*6635Sab196087  *	2) Signature of outfunc
6065892Sab196087  *
6075892Sab196087  * entry:
6085892Sab196087  *	str - String to be processed
6095892Sab196087  *	outfunc - Function to be called to move output characters. Note
6105892Sab196087  *		that this function has the same signature as elfedit_write(),
6115892Sab196087  *		and that function can be used to write the characters to
6125892Sab196087  *		the output.
6135892Sab196087  *
6145892Sab196087  * exit:
6155892Sab196087  *	The string has been processed, with the resulting data passed
6165892Sab196087  *	to outfunc for processing.
6175892Sab196087  */
618*6635Sab196087 static void
619*6635Sab196087 elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue)
620*6635Sab196087 {
621*6635Sab196087 	elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue;
622*6635Sab196087 
623*6635Sab196087 	(* outfunc)(ptr, size);
624*6635Sab196087 
625*6635Sab196087 }
6265892Sab196087 void
6275892Sab196087 elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc)
6285892Sab196087 {
629*6635Sab196087 	conv_str_to_c_literal(str, strlen(str),
630*6635Sab196087 	    elfedit_str_to_c_literal_cb, (void *) outfunc);
6315892Sab196087 }
6325892Sab196087 
6335892Sab196087 
6345892Sab196087 /*
6355088Sab196087  * Wrappers on malloc() and realloc() that check the result for success
6365088Sab196087  * and issue an error if not. The caller can use the result of these
6375088Sab196087  * functions without checking for a NULL pointer, as we do not return to
6385088Sab196087  * the caller in the failure case.
6395088Sab196087  */
6405088Sab196087 void *
6415088Sab196087 elfedit_malloc(const char *item_name, size_t size)
6425088Sab196087 {
6435088Sab196087 	void *m;
6445088Sab196087 
6455088Sab196087 	m = malloc(size);
6465088Sab196087 	if (m == NULL) {
6475088Sab196087 		int err = errno;
6485088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
6495088Sab196087 		    item_name, strerror(err));
6505088Sab196087 	}
6515088Sab196087 
6525088Sab196087 	return (m);
6535088Sab196087 }
6545088Sab196087 
6555088Sab196087 void *
6565088Sab196087 elfedit_realloc(const char *item_name, void *ptr, size_t size)
6575088Sab196087 {
6585088Sab196087 	void *m;
6595088Sab196087 
6605088Sab196087 	m = realloc(ptr, size);
6615088Sab196087 	if (m == NULL) {
6625088Sab196087 		int err = errno;
6635088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
6645088Sab196087 		    item_name, strerror(err));
6655088Sab196087 	}
6665088Sab196087 
6675088Sab196087 	return (m);
6685088Sab196087 }
6695088Sab196087 
6705088Sab196087 
6715088Sab196087 /*
6725088Sab196087  * Ensure that the given buffer has room for n bytes of data.
6735088Sab196087  */
6745088Sab196087 static void
6755088Sab196087 strbuf_ensure_size(STRBUF *str, size_t size)
6765088Sab196087 {
6775088Sab196087 #define	INITIAL_STR_ALLOC 128
6785088Sab196087 
6795088Sab196087 	size_t n;
6805088Sab196087 
6815088Sab196087 	n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n;
6825088Sab196087 	while (size > n)	/* Double buffer until string fits */
6835088Sab196087 		n *= 2;
6845088Sab196087 	if (n != str->n) {		/* Alloc new string buffer if needed */
6855088Sab196087 		str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR),
6865088Sab196087 		    str->buf, n);
6875088Sab196087 		str->n = n;
6885088Sab196087 	}
6895088Sab196087 
6905088Sab196087 #undef	INITIAL_STR_ALLOC
6915088Sab196087 }
6925088Sab196087 
6935088Sab196087 
6945088Sab196087 /*
6955088Sab196087  * Extract the argument/option information for the next item referenced
6965088Sab196087  * by optarg, and advance the pointer to the next item.
6975088Sab196087  *
6985088Sab196087  * entry:
6995088Sab196087  *	optarg - Address of pointer to argument or option array
7005088Sab196087  *	item - Struct to be filled in.
7015088Sab196087  *
7025088Sab196087  * exit:
7035088Sab196087  *	The item block has been filled in with the information for
7045088Sab196087  *	the next item in the optarg array. *optarg has been advanced
7055088Sab196087  *	to the next item.
7065088Sab196087  */
7075088Sab196087 void
7085088Sab196087 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item)
7095088Sab196087 {
7105088Sab196087 	/*
7115088Sab196087 	 * Array of inheritable options/arguments. Indexed by one less
7125088Sab196087 	 * than the corresponding ELFEDIT_STDOA_ value.
7135088Sab196087 	 */
7145088Sab196087 	static const elfedit_optarg_item_t stdoa[] = {
7155088Sab196087 		/* ELFEDIT_STDOA_O */
7165088Sab196087 		{ MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE),
7175088Sab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_O) */
7185088Sab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O,
7195088Sab196087 		    ELFEDIT_CMDOA_F_VALUE },
7205088Sab196087 
7215088Sab196087 		/* ELFEDIT_STDOA_AND */
7225088Sab196087 		{ MSG_ORIG(MSG_STR_MINUS_AND), NULL,
7235088Sab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
7245088Sab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 },
7255088Sab196087 
7265088Sab196087 		/* ELFEDIT_STDOA_CMP */
7275088Sab196087 		{ MSG_ORIG(MSG_STR_MINUS_CMP), NULL,
7285088Sab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
7295088Sab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 },
7305088Sab196087 
7315088Sab196087 		/* ELFEDIT_STDOA_OR */
7325088Sab196087 		{ MSG_ORIG(MSG_STR_MINUS_OR), NULL,
7335088Sab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
7345088Sab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 },
7355088Sab196087 	};
7365088Sab196087 
7375088Sab196087 	elfedit_cmd_optarg_t *oa;
7385088Sab196087 
7395088Sab196087 
7405088Sab196087 	/* Grab first item, advance the callers pointer over it */
7415088Sab196087 	oa = (*optarg)++;
7425088Sab196087 
7435088Sab196087 	if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
7445088Sab196087 		/* Values are pre-chewed in the stdoa array above */
7455088Sab196087 		*item = stdoa[((uintptr_t)oa->oa_name) - 1];
7465088Sab196087 
7475088Sab196087 		/*
7485088Sab196087 		 * Set the inherited flag so that elfedit_optarg_helpstr()
7495088Sab196087 		 * can tell who is responsible for translating the help string.
7505088Sab196087 		 */
7515088Sab196087 		item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT;
7525088Sab196087 	} else {	/* Non-inherited item */
7535088Sab196087 		item->oai_name = oa->oa_name;
7545088Sab196087 		if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) {
7555088Sab196087 			item->oai_vname = oa[1].oa_name;
7565088Sab196087 
7575088Sab196087 			/* Advance users pointer past value element */
7585088Sab196087 			(*optarg)++;
7595088Sab196087 		} else {
7605088Sab196087 			item->oai_vname = NULL;
7615088Sab196087 		}
7625088Sab196087 		item->oai_help = oa->oa_help;
7635088Sab196087 		item->oai_flags = oa->oa_flags;
7645088Sab196087 	}
7655088Sab196087 
7665088Sab196087 	/*
7675088Sab196087 	 * The module determines the idmask and excmask fields whether
7685088Sab196087 	 * or not inheritance is in play.
7695088Sab196087 	 */
7705088Sab196087 	item->oai_idmask = oa->oa_idmask;
7715088Sab196087 	item->oai_excmask = oa->oa_excmask;
7725088Sab196087 }
7735088Sab196087 
7745088Sab196087 
7755088Sab196087 
7765088Sab196087 /*
7775088Sab196087  * Return the help string for an option/argument item, as returned
7785088Sab196087  * by elfedit_next_optarg(). This routine handles the details of
7795088Sab196087  * knowing whether the string is provided by elfedit itself (inherited),
7805088Sab196087  * or needs to be translated by the module.
7815088Sab196087  */
7825088Sab196087 const char *
7835088Sab196087 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item)
7845088Sab196087 {
7855088Sab196087 	/*
7865088Sab196087 	 * The help string from an inherited item comes right out
7875088Sab196087 	 * of the main elfedit string table.
7885088Sab196087 	 */
7895088Sab196087 	if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT)
7905088Sab196087 		return (MSG_INTL((Msg) item->oai_help));
7915088Sab196087 
7925088Sab196087 	/*
7935088Sab196087 	 * If the string is defined by the module, then we need to
7945088Sab196087 	 * have the module translate it for us.
7955088Sab196087 	 */
7965088Sab196087 	return ((* mod->mod_i18nhdl_to_str)(item->oai_help));
7975088Sab196087 }
7985088Sab196087 
7995088Sab196087 
8005088Sab196087 
8015088Sab196087 /*
8025088Sab196087  * Used by usage_optarg() to insert a character into the output buffer,
8035088Sab196087  * advancing the buffer pointer and current column, and reducing the
8045088Sab196087  * amount of remaining space.
8055088Sab196087  */
8065088Sab196087 static void
8075088Sab196087 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col)
8085088Sab196087 {
8095088Sab196087 
8105088Sab196087 	*(*cur)++ = ch;
8115088Sab196087 	**cur = '\0';
8125088Sab196087 	(*n)--;
8135088Sab196087 	(*cur_col)++;
8145088Sab196087 }
8155088Sab196087 
8165088Sab196087 /*
8175088Sab196087  * Used by usage_optarg() to insert a string into the output
8185088Sab196087  * buffer, advancing the buffer pointer and current column, and reducing
8195088Sab196087  * the amount of remaining space.
8205088Sab196087  */
8215088Sab196087 static void
8225088Sab196087 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col,
8235088Sab196087     const char *format, ...)
8245088Sab196087 {
8255088Sab196087 	size_t len;
8265088Sab196087 	va_list args;
8275088Sab196087 
8285088Sab196087 	va_start(args, format);
8295088Sab196087 	len = vsnprintf(*cur, *n, format, args);
8305088Sab196087 	va_end(args);
8315088Sab196087 
8325088Sab196087 	*cur += len;
8335088Sab196087 	*n -= len;
8345088Sab196087 	*cur_col += len;
8355088Sab196087 }
8365088Sab196087 /*
8375088Sab196087  * Used by usage_optarg() to insert an optarg item string into the output
8385088Sab196087  * buffer, advancing the buffer pointer and current column, and reducing
8395088Sab196087  * the amount of remaining space.
8405088Sab196087  */
8415088Sab196087 static void
8425088Sab196087 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur,
8435088Sab196087     size_t *n, size_t *cur_col)
8445088Sab196087 {
8455088Sab196087 	size_t len;
8465088Sab196087 
8475088Sab196087 	if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) {
8485088Sab196087 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2),
8495088Sab196087 		    item->oai_name, item->oai_vname);
8505088Sab196087 	} else {
8515088Sab196087 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG),
8525088Sab196087 		    item->oai_name);
8535088Sab196087 	}
8545088Sab196087 	*cur += len;
8555088Sab196087 	*n -= len;
8565088Sab196087 	*cur_col += len;
8575088Sab196087 }
8585088Sab196087 
8595088Sab196087 
8605088Sab196087 
8615088Sab196087 /*
8625088Sab196087  * Write the options/arguments to the usage string.
8635088Sab196087  *
8645088Sab196087  * entry:
8655088Sab196087  *	main_buf_n - Size of main buffer from which buf and buf_n are
8665088Sab196087  *		allocated.
8675088Sab196087  *	buf - Address of pointer to where next item is to be placed.
8685088Sab196087  *	buf_n - Address of count of remaining bytes in buffer
8695088Sab196087  *	buf_cur_col - Address of current output column for current line
8705088Sab196087  *		of generated string.
8715088Sab196087  *	optarg - Options list
8725088Sab196087  *	isopt - True if these are options, false for arguments.
8735088Sab196087  *	wrap_str - String to indent wrapped lines. If NULL, lines
8745088Sab196087  *		are not wrapped
8755088Sab196087  */
8765088Sab196087 static void
8775088Sab196087 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col,
8785088Sab196087     elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str)
8795088Sab196087 {
8805088Sab196087 	/*
8815088Sab196087 	 * An option can be combined into a simple format if it lacks
8825088Sab196087 	 * these flags and is only one character in length.
8835088Sab196087 	 */
8845088Sab196087 	static const elfedit_cmd_oa_flag_t exflags =
8855088Sab196087 	    (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT);
8865088Sab196087 
8875088Sab196087 	/*
8885088Sab196087 	 * A static buffer, which is grown as needed to accomodate
8895088Sab196087 	 * the maximum usage string seen.
8905088Sab196087 	 */
8915088Sab196087 	static STRBUF simple_str;
8925088Sab196087 
8935088Sab196087 	char			*cur = *buf;
8945088Sab196087 	size_t			n = *buf_n;
8955088Sab196087 	size_t			cur_col = *buf_cur_col;
8965088Sab196087 	int			len;
8975088Sab196087 	int			use_simple = 0;
8985088Sab196087 	elfedit_optarg_item_t	item;
8995088Sab196087 	elfedit_cmd_oa_mask_t	optmask = 0;
9005088Sab196087 	int			use_bkt;
9015088Sab196087 
9025088Sab196087 	/*
9035088Sab196087 	 * If processing options, pull the 1-character ones that don't have
9045088Sab196087 	 * an associated value and don't have any mutual exclusion issues into
9055088Sab196087 	 * a single combination string to go at the beginning of the usage.
9065088Sab196087 	 */
9075088Sab196087 	if (isopt) {
9085088Sab196087 		elfedit_cmd_optarg_t *tmp_optarg = optarg;
9095088Sab196087 		char *s;
9105088Sab196087 
9115088Sab196087 		/*
9125088Sab196087 		 * The simple string is guaranteed to fit in the same
9135088Sab196087 		 * amount of space reserved for the main buffer.
9145088Sab196087 		 */
9155088Sab196087 		strbuf_ensure_size(&simple_str, main_buf_n);
9165088Sab196087 		s = simple_str.buf;
9175088Sab196087 		*s++ = ' ';
9185088Sab196087 		*s++ = '[';
9195088Sab196087 		*s++ = '-';
9205088Sab196087 		while (tmp_optarg->oa_name != NULL) {
9215088Sab196087 			elfedit_next_optarg(&tmp_optarg, &item);
9225088Sab196087 			if (((item.oai_flags & exflags) == 0) &&
9235088Sab196087 			    (item.oai_name[2] == '\0') &&
9245088Sab196087 			    (item.oai_excmask == 0)) {
9255088Sab196087 				optmask |= item.oai_idmask;
9265088Sab196087 				*s++ = item.oai_name[1];
9275088Sab196087 			}
9285088Sab196087 		}
9295088Sab196087 
9305088Sab196087 		/*
9315088Sab196087 		 * If we found more than one, then finish the string and
9325088Sab196087 		 * add it. Don't do this for a single option, because
9335088Sab196087 		 * it looks better in that case if the option shows up
9345088Sab196087 		 * in alphabetical order rather than being hoisted.
9355088Sab196087 		 */
9365088Sab196087 		use_simple = (s > (simple_str.buf + 4));
9375088Sab196087 		if (use_simple) {
9385088Sab196087 			*s++ = ']';
9395088Sab196087 			*s++ = '\0';
9405088Sab196087 			usage_optarg_insert_str(&cur, &n, &cur_col,
9415088Sab196087 			    MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf);
9425088Sab196087 		} else {
9435088Sab196087 			/* Not using it, so reset the cumulative options mask */
9445088Sab196087 			optmask = 0;
9455088Sab196087 		}
9465088Sab196087 	}
9475088Sab196087 
9485088Sab196087 	while (optarg->oa_name != NULL) {
9495088Sab196087 		elfedit_next_optarg(&optarg, &item);
9505088Sab196087 
9515088Sab196087 		if (isopt) {
9525088Sab196087 			/*
9535088Sab196087 			 * If this is an option that was pulled into the
9545088Sab196087 			 * combination string above, then skip over it.
9555088Sab196087 			 */
9565088Sab196087 			if (use_simple && ((item.oai_flags & exflags) == 0) &&
9575088Sab196087 			    (item.oai_name[2] == '\0') &&
9585088Sab196087 			    (item.oai_excmask == 0))
9595088Sab196087 				continue;
9605088Sab196087 
9615088Sab196087 			/*
9625088Sab196087 			 * If this is a mutual exclusion option that was
9635088Sab196087 			 * picked up out of order by a previous iteration
9645088Sab196087 			 * of this loop, then skip over it.
9655088Sab196087 			 */
9665088Sab196087 			if ((optmask & item.oai_idmask) != 0)
9675088Sab196087 				continue;
9685088Sab196087 
9695088Sab196087 			/* Add this item to the accumulating options mask */
9705088Sab196087 			optmask |= item.oai_idmask;
9715088Sab196087 		}
9725088Sab196087 
9735088Sab196087 		/* Wrap line, or insert blank separator */
9745088Sab196087 		if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) {
9755088Sab196087 			len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE),
9765088Sab196087 			    wrap_str);
9775088Sab196087 			cur += len;
9785088Sab196087 			n -= len;
9795088Sab196087 			cur_col = len - 1;   /* Don't count the newline */
9805088Sab196087 		} else {
9815088Sab196087 			usage_optarg_insert_ch(' ', &cur, &n, &cur_col);
9825088Sab196087 		}
9835088Sab196087 
9845088Sab196087 		use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt;
9855088Sab196087 		if (use_bkt)
9865088Sab196087 			usage_optarg_insert_ch('[', &cur, &n, &cur_col);
9875088Sab196087 
9885088Sab196087 		/* Add the item to the buffer */
9895088Sab196087 		usage_optarg_insert_item(&item, &cur, &n, &cur_col);
9905088Sab196087 
9915088Sab196087 		/*
9925088Sab196087 		 * If this item has a non-zero mutual exclusion mask,
9935088Sab196087 		 * then look for the other items and display them all
9945088Sab196087 		 * together with alternation (|). Note that plain arguments
9955088Sab196087 		 * cannot have a non-0 exclusion mask, so this is
9965088Sab196087 		 * effectively options-only (isopt != 0).
9975088Sab196087 		 */
9985088Sab196087 		if (item.oai_excmask != 0) {
9995088Sab196087 			elfedit_cmd_optarg_t *tmp_optarg = optarg;
10005088Sab196087 			elfedit_optarg_item_t tmp_item;
10015088Sab196087 
10025088Sab196087 			/*
10035088Sab196087 			 * When showing alternation, elipses for multiple
10045088Sab196087 			 * copies need to appear inside the [] brackets.
10055088Sab196087 			 */
10065088Sab196087 			if (item.oai_flags & ELFEDIT_CMDOA_F_MULT)
10075088Sab196087 				usage_optarg_insert_str(&cur, &n, &cur_col,
10085088Sab196087 				    MSG_ORIG(MSG_STR_ELIPSES));
10095088Sab196087 
10105088Sab196087 
10115088Sab196087 			while (tmp_optarg->oa_name != NULL) {
10125088Sab196087 				elfedit_next_optarg(&tmp_optarg, &tmp_item);
10135088Sab196087 				if ((item.oai_excmask & tmp_item.oai_idmask) ==
10145088Sab196087 				    0)
10155088Sab196087 					continue;
10165088Sab196087 				usage_optarg_insert_str(&cur, &n, &cur_col,
10175088Sab196087 				    MSG_ORIG(MSG_STR_SP_BAR_SP));
10185088Sab196087 				usage_optarg_insert_item(&tmp_item,
10195088Sab196087 				    &cur, &n, &cur_col);
10205088Sab196087 
10215088Sab196087 				/*
10225088Sab196087 				 * Add it to the mask of seen options.
10235088Sab196087 				 * This will keep us from showing it twice.
10245088Sab196087 				 */
10255088Sab196087 				optmask |= tmp_item.oai_idmask;
10265088Sab196087 			}
10275088Sab196087 		}
10285088Sab196087 		if (use_bkt)
10295088Sab196087 			usage_optarg_insert_ch(']', &cur, &n, &cur_col);
10305088Sab196087 
10315088Sab196087 		/*
10325088Sab196087 		 * If alternation was not shown above (non-zero exclusion mask)
10335088Sab196087 		 * then the elipses for multiple copies are shown outside
10345088Sab196087 		 * any [] brackets.
10355088Sab196087 		 */
10365088Sab196087 		if ((item.oai_excmask == 0) &&
10375088Sab196087 		    (item.oai_flags & ELFEDIT_CMDOA_F_MULT))
10385088Sab196087 			usage_optarg_insert_str(&cur, &n, &cur_col,
10395088Sab196087 			    MSG_ORIG(MSG_STR_ELIPSES));
10405088Sab196087 
10415088Sab196087 	}
10425088Sab196087 
10435088Sab196087 	*buf = cur;
10445088Sab196087 	*buf_n = n;
10455088Sab196087 	*buf_cur_col = cur_col;
10465088Sab196087 }
10475088Sab196087 
10485088Sab196087 
10495088Sab196087 
10505088Sab196087 /*
10515088Sab196087  * Format the usage string for a command into a static buffer and
10525088Sab196087  * return the pointer to the user. The resultant string is valid
10535088Sab196087  * until the next call to this routine, and which point it
10545088Sab196087  * will be overwritten or the memory is freed.
10555088Sab196087  *
10565088Sab196087  * entry:
10575088Sab196087  *	mod, cmd - Module and command definitions for command to be described
10585088Sab196087  *	wrap_str - NULL, or string to be used to indent when
10595088Sab196087  *		lines are wrapped. If NULL, no wrapping is done, and
10605088Sab196087  *		all output is on a single line.
10615088Sab196087  *	cur_col - Starting column at which the string will be displayed.
10625088Sab196087  *		Ignored if wrap_str is NULL.
10635088Sab196087  */
10645088Sab196087 const char *
10655088Sab196087 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd,
10665088Sab196087     const char *wrap_str, size_t cur_col)
10675088Sab196087 {
10685088Sab196087 
10695088Sab196087 	/*
10705088Sab196087 	 * A static buffer, which is grown as needed to accomodate
10715088Sab196087 	 * the maximum usage string seen.
10725088Sab196087 	 */
10735088Sab196087 	static STRBUF str;
10745088Sab196087 
10755088Sab196087 	elfedit_cmd_optarg_t	*optarg;
10765088Sab196087 	size_t			len, n, elipses_len;
10775088Sab196087 	char			*cur;
10785088Sab196087 	elfedit_optarg_item_t	item;
10795088Sab196087 
10805088Sab196087 	/*
10815088Sab196087 	 * Estimate a worst case size for the usage string:
10825088Sab196087 	 *	- module name
10835088Sab196087 	 *	- lengths of the strings
10845088Sab196087 	 *	- every option or argument is enclosed in brackets
10855088Sab196087 	 *	- space in between each item, with an alternation (" | ")
10865088Sab196087 	 *	- elipses will be displayed with each option and argument
10875088Sab196087 	 */
10885088Sab196087 	n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6;
10895088Sab196087 	elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES));
10905088Sab196087 	if ((optarg = cmd->cmd_opt) != NULL)
10915088Sab196087 		while (optarg->oa_name != NULL) {
10925088Sab196087 			elfedit_next_optarg(&optarg, &item);
10935088Sab196087 			n += strlen(item.oai_name) + 5 + elipses_len;
10945088Sab196087 		}
10955088Sab196087 	if ((optarg = cmd->cmd_args) != NULL)
10965088Sab196087 		while (optarg->oa_name != NULL) {
10975088Sab196087 			elfedit_next_optarg(&optarg, &item);
10985088Sab196087 			n += strlen(item.oai_name) + 5 + elipses_len;
10995088Sab196087 		}
11005088Sab196087 	n++;			/* Null termination */
11015088Sab196087 
11025088Sab196087 	/*
11035088Sab196087 	 * If wrapping lines, we insert a newline and then wrap_str
11045088Sab196087 	 * every USAGE_WRAP_COL characters.
11055088Sab196087 	 */
11065088Sab196087 	if (wrap_str != NULL)
11075088Sab196087 		n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) *
11085088Sab196087 		    (strlen(wrap_str) + 1);
11095088Sab196087 
11105088Sab196087 	strbuf_ensure_size(&str, n);
11115088Sab196087 
11125088Sab196087 	/* Command name */
11135088Sab196087 	cur = str.buf;
11145088Sab196087 	n = str.n;
11155088Sab196087 	if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0)
11165088Sab196087 		len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD),
11175088Sab196087 		    cmd->cmd_name[0]);
11185088Sab196087 	else
11195088Sab196087 		len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD),
11205088Sab196087 		    mod->mod_name, cmd->cmd_name[0]);
11215088Sab196087 	cur += len;
11225088Sab196087 	n -= len;
11235088Sab196087 	cur_col += len;
11245088Sab196087 
11255088Sab196087 	if (cmd->cmd_opt != NULL)
11265088Sab196087 		usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt,
11275088Sab196087 		    1, wrap_str);
11285088Sab196087 	if (cmd->cmd_args != NULL)
11295088Sab196087 		usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args,
11305088Sab196087 		    0, wrap_str);
11315088Sab196087 
11325088Sab196087 	return (str.buf);
11335088Sab196087 }
11345088Sab196087 
11355088Sab196087 /*
11365088Sab196087  * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE
11375088Sab196087  * error giving usage information for the command currently
11385088Sab196087  * referenced by state.cur_cmd.
11395088Sab196087  */
11405088Sab196087 void
11415088Sab196087 elfedit_command_usage(void)
11425088Sab196087 {
11435088Sab196087 	elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD),
11445088Sab196087 	    elfedit_format_command_usage(state.cur_cmd->ucmd_mod,
11455088Sab196087 	    state.cur_cmd->ucmd_cmd, NULL, 0));
11465088Sab196087 }
11475088Sab196087 
11485088Sab196087 
11495088Sab196087 /*
11505088Sab196087  * This function allows the loadable modules to get the command line
11515088Sab196087  * flags.
11525088Sab196087  */
11535088Sab196087 elfedit_flag_t
11545088Sab196087 elfedit_flags(void)
11555088Sab196087 {
11565088Sab196087 	return (state.flags);
11575088Sab196087 }
11585088Sab196087 
11595088Sab196087 /*
11605088Sab196087  * This function is used to register a per-command invocation output style
11615088Sab196087  * that will momentarily override the global output style for the duration
11625088Sab196087  * of the current command. This function must only be called by an
11635088Sab196087  * active command.
11645088Sab196087  *
11655088Sab196087  * entry:
11665088Sab196087  *	str - One of the valid strings for the output style
11675088Sab196087  */
11685088Sab196087 void
11695088Sab196087 elfedit_set_cmd_outstyle(const char *str)
11705088Sab196087 {
11715088Sab196087 	if ((state.cur_cmd != NULL) && (str != NULL)) {
11725088Sab196087 		if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0)
11735088Sab196087 			elfedit_msg(ELFEDIT_MSG_ERR,
11745088Sab196087 			    MSG_INTL(MSG_ERR_BADOSTYLE), str);
11755088Sab196087 		state.cur_cmd->ucmd_ostyle_set = 1;
11765088Sab196087 	}
11775088Sab196087 }
11785088Sab196087 
11795088Sab196087 /*
11805088Sab196087  * This function allows the loadable modules to get the output style.
11815088Sab196087  */
11825088Sab196087 elfedit_outstyle_t
11835088Sab196087 elfedit_outstyle(void)
11845088Sab196087 {
11855088Sab196087 	/*
11865088Sab196087 	 * If there is an active  per-command output style,
11875088Sab196087 	 * return it.
11885088Sab196087 	 */
11895088Sab196087 	if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set))
11905088Sab196087 		return (state.cur_cmd->ucmd_ostyle);
11915088Sab196087 
11925088Sab196087 
11935088Sab196087 	return (state.outstyle);
11945088Sab196087 }
11955088Sab196087 
11965088Sab196087 /*
11975088Sab196087  * Return the command descriptor of the currently executing command.
11985088Sab196087  * For use only by the modules or code called by the modules.
11995088Sab196087  */
12005088Sab196087 elfeditGC_cmd_t *
12015088Sab196087 elfedit_curcmd(void)
12025088Sab196087 {
12035088Sab196087 	return (state.cur_cmd->ucmd_cmd);
12045088Sab196087 }
12055088Sab196087 
12065088Sab196087 /*
12075088Sab196087  * Build a dynamically allocated elfedit_obj_state_t struct that
12085088Sab196087  * contains a cache of the ELF file contents. This pre-chewed form
12095088Sab196087  * is fed to each command, reducing the amount of ELF boilerplate
12105088Sab196087  * code each command needs to contain.
12115088Sab196087  *
12125088Sab196087  * entry:
12135088Sab196087  *	file - Name of file to process
12145088Sab196087  *
12155088Sab196087  * exit:
12165088Sab196087  *	Fills state.elf with the necessary information for the open file.
12175088Sab196087  *
12185088Sab196087  * note: The resulting elfedit_obj_state_t is allocated from a single
12195088Sab196087  *	piece of memory, such that a single call to free() suffices
12205088Sab196087  *	to release it as well as any memory it references.
12215088Sab196087  */
12225088Sab196087 static void
12235088Sab196087 init_obj_state(const char *file)
12245088Sab196087 {
12255088Sab196087 	int	fd;
12265088Sab196087 	Elf	*elf;
12275088Sab196087 	int	open_flag;
12285088Sab196087 
12295088Sab196087 	/*
12305088Sab196087 	 * In readonly mode, we open the file readonly so that it is
12315088Sab196087 	 * impossible to modify the file by accident. This also allows
12325088Sab196087 	 * us to access readonly files, perhaps in a case where we don't
12335088Sab196087 	 * intend to change it.
12345088Sab196087 	 *
12355088Sab196087 	 * We always use ELF_C_RDWR with elf_begin(), even in a readonly
12365088Sab196087 	 * session. This allows us to modify the in-memory image, which
12375088Sab196087 	 * can be useful when examining a file, even though we don't intend
12385088Sab196087 	 * to modify the on-disk data. The file is not writable in
12395088Sab196087 	 * this case, and we don't call elf_update(), so it is safe to do so.
12405088Sab196087 	 */
12415088Sab196087 	open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR);
12425088Sab196087 	if ((fd = open(file, open_flag)) == -1) {
12435088Sab196087 		int err = errno;
12445088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE),
12455088Sab196087 		    file, strerror(err));
12465088Sab196087 	}
12475088Sab196087 	(void) elf_version(EV_CURRENT);
12485088Sab196087 	elf = elf_begin(fd, ELF_C_RDWR, NULL);
12495088Sab196087 	if (elf == NULL) {
12505088Sab196087 		(void) close(fd);
12515088Sab196087 		elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN));
12525088Sab196087 		/*NOTREACHED*/
12535088Sab196087 	}
12545088Sab196087 
12555088Sab196087 	/* We only handle standalone ELF files */
12565088Sab196087 	switch (elf_kind(elf)) {
12575088Sab196087 	case ELF_K_AR:
12585088Sab196087 		(void) close(fd);
12595088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file);
12605088Sab196087 		break;
12615088Sab196087 	case ELF_K_ELF:
12625088Sab196087 		break;
12635088Sab196087 	default:
12645088Sab196087 		(void) close(fd);
12655088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE),
12665088Sab196087 		    file);
12675088Sab196087 		break;
12685088Sab196087 	}
12695088Sab196087 
12705088Sab196087 	/*
12715088Sab196087 	 * Tell libelf that we take responsibility for object layout.
12725088Sab196087 	 * Otherwise, it will compute "proper" values for layout and
12735088Sab196087 	 * alignment fields, and these values can overwrite the values
12745088Sab196087 	 * set in the elfedit session. We are modifying existing
12755088Sab196087 	 * objects --- the layout concerns have already been dealt
12765088Sab196087 	 * with when the object was built.
12775088Sab196087 	 */
12785088Sab196087 	(void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
12795088Sab196087 
12805088Sab196087 	/* Fill in state.elf.obj_state */
12815088Sab196087 	state.elf.elfclass = gelf_getclass(elf);
12825088Sab196087 	switch (state.elf.elfclass) {
12835088Sab196087 	case ELFCLASS32:
12845088Sab196087 		elfedit32_init_obj_state(file, fd, elf);
12855088Sab196087 		break;
12865088Sab196087 	case ELFCLASS64:
12875088Sab196087 		elfedit64_init_obj_state(file, fd, elf);
12885088Sab196087 		break;
12895088Sab196087 	default:
12905088Sab196087 		(void) close(fd);
12915088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS),
12925088Sab196087 		    file);
12935088Sab196087 		break;
12945088Sab196087 	}
12955088Sab196087 }
12965088Sab196087 
12975088Sab196087 
12985892Sab196087 #ifdef DEBUG_MODULE_LIST
12995088Sab196087 /*
13005088Sab196087  * Debug routine. Dump the module list to stdout.
13015088Sab196087  */
13025088Sab196087 static void
13035088Sab196087 dbg_module_list(char *title)
13045088Sab196087 {
13055088Sab196087 	MODLIST_T *m;
13065088Sab196087 
13075088Sab196087 	printf("<MODULE LIST: %s>\n", title);
13085088Sab196087 	for (m = state.modlist; m != NULL; m = m->next) {
13095088Sab196087 		printf("Module: >%s<\n", m->mod->mod_name);
13105088Sab196087 		printf("    hdl:  %llx\n", m->dl_hdl);
13115088Sab196087 		printf("    path: >%s<\n", m->path ? m->path : "<builtin>");
13125088Sab196087 	}
13135088Sab196087 	printf("<END OF MODULE LIST>\n");
13145088Sab196087 }
13155088Sab196087 #endif
13165088Sab196087 
13175088Sab196087 
13185088Sab196087 /*
13195088Sab196087  * Search the module list for the named module.
13205088Sab196087  *
13215088Sab196087  * entry:
13225088Sab196087  *	name - Name of module to find
13235088Sab196087  *	insdef - Address of variable to receive address of predecessor
13245088Sab196087  *		node to the desired one.
13255088Sab196087  *
13265088Sab196087  * exit:
13275088Sab196087  *	If the module is it is found, this routine returns the pointer to
13285088Sab196087  *	its MODLIST_T structure. *insdef references the predecessor node, or
13295088Sab196087  *	is NULL if the found item is at the head of the list.
13305088Sab196087  *
13315088Sab196087  *	If the module is not found, NULL is returned. *insdef references
13325088Sab196087  *	the predecessor node of the position where an entry for this module
13335088Sab196087  *	would be placed, or NULL if it would go at the beginning.
13345088Sab196087  */
13355088Sab196087 static MODLIST_T *
13365088Sab196087 module_loaded(const char *name, MODLIST_T **insdef)
13375088Sab196087 {
13385088Sab196087 	MODLIST_T	*moddef;
13395088Sab196087 	int		cmp;
13405088Sab196087 
13415088Sab196087 	*insdef = NULL;
13425088Sab196087 	moddef = state.modlist;
13435088Sab196087 	if (moddef != NULL) {
13445088Sab196087 		cmp = strcasecmp(name, moddef->ml_mod->mod_name);
13455088Sab196087 		if (cmp == 0) {		/* Desired module is first in list */
13465088Sab196087 			return (moddef);
13475088Sab196087 		} else if (cmp > 0) {	/* cmp > 0: Insert in middle/end */
13485088Sab196087 			*insdef = moddef;
13495088Sab196087 			moddef = moddef->ml_next;
13505088Sab196087 			cmp = -1;
13515088Sab196087 			while (moddef && (cmp < 0)) {
13525088Sab196087 				cmp = strcasecmp(moddef->ml_mod->mod_name,
13535088Sab196087 				    name);
13545088Sab196087 				if (cmp == 0)
13555088Sab196087 					return (moddef);
13565088Sab196087 				if (cmp < 0) {
13575088Sab196087 					*insdef = moddef;
13585088Sab196087 					moddef = (*insdef)->ml_next;
13595088Sab196087 				}
13605088Sab196087 			}
13615088Sab196087 		}
13625088Sab196087 	}
13635088Sab196087 
13645088Sab196087 	return (NULL);
13655088Sab196087 }
13665088Sab196087 
13675088Sab196087 
13685088Sab196087 /*
13695088Sab196087  * Determine if a file is a sharable object based on its file path.
13705088Sab196087  * If path ends in a .so, followed optionally by a period and 1 or more
13715088Sab196087  * digits, we say that it is and return a pointer to the first character
13725088Sab196087  * of the suffix. Otherwise NULL is returned.
13735088Sab196087  */
13745088Sab196087 static const char *
13755088Sab196087 path_is_so(const char *path)
13765088Sab196087 {
13775088Sab196087 	int		dotso_len;
13785088Sab196087 	const char	*tail;
13795088Sab196087 	size_t		len;
13805088Sab196087 
13815088Sab196087 	len = strlen(path);
13825088Sab196087 	if (len == 0)
13835088Sab196087 		return (NULL);
13845088Sab196087 	tail = path + len;
13855088Sab196087 	if (isdigit(*(tail - 1))) {
13865088Sab196087 		while ((tail > path) && isdigit(*(tail - 1)))
13875088Sab196087 			tail--;
13885088Sab196087 		if ((tail <= path) || (*tail != '.'))
13895088Sab196087 			return (NULL);
13905088Sab196087 	}
13915088Sab196087 	dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO));
13925088Sab196087 	if ((tail - path) < dotso_len)
13935088Sab196087 		return (NULL);
13945088Sab196087 	tail -= dotso_len;
13955088Sab196087 	if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0)
13965088Sab196087 		return (tail);
13975088Sab196087 
13985088Sab196087 	return (NULL);
13995088Sab196087 }
14005088Sab196087 
14015088Sab196087 
14025088Sab196087 /*
14035088Sab196087  * Locate the start of the unsuffixed file name within path. Returns pointer
14045088Sab196087  * to first character of that name in path.
14055088Sab196087  *
14065088Sab196087  * entry:
14075088Sab196087  *	path - Path to be examined.
14085088Sab196087  *	tail - NULL, or pointer to position at tail of path from which
14095088Sab196087  *		the search for '/' characters should start. If NULL,
14105088Sab196087  *		strlen() is used to locate the end of the string.
14115088Sab196087  *	buf - NULL, or buffer to receive a copy of the characters that
14125088Sab196087  *		lie between the start of the filename and tail.
14135088Sab196087  *	bufsize - sizeof(buf)
14145088Sab196087  *
14155088Sab196087  * exit:
14165088Sab196087  *	The pointer to the first character of the unsuffixed file name
14175088Sab196087  *	within path is returned. If buf is non-NULL, the characters
14185088Sab196087  *	lying between that point and tail (or the end of path if tail
14195088Sab196087  *	is NULL) are copied into buf.
14205088Sab196087  */
14215088Sab196087 static const char *
14225088Sab196087 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz)
14235088Sab196087 {
14245088Sab196087 	const char 	*s;
14255088Sab196087 
14265088Sab196087 	if (tail == NULL)
14275088Sab196087 		tail = path + strlen(path);
14285088Sab196087 	s = tail;
14295088Sab196087 	while ((s > path) && (*(s - 1) != '/'))
14305088Sab196087 		s--;
14315088Sab196087 	if (buf != NULL)
14325088Sab196087 		elfedit_strnbcpy(buf, s, tail - s, bufsiz);
14335088Sab196087 	return (s);
14345088Sab196087 }
14355088Sab196087 
14365088Sab196087 
14375088Sab196087 /*
14385088Sab196087  * Issue an error on behalf of load_module(), taking care to release
14395088Sab196087  * resources that routine may have aquired:
14405088Sab196087  *
14415088Sab196087  * entry:
14425088Sab196087  *	moddef - NULL, or a module definition to be released via free()
14435088Sab196087  *	dl_hdl - NULL, or a handle to a sharable object to release via
14445088Sab196087  *		dlclose().
14455088Sab196087  *	dl_path - If dl_hdl is non-NULL, the path to the sharable object
14465088Sab196087  *		file that was loaded.
14475088Sab196087  *	format - A format string to pass to elfedit_msg(), containing
14485088Sab196087  *		no more than (3) %s format codes, and no other format codes.
14495088Sab196087  *	[s1-s4] - Strings to pass to elfedit_msg() to satisfy the four
14505088Sab196087  *		allowed %s codes in format. Should be set to NULL if the
14515088Sab196087  *		format string does not need them.
14525088Sab196087  *
14535088Sab196087  * note:
14545088Sab196087  *	This routine makes a copy of the s1-s4 strings before freeing any
14555088Sab196087  *	memory or unmapping the sharable library. It is therefore safe to
14565088Sab196087  *	use strings from moddef, or from the sharable library (which will
14575088Sab196087  *	be unmapped) to satisfy the other arguments s1-s4.
14585088Sab196087  */
14595088Sab196087 static void
14605088Sab196087 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path,
14615088Sab196087     const char *format, const char *s1, const char *s2, const char *s3,
14625088Sab196087     const char *s4)
14635088Sab196087 {
14645088Sab196087 #define	SCRBUFSIZE (PATH_MAX + 256)   /* A path, plus some extra */
14655088Sab196087 
14665088Sab196087 	char s1_buf[SCRBUFSIZE];
14675088Sab196087 	char s2_buf[SCRBUFSIZE];
14685088Sab196087 	char s3_buf[SCRBUFSIZE];
14695088Sab196087 	char s4_buf[SCRBUFSIZE];
14705088Sab196087 
14715088Sab196087 	/*
14725088Sab196087 	 * The caller may provide strings for s1-s3 that are from
14735088Sab196087 	 * moddef. If we free moddef, the printf() will die on access
14745088Sab196087 	 * to free memory. We could push back on the user and force
14755088Sab196087 	 * each call to carefully make copies of such data. However, this
14765088Sab196087 	 * is an easy case to miss. Furthermore, this is an error case,
14775088Sab196087 	 * and machine efficiency is not the main issue. We therefore make
14785088Sab196087 	 * copies of the s1-s3 strings here into auto variables, and then
14795088Sab196087 	 * use those copies. The user is freed from worrying about it.
14805088Sab196087 	 *
14815088Sab196087 	 * We use oversized stack based buffers instead of malloc() to
14825088Sab196087 	 * reduce the number of ways that things can go wrong while
14835088Sab196087 	 * reporting the error.
14845088Sab196087 	 */
14855088Sab196087 	if (s1 != NULL)
14865088Sab196087 		(void) strlcpy(s1_buf, s1, sizeof (s1_buf));
14875088Sab196087 	if (s2 != NULL)
14885088Sab196087 		(void) strlcpy(s2_buf, s2, sizeof (s2_buf));
14895088Sab196087 	if (s3 != NULL)
14905088Sab196087 		(void) strlcpy(s3_buf, s3, sizeof (s3_buf));
14915088Sab196087 	if (s4 != NULL)
14925088Sab196087 		(void) strlcpy(s4_buf, s4, sizeof (s4_buf));
14935088Sab196087 
14945088Sab196087 
14955088Sab196087 	if (moddef != NULL)
14965088Sab196087 		free(moddef);
14975088Sab196087 
14985088Sab196087 	if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0))
14995088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
15005088Sab196087 		    dl_path, dlerror());
15015088Sab196087 
15025088Sab196087 	elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf);
15035088Sab196087 #undef	SCRBUFSIZE
15045088Sab196087 }
15055088Sab196087 
15065088Sab196087 
15075088Sab196087 /*
15085088Sab196087  * Load a module sharable object for load_module().
15095088Sab196087  *
15105088Sab196087  * entry:
15115088Sab196087  *	path - Path of file to open
15125088Sab196087  *	moddef - If this function issues a non-returning error, it will
15135088Sab196087  *		first return the memory referenced by moddef. This argument
15145088Sab196087  *		is not used otherwise.
15155088Sab196087  *	must_exist - If True, we consider it to be an error if the file given
15165088Sab196087  *		by path does not exist. If False, no error is issued
15175088Sab196087  *		and a NULL value is quietly returned.
15185088Sab196087  *
15195088Sab196087  * exit:
15205088Sab196087  *	Returns a handle to the loaded object on success, or NULL if no
15215088Sab196087  *	file was loaded.
15225088Sab196087  */
15235088Sab196087 static void *
15245088Sab196087 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist)
15255088Sab196087 {
15265088Sab196087 	int	fd;
15275088Sab196087 	void	*hdl;
15285088Sab196087 
15295088Sab196087 	/*
15305088Sab196087 	 * If the file is not required to exist, and it doesn't, then
15315088Sab196087 	 * we want to quietly return without an error.
15325088Sab196087 	 */
15335088Sab196087 	if (!must_exist) {
15345088Sab196087 		fd = open(path, O_RDONLY);
15355088Sab196087 		if (fd >= 0) {
15365088Sab196087 			(void) close(fd);
15375088Sab196087 		} else if (errno == ENOENT) {
15385088Sab196087 			return (NULL);
15395088Sab196087 		}
15405088Sab196087 	}
15415088Sab196087 
15425088Sab196087 	if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL)
15435088Sab196087 		load_module_err(moddef, NULL, NULL,
15445088Sab196087 		    MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL);
15455088Sab196087 
15465088Sab196087 	return (hdl);
15475088Sab196087 }
15485088Sab196087 
15495088Sab196087 
15505088Sab196087 /*
15515088Sab196087  * Sanity check option arguments to prevent common errors. The rest of
15525088Sab196087  * elfedit assumes these tests have been done, and does not check
15535088Sab196087  * again.
15545088Sab196087  */
15555088Sab196087 static void
15565088Sab196087 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef,
15575088Sab196087     const char *mod_name, const char *cmd_name,
15585088Sab196087     void *dl_hdl, const char *dl_path)
15595088Sab196087 {
15605088Sab196087 #define	FAIL(_msg) errmsg = _msg; goto fail
15615088Sab196087 
15625088Sab196087 	Msg errmsg;
15635088Sab196087 	elfedit_cmd_oa_mask_t	optmask = 0;
15645088Sab196087 
15655088Sab196087 	for (; optarg->oa_name != NULL; optarg++) {
15665088Sab196087 		/*
15675088Sab196087 		 * If ELFEDIT_CMDOA_F_INHERIT is set:
15685088Sab196087 		 *	- oa_name must be a value in the range of
15695088Sab196087 		 *		known ELFEDIT_STDOA_ values.
15705088Sab196087 		 *	- oa_help must be NULL
15715088Sab196087 		 *	- ELFEDIT_CMDOA_F_INHERIT must be the only flag set
15725088Sab196087 		 */
15735088Sab196087 		if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
15745088Sab196087 			if ((((uintptr_t)optarg->oa_name) >
15755088Sab196087 			    ELFEDIT_NUM_STDOA) ||
15765088Sab196087 			    (optarg->oa_help != 0) ||
15775088Sab196087 			    (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT))
15785088Sab196087 				/*
15795088Sab196087 				 * Can't use FAIL --- oa_name is not a valid
15805088Sab196087 				 * string, and load_module_err() looks at args.
15815088Sab196087 				 */
15825088Sab196087 				load_module_err(moddef, dl_hdl, dl_path,
15835088Sab196087 				    MSG_INTL(MSG_ERR_BADSTDOA), dl_path,
15845088Sab196087 				    mod_name, cmd_name, NULL);
15855088Sab196087 			continue;
15865088Sab196087 		}
15875088Sab196087 
15885088Sab196087 		if (isopt) {
15895088Sab196087 			/*
15905088Sab196087 			 * Option name must start with a '-', and must
15915088Sab196087 			 * have at one following character.
15925088Sab196087 			 */
15935088Sab196087 			if (optarg->oa_name[0] != '-') {
15945088Sab196087 				/* MSG_INTL(MSG_ERR_OPT_MODPRE) */
15955088Sab196087 				FAIL(MSG_ERR_OPT_MODPRE);
15965088Sab196087 			}
15975088Sab196087 			if (optarg->oa_name[1] == '\0') {
15985088Sab196087 				/* MSG_INTL(MSG_ERR_OPT_MODLEN) */
15995088Sab196087 				FAIL(MSG_ERR_OPT_MODLEN);
16005088Sab196087 			}
16015088Sab196087 
16025088Sab196087 			/*
16035088Sab196087 			 * oa_idmask must be 0, or it must have a single
16045088Sab196087 			 * bit set (a power of 2).oa_excmask must be 0
16055088Sab196087 			 * if oa_idmask is 0
16065088Sab196087 			 */
16075088Sab196087 			if (optarg->oa_idmask == 0) {
16085088Sab196087 				if (optarg->oa_excmask != 0) {
16095088Sab196087 					/* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */
16105088Sab196087 					FAIL(MSG_ERR_OPT_EXCMASKN0);
16115088Sab196087 				}
16125088Sab196087 			} else {
16135088Sab196087 				if (elfedit_bits_set(optarg->oa_idmask,
16145088Sab196087 				    sizeof (optarg->oa_idmask)) != 1) {
16155088Sab196087 					/* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */
16165088Sab196087 					FAIL(MSG_ERR_OPT_IDMASKPOW2);
16175088Sab196087 				}
16185088Sab196087 
16195088Sab196087 				/* Non-zero idmask must be unique */
16205088Sab196087 				if ((optarg->oa_idmask & optmask) != 0) {
16215088Sab196087 					/* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */
16225088Sab196087 					FAIL(MSG_ERR_OPT_IDMASKUNIQ);
16235088Sab196087 				}
16245088Sab196087 
16255088Sab196087 				/* Add this one to the overall mask */
16265088Sab196087 				optmask |= optarg->oa_idmask;
16275088Sab196087 			}
16285088Sab196087 		} else {
16295088Sab196087 			/*
16305088Sab196087 			 * Argument name cannot start with a'-', and must
16315088Sab196087 			 * not be a null string.
16325088Sab196087 			 */
16335088Sab196087 			if (optarg->oa_name[0] == '-') {
16345088Sab196087 				/* MSG_INTL(MSG_ERR_ARG_MODPRE) */
16355088Sab196087 				FAIL(MSG_ERR_ARG_MODPRE);
16365088Sab196087 			}
16375088Sab196087 			if (optarg->oa_name[1] == '\0') {
16385088Sab196087 				/* MSG_INTL(MSG_ERR_ARG_MODLEN) */
16395088Sab196087 				FAIL(MSG_ERR_ARG_MODLEN);
16405088Sab196087 			}
16415088Sab196087 
16425088Sab196087 
16435088Sab196087 			/* oa_idmask and oa_excmask must both be 0 */
16445088Sab196087 			if ((optarg->oa_idmask != 0) ||
16455088Sab196087 			    (optarg->oa_excmask != 0)) {
16465088Sab196087 				/* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */
16475088Sab196087 				FAIL(MSG_ERR_ARG_MASKNOT0);
16485088Sab196087 			}
16495088Sab196087 
16505088Sab196087 		}
16515088Sab196087 
16525088Sab196087 		/*
16535088Sab196087 		 * If it takes a value, make sure that we are
16545088Sab196087 		 * processing options, because CMDOA_F_VALUE is not
16555088Sab196087 		 * allowed for plain arguments. Then check the following
16565088Sab196087 		 * item in the list:
16575088Sab196087 		 *	- There must be a following item.
16585088Sab196087 		 *	- oa_name must be non-NULL. This is the only field
16595088Sab196087 		 *		that is used by elfedit.
16605088Sab196087 		 *	- oa_help, oa_flags, oa_idmask, and oa_excmask
16615088Sab196087 		 *		must be 0.
16625088Sab196087 		 */
16635088Sab196087 		if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) {
16645088Sab196087 			elfedit_cmd_optarg_t *oa1 = optarg + 1;
16655088Sab196087 
16665088Sab196087 			if (!isopt) {
16675088Sab196087 				/* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */
16685088Sab196087 				FAIL(MSG_ERR_ARG_CMDOA_VAL);
16695088Sab196087 			}
16705088Sab196087 
16715088Sab196087 			if ((optarg + 1)->oa_name == NULL) {
16725088Sab196087 				/* MSG_INTL(MSG_ERR_BADMODOPTVAL) */
16735088Sab196087 				FAIL(MSG_ERR_BADMODOPTVAL);
16745088Sab196087 			}
16755088Sab196087 
16765088Sab196087 			if (oa1->oa_name == NULL) {
16775088Sab196087 				/* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */
16785088Sab196087 				FAIL(MSG_ERR_CMDOA_VALNAM);
16795088Sab196087 			}
16805088Sab196087 			if ((oa1->oa_help != NULL) || (oa1->oa_flags != 0) ||
16815088Sab196087 			    (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) {
16825088Sab196087 				/* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */
16835088Sab196087 				FAIL(MSG_ERR_CMDOA_VALNOT0);
16845088Sab196087 			}
16855088Sab196087 			optarg++;
16865088Sab196087 		}
16875088Sab196087 	}
16885088Sab196087 
16895088Sab196087 
16905088Sab196087 	return;
16915088Sab196087 
16925088Sab196087 fail:
16935088Sab196087 	load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg),
16945088Sab196087 	    dl_path, mod_name, cmd_name, optarg->oa_name);
16955088Sab196087 }
16965088Sab196087 
16975088Sab196087 /*
16985088Sab196087  * Look up the specified module, loading the module if necessary,
16995088Sab196087  * and return its definition, or NULL on failure.
17005088Sab196087  *
17015088Sab196087  * entry:
17025088Sab196087  *	name - Name of module to load. If name contains a '/' character or has
17035088Sab196087  *		a ".so" suffix, then it is taken to be an absolute file path,
17045088Sab196087  *		and is used directly as is. If name does not contain a '/'
17055088Sab196087  *		character, then we look for it against the locations in
17065088Sab196087  *		the module path, addint the '.so' suffix, and taking the first
17075088Sab196087  *		one we find.
17085088Sab196087  *	must_exist - If True, we consider it to be an error if we are unable
17095088Sab196087  *		to locate a file to load and the module does not already exist.
17105088Sab196087  *		If False, NULL is returned quietly in this case.
17115088Sab196087  *	allow_abs - True if absolute paths are allowed. False to disallow
17125088Sab196087  *		them.
17135088Sab196087  *
17145088Sab196087  * note:
17155088Sab196087  *	If the path is absolute, then we load the file and take the module
17165088Sab196087  *	name from the data returned by its elfedit_init() function. If a
17175088Sab196087  *	module of that name is already loaded, it is unloaded and replaced
17185088Sab196087  *	with the new one.
17195088Sab196087  *
17205088Sab196087  *	If the path is non absolute, then we check to see if the module has
17215088Sab196087  *	already been loaded, and if so, we return that module definition.
17225088Sab196087  *	In this case, nothing new is loaded. If the module has not been loaded,
17235088Sab196087  *	we search the path for it and load it. If the module name provided
17245088Sab196087  *	by the elfedit_init() function does not match the name of the file,
17255088Sab196087  *	an error results.
17265088Sab196087  */
17275088Sab196087 elfeditGC_module_t *
17285088Sab196087 elfedit_load_module(const char *name, int must_exist, int allow_abs)
17295088Sab196087 {
17305088Sab196087 	elfedit_init_func_t	*init_func;
17315088Sab196087 	elfeditGC_module_t	*mod;
17325088Sab196087 	MODLIST_T		*moddef, *insdef;
17335088Sab196087 	const char		*path;
17345088Sab196087 	char			path_buf[PATH_MAX + 1];
17355088Sab196087 	void			*hdl;
17365088Sab196087 	size_t			i;
17375088Sab196087 	int			is_abs_path;
17385088Sab196087 	elfeditGC_cmd_t		*cmd;
17395088Sab196087 
17405088Sab196087 	/*
17415088Sab196087 	 * If the name includes a .so suffix, or has any '/' characters,
17425088Sab196087 	 * then it is an absolute path that we use as is to load the named
17435088Sab196087 	 * file. Otherwise, we iterate over the path, adding the .so suffix
17445088Sab196087 	 * and load the first file that matches.
17455088Sab196087 	 */
17465088Sab196087 	is_abs_path = (path_is_so(name) != NULL) ||
17475088Sab196087 	    (name != elfedit_basename(name, NULL, NULL, 0));
17485088Sab196087 
17495088Sab196087 	if (is_abs_path && !allow_abs)
17505088Sab196087 		load_module_err(NULL, NULL, NULL,
17515088Sab196087 		    MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
17525088Sab196087 
17535088Sab196087 	/*
17545088Sab196087 	 * If this is a non-absolute path, search for the module already
17555088Sab196087 	 * having been loaded, and return it if so.
17565088Sab196087 	 */
17575088Sab196087 	if (!is_abs_path) {
17585088Sab196087 		moddef = module_loaded(name, &insdef);
17595088Sab196087 		if (moddef != NULL)
17605088Sab196087 			return (moddef->ml_mod);
17615088Sab196087 		/*
17625088Sab196087 		 * As a result of module_loaded(), insdef now contains the
17635088Sab196087 		 * immediate predecessor node for the new one, or NULL if
17645088Sab196087 		 * it goes at the front. In the absolute-path case, we take
17655088Sab196087 		 * care of this below, after the sharable object is loaded.
17665088Sab196087 		 */
17675088Sab196087 	}
17685088Sab196087 
17695088Sab196087 	/*
17705088Sab196087 	 * malloc() a module definition block before trying to dlopen().
17715088Sab196087 	 * Doing things in the other order can cause the dlopen()'d object
17725088Sab196087 	 * to leak: If elfedit_malloc() fails, it can cause a jump to the
17735088Sab196087 	 * outer command loop without returning to the caller. Hence,
17745088Sab196087 	 * there will be no opportunity to clean up. Allocaing the module
17755088Sab196087 	 * first allows us to free it if necessary.
17765088Sab196087 	 */
17775088Sab196087 	moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF),
17785088Sab196087 	    sizeof (*moddef) + PATH_MAX + 1);
17795088Sab196087 	moddef->ml_path = ((char *)moddef) + sizeof (*moddef);
17805088Sab196087 
17815088Sab196087 	if (is_abs_path) {
17825088Sab196087 		path = name;
17835088Sab196087 		hdl = load_module_dlopen(name, moddef, must_exist);
17845088Sab196087 	} else {
17855088Sab196087 		hdl = NULL;
17865088Sab196087 		path = path_buf;
17875088Sab196087 		for (i = 0; i < state.modpath.n; i++) {
17885088Sab196087 			if (snprintf(path_buf, sizeof (path_buf),
17895088Sab196087 			    MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i],
17905088Sab196087 			    name) > sizeof (path_buf))
17915088Sab196087 				load_module_err(moddef, NULL, NULL,
17925088Sab196087 				    MSG_INTL(MSG_ERR_PATHTOOLONG),
17935088Sab196087 				    state.modpath.seg[i], name, NULL, NULL);
17945088Sab196087 			hdl = load_module_dlopen(path, moddef, 0);
17955088Sab196087 		}
17965088Sab196087 		if (must_exist && (hdl == NULL))
17975088Sab196087 			load_module_err(moddef, NULL, NULL,
17985088Sab196087 			    MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
17995088Sab196087 	}
18005088Sab196087 
18015088Sab196087 	if (hdl == NULL) {
18025088Sab196087 		free(moddef);
18035088Sab196087 		return (NULL);
18045088Sab196087 	}
18055088Sab196087 
18065088Sab196087 	if (state.elf.elfclass == ELFCLASS32) {
18075088Sab196087 		init_func = (elfedit_init_func_t *)
18085088Sab196087 		    dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32));
18095088Sab196087 	} else {
18105088Sab196087 		init_func = (elfedit_init_func_t *)
18115088Sab196087 		    dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64));
18125088Sab196087 	}
18135088Sab196087 	if (init_func == NULL)
18145088Sab196087 		load_module_err(moddef, hdl, path,
18155088Sab196087 		    MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL);
18165088Sab196087 
18175088Sab196087 	/*
18185088Sab196087 	 * Note that the init function will be passing us an
18195088Sab196087 	 * elfedit[32|64]_module_t pointer, which we cast to the
18205088Sab196087 	 * generic module pointer type in order to be able to manage
18215088Sab196087 	 * either type with one set of code.
18225088Sab196087 	 */
18235088Sab196087 	if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT)))
18245088Sab196087 		load_module_err(moddef, hdl, path,
18255088Sab196087 		    MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL);
18265088Sab196087 
18275088Sab196087 	/*
18285088Sab196087 	 * Enforce some rules, to help module developers:
18295088Sab196087 	 *	- The primary name of a command must not be
18305088Sab196087 	 *		the empty string ("").
18315088Sab196087 	 *	- Options must start with a '-' followed by at least
18325088Sab196087 	 *		one character.
18335088Sab196087 	 *	- Arguments and options must be well formed.
18345088Sab196087 	 */
18355088Sab196087 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
18365088Sab196087 		if (**cmd->cmd_name == '\0')
18375088Sab196087 			load_module_err(moddef, hdl, path,
18385088Sab196087 			    MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name,
18395088Sab196087 			    NULL, NULL, NULL);
18405088Sab196087 
18415088Sab196087 		if (cmd->cmd_args != NULL)
18425088Sab196087 			validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name,
18435088Sab196087 			    cmd->cmd_name[0], hdl, path);
18445088Sab196087 		if (cmd->cmd_opt != NULL)
18455088Sab196087 			validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name,
18465088Sab196087 			    cmd->cmd_name[0], hdl, path);
18475088Sab196087 	}
18485088Sab196087 
18495088Sab196087 	/*
18505088Sab196087 	 * Check the name the module provides. How we handle this depends
18515088Sab196087 	 * on whether the path is absolute or the result of a path search.
18525088Sab196087 	 */
18535088Sab196087 	if (is_abs_path) {
18545088Sab196087 		MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef);
18555088Sab196087 
18565088Sab196087 		if (old_moddef != NULL) {	/* Replace existing */
18575088Sab196087 			free(moddef);		/* Rare case: Don't need it */
18585088Sab196087 			/*
18595088Sab196087 			 * Be sure we don't unload builtin modules!
18605088Sab196087 			 * These have a NULL dl_hdl field.
18615088Sab196087 			 */
18625088Sab196087 			if (old_moddef->ml_dl_hdl == NULL)
18635088Sab196087 				load_module_err(NULL, hdl, path,
18645088Sab196087 				    MSG_INTL(MSG_ERR_CNTULSMOD),
18655088Sab196087 				    old_moddef->ml_mod->mod_name, NULL,
18665088Sab196087 				    NULL, NULL);
18675088Sab196087 
18685088Sab196087 			/* Unload existing */
18695088Sab196087 			if (dlclose(old_moddef->ml_dl_hdl) != 0)
18705088Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
18715088Sab196087 				    MSG_INTL(MSG_ERR_CNTDLCLOSE),
18725088Sab196087 				    old_moddef->ml_path, dlerror());
18735088Sab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
18745088Sab196087 			    MSG_INTL(MSG_DEBUG_MODUNLOAD),
18755088Sab196087 			    old_moddef->ml_mod->mod_name, old_moddef->ml_path);
18765088Sab196087 			old_moddef->ml_mod = mod;
18775088Sab196087 			old_moddef->ml_dl_hdl = hdl;
18785088Sab196087 			(void) strlcpy((char *)old_moddef->ml_path, path,
18795088Sab196087 			    PATH_MAX + 1);
18805088Sab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
18815088Sab196087 			    MSG_INTL(MSG_DEBUG_MODLOAD),
18825088Sab196087 			    old_moddef->ml_mod->mod_name, path);
18835088Sab196087 			return (old_moddef->ml_mod);
18845088Sab196087 		}
18855088Sab196087 		/*
18865088Sab196087 		 * insdef now contains the insertion point for the absolute
18875088Sab196087 		 * path case.
18885088Sab196087 		 */
18895088Sab196087 	} else {
18905088Sab196087 		/* If the names don't match, then error */
18915088Sab196087 		if (strcasecmp(name, mod->mod_name) != 0)
18925088Sab196087 			load_module_err(moddef, hdl, path,
18935088Sab196087 			    MSG_INTL(MSG_ERR_BADMODNAME),
18945088Sab196087 			    mod->mod_name, name, path, NULL);
18955088Sab196087 	}
18965088Sab196087 
18975088Sab196087 	/*
18985088Sab196087 	 * Link module into the module list. If insdef is NULL,
18995088Sab196087 	 * it goes at the head. If insdef is non-NULL, it goes immediately
19005088Sab196087 	 * after
19015088Sab196087 	 */
19025088Sab196087 	if (insdef == NULL) {
19035088Sab196087 		moddef->ml_next = state.modlist;
19045088Sab196087 		state.modlist = moddef;
19055088Sab196087 	} else {
19065088Sab196087 		moddef->ml_next = insdef->ml_next;
19075088Sab196087 		insdef->ml_next = moddef;
19085088Sab196087 	}
19095088Sab196087 	moddef->ml_mod = mod;
19105088Sab196087 	moddef->ml_dl_hdl = hdl;
19115088Sab196087 	(void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1);
19125088Sab196087 
19135088Sab196087 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD),
19145088Sab196087 	    moddef->ml_mod->mod_name, path);
19155088Sab196087 
19165088Sab196087 	return (moddef->ml_mod);
19175088Sab196087 }
19185088Sab196087 
19195088Sab196087 
19205088Sab196087 /*
19215088Sab196087  * Unload the specified module
19225088Sab196087  */
19235088Sab196087 void
19245088Sab196087 elfedit_unload_module(const char *name)
19255088Sab196087 {
19265088Sab196087 	MODLIST_T	*moddef, *insdef;
19275088Sab196087 
19285088Sab196087 	moddef = module_loaded(name, &insdef);
19295088Sab196087 	if (moddef == NULL)
19305088Sab196087 		return;
19315088Sab196087 
19325088Sab196087 	/* Built in modules cannot be unloaded. They have a NULL dl_hdl field */
19335088Sab196087 	if (moddef->ml_dl_hdl == NULL)
19345088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD),
19355088Sab196087 		    moddef->ml_mod->mod_name);
19365088Sab196087 
19375088Sab196087 	/*
19385088Sab196087 	 * When we unload it, the name string goes with it. So
19395088Sab196087 	 * announce it while we still can without having to make a copy.
19405088Sab196087 	 */
19415088Sab196087 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD),
19425088Sab196087 	    moddef->ml_mod->mod_name, moddef->ml_path);
19435088Sab196087 
19445088Sab196087 	/*
19455088Sab196087 	 * Close it before going further. On failure, we'll jump, and the
19465088Sab196087 	 * record will remain in the module list. On success,
19475088Sab196087 	 * we'll retain control, and can safely remove it.
19485088Sab196087 	 */
19495088Sab196087 	if (dlclose(moddef->ml_dl_hdl) != 0)
19505088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
19515088Sab196087 		    moddef->ml_path, dlerror());
19525088Sab196087 
19535088Sab196087 	/* Unlink the record from the module list */
19545088Sab196087 	if (insdef == NULL)
19555088Sab196087 		state.modlist = moddef->ml_next;
19565088Sab196087 	else
19575088Sab196087 		insdef->ml_next = moddef->ml_next;
19585088Sab196087 
19595088Sab196087 	/* Release the memory */
19605088Sab196087 	free(moddef);
19615088Sab196087 }
19625088Sab196087 
19635088Sab196087 
19645088Sab196087 /*
19655088Sab196087  * Load all sharable objects found in the specified directory.
19665088Sab196087  *
19675088Sab196087  * entry:
19685088Sab196087  *	dirpath - Path of directory to process.
19695088Sab196087  *	must_exist - If True, it is an error if diropen() fails to open
19705088Sab196087  *		the given directory. Of False, we quietly ignore it and return.
19715088Sab196087  *	abs_path - If True, files are loaded using their literal paths.
19725088Sab196087  *		If False, their module name is extracted from the dirpath
19735088Sab196087  *		and a path based search is used to locate it.
19745088Sab196087  */
19755088Sab196087 void
19765088Sab196087 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path)
19775088Sab196087 {
19785088Sab196087 	char		path[PATH_MAX + 1];
19795088Sab196087 	DIR		*dir;
19805088Sab196087 	struct dirent	*dp;
19815088Sab196087 	const char 	*tail;
19825088Sab196087 
19835088Sab196087 	dir = opendir(dirpath);
19845088Sab196087 	if (dir == NULL) {
19855088Sab196087 		int err = errno;
19865088Sab196087 
19875088Sab196087 		if (!must_exist && (err == ENOENT))
19885088Sab196087 			return;
19895088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR),
19905088Sab196087 		    dirpath, strerror(err));
19915088Sab196087 		/*NOTREACHED*/
19925088Sab196087 	}
19935088Sab196087 
19945088Sab196087 	while (dp = readdir(dir)) {
19955088Sab196087 		if ((tail = path_is_so(dp->d_name)) != NULL) {
19965088Sab196087 			if (abs_path) {
19975088Sab196087 				(void) snprintf(path, sizeof (path),
19985088Sab196087 				    MSG_ORIG(MSG_FMT_BLDPATH), dirpath,
19995088Sab196087 				    dp->d_name);
20005088Sab196087 			} else {
20015088Sab196087 				(void) elfedit_basename(dp->d_name, tail,
20025088Sab196087 				    path, sizeof (path));
20035088Sab196087 			}
20045088Sab196087 			(void) elfedit_load_module(path, must_exist, 1);
20055088Sab196087 		}
20065088Sab196087 	}
20075088Sab196087 	(void) closedir(dir);
20085088Sab196087 }
20095088Sab196087 
20105088Sab196087 
20115088Sab196087 /*
20125088Sab196087  * Follow the module load path, and load the first module found for each
20135088Sab196087  * given name.
20145088Sab196087  */
20155088Sab196087 void
20165088Sab196087 elfedit_load_modpath(void)
20175088Sab196087 {
20185088Sab196087 	size_t		i;
20195088Sab196087 
20205088Sab196087 	for (i = 0; i < state.modpath.n; i++)
20215088Sab196087 		elfedit_load_moddir(state.modpath.seg[i], 0, 0);
20225088Sab196087 }
20235088Sab196087 
20245088Sab196087 /*
20255088Sab196087  * Given a module definition, look for the specified command.
20265088Sab196087  * Returns the command if found, and NULL otherwise.
20275088Sab196087  */
20285088Sab196087 static elfeditGC_cmd_t *
20295088Sab196087 find_cmd(elfeditGC_module_t *mod, const char *name)
20305088Sab196087 {
20315088Sab196087 	elfeditGC_cmd_t *cmd;
20325088Sab196087 	const char **cmd_name;
20335088Sab196087 
20345088Sab196087 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
20355088Sab196087 		for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
20365088Sab196087 			if (strcasecmp(name, *cmd_name) == 0) {
20375088Sab196087 				if (cmd_name != cmd->cmd_name)
20385088Sab196087 					elfedit_msg(ELFEDIT_MSG_DEBUG,
20395088Sab196087 					    MSG_INTL(MSG_DEBUG_CMDALIAS),
20405088Sab196087 					    mod->mod_name, *cmd_name,
20415088Sab196087 					    mod->mod_name, *cmd->cmd_name);
20425088Sab196087 				return (cmd);
20435088Sab196087 			}
20445088Sab196087 
20455088Sab196087 	return (NULL);
20465088Sab196087 }
20475088Sab196087 
20485088Sab196087 
20495088Sab196087 /*
20505088Sab196087  * Given a command name, return its command definition.
20515088Sab196087  *
20525088Sab196087  * entry:
20535088Sab196087  *	name - Command to be looked up
20545088Sab196087  *	must_exist - If True, we consider it to be an error if the command
20555088Sab196087  *		does not exist. If False, NULL is returned quietly in
20565088Sab196087  *		this case.
20575088Sab196087  *	mod_ret - NULL, or address of a variable to receive the
20585088Sab196087  *		module definition block of the module containing
20595088Sab196087  *		the command.
20605088Sab196087  *
20615088Sab196087  * exit:
20625088Sab196087  *	On success, returns a pointer to the command definition, and
20635088Sab196087  *	if mod_ret is non-NULL, *mod_ret receives a pointer to the
20645088Sab196087  *	module definition. On failure, must_exist determines the
20655088Sab196087  *	action taken: If must_exist is True, an error is issued and
20665088Sab196087  *	control does not return to the caller. If must_exist is False,
20675088Sab196087  *	NULL is quietly returned.
20685088Sab196087  *
20695088Sab196087  * note:
20705088Sab196087  *	A ':' in name is used to delimit the module and command names.
20715088Sab196087  *	If it is omitted, or if it is the first non-whitespace character
20725088Sab196087  *	in the name, then the built in sys: module is implied.
20735088Sab196087  */
20745088Sab196087 elfeditGC_cmd_t *
20755088Sab196087 elfedit_find_command(const char *name, int must_exist,
20765088Sab196087     elfeditGC_module_t **mod_ret)
20775088Sab196087 {
20785088Sab196087 	elfeditGC_module_t	*mod;
20795088Sab196087 	const char		*mod_str;
20805088Sab196087 	const char		*cmd_str;
20815088Sab196087 	char			mod_buf[ELFEDIT_MAXMODNAM + 1];
20825088Sab196087 	size_t			n;
20835088Sab196087 	elfeditGC_cmd_t		*cmd;
20845088Sab196087 
20855088Sab196087 
20865088Sab196087 	cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON));
20875088Sab196087 	if (cmd_str == NULL) {		/* No module name -> sys: */
20885088Sab196087 		mod_str = MSG_ORIG(MSG_MOD_SYS);
20895088Sab196087 		cmd_str = name;
20905088Sab196087 	} else if (cmd_str == name) {	/* Empty module name -> sys: */
20915088Sab196087 		mod_str = MSG_ORIG(MSG_MOD_SYS);
20925088Sab196087 		cmd_str++;		/* Skip the colon */
20935088Sab196087 	} else {			/* Have both module and command */
20945088Sab196087 		n = cmd_str - name;
20955088Sab196087 		if (n >= sizeof (mod_buf)) {
20965088Sab196087 			if (must_exist)
20975088Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
20985088Sab196087 				    MSG_INTL(MSG_ERR_MODNAMTOOLONG), name);
20995088Sab196087 			return (NULL);
21005088Sab196087 		}
21015088Sab196087 		(void) strlcpy(mod_buf, name, n + 1);
21025088Sab196087 		mod_str = mod_buf;
21035088Sab196087 		cmd_str++;
21045088Sab196087 	}
21055088Sab196087 
21065088Sab196087 	/* Lookup/load module. Won't return on error */
21075088Sab196087 	mod = elfedit_load_module(mod_str, must_exist, 0);
21085088Sab196087 	if (mod == NULL)
21095088Sab196087 		return (NULL);
21105088Sab196087 
21115088Sab196087 	/* Locate the command */
21125088Sab196087 	cmd = find_cmd(mod, cmd_str);
21135088Sab196087 	if (cmd == NULL) {
21145088Sab196087 		if (must_exist) {
21155088Sab196087 			/*
21165088Sab196087 			 * Catch empty command in order to provide
21175088Sab196087 			 * a better error message.
21185088Sab196087 			 */
21195088Sab196087 			if (*cmd_str == '\0') {
21205088Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
21215088Sab196087 				    MSG_INTL(MSG_ERR_MODNOCMD), mod_str);
21225088Sab196087 			} else {
21235088Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
21245088Sab196087 				    MSG_INTL(MSG_ERR_UNRECCMD),
21255088Sab196087 				    mod_str, cmd_str);
21265088Sab196087 			}
21275088Sab196087 		}
21285088Sab196087 	} else {
21295088Sab196087 		if (mod_ret != NULL)
21305088Sab196087 			*mod_ret = mod;
21315088Sab196087 	}
21325088Sab196087 	return (cmd);
21335088Sab196087 }
21345088Sab196087 
21355088Sab196087 
21365088Sab196087 /*
21375088Sab196087  * Release all user command blocks found on state.ucmd
21385088Sab196087  */
21395088Sab196087 static void
21405088Sab196087 free_user_cmds(void)
21415088Sab196087 {
21425088Sab196087 	USER_CMD_T *next;
21435088Sab196087 
21445088Sab196087 	while (state.ucmd.list) {
21455088Sab196087 		next = state.ucmd.list->ucmd_next;
21465088Sab196087 		free(state.ucmd.list);
21475088Sab196087 		state.ucmd.list = next;
21485088Sab196087 	}
21495088Sab196087 	state.ucmd.tail = NULL;
21505088Sab196087 	state.ucmd.n = 0;
21515088Sab196087 	state.cur_cmd = NULL;
21525088Sab196087 }
21535088Sab196087 
21545088Sab196087 
21555088Sab196087 /*
21565088Sab196087  * Process all user command blocks found on state.ucmd, and then
21575088Sab196087  * remove them from the list.
21585088Sab196087  */
21595088Sab196087 static void
21605088Sab196087 dispatch_user_cmds()
21615088Sab196087 {
21625088Sab196087 	USER_CMD_T		*ucmd;
21635088Sab196087 	elfedit_cmdret_t	cmd_ret;
21645088Sab196087 
21655088Sab196087 	ucmd = state.ucmd.list;
21665088Sab196087 	if (ucmd) {
21675088Sab196087 		/* Do them, in order */
21685088Sab196087 		for (; ucmd; ucmd = ucmd->ucmd_next) {
21695088Sab196087 			state.cur_cmd = ucmd;
21705088Sab196087 			if (!state.msg_jbuf.active)
21715088Sab196087 				elfedit_msg(ELFEDIT_MSG_DEBUG,
21725088Sab196087 				    MSG_INTL(MSG_DEBUG_EXECCMD),
21735088Sab196087 				    ucmd->ucmd_orig_str);
21745088Sab196087 			/*
21755088Sab196087 			 * The cmd_func field is the generic definition.
21765088Sab196087 			 * We need to cast it to the type that matches
21775088Sab196087 			 * the proper ELFCLASS before calling it.
21785088Sab196087 			 */
21795088Sab196087 			if (state.elf.elfclass == ELFCLASS32) {
21805088Sab196087 				elfedit32_cmd_func_t *cmd_func =
21815088Sab196087 				    (elfedit32_cmd_func_t *)
21825088Sab196087 				    ucmd->ucmd_cmd->cmd_func;
21835088Sab196087 
21845088Sab196087 				cmd_ret = (* cmd_func)(state.elf.obj_state.s32,
21855088Sab196087 				    ucmd->ucmd_argc, ucmd->ucmd_argv);
21865088Sab196087 			} else {
21875088Sab196087 				elfedit64_cmd_func_t *cmd_func =
21885088Sab196087 				    (elfedit64_cmd_func_t *)
21895088Sab196087 				    ucmd->ucmd_cmd->cmd_func;
21905088Sab196087 
21915088Sab196087 				cmd_ret = (* cmd_func)(state.elf.obj_state.s64,
21925088Sab196087 				    ucmd->ucmd_argc, ucmd->ucmd_argv);
21935088Sab196087 			}
21945088Sab196087 			state.cur_cmd = NULL;
21955088Sab196087 			/* If a pager was started, wrap it up */
21965088Sab196087 			elfedit_pager_cleanup();
21975088Sab196087 
21985088Sab196087 			switch (cmd_ret) {
21995088Sab196087 			case ELFEDIT_CMDRET_MOD:
22005088Sab196087 				/*
22015088Sab196087 				 * Command modified the output ELF image,
22025088Sab196087 				 * mark the file as needing a flush to disk.
22035088Sab196087 				 */
22045088Sab196087 				state.file.dirty = 1;
22055088Sab196087 				break;
22065088Sab196087 			case ELFEDIT_CMDRET_FLUSH:
22075088Sab196087 				/*
22085088Sab196087 				 * Command flushed the output file,
22095088Sab196087 				 * clear the dirty bit.
22105088Sab196087 				 */
22115088Sab196087 				state.file.dirty = 0;
22125088Sab196087 			}
22135088Sab196087 		}
22145088Sab196087 		free_user_cmds();
22155088Sab196087 	}
22165088Sab196087 }
22175088Sab196087 
22185088Sab196087 
22195088Sab196087 /*
22205892Sab196087  * Given the pointer to the character following a '\' character in
22215892Sab196087  * a C style literal, return the ASCII character code it represents,
22225892Sab196087  * and advance the string pointer to the character following the last
22235892Sab196087  * character in the escape sequence.
22245892Sab196087  *
22255892Sab196087  * entry:
22265892Sab196087  *	str - Address of string pointer to first character following
22275892Sab196087  *		the backslash.
22285892Sab196087  *
22295892Sab196087  * exit:
22305892Sab196087  *	If the character is not valid, an error is thrown and this routine
22315892Sab196087  *	does not return to its caller. Otherwise, it returns the ASCII
22325892Sab196087  *	code for the translated character, and *str has been advanced.
22335892Sab196087  */
22345892Sab196087 static int
22355892Sab196087 translate_c_esc(char **str)
22365892Sab196087 {
22375892Sab196087 	char *s = *str;
22385892Sab196087 	int	ch;
22395892Sab196087 	int	i;
22405892Sab196087 
22415892Sab196087 	ch = *s++;
22425892Sab196087 	switch (ch) {
22435892Sab196087 	case 'a':
22445892Sab196087 		ch = '\a';
22455892Sab196087 		break;
22465892Sab196087 	case 'b':
22475892Sab196087 		ch = '\b';
22485892Sab196087 		break;
22495892Sab196087 	case 'f':
22505892Sab196087 		ch = '\f';
22515892Sab196087 		break;
22525892Sab196087 	case 'n':
22535892Sab196087 		ch = '\n';
22545892Sab196087 		break;
22555892Sab196087 	case 'r':
22565892Sab196087 		ch = '\r';
22575892Sab196087 		break;
22585892Sab196087 	case 't':
22595892Sab196087 		ch = '\t';
22605892Sab196087 		break;
22615892Sab196087 	case 'v':
22625892Sab196087 		ch = '\v';
22635892Sab196087 		break;
22645892Sab196087 
22655892Sab196087 	case '0':
22665892Sab196087 	case '1':
22675892Sab196087 	case '2':
22685892Sab196087 	case '3':
22695892Sab196087 	case '4':
22705892Sab196087 	case '5':
22715892Sab196087 	case '6':
22725892Sab196087 	case '7':
22735892Sab196087 		/* Octal constant: There can be up to 3 digits */
22745892Sab196087 		ch -= '0';
22755892Sab196087 		for (i = 0; i < 2; i++) {
22765892Sab196087 			if ((*s < '0') || (*s > '7'))
22775892Sab196087 				break;
22785892Sab196087 			ch = (ch << 3) + (*s++ - '0');
22795892Sab196087 		}
22805892Sab196087 		break;
22815892Sab196087 
22825892Sab196087 	/*
22835892Sab196087 	 * There are some cases where ch already has the desired value.
22845892Sab196087 	 * These cases exist simply to remove the special meaning that
22855892Sab196087 	 * character would otherwise have. We need to match them to
22865892Sab196087 	 * prevent them from falling into the default error case.
22875892Sab196087 	 */
22885892Sab196087 	case '\\':
22895892Sab196087 	case '\'':
22905892Sab196087 	case '"':
22915892Sab196087 		break;
22925892Sab196087 
22935892Sab196087 	default:
22945892Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADCESC), ch);
22955892Sab196087 		break;
22965892Sab196087 	}
22975892Sab196087 
22985892Sab196087 	*str = s;
22995892Sab196087 	return (ch);
23005892Sab196087 }
23015892Sab196087 
23025892Sab196087 
23035892Sab196087 /*
23045088Sab196087  * Prepare a GETTOK_STATE struct for gettok().
23055088Sab196087  *
23065088Sab196087  * entry:
23075088Sab196087  *	gettok_state - gettok state block to use
23085088Sab196087  *	str - Writable buffer to tokenize. Note that gettok()
23095088Sab196087  *		is allowed to change the contents of this buffer.
23105088Sab196087  *	inc_null_final - If the line ends in whitespace instead of
23115088Sab196087  *		immediately hitting a NULL, and inc_null_final is TRUE,
23125088Sab196087  *		then a null final token is generated. Otherwise trailing
23135088Sab196087  *		whitespace is ignored.
23145088Sab196087  */
23155088Sab196087 static void
23165088Sab196087 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final)
23175088Sab196087 {
23185088Sab196087 	gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf;
23195088Sab196087 	gettok_state->gtok_inc_null_final = inc_null_final;
23205088Sab196087 	gettok_state->gtok_null_seen = 0;
23215088Sab196087 }
23225088Sab196087 
23235088Sab196087 
23245088Sab196087 /*
23255088Sab196087  * Locate the next token from the buffer.
23265088Sab196087  *
23275088Sab196087  * entry:
23285088Sab196087  *	gettok_state - State of gettok() operation. Initialized
23295088Sab196087  *		by gettok_init(), and passed to gettok().
23305088Sab196087  *
23315088Sab196087  * exit:
23325088Sab196087  *	If a token is found, gettok_state->gtok_last_token is filled in
23335088Sab196087  *	with the details and True (1) is returned. If no token is found,
23345088Sab196087  *	False (1) is returned, and the contents of
23355088Sab196087  *	gettok_state->gtok_last_token are undefined.
23365088Sab196087  *
23375088Sab196087  * note:
23385088Sab196087  *	- The token returned references the memory in gettok_state->gtok_buf.
23395088Sab196087  *		The caller should not modify the buffer until all such
23405088Sab196087  *		pointers have been discarded.
23415088Sab196087  *	- This routine will modify the contents of gettok_state->gtok_buf
23425088Sab196087  *		as necessary to remove quotes and eliminate escape
23435088Sab196087  *		(\)characters.
23445088Sab196087  */
23455088Sab196087 static int
23465088Sab196087 gettok(GETTOK_STATE *gettok_state)
23475088Sab196087 {
23485088Sab196087 	char	*str = gettok_state->gtok_cur_buf;
23495088Sab196087 	char	*look;
23505088Sab196087 	int	quote_ch = '\0';
23515088Sab196087 
23525088Sab196087 	/* Skip leading whitespace */
23535088Sab196087 	while (isspace(*str))
23545088Sab196087 		str++;
23555088Sab196087 
23565088Sab196087 	if (*str == '\0') {
23575088Sab196087 		/*
23585088Sab196087 		 * If user requested it, and there was whitespace at the
23595088Sab196087 		 * end, then generate one last null token.
23605088Sab196087 		 */
23615088Sab196087 		if (gettok_state->gtok_inc_null_final &&
23625088Sab196087 		    !gettok_state->gtok_null_seen) {
23635088Sab196087 			gettok_state->gtok_inc_null_final = 0;
23645088Sab196087 			gettok_state->gtok_null_seen = 1;
23655088Sab196087 			gettok_state->gtok_last_token.tok_str = str;
23665088Sab196087 			gettok_state->gtok_last_token.tok_len = 0;
23675088Sab196087 			gettok_state->gtok_last_token.tok_line_off =
23685088Sab196087 			    str - gettok_state->gtok_buf;
23695088Sab196087 			return (1);
23705088Sab196087 		}
23715088Sab196087 		gettok_state->gtok_null_seen = 1;
23725088Sab196087 		return (0);
23735088Sab196087 	}
23745088Sab196087 
23755088Sab196087 	/*
23765088Sab196087 	 * Read token: The standard delimiter is whitespace, but
23775088Sab196087 	 * we honor either single or double quotes. Also, we honor
23785088Sab196087 	 * backslash escapes.
23795088Sab196087 	 */
23805088Sab196087 	gettok_state->gtok_last_token.tok_str = look = str;
23815088Sab196087 	gettok_state->gtok_last_token.tok_line_off =
23825088Sab196087 	    look - gettok_state->gtok_buf;
23835088Sab196087 	for (; *look; look++) {
23845088Sab196087 		if (*look == quote_ch) {	/* Terminates active quote */
23855088Sab196087 			quote_ch = '\0';
23865088Sab196087 			continue;
23875088Sab196087 		}
23885088Sab196087 
23895088Sab196087 		if (quote_ch == '\0') {		/* No quote currently active */
23905088Sab196087 			if ((*look == '\'') || (*look == '"')) {
23915088Sab196087 				quote_ch = *look;	/* New active quote */
23925088Sab196087 				continue;
23935088Sab196087 			}
23945088Sab196087 			if (isspace(*look))
23955088Sab196087 				break;
23965088Sab196087 		}
23975088Sab196087 
23985892Sab196087 		/*
23995892Sab196087 		 * The semantics of the backslash character depends on
24005892Sab196087 		 * the quote style in use:
24015892Sab196087 		 *	- Within single quotes, backslash is not
24025892Sab196087 		 *		an escape character, and is taken literally.
24035892Sab196087 		 *	- If outside of quotes, the backslash is an escape
24045892Sab196087 		 *		character. The backslash is ignored and the
24055892Sab196087 		 *		following character is taken literally, losing
24065892Sab196087 		 *		any special properties it normally has.
24075892Sab196087 		 *	- Within double quotes, backslash works like a
24085892Sab196087 		 *		backslash escape within a C literal. Certain
24095892Sab196087 		 *		escapes are recognized and replaced with their
24105892Sab196087 		 *		special character. Any others are an error.
24115892Sab196087 		 */
24125088Sab196087 		if (*look == '\\') {
24135892Sab196087 			if (quote_ch == '\'') {
24145892Sab196087 				*str++ = *look;
24155892Sab196087 				continue;
24165892Sab196087 			}
24175892Sab196087 
24185088Sab196087 			look++;
24195892Sab196087 			if (*look == '\0') {	/* Esc applied to NULL term? */
24205892Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
24215892Sab196087 				    MSG_INTL(MSG_ERR_ESCEOL));
24225892Sab196087 				/*NOTREACHED*/
24235892Sab196087 			}
24245892Sab196087 
24255892Sab196087 			if (quote_ch == '"') {
24265892Sab196087 				*str++ = translate_c_esc(&look);
24275892Sab196087 				look--;		/* for() will advance by 1 */
24285892Sab196087 				continue;
24295892Sab196087 			}
24305088Sab196087 		}
24315088Sab196087 
24325088Sab196087 		if (look != str)
24335088Sab196087 			*str = *look;
24345088Sab196087 		str++;
24355088Sab196087 	}
24365892Sab196087 
24375892Sab196087 	/* Don't allow unterminated quoted tokens */
24385892Sab196087 	if (quote_ch != '\0')
24395892Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNTERMQUOTE),
24405892Sab196087 		    quote_ch);
24415892Sab196087 
24425088Sab196087 	gettok_state->gtok_last_token.tok_len = str -
24435088Sab196087 	    gettok_state->gtok_last_token.tok_str;
24445088Sab196087 	gettok_state->gtok_null_seen = *look == '\0';
24455088Sab196087 	if (!gettok_state->gtok_null_seen)
24465088Sab196087 		look++;
24475088Sab196087 	*str = '\0';
24485088Sab196087 	gettok_state->gtok_cur_buf = look;
24495088Sab196087 
24505892Sab196087 #ifdef DEBUG_GETTOK
24515892Sab196087 	printf("GETTOK >");
24525892Sab196087 	elfedit_str_to_c_literal(gettok_state->gtok_last_token.tok_str,
24535892Sab196087 	    elfedit_write);
24545892Sab196087 	printf("< \tlen(%d) offset(%d)\n",
24555088Sab196087 	    gettok_state->gtok_last_token.tok_len,
24565088Sab196087 	    gettok_state->gtok_last_token.tok_line_off);
24575088Sab196087 #endif
24585088Sab196087 
24595088Sab196087 	return (1);
24605088Sab196087 }
24615088Sab196087 
24625088Sab196087 
24635088Sab196087 /*
24645088Sab196087  * Tokenize the user command string, and return a pointer to the
24655088Sab196087  * TOK_STATE buffer maintained by this function. That buffer contains
24665088Sab196087  * the tokenized strings.
24675088Sab196087  *
24685088Sab196087  * entry:
24695088Sab196087  *	user_cmd_str - String to tokenize
24705088Sab196087  *	len - # of characters in user_cmd_str to examine. If
24715088Sab196087  *		(len < 0), then the complete string is processed
24725088Sab196087  *		stopping with the NULL termination. Otherwise,
24735088Sab196087  *		processing stops after len characters, and any
24745088Sab196087  *		remaining characters are ignored.
24755088Sab196087  *	inc_null_final - If True, and if user_cmd_str has whitespace
24765088Sab196087  *		at the end following the last non-null token, then
24775088Sab196087  *		a final null token will be included. If False, null
24785088Sab196087  *		tokens are ignored.
24795088Sab196087  *
24805088Sab196087  * note:
24815088Sab196087  *	This routine returns pointers to internally allocated memory.
24825088Sab196087  *	The caller must not alter anything contained in the TOK_STATE
24835088Sab196087  *	buffer returned. Furthermore, the the contents of TOK_STATE
24845088Sab196087  *	are only valid until the next call to tokenize_user_cmd().
24855088Sab196087  */
24865088Sab196087 static TOK_STATE *
24875088Sab196087 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final)
24885088Sab196087 {
24895088Sab196087 #define	INITIAL_TOK_ALLOC 5
24905088Sab196087 
24915088Sab196087 	/*
24925088Sab196087 	 * As we parse the user command, we need temporary space to
24935088Sab196087 	 * hold the tokens. We do this by dynamically allocating a string
24945088Sab196087 	 * buffer and a token array, and doubling them as necessary. This
24955088Sab196087 	 * is a single threaded application, so static variables suffice.
24965088Sab196087 	 */
24975088Sab196087 	static STRBUF str;
24985088Sab196087 	static TOK_STATE tokst;
24995088Sab196087 
25005088Sab196087 	GETTOK_STATE	gettok_state;
25015088Sab196087 	size_t		n;
25025088Sab196087 
25035088Sab196087 	/*
25045088Sab196087 	 * Make a copy we can modify. If (len == 0), take the entire
25055088Sab196087 	 * string. Otherwise limit it to the specified length.
25065088Sab196087 	 */
25075088Sab196087 	tokst.tokst_cmd_len = strlen(user_cmd_str);
25085088Sab196087 	if ((len > 0) && (len < tokst.tokst_cmd_len))
25095088Sab196087 		tokst.tokst_cmd_len = len;
25105088Sab196087 	tokst.tokst_cmd_len++;	/* Room for NULL termination */
25115088Sab196087 	strbuf_ensure_size(&str, tokst.tokst_cmd_len);
25125088Sab196087 	(void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len);
25135088Sab196087 
25145088Sab196087 	/* Trim off any newline character that might be present */
25155088Sab196087 	if ((tokst.tokst_cmd_len > 1) &&
25165088Sab196087 	    (str.buf[tokst.tokst_cmd_len - 2] == '\n')) {
25175088Sab196087 		tokst.tokst_cmd_len--;
25185088Sab196087 		str.buf[tokst.tokst_cmd_len - 1] = '\0';
25195088Sab196087 	}
25205088Sab196087 
25215088Sab196087 	/* Tokenize the user command string into tok struct */
25225088Sab196087 	gettok_init(&gettok_state, str.buf, inc_null_final);
25235088Sab196087 	tokst.tokst_str_size = 0;	/* Space needed for token strings */
25245088Sab196087 	for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0;
25255088Sab196087 	    tokst.tokst_cnt++) {
25265088Sab196087 		/* If we need more room, expand the token buffer */
25275088Sab196087 		if (tokst.tokst_cnt >= tokst.tokst_bufsize) {
25285088Sab196087 			n = (tokst.tokst_bufsize == 0) ?
25295088Sab196087 			    INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2);
25305088Sab196087 			tokst.tokst_buf = elfedit_realloc(
25315088Sab196087 			    MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf,
25325088Sab196087 			    n * sizeof (*tokst.tokst_buf));
25335088Sab196087 			tokst.tokst_bufsize = n;
25345088Sab196087 		}
25355088Sab196087 		tokst.tokst_str_size +=
25365088Sab196087 		    gettok_state.gtok_last_token.tok_len + 1;
25375088Sab196087 		tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token;
25385088Sab196087 	}
25395088Sab196087 	/* fold the command token to lowercase */
25405088Sab196087 	if (tokst.tokst_cnt > 0) {
25415088Sab196087 		char *s;
25425088Sab196087 
25435088Sab196087 		for (s = tokst.tokst_buf[0].tok_str; *s; s++)
25445088Sab196087 			if (isupper(*s))
25455088Sab196087 				*s = tolower(*s);
25465088Sab196087 	}
25475088Sab196087 
25485088Sab196087 	return (&tokst);
25495088Sab196087 
25505088Sab196087 #undef	INITIAL_TOK_ALLOC
25515088Sab196087 }
25525088Sab196087 
25535088Sab196087 
25545088Sab196087 /*
25555088Sab196087  * Parse the user command string, and put an entry for it at the end
25565088Sab196087  * of state.ucmd.
25575088Sab196087  */
25585088Sab196087 static void
25595088Sab196087 parse_user_cmd(const char *user_cmd_str)
25605088Sab196087 {
25615088Sab196087 	TOK_STATE	*tokst;
25625088Sab196087 	char		*s;
25635088Sab196087 	size_t		n;
25645088Sab196087 	size_t		len;
25655088Sab196087 	USER_CMD_T	*ucmd;
25665088Sab196087 	elfeditGC_module_t *mod;
25675088Sab196087 	elfeditGC_cmd_t	*cmd;
25685088Sab196087 
25695088Sab196087 	/*
25705088Sab196087 	 * Break it into tokens. If there are none, then it is
25715088Sab196087 	 * an empty command and is ignored.
25725088Sab196087 	 */
25735088Sab196087 	tokst = tokenize_user_cmd(user_cmd_str, -1, 0);
25745088Sab196087 	if (tokst->tokst_cnt == 0)
25755088Sab196087 		return;
25765088Sab196087 
25775088Sab196087 	/* Find the command. Won't return on error */
25785088Sab196087 	cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod);
25795088Sab196087 
25805088Sab196087 	/*
25815088Sab196087 	 * If there is no ELF file being edited, then only commands
25825088Sab196087 	 * from the sys: module are allowed.
25835088Sab196087 	 */
25845088Sab196087 	if ((state.file.present == 0) &&
25855088Sab196087 	    (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0))
25865088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY),
25875088Sab196087 		    mod->mod_name, cmd->cmd_name[0]);
25885088Sab196087 
25895088Sab196087 
25905088Sab196087 	/* Allocate, fill in, and insert a USER_CMD_T block */
25915088Sab196087 	n = S_DROUND(sizeof (USER_CMD_T));
25925088Sab196087 	ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD),
25935088Sab196087 	    n + (sizeof (char *) * (tokst->tokst_cnt - 1)) +
25945088Sab196087 	    tokst->tokst_cmd_len + tokst->tokst_str_size);
25955088Sab196087 	ucmd->ucmd_next = NULL;
25965088Sab196087 	ucmd->ucmd_argc = tokst->tokst_cnt - 1;
25975088Sab196087 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
25985088Sab196087 	ucmd->ucmd_argv = (const char **)(n + (char *)ucmd);
25995088Sab196087 	ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc);
26005088Sab196087 	(void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len);
26015088Sab196087 	ucmd->ucmd_mod = mod;
26025088Sab196087 	ucmd->ucmd_cmd = cmd;
26035088Sab196087 	ucmd->ucmd_ostyle_set = 0;
26045088Sab196087 	s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len;
26055088Sab196087 	for (n = 1; n < tokst->tokst_cnt; n++) {
26065088Sab196087 		len = tokst->tokst_buf[n].tok_len + 1;
26075088Sab196087 		ucmd->ucmd_argv[n - 1] = s;
26085088Sab196087 		(void) strncpy(s, tokst->tokst_buf[n].tok_str, len);
26095088Sab196087 		s += len;
26105088Sab196087 	}
26115088Sab196087 	if (state.ucmd.list == NULL) {
26125088Sab196087 		state.ucmd.list = state.ucmd.tail = ucmd;
26135088Sab196087 	} else {
26145088Sab196087 		state.ucmd.tail->ucmd_next = ucmd;
26155088Sab196087 		state.ucmd.tail = ucmd;
26165088Sab196087 	}
26175088Sab196087 	state.ucmd.n++;
26185088Sab196087 }
26195088Sab196087 
26205088Sab196087 
26215088Sab196087 /*
26225088Sab196087  * Copy infile to a new file with the name given by outfile.
26235088Sab196087  */
26245088Sab196087 static void
26255088Sab196087 create_outfile(const char *infile, const char *outfile)
26265088Sab196087 {
26275088Sab196087 	pid_t pid;
26285088Sab196087 	int statloc;
26295088Sab196087 	struct stat statbuf;
26305088Sab196087 
26315088Sab196087 
26325088Sab196087 	pid = fork();
26335088Sab196087 	switch (pid) {
26345088Sab196087 	case -1:			/* Unable to create process */
26355088Sab196087 		{
26365088Sab196087 			int err = errno;
26375088Sab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK),
26385088Sab196087 			    strerror(err));
26395088Sab196087 		}
26405088Sab196087 		/*NOTREACHED*/
26415088Sab196087 		return;
26425088Sab196087 
26435088Sab196087 	case 0:
26445088Sab196087 		(void) execl(MSG_ORIG(MSG_STR_BINCP),
26455088Sab196087 		    MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL);
26465088Sab196087 		/*
26475088Sab196087 		 * exec() only returns on error. This is the child process,
26485088Sab196087 		 * so we want to stay away from the usual error mechanism
26495088Sab196087 		 * and handle things directly.
26505088Sab196087 		 */
26515088Sab196087 		{
26525088Sab196087 			int err = errno;
26535088Sab196087 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC),
26545088Sab196087 			    MSG_ORIG(MSG_STR_ELFEDIT),
26555088Sab196087 			    MSG_ORIG(MSG_STR_BINCP), strerror(err));
26565088Sab196087 		}
26575088Sab196087 		exit(1);
26585088Sab196087 		/*NOTREACHED*/
26595088Sab196087 	}
26605088Sab196087 
26615088Sab196087 	/* This is the parent: Wait for the child to terminate */
26625088Sab196087 	if (waitpid(pid, &statloc,  0) != pid) {
26635088Sab196087 		int err = errno;
26645088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT),
26655088Sab196087 		    strerror(err));
26665088Sab196087 	}
26675088Sab196087 	/*
26685088Sab196087 	 * If the child failed, then terminate the process. There is no
26695088Sab196087 	 * need for an error message, because the child will have taken
26705088Sab196087 	 * care of that.
26715088Sab196087 	 */
26725088Sab196087 	if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0))
26735088Sab196087 		exit(1);
26745088Sab196087 
26755088Sab196087 	/* Make sure the copy allows user write access */
26765088Sab196087 	if (stat(outfile, &statbuf) == -1) {
26775088Sab196087 		int err = errno;
26785088Sab196087 		(void) unlink(outfile);
26795088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT),
26805088Sab196087 		    outfile, strerror(err));
26815088Sab196087 	}
26825088Sab196087 	if ((statbuf.st_mode & S_IWUSR) == 0) {
26835088Sab196087 		/* Only keep permission bits, and add user write */
26845088Sab196087 		statbuf.st_mode |= S_IWUSR;
26855088Sab196087 		statbuf.st_mode &= 07777;   /* Only keep the permission bits */
26865088Sab196087 		if (chmod(outfile, statbuf.st_mode) == -1) {
26875088Sab196087 			int err = errno;
26885088Sab196087 			(void) unlink(outfile);
26895088Sab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD),
26905088Sab196087 			    outfile, strerror(err));
26915088Sab196087 		}
26925088Sab196087 	}
26935088Sab196087 }
26945088Sab196087 
26955088Sab196087 /*
26965088Sab196087  * Given a module path string, determine how long the resulting path will
26975088Sab196087  * be when all % tokens have been expanded.
26985088Sab196087  *
26995088Sab196087  * entry:
27005088Sab196087  *	path - Path for which expanded length is desired
27015088Sab196087  *	origin_root - Root of $ORIGIN  tree containing running elfedit program
27025088Sab196087  *
27035088Sab196087  * exit:
27045088Sab196087  *	Returns the value strlen() will give for the expanded path.
27055088Sab196087  */
27065088Sab196087 static size_t
27075088Sab196087 modpath_strlen(const char *path, const char *origin_root)
27085088Sab196087 {
27095088Sab196087 	size_t len = 0;
27105088Sab196087 	const char *s;
27115088Sab196087 
27125088Sab196087 	s = path;
27135088Sab196087 	len = 0;
27145088Sab196087 	for (s = path; *s != '\0'; s++) {
27155088Sab196087 		if (*s == '%') {
27165088Sab196087 			s++;
27175088Sab196087 			switch (*s) {
27185088Sab196087 			case 'i':	/* ISA of running elfedit */
27195088Sab196087 				len += strlen(isa_i_str);
27205088Sab196087 				break;
27215088Sab196087 			case 'I':	/* "" for 32-bit, same as %i for 64 */
27225088Sab196087 				len += strlen(isa_I_str);
27235088Sab196087 				break;
27245088Sab196087 			case 'o':	/* Insert default path */
27255088Sab196087 				len +=
27265088Sab196087 				    modpath_strlen(MSG_ORIG(MSG_STR_MODPATH),
27275088Sab196087 				    origin_root);
27285088Sab196087 				break;
27295088Sab196087 			case 'r':	/* root of tree with running elfedit */
27305088Sab196087 				len += strlen(origin_root);
27315088Sab196087 				break;
27325088Sab196087 
27335088Sab196087 			case '%':	/* %% is reduced to just '%' */
27345088Sab196087 				len++;
27355088Sab196087 				break;
27365088Sab196087 			default:	/* All other % codes are reserved */
27375088Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
27385088Sab196087 				    MSG_INTL(MSG_ERR_BADPATHCODE), *s);
27395088Sab196087 				/*NOTREACHED*/
27405088Sab196087 				break;
27415088Sab196087 			}
27425088Sab196087 		} else {	/* Non-% character passes straight through */
27435088Sab196087 			len++;
27445088Sab196087 		}
27455088Sab196087 	}
27465088Sab196087 
27475088Sab196087 	return (len);
27485088Sab196087 }
27495088Sab196087 
27505088Sab196087 
27515088Sab196087 /*
27525088Sab196087  * Given a module path string, and a buffer large enough to hold the results,
27535088Sab196087  * fill the buffer with the expanded path.
27545088Sab196087  *
27555088Sab196087  * entry:
27565088Sab196087  *	path - Path for which expanded length is desired
27575088Sab196087  *	origin_root - Root of tree containing running elfedit program
27585088Sab196087  *	buf - Buffer to receive the result. buf must as large or larger
27595088Sab196087  *		than the value given by modpath_strlen().
27605088Sab196087  *
27615088Sab196087  * exit:
27625088Sab196087  *	Returns pointer to location following the last character
27635088Sab196087  *	written to buf. A NULL byte is written to that address.
27645088Sab196087  */
27655088Sab196087 static char *
27665088Sab196087 modpath_expand(const char *path, const char *origin_root, char *buf)
27675088Sab196087 {
27685088Sab196087 	size_t len;
27695088Sab196087 	const char *cp_str;
27705088Sab196087 
27715088Sab196087 	for (; *path != '\0'; path++) {
27725088Sab196087 		if (*path == '%') {
27735088Sab196087 			path++;
27745088Sab196087 			cp_str = NULL;
27755088Sab196087 			switch (*path) {
27765088Sab196087 			case 'i':	/* ISA of running elfedit */
27775088Sab196087 				cp_str = isa_i_str;
27785088Sab196087 				break;
27795088Sab196087 			case 'I':	/* "" for 32-bit, same as %i for 64 */
27805088Sab196087 				cp_str = isa_I_str;
27815088Sab196087 				break;
27825088Sab196087 			case 'o':	/* Insert default path */
27835088Sab196087 				buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH),
27845088Sab196087 				    origin_root, buf);
27855088Sab196087 				break;
27865088Sab196087 			case 'r':
27875088Sab196087 				cp_str = origin_root;
27885088Sab196087 				break;
27895088Sab196087 			case '%':	/* %% is reduced to just '%' */
27905088Sab196087 				*buf++ = *path;
27915088Sab196087 				break;
27925088Sab196087 			default:	/* All other % codes are reserved */
27935088Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
27945088Sab196087 				    MSG_INTL(MSG_ERR_BADPATHCODE), *path);
27955088Sab196087 				/*NOTREACHED*/
27965088Sab196087 				break;
27975088Sab196087 			}
27985088Sab196087 			if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) {
27995088Sab196087 				bcopy(cp_str, buf, len);
28005088Sab196087 				buf += len;
28015088Sab196087 			}
28025088Sab196087 		} else {	/* Non-% character passes straight through */
28035088Sab196087 			*buf++ = *path;
28045088Sab196087 		}
28055088Sab196087 	}
28065088Sab196087 
28075088Sab196087 	*buf = '\0';
28085088Sab196087 	return (buf);
28095088Sab196087 }
28105088Sab196087 
28115088Sab196087 
28125088Sab196087 /*
28135088Sab196087  * Establish the module search path: state.modpath
28145088Sab196087  *
28155088Sab196087  * The path used comes from the following sources, taking the first
28165088Sab196087  * one that has a value, and ignoring any others:
28175088Sab196087  *
28185088Sab196087  *	- ELFEDIT_PATH environment variable
28195088Sab196087  *	- -L command line argument
28205088Sab196087  *	- Default value
28215088Sab196087  *
28225088Sab196087  * entry:
28235088Sab196087  *	path - NULL, or the value of the -L command line argument
28245088Sab196087  *
28255088Sab196087  * exit:
28265088Sab196087  *	state.modpath has been filled in
28275088Sab196087  */
28285088Sab196087 static void
28295088Sab196087 establish_modpath(const char *cmdline_path)
28305088Sab196087 {
28315088Sab196087 	char origin_root[PATH_MAX + 1];	/* Where elfedit binary is */
28325088Sab196087 	const char	*path;		/* Initial path */
28335088Sab196087 	char		*expath;	/* Expanded path */
28345088Sab196087 	size_t		len;
28355088Sab196087 	char		*src, *dst;
28365088Sab196087 
28375088Sab196087 	path = getenv(MSG_ORIG(MSG_STR_ENVVAR));
28385088Sab196087 	if (path == NULL)
28395088Sab196087 		path = cmdline_path;
28405088Sab196087 	if (path == NULL)
28415088Sab196087 		path = MSG_ORIG(MSG_STR_MODPATH);
28425088Sab196087 
28435088Sab196087 
28445088Sab196087 	/*
28455088Sab196087 	 * Root of tree containing running for running program. 32-bit elfedit
28465088Sab196087 	 * is installed in /usr/bin, and 64-bit elfedit is one level lower
28475088Sab196087 	 * in an ISA-specific subdirectory. So, we find the root by
28485088Sab196087 	 * getting the $ORGIN of the current running program, and trimming
28495088Sab196087 	 * off the last 2 (32-bit) or 3 (64-bit) directories.
28505088Sab196087 	 *
28515088Sab196087 	 * On a standard system, this will simply yield '/'. However,
28525088Sab196087 	 * doing it this way allows us to run elfedit from a proto area,
28535088Sab196087 	 * and pick up modules from the same proto area instead of those
28545088Sab196087 	 * installed on the system.
28555088Sab196087 	 */
28565088Sab196087 	if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1)
28575088Sab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN));
28585088Sab196087 	len = (sizeof (char *) == 8) ? 3 : 2;
28595088Sab196087 	src = origin_root + strlen(origin_root);
28605088Sab196087 	while ((src > origin_root) && (len > 0)) {
28615088Sab196087 		if (*(src - 1) == '/')
28625088Sab196087 			len--;
28635088Sab196087 		src--;
28645088Sab196087 	}
28655088Sab196087 	*src = '\0';
28665088Sab196087 
28675088Sab196087 
28685088Sab196087 	/*
28695088Sab196087 	 * Calculate space needed to hold expanded path. Note that
28705088Sab196087 	 * this assumes that MSG_STR_MODPATH will never contain a '%o'
28715088Sab196087 	 * code, and so, the expansion is not recursive. The codes allowed
28725088Sab196087 	 * are:
28735088Sab196087 	 *	%i - ISA of running elfedit (sparc, sparcv9, etc)
28745088Sab196087 	 *	%I - 64-bit ISA: Same as %i for 64-bit versions of elfedit,
28755088Sab196087 	 *		but yields empty string for 32-bit ISAs.
28765088Sab196087 	 *	%o - The original (default) path.
28775088Sab196087 	 *	%r - Root of tree holding elfedit program.
28785088Sab196087 	 *	%% - A single %
28795088Sab196087 	 *
28805088Sab196087 	 * A % followed by anything else is an error. This allows us to
28815088Sab196087 	 * add new codes in the future without backward compatability issues.
28825088Sab196087 	 */
28835088Sab196087 	len = modpath_strlen(path, origin_root);
28845088Sab196087 
28855088Sab196087 	expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1);
28865088Sab196087 	(void) modpath_expand(path, origin_root, expath);
28875088Sab196087 
28885088Sab196087 	/*
28895088Sab196087 	 * Count path segments, eliminate extra '/', and replace ':'
28905088Sab196087 	 * with NULL.
28915088Sab196087 	 */
28925088Sab196087 	state.modpath.n = 1;
28935088Sab196087 	for (src = dst = expath; *src; src++) {
28945088Sab196087 		if (*src == '/') {
28955088Sab196087 			switch (*(src + 1)) {
28965088Sab196087 			case '/':
28975088Sab196087 			case ':':
28985088Sab196087 			case '\0':
28995088Sab196087 				continue;
29005088Sab196087 			}
29015088Sab196087 		}
29025088Sab196087 		if (*src == ':') {
29035088Sab196087 			state.modpath.n++;
29045088Sab196087 			*dst = '\0';
29055088Sab196087 		} else if (src != dst) {
29065088Sab196087 			*dst = *src;
29075088Sab196087 		}
29085088Sab196087 		dst++;
29095088Sab196087 	}
29105088Sab196087 	if (src != dst)
29115088Sab196087 		*dst = '\0';
29125088Sab196087 
29135088Sab196087 	state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR),
29145088Sab196087 	    sizeof (state.modpath.seg[0]) * state.modpath.n);
29155088Sab196087 
29165088Sab196087 	src = expath;
29175088Sab196087 	for (len = 0; len < state.modpath.n; len++) {
29185088Sab196087 		if (*src == '\0') {
29195088Sab196087 			state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT);
29205088Sab196087 			src++;
29215088Sab196087 		} else {
29225088Sab196087 			state.modpath.seg[len] = src;
29235088Sab196087 			src += strlen(src) + 1;
29245088Sab196087 		}
29255088Sab196087 	}
29265088Sab196087 }
29275088Sab196087 
29285088Sab196087 /*
29295088Sab196087  * When interactive (reading commands from a tty), we catch
29305088Sab196087  * SIGINT in order to restart the outer command loop.
29315088Sab196087  */
29325088Sab196087 /*ARGSUSED*/
29335088Sab196087 static void
29345088Sab196087 sigint_handler(int sig, siginfo_t *sip, void *ucp)
29355088Sab196087 {
29365088Sab196087 	/* Jump to the outer loop to resume */
29375088Sab196087 	if (state.msg_jbuf.active) {
29385088Sab196087 		state.msg_jbuf.active = 0;
29395088Sab196087 		siglongjmp(state.msg_jbuf.env, 1);
29405088Sab196087 	}
29415088Sab196087 }
29425088Sab196087 
29435088Sab196087 
29445088Sab196087 static void
29455088Sab196087 usage(int full)
29465088Sab196087 {
29475088Sab196087 	elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF));
29485088Sab196087 	if (full) {
29495088Sab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1));
29505088Sab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2));
29515088Sab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3));
29525088Sab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4));
29535088Sab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5));
29545088Sab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6));
29555088Sab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST));
29565088Sab196087 	}
29575088Sab196087 	elfedit_exit(2);
29585088Sab196087 }
29595088Sab196087 
29605088Sab196087 
29615088Sab196087 /*
29625088Sab196087  * In order to complete commands, we need to know about them,
29635088Sab196087  * which means that we need to force all the modules to be
29645088Sab196087  * loaded. This is a relatively expensive operation, so we use
29655088Sab196087  * this function, which avoids doing it more than once in a session.
29665088Sab196087  */
29675088Sab196087 static void
29685088Sab196087 elfedit_cpl_load_modules(void)
29695088Sab196087 {
29705088Sab196087 	static int loaded;
29715088Sab196087 
29725088Sab196087 	if (!loaded) {
29735088Sab196087 		elfedit_load_modpath();
29745088Sab196087 		loaded = 1;	/* Don't do it again */
29755088Sab196087 	}
29765088Sab196087 }
29775088Sab196087 
29785088Sab196087 /*
29795088Sab196087  * Compare the token to the given string, and if they share a common
29805088Sab196087  * initial sequence, add the tail of string to the tecla command completion
29815088Sab196087  * buffer:
29825088Sab196087  *
29835088Sab196087  * entry:
29845088Sab196087  *	cpldata - Current completion state
29855088Sab196087  *	str - String to match against token
29865088Sab196087  *	casefold - True to allow case insensitive completion, False
29875088Sab196087  *		if case must match exactly.
29885088Sab196087  */
29895088Sab196087 void
29905088Sab196087 elfedit_cpl_match(void *cpldata, const char *str, int casefold)
29915088Sab196087 {
29925088Sab196087 	ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
29935088Sab196087 	const char	*cont_suffix;
29945088Sab196087 	const char	*type_suffix;
29955088Sab196087 
29965088Sab196087 	/*
29975088Sab196087 	 * Reasons to return immediately:
29985088Sab196087 	 *	- NULL strings have no completion value
29995088Sab196087 	 *	- The string is shorter than the existing item being completed
30005088Sab196087 	 */
30015088Sab196087 	if ((str == NULL) || (*str == '\0') ||
30025088Sab196087 	    ((cstate->ecpl_token_len != 0) &&
30035088Sab196087 	    ((strlen(str) < cstate->ecpl_token_len))))
30045088Sab196087 		return;
30055088Sab196087 
30065088Sab196087 	/* If the string does not share the existing prefix, don't use it */
30075088Sab196087 	if (casefold) {
30085088Sab196087 		if (strncasecmp(cstate->ecpl_token_str, str,
30095088Sab196087 		    cstate->ecpl_token_len) != 0)
30105088Sab196087 			return;
30115088Sab196087 	} else {
30125088Sab196087 		if (strncmp(cstate->ecpl_token_str, str,
30135088Sab196087 		    cstate->ecpl_token_len) != 0)
30145088Sab196087 			return;
30155088Sab196087 	}
30165088Sab196087 
30175088Sab196087 	if (cstate->ecpl_add_mod_colon) {
30185088Sab196087 		cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON);
30195088Sab196087 	} else {
30205088Sab196087 		cont_suffix = MSG_ORIG(MSG_STR_SPACE);
30215088Sab196087 		type_suffix = NULL;
30225088Sab196087 	}
30235088Sab196087 	(void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line,
30245088Sab196087 	    cstate->ecpl_word_start, cstate->ecpl_word_end,
30255088Sab196087 	    str + cstate->ecpl_token_len, type_suffix, cont_suffix);
30265088Sab196087 
30275088Sab196087 }
30285088Sab196087 
30295088Sab196087 
30305088Sab196087 /*
30316225Sab196087  * Convenience wrapper on elfedit_cpl_match(): Format an unsigned
30326225Sab196087  * 32-bit integer as a string and enter the result for command completion.
30336225Sab196087  */
30346225Sab196087 void
30356225Sab196087 elfedit_cpl_ndx(void *cpldata, uint_t ndx)
30366225Sab196087 {
30376225Sab196087 	Conv_inv_buf_t	buf;
30386225Sab196087 
30396225Sab196087 	(void) snprintf(buf.buf, sizeof (buf.buf),
30406225Sab196087 	    MSG_ORIG(MSG_FMT_WORDVAL), ndx);
30416225Sab196087 	elfedit_cpl_match(cpldata, buf.buf, 0);
30426225Sab196087 }
30436225Sab196087 
30446225Sab196087 
30456225Sab196087 /*
30465088Sab196087  * Compare the token to the names of the commands from the given module,
30475088Sab196087  * and if they share a common initial sequence, add the tail of string
30485088Sab196087  * to the tecla command completion buffer:
30495088Sab196087  *
30505088Sab196087  * entry:
30515088Sab196087  *	tok_buf - Token user has entered
30525088Sab196087  *	tok_len - strlen(tok_buf)
30535088Sab196087  *	mod - Module definition from which commands should be matched
30545088Sab196087  *	cpl, line, word_start, word_end, cont_suffix - As documented
30555088Sab196087  *		for gl_get_line() and cpl_add_completion.
30565088Sab196087  */
30575088Sab196087 static void
30585088Sab196087 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod)
30595088Sab196087 {
30605088Sab196087 	elfeditGC_cmd_t *cmd;
30615088Sab196087 	const char **cmd_name;
30625088Sab196087 
30635088Sab196087 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
30645088Sab196087 		for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
30655088Sab196087 			elfedit_cpl_match(cstate, *cmd_name, 1);
30665088Sab196087 }
30675088Sab196087 
30685088Sab196087 
30695088Sab196087 /*
30705088Sab196087  * Compare the token to the known module names, and add those that
30715088Sab196087  * match to the list of alternatives via elfedit_cpl_match().
30725088Sab196087  *
30735088Sab196087  * entry:
30745088Sab196087  *	load_all_modules - If True, causes all modules to be loaded
30755088Sab196087  *		before processing is done. If False, only the modules
30765088Sab196087  *		currently seen will be used.
30775088Sab196087  */
30785088Sab196087 void
30795088Sab196087 elfedit_cpl_module(void *cpldata, int load_all_modules)
30805088Sab196087 {
30815088Sab196087 	ELFEDIT_CPL_STATE	*cstate = (ELFEDIT_CPL_STATE *) cpldata;
30825088Sab196087 	MODLIST_T		*modlist;
30835088Sab196087 
30845088Sab196087 	if (load_all_modules)
30855088Sab196087 		elfedit_cpl_load_modules();
30865088Sab196087 
30875088Sab196087 	for (modlist = state.modlist; modlist != NULL;
30885088Sab196087 	    modlist = modlist->ml_next) {
30895088Sab196087 		elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1);
30905088Sab196087 	}
30915088Sab196087 }
30925088Sab196087 
30935088Sab196087 
30945088Sab196087 /*
30955088Sab196087  * Compare the token to all the known commands, and add those that
30965088Sab196087  * match to the list of alternatives.
30975088Sab196087  *
30985088Sab196087  * note:
30995088Sab196087  *	This routine will force modules to be loaded as necessary to
31005088Sab196087  *	obtain the names it needs to match.
31015088Sab196087  */
31025088Sab196087 void
31035088Sab196087 elfedit_cpl_command(void *cpldata)
31045088Sab196087 {
31055088Sab196087 	ELFEDIT_CPL_STATE	*cstate = (ELFEDIT_CPL_STATE *) cpldata;
31065088Sab196087 	ELFEDIT_CPL_STATE	colon_state;
31075088Sab196087 	const char		*colon_pos;
31085088Sab196087 	MODLIST_T		*modlist;
31095088Sab196087 	MODLIST_T		*insdef;
31105088Sab196087 	char			buf[128];
31115088Sab196087 
31125088Sab196087 	/*
31135088Sab196087 	 * Is there a colon in the command? If so, locate its offset within
31145088Sab196087 	 * the raw input line.
31155088Sab196087 	 */
31165088Sab196087 	for (colon_pos = cstate->ecpl_token_str;
31175088Sab196087 	    *colon_pos && (*colon_pos != ':'); colon_pos++)
31185088Sab196087 		;
31195088Sab196087 
31205088Sab196087 	/*
31215088Sab196087 	 * If no colon was seen, then we are completing a module name,
31225088Sab196087 	 * or one of the commands from 'sys:'
31235088Sab196087 	 */
31245088Sab196087 	if (*colon_pos == '\0') {
31255088Sab196087 		/*
31265088Sab196087 		 * Setting cstate->add_mod_colon tells elfedit_cpl_match()
31275088Sab196087 		 * to add an implicit ':' to the names it matches. We use it
31285088Sab196087 		 * here so the user doesn't have to enter the ':' manually.
31295088Sab196087 		 * Hiding this in the opaque state instead of making it
31305088Sab196087 		 * an argument to that function gives us the ability to
31315088Sab196087 		 * change it later without breaking the published interface.
31325088Sab196087 		 */
31335088Sab196087 		cstate->ecpl_add_mod_colon = 1;
31345088Sab196087 		elfedit_cpl_module(cpldata, 1);
31355088Sab196087 		cstate->ecpl_add_mod_colon = 0;
31365088Sab196087 
31375088Sab196087 		/* Add bare (no sys: prefix) commands from the sys: module */
31385088Sab196087 		match_module_cmds(cstate,
31395088Sab196087 		    elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0));
31405088Sab196087 
31415088Sab196087 		return;
31425088Sab196087 	}
31435088Sab196087 
31445088Sab196087 	/*
31455088Sab196087 	 * A colon was seen, so we have a module name. Extract the name,
31465088Sab196087 	 * substituting 'sys' for the case where the given name is empty.
31475088Sab196087 	 */
31485088Sab196087 	if (colon_pos == 0)
31495088Sab196087 		(void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf));
31505088Sab196087 	else
31515088Sab196087 		elfedit_strnbcpy(buf, cstate->ecpl_token_str,
31525088Sab196087 		    colon_pos - cstate->ecpl_token_str, sizeof (buf));
31535088Sab196087 
31545088Sab196087 	/*
31555088Sab196087 	 * Locate the module. If it isn't already loaded, make an explicit
31565088Sab196087 	 * attempt to load it and try again. If a module definition is
31575088Sab196087 	 * obtained, process the commands it supplies.
31585088Sab196087 	 */
31595088Sab196087 	modlist = module_loaded(buf, &insdef);
31605088Sab196087 	if (modlist == NULL) {
31615088Sab196087 		(void) elfedit_load_module(buf, 0, 0);
31625088Sab196087 		modlist = module_loaded(buf, &insdef);
31635088Sab196087 	}
31645088Sab196087 	if (modlist != NULL) {
31655088Sab196087 		/*
31665088Sab196087 		 * Make a copy of the cstate, and adjust the line and
31675088Sab196087 		 * token so that the new one starts just past the colon
31685088Sab196087 		 * character. We know that the colon exists because
31695088Sab196087 		 * of the preceeding test that found it. Therefore, we do
31705088Sab196087 		 * not need to test against running off the end of the
31715088Sab196087 		 * string here.
31725088Sab196087 		 */
31735088Sab196087 		colon_state = *cstate;
31745088Sab196087 		while (colon_state.ecpl_line[colon_state.ecpl_word_start] !=
31755088Sab196087 		    ':')
31765088Sab196087 			colon_state.ecpl_word_start++;
31775088Sab196087 		while (*colon_state.ecpl_token_str != ':') {
31785088Sab196087 			colon_state.ecpl_token_str++;
31795088Sab196087 			colon_state.ecpl_token_len--;
31805088Sab196087 		}
31815088Sab196087 		/* Skip past the ':' character */
31825088Sab196087 		colon_state.ecpl_word_start++;
31835088Sab196087 		colon_state.ecpl_token_str++;
31845088Sab196087 		colon_state.ecpl_token_len--;
31855088Sab196087 
31865088Sab196087 		match_module_cmds(&colon_state, modlist->ml_mod);
31875088Sab196087 	}
31885088Sab196087 }
31895088Sab196087 
31905088Sab196087 
31915088Sab196087 /*
31925088Sab196087  * Command completion function for use with libtacla.
31935088Sab196087  */
31945088Sab196087 /*ARGSUSED1*/
31955088Sab196087 static int
31965088Sab196087 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end)
31975088Sab196087 {
31985088Sab196087 	const char		*argv[ELFEDIT_MAXCPLARGS];
31995088Sab196087 	ELFEDIT_CPL_STATE	cstate;
32005088Sab196087 	TOK_STATE		*tokst;
32015088Sab196087 	int			ndx;
32025088Sab196087 	int			i;
32035088Sab196087 	elfeditGC_module_t	*mod;
32045088Sab196087 	elfeditGC_cmd_t		*cmd;
32055088Sab196087 	int			num_opt;
32065088Sab196087 	int			opt_term_seen;
32075088Sab196087 	int			skip_one;
32085088Sab196087 	elfedit_cmd_optarg_t	*optarg;
32095088Sab196087 	elfedit_optarg_item_t	item;
32105088Sab196087 	int			ostyle_ndx = -1;
32115088Sab196087 
32125088Sab196087 	/*
32135088Sab196087 	 * For debugging, enable the following block. It tells the tecla
32145088Sab196087 	 * library that the program using is going to write to stdout.
32155088Sab196087 	 * It will put the tty back into normal mode, and it will cause
32165088Sab196087 	 * tecla to redraw the current input line when it gets control back.
32175088Sab196087 	 */
32185892Sab196087 #ifdef DEBUG_CMD_MATCH
32195088Sab196087 	gl_normal_io(state.input.gl);
32205088Sab196087 #endif
32215088Sab196087 
32225088Sab196087 	/*
32235088Sab196087 	 * Tokenize the line up through word_end. The last token in
32245088Sab196087 	 * the list is the one requiring completion.
32255088Sab196087 	 */
32265088Sab196087 	tokst = tokenize_user_cmd(line, word_end, 1);
32275088Sab196087 	if (tokst->tokst_cnt == 0)
32285088Sab196087 		return (0);
32295088Sab196087 
32305088Sab196087 	/* Set up the cstate block, containing the completion state */
32315088Sab196087 	ndx = tokst->tokst_cnt - 1;	/* Index of token to complete */
32325088Sab196087 	cstate.ecpl_cpl = cpl;
32335088Sab196087 	cstate.ecpl_line = line;
32345088Sab196087 	cstate.ecpl_word_start = tokst->tokst_buf[ndx].tok_line_off;
32355088Sab196087 	cstate.ecpl_word_end = word_end;
32365088Sab196087 	cstate.ecpl_add_mod_colon = 0;
32375088Sab196087 	cstate.ecpl_token_str = tokst->tokst_buf[ndx].tok_str;
32385088Sab196087 	cstate.ecpl_token_len = tokst->tokst_buf[ndx].tok_len;
32395088Sab196087 
32405088Sab196087 	/*
32415088Sab196087 	 * If there is only one token, then we are completing the
32425088Sab196087 	 * command itself.
32435088Sab196087 	 */
32445088Sab196087 	if (ndx == 0) {
32455088Sab196087 		elfedit_cpl_command(&cstate);
32465088Sab196087 		return (0);
32475088Sab196087 	}
32485088Sab196087 
32495088Sab196087 	/*
32505088Sab196087 	 * There is more than one token. Use the first one to
32515088Sab196087 	 * locate the definition for the command. If we don't have
32525088Sab196087 	 * a definition for the command, then there's nothing more
32535088Sab196087 	 * we can do.
32545088Sab196087 	 */
32555088Sab196087 	cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 0, &mod);
32565088Sab196087 	if (cmd == NULL)
32575088Sab196087 		return (0);
32585088Sab196087 
32595088Sab196087 	/*
32605088Sab196087 	 * Since we know the command, give them a quick usage message.
32615088Sab196087 	 * It may be that they just need a quick reminder about the form
32625088Sab196087 	 * of the command and the options.
32635088Sab196087 	 */
32645088Sab196087 	(void) gl_normal_io(state.input.gl);
32655088Sab196087 	elfedit_printf(MSG_INTL(MSG_USAGE_CMD),
32665088Sab196087 	    elfedit_format_command_usage(mod, cmd, NULL, 0));
32675088Sab196087 
32685088Sab196087 
32695088Sab196087 	/*
32705088Sab196087 	 * We have a generous setting for ELFEDIT_MAXCPLARGS, so there
32715088Sab196087 	 * should always be plenty of room. If there's not room, we
32725088Sab196087 	 * can't proceed.
32735088Sab196087 	 */
32745088Sab196087 	if (ndx >= ELFEDIT_MAXCPLARGS)
32755088Sab196087 		return (0);
32765088Sab196087 
32775088Sab196087 	/*
32785088Sab196087 	 * Put pointers to the tokens into argv, and determine how
32795088Sab196087 	 * many of the tokens are optional arguments.
32805088Sab196087 	 *
32815088Sab196087 	 * We consider the final optional argument to be the rightmost
32825088Sab196087 	 * argument that starts with a '-'. If a '--' is seen, then
32835088Sab196087 	 * we stop there, and any argument that follows is a plain argument
32845088Sab196087 	 * (even if it starts with '-').
32855088Sab196087 	 *
32865088Sab196087 	 * We look for an inherited '-o' option, because we are willing
32875088Sab196087 	 * to supply command completion for these values.
32885088Sab196087 	 */
32895088Sab196087 	num_opt = 0;
32905088Sab196087 	opt_term_seen = 0;
32915088Sab196087 	skip_one = 0;
32925088Sab196087 	for (i = 0; i < ndx; i++) {
32935088Sab196087 		argv[i] = tokst->tokst_buf[i + 1].tok_str;
32945088Sab196087 		if (opt_term_seen || skip_one) {
32955088Sab196087 			skip_one = 0;
32965088Sab196087 			continue;
32975088Sab196087 		}
32985088Sab196087 		skip_one = 0;
32995088Sab196087 		ostyle_ndx = -1;
33005088Sab196087 		if ((strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_MINUS)) == NULL) ||
33015088Sab196087 		    (*argv[i] != '-')) {
33025088Sab196087 			opt_term_seen = 1;
33035088Sab196087 			continue;
33045088Sab196087 		}
33055088Sab196087 		num_opt = i + 1;
33065088Sab196087 		/*
33075088Sab196087 		 * If it is a recognised ELFEDIT_CMDOA_F_VALUE option,
33085088Sab196087 		 * then the item following it is the associated value.
33095088Sab196087 		 * Check for this and skip the value.
33105088Sab196087 		 *
33115088Sab196087 		 * At the same time, look for STDOA_OPT_O inherited
33125088Sab196087 		 * options. We want to identify the index of any such
33135088Sab196087 		 * item. Although the option is simply "-o", we are willing
33145088Sab196087 		 * to treat any option that starts with "-o" as a potential
33155088Sab196087 		 * STDOA_OPT_O. This lets us to command completion for things
33165088Sab196087 		 * like "-onum", and is otherwise harmless, the only cost
33175088Sab196087 		 * being a few additional strcmps by the cpl code.
33185088Sab196087 		 */
33195088Sab196087 		if ((optarg = cmd->cmd_opt) == NULL)
33205088Sab196087 			continue;
33215088Sab196087 		while (optarg->oa_name != NULL) {
33225088Sab196087 			int is_ostyle_optarg =
33235088Sab196087 			    (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) &&
33245088Sab196087 			    (optarg->oa_name == ELFEDIT_STDOA_OPT_O);
33255088Sab196087 
33265088Sab196087 			elfedit_next_optarg(&optarg, &item);
33275088Sab196087 			if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) {
33285088Sab196087 				if (is_ostyle_optarg && (strncmp(argv[i],
33295088Sab196087 				    MSG_ORIG(MSG_STR_MINUS_O), 2) == 0))
33305088Sab196087 					ostyle_ndx = i + 1;
33315088Sab196087 
33325088Sab196087 				if (strcmp(item.oai_name, argv[i]) == 0) {
33335088Sab196087 					num_opt = i + 2;
33345088Sab196087 					skip_one = 1;
33355088Sab196087 					break;
33365088Sab196087 				}
33375088Sab196087 				/*
33385088Sab196087 				 * If it didn't match "-o" exactly, but it is
33395088Sab196087 				 * ostyle_ndx, then it is a potential combined
33405088Sab196087 				 * STDOA_OPT_O, as discussed above. It counts
33415088Sab196087 				 * as a single argument.
33425088Sab196087 				 */
33435088Sab196087 				if (ostyle_ndx == ndx)
33445088Sab196087 					break;
33455088Sab196087 			}
33465088Sab196087 		}
33475088Sab196087 	}
33485088Sab196087 
33495892Sab196087 #ifdef DEBUG_CMD_MATCH
33505892Sab196087 	(void) printf("NDX(%d) NUM_OPT(%d) ostyle_ndx(%d)\n", ndx, num_opt,
33515088Sab196087 	    ostyle_ndx);
33525088Sab196087 #endif
33535088Sab196087 
33545088Sab196087 	if (ostyle_ndx != -1) {
33555088Sab196087 		/*
33565088Sab196087 		 * If ostyle_ndx is one less than ndx, and ndx is
33575088Sab196087 		 * the same as num_opt, then we have a definitive
33585088Sab196087 		 * STDOA_OPT_O inherited outstyle option. We supply
33595088Sab196087 		 * the value strings, and are done.
33605088Sab196087 		 */
33615088Sab196087 		if ((ostyle_ndx == (ndx - 1)) && (ndx == num_opt)) {
33625088Sab196087 			elfedit_cpl_atoconst(&cstate, ELFEDIT_CONST_OUTSTYLE);
33635088Sab196087 			return (0);
33645088Sab196087 		}
33655088Sab196087 
33665088Sab196087 		/*
33675088Sab196087 		 * If ostyle is the same as ndx, then we have an option
33685088Sab196087 		 * staring with "-o" that may end up being a STDOA_OPT_O,
33695088Sab196087 		 * and we are still inside that token. In this case, we
33705088Sab196087 		 * supply completion strings that include the leading
33715088Sab196087 		 * "-o" followed by the values, without a space
33725088Sab196087 		 * (i.e. "-onum"). We then fall through, allowing any
33735088Sab196087 		 * other options starting with "-o" to be added
33745088Sab196087 		 * below. elfedit_cpl_match() will throw out the incorrect
33755088Sab196087 		 * options, so it is harmless to add these extra items in
33765088Sab196087 		 * the worst case, and useful otherwise.
33775088Sab196087 		 */
33785088Sab196087 		if (ostyle_ndx == ndx)
33795088Sab196087 			elfedit_cpl_atoconst(&cstate,
33805088Sab196087 			    ELFEDIT_CONST_OUTSTYLE_MO);
33815088Sab196087 	}
33825088Sab196087 
33835088Sab196087 	/*
33845088Sab196087 	 * If (ndx <= num_opt), then the token needing completion
33855088Sab196087 	 * is an option. If the leading '-' is there, then we should fill
33865088Sab196087 	 * in all of the option alternatives. If anything follows the '-'
33875088Sab196087 	 * though, we assume that the user has already figured out what
33885088Sab196087 	 * option to use, and we leave well enough alone.
33895088Sab196087 	 *
33905088Sab196087 	 * Note that we are intentionally ignoring a related case
33915088Sab196087 	 * where supplying option strings would be legal: In the case
33925088Sab196087 	 * where we are one past the last option (ndx == (num_opt + 1)),
33935088Sab196087 	 * and the current option is an empty string, the argument can
33945088Sab196087 	 * be either a plain argument or an option --- the user needs to
33955088Sab196087 	 * enter the next character before we can tell. It would be
33965088Sab196087 	 * OK to enter the option strings in this case. However, consider
33975088Sab196087 	 * what happens when the first plain argument to the command does
33985088Sab196087 	 * not provide any command completion (e.g. it is a plain integer).
33995088Sab196087 	 * In this case, tecla will see that all the alternatives start
34005088Sab196087 	 * with '-', and will insert a '-' into the input. If the user
34015088Sab196087 	 * intends the next argument to be plain, they will have to delete
34025088Sab196087 	 * this '-', which is annoying. Worse than that, they may be confused
34035088Sab196087 	 * by it, and think that the plain argument is not allowed there.
34045088Sab196087 	 * The best solution is to not supply option strings unless the
34055088Sab196087 	 * user first enters the '-'.
34065088Sab196087 	 */
34075088Sab196087 	if ((ndx <= num_opt) && (argv[ndx - 1][0] == '-')) {
34085088Sab196087 		if ((optarg = cmd->cmd_opt) != NULL) {
34095088Sab196087 			while (optarg->oa_name != NULL) {
34105088Sab196087 				elfedit_next_optarg(&optarg, &item);
34115088Sab196087 				elfedit_cpl_match(&cstate, item.oai_name, 1);
34125088Sab196087 			}
34135088Sab196087 		}
34145088Sab196087 		return (0);
34155088Sab196087 	}
34165088Sab196087 
34175088Sab196087 	/*
34185088Sab196087 	 * At this point we know that ndx and num_opt are not equal.
34195088Sab196087 	 * If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE
34205088Sab196087 	 * argument at the end, and the following value has not been entered.
34215088Sab196087 	 *
34225088Sab196087 	 * If ndx is greater than num_opt, it means that we are looking
34235088Sab196087 	 * at a plain argument (or in the case where (ndx == (num_opt + 1)),
34245088Sab196087 	 * a *potential* plain argument.
34255088Sab196087 	 *
34265088Sab196087 	 * If the command has a completion function registered, then we
34275088Sab196087 	 * hand off the remaining work to it. The cmd_cplfunc field is
34285088Sab196087 	 * the generic definition. We need to cast it to the type that matches
34295088Sab196087 	 * the proper ELFCLASS before calling it.
34305088Sab196087 	 */
34315088Sab196087 	if (state.elf.elfclass == ELFCLASS32) {
34325088Sab196087 		elfedit32_cmdcpl_func_t *cmdcpl_func =
34335088Sab196087 		    (elfedit32_cmdcpl_func_t *)cmd->cmd_cplfunc;
34345088Sab196087 
34355088Sab196087 		if (cmdcpl_func != NULL)
34365088Sab196087 			(* cmdcpl_func)(state.elf.obj_state.s32,
34375088Sab196087 			    &cstate, ndx, argv, num_opt);
34385088Sab196087 	} else {
34395088Sab196087 		elfedit64_cmdcpl_func_t *cmdcpl_func =
34405088Sab196087 		    (elfedit64_cmdcpl_func_t *)cmd->cmd_cplfunc;
34415088Sab196087 
34425088Sab196087 		if (cmdcpl_func != NULL)
34435088Sab196087 			(* cmdcpl_func)(state.elf.obj_state.s64,
34445088Sab196087 			    &cstate, ndx, argv, num_opt);
34455088Sab196087 	}
34465088Sab196087 
34475088Sab196087 	return (0);
34485088Sab196087 }
34495088Sab196087 
34505088Sab196087 
34515088Sab196087 /*
34525088Sab196087  * Read a line of input from stdin, and return pointer to it.
34535088Sab196087  *
34545088Sab196087  * This routine uses a private buffer, so the contents of the returned
34555088Sab196087  * string are only good until the next call.
34565088Sab196087  */
34575088Sab196087 static const char *
34585088Sab196087 read_cmd(void)
34595088Sab196087 {
34605088Sab196087 	char *s;
34615088Sab196087 
34625088Sab196087 	if (state.input.full_tty) {
34635088Sab196087 		state.input.in_tecla = TRUE;
34645088Sab196087 		s = gl_get_line(state.input.gl,
34655088Sab196087 		    MSG_ORIG(MSG_STR_PROMPT), NULL, -1);
34665088Sab196087 		state.input.in_tecla = FALSE;
34675088Sab196087 		/*
34685088Sab196087 		 * gl_get_line() returns NULL for EOF or for error. EOF is fine,
34695088Sab196087 		 * but we need to catch and report anything else. Since
34705088Sab196087 		 * reading from stdin is critical to our operation, an
34715088Sab196087 		 * error implies that we cannot recover and must exit.
34725088Sab196087 		 */
34735088Sab196087 		if ((s == NULL) &&
34745088Sab196087 		    (gl_return_status(state.input.gl) == GLR_ERROR)) {
34755088Sab196087 			elfedit_msg(ELFEDIT_MSG_FATAL, MSG_INTL(MSG_ERR_GLREAD),
34765088Sab196087 			    gl_error_message(state.input.gl, NULL, 0));
34775088Sab196087 		}
34785088Sab196087 	} else {
34795088Sab196087 		/*
34805088Sab196087 		 * This should be a dynamically sized buffer, but for now,
34815088Sab196087 		 * I'm going to take a simpler path.
34825088Sab196087 		 */
34835088Sab196087 		static char cmd_buf[ELFEDIT_MAXCMD + 1];
34845088Sab196087 
34855088Sab196087 		s = fgets(cmd_buf, sizeof (cmd_buf), stdin);
34865088Sab196087 	}
34875088Sab196087 
34885088Sab196087 	/* Return user string, or 'quit' on EOF */
34895088Sab196087 	return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT));
34905088Sab196087 }
34915088Sab196087 
34925088Sab196087 int
34935088Sab196087 main(int argc, char **argv, char **envp)
34945088Sab196087 {
34955088Sab196087 	/*
34965088Sab196087 	 * Note: This function can use setjmp()/longjmp() which does
34975088Sab196087 	 * not preserve the values of auto/register variables. Hence,
34985088Sab196087 	 * variables that need their values preserved across a jump must
34995088Sab196087 	 * be marked volatile, or must not be auto/register.
35005088Sab196087 	 *
35015088Sab196087 	 * Volatile can be messy, because it requires explictly casting
35025088Sab196087 	 * away the attribute when passing it to functions, or declaring
35035088Sab196087 	 * those functions with the attribute as well. In a single threaded
35045088Sab196087 	 * program like this one, an easier approach is to make things
35055088Sab196087 	 * static. That can be done here, or by putting things in the
35065088Sab196087 	 * 'state' structure.
35075088Sab196087 	 */
35085088Sab196087 
35095088Sab196087 	int		c, i;
35105088Sab196087 	int		num_batch = 0;
35115088Sab196087 	char		**batch_list = NULL;
35125088Sab196087 	const char	*modpath = NULL;
35135088Sab196087 
35145088Sab196087 	/*
35155088Sab196087 	 * Always have liblddb display unclipped section names.
35165088Sab196087 	 * This global is exported by liblddb, and declared in debug.h.
35175088Sab196087 	 */
35185088Sab196087 	dbg_desc->d_extra |= DBG_E_LONG;
35195088Sab196087 
35205088Sab196087 	opterr = 0;
35215088Sab196087 	while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
35225088Sab196087 		switch (c) {
35235088Sab196087 		case 'a':
35245088Sab196087 			state.flags |= ELFEDIT_F_AUTOPRINT;
35255088Sab196087 			break;
35265088Sab196087 
35275088Sab196087 		case 'd':
35285088Sab196087 			state.flags |= ELFEDIT_F_DEBUG;
35295088Sab196087 			break;
35305088Sab196087 
35315088Sab196087 		case 'e':
35325088Sab196087 			/*
35335088Sab196087 			 * Delay parsing the -e options until after the call to
35345088Sab196087 			 * conv_check_native() so that we won't bother loading
35355088Sab196087 			 * modules of the wrong class.
35365088Sab196087 			 */
35375088Sab196087 			if (batch_list == NULL)
35385088Sab196087 				batch_list = elfedit_malloc(
35395088Sab196087 				    MSG_INTL(MSG_ALLOC_BATCHLST),
35405088Sab196087 				    sizeof (*batch_list) * (argc - 1));
35415088Sab196087 			batch_list[num_batch++] = optarg;
35425088Sab196087 			break;
35435088Sab196087 
35445088Sab196087 		case 'L':
35455088Sab196087 			modpath = optarg;
35465088Sab196087 			break;
35475088Sab196087 
35485088Sab196087 		case 'o':
35495088Sab196087 			if (elfedit_atooutstyle(optarg, &state.outstyle) == 0)
35505088Sab196087 				usage(1);
35515088Sab196087 			break;
35525088Sab196087 
35535088Sab196087 		case 'r':
35545088Sab196087 			state.flags |= ELFEDIT_F_READONLY;
35555088Sab196087 			break;
35565088Sab196087 
35575088Sab196087 		case '?':
35585088Sab196087 			usage(1);
35595088Sab196087 		}
35605088Sab196087 	}
35615088Sab196087 
35625088Sab196087 	/*
35635088Sab196087 	 * We allow 0, 1, or 2 files:
35645088Sab196087 	 *
35655088Sab196087 	 * The no-file case is an extremely limited mode, in which the
35665088Sab196087 	 * only commands allowed to execute come from the sys: module.
35675088Sab196087 	 * This mode exists primarily to allow easy access to the help
35685088Sab196087 	 * facility.
35695088Sab196087 	 *
35705088Sab196087 	 * To get full access to elfedit's capablities, there must
35715088Sab196087 	 * be an input file. If this is not a readonly
35725088Sab196087 	 * session, then an optional second output file is allowed.
35735088Sab196087 	 *
35745088Sab196087 	 * In the case where two files are given and the session is
35755088Sab196087 	 * readonly, use a full usage message, because the simple
35765088Sab196087 	 * one isn't enough for the user to understand their error.
35775088Sab196087 	 * Otherwise, the simple usage message suffices.
35785088Sab196087 	 */
35795088Sab196087 	argc = argc - optind;
35805088Sab196087 	if ((argc == 2) && (state.flags & ELFEDIT_F_READONLY))
35815088Sab196087 		usage(1);
35825088Sab196087 	if (argc > 2)
35835088Sab196087 		usage(0);
35845088Sab196087 
35855088Sab196087 	state.file.present = (argc != 0);
35865088Sab196087 
35875088Sab196087 	/*
35885088Sab196087 	 * If we have a file to edit, and unless told otherwise by the
35895088Sab196087 	 * caller, we try to run the 64-bit version of this program
35905088Sab196087 	 * when the system is capable of it. If that fails, then we
35915088Sab196087 	 * continue on with the currently running version.
35925088Sab196087 	 *
35935088Sab196087 	 * To force 32-bit execution on a 64-bit host, set the
35945088Sab196087 	 * LD_NOEXEC_64 environment variable to a non-empty value.
35955088Sab196087 	 *
35965088Sab196087 	 * There is no reason to bother with this if in "no file" mode.
35975088Sab196087 	 */
35985088Sab196087 	if (state.file.present != 0)
35995088Sab196087 		(void) conv_check_native(argv, envp);
36005088Sab196087 
36015088Sab196087 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_VERSION),
36025088Sab196087 	    (sizeof (char *) == 8) ? 64 : 32);
36035088Sab196087 
36045088Sab196087 	/*
36055088Sab196087 	 * Put a module definition for the builtin system module on the
36065088Sab196087 	 * module list. We know it starts out empty, so we do not have
36075088Sab196087 	 * to go through a more general insertion process than this.
36085088Sab196087 	 */
36095088Sab196087 	state.modlist = elfedit_sys_init(ELFEDIT_VER_CURRENT);
36105088Sab196087 
36115088Sab196087 	/* Establish the search path for loadable modules */
36125088Sab196087 	establish_modpath(modpath);
36135088Sab196087 
36145088Sab196087 	/*
36155088Sab196087 	 * Now that we are running the final version of this program,
36165088Sab196087 	 * deal with the input/output file(s).
36175088Sab196087 	 */
36185088Sab196087 	if (state.file.present == 0) {
36195088Sab196087 		/*
36205088Sab196087 		 * This is arbitrary --- we simply need to be able to
36215088Sab196087 		 * load modules so that we can access their help strings
36225088Sab196087 		 * and command completion functions. Without a file, we
36235088Sab196087 		 * will refuse to call commands from any module other
36245088Sab196087 		 * than sys. Those commands have been written to be aware
36255088Sab196087 		 * of the case where there is no input file, and are
36265088Sab196087 		 * therefore safe to run.
36275088Sab196087 		 */
36285088Sab196087 		state.elf.elfclass = ELFCLASS32;
36295088Sab196087 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NOFILE));
36305088Sab196087 
36315088Sab196087 	} else {
36325088Sab196087 		state.file.infile = argv[optind];
36335088Sab196087 		if (argc == 1) {
36345088Sab196087 			state.file.outfile = state.file.infile;
36355088Sab196087 			if (state.flags & ELFEDIT_F_READONLY)
36365088Sab196087 				elfedit_msg(ELFEDIT_MSG_DEBUG,
36375088Sab196087 				    MSG_INTL(MSG_DEBUG_READONLY));
36385088Sab196087 			else
36395088Sab196087 				elfedit_msg(ELFEDIT_MSG_DEBUG,
36405088Sab196087 				    MSG_INTL(MSG_DEBUG_INPLACEWARN),
36415088Sab196087 				    state.file.infile);
36425088Sab196087 		} else {
36435088Sab196087 			state.file.outfile = argv[optind + 1];
36445088Sab196087 			create_outfile(state.file.infile, state.file.outfile);
36455088Sab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
36465088Sab196087 			    MSG_INTL(MSG_DEBUG_CPFILE),
36475088Sab196087 			    state.file.infile, state.file.outfile);
36485088Sab196087 			/*
36495088Sab196087 			 * We are editing a copy of the original file that we
36505088Sab196087 			 * just created. If we should exit before the edits are
36515088Sab196087 			 * updated, then we want to unlink this copy so that we
36525088Sab196087 			 * don't leave junk lying around. Once an update
36535088Sab196087 			 * succeeds however, we'll leave it in place even
36545088Sab196087 			 * if an error occurs afterwards.
36555088Sab196087 			 */
36565088Sab196087 			state.file.unlink_on_exit = 1;
36575088Sab196087 			optind++;	/* Edit copy instead of the original */
36585088Sab196087 		}
36595088Sab196087 
36605088Sab196087 		init_obj_state(state.file.outfile);
36615088Sab196087 	}
36625088Sab196087 
36635088Sab196087 
36645088Sab196087 	/*
36655088Sab196087 	 * Process commands.
36665088Sab196087 	 *
36675088Sab196087 	 * If any -e options were used, then do them and
36685088Sab196087 	 * immediately exit. On error, exit immediately without
36695088Sab196087 	 * updating the target ELF file. On success, the 'write'
36705088Sab196087 	 * and 'quit' commands are implicit in this mode.
36715088Sab196087 	 *
36725088Sab196087 	 * If no -e options are used, read commands from stdin.
36735088Sab196087 	 * quit must be explicitly used. Exit is implicit on EOF.
36745088Sab196087 	 * If stdin is a tty, then errors do not cause the editor
36755088Sab196087 	 * to terminate. Rather, the error message is printed, and the
36765088Sab196087 	 * user prompted to continue.
36775088Sab196087 	 */
36785088Sab196087 	if (batch_list != NULL) {	/* -e was used */
36795088Sab196087 		/* Compile the commands */
36805088Sab196087 		for (i = 0; i < num_batch; i++)
36815088Sab196087 			parse_user_cmd(batch_list[i]);
36825088Sab196087 		free(batch_list);
36835088Sab196087 
36845088Sab196087 		/*
36855088Sab196087 		 * 'write' and 'quit' are implicit in this mode.
36865088Sab196087 		 * Add them as well.
36875088Sab196087 		 */
36885088Sab196087 		if ((state.flags & ELFEDIT_F_READONLY) == 0)
36895088Sab196087 			parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_WRITE));
36905088Sab196087 		parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_QUIT));
36915088Sab196087 
36925088Sab196087 		/* And run them. This won't return, thanks to the 'quit' */
36935088Sab196087 		dispatch_user_cmds();
36945088Sab196087 	} else {
36955088Sab196087 		state.input.is_tty = isatty(fileno(stdin));
36965088Sab196087 		state.input.full_tty = state.input.is_tty &&
36975088Sab196087 		    isatty(fileno(stdout));
36985088Sab196087 
36995088Sab196087 		if (state.input.full_tty) {
37005088Sab196087 			struct sigaction act;
37015088Sab196087 
37025088Sab196087 			act.sa_sigaction = sigint_handler;
37035088Sab196087 			(void) sigemptyset(&act.sa_mask);
37045088Sab196087 			act.sa_flags = 0;
37055088Sab196087 			if (sigaction(SIGINT, &act, NULL) == -1) {
37065088Sab196087 				int err = errno;
37075088Sab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
37085088Sab196087 				    MSG_INTL(MSG_ERR_SIGACTION), strerror(err));
37095088Sab196087 			}
37105088Sab196087 			/*
37115088Sab196087 			 * If pager process exits before we are done
37125088Sab196087 			 * writing, we can see SIGPIPE. Prevent it
37135088Sab196087 			 * from killing the process.
37145088Sab196087 			 */
37155088Sab196087 			(void) sigignore(SIGPIPE);
37165088Sab196087 
37175088Sab196087 			/* Open tecla handle for command line editing */
37185088Sab196087 			state.input.gl = new_GetLine(ELFEDIT_MAXCMD,
37195088Sab196087 			    ELFEDIT_MAXHIST);
37205088Sab196087 			/* Register our command completion function */
37215088Sab196087 			(void) gl_customize_completion(state.input.gl,
37225088Sab196087 			    NULL, cmd_match_fcn);
37235088Sab196087 
37245088Sab196087 			/*
37255088Sab196087 			 * Make autoprint the default for interactive
37265088Sab196087 			 * sessions.
37275088Sab196087 			 */
37285088Sab196087 			state.flags |= ELFEDIT_F_AUTOPRINT;
37295088Sab196087 		}
37305088Sab196087 		for (;;) {
37315088Sab196087 			/*
37325088Sab196087 			 * If this is an interactive session, then use
37335088Sab196087 			 * sigsetjmp()/siglongjmp() to recover from bad
37345088Sab196087 			 * commands and keep going. A non-0 return from
37355088Sab196087 			 * sigsetjmp() means that an error just occurred.
37365088Sab196087 			 * In that case, we simply restart this loop.
37375088Sab196087 			 */
37385088Sab196087 			if (state.input.is_tty) {
37395088Sab196087 				if (sigsetjmp(state.msg_jbuf.env, 1) != 0) {
37405088Sab196087 					if (state.input.full_tty)
37415088Sab196087 						gl_abandon_line(state.input.gl);
37425088Sab196087 					continue;
37435088Sab196087 				}
37445088Sab196087 				state.msg_jbuf.active = TRUE;
37455088Sab196087 			}
37465088Sab196087 
37475088Sab196087 			/*
37485088Sab196087 			 * Force all output out before each command.
37495088Sab196087 			 * This is a no-OP when a tty is in use, but
37505088Sab196087 			 * in a pipeline, it ensures that the block
37515088Sab196087 			 * mode buffering doesn't delay output past
37525088Sab196087 			 * the completion of each command.
37535088Sab196087 			 *
37545088Sab196087 			 * If we didn't do this, the output would eventually
37555088Sab196087 			 * arrive at its destination, but the lag can be
37565088Sab196087 			 * annoying when you pipe the output into a tool
37575088Sab196087 			 * that displays the results in real time.
37585088Sab196087 			 */
37595088Sab196087 			(void) fflush(stdout);
37605088Sab196087 			(void) fflush(stderr);
37615088Sab196087 
37625088Sab196087 			parse_user_cmd(read_cmd());
37635088Sab196087 			dispatch_user_cmds();
37645088Sab196087 			state.msg_jbuf.active = FALSE;
37655088Sab196087 		}
37665088Sab196087 	}
37675088Sab196087 
37685088Sab196087 
37695088Sab196087 	/*NOTREACHED*/
37705088Sab196087 	return (0);
37715088Sab196087 }
3772