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