15088Sab196087 /*
25088Sab196087 * CDDL HEADER START
35088Sab196087 *
45088Sab196087 * The contents of this file are subject to the terms of the
55088Sab196087 * Common Development and Distribution License (the "License").
65088Sab196087 * You may not use this file except in compliance with the License.
75088Sab196087 *
85088Sab196087 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95088Sab196087 * or http://www.opensolaris.org/os/licensing.
105088Sab196087 * See the License for the specific language governing permissions
115088Sab196087 * and limitations under the License.
125088Sab196087 *
135088Sab196087 * When distributing Covered Code, include this CDDL HEADER in each
145088Sab196087 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155088Sab196087 * If applicable, add the following below this CDDL HEADER, with the
165088Sab196087 * fields enclosed by brackets "[]" replaced with your own identifying
175088Sab196087 * information: Portions Copyright [yyyy] [name of copyright owner]
185088Sab196087 *
195088Sab196087 * CDDL HEADER END
205088Sab196087 */
215088Sab196087
225088Sab196087 /*
23*11734SAli.Bahrami@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
245088Sab196087 * Use is subject to license terms.
255088Sab196087 */
265088Sab196087
275088Sab196087 #include <sys/types.h>
285088Sab196087 #include <sys/stat.h>
295088Sab196087 #include <sys/wait.h>
305088Sab196087 #include <stdarg.h>
315088Sab196087 #include <fcntl.h>
325088Sab196087 #include <stdlib.h>
335088Sab196087 #include <stdio.h>
345088Sab196087 #include <signal.h>
355088Sab196087 #include <dirent.h>
365088Sab196087 #include <libelf.h>
375088Sab196087 #include <gelf.h>
385088Sab196087 #include <conv.h>
395088Sab196087 #include <dlfcn.h>
405088Sab196087 #include <link.h>
415088Sab196087 #include <stdarg.h>
425088Sab196087 #include <libgen.h>
435088Sab196087 #include <libintl.h>
445088Sab196087 #include <locale.h>
455088Sab196087 #include <unistd.h>
465088Sab196087 #include <errno.h>
475088Sab196087 #include <ctype.h>
485088Sab196087 #include <limits.h>
495088Sab196087 #include <strings.h>
505088Sab196087 #include <sgs.h>
515088Sab196087 #include "msg.h"
525088Sab196087 #include "_elfedit.h"
535088Sab196087 #include <debug.h> /* liblddb */
545088Sab196087
555088Sab196087
565088Sab196087
575088Sab196087 /*
585088Sab196087 * Column at which elfedit_format_command_usage() will wrap the
595088Sab196087 * generated usage string if the wrap argument is True (1).
605088Sab196087 */
615088Sab196087 #define USAGE_WRAP_COL 55
625088Sab196087
635088Sab196087
645088Sab196087
655088Sab196087
665088Sab196087 /*
675088Sab196087 * Type used to represent a string buffer that can grow as needed
685088Sab196087 * to hold strings of arbitrary length. The user should declare
695088Sab196087 * variables of this type sa static. The strbuf_ensure_size() function
705088Sab196087 * is used to ensure that it has a minimum desired size.
715088Sab196087 */
725088Sab196087 typedef struct {
735088Sab196087 char *buf; /* String buffer */
745088Sab196087 size_t n; /* Size of buffer */
755088Sab196087 } STRBUF;
765088Sab196087
775088Sab196087
785088Sab196087
795088Sab196087
805088Sab196087 /*
815088Sab196087 * Types used by tokenize_user_cmd() to represent the result of
825088Sab196087 * spliting a user command into individual tokens.
835088Sab196087 */
845088Sab196087 typedef struct {
855088Sab196087 char *tok_str; /* Token string */
865088Sab196087 size_t tok_len; /* strlen(str) */
875088Sab196087 size_t tok_line_off; /* Token offset in original string */
885088Sab196087 } TOK_ELT;
895088Sab196087 typedef struct {
905088Sab196087 size_t tokst_cmd_len; /* Length of original user command, without */
915088Sab196087 /* newline or NULL termination chars */
925088Sab196087 size_t tokst_str_size; /* Space needed to hold all the resulting */
935088Sab196087 /* tokens, including terminating NULL */
945088Sab196087 TOK_ELT *tokst_buf; /* The array of tokens */
955088Sab196087 size_t tokst_cnt; /* # of tokens in array */
965088Sab196087 size_t tokst_bufsize; /* capacity of array */
975088Sab196087 } TOK_STATE;
985088Sab196087
995088Sab196087
1005088Sab196087
1015088Sab196087
1025088Sab196087 /* State block used by gettok_init() and gettok() */
1035088Sab196087 typedef struct {
1045088Sab196087 const char *gtok_buf; /* Addr of buffer containing string */
1055088Sab196087 char *gtok_cur_buf; /* Addr withing buffer for next token */
1065088Sab196087 int gtok_inc_null_final; /* True if final NULL token used */
1075088Sab196087 int gtok_null_seen; /* True when NULL byte seen */
1085088Sab196087 TOK_ELT gtok_last_token; /* Last token parsed */
1095088Sab196087
1105088Sab196087 } GETTOK_STATE;
1115088Sab196087
1125088Sab196087
1135088Sab196087
1145088Sab196087
1155088Sab196087 /*
1165088Sab196087 * The elfedit_cpl_*() functions are used for command line completion.
1175088Sab196087 * Currently this uses the tecla library, but to allow for changing the
1185088Sab196087 * library used, we hide all tecla interfaces from our modules. Instead,
1195088Sab196087 * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
1205088Sab196087 * address of that struct as an opaque handle to the modules. Since the
1215088Sab196087 * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
1225088Sab196087 * as necessary.
1235088Sab196087 */
1245088Sab196087 typedef struct {
1255088Sab196087 WordCompletion *ecpl_cpl; /* tecla handle */
1265088Sab196087 const char *ecpl_line; /* raw input line */
1275088Sab196087 int ecpl_word_start; /* start offset within line */
1285088Sab196087 int ecpl_word_end; /* offset just past token */
1295088Sab196087 /*
1305088Sab196087 * ecpl_add_mod_colon is a secret handshake between
1315088Sab196087 * elfedit_cpl_command() and elfedit_cpl_add_match(). It adds
1325088Sab196087 * ':' to end of matched modules.
1335088Sab196087 */
1345088Sab196087 int ecpl_add_mod_colon;
1355088Sab196087 const char *ecpl_token_str; /* token being completed */
1365088Sab196087 size_t ecpl_token_len; /* strlen(ecpl_token_str) */
1375088Sab196087 } ELFEDIT_CPL_STATE;
1385088Sab196087
1395088Sab196087
1405088Sab196087
1415088Sab196087
1425088Sab196087 /* This structure maintains elfedit global state */
1435088Sab196087 STATE_T state;
1445088Sab196087
1455088Sab196087
1465088Sab196087
1475088Sab196087 /*
1485088Sab196087 * Define a pair of static global variables that contain the
1495088Sab196087 * ISA strings that correspond to %i and %I tokens in module search
1505088Sab196087 * paths.
1515088Sab196087 *
1525088Sab196087 * isa_i_str - The ISA string for the currently running program
1535088Sab196087 * isa_I_str - For 64-bit programs, the same as isa_i_str. For
1545088Sab196087 * 32-bit programs, an empty string.
1555088Sab196087 */
1565088Sab196087 #ifdef __sparc
1575088Sab196087 #ifdef __sparcv9
1585088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64);
1595088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64);
1605088Sab196087 #else
1615088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32);
1625088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
1635088Sab196087 #endif
1645088Sab196087 #endif
1655088Sab196087
1665088Sab196087 #ifdef __i386
1675088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32);
1685088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
1695088Sab196087 #endif
1705088Sab196087 #ifdef __amd64
1715088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64);
1725088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64);
1735088Sab196087 #endif
1745088Sab196087
1755088Sab196087
1765088Sab196087
1775088Sab196087 /* Forward declarations */
1785088Sab196087 static void free_user_cmds(void);
1795088Sab196087 static void elfedit_pager_cleanup(void);
1805088Sab196087
1815088Sab196087
1825088Sab196087
1835088Sab196087 /*
1845088Sab196087 * We supply this function for the msg module
1855088Sab196087 */
1865088Sab196087 const char *
_elfedit_msg(Msg mid)1875088Sab196087 _elfedit_msg(Msg mid)
1885088Sab196087 {
1895088Sab196087 return (gettext(MSG_ORIG(mid)));
1905088Sab196087 }
1915088Sab196087
1925088Sab196087
1935088Sab196087 /*
1945088Sab196087 * Copy at most min(cpsize, dstsize-1) bytes from src into dst,
1955088Sab196087 * truncating src if necessary. The result is always null-terminated.
1965088Sab196087 *
1975088Sab196087 * entry:
1985088Sab196087 * dst - Destination buffer
1995088Sab196087 * src - Source string
2005088Sab196087 * dstsize - sizeof(dst)
2015088Sab196087 *
2025088Sab196087 * note:
2035088Sab196087 * This is similar to strncpy(), but with two modifications:
2045088Sab196087 * 1) You specify the number of characters to copy, not just
2055088Sab196087 * the size of the destination. Hence, you can copy non-NULL
2065088Sab196087 * terminated strings.
2075088Sab196087 * 2) The destination is guaranteed to be NULL terminated. strncpy()
2085088Sab196087 * does not terminate a completely full buffer.
2095088Sab196087 */
2105088Sab196087 static void
elfedit_strnbcpy(char * dst,const char * src,size_t cpsize,size_t dstsize)2115088Sab196087 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize)
2125088Sab196087 {
2135088Sab196087 if (cpsize >= dstsize)
2145088Sab196087 cpsize = dstsize - 1;
2155088Sab196087 if (cpsize > 0)
2165088Sab196087 (void) strncpy(dst, src, cpsize + 1);
2175088Sab196087 dst[cpsize] = '\0';
2185088Sab196087 }
2195088Sab196087
2205088Sab196087
2215088Sab196087 /*
2225088Sab196087 * Calls exit() on behalf of elfedit.
2235088Sab196087 */
2245088Sab196087 void
elfedit_exit(int status)2255088Sab196087 elfedit_exit(int status)
2265088Sab196087 {
2275088Sab196087 if (state.file.present) {
2285088Sab196087 /* Exiting with unflushed changes pending? Issue debug notice */
2295088Sab196087 if (state.file.dirty)
2305088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
2315088Sab196087 MSG_INTL(MSG_DEBUG_DIRTYEXIT));
2325088Sab196087
2335088Sab196087 /*
2345088Sab196087 * If the edit file is marked for unlink on exit, then
2355088Sab196087 * take care of it here.
2365088Sab196087 */
2375088Sab196087 if (state.file.unlink_on_exit) {
2385088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
2395088Sab196087 MSG_INTL(MSG_DEBUG_UNLINKFILE),
2405088Sab196087 state.file.outfile);
2415088Sab196087 (void) unlink(state.file.outfile);
2425088Sab196087 }
2435088Sab196087 }
2445088Sab196087
2455088Sab196087 exit(status);
2465088Sab196087 }
2475088Sab196087
2485088Sab196087
2495088Sab196087 /*
2505088Sab196087 * Standard message function for elfedit. All user visible
2515088Sab196087 * output, for error or informational reasons, should go through
2525088Sab196087 * this function.
2535088Sab196087 *
2545088Sab196087 * entry:
2555088Sab196087 * type - Type of message. One of the ELFEDIT_MSG_* values.
2565088Sab196087 * format, ... - As per the printf() family
2575088Sab196087 *
2585088Sab196087 * exit:
2595088Sab196087 * The desired message has been output. For informational
2605088Sab196087 * messages, control returns to the caller. For errors,
2615088Sab196087 * this routine will terminate execution or strip the execution
2625088Sab196087 * stack and return control directly to the outer control loop.
2635088Sab196087 * In either case, the caller will not receive control.
2645088Sab196087 */
2655088Sab196087 /*PRINTFLIKE2*/
2665088Sab196087 void
elfedit_msg(elfedit_msg_t type,const char * format,...)2675088Sab196087 elfedit_msg(elfedit_msg_t type, const char *format, ...)
2685088Sab196087 {
2695088Sab196087 typedef enum { /* What to do after finished */
2705088Sab196087 DISP_RET = 0, /* Return to caller */
2715088Sab196087 DISP_JMP = 1, /* if (interactive) longjmp else exit */
2725088Sab196087 DISP_EXIT = 2 /* exit under all circumstances */
2735088Sab196087 } DISP;
2745088Sab196087
2755088Sab196087 va_list args;
2765088Sab196087 FILE *stream = stderr;
2775088Sab196087 DISP disp = DISP_RET;
2785088Sab196087 int do_output = 1;
2795088Sab196087 int need_prefix = 1;
2805088Sab196087
2815088Sab196087 va_start(args, format);
2825088Sab196087
2835088Sab196087 switch (type) {
2845088Sab196087 case ELFEDIT_MSG_ERR:
2855088Sab196087 case ELFEDIT_MSG_CMDUSAGE:
2865088Sab196087 disp = DISP_JMP;
2875088Sab196087 break;
2885088Sab196087 case ELFEDIT_MSG_FATAL:
2895088Sab196087 disp = DISP_EXIT;
2905088Sab196087 break;
2915088Sab196087 case ELFEDIT_MSG_USAGE:
2925088Sab196087 need_prefix = 0;
2935088Sab196087 break;
2945088Sab196087 case ELFEDIT_MSG_DEBUG:
2955088Sab196087 if (!(state.flags & ELFEDIT_F_DEBUG))
2965088Sab196087 return;
2975088Sab196087 stream = stdout;
2985088Sab196087 break;
2995088Sab196087 case ELFEDIT_MSG_QUIET:
3005088Sab196087 do_output = 0;
3015088Sab196087 disp = DISP_JMP;
3025088Sab196087 break;
3035088Sab196087 }
3045088Sab196087
3055088Sab196087
3065088Sab196087 /*
3075088Sab196087 * If there is a pager process running, we are returning to the
3085088Sab196087 * caller, and the output is going to stdout, then let the
3095088Sab196087 * pager handle it instead of writing it directly from this process.
3105088Sab196087 * That way, the output gets paged along with everything else.
3115088Sab196087 *
3125088Sab196087 * If there is a pager process running, and we are not returning
3135088Sab196087 * to the caller, then end the pager process now, before we generate
3145088Sab196087 * any new output. This allows for any text buffered in the pager
3155088Sab196087 * pipe to be output before the new stuff.
3165088Sab196087 */
3175088Sab196087 if (state.pager.fptr != NULL) {
3185088Sab196087 if (disp == DISP_RET) {
3195088Sab196087 if (stream == stdout)
3205088Sab196087 stream = state.pager.fptr;
3215088Sab196087 } else {
3225088Sab196087 elfedit_pager_cleanup();
3235088Sab196087 }
3245088Sab196087 }
3255088Sab196087
3265088Sab196087 /*
3275088Sab196087 * If this message is coming from within the libtecla command
3285088Sab196087 * completion code, call gl_normal_io() to give the library notice.
3295088Sab196087 * That function sets the tty back to cooked mode and advances
3305088Sab196087 * the cursor to the beginning of the next line so that our output
3315088Sab196087 * will appear properly. When we return to the command completion code,
3325088Sab196087 * tecla will re-enter raw mode and redraw the current command line.
3335088Sab196087 */
3345088Sab196087 if (state.input.in_tecla)
3355088Sab196087 (void) gl_normal_io(state.input.gl);
3365088Sab196087
3375088Sab196087 if (do_output) {
3385088Sab196087 if (need_prefix)
3395088Sab196087 (void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT));
3405088Sab196087 (void) vfprintf(stream, format, args);
3415088Sab196087 (void) fflush(stream);
3425088Sab196087 }
3435088Sab196087 va_end(args);
3445088Sab196087
3455088Sab196087 /*
3465088Sab196087 * If this is an error, then we do not return to the caller.
3475088Sab196087 * The action taken depends on whether the outer loop has registered
3485088Sab196087 * a jump buffer for us or not.
3495088Sab196087 */
3505088Sab196087 if (disp != DISP_RET) {
3515088Sab196087 if (state.msg_jbuf.active && (disp == DISP_JMP)) {
3525088Sab196087 /* Free the user command list */
3535088Sab196087 free_user_cmds();
3545088Sab196087
3555088Sab196087 /* Clean up to reflect effect of non-local goto */
3565088Sab196087 state.input.in_tecla = FALSE;
3575088Sab196087
3585088Sab196087 /* Jump to the outer loop to resume */
3595088Sab196087 siglongjmp(state.msg_jbuf.env, 1);
3605088Sab196087 } else {
3615088Sab196087 elfedit_exit(1);
3625088Sab196087 }
3635088Sab196087 }
3645088Sab196087 }
3655088Sab196087
3665088Sab196087
3675088Sab196087 /*
3685088Sab196087 * Wrapper on elfedit_msg() that issues an error that results from
3695088Sab196087 * a call to libelf.
3705088Sab196087 *
3715088Sab196087 * entry:
3725088Sab196087 * file - Name of ELF object
3735088Sab196087 * libelf_rtn_name - Name of routine that was called
3745088Sab196087 *
3755088Sab196087 * exit:
3765088Sab196087 * An error has been issued that shows the routine called
3775088Sab196087 * and the libelf error string for it from elf_errmsg().
3785088Sab196087 * This routine does not return to the caller.
3795088Sab196087 */
3805088Sab196087 void
elfedit_elferr(const char * file,const char * libelf_rtn_name)3815088Sab196087 elfedit_elferr(const char *file, const char *libelf_rtn_name)
3825088Sab196087 {
3835088Sab196087 const char *errstr = elf_errmsg(elf_errno());
3845088Sab196087
3855088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file,
3865088Sab196087 libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN));
3875088Sab196087 }
3885088Sab196087
3895088Sab196087
3905088Sab196087 /*
3915088Sab196087 * Start an output pager process for elfedit_printf()/elfedit_write() to use.
3925088Sab196087 *
3935088Sab196087 * note:
3945088Sab196087 * If this elfedit session is not interactive, then no pager is
3955088Sab196087 * started. Paging is only intended for interactive use. The caller
3965088Sab196087 * is not supposed to worry about this point, but simply to use
3975088Sab196087 * this function to flag situations in which paging might be needed.
3985088Sab196087 */
3995088Sab196087 void
elfedit_pager_init(void)4005088Sab196087 elfedit_pager_init(void)
4015088Sab196087 {
4025088Sab196087 const char *errstr;
4035088Sab196087 const char *cmd;
4045088Sab196087 int err;
4055088Sab196087
4065088Sab196087 /*
4075088Sab196087 * If there is no pager process running, start one.
4085088Sab196087 * Only do this for interactive sessions --- elfedit_pager()
4095088Sab196087 * won't use a pager in batch mode.
4105088Sab196087 */
4115088Sab196087 if (state.msg_jbuf.active && state.input.full_tty &&
4125088Sab196087 (state.pager.fptr == NULL)) {
4135088Sab196087 /*
4145088Sab196087 * If the user has the PAGER environment variable set,
4155088Sab196087 * then we will use that program. Otherwise we default
4165088Sab196087 * to /bin/more.
4175088Sab196087 */
4185088Sab196087 cmd = getenv(MSG_ORIG(MSG_STR_PAGER));
4195088Sab196087 if ((cmd == NULL) || (*cmd == '\0'))
4205088Sab196087 cmd = MSG_ORIG(MSG_STR_BINMORE);
4215088Sab196087
4225088Sab196087 /*
4235088Sab196087 * The popen() manpage says that on failure, it "may set errno",
4245088Sab196087 * which is somewhat ambiguous. We explicitly zero it here, and
4255088Sab196087 * assume that any change is due to popen() failing.
4265088Sab196087 */
4275088Sab196087 errno = 0;
4285088Sab196087 state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W));
4295088Sab196087 if (state.pager.fptr == NULL) {
4305088Sab196087 err = errno;
4315088Sab196087 errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) :
4325088Sab196087 strerror(err);
4335088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC),
4345088Sab196087 MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr);
4355088Sab196087 }
4365088Sab196087 }
4375088Sab196087 }
4385088Sab196087
4395088Sab196087
4405088Sab196087 /*
4415088Sab196087 * If there is a pager process present, close it out.
4425088Sab196087 *
4435088Sab196087 * note:
4445088Sab196087 * This function is called from within elfedit_msg(), and as
4455088Sab196087 * such, must not use elfedit_msg() to report errors. Furthermore,
4465088Sab196087 * any such errors are not a sufficient reason to terminate the process
4475088Sab196087 * or to longjmp(). This is a rare case where errors are written
4485088Sab196087 * directly to stderr.
4495088Sab196087 */
4505088Sab196087 static void
elfedit_pager_cleanup(void)4515088Sab196087 elfedit_pager_cleanup(void)
4525088Sab196087 {
4535088Sab196087 if (state.pager.fptr != NULL) {
4545088Sab196087 if (pclose(state.pager.fptr) == -1)
4555088Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI));
4565088Sab196087
4575088Sab196087 state.pager.fptr = NULL;
4585088Sab196087 }
4595088Sab196087 }
4605088Sab196087
4615088Sab196087
4625088Sab196087 /*
4635088Sab196087 * Print general formtted text for the user, using printf()-style
4645088Sab196087 * formatting. Uses the pager process if one has been started, or
4655088Sab196087 * stdout otherwise.
4665088Sab196087 */
4675088Sab196087 void
elfedit_printf(const char * format,...)4685088Sab196087 elfedit_printf(const char *format, ...)
4695088Sab196087 {
4705088Sab196087 va_list args;
4715088Sab196087 int err;
4725088Sab196087 FILE *fptr;
4735088Sab196087 int pager;
4745088Sab196087 int broken_pipe = 0;
4755088Sab196087
4765088Sab196087 /*
4775088Sab196087 * If there is a pager process, then use it. Otherwise write
4785088Sab196087 * directly to stdout.
4795088Sab196087 */
4805088Sab196087 pager = (state.pager.fptr != NULL);
4815088Sab196087 fptr = pager ? state.pager.fptr : stdout;
4825088Sab196087
4835088Sab196087 va_start(args, format);
4845088Sab196087 errno = 0;
4855088Sab196087 err = vfprintf(fptr, format, args);
4865088Sab196087
4875088Sab196087 /* Did we fail because a child pager process has exited? */
4885088Sab196087 broken_pipe = pager && (err < 0) && (errno == EPIPE);
4895088Sab196087
4905088Sab196087 va_end(args);
4915088Sab196087
4925088Sab196087 /*
4935088Sab196087 * On error, we simply issue the error without cleaning up
4945088Sab196087 * the pager process. The message code handles that as a standard
4955088Sab196087 * part of error processing.
4965088Sab196087 *
4975088Sab196087 * We handle failure due to an exited pager process differently
4985088Sab196087 * than a normal error, because it is usually due to the user
4995088Sab196087 * intentionally telling it to.
5005088Sab196087 */
5015088Sab196087 if (err < 0) {
5025088Sab196087 if (broken_pipe)
5035088Sab196087 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
5045088Sab196087 else
5055088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
5065088Sab196087 }
5075088Sab196087 }
5085088Sab196087
5095088Sab196087
5105088Sab196087 /*
5115088Sab196087 * Some our modules use liblddb routines to format ELF output.
5125088Sab196087 * In order to ensure that such output is sent to the pager pipe
5135088Sab196087 * when there is one, and stdout otherwise, we redefine the dbg_print()
5145088Sab196087 * function here.
5155088Sab196087 *
5165088Sab196087 * This item should be defined NODIRECT.
5175088Sab196087 */
5185088Sab196087 /* PRINTFLIKE2 */
5195088Sab196087 void
dbg_print(Lm_list * lml,const char * format,...)5205088Sab196087 dbg_print(Lm_list *lml, const char *format, ...)
5215088Sab196087 {
5225088Sab196087 va_list ap;
5235088Sab196087 int err;
5245088Sab196087 FILE *fptr;
5255088Sab196087 int pager;
5265088Sab196087 int broken_pipe = 0;
5275088Sab196087
5285088Sab196087 #if defined(lint)
5295088Sab196087 /*
5305088Sab196087 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
5315088Sab196087 * Supress the lint error by making a dummy assignment.
5325088Sab196087 */
5335088Sab196087 lml = 0;
5345088Sab196087 #endif
5355088Sab196087
5365088Sab196087 /*
5375088Sab196087 * If there is a pager process, then use it. Otherwise write
5385088Sab196087 * directly to stdout.
5395088Sab196087 */
5405088Sab196087 pager = (state.pager.fptr != NULL);
5415088Sab196087 fptr = pager ? state.pager.fptr : stdout;
5425088Sab196087
5435088Sab196087 va_start(ap, format);
5445088Sab196087 errno = 0;
5455088Sab196087 err = vfprintf(fptr, format, ap);
5465088Sab196087 if (err >= 0)
5475088Sab196087 err = fprintf(fptr, MSG_ORIG(MSG_STR_NL));
5485088Sab196087
5495088Sab196087 /* Did we fail because a child pager process has exited? */
5505088Sab196087 broken_pipe = (err < 0) && pager && (errno == EPIPE);
5515088Sab196087
5525088Sab196087 va_end(ap);
5535088Sab196087
5545088Sab196087 /*
5555088Sab196087 * On error, we simply issue the error without cleaning up
5565088Sab196087 * the pager process. The message code handles that as a standard
5575088Sab196087 * part of error processing.
5585088Sab196087 *
5595088Sab196087 * We handle failure due to an exited pager process differently
5605088Sab196087 * than a normal error, because it is usually due to the user
5615088Sab196087 * intentionally telling it to.
5625088Sab196087 */
5635088Sab196087 if (err < 0) {
5645088Sab196087 if (broken_pipe)
5655088Sab196087 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
5665088Sab196087 else
5675088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
5685088Sab196087 }
5695088Sab196087 }
5705088Sab196087
5715088Sab196087
5725088Sab196087 /*
5735088Sab196087 * Write raw bytes of text in a manner similar to fwrite().
5745088Sab196087 * Uses the pager process if one has been started, or
5755088Sab196087 * stdout otherwise.
5765088Sab196087 */
5775088Sab196087 void
elfedit_write(const void * ptr,size_t size)5785088Sab196087 elfedit_write(const void *ptr, size_t size)
5795088Sab196087 {
5805088Sab196087 FILE *fptr;
5815088Sab196087 int err;
5825088Sab196087
5835088Sab196087 /*
5845088Sab196087 * If there is a pager process, then use it. Otherwise write
5855088Sab196087 * directly to stdout.
5865088Sab196087 */
5875088Sab196087 fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr;
5885088Sab196087
5895088Sab196087 if (fwrite(ptr, 1, size, fptr) != size) {
5905088Sab196087 err = errno;
5915088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE),
5925088Sab196087 strerror(err));
5935088Sab196087 }
5945088Sab196087 }
5955088Sab196087
5965088Sab196087
5975088Sab196087 /*
5985892Sab196087 * Convert the NULL terminated string to the form used by the C
5996635Sab196087 * language to represent literal strings. See conv_str_to_c_literal()
6006635Sab196087 * for details.
6016635Sab196087 *
6026635Sab196087 * This routine differs from conv_str_to_c_literal() in two ways:
6036635Sab196087 * 1) String is NULL terminated instead of counted
6046635Sab196087 * 2) Signature of outfunc
6055892Sab196087 *
6065892Sab196087 * entry:
6075892Sab196087 * str - String to be processed
6085892Sab196087 * outfunc - Function to be called to move output characters. Note
6095892Sab196087 * that this function has the same signature as elfedit_write(),
6105892Sab196087 * and that function can be used to write the characters to
6115892Sab196087 * the output.
6125892Sab196087 *
6135892Sab196087 * exit:
6145892Sab196087 * The string has been processed, with the resulting data passed
6155892Sab196087 * to outfunc for processing.
6165892Sab196087 */
6176635Sab196087 static void
elfedit_str_to_c_literal_cb(const void * ptr,size_t size,void * uvalue)6186635Sab196087 elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue)
6196635Sab196087 {
6206635Sab196087 elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue;
6216635Sab196087
6226635Sab196087 (* outfunc)(ptr, size);
6236635Sab196087
6246635Sab196087 }
6255892Sab196087 void
elfedit_str_to_c_literal(const char * str,elfedit_write_func_t * outfunc)6265892Sab196087 elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc)
6275892Sab196087 {
6286635Sab196087 conv_str_to_c_literal(str, strlen(str),
6296635Sab196087 elfedit_str_to_c_literal_cb, (void *) outfunc);
6305892Sab196087 }
6315892Sab196087
6325892Sab196087
6335892Sab196087 /*
6345088Sab196087 * Wrappers on malloc() and realloc() that check the result for success
6355088Sab196087 * and issue an error if not. The caller can use the result of these
6365088Sab196087 * functions without checking for a NULL pointer, as we do not return to
6375088Sab196087 * the caller in the failure case.
6385088Sab196087 */
6395088Sab196087 void *
elfedit_malloc(const char * item_name,size_t size)6405088Sab196087 elfedit_malloc(const char *item_name, size_t size)
6415088Sab196087 {
6425088Sab196087 void *m;
6435088Sab196087
6445088Sab196087 m = malloc(size);
6455088Sab196087 if (m == NULL) {
6465088Sab196087 int err = errno;
6475088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
6485088Sab196087 item_name, strerror(err));
6495088Sab196087 }
6505088Sab196087
6515088Sab196087 return (m);
6525088Sab196087 }
6535088Sab196087
6545088Sab196087 void *
elfedit_realloc(const char * item_name,void * ptr,size_t size)6555088Sab196087 elfedit_realloc(const char *item_name, void *ptr, size_t size)
6565088Sab196087 {
6575088Sab196087 void *m;
6585088Sab196087
6595088Sab196087 m = realloc(ptr, size);
6605088Sab196087 if (m == NULL) {
6615088Sab196087 int err = errno;
6625088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
6635088Sab196087 item_name, strerror(err));
6645088Sab196087 }
6655088Sab196087
6665088Sab196087 return (m);
6675088Sab196087 }
6685088Sab196087
6695088Sab196087
6705088Sab196087 /*
6715088Sab196087 * Ensure that the given buffer has room for n bytes of data.
6725088Sab196087 */
6735088Sab196087 static void
strbuf_ensure_size(STRBUF * str,size_t size)6745088Sab196087 strbuf_ensure_size(STRBUF *str, size_t size)
6755088Sab196087 {
6765088Sab196087 #define INITIAL_STR_ALLOC 128
6775088Sab196087
6785088Sab196087 size_t n;
6795088Sab196087
6805088Sab196087 n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n;
6815088Sab196087 while (size > n) /* Double buffer until string fits */
6825088Sab196087 n *= 2;
6835088Sab196087 if (n != str->n) { /* Alloc new string buffer if needed */
6845088Sab196087 str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR),
6855088Sab196087 str->buf, n);
6865088Sab196087 str->n = n;
6875088Sab196087 }
6885088Sab196087
6895088Sab196087 #undef INITIAL_STR_ALLOC
6905088Sab196087 }
6915088Sab196087
6925088Sab196087
6935088Sab196087 /*
6945088Sab196087 * Extract the argument/option information for the next item referenced
6955088Sab196087 * by optarg, and advance the pointer to the next item.
6965088Sab196087 *
6975088Sab196087 * entry:
6985088Sab196087 * optarg - Address of pointer to argument or option array
6995088Sab196087 * item - Struct to be filled in.
7005088Sab196087 *
7015088Sab196087 * exit:
7025088Sab196087 * The item block has been filled in with the information for
7035088Sab196087 * the next item in the optarg array. *optarg has been advanced
7045088Sab196087 * to the next item.
7055088Sab196087 */
7065088Sab196087 void
elfedit_next_optarg(elfedit_cmd_optarg_t ** optarg,elfedit_optarg_item_t * item)7075088Sab196087 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item)
7085088Sab196087 {
7095088Sab196087 /*
7105088Sab196087 * Array of inheritable options/arguments. Indexed by one less
7115088Sab196087 * than the corresponding ELFEDIT_STDOA_ value.
7125088Sab196087 */
7135088Sab196087 static const elfedit_optarg_item_t stdoa[] = {
7145088Sab196087 /* ELFEDIT_STDOA_O */
7155088Sab196087 { MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE),
7165088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_O) */
7175088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O,
7185088Sab196087 ELFEDIT_CMDOA_F_VALUE },
7195088Sab196087
7205088Sab196087 /* ELFEDIT_STDOA_AND */
7215088Sab196087 { MSG_ORIG(MSG_STR_MINUS_AND), NULL,
7225088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
7235088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 },
7245088Sab196087
7255088Sab196087 /* ELFEDIT_STDOA_CMP */
7265088Sab196087 { MSG_ORIG(MSG_STR_MINUS_CMP), NULL,
7275088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
7285088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 },
7295088Sab196087
7305088Sab196087 /* ELFEDIT_STDOA_OR */
7315088Sab196087 { MSG_ORIG(MSG_STR_MINUS_OR), NULL,
7325088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
7335088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 },
7345088Sab196087 };
7355088Sab196087
7365088Sab196087 elfedit_cmd_optarg_t *oa;
7375088Sab196087
7385088Sab196087
7395088Sab196087 /* Grab first item, advance the callers pointer over it */
7405088Sab196087 oa = (*optarg)++;
7415088Sab196087
7425088Sab196087 if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
7435088Sab196087 /* Values are pre-chewed in the stdoa array above */
7445088Sab196087 *item = stdoa[((uintptr_t)oa->oa_name) - 1];
7455088Sab196087
7465088Sab196087 /*
7475088Sab196087 * Set the inherited flag so that elfedit_optarg_helpstr()
7485088Sab196087 * can tell who is responsible for translating the help string.
7495088Sab196087 */
7505088Sab196087 item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT;
7515088Sab196087 } else { /* Non-inherited item */
7525088Sab196087 item->oai_name = oa->oa_name;
7535088Sab196087 if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) {
7545088Sab196087 item->oai_vname = oa[1].oa_name;
7555088Sab196087
7565088Sab196087 /* Advance users pointer past value element */
7575088Sab196087 (*optarg)++;
7585088Sab196087 } else {
7595088Sab196087 item->oai_vname = NULL;
7605088Sab196087 }
7615088Sab196087 item->oai_help = oa->oa_help;
7625088Sab196087 item->oai_flags = oa->oa_flags;
7635088Sab196087 }
7645088Sab196087
7655088Sab196087 /*
7665088Sab196087 * The module determines the idmask and excmask fields whether
7675088Sab196087 * or not inheritance is in play.
7685088Sab196087 */
7695088Sab196087 item->oai_idmask = oa->oa_idmask;
7705088Sab196087 item->oai_excmask = oa->oa_excmask;
7715088Sab196087 }
7725088Sab196087
7735088Sab196087
7745088Sab196087
7755088Sab196087 /*
7765088Sab196087 * Return the help string for an option/argument item, as returned
7775088Sab196087 * by elfedit_next_optarg(). This routine handles the details of
7785088Sab196087 * knowing whether the string is provided by elfedit itself (inherited),
7795088Sab196087 * or needs to be translated by the module.
7805088Sab196087 */
7815088Sab196087 const char *
elfedit_optarg_helpstr(elfeditGC_module_t * mod,elfedit_optarg_item_t * item)7825088Sab196087 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item)
7835088Sab196087 {
7845088Sab196087 /*
7855088Sab196087 * The help string from an inherited item comes right out
7865088Sab196087 * of the main elfedit string table.
7875088Sab196087 */
7885088Sab196087 if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT)
7895088Sab196087 return (MSG_INTL((Msg) item->oai_help));
7905088Sab196087
7915088Sab196087 /*
7925088Sab196087 * If the string is defined by the module, then we need to
7935088Sab196087 * have the module translate it for us.
7945088Sab196087 */
7955088Sab196087 return ((* mod->mod_i18nhdl_to_str)(item->oai_help));
7965088Sab196087 }
7975088Sab196087
7985088Sab196087
7995088Sab196087
8005088Sab196087 /*
8015088Sab196087 * Used by usage_optarg() to insert a character into the output buffer,
8025088Sab196087 * advancing the buffer pointer and current column, and reducing the
8035088Sab196087 * amount of remaining space.
8045088Sab196087 */
8055088Sab196087 static void
usage_optarg_insert_ch(int ch,char ** cur,size_t * n,size_t * cur_col)8065088Sab196087 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col)
8075088Sab196087 {
8085088Sab196087
8095088Sab196087 *(*cur)++ = ch;
8105088Sab196087 **cur = '\0';
8115088Sab196087 (*n)--;
8125088Sab196087 (*cur_col)++;
8135088Sab196087 }
8145088Sab196087
8155088Sab196087 /*
8165088Sab196087 * Used by usage_optarg() to insert a string into the output
8175088Sab196087 * buffer, advancing the buffer pointer and current column, and reducing
8185088Sab196087 * the amount of remaining space.
8195088Sab196087 */
8205088Sab196087 static void
usage_optarg_insert_str(char ** cur,size_t * n,size_t * cur_col,const char * format,...)8215088Sab196087 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col,
8225088Sab196087 const char *format, ...)
8235088Sab196087 {
8245088Sab196087 size_t len;
8255088Sab196087 va_list args;
8265088Sab196087
8275088Sab196087 va_start(args, format);
8285088Sab196087 len = vsnprintf(*cur, *n, format, args);
8295088Sab196087 va_end(args);
8305088Sab196087
8315088Sab196087 *cur += len;
8325088Sab196087 *n -= len;
8335088Sab196087 *cur_col += len;
8345088Sab196087 }
8355088Sab196087 /*
8365088Sab196087 * Used by usage_optarg() to insert an optarg item string into the output
8375088Sab196087 * buffer, advancing the buffer pointer and current column, and reducing
8385088Sab196087 * the amount of remaining space.
8395088Sab196087 */
8405088Sab196087 static void
usage_optarg_insert_item(elfedit_optarg_item_t * item,char ** cur,size_t * n,size_t * cur_col)8415088Sab196087 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur,
8425088Sab196087 size_t *n, size_t *cur_col)
8435088Sab196087 {
8445088Sab196087 size_t len;
8455088Sab196087
8465088Sab196087 if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) {
8475088Sab196087 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2),
8485088Sab196087 item->oai_name, item->oai_vname);
8495088Sab196087 } else {
8505088Sab196087 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG),
8515088Sab196087 item->oai_name);
8525088Sab196087 }
8535088Sab196087 *cur += len;
8545088Sab196087 *n -= len;
8555088Sab196087 *cur_col += len;
8565088Sab196087 }
8575088Sab196087
8585088Sab196087
8595088Sab196087
8605088Sab196087 /*
8615088Sab196087 * Write the options/arguments to the usage string.
8625088Sab196087 *
8635088Sab196087 * entry:
8645088Sab196087 * main_buf_n - Size of main buffer from which buf and buf_n are
8655088Sab196087 * allocated.
8665088Sab196087 * buf - Address of pointer to where next item is to be placed.
8675088Sab196087 * buf_n - Address of count of remaining bytes in buffer
8685088Sab196087 * buf_cur_col - Address of current output column for current line
8695088Sab196087 * of generated string.
8705088Sab196087 * optarg - Options list
8715088Sab196087 * isopt - True if these are options, false for arguments.
8725088Sab196087 * wrap_str - String to indent wrapped lines. If NULL, lines
8735088Sab196087 * are not wrapped
8745088Sab196087 */
8755088Sab196087 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)8765088Sab196087 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col,
8775088Sab196087 elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str)
8785088Sab196087 {
8795088Sab196087 /*
8805088Sab196087 * An option can be combined into a simple format if it lacks
8815088Sab196087 * these flags and is only one character in length.
8825088Sab196087 */
8835088Sab196087 static const elfedit_cmd_oa_flag_t exflags =
8845088Sab196087 (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT);
8855088Sab196087
8865088Sab196087 /*
8875088Sab196087 * A static buffer, which is grown as needed to accomodate
8885088Sab196087 * the maximum usage string seen.
8895088Sab196087 */
8905088Sab196087 static STRBUF simple_str;
8915088Sab196087
8925088Sab196087 char *cur = *buf;
8935088Sab196087 size_t n = *buf_n;
8945088Sab196087 size_t cur_col = *buf_cur_col;
8955088Sab196087 int len;
8965088Sab196087 int use_simple = 0;
8975088Sab196087 elfedit_optarg_item_t item;
8985088Sab196087 elfedit_cmd_oa_mask_t optmask = 0;
8995088Sab196087 int use_bkt;
9005088Sab196087
9015088Sab196087 /*
9025088Sab196087 * If processing options, pull the 1-character ones that don't have
9035088Sab196087 * an associated value and don't have any mutual exclusion issues into
9045088Sab196087 * a single combination string to go at the beginning of the usage.
9055088Sab196087 */
9065088Sab196087 if (isopt) {
9075088Sab196087 elfedit_cmd_optarg_t *tmp_optarg = optarg;
9085088Sab196087 char *s;
9095088Sab196087
9105088Sab196087 /*
9115088Sab196087 * The simple string is guaranteed to fit in the same
9125088Sab196087 * amount of space reserved for the main buffer.
9135088Sab196087 */
9145088Sab196087 strbuf_ensure_size(&simple_str, main_buf_n);
9155088Sab196087 s = simple_str.buf;
9165088Sab196087 *s++ = ' ';
9175088Sab196087 *s++ = '[';
9185088Sab196087 *s++ = '-';
9195088Sab196087 while (tmp_optarg->oa_name != NULL) {
9205088Sab196087 elfedit_next_optarg(&tmp_optarg, &item);
9215088Sab196087 if (((item.oai_flags & exflags) == 0) &&
9225088Sab196087 (item.oai_name[2] == '\0') &&
9235088Sab196087 (item.oai_excmask == 0)) {
9245088Sab196087 optmask |= item.oai_idmask;
9255088Sab196087 *s++ = item.oai_name[1];
9265088Sab196087 }
9275088Sab196087 }
9285088Sab196087
9295088Sab196087 /*
9305088Sab196087 * If we found more than one, then finish the string and
9315088Sab196087 * add it. Don't do this for a single option, because
9325088Sab196087 * it looks better in that case if the option shows up
9335088Sab196087 * in alphabetical order rather than being hoisted.
9345088Sab196087 */
9355088Sab196087 use_simple = (s > (simple_str.buf + 4));
9365088Sab196087 if (use_simple) {
9375088Sab196087 *s++ = ']';
9385088Sab196087 *s++ = '\0';
9395088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col,
9405088Sab196087 MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf);
9415088Sab196087 } else {
9425088Sab196087 /* Not using it, so reset the cumulative options mask */
9435088Sab196087 optmask = 0;
9445088Sab196087 }
9455088Sab196087 }
9465088Sab196087
9475088Sab196087 while (optarg->oa_name != NULL) {
9485088Sab196087 elfedit_next_optarg(&optarg, &item);
9495088Sab196087
9505088Sab196087 if (isopt) {
9515088Sab196087 /*
9525088Sab196087 * If this is an option that was pulled into the
9535088Sab196087 * combination string above, then skip over it.
9545088Sab196087 */
9555088Sab196087 if (use_simple && ((item.oai_flags & exflags) == 0) &&
9565088Sab196087 (item.oai_name[2] == '\0') &&
9575088Sab196087 (item.oai_excmask == 0))
9585088Sab196087 continue;
9595088Sab196087
9605088Sab196087 /*
9615088Sab196087 * If this is a mutual exclusion option that was
9625088Sab196087 * picked up out of order by a previous iteration
9635088Sab196087 * of this loop, then skip over it.
9645088Sab196087 */
9655088Sab196087 if ((optmask & item.oai_idmask) != 0)
9665088Sab196087 continue;
9675088Sab196087
9685088Sab196087 /* Add this item to the accumulating options mask */
9695088Sab196087 optmask |= item.oai_idmask;
9705088Sab196087 }
9715088Sab196087
9725088Sab196087 /* Wrap line, or insert blank separator */
9735088Sab196087 if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) {
9745088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE),
9755088Sab196087 wrap_str);
9765088Sab196087 cur += len;
9775088Sab196087 n -= len;
9785088Sab196087 cur_col = len - 1; /* Don't count the newline */
9795088Sab196087 } else {
9805088Sab196087 usage_optarg_insert_ch(' ', &cur, &n, &cur_col);
9815088Sab196087 }
9825088Sab196087
9835088Sab196087 use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt;
9845088Sab196087 if (use_bkt)
9855088Sab196087 usage_optarg_insert_ch('[', &cur, &n, &cur_col);
9865088Sab196087
9875088Sab196087 /* Add the item to the buffer */
9885088Sab196087 usage_optarg_insert_item(&item, &cur, &n, &cur_col);
9895088Sab196087
9905088Sab196087 /*
9915088Sab196087 * If this item has a non-zero mutual exclusion mask,
9925088Sab196087 * then look for the other items and display them all
9935088Sab196087 * together with alternation (|). Note that plain arguments
9945088Sab196087 * cannot have a non-0 exclusion mask, so this is
9955088Sab196087 * effectively options-only (isopt != 0).
9965088Sab196087 */
9975088Sab196087 if (item.oai_excmask != 0) {
9985088Sab196087 elfedit_cmd_optarg_t *tmp_optarg = optarg;
9995088Sab196087 elfedit_optarg_item_t tmp_item;
10005088Sab196087
10015088Sab196087 /*
10025088Sab196087 * When showing alternation, elipses for multiple
10035088Sab196087 * copies need to appear inside the [] brackets.
10045088Sab196087 */
10055088Sab196087 if (item.oai_flags & ELFEDIT_CMDOA_F_MULT)
10065088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col,
10075088Sab196087 MSG_ORIG(MSG_STR_ELIPSES));
10085088Sab196087
10095088Sab196087
10105088Sab196087 while (tmp_optarg->oa_name != NULL) {
10115088Sab196087 elfedit_next_optarg(&tmp_optarg, &tmp_item);
10125088Sab196087 if ((item.oai_excmask & tmp_item.oai_idmask) ==
10135088Sab196087 0)
10145088Sab196087 continue;
10155088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col,
10165088Sab196087 MSG_ORIG(MSG_STR_SP_BAR_SP));
10175088Sab196087 usage_optarg_insert_item(&tmp_item,
10185088Sab196087 &cur, &n, &cur_col);
10195088Sab196087
10205088Sab196087 /*
10215088Sab196087 * Add it to the mask of seen options.
10225088Sab196087 * This will keep us from showing it twice.
10235088Sab196087 */
10245088Sab196087 optmask |= tmp_item.oai_idmask;
10255088Sab196087 }
10265088Sab196087 }
10275088Sab196087 if (use_bkt)
10285088Sab196087 usage_optarg_insert_ch(']', &cur, &n, &cur_col);
10295088Sab196087
10305088Sab196087 /*
10315088Sab196087 * If alternation was not shown above (non-zero exclusion mask)
10325088Sab196087 * then the elipses for multiple copies are shown outside
10335088Sab196087 * any [] brackets.
10345088Sab196087 */
10355088Sab196087 if ((item.oai_excmask == 0) &&
10365088Sab196087 (item.oai_flags & ELFEDIT_CMDOA_F_MULT))
10375088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col,
10385088Sab196087 MSG_ORIG(MSG_STR_ELIPSES));
10395088Sab196087
10405088Sab196087 }
10415088Sab196087
10425088Sab196087 *buf = cur;
10435088Sab196087 *buf_n = n;
10445088Sab196087 *buf_cur_col = cur_col;
10455088Sab196087 }
10465088Sab196087
10475088Sab196087
10485088Sab196087
10495088Sab196087 /*
10505088Sab196087 * Format the usage string for a command into a static buffer and
10515088Sab196087 * return the pointer to the user. The resultant string is valid
10525088Sab196087 * until the next call to this routine, and which point it
10535088Sab196087 * will be overwritten or the memory is freed.
10545088Sab196087 *
10555088Sab196087 * entry:
10565088Sab196087 * mod, cmd - Module and command definitions for command to be described
10575088Sab196087 * wrap_str - NULL, or string to be used to indent when
10585088Sab196087 * lines are wrapped. If NULL, no wrapping is done, and
10595088Sab196087 * all output is on a single line.
10605088Sab196087 * cur_col - Starting column at which the string will be displayed.
10615088Sab196087 * Ignored if wrap_str is NULL.
10625088Sab196087 */
10635088Sab196087 const char *
elfedit_format_command_usage(elfeditGC_module_t * mod,elfeditGC_cmd_t * cmd,const char * wrap_str,size_t cur_col)10645088Sab196087 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd,
10655088Sab196087 const char *wrap_str, size_t cur_col)
10665088Sab196087 {
10675088Sab196087
10685088Sab196087 /*
10695088Sab196087 * A static buffer, which is grown as needed to accomodate
10705088Sab196087 * the maximum usage string seen.
10715088Sab196087 */
10725088Sab196087 static STRBUF str;
10735088Sab196087
10745088Sab196087 elfedit_cmd_optarg_t *optarg;
10755088Sab196087 size_t len, n, elipses_len;
10765088Sab196087 char *cur;
10775088Sab196087 elfedit_optarg_item_t item;
10785088Sab196087
10795088Sab196087 /*
10805088Sab196087 * Estimate a worst case size for the usage string:
10815088Sab196087 * - module name
10825088Sab196087 * - lengths of the strings
10835088Sab196087 * - every option or argument is enclosed in brackets
10845088Sab196087 * - space in between each item, with an alternation (" | ")
10855088Sab196087 * - elipses will be displayed with each option and argument
10865088Sab196087 */
10875088Sab196087 n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6;
10885088Sab196087 elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES));
10895088Sab196087 if ((optarg = cmd->cmd_opt) != NULL)
10905088Sab196087 while (optarg->oa_name != NULL) {
10915088Sab196087 elfedit_next_optarg(&optarg, &item);
10925088Sab196087 n += strlen(item.oai_name) + 5 + elipses_len;
10935088Sab196087 }
10945088Sab196087 if ((optarg = cmd->cmd_args) != NULL)
10955088Sab196087 while (optarg->oa_name != NULL) {
10965088Sab196087 elfedit_next_optarg(&optarg, &item);
10975088Sab196087 n += strlen(item.oai_name) + 5 + elipses_len;
10985088Sab196087 }
10995088Sab196087 n++; /* Null termination */
11005088Sab196087
11015088Sab196087 /*
11025088Sab196087 * If wrapping lines, we insert a newline and then wrap_str
11035088Sab196087 * every USAGE_WRAP_COL characters.
11045088Sab196087 */
11055088Sab196087 if (wrap_str != NULL)
11065088Sab196087 n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) *
11075088Sab196087 (strlen(wrap_str) + 1);
11085088Sab196087
11095088Sab196087 strbuf_ensure_size(&str, n);
11105088Sab196087
11115088Sab196087 /* Command name */
11125088Sab196087 cur = str.buf;
11135088Sab196087 n = str.n;
11145088Sab196087 if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0)
11155088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD),
11165088Sab196087 cmd->cmd_name[0]);
11175088Sab196087 else
11185088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD),
11195088Sab196087 mod->mod_name, cmd->cmd_name[0]);
11205088Sab196087 cur += len;
11215088Sab196087 n -= len;
11225088Sab196087 cur_col += len;
11235088Sab196087
11245088Sab196087 if (cmd->cmd_opt != NULL)
11255088Sab196087 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt,
11265088Sab196087 1, wrap_str);
11275088Sab196087 if (cmd->cmd_args != NULL)
11285088Sab196087 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args,
11295088Sab196087 0, wrap_str);
11305088Sab196087
11315088Sab196087 return (str.buf);
11325088Sab196087 }
11335088Sab196087
11345088Sab196087 /*
11355088Sab196087 * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE
11365088Sab196087 * error giving usage information for the command currently
11375088Sab196087 * referenced by state.cur_cmd.
11385088Sab196087 */
11395088Sab196087 void
elfedit_command_usage(void)11405088Sab196087 elfedit_command_usage(void)
11415088Sab196087 {
11425088Sab196087 elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD),
11435088Sab196087 elfedit_format_command_usage(state.cur_cmd->ucmd_mod,
11445088Sab196087 state.cur_cmd->ucmd_cmd, NULL, 0));
11455088Sab196087 }
11465088Sab196087
11475088Sab196087
11485088Sab196087 /*
11495088Sab196087 * This function allows the loadable modules to get the command line
11505088Sab196087 * flags.
11515088Sab196087 */
11525088Sab196087 elfedit_flag_t
elfedit_flags(void)11535088Sab196087 elfedit_flags(void)
11545088Sab196087 {
11555088Sab196087 return (state.flags);
11565088Sab196087 }
11575088Sab196087
11585088Sab196087 /*
11595088Sab196087 * This function is used to register a per-command invocation output style
11605088Sab196087 * that will momentarily override the global output style for the duration
11615088Sab196087 * of the current command. This function must only be called by an
11625088Sab196087 * active command.
11635088Sab196087 *
11645088Sab196087 * entry:
11655088Sab196087 * str - One of the valid strings for the output style
11665088Sab196087 */
11675088Sab196087 void
elfedit_set_cmd_outstyle(const char * str)11685088Sab196087 elfedit_set_cmd_outstyle(const char *str)
11695088Sab196087 {
11705088Sab196087 if ((state.cur_cmd != NULL) && (str != NULL)) {
11715088Sab196087 if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0)
11725088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
11735088Sab196087 MSG_INTL(MSG_ERR_BADOSTYLE), str);
11745088Sab196087 state.cur_cmd->ucmd_ostyle_set = 1;
11755088Sab196087 }
11765088Sab196087 }
11775088Sab196087
11785088Sab196087 /*
11795088Sab196087 * This function allows the loadable modules to get the output style.
11805088Sab196087 */
11815088Sab196087 elfedit_outstyle_t
elfedit_outstyle(void)11825088Sab196087 elfedit_outstyle(void)
11835088Sab196087 {
11845088Sab196087 /*
11855088Sab196087 * If there is an active per-command output style,
11865088Sab196087 * return it.
11875088Sab196087 */
11885088Sab196087 if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set))
11895088Sab196087 return (state.cur_cmd->ucmd_ostyle);
11905088Sab196087
11915088Sab196087
11925088Sab196087 return (state.outstyle);
11935088Sab196087 }
11945088Sab196087
11955088Sab196087 /*
11965088Sab196087 * Return the command descriptor of the currently executing command.
11975088Sab196087 * For use only by the modules or code called by the modules.
11985088Sab196087 */
11995088Sab196087 elfeditGC_cmd_t *
elfedit_curcmd(void)12005088Sab196087 elfedit_curcmd(void)
12015088Sab196087 {
12025088Sab196087 return (state.cur_cmd->ucmd_cmd);
12035088Sab196087 }
12045088Sab196087
12055088Sab196087 /*
12065088Sab196087 * Build a dynamically allocated elfedit_obj_state_t struct that
12075088Sab196087 * contains a cache of the ELF file contents. This pre-chewed form
12085088Sab196087 * is fed to each command, reducing the amount of ELF boilerplate
12095088Sab196087 * code each command needs to contain.
12105088Sab196087 *
12115088Sab196087 * entry:
12125088Sab196087 * file - Name of file to process
12135088Sab196087 *
12145088Sab196087 * exit:
12155088Sab196087 * Fills state.elf with the necessary information for the open file.
12165088Sab196087 *
12175088Sab196087 * note: The resulting elfedit_obj_state_t is allocated from a single
12185088Sab196087 * piece of memory, such that a single call to free() suffices
12195088Sab196087 * to release it as well as any memory it references.
12205088Sab196087 */
12215088Sab196087 static void
init_obj_state(const char * file)12225088Sab196087 init_obj_state(const char *file)
12235088Sab196087 {
12245088Sab196087 int fd;
12255088Sab196087 Elf *elf;
12265088Sab196087 int open_flag;
12275088Sab196087
12285088Sab196087 /*
12295088Sab196087 * In readonly mode, we open the file readonly so that it is
12305088Sab196087 * impossible to modify the file by accident. This also allows
12315088Sab196087 * us to access readonly files, perhaps in a case where we don't
12325088Sab196087 * intend to change it.
12335088Sab196087 *
12345088Sab196087 * We always use ELF_C_RDWR with elf_begin(), even in a readonly
12355088Sab196087 * session. This allows us to modify the in-memory image, which
12365088Sab196087 * can be useful when examining a file, even though we don't intend
12375088Sab196087 * to modify the on-disk data. The file is not writable in
12385088Sab196087 * this case, and we don't call elf_update(), so it is safe to do so.
12395088Sab196087 */
12405088Sab196087 open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR);
12415088Sab196087 if ((fd = open(file, open_flag)) == -1) {
12425088Sab196087 int err = errno;
12435088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE),
12445088Sab196087 file, strerror(err));
12455088Sab196087 }
12465088Sab196087 (void) elf_version(EV_CURRENT);
12475088Sab196087 elf = elf_begin(fd, ELF_C_RDWR, NULL);
12485088Sab196087 if (elf == NULL) {
12495088Sab196087 (void) close(fd);
12505088Sab196087 elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN));
12515088Sab196087 /*NOTREACHED*/
12525088Sab196087 }
12535088Sab196087
12545088Sab196087 /* We only handle standalone ELF files */
12555088Sab196087 switch (elf_kind(elf)) {
12565088Sab196087 case ELF_K_AR:
12575088Sab196087 (void) close(fd);
12585088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file);
12595088Sab196087 break;
12605088Sab196087 case ELF_K_ELF:
12615088Sab196087 break;
12625088Sab196087 default:
12635088Sab196087 (void) close(fd);
12645088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE),
12655088Sab196087 file);
12665088Sab196087 break;
12675088Sab196087 }
12685088Sab196087
12695088Sab196087 /*
12705088Sab196087 * Tell libelf that we take responsibility for object layout.
12715088Sab196087 * Otherwise, it will compute "proper" values for layout and
12725088Sab196087 * alignment fields, and these values can overwrite the values
12735088Sab196087 * set in the elfedit session. We are modifying existing
12745088Sab196087 * objects --- the layout concerns have already been dealt
12755088Sab196087 * with when the object was built.
12765088Sab196087 */
12775088Sab196087 (void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
12785088Sab196087
12795088Sab196087 /* Fill in state.elf.obj_state */
12805088Sab196087 state.elf.elfclass = gelf_getclass(elf);
12815088Sab196087 switch (state.elf.elfclass) {
12825088Sab196087 case ELFCLASS32:
12835088Sab196087 elfedit32_init_obj_state(file, fd, elf);
12845088Sab196087 break;
12855088Sab196087 case ELFCLASS64:
12865088Sab196087 elfedit64_init_obj_state(file, fd, elf);
12875088Sab196087 break;
12885088Sab196087 default:
12895088Sab196087 (void) close(fd);
12905088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS),
12915088Sab196087 file);
12925088Sab196087 break;
12935088Sab196087 }
12945088Sab196087 }
12955088Sab196087
12965088Sab196087
12975892Sab196087 #ifdef DEBUG_MODULE_LIST
12985088Sab196087 /*
12995088Sab196087 * Debug routine. Dump the module list to stdout.
13005088Sab196087 */
13015088Sab196087 static void
dbg_module_list(char * title)13025088Sab196087 dbg_module_list(char *title)
13035088Sab196087 {
13045088Sab196087 MODLIST_T *m;
13055088Sab196087
13065088Sab196087 printf("<MODULE LIST: %s>\n", title);
13075088Sab196087 for (m = state.modlist; m != NULL; m = m->next) {
13085088Sab196087 printf("Module: >%s<\n", m->mod->mod_name);
13095088Sab196087 printf(" hdl: %llx\n", m->dl_hdl);
13105088Sab196087 printf(" path: >%s<\n", m->path ? m->path : "<builtin>");
13115088Sab196087 }
13125088Sab196087 printf("<END OF MODULE LIST>\n");
13135088Sab196087 }
13145088Sab196087 #endif
13155088Sab196087
13165088Sab196087
13175088Sab196087 /*
13185088Sab196087 * Search the module list for the named module.
13195088Sab196087 *
13205088Sab196087 * entry:
13215088Sab196087 * name - Name of module to find
13225088Sab196087 * insdef - Address of variable to receive address of predecessor
13235088Sab196087 * node to the desired one.
13245088Sab196087 *
13255088Sab196087 * exit:
13265088Sab196087 * If the module is it is found, this routine returns the pointer to
13275088Sab196087 * its MODLIST_T structure. *insdef references the predecessor node, or
13285088Sab196087 * is NULL if the found item is at the head of the list.
13295088Sab196087 *
13305088Sab196087 * If the module is not found, NULL is returned. *insdef references
13315088Sab196087 * the predecessor node of the position where an entry for this module
13325088Sab196087 * would be placed, or NULL if it would go at the beginning.
13335088Sab196087 */
13345088Sab196087 static MODLIST_T *
module_loaded(const char * name,MODLIST_T ** insdef)13355088Sab196087 module_loaded(const char *name, MODLIST_T **insdef)
13365088Sab196087 {
13375088Sab196087 MODLIST_T *moddef;
13385088Sab196087 int cmp;
13395088Sab196087
13405088Sab196087 *insdef = NULL;
13415088Sab196087 moddef = state.modlist;
13425088Sab196087 if (moddef != NULL) {
13435088Sab196087 cmp = strcasecmp(name, moddef->ml_mod->mod_name);
13445088Sab196087 if (cmp == 0) { /* Desired module is first in list */
13455088Sab196087 return (moddef);
13465088Sab196087 } else if (cmp > 0) { /* cmp > 0: Insert in middle/end */
13475088Sab196087 *insdef = moddef;
13485088Sab196087 moddef = moddef->ml_next;
13495088Sab196087 cmp = -1;
13505088Sab196087 while (moddef && (cmp < 0)) {
13515088Sab196087 cmp = strcasecmp(moddef->ml_mod->mod_name,
13525088Sab196087 name);
13535088Sab196087 if (cmp == 0)
13545088Sab196087 return (moddef);
13555088Sab196087 if (cmp < 0) {
13565088Sab196087 *insdef = moddef;
13575088Sab196087 moddef = (*insdef)->ml_next;
13585088Sab196087 }
13595088Sab196087 }
13605088Sab196087 }
13615088Sab196087 }
13625088Sab196087
13635088Sab196087 return (NULL);
13645088Sab196087 }
13655088Sab196087
13665088Sab196087
13675088Sab196087 /*
13685088Sab196087 * Determine if a file is a sharable object based on its file path.
13695088Sab196087 * If path ends in a .so, followed optionally by a period and 1 or more
13705088Sab196087 * digits, we say that it is and return a pointer to the first character
13715088Sab196087 * of the suffix. Otherwise NULL is returned.
13725088Sab196087 */
13735088Sab196087 static const char *
path_is_so(const char * path)13745088Sab196087 path_is_so(const char *path)
13755088Sab196087 {
13765088Sab196087 int dotso_len;
13775088Sab196087 const char *tail;
13785088Sab196087 size_t len;
13795088Sab196087
13805088Sab196087 len = strlen(path);
13815088Sab196087 if (len == 0)
13825088Sab196087 return (NULL);
13835088Sab196087 tail = path + len;
13845088Sab196087 if (isdigit(*(tail - 1))) {
13855088Sab196087 while ((tail > path) && isdigit(*(tail - 1)))
13865088Sab196087 tail--;
13875088Sab196087 if ((tail <= path) || (*tail != '.'))
13885088Sab196087 return (NULL);
13895088Sab196087 }
13905088Sab196087 dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO));
13915088Sab196087 if ((tail - path) < dotso_len)
13925088Sab196087 return (NULL);
13935088Sab196087 tail -= dotso_len;
13945088Sab196087 if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0)
13955088Sab196087 return (tail);
13965088Sab196087
13975088Sab196087 return (NULL);
13985088Sab196087 }
13995088Sab196087
14005088Sab196087
14015088Sab196087 /*
14025088Sab196087 * Locate the start of the unsuffixed file name within path. Returns pointer
14035088Sab196087 * to first character of that name in path.
14045088Sab196087 *
14055088Sab196087 * entry:
14065088Sab196087 * path - Path to be examined.
14075088Sab196087 * tail - NULL, or pointer to position at tail of path from which
14085088Sab196087 * the search for '/' characters should start. If NULL,
14095088Sab196087 * strlen() is used to locate the end of the string.
14105088Sab196087 * buf - NULL, or buffer to receive a copy of the characters that
14115088Sab196087 * lie between the start of the filename and tail.
14125088Sab196087 * bufsize - sizeof(buf)
14135088Sab196087 *
14145088Sab196087 * exit:
14155088Sab196087 * The pointer to the first character of the unsuffixed file name
14165088Sab196087 * within path is returned. If buf is non-NULL, the characters
14175088Sab196087 * lying between that point and tail (or the end of path if tail
14185088Sab196087 * is NULL) are copied into buf.
14195088Sab196087 */
14205088Sab196087 static const char *
elfedit_basename(const char * path,const char * tail,char * buf,size_t bufsiz)14215088Sab196087 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz)
14225088Sab196087 {
14235088Sab196087 const char *s;
14245088Sab196087
14255088Sab196087 if (tail == NULL)
14265088Sab196087 tail = path + strlen(path);
14275088Sab196087 s = tail;
14285088Sab196087 while ((s > path) && (*(s - 1) != '/'))
14295088Sab196087 s--;
14305088Sab196087 if (buf != NULL)
14315088Sab196087 elfedit_strnbcpy(buf, s, tail - s, bufsiz);
14325088Sab196087 return (s);
14335088Sab196087 }
14345088Sab196087
14355088Sab196087
14365088Sab196087 /*
14375088Sab196087 * Issue an error on behalf of load_module(), taking care to release
14385088Sab196087 * resources that routine may have aquired:
14395088Sab196087 *
14405088Sab196087 * entry:
14415088Sab196087 * moddef - NULL, or a module definition to be released via free()
14425088Sab196087 * dl_hdl - NULL, or a handle to a sharable object to release via
14435088Sab196087 * dlclose().
14445088Sab196087 * dl_path - If dl_hdl is non-NULL, the path to the sharable object
14455088Sab196087 * file that was loaded.
14465088Sab196087 * format - A format string to pass to elfedit_msg(), containing
14475088Sab196087 * no more than (3) %s format codes, and no other format codes.
14485088Sab196087 * [s1-s4] - Strings to pass to elfedit_msg() to satisfy the four
14495088Sab196087 * allowed %s codes in format. Should be set to NULL if the
14505088Sab196087 * format string does not need them.
14515088Sab196087 *
14525088Sab196087 * note:
14535088Sab196087 * This routine makes a copy of the s1-s4 strings before freeing any
14545088Sab196087 * memory or unmapping the sharable library. It is therefore safe to
14555088Sab196087 * use strings from moddef, or from the sharable library (which will
14565088Sab196087 * be unmapped) to satisfy the other arguments s1-s4.
14575088Sab196087 */
14585088Sab196087 static void
load_module_err(MODLIST_T * moddef,void * dl_hdl,const char * dl_path,const char * format,const char * s1,const char * s2,const char * s3,const char * s4)14595088Sab196087 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path,
14605088Sab196087 const char *format, const char *s1, const char *s2, const char *s3,
14615088Sab196087 const char *s4)
14625088Sab196087 {
14635088Sab196087 #define SCRBUFSIZE (PATH_MAX + 256) /* A path, plus some extra */
14645088Sab196087
14655088Sab196087 char s1_buf[SCRBUFSIZE];
14665088Sab196087 char s2_buf[SCRBUFSIZE];
14675088Sab196087 char s3_buf[SCRBUFSIZE];
14685088Sab196087 char s4_buf[SCRBUFSIZE];
14695088Sab196087
14705088Sab196087 /*
14715088Sab196087 * The caller may provide strings for s1-s3 that are from
14725088Sab196087 * moddef. If we free moddef, the printf() will die on access
14735088Sab196087 * to free memory. We could push back on the user and force
14745088Sab196087 * each call to carefully make copies of such data. However, this
14755088Sab196087 * is an easy case to miss. Furthermore, this is an error case,
14765088Sab196087 * and machine efficiency is not the main issue. We therefore make
14775088Sab196087 * copies of the s1-s3 strings here into auto variables, and then
14785088Sab196087 * use those copies. The user is freed from worrying about it.
14795088Sab196087 *
14805088Sab196087 * We use oversized stack based buffers instead of malloc() to
14815088Sab196087 * reduce the number of ways that things can go wrong while
14825088Sab196087 * reporting the error.
14835088Sab196087 */
14845088Sab196087 if (s1 != NULL)
14855088Sab196087 (void) strlcpy(s1_buf, s1, sizeof (s1_buf));
14865088Sab196087 if (s2 != NULL)
14875088Sab196087 (void) strlcpy(s2_buf, s2, sizeof (s2_buf));
14885088Sab196087 if (s3 != NULL)
14895088Sab196087 (void) strlcpy(s3_buf, s3, sizeof (s3_buf));
14905088Sab196087 if (s4 != NULL)
14915088Sab196087 (void) strlcpy(s4_buf, s4, sizeof (s4_buf));
14925088Sab196087
14935088Sab196087
14945088Sab196087 if (moddef != NULL)
14955088Sab196087 free(moddef);
14965088Sab196087
14975088Sab196087 if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0))
14985088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
14995088Sab196087 dl_path, dlerror());
15005088Sab196087
15015088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf);
15025088Sab196087 #undef SCRBUFSIZE
15035088Sab196087 }
15045088Sab196087
15055088Sab196087
15065088Sab196087 /*
15075088Sab196087 * Load a module sharable object for load_module().
15085088Sab196087 *
15095088Sab196087 * entry:
15105088Sab196087 * path - Path of file to open
15115088Sab196087 * moddef - If this function issues a non-returning error, it will
15125088Sab196087 * first return the memory referenced by moddef. This argument
15135088Sab196087 * is not used otherwise.
15145088Sab196087 * must_exist - If True, we consider it to be an error if the file given
15155088Sab196087 * by path does not exist. If False, no error is issued
15165088Sab196087 * and a NULL value is quietly returned.
15175088Sab196087 *
15185088Sab196087 * exit:
15195088Sab196087 * Returns a handle to the loaded object on success, or NULL if no
15205088Sab196087 * file was loaded.
15215088Sab196087 */
15225088Sab196087 static void *
load_module_dlopen(const char * path,MODLIST_T * moddef,int must_exist)15235088Sab196087 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist)
15245088Sab196087 {
15255088Sab196087 int fd;
15265088Sab196087 void *hdl;
15275088Sab196087
15285088Sab196087 /*
15295088Sab196087 * If the file is not required to exist, and it doesn't, then
15305088Sab196087 * we want to quietly return without an error.
15315088Sab196087 */
15325088Sab196087 if (!must_exist) {
15335088Sab196087 fd = open(path, O_RDONLY);
15345088Sab196087 if (fd >= 0) {
15355088Sab196087 (void) close(fd);
15365088Sab196087 } else if (errno == ENOENT) {
15375088Sab196087 return (NULL);
15385088Sab196087 }
15395088Sab196087 }
15405088Sab196087
15415088Sab196087 if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL)
15425088Sab196087 load_module_err(moddef, NULL, NULL,
15435088Sab196087 MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL);
15445088Sab196087
15455088Sab196087 return (hdl);
15465088Sab196087 }
15475088Sab196087
15485088Sab196087
15495088Sab196087 /*
15505088Sab196087 * Sanity check option arguments to prevent common errors. The rest of
15515088Sab196087 * elfedit assumes these tests have been done, and does not check
15525088Sab196087 * again.
15535088Sab196087 */
15545088Sab196087 static void
validate_optarg(elfedit_cmd_optarg_t * optarg,int isopt,MODLIST_T * moddef,const char * mod_name,const char * cmd_name,void * dl_hdl,const char * dl_path)15555088Sab196087 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef,
15565088Sab196087 const char *mod_name, const char *cmd_name,
15575088Sab196087 void *dl_hdl, const char *dl_path)
15585088Sab196087 {
15595088Sab196087 #define FAIL(_msg) errmsg = _msg; goto fail
15605088Sab196087
15615088Sab196087 Msg errmsg;
15625088Sab196087 elfedit_cmd_oa_mask_t optmask = 0;
15635088Sab196087
15645088Sab196087 for (; optarg->oa_name != NULL; optarg++) {
15655088Sab196087 /*
15665088Sab196087 * If ELFEDIT_CMDOA_F_INHERIT is set:
15675088Sab196087 * - oa_name must be a value in the range of
15685088Sab196087 * known ELFEDIT_STDOA_ values.
15695088Sab196087 * - oa_help must be NULL
15705088Sab196087 * - ELFEDIT_CMDOA_F_INHERIT must be the only flag set
15715088Sab196087 */
15725088Sab196087 if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
15735088Sab196087 if ((((uintptr_t)optarg->oa_name) >
15745088Sab196087 ELFEDIT_NUM_STDOA) ||
15755088Sab196087 (optarg->oa_help != 0) ||
15765088Sab196087 (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT))
15775088Sab196087 /*
15785088Sab196087 * Can't use FAIL --- oa_name is not a valid
15795088Sab196087 * string, and load_module_err() looks at args.
15805088Sab196087 */
15815088Sab196087 load_module_err(moddef, dl_hdl, dl_path,
15825088Sab196087 MSG_INTL(MSG_ERR_BADSTDOA), dl_path,
15835088Sab196087 mod_name, cmd_name, NULL);
15845088Sab196087 continue;
15855088Sab196087 }
15865088Sab196087
15875088Sab196087 if (isopt) {
15885088Sab196087 /*
15895088Sab196087 * Option name must start with a '-', and must
15905088Sab196087 * have at one following character.
15915088Sab196087 */
15925088Sab196087 if (optarg->oa_name[0] != '-') {
15935088Sab196087 /* MSG_INTL(MSG_ERR_OPT_MODPRE) */
15945088Sab196087 FAIL(MSG_ERR_OPT_MODPRE);
15955088Sab196087 }
15965088Sab196087 if (optarg->oa_name[1] == '\0') {
15975088Sab196087 /* MSG_INTL(MSG_ERR_OPT_MODLEN) */
15985088Sab196087 FAIL(MSG_ERR_OPT_MODLEN);
15995088Sab196087 }
16005088Sab196087
16015088Sab196087 /*
16025088Sab196087 * oa_idmask must be 0, or it must have a single
16035088Sab196087 * bit set (a power of 2).oa_excmask must be 0
16045088Sab196087 * if oa_idmask is 0
16055088Sab196087 */
16065088Sab196087 if (optarg->oa_idmask == 0) {
16075088Sab196087 if (optarg->oa_excmask != 0) {
16085088Sab196087 /* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */
16095088Sab196087 FAIL(MSG_ERR_OPT_EXCMASKN0);
16105088Sab196087 }
16115088Sab196087 } else {
16125088Sab196087 if (elfedit_bits_set(optarg->oa_idmask,
16135088Sab196087 sizeof (optarg->oa_idmask)) != 1) {
16145088Sab196087 /* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */
16155088Sab196087 FAIL(MSG_ERR_OPT_IDMASKPOW2);
16165088Sab196087 }
16175088Sab196087
16185088Sab196087 /* Non-zero idmask must be unique */
16195088Sab196087 if ((optarg->oa_idmask & optmask) != 0) {
16205088Sab196087 /* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */
16215088Sab196087 FAIL(MSG_ERR_OPT_IDMASKUNIQ);
16225088Sab196087 }
16235088Sab196087
16245088Sab196087 /* Add this one to the overall mask */
16255088Sab196087 optmask |= optarg->oa_idmask;
16265088Sab196087 }
16275088Sab196087 } else {
16285088Sab196087 /*
16295088Sab196087 * Argument name cannot start with a'-', and must
16305088Sab196087 * not be a null string.
16315088Sab196087 */
16325088Sab196087 if (optarg->oa_name[0] == '-') {
16335088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MODPRE) */
16345088Sab196087 FAIL(MSG_ERR_ARG_MODPRE);
16355088Sab196087 }
16365088Sab196087 if (optarg->oa_name[1] == '\0') {
16375088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MODLEN) */
16385088Sab196087 FAIL(MSG_ERR_ARG_MODLEN);
16395088Sab196087 }
16405088Sab196087
16415088Sab196087
16425088Sab196087 /* oa_idmask and oa_excmask must both be 0 */
16435088Sab196087 if ((optarg->oa_idmask != 0) ||
16445088Sab196087 (optarg->oa_excmask != 0)) {
16455088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */
16465088Sab196087 FAIL(MSG_ERR_ARG_MASKNOT0);
16475088Sab196087 }
16485088Sab196087
16495088Sab196087 }
16505088Sab196087
16515088Sab196087 /*
16525088Sab196087 * If it takes a value, make sure that we are
16535088Sab196087 * processing options, because CMDOA_F_VALUE is not
16545088Sab196087 * allowed for plain arguments. Then check the following
16555088Sab196087 * item in the list:
16565088Sab196087 * - There must be a following item.
16575088Sab196087 * - oa_name must be non-NULL. This is the only field
16585088Sab196087 * that is used by elfedit.
16595088Sab196087 * - oa_help, oa_flags, oa_idmask, and oa_excmask
16605088Sab196087 * must be 0.
16615088Sab196087 */
16625088Sab196087 if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) {
16635088Sab196087 elfedit_cmd_optarg_t *oa1 = optarg + 1;
16645088Sab196087
16655088Sab196087 if (!isopt) {
16665088Sab196087 /* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */
16675088Sab196087 FAIL(MSG_ERR_ARG_CMDOA_VAL);
16685088Sab196087 }
16695088Sab196087
16705088Sab196087 if ((optarg + 1)->oa_name == NULL) {
16715088Sab196087 /* MSG_INTL(MSG_ERR_BADMODOPTVAL) */
16725088Sab196087 FAIL(MSG_ERR_BADMODOPTVAL);
16735088Sab196087 }
16745088Sab196087
16755088Sab196087 if (oa1->oa_name == NULL) {
16765088Sab196087 /* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */
16775088Sab196087 FAIL(MSG_ERR_CMDOA_VALNAM);
16785088Sab196087 }
16795088Sab196087 if ((oa1->oa_help != NULL) || (oa1->oa_flags != 0) ||
16805088Sab196087 (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) {
16815088Sab196087 /* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */
16825088Sab196087 FAIL(MSG_ERR_CMDOA_VALNOT0);
16835088Sab196087 }
16845088Sab196087 optarg++;
16855088Sab196087 }
16865088Sab196087 }
16875088Sab196087
16885088Sab196087
16895088Sab196087 return;
16905088Sab196087
16915088Sab196087 fail:
16925088Sab196087 load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg),
16935088Sab196087 dl_path, mod_name, cmd_name, optarg->oa_name);
16945088Sab196087 }
16955088Sab196087
16965088Sab196087 /*
16975088Sab196087 * Look up the specified module, loading the module if necessary,
16985088Sab196087 * and return its definition, or NULL on failure.
16995088Sab196087 *
17005088Sab196087 * entry:
17015088Sab196087 * name - Name of module to load. If name contains a '/' character or has
17025088Sab196087 * a ".so" suffix, then it is taken to be an absolute file path,
17035088Sab196087 * and is used directly as is. If name does not contain a '/'
17045088Sab196087 * character, then we look for it against the locations in
17055088Sab196087 * the module path, addint the '.so' suffix, and taking the first
17065088Sab196087 * one we find.
17075088Sab196087 * must_exist - If True, we consider it to be an error if we are unable
17085088Sab196087 * to locate a file to load and the module does not already exist.
17095088Sab196087 * If False, NULL is returned quietly in this case.
17105088Sab196087 * allow_abs - True if absolute paths are allowed. False to disallow
17115088Sab196087 * them.
17125088Sab196087 *
17135088Sab196087 * note:
17145088Sab196087 * If the path is absolute, then we load the file and take the module
17155088Sab196087 * name from the data returned by its elfedit_init() function. If a
17165088Sab196087 * module of that name is already loaded, it is unloaded and replaced
17175088Sab196087 * with the new one.
17185088Sab196087 *
17195088Sab196087 * If the path is non absolute, then we check to see if the module has
17205088Sab196087 * already been loaded, and if so, we return that module definition.
17215088Sab196087 * In this case, nothing new is loaded. If the module has not been loaded,
17225088Sab196087 * we search the path for it and load it. If the module name provided
17235088Sab196087 * by the elfedit_init() function does not match the name of the file,
17245088Sab196087 * an error results.
17255088Sab196087 */
17265088Sab196087 elfeditGC_module_t *
elfedit_load_module(const char * name,int must_exist,int allow_abs)17275088Sab196087 elfedit_load_module(const char *name, int must_exist, int allow_abs)
17285088Sab196087 {
17295088Sab196087 elfedit_init_func_t *init_func;
17305088Sab196087 elfeditGC_module_t *mod;
17315088Sab196087 MODLIST_T *moddef, *insdef;
17325088Sab196087 const char *path;
17335088Sab196087 char path_buf[PATH_MAX + 1];
17345088Sab196087 void *hdl;
17355088Sab196087 size_t i;
17365088Sab196087 int is_abs_path;
17375088Sab196087 elfeditGC_cmd_t *cmd;
17385088Sab196087
17395088Sab196087 /*
17405088Sab196087 * If the name includes a .so suffix, or has any '/' characters,
17415088Sab196087 * then it is an absolute path that we use as is to load the named
17425088Sab196087 * file. Otherwise, we iterate over the path, adding the .so suffix
17435088Sab196087 * and load the first file that matches.
17445088Sab196087 */
17455088Sab196087 is_abs_path = (path_is_so(name) != NULL) ||
17465088Sab196087 (name != elfedit_basename(name, NULL, NULL, 0));
17475088Sab196087
17485088Sab196087 if (is_abs_path && !allow_abs)
17495088Sab196087 load_module_err(NULL, NULL, NULL,
17505088Sab196087 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
17515088Sab196087
17525088Sab196087 /*
17535088Sab196087 * If this is a non-absolute path, search for the module already
17545088Sab196087 * having been loaded, and return it if so.
17555088Sab196087 */
17565088Sab196087 if (!is_abs_path) {
17575088Sab196087 moddef = module_loaded(name, &insdef);
17585088Sab196087 if (moddef != NULL)
17595088Sab196087 return (moddef->ml_mod);
17605088Sab196087 /*
17615088Sab196087 * As a result of module_loaded(), insdef now contains the
17625088Sab196087 * immediate predecessor node for the new one, or NULL if
17635088Sab196087 * it goes at the front. In the absolute-path case, we take
17645088Sab196087 * care of this below, after the sharable object is loaded.
17655088Sab196087 */
17665088Sab196087 }
17675088Sab196087
17685088Sab196087 /*
17695088Sab196087 * malloc() a module definition block before trying to dlopen().
17705088Sab196087 * Doing things in the other order can cause the dlopen()'d object
17715088Sab196087 * to leak: If elfedit_malloc() fails, it can cause a jump to the
17725088Sab196087 * outer command loop without returning to the caller. Hence,
17735088Sab196087 * there will be no opportunity to clean up. Allocaing the module
17745088Sab196087 * first allows us to free it if necessary.
17755088Sab196087 */
17765088Sab196087 moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF),
17775088Sab196087 sizeof (*moddef) + PATH_MAX + 1);
17785088Sab196087 moddef->ml_path = ((char *)moddef) + sizeof (*moddef);
17795088Sab196087
17805088Sab196087 if (is_abs_path) {
17815088Sab196087 path = name;
17825088Sab196087 hdl = load_module_dlopen(name, moddef, must_exist);
17835088Sab196087 } else {
17845088Sab196087 hdl = NULL;
17855088Sab196087 path = path_buf;
17865088Sab196087 for (i = 0; i < state.modpath.n; i++) {
17875088Sab196087 if (snprintf(path_buf, sizeof (path_buf),
17885088Sab196087 MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i],
17895088Sab196087 name) > sizeof (path_buf))
17905088Sab196087 load_module_err(moddef, NULL, NULL,
17915088Sab196087 MSG_INTL(MSG_ERR_PATHTOOLONG),
17925088Sab196087 state.modpath.seg[i], name, NULL, NULL);
17935088Sab196087 hdl = load_module_dlopen(path, moddef, 0);
17945088Sab196087 }
17955088Sab196087 if (must_exist && (hdl == NULL))
17965088Sab196087 load_module_err(moddef, NULL, NULL,
17975088Sab196087 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
17985088Sab196087 }
17995088Sab196087
18005088Sab196087 if (hdl == NULL) {
18015088Sab196087 free(moddef);
18025088Sab196087 return (NULL);
18035088Sab196087 }
18045088Sab196087
18055088Sab196087 if (state.elf.elfclass == ELFCLASS32) {
18065088Sab196087 init_func = (elfedit_init_func_t *)
18075088Sab196087 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32));
18085088Sab196087 } else {
18095088Sab196087 init_func = (elfedit_init_func_t *)
18105088Sab196087 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64));
18115088Sab196087 }
18125088Sab196087 if (init_func == NULL)
18135088Sab196087 load_module_err(moddef, hdl, path,
18145088Sab196087 MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL);
18155088Sab196087
18165088Sab196087 /*
18175088Sab196087 * Note that the init function will be passing us an
18185088Sab196087 * elfedit[32|64]_module_t pointer, which we cast to the
18195088Sab196087 * generic module pointer type in order to be able to manage
18205088Sab196087 * either type with one set of code.
18215088Sab196087 */
18225088Sab196087 if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT)))
18235088Sab196087 load_module_err(moddef, hdl, path,
18245088Sab196087 MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL);
18255088Sab196087
18265088Sab196087 /*
18275088Sab196087 * Enforce some rules, to help module developers:
18285088Sab196087 * - The primary name of a command must not be
18295088Sab196087 * the empty string ("").
18305088Sab196087 * - Options must start with a '-' followed by at least
18315088Sab196087 * one character.
18325088Sab196087 * - Arguments and options must be well formed.
18335088Sab196087 */
18345088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
18355088Sab196087 if (**cmd->cmd_name == '\0')
18365088Sab196087 load_module_err(moddef, hdl, path,
18375088Sab196087 MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name,
18385088Sab196087 NULL, NULL, NULL);
18395088Sab196087
18405088Sab196087 if (cmd->cmd_args != NULL)
18415088Sab196087 validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name,
18425088Sab196087 cmd->cmd_name[0], hdl, path);
18435088Sab196087 if (cmd->cmd_opt != NULL)
18445088Sab196087 validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name,
18455088Sab196087 cmd->cmd_name[0], hdl, path);
18465088Sab196087 }
18475088Sab196087
18485088Sab196087 /*
18495088Sab196087 * Check the name the module provides. How we handle this depends
18505088Sab196087 * on whether the path is absolute or the result of a path search.
18515088Sab196087 */
18525088Sab196087 if (is_abs_path) {
18535088Sab196087 MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef);
18545088Sab196087
18555088Sab196087 if (old_moddef != NULL) { /* Replace existing */
18565088Sab196087 free(moddef); /* Rare case: Don't need it */
18575088Sab196087 /*
18585088Sab196087 * Be sure we don't unload builtin modules!
18595088Sab196087 * These have a NULL dl_hdl field.
18605088Sab196087 */
18615088Sab196087 if (old_moddef->ml_dl_hdl == NULL)
18625088Sab196087 load_module_err(NULL, hdl, path,
18635088Sab196087 MSG_INTL(MSG_ERR_CNTULSMOD),
18645088Sab196087 old_moddef->ml_mod->mod_name, NULL,
18655088Sab196087 NULL, NULL);
18665088Sab196087
18675088Sab196087 /* Unload existing */
18685088Sab196087 if (dlclose(old_moddef->ml_dl_hdl) != 0)
18695088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
18705088Sab196087 MSG_INTL(MSG_ERR_CNTDLCLOSE),
18715088Sab196087 old_moddef->ml_path, dlerror());
18725088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
18735088Sab196087 MSG_INTL(MSG_DEBUG_MODUNLOAD),
18745088Sab196087 old_moddef->ml_mod->mod_name, old_moddef->ml_path);
18755088Sab196087 old_moddef->ml_mod = mod;
18765088Sab196087 old_moddef->ml_dl_hdl = hdl;
18775088Sab196087 (void) strlcpy((char *)old_moddef->ml_path, path,
18785088Sab196087 PATH_MAX + 1);
18795088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
18805088Sab196087 MSG_INTL(MSG_DEBUG_MODLOAD),
18815088Sab196087 old_moddef->ml_mod->mod_name, path);
18825088Sab196087 return (old_moddef->ml_mod);
18835088Sab196087 }
18845088Sab196087 /*
18855088Sab196087 * insdef now contains the insertion point for the absolute
18865088Sab196087 * path case.
18875088Sab196087 */
18885088Sab196087 } else {
18895088Sab196087 /* If the names don't match, then error */
18905088Sab196087 if (strcasecmp(name, mod->mod_name) != 0)
18915088Sab196087 load_module_err(moddef, hdl, path,
18925088Sab196087 MSG_INTL(MSG_ERR_BADMODNAME),
18935088Sab196087 mod->mod_name, name, path, NULL);
18945088Sab196087 }
18955088Sab196087
18965088Sab196087 /*
18975088Sab196087 * Link module into the module list. If insdef is NULL,
18985088Sab196087 * it goes at the head. If insdef is non-NULL, it goes immediately
18995088Sab196087 * after
19005088Sab196087 */
19015088Sab196087 if (insdef == NULL) {
19025088Sab196087 moddef->ml_next = state.modlist;
19035088Sab196087 state.modlist = moddef;
19045088Sab196087 } else {
19055088Sab196087 moddef->ml_next = insdef->ml_next;
19065088Sab196087 insdef->ml_next = moddef;
19075088Sab196087 }
19085088Sab196087 moddef->ml_mod = mod;
19095088Sab196087 moddef->ml_dl_hdl = hdl;
19105088Sab196087 (void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1);
19115088Sab196087
19125088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD),
19135088Sab196087 moddef->ml_mod->mod_name, path);
19145088Sab196087
19155088Sab196087 return (moddef->ml_mod);
19165088Sab196087 }
19175088Sab196087
19185088Sab196087
19195088Sab196087 /*
19205088Sab196087 * Unload the specified module
19215088Sab196087 */
19225088Sab196087 void
elfedit_unload_module(const char * name)19235088Sab196087 elfedit_unload_module(const char *name)
19245088Sab196087 {
19255088Sab196087 MODLIST_T *moddef, *insdef;
19265088Sab196087
19275088Sab196087 moddef = module_loaded(name, &insdef);
19285088Sab196087 if (moddef == NULL)
19295088Sab196087 return;
19305088Sab196087
19315088Sab196087 /* Built in modules cannot be unloaded. They have a NULL dl_hdl field */
19325088Sab196087 if (moddef->ml_dl_hdl == NULL)
19335088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD),
19345088Sab196087 moddef->ml_mod->mod_name);
19355088Sab196087
19365088Sab196087 /*
19375088Sab196087 * When we unload it, the name string goes with it. So
19385088Sab196087 * announce it while we still can without having to make a copy.
19395088Sab196087 */
19405088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD),
19415088Sab196087 moddef->ml_mod->mod_name, moddef->ml_path);
19425088Sab196087
19435088Sab196087 /*
19445088Sab196087 * Close it before going further. On failure, we'll jump, and the
19455088Sab196087 * record will remain in the module list. On success,
19465088Sab196087 * we'll retain control, and can safely remove it.
19475088Sab196087 */
19485088Sab196087 if (dlclose(moddef->ml_dl_hdl) != 0)
19495088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
19505088Sab196087 moddef->ml_path, dlerror());
19515088Sab196087
19525088Sab196087 /* Unlink the record from the module list */
19535088Sab196087 if (insdef == NULL)
19545088Sab196087 state.modlist = moddef->ml_next;
19555088Sab196087 else
19565088Sab196087 insdef->ml_next = moddef->ml_next;
19575088Sab196087
19585088Sab196087 /* Release the memory */
19595088Sab196087 free(moddef);
19605088Sab196087 }
19615088Sab196087
19625088Sab196087
19635088Sab196087 /*
19645088Sab196087 * Load all sharable objects found in the specified directory.
19655088Sab196087 *
19665088Sab196087 * entry:
19675088Sab196087 * dirpath - Path of directory to process.
19685088Sab196087 * must_exist - If True, it is an error if diropen() fails to open
19695088Sab196087 * the given directory. Of False, we quietly ignore it and return.
19705088Sab196087 * abs_path - If True, files are loaded using their literal paths.
19715088Sab196087 * If False, their module name is extracted from the dirpath
19725088Sab196087 * and a path based search is used to locate it.
19735088Sab196087 */
19745088Sab196087 void
elfedit_load_moddir(const char * dirpath,int must_exist,int abs_path)19755088Sab196087 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path)
19765088Sab196087 {
19775088Sab196087 char path[PATH_MAX + 1];
19785088Sab196087 DIR *dir;
19795088Sab196087 struct dirent *dp;
19805088Sab196087 const char *tail;
19815088Sab196087
19825088Sab196087 dir = opendir(dirpath);
19835088Sab196087 if (dir == NULL) {
19845088Sab196087 int err = errno;
19855088Sab196087
19865088Sab196087 if (!must_exist && (err == ENOENT))
19875088Sab196087 return;
19885088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR),
19895088Sab196087 dirpath, strerror(err));
19905088Sab196087 /*NOTREACHED*/
19915088Sab196087 }
19925088Sab196087
19935088Sab196087 while (dp = readdir(dir)) {
19945088Sab196087 if ((tail = path_is_so(dp->d_name)) != NULL) {
19955088Sab196087 if (abs_path) {
19965088Sab196087 (void) snprintf(path, sizeof (path),
19975088Sab196087 MSG_ORIG(MSG_FMT_BLDPATH), dirpath,
19985088Sab196087 dp->d_name);
19995088Sab196087 } else {
20005088Sab196087 (void) elfedit_basename(dp->d_name, tail,
20015088Sab196087 path, sizeof (path));
20025088Sab196087 }
20035088Sab196087 (void) elfedit_load_module(path, must_exist, 1);
20045088Sab196087 }
20055088Sab196087 }
20065088Sab196087 (void) closedir(dir);
20075088Sab196087 }
20085088Sab196087
20095088Sab196087
20105088Sab196087 /*
20115088Sab196087 * Follow the module load path, and load the first module found for each
20125088Sab196087 * given name.
20135088Sab196087 */
20145088Sab196087 void
elfedit_load_modpath(void)20155088Sab196087 elfedit_load_modpath(void)
20165088Sab196087 {
20175088Sab196087 size_t i;
20185088Sab196087
20195088Sab196087 for (i = 0; i < state.modpath.n; i++)
20205088Sab196087 elfedit_load_moddir(state.modpath.seg[i], 0, 0);
20215088Sab196087 }
20225088Sab196087
20235088Sab196087 /*
20245088Sab196087 * Given a module definition, look for the specified command.
20255088Sab196087 * Returns the command if found, and NULL otherwise.
20265088Sab196087 */
20275088Sab196087 static elfeditGC_cmd_t *
find_cmd(elfeditGC_module_t * mod,const char * name)20285088Sab196087 find_cmd(elfeditGC_module_t *mod, const char *name)
20295088Sab196087 {
20305088Sab196087 elfeditGC_cmd_t *cmd;
20315088Sab196087 const char **cmd_name;
20325088Sab196087
20335088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
20345088Sab196087 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
20355088Sab196087 if (strcasecmp(name, *cmd_name) == 0) {
20365088Sab196087 if (cmd_name != cmd->cmd_name)
20375088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
20385088Sab196087 MSG_INTL(MSG_DEBUG_CMDALIAS),
20395088Sab196087 mod->mod_name, *cmd_name,
20405088Sab196087 mod->mod_name, *cmd->cmd_name);
20415088Sab196087 return (cmd);
20425088Sab196087 }
20435088Sab196087
20445088Sab196087 return (NULL);
20455088Sab196087 }
20465088Sab196087
20475088Sab196087
20485088Sab196087 /*
20495088Sab196087 * Given a command name, return its command definition.
20505088Sab196087 *
20515088Sab196087 * entry:
20525088Sab196087 * name - Command to be looked up
20535088Sab196087 * must_exist - If True, we consider it to be an error if the command
20545088Sab196087 * does not exist. If False, NULL is returned quietly in
20555088Sab196087 * this case.
20565088Sab196087 * mod_ret - NULL, or address of a variable to receive the
20575088Sab196087 * module definition block of the module containing
20585088Sab196087 * the command.
20595088Sab196087 *
20605088Sab196087 * exit:
20615088Sab196087 * On success, returns a pointer to the command definition, and
20625088Sab196087 * if mod_ret is non-NULL, *mod_ret receives a pointer to the
20635088Sab196087 * module definition. On failure, must_exist determines the
20645088Sab196087 * action taken: If must_exist is True, an error is issued and
20655088Sab196087 * control does not return to the caller. If must_exist is False,
20665088Sab196087 * NULL is quietly returned.
20675088Sab196087 *
20685088Sab196087 * note:
20695088Sab196087 * A ':' in name is used to delimit the module and command names.
20705088Sab196087 * If it is omitted, or if it is the first non-whitespace character
20715088Sab196087 * in the name, then the built in sys: module is implied.
20725088Sab196087 */
20735088Sab196087 elfeditGC_cmd_t *
elfedit_find_command(const char * name,int must_exist,elfeditGC_module_t ** mod_ret)20745088Sab196087 elfedit_find_command(const char *name, int must_exist,
20755088Sab196087 elfeditGC_module_t **mod_ret)
20765088Sab196087 {
20775088Sab196087 elfeditGC_module_t *mod;
20785088Sab196087 const char *mod_str;
20795088Sab196087 const char *cmd_str;
20805088Sab196087 char mod_buf[ELFEDIT_MAXMODNAM + 1];
20815088Sab196087 size_t n;
20825088Sab196087 elfeditGC_cmd_t *cmd;
20835088Sab196087
20845088Sab196087
20855088Sab196087 cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON));
20865088Sab196087 if (cmd_str == NULL) { /* No module name -> sys: */
20875088Sab196087 mod_str = MSG_ORIG(MSG_MOD_SYS);
20885088Sab196087 cmd_str = name;
20895088Sab196087 } else if (cmd_str == name) { /* Empty module name -> sys: */
20905088Sab196087 mod_str = MSG_ORIG(MSG_MOD_SYS);
20915088Sab196087 cmd_str++; /* Skip the colon */
20925088Sab196087 } else { /* Have both module and command */
20935088Sab196087 n = cmd_str - name;
20945088Sab196087 if (n >= sizeof (mod_buf)) {
20955088Sab196087 if (must_exist)
20965088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
20975088Sab196087 MSG_INTL(MSG_ERR_MODNAMTOOLONG), name);
20985088Sab196087 return (NULL);
20995088Sab196087 }
21005088Sab196087 (void) strlcpy(mod_buf, name, n + 1);
21015088Sab196087 mod_str = mod_buf;
21025088Sab196087 cmd_str++;
21035088Sab196087 }
21045088Sab196087
21055088Sab196087 /* Lookup/load module. Won't return on error */
21065088Sab196087 mod = elfedit_load_module(mod_str, must_exist, 0);
21075088Sab196087 if (mod == NULL)
21085088Sab196087 return (NULL);
21095088Sab196087
21105088Sab196087 /* Locate the command */
21115088Sab196087 cmd = find_cmd(mod, cmd_str);
21125088Sab196087 if (cmd == NULL) {
21135088Sab196087 if (must_exist) {
21145088Sab196087 /*
21155088Sab196087 * Catch empty command in order to provide
21165088Sab196087 * a better error message.
21175088Sab196087 */
21185088Sab196087 if (*cmd_str == '\0') {
21195088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
21205088Sab196087 MSG_INTL(MSG_ERR_MODNOCMD), mod_str);
21215088Sab196087 } else {
21225088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
21235088Sab196087 MSG_INTL(MSG_ERR_UNRECCMD),
21245088Sab196087 mod_str, cmd_str);
21255088Sab196087 }
21265088Sab196087 }
21275088Sab196087 } else {
21285088Sab196087 if (mod_ret != NULL)
21295088Sab196087 *mod_ret = mod;
21305088Sab196087 }
21315088Sab196087 return (cmd);
21325088Sab196087 }
21335088Sab196087
21345088Sab196087
21355088Sab196087 /*
21365088Sab196087 * Release all user command blocks found on state.ucmd
21375088Sab196087 */
21385088Sab196087 static void
free_user_cmds(void)21395088Sab196087 free_user_cmds(void)
21405088Sab196087 {
21415088Sab196087 USER_CMD_T *next;
21425088Sab196087
21435088Sab196087 while (state.ucmd.list) {
21445088Sab196087 next = state.ucmd.list->ucmd_next;
21455088Sab196087 free(state.ucmd.list);
21465088Sab196087 state.ucmd.list = next;
21475088Sab196087 }
21485088Sab196087 state.ucmd.tail = NULL;
21495088Sab196087 state.ucmd.n = 0;
21505088Sab196087 state.cur_cmd = NULL;
21515088Sab196087 }
21525088Sab196087
21535088Sab196087
21545088Sab196087 /*
21555088Sab196087 * Process all user command blocks found on state.ucmd, and then
21565088Sab196087 * remove them from the list.
21575088Sab196087 */
21585088Sab196087 static void
dispatch_user_cmds()21595088Sab196087 dispatch_user_cmds()
21605088Sab196087 {
21615088Sab196087 USER_CMD_T *ucmd;
21625088Sab196087 elfedit_cmdret_t cmd_ret;
21635088Sab196087
21645088Sab196087 ucmd = state.ucmd.list;
21655088Sab196087 if (ucmd) {
21665088Sab196087 /* Do them, in order */
21675088Sab196087 for (; ucmd; ucmd = ucmd->ucmd_next) {
21685088Sab196087 state.cur_cmd = ucmd;
21695088Sab196087 if (!state.msg_jbuf.active)
21705088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
21715088Sab196087 MSG_INTL(MSG_DEBUG_EXECCMD),
21725088Sab196087 ucmd->ucmd_orig_str);
21735088Sab196087 /*
21745088Sab196087 * The cmd_func field is the generic definition.
21755088Sab196087 * We need to cast it to the type that matches
21765088Sab196087 * the proper ELFCLASS before calling it.
21775088Sab196087 */
21785088Sab196087 if (state.elf.elfclass == ELFCLASS32) {
21795088Sab196087 elfedit32_cmd_func_t *cmd_func =
21805088Sab196087 (elfedit32_cmd_func_t *)
21815088Sab196087 ucmd->ucmd_cmd->cmd_func;
21825088Sab196087
21835088Sab196087 cmd_ret = (* cmd_func)(state.elf.obj_state.s32,
21845088Sab196087 ucmd->ucmd_argc, ucmd->ucmd_argv);
21855088Sab196087 } else {
21865088Sab196087 elfedit64_cmd_func_t *cmd_func =
21875088Sab196087 (elfedit64_cmd_func_t *)
21885088Sab196087 ucmd->ucmd_cmd->cmd_func;
21895088Sab196087
21905088Sab196087 cmd_ret = (* cmd_func)(state.elf.obj_state.s64,
21915088Sab196087 ucmd->ucmd_argc, ucmd->ucmd_argv);
21925088Sab196087 }
21935088Sab196087 state.cur_cmd = NULL;
21945088Sab196087 /* If a pager was started, wrap it up */
21955088Sab196087 elfedit_pager_cleanup();
21965088Sab196087
21975088Sab196087 switch (cmd_ret) {
21989273SAli.Bahrami@Sun.COM case ELFEDIT_CMDRET_MOD_OS_MACH:
21999273SAli.Bahrami@Sun.COM /*
22009273SAli.Bahrami@Sun.COM * Inform the elfconst module that the machine
22019273SAli.Bahrami@Sun.COM * or osabi has has changed. It may be necessary
22029273SAli.Bahrami@Sun.COM * to fetch new strings from libconv.
22039273SAli.Bahrami@Sun.COM */
22049273SAli.Bahrami@Sun.COM state.elf.elfconst_ehdr_change = 1;
22059273SAli.Bahrami@Sun.COM /*FALLTHROUGH*/
22065088Sab196087 case ELFEDIT_CMDRET_MOD:
22075088Sab196087 /*
22085088Sab196087 * Command modified the output ELF image,
22095088Sab196087 * mark the file as needing a flush to disk.
22105088Sab196087 */
22115088Sab196087 state.file.dirty = 1;
22125088Sab196087 break;
22135088Sab196087 case ELFEDIT_CMDRET_FLUSH:
22145088Sab196087 /*
22155088Sab196087 * Command flushed the output file,
22165088Sab196087 * clear the dirty bit.
22175088Sab196087 */
22185088Sab196087 state.file.dirty = 0;
22195088Sab196087 }
22205088Sab196087 }
22215088Sab196087 free_user_cmds();
22225088Sab196087 }
22235088Sab196087 }
22245088Sab196087
22255088Sab196087
22265088Sab196087 /*
22275088Sab196087 * Prepare a GETTOK_STATE struct for gettok().
22285088Sab196087 *
22295088Sab196087 * entry:
22305088Sab196087 * gettok_state - gettok state block to use
22315088Sab196087 * str - Writable buffer to tokenize. Note that gettok()
22325088Sab196087 * is allowed to change the contents of this buffer.
22335088Sab196087 * inc_null_final - If the line ends in whitespace instead of
22345088Sab196087 * immediately hitting a NULL, and inc_null_final is TRUE,
22355088Sab196087 * then a null final token is generated. Otherwise trailing
22365088Sab196087 * whitespace is ignored.
22375088Sab196087 */
22385088Sab196087 static void
gettok_init(GETTOK_STATE * gettok_state,char * buf,int inc_null_final)22395088Sab196087 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final)
22405088Sab196087 {
22415088Sab196087 gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf;
22425088Sab196087 gettok_state->gtok_inc_null_final = inc_null_final;
22435088Sab196087 gettok_state->gtok_null_seen = 0;
22445088Sab196087 }
22455088Sab196087
22465088Sab196087
22475088Sab196087 /*
22485088Sab196087 * Locate the next token from the buffer.
22495088Sab196087 *
22505088Sab196087 * entry:
22515088Sab196087 * gettok_state - State of gettok() operation. Initialized
22525088Sab196087 * by gettok_init(), and passed to gettok().
22535088Sab196087 *
22545088Sab196087 * exit:
22555088Sab196087 * If a token is found, gettok_state->gtok_last_token is filled in
22565088Sab196087 * with the details and True (1) is returned. If no token is found,
22575088Sab196087 * False (1) is returned, and the contents of
22585088Sab196087 * gettok_state->gtok_last_token are undefined.
22595088Sab196087 *
22605088Sab196087 * note:
22615088Sab196087 * - The token returned references the memory in gettok_state->gtok_buf.
22625088Sab196087 * The caller should not modify the buffer until all such
22635088Sab196087 * pointers have been discarded.
22645088Sab196087 * - This routine will modify the contents of gettok_state->gtok_buf
22655088Sab196087 * as necessary to remove quotes and eliminate escape
22665088Sab196087 * (\)characters.
22675088Sab196087 */
22685088Sab196087 static int
gettok(GETTOK_STATE * gettok_state)22695088Sab196087 gettok(GETTOK_STATE *gettok_state)
22705088Sab196087 {
22715088Sab196087 char *str = gettok_state->gtok_cur_buf;
22725088Sab196087 char *look;
22735088Sab196087 int quote_ch = '\0';
22745088Sab196087
22755088Sab196087 /* Skip leading whitespace */
22765088Sab196087 while (isspace(*str))
22775088Sab196087 str++;
22785088Sab196087
22795088Sab196087 if (*str == '\0') {
22805088Sab196087 /*
22815088Sab196087 * If user requested it, and there was whitespace at the
22825088Sab196087 * end, then generate one last null token.
22835088Sab196087 */
22845088Sab196087 if (gettok_state->gtok_inc_null_final &&
22855088Sab196087 !gettok_state->gtok_null_seen) {
22865088Sab196087 gettok_state->gtok_inc_null_final = 0;
22875088Sab196087 gettok_state->gtok_null_seen = 1;
22885088Sab196087 gettok_state->gtok_last_token.tok_str = str;
22895088Sab196087 gettok_state->gtok_last_token.tok_len = 0;
22905088Sab196087 gettok_state->gtok_last_token.tok_line_off =
22915088Sab196087 str - gettok_state->gtok_buf;
22925088Sab196087 return (1);
22935088Sab196087 }
22945088Sab196087 gettok_state->gtok_null_seen = 1;
22955088Sab196087 return (0);
22965088Sab196087 }
22975088Sab196087
22985088Sab196087 /*
22995088Sab196087 * Read token: The standard delimiter is whitespace, but
23005088Sab196087 * we honor either single or double quotes. Also, we honor
23015088Sab196087 * backslash escapes.
23025088Sab196087 */
23035088Sab196087 gettok_state->gtok_last_token.tok_str = look = str;
23045088Sab196087 gettok_state->gtok_last_token.tok_line_off =
23055088Sab196087 look - gettok_state->gtok_buf;
23065088Sab196087 for (; *look; look++) {
23075088Sab196087 if (*look == quote_ch) { /* Terminates active quote */
23085088Sab196087 quote_ch = '\0';
23095088Sab196087 continue;
23105088Sab196087 }
23115088Sab196087
23125088Sab196087 if (quote_ch == '\0') { /* No quote currently active */
23135088Sab196087 if ((*look == '\'') || (*look == '"')) {
23145088Sab196087 quote_ch = *look; /* New active quote */
23155088Sab196087 continue;
23165088Sab196087 }
23175088Sab196087 if (isspace(*look))
23185088Sab196087 break;
23195088Sab196087 }
23205088Sab196087
23215892Sab196087 /*
23225892Sab196087 * The semantics of the backslash character depends on
23235892Sab196087 * the quote style in use:
23245892Sab196087 * - Within single quotes, backslash is not
23255892Sab196087 * an escape character, and is taken literally.
23265892Sab196087 * - If outside of quotes, the backslash is an escape
23275892Sab196087 * character. The backslash is ignored and the
23285892Sab196087 * following character is taken literally, losing
23295892Sab196087 * any special properties it normally has.
23305892Sab196087 * - Within double quotes, backslash works like a
23315892Sab196087 * backslash escape within a C literal. Certain
23325892Sab196087 * escapes are recognized and replaced with their
23335892Sab196087 * special character. Any others are an error.
23345892Sab196087 */
23355088Sab196087 if (*look == '\\') {
23365892Sab196087 if (quote_ch == '\'') {
23375892Sab196087 *str++ = *look;
23385892Sab196087 continue;
23395892Sab196087 }
23405892Sab196087
23415088Sab196087 look++;
23425892Sab196087 if (*look == '\0') { /* Esc applied to NULL term? */
23435892Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
23445892Sab196087 MSG_INTL(MSG_ERR_ESCEOL));
23455892Sab196087 /*NOTREACHED*/
23465892Sab196087 }
23475892Sab196087
23485892Sab196087 if (quote_ch == '"') {
2349*11734SAli.Bahrami@Sun.COM int ch = conv_translate_c_esc(&look);
2350*11734SAli.Bahrami@Sun.COM
2351*11734SAli.Bahrami@Sun.COM if (ch == -1)
2352*11734SAli.Bahrami@Sun.COM elfedit_msg(ELFEDIT_MSG_ERR,
2353*11734SAli.Bahrami@Sun.COM MSG_INTL(MSG_ERR_BADCESC), *look);
2354*11734SAli.Bahrami@Sun.COM *str++ = ch;
23555892Sab196087 look--; /* for() will advance by 1 */
23565892Sab196087 continue;
23575892Sab196087 }
23585088Sab196087 }
23595088Sab196087
23605088Sab196087 if (look != str)
23615088Sab196087 *str = *look;
23625088Sab196087 str++;
23635088Sab196087 }
23645892Sab196087
23655892Sab196087 /* Don't allow unterminated quoted tokens */
23665892Sab196087 if (quote_ch != '\0')
23675892Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNTERMQUOTE),
23685892Sab196087 quote_ch);
23695892Sab196087
23705088Sab196087 gettok_state->gtok_last_token.tok_len = str -
23715088Sab196087 gettok_state->gtok_last_token.tok_str;
23725088Sab196087 gettok_state->gtok_null_seen = *look == '\0';
23735088Sab196087 if (!gettok_state->gtok_null_seen)
23745088Sab196087 look++;
23755088Sab196087 *str = '\0';
23765088Sab196087 gettok_state->gtok_cur_buf = look;
23775088Sab196087
23785892Sab196087 #ifdef DEBUG_GETTOK
23795892Sab196087 printf("GETTOK >");
23805892Sab196087 elfedit_str_to_c_literal(gettok_state->gtok_last_token.tok_str,
23815892Sab196087 elfedit_write);
23825892Sab196087 printf("< \tlen(%d) offset(%d)\n",
23835088Sab196087 gettok_state->gtok_last_token.tok_len,
23845088Sab196087 gettok_state->gtok_last_token.tok_line_off);
23855088Sab196087 #endif
23865088Sab196087
23875088Sab196087 return (1);
23885088Sab196087 }
23895088Sab196087
23905088Sab196087
23915088Sab196087 /*
23925088Sab196087 * Tokenize the user command string, and return a pointer to the
23935088Sab196087 * TOK_STATE buffer maintained by this function. That buffer contains
23945088Sab196087 * the tokenized strings.
23955088Sab196087 *
23965088Sab196087 * entry:
23975088Sab196087 * user_cmd_str - String to tokenize
23985088Sab196087 * len - # of characters in user_cmd_str to examine. If
23995088Sab196087 * (len < 0), then the complete string is processed
24005088Sab196087 * stopping with the NULL termination. Otherwise,
24015088Sab196087 * processing stops after len characters, and any
24025088Sab196087 * remaining characters are ignored.
24035088Sab196087 * inc_null_final - If True, and if user_cmd_str has whitespace
24045088Sab196087 * at the end following the last non-null token, then
24055088Sab196087 * a final null token will be included. If False, null
24065088Sab196087 * tokens are ignored.
24075088Sab196087 *
24085088Sab196087 * note:
24095088Sab196087 * This routine returns pointers to internally allocated memory.
24105088Sab196087 * The caller must not alter anything contained in the TOK_STATE
24115088Sab196087 * buffer returned. Furthermore, the the contents of TOK_STATE
24125088Sab196087 * are only valid until the next call to tokenize_user_cmd().
24135088Sab196087 */
24145088Sab196087 static TOK_STATE *
tokenize_user_cmd(const char * user_cmd_str,size_t len,int inc_null_final)24155088Sab196087 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final)
24165088Sab196087 {
24175088Sab196087 #define INITIAL_TOK_ALLOC 5
24185088Sab196087
24195088Sab196087 /*
24205088Sab196087 * As we parse the user command, we need temporary space to
24215088Sab196087 * hold the tokens. We do this by dynamically allocating a string
24225088Sab196087 * buffer and a token array, and doubling them as necessary. This
24235088Sab196087 * is a single threaded application, so static variables suffice.
24245088Sab196087 */
24255088Sab196087 static STRBUF str;
24265088Sab196087 static TOK_STATE tokst;
24275088Sab196087
24285088Sab196087 GETTOK_STATE gettok_state;
24295088Sab196087 size_t n;
24305088Sab196087
24315088Sab196087 /*
24325088Sab196087 * Make a copy we can modify. If (len == 0), take the entire
24335088Sab196087 * string. Otherwise limit it to the specified length.
24345088Sab196087 */
24355088Sab196087 tokst.tokst_cmd_len = strlen(user_cmd_str);
24365088Sab196087 if ((len > 0) && (len < tokst.tokst_cmd_len))
24375088Sab196087 tokst.tokst_cmd_len = len;
24385088Sab196087 tokst.tokst_cmd_len++; /* Room for NULL termination */
24395088Sab196087 strbuf_ensure_size(&str, tokst.tokst_cmd_len);
24405088Sab196087 (void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len);
24415088Sab196087
24425088Sab196087 /* Trim off any newline character that might be present */
24435088Sab196087 if ((tokst.tokst_cmd_len > 1) &&
24445088Sab196087 (str.buf[tokst.tokst_cmd_len - 2] == '\n')) {
24455088Sab196087 tokst.tokst_cmd_len--;
24465088Sab196087 str.buf[tokst.tokst_cmd_len - 1] = '\0';
24475088Sab196087 }
24485088Sab196087
24495088Sab196087 /* Tokenize the user command string into tok struct */
24505088Sab196087 gettok_init(&gettok_state, str.buf, inc_null_final);
24515088Sab196087 tokst.tokst_str_size = 0; /* Space needed for token strings */
24525088Sab196087 for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0;
24535088Sab196087 tokst.tokst_cnt++) {
24545088Sab196087 /* If we need more room, expand the token buffer */
24555088Sab196087 if (tokst.tokst_cnt >= tokst.tokst_bufsize) {
24565088Sab196087 n = (tokst.tokst_bufsize == 0) ?
24575088Sab196087 INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2);
24585088Sab196087 tokst.tokst_buf = elfedit_realloc(
24595088Sab196087 MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf,
24605088Sab196087 n * sizeof (*tokst.tokst_buf));
24615088Sab196087 tokst.tokst_bufsize = n;
24625088Sab196087 }
24635088Sab196087 tokst.tokst_str_size +=
24645088Sab196087 gettok_state.gtok_last_token.tok_len + 1;
24655088Sab196087 tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token;
24665088Sab196087 }
24675088Sab196087 /* fold the command token to lowercase */
24685088Sab196087 if (tokst.tokst_cnt > 0) {
24695088Sab196087 char *s;
24705088Sab196087
24715088Sab196087 for (s = tokst.tokst_buf[0].tok_str; *s; s++)
24725088Sab196087 if (isupper(*s))
24735088Sab196087 *s = tolower(*s);
24745088Sab196087 }
24755088Sab196087
24765088Sab196087 return (&tokst);
24775088Sab196087
24785088Sab196087 #undef INITIAL_TOK_ALLOC
24795088Sab196087 }
24805088Sab196087
24815088Sab196087
24825088Sab196087 /*
24835088Sab196087 * Parse the user command string, and put an entry for it at the end
24845088Sab196087 * of state.ucmd.
24855088Sab196087 */
24865088Sab196087 static void
parse_user_cmd(const char * user_cmd_str)24875088Sab196087 parse_user_cmd(const char *user_cmd_str)
24885088Sab196087 {
24895088Sab196087 TOK_STATE *tokst;
24905088Sab196087 char *s;
24915088Sab196087 size_t n;
24925088Sab196087 size_t len;
24935088Sab196087 USER_CMD_T *ucmd;
24945088Sab196087 elfeditGC_module_t *mod;
24955088Sab196087 elfeditGC_cmd_t *cmd;
24965088Sab196087
24975088Sab196087 /*
24985088Sab196087 * Break it into tokens. If there are none, then it is
24995088Sab196087 * an empty command and is ignored.
25005088Sab196087 */
25015088Sab196087 tokst = tokenize_user_cmd(user_cmd_str, -1, 0);
25025088Sab196087 if (tokst->tokst_cnt == 0)
25035088Sab196087 return;
25045088Sab196087
25055088Sab196087 /* Find the command. Won't return on error */
25065088Sab196087 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod);
25075088Sab196087
25085088Sab196087 /*
25095088Sab196087 * If there is no ELF file being edited, then only commands
25105088Sab196087 * from the sys: module are allowed.
25115088Sab196087 */
25125088Sab196087 if ((state.file.present == 0) &&
25135088Sab196087 (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0))
25145088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY),
25155088Sab196087 mod->mod_name, cmd->cmd_name[0]);
25165088Sab196087
25175088Sab196087
25185088Sab196087 /* Allocate, fill in, and insert a USER_CMD_T block */
25195088Sab196087 n = S_DROUND(sizeof (USER_CMD_T));
25205088Sab196087 ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD),
25215088Sab196087 n + (sizeof (char *) * (tokst->tokst_cnt - 1)) +
25225088Sab196087 tokst->tokst_cmd_len + tokst->tokst_str_size);
25235088Sab196087 ucmd->ucmd_next = NULL;
25245088Sab196087 ucmd->ucmd_argc = tokst->tokst_cnt - 1;
25255088Sab196087 /*LINTED E_BAD_PTR_CAST_ALIGN*/
25265088Sab196087 ucmd->ucmd_argv = (const char **)(n + (char *)ucmd);
25275088Sab196087 ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc);
25285088Sab196087 (void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len);
25295088Sab196087 ucmd->ucmd_mod = mod;
25305088Sab196087 ucmd->ucmd_cmd = cmd;
25315088Sab196087 ucmd->ucmd_ostyle_set = 0;
25325088Sab196087 s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len;
25335088Sab196087 for (n = 1; n < tokst->tokst_cnt; n++) {
25345088Sab196087 len = tokst->tokst_buf[n].tok_len + 1;
25355088Sab196087 ucmd->ucmd_argv[n - 1] = s;
25365088Sab196087 (void) strncpy(s, tokst->tokst_buf[n].tok_str, len);
25375088Sab196087 s += len;
25385088Sab196087 }
25395088Sab196087 if (state.ucmd.list == NULL) {
25405088Sab196087 state.ucmd.list = state.ucmd.tail = ucmd;
25415088Sab196087 } else {
25425088Sab196087 state.ucmd.tail->ucmd_next = ucmd;
25435088Sab196087 state.ucmd.tail = ucmd;
25445088Sab196087 }
25455088Sab196087 state.ucmd.n++;
25465088Sab196087 }
25475088Sab196087
25485088Sab196087
25495088Sab196087 /*
25505088Sab196087 * Copy infile to a new file with the name given by outfile.
25515088Sab196087 */
25525088Sab196087 static void
create_outfile(const char * infile,const char * outfile)25535088Sab196087 create_outfile(const char *infile, const char *outfile)
25545088Sab196087 {
25555088Sab196087 pid_t pid;
25565088Sab196087 int statloc;
25575088Sab196087 struct stat statbuf;
25585088Sab196087
25595088Sab196087
25605088Sab196087 pid = fork();
25615088Sab196087 switch (pid) {
25625088Sab196087 case -1: /* Unable to create process */
25635088Sab196087 {
25645088Sab196087 int err = errno;
25655088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK),
25665088Sab196087 strerror(err));
25675088Sab196087 }
25685088Sab196087 /*NOTREACHED*/
25695088Sab196087 return;
25705088Sab196087
25715088Sab196087 case 0:
25725088Sab196087 (void) execl(MSG_ORIG(MSG_STR_BINCP),
25735088Sab196087 MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL);
25745088Sab196087 /*
25755088Sab196087 * exec() only returns on error. This is the child process,
25765088Sab196087 * so we want to stay away from the usual error mechanism
25775088Sab196087 * and handle things directly.
25785088Sab196087 */
25795088Sab196087 {
25805088Sab196087 int err = errno;
25815088Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC),
25825088Sab196087 MSG_ORIG(MSG_STR_ELFEDIT),
25835088Sab196087 MSG_ORIG(MSG_STR_BINCP), strerror(err));
25845088Sab196087 }
25855088Sab196087 exit(1);
25865088Sab196087 /*NOTREACHED*/
25875088Sab196087 }
25885088Sab196087
25895088Sab196087 /* This is the parent: Wait for the child to terminate */
25905088Sab196087 if (waitpid(pid, &statloc, 0) != pid) {
25915088Sab196087 int err = errno;
25925088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT),
25935088Sab196087 strerror(err));
25945088Sab196087 }
25955088Sab196087 /*
25965088Sab196087 * If the child failed, then terminate the process. There is no
25975088Sab196087 * need for an error message, because the child will have taken
25985088Sab196087 * care of that.
25995088Sab196087 */
26005088Sab196087 if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0))
26015088Sab196087 exit(1);
26025088Sab196087
26035088Sab196087 /* Make sure the copy allows user write access */
26045088Sab196087 if (stat(outfile, &statbuf) == -1) {
26055088Sab196087 int err = errno;
26065088Sab196087 (void) unlink(outfile);
26075088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT),
26085088Sab196087 outfile, strerror(err));
26095088Sab196087 }
26105088Sab196087 if ((statbuf.st_mode & S_IWUSR) == 0) {
26115088Sab196087 /* Only keep permission bits, and add user write */
26125088Sab196087 statbuf.st_mode |= S_IWUSR;
26135088Sab196087 statbuf.st_mode &= 07777; /* Only keep the permission bits */
26145088Sab196087 if (chmod(outfile, statbuf.st_mode) == -1) {
26155088Sab196087 int err = errno;
26165088Sab196087 (void) unlink(outfile);
26175088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD),
26185088Sab196087 outfile, strerror(err));
26195088Sab196087 }
26205088Sab196087 }
26215088Sab196087 }
26225088Sab196087
26235088Sab196087 /*
26245088Sab196087 * Given a module path string, determine how long the resulting path will
26255088Sab196087 * be when all % tokens have been expanded.
26265088Sab196087 *
26275088Sab196087 * entry:
26285088Sab196087 * path - Path for which expanded length is desired
26295088Sab196087 * origin_root - Root of $ORIGIN tree containing running elfedit program
26305088Sab196087 *
26315088Sab196087 * exit:
26325088Sab196087 * Returns the value strlen() will give for the expanded path.
26335088Sab196087 */
26345088Sab196087 static size_t
modpath_strlen(const char * path,const char * origin_root)26355088Sab196087 modpath_strlen(const char *path, const char *origin_root)
26365088Sab196087 {
26375088Sab196087 size_t len = 0;
26385088Sab196087 const char *s;
26395088Sab196087
26405088Sab196087 s = path;
26415088Sab196087 len = 0;
26425088Sab196087 for (s = path; *s != '\0'; s++) {
26435088Sab196087 if (*s == '%') {
26445088Sab196087 s++;
26455088Sab196087 switch (*s) {
26465088Sab196087 case 'i': /* ISA of running elfedit */
26475088Sab196087 len += strlen(isa_i_str);
26485088Sab196087 break;
26495088Sab196087 case 'I': /* "" for 32-bit, same as %i for 64 */
26505088Sab196087 len += strlen(isa_I_str);
26515088Sab196087 break;
26525088Sab196087 case 'o': /* Insert default path */
26535088Sab196087 len +=
26545088Sab196087 modpath_strlen(MSG_ORIG(MSG_STR_MODPATH),
26555088Sab196087 origin_root);
26565088Sab196087 break;
26575088Sab196087 case 'r': /* root of tree with running elfedit */
26585088Sab196087 len += strlen(origin_root);
26595088Sab196087 break;
26605088Sab196087
26615088Sab196087 case '%': /* %% is reduced to just '%' */
26625088Sab196087 len++;
26635088Sab196087 break;
26645088Sab196087 default: /* All other % codes are reserved */
26655088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
26665088Sab196087 MSG_INTL(MSG_ERR_BADPATHCODE), *s);
26675088Sab196087 /*NOTREACHED*/
26685088Sab196087 break;
26695088Sab196087 }
26705088Sab196087 } else { /* Non-% character passes straight through */
26715088Sab196087 len++;
26725088Sab196087 }
26735088Sab196087 }
26745088Sab196087
26755088Sab196087 return (len);
26765088Sab196087 }
26775088Sab196087
26785088Sab196087
26795088Sab196087 /*
26805088Sab196087 * Given a module path string, and a buffer large enough to hold the results,
26815088Sab196087 * fill the buffer with the expanded path.
26825088Sab196087 *
26835088Sab196087 * entry:
26845088Sab196087 * path - Path for which expanded length is desired
26855088Sab196087 * origin_root - Root of tree containing running elfedit program
26865088Sab196087 * buf - Buffer to receive the result. buf must as large or larger
26875088Sab196087 * than the value given by modpath_strlen().
26885088Sab196087 *
26895088Sab196087 * exit:
26905088Sab196087 * Returns pointer to location following the last character
26915088Sab196087 * written to buf. A NULL byte is written to that address.
26925088Sab196087 */
26935088Sab196087 static char *
modpath_expand(const char * path,const char * origin_root,char * buf)26945088Sab196087 modpath_expand(const char *path, const char *origin_root, char *buf)
26955088Sab196087 {
26965088Sab196087 size_t len;
26975088Sab196087 const char *cp_str;
26985088Sab196087
26995088Sab196087 for (; *path != '\0'; path++) {
27005088Sab196087 if (*path == '%') {
27015088Sab196087 path++;
27025088Sab196087 cp_str = NULL;
27035088Sab196087 switch (*path) {
27045088Sab196087 case 'i': /* ISA of running elfedit */
27055088Sab196087 cp_str = isa_i_str;
27065088Sab196087 break;
27075088Sab196087 case 'I': /* "" for 32-bit, same as %i for 64 */
27085088Sab196087 cp_str = isa_I_str;
27095088Sab196087 break;
27105088Sab196087 case 'o': /* Insert default path */
27115088Sab196087 buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH),
27125088Sab196087 origin_root, buf);
27135088Sab196087 break;
27145088Sab196087 case 'r':
27155088Sab196087 cp_str = origin_root;
27165088Sab196087 break;
27175088Sab196087 case '%': /* %% is reduced to just '%' */
27185088Sab196087 *buf++ = *path;
27195088Sab196087 break;
27205088Sab196087 default: /* All other % codes are reserved */
27215088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
27225088Sab196087 MSG_INTL(MSG_ERR_BADPATHCODE), *path);
27235088Sab196087 /*NOTREACHED*/
27245088Sab196087 break;
27255088Sab196087 }
27265088Sab196087 if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) {
27275088Sab196087 bcopy(cp_str, buf, len);
27285088Sab196087 buf += len;
27295088Sab196087 }
27305088Sab196087 } else { /* Non-% character passes straight through */
27315088Sab196087 *buf++ = *path;
27325088Sab196087 }
27335088Sab196087 }
27345088Sab196087
27355088Sab196087 *buf = '\0';
27365088Sab196087 return (buf);
27375088Sab196087 }
27385088Sab196087
27395088Sab196087
27405088Sab196087 /*
27415088Sab196087 * Establish the module search path: state.modpath
27425088Sab196087 *
27435088Sab196087 * The path used comes from the following sources, taking the first
27445088Sab196087 * one that has a value, and ignoring any others:
27455088Sab196087 *
27465088Sab196087 * - ELFEDIT_PATH environment variable
27475088Sab196087 * - -L command line argument
27485088Sab196087 * - Default value
27495088Sab196087 *
27505088Sab196087 * entry:
27515088Sab196087 * path - NULL, or the value of the -L command line argument
27525088Sab196087 *
27535088Sab196087 * exit:
27545088Sab196087 * state.modpath has been filled in
27555088Sab196087 */
27565088Sab196087 static void
establish_modpath(const char * cmdline_path)27575088Sab196087 establish_modpath(const char *cmdline_path)
27585088Sab196087 {
27595088Sab196087 char origin_root[PATH_MAX + 1]; /* Where elfedit binary is */
27605088Sab196087 const char *path; /* Initial path */
27615088Sab196087 char *expath; /* Expanded path */
27625088Sab196087 size_t len;
27635088Sab196087 char *src, *dst;
27645088Sab196087
27655088Sab196087 path = getenv(MSG_ORIG(MSG_STR_ENVVAR));
27665088Sab196087 if (path == NULL)
27675088Sab196087 path = cmdline_path;
27685088Sab196087 if (path == NULL)
27695088Sab196087 path = MSG_ORIG(MSG_STR_MODPATH);
27705088Sab196087
27715088Sab196087
27725088Sab196087 /*
27735088Sab196087 * Root of tree containing running for running program. 32-bit elfedit
27745088Sab196087 * is installed in /usr/bin, and 64-bit elfedit is one level lower
27755088Sab196087 * in an ISA-specific subdirectory. So, we find the root by
27765088Sab196087 * getting the $ORGIN of the current running program, and trimming
27775088Sab196087 * off the last 2 (32-bit) or 3 (64-bit) directories.
27785088Sab196087 *
27795088Sab196087 * On a standard system, this will simply yield '/'. However,
27805088Sab196087 * doing it this way allows us to run elfedit from a proto area,
27815088Sab196087 * and pick up modules from the same proto area instead of those
27825088Sab196087 * installed on the system.
27835088Sab196087 */
27845088Sab196087 if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1)
27855088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN));
27865088Sab196087 len = (sizeof (char *) == 8) ? 3 : 2;
27875088Sab196087 src = origin_root + strlen(origin_root);
27885088Sab196087 while ((src > origin_root) && (len > 0)) {
27895088Sab196087 if (*(src - 1) == '/')
27905088Sab196087 len--;
27915088Sab196087 src--;
27925088Sab196087 }
27935088Sab196087 *src = '\0';
27945088Sab196087
27955088Sab196087
27965088Sab196087 /*
27975088Sab196087 * Calculate space needed to hold expanded path. Note that
27985088Sab196087 * this assumes that MSG_STR_MODPATH will never contain a '%o'
27995088Sab196087 * code, and so, the expansion is not recursive. The codes allowed
28005088Sab196087 * are:
28015088Sab196087 * %i - ISA of running elfedit (sparc, sparcv9, etc)
28025088Sab196087 * %I - 64-bit ISA: Same as %i for 64-bit versions of elfedit,
28035088Sab196087 * but yields empty string for 32-bit ISAs.
28045088Sab196087 * %o - The original (default) path.
28055088Sab196087 * %r - Root of tree holding elfedit program.
28065088Sab196087 * %% - A single %
28075088Sab196087 *
28085088Sab196087 * A % followed by anything else is an error. This allows us to
28095088Sab196087 * add new codes in the future without backward compatability issues.
28105088Sab196087 */
28115088Sab196087 len = modpath_strlen(path, origin_root);
28125088Sab196087
28135088Sab196087 expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1);
28145088Sab196087 (void) modpath_expand(path, origin_root, expath);
28155088Sab196087
28165088Sab196087 /*
28175088Sab196087 * Count path segments, eliminate extra '/', and replace ':'
28185088Sab196087 * with NULL.
28195088Sab196087 */
28205088Sab196087 state.modpath.n = 1;
28215088Sab196087 for (src = dst = expath; *src; src++) {
28225088Sab196087 if (*src == '/') {
28235088Sab196087 switch (*(src + 1)) {
28245088Sab196087 case '/':
28255088Sab196087 case ':':
28265088Sab196087 case '\0':
28275088Sab196087 continue;
28285088Sab196087 }
28295088Sab196087 }
28305088Sab196087 if (*src == ':') {
28315088Sab196087 state.modpath.n++;
28325088Sab196087 *dst = '\0';
28335088Sab196087 } else if (src != dst) {
28345088Sab196087 *dst = *src;
28355088Sab196087 }
28365088Sab196087 dst++;
28375088Sab196087 }
28385088Sab196087 if (src != dst)
28395088Sab196087 *dst = '\0';
28405088Sab196087
28415088Sab196087 state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR),
28425088Sab196087 sizeof (state.modpath.seg[0]) * state.modpath.n);
28435088Sab196087
28445088Sab196087 src = expath;
28455088Sab196087 for (len = 0; len < state.modpath.n; len++) {
28465088Sab196087 if (*src == '\0') {
28475088Sab196087 state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT);
28485088Sab196087 src++;
28495088Sab196087 } else {
28505088Sab196087 state.modpath.seg[len] = src;
28515088Sab196087 src += strlen(src) + 1;
28525088Sab196087 }
28535088Sab196087 }
28545088Sab196087 }
28555088Sab196087
28565088Sab196087 /*
28575088Sab196087 * When interactive (reading commands from a tty), we catch
28585088Sab196087 * SIGINT in order to restart the outer command loop.
28595088Sab196087 */
28605088Sab196087 /*ARGSUSED*/
28615088Sab196087 static void
sigint_handler(int sig,siginfo_t * sip,void * ucp)28625088Sab196087 sigint_handler(int sig, siginfo_t *sip, void *ucp)
28635088Sab196087 {
28645088Sab196087 /* Jump to the outer loop to resume */
28655088Sab196087 if (state.msg_jbuf.active) {
28665088Sab196087 state.msg_jbuf.active = 0;
28675088Sab196087 siglongjmp(state.msg_jbuf.env, 1);
28685088Sab196087 }
28695088Sab196087 }
28705088Sab196087
28715088Sab196087
28725088Sab196087 static void
usage(int full)28735088Sab196087 usage(int full)
28745088Sab196087 {
28755088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF));
28765088Sab196087 if (full) {
28775088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1));
28785088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2));
28795088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3));
28805088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4));
28815088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5));
28825088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6));
28835088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST));
28845088Sab196087 }
28855088Sab196087 elfedit_exit(2);
28865088Sab196087 }
28875088Sab196087
28885088Sab196087
28895088Sab196087 /*
28905088Sab196087 * In order to complete commands, we need to know about them,
28915088Sab196087 * which means that we need to force all the modules to be
28925088Sab196087 * loaded. This is a relatively expensive operation, so we use
28935088Sab196087 * this function, which avoids doing it more than once in a session.
28945088Sab196087 */
28955088Sab196087 static void
elfedit_cpl_load_modules(void)28965088Sab196087 elfedit_cpl_load_modules(void)
28975088Sab196087 {
28985088Sab196087 static int loaded;
28995088Sab196087
29005088Sab196087 if (!loaded) {
29015088Sab196087 elfedit_load_modpath();
29025088Sab196087 loaded = 1; /* Don't do it again */
29035088Sab196087 }
29045088Sab196087 }
29055088Sab196087
29065088Sab196087 /*
29075088Sab196087 * Compare the token to the given string, and if they share a common
29085088Sab196087 * initial sequence, add the tail of string to the tecla command completion
29095088Sab196087 * buffer:
29105088Sab196087 *
29115088Sab196087 * entry:
29125088Sab196087 * cpldata - Current completion state
29135088Sab196087 * str - String to match against token
29145088Sab196087 * casefold - True to allow case insensitive completion, False
29155088Sab196087 * if case must match exactly.
29165088Sab196087 */
29175088Sab196087 void
elfedit_cpl_match(void * cpldata,const char * str,int casefold)29185088Sab196087 elfedit_cpl_match(void *cpldata, const char *str, int casefold)
29195088Sab196087 {
29205088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
29215088Sab196087 const char *cont_suffix;
29225088Sab196087 const char *type_suffix;
29235088Sab196087
29245088Sab196087 /*
29255088Sab196087 * Reasons to return immediately:
29265088Sab196087 * - NULL strings have no completion value
29275088Sab196087 * - The string is shorter than the existing item being completed
29285088Sab196087 */
29295088Sab196087 if ((str == NULL) || (*str == '\0') ||
29305088Sab196087 ((cstate->ecpl_token_len != 0) &&
29315088Sab196087 ((strlen(str) < cstate->ecpl_token_len))))
29325088Sab196087 return;
29335088Sab196087
29345088Sab196087 /* If the string does not share the existing prefix, don't use it */
29355088Sab196087 if (casefold) {
29365088Sab196087 if (strncasecmp(cstate->ecpl_token_str, str,
29375088Sab196087 cstate->ecpl_token_len) != 0)
29385088Sab196087 return;
29395088Sab196087 } else {
29405088Sab196087 if (strncmp(cstate->ecpl_token_str, str,
29415088Sab196087 cstate->ecpl_token_len) != 0)
29425088Sab196087 return;
29435088Sab196087 }
29445088Sab196087
29455088Sab196087 if (cstate->ecpl_add_mod_colon) {
29465088Sab196087 cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON);
29475088Sab196087 } else {
29485088Sab196087 cont_suffix = MSG_ORIG(MSG_STR_SPACE);
29495088Sab196087 type_suffix = NULL;
29505088Sab196087 }
29515088Sab196087 (void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line,
29525088Sab196087 cstate->ecpl_word_start, cstate->ecpl_word_end,
29535088Sab196087 str + cstate->ecpl_token_len, type_suffix, cont_suffix);
29545088Sab196087
29555088Sab196087 }
29565088Sab196087
29575088Sab196087
29585088Sab196087 /*
29596225Sab196087 * Convenience wrapper on elfedit_cpl_match(): Format an unsigned
29606225Sab196087 * 32-bit integer as a string and enter the result for command completion.
29616225Sab196087 */
29626225Sab196087 void
elfedit_cpl_ndx(void * cpldata,uint_t ndx)29636225Sab196087 elfedit_cpl_ndx(void *cpldata, uint_t ndx)
29646225Sab196087 {
29656225Sab196087 Conv_inv_buf_t buf;
29666225Sab196087
29676225Sab196087 (void) snprintf(buf.buf, sizeof (buf.buf),
29686225Sab196087 MSG_ORIG(MSG_FMT_WORDVAL), ndx);
29696225Sab196087 elfedit_cpl_match(cpldata, buf.buf, 0);
29706225Sab196087 }
29716225Sab196087
29726225Sab196087
29736225Sab196087 /*
29745088Sab196087 * Compare the token to the names of the commands from the given module,
29755088Sab196087 * and if they share a common initial sequence, add the tail of string
29765088Sab196087 * to the tecla command completion buffer:
29775088Sab196087 *
29785088Sab196087 * entry:
29795088Sab196087 * tok_buf - Token user has entered
29805088Sab196087 * tok_len - strlen(tok_buf)
29815088Sab196087 * mod - Module definition from which commands should be matched
29825088Sab196087 * cpl, line, word_start, word_end, cont_suffix - As documented
29835088Sab196087 * for gl_get_line() and cpl_add_completion.
29845088Sab196087 */
29855088Sab196087 static void
match_module_cmds(ELFEDIT_CPL_STATE * cstate,elfeditGC_module_t * mod)29865088Sab196087 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod)
29875088Sab196087 {
29885088Sab196087 elfeditGC_cmd_t *cmd;
29895088Sab196087 const char **cmd_name;
29905088Sab196087
29915088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
29925088Sab196087 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
29935088Sab196087 elfedit_cpl_match(cstate, *cmd_name, 1);
29945088Sab196087 }
29955088Sab196087
29965088Sab196087
29975088Sab196087 /*
29985088Sab196087 * Compare the token to the known module names, and add those that
29995088Sab196087 * match to the list of alternatives via elfedit_cpl_match().
30005088Sab196087 *
30015088Sab196087 * entry:
30025088Sab196087 * load_all_modules - If True, causes all modules to be loaded
30035088Sab196087 * before processing is done. If False, only the modules
30045088Sab196087 * currently seen will be used.
30055088Sab196087 */
30065088Sab196087 void
elfedit_cpl_module(void * cpldata,int load_all_modules)30075088Sab196087 elfedit_cpl_module(void *cpldata, int load_all_modules)
30085088Sab196087 {
30095088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
30105088Sab196087 MODLIST_T *modlist;
30115088Sab196087
30125088Sab196087 if (load_all_modules)
30135088Sab196087 elfedit_cpl_load_modules();
30145088Sab196087
30155088Sab196087 for (modlist = state.modlist; modlist != NULL;
30165088Sab196087 modlist = modlist->ml_next) {
30175088Sab196087 elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1);
30185088Sab196087 }
30195088Sab196087 }
30205088Sab196087
30215088Sab196087
30225088Sab196087 /*
30235088Sab196087 * Compare the token to all the known commands, and add those that
30245088Sab196087 * match to the list of alternatives.
30255088Sab196087 *
30265088Sab196087 * note:
30275088Sab196087 * This routine will force modules to be loaded as necessary to
30285088Sab196087 * obtain the names it needs to match.
30295088Sab196087 */
30305088Sab196087 void
elfedit_cpl_command(void * cpldata)30315088Sab196087 elfedit_cpl_command(void *cpldata)
30325088Sab196087 {
30335088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
30345088Sab196087 ELFEDIT_CPL_STATE colon_state;
30355088Sab196087 const char *colon_pos;
30365088Sab196087 MODLIST_T *modlist;
30375088Sab196087 MODLIST_T *insdef;
30385088Sab196087 char buf[128];
30395088Sab196087
30405088Sab196087 /*
30415088Sab196087 * Is there a colon in the command? If so, locate its offset within
30425088Sab196087 * the raw input line.
30435088Sab196087 */
30445088Sab196087 for (colon_pos = cstate->ecpl_token_str;
30455088Sab196087 *colon_pos && (*colon_pos != ':'); colon_pos++)
30465088Sab196087 ;
30475088Sab196087
30485088Sab196087 /*
30495088Sab196087 * If no colon was seen, then we are completing a module name,
30505088Sab196087 * or one of the commands from 'sys:'
30515088Sab196087 */
30525088Sab196087 if (*colon_pos == '\0') {
30535088Sab196087 /*
30545088Sab196087 * Setting cstate->add_mod_colon tells elfedit_cpl_match()
30555088Sab196087 * to add an implicit ':' to the names it matches. We use it
30565088Sab196087 * here so the user doesn't have to enter the ':' manually.
30575088Sab196087 * Hiding this in the opaque state instead of making it
30585088Sab196087 * an argument to that function gives us the ability to
30595088Sab196087 * change it later without breaking the published interface.
30605088Sab196087 */
30615088Sab196087 cstate->ecpl_add_mod_colon = 1;
30625088Sab196087 elfedit_cpl_module(cpldata, 1);
30635088Sab196087 cstate->ecpl_add_mod_colon = 0;
30645088Sab196087
30655088Sab196087 /* Add bare (no sys: prefix) commands from the sys: module */
30665088Sab196087 match_module_cmds(cstate,
30675088Sab196087 elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0));
30685088Sab196087
30695088Sab196087 return;
30705088Sab196087 }
30715088Sab196087
30725088Sab196087 /*
30735088Sab196087 * A colon was seen, so we have a module name. Extract the name,
30745088Sab196087 * substituting 'sys' for the case where the given name is empty.
30755088Sab196087 */
30765088Sab196087 if (colon_pos == 0)
30775088Sab196087 (void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf));
30785088Sab196087 else
30795088Sab196087 elfedit_strnbcpy(buf, cstate->ecpl_token_str,
30805088Sab196087 colon_pos - cstate->ecpl_token_str, sizeof (buf));
30815088Sab196087
30825088Sab196087 /*
30835088Sab196087 * Locate the module. If it isn't already loaded, make an explicit
30845088Sab196087 * attempt to load it and try again. If a module definition is
30855088Sab196087 * obtained, process the commands it supplies.
30865088Sab196087 */
30875088Sab196087 modlist = module_loaded(buf, &insdef);
30885088Sab196087 if (modlist == NULL) {
30895088Sab196087 (void) elfedit_load_module(buf, 0, 0);
30905088Sab196087 modlist = module_loaded(buf, &insdef);
30915088Sab196087 }
30925088Sab196087 if (modlist != NULL) {
30935088Sab196087 /*
30945088Sab196087 * Make a copy of the cstate, and adjust the line and
30955088Sab196087 * token so that the new one starts just past the colon
30965088Sab196087 * character. We know that the colon exists because
30975088Sab196087 * of the preceeding test that found it. Therefore, we do
30985088Sab196087 * not need to test against running off the end of the
30995088Sab196087 * string here.
31005088Sab196087 */
31015088Sab196087 colon_state = *cstate;
31025088Sab196087 while (colon_state.ecpl_line[colon_state.ecpl_word_start] !=
31035088Sab196087 ':')
31045088Sab196087 colon_state.ecpl_word_start++;
31055088Sab196087 while (*colon_state.ecpl_token_str != ':') {
31065088Sab196087 colon_state.ecpl_token_str++;
31075088Sab196087 colon_state.ecpl_token_len--;
31085088Sab196087 }
31095088Sab196087 /* Skip past the ':' character */
31105088Sab196087 colon_state.ecpl_word_start++;
31115088Sab196087 colon_state.ecpl_token_str++;
31125088Sab196087 colon_state.ecpl_token_len--;
31135088Sab196087
31145088Sab196087 match_module_cmds(&colon_state, modlist->ml_mod);
31155088Sab196087 }
31165088Sab196087 }
31175088Sab196087
31185088Sab196087
31195088Sab196087 /*
31205088Sab196087 * Command completion function for use with libtacla.
31215088Sab196087 */
31225088Sab196087 /*ARGSUSED1*/
31235088Sab196087 static int
cmd_match_fcn(WordCompletion * cpl,void * data,const char * line,int word_end)31245088Sab196087 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end)
31255088Sab196087 {
31265088Sab196087 const char *argv[ELFEDIT_MAXCPLARGS];
31275088Sab196087 ELFEDIT_CPL_STATE cstate;
31285088Sab196087 TOK_STATE *tokst;
31295088Sab196087 int ndx;
31305088Sab196087 int i;
31315088Sab196087 elfeditGC_module_t *mod;
31325088Sab196087 elfeditGC_cmd_t *cmd;
31335088Sab196087 int num_opt;
31345088Sab196087 int opt_term_seen;
31355088Sab196087 int skip_one;
31365088Sab196087 elfedit_cmd_optarg_t *optarg;
31375088Sab196087 elfedit_optarg_item_t item;
31385088Sab196087 int ostyle_ndx = -1;
31395088Sab196087
31405088Sab196087 /*
31415088Sab196087 * For debugging, enable the following block. It tells the tecla
31425088Sab196087 * library that the program using is going to write to stdout.
31435088Sab196087 * It will put the tty back into normal mode, and it will cause
31445088Sab196087 * tecla to redraw the current input line when it gets control back.
31455088Sab196087 */
31465892Sab196087 #ifdef DEBUG_CMD_MATCH
31475088Sab196087 gl_normal_io(state.input.gl);
31485088Sab196087 #endif
31495088Sab196087
31505088Sab196087 /*
31515088Sab196087 * Tokenize the line up through word_end. The last token in
31525088Sab196087 * the list is the one requiring completion.
31535088Sab196087 */
31545088Sab196087 tokst = tokenize_user_cmd(line, word_end, 1);
31555088Sab196087 if (tokst->tokst_cnt == 0)
31565088Sab196087 return (0);
31575088Sab196087
31585088Sab196087 /* Set up the cstate block, containing the completion state */
31595088Sab196087 ndx = tokst->tokst_cnt - 1; /* Index of token to complete */
31605088Sab196087 cstate.ecpl_cpl = cpl;
31615088Sab196087 cstate.ecpl_line = line;
31625088Sab196087 cstate.ecpl_word_start = tokst->tokst_buf[ndx].tok_line_off;
31635088Sab196087 cstate.ecpl_word_end = word_end;
31645088Sab196087 cstate.ecpl_add_mod_colon = 0;
31655088Sab196087 cstate.ecpl_token_str = tokst->tokst_buf[ndx].tok_str;
31665088Sab196087 cstate.ecpl_token_len = tokst->tokst_buf[ndx].tok_len;
31675088Sab196087
31685088Sab196087 /*
31695088Sab196087 * If there is only one token, then we are completing the
31705088Sab196087 * command itself.
31715088Sab196087 */
31725088Sab196087 if (ndx == 0) {
31735088Sab196087 elfedit_cpl_command(&cstate);
31745088Sab196087 return (0);
31755088Sab196087 }
31765088Sab196087
31775088Sab196087 /*
31785088Sab196087 * There is more than one token. Use the first one to
31795088Sab196087 * locate the definition for the command. If we don't have
31805088Sab196087 * a definition for the command, then there's nothing more
31815088Sab196087 * we can do.
31825088Sab196087 */
31835088Sab196087 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 0, &mod);
31845088Sab196087 if (cmd == NULL)
31855088Sab196087 return (0);
31865088Sab196087
31875088Sab196087 /*
31885088Sab196087 * Since we know the command, give them a quick usage message.
31895088Sab196087 * It may be that they just need a quick reminder about the form
31905088Sab196087 * of the command and the options.
31915088Sab196087 */
31925088Sab196087 (void) gl_normal_io(state.input.gl);
31935088Sab196087 elfedit_printf(MSG_INTL(MSG_USAGE_CMD),
31945088Sab196087 elfedit_format_command_usage(mod, cmd, NULL, 0));
31955088Sab196087
31965088Sab196087
31975088Sab196087 /*
31985088Sab196087 * We have a generous setting for ELFEDIT_MAXCPLARGS, so there
31995088Sab196087 * should always be plenty of room. If there's not room, we
32005088Sab196087 * can't proceed.
32015088Sab196087 */
32025088Sab196087 if (ndx >= ELFEDIT_MAXCPLARGS)
32035088Sab196087 return (0);
32045088Sab196087
32055088Sab196087 /*
32065088Sab196087 * Put pointers to the tokens into argv, and determine how
32075088Sab196087 * many of the tokens are optional arguments.
32085088Sab196087 *
32095088Sab196087 * We consider the final optional argument to be the rightmost
32105088Sab196087 * argument that starts with a '-'. If a '--' is seen, then
32115088Sab196087 * we stop there, and any argument that follows is a plain argument
32125088Sab196087 * (even if it starts with '-').
32135088Sab196087 *
32145088Sab196087 * We look for an inherited '-o' option, because we are willing
32155088Sab196087 * to supply command completion for these values.
32165088Sab196087 */
32175088Sab196087 num_opt = 0;
32185088Sab196087 opt_term_seen = 0;
32195088Sab196087 skip_one = 0;
32205088Sab196087 for (i = 0; i < ndx; i++) {
32215088Sab196087 argv[i] = tokst->tokst_buf[i + 1].tok_str;
32225088Sab196087 if (opt_term_seen || skip_one) {
32235088Sab196087 skip_one = 0;
32245088Sab196087 continue;
32255088Sab196087 }
32265088Sab196087 skip_one = 0;
32275088Sab196087 ostyle_ndx = -1;
32285088Sab196087 if ((strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_MINUS)) == NULL) ||
32295088Sab196087 (*argv[i] != '-')) {
32305088Sab196087 opt_term_seen = 1;
32315088Sab196087 continue;
32325088Sab196087 }
32335088Sab196087 num_opt = i + 1;
32345088Sab196087 /*
32355088Sab196087 * If it is a recognised ELFEDIT_CMDOA_F_VALUE option,
32365088Sab196087 * then the item following it is the associated value.
32375088Sab196087 * Check for this and skip the value.
32385088Sab196087 *
32395088Sab196087 * At the same time, look for STDOA_OPT_O inherited
32405088Sab196087 * options. We want to identify the index of any such
32415088Sab196087 * item. Although the option is simply "-o", we are willing
32425088Sab196087 * to treat any option that starts with "-o" as a potential
32435088Sab196087 * STDOA_OPT_O. This lets us to command completion for things
32445088Sab196087 * like "-onum", and is otherwise harmless, the only cost
32455088Sab196087 * being a few additional strcmps by the cpl code.
32465088Sab196087 */
32475088Sab196087 if ((optarg = cmd->cmd_opt) == NULL)
32485088Sab196087 continue;
32495088Sab196087 while (optarg->oa_name != NULL) {
32505088Sab196087 int is_ostyle_optarg =
32515088Sab196087 (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) &&
32525088Sab196087 (optarg->oa_name == ELFEDIT_STDOA_OPT_O);
32535088Sab196087
32545088Sab196087 elfedit_next_optarg(&optarg, &item);
32555088Sab196087 if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) {
32565088Sab196087 if (is_ostyle_optarg && (strncmp(argv[i],
32575088Sab196087 MSG_ORIG(MSG_STR_MINUS_O), 2) == 0))
32585088Sab196087 ostyle_ndx = i + 1;
32595088Sab196087
32605088Sab196087 if (strcmp(item.oai_name, argv[i]) == 0) {
32615088Sab196087 num_opt = i + 2;
32625088Sab196087 skip_one = 1;
32635088Sab196087 break;
32645088Sab196087 }
32655088Sab196087 /*
32665088Sab196087 * If it didn't match "-o" exactly, but it is
32675088Sab196087 * ostyle_ndx, then it is a potential combined
32685088Sab196087 * STDOA_OPT_O, as discussed above. It counts
32695088Sab196087 * as a single argument.
32705088Sab196087 */
32715088Sab196087 if (ostyle_ndx == ndx)
32725088Sab196087 break;
32735088Sab196087 }
32745088Sab196087 }
32755088Sab196087 }
32765088Sab196087
32775892Sab196087 #ifdef DEBUG_CMD_MATCH
32785892Sab196087 (void) printf("NDX(%d) NUM_OPT(%d) ostyle_ndx(%d)\n", ndx, num_opt,
32795088Sab196087 ostyle_ndx);
32805088Sab196087 #endif
32815088Sab196087
32825088Sab196087 if (ostyle_ndx != -1) {
32835088Sab196087 /*
32845088Sab196087 * If ostyle_ndx is one less than ndx, and ndx is
32855088Sab196087 * the same as num_opt, then we have a definitive
32865088Sab196087 * STDOA_OPT_O inherited outstyle option. We supply
32875088Sab196087 * the value strings, and are done.
32885088Sab196087 */
32895088Sab196087 if ((ostyle_ndx == (ndx - 1)) && (ndx == num_opt)) {
32905088Sab196087 elfedit_cpl_atoconst(&cstate, ELFEDIT_CONST_OUTSTYLE);
32915088Sab196087 return (0);
32925088Sab196087 }
32935088Sab196087
32945088Sab196087 /*
32955088Sab196087 * If ostyle is the same as ndx, then we have an option
32965088Sab196087 * staring with "-o" that may end up being a STDOA_OPT_O,
32975088Sab196087 * and we are still inside that token. In this case, we
32985088Sab196087 * supply completion strings that include the leading
32995088Sab196087 * "-o" followed by the values, without a space
33005088Sab196087 * (i.e. "-onum"). We then fall through, allowing any
33015088Sab196087 * other options starting with "-o" to be added
33025088Sab196087 * below. elfedit_cpl_match() will throw out the incorrect
33035088Sab196087 * options, so it is harmless to add these extra items in
33045088Sab196087 * the worst case, and useful otherwise.
33055088Sab196087 */
33065088Sab196087 if (ostyle_ndx == ndx)
33075088Sab196087 elfedit_cpl_atoconst(&cstate,
33085088Sab196087 ELFEDIT_CONST_OUTSTYLE_MO);
33095088Sab196087 }
33105088Sab196087
33115088Sab196087 /*
33125088Sab196087 * If (ndx <= num_opt), then the token needing completion
33135088Sab196087 * is an option. If the leading '-' is there, then we should fill
33145088Sab196087 * in all of the option alternatives. If anything follows the '-'
33155088Sab196087 * though, we assume that the user has already figured out what
33165088Sab196087 * option to use, and we leave well enough alone.
33175088Sab196087 *
33185088Sab196087 * Note that we are intentionally ignoring a related case
33195088Sab196087 * where supplying option strings would be legal: In the case
33205088Sab196087 * where we are one past the last option (ndx == (num_opt + 1)),
33215088Sab196087 * and the current option is an empty string, the argument can
33225088Sab196087 * be either a plain argument or an option --- the user needs to
33235088Sab196087 * enter the next character before we can tell. It would be
33245088Sab196087 * OK to enter the option strings in this case. However, consider
33255088Sab196087 * what happens when the first plain argument to the command does
33265088Sab196087 * not provide any command completion (e.g. it is a plain integer).
33275088Sab196087 * In this case, tecla will see that all the alternatives start
33285088Sab196087 * with '-', and will insert a '-' into the input. If the user
33295088Sab196087 * intends the next argument to be plain, they will have to delete
33305088Sab196087 * this '-', which is annoying. Worse than that, they may be confused
33315088Sab196087 * by it, and think that the plain argument is not allowed there.
33325088Sab196087 * The best solution is to not supply option strings unless the
33335088Sab196087 * user first enters the '-'.
33345088Sab196087 */
33355088Sab196087 if ((ndx <= num_opt) && (argv[ndx - 1][0] == '-')) {
33365088Sab196087 if ((optarg = cmd->cmd_opt) != NULL) {
33375088Sab196087 while (optarg->oa_name != NULL) {
33385088Sab196087 elfedit_next_optarg(&optarg, &item);
33395088Sab196087 elfedit_cpl_match(&cstate, item.oai_name, 1);
33405088Sab196087 }
33415088Sab196087 }
33425088Sab196087 return (0);
33435088Sab196087 }
33445088Sab196087
33455088Sab196087 /*
33465088Sab196087 * At this point we know that ndx and num_opt are not equal.
33475088Sab196087 * If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE
33485088Sab196087 * argument at the end, and the following value has not been entered.
33495088Sab196087 *
33505088Sab196087 * If ndx is greater than num_opt, it means that we are looking
33515088Sab196087 * at a plain argument (or in the case where (ndx == (num_opt + 1)),
33525088Sab196087 * a *potential* plain argument.
33535088Sab196087 *
33545088Sab196087 * If the command has a completion function registered, then we
33555088Sab196087 * hand off the remaining work to it. The cmd_cplfunc field is
33565088Sab196087 * the generic definition. We need to cast it to the type that matches
33575088Sab196087 * the proper ELFCLASS before calling it.
33585088Sab196087 */
33595088Sab196087 if (state.elf.elfclass == ELFCLASS32) {
33605088Sab196087 elfedit32_cmdcpl_func_t *cmdcpl_func =
33615088Sab196087 (elfedit32_cmdcpl_func_t *)cmd->cmd_cplfunc;
33625088Sab196087
33635088Sab196087 if (cmdcpl_func != NULL)
33645088Sab196087 (* cmdcpl_func)(state.elf.obj_state.s32,
33655088Sab196087 &cstate, ndx, argv, num_opt);
33665088Sab196087 } else {
33675088Sab196087 elfedit64_cmdcpl_func_t *cmdcpl_func =
33685088Sab196087 (elfedit64_cmdcpl_func_t *)cmd->cmd_cplfunc;
33695088Sab196087
33705088Sab196087 if (cmdcpl_func != NULL)
33715088Sab196087 (* cmdcpl_func)(state.elf.obj_state.s64,
33725088Sab196087 &cstate, ndx, argv, num_opt);
33735088Sab196087 }
33745088Sab196087
33755088Sab196087 return (0);
33765088Sab196087 }
33775088Sab196087
33785088Sab196087
33795088Sab196087 /*
33805088Sab196087 * Read a line of input from stdin, and return pointer to it.
33815088Sab196087 *
33825088Sab196087 * This routine uses a private buffer, so the contents of the returned
33835088Sab196087 * string are only good until the next call.
33845088Sab196087 */
33855088Sab196087 static const char *
read_cmd(void)33865088Sab196087 read_cmd(void)
33875088Sab196087 {
33885088Sab196087 char *s;
33895088Sab196087
33905088Sab196087 if (state.input.full_tty) {
33915088Sab196087 state.input.in_tecla = TRUE;
33925088Sab196087 s = gl_get_line(state.input.gl,
33935088Sab196087 MSG_ORIG(MSG_STR_PROMPT), NULL, -1);
33945088Sab196087 state.input.in_tecla = FALSE;
33955088Sab196087 /*
33965088Sab196087 * gl_get_line() returns NULL for EOF or for error. EOF is fine,
33975088Sab196087 * but we need to catch and report anything else. Since
33985088Sab196087 * reading from stdin is critical to our operation, an
33995088Sab196087 * error implies that we cannot recover and must exit.
34005088Sab196087 */
34015088Sab196087 if ((s == NULL) &&
34025088Sab196087 (gl_return_status(state.input.gl) == GLR_ERROR)) {
34035088Sab196087 elfedit_msg(ELFEDIT_MSG_FATAL, MSG_INTL(MSG_ERR_GLREAD),
34045088Sab196087 gl_error_message(state.input.gl, NULL, 0));
34055088Sab196087 }
34065088Sab196087 } else {
34075088Sab196087 /*
34085088Sab196087 * This should be a dynamically sized buffer, but for now,
34095088Sab196087 * I'm going to take a simpler path.
34105088Sab196087 */
34115088Sab196087 static char cmd_buf[ELFEDIT_MAXCMD + 1];
34125088Sab196087
34135088Sab196087 s = fgets(cmd_buf, sizeof (cmd_buf), stdin);
34145088Sab196087 }
34155088Sab196087
34165088Sab196087 /* Return user string, or 'quit' on EOF */
34175088Sab196087 return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT));
34185088Sab196087 }
34195088Sab196087
34205088Sab196087 int
main(int argc,char ** argv,char ** envp)34215088Sab196087 main(int argc, char **argv, char **envp)
34225088Sab196087 {
34235088Sab196087 /*
34245088Sab196087 * Note: This function can use setjmp()/longjmp() which does
34255088Sab196087 * not preserve the values of auto/register variables. Hence,
34265088Sab196087 * variables that need their values preserved across a jump must
34275088Sab196087 * be marked volatile, or must not be auto/register.
34285088Sab196087 *
34295088Sab196087 * Volatile can be messy, because it requires explictly casting
34305088Sab196087 * away the attribute when passing it to functions, or declaring
34315088Sab196087 * those functions with the attribute as well. In a single threaded
34325088Sab196087 * program like this one, an easier approach is to make things
34335088Sab196087 * static. That can be done here, or by putting things in the
34345088Sab196087 * 'state' structure.
34355088Sab196087 */
34365088Sab196087
34375088Sab196087 int c, i;
34385088Sab196087 int num_batch = 0;
34395088Sab196087 char **batch_list = NULL;
34405088Sab196087 const char *modpath = NULL;
34415088Sab196087
34425088Sab196087 /*
34435088Sab196087 * Always have liblddb display unclipped section names.
34445088Sab196087 * This global is exported by liblddb, and declared in debug.h.
34455088Sab196087 */
34465088Sab196087 dbg_desc->d_extra |= DBG_E_LONG;
34475088Sab196087
34485088Sab196087 opterr = 0;
34495088Sab196087 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
34505088Sab196087 switch (c) {
34515088Sab196087 case 'a':
34525088Sab196087 state.flags |= ELFEDIT_F_AUTOPRINT;
34535088Sab196087 break;
34545088Sab196087
34555088Sab196087 case 'd':
34565088Sab196087 state.flags |= ELFEDIT_F_DEBUG;
34575088Sab196087 break;
34585088Sab196087
34595088Sab196087 case 'e':
34605088Sab196087 /*
34615088Sab196087 * Delay parsing the -e options until after the call to
34625088Sab196087 * conv_check_native() so that we won't bother loading
34635088Sab196087 * modules of the wrong class.
34645088Sab196087 */
34655088Sab196087 if (batch_list == NULL)
34665088Sab196087 batch_list = elfedit_malloc(
34675088Sab196087 MSG_INTL(MSG_ALLOC_BATCHLST),
34685088Sab196087 sizeof (*batch_list) * (argc - 1));
34695088Sab196087 batch_list[num_batch++] = optarg;
34705088Sab196087 break;
34715088Sab196087
34725088Sab196087 case 'L':
34735088Sab196087 modpath = optarg;
34745088Sab196087 break;
34755088Sab196087
34765088Sab196087 case 'o':
34775088Sab196087 if (elfedit_atooutstyle(optarg, &state.outstyle) == 0)
34785088Sab196087 usage(1);
34795088Sab196087 break;
34805088Sab196087
34815088Sab196087 case 'r':
34825088Sab196087 state.flags |= ELFEDIT_F_READONLY;
34835088Sab196087 break;
34845088Sab196087
34855088Sab196087 case '?':
34865088Sab196087 usage(1);
34875088Sab196087 }
34885088Sab196087 }
34895088Sab196087
34905088Sab196087 /*
34915088Sab196087 * We allow 0, 1, or 2 files:
34925088Sab196087 *
34935088Sab196087 * The no-file case is an extremely limited mode, in which the
34945088Sab196087 * only commands allowed to execute come from the sys: module.
34955088Sab196087 * This mode exists primarily to allow easy access to the help
34965088Sab196087 * facility.
34975088Sab196087 *
34985088Sab196087 * To get full access to elfedit's capablities, there must
34995088Sab196087 * be an input file. If this is not a readonly
35005088Sab196087 * session, then an optional second output file is allowed.
35015088Sab196087 *
35025088Sab196087 * In the case where two files are given and the session is
35035088Sab196087 * readonly, use a full usage message, because the simple
35045088Sab196087 * one isn't enough for the user to understand their error.
35055088Sab196087 * Otherwise, the simple usage message suffices.
35065088Sab196087 */
35075088Sab196087 argc = argc - optind;
35085088Sab196087 if ((argc == 2) && (state.flags & ELFEDIT_F_READONLY))
35095088Sab196087 usage(1);
35105088Sab196087 if (argc > 2)
35115088Sab196087 usage(0);
35125088Sab196087
35135088Sab196087 state.file.present = (argc != 0);
35145088Sab196087
35155088Sab196087 /*
35165088Sab196087 * If we have a file to edit, and unless told otherwise by the
35175088Sab196087 * caller, we try to run the 64-bit version of this program
35185088Sab196087 * when the system is capable of it. If that fails, then we
35195088Sab196087 * continue on with the currently running version.
35205088Sab196087 *
35215088Sab196087 * To force 32-bit execution on a 64-bit host, set the
35225088Sab196087 * LD_NOEXEC_64 environment variable to a non-empty value.
35235088Sab196087 *
35245088Sab196087 * There is no reason to bother with this if in "no file" mode.
35255088Sab196087 */
35265088Sab196087 if (state.file.present != 0)
35275088Sab196087 (void) conv_check_native(argv, envp);
35285088Sab196087
35295088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_VERSION),
35305088Sab196087 (sizeof (char *) == 8) ? 64 : 32);
35315088Sab196087
35325088Sab196087 /*
35335088Sab196087 * Put a module definition for the builtin system module on the
35345088Sab196087 * module list. We know it starts out empty, so we do not have
35355088Sab196087 * to go through a more general insertion process than this.
35365088Sab196087 */
35375088Sab196087 state.modlist = elfedit_sys_init(ELFEDIT_VER_CURRENT);
35385088Sab196087
35395088Sab196087 /* Establish the search path for loadable modules */
35405088Sab196087 establish_modpath(modpath);
35415088Sab196087
35425088Sab196087 /*
35435088Sab196087 * Now that we are running the final version of this program,
35445088Sab196087 * deal with the input/output file(s).
35455088Sab196087 */
35465088Sab196087 if (state.file.present == 0) {
35475088Sab196087 /*
35485088Sab196087 * This is arbitrary --- we simply need to be able to
35495088Sab196087 * load modules so that we can access their help strings
35505088Sab196087 * and command completion functions. Without a file, we
35515088Sab196087 * will refuse to call commands from any module other
35525088Sab196087 * than sys. Those commands have been written to be aware
35535088Sab196087 * of the case where there is no input file, and are
35545088Sab196087 * therefore safe to run.
35555088Sab196087 */
35565088Sab196087 state.elf.elfclass = ELFCLASS32;
35575088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NOFILE));
35585088Sab196087
35595088Sab196087 } else {
35605088Sab196087 state.file.infile = argv[optind];
35615088Sab196087 if (argc == 1) {
35625088Sab196087 state.file.outfile = state.file.infile;
35635088Sab196087 if (state.flags & ELFEDIT_F_READONLY)
35645088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
35655088Sab196087 MSG_INTL(MSG_DEBUG_READONLY));
35665088Sab196087 else
35675088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
35685088Sab196087 MSG_INTL(MSG_DEBUG_INPLACEWARN),
35695088Sab196087 state.file.infile);
35705088Sab196087 } else {
35715088Sab196087 state.file.outfile = argv[optind + 1];
35725088Sab196087 create_outfile(state.file.infile, state.file.outfile);
35735088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG,
35745088Sab196087 MSG_INTL(MSG_DEBUG_CPFILE),
35755088Sab196087 state.file.infile, state.file.outfile);
35765088Sab196087 /*
35775088Sab196087 * We are editing a copy of the original file that we
35785088Sab196087 * just created. If we should exit before the edits are
35795088Sab196087 * updated, then we want to unlink this copy so that we
35805088Sab196087 * don't leave junk lying around. Once an update
35815088Sab196087 * succeeds however, we'll leave it in place even
35825088Sab196087 * if an error occurs afterwards.
35835088Sab196087 */
35845088Sab196087 state.file.unlink_on_exit = 1;
35855088Sab196087 optind++; /* Edit copy instead of the original */
35865088Sab196087 }
35875088Sab196087
35885088Sab196087 init_obj_state(state.file.outfile);
35895088Sab196087 }
35905088Sab196087
35915088Sab196087
35925088Sab196087 /*
35935088Sab196087 * Process commands.
35945088Sab196087 *
35955088Sab196087 * If any -e options were used, then do them and
35965088Sab196087 * immediately exit. On error, exit immediately without
35975088Sab196087 * updating the target ELF file. On success, the 'write'
35985088Sab196087 * and 'quit' commands are implicit in this mode.
35995088Sab196087 *
36005088Sab196087 * If no -e options are used, read commands from stdin.
36015088Sab196087 * quit must be explicitly used. Exit is implicit on EOF.
36025088Sab196087 * If stdin is a tty, then errors do not cause the editor
36035088Sab196087 * to terminate. Rather, the error message is printed, and the
36045088Sab196087 * user prompted to continue.
36055088Sab196087 */
36065088Sab196087 if (batch_list != NULL) { /* -e was used */
36075088Sab196087 /* Compile the commands */
36085088Sab196087 for (i = 0; i < num_batch; i++)
36095088Sab196087 parse_user_cmd(batch_list[i]);
36105088Sab196087 free(batch_list);
36115088Sab196087
36125088Sab196087 /*
36135088Sab196087 * 'write' and 'quit' are implicit in this mode.
36145088Sab196087 * Add them as well.
36155088Sab196087 */
36165088Sab196087 if ((state.flags & ELFEDIT_F_READONLY) == 0)
36175088Sab196087 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_WRITE));
36185088Sab196087 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_QUIT));
36195088Sab196087
36205088Sab196087 /* And run them. This won't return, thanks to the 'quit' */
36215088Sab196087 dispatch_user_cmds();
36225088Sab196087 } else {
36235088Sab196087 state.input.is_tty = isatty(fileno(stdin));
36245088Sab196087 state.input.full_tty = state.input.is_tty &&
36255088Sab196087 isatty(fileno(stdout));
36265088Sab196087
36275088Sab196087 if (state.input.full_tty) {
36285088Sab196087 struct sigaction act;
36295088Sab196087
36305088Sab196087 act.sa_sigaction = sigint_handler;
36315088Sab196087 (void) sigemptyset(&act.sa_mask);
36325088Sab196087 act.sa_flags = 0;
36335088Sab196087 if (sigaction(SIGINT, &act, NULL) == -1) {
36345088Sab196087 int err = errno;
36355088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR,
36365088Sab196087 MSG_INTL(MSG_ERR_SIGACTION), strerror(err));
36375088Sab196087 }
36385088Sab196087 /*
36395088Sab196087 * If pager process exits before we are done
36405088Sab196087 * writing, we can see SIGPIPE. Prevent it
36415088Sab196087 * from killing the process.
36425088Sab196087 */
36435088Sab196087 (void) sigignore(SIGPIPE);
36445088Sab196087
36455088Sab196087 /* Open tecla handle for command line editing */
36465088Sab196087 state.input.gl = new_GetLine(ELFEDIT_MAXCMD,
36475088Sab196087 ELFEDIT_MAXHIST);
36485088Sab196087 /* Register our command completion function */
36495088Sab196087 (void) gl_customize_completion(state.input.gl,
36505088Sab196087 NULL, cmd_match_fcn);
36515088Sab196087
36525088Sab196087 /*
36535088Sab196087 * Make autoprint the default for interactive
36545088Sab196087 * sessions.
36555088Sab196087 */
36565088Sab196087 state.flags |= ELFEDIT_F_AUTOPRINT;
36575088Sab196087 }
36585088Sab196087 for (;;) {
36595088Sab196087 /*
36605088Sab196087 * If this is an interactive session, then use
36615088Sab196087 * sigsetjmp()/siglongjmp() to recover from bad
36625088Sab196087 * commands and keep going. A non-0 return from
36635088Sab196087 * sigsetjmp() means that an error just occurred.
36645088Sab196087 * In that case, we simply restart this loop.
36655088Sab196087 */
36665088Sab196087 if (state.input.is_tty) {
36675088Sab196087 if (sigsetjmp(state.msg_jbuf.env, 1) != 0) {
36685088Sab196087 if (state.input.full_tty)
36695088Sab196087 gl_abandon_line(state.input.gl);
36705088Sab196087 continue;
36715088Sab196087 }
36725088Sab196087 state.msg_jbuf.active = TRUE;
36735088Sab196087 }
36745088Sab196087
36755088Sab196087 /*
36765088Sab196087 * Force all output out before each command.
36775088Sab196087 * This is a no-OP when a tty is in use, but
36785088Sab196087 * in a pipeline, it ensures that the block
36795088Sab196087 * mode buffering doesn't delay output past
36805088Sab196087 * the completion of each command.
36815088Sab196087 *
36825088Sab196087 * If we didn't do this, the output would eventually
36835088Sab196087 * arrive at its destination, but the lag can be
36845088Sab196087 * annoying when you pipe the output into a tool
36855088Sab196087 * that displays the results in real time.
36865088Sab196087 */
36875088Sab196087 (void) fflush(stdout);
36885088Sab196087 (void) fflush(stderr);
36895088Sab196087
36905088Sab196087 parse_user_cmd(read_cmd());
36915088Sab196087 dispatch_user_cmds();
36925088Sab196087 state.msg_jbuf.active = FALSE;
36935088Sab196087 }
36945088Sab196087 }
36955088Sab196087
36965088Sab196087
36975088Sab196087 /*NOTREACHED*/
36985088Sab196087 return (0);
36995088Sab196087 }
3700