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