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