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