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