X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fserver%2Ftelnet_server.c;h=407ab68aefcd74d41d31f205e6e16fb3828cbef2;hp=7507afea8d688613eeadff39940e22ba743cc911;hb=9e2a0effb2b7b933db00543f1e6879541707a2f3;hpb=e78b33e3ca2fe05e82f974fcd6e745dbcfa6ca37 diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index 7507afea8d..407ab68aef 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -54,7 +54,7 @@ static int telnet_write(struct connection *connection, const void *data, if (connection_write(connection, data, len) == len) return ERROR_OK; - t_con->closed = 1; + t_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } @@ -101,29 +101,36 @@ static void telnet_log_callback(void *priv, const char *file, unsigned line, { struct connection *connection = priv; struct telnet_connection *t_con = connection->priv; - int i; + size_t i; + size_t tmp; - /* if there is no prompt, simply output the message */ - if (t_con->line_cursor < 0) { + /* If the prompt is not visible, simply output the message. */ + if (!t_con->prompt_visible) { telnet_outputline(connection, string); return; } - /* clear the command line */ - for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16) - telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i); - for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16) - telnet_write(connection, " ", i > 16 ? 16 : i); - for (i = strlen(t_con->prompt) + t_con->line_size; i > 0; i -= 16) - telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", i > 16 ? 16 : i); + /* Clear the command line. */ + tmp = strlen(t_con->prompt) + t_con->line_size; + + for (i = 0; i < tmp; i += 16) + telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", + MIN(tmp - i, 16)); + + for (i = 0; i < tmp; i += 16) + telnet_write(connection, " ", MIN(tmp - i, 16)); + + for (i = 0; i < tmp; i += 16) + telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", + MIN(tmp - i, 16)); - /* output the message */ telnet_outputline(connection, string); - /* put the command line to its previous state */ + /* Put the command line to its previous state. */ telnet_prompt(connection); telnet_write(connection, t_con->line, t_con->line_size); - for (i = t_con->line_size; i > t_con->line_cursor; i--) + + for (i = t_con->line_cursor; i < t_con->line_size; i++) telnet_write(connection, "\b", 1); } @@ -219,10 +226,11 @@ static int telnet_new_connection(struct connection *connection) connection->priv = telnet_connection; /* initialize telnet connection information */ - telnet_connection->closed = 0; + telnet_connection->closed = false; telnet_connection->line_size = 0; telnet_connection->line_cursor = 0; telnet_connection->prompt = strdup("> "); + telnet_connection->prompt_visible = true; telnet_connection->state = TELNET_STATE_DATA; /* output goes through telnet connection */ @@ -289,7 +297,7 @@ static void telnet_history_up(struct connection *connection) { struct telnet_connection *t_con = connection->priv; - int last_history = (t_con->current_history > 0) ? + size_t last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1; telnet_history_go(connection, last_history); @@ -298,11 +306,66 @@ static void telnet_history_up(struct connection *connection) static void telnet_history_down(struct connection *connection) { struct telnet_connection *t_con = connection->priv; + size_t next_history; - int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE; + next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE; telnet_history_go(connection, next_history); } +static int telnet_history_print(struct connection *connection) +{ + struct telnet_connection *tc; + + tc = connection->priv; + + for (size_t i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) { + char *line; + + /* + * The tc->next_history line contains empty string (unless NULL), thus + * it is not printed. + */ + line = tc->history[(tc->next_history + i) % TELNET_LINE_HISTORY_SIZE]; + + if (line) { + telnet_write(connection, line, strlen(line)); + telnet_write(connection, "\r\n\x00", 3); + } + } + + tc->line_size = 0; + tc->line_cursor = 0; + + /* The prompt is always placed at the line beginning. */ + telnet_write(connection, "\r", 1); + + return telnet_prompt(connection); +} + +static void telnet_move_cursor(struct connection *connection, size_t pos) +{ + struct telnet_connection *tc; + size_t tmp; + + tc = connection->priv; + + if (pos < tc->line_cursor) { + tmp = tc->line_cursor - pos; + + for (size_t i = 0; i < tmp; i += 16) + telnet_write(connection, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", + MIN(tmp - i, 16)); + } else { + tmp = pos - tc->line_cursor; + + for (size_t i = 0; i < tmp; i += 16) + telnet_write(connection, tc->line + tc->line_cursor + i, + MIN(tmp - i, 16)); + } + + tc->line_cursor = pos; +} + static int telnet_input(struct connection *connection) { int bytes_read; @@ -339,7 +402,7 @@ static int telnet_input(struct connection *connection) t_con->line[t_con->line_size++] = *buf_p; t_con->line_cursor++; } else { - int i; + size_t i; memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor); @@ -374,21 +437,11 @@ static int telnet_input(struct connection *connection) telnet_write(connection, "\r\n\x00", 3); if (strcmp(t_con->line, "history") == 0) { - int i; - for (i = 1; i < TELNET_LINE_HISTORY_SIZE; i++) { - /* the t_con->next_history line contains empty string - * (unless NULL), thus it is not printed */ - char *history_line = t_con->history[(t_con-> - next_history + i) % - TELNET_LINE_HISTORY_SIZE]; - if (history_line) { - telnet_write(connection, history_line, - strlen(history_line)); - telnet_write(connection, "\r\n\x00", 3); - } - } - t_con->line_size = 0; - t_con->line_cursor = 0; + retval = telnet_history_print(connection); + + if (retval != ERROR_OK) + return retval; + continue; } @@ -398,8 +451,7 @@ static int telnet_input(struct connection *connection) if (*t_con->line && (prev_line == NULL || strcmp(t_con->line, prev_line))) { /* if the history slot is already taken, free it */ - if (t_con->history[t_con->next_history]) - free(t_con->history[t_con->next_history]); + free(t_con->history[t_con->next_history]); /* add line to history */ t_con->history[t_con->next_history] = strdup(t_con->line); @@ -412,15 +464,14 @@ static int telnet_input(struct connection *connection) t_con->current_history = t_con->next_history; - if (t_con->history[t_con->current_history]) - free(t_con->history[t_con->current_history]); + free(t_con->history[t_con->current_history]); t_con->history[t_con->current_history] = strdup(""); } t_con->line_size = 0; /* to suppress prompt in log callback during command execution */ - t_con->line_cursor = -1; + t_con->prompt_visible = false; if (strcmp(t_con->line, "shutdown") == 0) telnet_save_history(t_con); @@ -428,6 +479,7 @@ static int telnet_input(struct connection *connection) retval = command_run_line(command_context, t_con->line); t_con->line_cursor = 0; + t_con->prompt_visible = true; if (retval == ERROR_COMMAND_CLOSE_CONNECTION) return ERROR_SERVER_REMOTE_CLOSED; @@ -442,7 +494,7 @@ static int telnet_input(struct connection *connection) } else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) { /* delete character */ if (t_con->line_cursor > 0) { if (t_con->line_cursor != t_con->line_size) { - int i; + size_t i; telnet_write(connection, "\b", 1); t_con->line_cursor--; t_con->line_size--; @@ -482,7 +534,21 @@ static int telnet_input(struct connection *connection) telnet_history_up(connection); else if (*buf_p == CTRL('N')) /* cursor down */ telnet_history_down(connection); - else + else if (*buf_p == CTRL('A')) + telnet_move_cursor(connection, 0); + else if (*buf_p == CTRL('E')) + telnet_move_cursor(connection, t_con->line_size); + else if (*buf_p == CTRL('K')) { /* kill line to end */ + if (t_con->line_cursor < t_con->line_size) { + /* overwrite with space, until end of line, move back */ + for (size_t i = t_con->line_cursor; i < t_con->line_size; i++) + telnet_write(connection, " ", 1); + for (size_t i = t_con->line_cursor; i < t_con->line_size; i++) + telnet_write(connection, "\b", 1); + t_con->line[t_con->line_cursor] = '\0'; + t_con->line_size = t_con->line_cursor; + } + } else LOG_DEBUG("unhandled nonprintable: %2.2x", *buf_p); } } @@ -538,7 +604,7 @@ static int telnet_input(struct connection *connection) /* Remove character */ if (*buf_p == '~') { if (t_con->line_cursor < t_con->line_size) { - int i; + size_t i; t_con->line_size--; /* remove char from line buffer */ memmove(t_con->line + t_con->line_cursor, @@ -589,29 +655,22 @@ static int telnet_connection_closed(struct connection *connection) log_remove_callback(telnet_log_callback, connection); - if (t_con->prompt) { - free(t_con->prompt); - t_con->prompt = NULL; - } + free(t_con->prompt); + t_con->prompt = NULL; /* save telnet history */ telnet_save_history(t_con); for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++) { - if (t_con->history[i]) { - free(t_con->history[i]); - t_con->history[i] = NULL; - } + free(t_con->history[i]); + t_con->history[i] = NULL; } /* if this connection registered a debug-message receiver delete it */ delete_debug_msg_receiver(connection->cmd_ctx, NULL); - if (connection->priv) { - free(connection->priv); - connection->priv = NULL; - } else - LOG_ERROR("BUG: connection->priv == NULL"); + free(connection->priv); + connection->priv = NULL; return ERROR_OK; } @@ -667,7 +726,7 @@ static const struct command_registration telnet_command_handlers[] = { { .name = "telnet_port", .handler = handle_telnet_port_command, - .mode = COMMAND_ANY, + .mode = COMMAND_CONFIG, .help = "Specify port on which to listen " "for incoming telnet connections. " "Read help on 'gdb_port'.", @@ -681,3 +740,8 @@ int telnet_register_commands(struct command_context *cmd_ctx) telnet_port = strdup("4444"); return register_commands(cmd_ctx, NULL, telnet_command_handlers); } + +void telnet_service_free(void) +{ + free(telnet_port); +}