This repository has been archived by the owner on Mar 3, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shelly.cpp
201 lines (160 loc) · 4.14 KB
/
shelly.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include <cstdlib>
#include <csignal>
#include <iterator>
#include <iostream>
#include <sstream>
#include <string>
#include <list>
#include <sys/wait.h>
#include <unistd.h>
#include "shelly.hpp"
#include "commands.hpp"
Shelly::Shelly() : exit(false)
{ }
int Shelly::start()
{
while( ! exit)
{
// Display the user prompt
std::cout << prompt();
// Read the use input into the command string
std::string input_command;
std::getline(std::cin, input_command);
// Exit on EOF
if (std::cin.eof())
{
std::cout << (input_command = "exit") << '\n';
}
// List out any jobs that have completed
for (auto job : jobs_list(true))
{
std::cout << "[" << job.pid << "] Done : " << job.cmd.command
<< '\n';
}
// Ignore empty inputs
if (input_command.empty()) continue;
// Execute the command
execute(input_command);
}
return 0;
}
void Shelly::finish()
{
terminate_jobs();
exit = true;
}
std::string Shelly::prompt()
{
char hostname[1024];
char *username = getenv("USER");
gethostname(hostname, sizeof hostname);
return std::string("\033[0;35m") + hostname + "(" + username + ")%\033[0m ";
}
Shelly::command Shelly::parse_command(std::string input_command)
{
// Setup the command struct
command command;
// This command is a background job if the last character is a '&'
command.background = input_command.back() == '&';
// Remove the background flag from the command string
if (command.background)
{
input_command.erase(input_command.size() - 1);
}
// Tokenize the input command into a vector of strings
std::istringstream iss(input_command);
std::vector<std::string> argv;
copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter<std::vector<std::string> >(argv));
// We also want the arguments as a vector of C style strings
std::vector<const char *> argv_c;
for (auto arg : argv)
{
argv_c.push_back(arg.c_str());
}
command.command = input_command;
command.name = argv[0];
command.argc = argv.size();
command.argv = argv;
command.argv_c = argv_c;
return command;
}
int Shelly::execute(std::string input_command)
{
return execute(parse_command(input_command));
}
int Shelly::execute(Shelly::command command)
{
int status = 0;
// Check if the command is a built in function of the shell
commands::internal_function built_in = commands::internal[command.name];
if (built_in != 0)
{
return built_in(*this, command);
}
int pid = fork();
if (pid == 0)
{
// execvp always expects the last argument to be a null terminator
command.argv_c.push_back(0);
// Because the argv_c vector stores the C-strings in order we can
// simply de-refrence the first element of the argv_c vector
execvp(command.argv_c[0], (char * const *) &command.argv_c[0]);
// exec failed, the command was _probably_ not found
std::cerr << command.name << ": command not found\n";
finish();
}
else
{
if (command.background)
{
if (background_jobs.size() < Shelly::MAX_BG_JOBS)
{
background_jobs.push_back({command, pid});
std::cout << "[" << pid << "] " << command.command << '\n';
return status;
}
// Let the user know only so many commands may be run in the bg
std::cerr << "No more than " << Shelly::MAX_BG_JOBS
<< " background jobs may be run at once.\n"
<< "Executing " << command.name << " in the foreground\n";
}
wait(&status);
}
return status;
}
void Shelly::terminate_jobs()
{
for (auto job : background_jobs)
{
kill(job.pid, SIGTERM);
wait(0);
}
background_jobs.clear();
}
std::list<Shelly::job> Shelly::jobs_list(bool completed)
{
if ( ! completed)
{
return background_jobs;
}
std::list<Shelly::job> completed_jobs;
std::list<Shelly::job>::iterator it;
// Get completed jobs and splice them into the completed_jobs list
for (it = background_jobs.begin(); it != background_jobs.end(); ++it)
{
// Check if the process is not done yet
if (waitpid(it->pid, 0, WNOHANG) == 0) continue;
completed_jobs.splice(completed_jobs.end(), background_jobs, it--);
}
return completed_jobs;
}
/**
* Begin execution
*/
int main(int, char**)
{
Shelly Shelly;
return Shelly.start();
}