-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathshell.c
127 lines (99 loc) · 3.41 KB
/
shell.c
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
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include "tokenizer.h"
/* Convenience macro to silence compiler warnings about unused function parameters. */
#define unused __attribute__((unused))
/* Whether the shell is connected to an actual terminal or not. */
bool shell_is_interactive;
/* File descriptor for the shell input */
int shell_terminal;
/* Terminal mode settings for the shell */
struct termios shell_tmodes;
/* Process group id for the shell */
pid_t shell_pgid;
int cmd_exit(struct tokens *tokens);
int cmd_help(struct tokens *tokens);
/* Built-in command functions take token array (see parse.h) and return int */
typedef int cmd_fun_t(struct tokens *tokens);
/* Built-in command struct and lookup table */
typedef struct fun_desc {
cmd_fun_t *fun;
char *cmd;
char *doc;
} fun_desc_t;
fun_desc_t cmd_table[] = {
{cmd_help, "?", "show this help menu"},
{cmd_exit, "exit", "exit the command shell"},
};
/* Prints a helpful description for the given command */
int cmd_help(unused struct tokens *tokens) {
for (unsigned int i = 0; i < sizeof(cmd_table) / sizeof(fun_desc_t); i++)
printf("%s - %s\n", cmd_table[i].cmd, cmd_table[i].doc);
return 1;
}
/* Exits this shell */
int cmd_exit(unused struct tokens *tokens) {
exit(0);
}
/* Looks up the built-in command, if it exists. */
int lookup(char cmd[]) {
for (unsigned int i = 0; i < sizeof(cmd_table) / sizeof(fun_desc_t); i++)
if (cmd && (strcmp(cmd_table[i].cmd, cmd) == 0))
return i;
return -1;
}
/* Intialization procedures for this shell */
void init_shell() {
/* Our shell is connected to standard input. */
shell_terminal = STDIN_FILENO;
/* Check if we are running interactively */
shell_is_interactive = isatty(shell_terminal);
if (shell_is_interactive) {
/* If the shell is not currently in the foreground, we must pause the shell until it becomes a
* foreground process. We use SIGTTIN to pause the shell. When the shell gets moved to the
* foreground, we'll receive a SIGCONT. */
while (tcgetpgrp(shell_terminal) != (shell_pgid = getpgrp()))
kill(-shell_pgid, SIGTTIN);
/* Saves the shell's process id */
shell_pgid = getpid();
/* Take control of the terminal */
tcsetpgrp(shell_terminal, shell_pgid);
/* Save the current termios to a variable, so it can be restored later. */
tcgetattr(shell_terminal, &shell_tmodes);
}
}
int main(unused int argc, unused char *argv[]) {
init_shell();
static char line[4096];
int line_num = 0;
/* Please only print shell prompts when standard input is not a tty */
if (shell_is_interactive)
fprintf(stdout, "%d: ", line_num);
while (fgets(line, 4096, stdin)) {
/* Split our line into words. */
struct tokens *tokens = tokenize(line);
/* Find which built-in function to run. */
int fundex = lookup(tokens_get_token(tokens, 0));
if (fundex >= 0) {
cmd_table[fundex].fun(tokens);
} else {
/* REPLACE this to run commands as programs. */
fprintf(stdout, "This shell doesn't know how to run programs.\n");
}
if (shell_is_interactive)
/* Please only print shell prompts when standard input is not a tty */
fprintf(stdout, "%d: ", ++line_num);
/* Clean up memory */
tokens_destroy(tokens);
}
return 0;
}