-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLinuxShell.cpp
More file actions
414 lines (398 loc) · 11.8 KB
/
LinuxShell.cpp
File metadata and controls
414 lines (398 loc) · 11.8 KB
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pwd.h>
#include <cstring>
#include <signal.h>
#include <dirent.h>
#include <termios.h>
using namespace std;
#define CHECK(x) if(!(x)) { perror(#x " failed"); abort(); }
vector<string> arguments;
int ParentPID;
bool intsig = false;
vector<string> samefiles;
string data;
vector<string> CommandHistory;
void inthandler(int signum){
if(ParentPID == getpid()){
printf("^C\n");
intsig = true;
}
else
raise(SIGKILL);
}
void sigchld_hdl (int sig){
/* Wait for all dead processes.
* We use a non-blocking call to be sure this signal handler will not
* block if a child was cleaned up in another part of the program. */
while (waitpid(-1, NULL, WNOHANG) > 0) {
}
}
string Initialize(char *hostname, int size = HOST_NAME_MAX) {
string s1(getlogin()); //de ele eshta3alet :D
int x = gethostname(hostname, size);
string s2(hostname);
return s1+"@"+s2+":";
}
char**ConvertStringToChar() {
char**myargs = new char*[arguments.size()+1];
myargs[arguments.size()] = NULL;
for (int i = 0; i < arguments.size(); i++)
{
myargs[i] = new char [arguments[i].length()];
strcpy(myargs[i], const_cast<char*>(arguments[i].c_str()));
}
return myargs;
}
string getPath() {
char Path[1024]; int size2;
getcwd(Path,sizeof(Path)); //current directory
string s3(Path);
return s3;//Required: username@hostname:path
}
//list the files and folders that match the prefix sent in that path
bool getfiles(string path,string pre){
bool s = false;
samefiles.clear();
DIR *dir;
struct dirent *ent;
if ((dir = opendir (path.c_str())) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
if (ent->d_type == DT_REG || ent->d_type == DT_DIR){
string temp = ent->d_name;
if(pre == temp.substr(0,pre.length())){
s = true;
samefiles.push_back(temp);
}
}
}
closedir (dir);
} else {
/* could not open directory */
return false;
}
return s;
}
//function that read char from keyboard whitout wait for enter
int getch(void){
struct termios oldattr, newattr;
int ch;
tcgetattr( STDIN_FILENO, &oldattr );
newattr = oldattr;
newattr.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
return ch;
}
//gets the new path from a start path using cd commands like (../ or dirname/)
string findnewpath(string startpath,string comm){
vector<string>paths;
int start = 0;
for(int i=0;i<startpath.length();i++){
if(startpath[i] == '/'){
paths.push_back(startpath.substr(start,i-start));
start = i+1;
}
}
string temp;
for(int i = 0;i<comm.length();i++){
temp += comm[i];
if(comm[i] == '/'){
paths.push_back(temp);
temp = "";
}
else if(temp == ".."){
paths.pop_back();
temp = "";
i++;
}
}
string finalpath;
for(int i=0;i<paths.size();i++){
finalpath += paths[i]+'/';
}
if(finalpath[finalpath.length()-2] == '/')
finalpath = finalpath.substr(0,finalpath.length()-1);
return finalpath;
}
//Read the command char by char and check for (tab - enter - arrow keys - backspace)
string ReadCommand() {
int mypointer = CommandHistory.size();
string p = getPath(); //first get the path and print it
printf("%s$ ",p.c_str());
string temp;
char x;
bool cdcom = false; //indecator if the command starts with ./ so i can search for file on tap press
bool runcom = false; //indecator if the command starts with ./ so i can search for file on tap press
int point = 0; //points where the cursor on the string
do{ //we get chars util the enter is pressed
int tx = getch(); //acsii
x = (char) tx; //the actual char
//handling arrow keys it pushes 3 chars starting with \033
if(tx == '\033') { // if the first value is esc
getch(); // skip the [
switch(getch()) { // the real value
case 'A':// code for arrow up
for(point;point < temp.length();point++){ //first put the cursor to the end of the string
printf("%c",temp[point]);
}
if(mypointer > 0) {
for(int i=0;i<temp.length();i++)
printf("\b \b");
mypointer--;
temp = CommandHistory[mypointer];
point = temp.length();
printf("%s",temp.c_str());
}
break;
case 'B':// code for arrow down
for(point;point < temp.length();point++){ //first put the cursor to the end of the string
printf("%c",temp[point]);
}
if(mypointer==CommandHistory.size()-1) {
for(int i=0;i<temp.length();i++)
printf("\b \b");
mypointer++;
temp = "";
point = 0;
}
else if(mypointer < CommandHistory.size()-1) {
for(int i=0;i<temp.length();i++)
printf("\b \b");
mypointer++;
temp = CommandHistory[mypointer];
point = temp.length();
printf("%s",temp.c_str());
}
break;
case 'C':// code for arrow right
if(point < temp.size()){ //check if I'm not at the end of the string
printf("%c",temp[point]); //print the char again give the illusion of moving the cursor :D
point++; //move the cursor right
}
break;
case 'D':// code for arrow left
if(point >0){ //check if I'm at the beginning of the string
point--; //move the cursor to the left
printf("\b"); //by printing backspace the cursor moves to the left :D
}
break;
}
}
else if(x == '\t'){ //check for the tab
string added;
if(getfiles(p,temp)){ //if there are matches in the hole command
for(point;point < temp.length();point++){ //first put the cursor to the end of the string
printf("%c",temp[point]);
}
if(samefiles.size() == 1){ //if there is only one match then add it to the string and complete the word
added = samefiles[0].substr(temp.length(),samefiles[0].length());
temp += added;
printf("%s",added.c_str());
point+=added.length();
}
else{ //if there are multible matches first sort the vector and then print them
sort(samefiles.begin(),samefiles.end());
for(int i=0;i<samefiles.size();i++){
printf("\n%s",samefiles[i].c_str());
}
printf("\n%s%s$ %s",data.c_str(),p.c_str(),temp.c_str()); //print the entered string to resume typing
}
}
else if(cdcom){ //if there are matches after the cd command assuming the command is like [cd (dir)] with single space after cd
int startpoint = temp.length(); //to enable multitap each time I get a complete path I begin search in that path not the original
for(startpoint;startpoint>3;startpoint--){
if(temp[startpoint] == '/'){
startpoint++;
break;
}
}
string newpath = findnewpath(p+"/",temp.substr(3,temp.length()));
//printf("\n%s",newpath.c_str());
if(getfiles(newpath,temp.substr(startpoint,temp.length()))){ //match the string after 3rd char (the space)
for(point;point < temp.length();point++){
printf("%c",temp[point]);
}
if(samefiles.size() == 1){
added = samefiles[0].substr(temp.length()-startpoint,samefiles[0].length());
temp += added;
point+=added.length();
printf("%s",added.c_str());
}
else{
sort(samefiles.begin(),samefiles.end());
for(int i=0;i<samefiles.size();i++){
printf("\n%s",samefiles[i].c_str());
}
printf("\n%s%s$ %s",data.c_str(),p.c_str(),temp.c_str());
}
}
}else if(runcom){ //same as above but now 2 chars (./)
if(getfiles(p,temp.substr(2,temp.length()))){
for(point;point < temp.length();point++){
printf("%c",temp[point]);
}
if(samefiles.size() == 1){
added = samefiles[0].substr(temp.length()-2,samefiles[0].length());
temp += added;
point+=added.length();
printf("%s",added.c_str());
}
else{
sort(samefiles.begin(),samefiles.end());
for(int i=0;i<samefiles.size();i++){
printf("\n%s",samefiles[i].c_str());
}
printf("\n%s$ %s",p.c_str(),temp.c_str());
}
}
}
}
else if(tx == 127 || tx == 8){ //check for the backspace
if(temp.length()>0 && point == temp.length()){
printf("\b \b"); //first we move the cursor to the left then put a space and then to the left again
temp = temp.substr(0,temp.length()-1); //remove the last char
point--; //decrease the pointer
}
}
else if(x != '\n'){ //if it's usual char
if(point < temp.size()){ //if the cursor in certen postion so we replace that char
printf("%c",x);
temp[point] = x;
point++;
}
else{ //else append the string
printf("%c",x);
temp += x;
if(temp == "cd ")
cdcom = true;
else if(temp == "./")
runcom = true;
point++; //increase the pointer
}
}
else{
printf("\n"); //enter is pressed so we enter endl
}
if(temp.length() <= 2 && temp!="cd") //if we delete and the string don't have cd so we make it false
cdcom = false;
else if(temp.length() <= 2 && temp!="./")
runcom = false;
}while(x != '\n' && !intsig);
temp += ' ';
if(intsig)
return "\n";
return temp;
}
int ChangeDirectory(char *x) {
return chdir(x);
}
char** ParseCommand(string input) {
string temp = "";
for (int i = 0; i < input.length(); i++)
{
if(input[i] != ' ') {
temp += input[i];
}
else {
arguments.push_back(temp);
temp="";
}
}
//here we have a vector of words that is the arguments of our command
//Parsing
//Second argument in execvp is array of char pointers so we need to convert the vector of strings into char
return ConvertStringToChar();
}
bool ExecuteCommand(char**arg,bool background) {
sigset_t mask, omask;
if(background){
/* Block SIGINT. */
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
CHECK(sigprocmask(SIG_BLOCK, &mask, &omask) == 0);
}
pid_t pid, wpid;
int status;
pid = fork();
if (pid == 0) {
// Child process
if(background)
CHECK(setpgid(0, 0) == 0); //change process group so ctrl c don't terminate it
if(execvp(arg[0],arg) == -1) {
perror("ERROR");
}
exit(EXIT_FAILURE);
}
else if (pid < 0) {
// Error forking
perror("ERROR");
}
else {
// Parent process
if(background){
if (setpgid(pid, pid) < 0 && errno != EACCES)
abort();
/* Unblock SIGINT */
CHECK(sigprocmask(SIG_SETMASK, &omask, NULL) == 0);
}
else{
do {
wpid = waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
for(int i=0;i<arguments.size();i++)
delete[] arg[i];
}
return true;
}
int main () {
ParentPID = getpid();
signal(SIGINT, inthandler);
signal(SIGCHLD, sigchld_hdl);
char hostname[HOST_NAME_MAX + 1];
data = Initialize(hostname);
while(1){
printf("\n%s",data.c_str());
string In = ReadCommand();
if(In.length() <= 1){
intsig = false;
continue;
}
//to terminate the custom shell and back to original linux shell
if(In == "end shell "){
printf("Mashi khalsana be shiaka\n");
raise(SIGKILL);
}
CommandHistory.push_back(In.substr(0,In.length()-1));
string temp = In.substr(0,2);
char**C=ParseCommand(In);
if(temp == "cd"){
if(ChangeDirectory(C[1])!=0){
int err = errno;
printf("Error: ");
if(err == ENOENT)
printf("The directory specified in path does not exist.\n");
else if(err == EACCES)
printf("Search permission is denied for one of the components of path.\n");
else
printf("Cannot change the path\n");
}
}
else {
ExecuteCommand(C,In[In.length()-2] == '&');
}
arguments.clear();
}
return 0;
}