From ff1108ad383c6c36095a36fc69fd3d19d6995e0f Mon Sep 17 00:00:00 2001
From: Spencer Oliver <spen@spen-soft.co.uk>
Date: Wed, 27 Mar 2013 16:28:31 +0000
Subject: [PATCH] telnet: add telnet history support

adapted from Yoshinori Sato's patch:
https://github.com/ysat0/openocd/commit/2f07f4600a0da8206612d78c159bbe1171aa41c2

Change-Id: I084b86d316b0aa6e9593f007c024961dbda805e9
Signed-off-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-on: http://openocd.zylin.com/1310
Tested-by: jenkins
Reviewed-by: Freddie Chopin <freddie.chopin@gmail.com>
---
 src/helper/configuration.c | 44 ++++++++++++++++++++
 src/helper/configuration.h |  1 +
 src/server/telnet_server.c | 85 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+)

diff --git a/src/helper/configuration.c b/src/helper/configuration.c
index 49c1c08f7..bfb73fd7b 100644
--- a/src/helper/configuration.c
+++ b/src/helper/configuration.c
@@ -128,3 +128,47 @@ int parse_config_file(struct command_context *cmd_ctx)
 
 	return ERROR_OK;
 }
+
+#ifndef _WIN32
+#include <pwd.h>
+#endif
+
+char *get_home_dir(const char *append_path)
+{
+	char *home = getenv("HOME");
+
+	if (home == NULL) {
+
+#ifdef _WIN32
+		home = getenv("USERPROFILE");
+
+		if (home == NULL) {
+
+			char homepath[MAX_PATH];
+			char *drive = getenv("HOMEDRIVE");
+			char *path = getenv("HOMEPATH");
+			if (drive && path) {
+				snprintf(homepath, MAX_PATH, "%s/%s", drive, path);
+				home = homepath;
+			}
+		}
+#else
+		struct passwd *pwd = getpwuid(getuid());
+		if (pwd)
+			home = pwd->pw_dir;
+
+#endif
+	}
+
+	if (home == NULL)
+		return home;
+
+	char *home_path;
+
+	if (append_path)
+		home_path = alloc_printf("%s/%s", home, append_path);
+	else
+		home_path = alloc_printf("%s", home);
+
+	return home_path;
+}
diff --git a/src/helper/configuration.h b/src/helper/configuration.h
index b329da76f..749f007d3 100644
--- a/src/helper/configuration.h
+++ b/src/helper/configuration.h
@@ -40,5 +40,6 @@ int configuration_output_handler(struct command_context *cmd_ctx,
 FILE *open_file_from_path(const char *file, const char *mode);
 
 char *find_file(const char *name);
+char *get_home_dir(const char *append_path);
 
 #endif	/* CONFIGURATION_H */
diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c
index f182a0430..ecdb2bfa2 100644
--- a/src/server/telnet_server.c
+++ b/src/server/telnet_server.c
@@ -30,6 +30,7 @@
 
 #include "telnet_server.h"
 #include <target/target_request.h>
+#include <helper/configuration.h>
 
 static const char *telnet_port;
 
@@ -40,6 +41,7 @@ static char *negotiate =
 	"\xFF\xFE\x01";			/* IAC DON'T Echo */
 
 #define CTRL(c) (c - '@')
+#define TELNET_HISTORY	".openocd_history"
 
 /* The only way we can detect that the socket is closed is the first time
  * we write to it, we will fail. Subsequent write operations will
@@ -127,6 +129,82 @@ static void telnet_log_callback(void *priv, const char *file, unsigned line,
 		telnet_write(connection, "\b", 1);
 }
 
+static void telnet_load_history(struct telnet_connection *t_con)
+{
+	FILE *histfp;
+	char buffer[TELNET_BUFFER_SIZE];
+	int i = 0;
+
+	char *history = get_home_dir(TELNET_HISTORY);
+
+	if (history == NULL) {
+		LOG_INFO("unable to get user home directory, telnet history will be disabled");
+		return;
+	}
+
+	histfp = fopen(history, "rb");
+
+	if (histfp) {
+
+		while (fgets(buffer, sizeof(buffer), histfp) != NULL) {
+
+			char *p = strchr(buffer, '\n');
+			if (p)
+				*p = '\0';
+			if (buffer[0] && i < TELNET_LINE_HISTORY_SIZE)
+				t_con->history[i++] = strdup(buffer);
+		}
+
+		t_con->next_history = i;
+		t_con->next_history %= TELNET_LINE_HISTORY_SIZE;
+		/* try to set to last entry - 1, that way we skip over any exit/shutdown cmds */
+		t_con->current_history = t_con->next_history > 0 ? i - 1 : 0;
+		fclose(histfp);
+	}
+
+	free(history);
+}
+
+static void telnet_save_history(struct telnet_connection *t_con)
+{
+	FILE *histfp;
+	int i;
+	int num;
+
+	char *history = get_home_dir(TELNET_HISTORY);
+
+	if (history == NULL) {
+		LOG_INFO("unable to get user home directory, telnet history will be disabled");
+		return;
+	}
+
+	histfp = fopen(history, "wb");
+
+	if (histfp) {
+
+		num = TELNET_LINE_HISTORY_SIZE;
+		i = t_con->current_history + 1;
+		i %= TELNET_LINE_HISTORY_SIZE;
+
+		while (t_con->history[i] == NULL && num > 0) {
+			i++;
+			i %= TELNET_LINE_HISTORY_SIZE;
+			num--;
+		}
+
+		if (num > 0) {
+			for (; num > 0; num--) {
+				fprintf(histfp, "%s\n", t_con->history[i]);
+				i++;
+				i %= TELNET_LINE_HISTORY_SIZE;
+			}
+		}
+		fclose(histfp);
+	}
+
+	free(history);
+}
+
 static int telnet_new_connection(struct connection *connection)
 {
 	struct telnet_connection *telnet_connection = malloc(sizeof(struct telnet_connection));
@@ -164,6 +242,7 @@ static int telnet_new_connection(struct connection *connection)
 		telnet_connection->history[i] = NULL;
 	telnet_connection->next_history = 0;
 	telnet_connection->current_history = 0;
+	telnet_load_history(telnet_connection);
 
 	log_add_callback(telnet_log_callback, connection);
 
@@ -306,6 +385,9 @@ static int telnet_input(struct connection *connection)
 							/* to suppress prompt in log callback during command execution */
 							t_con->line_cursor = -1;
 
+							if (strcmp(t_con->line, "shutdown") == 0)
+								telnet_save_history(t_con);
+
 							retval = command_run_line(command_context, t_con->line);
 
 							t_con->line_cursor = 0;
@@ -490,6 +572,9 @@ static int telnet_connection_closed(struct connection *connection)
 		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]);
-- 
GitLab