· 6 years ago · Jun 27, 2019, 05:14 PM
1#include <iostream>
2#include <unistd.h>
3#include <fcntl.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <sys/wait.h>
7#include <string>
8#include <stdlib.h>
9#include "SimpleCommand.h"
10
11using namespace std;
12
13const char *homedir = getenv("HOME");
14
15char cwdBuffer[512];
16
17void executeProgram(string command, vector<string> arguments, vector<IORedirect> redirects){
18
19 //Create a new Process based on the current process
20 //fork(0) can give back -1 = error, 0 = the new child
21 //or any positive number = the parent/caller
22 int pid = fork();
23
24 //If fork() returns a negative value an error occurred
25 //provide the user with an error
26 if(pid < 0){
27 cerr << "An error occurred while forking the current process!" << endl;
28 }
29
30 //If fork() returns 0, the process is the new child
31 //process. In here, start the program
32 else if (pid == 0){
33
34 //Create an array of type chars that will be used for conversion later
35 char *charArguments[arguments.size() + 1];
36
37 //Loop through all given arguments and
38 //place them inside an char array that is
39 //necessary for the execvp command later
40 for( int i = 0; i < arguments.size(); i++ ){
41
42 //Take string from arguments and convert to char
43 //then place converted char in char array
44 charArguments[i] = (char *) arguments[i].c_str();
45 }
46
47 //When conversion is done, the last element in
48 //the char array needs to be NULL, this way
49 //the execvp function knows that its done
50 charArguments[arguments.size() + 1] = NULL;
51
52 //Execute the program based on the command and its arguments
53 int errorCode = execvp(command.c_str(),charArguments);
54
55 //execvp only returns something when an error occurred
56 //in that case it always returns -1
57 if(errorCode == -1){
58 cerr << "Execution of program " << command << " failed!" << endl;
59 }
60 }
61
62 //If fork() returns a positive number, then this
63 //is the parent process where fork() was called from
64 //Here you wait for the fork() process to terminate
65 else if (pid > 0){
66
67 //Value that will be returned from the fork() process
68 int status;
69
70 //Parent will wait for fork() process to be terminated
71 //that was initiated earlier
72 waitpid(pid,&status, 0);
73
74 //The status displayed to the user
75 cout << "Program " << command<< " ended with status code: " << status << endl;
76 }
77}
78
79void changeDirectory(string command, vector<string> arguments, vector<IORedirect> redirects){
80 //Value that is returned from the chdir function
81 int returnValue;
82
83 //Check if the "cd" command has any arguments provided by the user
84 //if it does not hold any arguments that means the directory will
85 //be changed to the home directory (from the HOME variable)
86 if(arguments.empty()){
87 returnValue = chdir(homedir);
88 }
89
90 //If there are any arguments given, try to change directory
91 //based on the arguments given
92 else{
93 returnValue = chdir(arguments[0].c_str());
94 }
95
96 //If chdir returns -1 it means some error has occurred
97 //display an error to the user when this happens
98 if(returnValue == -1){
99 cerr << "Could not change directory, directory does not exist or no permissions!" << endl;
100 }
101
102 //If no error occur the change of directory was successful
103 //and the current new working directory should be printed out
104 else{
105 cout << "Current working directory: " << getcwd(cwdBuffer, sizeof(cwdBuffer)) << endl;
106 }
107}
108
109void handleIO(string command, vector<string> arguments, vector<IORedirect> redirects){
110 int fileDescriptor;
111
112 //Loop over all the redirects provided by the user
113 for(const IORedirect &redirection : redirects){
114
115 //Checks if the redirection contains a ampersand which indicates
116 //that we are dealing with a stream to stream redirection
117 if(redirection.getNewFile().find('&') != std::string::npos){
118 //Gets number and parses the returned char to a integer
119 fileDescriptor = (redirection.getNewFile().back()) - '0';
120 }
121
122 //Checks if the redirection is a INPUT (<) redirection
123 //if that is the case, it tries to open the file that was given
124 else if(redirection.getType() == IORedirect::INPUT){
125 fileDescriptor = open(redirection.getNewFile().c_str(), O_RDONLY);
126 }
127
128 //Checks if the redirection is a OUTPUT (>) redirection
129 //if that is the case, it will overwrite the existing file
130 //if that file does not yet exists, it will create it
131 else if(redirection.getType() == IORedirect::OUTPUT){
132 fileDescriptor = open(redirection.getNewFile().c_str(), O_WRONLY | O_CREAT, 0666);
133 }
134
135 //Checks if the redirection is a APPEND (>>) redirection
136 //if that is the case it will append to a existing file
137 else if (redirection.getType() == IORedirect::APPEND){
138 fileDescriptor = open(redirection.getNewFile().c_str(), O_RDWR | O_APPEND);
139 }
140
141 //If none of the above apply you are dealing with an
142 //error (>2) redirection
143 else{
144 fileDescriptor = open(redirection.getNewFile().c_str(), O_WRONLY | O_CREAT, 0666);
145 }
146
147 //If for some reason the file descriptor is not working
148 //and a -1 index was given, an error will be shown to the user
149 if(fileDescriptor == -1){
150 cerr << "The file: " << redirection.getNewFile() << " cannot be found!" << endl;
151 }
152
153 //Close the position in the FD table that you want to be replaced
154 close(redirection.getOldFileDescriptor());
155
156 //Make a copy of the file and place it on the position that is now
157 //available. Dup always finds the first open position which is the
158 //one you just closed
159 dup(fileDescriptor);
160
161 }
162}
163
164void SimpleCommand::execute() {
165
166 //If the given command contains any kind of redirect
167 //such as (> , >> , < , 2> OR &[0,1,2,etc...]) then run the HandleIO function
168 if(!redirects.empty()){
169 handleIO(command,arguments,redirects);
170 }
171
172 //If the given command is a cd command, run the change directory function
173 if(command == "cd") {
174 changeDirectory(command, arguments, redirects);
175 }
176
177 //If the given command is a cdir command, show the current working directory
178 else if (command == "cdir"){
179 cout << "Current working directory:" << getcwd(cwdBuffer, sizeof(cwdBuffer)) << endl;
180 }
181
182 //If none of the above apply, it is assumed that a program was given as a command
183 else{
184 executeProgram(command,arguments,redirects);
185 }
186
187}
188
189//www.cplusplus.com/reference/cstdlib/getenv/
190//www.cplusplus.com/reference/array/array/empty/
191//stack: change-the-current-working-directory-in-c
192//stack: how-to-get-the-current-directory-in-a-c-program
193//stacK: how-to-use-execvp
194// csl.mtu.edu : the wait() system call
195//stack: how can i convert a stdstring to int
196//stackL convert char to int in c and c++