1d29b2c44Sab /*
2d29b2c44Sab  * CDDL HEADER START
3d29b2c44Sab  *
4d29b2c44Sab  * The contents of this file are subject to the terms of the
5d29b2c44Sab  * Common Development and Distribution License (the "License").
6d29b2c44Sab  * You may not use this file except in compliance with the License.
7d29b2c44Sab  *
8d29b2c44Sab  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d29b2c44Sab  * or http://www.opensolaris.org/os/licensing.
10d29b2c44Sab  * See the License for the specific language governing permissions
11d29b2c44Sab  * and limitations under the License.
12d29b2c44Sab  *
13d29b2c44Sab  * When distributing Covered Code, include this CDDL HEADER in each
14d29b2c44Sab  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d29b2c44Sab  * If applicable, add the following below this CDDL HEADER, with the
16d29b2c44Sab  * fields enclosed by brackets "[]" replaced with your own identifying
17d29b2c44Sab  * information: Portions Copyright [yyyy] [name of copyright owner]
18d29b2c44Sab  *
19d29b2c44Sab  * CDDL HEADER END
20d29b2c44Sab  */
21d29b2c44Sab 
22d29b2c44Sab /*
2369112eddSAli Bahrami  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24d29b2c44Sab  * Use is subject to license terms.
25d29b2c44Sab  */
26d29b2c44Sab 
27d29b2c44Sab #include	<sys/types.h>
28d29b2c44Sab #include	<sys/stat.h>
29d29b2c44Sab #include	<sys/wait.h>
30d29b2c44Sab #include	<stdarg.h>
31d29b2c44Sab #include	<fcntl.h>
32d29b2c44Sab #include	<stdlib.h>
33d29b2c44Sab #include	<stdio.h>
34d29b2c44Sab #include	<signal.h>
35d29b2c44Sab #include	<dirent.h>
36d29b2c44Sab #include	<libelf.h>
37d29b2c44Sab #include	<gelf.h>
38d29b2c44Sab #include	<conv.h>
39d29b2c44Sab #include	<dlfcn.h>
40d29b2c44Sab #include	<link.h>
41d29b2c44Sab #include	<stdarg.h>
42d29b2c44Sab #include	<libgen.h>
43d29b2c44Sab #include	<libintl.h>
44d29b2c44Sab #include	<locale.h>
45d29b2c44Sab #include	<unistd.h>
46d29b2c44Sab #include	<errno.h>
47d29b2c44Sab #include	<ctype.h>
48d29b2c44Sab #include	<limits.h>
49d29b2c44Sab #include	<strings.h>
50d29b2c44Sab #include	<sgs.h>
51d29b2c44Sab #include	"msg.h"
52d29b2c44Sab #include	"_elfedit.h"
53d29b2c44Sab #include	<debug.h>	/* liblddb */
54d29b2c44Sab 
55d29b2c44Sab 
56d29b2c44Sab 
57d29b2c44Sab /*
58d29b2c44Sab  * Column at which elfedit_format_command_usage() will wrap the
59d29b2c44Sab  * generated usage string if the wrap argument is True (1).
60d29b2c44Sab  */
61d29b2c44Sab #define	USAGE_WRAP_COL 55
62d29b2c44Sab 
63d29b2c44Sab 
64d29b2c44Sab 
65d29b2c44Sab 
66d29b2c44Sab /*
67d29b2c44Sab  * Type used to represent a string buffer that can grow as needed
68d29b2c44Sab  * to hold strings of arbitrary length. The user should declare
69d29b2c44Sab  * variables of this type sa static. The strbuf_ensure_size() function
70d29b2c44Sab  * is used to ensure that it has a minimum desired size.
71d29b2c44Sab  */
72d29b2c44Sab typedef struct {
73d29b2c44Sab 	char *buf;		/* String buffer */
74d29b2c44Sab 	size_t n;		/* Size of buffer */
75d29b2c44Sab } STRBUF;
76d29b2c44Sab 
77d29b2c44Sab 
78d29b2c44Sab 
79d29b2c44Sab 
80d29b2c44Sab /*
81d29b2c44Sab  * Types used by tokenize_user_cmd() to represent the result of
82d29b2c44Sab  * spliting a user command into individual tokens.
83d29b2c44Sab  */
84d29b2c44Sab typedef struct {
85d29b2c44Sab 	char	*tok_str;	/* Token string */
86d29b2c44Sab 	size_t	tok_len;	/* strlen(str) */
87d29b2c44Sab 	size_t	tok_line_off;	/* Token offset in original string */
88d29b2c44Sab } TOK_ELT;
89d29b2c44Sab typedef struct {
90d29b2c44Sab 	size_t	tokst_cmd_len;	/* Length of original user command, without */
91d29b2c44Sab 				/*	newline or NULL termination chars */
92d29b2c44Sab 	size_t	tokst_str_size;	/* Space needed to hold all the resulting */
93d29b2c44Sab 				/*	tokens, including terminating NULL */
94d29b2c44Sab 	TOK_ELT	*tokst_buf;	/* The array of tokens */
95d29b2c44Sab 	size_t	tokst_cnt;	/* # of tokens in array */
96d29b2c44Sab 	size_t	tokst_bufsize;	/* capacity of array */
97d29b2c44Sab } TOK_STATE;
98d29b2c44Sab 
99d29b2c44Sab 
100d29b2c44Sab 
101d29b2c44Sab 
102d29b2c44Sab /* State block used by gettok_init() and gettok() */
103d29b2c44Sab typedef struct {
104d29b2c44Sab 	const char	*gtok_buf;	/* Addr of buffer containing string */
105d29b2c44Sab 	char		*gtok_cur_buf;	/* Addr withing buffer for next token */
106d29b2c44Sab 	int		gtok_inc_null_final; /* True if final NULL token used */
107d29b2c44Sab 	int		gtok_null_seen;	/* True when NULL byte seen */
108d29b2c44Sab 	TOK_ELT		gtok_last_token; /* Last token parsed */
109d29b2c44Sab 
110d29b2c44Sab } GETTOK_STATE;
111d29b2c44Sab 
112d29b2c44Sab 
113d29b2c44Sab 
114d29b2c44Sab 
115d29b2c44Sab /*
116d29b2c44Sab  * The elfedit_cpl_*() functions are used for command line completion.
117d29b2c44Sab  * Currently this uses the tecla library, but to allow for changing the
118d29b2c44Sab  * library used, we hide all tecla interfaces from our modules. Instead,
119d29b2c44Sab  * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
120d29b2c44Sab  * address of that struct as an opaque handle to the modules. Since the
121d29b2c44Sab  * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
122d29b2c44Sab  * as necessary.
123d29b2c44Sab  */
124d29b2c44Sab typedef struct {
125d29b2c44Sab 	WordCompletion	*ecpl_cpl;		/* tecla handle */
126d29b2c44Sab 	const char	*ecpl_line;		/* raw input line */
127d29b2c44Sab 	int		ecpl_word_start;	/* start offset within line */
128d29b2c44Sab 	int		ecpl_word_end;		/* offset just past token */
129d29b2c44Sab 	/*
130d29b2c44Sab 	 * ecpl_add_mod_colon is a secret handshake between
131d29b2c44Sab 	 * elfedit_cpl_command() and  elfedit_cpl_add_match(). It adds
132d29b2c44Sab 	 * ':' to end of matched modules.
133d29b2c44Sab 	 */
134d29b2c44Sab 	int		ecpl_add_mod_colon;
135d29b2c44Sab 	const char	*ecpl_token_str;	/* token being completed */
136d29b2c44Sab 	size_t		ecpl_token_len;		/* strlen(ecpl_token_str) */
137d29b2c44Sab } ELFEDIT_CPL_STATE;
138d29b2c44Sab 
139d29b2c44Sab 
140d29b2c44Sab 
141d29b2c44Sab 
142d29b2c44Sab /* This structure maintains elfedit global state */
143d29b2c44Sab STATE_T state;
144d29b2c44Sab 
145d29b2c44Sab 
146d29b2c44Sab 
147d29b2c44Sab /*
148d29b2c44Sab  * Define a pair of static global variables that contain the
149d29b2c44Sab  * ISA strings that correspond to %i and %I tokens in module search
150d29b2c44Sab  * paths.
151d29b2c44Sab  *
152d29b2c44Sab  *	isa_i_str - The ISA string for the currently running program
153d29b2c44Sab  *	isa_I_str - For 64-bit programs, the same as isa_i_str. For
154d29b2c44Sab  *		32-bit programs, an empty string.
155d29b2c44Sab  */
156d29b2c44Sab #ifdef __sparc
157d29b2c44Sab #ifdef __sparcv9
158d29b2c44Sab static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64);
159d29b2c44Sab static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64);
160d29b2c44Sab #else
161d29b2c44Sab static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32);
162d29b2c44Sab static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
163d29b2c44Sab #endif
164d29b2c44Sab #endif
165d29b2c44Sab 
166d29b2c44Sab #ifdef __i386
167d29b2c44Sab static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32);
168d29b2c44Sab static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
169d29b2c44Sab #endif
170d29b2c44Sab #ifdef __amd64
171d29b2c44Sab static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64);
172d29b2c44Sab static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64);
173d29b2c44Sab #endif
174d29b2c44Sab 
175d29b2c44Sab 
176d29b2c44Sab 
177d29b2c44Sab /* Forward declarations */
178d29b2c44Sab static void free_user_cmds(void);
179d29b2c44Sab static void elfedit_pager_cleanup(void);
180d29b2c44Sab 
181d29b2c44Sab 
182d29b2c44Sab 
183d29b2c44Sab /*
184d29b2c44Sab  * We supply this function for the msg module
185d29b2c44Sab  */
186d29b2c44Sab const char *
_elfedit_msg(Msg mid)187d29b2c44Sab _elfedit_msg(Msg mid)
188d29b2c44Sab {
189d29b2c44Sab 	return (gettext(MSG_ORIG(mid)));
190d29b2c44Sab }
191d29b2c44Sab 
192d29b2c44Sab 
193d29b2c44Sab /*
194d29b2c44Sab  * Copy at most min(cpsize, dstsize-1) bytes from src into dst,
195d29b2c44Sab  * truncating src if necessary.  The  result is always null-terminated.
196d29b2c44Sab  *
197d29b2c44Sab  * entry:
198d29b2c44Sab  *	dst - Destination buffer
199d29b2c44Sab  *	src - Source string
200d29b2c44Sab  *	dstsize - sizeof(dst)
201d29b2c44Sab  *
202d29b2c44Sab  * note:
203d29b2c44Sab  *	This is similar to strncpy(), but with two modifications:
204d29b2c44Sab  *	1) You specify the number of characters to copy, not just
205d29b2c44Sab  *		the size of the destination. Hence, you can copy non-NULL
206d29b2c44Sab  *		terminated strings.
207d29b2c44Sab  *	2) The destination is guaranteed to be NULL terminated. strncpy()
208d29b2c44Sab  *		does not terminate a completely full buffer.
209d29b2c44Sab  */
210d29b2c44Sab static void
elfedit_strnbcpy(char * dst,const char * src,size_t cpsize,size_t dstsize)211d29b2c44Sab elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize)
212d29b2c44Sab {
213d29b2c44Sab 	if (cpsize >= dstsize)
214d29b2c44Sab 		cpsize = dstsize - 1;
215d29b2c44Sab 	if (cpsize > 0)
216d29b2c44Sab 		(void) strncpy(dst, src, cpsize + 1);
217d29b2c44Sab 	dst[cpsize] = '\0';
218d29b2c44Sab }
219d29b2c44Sab 
220d29b2c44Sab 
221d29b2c44Sab /*
222d29b2c44Sab  * Calls exit() on behalf of elfedit.
223d29b2c44Sab  */
224d29b2c44Sab void
elfedit_exit(int status)225d29b2c44Sab elfedit_exit(int status)
226d29b2c44Sab {
227d29b2c44Sab 	if (state.file.present) {
228d29b2c44Sab 		/* Exiting with unflushed changes pending? Issue debug notice */
229d29b2c44Sab 		if (state.file.dirty)
230d29b2c44Sab 			elfedit_msg(ELFEDIT_MSG_DEBUG,
231d29b2c44Sab 			    MSG_INTL(MSG_DEBUG_DIRTYEXIT));
232d29b2c44Sab 
233d29b2c44Sab 		/*
234d29b2c44Sab 		 * If the edit file is marked for unlink on exit, then
235d29b2c44Sab 		 * take care of it here.
236d29b2c44Sab 		 */
237d29b2c44Sab 		if (state.file.unlink_on_exit) {
238d29b2c44Sab 			elfedit_msg(ELFEDIT_MSG_DEBUG,
239d29b2c44Sab 			    MSG_INTL(MSG_DEBUG_UNLINKFILE),
240d29b2c44Sab 			    state.file.outfile);
241d29b2c44Sab 			(void) unlink(state.file.outfile);
242d29b2c44Sab 		}
243d29b2c44Sab 	}
244d29b2c44Sab 
245d29b2c44Sab 	exit(status);
246d29b2c44Sab }
247d29b2c44Sab 
248d29b2c44Sab 
249d29b2c44Sab /*
250d29b2c44Sab  * Standard message function for elfedit. All user visible
251d29b2c44Sab  * output, for error or informational reasons, should go through
252d29b2c44Sab  * this function.
253d29b2c44Sab  *
254d29b2c44Sab  * entry:
255d29b2c44Sab  *	type - Type of message. One of the ELFEDIT_MSG_* values.
256d29b2c44Sab  *	format, ... - As per the printf() family
257d29b2c44Sab  *
258d29b2c44Sab  * exit:
259d29b2c44Sab  *	The desired message has been output. For informational
260d29b2c44Sab  *	messages, control returns to the caller. For errors,
261d29b2c44Sab  *	this routine will terminate execution or strip the execution
262d29b2c44Sab  *	stack and return control directly to the outer control loop.
263d29b2c44Sab  *	In either case, the caller will not receive control.
264d29b2c44Sab  */
265d29b2c44Sab /*PRINTFLIKE2*/
266d29b2c44Sab void
elfedit_msg(elfedit_msg_t type,const char * format,...)267d29b2c44Sab elfedit_msg(elfedit_msg_t type, const char *format, ...)
268d29b2c44Sab {
269d29b2c44Sab 	typedef enum {			/* What to do after finished */
270d29b2c44Sab 		DISP_RET = 0,		/* Return to caller */
271*9320f495SToomas Soome 		DISP_JMP = 1,		/* if (interactive) longjmp else exit */
272d29b2c44Sab 		DISP_EXIT = 2		/* exit under all circumstances */
273d29b2c44Sab 	} DISP;
274d29b2c44Sab 
275d29b2c44Sab 	va_list args;
276d29b2c44Sab 	FILE *stream = stderr;
277d29b2c44Sab 	DISP disp = DISP_RET;
278d29b2c44Sab 	int do_output = 1;
279d29b2c44Sab 	int need_prefix = 1;
280d29b2c44Sab 
281d29b2c44Sab 	va_start(args, format);
282d29b2c44Sab 
283d29b2c44Sab 	switch (type) {
284d29b2c44Sab 	case ELFEDIT_MSG_ERR:
285d29b2c44Sab 	case ELFEDIT_MSG_CMDUSAGE:
286d29b2c44Sab 		disp = DISP_JMP;
287d29b2c44Sab 		break;
288d29b2c44Sab 	case ELFEDIT_MSG_FATAL:
289d29b2c44Sab 		disp = DISP_EXIT;
290d29b2c44Sab 		break;
291d29b2c44Sab 	case ELFEDIT_MSG_USAGE:
292d29b2c44Sab 		need_prefix = 0;
293d29b2c44Sab 		break;
294d29b2c44Sab 	case ELFEDIT_MSG_DEBUG:
295d29b2c44Sab 		if (!(state.flags & ELFEDIT_F_DEBUG))
296d29b2c44Sab 			return;
297d29b2c44Sab 		stream = stdout;
298d29b2c44Sab 		break;
299d29b2c44Sab 	case ELFEDIT_MSG_QUIET:
300d29b2c44Sab 		do_output = 0;
301d29b2c44Sab 		disp = DISP_JMP;
302d29b2c44Sab 		break;
303d29b2c44Sab 	}
304d29b2c44Sab 
305d29b2c44Sab 
306d29b2c44Sab 	/*
307d29b2c44Sab 	 * If there is a pager process running, we are returning to the
308d29b2c44Sab 	 * caller, and the output is going to stdout, then let the
309d29b2c44Sab 	 * pager handle it instead of writing it directly from this process.
310d29b2c44Sab 	 * That way, the output gets paged along with everything else.
311d29b2c44Sab 	 *
312d29b2c44Sab 	 * If there is a pager process running, and we are not returning
313d29b2c44Sab 	 * to the caller, then end the pager process now, before we generate
314d29b2c44Sab 	 * any new output. This allows for any text buffered in the pager
315d29b2c44Sab 	 * pipe to be output before the new stuff.
316d29b2c44Sab 	 */
317d29b2c44Sab 	if (state.pager.fptr != NULL) {
318d29b2c44Sab 		if (disp == DISP_RET) {
319d29b2c44Sab 			if (stream == stdout)
320d29b2c44Sab 				stream = state.pager.fptr;
321d29b2c44Sab 		} else {
322d29b2c44Sab 			elfedit_pager_cleanup();
323d29b2c44Sab 		}
324d29b2c44Sab 	}
325d29b2c44Sab 
326d29b2c44Sab 	/*
327d29b2c44Sab 	 * If this message is coming from within the libtecla command
328d29b2c44Sab 	 * completion code, call gl_normal_io() to give the library notice.
329d29b2c44Sab 	 * That function sets the tty back to cooked mode and advances
330d29b2c44Sab 	 * the cursor to the beginning of the next line so that our output
331d29b2c44Sab 	 * will appear properly. When we return to the command completion code,
332d29b2c44Sab 	 * tecla will re-enter raw mode and redraw the current command line.
333d29b2c44Sab 	 */
334d29b2c44Sab 	if (state.input.in_tecla)
335d29b2c44Sab 		(void) gl_normal_io(state.input.gl);
336d29b2c44Sab 
337d29b2c44Sab 	if (do_output) {
338d29b2c44Sab 		if (need_prefix)
339d29b2c44Sab 			(void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT));
340d29b2c44Sab 		(void) vfprintf(stream, format, args);
341d29b2c44Sab 		(void) fflush(stream);
342d29b2c44Sab 	}
343d29b2c44Sab 	va_end(args);
344d29b2c44Sab 
345d29b2c44Sab 	/*
346d29b2c44Sab 	 * If this is an error, then we do not return to the caller.
347d29b2c44Sab 	 * The action taken depends on whether the outer loop has registered
348d29b2c44Sab 	 * a jump buffer for us or not.
349d29b2c44Sab 	 */
350d29b2c44Sab 	if (disp != DISP_RET) {
351d29b2c44Sab 		if (state.msg_jbuf.active && (disp == DISP_JMP)) {
352d29b2c44Sab 			/* Free the user command list */
353d29b2c44Sab 			free_user_cmds();
354d29b2c44Sab 
355d29b2c44Sab 			/* Clean up to reflect effect of non-local goto */
356d29b2c44Sab 			state.input.in_tecla = FALSE;
357d29b2c44Sab 
358d29b2c44Sab 			/* Jump to the outer loop to resume */
359d29b2c44Sab 			siglongjmp(state.msg_jbuf.env, 1);
360d29b2c44Sab 		} else {
361d29b2c44Sab 			elfedit_exit(1);
362d29b2c44Sab 		}
363d29b2c44Sab 	}
364d29b2c44Sab }
365d29b2c44Sab 
366d29b2c44Sab 
367d29b2c44Sab /*
368d29b2c44Sab  * Wrapper on elfedit_msg() that issues an error that results from
369d29b2c44Sab  * a call to libelf.
370d29b2c44Sab  *
371d29b2c44Sab  * entry:
372d29b2c44Sab  *	file - Name of ELF object
373d29b2c44Sab  *	libelf_rtn_name - Name of routine that was called
374d29b2c44Sab  *
375d29b2c44Sab  * exit:
376d29b2c44Sab  *	An error has been issued that shows the routine called
377d29b2c44Sab  *	and the libelf error string for it from elf_errmsg().
378d29b2c44Sab  *	This routine does not return to the caller.
379d29b2c44Sab  */
380d29b2c44Sab void
elfedit_elferr(const char * file,const char * libelf_rtn_name)381d29b2c44Sab elfedit_elferr(const char *file, const char *libelf_rtn_name)
382d29b2c44Sab {
383d29b2c44Sab 	const char *errstr = elf_errmsg(elf_errno());
384d29b2c44Sab 
385d29b2c44Sab 	elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file,
386d29b2c44Sab 	    libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN));
387d29b2c44Sab }
388d29b2c44Sab 
389d29b2c44Sab 
390d29b2c44Sab /*
391d29b2c44Sab  * Start an output pager process for elfedit_printf()/elfedit_write() to use.
392d29b2c44Sab  *
393d29b2c44Sab  * note:
394d29b2c44Sab  *	If this elfedit session is not interactive, then no pager is
395d29b2c44Sab  *	started. Paging is only intended for interactive use. The caller
396d29b2c44Sab  *	is not supposed to worry about this point, but simply to use
397d29b2c44Sab  *	this function to flag situations in which paging might be needed.
398d29b2c44Sab  */
399d29b2c44Sab void
elfedit_pager_init(void)400d29b2c44Sab elfedit_pager_init(void)
401d29b2c44Sab {
402d29b2c44Sab 	const char	*errstr;
403d29b2c44Sab 	const char	*cmd;
404d29b2c44Sab 	int		err;
405d29b2c44Sab 
406d29b2c44Sab 	/*
407d29b2c44Sab 	 * If there is no pager process running, start one.
408d29b2c44Sab 	 * Only do this for interactive sessions --- elfedit_pager()
409d29b2c44Sab 	 * won't use a pager in batch mode.
410d29b2c44Sab 	 */
411d29b2c44Sab 	if (state.msg_jbuf.active && state.input.full_tty &&
412d29b2c44Sab 	    (state.pager.fptr == NULL)) {
413d29b2c44Sab 		/*
414d29b2c44Sab 		 * If the user has the PAGER environment variable set,
415d29b2c44Sab 		 * then we will use that program. Otherwise we default
416d29b2c44Sab 		 * to /bin/more.
417d29b2c44Sab 		 */
418d29b2c44Sab 		cmd = getenv(MSG_ORIG(MSG_STR_PAGER));
419d29b2c44Sab 		if ((cmd == NULL) || (*cmd == '\0'))
420d29b2c44Sab 			cmd = MSG_ORIG(MSG_STR_BINMORE);
421d29b2c44Sab 
422d29b2c44Sab 		/*
423d29b2c44Sab 		 * The popen() manpage says that on failure, it "may set errno",
424d29b2c44Sab 		 * which is somewhat ambiguous. We explicitly zero it here, and
425d29b2c44Sab 		 * assume that any change is due to popen() failing.
426d29b2c44Sab 		 */
427d29b2c44Sab 		errno = 0;
428d29b2c44Sab 		state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W));
429d29b2c44Sab 		if (state.pager.fptr == NULL) {
430d29b2c44Sab 			err = errno;
431d29b2c44Sab 			errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) :
432d29b2c44Sab 			    strerror(err);
433d29b2c44Sab 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC),
434d29b2c44Sab 			    MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr);
435d29b2c44Sab 		}
436d29b2c44Sab 	}
437d29b2c44Sab }
438d29b2c44Sab 
439d29b2c44Sab 
440d29b2c44Sab /*
441d29b2c44Sab  * If there is a pager process present, close it out.
442d29b2c44Sab  *
443d29b2c44Sab  * note:
444d29b2c44Sab  *	This function is called from within elfedit_msg(), and as
445d29b2c44Sab  *	such, must not use elfedit_msg() to report errors. Furthermore,
446d29b2c44Sab  *	any such errors are not a sufficient reason to terminate the process
447d29b2c44Sab  *	or to longjmp(). This is a rare case where errors are written
448d29b2c44Sab  *	directly to stderr.
449d29b2c44Sab  */
450d29b2c44Sab static void
elfedit_pager_cleanup(void)451d29b2c44Sab elfedit_pager_cleanup(void)
452d29b2c44Sab {
453d29b2c44Sab 	if (state.pager.fptr != NULL) {
454d29b2c44Sab 		if (pclose(state.pager.fptr) == -1)
455d29b2c44Sab 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI));
456d29b2c44Sab 
457d29b2c44Sab 		state.pager.fptr = NULL;
458d29b2c44Sab 	}
459d29b2c44Sab }
460d29b2c44Sab 
461d29b2c44Sab 
462d29b2c44Sab /*
463d29b2c44Sab  * Print general formtted text for the user, using printf()-style
464d29b2c44Sab  * formatting. Uses the pager process if one has been started, or
465d29b2c44Sab  * stdout otherwise.
466d29b2c44Sab  */
467d29b2c44Sab void
elfedit_printf(const char * format,...)468d29b2c44Sab elfedit_printf(const char *format, ...)
469d29b2c44Sab {
470d29b2c44Sab 	va_list	args;
471d29b2c44Sab 	int	err;
472d29b2c44Sab 	FILE	*fptr;
473d29b2c44Sab 	int	pager;
474d29b2c44Sab 	int	broken_pipe = 0;
475d29b2c44Sab 
476d29b2c44Sab 	/*
477d29b2c44Sab 	 * If there is a pager process, then use it. Otherwise write
478d29b2c44Sab 	 * directly to stdout.
479d29b2c44Sab 	 */
480d29b2c44Sab 	pager = (state.pager.fptr != NULL);
481d29b2c44Sab 	fptr = pager ? state.pager.fptr : stdout;
482d29b2c44Sab 
483d29b2c44Sab 	va_start(args, format);
484d29b2c44Sab 	errno = 0;
485d29b2c44Sab 	err = vfprintf(fptr, format, args);
486d29b2c44Sab 
487d29b2c44Sab 	/* Did we fail because a child pager process has exited? */
488d29b2c44Sab 	broken_pipe = pager && (err < 0) && (errno == EPIPE);
489d29b2c44Sab 
490d29b2c44Sab 	va_end(args);
491d29b2c44Sab 
492d29b2c44Sab 	/*
493d29b2c44Sab 	 * On error, we simply issue the error without cleaning up
494d29b2c44Sab 	 * the pager process. The message code handles that as a standard
495d29b2c44Sab 	 * part of error processing.
496d29b2c44Sab 	 *
497d29b2c44Sab 	 * We handle failure due to an exited pager process differently
498d29b2c44Sab 	 * than a normal error, because it is usually due to the user
499d29b2c44Sab 	 * intentionally telling it to.
500d29b2c44Sab 	 */
501d29b2c44Sab 	if (err < 0) {
502d29b2c44Sab 		if (broken_pipe)
503d29b2c44Sab 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
504d29b2c44Sab 		else
505d29b2c44Sab 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
506d29b2c44Sab 	}
507d29b2c44Sab }
508d29b2c44Sab 
509d29b2c44Sab 
510d29b2c44Sab /*
511d29b2c44Sab  * Some our modules use liblddb routines to format ELF output.
512d29b2c44Sab  * In order to ensure that such output is sent to the pager pipe
513d29b2c44Sab  * when there is one, and stdout otherwise, we redefine the dbg_print()
514d29b2c44Sab  * function here.
515d29b2c44Sab  *
516d29b2c44Sab  * This item should be defined NODIRECT.
517d29b2c44Sab  */
518d29b2c44Sab /* PRINTFLIKE2 */
519d29b2c44Sab void
dbg_print(Lm_list * lml,const char * format,...)520d29b2c44Sab dbg_print(Lm_list *lml, const char *format, ...)
521d29b2c44Sab {
522d29b2c44Sab 	va_list	ap;
523d29b2c44Sab 	int	err;
524d29b2c44Sab 	FILE	*fptr;
525d29b2c44Sab 	int	pager;
526d29b2c44Sab 	int	broken_pipe = 0;
527d29b2c44Sab 
528d29b2c44Sab #if	defined(lint)
529d29b2c44Sab 	/*
530d29b2c44Sab 	 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
531d29b2c44Sab 	 * Supress the lint error by making a dummy assignment.
532d29b2c44Sab 	 */
533d29b2c44Sab 	lml = 0;
534d29b2c44Sab #endif
535d29b2c44Sab 
536d29b2c44Sab 	/*
537d29b2c44Sab 	 * If there is a pager process, then use it. Otherwise write
538d29b2c44Sab 	 * directly to stdout.
539d29b2c44Sab 	 */
540d29b2c44Sab 	pager = (state.pager.fptr != NULL);
541d29b2c44Sab 	fptr = pager ? state.pager.fptr : stdout;
542d29b2c44Sab 
543d29b2c44Sab 	va_start(ap, format);
544d29b2c44Sab 	errno = 0;
545d29b2c44Sab 	err = vfprintf(fptr, format, ap);
546d29b2c44Sab 	if (err >= 0)
547d29b2c44Sab 		err = fprintf(fptr, MSG_ORIG(MSG_STR_NL));
548d29b2c44Sab 
549d29b2c44Sab 	/* Did we fail because a child pager process has exited? */
550d29b2c44Sab 	broken_pipe = (err < 0) && pager && (errno == EPIPE);
551d29b2c44Sab 
552d29b2c44Sab 	va_end(ap);
553d29b2c44Sab 
554d29b2c44Sab 	/*
555d29b2c44Sab 	 * On error, we simply issue the error without cleaning up
556d29b2c44Sab 	 * the pager process. The message code handles that as a standard
557d29b2c44Sab 	 * part of error processing.
558d29b2c44Sab 	 *
559d29b2c44Sab 	 * We handle failure due to an exited pager process differently
560d29b2c44Sab 	 * than a normal error, because it is usually due to the user
561d29b2c44Sab 	 * intentionally telling it to.
562d29b2c44Sab 	 */
563d29b2c44Sab 	if (err < 0) {
564d29b2c44Sab 		if (broken_pipe)
565d29b2c44Sab 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
566d29b2c44Sab 		else
567d29b2c44Sab 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
568d29b2c44Sab 	}
569d29b2c44Sab }
570d29b2c44Sab 
571d29b2c44Sab 
572d29b2c44Sab /*
573d29b2c44Sab  * Write raw bytes of text in a manner similar to fwrite().
574d29b2c44Sab  * Uses the pager process if one has been started, or
575d29b2c44Sab  * stdout otherwise.
576d29b2c44Sab  */
577d29b2c44Sab void
elfedit_write(const void * ptr,size_t size)578d29b2c44Sab elfedit_write(const void *ptr, size_t size)
579d29b2c44Sab {
580d29b2c44Sab 	FILE	*fptr;
581d29b2c44Sab 	int	err;
582d29b2c44Sab 
583d29b2c44Sab 	/*
584d29b2c44Sab 	 * If there is a pager process, then use it. Otherwise write
585d29b2c44Sab 	 * directly to stdout.
586d29b2c44Sab 	 */
587d29b2c44Sab 	fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr;
588d29b2c44Sab 
589d29b2c44Sab 	if (fwrite(ptr, 1, size, fptr) != size) {
590d29b2c44Sab 		err = errno;
591d29b2c44Sab 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE),
592d29b2c44Sab 		    strerror(err));
593d29b2c44Sab 	}
594d29b2c44Sab }
595d29b2c44Sab 
596d29b2c44Sab 
597cce0e03bSab /*
598cce0e03bSab  * Convert the NULL terminated string to the form used by the C
599c6c9aed4Sab  * language to represent literal strings. See conv_str_to_c_literal()
600c6c9aed4Sab  * for details.
601c6c9aed4Sab  *
602c6c9aed4Sab  * This routine differs from conv_str_to_c_literal() in two ways:
603c6c9aed4Sab  *	1) String is NULL terminated instead of counted
604c6c9aed4Sab  *	2) Signature of outfunc
605cce0e03bSab  *
606cce0e03bSab  * entry:
607cce0e03bSab  *	str - String to be processed
608cce0e03bSab  *	outfunc - Function to be called to move output characters. Note
609cce0e03bSab  *		that this function has the same signature as elfedit_write(),
610cce0e03bSab  *		and that function can be used to write the characters to
611cce0e03bSab  *		the output.
612cce0e03bSab  *
613cce0e03bSab  * exit:
614cce0e03bSab  *	The string has been processed, with the resulting data passed
615cce0e03bSab  *	to outfunc for processing.
616cce0e03bSab  */
617c6c9aed4Sab static void
elfedit_str_to_c_literal_cb(const void * ptr,size_t size,void * uvalue)618c6c9aed4Sab elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue)
619c6c9aed4Sab {
620c6c9aed4Sab 	elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue;
621c6c9aed4Sab 
622c6c9aed4Sab 	(* outfunc)(ptr, size);
623c6c9aed4Sab 
624c6c9aed4Sab }
625cce0e03bSab void
elfedit_str_to_c_literal(const char * str,elfedit_write_func_t * outfunc)626cce0e03bSab elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc)
627cce0e03bSab {
628c6c9aed4Sab 	conv_str_to_c_literal(str, strlen(str),
629c6c9aed4Sab 	    elfedit_str_to_c_literal_cb, (void *) outfunc);
630cce0e03bSab }
631cce0e03bSab 
632cce0e03bSab 
633d29b2c44Sab /*
634d29b2c44Sab  * Wrappers on malloc() and realloc() that check the result for success
635d29b2c44Sab  * and issue an error if not. The caller can use the result of these
636d29b2c44Sab  * functions without checking for a NULL pointer, as we do not return to
637d29b2c44Sab  * the caller in the failure case.
638d29b2c44Sab  */
639d29b2c44Sab void *
elfedit_malloc(const char * item_name,size_t size)640d29b2c44Sab elfedit_malloc(const char *item_name, size_t size)
641d29b2c44Sab {
642d29b2c44Sab 	void *m;
643d29b2c44Sab 
644d29b2c44Sab 	m = malloc(size);
645d29b2c44Sab 	if (m == NULL) {
646d29b2c44Sab 		int err = errno;
647d29b2c44Sab 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
648d29b2c44Sab 		    item_name, strerror(err));
649d29b2c44Sab 	}
650d29b2c44Sab 
651d29b2c44Sab 	return (m);
652d29b2c44Sab }
653d29b2c44Sab 
654d29b2c44Sab void *
elfedit_realloc(const char * item_name,void * ptr,size_t size)655d29b2c44Sab elfedit_realloc(const char *item_name, void *ptr, size_t size)
656d29b2c44Sab {
657d29b2c44Sab 	void *m;
658d29b2c44Sab 
659d29b2c44Sab 	m = realloc(ptr, size);
660d29b2c44Sab 	if (m == NULL) {
661d29b2c44Sab 		int err = errno;
662d29b2c44Sab 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
663d29b2c44Sab 		    item_name, strerror(err));
664d29b2c44Sab 	}
665d29b2c44Sab 
666d29b2c44Sab 	return (m);
667d29b2c44Sab }
668d29b2c44Sab 
669d29b2c44Sab 
670d29b2c44Sab /*
671d29b2c44Sab  * Ensure that the given buffer has room for n bytes of data.
672d29b2c44Sab  */
673d29b2c44Sab static void
strbuf_ensure_size(STRBUF * str,size_t size)674d29b2c44Sab strbuf_ensure_size(STRBUF *str, size_t size)
675d29b2c44Sab {
676d29b2c44Sab #define	INITIAL_STR_ALLOC 128
677d29b2c44Sab 
678d29b2c44Sab 	size_t n;
679d29b2c44Sab 
680d29b2c44Sab 	n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n;
681d29b2c44Sab 	while (size > n)	/* Double buffer until string fits */
682d29b2c44Sab 		n *= 2;
683d29b2c44Sab 	if (n != str->n) {		/* Alloc new string buffer if needed */
684d29b2c44Sab 		str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR),
685d29b2c44Sab 		    str->buf, n);
686d29b2c44Sab 		str->n = n;
687d29b2c44Sab 	}
688d29b2c44Sab 
689d29b2c44Sab #undef	INITIAL_STR_ALLOC
690d29b2c44Sab }
691d29b2c44Sab 
692d29b2c44Sab 
693d29b2c44Sab /*
694d29b2c44Sab  * Extract the argument/option information for the next item referenced
695d29b2c44Sab  * by optarg, and advance the pointer to the next item.
696d29b2c44Sab  *
697d29b2c44Sab  * entry:
698d29b2c44Sab  *	optarg - Address of pointer to argument or option array
699d29b2c44Sab  *	item - Struct to be filled in.
700d29b2c44Sab  *
701d29b2c44Sab  * exit:
702d29b2c44Sab  *	The item block has been filled in with the information for
703d29b2c44Sab  *	the next item in the optarg array. *optarg has been advanced
704d29b2c44Sab  *	to the next item.
705d29b2c44Sab  */
706d29b2c44Sab void
elfedit_next_optarg(elfedit_cmd_optarg_t ** optarg,elfedit_optarg_item_t * item)707d29b2c44Sab elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item)
708d29b2c44Sab {
709d29b2c44Sab 	/*
710d29b2c44Sab 	 * Array of inheritable options/arguments. Indexed by one less
711d29b2c44Sab 	 * than the corresponding ELFEDIT_STDOA_ value.
712d29b2c44Sab 	 */
713d29b2c44Sab 	static const elfedit_optarg_item_t stdoa[] = {
714d29b2c44Sab 		/* ELFEDIT_STDOA_O */
715d29b2c44Sab 		{ MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE),
716d29b2c44Sab 		    /* MSG_INTL(MSG_STDOA_OPTDESC_O) */
717d29b2c44Sab 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O,
718d29b2c44Sab 		    ELFEDIT_CMDOA_F_VALUE },
719d29b2c44Sab 
720d29b2c44Sab 		/* ELFEDIT_STDOA_AND */
721d29b2c44Sab 		{ MSG_ORIG(MSG_STR_MINUS_AND), NULL,
722d29b2c44Sab 		    /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
723d29b2c44Sab 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 },
724d29b2c44Sab 
725d29b2c44Sab 		/* ELFEDIT_STDOA_CMP */
726d29b2c44Sab 		{ MSG_ORIG(MSG_STR_MINUS_CMP), NULL,
727d29b2c44Sab 		    /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
728d29b2c44Sab 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 },
729d29b2c44Sab 
730d29b2c44Sab 		/* ELFEDIT_STDOA_OR */
731d29b2c44Sab 		{ MSG_ORIG(MSG_STR_MINUS_OR), NULL,
732d29b2c44Sab 		    /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
733d29b2c44Sab 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 },
734d29b2c44Sab 	};
735d29b2c44Sab 
736d29b2c44Sab 	elfedit_cmd_optarg_t *oa;
737d29b2c44Sab 
738d29b2c44Sab 
739d29b2c44Sab 	/* Grab first item, advance the callers pointer over it */
740d29b2c44Sab 	oa = (*optarg)++;
741d29b2c44Sab 
742d29b2c44Sab 	if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
743d29b2c44Sab 		/* Values are pre-chewed in the stdoa array above */
744d29b2c44Sab 		*item = stdoa[((uintptr_t)oa->oa_name) - 1];
745d29b2c44Sab 
746d29b2c44Sab 		/*
747d29b2c44Sab 		 * Set the inherited flag so that elfedit_optarg_helpstr()
748d29b2c44Sab 		 * can tell who is responsible for translating the help string.
749d29b2c44Sab 		 */
750d29b2c44Sab 		item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT;
751d29b2c44Sab 	} else {	/* Non-inherited item */
752d29b2c44Sab 		item->oai_name = oa->oa_name;
753d29b2c44Sab 		if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) {
754d29b2c44Sab 			item->oai_vname = oa[1].oa_name;
755d29b2c44Sab 
756d29b2c44Sab 			/* Advance users pointer past value element */
757d29b2c44Sab 			(*optarg)++;
758d29b2c44Sab 		} else {
759d29b2c44Sab 			item->oai_vname = NULL;
760d29b2c44Sab 		}
761d29b2c44Sab 		item->oai_help = oa->oa_help;
762d29b2c44Sab 		item->oai_flags = oa->oa_flags;
763d29b2c44Sab 	}
764d29b2c44Sab 
765d29b2c44Sab 	/*
766d29b2c44Sab 	 * The module determines the idmask and excmask fields whether
767d29b2c44Sab 	 * or not inheritance is in play.
768d29b2c44Sab 	 */
769d29b2c44Sab 	item->oai_idmask = oa->oa_idmask;
770d29b2c44Sab 	item->oai_excmask = oa->oa_excmask;
771d29b2c44Sab }
772d29b2c44Sab 
773d29b2c44Sab 
774d29b2c44Sab 
775d29b2c44Sab /*
776d29b2c44Sab  * Return the help string for an option/argument item, as returned
777d29b2c44Sab  * by elfedit_next_optarg(). This routine handles the details of
778d29b2c44Sab  * knowing whether the string is provided by elfedit itself (inherited),
779d29b2c44Sab  * or needs to be translated by the module.
780d29b2c44Sab  */
781d29b2c44Sab const char *
elfedit_optarg_helpstr(elfeditGC_module_t * mod,elfedit_optarg_item_t * item)782d29b2c44Sab elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item)
783d29b2c44Sab {
784d29b2c44Sab 	/*
785d29b2c44Sab 	 * The help string from an inherited item comes right out
786d29b2c44Sab 	 * of the main elfedit string table.
787d29b2c44Sab 	 */
788d29b2c44Sab 	if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT)
789d29b2c44Sab 		return (MSG_INTL((Msg) item->oai_help));
790d29b2c44Sab 
791d29b2c44Sab 	/*
792d29b2c44Sab 	 * If the string is defined by the module, then we need to
793d29b2c44Sab 	 * have the module translate it for us.
794d29b2c44Sab 	 */
795d29b2c44Sab 	return ((* mod->mod_i18nhdl_to_str)(item->oai_help));
796d29b2c44Sab }
797d29b2c44Sab 
798d29b2c44Sab 
799d29b2c44Sab 
800d29b2c44Sab /*
801d29b2c44Sab  * Used by usage_optarg() to insert a character into the output buffer,
802d29b2c44Sab  * advancing the buffer pointer and current column, and reducing the
803d29b2c44Sab  * amount of remaining space.
804d29b2c44Sab  */
805d29b2c44Sab static void
usage_optarg_insert_ch(int ch,char ** cur,size_t * n,size_t * cur_col)806d29b2c44Sab usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col)
807d29b2c44Sab {
808d29b2c44Sab 
809d29b2c44Sab 	*(*cur)++ = ch;
810d29b2c44Sab 	**cur = '\0';
811d29b2c44Sab 	(*n)--;
812d29b2c44Sab 	(*cur_col)++;
813d29b2c44Sab }
814d29b2c44Sab 
815d29b2c44Sab /*
816d29b2c44Sab  * Used by usage_optarg() to insert a string into the output
817d29b2c44Sab  * buffer, advancing the buffer pointer and current column, and reducing
818d29b2c44Sab  * the amount of remaining space.
819d29b2c44Sab  */
820d29b2c44Sab static void
usage_optarg_insert_str(char ** cur,size_t * n,size_t * cur_col,const char * format,...)821d29b2c44Sab usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col,
822d29b2c44Sab     const char *format, ...)
823d29b2c44Sab {
824d29b2c44Sab 	size_t len;
825d29b2c44Sab 	va_list args;
826d29b2c44Sab 
827d29b2c44Sab 	va_start(args, format);
828d29b2c44Sab 	len = vsnprintf(*cur, *n, format, args);
829d29b2c44Sab 	va_end(args);
830d29b2c44Sab 
831d29b2c44Sab 	*cur += len;
832d29b2c44Sab 	*n -= len;
833d29b2c44Sab 	*cur_col += len;
834d29b2c44Sab }
835d29b2c44Sab /*
836d29b2c44Sab  * Used by usage_optarg() to insert an optarg item string into the output
837d29b2c44Sab  * buffer, advancing the buffer pointer and current column, and reducing
838d29b2c44Sab  * the amount of remaining space.
839d29b2c44Sab  */
840d29b2c44Sab static void
usage_optarg_insert_item(elfedit_optarg_item_t * item,char ** cur,size_t * n,size_t * cur_col)841d29b2c44Sab usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur,
842d29b2c44Sab     size_t *n, size_t *cur_col)
843d29b2c44Sab {
844d29b2c44Sab 	size_t len;
845d29b2c44Sab 
846d29b2c44Sab 	if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) {
847d29b2c44Sab 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2),
848d29b2c44Sab 		    item->oai_name, item->oai_vname);
849d29b2c44Sab 	} else {
850d29b2c44Sab 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG),
851d29b2c44Sab 		    item->oai_name);
852d29b2c44Sab 	}
853d29b2c44Sab 	*cur += len;
854d29b2c44Sab 	*n -= len;
855d29b2c44Sab 	*cur_col += len;
856d29b2c44Sab }
857d29b2c44Sab 
858d29b2c44Sab 
859d29b2c44Sab 
860d29b2c44Sab /*
861d29b2c44Sab  * Write the options/arguments to the usage string.
862d29b2c44Sab  *
863d29b2c44Sab  * entry:
864d29b2c44Sab  *	main_buf_n - Size of main buffer from which buf and buf_n are
865d29b2c44Sab  *		allocated.
866d29b2c44Sab  *	buf - Address of pointer to where next item is to be placed.
867d29b2c44Sab  *	buf_n - Address of count of remaining bytes in buffer
868d29b2c44Sab  *	buf_cur_col - Address of current output column for current line
869d29b2c44Sab  *		of generated string.
870d29b2c44Sab  *	optarg - Options list
871d29b2c44Sab  *	isopt - True if these are options, false for arguments.
872d29b2c44Sab  *	wrap_str - String to indent wrapped lines. If NULL, lines
873d29b2c44Sab  *		are not wrapped
874d29b2c44Sab  */
875d29b2c44Sab static void
usage_optarg(size_t main_buf_n,char ** buf,size_t * buf_n,size_t * buf_cur_col,elfedit_cmd_optarg_t * optarg,int isopt,const char * wrap_str)876d29b2c44Sab usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col,
877d29b2c44Sab     elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str)
878d29b2c44Sab {
879d29b2c44Sab 	/*
880d29b2c44Sab 	 * An option can be combined into a simple format if it lacks
881d29b2c44Sab 	 * these flags and is only one character in length.
882d29b2c44Sab 	 */
883d29b2c44Sab 	static const elfedit_cmd_oa_flag_t exflags =
884d29b2c44Sab 	    (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT);
885d29b2c44Sab 
886d29b2c44Sab 	/*
887d29b2c44Sab 	 * A static buffer, which is grown as needed to accomodate
888d29b2c44Sab 	 * the maximum usage string seen.
889d29b2c44Sab 	 */
890d29b2c44Sab 	static STRBUF simple_str;
891d29b2c44Sab 
892d29b2c44Sab 	char			*cur = *buf;
893d29b2c44Sab 	size_t			n = *buf_n;
894d29b2c44Sab 	size_t			cur_col = *buf_cur_col;
895d29b2c44Sab 	int			len;
896d29b2c44Sab 	int			use_simple = 0;
897d29b2c44Sab 	elfedit_optarg_item_t	item;
898d29b2c44Sab 	elfedit_cmd_oa_mask_t	optmask = 0;
899d29b2c44Sab 	int			use_bkt;
900d29b2c44Sab 
901d29b2c44Sab 	/*
902d29b2c44Sab 	 * If processing options, pull the 1-character ones that don't have
903d29b2c44Sab 	 * an associated value and don't have any mutual exclusion issues into
904d29b2c44Sab 	 * a single combination string to go at the beginning of the usage.
905d29b2c44Sab 	 */
906d29b2c44Sab 	if (isopt) {
907d29b2c44Sab 		elfedit_cmd_optarg_t *tmp_optarg = optarg;
908d29b2c44Sab 		char *s;
909d29b2c44Sab 
910d29b2c44Sab 		/*
911d29b2c44Sab 		 * The simple string is guaranteed to fit in the same
912d29b2c44Sab 		 * amount of space reserved for the main buffer.
913d29b2c44S