From 4e291984f494ab898902ffd6c240e190ebb50942 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Gendre?= <seb@k-7.ch>
Date: Mon, 18 May 2020 21:31:24 +0200
Subject: [PATCH] Jobs: Add management of stdout redirection to file

---
 src/jobs.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/jobs.h | 40 ++++++++++++++++++++++++++++++-
 2 files changed, 108 insertions(+), 1 deletion(-)

diff --git a/src/jobs.c b/src/jobs.c
index fd9411d..45e0cd1 100644
--- a/src/jobs.c
+++ b/src/jobs.c
@@ -5,6 +5,56 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+/* Search file redirection argument ('>') in argv */
+int job_write_redirection_position_in_argv(int argc, char *argv[]) {
+  // Check each argument in the argv
+  for (int i = 0; i < argc; ++i) {
+    if (strcmp(argv[i], ">") == 0) {
+      // If ">" found, return its position
+      return i;
+    }
+  }
+  return -1;
+}
+
+
+/* See if argv has an redirection symbole '>' and return its position */
+char* job_write_redirection_path_in_argv(int argc, char *argv[]) {
+  int redirect_pos = job_write_redirection_position_in_argv(argc, argv);
+  if (redirect_pos == -1) {
+    return NULL;
+  } else {
+    return argv[redirect_pos + 1];
+  }
+  
+}
+
+
+/* Clean arguments after stdout redirection */
+int job_clean_argv_after_redirection_char(int *argc, char **argv) {
+  int redirect_arg_pos = job_write_redirection_position_in_argv(*argc, argv);
+  // If the redirection char argument position are beyound argv length, return error
+  // code
+  if (redirect_arg_pos >= *argc) {
+    return -1;
+  }
+  /* // Free all arguments after the redirection argument position */
+  /* for (int i = redirect_arg_pos; i < *argc; ++i) { */
+  /*   free(argv[i]); */
+  /* } */
+  // Place a NULL at the redirection argument position
+  argv[redirect_arg_pos] = NULL;
+  // Define the new argc value
+  *argc = redirect_arg_pos;
+  // Reduce argv length
+  argv = realloc(argv, (*argc+1)*sizeof(char*));
+  return 0;
+}
 
 
 // Execute a given command as job
@@ -13,6 +63,9 @@ int job_exec_command(int argc, char *argv[]) {
   pid_t pid;
   int child_process_status;
   int exec_status;
+  char *redirection_path = NULL;
+  int redirection_file;
+  int stdout_new_desc = -1;
 
   pid = fork();
 
@@ -20,6 +73,21 @@ int job_exec_command(int argc, char *argv[]) {
     // In case of fork error
   } else if (pid == 0) {
     // I'm the child process
+    // Check if the redirection of stdout to file is requested by user
+    redirection_path = job_write_redirection_path_in_argv(argc, argv);
+    if (redirection_path) {
+      // If yes, open the file path
+      redirection_file = open(redirection_path, O_WRONLY | O_CREAT, 0640);
+      // Replace stdout file descriptor by a copy of the file
+      // descriptor of the openned file
+      stdout_new_desc = dup2(redirection_file, STDOUT_FILENO);
+      if (stdout_new_desc == -1) {
+        perror("Cannot redirect stdout");
+      } else {
+        // Remove all arguments after the '>'
+        job_clean_argv_after_redirection_char(&argc, argv);
+      }
+    }
     // Exec the command found in PATH env by using execvpe()
     exec_status = execvpe(argv[0], argv, NULL);
     if (exec_status == -1) {
@@ -44,3 +112,4 @@ int job_exec_command(int argc, char *argv[]) {
     }
   }
  }
+
diff --git a/src/jobs.h b/src/jobs.h
index 1350561..ac8ee6a 100644
--- a/src/jobs.h
+++ b/src/jobs.h
@@ -11,7 +11,8 @@
 /* Execute a given command as job
  *
  * Parameters:
- * - cmd: The command to execute
+ * - argc: The command arguments counter
+ * - argv: The command arguments 
  * 
  * Return:
  * - The command return code (0 is success)
@@ -19,4 +20,41 @@
 */
 int job_exec_command(int argc, char *argv[]);
 
+
+/* Search file redirection argument ('>') in argv
+ *
+ * Parameters:
+ * - argc: The command arguments counter
+ * - argv: The command arguments 
+ *
+ * Return:
+ * The position of '>' argument in the argv, -1 if not found
+*/
+int job_write_redirection_position_in_argv(int argc, char *argv[]);
+
+/* Get file path where user ask to redirect stdout, if defined by the
+ * user with a '>'
+ *
+ * Parameters:
+ * - argc: The command arguments counter
+ * - argv: The command arguments 
+ *
+ * Return:
+ * The file path where user ask to redirect stdout
+ * If not defined, return NULL
+*/
+char* job_write_redirection_path_in_argv(int argc, char *argv[]);
+
+/* Clean arguments after stdout redirection
+ *
+ * Parameters:
+ * - argc: The command arguments counter
+ * - argv: The command arguments 
+ *
+ * Return:
+ * 0 if success, -1 if error
+*/
+int job_clean_argv_after_redirection_char(int *argc, char **argv);
+
+
 #endif /* JOBS_H */
-- 
GitLab