Browse Source

Fix line feed after stderr or stdin without final crlf

Serj Kalichev 6 months ago
parent
commit
a631191848
4 changed files with 94 additions and 12 deletions
  1. 25 12
      bin/klish/interactive.c
  2. 14 0
      klish-no-pager.conf
  3. 52 0
      klish/ktp/ktp_session.c
  4. 3 0
      klish/ktp_session.h

+ 25 - 12
bin/klish/interactive.c

@@ -263,6 +263,31 @@ bool_t cmd_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
 	ctx_t *ctx = (ctx_t *)udata;
 	int rc = -1;
 	faux_error_t *error = NULL;
+	bool_t it_was_pager = BOOL_FALSE;
+
+	// Wait for pager
+	if (ctx->pager_working != TRI_UNDEFINED) {
+		pclose(ctx->pager_pipe);
+		ctx->pager_working = TRI_UNDEFINED;
+		ctx->pager_pipe = NULL;
+		it_was_pager = BOOL_TRUE;
+	}
+
+	// Disable SIGINT caught for non-interactive commands.
+	// Do it after pager exit. Else it can restore wrong tty mode after
+	// ISIG disabling
+	tinyrl_disable_isig(ctx->tinyrl);
+
+	// Sometimes output stream from server doesn't contain final crlf so
+	// goto newline itself
+	if (ktp_session_last_stream(ktp) == STDERR_FILENO) {
+		if (ktp_session_stderr_need_newline(ktp))
+			fprintf(stderr, "\n");
+	} else {
+		// Pager adds newline itself
+		if (ktp_session_stdout_need_newline(ktp) && !it_was_pager)
+			tinyrl_crlf(ctx->tinyrl);
+	}
 
 	process_prompt_param(ctx->tinyrl, msg);
 	process_hotkey_param(ctx, msg);
@@ -278,18 +303,6 @@ bool_t cmd_ack_cb(ktp_session_t *ktp, const faux_msg_t *msg, void *udata)
 	}
 	faux_error_free(error);
 
-	// Wait for pager
-	if (ctx->pager_working != TRI_UNDEFINED) {
-		pclose(ctx->pager_pipe);
-		ctx->pager_working = TRI_UNDEFINED;
-		ctx->pager_pipe = NULL;
-	}
-
-	// Disable SIGINT caught for non-interactive commands.
-	// Do it after pager exit. Else it can restore wrong tty mode after
-	// ISIG disabling
-	tinyrl_disable_isig(ctx->tinyrl);
-
 	tinyrl_set_busy(ctx->tinyrl, BOOL_FALSE);
 	if (!ktp_session_done(ktp))
 		tinyrl_redisplay(ctx->tinyrl);

+ 14 - 0
klish-no-pager.conf

@@ -0,0 +1,14 @@
+# Template for config file /etc/klish/klish.conf. It's used by klish utility.
+
+# The klishd uses UNIX domain socket to receive connections. It will create an
+# filesystem entry to allow clients to find connection point. By default klish
+# client utility will connect to /tmp/klish-unix-socket.
+#UnixSocketPath=/tmp/klish-unix-socket
+
+# The klish can use external pager for non-interactive commands. By default it
+# will execute "/usr/bin/less -I -F -e -X -K -d -r" process as a pager.
+Pager="/usr/bin/less -I -F -e -X -K -d -r"
+
+# External pager is enabled by default. But user can explicitly enable or
+# disable it. Use "y" or "n" values.
+UsePager=n

+ 52 - 0
klish/ktp/ktp_session.c

@@ -35,6 +35,9 @@ struct ktp_session_s {
 	ktp_status_e cmd_features;
 	bool_t cmd_features_available;
 	bool_t stop_on_answer; // Stop the loop when answer is received (for non-interactive mode)
+	bool_t stdout_need_newline; // Does stdout has final line feed. If no then newline is needed
+	bool_t stderr_need_newline; // Does stderr has final line feed. If no then newline is needed
+	int last_stream; // Last active stream: stdout or stderr
 };
 
 
