Browse Source

Don't show filters while completion of first component. Don't show non-filters after pipe sign

Serj Kalichev 1 year ago
parent
commit
e4f3caf887
3 changed files with 106 additions and 91 deletions
  1. 2 2
      klish/ksession/kpargv.c
  2. 103 88
      klish/ksession/ksession_parse.c
  3. 1 1
      klish/ksession_parse.h

+ 2 - 2
klish/ksession/kpargv.c

@@ -176,10 +176,10 @@ const char *kpargv_status_decode(kpargv_status_e status)
 		s = "Not found";
 		s = "Not found";
 		break;
 		break;
 	case KPARSE_INCOMPLETED:
 	case KPARSE_INCOMPLETED:
-		s = "Incompleted";
+		s = "Incompleted command";
 		break;
 		break;
 	case KPARSE_ILLEGAL:
 	case KPARSE_ILLEGAL:
-		s = "Illegal";
+		s = "Illegal command";
 		break;
 		break;
 	case KPARSE_NOACTION:
 	case KPARSE_NOACTION:
 		s = "Has no action";
 		s = "Has no action";

+ 103 - 88
klish/ksession/ksession_parse.c

@@ -8,6 +8,7 @@
 #include <sys/types.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <unistd.h>
+#include <syslog.h>
 
 
 #include <faux/eloop.h>
 #include <faux/eloop.h>
 #include <faux/buf.h>
 #include <faux/buf.h>
