Home | History | Annotate | Download | only in common
      1  5088  ab196087 /*
      2  5088  ab196087  * CDDL HEADER START
      3  5088  ab196087  *
      4  5088  ab196087  * The contents of this file are subject to the terms of the
      5  5088  ab196087  * Common Development and Distribution License (the "License").
      6  5088  ab196087  * You may not use this file except in compliance with the License.
      7  5088  ab196087  *
      8  5088  ab196087  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  5088  ab196087  * or http://www.opensolaris.org/os/licensing.
     10  5088  ab196087  * See the License for the specific language governing permissions
     11  5088  ab196087  * and limitations under the License.
     12  5088  ab196087  *
     13  5088  ab196087  * When distributing Covered Code, include this CDDL HEADER in each
     14  5088  ab196087  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  5088  ab196087  * If applicable, add the following below this CDDL HEADER, with the
     16  5088  ab196087  * fields enclosed by brackets "[]" replaced with your own identifying
     17  5088  ab196087  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  5088  ab196087  *
     19  5088  ab196087  * CDDL HEADER END
     20  5088  ab196087  */
     21  5088  ab196087 
     22  5088  ab196087 /*
     23  9273       Ali  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  5088  ab196087  * Use is subject to license terms.
     25  5088  ab196087  */
     26  5088  ab196087 
     27  5088  ab196087 #include	<sys/types.h>
     28  5088  ab196087 #include	<sys/stat.h>
     29  5088  ab196087 #include	<sys/wait.h>
     30  5088  ab196087 #include	<stdarg.h>
     31  5088  ab196087 #include	<fcntl.h>
     32  5088  ab196087 #include	<stdlib.h>
     33  5088  ab196087 #include	<stdio.h>
     34  5088  ab196087 #include	<signal.h>
     35  5088  ab196087 #include	<dirent.h>
     36  5088  ab196087 #include	<libelf.h>
     37  5088  ab196087 #include	<gelf.h>
     38  5088  ab196087 #include	<conv.h>
     39  5088  ab196087 #include	<dlfcn.h>
     40  5088  ab196087 #include	<link.h>
     41  5088  ab196087 #include	<stdarg.h>
     42  5088  ab196087 #include	<libgen.h>
     43  5088  ab196087 #include	<libintl.h>
     44  5088  ab196087 #include	<locale.h>
     45  5088  ab196087 #include	<unistd.h>
     46  5088  ab196087 #include	<errno.h>
     47  5088  ab196087 #include	<ctype.h>
     48  5088  ab196087 #include	<limits.h>
     49  5088  ab196087 #include	<strings.h>
     50  5088  ab196087 #include	<sgs.h>
     51  5088  ab196087 #include	"msg.h"
     52  5088  ab196087 #include	"_elfedit.h"
     53  5088  ab196087 #include	<debug.h>	/* liblddb */
     54  5088  ab196087 
     55  5088  ab196087 
     56  5088  ab196087 
     57  5088  ab196087 /*
     58  5088  ab196087  * Column at which elfedit_format_command_usage() will wrap the
     59  5088  ab196087  * generated usage string if the wrap argument is True (1).
     60  5088  ab196087  */
     61  5088  ab196087 #define	USAGE_WRAP_COL 55
     62  5088  ab196087 
     63  5088  ab196087 
     64  5088  ab196087 
     65  5088  ab196087 
     66  5088  ab196087 /*
     67  5088  ab196087  * Type used to represent a string buffer that can grow as needed
     68  5088  ab196087  * to hold strings of arbitrary length. The user should declare
     69  5088  ab196087  * variables of this type sa static. The strbuf_ensure_size() function
     70  5088  ab196087  * is used to ensure that it has a minimum desired size.
     71  5088  ab196087  */
     72  5088  ab196087 typedef struct {
     73  5088  ab196087 	char *buf;		/* String buffer */
     74  5088  ab196087 	size_t n;		/* Size of buffer */
     75  5088  ab196087 } STRBUF;
     76  5088  ab196087 
     77  5088  ab196087 
     78  5088  ab196087 
     79  5088  ab196087 
     80  5088  ab196087 /*
     81  5088  ab196087  * Types used by tokenize_user_cmd() to represent the result of
     82  5088  ab196087  * spliting a user command into individual tokens.
     83  5088  ab196087  */
     84  5088  ab196087 typedef struct {
     85  5088  ab196087 	char	*tok_str;	/* Token string */
     86  5088  ab196087 	size_t	tok_len;	/* strlen(str) */
     87  5088  ab196087 	size_t	tok_line_off;	/* Token offset in original string */
     88  5088  ab196087 } TOK_ELT;
     89  5088  ab196087 typedef struct {
     90  5088  ab196087 	size_t	tokst_cmd_len;	/* Length of original user command, without */
     91  5088  ab196087 				/*	newline or NULL termination chars */
     92  5088  ab196087 	size_t	tokst_str_size;	/* Space needed to hold all the resulting */
     93  5088  ab196087 				/*	tokens, including terminating NULL */
     94  5088  ab196087 	TOK_ELT	*tokst_buf;	/* The array of tokens */
     95  5088  ab196087 	size_t	tokst_cnt;	/* # of tokens in array */
     96  5088  ab196087 	size_t	tokst_bufsize;	/* capacity of array */
     97  5088  ab196087 } TOK_STATE;
     98  5088  ab196087 
     99  5088  ab196087 
    100  5088  ab196087 
    101  5088  ab196087 
    102  5088  ab196087 /* State block used by gettok_init() and gettok() */
    103  5088  ab196087 typedef struct {
    104  5088  ab196087 	const char	*gtok_buf;	/* Addr of buffer containing string */
    105  5088  ab196087 	char		*gtok_cur_buf;	/* Addr withing buffer for next token */
    106  5088  ab196087 	int		gtok_inc_null_final; /* True if final NULL token used */
    107  5088  ab196087 	int		gtok_null_seen;	/* True when NULL byte seen */
    108  5088  ab196087 	TOK_ELT		gtok_last_token; /* Last token parsed */
    109  5088  ab196087 
    110  5088  ab196087 } GETTOK_STATE;
    111  5088  ab196087 
    112  5088  ab196087 
    113  5088  ab196087 
    114  5088  ab196087 
    115  5088  ab196087 /*
    116  5088  ab196087  * The elfedit_cpl_*() functions are used for command line completion.
    117  5088  ab196087  * Currently this uses the tecla library, but to allow for changing the
    118  5088  ab196087  * library used, we hide all tecla interfaces from our modules. Instead,
    119  5088  ab196087  * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
    120  5088  ab196087  * address of that struct as an opaque handle to the modules. Since the
    121  5088  ab196087  * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
    122  5088  ab196087  * as necessary.
    123  5088  ab196087  */
    124  5088  ab196087 typedef struct {
    125  5088  ab196087 	WordCompletion	*ecpl_cpl;		/* tecla handle */
    126  5088  ab196087 	const char	*ecpl_line;		/* raw input line */
    127  5088  ab196087 	int		ecpl_word_start;	/* start offset within line */
    128  5088  ab196087 	int		ecpl_word_end;		/* offset just past token */
    129  5088  ab196087 	/*
    130  5088  ab196087 	 * ecpl_add_mod_colon is a secret handshake between
    131  5088  ab196087 	 * elfedit_cpl_command() and  elfedit_cpl_add_match(). It adds
    132  5088  ab196087 	 * ':' to end of matched modules.
    133  5088  ab196087 	 */
    134  5088  ab196087 	int		ecpl_add_mod_colon;
    135  5088  ab196087 	const char	*ecpl_token_str;	/* token being completed */
    136  5088  ab196087 	size_t		ecpl_token_len;		/* strlen(ecpl_token_str) */
    137  5088  ab196087 } ELFEDIT_CPL_STATE;
    138  5088  ab196087 
    139  5088  ab196087 
    140  5088  ab196087 
    141  5088  ab196087 
    142  5088  ab196087 /* This structure maintains elfedit global state */
    143  5088  ab196087 STATE_T state;
    144  5088  ab196087 
    145  5088  ab196087 
    146  5088  ab196087 
    147  5088  ab196087 /*
    148  5088  ab196087  * Define a pair of static global variables that contain the
    149  5088  ab196087  * ISA strings that correspond to %i and %I tokens in module search
    150  5088  ab196087  * paths.
    151  5088  ab196087  *
    152  5088  ab196087  *	isa_i_str - The ISA string for the currently running program
    153  5088  ab196087  *	isa_I_str - For 64-bit programs, the same as isa_i_str. For
    154  5088  ab196087  *		32-bit programs, an empty string.
    155  5088  ab196087  */
    156  5088  ab196087 #ifdef __sparc
    157  5088  ab196087 #ifdef __sparcv9
    158  5088  ab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64);
    159  5088  ab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64);
    160  5088  ab196087 #else
    161  5088  ab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32);
    162  5088  ab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
    163  5088  ab196087 #endif
    164  5088  ab196087 #endif
    165  5088  ab196087 
    166  5088  ab196087 #ifdef __i386
    167  5088  ab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32);
    168  5088  ab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
    169  5088  ab196087 #endif
    170  5088  ab196087 #ifdef __amd64
    171  5088  ab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64);
    172  5088  ab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64);
    173  5088  ab196087 #endif
    174  5088  ab196087 
    175  5088  ab196087 
    176  5088  ab196087 
    177  5088  ab196087 /* Forward declarations */
    178  5088  ab196087 static void free_user_cmds(void);
    179  5088  ab196087 static void elfedit_pager_cleanup(void);
    180  5088  ab196087 
    181  5088  ab196087 
    182  5088  ab196087 
    183  5088  ab196087 /*
    184  5088  ab196087  * We supply this function for the msg module
    185  5088  ab196087  */
    186  5088  ab196087 const char *
    187  5088  ab196087 _elfedit_msg(Msg mid)
    188  5088  ab196087 {
    189  5088  ab196087 	return (gettext(MSG_ORIG(mid)));
    190  5088  ab196087 }
    191  5088  ab196087 
    192  5088  ab196087 
    193  5088  ab196087 /*
    194  5088  ab196087  * Copy at most min(cpsize, dstsize-1) bytes from src into dst,
    195  5088  ab196087  * truncating src if necessary.  The  result is always null-terminated.
    196  5088  ab196087  *
    197  5088  ab196087  * entry:
    198  5088  ab196087  *	dst - Destination buffer
    199  5088  ab196087  *	src - Source string
    200  5088  ab196087  *	dstsize - sizeof(dst)
    201  5088  ab196087  *
    202  5088  ab196087  * note:
    203  5088  ab196087  *	This is similar to strncpy(), but with two modifications:
    204  5088  ab196087  *	1) You specify the number of characters to copy, not just
    205  5088  ab196087  *		the size of the destination. Hence, you can copy non-NULL
    206  5088  ab196087  *		terminated strings.
    207  5088  ab196087  *	2) The destination is guaranteed to be NULL terminated. strncpy()
    208  5088  ab196087  *		does not terminate a completely full buffer.
    209  5088  ab196087  */
    210  5088  ab196087 static void
    211  5088  ab196087 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize)
    212  5088  ab196087 {
    213  5088  ab196087 	if (cpsize >= dstsize)
    214  5088  ab196087 		cpsize = dstsize - 1;
    215  5088  ab196087 	if (cpsize > 0)
    216  5088  ab196087 		(void) strncpy(dst, src, cpsize + 1);
    217  5088  ab196087 	dst[cpsize] = '\0';
    218  5088  ab196087 }
    219  5088  ab196087 
    220  5088  ab196087 
    221  5088  ab196087 /*
    222  5088  ab196087  * Calls exit() on behalf of elfedit.
    223  5088  ab196087  */
    224  5088  ab196087 void
    225  5088  ab196087 elfedit_exit(int status)
    226  5088  ab196087 {
    227  5088  ab196087 	if (state.file.present) {
    228  5088  ab196087 		/* Exiting with unflushed changes pending? Issue debug notice */
    229  5088  ab196087 		if (state.file.dirty)
    230  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
    231  5088  ab196087 			    MSG_INTL(MSG_DEBUG_DIRTYEXIT));
    232  5088  ab196087 
    233  5088  ab196087 		/*
    234  5088  ab196087 		 * If the edit file is marked for unlink on exit, then
    235  5088  ab196087 		 * take care of it here.
    236  5088  ab196087 		 */
    237  5088  ab196087 		if (state.file.unlink_on_exit) {
    238  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
    239  5088  ab196087 			    MSG_INTL(MSG_DEBUG_UNLINKFILE),
    240  5088  ab196087 			    state.file.outfile);
    241  5088  ab196087 			(void) unlink(state.file.outfile);
    242  5088  ab196087 		}
    243  5088  ab196087 	}
    244  5088  ab196087 
    245  5088  ab196087 	exit(status);
    246  5088  ab196087 }
    247  5088  ab196087 
    248  5088  ab196087 
    249  5088  ab196087 /*
    250  5088  ab196087  * Standard message function for elfedit. All user visible
    251  5088  ab196087  * output, for error or informational reasons, should go through
    252  5088  ab196087  * this function.
    253  5088  ab196087  *
    254  5088  ab196087  * entry:
    255  5088  ab196087  *	type - Type of message. One of the ELFEDIT_MSG_* values.
    256  5088  ab196087  *	format, ... - As per the printf() family
    257  5088  ab196087  *
    258  5088  ab196087  * exit:
    259  5088  ab196087  *	The desired message has been output. For informational
    260  5088  ab196087  *	messages, control returns to the caller. For errors,
    261  5088  ab196087  *	this routine will terminate execution or strip the execution
    262  5088  ab196087  *	stack and return control directly to the outer control loop.
    263  5088  ab196087  *	In either case, the caller will not receive control.
    264  5088  ab196087  */
    265  5088  ab196087 /*PRINTFLIKE2*/
    266  5088  ab196087 void
    267  5088  ab196087 elfedit_msg(elfedit_msg_t type, const char *format, ...)
    268  5088  ab196087 {
    269  5088  ab196087 	typedef enum {			/* What to do after finished */
    270  5088  ab196087 		DISP_RET = 0,		/* Return to caller */
    271  5088  ab196087 		DISP_JMP = 1, 		/* if (interactive) longjmp else exit */
    272  5088  ab196087 		DISP_EXIT = 2		/* exit under all circumstances */
    273  5088  ab196087 	} DISP;
    274  5088  ab196087 
    275  5088  ab196087 	va_list args;
    276  5088  ab196087 	FILE *stream = stderr;
    277  5088  ab196087 	DISP disp = DISP_RET;
    278  5088  ab196087 	int do_output = 1;
    279  5088  ab196087 	int need_prefix = 1;
    280  5088  ab196087 
    281  5088  ab196087 	va_start(args, format);
    282  5088  ab196087 
    283  5088  ab196087 	switch (type) {
    284  5088  ab196087 	case ELFEDIT_MSG_ERR:
    285  5088  ab196087 	case ELFEDIT_MSG_CMDUSAGE:
    286  5088  ab196087 		disp = DISP_JMP;
    287  5088  ab196087 		break;
    288  5088  ab196087 	case ELFEDIT_MSG_FATAL:
    289  5088  ab196087 		disp = DISP_EXIT;
    290  5088  ab196087 		break;
    291  5088  ab196087 	case ELFEDIT_MSG_USAGE:
    292  5088  ab196087 		need_prefix = 0;
    293  5088  ab196087 		break;
    294  5088  ab196087 	case ELFEDIT_MSG_DEBUG:
    295  5088  ab196087 		if (!(state.flags & ELFEDIT_F_DEBUG))
    296  5088  ab196087 			return;
    297  5088  ab196087 		stream = stdout;
    298  5088  ab196087 		break;
    299  5088  ab196087 	case ELFEDIT_MSG_QUIET:
    300  5088  ab196087 		do_output = 0;
    301  5088  ab196087 		disp = DISP_JMP;
    302  5088  ab196087 		break;
    303  5088  ab196087 	}
    304  5088  ab196087 
    305  5088  ab196087 
    306  5088  ab196087 	/*
    307  5088  ab196087 	 * If there is a pager process running, we are returning to the
    308  5088  ab196087 	 * caller, and the output is going to stdout, then let the
    309  5088  ab196087 	 * pager handle it instead of writing it directly from this process.
    310  5088  ab196087 	 * That way, the output gets paged along with everything else.
    311  5088  ab196087 	 *
    312  5088  ab196087 	 * If there is a pager process running, and we are not returning
    313  5088  ab196087 	 * to the caller, then end the pager process now, before we generate
    314  5088  ab196087 	 * any new output. This allows for any text buffered in the pager
    315  5088  ab196087 	 * pipe to be output before the new stuff.
    316  5088  ab196087 	 */
    317  5088  ab196087 	if (state.pager.fptr != NULL) {
    318  5088  ab196087 		if (disp == DISP_RET) {
    319  5088  ab196087 			if (stream == stdout)
    320  5088  ab196087 				stream = state.pager.fptr;
    321  5088  ab196087 		} else {
    322  5088  ab196087 			elfedit_pager_cleanup();
    323  5088  ab196087 		}
    324  5088  ab196087 	}
    325  5088  ab196087 
    326  5088  ab196087 	/*
    327  5088  ab196087 	 * If this message is coming from within the libtecla command
    328  5088  ab196087 	 * completion code, call gl_normal_io() to give the library notice.
    329  5088  ab196087 	 * That function sets the tty back to cooked mode and advances
    330  5088  ab196087 	 * the cursor to the beginning of the next line so that our output
    331  5088  ab196087 	 * will appear properly. When we return to the command completion code,
    332  5088  ab196087 	 * tecla will re-enter raw mode and redraw the current command line.
    333  5088  ab196087 	 */
    334  5088  ab196087 	if (state.input.in_tecla)
    335  5088  ab196087 		(void) gl_normal_io(state.input.gl);
    336  5088  ab196087 
    337  5088  ab196087 	if (do_output) {
    338  5088  ab196087 		if (need_prefix)
    339  5088  ab196087 			(void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT));
    340  5088  ab196087 		(void) vfprintf(stream, format, args);
    341  5088  ab196087 		(void) fflush(stream);
    342  5088  ab196087 	}
    343  5088  ab196087 	va_end(args);
    344  5088  ab196087 
    345  5088  ab196087 	/*
    346  5088  ab196087 	 * If this is an error, then we do not return to the caller.
    347  5088  ab196087 	 * The action taken depends on whether the outer loop has registered
    348  5088  ab196087 	 * a jump buffer for us or not.
    349  5088  ab196087 	 */
    350  5088  ab196087 	if (disp != DISP_RET) {
    351  5088  ab196087 		if (state.msg_jbuf.active && (disp == DISP_JMP)) {
    352  5088  ab196087 			/* Free the user command list */
    353  5088  ab196087 			free_user_cmds();
    354  5088  ab196087 
    355  5088  ab196087 			/* Clean up to reflect effect of non-local goto */
    356  5088  ab196087 			state.input.in_tecla = FALSE;
    357  5088  ab196087 
    358  5088  ab196087 			/* Jump to the outer loop to resume */
    359  5088  ab196087 			siglongjmp(state.msg_jbuf.env, 1);
    360  5088  ab196087 		} else {
    361  5088  ab196087 			elfedit_exit(1);
    362  5088  ab196087 		}
    363  5088  ab196087 	}
    364  5088  ab196087 }
    365  5088  ab196087 
    366  5088  ab196087 
    367  5088  ab196087 /*
    368  5088  ab196087  * Wrapper on elfedit_msg() that issues an error that results from
    369  5088  ab196087  * a call to libelf.
    370  5088  ab196087  *
    371  5088  ab196087  * entry:
    372  5088  ab196087  *	file - Name of ELF object
    373  5088  ab196087  *	libelf_rtn_name - Name of routine that was called
    374  5088  ab196087  *
    375  5088  ab196087  * exit:
    376  5088  ab196087  *	An error has been issued that shows the routine called
    377  5088  ab196087  *	and the libelf error string for it from elf_errmsg().
    378  5088  ab196087  *	This routine does not return to the caller.
    379  5088  ab196087  */
    380  5088  ab196087 void
    381  5088  ab196087 elfedit_elferr(const char *file, const char *libelf_rtn_name)
    382  5088  ab196087 {
    383  5088  ab196087 	const char *errstr = elf_errmsg(elf_errno());
    384  5088  ab196087 
    385  5088  ab196087 	elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file,
    386  5088  ab196087 	    libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN));
    387  5088  ab196087 }
    388  5088  ab196087 
    389  5088  ab196087 
    390  5088  ab196087 /*
    391  5088  ab196087  * Start an output pager process for elfedit_printf()/elfedit_write() to use.
    392  5088  ab196087  *
    393  5088  ab196087  * note:
    394  5088  ab196087  *	If this elfedit session is not interactive, then no pager is
    395  5088  ab196087  *	started. Paging is only intended for interactive use. The caller
    396  5088  ab196087  *	is not supposed to worry about this point, but simply to use
    397  5088  ab196087  *	this function to flag situations in which paging might be needed.
    398  5088  ab196087  */
    399  5088  ab196087 void
    400  5088  ab196087 elfedit_pager_init(void)
    401  5088  ab196087 {
    402  5088  ab196087 	const char	*errstr;
    403  5088  ab196087 	const char	*cmd;
    404  5088  ab196087 	int		err;
    405  5088  ab196087 
    406  5088  ab196087 	/*
    407  5088  ab196087 	 * If there is no pager process running, start one.
    408  5088  ab196087 	 * Only do this for interactive sessions --- elfedit_pager()
    409  5088  ab196087 	 * won't use a pager in batch mode.
    410  5088  ab196087 	 */
    411  5088  ab196087 	if (state.msg_jbuf.active && state.input.full_tty &&
    412  5088  ab196087 	    (state.pager.fptr == NULL)) {
    413  5088  ab196087 		/*
    414  5088  ab196087 		 * If the user has the PAGER environment variable set,
    415  5088  ab196087 		 * then we will use that program. Otherwise we default
    416  5088  ab196087 		 * to /bin/more.
    417  5088  ab196087 		 */
    418  5088  ab196087 		cmd = getenv(MSG_ORIG(MSG_STR_PAGER));
    419  5088  ab196087 		if ((cmd == NULL) || (*cmd == '\0'))
    420  5088  ab196087 			cmd = MSG_ORIG(MSG_STR_BINMORE);
    421  5088  ab196087 
    422  5088  ab196087 		/*
    423  5088  ab196087 		 * The popen() manpage says that on failure, it "may set errno",
    424  5088  ab196087 		 * which is somewhat ambiguous. We explicitly zero it here, and
    425  5088  ab196087 		 * assume that any change is due to popen() failing.
    426  5088  ab196087 		 */
    427  5088  ab196087 		errno = 0;
    428  5088  ab196087 		state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W));
    429  5088  ab196087 		if (state.pager.fptr == NULL) {
    430  5088  ab196087 			err = errno;
    431  5088  ab196087 			errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) :
    432  5088  ab196087 			    strerror(err);
    433  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC),
    434  5088  ab196087 			    MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr);
    435  5088  ab196087 		}
    436  5088  ab196087 	}
    437  5088  ab196087 }
    438  5088  ab196087 
    439  5088  ab196087 
    440  5088  ab196087 /*
    441  5088  ab196087  * If there is a pager process present, close it out.
    442  5088  ab196087  *
    443  5088  ab196087  * note:
    444  5088  ab196087  *	This function is called from within elfedit_msg(), and as
    445  5088  ab196087  *	such, must not use elfedit_msg() to report errors. Furthermore,
    446  5088  ab196087  *	any such errors are not a sufficient reason to terminate the process
    447  5088  ab196087  *	or to longjmp(). This is a rare case where errors are written
    448  5088  ab196087  *	directly to stderr.
    449  5088  ab196087  */
    450  5088  ab196087 static void
    451  5088  ab196087 elfedit_pager_cleanup(void)
    452  5088  ab196087 {
    453  5088  ab196087 	if (state.pager.fptr != NULL) {
    454  5088  ab196087 		if (pclose(state.pager.fptr) == -1)
    455  5088  ab196087 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI));
    456  5088  ab196087 
    457  5088  ab196087 		state.pager.fptr = NULL;
    458  5088  ab196087 	}
    459  5088  ab196087 }
    460  5088  ab196087 
    461  5088  ab196087 
    462  5088  ab196087 /*
    463  5088  ab196087  * Print general formtted text for the user, using printf()-style
    464  5088  ab196087  * formatting. Uses the pager process if one has been started, or
    465  5088  ab196087  * stdout otherwise.
    466  5088  ab196087  */
    467  5088  ab196087 void
    468  5088  ab196087 elfedit_printf(const char *format, ...)
    469  5088  ab196087 {
    470  5088  ab196087 	va_list	args;
    471  5088  ab196087 	int	err;
    472  5088  ab196087 	FILE	*fptr;
    473  5088  ab196087 	int	pager;
    474  5088  ab196087 	int	broken_pipe = 0;
    475  5088  ab196087 
    476  5088  ab196087 	/*
    477  5088  ab196087 	 * If there is a pager process, then use it. Otherwise write
    478  5088  ab196087 	 * directly to stdout.
    479  5088  ab196087 	 */
    480  5088  ab196087 	pager = (state.pager.fptr != NULL);
    481  5088  ab196087 	fptr = pager ? state.pager.fptr : stdout;
    482  5088  ab196087 
    483  5088  ab196087 	va_start(args, format);
    484  5088  ab196087 	errno = 0;
    485  5088  ab196087 	err = vfprintf(fptr, format, args);
    486  5088  ab196087 
    487  5088  ab196087 	/* Did we fail because a child pager process has exited? */
    488  5088  ab196087 	broken_pipe = pager && (err < 0) && (errno == EPIPE);
    489  5088  ab196087 
    490  5088  ab196087 	va_end(args);
    491  5088  ab196087 
    492  5088  ab196087 	/*
    493  5088  ab196087 	 * On error, we simply issue the error without cleaning up
    494  5088  ab196087 	 * the pager process. The message code handles that as a standard
    495  5088  ab196087 	 * part of error processing.
    496  5088  ab196087 	 *
    497  5088  ab196087 	 * We handle failure due to an exited pager process differently
    498  5088  ab196087 	 * than a normal error, because it is usually due to the user
    499  5088  ab196087 	 * intentionally telling it to.
    500  5088  ab196087 	 */
    501  5088  ab196087 	if (err < 0) {
    502  5088  ab196087 		if (broken_pipe)
    503  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
    504  5088  ab196087 		else
    505  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
    506  5088  ab196087 	}
    507  5088  ab196087 }
    508  5088  ab196087 
    509  5088  ab196087 
    510  5088  ab196087 /*
    511  5088  ab196087  * Some our modules use liblddb routines to format ELF output.
    512  5088  ab196087  * In order to ensure that such output is sent to the pager pipe
    513  5088  ab196087  * when there is one, and stdout otherwise, we redefine the dbg_print()
    514  5088  ab196087  * function here.
    515  5088  ab196087  *
    516  5088  ab196087  * This item should be defined NODIRECT.
    517  5088  ab196087  */
    518  5088  ab196087 /* PRINTFLIKE2 */
    519  5088  ab196087 void
    520  5088  ab196087 dbg_print(Lm_list *lml, const char *format, ...)
    521  5088  ab196087 {
    522  5088  ab196087 	va_list	ap;
    523  5088  ab196087 	int	err;
    524  5088  ab196087 	FILE	*fptr;
    525  5088  ab196087 	int	pager;
    526  5088  ab196087 	int	broken_pipe = 0;
    527  5088  ab196087 
    528  5088  ab196087 #if	defined(lint)
    529  5088  ab196087 	/*
    530  5088  ab196087 	 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
    531  5088  ab196087 	 * Supress the lint error by making a dummy assignment.
    532  5088  ab196087 	 */
    533  5088  ab196087 	lml = 0;
    534  5088  ab196087 #endif
    535  5088  ab196087 
    536  5088  ab196087 	/*
    537  5088  ab196087 	 * If there is a pager process, then use it. Otherwise write
    538  5088  ab196087 	 * directly to stdout.
    539  5088  ab196087 	 */
    540  5088  ab196087 	pager = (state.pager.fptr != NULL);
    541  5088  ab196087 	fptr = pager ? state.pager.fptr : stdout;
    542  5088  ab196087 
    543  5088  ab196087 	va_start(ap, format);
    544  5088  ab196087 	errno = 0;
    545  5088  ab196087 	err = vfprintf(fptr, format, ap);
    546  5088  ab196087 	if (err >= 0)
    547  5088  ab196087 		err = fprintf(fptr, MSG_ORIG(MSG_STR_NL));
    548  5088  ab196087 
    549  5088  ab196087 	/* Did we fail because a child pager process has exited? */
    550  5088  ab196087 	broken_pipe = (err < 0) && pager && (errno == EPIPE);
    551  5088  ab196087 
    552  5088  ab196087 	va_end(ap);
    553  5088  ab196087 
    554  5088  ab196087 	/*
    555  5088  ab196087 	 * On error, we simply issue the error without cleaning up
    556  5088  ab196087 	 * the pager process. The message code handles that as a standard
    557  5088  ab196087 	 * part of error processing.
    558  5088  ab196087 	 *
    559  5088  ab196087 	 * We handle failure due to an exited pager process differently
    560  5088  ab196087 	 * than a normal error, because it is usually due to the user
    561  5088  ab196087 	 * intentionally telling it to.
    562  5088  ab196087 	 */
    563  5088  ab196087 	if (err < 0) {
    564  5088  ab196087 		if (broken_pipe)
    565  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
    566  5088  ab196087 		else
    567  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
    568  5088  ab196087 	}
    569  5088  ab196087 }
    570  5088  ab196087 
    571  5088  ab196087 
    572  5088  ab196087 /*
    573  5088  ab196087  * Write raw bytes of text in a manner similar to fwrite().
    574  5088  ab196087  * Uses the pager process if one has been started, or
    575  5088  ab196087  * stdout otherwise.
    576  5088  ab196087  */
    577  5088  ab196087 void
    578  5088  ab196087 elfedit_write(const void *ptr, size_t size)
    579  5088  ab196087 {
    580  5088  ab196087 	FILE	*fptr;
    581  5088  ab196087 	int	err;
    582  5088  ab196087 
    583  5088  ab196087 	/*
    584  5088  ab196087 	 * If there is a pager process, then use it. Otherwise write
    585  5088  ab196087 	 * directly to stdout.
    586  5088  ab196087 	 */
    587  5088  ab196087 	fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr;
    588  5088  ab196087 
    589  5088  ab196087 	if (fwrite(ptr, 1, size, fptr) != size) {
    590  5088  ab196087 		err = errno;
    591  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE),
    592  5088  ab196087 		    strerror(err));
    593  5892  ab196087 	}
    594  5892  ab196087 }
    595  5892  ab196087 
    596  5892  ab196087 
    597  5892  ab196087 /*
    598  5892  ab196087  * Convert the NULL terminated string to the form used by the C
    599  6635  ab196087  * language to represent literal strings. See conv_str_to_c_literal()
    600  6635  ab196087  * for details.
    601  6635  ab196087  *
    602  6635  ab196087  * This routine differs from conv_str_to_c_literal() in two ways:
    603  6635  ab196087  *	1) String is NULL terminated instead of counted
    604  6635  ab196087  *	2) Signature of outfunc
    605  5892  ab196087  *
    606  5892  ab196087  * entry:
    607  5892  ab196087  *	str - String to be processed
    608  5892  ab196087  *	outfunc - Function to be called to move output characters. Note
    609  5892  ab196087  *		that this function has the same signature as elfedit_write(),
    610  5892  ab196087  *		and that function can be used to write the characters to
    611  5892  ab196087  *		the output.
    612  5892  ab196087  *
    613  5892  ab196087  * exit:
    614  5892  ab196087  *	The string has been processed, with the resulting data passed
    615  5892  ab196087  *	to outfunc for processing.
    616  5892  ab196087  */
    617  6635  ab196087 static void
    618  6635  ab196087 elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue)
    619  6635  ab196087 {
    620  6635  ab196087 	elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue;
    621  6635  ab196087 
    622  6635  ab196087 	(* outfunc)(ptr, size);
    623  6635  ab196087 
    624  6635  ab196087 }
    625  5892  ab196087 void
    626  5892  ab196087 elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc)
    627  5892  ab196087 {
    628  6635  ab196087 	conv_str_to_c_literal(str, strlen(str),
    629  6635  ab196087 	    elfedit_str_to_c_literal_cb, (void *) outfunc);
    630  5088  ab196087 }
    631  5088  ab196087 
    632  5088  ab196087 
    633  5088  ab196087 /*
    634  5088  ab196087  * Wrappers on malloc() and realloc() that check the result for success
    635  5088  ab196087  * and issue an error if not. The caller can use the result of these
    636  5088  ab196087  * functions without checking for a NULL pointer, as we do not return to
    637  5088  ab196087  * the caller in the failure case.
    638  5088  ab196087  */
    639  5088  ab196087 void *
    640  5088  ab196087 elfedit_malloc(const char *item_name, size_t size)
    641  5088  ab196087 {
    642  5088  ab196087 	void *m;
    643  5088  ab196087 
    644  5088  ab196087 	m = malloc(size);
    645  5088  ab196087 	if (m == NULL) {
    646  5088  ab196087 		int err = errno;
    647  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
    648  5088  ab196087 		    item_name, strerror(err));
    649  5088  ab196087 	}
    650  5088  ab196087 
    651  5088  ab196087 	return (m);
    652  5088  ab196087 }
    653  5088  ab196087 
    654  5088  ab196087 void *
    655  5088  ab196087 elfedit_realloc(const char *item_name, void *ptr, size_t size)
    656  5088  ab196087 {
    657  5088  ab196087 	void *m;
    658  5088  ab196087 
    659  5088  ab196087 	m = realloc(ptr, size);
    660  5088  ab196087 	if (m == NULL) {
    661  5088  ab196087 		int err = errno;
    662  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
    663  5088  ab196087 		    item_name, strerror(err));
    664  5088  ab196087 	}
    665  5088  ab196087 
    666  5088  ab196087 	return (m);
    667  5088  ab196087 }
    668  5088  ab196087 
    669  5088  ab196087 
    670  5088  ab196087 /*
    671  5088  ab196087  * Ensure that the given buffer has room for n bytes of data.
    672  5088  ab196087  */
    673  5088  ab196087 static void
    674  5088  ab196087 strbuf_ensure_size(STRBUF *str, size_t size)
    675  5088  ab196087 {
    676  5088  ab196087 #define	INITIAL_STR_ALLOC 128
    677  5088  ab196087 
    678  5088  ab196087 	size_t n;
    679  5088  ab196087 
    680  5088  ab196087 	n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n;
    681  5088  ab196087 	while (size > n)	/* Double buffer until string fits */
    682  5088  ab196087 		n *= 2;
    683  5088  ab196087 	if (n != str->n) {		/* Alloc new string buffer if needed */
    684  5088  ab196087 		str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR),
    685  5088  ab196087 		    str->buf, n);
    686  5088  ab196087 		str->n = n;
    687  5088  ab196087 	}
    688  5088  ab196087 
    689  5088  ab196087 #undef	INITIAL_STR_ALLOC
    690  5088  ab196087 }
    691  5088  ab196087 
    692  5088  ab196087 
    693  5088  ab196087 /*
    694  5088  ab196087  * Extract the argument/option information for the next item referenced
    695  5088  ab196087  * by optarg, and advance the pointer to the next item.
    696  5088  ab196087  *
    697  5088  ab196087  * entry:
    698  5088  ab196087  *	optarg - Address of pointer to argument or option array
    699  5088  ab196087  *	item - Struct to be filled in.
    700  5088  ab196087  *
    701  5088  ab196087  * exit:
    702  5088  ab196087  *	The item block has been filled in with the information for
    703  5088  ab196087  *	the next item in the optarg array. *optarg has been advanced
    704  5088  ab196087  *	to the next item.
    705  5088  ab196087  */
    706  5088  ab196087 void
    707  5088  ab196087 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item)
    708  5088  ab196087 {
    709  5088  ab196087 	/*
    710  5088  ab196087 	 * Array of inheritable options/arguments. Indexed by one less
    711  5088  ab196087 	 * than the corresponding ELFEDIT_STDOA_ value.
    712  5088  ab196087 	 */
    713  5088  ab196087 	static const elfedit_optarg_item_t stdoa[] = {
    714  5088  ab196087 		/* ELFEDIT_STDOA_O */
    715  5088  ab196087 		{ MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE),
    716  5088  ab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_O) */
    717  5088  ab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O,
    718  5088  ab196087 		    ELFEDIT_CMDOA_F_VALUE },
    719  5088  ab196087 
    720  5088  ab196087 		/* ELFEDIT_STDOA_AND */
    721  5088  ab196087 		{ MSG_ORIG(MSG_STR_MINUS_AND), NULL,
    722  5088  ab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
    723  5088  ab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 },
    724  5088  ab196087 
    725  5088  ab196087 		/* ELFEDIT_STDOA_CMP */
    726  5088  ab196087 		{ MSG_ORIG(MSG_STR_MINUS_CMP), NULL,
    727  5088  ab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
    728  5088  ab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 },
    729  5088  ab196087 
    730  5088  ab196087 		/* ELFEDIT_STDOA_OR */
    731  5088  ab196087 		{ MSG_ORIG(MSG_STR_MINUS_OR), NULL,
    732  5088  ab196087 		    /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
    733  5088  ab196087 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 },
    734  5088  ab196087 	};
    735  5088  ab196087 
    736  5088  ab196087 	elfedit_cmd_optarg_t *oa;
    737  5088  ab196087 
    738  5088  ab196087 
    739  5088  ab196087 	/* Grab first item, advance the callers pointer over it */
    740  5088  ab196087 	oa = (*optarg)++;
    741  5088  ab196087 
    742  5088  ab196087 	if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
    743  5088  ab196087 		/* Values are pre-chewed in the stdoa array above */
    744  5088  ab196087 		*item = stdoa[((uintptr_t)oa->oa_name) - 1];
    745  5088  ab196087 
    746  5088  ab196087 		/*
    747  5088  ab196087 		 * Set the inherited flag so that elfedit_optarg_helpstr()
    748  5088  ab196087 		 * can tell who is responsible for translating the help string.
    749  5088  ab196087 		 */
    750  5088  ab196087 		item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT;
    751  5088  ab196087 	} else {	/* Non-inherited item */
    752  5088  ab196087 		item->oai_name = oa->oa_name;
    753  5088  ab196087 		if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) {
    754  5088  ab196087 			item->oai_vname = oa[1].oa_name;
    755  5088  ab196087 
    756  5088  ab196087 			/* Advance users pointer past value element */
    757  5088  ab196087 			(*optarg)++;
    758  5088  ab196087 		} else {
    759  5088  ab196087 			item->oai_vname = NULL;
    760  5088  ab196087 		}
    761  5088  ab196087 		item->oai_help = oa->oa_help;
    762  5088  ab196087 		item->oai_flags = oa->oa_flags;
    763  5088  ab196087 	}
    764  5088  ab196087 
    765  5088  ab196087 	/*
    766  5088  ab196087 	 * The module determines the idmask and excmask fields whether
    767  5088  ab196087 	 * or not inheritance is in play.
    768  5088  ab196087 	 */
    769  5088  ab196087 	item->oai_idmask = oa->oa_idmask;
    770  5088  ab196087 	item->oai_excmask = oa->oa_excmask;
    771  5088  ab196087 }
    772  5088  ab196087 
    773  5088  ab196087 
    774  5088  ab196087 
    775  5088  ab196087 /*
    776  5088  ab196087  * Return the help string for an option/argument item, as returned
    777  5088  ab196087  * by elfedit_next_optarg(). This routine handles the details of
    778  5088  ab196087  * knowing whether the string is provided by elfedit itself (inherited),
    779  5088  ab196087  * or needs to be translated by the module.
    780  5088  ab196087  */
    781  5088  ab196087 const char *
    782  5088  ab196087 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item)
    783  5088  ab196087 {
    784  5088  ab196087 	/*
    785  5088  ab196087 	 * The help string from an inherited item comes right out
    786  5088  ab196087 	 * of the main elfedit string table.
    787  5088  ab196087 	 */
    788  5088  ab196087 	if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT)
    789  5088  ab196087 		return (MSG_INTL((Msg) item->oai_help));
    790  5088  ab196087 
    791  5088  ab196087 	/*
    792  5088  ab196087 	 * If the string is defined by the module, then we need to
    793  5088  ab196087 	 * have the module translate it for us.
    794  5088  ab196087 	 */
    795  5088  ab196087 	return ((* mod->mod_i18nhdl_to_str)(item->oai_help));
    796  5088  ab196087 }
    797  5088  ab196087 
    798  5088  ab196087 
    799  5088  ab196087 
    800  5088  ab196087 /*
    801  5088  ab196087  * Used by usage_optarg() to insert a character into the output buffer,
    802  5088  ab196087  * advancing the buffer pointer and current column, and reducing the
    803  5088  ab196087  * amount of remaining space.
    804  5088  ab196087  */
    805  5088  ab196087 static void
    806  5088  ab196087 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col)
    807  5088  ab196087 {
    808  5088  ab196087 
    809  5088  ab196087 	*(*cur)++ = ch;
    810  5088  ab196087 	**cur = '\0';
    811  5088  ab196087 	(*n)--;
    812  5088  ab196087 	(*cur_col)++;
    813  5088  ab196087 }
    814  5088  ab196087 
    815  5088  ab196087 /*
    816  5088  ab196087  * Used by usage_optarg() to insert a string into the output
    817  5088  ab196087  * buffer, advancing the buffer pointer and current column, and reducing
    818  5088  ab196087  * the amount of remaining space.
    819  5088  ab196087  */
    820  5088  ab196087 static void
    821  5088  ab196087 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col,
    822  5088  ab196087     const char *format, ...)
    823  5088  ab196087 {
    824  5088  ab196087 	size_t len;
    825  5088  ab196087 	va_list args;
    826  5088  ab196087 
    827  5088  ab196087 	va_start(args, format);
    828  5088  ab196087 	len = vsnprintf(*cur, *n, format, args);
    829  5088  ab196087 	va_end(args);
    830  5088  ab196087 
    831  5088  ab196087 	*cur += len;
    832  5088  ab196087 	*n -= len;
    833  5088  ab196087 	*cur_col += len;
    834  5088  ab196087 }
    835  5088  ab196087 /*
    836  5088  ab196087  * Used by usage_optarg() to insert an optarg item string into the output
    837  5088  ab196087  * buffer, advancing the buffer pointer and current column, and reducing
    838  5088  ab196087  * the amount of remaining space.
    839  5088  ab196087  */
    840  5088  ab196087 static void
    841  5088  ab196087 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur,
    842  5088  ab196087     size_t *n, size_t *cur_col)
    843  5088  ab196087 {
    844  5088  ab196087 	size_t len;
    845  5088  ab196087 
    846  5088  ab196087 	if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) {
    847  5088  ab196087 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2),
    848  5088  ab196087 		    item->oai_name, item->oai_vname);
    849  5088  ab196087 	} else {
    850  5088  ab196087 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG),
    851  5088  ab196087 		    item->oai_name);
    852  5088  ab196087 	}
    853  5088  ab196087 	*cur += len;
    854  5088  ab196087 	*n -= len;
    855  5088  ab196087 	*cur_col += len;
    856  5088  ab196087 }
    857  5088  ab196087 
    858  5088  ab196087 
    859  5088  ab196087 
    860  5088  ab196087 /*
    861  5088  ab196087  * Write the options/arguments to the usage string.
    862  5088  ab196087  *
    863  5088  ab196087  * entry:
    864  5088  ab196087  *	main_buf_n - Size of main buffer from which buf and buf_n are
    865  5088  ab196087  *		allocated.
    866  5088  ab196087  *	buf - Address of pointer to where next item is to be placed.
    867  5088  ab196087  *	buf_n - Address of count of remaining bytes in buffer
    868  5088  ab196087  *	buf_cur_col - Address of current output column for current line
    869  5088  ab196087  *		of generated string.
    870  5088  ab196087  *	optarg - Options list
    871  5088  ab196087  *	isopt - True if these are options, false for arguments.
    872  5088  ab196087  *	wrap_str - String to indent wrapped lines. If NULL, lines
    873  5088  ab196087  *		are not wrapped
    874  5088  ab196087  */
    875  5088  ab196087 static void
    876  5088  ab196087 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col,
    877  5088  ab196087     elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str)
    878  5088  ab196087 {
    879  5088  ab196087 	/*
    880  5088  ab196087 	 * An option can be combined into a simple format if it lacks
    881  5088  ab196087 	 * these flags and is only one character in length.
    882  5088  ab196087 	 */
    883  5088  ab196087 	static const elfedit_cmd_oa_flag_t exflags =
    884  5088  ab196087 	    (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT);
    885  5088  ab196087 
    886  5088  ab196087 	/*
    887  5088  ab196087 	 * A static buffer, which is grown as needed to accomodate
    888  5088  ab196087 	 * the maximum usage string seen.
    889  5088  ab196087 	 */
    890  5088  ab196087 	static STRBUF simple_str;
    891  5088  ab196087 
    892  5088  ab196087 	char			*cur = *buf;
    893  5088  ab196087 	size_t			n = *buf_n;
    894  5088  ab196087 	size_t			cur_col = *buf_cur_col;
    895  5088  ab196087 	int			len;
    896  5088  ab196087 	int			use_simple = 0;
    897  5088  ab196087 	elfedit_optarg_item_t	item;
    898  5088  ab196087 	elfedit_cmd_oa_mask_t	optmask = 0;
    899  5088  ab196087 	int			use_bkt;
    900  5088  ab196087 
    901  5088  ab196087 	/*
    902  5088  ab196087 	 * If processing options, pull the 1-character ones that don't have
    903  5088  ab196087 	 * an associated value and don't have any mutual exclusion issues into
    904  5088  ab196087 	 * a single combination string to go at the beginning of the usage.
    905  5088  ab196087 	 */
    906  5088  ab196087 	if (isopt) {
    907  5088  ab196087 		elfedit_cmd_optarg_t *tmp_optarg = optarg;
    908  5088  ab196087 		char *s;
    909  5088  ab196087 
    910  5088  ab196087 		/*
    911  5088  ab196087 		 * The simple string is guaranteed to fit in the same
    912  5088  ab196087 		 * amount of space reserved for the main buffer.
    913  5088  ab196087 		 */
    914  5088  ab196087 		strbuf_ensure_size(&simple_str, main_buf_n);
    915  5088  ab196087 		s = simple_str.buf;
    916  5088  ab196087 		*s++ = ' ';
    917  5088  ab196087 		*s++ = '[';
    918  5088  ab196087 		*s++ = '-';
    919  5088  ab196087 		while (tmp_optarg->oa_name != NULL) {
    920  5088  ab196087 			elfedit_next_optarg(&tmp_optarg, &item);
    921  5088  ab196087 			if (((item.oai_flags & exflags) == 0) &&
    922  5088  ab196087 			    (item.oai_name[2] == '\0') &&
    923  5088  ab196087 			    (item.oai_excmask == 0)) {
    924  5088  ab196087 				optmask |= item.oai_idmask;
    925  5088  ab196087 				*s++ = item.oai_name[1];
    926  5088  ab196087 			}
    927  5088  ab196087 		}
    928  5088  ab196087 
    929  5088  ab196087 		/*
    930  5088  ab196087 		 * If we found more than one, then finish the string and
    931  5088  ab196087 		 * add it. Don't do this for a single option, because
    932  5088  ab196087 		 * it looks better in that case if the option shows up
    933  5088  ab196087 		 * in alphabetical order rather than being hoisted.
    934  5088  ab196087 		 */
    935  5088  ab196087 		use_simple = (s > (simple_str.buf + 4));
    936  5088  ab196087 		if (use_simple) {
    937  5088  ab196087 			*s++ = ']';
    938  5088  ab196087 			*s++ = '\0';
    939  5088  ab196087 			usage_optarg_insert_str(&cur, &n, &cur_col,
    940  5088  ab196087 			    MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf);
    941  5088  ab196087 		} else {
    942  5088  ab196087 			/* Not using it, so reset the cumulative options mask */
    943  5088  ab196087 			optmask = 0;
    944  5088  ab196087 		}
    945  5088  ab196087 	}
    946  5088  ab196087 
    947  5088  ab196087 	while (optarg->oa_name != NULL) {
    948  5088  ab196087 		elfedit_next_optarg(&optarg, &item);
    949  5088  ab196087 
    950  5088  ab196087 		if (isopt) {
    951  5088  ab196087 			/*
    952  5088  ab196087 			 * If this is an option that was pulled into the
    953  5088  ab196087 			 * combination string above, then skip over it.
    954  5088  ab196087 			 */
    955  5088  ab196087 			if (use_simple && ((item.oai_flags & exflags) == 0) &&
    956  5088  ab196087 			    (item.oai_name[2] == '\0') &&
    957  5088  ab196087 			    (item.oai_excmask == 0))
    958  5088  ab196087 				continue;
    959  5088  ab196087 
    960  5088  ab196087 			/*
    961  5088  ab196087 			 * If this is a mutual exclusion option that was
    962  5088  ab196087 			 * picked up out of order by a previous iteration
    963  5088  ab196087 			 * of this loop, then skip over it.
    964  5088  ab196087 			 */
    965  5088  ab196087 			if ((optmask & item.oai_idmask) != 0)
    966  5088  ab196087 				continue;
    967  5088  ab196087 
    968  5088  ab196087 			/* Add this item to the accumulating options mask */
    969  5088  ab196087 			optmask |= item.oai_idmask;
    970  5088  ab196087 		}
    971  5088  ab196087 
    972  5088  ab196087 		/* Wrap line, or insert blank separator */
    973  5088  ab196087 		if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) {
    974  5088  ab196087 			len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE),
    975  5088  ab196087 			    wrap_str);
    976  5088  ab196087 			cur += len;
    977  5088  ab196087 			n -= len;
    978  5088  ab196087 			cur_col = len - 1;   /* Don't count the newline */
    979  5088  ab196087 		} else {
    980  5088  ab196087 			usage_optarg_insert_ch(' ', &cur, &n, &cur_col);
    981  5088  ab196087 		}
    982  5088  ab196087 
    983  5088  ab196087 		use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt;
    984  5088  ab196087 		if (use_bkt)
    985  5088  ab196087 			usage_optarg_insert_ch('[', &cur, &n, &cur_col);
    986  5088  ab196087 
    987  5088  ab196087 		/* Add the item to the buffer */
    988  5088  ab196087 		usage_optarg_insert_item(&item, &cur, &n, &cur_col);
    989  5088  ab196087 
    990  5088  ab196087 		/*
    991  5088  ab196087 		 * If this item has a non-zero mutual exclusion mask,
    992  5088  ab196087 		 * then look for the other items and display them all
    993  5088  ab196087 		 * together with alternation (|). Note that plain arguments
    994  5088  ab196087 		 * cannot have a non-0 exclusion mask, so this is
    995  5088  ab196087 		 * effectively options-only (isopt != 0).
    996  5088  ab196087 		 */
    997  5088  ab196087 		if (item.oai_excmask != 0) {
    998  5088  ab196087 			elfedit_cmd_optarg_t *tmp_optarg = optarg;
    999  5088  ab196087 			elfedit_optarg_item_t tmp_item;
   1000  5088  ab196087 
   1001  5088  ab196087 			/*
   1002  5088  ab196087 			 * When showing alternation, elipses for multiple
   1003  5088  ab196087 			 * copies need to appear inside the [] brackets.
   1004  5088  ab196087 			 */
   1005  5088  ab196087 			if (item.oai_flags & ELFEDIT_CMDOA_F_MULT)
   1006  5088  ab196087 				usage_optarg_insert_str(&cur, &n, &cur_col,
   1007  5088  ab196087 				    MSG_ORIG(MSG_STR_ELIPSES));
   1008  5088  ab196087 
   1009  5088  ab196087 
   1010  5088  ab196087 			while (tmp_optarg->oa_name != NULL) {
   1011  5088  ab196087 				elfedit_next_optarg(&tmp_optarg, &tmp_item);
   1012  5088  ab196087 				if ((item.oai_excmask & tmp_item.oai_idmask) ==
   1013  5088  ab196087 				    0)
   1014  5088  ab196087 					continue;
   1015  5088  ab196087 				usage_optarg_insert_str(&cur, &n, &cur_col,
   1016  5088  ab196087 				    MSG_ORIG(MSG_STR_SP_BAR_SP));
   1017  5088  ab196087 				usage_optarg_insert_item(&tmp_item,
   1018  5088  ab196087 				    &cur, &n, &cur_col);
   1019  5088  ab196087 
   1020  5088  ab196087 				/*
   1021  5088  ab196087 				 * Add it to the mask of seen options.
   1022  5088  ab196087 				 * This will keep us from showing it twice.
   1023  5088  ab196087 				 */
   1024  5088  ab196087 				optmask |= tmp_item.oai_idmask;
   1025  5088  ab196087 			}
   1026  5088  ab196087 		}
   1027  5088  ab196087 		if (use_bkt)
   1028  5088  ab196087 			usage_optarg_insert_ch(']', &cur, &n, &cur_col);
   1029  5088  ab196087 
   1030  5088  ab196087 		/*
   1031  5088  ab196087 		 * If alternation was not shown above (non-zero exclusion mask)
   1032  5088  ab196087 		 * then the elipses for multiple copies are shown outside
   1033  5088  ab196087 		 * any [] brackets.
   1034  5088  ab196087 		 */
   1035  5088  ab196087 		if ((item.oai_excmask == 0) &&
   1036  5088  ab196087 		    (item.oai_flags & ELFEDIT_CMDOA_F_MULT))
   1037  5088  ab196087 			usage_optarg_insert_str(&cur, &n, &cur_col,
   1038  5088  ab196087 			    MSG_ORIG(MSG_STR_ELIPSES));
   1039  5088  ab196087 
   1040  5088  ab196087 	}
   1041  5088  ab196087 
   1042  5088  ab196087 	*buf = cur;
   1043  5088  ab196087 	*buf_n = n;
   1044  5088  ab196087 	*buf_cur_col = cur_col;
   1045  5088  ab196087 }
   1046  5088  ab196087 
   1047  5088  ab196087 
   1048  5088  ab196087 
   1049  5088  ab196087 /*
   1050  5088  ab196087  * Format the usage string for a command into a static buffer and
   1051  5088  ab196087  * return the pointer to the user. The resultant string is valid
   1052  5088  ab196087  * until the next call to this routine, and which point it
   1053  5088  ab196087  * will be overwritten or the memory is freed.
   1054  5088  ab196087  *
   1055  5088  ab196087  * entry:
   1056  5088  ab196087  *	mod, cmd - Module and command definitions for command to be described
   1057  5088  ab196087  *	wrap_str - NULL, or string to be used to indent when
   1058  5088  ab196087  *		lines are wrapped. If NULL, no wrapping is done, and
   1059  5088  ab196087  *		all output is on a single line.
   1060  5088  ab196087  *	cur_col - Starting column at which the string will be displayed.
   1061  5088  ab196087  *		Ignored if wrap_str is NULL.
   1062  5088  ab196087  */
   1063  5088  ab196087 const char *
   1064  5088  ab196087 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd,
   1065  5088  ab196087     const char *wrap_str, size_t cur_col)
   1066  5088  ab196087 {
   1067  5088  ab196087 
   1068  5088  ab196087 	/*
   1069  5088  ab196087 	 * A static buffer, which is grown as needed to accomodate
   1070  5088  ab196087 	 * the maximum usage string seen.
   1071  5088  ab196087 	 */
   1072  5088  ab196087 	static STRBUF str;
   1073  5088  ab196087 
   1074  5088  ab196087 	elfedit_cmd_optarg_t	*optarg;
   1075  5088  ab196087 	size_t			len, n, elipses_len;
   1076  5088  ab196087 	char			*cur;
   1077  5088  ab196087 	elfedit_optarg_item_t	item;
   1078  5088  ab196087 
   1079  5088  ab196087 	/*
   1080  5088  ab196087 	 * Estimate a worst case size for the usage string:
   1081  5088  ab196087 	 *	- module name
   1082  5088  ab196087 	 *	- lengths of the strings
   1083  5088  ab196087 	 *	- every option or argument is enclosed in brackets
   1084  5088  ab196087 	 *	- space in between each item, with an alternation (" | ")
   1085  5088  ab196087 	 *	- elipses will be displayed with each option and argument
   1086  5088  ab196087 	 */
   1087  5088  ab196087 	n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6;
   1088  5088  ab196087 	elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES));
   1089  5088  ab196087 	if ((optarg = cmd->cmd_opt) != NULL)
   1090  5088  ab196087 		while (optarg->oa_name != NULL) {
   1091  5088  ab196087 			elfedit_next_optarg(&optarg, &item);
   1092  5088  ab196087 			n += strlen(item.oai_name) + 5 + elipses_len;
   1093  5088  ab196087 		}
   1094  5088  ab196087 	if ((optarg = cmd->cmd_args) != NULL)
   1095  5088  ab196087 		while (optarg->oa_name != NULL) {
   1096  5088  ab196087 			elfedit_next_optarg(&optarg, &item);
   1097  5088  ab196087 			n += strlen(item.oai_name) + 5 + elipses_len;
   1098  5088  ab196087 		}
   1099  5088  ab196087 	n++;			/* Null termination */
   1100  5088  ab196087 
   1101  5088  ab196087 	/*
   1102  5088  ab196087 	 * If wrapping lines, we insert a newline and then wrap_str
   1103  5088  ab196087 	 * every USAGE_WRAP_COL characters.
   1104  5088  ab196087 	 */
   1105  5088  ab196087 	if (wrap_str != NULL)
   1106  5088  ab196087 		n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) *
   1107  5088  ab196087 		    (strlen(wrap_str) + 1);
   1108  5088  ab196087 
   1109  5088  ab196087 	strbuf_ensure_size(&str, n);
   1110  5088  ab196087 
   1111  5088  ab196087 	/* Command name */
   1112  5088  ab196087 	cur = str.buf;
   1113  5088  ab196087 	n = str.n;
   1114  5088  ab196087 	if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0)
   1115  5088  ab196087 		len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD),
   1116  5088  ab196087 		    cmd->cmd_name[0]);
   1117  5088  ab196087 	else
   1118  5088  ab196087 		len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD),
   1119  5088  ab196087 		    mod->mod_name, cmd->cmd_name[0]);
   1120  5088  ab196087 	cur += len;
   1121  5088  ab196087 	n -= len;
   1122  5088  ab196087 	cur_col += len;
   1123  5088  ab196087 
   1124  5088  ab196087 	if (cmd->cmd_opt != NULL)
   1125  5088  ab196087 		usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt,
   1126  5088  ab196087 		    1, wrap_str);
   1127  5088  ab196087 	if (cmd->cmd_args != NULL)
   1128  5088  ab196087 		usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args,
   1129  5088  ab196087 		    0, wrap_str);
   1130  5088  ab196087 
   1131  5088  ab196087 	return (str.buf);
   1132  5088  ab196087 }
   1133  5088  ab196087 
   1134  5088  ab196087 /*
   1135  5088  ab196087  * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE
   1136  5088  ab196087  * error giving usage information for the command currently
   1137  5088  ab196087  * referenced by state.cur_cmd.
   1138  5088  ab196087  */
   1139  5088  ab196087 void
   1140  5088  ab196087 elfedit_command_usage(void)
   1141  5088  ab196087 {
   1142  5088  ab196087 	elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD),
   1143  5088  ab196087 	    elfedit_format_command_usage(state.cur_cmd->ucmd_mod,
   1144  5088  ab196087 	    state.cur_cmd->ucmd_cmd, NULL, 0));
   1145  5088  ab196087 }
   1146  5088  ab196087 
   1147  5088  ab196087 
   1148  5088  ab196087 /*
   1149  5088  ab196087  * This function allows the loadable modules to get the command line
   1150  5088  ab196087  * flags.
   1151  5088  ab196087  */
   1152  5088  ab196087 elfedit_flag_t
   1153  5088  ab196087 elfedit_flags(void)
   1154  5088  ab196087 {
   1155  5088  ab196087 	return (state.flags);
   1156  5088  ab196087 }
   1157  5088  ab196087 
   1158  5088  ab196087 /*
   1159  5088  ab196087  * This function is used to register a per-command invocation output style
   1160  5088  ab196087  * that will momentarily override the global output style for the duration
   1161  5088  ab196087  * of the current command. This function must only be called by an
   1162  5088  ab196087  * active command.
   1163  5088  ab196087  *
   1164  5088  ab196087  * entry:
   1165  5088  ab196087  *	str - One of the valid strings for the output style
   1166  5088  ab196087  */
   1167  5088  ab196087 void
   1168  5088  ab196087 elfedit_set_cmd_outstyle(const char *str)
   1169  5088  ab196087 {
   1170  5088  ab196087 	if ((state.cur_cmd != NULL) && (str != NULL)) {
   1171  5088  ab196087 		if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0)
   1172  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_ERR,
   1173  5088  ab196087 			    MSG_INTL(MSG_ERR_BADOSTYLE), str);
   1174  5088  ab196087 		state.cur_cmd->ucmd_ostyle_set = 1;
   1175  5088  ab196087 	}
   1176  5088  ab196087 }
   1177  5088  ab196087 
   1178  5088  ab196087 /*
   1179  5088  ab196087  * This function allows the loadable modules to get the output style.
   1180  5088  ab196087  */
   1181  5088  ab196087 elfedit_outstyle_t
   1182  5088  ab196087 elfedit_outstyle(void)
   1183  5088  ab196087 {
   1184  5088  ab196087 	/*
   1185  5088  ab196087 	 * If there is an active  per-command output style,
   1186  5088  ab196087 	 * return it.
   1187  5088  ab196087 	 */
   1188  5088  ab196087 	if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set))
   1189  5088  ab196087 		return (state.cur_cmd->ucmd_ostyle);
   1190  5088  ab196087 
   1191  5088  ab196087 
   1192  5088  ab196087 	return (state.outstyle);
   1193  5088  ab196087 }
   1194  5088  ab196087 
   1195  5088  ab196087 /*
   1196  5088  ab196087  * Return the command descriptor of the currently executing command.
   1197  5088  ab196087  * For use only by the modules or code called by the modules.
   1198  5088  ab196087  */
   1199  5088  ab196087 elfeditGC_cmd_t *
   1200  5088  ab196087 elfedit_curcmd(void)
   1201  5088  ab196087 {
   1202  5088  ab196087 	return (state.cur_cmd->ucmd_cmd);
   1203  5088  ab196087 }
   1204  5088  ab196087 
   1205  5088  ab196087 /*
   1206  5088  ab196087  * Build a dynamically allocated elfedit_obj_state_t struct that
   1207  5088  ab196087  * contains a cache of the ELF file contents. This pre-chewed form
   1208  5088  ab196087  * is fed to each command, reducing the amount of ELF boilerplate
   1209  5088  ab196087  * code each command needs to contain.
   1210  5088  ab196087  *
   1211  5088  ab196087  * entry:
   1212  5088  ab196087  *	file - Name of file to process
   1213  5088  ab196087  *
   1214  5088  ab196087  * exit:
   1215  5088  ab196087  *	Fills state.elf with the necessary information for the open file.
   1216  5088  ab196087  *
   1217  5088  ab196087  * note: The resulting elfedit_obj_state_t is allocated from a single
   1218  5088  ab196087  *	piece of memory, such that a single call to free() suffices
   1219  5088  ab196087  *	to release it as well as any memory it references.
   1220  5088  ab196087  */
   1221  5088  ab196087 static void
   1222  5088  ab196087 init_obj_state(const char *file)
   1223  5088  ab196087 {
   1224  5088  ab196087 	int	fd;
   1225  5088  ab196087 	Elf	*elf;
   1226  5088  ab196087 	int	open_flag;
   1227  5088  ab196087 
   1228  5088  ab196087 	/*
   1229  5088  ab196087 	 * In readonly mode, we open the file readonly so that it is
   1230  5088  ab196087 	 * impossible to modify the file by accident. This also allows
   1231  5088  ab196087 	 * us to access readonly files, perhaps in a case where we don't
   1232  5088  ab196087 	 * intend to change it.
   1233  5088  ab196087 	 *
   1234  5088  ab196087 	 * We always use ELF_C_RDWR with elf_begin(), even in a readonly
   1235  5088  ab196087 	 * session. This allows us to modify the in-memory image, which
   1236  5088  ab196087 	 * can be useful when examining a file, even though we don't intend
   1237  5088  ab196087 	 * to modify the on-disk data. The file is not writable in
   1238  5088  ab196087 	 * this case, and we don't call elf_update(), so it is safe to do so.
   1239  5088  ab196087 	 */
   1240  5088  ab196087 	open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR);
   1241  5088  ab196087 	if ((fd = open(file, open_flag)) == -1) {
   1242  5088  ab196087 		int err = errno;
   1243  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE),
   1244  5088  ab196087 		    file, strerror(err));
   1245  5088  ab196087 	}
   1246  5088  ab196087 	(void) elf_version(EV_CURRENT);
   1247  5088  ab196087 	elf = elf_begin(fd, ELF_C_RDWR, NULL);
   1248  5088  ab196087 	if (elf == NULL) {
   1249  5088  ab196087 		(void) close(fd);
   1250  5088  ab196087 		elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN));
   1251  5088  ab196087 		/*NOTREACHED*/
   1252  5088  ab196087 	}
   1253  5088  ab196087 
   1254  5088  ab196087 	/* We only handle standalone ELF files */
   1255  5088  ab196087 	switch (elf_kind(elf)) {
   1256  5088  ab196087 	case ELF_K_AR:
   1257  5088  ab196087 		(void) close(fd);
   1258  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file);
   1259  5088  ab196087 		break;
   1260  5088  ab196087 	case ELF_K_ELF:
   1261  5088  ab196087 		break;
   1262  5088  ab196087 	default:
   1263  5088  ab196087 		(void) close(fd);
   1264  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE),
   1265  5088  ab196087 		    file);
   1266  5088  ab196087 		break;
   1267  5088  ab196087 	}
   1268  5088  ab196087 
   1269  5088  ab196087 	/*
   1270  5088  ab196087 	 * Tell libelf that we take responsibility for object layout.
   1271  5088  ab196087 	 * Otherwise, it will compute "proper" values for layout and
   1272  5088  ab196087 	 * alignment fields, and these values can overwrite the values
   1273  5088  ab196087 	 * set in the elfedit session. We are modifying existing
   1274  5088  ab196087 	 * objects --- the layout concerns have already been dealt
   1275  5088  ab196087 	 * with when the object was built.
   1276  5088  ab196087 	 */
   1277  5088  ab196087 	(void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
   1278  5088  ab196087 
   1279  5088  ab196087 	/* Fill in state.elf.obj_state */
   1280  5088  ab196087 	state.elf.elfclass = gelf_getclass(elf);
   1281  5088  ab196087 	switch (state.elf.elfclass) {
   1282  5088  ab196087 	case ELFCLASS32:
   1283  5088  ab196087 		elfedit32_init_obj_state(file, fd, elf);
   1284  5088  ab196087 		break;
   1285  5088  ab196087 	case ELFCLASS64:
   1286  5088  ab196087 		elfedit64_init_obj_state(file, fd, elf);
   1287  5088  ab196087 		break;
   1288  5088  ab196087 	default:
   1289  5088  ab196087 		(void) close(fd);
   1290  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS),
   1291  5088  ab196087 		    file);
   1292  5088  ab196087 		break;
   1293  5088  ab196087 	}
   1294  5088  ab196087 }
   1295  5088  ab196087 
   1296  5088  ab196087 
   1297  5892  ab196087 #ifdef DEBUG_MODULE_LIST
   1298  5088  ab196087 /*
   1299  5088  ab196087  * Debug routine. Dump the module list to stdout.
   1300  5088  ab196087  */
   1301  5088  ab196087 static void
   1302  5088  ab196087 dbg_module_list(char *title)
   1303  5088  ab196087 {
   1304  5088  ab196087 	MODLIST_T *m;
   1305  5088  ab196087 
   1306  5088  ab196087 	printf("<MODULE LIST: %s>\n", title);
   1307  5088  ab196087 	for (m = state.modlist; m != NULL; m = m->next) {
   1308  5088  ab196087 		printf("Module: >%s<\n", m->mod->mod_name);
   1309  5088  ab196087 		printf("    hdl:  %llx\n", m->dl_hdl);
   1310  5088  ab196087 		printf("    path: >%s<\n", m->path ? m->path : "<builtin>");
   1311  5088  ab196087 	}
   1312  5088  ab196087 	printf("<END OF MODULE LIST>\n");
   1313  5088  ab196087 }
   1314  5088  ab196087 #endif
   1315  5088  ab196087 
   1316  5088  ab196087 
   1317  5088  ab196087 /*
   1318  5088  ab196087  * Search the module list for the named module.
   1319  5088  ab196087  *
   1320  5088  ab196087  * entry:
   1321  5088  ab196087  *	name - Name of module to find
   1322  5088  ab196087  *	insdef - Address of variable to receive address of predecessor
   1323  5088  ab196087  *		node to the desired one.
   1324  5088  ab196087  *
   1325  5088  ab196087  * exit:
   1326  5088  ab196087  *	If the module is it is found, this routine returns the pointer to
   1327  5088  ab196087  *	its MODLIST_T structure. *insdef references the predecessor node, or
   1328  5088  ab196087  *	is NULL if the found item is at the head of the list.
   1329  5088  ab196087  *
   1330  5088  ab196087  *	If the module is not found, NULL is returned. *insdef references
   1331  5088  ab196087  *	the predecessor node of the position where an entry for this module
   1332  5088  ab196087  *	would be placed, or NULL if it would go at the beginning.
   1333  5088  ab196087  */
   1334  5088  ab196087 static MODLIST_T *
   1335  5088  ab196087 module_loaded(const char *name, MODLIST_T **insdef)
   1336  5088  ab196087 {
   1337  5088  ab196087 	MODLIST_T	*moddef;
   1338  5088  ab196087 	int		cmp;
   1339  5088  ab196087 
   1340  5088  ab196087 	*insdef = NULL;
   1341  5088  ab196087 	moddef = state.modlist;
   1342  5088  ab196087 	if (moddef != NULL) {
   1343  5088  ab196087 		cmp = strcasecmp(name, moddef->ml_mod->mod_name);
   1344  5088  ab196087 		if (cmp == 0) {		/* Desired module is first in list */
   1345  5088  ab196087 			return (moddef);
   1346  5088  ab196087 		} else if (cmp > 0) {	/* cmp > 0: Insert in middle/end */
   1347  5088  ab196087 			*insdef = moddef;
   1348  5088  ab196087 			moddef = moddef->ml_next;
   1349  5088  ab196087 			cmp = -1;
   1350  5088  ab196087 			while (moddef && (cmp < 0)) {
   1351  5088  ab196087 				cmp = strcasecmp(moddef->ml_mod->mod_name,
   1352  5088  ab196087 				    name);
   1353  5088  ab196087 				if (cmp == 0)
   1354  5088  ab196087 					return (moddef);
   1355  5088  ab196087 				if (cmp < 0) {
   1356  5088  ab196087 					*insdef = moddef;
   1357  5088  ab196087 					moddef = (*insdef)->ml_next;
   1358  5088  ab196087 				}
   1359  5088  ab196087 			}
   1360  5088  ab196087 		}
   1361  5088  ab196087 	}
   1362  5088  ab196087 
   1363  5088  ab196087 	return (NULL);
   1364  5088  ab196087 }
   1365  5088  ab196087 
   1366  5088  ab196087 
   1367  5088  ab196087 /*
   1368  5088  ab196087  * Determine if a file is a sharable object based on its file path.
   1369  5088  ab196087  * If path ends in a .so, followed optionally by a period and 1 or more
   1370  5088  ab196087  * digits, we say that it is and return a pointer to the first character
   1371  5088  ab196087  * of the suffix. Otherwise NULL is returned.
   1372  5088  ab196087  */
   1373  5088  ab196087 static const char *
   1374  5088  ab196087 path_is_so(const char *path)
   1375  5088  ab196087 {
   1376  5088  ab196087 	int		dotso_len;
   1377  5088  ab196087 	const char	*tail;
   1378  5088  ab196087 	size_t		len;
   1379  5088  ab196087 
   1380  5088  ab196087 	len = strlen(path);
   1381  5088  ab196087 	if (len == 0)
   1382  5088  ab196087 		return (NULL);
   1383  5088  ab196087 	tail = path + len;
   1384  5088  ab196087 	if (isdigit(*(tail - 1))) {
   1385  5088  ab196087 		while ((tail > path) && isdigit(*(tail - 1)))
   1386  5088  ab196087 			tail--;
   1387  5088  ab196087 		if ((tail <= path) || (*tail != '.'))
   1388  5088  ab196087 			return (NULL);
   1389  5088  ab196087 	}
   1390  5088  ab196087 	dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO));
   1391  5088  ab196087 	if ((tail - path) < dotso_len)
   1392  5088  ab196087 		return (NULL);
   1393  5088  ab196087 	tail -= dotso_len;
   1394  5088  ab196087 	if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0)
   1395  5088  ab196087 		return (tail);
   1396  5088  ab196087 
   1397  5088  ab196087 	return (NULL);
   1398  5088  ab196087 }
   1399  5088  ab196087 
   1400  5088  ab196087 
   1401  5088  ab196087 /*
   1402  5088  ab196087  * Locate the start of the unsuffixed file name within path. Returns pointer
   1403  5088  ab196087  * to first character of that name in path.
   1404  5088  ab196087  *
   1405  5088  ab196087  * entry:
   1406  5088  ab196087  *	path - Path to be examined.
   1407  5088  ab196087  *	tail - NULL, or pointer to position at tail of path from which
   1408  5088  ab196087  *		the search for '/' characters should start. If NULL,
   1409  5088  ab196087  *		strlen() is used to locate the end of the string.
   1410  5088  ab196087  *	buf - NULL, or buffer to receive a copy of the characters that
   1411  5088  ab196087  *		lie between the start of the filename and tail.
   1412  5088  ab196087  *	bufsize - sizeof(buf)
   1413  5088  ab196087  *
   1414  5088  ab196087  * exit:
   1415  5088  ab196087  *	The pointer to the first character of the unsuffixed file name
   1416  5088  ab196087  *	within path is returned. If buf is non-NULL, the characters
   1417  5088  ab196087  *	lying between that point and tail (or the end of path if tail
   1418  5088  ab196087  *	is NULL) are copied into buf.
   1419  5088  ab196087  */
   1420  5088  ab196087 static const char *
   1421  5088  ab196087 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz)
   1422  5088  ab196087 {
   1423  5088  ab196087 	const char 	*s;
   1424  5088  ab196087 
   1425  5088  ab196087 	if (tail == NULL)
   1426  5088  ab196087 		tail = path + strlen(path);
   1427  5088  ab196087 	s = tail;
   1428  5088  ab196087 	while ((s > path) && (*(s - 1) != '/'))
   1429  5088  ab196087 		s--;
   1430  5088  ab196087 	if (buf != NULL)
   1431  5088  ab196087 		elfedit_strnbcpy(buf, s, tail - s, bufsiz);
   1432  5088  ab196087 	return (s);
   1433  5088  ab196087 }
   1434  5088  ab196087 
   1435  5088  ab196087 
   1436  5088  ab196087 /*
   1437  5088  ab196087  * Issue an error on behalf of load_module(), taking care to release
   1438  5088  ab196087  * resources that routine may have aquired:
   1439  5088  ab196087  *
   1440  5088  ab196087  * entry:
   1441  5088  ab196087  *	moddef - NULL, or a module definition to be released via free()
   1442  5088  ab196087  *	dl_hdl - NULL, or a handle to a sharable object to release via
   1443  5088  ab196087  *		dlclose().
   1444  5088  ab196087  *	dl_path - If dl_hdl is non-NULL, the path to the sharable object
   1445  5088  ab196087  *		file that was loaded.
   1446  5088  ab196087  *	format - A format string to pass to elfedit_msg(), containing
   1447  5088  ab196087  *		no more than (3) %s format codes, and no other format codes.
   1448  5088  ab196087  *	[s1-s4] - Strings to pass to elfedit_msg() to satisfy the four
   1449  5088  ab196087  *		allowed %s codes in format. Should be set to NULL if the
   1450  5088  ab196087  *		format string does not need them.
   1451  5088  ab196087  *
   1452  5088  ab196087  * note:
   1453  5088  ab196087  *	This routine makes a copy of the s1-s4 strings before freeing any
   1454  5088  ab196087  *	memory or unmapping the sharable library. It is therefore safe to
   1455  5088  ab196087  *	use strings from moddef, or from the sharable library (which will
   1456  5088  ab196087  *	be unmapped) to satisfy the other arguments s1-s4.
   1457  5088  ab196087  */
   1458  5088  ab196087 static void
   1459  5088  ab196087 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path,
   1460  5088  ab196087     const char *format, const char *s1, const char *s2, const char *s3,
   1461  5088  ab196087     const char *s4)
   1462  5088  ab196087 {
   1463  5088  ab196087 #define	SCRBUFSIZE (PATH_MAX + 256)   /* A path, plus some extra */
   1464  5088  ab196087 
   1465  5088  ab196087 	char s1_buf[SCRBUFSIZE];
   1466  5088  ab196087 	char s2_buf[SCRBUFSIZE];
   1467  5088  ab196087 	char s3_buf[SCRBUFSIZE];
   1468  5088  ab196087 	char s4_buf[SCRBUFSIZE];
   1469  5088  ab196087 
   1470  5088  ab196087 	/*
   1471  5088  ab196087 	 * The caller may provide strings for s1-s3 that are from
   1472  5088  ab196087 	 * moddef. If we free moddef, the printf() will die on access
   1473  5088  ab196087 	 * to free memory. We could push back on the user and force
   1474  5088  ab196087 	 * each call to carefully make copies of such data. However, this
   1475  5088  ab196087 	 * is an easy case to miss. Furthermore, this is an error case,
   1476  5088  ab196087 	 * and machine efficiency is not the main issue. We therefore make
   1477  5088  ab196087 	 * copies of the s1-s3 strings here into auto variables, and then
   1478  5088  ab196087 	 * use those copies. The user is freed from worrying about it.
   1479  5088  ab196087 	 *
   1480  5088  ab196087 	 * We use oversized stack based buffers instead of malloc() to
   1481  5088  ab196087 	 * reduce the number of ways that things can go wrong while
   1482  5088  ab196087 	 * reporting the error.
   1483  5088  ab196087 	 */
   1484  5088  ab196087 	if (s1 != NULL)
   1485  5088  ab196087 		(void) strlcpy(s1_buf, s1, sizeof (s1_buf));
   1486  5088  ab196087 	if (s2 != NULL)
   1487  5088  ab196087 		(void) strlcpy(s2_buf, s2, sizeof (s2_buf));
   1488  5088  ab196087 	if (s3 != NULL)
   1489  5088  ab196087 		(void) strlcpy(s3_buf, s3, sizeof (s3_buf));
   1490  5088  ab196087 	if (s4 != NULL)
   1491  5088  ab196087 		(void) strlcpy(s4_buf, s4, sizeof (s4_buf));
   1492  5088  ab196087 
   1493  5088  ab196087 
   1494  5088  ab196087 	if (moddef != NULL)
   1495  5088  ab196087 		free(moddef);
   1496  5088  ab196087 
   1497  5088  ab196087 	if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0))
   1498  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
   1499  5088  ab196087 		    dl_path, dlerror());
   1500  5088  ab196087 
   1501  5088  ab196087 	elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf);
   1502  5088  ab196087 #undef	SCRBUFSIZE
   1503  5088  ab196087 }
   1504  5088  ab196087 
   1505  5088  ab196087 
   1506  5088  ab196087 /*
   1507  5088  ab196087  * Load a module sharable object for load_module().
   1508  5088  ab196087  *
   1509  5088  ab196087  * entry:
   1510  5088  ab196087  *	path - Path of file to open
   1511  5088  ab196087  *	moddef - If this function issues a non-returning error, it will
   1512  5088  ab196087  *		first return the memory referenced by moddef. This argument
   1513  5088  ab196087  *		is not used otherwise.
   1514  5088  ab196087  *	must_exist - If True, we consider it to be an error if the file given
   1515  5088  ab196087  *		by path does not exist. If False, no error is issued
   1516  5088  ab196087  *		and a NULL value is quietly returned.
   1517  5088  ab196087  *
   1518  5088  ab196087  * exit:
   1519  5088  ab196087  *	Returns a handle to the loaded object on success, or NULL if no
   1520  5088  ab196087  *	file was loaded.
   1521  5088  ab196087  */
   1522  5088  ab196087 static void *
   1523  5088  ab196087 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist)
   1524  5088  ab196087 {
   1525  5088  ab196087 	int	fd;
   1526  5088  ab196087 	void	*hdl;
   1527  5088  ab196087 
   1528  5088  ab196087 	/*
   1529  5088  ab196087 	 * If the file is not required to exist, and it doesn't, then
   1530  5088  ab196087 	 * we want to quietly return without an error.
   1531  5088  ab196087 	 */
   1532  5088  ab196087 	if (!must_exist) {
   1533  5088  ab196087 		fd = open(path, O_RDONLY);
   1534  5088  ab196087 		if (fd >= 0) {
   1535  5088  ab196087 			(void) close(fd);
   1536  5088  ab196087 		} else if (errno == ENOENT) {
   1537  5088  ab196087 			return (NULL);
   1538  5088  ab196087 		}
   1539  5088  ab196087 	}
   1540  5088  ab196087 
   1541  5088  ab196087 	if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL)
   1542  5088  ab196087 		load_module_err(moddef, NULL, NULL,
   1543  5088  ab196087 		    MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL);
   1544  5088  ab196087 
   1545  5088  ab196087 	return (hdl);
   1546  5088  ab196087 }
   1547  5088  ab196087 
   1548  5088  ab196087 
   1549  5088  ab196087 /*
   1550  5088  ab196087  * Sanity check option arguments to prevent common errors. The rest of
   1551  5088  ab196087  * elfedit assumes these tests have been done, and does not check
   1552  5088  ab196087  * again.
   1553  5088  ab196087  */
   1554  5088  ab196087 static void
   1555  5088  ab196087 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef,
   1556  5088  ab196087     const char *mod_name, const char *cmd_name,
   1557  5088  ab196087     void *dl_hdl, const char *dl_path)
   1558  5088  ab196087 {
   1559  5088  ab196087 #define	FAIL(_msg) errmsg = _msg; goto fail
   1560  5088  ab196087 
   1561  5088  ab196087 	Msg errmsg;
   1562  5088  ab196087 	elfedit_cmd_oa_mask_t	optmask = 0;
   1563  5088  ab196087 
   1564  5088  ab196087 	for (; optarg->oa_name != NULL; optarg++) {
   1565  5088  ab196087 		/*
   1566  5088  ab196087 		 * If ELFEDIT_CMDOA_F_INHERIT is set:
   1567  5088  ab196087 		 *	- oa_name must be a value in the range of
   1568  5088  ab196087 		 *		known ELFEDIT_STDOA_ values.
   1569  5088  ab196087 		 *	- oa_help must be NULL
   1570  5088  ab196087 		 *	- ELFEDIT_CMDOA_F_INHERIT must be the only flag set
   1571  5088  ab196087 		 */
   1572  5088  ab196087 		if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
   1573  5088  ab196087 			if ((((uintptr_t)optarg->oa_name) >
   1574  5088  ab196087 			    ELFEDIT_NUM_STDOA) ||
   1575  5088  ab196087 			    (optarg->oa_help != 0) ||
   1576  5088  ab196087 			    (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT))
   1577  5088  ab196087 				/*
   1578  5088  ab196087 				 * Can't use FAIL --- oa_name is not a valid
   1579  5088  ab196087 				 * string, and load_module_err() looks at args.
   1580  5088  ab196087 				 */
   1581  5088  ab196087 				load_module_err(moddef, dl_hdl, dl_path,
   1582  5088  ab196087 				    MSG_INTL(MSG_ERR_BADSTDOA), dl_path,
   1583  5088  ab196087 				    mod_name, cmd_name, NULL);
   1584  5088  ab196087 			continue;
   1585  5088  ab196087 		}
   1586  5088  ab196087 
   1587  5088  ab196087 		if (isopt) {
   1588  5088  ab196087 			/*
   1589  5088  ab196087 			 * Option name must start with a '-', and must
   1590  5088  ab196087 			 * have at one following character.
   1591  5088  ab196087 			 */
   1592  5088  ab196087 			if (optarg->oa_name[0] != '-') {
   1593  5088  ab196087 				/* MSG_INTL(MSG_ERR_OPT_MODPRE) */
   1594  5088  ab196087 				FAIL(MSG_ERR_OPT_MODPRE);
   1595  5088  ab196087 			}
   1596  5088  ab196087 			if (optarg->oa_name[1] == '\0') {
   1597  5088  ab196087 				/* MSG_INTL(MSG_ERR_OPT_MODLEN) */
   1598  5088  ab196087 				FAIL(MSG_ERR_OPT_MODLEN);
   1599  5088  ab196087 			}
   1600  5088  ab196087 
   1601  5088  ab196087 			/*
   1602  5088  ab196087 			 * oa_idmask must be 0, or it must have a single
   1603  5088  ab196087 			 * bit set (a power of 2).oa_excmask must be 0
   1604  5088  ab196087 			 * if oa_idmask is 0
   1605  5088  ab196087 			 */
   1606  5088  ab196087 			if (optarg->oa_idmask == 0) {
   1607  5088  ab196087 				if (optarg->oa_excmask != 0) {
   1608  5088  ab196087 					/* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */
   1609  5088  ab196087 					FAIL(MSG_ERR_OPT_EXCMASKN0);
   1610  5088  ab196087 				}
   1611  5088  ab196087 			} else {
   1612  5088  ab196087 				if (elfedit_bits_set(optarg->oa_idmask,
   1613  5088  ab196087 				    sizeof (optarg->oa_idmask)) != 1) {
   1614  5088  ab196087 					/* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */
   1615  5088  ab196087 					FAIL(MSG_ERR_OPT_IDMASKPOW2);
   1616  5088  ab196087 				}
   1617  5088  ab196087 
   1618  5088  ab196087 				/* Non-zero idmask must be unique */
   1619  5088  ab196087 				if ((optarg->oa_idmask & optmask) != 0) {
   1620  5088  ab196087 					/* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */
   1621  5088  ab196087 					FAIL(MSG_ERR_OPT_IDMASKUNIQ);
   1622  5088  ab196087 				}
   1623  5088  ab196087 
   1624  5088  ab196087 				/* Add this one to the overall mask */
   1625  5088  ab196087 				optmask |= optarg->oa_idmask;
   1626  5088  ab196087 			}
   1627  5088  ab196087 		} else {
   1628  5088  ab196087 			/*
   1629  5088  ab196087 			 * Argument name cannot start with a'-', and must
   1630  5088  ab196087 			 * not be a null string.
   1631  5088  ab196087 			 */
   1632  5088  ab196087 			if (optarg->oa_name[0] == '-') {
   1633  5088  ab196087 				/* MSG_INTL(MSG_ERR_ARG_MODPRE) */
   1634  5088  ab196087 				FAIL(MSG_ERR_ARG_MODPRE);
   1635  5088  ab196087 			}
   1636  5088  ab196087 			if (optarg->oa_name[1] == '\0') {
   1637  5088  ab196087 				/* MSG_INTL(MSG_ERR_ARG_MODLEN) */
   1638  5088  ab196087 				FAIL(MSG_ERR_ARG_MODLEN);
   1639  5088  ab196087 			}
   1640  5088  ab196087 
   1641  5088  ab196087 
   1642  5088  ab196087 			/* oa_idmask and oa_excmask must both be 0 */
   1643  5088  ab196087 			if ((optarg->oa_idmask != 0) ||
   1644  5088  ab196087 			    (optarg->oa_excmask != 0)) {
   1645  5088  ab196087 				/* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */
   1646  5088  ab196087 				FAIL(MSG_ERR_ARG_MASKNOT0);
   1647  5088  ab196087 			}
   1648  5088  ab196087 
   1649  5088  ab196087 		}
   1650  5088  ab196087 
   1651  5088  ab196087 		/*
   1652  5088  ab196087 		 * If it takes a value, make sure that we are
   1653  5088  ab196087 		 * processing options, because CMDOA_F_VALUE is not
   1654  5088  ab196087 		 * allowed for plain arguments. Then check the following
   1655  5088  ab196087 		 * item in the list:
   1656  5088  ab196087 		 *	- There must be a following item.
   1657  5088  ab196087 		 *	- oa_name must be non-NULL. This is the only field
   1658  5088  ab196087 		 *		that is used by elfedit.
   1659  5088  ab196087 		 *	- oa_help, oa_flags, oa_idmask, and oa_excmask
   1660  5088  ab196087 		 *		must be 0.
   1661  5088  ab196087 		 */
   1662  5088  ab196087 		if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) {
   1663  5088  ab196087 			elfedit_cmd_optarg_t *oa1 = optarg + 1;
   1664  5088  ab196087 
   1665  5088  ab196087 			if (!isopt) {
   1666  5088  ab196087 				/* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */
   1667  5088  ab196087 				FAIL(MSG_ERR_ARG_CMDOA_VAL);
   1668  5088  ab196087 			}
   1669  5088  ab196087 
   1670  5088  ab196087 			if ((optarg + 1)->oa_name == NULL) {
   1671  5088  ab196087 				/* MSG_INTL(MSG_ERR_BADMODOPTVAL) */
   1672  5088  ab196087 				FAIL(MSG_ERR_BADMODOPTVAL);
   1673  5088  ab196087 			}
   1674  5088  ab196087 
   1675  5088  ab196087 			if (oa1->oa_name == NULL) {
   1676  5088  ab196087 				/* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */
   1677  5088  ab196087 				FAIL(MSG_ERR_CMDOA_VALNAM);
   1678  5088  ab196087 			}
   1679  5088  ab196087 			if ((oa1->oa_help != NULL) || (oa1->oa_flags != 0) ||
   1680  5088  ab196087 			    (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) {
   1681  5088  ab196087 				/* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */
   1682  5088  ab196087 				FAIL(MSG_ERR_CMDOA_VALNOT0);
   1683  5088  ab196087 			}
   1684  5088  ab196087 			optarg++;
   1685  5088  ab196087 		}
   1686  5088  ab196087 	}
   1687  5088  ab196087 
   1688  5088  ab196087 
   1689  5088  ab196087 	return;
   1690  5088  ab196087 
   1691  5088  ab196087 fail:
   1692  5088  ab196087 	load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg),
   1693  5088  ab196087 	    dl_path, mod_name, cmd_name, optarg->oa_name);
   1694  5088  ab196087 }
   1695  5088  ab196087 
   1696  5088  ab196087 /*
   1697  5088  ab196087  * Look up the specified module, loading the module if necessary,
   1698  5088  ab196087  * and return its definition, or NULL on failure.
   1699  5088  ab196087  *
   1700  5088  ab196087  * entry:
   1701  5088  ab196087  *	name - Name of module to load. If name contains a '/' character or has
   1702  5088  ab196087  *		a ".so" suffix, then it is taken to be an absolute file path,
   1703  5088  ab196087  *		and is used directly as is. If name does not contain a '/'
   1704  5088  ab196087  *		character, then we look for it against the locations in
   1705  5088  ab196087  *		the module path, addint the '.so' suffix, and taking the first
   1706  5088  ab196087  *		one we find.
   1707  5088  ab196087  *	must_exist - If True, we consider it to be an error if we are unable
   1708  5088  ab196087  *		to locate a file to load and the module does not already exist.
   1709  5088  ab196087  *		If False, NULL is returned quietly in this case.
   1710  5088  ab196087  *	allow_abs - True if absolute paths are allowed. False to disallow
   1711  5088  ab196087  *		them.
   1712  5088  ab196087  *
   1713  5088  ab196087  * note:
   1714  5088  ab196087  *	If the path is absolute, then we load the file and take the module
   1715  5088  ab196087  *	name from the data returned by its elfedit_init() function. If a
   1716  5088  ab196087  *	module of that name is already loaded, it is unloaded and replaced
   1717  5088  ab196087  *	with the new one.
   1718  5088  ab196087  *
   1719  5088  ab196087  *	If the path is non absolute, then we check to see if the module has
   1720  5088  ab196087  *	already been loaded, and if so, we return that module definition.
   1721  5088  ab196087  *	In this case, nothing new is loaded. If the module has not been loaded,
   1722  5088  ab196087  *	we search the path for it and load it. If the module name provided
   1723  5088  ab196087  *	by the elfedit_init() function does not match the name of the file,
   1724  5088  ab196087  *	an error results.
   1725  5088  ab196087  */
   1726  5088  ab196087 elfeditGC_module_t *
   1727  5088  ab196087 elfedit_load_module(const char *name, int must_exist, int allow_abs)
   1728  5088  ab196087 {
   1729  5088  ab196087 	elfedit_init_func_t	*init_func;
   1730  5088  ab196087 	elfeditGC_module_t	*mod;
   1731  5088  ab196087 	MODLIST_T		*moddef, *insdef;
   1732  5088  ab196087 	const char		*path;
   1733  5088  ab196087 	char			path_buf[PATH_MAX + 1];
   1734  5088  ab196087 	void			*hdl;
   1735  5088  ab196087 	size_t			i;
   1736  5088  ab196087 	int			is_abs_path;
   1737  5088  ab196087 	elfeditGC_cmd_t		*cmd;
   1738  5088  ab196087 
   1739  5088  ab196087 	/*
   1740  5088  ab196087 	 * If the name includes a .so suffix, or has any '/' characters,
   1741  5088  ab196087 	 * then it is an absolute path that we use as is to load the named
   1742  5088  ab196087 	 * file. Otherwise, we iterate over the path, adding the .so suffix
   1743  5088  ab196087 	 * and load the first file that matches.
   1744  5088  ab196087 	 */
   1745  5088  ab196087 	is_abs_path = (path_is_so(name) != NULL) ||
   1746  5088  ab196087 	    (name != elfedit_basename(name, NULL, NULL, 0));
   1747  5088  ab196087 
   1748  5088  ab196087 	if (is_abs_path && !allow_abs)
   1749  5088  ab196087 		load_module_err(NULL, NULL, NULL,
   1750  5088  ab196087 		    MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
   1751  5088  ab196087 
   1752  5088  ab196087 	/*
   1753  5088  ab196087 	 * If this is a non-absolute path, search for the module already
   1754  5088  ab196087 	 * having been loaded, and return it if so.
   1755  5088  ab196087 	 */
   1756  5088  ab196087 	if (!is_abs_path) {
   1757  5088  ab196087 		moddef = module_loaded(name, &insdef);
   1758  5088  ab196087 		if (moddef != NULL)
   1759  5088  ab196087 			return (moddef->ml_mod);
   1760  5088  ab196087 		/*
   1761  5088  ab196087 		 * As a result of module_loaded(), insdef now contains the
   1762  5088  ab196087 		 * immediate predecessor node for the new one, or NULL if
   1763  5088  ab196087 		 * it goes at the front. In the absolute-path case, we take
   1764  5088  ab196087 		 * care of this below, after the sharable object is loaded.
   1765  5088  ab196087 		 */
   1766  5088  ab196087 	}
   1767  5088  ab196087 
   1768  5088  ab196087 	/*
   1769  5088  ab196087 	 * malloc() a module definition block before trying to dlopen().
   1770  5088  ab196087 	 * Doing things in the other order can cause the dlopen()'d object
   1771  5088  ab196087 	 * to leak: If elfedit_malloc() fails, it can cause a jump to the
   1772  5088  ab196087 	 * outer command loop without returning to the caller. Hence,
   1773  5088  ab196087 	 * there will be no opportunity to clean up. Allocaing the module
   1774  5088  ab196087 	 * first allows us to free it if necessary.
   1775  5088  ab196087 	 */
   1776  5088  ab196087 	moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF),
   1777  5088  ab196087 	    sizeof (*moddef) + PATH_MAX + 1);
   1778  5088  ab196087 	moddef->ml_path = ((char *)moddef) + sizeof (*moddef);
   1779  5088  ab196087 
   1780  5088  ab196087 	if (is_abs_path) {
   1781  5088  ab196087 		path = name;
   1782  5088  ab196087 		hdl = load_module_dlopen(name, moddef, must_exist);
   1783  5088  ab196087 	} else {
   1784  5088  ab196087 		hdl = NULL;
   1785  5088  ab196087 		path = path_buf;
   1786  5088  ab196087 		for (i = 0; i < state.modpath.n; i++) {
   1787  5088  ab196087 			if (snprintf(path_buf, sizeof (path_buf),
   1788  5088  ab196087 			    MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i],
   1789  5088  ab196087 			    name) > sizeof (path_buf))
   1790  5088  ab196087 				load_module_err(moddef, NULL, NULL,
   1791  5088  ab196087 				    MSG_INTL(MSG_ERR_PATHTOOLONG),
   1792  5088  ab196087 				    state.modpath.seg[i], name, NULL, NULL);
   1793  5088  ab196087 			hdl = load_module_dlopen(path, moddef, 0);
   1794  5088  ab196087 		}
   1795  5088  ab196087 		if (must_exist && (hdl == NULL))
   1796  5088  ab196087 			load_module_err(moddef, NULL, NULL,
   1797  5088  ab196087 			    MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
   1798  5088  ab196087 	}
   1799  5088  ab196087 
   1800  5088  ab196087 	if (hdl == NULL) {
   1801  5088  ab196087 		free(moddef);
   1802  5088  ab196087 		return (NULL);
   1803  5088  ab196087 	}
   1804  5088  ab196087 
   1805  5088  ab196087 	if (state.elf.elfclass == ELFCLASS32) {
   1806  5088  ab196087 		init_func = (elfedit_init_func_t *)
   1807  5088  ab196087 		    dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32));
   1808  5088  ab196087 	} else {
   1809  5088  ab196087 		init_func = (elfedit_init_func_t *)
   1810  5088  ab196087 		    dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64));
   1811  5088  ab196087 	}
   1812  5088  ab196087 	if (init_func == NULL)
   1813  5088  ab196087 		load_module_err(moddef, hdl, path,
   1814  5088  ab196087 		    MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL);
   1815  5088  ab196087 
   1816  5088  ab196087 	/*
   1817  5088  ab196087 	 * Note that the init function will be passing us an
   1818  5088  ab196087 	 * elfedit[32|64]_module_t pointer, which we cast to the
   1819  5088  ab196087 	 * generic module pointer type in order to be able to manage
   1820  5088  ab196087 	 * either type with one set of code.
   1821  5088  ab196087 	 */
   1822  5088  ab196087 	if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT)))
   1823  5088  ab196087 		load_module_err(moddef, hdl, path,
   1824  5088  ab196087 		    MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL);
   1825  5088  ab196087 
   1826  5088  ab196087 	/*
   1827  5088  ab196087 	 * Enforce some rules, to help module developers:
   1828  5088  ab196087 	 *	- The primary name of a command must not be
   1829  5088  ab196087 	 *		the empty string ("").
   1830  5088  ab196087 	 *	- Options must start with a '-' followed by at least
   1831  5088  ab196087 	 *		one character.
   1832  5088  ab196087 	 *	- Arguments and options must be well formed.
   1833  5088  ab196087 	 */
   1834  5088  ab196087 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
   1835  5088  ab196087 		if (**cmd->cmd_name == '\0')
   1836  5088  ab196087 			load_module_err(moddef, hdl, path,
   1837  5088  ab196087 			    MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name,
   1838  5088  ab196087 			    NULL, NULL, NULL);
   1839  5088  ab196087 
   1840  5088  ab196087 		if (cmd->cmd_args != NULL)
   1841  5088  ab196087 			validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name,
   1842  5088  ab196087 			    cmd->cmd_name[0], hdl, path);
   1843  5088  ab196087 		if (cmd->cmd_opt != NULL)
   1844  5088  ab196087 			validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name,
   1845  5088  ab196087 			    cmd->cmd_name[0], hdl, path);
   1846  5088  ab196087 	}
   1847  5088  ab196087 
   1848  5088  ab196087 	/*
   1849  5088  ab196087 	 * Check the name the module provides. How we handle this depends
   1850  5088  ab196087 	 * on whether the path is absolute or the result of a path search.
   1851  5088  ab196087 	 */
   1852  5088  ab196087 	if (is_abs_path) {
   1853  5088  ab196087 		MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef);
   1854  5088  ab196087 
   1855  5088  ab196087 		if (old_moddef != NULL) {	/* Replace existing */
   1856  5088  ab196087 			free(moddef);		/* Rare case: Don't need it */
   1857  5088  ab196087 			/*
   1858  5088  ab196087 			 * Be sure we don't unload builtin modules!
   1859  5088  ab196087 			 * These have a NULL dl_hdl field.
   1860  5088  ab196087 			 */
   1861  5088  ab196087 			if (old_moddef->ml_dl_hdl == NULL)
   1862  5088  ab196087 				load_module_err(NULL, hdl, path,
   1863  5088  ab196087 				    MSG_INTL(MSG_ERR_CNTULSMOD),
   1864  5088  ab196087 				    old_moddef->ml_mod->mod_name, NULL,
   1865  5088  ab196087 				    NULL, NULL);
   1866  5088  ab196087 
   1867  5088  ab196087 			/* Unload existing */
   1868  5088  ab196087 			if (dlclose(old_moddef->ml_dl_hdl) != 0)
   1869  5088  ab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
   1870  5088  ab196087 				    MSG_INTL(MSG_ERR_CNTDLCLOSE),
   1871  5088  ab196087 				    old_moddef->ml_path, dlerror());
   1872  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
   1873  5088  ab196087 			    MSG_INTL(MSG_DEBUG_MODUNLOAD),
   1874  5088  ab196087 			    old_moddef->ml_mod->mod_name, old_moddef->ml_path);
   1875  5088  ab196087 			old_moddef->ml_mod = mod;
   1876  5088  ab196087 			old_moddef->ml_dl_hdl = hdl;
   1877  5088  ab196087 			(void) strlcpy((char *)old_moddef->ml_path, path,
   1878  5088  ab196087 			    PATH_MAX + 1);
   1879  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_DEBUG,
   1880  5088  ab196087 			    MSG_INTL(MSG_DEBUG_MODLOAD),
   1881  5088  ab196087 			    old_moddef->ml_mod->mod_name, path);
   1882  5088  ab196087 			return (old_moddef->ml_mod);
   1883  5088  ab196087 		}
   1884  5088  ab196087 		/*
   1885  5088  ab196087 		 * insdef now contains the insertion point for the absolute
   1886  5088  ab196087 		 * path case.
   1887  5088  ab196087 		 */
   1888  5088  ab196087 	} else {
   1889  5088  ab196087 		/* If the names don't match, then error */
   1890  5088  ab196087 		if (strcasecmp(name, mod->mod_name) != 0)
   1891  5088  ab196087 			load_module_err(moddef, hdl, path,
   1892  5088  ab196087 			    MSG_INTL(MSG_ERR_BADMODNAME),
   1893  5088  ab196087 			    mod->mod_name, name, path, NULL);
   1894  5088  ab196087 	}
   1895  5088  ab196087 
   1896  5088  ab196087 	/*
   1897  5088  ab196087 	 * Link module into the module list. If insdef is NULL,
   1898  5088  ab196087 	 * it goes at the head. If insdef is non-NULL, it goes immediately
   1899  5088  ab196087 	 * after
   1900  5088  ab196087 	 */
   1901  5088  ab196087 	if (insdef == NULL) {
   1902  5088  ab196087 		moddef->ml_next = state.modlist;
   1903  5088  ab196087 		state.modlist = moddef;
   1904  5088  ab196087 	} else {
   1905  5088  ab196087 		moddef->ml_next = insdef->ml_next;
   1906  5088  ab196087 		insdef->ml_next = moddef;
   1907  5088  ab196087 	}
   1908  5088  ab196087 	moddef->ml_mod = mod;
   1909  5088  ab196087 	moddef->ml_dl_hdl = hdl;
   1910  5088  ab196087 	(void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1);
   1911  5088  ab196087 
   1912  5088  ab196087 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD),
   1913  5088  ab196087 	    moddef->ml_mod->mod_name, path);
   1914  5088  ab196087 
   1915  5088  ab196087 	return (moddef->ml_mod);
   1916  5088  ab196087 }
   1917  5088  ab196087 
   1918  5088  ab196087 
   1919  5088  ab196087 /*
   1920  5088  ab196087  * Unload the specified module
   1921  5088  ab196087  */
   1922  5088  ab196087 void
   1923  5088  ab196087 elfedit_unload_module(const char *name)
   1924  5088  ab196087 {
   1925  5088  ab196087 	MODLIST_T	*moddef, *insdef;
   1926  5088  ab196087 
   1927  5088  ab196087 	moddef = module_loaded(name, &insdef);
   1928  5088  ab196087 	if (moddef == NULL)
   1929  5088  ab196087 		return;
   1930  5088  ab196087 
   1931  5088  ab196087 	/* Built in modules cannot be unloaded. They have a NULL dl_hdl field */
   1932  5088  ab196087 	if (moddef->ml_dl_hdl == NULL)
   1933  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD),
   1934  5088  ab196087 		    moddef->ml_mod->mod_name);
   1935  5088  ab196087 
   1936  5088  ab196087 	/*
   1937  5088  ab196087 	 * When we unload it, the name string goes with it. So
   1938  5088  ab196087 	 * announce it while we still can without having to make a copy.
   1939  5088  ab196087 	 */
   1940  5088  ab196087 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD),
   1941  5088  ab196087 	    moddef->ml_mod->mod_name, moddef->ml_path);
   1942  5088  ab196087 
   1943  5088  ab196087 	/*
   1944  5088  ab196087 	 * Close it before going further. On failure, we'll jump, and the
   1945  5088  ab196087 	 * record will remain in the module list. On success,
   1946  5088  ab196087 	 * we'll retain control, and can safely remove it.
   1947  5088  ab196087 	 */
   1948  5088  ab196087 	if (dlclose(moddef->ml_dl_hdl) != 0)
   1949  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
   1950  5088  ab196087 		    moddef->ml_path, dlerror());
   1951  5088  ab196087 
   1952  5088  ab196087 	/* Unlink the record from the module list */
   1953  5088  ab196087 	if (insdef == NULL)
   1954  5088  ab196087 		state.modlist = moddef->ml_next;
   1955  5088  ab196087 	else
   1956  5088  ab196087 		insdef->ml_next = moddef->ml_next;
   1957  5088  ab196087 
   1958  5088  ab196087 	/* Release the memory */
   1959  5088  ab196087 	free(moddef);
   1960  5088  ab196087 }
   1961  5088  ab196087 
   1962  5088  ab196087 
   1963  5088  ab196087 /*
   1964  5088  ab196087  * Load all sharable objects found in the specified directory.
   1965  5088  ab196087  *
   1966  5088  ab196087  * entry:
   1967  5088  ab196087  *	dirpath - Path of directory to process.
   1968  5088  ab196087  *	must_exist - If True, it is an error if diropen() fails to open
   1969  5088  ab196087  *		the given directory. Of False, we quietly ignore it and return.
   1970  5088  ab196087  *	abs_path - If True, files are loaded using their literal paths.
   1971  5088  ab196087  *		If False, their module name is extracted from the dirpath
   1972  5088  ab196087  *		and a path based search is used to locate it.
   1973  5088  ab196087  */
   1974  5088  ab196087 void
   1975  5088  ab196087 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path)
   1976  5088  ab196087 {
   1977  5088  ab196087 	char		path[PATH_MAX + 1];
   1978  5088  ab196087 	DIR		*dir;
   1979  5088  ab196087 	struct dirent	*dp;
   1980  5088  ab196087 	const char 	*tail;
   1981  5088  ab196087 
   1982  5088  ab196087 	dir = opendir(dirpath);
   1983  5088  ab196087 	if (dir == NULL) {
   1984  5088  ab196087 		int err = errno;
   1985  5088  ab196087 
   1986  5088  ab196087 		if (!must_exist && (err == ENOENT))
   1987  5088  ab196087 			return;
   1988  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR),
   1989  5088  ab196087 		    dirpath, strerror(err));
   1990  5088  ab196087 		/*NOTREACHED*/
   1991  5088  ab196087 	}
   1992  5088  ab196087 
   1993  5088  ab196087 	while (dp = readdir(dir)) {
   1994  5088  ab196087 		if ((tail = path_is_so(dp->d_name)) != NULL) {
   1995  5088  ab196087 			if (abs_path) {
   1996  5088  ab196087 				(void) snprintf(path, sizeof (path),
   1997  5088  ab196087 				    MSG_ORIG(MSG_FMT_BLDPATH), dirpath,
   1998  5088  ab196087 				    dp->d_name);
   1999  5088  ab196087 			} else {
   2000  5088  ab196087 				(void) elfedit_basename(dp->d_name, tail,
   2001  5088  ab196087 				    path, sizeof (path));
   2002  5088  ab196087 			}
   2003  5088  ab196087 			(void) elfedit_load_module(path, must_exist, 1);
   2004  5088  ab196087 		}
   2005  5088  ab196087 	}
   2006  5088  ab196087 	(void) closedir(dir);
   2007  5088  ab196087 }
   2008  5088  ab196087 
   2009  5088  ab196087 
   2010  5088  ab196087 /*
   2011  5088  ab196087  * Follow the module load path, and load the first module found for each
   2012  5088  ab196087  * given name.
   2013  5088  ab196087  */
   2014  5088  ab196087 void
   2015  5088  ab196087 elfedit_load_modpath(void)
   2016  5088  ab196087 {
   2017  5088  ab196087 	size_t		i;
   2018  5088  ab196087 
   2019  5088  ab196087 	for (i = 0; i < state.modpath.n; i++)
   2020  5088  ab196087 		elfedit_load_moddir(state.modpath.seg[i], 0, 0);
   2021  5088  ab196087 }
   2022  5088  ab196087 
   2023  5088  ab196087 /*
   2024  5088  ab196087  * Given a module definition, look for the specified command.
   2025  5088  ab196087  * Returns the command if found, and NULL otherwise.
   2026  5088  ab196087  */
   2027  5088  ab196087 static elfeditGC_cmd_t *
   2028  5088  ab196087 find_cmd(elfeditGC_module_t *mod, const char *name)
   2029  5088  ab196087 {
   2030  5088  ab196087 	elfeditGC_cmd_t *cmd;
   2031  5088  ab196087 	const char **cmd_name;
   2032  5088  ab196087 
   2033  5088  ab196087 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
   2034  5088  ab196087 		for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
   2035  5088  ab196087 			if (strcasecmp(name, *cmd_name) == 0) {
   2036  5088  ab196087 				if (cmd_name != cmd->cmd_name)
   2037  5088  ab196087 					elfedit_msg(ELFEDIT_MSG_DEBUG,
   2038  5088  ab196087 					    MSG_INTL(MSG_DEBUG_CMDALIAS),
   2039  5088  ab196087 					    mod->mod_name, *cmd_name,
   2040  5088  ab196087 					    mod->mod_name, *cmd->cmd_name);
   2041  5088  ab196087 				return (cmd);
   2042  5088  ab196087 			}
   2043  5088  ab196087 
   2044  5088  ab196087 	return (NULL);
   2045  5088  ab196087 }
   2046  5088  ab196087 
   2047  5088  ab196087 
   2048  5088  ab196087 /*
   2049  5088  ab196087  * Given a command name, return its command definition.
   2050  5088  ab196087  *
   2051  5088  ab196087  * entry:
   2052  5088  ab196087  *	name - Command to be looked up
   2053  5088  ab196087  *	must_exist - If True, we consider it to be an error if the command
   2054  5088  ab196087  *		does not exist. If False, NULL is returned quietly in
   2055  5088  ab196087  *		this case.
   2056  5088  ab196087  *	mod_ret - NULL, or address of a variable to receive the
   2057  5088  ab196087  *		module definition block of the module containing
   2058  5088  ab196087  *		the command.
   2059  5088  ab196087  *
   2060  5088  ab196087  * exit:
   2061  5088  ab196087  *	On success, returns a pointer to the command definition, and
   2062  5088  ab196087  *	if mod_ret is non-NULL, *mod_ret receives a pointer to the
   2063  5088  ab196087  *	module definition. On failure, must_exist determines the
   2064  5088  ab196087  *	action taken: If must_exist is True, an error is issued and
   2065  5088  ab196087  *	control does not return to the caller. If must_exist is False,
   2066  5088  ab196087  *	NULL is quietly returned.
   2067  5088  ab196087  *
   2068  5088  ab196087  * note:
   2069  5088  ab196087  *	A ':' in name is used to delimit the module and command names.
   2070  5088  ab196087  *	If it is omitted, or if it is the first non-whitespace character
   2071  5088  ab196087  *	in the name, then the built in sys: module is implied.
   2072  5088  ab196087  */
   2073  5088  ab196087 elfeditGC_cmd_t *
   2074  5088  ab196087 elfedit_find_command(const char *name, int must_exist,
   2075  5088  ab196087     elfeditGC_module_t **mod_ret)
   2076  5088  ab196087 {
   2077  5088  ab196087 	elfeditGC_module_t	*mod;
   2078  5088  ab196087 	const char		*mod_str;
   2079  5088  ab196087 	const char		*cmd_str;
   2080  5088  ab196087 	char			mod_buf[ELFEDIT_MAXMODNAM + 1];
   2081  5088  ab196087 	size_t			n;
   2082  5088  ab196087 	elfeditGC_cmd_t		*cmd;
   2083  5088  ab196087 
   2084  5088  ab196087 
   2085  5088  ab196087 	cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON));
   2086  5088  ab196087 	if (cmd_str == NULL) {		/* No module name -> sys: */
   2087  5088  ab196087 		mod_str = MSG_ORIG(MSG_MOD_SYS);
   2088  5088  ab196087 		cmd_str = name;
   2089  5088  ab196087 	} else if (cmd_str == name) {	/* Empty module name -> sys: */
   2090  5088  ab196087 		mod_str = MSG_ORIG(MSG_MOD_SYS);
   2091  5088  ab196087 		cmd_str++;		/* Skip the colon */
   2092  5088  ab196087 	} else {			/* Have both module and command */
   2093  5088  ab196087 		n = cmd_str - name;
   2094  5088  ab196087 		if (n >= sizeof (mod_buf)) {
   2095  5088  ab196087 			if (must_exist)
   2096  5088  ab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
   2097  5088  ab196087 				    MSG_INTL(MSG_ERR_MODNAMTOOLONG), name);
   2098  5088  ab196087 			return (NULL);
   2099  5088  ab196087 		}
   2100  5088  ab196087 		(void) strlcpy(mod_buf, name, n + 1);
   2101  5088  ab196087 		mod_str = mod_buf;
   2102  5088  ab196087 		cmd_str++;
   2103  5088  ab196087 	}
   2104  5088  ab196087 
   2105  5088  ab196087 	/* Lookup/load module. Won't return on error */
   2106  5088  ab196087 	mod = elfedit_load_module(mod_str, must_exist, 0);
   2107  5088  ab196087 	if (mod == NULL)
   2108  5088  ab196087 		return (NULL);
   2109  5088  ab196087 
   2110  5088  ab196087 	/* Locate the command */
   2111  5088  ab196087 	cmd = find_cmd(mod, cmd_str);
   2112  5088  ab196087 	if (cmd == NULL) {
   2113  5088  ab196087 		if (must_exist) {
   2114  5088  ab196087 			/*
   2115  5088  ab196087 			 * Catch empty command in order to provide
   2116  5088  ab196087 			 * a better error message.
   2117  5088  ab196087 			 */
   2118  5088  ab196087 			if (*cmd_str == '\0') {
   2119  5088  ab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
   2120  5088  ab196087 				    MSG_INTL(MSG_ERR_MODNOCMD), mod_str);
   2121  5088  ab196087 			} else {
   2122  5088  ab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
   2123  5088  ab196087 				    MSG_INTL(MSG_ERR_UNRECCMD),
   2124  5088  ab196087 				    mod_str, cmd_str);
   2125  5088  ab196087 			}
   2126  5088  ab196087 		}
   2127  5088  ab196087 	} else {
   2128  5088  ab196087 		if (mod_ret != NULL)
   2129  5088  ab196087 			*mod_ret = mod;
   2130  5088  ab196087 	}
   2131  5088  ab196087 	return (cmd);
   2132  5088  ab196087 }
   2133  5088  ab196087 
   2134  5088  ab196087 
   2135  5088  ab196087 /*
   2136  5088  ab196087  * Release all user command blocks found on state.ucmd
   2137  5088  ab196087  */
   2138  5088  ab196087 static void
   2139  5088  ab196087 free_user_cmds(void)
   2140  5088  ab196087 {
   2141  5088  ab196087 	USER_CMD_T *next;
   2142  5088  ab196087 
   2143  5088  ab196087 	while (state.ucmd.list) {
   2144  5088  ab196087 		next = state.ucmd.list->ucmd_next;
   2145  5088  ab196087 		free(state.ucmd.list);
   2146  5088  ab196087 		state.ucmd.list = next;
   2147  5088  ab196087 	}
   2148  5088  ab196087 	state.ucmd.tail = NULL;
   2149  5088  ab196087 	state.ucmd.n = 0;
   2150  5088  ab196087 	state.cur_cmd = NULL;
   2151  5088  ab196087 }
   2152  5088  ab196087 
   2153  5088  ab196087 
   2154  5088  ab196087 /*
   2155  5088  ab196087  * Process all user command blocks found on state.ucmd, and then
   2156  5088  ab196087  * remove them from the list.
   2157  5088  ab196087  */
   2158  5088  ab196087 static void
   2159  5088  ab196087 dispatch_user_cmds()
   2160  5088  ab196087 {
   2161  5088  ab196087 	USER_CMD_T		*ucmd;
   2162  5088  ab196087 	elfedit_cmdret_t	cmd_ret;
   2163  5088  ab196087 
   2164  5088  ab196087 	ucmd = state.ucmd.list;
   2165  5088  ab196087 	if (ucmd) {
   2166  5088  ab196087 		/* Do them, in order */
   2167  5088  ab196087 		for (; ucmd; ucmd = ucmd->ucmd_next) {
   2168  5088  ab196087 			state.cur_cmd = ucmd;
   2169  5088  ab196087 			if (!state.msg_jbuf.active)
   2170  5088  ab196087 				elfedit_msg(ELFEDIT_MSG_DEBUG,
   2171  5088  ab196087 				    MSG_INTL(MSG_DEBUG_EXECCMD),
   2172  5088  ab196087 				    ucmd->ucmd_orig_str);
   2173  5088  ab196087 			/*
   2174  5088  ab196087 			 * The cmd_func field is the generic definition.
   2175  5088  ab196087 			 * We need to cast it to the type that matches
   2176  5088  ab196087 			 * the proper ELFCLASS before calling it.
   2177  5088  ab196087 			 */
   2178  5088  ab196087 			if (state.elf.elfclass == ELFCLASS32) {
   2179  5088  ab196087 				elfedit32_cmd_func_t *cmd_func =
   2180  5088  ab196087 				    (elfedit32_cmd_func_t *)
   2181  5088  ab196087 				    ucmd->ucmd_cmd->cmd_func;
   2182  5088  ab196087 
   2183  5088  ab196087 				cmd_ret = (* cmd_func)(state.elf.obj_state.s32,
   2184  5088  ab196087 				    ucmd->ucmd_argc, ucmd->ucmd_argv);
   2185  5088  ab196087 			} else {
   2186  5088  ab196087 				elfedit64_cmd_func_t *cmd_func =
   2187  5088  ab196087 				    (elfedit64_cmd_func_t *)
   2188  5088  ab196087 				    ucmd->ucmd_cmd->cmd_func;
   2189  5088  ab196087 
   2190  5088  ab196087 				cmd_ret = (* cmd_func)(state.elf.obj_state.s64,
   2191  5088  ab196087 				    ucmd->ucmd_argc, ucmd->ucmd_argv);
   2192  5088  ab196087 			}
   2193  5088  ab196087 			state.cur_cmd = NULL;
   2194  5088  ab196087 			/* If a pager was started, wrap it up */
   2195  5088  ab196087 			elfedit_pager_cleanup();
   2196  5088  ab196087 
   2197  5088  ab196087 			switch (cmd_ret) {
   2198  9273       Ali 			case ELFEDIT_CMDRET_MOD_OS_MACH:
   2199  9273       Ali 				/*
   2200  9273       Ali 				 * Inform the elfconst module that the machine
   2201  9273       Ali 				 * or osabi has has changed. It may be necessary
   2202  9273       Ali 				 * to fetch new strings from libconv.
   2203  9273       Ali 				 */
   2204  9273       Ali 				state.elf.elfconst_ehdr_change = 1;
   2205  9273       Ali 				/*FALLTHROUGH*/
   2206  5088  ab196087 			case ELFEDIT_CMDRET_MOD:
   2207  5088  ab196087 				/*
   2208  5088  ab196087 				 * Command modified the output ELF image,
   2209  5088  ab196087 				 * mark the file as needing a flush to disk.
   2210  5088  ab196087 				 */
   2211  5088  ab196087 				state.file.dirty = 1;
   2212  5088  ab196087 				break;
   2213  5088  ab196087 			case ELFEDIT_CMDRET_FLUSH:
   2214  5088  ab196087 				/*
   2215  5088  ab196087 				 * Command flushed the output file,
   2216  5088  ab196087 				 * clear the dirty bit.
   2217  5088  ab196087 				 */
   2218  5088  ab196087 				state.file.dirty = 0;
   2219  5088  ab196087 			}
   2220  5088  ab196087 		}
   2221  5088  ab196087 		free_user_cmds();
   2222  5088  ab196087 	}
   2223  5088  ab196087 }
   2224  5088  ab196087 
   2225  5088  ab196087 
   2226  5088  ab196087 /*
   2227  5892  ab196087  * Given the pointer to the character following a '\' character in
   2228  5892  ab196087  * a C style literal, return the ASCII character code it represents,
   2229  5892  ab196087  * and advance the string pointer to the character following the last
   2230  5892  ab196087  * character in the escape sequence.
   2231  5892  ab196087  *
   2232  5892  ab196087  * entry:
   2233  5892  ab196087  *	str - Address of string pointer to first character following
   2234  5892  ab196087  *		the backslash.
   2235  5892  ab196087  *
   2236  5892  ab196087  * exit:
   2237  5892  ab196087  *	If the character is not valid, an error is thrown and this routine
   2238  5892  ab196087  *	does not return to its caller. Otherwise, it returns the ASCII
   2239  5892  ab196087  *	code for the translated character, and *str has been advanced.
   2240  5892  ab196087  */
   2241  5892  ab196087 static int
   2242  5892  ab196087 translate_c_esc(char **str)
   2243  5892  ab196087 {
   2244  5892  ab196087 	char *s = *str;
   2245  5892  ab196087 	int	ch;
   2246  5892  ab196087 	int	i;
   2247  5892  ab196087 
   2248  5892  ab196087 	ch = *s++;
   2249  5892  ab196087 	switch (ch) {
   2250  5892  ab196087 	case 'a':
   2251  5892  ab196087 		ch = '\a';
   2252  5892  ab196087 		break;
   2253  5892  ab196087 	case 'b':
   2254  5892  ab196087 		ch = '\b';
   2255  5892  ab196087 		break;
   2256  5892  ab196087 	case 'f':
   2257  5892  ab196087 		ch = '\f';
   2258  5892  ab196087 		break;
   2259  5892  ab196087 	case 'n':
   2260  5892  ab196087 		ch = '\n';
   2261  5892  ab196087 		break;
   2262  5892  ab196087 	case 'r':
   2263  5892  ab196087 		ch = '\r';
   2264  5892  ab196087 		break;
   2265  5892  ab196087 	case 't':
   2266  5892  ab196087 		ch = '\t';
   2267  5892  ab196087 		break;
   2268  5892  ab196087 	case 'v':
   2269  5892  ab196087 		ch = '\v';
   2270  5892  ab196087 		break;
   2271  5892  ab196087 
   2272  5892  ab196087 	case '0':
   2273  5892  ab196087 	case '1':
   2274  5892  ab196087 	case '2':
   2275  5892  ab196087 	case '3':
   2276  5892  ab196087 	case '4':
   2277  5892  ab196087 	case '5':
   2278  5892  ab196087 	case '6':
   2279  5892  ab196087 	case '7':
   2280  5892  ab196087 		/* Octal constant: There can be up to 3 digits */
   2281  5892  ab196087 		ch -= '0';
   2282  5892  ab196087 		for (i = 0; i < 2; i++) {
   2283  5892  ab196087 			if ((*s < '0') || (*s > '7'))
   2284  5892  ab196087 				break;
   2285  5892  ab196087 			ch = (ch << 3) + (*s++ - '0');
   2286  5892  ab196087 		}
   2287  5892  ab196087 		break;
   2288  5892  ab196087 
   2289  5892  ab196087 	/*
   2290  5892  ab196087 	 * There are some cases where ch already has the desired value.
   2291  5892  ab196087 	 * These cases exist simply to remove the special meaning that
   2292  5892  ab196087 	 * character would otherwise have. We need to match them to
   2293  5892  ab196087 	 * prevent them from falling into the default error case.
   2294  5892  ab196087 	 */
   2295  5892  ab196087 	case '\\':
   2296  5892  ab196087 	case '\'':
   2297  5892  ab196087 	case '"':
   2298  5892  ab196087 		break;
   2299  5892  ab196087 
   2300  5892  ab196087 	default:
   2301  5892  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADCESC), ch);
   2302  5892  ab196087 		break;
   2303  5892  ab196087 	}
   2304  5892  ab196087 
   2305  5892  ab196087 	*str = s;
   2306  5892  ab196087 	return (ch);
   2307  5892  ab196087 }
   2308  5892  ab196087 
   2309  5892  ab196087 
   2310  5892  ab196087 /*
   2311  5088  ab196087  * Prepare a GETTOK_STATE struct for gettok().
   2312  5088  ab196087  *
   2313  5088  ab196087  * entry:
   2314  5088  ab196087  *	gettok_state - gettok state block to use
   2315  5088  ab196087  *	str - Writable buffer to tokenize. Note that gettok()
   2316  5088  ab196087  *		is allowed to change the contents of this buffer.
   2317  5088  ab196087  *	inc_null_final - If the line ends in whitespace instead of
   2318  5088  ab196087  *		immediately hitting a NULL, and inc_null_final is TRUE,
   2319  5088  ab196087  *		then a null final token is generated. Otherwise trailing
   2320  5088  ab196087  *		whitespace is ignored.
   2321  5088  ab196087  */
   2322  5088  ab196087 static void
   2323  5088  ab196087 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final)
   2324  5088  ab196087 {
   2325  5088  ab196087 	gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf;
   2326  5088  ab196087 	gettok_state->gtok_inc_null_final = inc_null_final;
   2327  5088  ab196087 	gettok_state->gtok_null_seen = 0;
   2328  5088  ab196087 }
   2329  5088  ab196087 
   2330  5088  ab196087 
   2331  5088  ab196087 /*
   2332  5088  ab196087  * Locate the next token from the buffer.
   2333  5088  ab196087  *
   2334  5088  ab196087  * entry:
   2335  5088  ab196087  *	gettok_state - State of gettok() operation. Initialized
   2336  5088  ab196087  *		by gettok_init(), and passed to gettok().
   2337  5088  ab196087  *
   2338  5088  ab196087  * exit:
   2339  5088  ab196087  *	If a token is found, gettok_state->gtok_last_token is filled in
   2340  5088  ab196087  *	with the details and True (1) is returned. If no token is found,
   2341  5088  ab196087  *	False (1) is returned, and the contents of
   2342  5088  ab196087  *	gettok_state->gtok_last_token are undefined.
   2343  5088  ab196087  *
   2344  5088  ab196087  * note:
   2345  5088  ab196087  *	- The token returned references the memory in gettok_state->gtok_buf.
   2346  5088  ab196087  *		The caller should not modify the buffer until all such
   2347  5088  ab196087  *		pointers have been discarded.
   2348  5088  ab196087  *	- This routine will modify the contents of gettok_state->gtok_buf
   2349  5088  ab196087  *		as necessary to remove quotes and eliminate escape
   2350  5088  ab196087  *		(\)characters.
   2351  5088  ab196087  */
   2352  5088  ab196087 static int
   2353  5088  ab196087 gettok(GETTOK_STATE *gettok_state)
   2354  5088  ab196087 {
   2355  5088  ab196087 	char	*str = gettok_state->gtok_cur_buf;
   2356  5088  ab196087 	char	*look;
   2357  5088  ab196087 	int	quote_ch = '\0';
   2358  5088  ab196087 
   2359  5088  ab196087 	/* Skip leading whitespace */
   2360  5088  ab196087 	while (isspace(*str))
   2361  5088  ab196087 		str++;
   2362  5088  ab196087 
   2363  5088  ab196087 	if (*str == '\0') {
   2364  5088  ab196087 		/*
   2365  5088  ab196087 		 * If user requested it, and there was whitespace at the
   2366  5088  ab196087 		 * end, then generate one last null token.
   2367  5088  ab196087 		 */
   2368  5088  ab196087 		if (gettok_state->gtok_inc_null_final &&
   2369  5088  ab196087 		    !gettok_state->gtok_null_seen) {
   2370  5088  ab196087 			gettok_state->gtok_inc_null_final = 0;
   2371  5088  ab196087 			gettok_state->gtok_null_seen = 1;
   2372  5088  ab196087 			gettok_state->gtok_last_token.tok_str = str;
   2373  5088  ab196087 			gettok_state->gtok_last_token.tok_len = 0;
   2374  5088  ab196087 			gettok_state->gtok_last_token.tok_line_off =
   2375  5088  ab196087 			    str - gettok_state->gtok_buf;
   2376  5088  ab196087 			return (1);
   2377  5088  ab196087 		}
   2378  5088  ab196087 		gettok_state->gtok_null_seen = 1;
   2379  5088  ab196087 		return (0);
   2380  5088  ab196087 	}
   2381  5088  ab196087 
   2382  5088  ab196087 	/*
   2383  5088  ab196087 	 * Read token: The standard delimiter is whitespace, but
   2384  5088  ab196087 	 * we honor either single or double quotes. Also, we honor
   2385  5088  ab196087 	 * backslash escapes.
   2386  5088  ab196087 	 */
   2387  5088  ab196087 	gettok_state->gtok_last_token.tok_str = look = str;
   2388  5088  ab196087 	gettok_state->gtok_last_token.tok_line_off =
   2389  5088  ab196087 	    look - gettok_state->gtok_buf;
   2390  5088  ab196087 	for (; *look; look++) {
   2391  5088  ab196087 		if (*look == quote_ch) {	/* Terminates active quote */
   2392  5088  ab196087 			quote_ch = '\0';
   2393  5088  ab196087 			continue;
   2394  5088  ab196087 		}
   2395  5088  ab196087 
   2396  5088  ab196087 		if (quote_ch == '\0') {		/* No quote currently active */
   2397  5088  ab196087 			if ((*look == '\'') || (*look == '"')) {
   2398  5088  ab196087 				quote_ch = *look;	/* New active quote */
   2399  5088  ab196087 				continue;
   2400  5088  ab196087 			}
   2401  5088  ab196087 			if (isspace(*look))
   2402  5088  ab196087 				break;
   2403  5088  ab196087 		}
   2404  5088  ab196087 
   2405  5892  ab196087 		/*
   2406  5892  ab196087 		 * The semantics of the backslash character depends on
   2407  5892  ab196087 		 * the quote style in use:
   2408  5892  ab196087 		 *	- Within single quotes, backslash is not
   2409  5892  ab196087 		 *		an escape character, and is taken literally.
   2410  5892  ab196087 		 *	- If outside of quotes, the backslash is an escape
   2411  5892  ab196087 		 *		character. The backslash is ignored and the
   2412  5892  ab196087 		 *		following character is taken literally, losing
   2413  5892  ab196087 		 *		any special properties it normally has.
   2414  5892  ab196087 		 *	- Within double quotes, backslash works like a
   2415  5892  ab196087 		 *		backslash escape within a C literal. Certain
   2416  5892  ab196087 		 *		escapes are recognized and replaced with their
   2417  5892  ab196087 		 *		special character. Any others are an error.
   2418  5892  ab196087 		 */
   2419  5088  ab196087 		if (*look == '\\') {
   2420  5892  ab196087 			if (quote_ch == '\'') {
   2421  5892  ab196087 				*str++ = *look;
   2422  5892  ab196087 				continue;
   2423  5892  ab196087 			}
   2424  5892  ab196087 
   2425  5088  ab196087 			look++;
   2426  5892  ab196087 			if (*look == '\0') {	/* Esc applied to NULL term? */
   2427  5892  ab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
   2428  5892  ab196087 				    MSG_INTL(MSG_ERR_ESCEOL));
   2429  5892  ab196087 				/*NOTREACHED*/
   2430  5892  ab196087 			}
   2431  5892  ab196087 
   2432  5892  ab196087 			if (quote_ch == '"') {
   2433  5892  ab196087 				*str++ = translate_c_esc(&look);
   2434  5892  ab196087 				look--;		/* for() will advance by 1 */
   2435  5892  ab196087 				continue;
   2436  5892  ab196087 			}
   2437  5088  ab196087 		}
   2438  5088  ab196087 
   2439  5088  ab196087 		if (look != str)
   2440  5088  ab196087 			*str = *look;
   2441  5088  ab196087 		str++;
   2442  5088  ab196087 	}
   2443  5892  ab196087 
   2444  5892  ab196087 	/* Don't allow unterminated quoted tokens */
   2445  5892  ab196087 	if (quote_ch != '\0')
   2446  5892  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNTERMQUOTE),
   2447  5892  ab196087 		    quote_ch);
   2448  5892  ab196087 
   2449  5088  ab196087 	gettok_state->gtok_last_token.tok_len = str -
   2450  5088  ab196087 	    gettok_state->gtok_last_token.tok_str;
   2451  5088  ab196087 	gettok_state->gtok_null_seen = *look == '\0';
   2452  5088  ab196087 	if (!gettok_state->gtok_null_seen)
   2453  5088  ab196087 		look++;
   2454  5088  ab196087 	*str = '\0';
   2455  5088  ab196087 	gettok_state->gtok_cur_buf = look;
   2456  5088  ab196087 
   2457  5892  ab196087 #ifdef DEBUG_GETTOK
   2458  5892  ab196087 	printf("GETTOK >");
   2459  5892  ab196087 	elfedit_str_to_c_literal(gettok_state->gtok_last_token.tok_str,
   2460  5892  ab196087 	    elfedit_write);
   2461  5892  ab196087 	printf("< \tlen(%d) offset(%d)\n",
   2462  5088  ab196087 	    gettok_state->gtok_last_token.tok_len,
   2463  5088  ab196087 	    gettok_state->gtok_last_token.tok_line_off);
   2464  5088  ab196087 #endif
   2465  5088  ab196087 
   2466  5088  ab196087 	return (1);
   2467  5088  ab196087 }
   2468  5088  ab196087 
   2469  5088  ab196087 
   2470  5088  ab196087 /*
   2471  5088  ab196087  * Tokenize the user command string, and return a pointer to the
   2472  5088  ab196087  * TOK_STATE buffer maintained by this function. That buffer contains
   2473  5088  ab196087  * the tokenized strings.
   2474  5088  ab196087  *
   2475  5088  ab196087  * entry:
   2476  5088  ab196087  *	user_cmd_str - String to tokenize
   2477  5088  ab196087  *	len - # of characters in user_cmd_str to examine. If
   2478  5088  ab196087  *		(len < 0), then the complete string is processed
   2479  5088  ab196087  *		stopping with the NULL termination. Otherwise,
   2480  5088  ab196087  *		processing stops after len characters, and any
   2481  5088  ab196087  *		remaining characters are ignored.
   2482  5088  ab196087  *	inc_null_final - If True, and if user_cmd_str has whitespace
   2483  5088  ab196087  *		at the end following the last non-null token, then
   2484  5088  ab196087  *		a final null token will be included. If False, null
   2485  5088  ab196087  *		tokens are ignored.
   2486  5088  ab196087  *
   2487  5088  ab196087  * note:
   2488  5088  ab196087  *	This routine returns pointers to internally allocated memory.
   2489  5088  ab196087  *	The caller must not alter anything contained in the TOK_STATE
   2490  5088  ab196087  *	buffer returned. Furthermore, the the contents of TOK_STATE
   2491  5088  ab196087  *	are only valid until the next call to tokenize_user_cmd().
   2492  5088  ab196087  */
   2493  5088  ab196087 static TOK_STATE *
   2494  5088  ab196087 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final)
   2495  5088  ab196087 {
   2496  5088  ab196087 #define	INITIAL_TOK_ALLOC 5
   2497  5088  ab196087 
   2498  5088  ab196087 	/*
   2499  5088  ab196087 	 * As we parse the user command, we need temporary space to
   2500  5088  ab196087 	 * hold the tokens. We do this by dynamically allocating a string
   2501  5088  ab196087 	 * buffer and a token array, and doubling them as necessary. This
   2502  5088  ab196087 	 * is a single threaded application, so static variables suffice.
   2503  5088  ab196087 	 */
   2504  5088  ab196087 	static STRBUF str;
   2505  5088  ab196087 	static TOK_STATE tokst;
   2506  5088  ab196087 
   2507  5088  ab196087 	GETTOK_STATE	gettok_state;
   2508  5088  ab196087 	size_t		n;
   2509  5088  ab196087 
   2510  5088  ab196087 	/*
   2511  5088  ab196087 	 * Make a copy we can modify. If (len == 0), take the entire
   2512  5088  ab196087 	 * string. Otherwise limit it to the specified length.
   2513  5088  ab196087 	 */
   2514  5088  ab196087 	tokst.tokst_cmd_len = strlen(user_cmd_str);
   2515  5088  ab196087 	if ((len > 0) && (len < tokst.tokst_cmd_len))
   2516  5088  ab196087 		tokst.tokst_cmd_len = len;
   2517  5088  ab196087 	tokst.tokst_cmd_len++;	/* Room for NULL termination */
   2518  5088  ab196087 	strbuf_ensure_size(&str, tokst.tokst_cmd_len);
   2519  5088  ab196087 	(void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len);
   2520  5088  ab196087 
   2521  5088  ab196087 	/* Trim off any newline character that might be present */
   2522  5088  ab196087 	if ((tokst.tokst_cmd_len > 1) &&
   2523  5088  ab196087 	    (str.buf[tokst.tokst_cmd_len - 2] == '\n')) {
   2524  5088  ab196087 		tokst.tokst_cmd_len--;
   2525  5088  ab196087 		str.buf[tokst.tokst_cmd_len - 1] = '\0';
   2526  5088  ab196087 	}
   2527  5088  ab196087 
   2528  5088  ab196087 	/* Tokenize the user command string into tok struct */
   2529  5088  ab196087 	gettok_init(&gettok_state, str.buf, inc_null_final);
   2530  5088  ab196087 	tokst.tokst_str_size = 0;	/* Space needed for token strings */
   2531  5088  ab196087 	for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0;
   2532  5088  ab196087 	    tokst.tokst_cnt++) {
   2533  5088  ab196087 		/* If we need more room, expand the token buffer */
   2534  5088  ab196087 		if (tokst.tokst_cnt >= tokst.tokst_bufsize) {
   2535  5088  ab196087 			n = (tokst.tokst_bufsize == 0) ?
   2536  5088  ab196087 			    INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2);
   2537  5088  ab196087 			tokst.tokst_buf = elfedit_realloc(
   2538  5088  ab196087 			    MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf,
   2539  5088  ab196087 			    n * sizeof (*tokst.tokst_buf));
   2540  5088  ab196087 			tokst.tokst_bufsize = n;
   2541  5088  ab196087 		}
   2542  5088  ab196087 		tokst.tokst_str_size +=
   2543  5088  ab196087 		    gettok_state.gtok_last_token.tok_len + 1;
   2544  5088  ab196087 		tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token;
   2545  5088  ab196087 	}
   2546  5088  ab196087 	/* fold the command token to lowercase */
   2547  5088  ab196087 	if (tokst.tokst_cnt > 0) {
   2548  5088  ab196087 		char *s;
   2549  5088  ab196087 
   2550  5088  ab196087 		for (s = tokst.tokst_buf[0].tok_str; *s; s++)
   2551  5088  ab196087 			if (isupper(*s))
   2552  5088  ab196087 				*s = tolower(*s);
   2553  5088  ab196087 	}
   2554  5088  ab196087 
   2555  5088  ab196087 	return (&tokst);
   2556  5088  ab196087 
   2557  5088  ab196087 #undef	INITIAL_TOK_ALLOC
   2558  5088  ab196087 }
   2559  5088  ab196087 
   2560  5088  ab196087 
   2561  5088  ab196087 /*
   2562  5088  ab196087  * Parse the user command string, and put an entry for it at the end
   2563  5088  ab196087  * of state.ucmd.
   2564  5088  ab196087  */
   2565  5088  ab196087 static void
   2566  5088  ab196087 parse_user_cmd(const char *user_cmd_str)
   2567  5088  ab196087 {
   2568  5088  ab196087 	TOK_STATE	*tokst;
   2569  5088  ab196087 	char		*s;
   2570  5088  ab196087 	size_t		n;
   2571  5088  ab196087 	size_t		len;
   2572  5088  ab196087 	USER_CMD_T	*ucmd;
   2573  5088  ab196087 	elfeditGC_module_t *mod;
   2574  5088  ab196087 	elfeditGC_cmd_t	*cmd;
   2575  5088  ab196087 
   2576  5088  ab196087 	/*
   2577  5088  ab196087 	 * Break it into tokens. If there are none, then it is
   2578  5088  ab196087 	 * an empty command and is ignored.
   2579  5088  ab196087 	 */
   2580  5088  ab196087 	tokst = tokenize_user_cmd(user_cmd_str, -1, 0);
   2581  5088  ab196087 	if (tokst->tokst_cnt == 0)
   2582  5088  ab196087 		return;
   2583  5088  ab196087 
   2584  5088  ab196087 	/* Find the command. Won't return on error */
   2585  5088  ab196087 	cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod);
   2586  5088  ab196087 
   2587  5088  ab196087 	/*
   2588  5088  ab196087 	 * If there is no ELF file being edited, then only commands
   2589  5088  ab196087 	 * from the sys: module are allowed.
   2590  5088  ab196087 	 */
   2591  5088  ab196087 	if ((state.file.present == 0) &&
   2592  5088  ab196087 	    (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0))
   2593  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY),
   2594  5088  ab196087 		    mod->mod_name, cmd->cmd_name[0]);
   2595  5088  ab196087 
   2596  5088  ab196087 
   2597  5088  ab196087 	/* Allocate, fill in, and insert a USER_CMD_T block */
   2598  5088  ab196087 	n = S_DROUND(sizeof (USER_CMD_T));
   2599  5088  ab196087 	ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD),
   2600  5088  ab196087 	    n + (sizeof (char *) * (tokst->tokst_cnt - 1)) +
   2601  5088  ab196087 	    tokst->tokst_cmd_len + tokst->tokst_str_size);
   2602  5088  ab196087 	ucmd->ucmd_next = NULL;
   2603  5088  ab196087 	ucmd->ucmd_argc = tokst->tokst_cnt - 1;
   2604  5088  ab196087 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
   2605  5088  ab196087 	ucmd->ucmd_argv = (const char **)(n + (char *)ucmd);
   2606  5088  ab196087 	ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc);
   2607  5088  ab196087 	(void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len);
   2608  5088  ab196087 	ucmd->ucmd_mod = mod;
   2609  5088  ab196087 	ucmd->ucmd_cmd = cmd;
   2610  5088  ab196087 	ucmd->ucmd_ostyle_set = 0;
   2611  5088  ab196087 	s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len;
   2612  5088  ab196087 	for (n = 1; n < tokst->tokst_cnt; n++) {
   2613  5088  ab196087 		len = tokst->tokst_buf[n].tok_len + 1;
   2614  5088  ab196087 		ucmd->ucmd_argv[n - 1] = s;
   2615  5088  ab196087 		(void) strncpy(s, tokst->tokst_buf[n].tok_str, len);
   2616  5088  ab196087 		s += len;
   2617  5088  ab196087 	}
   2618  5088  ab196087 	if (state.ucmd.list == NULL) {
   2619  5088  ab196087 		state.ucmd.list = state.ucmd.tail = ucmd;
   2620  5088  ab196087 	} else {
   2621  5088  ab196087 		state.ucmd.tail->ucmd_next = ucmd;
   2622  5088  ab196087 		state.ucmd.tail = ucmd;
   2623  5088  ab196087 	}
   2624  5088  ab196087 	state.ucmd.n++;
   2625  5088  ab196087 }
   2626  5088  ab196087 
   2627  5088  ab196087 
   2628  5088  ab196087 /*
   2629  5088  ab196087  * Copy infile to a new file with the name given by outfile.
   2630  5088  ab196087  */
   2631  5088  ab196087 static void
   2632  5088  ab196087 create_outfile(const char *infile, const char *outfile)
   2633  5088  ab196087 {
   2634  5088  ab196087 	pid_t pid;
   2635  5088  ab196087 	int statloc;
   2636  5088  ab196087 	struct stat statbuf;
   2637  5088  ab196087 
   2638  5088  ab196087 
   2639  5088  ab196087 	pid = fork();
   2640  5088  ab196087 	switch (pid) {
   2641  5088  ab196087 	case -1:			/* Unable to create process */
   2642  5088  ab196087 		{
   2643  5088  ab196087 			int err = errno;
   2644  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK),
   2645  5088  ab196087 			    strerror(err));
   2646  5088  ab196087 		}
   2647  5088  ab196087 		/*NOTREACHED*/
   2648  5088  ab196087 		return;
   2649  5088  ab196087 
   2650  5088  ab196087 	case 0:
   2651  5088  ab196087 		(void) execl(MSG_ORIG(MSG_STR_BINCP),
   2652  5088  ab196087 		    MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL);
   2653  5088  ab196087 		/*
   2654  5088  ab196087 		 * exec() only returns on error. This is the child process,
   2655  5088  ab196087 		 * so we want to stay away from the usual error mechanism
   2656  5088  ab196087 		 * and handle things directly.
   2657  5088  ab196087 		 */
   2658  5088  ab196087 		{
   2659  5088  ab196087 			int err = errno;
   2660  5088  ab196087 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC),
   2661  5088  ab196087 			    MSG_ORIG(MSG_STR_ELFEDIT),
   2662  5088  ab196087 			    MSG_ORIG(MSG_STR_BINCP), strerror(err));
   2663  5088  ab196087 		}
   2664  5088  ab196087 		exit(1);
   2665  5088  ab196087 		/*NOTREACHED*/
   2666  5088  ab196087 	}
   2667  5088  ab196087 
   2668  5088  ab196087 	/* This is the parent: Wait for the child to terminate */
   2669  5088  ab196087 	if (waitpid(pid, &statloc,  0) != pid) {
   2670  5088  ab196087 		int err = errno;
   2671  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT),
   2672  5088  ab196087 		    strerror(err));
   2673  5088  ab196087 	}
   2674  5088  ab196087 	/*
   2675  5088  ab196087 	 * If the child failed, then terminate the process. There is no
   2676  5088  ab196087 	 * need for an error message, because the child will have taken
   2677  5088  ab196087 	 * care of that.
   2678  5088  ab196087 	 */
   2679  5088  ab196087 	if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0))
   2680  5088  ab196087 		exit(1);
   2681  5088  ab196087 
   2682  5088  ab196087 	/* Make sure the copy allows user write access */
   2683  5088  ab196087 	if (stat(outfile, &statbuf) == -1) {
   2684  5088  ab196087 		int err = errno;
   2685  5088  ab196087 		(void) unlink(outfile);
   2686  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT),
   2687  5088  ab196087 		    outfile, strerror(err));
   2688  5088  ab196087 	}
   2689  5088  ab196087 	if ((statbuf.st_mode & S_IWUSR) == 0) {
   2690  5088  ab196087 		/* Only keep permission bits, and add user write */
   2691  5088  ab196087 		statbuf.st_mode |= S_IWUSR;
   2692  5088  ab196087 		statbuf.st_mode &= 07777;   /* Only keep the permission bits */
   2693  5088  ab196087 		if (chmod(outfile, statbuf.st_mode) == -1) {
   2694  5088  ab196087 			int err = errno;
   2695  5088  ab196087 			(void) unlink(outfile);
   2696  5088  ab196087 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD),
   2697  5088  ab196087 			    outfile, strerror(err));
   2698  5088  ab196087 		}
   2699  5088  ab196087 	}
   2700  5088  ab196087 }
   2701  5088  ab196087 
   2702  5088  ab196087 /*
   2703  5088  ab196087  * Given a module path string, determine how long the resulting path will
   2704  5088  ab196087  * be when all % tokens have been expanded.
   2705  5088  ab196087  *
   2706  5088  ab196087  * entry:
   2707  5088  ab196087  *	path - Path for which expanded length is desired
   2708  5088  ab196087  *	origin_root - Root of $ORIGIN  tree containing running elfedit program
   2709  5088  ab196087  *
   2710  5088  ab196087  * exit:
   2711  5088  ab196087  *	Returns the value strlen() will give for the expanded path.
   2712  5088  ab196087  */
   2713  5088  ab196087 static size_t
   2714  5088  ab196087 modpath_strlen(const char *path, const char *origin_root)
   2715  5088  ab196087 {
   2716  5088  ab196087 	size_t len = 0;
   2717  5088  ab196087 	const char *s;
   2718  5088  ab196087 
   2719  5088  ab196087 	s = path;
   2720  5088  ab196087 	len = 0;
   2721  5088  ab196087 	for (s = path; *s != '\0'; s++) {
   2722  5088  ab196087 		if (*s == '%') {
   2723  5088  ab196087 			s++;
   2724  5088  ab196087 			switch (*s) {
   2725  5088  ab196087 			case 'i':	/* ISA of running elfedit */
   2726  5088  ab196087 				len += strlen(isa_i_str);
   2727  5088  ab196087 				break;
   2728  5088  ab196087 			case 'I':	/* "" for 32-bit, same as %i for 64 */
   2729  5088  ab196087 				len += strlen(isa_I_str);
   2730  5088  ab196087 				break;
   2731  5088  ab196087 			case 'o':	/* Insert default path */
   2732  5088  ab196087 				len +=
   2733  5088  ab196087 				    modpath_strlen(MSG_ORIG(MSG_STR_MODPATH),
   2734  5088  ab196087 				    origin_root);
   2735  5088  ab196087 				break;
   2736  5088  ab196087 			case 'r':	/* root of tree with running elfedit */
   2737  5088  ab196087 				len += strlen(origin_root);
   2738  5088  ab196087 				break;
   2739  5088  ab196087 
   2740  5088  ab196087 			case '%':	/* %% is reduced to just '%' */
   2741  5088  ab196087 				len++;
   2742  5088  ab196087 				break;
   2743  5088  ab196087 			default:	/* All other % codes are reserved */
   2744  5088  ab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
   2745  5088  ab196087 				    MSG_INTL(MSG_ERR_BADPATHCODE), *s);
   2746  5088  ab196087 				/*NOTREACHED*/
   2747  5088  ab196087 				break;
   2748  5088  ab196087 			}
   2749  5088  ab196087 		} else {	/* Non-% character passes straight through */
   2750  5088  ab196087 			len++;
   2751  5088  ab196087 		}
   2752  5088  ab196087 	}
   2753  5088  ab196087 
   2754  5088  ab196087 	return (len);
   2755  5088  ab196087 }
   2756  5088  ab196087 
   2757  5088  ab196087 
   2758  5088  ab196087 /*
   2759  5088  ab196087  * Given a module path string, and a buffer large enough to hold the results,
   2760  5088  ab196087  * fill the buffer with the expanded path.
   2761  5088  ab196087  *
   2762  5088  ab196087  * entry:
   2763  5088  ab196087  *	path - Path for which expanded length is desired
   2764  5088  ab196087  *	origin_root - Root of tree containing running elfedit program
   2765  5088  ab196087  *	buf - Buffer to receive the result. buf must as large or larger
   2766  5088  ab196087  *		than the value given by modpath_strlen().
   2767  5088  ab196087  *
   2768  5088  ab196087  * exit:
   2769  5088  ab196087  *	Returns pointer to location following the last character
   2770  5088  ab196087  *	written to buf. A NULL byte is written to that address.
   2771  5088  ab196087  */
   2772  5088  ab196087 static char *
   2773  5088  ab196087 modpath_expand(const char *path, const char *origin_root, char *buf)
   2774  5088  ab196087 {
   2775  5088  ab196087 	size_t len;
   2776  5088  ab196087 	const char *cp_str;
   2777  5088  ab196087 
   2778  5088  ab196087 	for (; *path != '\0'; path++) {
   2779  5088  ab196087 		if (*path == '%') {
   2780  5088  ab196087 			path++;
   2781  5088  ab196087 			cp_str = NULL;
   2782  5088  ab196087 			switch (*path) {
   2783  5088  ab196087 			case 'i':	/* ISA of running elfedit */
   2784  5088  ab196087 				cp_str = isa_i_str;
   2785  5088  ab196087 				break;
   2786  5088  ab196087 			case 'I':	/* "" for 32-bit, same as %i for 64 */
   2787  5088  ab196087 				cp_str = isa_I_str;
   2788  5088  ab196087 				break;
   2789  5088  ab196087 			case 'o':	/* Insert default path */
   2790  5088  ab196087 				buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH),
   2791  5088  ab196087 				    origin_root, buf);
   2792  5088  ab196087 				break;
   2793  5088  ab196087 			case 'r':
   2794  5088  ab196087 				cp_str = origin_root;
   2795  5088  ab196087 				break;
   2796  5088  ab196087 			case '%':	/* %% is reduced to just '%' */
   2797  5088  ab196087 				*buf++ = *path;
   2798  5088  ab196087 				break;
   2799  5088  ab196087 			default:	/* All other % codes are reserved */
   2800  5088  ab196087 				elfedit_msg(ELFEDIT_MSG_ERR,
   2801  5088  ab196087 				    MSG_INTL(MSG_ERR_BADPATHCODE), *path);
   2802  5088  ab196087 				/*NOTREACHED*/
   2803  5088  ab196087 				break;
   2804  5088  ab196087 			}
   2805  5088  ab196087 			if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) {
   2806  5088  ab196087 				bcopy(cp_str, buf, len);
   2807  5088  ab196087 				buf += len;
   2808  5088  ab196087 			}
   2809  5088  ab196087 		} else {	/* Non-% character passes straight through */
   2810  5088  ab196087 			*buf++ = *path;
   2811  5088  ab196087 		}
   2812  5088  ab196087 	}
   2813  5088  ab196087 
   2814  5088  ab196087 	*buf = '\0';
   2815  5088  ab196087 	return (buf);
   2816  5088  ab196087 }
   2817  5088  ab196087 
   2818  5088  ab196087 
   2819  5088  ab196087 /*
   2820  5088  ab196087  * Establish the module search path: state.modpath
   2821  5088  ab196087  *
   2822  5088  ab196087  * The path used comes from the following sources, taking the first
   2823  5088  ab196087  * one that has a value, and ignoring any others:
   2824  5088  ab196087  *
   2825  5088  ab196087  *	- ELFEDIT_PATH environment variable
   2826  5088  ab196087  *	- -L command line argument
   2827  5088  ab196087  *	- Default value
   2828  5088  ab196087  *
   2829  5088  ab196087  * entry:
   2830  5088  ab196087  *	path - NULL, or the value of the -L command line argument
   2831  5088  ab196087  *
   2832  5088  ab196087  * exit:
   2833  5088  ab196087  *	state.modpath has been filled in
   2834  5088  ab196087  */
   2835  5088  ab196087 static void
   2836  5088  ab196087 establish_modpath(const char *cmdline_path)
   2837  5088  ab196087 {
   2838  5088  ab196087 	char origin_root[PATH_MAX + 1];	/* Where elfedit binary is */
   2839  5088  ab196087 	const char	*path;		/* Initial path */
   2840  5088  ab196087 	char		*expath;	/* Expanded path */
   2841  5088  ab196087 	size_t		len;
   2842  5088  ab196087 	char		*src, *dst;
   2843  5088  ab196087 
   2844  5088  ab196087 	path = getenv(MSG_ORIG(MSG_STR_ENVVAR));
   2845  5088  ab196087 	if (path == NULL)
   2846  5088  ab196087 		path = cmdline_path;
   2847  5088  ab196087 	if (path == NULL)
   2848  5088  ab196087 		path = MSG_ORIG(MSG_STR_MODPATH);
   2849  5088  ab196087 
   2850  5088  ab196087 
   2851  5088  ab196087 	/*
   2852  5088  ab196087 	 * Root of tree containing running for running program. 32-bit elfedit
   2853  5088  ab196087 	 * is installed in /usr/bin, and 64-bit elfedit is one level lower
   2854  5088  ab196087 	 * in an ISA-specific subdirectory. So, we find the root by
   2855  5088  ab196087 	 * getting the $ORGIN of the current running program, and trimming
   2856  5088  ab196087 	 * off the last 2 (32-bit) or 3 (64-bit) directories.
   2857  5088  ab196087 	 *
   2858  5088  ab196087 	 * On a standard system, this will simply yield '/'. However,
   2859  5088  ab196087 	 * doing it this way allows us to run elfedit from a proto area,
   2860  5088  ab196087 	 * and pick up modules from the same proto area instead of those
   2861  5088  ab196087 	 * installed on the system.
   2862  5088  ab196087 	 */
   2863  5088  ab196087 	if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1)
   2864  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN));
   2865  5088  ab196087 	len = (sizeof (char *) == 8) ? 3 : 2;
   2866  5088  ab196087 	src = origin_root + strlen(origin_root);
   2867  5088  ab196087 	while ((src > origin_root) && (len > 0)) {
   2868  5088  ab196087 		if (*(src - 1) == '/')
   2869  5088  ab196087 			len--;
   2870  5088  ab196087 		src--;
   2871  5088  ab196087 	}
   2872  5088  ab196087 	*src = '\0';
   2873  5088  ab196087 
   2874  5088  ab196087 
   2875  5088  ab196087 	/*
   2876  5088  ab196087 	 * Calculate space needed to hold expanded path. Note that
   2877  5088  ab196087 	 * this assumes that MSG_STR_MODPATH will never contain a '%o'
   2878  5088  ab196087 	 * code, and so, the expansion is not recursive. The codes allowed
   2879  5088  ab196087 	 * are:
   2880  5088  ab196087 	 *	%i - ISA of running elfedit (sparc, sparcv9, etc)
   2881  5088  ab196087 	 *	%I - 64-bit ISA: Same as %i for 64-bit versions of elfedit,
   2882  5088  ab196087 	 *		but yields empty string for 32-bit ISAs.
   2883  5088  ab196087 	 *	%o - The original (default) path.
   2884  5088  ab196087 	 *	%r - Root of tree holding elfedit program.
   2885  5088  ab196087 	 *	%% - A single %
   2886  5088  ab196087 	 *
   2887  5088  ab196087 	 * A % followed by anything else is an error. This allows us to
   2888  5088  ab196087 	 * add new codes in the future without backward compatability issues.
   2889  5088  ab196087 	 */
   2890  5088  ab196087 	len = modpath_strlen(path, origin_root);
   2891  5088  ab196087 
   2892  5088  ab196087 	expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1);
   2893  5088  ab196087 	(void) modpath_expand(path, origin_root, expath);
   2894  5088  ab196087 
   2895  5088  ab196087 	/*
   2896  5088  ab196087 	 * Count path segments, eliminate extra '/', and replace ':'
   2897  5088  ab196087 	 * with NULL.
   2898  5088  ab196087 	 */
   2899  5088  ab196087 	state.modpath.n = 1;
   2900  5088  ab196087 	for (src = dst = expath; *src; src++) {
   2901  5088  ab196087 		if (*src == '/') {
   2902  5088  ab196087 			switch (*(src + 1)) {
   2903  5088  ab196087 			case '/':
   2904  5088  ab196087 			case ':':
   2905  5088  ab196087 			case '\0':
   2906  5088  ab196087 				continue;
   2907  5088  ab196087 			}
   2908  5088  ab196087 		}
   2909  5088  ab196087 		if (*src == ':') {
   2910  5088  ab196087 			state.modpath.n++;
   2911  5088  ab196087 			*dst = '\0';
   2912  5088  ab196087 		} else if (src != dst) {
   2913  5088  ab196087 			*dst = *src;
   2914  5088  ab196087 		}
   2915  5088  ab196087 		dst++;
   2916  5088  ab196087 	}
   2917  5088  ab196087 	if (src != dst)
   2918  5088  ab196087 		*dst = '\0';
   2919  5088  ab196087 
   2920  5088  ab196087 	state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR),
   2921  5088  ab196087 	    sizeof (state.modpath.seg[0]) * state.modpath.n);
   2922  5088  ab196087 
   2923  5088  ab196087 	src = expath;
   2924  5088  ab196087 	for (len = 0; len < state.modpath.n; len++) {
   2925  5088  ab196087 		if (*src == '\0') {
   2926  5088  ab196087 			state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT);
   2927  5088  ab196087 			src++;
   2928  5088  ab196087 		} else {
   2929  5088  ab196087 			state.modpath.seg[len] = src;
   2930  5088  ab196087 			src += strlen(src) + 1;
   2931  5088  ab196087 		}
   2932  5088  ab196087 	}
   2933  5088  ab196087 }
   2934  5088  ab196087 
   2935  5088  ab196087 /*
   2936  5088  ab196087  * When interactive (reading commands from a tty), we catch
   2937  5088  ab196087  * SIGINT in order to restart the outer command loop.
   2938  5088  ab196087  */
   2939  5088  ab196087 /*ARGSUSED*/
   2940  5088  ab196087 static void
   2941  5088  ab196087 sigint_handler(int sig, siginfo_t *sip, void *ucp)
   2942  5088  ab196087 {
   2943  5088  ab196087 	/* Jump to the outer loop to resume */
   2944  5088  ab196087 	if (state.msg_jbuf.active) {
   2945  5088  ab196087 		state.msg_jbuf.active = 0;
   2946  5088  ab196087 		siglongjmp(state.msg_jbuf.env, 1);
   2947  5088  ab196087 	}
   2948  5088  ab196087 }
   2949  5088  ab196087 
   2950  5088  ab196087 
   2951  5088  ab196087 static void
   2952  5088  ab196087 usage(int full)
   2953  5088  ab196087 {
   2954  5088  ab196087 	elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF));
   2955  5088  ab196087 	if (full) {
   2956  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1));
   2957  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2));
   2958  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3));
   2959  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4));
   2960  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5));
   2961  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6));
   2962  5088  ab196087 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST));
   2963  5088  ab196087 	}
   2964  5088  ab196087 	elfedit_exit(2);
   2965  5088  ab196087 }
   2966  5088  ab196087 
   2967  5088  ab196087 
   2968  5088  ab196087 /*
   2969  5088  ab196087  * In order to complete commands, we need to know about them,
   2970  5088  ab196087  * which means that we need to force all the modules to be
   2971  5088  ab196087  * loaded. This is a relatively expensive operation, so we use
   2972  5088  ab196087  * this function, which avoids doing it more than once in a session.
   2973  5088  ab196087  */
   2974  5088  ab196087 static void
   2975  5088  ab196087 elfedit_cpl_load_modules(void)
   2976  5088  ab196087 {
   2977  5088  ab196087 	static int loaded;
   2978  5088  ab196087 
   2979  5088  ab196087 	if (!loaded) {
   2980  5088  ab196087 		elfedit_load_modpath();
   2981  5088  ab196087 		loaded = 1;	/* Don't do it again */
   2982  5088  ab196087 	}
   2983  5088  ab196087 }
   2984  5088  ab196087 
   2985  5088  ab196087 /*
   2986  5088  ab196087  * Compare the token to the given string, and if they share a common
   2987  5088  ab196087  * initial sequence, add the tail of string to the tecla command completion
   2988  5088  ab196087  * buffer:
   2989  5088  ab196087  *
   2990  5088  ab196087  * entry:
   2991  5088  ab196087  *	cpldata - Current completion state
   2992  5088  ab196087  *	str - String to match against token
   2993  5088  ab196087  *	casefold - True to allow case insensitive completion, False
   2994  5088  ab196087  *		if case must match exactly.
   2995  5088  ab196087  */
   2996  5088  ab196087 void
   2997  5088  ab196087 elfedit_cpl_match(void *cpldata, const char *str, int casefold)
   2998  5088  ab196087 {
   2999  5088  ab196087 	ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
   3000  5088  ab196087 	const char	*cont_suffix;
   3001  5088  ab196087 	const char	*type_suffix;
   3002  5088  ab196087 
   3003  5088  ab196087 	/*
   3004  5088  ab196087 	 * Reasons to return immediately:
   3005  5088  ab196087 	 *	- NULL strings have no completion value
   3006  5088  ab196087 	 *	- The string is shorter than the existing item being completed
   3007  5088  ab196087 	 */
   3008  5088  ab196087 	if ((str == NULL) || (*str == '\0') ||
   3009  5088  ab196087 	    ((cstate->ecpl_token_len != 0) &&
   3010  5088  ab196087 	    ((strlen(str) < cstate->ecpl_token_len))))
   3011  5088  ab196087 		return;
   3012  5088  ab196087 
   3013  5088  ab196087 	/* If the string does not share the existing prefix, don't use it */
   3014  5088  ab196087 	if (casefold) {
   3015  5088  ab196087 		if (strncasecmp(cstate->ecpl_token_str, str,
   3016  5088  ab196087 		    cstate->ecpl_token_len) != 0)
   3017  5088  ab196087 			return;
   3018  5088  ab196087 	} else {
   3019  5088  ab196087 		if (strncmp(cstate->ecpl_token_str, str,
   3020  5088  ab196087 		    cstate->ecpl_token_len) != 0)
   3021  5088  ab196087 			return;
   3022  5088  ab196087 	}
   3023  5088  ab196087 
   3024  5088  ab196087 	if (cstate->ecpl_add_mod_colon) {
   3025  5088  ab196087 		cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON);
   3026  5088  ab196087 	} else {
   3027  5088  ab196087 		cont_suffix = MSG_ORIG(MSG_STR_SPACE);
   3028  5088  ab196087 		type_suffix = NULL;
   3029  5088  ab196087 	}
   3030  5088  ab196087 	(void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line,
   3031  5088  ab196087 	    cstate->ecpl_word_start, cstate->ecpl_word_end,
   3032  5088  ab196087 	    str + cstate->ecpl_token_len, type_suffix, cont_suffix);
   3033  5088  ab196087 
   3034  5088  ab196087 }
   3035  5088  ab196087 
   3036  5088  ab196087 
   3037  5088  ab196087 /*
   3038  6225  ab196087  * Convenience wrapper on elfedit_cpl_match(): Format an unsigned
   3039  6225  ab196087  * 32-bit integer as a string and enter the result for command completion.
   3040  6225  ab196087  */
   3041  6225  ab196087 void
   3042  6225  ab196087 elfedit_cpl_ndx(void *cpldata, uint_t ndx)
   3043  6225  ab196087 {
   3044  6225  ab196087 	Conv_inv_buf_t	buf;
   3045  6225  ab196087 
   3046  6225  ab196087 	(void) snprintf(buf.buf, sizeof (buf.buf),
   3047  6225  ab196087 	    MSG_ORIG(MSG_FMT_WORDVAL), ndx);
   3048  6225  ab196087 	elfedit_cpl_match(cpldata, buf.buf, 0);
   3049  6225  ab196087 }
   3050  6225  ab196087 
   3051  6225  ab196087 
   3052  6225  ab196087 /*
   3053  5088  ab196087  * Compare the token to the names of the commands from the given module,
   3054  5088  ab196087  * and if they share a common initial sequence, add the tail of string
   3055  5088  ab196087  * to the tecla command completion buffer:
   3056  5088  ab196087  *
   3057  5088  ab196087  * entry:
   3058  5088  ab196087  *	tok_buf - Token user has entered
   3059  5088  ab196087  *	tok_len - strlen(tok_buf)
   3060  5088  ab196087  *	mod - Module definition from which commands should be matched
   3061  5088  ab196087  *	cpl, line, word_start, word_end, cont_suffix - As documented
   3062  5088  ab196087  *		for gl_get_line() and cpl_add_completion.
   3063  5088  ab196087  */
   3064  5088  ab196087 static void
   3065  5088  ab196087 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod)
   3066  5088  ab196087 {
   3067  5088  ab196087 	elfeditGC_cmd_t *cmd;
   3068  5088  ab196087 	const char **cmd_name;
   3069  5088  ab196087 
   3070  5088  ab196087 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
   3071  5088  ab196087 		for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
   3072  5088  ab196087 			elfedit_cpl_match(cstate, *cmd_name, 1);
   3073  5088  ab196087 }
   3074  5088  ab196087 
   3075  5088  ab196087 
   3076  5088  ab196087 /*
   3077  5088  ab196087  * Compare the token to the known module names, and add those that
   3078  5088  ab196087  * match to the list of alternatives via elfedit_cpl_match().
   3079  5088  ab196087  *
   3080  5088  ab196087  * entry:
   3081  5088  ab196087  *	load_all_modules - If True, causes all modules to be loaded
   3082  5088  ab196087  *		before processing is done. If False, only the modules
   3083  5088  ab196087  *		currently seen will be used.
   3084  5088  ab196087  */
   3085  5088  ab196087 void
   3086  5088  ab196087 elfedit_cpl_module(void *cpldata, int load_all_modules)
   3087  5088  ab196087 {
   3088  5088  ab196087 	ELFEDIT_CPL_STATE	*cstate = (ELFEDIT_CPL_STATE *) cpldata;
   3089  5088  ab196087 	MODLIST_T		*modlist;
   3090  5088  ab196087 
   3091  5088  ab196087 	if (load_all_modules)
   3092  5088  ab196087 		elfedit_cpl_load_modules();
   3093  5088  ab196087 
   3094  5088  ab196087 	for (modlist = state.modlist; modlist != NULL;
   3095  5088  ab196087 	    modlist = modlist->ml_next) {
   3096  5088  ab196087 		elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1);
   3097  5088  ab196087 	}
   3098  5088  ab196087 }
   3099  5088  ab196087 
   3100  5088  ab196087 
   3101  5088  ab196087 /*
   3102  5088  ab196087  * Compare the token to all the known commands, and add those that
   3103  5088  ab196087  * match to the list of alternatives.
   3104  5088  ab196087  *
   3105  5088  ab196087  * note:
   3106  5088  ab196087  *	This routine will force modules to be loaded as necessary to
   3107  5088  ab196087  *	obtain the names it needs to match.
   3108  5088  ab196087  */
   3109  5088  ab196087 void
   3110  5088  ab196087 elfedit_cpl_command(void *cpldata)
   3111  5088  ab196087 {
   3112  5088  ab196087 	ELFEDIT_CPL_STATE	*cstate = (ELFEDIT_CPL_STATE *) cpldata;
   3113  5088  ab196087 	ELFEDIT_CPL_STATE	colon_state;
   3114  5088  ab196087 	const char		*colon_pos;
   3115  5088  ab196087 	MODLIST_T		*modlist;
   3116  5088  ab196087 	MODLIST_T		*insdef;
   3117  5088  ab196087 	char			buf[128];
   3118  5088  ab196087 
   3119  5088  ab196087 	/*
   3120  5088  ab196087 	 * Is there a colon in the command? If so, locate its offset within
   3121  5088  ab196087 	 * the raw input line.
   3122  5088  ab196087 	 */
   3123  5088  ab196087 	for (colon_pos = cstate->ecpl_token_str;
   3124  5088  ab196087 	    *colon_pos && (*colon_pos != ':'); colon_pos++)
   3125  5088  ab196087 		;
   3126  5088  ab196087 
   3127  5088  ab196087 	/*
   3128  5088  ab196087 	 * If no colon was seen, then we are completing a module name,
   3129  5088  ab196087 	 * or one of the commands from 'sys:'
   3130  5088  ab196087 	 */
   3131  5088  ab196087 	if (*colon_pos == '\0') {
   3132  5088  ab196087 		/*
   3133  5088  ab196087 		 * Setting cstate->add_mod_colon tells elfedit_cpl_match()
   3134  5088  ab196087 		 * to add an implicit ':' to the names it matches. We use it
   3135  5088  ab196087 		 * here so the user doesn't have to enter the ':' manually.
   3136  5088  ab196087 		 * Hiding this in the opaque state instead of making it
   3137  5088  ab196087 		 * an argument to that function gives us the ability to
   3138  5088  ab196087 		 * change it later without breaking the published interface.
   3139  5088  ab196087 		 */
   3140  5088  ab196087 		cstate->ecpl_add_mod_colon = 1;
   3141  5088  ab196087 		elfedit_cpl_module(cpldata, 1);
   3142  5088  ab196087 		cstate->ecpl_add_mod_colon = 0;
   3143  5088  ab196087 
   3144  5088  ab196087 		/* Add bare (no sys: prefix) commands from the sys: module */
   3145  5088  ab196087 		match_module_cmds(cstate,
   3146  5088  ab196087 		    elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0));
   3147  5088  ab196087 
   3148  5088  ab196087 		return;
   3149  5088  ab196087 	}
   3150  5088  ab196087 
   3151  5088  ab196087 	/*
   3152  5088  ab196087 	 * A colon was seen, so we have a module name. Extract the name,
   3153  5088  ab196087 	 * substituting 'sys' for the case where the given name is empty.
   3154  5088  ab196087 	 */
   3155  5088  ab196087 	if (colon_pos == 0)
   3156  5088  ab196087 		(void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf));
   3157  5088  ab196087 	else
   3158  5088  ab196087 		elfedit_strnbcpy(buf, cstate->ecpl_token_str,
   3159  5088  ab196087 		    colon_pos - cstate->ecpl_token_str, sizeof (buf));
   3160  5088  ab196087 
   3161  5088  ab196087 	/*
   3162  5088  ab196087 	 * Locate the module. If it isn't already loaded, make an explicit
   3163  5088  ab196087 	 * attempt to load it and try again. If a module definition is
   3164  5088  ab196087 	 * obtained, process the commands it supplies.
   3165  5088  ab196087 	 */
   3166  5088  ab196087 	modlist = module_loaded(buf, &insdef);
   3167  5088  ab196087 	if (modlist == NULL) {
   3168  5088  ab196087 		(void) elfedit_load_module(buf, 0, 0);
   3169  5088  ab196087 		modlist = module_loaded(buf, &insdef);
   3170  5088  ab196087 	}
   3171  5088  ab196087 	if (modlist != NULL) {
   3172  5088  ab196087 		/*
   3173  5088  ab196087 		 * Make a copy of the cstate, and adjust the line and
   3174  5088  ab196087 		 * token so that the new one starts just past the colon
   3175  5088  ab196087 		 * character. We know that the colon exists because
   3176  5088  ab196087 		 * of the preceeding test that found it. Therefore, we do
   3177  5088  ab196087 		 * not need to test against running off the end of the
   3178  5088  ab196087 		 * string here.
   3179  5088  ab196087 		 */
   3180  5088  ab196087 		colon_state = *cstate;
   3181  5088  ab196087 		while (colon_state.ecpl_line[colon_state.ecpl_word_start] !=
   3182  5088  ab196087 		    ':')
   3183  5088  ab196087 			colon_state.ecpl_word_start++;
   3184  5088  ab196087 		while (*colon_state.ecpl_token_str != ':') {
   3185  5088  ab196087 			colon_state.ecpl_token_str++;
   3186  5088  ab196087 			colon_state.ecpl_token_len--;
   3187  5088  ab196087 		}
   3188  5088  ab196087 		/* Skip past the ':' character */
   3189  5088  ab196087 		colon_state.ecpl_word_start++;
   3190  5088  ab196087 		colon_state.ecpl_token_str++;
   3191  5088  ab196087 		colon_state.ecpl_token_len--;
   3192  5088  ab196087 
   3193  5088  ab196087 		match_module_cmds(&colon_state, modlist->ml_mod);
   3194  5088  ab196087 	}
   3195  5088  ab196087 }
   3196  5088  ab196087 
   3197  5088  ab196087 
   3198  5088  ab196087 /*
   3199  5088  ab196087  * Command completion function for use with libtacla.
   3200  5088  ab196087  */
   3201  5088  ab196087 /*ARGSUSED1*/
   3202  5088  ab196087 static int
   3203  5088  ab196087 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end)
   3204  5088  ab196087 {
   3205  5088  ab196087 	const char		*argv[ELFEDIT_MAXCPLARGS];
   3206  5088  ab196087 	ELFEDIT_CPL_STATE	cstate;
   3207  5088  ab196087 	TOK_STATE		*tokst;
   3208  5088  ab196087 	int			ndx;
   3209  5088  ab196087 	int			i;
   3210  5088  ab196087 	elfeditGC_module_t	*mod;
   3211  5088  ab196087 	elfeditGC_cmd_t		*cmd;
   3212  5088  ab196087 	int			num_opt;
   3213  5088  ab196087 	int			opt_term_seen;
   3214  5088  ab196087 	int			skip_one;
   3215  5088  ab196087 	elfedit_cmd_optarg_t	*optarg;
   3216  5088  ab196087 	elfedit_optarg_item_t	item;
   3217  5088  ab196087 	int			ostyle_ndx = -1;
   3218  5088  ab196087 
   3219  5088  ab196087 	/*
   3220  5088  ab196087 	 * For debugging, enable the following block. It tells the tecla
   3221  5088  ab196087 	 * library that the program using is going to write to stdout.
   3222  5088  ab196087 	 * It will put the tty back into normal mode, and it will cause
   3223  5088