preprocessor cleanup: __sparc
[unleashed/tickless.git] / usr / src / cmd / sgs / elfedit / common / elfedit.c
blob8a655841979ea49a03f04640e69891469b7da443
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <stdarg.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <signal.h>
35 #include <dirent.h>
36 #include <libelf.h>
37 #include <gelf.h>
38 #include <conv.h>
39 #include <dlfcn.h>
40 #include <link.h>
41 #include <stdarg.h>
42 #include <libgen.h>
43 #include <libintl.h>
44 #include <locale.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <limits.h>
49 #include <strings.h>
50 #include <sgs.h>
51 #include "msg.h"
52 #include "_elfedit.h"
53 #include <debug.h> /* liblddb */
58 * Column at which elfedit_format_command_usage() will wrap the
59 * generated usage string if the wrap argument is True (1).
61 #define USAGE_WRAP_COL 55
67 * Type used to represent a string buffer that can grow as needed
68 * to hold strings of arbitrary length. The user should declare
69 * variables of this type sa static. The strbuf_ensure_size() function
70 * is used to ensure that it has a minimum desired size.
72 typedef struct {
73 char *buf; /* String buffer */
74 size_t n; /* Size of buffer */
75 } STRBUF;
81 * Types used by tokenize_user_cmd() to represent the result of
82 * spliting a user command into individual tokens.
84 typedef struct {
85 char *tok_str; /* Token string */
86 size_t tok_len; /* strlen(str) */
87 size_t tok_line_off; /* Token offset in original string */
88 } TOK_ELT;
89 typedef struct {
90 size_t tokst_cmd_len; /* Length of original user command, without */
91 /* newline or NULL termination chars */
92 size_t tokst_str_size; /* Space needed to hold all the resulting */
93 /* tokens, including terminating NULL */
94 TOK_ELT *tokst_buf; /* The array of tokens */
95 size_t tokst_cnt; /* # of tokens in array */
96 size_t tokst_bufsize; /* capacity of array */
97 } TOK_STATE;
102 /* State block used by gettok_init() and gettok() */
103 typedef struct {
104 const char *gtok_buf; /* Addr of buffer containing string */
105 char *gtok_cur_buf; /* Addr withing buffer for next token */
106 int gtok_inc_null_final; /* True if final NULL token used */
107 int gtok_null_seen; /* True when NULL byte seen */
108 TOK_ELT gtok_last_token; /* Last token parsed */
110 } GETTOK_STATE;
116 * The elfedit_cpl_*() functions are used for command line completion.
117 * Currently this uses the tecla library, but to allow for changing the
118 * library used, we hide all tecla interfaces from our modules. Instead,
119 * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
120 * address of that struct as an opaque handle to the modules. Since the
121 * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
122 * as necessary.
124 typedef struct {
125 WordCompletion *ecpl_cpl; /* tecla handle */
126 const char *ecpl_line; /* raw input line */
127 int ecpl_word_start; /* start offset within line */
128 int ecpl_word_end; /* offset just past token */
130 * ecpl_add_mod_colon is a secret handshake between
131 * elfedit_cpl_command() and elfedit_cpl_add_match(). It adds
132 * ':' to end of matched modules.
134 int ecpl_add_mod_colon;
135 const char *ecpl_token_str; /* token being completed */
136 size_t ecpl_token_len; /* strlen(ecpl_token_str) */
137 } ELFEDIT_CPL_STATE;
142 /* This structure maintains elfedit global state */
143 STATE_T state;
148 * Define a pair of static global variables that contain the
149 * ISA strings that correspond to %i and %I tokens in module search
150 * paths.
152 * isa_i_str - The ISA string for the currently running program
153 * isa_I_str - For 64-bit programs, the same as isa_i_str. For
154 * 32-bit programs, an empty string.
157 #ifdef __i386
158 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32);
159 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
160 #endif
161 #ifdef __amd64
162 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64);
163 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64);
164 #endif
168 /* Forward declarations */
169 static void free_user_cmds(void);
170 static void elfedit_pager_cleanup(void);
175 * We supply this function for the msg module
177 const char *
178 _elfedit_msg(Msg mid)
180 return (gettext(MSG_ORIG(mid)));
185 * Copy at most min(cpsize, dstsize-1) bytes from src into dst,
186 * truncating src if necessary. The result is always null-terminated.
188 * entry:
189 * dst - Destination buffer
190 * src - Source string
191 * dstsize - sizeof(dst)
193 * note:
194 * This is similar to strncpy(), but with two modifications:
195 * 1) You specify the number of characters to copy, not just
196 * the size of the destination. Hence, you can copy non-NULL
197 * terminated strings.
198 * 2) The destination is guaranteed to be NULL terminated. strncpy()
199 * does not terminate a completely full buffer.
201 static void
202 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize)
204 if (cpsize >= dstsize)
205 cpsize = dstsize - 1;
206 if (cpsize > 0)
207 (void) strncpy(dst, src, cpsize + 1);
208 dst[cpsize] = '\0';
213 * Calls exit() on behalf of elfedit.
215 void
216 elfedit_exit(int status)
218 if (state.file.present) {
219 /* Exiting with unflushed changes pending? Issue debug notice */
220 if (state.file.dirty)
221 elfedit_msg(ELFEDIT_MSG_DEBUG,
222 MSG_INTL(MSG_DEBUG_DIRTYEXIT));
225 * If the edit file is marked for unlink on exit, then
226 * take care of it here.
228 if (state.file.unlink_on_exit) {
229 elfedit_msg(ELFEDIT_MSG_DEBUG,
230 MSG_INTL(MSG_DEBUG_UNLINKFILE),
231 state.file.outfile);
232 (void) unlink(state.file.outfile);
236 exit(status);
241 * Standard message function for elfedit. All user visible
242 * output, for error or informational reasons, should go through
243 * this function.
245 * entry:
246 * type - Type of message. One of the ELFEDIT_MSG_* values.
247 * format, ... - As per the printf() family
249 * exit:
250 * The desired message has been output. For informational
251 * messages, control returns to the caller. For errors,
252 * this routine will terminate execution or strip the execution
253 * stack and return control directly to the outer control loop.
254 * In either case, the caller will not receive control.
256 /*PRINTFLIKE2*/
257 void
258 elfedit_msg(elfedit_msg_t type, const char *format, ...)
260 typedef enum { /* What to do after finished */
261 DISP_RET = 0, /* Return to caller */
262 DISP_JMP = 1, /* if (interactive) longjmp else exit */
263 DISP_EXIT = 2 /* exit under all circumstances */
264 } DISP;
266 va_list args;
267 FILE *stream = stderr;
268 DISP disp = DISP_RET;
269 int do_output = 1;
270 int need_prefix = 1;
272 va_start(args, format);
274 switch (type) {
275 case ELFEDIT_MSG_ERR:
276 case ELFEDIT_MSG_CMDUSAGE:
277 disp = DISP_JMP;
278 break;
279 case ELFEDIT_MSG_FATAL:
280 disp = DISP_EXIT;
281 break;
282 case ELFEDIT_MSG_USAGE:
283 need_prefix = 0;
284 break;
285 case ELFEDIT_MSG_DEBUG:
286 if (!(state.flags & ELFEDIT_F_DEBUG))
287 return;
288 stream = stdout;
289 break;
290 case ELFEDIT_MSG_QUIET:
291 do_output = 0;
292 disp = DISP_JMP;
293 break;
298 * If there is a pager process running, we are returning to the
299 * caller, and the output is going to stdout, then let the
300 * pager handle it instead of writing it directly from this process.
301 * That way, the output gets paged along with everything else.
303 * If there is a pager process running, and we are not returning
304 * to the caller, then end the pager process now, before we generate
305 * any new output. This allows for any text buffered in the pager
306 * pipe to be output before the new stuff.
308 if (state.pager.fptr != NULL) {
309 if (disp == DISP_RET) {
310 if (stream == stdout)
311 stream = state.pager.fptr;
312 } else {
313 elfedit_pager_cleanup();
318 * If this message is coming from within the libtecla command
319 * completion code, call gl_normal_io() to give the library notice.
320 * That function sets the tty back to cooked mode and advances
321 * the cursor to the beginning of the next line so that our output
322 * will appear properly. When we return to the command completion code,
323 * tecla will re-enter raw mode and redraw the current command line.
325 if (state.input.in_tecla)
326 (void) gl_normal_io(state.input.gl);
328 if (do_output) {
329 if (need_prefix)
330 (void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT));
331 (void) vfprintf(stream, format, args);
332 (void) fflush(stream);
334 va_end(args);
337 * If this is an error, then we do not return to the caller.
338 * The action taken depends on whether the outer loop has registered
339 * a jump buffer for us or not.
341 if (disp != DISP_RET) {
342 if (state.msg_jbuf.active && (disp == DISP_JMP)) {
343 /* Free the user command list */
344 free_user_cmds();
346 /* Clean up to reflect effect of non-local goto */
347 state.input.in_tecla = FALSE;
349 /* Jump to the outer loop to resume */
350 siglongjmp(state.msg_jbuf.env, 1);
351 } else {
352 elfedit_exit(1);
359 * Wrapper on elfedit_msg() that issues an error that results from
360 * a call to libelf.
362 * entry:
363 * file - Name of ELF object
364 * libelf_rtn_name - Name of routine that was called
366 * exit:
367 * An error has been issued that shows the routine called
368 * and the libelf error string for it from elf_errmsg().
369 * This routine does not return to the caller.
371 void
372 elfedit_elferr(const char *file, const char *libelf_rtn_name)
374 const char *errstr = elf_errmsg(elf_errno());
376 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file,
377 libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN));
382 * Start an output pager process for elfedit_printf()/elfedit_write() to use.
384 * note:
385 * If this elfedit session is not interactive, then no pager is
386 * started. Paging is only intended for interactive use. The caller
387 * is not supposed to worry about this point, but simply to use
388 * this function to flag situations in which paging might be needed.
390 void
391 elfedit_pager_init(void)
393 const char *errstr;
394 const char *cmd;
395 int err;
398 * If there is no pager process running, start one.
399 * Only do this for interactive sessions --- elfedit_pager()
400 * won't use a pager in batch mode.
402 if (state.msg_jbuf.active && state.input.full_tty &&
403 (state.pager.fptr == NULL)) {
405 * If the user has the PAGER environment variable set,
406 * then we will use that program. Otherwise we default
407 * to /bin/more.
409 cmd = getenv(MSG_ORIG(MSG_STR_PAGER));
410 if ((cmd == NULL) || (*cmd == '\0'))
411 cmd = MSG_ORIG(MSG_STR_BINMORE);
414 * The popen() manpage says that on failure, it "may set errno",
415 * which is somewhat ambiguous. We explicitly zero it here, and
416 * assume that any change is due to popen() failing.
418 errno = 0;
419 state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W));
420 if (state.pager.fptr == NULL) {
421 err = errno;
422 errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) :
423 strerror(err);
424 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC),
425 MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr);
432 * If there is a pager process present, close it out.
434 * note:
435 * This function is called from within elfedit_msg(), and as
436 * such, must not use elfedit_msg() to report errors. Furthermore,
437 * any such errors are not a sufficient reason to terminate the process
438 * or to longjmp(). This is a rare case where errors are written
439 * directly to stderr.
441 static void
442 elfedit_pager_cleanup(void)
444 if (state.pager.fptr != NULL) {
445 if (pclose(state.pager.fptr) == -1)
446 (void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI));
448 state.pager.fptr = NULL;
454 * Print general formtted text for the user, using printf()-style
455 * formatting. Uses the pager process if one has been started, or
456 * stdout otherwise.
458 void
459 elfedit_printf(const char *format, ...)
461 va_list args;
462 int err;
463 FILE *fptr;
464 int pager;
465 int broken_pipe = 0;
468 * If there is a pager process, then use it. Otherwise write
469 * directly to stdout.
471 pager = (state.pager.fptr != NULL);
472 fptr = pager ? state.pager.fptr : stdout;
474 va_start(args, format);
475 errno = 0;
476 err = vfprintf(fptr, format, args);
478 /* Did we fail because a child pager process has exited? */
479 broken_pipe = pager && (err < 0) && (errno == EPIPE);
481 va_end(args);
484 * On error, we simply issue the error without cleaning up
485 * the pager process. The message code handles that as a standard
486 * part of error processing.
488 * We handle failure due to an exited pager process differently
489 * than a normal error, because it is usually due to the user
490 * intentionally telling it to.
492 if (err < 0) {
493 if (broken_pipe)
494 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
495 else
496 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
502 * Some our modules use liblddb routines to format ELF output.
503 * In order to ensure that such output is sent to the pager pipe
504 * when there is one, and stdout otherwise, we redefine the dbg_print()
505 * function here.
507 * This item should be defined NODIRECT.
509 /* PRINTFLIKE2 */
510 void
511 dbg_print(Lm_list *lml, const char *format, ...)
513 va_list ap;
514 int err;
515 FILE *fptr;
516 int pager;
517 int broken_pipe = 0;
519 #if defined(lint)
521 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
522 * Supress the lint error by making a dummy assignment.
524 lml = 0;
525 #endif
528 * If there is a pager process, then use it. Otherwise write
529 * directly to stdout.
531 pager = (state.pager.fptr != NULL);
532 fptr = pager ? state.pager.fptr : stdout;
534 va_start(ap, format);
535 errno = 0;
536 err = vfprintf(fptr, format, ap);
537 if (err >= 0)
538 err = fprintf(fptr, MSG_ORIG(MSG_STR_NL));
540 /* Did we fail because a child pager process has exited? */
541 broken_pipe = (err < 0) && pager && (errno == EPIPE);
543 va_end(ap);
546 * On error, we simply issue the error without cleaning up
547 * the pager process. The message code handles that as a standard
548 * part of error processing.
550 * We handle failure due to an exited pager process differently
551 * than a normal error, because it is usually due to the user
552 * intentionally telling it to.
554 if (err < 0) {
555 if (broken_pipe)
556 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
557 else
558 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
564 * Write raw bytes of text in a manner similar to fwrite().
565 * Uses the pager process if one has been started, or
566 * stdout otherwise.
568 void
569 elfedit_write(const void *ptr, size_t size)
571 FILE *fptr;
572 int err;
575 * If there is a pager process, then use it. Otherwise write
576 * directly to stdout.
578 fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr;
580 if (fwrite(ptr, 1, size, fptr) != size) {
581 err = errno;
582 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE),
583 strerror(err));
589 * Convert the NULL terminated string to the form used by the C
590 * language to represent literal strings. See conv_str_to_c_literal()
591 * for details.
593 * This routine differs from conv_str_to_c_literal() in two ways:
594 * 1) String is NULL terminated instead of counted
595 * 2) Signature of outfunc
597 * entry:
598 * str - String to be processed
599 * outfunc - Function to be called to move output characters. Note
600 * that this function has the same signature as elfedit_write(),
601 * and that function can be used to write the characters to
602 * the output.
604 * exit:
605 * The string has been processed, with the resulting data passed
606 * to outfunc for processing.
608 static void
609 elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue)
611 elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue;
613 (* outfunc)(ptr, size);
616 void
617 elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc)
619 conv_str_to_c_literal(str, strlen(str),
620 elfedit_str_to_c_literal_cb, (void *) outfunc);
625 * Wrappers on malloc() and realloc() that check the result for success
626 * and issue an error if not. The caller can use the result of these
627 * functions without checking for a NULL pointer, as we do not return to
628 * the caller in the failure case.
630 void *
631 elfedit_malloc(const char *item_name, size_t size)
633 void *m;
635 m = malloc(size);
636 if (m == NULL) {
637 int err = errno;
638 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
639 item_name, strerror(err));
642 return (m);
645 void *
646 elfedit_realloc(const char *item_name, void *ptr, size_t size)
648 void *m;
650 m = realloc(ptr, size);
651 if (m == NULL) {
652 int err = errno;
653 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
654 item_name, strerror(err));
657 return (m);
662 * Ensure that the given buffer has room for n bytes of data.
664 static void
665 strbuf_ensure_size(STRBUF *str, size_t size)
667 #define INITIAL_STR_ALLOC 128
669 size_t n;
671 n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n;
672 while (size > n) /* Double buffer until string fits */
673 n *= 2;
674 if (n != str->n) { /* Alloc new string buffer if needed */
675 str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR),
676 str->buf, n);
677 str->n = n;
680 #undef INITIAL_STR_ALLOC
685 * Extract the argument/option information for the next item referenced
686 * by optarg, and advance the pointer to the next item.
688 * entry:
689 * optarg - Address of pointer to argument or option array
690 * item - Struct to be filled in.
692 * exit:
693 * The item block has been filled in with the information for
694 * the next item in the optarg array. *optarg has been advanced
695 * to the next item.
697 void
698 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item)
701 * Array of inheritable options/arguments. Indexed by one less
702 * than the corresponding ELFEDIT_STDOA_ value.
704 static const elfedit_optarg_item_t stdoa[] = {
705 /* ELFEDIT_STDOA_O */
706 { MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE),
707 /* MSG_INTL(MSG_STDOA_OPTDESC_O) */
708 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O,
709 ELFEDIT_CMDOA_F_VALUE },
711 /* ELFEDIT_STDOA_AND */
712 { MSG_ORIG(MSG_STR_MINUS_AND), NULL,
713 /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
714 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 },
716 /* ELFEDIT_STDOA_CMP */
717 { MSG_ORIG(MSG_STR_MINUS_CMP), NULL,
718 /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
719 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 },
721 /* ELFEDIT_STDOA_OR */
722 { MSG_ORIG(MSG_STR_MINUS_OR), NULL,
723 /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
724 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 },
727 elfedit_cmd_optarg_t *oa;
730 /* Grab first item, advance the callers pointer over it */
731 oa = (*optarg)++;
733 if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
734 /* Values are pre-chewed in the stdoa array above */
735 *item = stdoa[((uintptr_t)oa->oa_name) - 1];
738 * Set the inherited flag so that elfedit_optarg_helpstr()
739 * can tell who is responsible for translating the help string.
741 item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT;
742 } else { /* Non-inherited item */
743 item->oai_name = oa->oa_name;
744 if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) {
745 item->oai_vname = oa[1].oa_name;
747 /* Advance users pointer past value element */
748 (*optarg)++;
749 } else {
750 item->oai_vname = NULL;
752 item->oai_help = oa->oa_help;
753 item->oai_flags = oa->oa_flags;
757 * The module determines the idmask and excmask fields whether
758 * or not inheritance is in play.
760 item->oai_idmask = oa->oa_idmask;
761 item->oai_excmask = oa->oa_excmask;
767 * Return the help string for an option/argument item, as returned
768 * by elfedit_next_optarg(). This routine handles the details of
769 * knowing whether the string is provided by elfedit itself (inherited),
770 * or needs to be translated by the module.
772 const char *
773 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item)
776 * The help string from an inherited item comes right out
777 * of the main elfedit string table.
779 if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT)
780 return (MSG_INTL((Msg) item->oai_help));
783 * If the string is defined by the module, then we need to
784 * have the module translate it for us.
786 return ((* mod->mod_i18nhdl_to_str)(item->oai_help));
792 * Used by usage_optarg() to insert a character into the output buffer,
793 * advancing the buffer pointer and current column, and reducing the
794 * amount of remaining space.
796 static void
797 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col)
800 *(*cur)++ = ch;
801 **cur = '\0';
802 (*n)--;
803 (*cur_col)++;
807 * Used by usage_optarg() to insert a string into the output
808 * buffer, advancing the buffer pointer and current column, and reducing
809 * the amount of remaining space.
811 static void
812 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col,
813 const char *format, ...)
815 size_t len;
816 va_list args;
818 va_start(args, format);
819 len = vsnprintf(*cur, *n, format, args);
820 va_end(args);
822 *cur += len;
823 *n -= len;
824 *cur_col += len;
827 * Used by usage_optarg() to insert an optarg item string into the output
828 * buffer, advancing the buffer pointer and current column, and reducing
829 * the amount of remaining space.
831 static void
832 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur,
833 size_t *n, size_t *cur_col)
835 size_t len;
837 if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) {
838 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2),
839 item->oai_name, item->oai_vname);
840 } else {
841 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG),
842 item->oai_name);
844 *cur += len;
845 *n -= len;
846 *cur_col += len;
852 * Write the options/arguments to the usage string.
854 * entry:
855 * main_buf_n - Size of main buffer from which buf and buf_n are
856 * allocated.
857 * buf - Address of pointer to where next item is to be placed.
858 * buf_n - Address of count of remaining bytes in buffer
859 * buf_cur_col - Address of current output column for current line
860 * of generated string.
861 * optarg - Options list
862 * isopt - True if these are options, false for arguments.
863 * wrap_str - String to indent wrapped lines. If NULL, lines
864 * are not wrapped
866 static void
867 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col,
868 elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str)
871 * An option can be combined into a simple format if it lacks
872 * these flags and is only one character in length.
874 static const elfedit_cmd_oa_flag_t exflags =
875 (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT);
878 * A static buffer, which is grown as needed to accomodate
879 * the maximum usage string seen.
881 static STRBUF simple_str;
883 char *cur = *buf;
884 size_t n = *buf_n;
885 size_t cur_col = *buf_cur_col;
886 int len;
887 int use_simple = 0;
888 elfedit_optarg_item_t item;
889 elfedit_cmd_oa_mask_t optmask = 0;
890 int use_bkt;
893 * If processing options, pull the 1-character ones that don't have
894 * an associated value and don't have any mutual exclusion issues into
895 * a single combination string to go at the beginning of the usage.
897 if (isopt) {
898 elfedit_cmd_optarg_t *tmp_optarg = optarg;
899 char *s;
902 * The simple string is guaranteed to fit in the same
903 * amount of space reserved for the main buffer.
905 strbuf_ensure_size(&simple_str, main_buf_n);
906 s = simple_str.buf;
907 *s++ = ' ';
908 *s++ = '[';
909 *s++ = '-';
910 while (tmp_optarg->oa_name != NULL) {
911 elfedit_next_optarg(&tmp_optarg, &item);
912 if (((item.oai_flags & exflags) == 0) &&
913 (item.oai_name[2] == '\0') &&
914 (item.oai_excmask == 0)) {
915 optmask |= item.oai_idmask;
916 *s++ = item.oai_name[1];
921 * If we found more than one, then finish the string and
922 * add it. Don't do this for a single option, because
923 * it looks better in that case if the option shows up
924 * in alphabetical order rather than being hoisted.
926 use_simple = (s > (simple_str.buf + 4));
927 if (use_simple) {
928 *s++ = ']';
929 *s++ = '\0';
930 usage_optarg_insert_str(&cur, &n, &cur_col,
931 MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf);
932 } else {
933 /* Not using it, so reset the cumulative options mask */
934 optmask = 0;
938 while (optarg->oa_name != NULL) {
939 elfedit_next_optarg(&optarg, &item);
941 if (isopt) {
943 * If this is an option that was pulled into the
944 * combination string above, then skip over it.
946 if (use_simple && ((item.oai_flags & exflags) == 0) &&
947 (item.oai_name[2] == '\0') &&
948 (item.oai_excmask == 0))
949 continue;
952 * If this is a mutual exclusion option that was
953 * picked up out of order by a previous iteration
954 * of this loop, then skip over it.
956 if ((optmask & item.oai_idmask) != 0)
957 continue;
959 /* Add this item to the accumulating options mask */
960 optmask |= item.oai_idmask;
963 /* Wrap line, or insert blank separator */
964 if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) {
965 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE),
966 wrap_str);
967 cur += len;
968 n -= len;
969 cur_col = len - 1; /* Don't count the newline */
970 } else {
971 usage_optarg_insert_ch(' ', &cur, &n, &cur_col);
974 use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt;
975 if (use_bkt)
976 usage_optarg_insert_ch('[', &cur, &n, &cur_col);
978 /* Add the item to the buffer */
979 usage_optarg_insert_item(&item, &cur, &n, &cur_col);
982 * If this item has a non-zero mutual exclusion mask,
983 * then look for the other items and display them all
984 * together with alternation (|). Note that plain arguments
985 * cannot have a non-0 exclusion mask, so this is
986 * effectively options-only (isopt != 0).
988 if (item.oai_excmask != 0) {
989 elfedit_cmd_optarg_t *tmp_optarg = optarg;
990 elfedit_optarg_item_t tmp_item;
993 * When showing alternation, elipses for multiple
994 * copies need to appear inside the [] brackets.
996 if (item.oai_flags & ELFEDIT_CMDOA_F_MULT)
997 usage_optarg_insert_str(&cur, &n, &cur_col,
998 MSG_ORIG(MSG_STR_ELIPSES));
1001 while (tmp_optarg->oa_name != NULL) {
1002 elfedit_next_optarg(&tmp_optarg, &tmp_item);
1003 if ((item.oai_excmask & tmp_item.oai_idmask) ==
1005 continue;
1006 usage_optarg_insert_str(&cur, &n, &cur_col,
1007 MSG_ORIG(MSG_STR_SP_BAR_SP));
1008 usage_optarg_insert_item(&tmp_item,
1009 &cur, &n, &cur_col);
1012 * Add it to the mask of seen options.
1013 * This will keep us from showing it twice.
1015 optmask |= tmp_item.oai_idmask;
1018 if (use_bkt)
1019 usage_optarg_insert_ch(']', &cur, &n, &cur_col);
1022 * If alternation was not shown above (non-zero exclusion mask)
1023 * then the elipses for multiple copies are shown outside
1024 * any [] brackets.
1026 if ((item.oai_excmask == 0) &&
1027 (item.oai_flags & ELFEDIT_CMDOA_F_MULT))
1028 usage_optarg_insert_str(&cur, &n, &cur_col,
1029 MSG_ORIG(MSG_STR_ELIPSES));
1033 *buf = cur;
1034 *buf_n = n;
1035 *buf_cur_col = cur_col;
1041 * Format the usage string for a command into a static buffer and
1042 * return the pointer to the user. The resultant string is valid
1043 * until the next call to this routine, and which point it
1044 * will be overwritten or the memory is freed.
1046 * entry:
1047 * mod, cmd - Module and command definitions for command to be described
1048 * wrap_str - NULL, or string to be used to indent when
1049 * lines are wrapped. If NULL, no wrapping is done, and
1050 * all output is on a single line.
1051 * cur_col - Starting column at which the string will be displayed.
1052 * Ignored if wrap_str is NULL.
1054 const char *
1055 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd,
1056 const char *wrap_str, size_t cur_col)
1060 * A static buffer, which is grown as needed to accomodate
1061 * the maximum usage string seen.
1063 static STRBUF str;
1065 elfedit_cmd_optarg_t *optarg;
1066 size_t len, n, elipses_len;
1067 char *cur;
1068 elfedit_optarg_item_t item;
1071 * Estimate a worst case size for the usage string:
1072 * - module name
1073 * - lengths of the strings
1074 * - every option or argument is enclosed in brackets
1075 * - space in between each item, with an alternation (" | ")
1076 * - elipses will be displayed with each option and argument
1078 n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6;
1079 elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES));
1080 if ((optarg = cmd->cmd_opt) != NULL)
1081 while (optarg->oa_name != NULL) {
1082 elfedit_next_optarg(&optarg, &item);
1083 n += strlen(item.oai_name) + 5 + elipses_len;
1085 if ((optarg = cmd->cmd_args) != NULL)
1086 while (optarg->oa_name != NULL) {
1087 elfedit_next_optarg(&optarg, &item);
1088 n += strlen(item.oai_name) + 5 + elipses_len;
1090 n++; /* Null termination */
1093 * If wrapping lines, we insert a newline and then wrap_str
1094 * every USAGE_WRAP_COL characters.
1096 if (wrap_str != NULL)
1097 n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) *
1098 (strlen(wrap_str) + 1);
1100 strbuf_ensure_size(&str, n);
1102 /* Command name */
1103 cur = str.buf;
1104 n = str.n;
1105 if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0)
1106 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD),
1107 cmd->cmd_name[0]);
1108 else
1109 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD),
1110 mod->mod_name, cmd->cmd_name[0]);
1111 cur += len;
1112 n -= len;
1113 cur_col += len;
1115 if (cmd->cmd_opt != NULL)
1116 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt,
1117 1, wrap_str);
1118 if (cmd->cmd_args != NULL)
1119 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args,
1120 0, wrap_str);
1122 return (str.buf);
1126 * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE
1127 * error giving usage information for the command currently
1128 * referenced by state.cur_cmd.
1130 void
1131 elfedit_command_usage(void)
1133 elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD),
1134 elfedit_format_command_usage(state.cur_cmd->ucmd_mod,
1135 state.cur_cmd->ucmd_cmd, NULL, 0));
1140 * This function allows the loadable modules to get the command line
1141 * flags.
1143 elfedit_flag_t
1144 elfedit_flags(void)
1146 return (state.flags);
1150 * This function is used to register a per-command invocation output style
1151 * that will momentarily override the global output style for the duration
1152 * of the current command. This function must only be called by an
1153 * active command.
1155 * entry:
1156 * str - One of the valid strings for the output style
1158 void
1159 elfedit_set_cmd_outstyle(const char *str)
1161 if ((state.cur_cmd != NULL) && (str != NULL)) {
1162 if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0)
1163 elfedit_msg(ELFEDIT_MSG_ERR,
1164 MSG_INTL(MSG_ERR_BADOSTYLE), str);
1165 state.cur_cmd->ucmd_ostyle_set = 1;
1170 * This function allows the loadable modules to get the output style.
1172 elfedit_outstyle_t
1173 elfedit_outstyle(void)
1176 * If there is an active per-command output style,
1177 * return it.
1179 if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set))
1180 return (state.cur_cmd->ucmd_ostyle);
1183 return (state.outstyle);
1187 * Return the command descriptor of the currently executing command.
1188 * For use only by the modules or code called by the modules.
1190 elfeditGC_cmd_t *
1191 elfedit_curcmd(void)
1193 return (state.cur_cmd->ucmd_cmd);
1197 * Build a dynamically allocated elfedit_obj_state_t struct that
1198 * contains a cache of the ELF file contents. This pre-chewed form
1199 * is fed to each command, reducing the amount of ELF boilerplate
1200 * code each command needs to contain.
1202 * entry:
1203 * file - Name of file to process
1205 * exit:
1206 * Fills state.elf with the necessary information for the open file.
1208 * note: The resulting elfedit_obj_state_t is allocated from a single
1209 * piece of memory, such that a single call to free() suffices
1210 * to release it as well as any memory it references.
1212 static void
1213 init_obj_state(const char *file)
1215 int fd;
1216 Elf *elf;
1217 int open_flag;
1220 * In readonly mode, we open the file readonly so that it is
1221 * impossible to modify the file by accident. This also allows
1222 * us to access readonly files, perhaps in a case where we don't
1223 * intend to change it.
1225 * We always use ELF_C_RDWR with elf_begin(), even in a readonly
1226 * session. This allows us to modify the in-memory image, which
1227 * can be useful when examining a file, even though we don't intend
1228 * to modify the on-disk data. The file is not writable in
1229 * this case, and we don't call elf_update(), so it is safe to do so.
1231 open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR);
1232 if ((fd = open(file, open_flag)) == -1) {
1233 int err = errno;
1234 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE),
1235 file, strerror(err));
1237 (void) elf_version(EV_CURRENT);
1238 elf = elf_begin(fd, ELF_C_RDWR, NULL);
1239 if (elf == NULL) {
1240 (void) close(fd);
1241 elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN));
1242 /*NOTREACHED*/
1245 /* We only handle standalone ELF files */
1246 switch (elf_kind(elf)) {
1247 case ELF_K_AR:
1248 (void) close(fd);
1249 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file);
1250 break;
1251 case ELF_K_ELF:
1252 break;
1253 default:
1254 (void) close(fd);
1255 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE),
1256 file);
1257 break;
1261 * Tell libelf that we take responsibility for object layout.
1262 * Otherwise, it will compute "proper" values for layout and
1263 * alignment fields, and these values can overwrite the values
1264 * set in the elfedit session. We are modifying existing
1265 * objects --- the layout concerns have already been dealt
1266 * with when the object was built.
1268 (void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
1270 /* Fill in state.elf.obj_state */
1271 state.elf.elfclass = gelf_getclass(elf);
1272 switch (state.elf.elfclass) {
1273 case ELFCLASS32:
1274 elfedit32_init_obj_state(file, fd, elf);
1275 break;
1276 case ELFCLASS64:
1277 elfedit64_init_obj_state(file, fd, elf);
1278 break;
1279 default:
1280 (void) close(fd);
1281 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS),
1282 file);
1283 break;
1288 #ifdef DEBUG_MODULE_LIST
1290 * Debug routine. Dump the module list to stdout.
1292 static void
1293 dbg_module_list(char *title)
1295 MODLIST_T *m;
1297 printf("<MODULE LIST: %s>\n", title);
1298 for (m = state.modlist; m != NULL; m = m->next) {
1299 printf("Module: >%s<\n", m->mod->mod_name);
1300 printf(" hdl: %llx\n", m->dl_hdl);
1301 printf(" path: >%s<\n", m->path ? m->path : "<builtin>");
1303 printf("<END OF MODULE LIST>\n");
1305 #endif
1309 * Search the module list for the named module.
1311 * entry:
1312 * name - Name of module to find
1313 * insdef - Address of variable to receive address of predecessor
1314 * node to the desired one.
1316 * exit:
1317 * If the module is it is found, this routine returns the pointer to
1318 * its MODLIST_T structure. *insdef references the predecessor node, or
1319 * is NULL if the found item is at the head of the list.
1321 * If the module is not found, NULL is returned. *insdef references
1322 * the predecessor node of the position where an entry for this module
1323 * would be placed, or NULL if it would go at the beginning.
1325 static MODLIST_T *
1326 module_loaded(const char *name, MODLIST_T **insdef)
1328 MODLIST_T *moddef;
1329 int cmp;
1331 *insdef = NULL;
1332 moddef = state.modlist;
1333 if (moddef != NULL) {
1334 cmp = strcasecmp(name, moddef->ml_mod->mod_name);
1335 if (cmp == 0) { /* Desired module is first in list */
1336 return (moddef);
1337 } else if (cmp > 0) { /* cmp > 0: Insert in middle/end */
1338 *insdef = moddef;
1339 moddef = moddef->ml_next;
1340 cmp = -1;
1341 while (moddef && (cmp < 0)) {
1342 cmp = strcasecmp(moddef->ml_mod->mod_name,
1343 name);
1344 if (cmp == 0)
1345 return (moddef);
1346 if (cmp < 0) {
1347 *insdef = moddef;
1348 moddef = (*insdef)->ml_next;
1354 return (NULL);
1359 * Determine if a file is a sharable object based on its file path.
1360 * If path ends in a .so, followed optionally by a period and 1 or more
1361 * digits, we say that it is and return a pointer to the first character
1362 * of the suffix. Otherwise NULL is returned.
1364 static const char *
1365 path_is_so(const char *path)
1367 int dotso_len;
1368 const char *tail;
1369 size_t len;
1371 len = strlen(path);
1372 if (len == 0)
1373 return (NULL);
1374 tail = path + len;
1375 if (isdigit(*(tail - 1))) {
1376 while ((tail > path) && isdigit(*(tail - 1)))
1377 tail--;
1378 if ((tail <= path) || (*tail != '.'))
1379 return (NULL);
1381 dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO));
1382 if ((tail - path) < dotso_len)
1383 return (NULL);
1384 tail -= dotso_len;
1385 if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0)
1386 return (tail);
1388 return (NULL);
1393 * Locate the start of the unsuffixed file name within path. Returns pointer
1394 * to first character of that name in path.
1396 * entry:
1397 * path - Path to be examined.
1398 * tail - NULL, or pointer to position at tail of path from which
1399 * the search for '/' characters should start. If NULL,
1400 * strlen() is used to locate the end of the string.
1401 * buf - NULL, or buffer to receive a copy of the characters that
1402 * lie between the start of the filename and tail.
1403 * bufsize - sizeof(buf)
1405 * exit:
1406 * The pointer to the first character of the unsuffixed file name
1407 * within path is returned. If buf is non-NULL, the characters
1408 * lying between that point and tail (or the end of path if tail
1409 * is NULL) are copied into buf.
1411 static const char *
1412 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz)
1414 const char *s;
1416 if (tail == NULL)
1417 tail = path + strlen(path);
1418 s = tail;
1419 while ((s > path) && (*(s - 1) != '/'))
1420 s--;
1421 if (buf != NULL)
1422 elfedit_strnbcpy(buf, s, tail - s, bufsiz);
1423 return (s);
1428 * Issue an error on behalf of load_module(), taking care to release
1429 * resources that routine may have aquired:
1431 * entry:
1432 * moddef - NULL, or a module definition to be released via free()
1433 * dl_hdl - NULL, or a handle to a sharable object to release via
1434 * dlclose().
1435 * dl_path - If dl_hdl is non-NULL, the path to the sharable object
1436 * file that was loaded.
1437 * format - A format string to pass to elfedit_msg(), containing
1438 * no more than (3) %s format codes, and no other format codes.
1439 * [s1-s4] - Strings to pass to elfedit_msg() to satisfy the four
1440 * allowed %s codes in format. Should be set to NULL if the
1441 * format string does not need them.
1443 * note:
1444 * This routine makes a copy of the s1-s4 strings before freeing any
1445 * memory or unmapping the sharable library. It is therefore safe to
1446 * use strings from moddef, or from the sharable library (which will
1447 * be unmapped) to satisfy the other arguments s1-s4.
1449 static void
1450 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path,
1451 const char *format, const char *s1, const char *s2, const char *s3,
1452 const char *s4)
1454 #define SCRBUFSIZE (PATH_MAX + 256) /* A path, plus some extra */
1456 char s1_buf[SCRBUFSIZE];
1457 char s2_buf[SCRBUFSIZE];
1458 char s3_buf[SCRBUFSIZE];
1459 char s4_buf[SCRBUFSIZE];
1462 * The caller may provide strings for s1-s3 that are from
1463 * moddef. If we free moddef, the printf() will die on access
1464 * to free memory. We could push back on the user and force
1465 * each call to carefully make copies of such data. However, this
1466 * is an easy case to miss. Furthermore, this is an error case,
1467 * and machine efficiency is not the main issue. We therefore make
1468 * copies of the s1-s3 strings here into auto variables, and then
1469 * use those copies. The user is freed from worrying about it.
1471 * We use oversized stack based buffers instead of malloc() to
1472 * reduce the number of ways that things can go wrong while
1473 * reporting the error.
1475 if (s1 != NULL)
1476 (void) strlcpy(s1_buf, s1, sizeof (s1_buf));
1477 if (s2 != NULL)
1478 (void) strlcpy(s2_buf, s2, sizeof (s2_buf));
1479 if (s3 != NULL)
1480 (void) strlcpy(s3_buf, s3, sizeof (s3_buf));
1481 if (s4 != NULL)
1482 (void) strlcpy(s4_buf, s4, sizeof (s4_buf));
1485 free(moddef);
1487 if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0))
1488 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
1489 dl_path, dlerror());
1491 elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf);
1492 #undef SCRBUFSIZE
1497 * Load a module sharable object for load_module().
1499 * entry:
1500 * path - Path of file to open
1501 * moddef - If this function issues a non-returning error, it will
1502 * first return the memory referenced by moddef. This argument
1503 * is not used otherwise.
1504 * must_exist - If True, we consider it to be an error if the file given
1505 * by path does not exist. If False, no error is issued
1506 * and a NULL value is quietly returned.
1508 * exit:
1509 * Returns a handle to the loaded object on success, or NULL if no
1510 * file was loaded.
1512 static void *
1513 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist)
1515 int fd;
1516 void *hdl;
1519 * If the file is not required to exist, and it doesn't, then
1520 * we want to quietly return without an error.
1522 if (!must_exist) {
1523 fd = open(path, O_RDONLY);
1524 if (fd >= 0) {
1525 (void) close(fd);
1526 } else if (errno == ENOENT) {
1527 return (NULL);
1531 if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL)
1532 load_module_err(moddef, NULL, NULL,
1533 MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL);
1535 return (hdl);
1540 * Sanity check option arguments to prevent common errors. The rest of
1541 * elfedit assumes these tests have been done, and does not check
1542 * again.
1544 static void
1545 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef,
1546 const char *mod_name, const char *cmd_name,
1547 void *dl_hdl, const char *dl_path)
1549 #define FAIL(_msg) errmsg = _msg; goto fail
1551 Msg errmsg;
1552 elfedit_cmd_oa_mask_t optmask = 0;
1554 for (; optarg->oa_name != NULL; optarg++) {
1556 * If ELFEDIT_CMDOA_F_INHERIT is set:
1557 * - oa_name must be a value in the range of
1558 * known ELFEDIT_STDOA_ values.
1559 * - oa_help must be NULL
1560 * - ELFEDIT_CMDOA_F_INHERIT must be the only flag set
1562 if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
1563 if ((((uintptr_t)optarg->oa_name) >
1564 ELFEDIT_NUM_STDOA) ||
1565 (optarg->oa_help != 0) ||
1566 (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT))
1568 * Can't use FAIL --- oa_name is not a valid
1569 * string, and load_module_err() looks at args.
1571 load_module_err(moddef, dl_hdl, dl_path,
1572 MSG_INTL(MSG_ERR_BADSTDOA), dl_path,
1573 mod_name, cmd_name, NULL);
1574 continue;
1577 if (isopt) {
1579 * Option name must start with a '-', and must
1580 * have at one following character.
1582 if (optarg->oa_name[0] != '-') {
1583 /* MSG_INTL(MSG_ERR_OPT_MODPRE) */
1584 FAIL(MSG_ERR_OPT_MODPRE);
1586 if (optarg->oa_name[1] == '\0') {
1587 /* MSG_INTL(MSG_ERR_OPT_MODLEN) */
1588 FAIL(MSG_ERR_OPT_MODLEN);
1592 * oa_idmask must be 0, or it must have a single
1593 * bit set (a power of 2).oa_excmask must be 0
1594 * if oa_idmask is 0
1596 if (optarg->oa_idmask == 0) {
1597 if (optarg->oa_excmask != 0) {
1598 /* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */
1599 FAIL(MSG_ERR_OPT_EXCMASKN0);
1601 } else {
1602 if (elfedit_bits_set(optarg->oa_idmask,
1603 sizeof (optarg->oa_idmask)) != 1) {
1604 /* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */
1605 FAIL(MSG_ERR_OPT_IDMASKPOW2);
1608 /* Non-zero idmask must be unique */
1609 if ((optarg->oa_idmask & optmask) != 0) {
1610 /* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */
1611 FAIL(MSG_ERR_OPT_IDMASKUNIQ);
1614 /* Add this one to the overall mask */
1615 optmask |= optarg->oa_idmask;
1617 } else {
1619 * Argument name cannot start with a'-', and must
1620 * not be a null string.
1622 if (optarg->oa_name[0] == '-') {
1623 /* MSG_INTL(MSG_ERR_ARG_MODPRE) */
1624 FAIL(MSG_ERR_ARG_MODPRE);
1626 if (optarg->oa_name[1] == '\0') {
1627 /* MSG_INTL(MSG_ERR_ARG_MODLEN) */
1628 FAIL(MSG_ERR_ARG_MODLEN);
1632 /* oa_idmask and oa_excmask must both be 0 */
1633 if ((optarg->oa_idmask != 0) ||
1634 (optarg->oa_excmask != 0)) {
1635 /* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */
1636 FAIL(MSG_ERR_ARG_MASKNOT0);
1642 * If it takes a value, make sure that we are
1643 * processing options, because CMDOA_F_VALUE is not
1644 * allowed for plain arguments. Then check the following
1645 * item in the list:
1646 * - There must be a following item.
1647 * - oa_name must be non-NULL. This is the only field
1648 * that is used by elfedit.
1649 * - oa_help, oa_flags, oa_idmask, and oa_excmask
1650 * must be 0.
1652 if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) {
1653 elfedit_cmd_optarg_t *oa1 = optarg + 1;
1655 if (!isopt) {
1656 /* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */
1657 FAIL(MSG_ERR_ARG_CMDOA_VAL);
1660 if ((optarg + 1)->oa_name == NULL) {
1661 /* MSG_INTL(MSG_ERR_BADMODOPTVAL) */
1662 FAIL(MSG_ERR_BADMODOPTVAL);
1665 if (oa1->oa_name == NULL) {
1666 /* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */
1667 FAIL(MSG_ERR_CMDOA_VALNAM);
1669 if ((oa1->oa_help != (elfedit_i18nhdl_t)NULL) ||
1670 (oa1->oa_flags != 0) ||
1671 (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) {
1672 /* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */
1673 FAIL(MSG_ERR_CMDOA_VALNOT0);
1675 optarg++;
1680 return;
1682 fail:
1683 load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg),
1684 dl_path, mod_name, cmd_name, optarg->oa_name);
1688 * Look up the specified module, loading the module if necessary,
1689 * and return its definition, or NULL on failure.
1691 * entry:
1692 * name - Name of module to load. If name contains a '/' character or has
1693 * a ".so" suffix, then it is taken to be an absolute file path,
1694 * and is used directly as is. If name does not contain a '/'
1695 * character, then we look for it against the locations in
1696 * the module path, addint the '.so' suffix, and taking the first
1697 * one we find.
1698 * must_exist - If True, we consider it to be an error if we are unable
1699 * to locate a file to load and the module does not already exist.
1700 * If False, NULL is returned quietly in this case.
1701 * allow_abs - True if absolute paths are allowed. False to disallow
1702 * them.
1704 * note:
1705 * If the path is absolute, then we load the file and take the module
1706 * name from the data returned by its elfedit_init() function. If a
1707 * module of that name is already loaded, it is unloaded and replaced
1708 * with the new one.
1710 * If the path is non absolute, then we check to see if the module has
1711 * already been loaded, and if so, we return that module definition.
1712 * In this case, nothing new is loaded. If the module has not been loaded,
1713 * we search the path for it and load it. If the module name provided
1714 * by the elfedit_init() function does not match the name of the file,
1715 * an error results.
1717 elfeditGC_module_t *
1718 elfedit_load_module(const char *name, int must_exist, int allow_abs)
1720 elfedit_init_func_t *init_func;
1721 elfeditGC_module_t *mod;
1722 MODLIST_T *moddef, *insdef;
1723 const char *path;
1724 char path_buf[PATH_MAX + 1];
1725 void *hdl;
1726 size_t i;
1727 int is_abs_path;
1728 elfeditGC_cmd_t *cmd;
1731 * If the name includes a .so suffix, or has any '/' characters,
1732 * then it is an absolute path that we use as is to load the named
1733 * file. Otherwise, we iterate over the path, adding the .so suffix
1734 * and load the first file that matches.
1736 is_abs_path = (path_is_so(name) != NULL) ||
1737 (name != elfedit_basename(name, NULL, NULL, 0));
1739 if (is_abs_path && !allow_abs)
1740 load_module_err(NULL, NULL, NULL,
1741 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
1744 * If this is a non-absolute path, search for the module already
1745 * having been loaded, and return it if so.
1747 if (!is_abs_path) {
1748 moddef = module_loaded(name, &insdef);
1749 if (moddef != NULL)
1750 return (moddef->ml_mod);
1752 * As a result of module_loaded(), insdef now contains the
1753 * immediate predecessor node for the new one, or NULL if
1754 * it goes at the front. In the absolute-path case, we take
1755 * care of this below, after the sharable object is loaded.
1760 * malloc() a module definition block before trying to dlopen().
1761 * Doing things in the other order can cause the dlopen()'d object
1762 * to leak: If elfedit_malloc() fails, it can cause a jump to the
1763 * outer command loop without returning to the caller. Hence,
1764 * there will be no opportunity to clean up. Allocaing the module
1765 * first allows us to free it if necessary.
1767 moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF),
1768 sizeof (*moddef) + PATH_MAX + 1);
1769 moddef->ml_path = ((char *)moddef) + sizeof (*moddef);
1771 if (is_abs_path) {
1772 path = name;
1773 hdl = load_module_dlopen(name, moddef, must_exist);
1774 } else {
1775 hdl = NULL;
1776 path = path_buf;
1777 for (i = 0; i < state.modpath.n; i++) {
1778 if (snprintf(path_buf, sizeof (path_buf),
1779 MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i],
1780 name) > sizeof (path_buf))
1781 load_module_err(moddef, NULL, NULL,
1782 MSG_INTL(MSG_ERR_PATHTOOLONG),
1783 state.modpath.seg[i], name, NULL, NULL);
1784 hdl = load_module_dlopen(path, moddef, 0);
1786 if (must_exist && (hdl == NULL))
1787 load_module_err(moddef, NULL, NULL,
1788 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
1791 if (hdl == NULL) {
1792 free(moddef);
1793 return (NULL);
1796 if (state.elf.elfclass == ELFCLASS32) {
1797 init_func = (elfedit_init_func_t *)
1798 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32));
1799 } else {
1800 init_func = (elfedit_init_func_t *)
1801 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64));
1803 if (init_func == NULL)
1804 load_module_err(moddef, hdl, path,
1805 MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL);
1808 * Note that the init function will be passing us an
1809 * elfedit[32|64]_module_t pointer, which we cast to the
1810 * generic module pointer type in order to be able to manage
1811 * either type with one set of code.
1813 if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT)))
1814 load_module_err(moddef, hdl, path,
1815 MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL);
1818 * Enforce some rules, to help module developers:
1819 * - The primary name of a command must not be
1820 * the empty string ("").
1821 * - Options must start with a '-' followed by at least
1822 * one character.
1823 * - Arguments and options must be well formed.
1825 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
1826 if (**cmd->cmd_name == '\0')
1827 load_module_err(moddef, hdl, path,
1828 MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name,
1829 NULL, NULL, NULL);
1831 if (cmd->cmd_args != NULL)
1832 validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name,
1833 cmd->cmd_name[0], hdl, path);
1834 if (cmd->cmd_opt != NULL)
1835 validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name,
1836 cmd->cmd_name[0], hdl, path);
1840 * Check the name the module provides. How we handle this depends
1841 * on whether the path is absolute or the result of a path search.
1843 if (is_abs_path) {
1844 MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef);
1846 if (old_moddef != NULL) { /* Replace existing */
1847 free(moddef); /* Rare case: Don't need it */
1849 * Be sure we don't unload builtin modules!
1850 * These have a NULL dl_hdl field.
1852 if (old_moddef->ml_dl_hdl == NULL)
1853 load_module_err(NULL, hdl, path,
1854 MSG_INTL(MSG_ERR_CNTULSMOD),
1855 old_moddef->ml_mod->mod_name, NULL,
1856 NULL, NULL);
1858 /* Unload existing */
1859 if (dlclose(old_moddef->ml_dl_hdl) != 0)
1860 elfedit_msg(ELFEDIT_MSG_ERR,
1861 MSG_INTL(MSG_ERR_CNTDLCLOSE),
1862 old_moddef->ml_path, dlerror());
1863 elfedit_msg(ELFEDIT_MSG_DEBUG,
1864 MSG_INTL(MSG_DEBUG_MODUNLOAD),
1865 old_moddef->ml_mod->mod_name, old_moddef->ml_path);
1866 old_moddef->ml_mod = mod;
1867 old_moddef->ml_dl_hdl = hdl;
1868 (void) strlcpy((char *)old_moddef->ml_path, path,
1869 PATH_MAX + 1);
1870 elfedit_msg(ELFEDIT_MSG_DEBUG,
1871 MSG_INTL(MSG_DEBUG_MODLOAD),
1872 old_moddef->ml_mod->mod_name, path);
1873 return (old_moddef->ml_mod);
1876 * insdef now contains the insertion point for the absolute
1877 * path case.
1879 } else {
1880 /* If the names don't match, then error */
1881 if (strcasecmp(name, mod->mod_name) != 0)
1882 load_module_err(moddef, hdl, path,
1883 MSG_INTL(MSG_ERR_BADMODNAME),
1884 mod->mod_name, name, path, NULL);
1888 * Link module into the module list. If insdef is NULL,
1889 * it goes at the head. If insdef is non-NULL, it goes immediately
1890 * after
1892 if (insdef == NULL) {
1893 moddef->ml_next = state.modlist;
1894 state.modlist = moddef;
1895 } else {
1896 moddef->ml_next = insdef->ml_next;
1897 insdef->ml_next = moddef;
1899 moddef->ml_mod = mod;
1900 moddef->ml_dl_hdl = hdl;
1901 (void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1);
1903 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD),
1904 moddef->ml_mod->mod_name, path);
1906 return (moddef->ml_mod);
1911 * Unload the specified module
1913 void
1914 elfedit_unload_module(const char *name)
1916 MODLIST_T *moddef, *insdef;
1918 moddef = module_loaded(name, &insdef);
1919 if (moddef == NULL)
1920 return;
1922 /* Built in modules cannot be unloaded. They have a NULL dl_hdl field */
1923 if (moddef->ml_dl_hdl == NULL)
1924 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD),
1925 moddef->ml_mod->mod_name);
1928 * When we unload it, the name string goes with it. So
1929 * announce it while we still can without having to make a copy.
1931 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD),
1932 moddef->ml_mod->mod_name, moddef->ml_path);
1935 * Close it before going further. On failure, we'll jump, and the
1936 * record will remain in the module list. On success,
1937 * we'll retain control, and can safely remove it.
1939 if (dlclose(moddef->ml_dl_hdl) != 0)
1940 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
1941 moddef->ml_path, dlerror());
1943 /* Unlink the record from the module list */
1944 if (insdef == NULL)
1945 state.modlist = moddef->ml_next;
1946 else
1947 insdef->ml_next = moddef->ml_next;
1949 /* Release the memory */
1950 free(moddef);
1955 * Load all sharable objects found in the specified directory.
1957 * entry:
1958 * dirpath - Path of directory to process.
1959 * must_exist - If True, it is an error if diropen() fails to open
1960 * the given directory. Of False, we quietly ignore it and return.
1961 * abs_path - If True, files are loaded using their literal paths.
1962 * If False, their module name is extracted from the dirpath
1963 * and a path based search is used to locate it.
1965 void
1966 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path)
1968 char path[PATH_MAX + 1];
1969 DIR *dir;
1970 struct dirent *dp;
1971 const char *tail;
1973 dir = opendir(dirpath);
1974 if (dir == NULL) {
1975 int err = errno;
1977 if (!must_exist && (err == ENOENT))
1978 return;
1979 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR),
1980 dirpath, strerror(err));
1981 /*NOTREACHED*/
1984 while (dp = readdir(dir)) {
1985 if ((tail = path_is_so(dp->d_name)) != NULL) {
1986 if (abs_path) {
1987 (void) snprintf(path, sizeof (path),
1988 MSG_ORIG(MSG_FMT_BLDPATH), dirpath,
1989 dp->d_name);
1990 } else {
1991 (void) elfedit_basename(dp->d_name, tail,
1992 path, sizeof (path));
1994 (void) elfedit_load_module(path, must_exist, 1);
1997 (void) closedir(dir);
2002 * Follow the module load path, and load the first module found for each
2003 * given name.
2005 void
2006 elfedit_load_modpath(void)
2008 size_t i;
2010 for (i = 0; i < state.modpath.n; i++)
2011 elfedit_load_moddir(state.modpath.seg[i], 0, 0);
2015 * Given a module definition, look for the specified command.
2016 * Returns the command if found, and NULL otherwise.
2018 static elfeditGC_cmd_t *
2019 find_cmd(elfeditGC_module_t *mod, const char *name)
2021 elfeditGC_cmd_t *cmd;
2022 const char **cmd_name;
2024 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
2025 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
2026 if (strcasecmp(name, *cmd_name) == 0) {
2027 if (cmd_name != cmd->cmd_name)
2028 elfedit_msg(ELFEDIT_MSG_DEBUG,
2029 MSG_INTL(MSG_DEBUG_CMDALIAS),
2030 mod->mod_name, *cmd_name,
2031 mod->mod_name, *cmd->cmd_name);
2032 return (cmd);
2035 return (NULL);
2040 * Given a command name, return its command definition.
2042 * entry:
2043 * name - Command to be looked up
2044 * must_exist - If True, we consider it to be an error if the command
2045 * does not exist. If False, NULL is returned quietly in
2046 * this case.
2047 * mod_ret - NULL, or address of a variable to receive the
2048 * module definition block of the module containing
2049 * the command.
2051 * exit:
2052 * On success, returns a pointer to the command definition, and
2053 * if mod_ret is non-NULL, *mod_ret receives a pointer to the
2054 * module definition. On failure, must_exist determines the
2055 * action taken: If must_exist is True, an error is issued and
2056 * control does not return to the caller. If must_exist is False,
2057 * NULL is quietly returned.
2059 * note:
2060 * A ':' in name is used to delimit the module and command names.
2061 * If it is omitted, or if it is the first non-whitespace character
2062 * in the name, then the built in sys: module is implied.
2064 elfeditGC_cmd_t *
2065 elfedit_find_command(const char *name, int must_exist,
2066 elfeditGC_module_t **mod_ret)
2068 elfeditGC_module_t *mod;
2069 const char *mod_str;
2070 const char *cmd_str;
2071 char mod_buf[ELFEDIT_MAXMODNAM + 1];
2072 size_t n;
2073 elfeditGC_cmd_t *cmd;
2076 cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON));
2077 if (cmd_str == NULL) { /* No module name -> sys: */
2078 mod_str = MSG_ORIG(MSG_MOD_SYS);
2079 cmd_str = name;
2080 } else if (cmd_str == name) { /* Empty module name -> sys: */
2081 mod_str = MSG_ORIG(MSG_MOD_SYS);
2082 cmd_str++; /* Skip the colon */
2083 } else { /* Have both module and command */
2084 n = cmd_str - name;
2085 if (n >= sizeof (mod_buf)) {
2086 if (must_exist)
2087 elfedit_msg(ELFEDIT_MSG_ERR,
2088 MSG_INTL(MSG_ERR_MODNAMTOOLONG), name);
2089 return (NULL);
2091 (void) strlcpy(mod_buf, name, n + 1);
2092 mod_str = mod_buf;
2093 cmd_str++;
2096 /* Lookup/load module. Won't return on error */
2097 mod = elfedit_load_module(mod_str, must_exist, 0);
2098 if (mod == NULL)
2099 return (NULL);
2101 /* Locate the command */
2102 cmd = find_cmd(mod, cmd_str);
2103 if (cmd == NULL) {
2104 if (must_exist) {
2106 * Catch empty command in order to provide
2107 * a better error message.
2109 if (*cmd_str == '\0') {
2110 elfedit_msg(ELFEDIT_MSG_ERR,
2111 MSG_INTL(MSG_ERR_MODNOCMD), mod_str);
2112 } else {
2113 elfedit_msg(ELFEDIT_MSG_ERR,
2114 MSG_INTL(MSG_ERR_UNRECCMD),
2115 mod_str, cmd_str);
2118 } else {
2119 if (mod_ret != NULL)
2120 *mod_ret = mod;
2122 return (cmd);
2127 * Release all user command blocks found on state.ucmd
2129 static void
2130 free_user_cmds(void)
2132 USER_CMD_T *next;
2134 while (state.ucmd.list) {
2135 next = state.ucmd.list->ucmd_next;
2136 free(state.ucmd.list);
2137 state.ucmd.list = next;
2139 state.ucmd.tail = NULL;
2140 state.ucmd.n = 0;
2141 state.cur_cmd = NULL;
2146 * Process all user command blocks found on state.ucmd, and then
2147 * remove them from the list.
2149 static void
2150 dispatch_user_cmds()
2152 USER_CMD_T *ucmd;
2153 elfedit_cmdret_t cmd_ret;
2155 ucmd = state.ucmd.list;
2156 if (ucmd) {
2157 /* Do them, in order */
2158 for (; ucmd; ucmd = ucmd->ucmd_next) {
2159 state.cur_cmd = ucmd;
2160 if (!state.msg_jbuf.active)
2161 elfedit_msg(ELFEDIT_MSG_DEBUG,
2162 MSG_INTL(MSG_DEBUG_EXECCMD),
2163 ucmd->ucmd_orig_str);
2165 * The cmd_func field is the generic definition.
2166 * We need to cast it to the type that matches
2167 * the proper ELFCLASS before calling it.
2169 if (state.elf.elfclass == ELFCLASS32) {
2170 elfedit32_cmd_func_t *cmd_func =
2171 (elfedit32_cmd_func_t *)
2172 ucmd->ucmd_cmd->cmd_func;
2174 cmd_ret = (* cmd_func)(state.elf.obj_state.s32,
2175 ucmd->ucmd_argc, ucmd->ucmd_argv);
2176 } else {
2177 elfedit64_cmd_func_t *cmd_func =
2178 (elfedit64_cmd_func_t *)
2179 ucmd->ucmd_cmd->cmd_func;
2181 cmd_ret = (* cmd_func)(state.elf.obj_state.s64,
2182 ucmd->ucmd_argc, ucmd->ucmd_argv);
2184 state.cur_cmd = NULL;
2185 /* If a pager was started, wrap it up */
2186 elfedit_pager_cleanup();
2188 switch (cmd_ret) {
2189 case ELFEDIT_CMDRET_MOD_OS_MACH:
2191 * Inform the elfconst module that the machine
2192 * or osabi has has changed. It may be necessary
2193 * to fetch new strings from libconv.
2195 state.elf.elfconst_ehdr_change = 1;
2196 /*FALLTHROUGH*/
2197 case ELFEDIT_CMDRET_MOD:
2199 * Command modified the output ELF image,
2200 * mark the file as needing a flush to disk.
2202 state.file.dirty = 1;
2203 break;
2204 case ELFEDIT_CMDRET_FLUSH:
2206 * Command flushed the output file,
2207 * clear the dirty bit.
2209 state.file.dirty = 0;
2212 free_user_cmds();
2218 * Prepare a GETTOK_STATE struct for gettok().
2220 * entry:
2221 * gettok_state - gettok state block to use
2222 * str - Writable buffer to tokenize. Note that gettok()
2223 * is allowed to change the contents of this buffer.
2224 * inc_null_final - If the line ends in whitespace instead of
2225 * immediately hitting a NULL, and inc_null_final is TRUE,
2226 * then a null final token is generated. Otherwise trailing
2227 * whitespace is ignored.
2229 static void
2230 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final)
2232 gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf;
2233 gettok_state->gtok_inc_null_final = inc_null_final;
2234 gettok_state->gtok_null_seen = 0;
2239 * Locate the next token from the buffer.
2241 * entry:
2242 * gettok_state - State of gettok() operation. Initialized
2243 * by gettok_init(), and passed to gettok().
2245 * exit:
2246 * If a token is found, gettok_state->gtok_last_token is filled in
2247 * with the details and True (1) is returned. If no token is found,
2248 * False (1) is returned, and the contents of
2249 * gettok_state->gtok_last_token are undefined.
2251 * note:
2252 * - The token returned references the memory in gettok_state->gtok_buf.
2253 * The caller should not modify the buffer until all such
2254 * pointers have been discarded.
2255 * - This routine will modify the contents of gettok_state->gtok_buf
2256 * as necessary to remove quotes and eliminate escape
2257 * (\)characters.
2259 static int
2260 gettok(GETTOK_STATE *gettok_state)
2262 char *str = gettok_state->gtok_cur_buf;
2263 char *look;
2264 int quote_ch = '\0';
2266 /* Skip leading whitespace */
2267 while (isspace(*str))
2268 str++;
2270 if (*str == '\0') {
2272 * If user requested it, and there was whitespace at the
2273 * end, then generate one last null token.
2275 if (gettok_state->gtok_inc_null_final &&
2276 !gettok_state->gtok_null_seen) {
2277 gettok_state->gtok_inc_null_final = 0;
2278 gettok_state->gtok_null_seen = 1;
2279 gettok_state->gtok_last_token.tok_str = str;
2280 gettok_state->gtok_last_token.tok_len = 0;
2281 gettok_state->gtok_last_token.tok_line_off =
2282 str - gettok_state->gtok_buf;
2283 return (1);
2285 gettok_state->gtok_null_seen = 1;
2286 return (0);
2290 * Read token: The standard delimiter is whitespace, but
2291 * we honor either single or double quotes. Also, we honor
2292 * backslash escapes.
2294 gettok_state->gtok_last_token.tok_str = look = str;
2295 gettok_state->gtok_last_token.tok_line_off =
2296 look - gettok_state->gtok_buf;
2297 for (; *look; look++) {
2298 if (*look == quote_ch) { /* Terminates active quote */
2299 quote_ch = '\0';
2300 continue;
2303 if (quote_ch == '\0') { /* No quote currently active */
2304 if ((*look == '\'') || (*look == '"')) {
2305 quote_ch = *look; /* New active quote */
2306 continue;
2308 if (isspace(*look))
2309 break;
2313 * The semantics of the backslash character depends on
2314 * the quote style in use:
2315 * - Within single quotes, backslash is not
2316 * an escape character, and is taken literally.
2317 * - If outside of quotes, the backslash is an escape
2318 * character. The backslash is ignored and the
2319 * following character is taken literally, losing
2320 * any special properties it normally has.
2321 * - Within double quotes, backslash works like a
2322 * backslash escape within a C literal. Certain
2323 * escapes are recognized and replaced with their
2324 * special character. Any others are an error.
2326 if (*look == '\\') {
2327 if (quote_ch == '\'') {
2328 *str++ = *look;
2329 continue;
2332 look++;
2333 if (*look == '\0') { /* Esc applied to NULL term? */
2334 elfedit_msg(ELFEDIT_MSG_ERR,
2335 MSG_INTL(MSG_ERR_ESCEOL));
2336 /*NOTREACHED*/
2339 if (quote_ch == '"') {
2340 int ch = conv_translate_c_esc(&look);
2342 if (ch == -1)
2343 elfedit_msg(ELFEDIT_MSG_ERR,
2344 MSG_INTL(MSG_ERR_BADCESC), *look);
2345 *str++ = ch;
2346 look--; /* for() will advance by 1 */
2347 continue;
2351 if (look != str)
2352 *str = *look;
2353 str++;
2356 /* Don't allow unterminated quoted tokens */
2357 if (quote_ch != '\0')
2358 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNTERMQUOTE),
2359 quote_ch);
2361 gettok_state->gtok_last_token.tok_len = str -
2362 gettok_state->gtok_last_token.tok_str;
2363 gettok_state->gtok_null_seen = *look == '\0';
2364 if (!gettok_state->gtok_null_seen)
2365 look++;
2366 *str = '\0';
2367 gettok_state->gtok_cur_buf = look;
2369 #ifdef DEBUG_GETTOK
2370 printf("GETTOK >");
2371 elfedit_str_to_c_literal(gettok_state->gtok_last_token.tok_str,
2372 elfedit_write);
2373 printf("< \tlen(%d) offset(%d)\n",
2374 gettok_state->gtok_last_token.tok_len,
2375 gettok_state->gtok_last_token.tok_line_off);
2376 #endif
2378 return (1);
2383 * Tokenize the user command string, and return a pointer to the
2384 * TOK_STATE buffer maintained by this function. That buffer contains
2385 * the tokenized strings.
2387 * entry:
2388 * user_cmd_str - String to tokenize
2389 * len - # of characters in user_cmd_str to examine. If
2390 * (len < 0), then the complete string is processed
2391 * stopping with the NULL termination. Otherwise,
2392 * processing stops after len characters, and any
2393 * remaining characters are ignored.
2394 * inc_null_final - If True, and if user_cmd_str has whitespace
2395 * at the end following the last non-null token, then
2396 * a final null token will be included. If False, null
2397 * tokens are ignored.
2399 * note:
2400 * This routine returns pointers to internally allocated memory.
2401 * The caller must not alter anything contained in the TOK_STATE
2402 * buffer returned. Furthermore, the the contents of TOK_STATE
2403 * are only valid until the next call to tokenize_user_cmd().
2405 static TOK_STATE *
2406 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final)
2408 #define INITIAL_TOK_ALLOC 5
2411 * As we parse the user command, we need temporary space to
2412 * hold the tokens. We do this by dynamically allocating a string
2413 * buffer and a token array, and doubling them as necessary. This
2414 * is a single threaded application, so static variables suffice.
2416 static STRBUF str;
2417 static TOK_STATE tokst;
2419 GETTOK_STATE gettok_state;
2420 size_t n;
2423 * Make a copy we can modify. If (len == 0), take the entire
2424 * string. Otherwise limit it to the specified length.
2426 tokst.tokst_cmd_len = strlen(user_cmd_str);
2427 if ((len > 0) && (len < tokst.tokst_cmd_len))
2428 tokst.tokst_cmd_len = len;
2429 tokst.tokst_cmd_len++; /* Room for NULL termination */
2430 strbuf_ensure_size(&str, tokst.tokst_cmd_len);
2431 (void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len);
2433 /* Trim off any newline character that might be present */
2434 if ((tokst.tokst_cmd_len > 1) &&
2435 (str.buf[tokst.tokst_cmd_len - 2] == '\n')) {
2436 tokst.tokst_cmd_len--;
2437 str.buf[tokst.tokst_cmd_len - 1] = '\0';
2440 /* Tokenize the user command string into tok struct */
2441 gettok_init(&gettok_state, str.buf, inc_null_final);
2442 tokst.tokst_str_size = 0; /* Space needed for token strings */
2443 for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0;
2444 tokst.tokst_cnt++) {
2445 /* If we need more room, expand the token buffer */
2446 if (tokst.tokst_cnt >= tokst.tokst_bufsize) {
2447 n = (tokst.tokst_bufsize == 0) ?
2448 INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2);
2449 tokst.tokst_buf = elfedit_realloc(
2450 MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf,
2451 n * sizeof (*tokst.tokst_buf));
2452 tokst.tokst_bufsize = n;
2454 tokst.tokst_str_size +=
2455 gettok_state.gtok_last_token.tok_len + 1;
2456 tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token;
2458 /* fold the command token to lowercase */
2459 if (tokst.tokst_cnt > 0) {
2460 char *s;
2462 for (s = tokst.tokst_buf[0].tok_str; *s; s++)
2463 if (isupper(*s))
2464 *s = tolower(*s);
2467 return (&tokst);
2469 #undef INITIAL_TOK_ALLOC
2474 * Parse the user command string, and put an entry for it at the end
2475 * of state.ucmd.
2477 static void
2478 parse_user_cmd(const char *user_cmd_str)
2480 TOK_STATE *tokst;
2481 char *s;
2482 size_t n;
2483 size_t len;
2484 USER_CMD_T *ucmd;
2485 elfeditGC_module_t *mod;
2486 elfeditGC_cmd_t *cmd;
2489 * Break it into tokens. If there are none, then it is
2490 * an empty command and is ignored.
2492 tokst = tokenize_user_cmd(user_cmd_str, -1, 0);
2493 if (tokst->tokst_cnt == 0)
2494 return;
2496 /* Find the command. Won't return on error */
2497 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod);
2500 * If there is no ELF file being edited, then only commands
2501 * from the sys: module are allowed.
2503 if ((state.file.present == 0) &&
2504 (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0))
2505 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY),
2506 mod->mod_name, cmd->cmd_name[0]);
2509 /* Allocate, fill in, and insert a USER_CMD_T block */
2510 n = S_DROUND(sizeof (USER_CMD_T));
2511 ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD),
2512 n + (sizeof (char *) * (tokst->tokst_cnt - 1)) +
2513 tokst->tokst_cmd_len + tokst->tokst_str_size);
2514 ucmd->ucmd_next = NULL;
2515 ucmd->ucmd_argc = tokst->tokst_cnt - 1;
2516 /*LINTED E_BAD_PTR_CAST_ALIGN*/
2517 ucmd->ucmd_argv = (const char **)(n + (char *)ucmd);
2518 ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc);
2519 (void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len);
2520 ucmd->ucmd_mod = mod;
2521 ucmd->ucmd_cmd = cmd;
2522 ucmd->ucmd_ostyle_set = 0;
2523 s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len;
2524 for (n = 1; n < tokst->tokst_cnt; n++) {
2525 len = tokst->tokst_buf[n].tok_len + 1;
2526 ucmd->ucmd_argv[n - 1] = s;
2527 (void) strncpy(s, tokst->tokst_buf[n].tok_str, len);
2528 s += len;
2530 if (state.ucmd.list == NULL) {
2531 state.ucmd.list = state.ucmd.tail = ucmd;
2532 } else {
2533 state.ucmd.tail->ucmd_next = ucmd;
2534 state.ucmd.tail = ucmd;
2536 state.ucmd.n++;
2541 * Copy infile to a new file with the name given by outfile.
2543 static void
2544 create_outfile(const char *infile, const char *outfile)
2546 pid_t pid;
2547 int statloc;
2548 struct stat statbuf;
2551 pid = fork();
2552 switch (pid) {
2553 case -1: /* Unable to create process */
2555 int err = errno;
2556 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK),
2557 strerror(err));
2559 /*NOTREACHED*/
2560 return;
2562 case 0:
2563 (void) execl(MSG_ORIG(MSG_STR_BINCP),
2564 MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL);
2566 * exec() only returns on error. This is the child process,
2567 * so we want to stay away from the usual error mechanism
2568 * and handle things directly.
2571 int err = errno;
2572 (void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC),
2573 MSG_ORIG(MSG_STR_ELFEDIT),
2574 MSG_ORIG(MSG_STR_BINCP), strerror(err));
2576 exit(1);
2577 /*NOTREACHED*/
2580 /* This is the parent: Wait for the child to terminate */
2581 if (waitpid(pid, &statloc, 0) != pid) {
2582 int err = errno;
2583 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT),
2584 strerror(err));
2587 * If the child failed, then terminate the process. There is no
2588 * need for an error message, because the child will have taken
2589 * care of that.
2591 if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0))
2592 exit(1);
2594 /* Make sure the copy allows user write access */
2595 if (stat(outfile, &statbuf) == -1) {
2596 int err = errno;
2597 (void) unlink(outfile);
2598 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT),
2599 outfile, strerror(err));
2601 if ((statbuf.st_mode & S_IWUSR) == 0) {
2602 /* Only keep permission bits, and add user write */
2603 statbuf.st_mode |= S_IWUSR;
2604 statbuf.st_mode &= 07777; /* Only keep the permission bits */
2605 if (chmod(outfile, statbuf.st_mode) == -1) {
2606 int err = errno;
2607 (void) unlink(outfile);
2608 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD),
2609 outfile, strerror(err));
2615 * Given a module path string, determine how long the resulting path will
2616 * be when all % tokens have been expanded.
2618 * entry:
2619 * path - Path for which expanded length is desired
2620 * origin_root - Root of $ORIGIN tree containing running elfedit program
2622 * exit:
2623 * Returns the value strlen() will give for the expanded path.
2625 static size_t
2626 modpath_strlen(const char *path, const char *origin_root)
2628 size_t len = 0;
2629 const char *s;
2631 s = path;
2632 len = 0;
2633 for (s = path; *s != '\0'; s++) {
2634 if (*s == '%') {
2635 s++;
2636 switch (*s) {
2637 case 'i': /* ISA of running elfedit */
2638 len += strlen(isa_i_str);
2639 break;
2640 case 'I': /* "" for 32-bit, same as %i for 64 */
2641 len += strlen(isa_I_str);
2642 break;
2643 case 'o': /* Insert default path */
2644 len +=
2645 modpath_strlen(MSG_ORIG(MSG_STR_MODPATH),
2646 origin_root);
2647 break;
2648 case 'r': /* root of tree with running elfedit */
2649 len += strlen(origin_root);
2650 break;
2652 case '%': /* %% is reduced to just '%' */
2653 len++;
2654 break;
2655 default: /* All other % codes are reserved */
2656 elfedit_msg(ELFEDIT_MSG_ERR,
2657 MSG_INTL(MSG_ERR_BADPATHCODE), *s);
2658 /*NOTREACHED*/
2659 break;
2661 } else { /* Non-% character passes straight through */
2662 len++;
2666 return (len);
2671 * Given a module path string, and a buffer large enough to hold the results,
2672 * fill the buffer with the expanded path.
2674 * entry:
2675 * path - Path for which expanded length is desired
2676 * origin_root - Root of tree containing running elfedit program
2677 * buf - Buffer to receive the result. buf must as large or larger
2678 * than the value given by modpath_strlen().
2680 * exit:
2681 * Returns pointer to location following the last character
2682 * written to buf. A NULL byte is written to that address.
2684 static char *
2685 modpath_expand(const char *path, const char *origin_root, char *buf)
2687 size_t len;
2688 const char *cp_str;
2690 for (; *path != '\0'; path++) {
2691 if (*path == '%') {
2692 path++;
2693 cp_str = NULL;
2694 switch (*path) {
2695 case 'i': /* ISA of running elfedit */
2696 cp_str = isa_i_str;
2697 break;
2698 case 'I': /* "" for 32-bit, same as %i for 64 */
2699 cp_str = isa_I_str;
2700 break;
2701 case 'o': /* Insert default path */
2702 buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH),
2703 origin_root, buf);
2704 break;
2705 case 'r':
2706 cp_str = origin_root;
2707 break;
2708 case '%': /* %% is reduced to just '%' */
2709 *buf++ = *path;
2710 break;
2711 default: /* All other % codes are reserved */
2712 elfedit_msg(ELFEDIT_MSG_ERR,
2713 MSG_INTL(MSG_ERR_BADPATHCODE), *path);
2714 /*NOTREACHED*/
2715 break;
2717 if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) {
2718 bcopy(cp_str, buf, len);
2719 buf += len;
2721 } else { /* Non-% character passes straight through */
2722 *buf++ = *path;
2726 *buf = '\0';
2727 return (buf);
2732 * Establish the module search path: state.modpath
2734 * The path used comes from the following sources, taking the first
2735 * one that has a value, and ignoring any others:
2737 * - ELFEDIT_PATH environment variable
2738 * - -L command line argument
2739 * - Default value
2741 * entry:
2742 * path - NULL, or the value of the -L command line argument
2744 * exit:
2745 * state.modpath has been filled in
2747 static void
2748 establish_modpath(const char *cmdline_path)
2750 char origin_root[PATH_MAX + 1]; /* Where elfedit binary is */
2751 const char *path; /* Initial path */
2752 char *expath; /* Expanded path */
2753 size_t len;
2754 char *src, *dst;
2756 path = getenv(MSG_ORIG(MSG_STR_ENVVAR));
2757 if (path == NULL)
2758 path = cmdline_path;
2759 if (path == NULL)
2760 path = MSG_ORIG(MSG_STR_MODPATH);
2764 * Root of tree containing running for running program. 32-bit elfedit
2765 * is installed in /usr/bin, and 64-bit elfedit is one level lower
2766 * in an ISA-specific subdirectory. So, we find the root by
2767 * getting the $ORGIN of the current running program, and trimming
2768 * off the last 2 (32-bit) or 3 (64-bit) directories.
2770 * On a standard system, this will simply yield '/'. However,
2771 * doing it this way allows us to run elfedit from a proto area,
2772 * and pick up modules from the same proto area instead of those
2773 * installed on the system.
2775 if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1)
2776 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN));
2777 len = (sizeof (char *) == 8) ? 3 : 2;
2778 src = origin_root + strlen(origin_root);
2779 while ((src > origin_root) && (len > 0)) {
2780 if (*(src - 1) == '/')
2781 len--;
2782 src--;
2784 *src = '\0';
2788 * Calculate space needed to hold expanded path. Note that
2789 * this assumes that MSG_STR_MODPATH will never contain a '%o'
2790 * code, and so, the expansion is not recursive. The codes allowed
2791 * are:
2792 * %i - ISA of running elfedit (sparc, sparcv9, etc)
2793 * %I - 64-bit ISA: Same as %i for 64-bit versions of elfedit,
2794 * but yields empty string for 32-bit ISAs.
2795 * %o - The original (default) path.
2796 * %r - Root of tree holding elfedit program.
2797 * %% - A single %
2799 * A % followed by anything else is an error. This allows us to
2800 * add new codes in the future without backward compatability issues.
2802 len = modpath_strlen(path, origin_root);
2804 expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1);
2805 (void) modpath_expand(path, origin_root, expath);
2808 * Count path segments, eliminate extra '/', and replace ':'
2809 * with NULL.
2811 state.modpath.n = 1;
2812 for (src = dst = expath; *src; src++) {
2813 if (*src == '/') {
2814 switch (*(src + 1)) {
2815 case '/':
2816 case ':':
2817 case '\0':
2818 continue;
2821 if (*src == ':') {
2822 state.modpath.n++;
2823 *dst = '\0';
2824 } else if (src != dst) {
2825 *dst = *src;
2827 dst++;
2829 if (src != dst)
2830 *dst = '\0';
2832 state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR),
2833 sizeof (state.modpath.seg[0]) * state.modpath.n);
2835 src = expath;
2836 for (len = 0; len < state.modpath.n; len++) {
2837 if (*src == '\0') {
2838 state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT);
2839 src++;
2840 } else {
2841 state.modpath.seg[len] = src;
2842 src += strlen(src) + 1;
2848 * When interactive (reading commands from a tty), we catch
2849 * SIGINT in order to restart the outer command loop.
2851 /*ARGSUSED*/
2852 static void
2853 sigint_handler(int sig, siginfo_t *sip, void *ucp)
2855 /* Jump to the outer loop to resume */
2856 if (state.msg_jbuf.active) {
2857 state.msg_jbuf.active = 0;
2858 siglongjmp(state.msg_jbuf.env, 1);
2863 static void
2864 usage(int full)
2866 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF));
2867 if (full) {
2868 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1));
2869 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2));
2870 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3));
2871 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4));
2872 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5));
2873 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6));
2874 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST));
2876 elfedit_exit(2);
2881 * In order to complete commands, we need to know about them,
2882 * which means that we need to force all the modules to be
2883 * loaded. This is a relatively expensive operation, so we use
2884 * this function, which avoids doing it more than once in a session.
2886 static void
2887 elfedit_cpl_load_modules(void)
2889 static int loaded;
2891 if (!loaded) {
2892 elfedit_load_modpath();
2893 loaded = 1; /* Don't do it again */
2898 * Compare the token to the given string, and if they share a common
2899 * initial sequence, add the tail of string to the tecla command completion
2900 * buffer:
2902 * entry:
2903 * cpldata - Current completion state
2904 * str - String to match against token
2905 * casefold - True to allow case insensitive completion, False
2906 * if case must match exactly.
2908 void
2909 elfedit_cpl_match(void *cpldata, const char *str, int casefold)
2911 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
2912 const char *cont_suffix;
2913 const char *type_suffix;
2916 * Reasons to return immediately:
2917 * - NULL strings have no completion value
2918 * - The string is shorter than the existing item being completed
2920 if ((str == NULL) || (*str == '\0') ||
2921 ((cstate->ecpl_token_len != 0) &&
2922 ((strlen(str) < cstate->ecpl_token_len))))
2923 return;
2925 /* If the string does not share the existing prefix, don't use it */
2926 if (casefold) {
2927 if (strncasecmp(cstate->ecpl_token_str, str,
2928 cstate->ecpl_token_len) != 0)
2929 return;
2930 } else {
2931 if (strncmp(cstate->ecpl_token_str, str,
2932 cstate->ecpl_token_len) != 0)
2933 return;
2936 if (cstate->ecpl_add_mod_colon) {
2937 cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON);
2938 } else {
2939 cont_suffix = MSG_ORIG(MSG_STR_SPACE);
2940 type_suffix = NULL;
2942 (void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line,
2943 cstate->ecpl_word_start, cstate->ecpl_word_end,
2944 str + cstate->ecpl_token_len, type_suffix, cont_suffix);
2950 * Convenience wrapper on elfedit_cpl_match(): Format an unsigned
2951 * 32-bit integer as a string and enter the result for command completion.
2953 void
2954 elfedit_cpl_ndx(void *cpldata, uint_t ndx)
2956 Conv_inv_buf_t buf;
2958 (void) snprintf(buf.buf, sizeof (buf.buf),
2959 MSG_ORIG(MSG_FMT_WORDVAL), ndx);
2960 elfedit_cpl_match(cpldata, buf.buf, 0);
2965 * Compare the token to the names of the commands from the given module,
2966 * and if they share a common initial sequence, add the tail of string
2967 * to the tecla command completion buffer:
2969 * entry:
2970 * tok_buf - Token user has entered
2971 * tok_len - strlen(tok_buf)
2972 * mod - Module definition from which commands should be matched
2973 * cpl, line, word_start, word_end, cont_suffix - As documented
2974 * for gl_get_line() and cpl_add_completion.
2976 static void
2977 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod)
2979 elfeditGC_cmd_t *cmd;
2980 const char **cmd_name;
2982 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
2983 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
2984 elfedit_cpl_match(cstate, *cmd_name, 1);
2989 * Compare the token to the known module names, and add those that
2990 * match to the list of alternatives via elfedit_cpl_match().
2992 * entry:
2993 * load_all_modules - If True, causes all modules to be loaded
2994 * before processing is done. If False, only the modules
2995 * currently seen will be used.
2997 void
2998 elfedit_cpl_module(void *cpldata, int load_all_modules)
3000 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
3001 MODLIST_T *modlist;
3003 if (load_all_modules)
3004 elfedit_cpl_load_modules();
3006 for (modlist = state.modlist; modlist != NULL;
3007 modlist = modlist->ml_next) {
3008 elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1);
3014 * Compare the token to all the known commands, and add those that
3015 * match to the list of alternatives.
3017 * note:
3018 * This routine will force modules to be loaded as necessary to
3019 * obtain the names it needs to match.
3021 void
3022 elfedit_cpl_command(void *cpldata)
3024 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
3025 ELFEDIT_CPL_STATE colon_state;
3026 const char *colon_pos;
3027 MODLIST_T *modlist;
3028 MODLIST_T *insdef;
3029 char buf[128];
3032 * Is there a colon in the command? If so, locate its offset within
3033 * the raw input line.
3035 for (colon_pos = cstate->ecpl_token_str;
3036 *colon_pos && (*colon_pos != ':'); colon_pos++)
3040 * If no colon was seen, then we are completing a module name,
3041 * or one of the commands from 'sys:'
3043 if (*colon_pos == '\0') {
3045 * Setting cstate->add_mod_colon tells elfedit_cpl_match()
3046 * to add an implicit ':' to the names it matches. We use it
3047 * here so the user doesn't have to enter the ':' manually.
3048 * Hiding this in the opaque state instead of making it
3049 * an argument to that function gives us the ability to
3050 * change it later without breaking the published interface.
3052 cstate->ecpl_add_mod_colon = 1;
3053 elfedit_cpl_module(cpldata, 1);
3054 cstate->ecpl_add_mod_colon = 0;
3056 /* Add bare (no sys: prefix) commands from the sys: module */
3057 match_module_cmds(cstate,
3058 elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0));
3060 return;
3064 * A colon was seen, so we have a module name. Extract the name,
3065 * substituting 'sys' for the case where the given name is empty.
3067 if (colon_pos == 0)
3068 (void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf));
3069 else
3070 elfedit_strnbcpy(buf, cstate->ecpl_token_str,
3071 colon_pos - cstate->ecpl_token_str, sizeof (buf));
3074 * Locate the module. If it isn't already loaded, make an explicit
3075 * attempt to load it and try again. If a module definition is
3076 * obtained, process the commands it supplies.
3078 modlist = module_loaded(buf, &insdef);
3079 if (modlist == NULL) {
3080 (void) elfedit_load_module(buf, 0, 0);
3081 modlist = module_loaded(buf, &insdef);
3083 if (modlist != NULL) {
3085 * Make a copy of the cstate, and adjust the line and
3086 * token so that the new one starts just past the colon
3087 * character. We know that the colon exists because
3088 * of the preceeding test that found it. Therefore, we do
3089 * not need to test against running off the end of the
3090 * string here.
3092 colon_state = *cstate;
3093 while (colon_state.ecpl_line[colon_state.ecpl_word_start] !=
3094 ':')
3095 colon_state.ecpl_word_start++;
3096 while (*colon_state.ecpl_token_str != ':') {
3097 colon_state.ecpl_token_str++;
3098 colon_state.ecpl_token_len--;
3100 /* Skip past the ':' character */
3101 colon_state.ecpl_word_start++;
3102 colon_state.ecpl_token_str++;
3103 colon_state.ecpl_token_len--;
3105 match_module_cmds(&colon_state, modlist->ml_mod);
3111 * Command completion function for use with libtacla.
3113 /*ARGSUSED1*/
3114 static int
3115 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end)
3117 const char *argv[ELFEDIT_MAXCPLARGS];
3118 ELFEDIT_CPL_STATE cstate;
3119 TOK_STATE *tokst;
3120 int ndx;
3121 int i;
3122 elfeditGC_module_t *mod;
3123 elfeditGC_cmd_t *cmd;
3124 int num_opt;
3125 int opt_term_seen;
3126 int skip_one;
3127 elfedit_cmd_optarg_t *optarg;
3128 elfedit_optarg_item_t item;
3129 int ostyle_ndx = -1;
3132 * For debugging, enable the following block. It tells the tecla
3133 * library that the program using is going to write to stdout.
3134 * It will put the tty back into normal mode, and it will cause
3135 * tecla to redraw the current input line when it gets control back.
3137 #ifdef DEBUG_CMD_MATCH
3138 gl_normal_io(state.input.gl);
3139 #endif
3142 * Tokenize the line up through word_end. The last token in
3143 * the list is the one requiring completion.
3145 tokst = tokenize_user_cmd(line, word_end, 1);
3146 if (tokst->tokst_cnt == 0)
3147 return (0);
3149 /* Set up the cstate block, containing the completion state */
3150 ndx = tokst->tokst_cnt - 1; /* Index of token to complete */
3151 cstate.ecpl_cpl = cpl;
3152 cstate.ecpl_line = line;
3153 cstate.ecpl_word_start = tokst->tokst_buf[ndx].tok_line_off;
3154 cstate.ecpl_word_end = word_end;
3155 cstate.ecpl_add_mod_colon = 0;
3156 cstate.ecpl_token_str = tokst->tokst_buf[ndx].tok_str;
3157 cstate.ecpl_token_len = tokst->tokst_buf[ndx].tok_len;
3160 * If there is only one token, then we are completing the
3161 * command itself.
3163 if (ndx == 0) {
3164 elfedit_cpl_command(&cstate);
3165 return (0);
3169 * There is more than one token. Use the first one to
3170 * locate the definition for the command. If we don't have
3171 * a definition for the command, then there's nothing more
3172 * we can do.
3174 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 0, &mod);
3175 if (cmd == NULL)
3176 return (0);
3179 * Since we know the command, give them a quick usage message.
3180 * It may be that they just need a quick reminder about the form
3181 * of the command and the options.
3183 (void) gl_normal_io(state.input.gl);
3184 elfedit_printf(MSG_INTL(MSG_USAGE_CMD),
3185 elfedit_format_command_usage(mod, cmd, NULL, 0));
3189 * We have a generous setting for ELFEDIT_MAXCPLARGS, so there
3190 * should always be plenty of room. If there's not room, we
3191 * can't proceed.
3193 if (ndx >= ELFEDIT_MAXCPLARGS)
3194 return (0);
3197 * Put pointers to the tokens into argv, and determine how
3198 * many of the tokens are optional arguments.
3200 * We consider the final optional argument to be the rightmost
3201 * argument that starts with a '-'. If a '--' is seen, then
3202 * we stop there, and any argument that follows is a plain argument
3203 * (even if it starts with '-').
3205 * We look for an inherited '-o' option, because we are willing
3206 * to supply command completion for these values.
3208 num_opt = 0;
3209 opt_term_seen = 0;
3210 skip_one = 0;
3211 for (i = 0; i < ndx; i++) {
3212 argv[i] = tokst->tokst_buf[i + 1].tok_str;
3213 if (opt_term_seen || skip_one) {
3214 skip_one = 0;
3215 continue;
3217 skip_one = 0;
3218 ostyle_ndx = -1;
3219 if ((strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_MINUS)) == 0) ||
3220 (*argv[i] != '-')) {
3221 opt_term_seen = 1;
3222 continue;
3224 num_opt = i + 1;
3226 * If it is a recognised ELFEDIT_CMDOA_F_VALUE option,
3227 * then the item following it is the associated value.
3228 * Check for this and skip the value.
3230 * At the same time, look for STDOA_OPT_O inherited
3231 * options. We want to identify the index of any such
3232 * item. Although the option is simply "-o", we are willing
3233 * to treat any option that starts with "-o" as a potential
3234 * STDOA_OPT_O. This lets us to command completion for things
3235 * like "-onum", and is otherwise harmless, the only cost
3236 * being a few additional strcmps by the cpl code.
3238 if ((optarg = cmd->cmd_opt) == NULL)
3239 continue;
3240 while (optarg->oa_name != NULL) {
3241 int is_ostyle_optarg =
3242 (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) &&
3243 (optarg->oa_name == ELFEDIT_STDOA_OPT_O);
3245 elfedit_next_optarg(&optarg, &item);
3246 if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) {
3247 if (is_ostyle_optarg && (strncmp(argv[i],
3248 MSG_ORIG(MSG_STR_MINUS_O), 2) == 0))
3249 ostyle_ndx = i + 1;
3251 if (strcmp(item.oai_name, argv[i]) == 0) {
3252 num_opt = i + 2;
3253 skip_one = 1;
3254 break;
3257 * If it didn't match "-o" exactly, but it is
3258 * ostyle_ndx, then it is a potential combined
3259 * STDOA_OPT_O, as discussed above. It counts
3260 * as a single argument.
3262 if (ostyle_ndx == ndx)
3263 break;
3268 #ifdef DEBUG_CMD_MATCH
3269 (void) printf("NDX(%d) NUM_OPT(%d) ostyle_ndx(%d)\n", ndx, num_opt,
3270 ostyle_ndx);
3271 #endif
3273 if (ostyle_ndx != -1) {
3275 * If ostyle_ndx is one less than ndx, and ndx is
3276 * the same as num_opt, then we have a definitive
3277 * STDOA_OPT_O inherited outstyle option. We supply
3278 * the value strings, and are done.
3280 if ((ostyle_ndx == (ndx - 1)) && (ndx == num_opt)) {
3281 elfedit_cpl_atoconst(&cstate, ELFEDIT_CONST_OUTSTYLE);
3282 return (0);
3286 * If ostyle is the same as ndx, then we have an option
3287 * staring with "-o" that may end up being a STDOA_OPT_O,
3288 * and we are still inside that token. In this case, we
3289 * supply completion strings that include the leading
3290 * "-o" followed by the values, without a space
3291 * (i.e. "-onum"). We then fall through, allowing any
3292 * other options starting with "-o" to be added
3293 * below. elfedit_cpl_match() will throw out the incorrect
3294 * options, so it is harmless to add these extra items in
3295 * the worst case, and useful otherwise.
3297 if (ostyle_ndx == ndx)
3298 elfedit_cpl_atoconst(&cstate,
3299 ELFEDIT_CONST_OUTSTYLE_MO);
3303 * If (ndx <= num_opt), then the token needing completion
3304 * is an option. If the leading '-' is there, then we should fill
3305 * in all of the option alternatives. If anything follows the '-'
3306 * though, we assume that the user has already figured out what
3307 * option to use, and we leave well enough alone.
3309 * Note that we are intentionally ignoring a related case
3310 * where supplying option strings would be legal: In the case
3311 * where we are one past the last option (ndx == (num_opt + 1)),
3312 * and the current option is an empty string, the argument can
3313 * be either a plain argument or an option --- the user needs to
3314 * enter the next character before we can tell. It would be
3315 * OK to enter the option strings in this case. However, consider
3316 * what happens when the first plain argument to the command does
3317 * not provide any command completion (e.g. it is a plain integer).
3318 * In this case, tecla will see that all the alternatives start
3319 * with '-', and will insert a '-' into the input. If the user
3320 * intends the next argument to be plain, they will have to delete
3321 * this '-', which is annoying. Worse than that, they may be confused
3322 * by it, and think that the plain argument is not allowed there.
3323 * The best solution is to not supply option strings unless the
3324 * user first enters the '-'.
3326 if ((ndx <= num_opt) && (argv[ndx - 1][0] == '-')) {
3327 if ((optarg = cmd->cmd_opt) != NULL) {
3328 while (optarg->oa_name != NULL) {
3329 elfedit_next_optarg(&optarg, &item);
3330 elfedit_cpl_match(&cstate, item.oai_name, 1);
3333 return (0);
3337 * At this point we know that ndx and num_opt are not equal.
3338 * If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE
3339 * argument at the end, and the following value has not been entered.
3341 * If ndx is greater than num_opt, it means that we are looking
3342 * at a plain argument (or in the case where (ndx == (num_opt + 1)),
3343 * a *potential* plain argument.
3345 * If the command has a completion function registered, then we
3346 * hand off the remaining work to it. The cmd_cplfunc field is
3347 * the generic definition. We need to cast it to the type that matches
3348 * the proper ELFCLASS before calling it.
3350 if (state.elf.elfclass == ELFCLASS32) {
3351 elfedit32_cmdcpl_func_t *cmdcpl_func =
3352 (elfedit32_cmdcpl_func_t *)cmd->cmd_cplfunc;
3354 if (cmdcpl_func != NULL)
3355 (* cmdcpl_func)(state.elf.obj_state.s32,
3356 &cstate, ndx, argv, num_opt);
3357 } else {
3358 elfedit64_cmdcpl_func_t *cmdcpl_func =
3359 (elfedit64_cmdcpl_func_t *)cmd->cmd_cplfunc;
3361 if (cmdcpl_func != NULL)
3362 (* cmdcpl_func)(state.elf.obj_state.s64,
3363 &cstate, ndx, argv, num_opt);
3366 return (0);
3371 * Read a line of input from stdin, and return pointer to it.
3373 * This routine uses a private buffer, so the contents of the returned
3374 * string are only good until the next call.
3376 static const char *
3377 read_cmd(void)
3379 char *s;
3381 if (state.input.full_tty) {
3382 state.input.in_tecla = TRUE;
3383 s = gl_get_line(state.input.gl,
3384 MSG_ORIG(MSG_STR_PROMPT), NULL, -1);
3385 state.input.in_tecla = FALSE;
3387 * gl_get_line() returns NULL for EOF or for error. EOF is fine,
3388 * but we need to catch and report anything else. Since
3389 * reading from stdin is critical to our operation, an
3390 * error implies that we cannot recover and must exit.
3392 if ((s == NULL) &&
3393 (gl_return_status(state.input.gl) == GLR_ERROR)) {
3394 elfedit_msg(ELFEDIT_MSG_FATAL, MSG_INTL(MSG_ERR_GLREAD),
3395 gl_error_message(state.input.gl, NULL, 0));
3397 } else {
3399 * This should be a dynamically sized buffer, but for now,
3400 * I'm going to take a simpler path.
3402 static char cmd_buf[ELFEDIT_MAXCMD + 1];
3404 s = fgets(cmd_buf, sizeof (cmd_buf), stdin);
3407 /* Return user string, or 'quit' on EOF */
3408 return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT));
3412 main(int argc, char **argv, char **envp)
3415 * Note: This function can use setjmp()/longjmp() which does
3416 * not preserve the values of auto/register variables. Hence,
3417 * variables that need their values preserved across a jump must
3418 * be marked volatile, or must not be auto/register.
3420 * Volatile can be messy, because it requires explictly casting
3421 * away the attribute when passing it to functions, or declaring
3422 * those functions with the attribute as well. In a single threaded
3423 * program like this one, an easier approach is to make things
3424 * static. That can be done here, or by putting things in the
3425 * 'state' structure.
3428 int c, i;
3429 int num_batch = 0;
3430 char **batch_list = NULL;
3431 const char *modpath = NULL;
3434 * Always have liblddb display unclipped section names.
3435 * This global is exported by liblddb, and declared in debug.h.
3437 dbg_desc->d_extra |= DBG_E_LONG;
3439 opterr = 0;
3440 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
3441 switch (c) {
3442 case 'a':
3443 state.flags |= ELFEDIT_F_AUTOPRINT;
3444 break;
3446 case 'd':
3447 state.flags |= ELFEDIT_F_DEBUG;
3448 break;
3450 case 'e':
3452 * Delay parsing the -e options until after the call to
3453 * conv_check_native() so that we won't bother loading
3454 * modules of the wrong class.
3456 if (batch_list == NULL)
3457 batch_list = elfedit_malloc(
3458 MSG_INTL(MSG_ALLOC_BATCHLST),
3459 sizeof (*batch_list) * (argc - 1));
3460 batch_list[num_batch++] = optarg;
3461 break;
3463 case 'L':
3464 modpath = optarg;
3465 break;
3467 case 'o':
3468 if (elfedit_atooutstyle(optarg, &state.outstyle) == 0)
3469 usage(1);
3470 break;
3472 case 'r':
3473 state.flags |= ELFEDIT_F_READONLY;
3474 break;
3476 case '?':
3477 usage(1);
3482 * We allow 0, 1, or 2 files:
3484 * The no-file case is an extremely limited mode, in which the
3485 * only commands allowed to execute come from the sys: module.
3486 * This mode exists primarily to allow easy access to the help
3487 * facility.
3489 * To get full access to elfedit's capablities, there must
3490 * be an input file. If this is not a readonly
3491 * session, then an optional second output file is allowed.
3493 * In the case where two files are given and the session is
3494 * readonly, use a full usage message, because the simple
3495 * one isn't enough for the user to understand their error.
3496 * Otherwise, the simple usage message suffices.
3498 argc = argc - optind;
3499 if ((argc == 2) && (state.flags & ELFEDIT_F_READONLY))
3500 usage(1);
3501 if (argc > 2)
3502 usage(0);
3504 state.file.present = (argc != 0);
3507 * If we have a file to edit, and unless told otherwise by the
3508 * caller, we try to run the 64-bit version of this program
3509 * when the system is capable of it. If that fails, then we
3510 * continue on with the currently running version.
3512 * To force 32-bit execution on a 64-bit host, set the
3513 * LD_NOEXEC_64 environment variable to a non-empty value.
3515 * There is no reason to bother with this if in "no file" mode.
3517 if (state.file.present != 0)
3518 (void) conv_check_native(argv, envp);
3520 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_VERSION),
3521 (sizeof (char *) == 8) ? 64 : 32);
3524 * Put a module definition for the builtin system module on the
3525 * module list. We know it starts out empty, so we do not have
3526 * to go through a more general insertion process than this.
3528 state.modlist = elfedit_sys_init(ELFEDIT_VER_CURRENT);
3530 /* Establish the search path for loadable modules */
3531 establish_modpath(modpath);
3534 * Now that we are running the final version of this program,
3535 * deal with the input/output file(s).
3537 if (state.file.present == 0) {
3539 * This is arbitrary --- we simply need to be able to
3540 * load modules so that we can access their help strings
3541 * and command completion functions. Without a file, we
3542 * will refuse to call commands from any module other
3543 * than sys. Those commands have been written to be aware
3544 * of the case where there is no input file, and are
3545 * therefore safe to run.
3547 state.elf.elfclass = ELFCLASS32;
3548 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NOFILE));
3550 } else {
3551 state.file.infile = argv[optind];
3552 if (argc == 1) {
3553 state.file.outfile = state.file.infile;
3554 if (state.flags & ELFEDIT_F_READONLY)
3555 elfedit_msg(ELFEDIT_MSG_DEBUG,
3556 MSG_INTL(MSG_DEBUG_READONLY));
3557 else
3558 elfedit_msg(ELFEDIT_MSG_DEBUG,
3559 MSG_INTL(MSG_DEBUG_INPLACEWARN),
3560 state.file.infile);
3561 } else {
3562 state.file.outfile = argv[optind + 1];
3563 create_outfile(state.file.infile, state.file.outfile);
3564 elfedit_msg(ELFEDIT_MSG_DEBUG,
3565 MSG_INTL(MSG_DEBUG_CPFILE),
3566 state.file.infile, state.file.outfile);
3568 * We are editing a copy of the original file that we
3569 * just created. If we should exit before the edits are
3570 * updated, then we want to unlink this copy so that we
3571 * don't leave junk lying around. Once an update
3572 * succeeds however, we'll leave it in place even
3573 * if an error occurs afterwards.
3575 state.file.unlink_on_exit = 1;
3576 optind++; /* Edit copy instead of the original */
3579 init_obj_state(state.file.outfile);
3584 * Process commands.
3586 * If any -e options were used, then do them and
3587 * immediately exit. On error, exit immediately without
3588 * updating the target ELF file. On success, the 'write'
3589 * and 'quit' commands are implicit in this mode.
3591 * If no -e options are used, read commands from stdin.
3592 * quit must be explicitly used. Exit is implicit on EOF.
3593 * If stdin is a tty, then errors do not cause the editor
3594 * to terminate. Rather, the error message is printed, and the
3595 * user prompted to continue.
3597 if (batch_list != NULL) { /* -e was used */
3598 /* Compile the commands */
3599 for (i = 0; i < num_batch; i++)
3600 parse_user_cmd(batch_list[i]);
3601 free(batch_list);
3604 * 'write' and 'quit' are implicit in this mode.
3605 * Add them as well.
3607 if ((state.flags & ELFEDIT_F_READONLY) == 0)
3608 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_WRITE));
3609 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_QUIT));
3611 /* And run them. This won't return, thanks to the 'quit' */
3612 dispatch_user_cmds();
3613 } else {
3614 state.input.is_tty = isatty(fileno(stdin));
3615 state.input.full_tty = state.input.is_tty &&
3616 isatty(fileno(stdout));
3618 if (state.input.full_tty) {
3619 struct sigaction act;
3621 act.sa_sigaction = sigint_handler;
3622 (void) sigemptyset(&act.sa_mask);
3623 act.sa_flags = 0;
3624 if (sigaction(SIGINT, &act, NULL) == -1) {
3625 int err = errno;
3626 elfedit_msg(ELFEDIT_MSG_ERR,
3627 MSG_INTL(MSG_ERR_SIGACTION), strerror(err));
3630 * If pager process exits before we are done
3631 * writing, we can see SIGPIPE. Prevent it
3632 * from killing the process.
3634 (void) sigignore(SIGPIPE);
3636 /* Open tecla handle for command line editing */
3637 state.input.gl = new_GetLine(ELFEDIT_MAXCMD,
3638 ELFEDIT_MAXHIST);
3639 /* Register our command completion function */
3640 (void) gl_customize_completion(state.input.gl,
3641 NULL, cmd_match_fcn);
3644 * Make autoprint the default for interactive
3645 * sessions.
3647 state.flags |= ELFEDIT_F_AUTOPRINT;
3649 for (;;) {
3651 * If this is an interactive session, then use
3652 * sigsetjmp()/siglongjmp() to recover from bad
3653 * commands and keep going. A non-0 return from
3654 * sigsetjmp() means that an error just occurred.
3655 * In that case, we simply restart this loop.
3657 if (state.input.is_tty) {
3658 if (sigsetjmp(state.msg_jbuf.env, 1) != 0) {
3659 if (state.input.full_tty)
3660 gl_abandon_line(state.input.gl);
3661 continue;
3663 state.msg_jbuf.active = TRUE;
3667 * Force all output out before each command.
3668 * This is a no-OP when a tty is in use, but
3669 * in a pipeline, it ensures that the block
3670 * mode buffering doesn't delay output past
3671 * the completion of each command.
3673 * If we didn't do this, the output would eventually
3674 * arrive at its destination, but the lag can be
3675 * annoying when you pipe the output into a tool
3676 * that displays the results in real time.
3678 (void) fflush(stdout);
3679 (void) fflush(stderr);
3681 parse_user_cmd(read_cmd());
3682 dispatch_user_cmds();
3683 state.msg_jbuf.active = FALSE;
3688 /*NOTREACHED*/
3689 return (0);