@@ -61,7 +62,7 @@ static bool_t ksession_validate_arg(ksession_t *session, kpargv_t *pargv)
 
 
 static kpargv_status_e ksession_parse_arg(ksession_t *session,
 static kpargv_status_e ksession_parse_arg(ksession_t *session,
 	const kentry_t *current_entry, faux_argv_node_t **argv_iter,
 	const kentry_t *current_entry, faux_argv_node_t **argv_iter,
-	kpargv_t *pargv, bool_t entry_is_command)
+	kpargv_t *pargv, bool_t entry_is_command, bool_t is_filter)
 {
 {
 	const kentry_t *entry = current_entry;
 	const kentry_t *entry = current_entry;
 	kentry_mode_e mode = KENTRY_MODE_NONE;
 	kentry_mode_e mode = KENTRY_MODE_NONE;
@@ -110,8 +111,9 @@ static kpargv_status_e ksession_parse_arg(ksession_t *session,
 		// Additionally if it's last continuable argument then lie to
 		// Additionally if it's last continuable argument then lie to
 		// engine: make all last arguments NOTFOUND. It's necessary to walk
 		// engine: make all last arguments NOTFOUND. It's necessary to walk
 		// through all variants to gether all completions.
 		// through all variants to gether all completions.
-		if ((KPURPOSE_COMPLETION == purpose) ||
-			(KPURPOSE_HELP == purpose)) {
+		if (((KPURPOSE_COMPLETION == purpose) ||
+			(KPURPOSE_HELP == purpose)) &&
+			(is_filter == kentry_filter(entry))) {
 			if (!*argv_iter) {
 			if (!*argv_iter) {
 				// That's time to add entry to completions list.
 				// That's time to add entry to completions list.
 				if (!kpargv_continuable(pargv))
 				if (!kpargv_continuable(pargv))
@@ -183,7 +185,7 @@ static kpargv_status_e ksession_parse_arg(ksession_t *session,
 			if (kentry_purpose(nested) != KENTRY_PURPOSE_COMMON)
 			if (kentry_purpose(nested) != KENTRY_PURPOSE_COMMON)
 				continue;
 				continue;
 			rc = ksession_parse_arg(session, nested, argv_iter,
 			rc = ksession_parse_arg(session, nested, argv_iter,
-				pargv, BOOL_FALSE);
+				pargv, BOOL_FALSE, is_filter);
 //printf("%s\n", kpargv_status_decode(rc));
 //printf("%s\n", kpargv_status_decode(rc));
 			// If some arguments was consumed then we will not check
 			// If some arguments was consumed then we will not check
 			// next SWITCH's entries in any case.
 			// next SWITCH's entries in any case.
@@ -218,7 +220,7 @@ static kpargv_status_e ksession_parse_arg(ksession_t *session,
 			// (from 'min' to 'max' times)
 			// (from 'min' to 'max' times)
 			for (num = 0; num < kentry_max(nested); num++) {
 			for (num = 0; num < kentry_max(nested); num++) {
 				nrc = ksession_parse_arg(session, nested,
 				nrc = ksession_parse_arg(session, nested,
-					argv_iter, pargv, BOOL_FALSE);
+					argv_iter, pargv, BOOL_FALSE, is_filter);
 //fprintf(stderr, "%s: %s\n", kentry_name(nested), kpargv_status_decode(nrc));
 //fprintf(stderr, "%s: %s\n", kentry_name(nested), kpargv_status_decode(nrc));
 				if (nrc != KPARSE_INPROGRESS)
 				if (nrc != KPARSE_INPROGRESS)
 					break;
 					break;
@@ -265,7 +267,7 @@ static kpargv_status_e ksession_parse_arg(ksession_t *session,
 
 
 
 
 kpargv_t *ksession_parse_line(ksession_t *session, const faux_argv_t *argv,
 kpargv_t *ksession_parse_line(ksession_t *session, const faux_argv_t *argv,
-	kpargv_purpose_e purpose)
+	kpargv_purpose_e purpose, bool_t is_filter)
 {
 {
 	faux_argv_node_t *argv_iter = NULL;
 	faux_argv_node_t *argv_iter = NULL;
 	kpargv_t *pargv = NULL;
 	kpargv_t *pargv = NULL;
@@ -303,7 +305,7 @@ kpargv_t *ksession_parse_line(ksession_t *session, const faux_argv_t *argv,
 			continue;
 			continue;
 		// Parsing
 		// Parsing
 		pstatus = ksession_parse_arg(session, current_entry, &argv_iter,
 		pstatus = ksession_parse_arg(session, current_entry, &argv_iter,
-			pargv, BOOL_FALSE);
+			pargv, BOOL_FALSE, is_filter);
 		if (pstatus != KPARSE_NOTFOUND)
 		if (pstatus != KPARSE_NOTFOUND)
 			break;
 			break;
 		// NOTFOUND but some args were parsed.
 		// NOTFOUND but some args were parsed.
@@ -414,6 +416,82 @@ faux_list_t *ksession_split_pipes(const char *raw_line, faux_error_t *error)
 }
 }
 
 
 
 
+// is_piped means full command contains more than one piped components
+static bool_t ksession_check_line(const kpargv_t *pargv, faux_error_t *error,
+	bool_t is_first, bool_t is_piped)
+{
+	kpargv_purpose_e purpose = KPURPOSE_EXEC;
+	const kentry_t *cmd = NULL;
+
+	if (!pargv)
+		return BOOL_FALSE;
+
+	purpose = kpargv_purpose(pargv);
+	cmd = kpargv_command(pargv);
+
+	// For execution pargv must be fully correct but for completion
+	// it's not a case
+	if ((KPURPOSE_EXEC == purpose) && (kpargv_status(pargv) != KPARSE_OK)) {
+		faux_error_sprintf(error, "%s", kpargv_status_str(pargv));
+		return BOOL_FALSE;
+	}
+
+	// Can't check following conditions without cmd
+	if (!cmd)
+		return BOOL_TRUE;
+
+	// First component
+	if (is_first) {
+
+		// First component can't be filter
+		if (kentry_filter(cmd)) {
+			faux_error_sprintf(error, "The filter \"%s\" "
+				"can't be used without previous pipeline",
+				kentry_name(cmd));
+			return BOOL_FALSE;
+		}
+
+		// Interactive command can't have filters
+		if (kentry_interactive(cmd) && is_piped) {
+			faux_error_sprintf(error, "The interactive command \"%s\" "
+				"can't have filters",
+				kentry_name(cmd));
+			return BOOL_FALSE;
+		}
+
+	// Components after pipe "|"
+	} else {
+
+		// Only the first component can be non-filter
+		if (!kentry_filter(cmd)) {
+			faux_error_sprintf(error, "The non-filter command \"%s\" "
+				"can't be destination of pipe",
+				kentry_name(cmd));
+			return BOOL_FALSE;
+		}
+
+		// Only the first component can have 'restore=true' attribute
+		if (kentry_restore(cmd)) {
+			faux_error_sprintf(error, "The command \"%s\" "
+				"can't be destination of pipe",
+				kentry_name(cmd));
+			return BOOL_FALSE;
+		}
+
+		// Only the first component can have 'interactive=true' attribute
+		if (kentry_interactive(cmd)) {
+			faux_error_sprintf(error, "The filter \"%s\" "
+				"can't be interactive",
+				kentry_name(cmd));
+			return BOOL_FALSE;
+		}
+
+	}
+
+	return BOOL_TRUE;
+}
+
+
 // All components except last one must be legal for execution but last
 // All components except last one must be legal for execution but last
 // component must be parsed for completion.
 // component must be parsed for completion.
 // Completion is a "back-end" operation so it doesn't need detailed error
 // Completion is a "back-end" operation so it doesn't need detailed error
@@ -424,6 +502,7 @@ kpargv_t *ksession_parse_for_completion(ksession_t *session,
 	faux_list_t *split = NULL;
 	faux_list_t *split = NULL;
 	faux_list_node_t *iter = NULL;
 	faux_list_node_t *iter = NULL;
 	kpargv_t *pargv = NULL;
 	kpargv_t *pargv = NULL;
+	bool_t is_piped = BOOL_FALSE;
 
 
 	assert(session);
 	assert(session);
 	if (!session)
 	if (!session)
@@ -438,27 +517,23 @@ kpargv_t *ksession_parse_for_completion(ksession_t *session,
 		faux_list_free(split);
 		faux_list_free(split);
 		return NULL;
 		return NULL;
 	}
 	}
+	is_piped = (faux_list_len(split) > 1);
 
 
 	iter = faux_list_head(split);
 	iter = faux_list_head(split);
 	while (iter) {
 	while (iter) {
 		faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
 		faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
-		if (iter == faux_list_tail(split)) { // Last item
-			pargv = ksession_parse_line(session, argv,
-				KPURPOSE_COMPLETION);
-			if (!pargv)
-				break;
-		} else { // Non-last item
-			pargv = ksession_parse_line(session, argv,
-				KPURPOSE_EXEC);
-			// All non-last components must be ready for execution
-			if (!pargv)
-				break;
-			if (kpargv_status(pargv) != KPARSE_OK) {
-				kpargv_free(pargv);
-				break;
-			}
+		bool_t is_last = (iter == faux_list_tail(split));
+		bool_t is_first = (iter == faux_list_head(split));
+		kpargv_purpose_e purpose = is_last ? KPURPOSE_COMPLETION : KPURPOSE_EXEC;
+
+		pargv = ksession_parse_line(session, argv, purpose, !is_first);
+		if (!ksession_check_line(pargv, NULL, is_first, is_piped)) {
 			kpargv_free(pargv);
 			kpargv_free(pargv);
+			pargv = NULL;
+			break;
 		}
 		}
+		if (!is_last)
+			kpargv_free(pargv);
 		iter = faux_list_next_node(iter);
 		iter = faux_list_next_node(iter);
 	}
 	}
 
 
@@ -475,6 +550,7 @@ kexec_t *ksession_parse_for_exec(ksession_t *session, const char *raw_line,
 	faux_list_node_t *iter = NULL;
 	faux_list_node_t *iter = NULL;
 	kpargv_t *pargv = NULL;
 	kpargv_t *pargv = NULL;
 	kexec_t *exec = NULL;
 	kexec_t *exec = NULL;
+	bool_t is_piped = BOOL_FALSE;
 
 
 	assert(session);
 	assert(session);
 	if (!session)
 	if (!session)
@@ -489,6 +565,7 @@ kexec_t *ksession_parse_for_exec(ksession_t *session, const char *raw_line,
 		faux_list_free(split);
 		faux_list_free(split);
 		return NULL;
 		return NULL;
 	}
 	}
+	is_piped = (faux_list_len(split) > 1);
 
 
 	// Create exec list
 	// Create exec list
 	exec = kexec_new();
 	exec = kexec_new();
@@ -502,73 +579,11 @@ kexec_t *ksession_parse_for_exec(ksession_t *session, const char *raw_line,
 	while (iter) {
 	while (iter) {
 		faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
 		faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
 		kcontext_t *context = NULL;
 		kcontext_t *context = NULL;
-		bool_t check_failed = BOOL_FALSE;
+		bool_t is_first = (iter == faux_list_head(split));
 
 
-		pargv = ksession_parse_line(session, argv, KPURPOSE_EXEC);
+		pargv = ksession_parse_line(session, argv, KPURPOSE_EXEC, !is_first);
 		// All components must be ready for execution
 		// All components must be ready for execution
-		if (!pargv) {
-			kexec_free(exec);
-			faux_list_free(split);
-			return NULL;
-		}
-		if (kpargv_status(pargv) != KPARSE_OK) {
-			faux_error_sprintf(error, "%s",
-				kpargv_status_str(pargv));
-			kpargv_free(pargv);
-			kexec_free(exec);
-			faux_list_free(split);
-			return NULL;
-		}
-
-		// First component
-		if (iter == faux_list_head(split)) {
-
-			// First component can't be filter
-			if (kentry_filter(kpargv_command(pargv))) {
-				faux_error_sprintf(error, "The filter \"%s\" "
-					"can't be used without previous pipeline",
-					kentry_name(kpargv_command(pargv)));
-				check_failed = BOOL_TRUE;
-			}
-
-		// Components after pipe "|"
-		} else {
-
-			// Only the first component can be non-filter
-			if (!kentry_filter(kpargv_command(pargv))) {
-				faux_error_sprintf(error, "The non-filter command \"%s\" "
-					"can't be destination of pipe",
-					kentry_name(kpargv_command(pargv)));
-				check_failed = BOOL_TRUE;
-			}
-
-			// Only the first component can have 'restore=true' attribute
-			if (kentry_restore(kpargv_command(pargv))) {
-				faux_error_sprintf(error, "The command \"%s\" "
-					"can't be destination of pipe",
-					kentry_name(kpargv_command(pargv)));
-				check_failed = BOOL_TRUE;
-			}
-
-			// Only the first component can have 'interactive=true' attribute
-			if (kentry_interactive(kpargv_command(pargv))) {
-				faux_error_sprintf(error, "The filter \"%s\" "
-					"can't be interactive",
-					kentry_name(kpargv_command(pargv)));
-				check_failed = BOOL_TRUE;
-			}
-
-			// Interactive command can't have filters
-			if (kexec_interactive(exec)) {
-				faux_error_sprintf(error, "The interactive command \"%s\" "
-					"can't have filters",
-					kentry_name(kpargv_command(pargv)));
-				check_failed = BOOL_TRUE;
-			}
-		}
-
-		// Some checks were failed
-		if (check_failed) {
+		if (!ksession_check_line(pargv, error, is_first, is_piped)) {
 			kpargv_free(pargv);
 			kpargv_free(pargv);
 			kexec_free(exec);
 			kexec_free(exec);
 			faux_list_free(split);
 			faux_list_free(split);
@@ -623,7 +638,7 @@ kexec_t *ksession_parse_for_local_exec(ksession_t *session,
 	kpargv_set_purpose(pargv, KPURPOSE_EXEC);
 	kpargv_set_purpose(pargv, KPURPOSE_EXEC);
 
 
 	pstatus = ksession_parse_arg(session, entry, &argv_iter, pargv,
 	pstatus = ksession_parse_arg(session, entry, &argv_iter, pargv,
-		BOOL_TRUE);
+		BOOL_TRUE, BOOL_FALSE);
 	// Parsing problems
 	// Parsing problems
 	if ((pstatus != KPARSE_INPROGRESS) || (argv_iter != NULL)) {
 	if ((pstatus != KPARSE_INPROGRESS) || (argv_iter != NULL)) {
 		kexec_free(exec);
 		kexec_free(exec);

+ 1 - 1
klish/ksession_parse.h

@@ -14,7 +14,7 @@
 C_DECL_BEGIN
 C_DECL_BEGIN
 
 
 kpargv_t *ksession_parse_line(ksession_t *session, const faux_argv_t *argv,
 kpargv_t *ksession_parse_line(ksession_t *session, const faux_argv_t *argv,
-	kpargv_purpose_e purpose);
+	kpargv_purpose_e purpose, bool_t is_filter);
 faux_list_t *ksession_split_pipes(const char *raw_line, faux_error_t *error);
 faux_list_t *ksession_split_pipes(const char *raw_line, faux_error_t *error);
 kpargv_t *ksession_parse_for_completion(ksession_t *session,
 kpargv_t *ksession_parse_for_completion(ksession_t *session,
 	const char *raw_line);
 	const char *raw_line);