diff options
Diffstat (limited to 'gdb/cli/cli-script.c')
-rw-r--r-- | gdb/cli/cli-script.c | 1315 |
1 files changed, 1315 insertions, 0 deletions
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c new file mode 100644 index 0000000..67a6515 --- /dev/null +++ b/gdb/cli/cli-script.c @@ -0,0 +1,1315 @@ +/* GDB CLI command scripting. + Copyright 1986-2000 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "value.h" +#include "language.h" /* For value_true */ +#include <ctype.h> + +#ifdef UI_OUT +#include "ui-out.h" +#endif + +#include "top.h" +#include "cli/cli-cmds.h" +#include "cli/cli-decode.h" +#include "cli/cli-script.h" + +/* From gdb/top.c */ + +extern void dont_repeat (void); + +extern void do_restore_instream_cleanup (void *stream); + +/* Prototypes for local functions */ + +static struct cleanup * + make_cleanup_free_command_lines (struct command_line **arg); + +static enum command_control_type + recurse_read_control_structure (struct command_line *current_cmd); + +static char *insert_args (char *line); + +static struct cleanup * setup_user_args (char *p); + +static void validate_comname (char *); + +/* Level of control structure. */ +static int control_level; + +/* Source command state variable. */ +static int source_error_allocated; + +/* Structure for arguments to user defined functions. */ +#define MAXUSERARGS 10 +struct user_args + { + struct user_args *next; + struct + { + char *arg; + int len; + } + a[MAXUSERARGS]; + int count; + } + *user_args; + + +/* Allocate, initialize a new command line structure for one of the + control commands (if/while). */ + +static struct command_line * +build_command_line (enum command_control_type type, char *args) +{ + struct command_line *cmd; + + if (args == NULL) + error ("if/while commands require arguments.\n"); + + cmd = (struct command_line *) xmalloc (sizeof (struct command_line)); + cmd->next = NULL; + cmd->control_type = type; + + cmd->body_count = 1; + cmd->body_list + = (struct command_line **) xmalloc (sizeof (struct command_line *) + * cmd->body_count); + memset (cmd->body_list, 0, sizeof (struct command_line *) * cmd->body_count); + cmd->line = savestring (args, strlen (args)); + return cmd; +} + +/* Build and return a new command structure for the control commands + such as "if" and "while". */ + +static struct command_line * +get_command_line (enum command_control_type type, char *arg) +{ + struct command_line *cmd; + struct cleanup *old_chain = NULL; + + /* Allocate and build a new command line structure. */ + cmd = build_command_line (type, arg); + + old_chain = make_cleanup_free_command_lines (&cmd); + + /* Read in the body of this command. */ + if (recurse_read_control_structure (cmd) == invalid_control) + { + warning ("error reading in control structure\n"); + do_cleanups (old_chain); + return NULL; + } + + discard_cleanups (old_chain); + return cmd; +} + +/* Recursively print a command (including full control structures). */ +#ifdef UI_OUT +void +print_command_lines (struct ui_out *uiout, struct command_line *cmd, + unsigned int depth) +{ + struct command_line *list; + + list = cmd; + while (list) + { + + if (depth) + ui_out_spaces (uiout, 2 * depth); + + /* A simple command, print it and continue. */ + if (list->control_type == simple_control) + { + ui_out_field_string (uiout, NULL, list->line); + ui_out_text (uiout, "\n"); + list = list->next; + continue; + } + + /* loop_continue to jump to the start of a while loop, print it + and continue. */ + if (list->control_type == continue_control) + { + ui_out_field_string (uiout, NULL, "loop_continue"); + ui_out_text (uiout, "\n"); + list = list->next; + continue; + } + + /* loop_break to break out of a while loop, print it and continue. */ + if (list->control_type == break_control) + { + ui_out_field_string (uiout, NULL, "loop_break"); + ui_out_text (uiout, "\n"); + list = list->next; + continue; + } + + /* A while command. Recursively print its subcommands and continue. */ + if (list->control_type == while_control) + { + ui_out_text (uiout, "while "); + ui_out_field_fmt (uiout, NULL, "while %s", list->line); + ui_out_text (uiout, "\n"); + print_command_lines (uiout, *list->body_list, depth + 1); + ui_out_field_string (uiout, NULL, "end"); + if (depth) + ui_out_spaces (uiout, 2 * depth); + ui_out_text (uiout, "end\n"); + list = list->next; + continue; + } + + /* An if command. Recursively print both arms before continueing. */ + if (list->control_type == if_control) + { + ui_out_text (uiout, "if "); + ui_out_field_fmt (uiout, NULL, "if %s", list->line); + ui_out_text (uiout, "\n"); + /* The true arm. */ + print_command_lines (uiout, list->body_list[0], depth + 1); + + /* Show the false arm if it exists. */ + if (list->body_count == 2) + { + if (depth) + ui_out_spaces (uiout, 2 * depth); + ui_out_field_string (uiout, NULL, "else"); + ui_out_text (uiout, "else\n"); + print_command_lines (uiout, list->body_list[1], depth + 1); + } + + ui_out_field_string (uiout, NULL, "end"); + if (depth) + ui_out_spaces (uiout, 2 * depth); + ui_out_text (uiout, "end\n"); + list = list->next; + continue; + } + + /* ignore illegal command type and try next */ + list = list->next; + } /* while (list) */ +} +#else +void +print_command_line (struct command_line *cmd, unsigned int depth, + struct ui_file *stream) +{ + unsigned int i; + + if (depth) + { + for (i = 0; i < depth; i++) + fputs_filtered (" ", stream); + } + + /* A simple command, print it and return. */ + if (cmd->control_type == simple_control) + { + fputs_filtered (cmd->line, stream); + fputs_filtered ("\n", stream); + return; + } + + /* loop_continue to jump to the start of a while loop, print it + and return. */ + if (cmd->control_type == continue_control) + { + fputs_filtered ("loop_continue\n", stream); + return; + } + + /* loop_break to break out of a while loop, print it and return. */ + if (cmd->control_type == break_control) + { + fputs_filtered ("loop_break\n", stream); + return; + } + + /* A while command. Recursively print its subcommands before returning. */ + if (cmd->control_type == while_control) + { + struct command_line *list; + fputs_filtered ("while ", stream); + fputs_filtered (cmd->line, stream); + fputs_filtered ("\n", stream); + list = *cmd->body_list; + while (list) + { + print_command_line (list, depth + 1, stream); + list = list->next; + } + } + + /* An if command. Recursively print both arms before returning. */ + if (cmd->control_type == if_control) + { + fputs_filtered ("if ", stream); + fputs_filtered (cmd->line, stream); + fputs_filtered ("\n", stream); + /* The true arm. */ + print_command_line (cmd->body_list[0], depth + 1, stream); + + /* Show the false arm if it exists. */ + if (cmd->body_count == 2) + { + if (depth) + { + for (i = 0; i < depth; i++) + fputs_filtered (" ", stream); + } + fputs_filtered ("else\n", stream); + print_command_line (cmd->body_list[1], depth + 1, stream); + } + if (depth) + { + for (i = 0; i < depth; i++) + fputs_filtered (" ", stream); + } + fputs_filtered ("end\n", stream); + } +} +#endif + +/* Execute the command in CMD. */ + +void +execute_user_command (struct cmd_list_element *c, char *args) +{ + register struct command_line *cmdlines; + struct cleanup *old_chain; + enum command_control_type ret; + + old_chain = setup_user_args (args); + + cmdlines = c->user_commands; + if (cmdlines == 0) + /* Null command */ + return; + + /* Set the instream to 0, indicating execution of a + user-defined function. */ + old_chain = make_cleanup (do_restore_instream_cleanup, instream); + instream = (FILE *) 0; + while (cmdlines) + { + ret = execute_control_command (cmdlines); + if (ret != simple_control && ret != break_control) + { + warning ("Error in control structure.\n"); + break; + } + cmdlines = cmdlines->next; + } + do_cleanups (old_chain); +} + +enum command_control_type +execute_control_command (struct command_line *cmd) +{ + struct expression *expr; + struct command_line *current; + struct cleanup *old_chain = 0; + value_ptr val; + value_ptr val_mark; + int loop; + enum command_control_type ret; + char *new_line; + + switch (cmd->control_type) + { + case simple_control: + /* A simple command, execute it and return. */ + new_line = insert_args (cmd->line); + if (!new_line) + return invalid_control; + old_chain = make_cleanup (free_current_contents, &new_line); + execute_command (new_line, 0); + ret = cmd->control_type; + break; + + case continue_control: + case break_control: + /* Return for "continue", and "break" so we can either + continue the loop at the top, or break out. */ + ret = cmd->control_type; + break; + + case while_control: + { + /* Parse the loop control expression for the while statement. */ + new_line = insert_args (cmd->line); + if (!new_line) + return invalid_control; + old_chain = make_cleanup (free_current_contents, &new_line); + expr = parse_expression (new_line); + make_cleanup (free_current_contents, &expr); + + ret = simple_control; + loop = 1; + + /* Keep iterating so long as the expression is true. */ + while (loop == 1) + { + int cond_result; + + QUIT; + + /* Evaluate the expression. */ + val_mark = value_mark (); + val = evaluate_expression (expr); + cond_result = value_true (val); + value_free_to_mark (val_mark); + + /* If the value is false, then break out of the loop. */ + if (!cond_result) + break; + + /* Execute the body of the while statement. */ + current = *cmd->body_list; + while (current) + { + ret = execute_control_command (current); + + /* If we got an error, or a "break" command, then stop + looping. */ + if (ret == invalid_control || ret == break_control) + { + loop = 0; + break; + } + + /* If we got a "continue" command, then restart the loop + at this point. */ + if (ret == continue_control) + break; + + /* Get the next statement. */ + current = current->next; + } + } + + /* Reset RET so that we don't recurse the break all the way down. */ + if (ret == break_control) + ret = simple_control; + + break; + } + + case if_control: + { + new_line = insert_args (cmd->line); + if (!new_line) + return invalid_control; + old_chain = make_cleanup (free_current_contents, &new_line); + /* Parse the conditional for the if statement. */ + expr = parse_expression (new_line); + make_cleanup (free_current_contents, &expr); + + current = NULL; + ret = simple_control; + + /* Evaluate the conditional. */ + val_mark = value_mark (); + val = evaluate_expression (expr); + + /* Choose which arm to take commands from based on the value of the + conditional expression. */ + if (value_true (val)) + current = *cmd->body_list; + else if (cmd->body_count == 2) + current = *(cmd->body_list + 1); + value_free_to_mark (val_mark); + + /* Execute commands in the given arm. */ + while (current) + { + ret = execute_control_command (current); + + /* If we got an error, get out. */ + if (ret != simple_control) + break; + + /* Get the next statement in the body. */ + current = current->next; + } + + break; + } + + default: + warning ("Invalid control type in command structure."); + return invalid_control; + } + + if (old_chain) + do_cleanups (old_chain); + + return ret; +} + +/* "while" command support. Executes a body of statements while the + loop condition is nonzero. */ + +void +while_command (char *arg, int from_tty) +{ + struct command_line *command = NULL; + + control_level = 1; + command = get_command_line (while_control, arg); + + if (command == NULL) + return; + + execute_control_command (command); + free_command_lines (&command); +} + +/* "if" command support. Execute either the true or false arm depending + on the value of the if conditional. */ + +void +if_command (char *arg, int from_tty) +{ + struct command_line *command = NULL; + + control_level = 1; + command = get_command_line (if_control, arg); + + if (command == NULL) + return; + + execute_control_command (command); + free_command_lines (&command); +} + +/* Cleanup */ +static void +arg_cleanup (void *ignore) +{ + struct user_args *oargs = user_args; + if (!user_args) + internal_error ("Internal error, arg_cleanup called with no user args.\n"); + + user_args = user_args->next; + free (oargs); +} + +/* Bind the incomming arguments for a user defined command to + $arg0, $arg1 ... $argMAXUSERARGS. */ + +static struct cleanup * +setup_user_args (char *p) +{ + struct user_args *args; + struct cleanup *old_chain; + unsigned int arg_count = 0; + + args = (struct user_args *) xmalloc (sizeof (struct user_args)); + memset (args, 0, sizeof (struct user_args)); + + args->next = user_args; + user_args = args; + + old_chain = make_cleanup (arg_cleanup, 0/*ignored*/); + + if (p == NULL) + return old_chain; + + while (*p) + { + char *start_arg; + int squote = 0; + int dquote = 0; + int bsquote = 0; + + if (arg_count >= MAXUSERARGS) + { + error ("user defined function may only have %d arguments.\n", + MAXUSERARGS); + return old_chain; + } + + /* Strip whitespace. */ + while (*p == ' ' || *p == '\t') + p++; + + /* P now points to an argument. */ + start_arg = p; + user_args->a[arg_count].arg = p; + + /* Get to the end of this argument. */ + while (*p) + { + if (((*p == ' ' || *p == '\t')) && !squote && !dquote && !bsquote) + break; + else + { + if (bsquote) + bsquote = 0; + else if (*p == '\\') + bsquote = 1; + else if (squote) + { + if (*p == '\'') + squote = 0; + } + else if (dquote) + { + if (*p == '"') + dquote = 0; + } + else + { + if (*p == '\'') + squote = 1; + else if (*p == '"') + dquote = 1; + } + p++; + } + } + + user_args->a[arg_count].len = p - start_arg; + arg_count++; + user_args->count++; + } + return old_chain; +} + +/* Given character string P, return a point to the first argument ($arg), + or NULL if P contains no arguments. */ + +static char * +locate_arg (char *p) +{ + while ((p = strchr (p, '$'))) + { + if (strncmp (p, "$arg", 4) == 0 && isdigit (p[4])) + return p; + p++; + } + return NULL; +} + +/* Insert the user defined arguments stored in user_arg into the $arg + arguments found in line, with the updated copy being placed into nline. */ + +static char * +insert_args (char *line) +{ + char *p, *save_line, *new_line; + unsigned len, i; + + /* First we need to know how much memory to allocate for the new line. */ + save_line = line; + len = 0; + while ((p = locate_arg (line))) + { + len += p - line; + i = p[4] - '0'; + + if (i >= user_args->count) + { + error ("Missing argument %d in user function.\n", i); + return NULL; + } + len += user_args->a[i].len; + line = p + 5; + } + + /* Don't forget the tail. */ + len += strlen (line); + + /* Allocate space for the new line and fill it in. */ + new_line = (char *) xmalloc (len + 1); + if (new_line == NULL) + return NULL; + + /* Restore pointer to beginning of old line. */ + line = save_line; + + /* Save pointer to beginning of new line. */ + save_line = new_line; + + while ((p = locate_arg (line))) + { + int i, len; + + memcpy (new_line, line, p - line); + new_line += p - line; + i = p[4] - '0'; + + len = user_args->a[i].len; + if (len) + { + memcpy (new_line, user_args->a[i].arg, len); + new_line += len; + } + line = p + 5; + } + /* Don't forget the tail. */ + strcpy (new_line, line); + + /* Return a pointer to the beginning of the new line. */ + return save_line; +} + + +/* Expand the body_list of COMMAND so that it can hold NEW_LENGTH + code bodies. This is typically used when we encounter an "else" + clause for an "if" command. */ + +static void +realloc_body_list (struct command_line *command, int new_length) +{ + int n; + struct command_line **body_list; + + n = command->body_count; + + /* Nothing to do? */ + if (new_length <= n) + return; + + body_list = (struct command_line **) + xmalloc (sizeof (struct command_line *) * new_length); + + memcpy (body_list, command->body_list, sizeof (struct command_line *) * n); + + free (command->body_list); + command->body_list = body_list; + command->body_count = new_length; +} + +/* Read one line from the input stream. If the command is an "else" or + "end", return such an indication to the caller. */ + +static enum misc_command_type +read_next_line (struct command_line **command) +{ + char *p, *p1, *prompt_ptr, control_prompt[256]; + int i = 0; + + if (control_level >= 254) + error ("Control nesting too deep!\n"); + + /* Set a prompt based on the nesting of the control commands. */ + if (instream == stdin || (instream == 0 && readline_hook != NULL)) + { + for (i = 0; i < control_level; i++) + control_prompt[i] = ' '; + control_prompt[i] = '>'; + control_prompt[i + 1] = '\0'; + prompt_ptr = (char *) &control_prompt[0]; + } + else + prompt_ptr = NULL; + + p = command_line_input (prompt_ptr, instream == stdin, "commands"); + + /* Not sure what to do here. */ + if (p == NULL) + return end_command; + + /* Strip leading and trailing whitespace. */ + while (*p == ' ' || *p == '\t') + p++; + + p1 = p + strlen (p); + while (p1 != p && (p1[-1] == ' ' || p1[-1] == '\t')) + p1--; + + /* Blanks and comments don't really do anything, but we need to + distinguish them from else, end and other commands which can be + executed. */ + if (p1 == p || p[0] == '#') + return nop_command; + + /* Is this the end of a simple, while, or if control structure? */ + if (p1 - p == 3 && !strncmp (p, "end", 3)) + return end_command; + + /* Is the else clause of an if control structure? */ + if (p1 - p == 4 && !strncmp (p, "else", 4)) + return else_command; + + /* Check for while, if, break, continue, etc and build a new command + line structure for them. */ + if (p1 - p > 5 && !strncmp (p, "while", 5)) + *command = build_command_line (while_control, p + 6); + else if (p1 - p > 2 && !strncmp (p, "if", 2)) + *command = build_command_line (if_control, p + 3); + else if (p1 - p == 10 && !strncmp (p, "loop_break", 10)) + { + *command = (struct command_line *) + xmalloc (sizeof (struct command_line)); + (*command)->next = NULL; + (*command)->line = NULL; + (*command)->control_type = break_control; + (*command)->body_count = 0; + (*command)->body_list = NULL; + } + else if (p1 - p == 13 && !strncmp (p, "loop_continue", 13)) + { + *command = (struct command_line *) + xmalloc (sizeof (struct command_line)); + (*command)->next = NULL; + (*command)->line = NULL; + (*command)->control_type = continue_control; + (*command)->body_count = 0; + (*command)->body_list = NULL; + } + else + { + /* A normal command. */ + *command = (struct command_line *) + xmalloc (sizeof (struct command_line)); + (*command)->next = NULL; + (*command)->line = savestring (p, p1 - p); + (*command)->control_type = simple_control; + (*command)->body_count = 0; + (*command)->body_list = NULL; + } + + /* Nothing special. */ + return ok_command; +} + +/* Recursively read in the control structures and create a command_line + structure from them. + + The parent_control parameter is the control structure in which the + following commands are nested. */ + +static enum command_control_type +recurse_read_control_structure (struct command_line *current_cmd) +{ + int current_body, i; + enum misc_command_type val; + enum command_control_type ret; + struct command_line **body_ptr, *child_tail, *next; + + child_tail = NULL; + current_body = 1; + + /* Sanity checks. */ + if (current_cmd->control_type == simple_control) + { + error ("Recursed on a simple control type\n"); + return invalid_control; + } + + if (current_body > current_cmd->body_count) + { + error ("Allocated body is smaller than this command type needs\n"); + return invalid_control; + } + + /* Read lines from the input stream and build control structures. */ + while (1) + { + dont_repeat (); + + next = NULL; + val = read_next_line (&next); + + /* Just skip blanks and comments. */ + if (val == nop_command) + continue; + + if (val == end_command) + { + if (current_cmd->control_type == while_control + || current_cmd->control_type == if_control) + { + /* Success reading an entire control structure. */ + ret = simple_control; + break; + } + else + { + ret = invalid_control; + break; + } + } + + /* Not the end of a control structure. */ + if (val == else_command) + { + if (current_cmd->control_type == if_control + && current_body == 1) + { + realloc_body_list (current_cmd, 2); + current_body = 2; + child_tail = NULL; + continue; + } + else + { + ret = invalid_control; + break; + } + } + + if (child_tail) + { + child_tail->next = next; + } + else + { + body_ptr = current_cmd->body_list; + for (i = 1; i < current_body; i++) + body_ptr++; + + *body_ptr = next; + + } + + child_tail = next; + + /* If the latest line is another control structure, then recurse + on it. */ + if (next->control_type == while_control + || next->control_type == if_control) + { + control_level++; + ret = recurse_read_control_structure (next); + control_level--; + + if (ret != simple_control) + break; + } + } + + dont_repeat (); + + return ret; +} + +/* Read lines from the input stream and accumulate them in a chain of + struct command_line's, which is then returned. For input from a + terminal, the special command "end" is used to mark the end of the + input, and is not included in the returned chain of commands. */ + +#define END_MESSAGE "End with a line saying just \"end\"." + +struct command_line * +read_command_lines (char *prompt_arg, int from_tty) +{ + struct command_line *head, *tail, *next; + struct cleanup *old_chain; + enum command_control_type ret; + enum misc_command_type val; + + control_level = 0; + if (readline_begin_hook) + { + /* Note - intentional to merge messages with no newline */ + (*readline_begin_hook) ("%s %s\n", prompt_arg, END_MESSAGE); + } + else if (from_tty && input_from_terminal_p ()) + { + printf_unfiltered ("%s\n%s\n", prompt_arg, END_MESSAGE); + gdb_flush (gdb_stdout); + } + + head = tail = NULL; + old_chain = NULL; + + while (1) + { + val = read_next_line (&next); + + /* Ignore blank lines or comments. */ + if (val == nop_command) + continue; + + if (val == end_command) + { + ret = simple_control; + break; + } + + if (val != ok_command) + { + ret = invalid_control; + break; + } + + if (next->control_type == while_control + || next->control_type == if_control) + { + control_level++; + ret = recurse_read_control_structure (next); + control_level--; + + if (ret == invalid_control) + break; + } + + if (tail) + { + tail->next = next; + } + else + { + head = next; + old_chain = make_cleanup_free_command_lines (&head); + } + tail = next; + } + + dont_repeat (); + + if (head) + { + if (ret != invalid_control) + { + discard_cleanups (old_chain); + } + else + do_cleanups (old_chain); + } + + if (readline_end_hook) + { + (*readline_end_hook) (); + } + return (head); +} + +/* Free a chain of struct command_line's. */ + +void +free_command_lines (struct command_line **lptr) +{ + register struct command_line *l = *lptr; + register struct command_line *next; + struct command_line **blist; + int i; + + while (l) + { + if (l->body_count > 0) + { + blist = l->body_list; + for (i = 0; i < l->body_count; i++, blist++) + free_command_lines (blist); + } + next = l->next; + free (l->line); + free ((PTR) l); + l = next; + } +} + +static void +do_free_command_lines_cleanup (void *arg) +{ + free_command_lines (arg); +} + +static struct cleanup * +make_cleanup_free_command_lines (struct command_line **arg) +{ + return make_cleanup (do_free_command_lines_cleanup, arg); +} + +static void +validate_comname (char *comname) +{ + register char *p; + + if (comname == 0) + error_no_arg ("name of command to define"); + + p = comname; + while (*p) + { + if (!isalnum (*p) && *p != '-' && *p != '_') + error ("Junk in argument list: \"%s\"", p); + p++; + } +} + +/* This is just a placeholder in the command data structures. */ +static void +user_defined_command (char *ignore, int from_tty) +{ +} + +void +define_command (char *comname, int from_tty) +{ +#define MAX_TMPBUF 128 + enum cmd_hook_type + { + CMD_NO_HOOK = 0, + CMD_PRE_HOOK, + CMD_POST_HOOK + }; + register struct command_line *cmds; + register struct cmd_list_element *c, *newc, *oldc, *hookc = 0; + char *tem = comname; + char *tem2; + char tmpbuf[MAX_TMPBUF]; + int hook_type = CMD_NO_HOOK; + int hook_name_size = 0; + +#define HOOK_STRING "hook-" +#define HOOK_LEN 5 +#define HOOK_POST_STRING "hookpost-" +#define HOOK_POST_LEN 9 + + validate_comname (comname); + + /* Look it up, and verify that we got an exact match. */ + c = lookup_cmd (&tem, cmdlist, "", -1, 1); + if (c && !STREQ (comname, c->name)) + c = 0; + + if (c) + { + if (c->class == class_user || c->class == class_alias) + tem = "Redefine command \"%s\"? "; + else + tem = "Really redefine built-in command \"%s\"? "; + if (!query (tem, c->name)) + error ("Command \"%s\" not redefined.", c->name); + } + + /* If this new command is a hook, then mark the command which it + is hooking. Note that we allow hooking `help' commands, so that + we can hook the `stop' pseudo-command. */ + + if (!strncmp (comname, HOOK_STRING, HOOK_LEN)) + { + hook_type = CMD_PRE_HOOK; + hook_name_size = HOOK_LEN; + } + else if (!strncmp (comname, HOOK_POST_STRING, HOOK_POST_LEN)) + { + hook_type = CMD_POST_HOOK; + hook_name_size = HOOK_POST_LEN; + } + + if (hook_type != CMD_NO_HOOK) + { + /* Look up cmd it hooks, and verify that we got an exact match. */ + tem = comname + hook_name_size; + hookc = lookup_cmd (&tem, cmdlist, "", -1, 0); + if (hookc && !STREQ (comname + hook_name_size, hookc->name)) + hookc = 0; + if (!hookc) + { + warning ("Your new `%s' command does not hook any existing command.", + comname); + if (!query ("Proceed? ")) + error ("Not confirmed."); + } + } + + comname = savestring (comname, strlen (comname)); + + /* If the rest of the commands will be case insensitive, this one + should behave in the same manner. */ + for (tem = comname; *tem; tem++) + if (isupper (*tem)) + *tem = tolower (*tem); + + sprintf (tmpbuf, "Type commands for definition of \"%s\".", comname); + cmds = read_command_lines (tmpbuf, from_tty); + + if (c && c->class == class_user) + free_command_lines (&c->user_commands); + + newc = add_cmd (comname, class_user, user_defined_command, + (c && c->class == class_user) + ? c->doc : savestring ("User-defined.", 13), &cmdlist); + newc->user_commands = cmds; + + /* If this new command is a hook, then mark both commands as being + tied. */ + if (hookc) + { + switch (hook_type) + { + case CMD_PRE_HOOK: + hookc->hook_pre = newc; /* Target gets hooked. */ + newc->hookee_pre = hookc; /* We are marked as hooking target cmd. */ + break; + case CMD_POST_HOOK: + hookc->hook_pre = newc; /* Target gets hooked. */ + newc->hookee_pre = hookc; /* We are marked as hooking target cmd. */ + break; + default: + /* Should never come here as hookc would be 0. */ + } + } +} + +void +document_command (char *comname, int from_tty) +{ + struct command_line *doclines; + register struct cmd_list_element *c; + char *tem = comname; + char tmpbuf[128]; + + validate_comname (comname); + + c = lookup_cmd (&tem, cmdlist, "", 0, 1); + + if (c->class != class_user) + error ("Command \"%s\" is built-in.", comname); + + sprintf (tmpbuf, "Type documentation for \"%s\".", comname); + doclines = read_command_lines (tmpbuf, from_tty); + + if (c->doc) + free (c->doc); + + { + register struct command_line *cl1; + register int len = 0; + + for (cl1 = doclines; cl1; cl1 = cl1->next) + len += strlen (cl1->line) + 1; + + c->doc = (char *) xmalloc (len + 1); + *c->doc = 0; + + for (cl1 = doclines; cl1; cl1 = cl1->next) + { + strcat (c->doc, cl1->line); + if (cl1->next) + strcat (c->doc, "\n"); + } + } + + free_command_lines (&doclines); +} + +struct source_cleanup_lines_args +{ + int old_line; + char *old_file; + char *old_pre_error; + char *old_error_pre_print; +}; + +static void +source_cleanup_lines (PTR args) +{ + struct source_cleanup_lines_args *p = + (struct source_cleanup_lines_args *) args; + source_line_number = p->old_line; + source_file_name = p->old_file; + source_pre_error = p->old_pre_error; + error_pre_print = p->old_error_pre_print; +} + +/* ARGSUSED */ +static void +do_fclose_cleanup (void *stream) +{ + fclose (stream); +} + +/* Used to implement source_command */ + +void +script_from_file (FILE *stream, char *file) +{ + struct cleanup *old_cleanups; + struct source_cleanup_lines_args old_lines; + int needed_length; + + if (stream == NULL) + { + internal_error ("called with NULL file pointer!"); + } + + old_cleanups = make_cleanup (do_fclose_cleanup, stream); + + old_lines.old_line = source_line_number; + old_lines.old_file = source_file_name; + old_lines.old_pre_error = source_pre_error; + old_lines.old_error_pre_print = error_pre_print; + make_cleanup (source_cleanup_lines, &old_lines); + source_line_number = 0; + source_file_name = file; + source_pre_error = error_pre_print == NULL ? "" : error_pre_print; + source_pre_error = savestring (source_pre_error, strlen (source_pre_error)); + make_cleanup (free, source_pre_error); + /* This will get set every time we read a line. So it won't stay "" for + long. */ + error_pre_print = ""; + + needed_length = strlen (source_file_name) + strlen (source_pre_error) + 80; + if (source_error_allocated < needed_length) + { + source_error_allocated *= 2; + if (source_error_allocated < needed_length) + source_error_allocated = needed_length; + if (source_error == NULL) + source_error = xmalloc (source_error_allocated); + else + source_error = xrealloc (source_error, source_error_allocated); + } + + read_command_file (stream); + + do_cleanups (old_cleanups); +} + +void +show_user_1 (struct cmd_list_element *c, struct ui_file *stream) +{ + register struct command_line *cmdlines; + + cmdlines = c->user_commands; + if (!cmdlines) + return; + fputs_filtered ("User command ", stream); + fputs_filtered (c->name, stream); + fputs_filtered (":\n", stream); + +#ifdef UI_OUT + print_command_lines (uiout, cmdlines, 1); + fputs_filtered ("\n", stream); +#else + while (cmdlines) + { + print_command_line (cmdlines, 4, stream); + cmdlines = cmdlines->next; + } + fputs_filtered ("\n", stream); +#endif +} + |