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