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