@@ -70,6 +73,9 @@ ktp_session_t *ktp_session_new(int sock, faux_eloop_t *eloop)
 	ktp->request_done = BOOL_FALSE;
 	ktp->cmd_features = KTP_STATUS_NONE;
 	ktp->cmd_features_available = BOOL_FALSE;
+	ktp->stdout_need_newline = BOOL_FALSE;
+	ktp->stderr_need_newline = BOOL_FALSE;
+	ktp->last_stream = STDOUT_FILENO;
 
 	// Async object
 	ktp->async = faux_async_new(sock);
@@ -299,6 +305,14 @@ static bool_t ktp_session_process_stdout(ktp_session_t *ktp, const faux_msg_t *m
 	if (!faux_msg_get_param_by_type(msg, KTP_PARAM_LINE, (void **)&line, &len))
 		return BOOL_TRUE; // It's strange but not a bug
 
+	if (len > 0) {
+		if (line[len - 1] == '\n')
+			ktp->stdout_need_newline = BOOL_FALSE;
+		else
+			ktp->stdout_need_newline = BOOL_TRUE;
+		ktp->last_stream = STDOUT_FILENO;
+	}
+
 	return ((ktp_session_stdout_cb_fn)ktp->cb[KTP_SESSION_CB_STDOUT].fn)(
 		ktp, line, len, ktp->cb[KTP_SESSION_CB_STDOUT].udata);
 }
@@ -319,6 +333,14 @@ static bool_t ktp_session_process_stderr(ktp_session_t *ktp, const faux_msg_t *m
 			(void **)&line, &len))
 		return BOOL_TRUE; // It's strange but not a bug
 
+	if (len > 0) {
+		if (line[len - 1] == '\n')
+			ktp->stderr_need_newline = BOOL_FALSE;
+		else
+			ktp->stderr_need_newline = BOOL_TRUE;
+		ktp->last_stream = STDERR_FILENO;
+	}
+
 	return ((ktp_session_stdout_cb_fn)ktp->cb[KTP_SESSION_CB_STDERR].fn)(
 		ktp, line, len, ktp->cb[KTP_SESSION_CB_STDERR].udata);
 }
@@ -617,6 +639,9 @@ static bool_t ktp_session_drop_state(ktp_session_t *ktp, faux_error_t *error)
 	ktp->request_done = BOOL_FALSE;
 	ktp->cmd_features = KTP_STATUS_NONE;
 	ktp->cmd_features_available = BOOL_FALSE;
+	ktp->stdout_need_newline = BOOL_FALSE;
+	ktp->stderr_need_newline = BOOL_FALSE;
+	ktp->last_stream = STDOUT_FILENO;
 
 	return BOOL_TRUE;
 }
@@ -746,3 +771,30 @@ bool_t ktp_session_retcode(ktp_session_t *ktp, int *retcode)
 
 	return ktp->cmd_retcode_available; // Sign of server answer
 }
+
+
+bool_t ktp_session_stdout_need_newline(ktp_session_t *ktp)
+{
+	if (!ktp)
+		return BOOL_FALSE;
+
+	return ktp->stdout_need_newline;
+}
+
+
+bool_t ktp_session_stderr_need_newline(ktp_session_t *ktp)
+{
+	if (!ktp)
+		return BOOL_FALSE;
+
+	return ktp->stderr_need_newline;
+}
+
+
+int ktp_session_last_stream(ktp_session_t *ktp)
+{
+	if (!ktp)
+		return BOOL_FALSE;
+
+	return ktp->last_stream;
+}

+ 3 - 0
klish/ktp_session.h

@@ -98,6 +98,9 @@ bool_t ktp_session_stdin(ktp_session_t *ktp, const char *line, size_t line_len);
 bool_t ktp_session_stdout_close(ktp_session_t *ktp);
 bool_t ktp_session_retcode(ktp_session_t *ktp, int *retcode);
 ktp_status_e ktp_session_cmd_features(const ktp_session_t *ktp);
+bool_t ktp_session_stdout_need_newline(ktp_session_t *ktp);
+bool_t ktp_session_stderr_need_newline(ktp_session_t *ktp);
+int ktp_session_last_stream(ktp_session_t *ktp);
 
 
 // Server KTP session