-
Notifications
You must be signed in to change notification settings - Fork 0
/
command.cpp
162 lines (144 loc) · 4.36 KB
/
command.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Standard Includes
#include <vector>
#include <string>
#include <string.h>
#include <stdio.h>
#include <sstream>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <cstdlib>
#include <glob.h>
// Local Includes
#include "command.h"
using namespace std;
command::command(string cmd, vector<string> args, string PATH) {
this->cmd = cmd;
this->args = args;
this->PATH = PATH;
this->cmdPath = "";
}
command::~command(void) {
}
void command::error(const char *msg, bool flag) {
if (flag) {
perror(msg);
exit(1);
}
}
// Adapted this method from a combination of answers from stack overflow:
// https://stackoverflow.com/questions/53849/how-do-i-tokenize-a-string-in-c
// https://stackoverflow.com/questions/236129/the-most-elegant-way-to-iterate-the-words-of-a-string
vector<string> command::split(string input, char token) {
stringstream ss(input);
string str;
vector<string> words;
while (getline(ss, str, token)) {
words.push_back(str);
}
return words;
}
// Used this vector<string> to char** conversion method from stackoverflow:
// https://stackoverflow.com/questions/7048888/stdvectorstdstring-to-char-array
// https://stackoverflow.com/questions/26032039/convert-vectorstring-into-char-c
/* After much debugging, the following comment saved my skin:
"you could also do std::vector<const char *>cStrArray( origVector.size()+1, NULL); and then in the iterator use
cStrArray[i]=origVector[i].c_str(); This can help with functs like execv().
But as the note above says, we could use more info about ModelInitialize. – don bright Jul 14 '12 at 21:44 "
This method was pure cancer for me to figure out, and I actually still don't understand why it works. I can't even write
a proper unit test for it lmao ¯\_(ツ)_/¯
*/
char ** command::vectorToCharArray(vector<string> strings) {
char** cstrings = new char*[strings.size()];
size_t i = 0;
while(i < strings.size()) {
cstrings[i] = new char[strings[i].size() + 1]; //allocate enough room to null terminate the c strings
strcpy(cstrings[i], strings[i].c_str());
++i;
}
cstrings[i] = NULL;
return cstrings;
}
bool command::cmdExists(string dir) {
string name = dir + "/" + cmd;
return ( access( name.c_str(), F_OK ) != -1 );
}
// Removes all args with "*"
// Adapted from cplusplus:
// http://www.cplusplus.com/forum/beginner/99524/
void command::cleanArgs() {
vector<string>::iterator iter = args.begin();
while (iter != args.end()) {
if ((*iter).find("*") != -1) {
iter = args.erase(iter);
}
else {
iter++;
}
}
}
// Adapted from stackoverflow:
// https://stackoverflow.com/questions/8401777/simple-glob-in-c-on-unix-system
void command::globExpand() {
vector<string> expandedArgs;
// Iterate through each argument that needs to be expanded
for (auto argsIterator = args.begin(); argsIterator != args.end(); ++argsIterator) {
glob_t glob_result;
string arg = *argsIterator;
glob(arg.c_str(), GLOB_TILDE, NULL, &glob_result);
// Iterate through every result that glob returns for that argument
for(unsigned int i = 0; i < glob_result.gl_pathc; ++i){
expandedArgs.push_back(string(glob_result.gl_pathv[i]));
}
globfree(&glob_result);
}
cleanArgs();
args.insert(args.end(), expandedArgs.begin(), expandedArgs.end());
}
string command::findCmdPath() {
if (cmd.find("/") == 0) {
//execute the command immediately
cmdPath = cmd;
return cmdPath;
}
vector<string> splitPaths = split(PATH, ':');
vector<string>::iterator p = splitPaths.begin();
while(p != splitPaths.end()){
if(cmdExists(*p)) {
cmdPath = *p + "/" + cmd;
return cmdPath;
}
p++;
}
return "";
}
void command::execChild() {
cmdPath = findCmdPath();
error("Could not find command", cmdPath == "");
globExpand();
args.insert(args.begin(), cmdPath); //prepend the command name to the arg list so execv works
char ** cmdArgs = vectorToCharArray(args);
error("Exec failed", execv(cmdPath.c_str(), cmdArgs) == -1);
}
// Adapted this method from stackoverflow:
// https://stackoverflow.com/questions/19099663/how-to-correctly-use-fork-exec-wait
void command::forkAndExec() {
if (cmd == "cd") {
error("Change Directory failed", chdir(args[0].c_str()) < 0);
}
else {
pid_t pid = fork();
error("Fork failed", pid == -1);
if (pid > 0) {
// We are the parent
int status;
waitpid(pid, &status, 0);
}
else {
// We are the child
execChild();
}
}
}