FreeNOS
Shell.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2009 Niek Linnenbank
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <TerminalCodes.h>
19#include "Shell.h"
20#include "ChangeDirCommand.h"
21#include "ExitCommand.h"
22#include "StdioCommand.h"
23#include "WriteCommand.h"
24#include "HelpCommand.h"
25#include "TimeCommand.h"
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <errno.h>
30#include <dirent.h>
31#include <sys/utsname.h>
32#include <sys/wait.h>
33#include <sys/stat.h>
34#include <unistd.h>
35#include <fcntl.h>
36
38#define MAX_ARGV 16
39
40Shell::Shell(int argc, char **argv)
41 : POSIXApplication(argc, argv)
42{
43 parser().setDescription("System command shell interpreter");
44 parser().registerPositional("FILE", "File(s) containing shell commands to execute", 0);
45
52}
53
55{
57 {
58 delete i.current();
59 }
60}
61
63{
64 const Vector<Argument *> & positionals = arguments().getPositionals();
65 FILE *fp;
66 struct stat st;
67 char *contents;
68
69 // Check if shell script was given as argument
70 if (positionals.count() > 0)
71 {
72 // Execute commands in each file
73 for (Size i = 0; i < positionals.count(); i++)
74 {
75 const char *file = *(positionals[i]->getValue());
76
77 // Query the file size
78 if (stat(file, &st) != 0)
79 {
80 ERROR("failed to stat() `" << file << "': " << strerror(errno));
81 continue;
82 }
83
84 // Open file
85 if ((fp = fopen(file, "r")) == NULL)
86 {
87 ERROR("failed to fopen() `" << file << "': " << strerror(errno));
88 continue;
89 }
90
91 // Allocate buffer storage
92 contents = new char[st.st_size + 1];
93
94 // Read the entire file into memory
95 if (fread(contents, st.st_size, 1, fp) != (size_t) 1U)
96 {
97 ERROR("failed to fread() `" << file << "': " << strerror(errno));
98 fclose(fp);
99 continue;
100 }
101 // Null terminate
102 contents[st.st_size] = 0;
103
104 // Parse it into lines
105 String contentString(contents);
106 List<String> lines = contentString.split('\n');
107
108 // Execute each command
109 for (ListIterator<String> i(lines); i.hasCurrent(); i++)
110 {
111 executeInput((char *) *i.current());
112 }
113
114 // Cleanup
115 delete[] contents;
116 fclose(fp);
117 }
118 }
119 // Run an interactive Shell
120 else
121 {
122 // Show the user where to get help
123 printf( "\r\n"
124 "Entering interactive Shell. Type 'help' for the command list.\r\n"
125 "\r\n");
126
127 // Begin loop
128 return runInteractive();
129 }
130
131 // Done
132 return Success;
133}
134
136{
137 char *cmdStr;
138
139 // Read commands
140 while (true)
141 {
142 // Print the prompt
143 prompt();
144
145 // Wait for a command string
146 cmdStr = getInput();
147
148 // Enough input?
149 if (!cmdStr || strlen(cmdStr) == 0)
150 {
151 continue;
152 }
153 // Execute the command
154 executeInput(cmdStr);
155 }
156
157 // Done (never reached)
158 return Success;
159}
160
161int Shell::executeInput(const Size argc, const char **argv, const bool background)
162{
163 char tmp[128];
164 ShellCommand *cmd;
165 int pid, status;
166
167 for (Size i = 0; i < argc; i++)
168 {
169 DEBUG("argv[" << i << "] = " << argv[i]);
170 }
171
172 // Ignore comments
173 if (argv[0][0] == '#')
174 return EXIT_SUCCESS;
175
176 // Do we have a matching ShellCommand?
177 if (!(cmd = getCommand(argv[0])))
178 {
179 // If not, try to execute it as a file directly
180 if ((pid = runProgram(argv[0], argv)) != -1)
181 {
182 if (!background)
183 {
184 waitpid(pid, &status, 0);
185 return status;
186 } else
187 return EXIT_SUCCESS;
188 }
189
190 // Try to find it on the filesystem. (temporary hardcoded PATH)
191 else if (argv[0][0] != '/' && snprintf(tmp, sizeof(tmp), "/bin/%s", argv[0]) &&
192 (pid = runProgram(tmp, argv)) != -1)
193 {
194 if (!background)
195 {
196 waitpid(pid, &status, 0);
197 return status;
198 } else
199 return EXIT_SUCCESS;
200 }
201 else
202 {
203 ERROR("exec `" << argv[0] << "' failed: " << strerror(errno));
204 }
205 }
206 // Enough arguments given?
207 else if (argc - 1 < cmd->getMinimumParams())
208 {
209 ERROR(cmd->getName() << ": not enough arguments (" << cmd->getMinimumParams() << " required)");
210 }
211 // Execute it
212 else
213 {
214 return cmd->execute(argc - 1, argv + 1);
215 }
216 // Not successful
217 return EXIT_FAILURE;
218}
219
220int Shell::executeInput(char *command)
221{
222 char *argv[MAX_ARGV];
223 Size argc;
224 bool background;
225
226 // Valid argument?
227 if (!strlen(command))
228 {
229 return EXIT_SUCCESS;
230 }
231
232 // Attempt to extract arguments
233 argc = parse(command, argv, MAX_ARGV, &background);
234
235 // Run command
236 return executeInput(argc, (const char **)argv, background);
237}
238
239char * Shell::getInput() const
240{
241 static char line[1024];
242 Size total = 0;
243#ifdef __HOST__
244 const bool echo = false;
245#else
246 const bool echo = true;
247#endif /* __HOST__ */
248
249 // Read a line
250 while (total < sizeof(line) - 1)
251 {
252 // Read a character
253 const ssize_t result = read(0, line + total, 1);
254 if (result == -1)
255 {
256 return (char *) NULL;
257 }
258
259 // Process character
260 switch (line[total])
261 {
262 // Enter
263 case '\r':
264 case '\n':
265 if (echo) printf("\r\n");
266 line[total] = ZERO;
267 return line;
268
269 // Backspace
270 case '\b':
271 case 0x7F:
272 if (total > 0)
273 {
274 total--;
275 if (echo) printf("\b \b");
276 }
277 break;
278
279 default:
280 if (echo) printf("%c", line[total]);
281 total++;
282 break;
283 }
284 }
285 line[total] = ZERO;
286 return line;
287}
288
289void Shell::prompt() const
290{
291 char host[128], cwd[128];
292
293 // Retrieve current hostname
294 gethostname(host, sizeof(host));
295
296 // Retrieve current working directory
297 if (getcwd(cwd, sizeof(cwd)) == NULL)
298 {
299 cwd[0] = '/';
300 cwd[1] = 0;
301 }
302
303 // Print out the prompt
304 printf(WHITE "(" GREEN "%s" WHITE ") " BLUE "%s" WHITE " # ",
305 host, cwd);
306
307#ifdef __HOST__
308 fflush(stdout);
309#endif /* __HOST__ */
310}
311
316
318{
319 return m_commands.get(name) ? *m_commands.get(name) : ZERO;
320}
321
323{
324 m_commands.insert(command->getName(), command);
325}
326
327Size Shell::parse(char *cmdline, char **argv, Size maxArgv, bool *background)
328{
329 Size argc;
330
331 *background = false;
332
333 for (argc = 0; argc < maxArgv && *cmdline; argc++)
334 {
335 while (*cmdline && *cmdline == ' ')
336 cmdline++;
337
338 if (*cmdline == '&')
339 {
340 *background = true;
341 break;
342 }
343
344 argv[argc] = cmdline;
345
346 while (*cmdline && *cmdline != ' ')
347 cmdline++;
348
349 if (*cmdline) *cmdline++ = ZERO;
350 }
351 argv[argc] = ZERO;
352 return argc;
353}
u64 fp
Definition ARM64Control.h:3
#define MAX_ARGV
Maximum number of supported command arguments.
Definition Shell.cpp:38
Result
Result codes.
Definition Application.h:54
const ArgumentContainer & arguments() const
Get program arguments.
ArgumentParser & parser()
Get program arguments parser.
const Vector< Argument * > & getPositionals() const
Get positional arguments.
void setDescription(const String &desc)
Set program description.
Result registerPositional(const char *name, const char *description, Size count=1)
Register a positional argument.
Change the current working directory.
Exit the Shell.
Definition ExitCommand.h:36
Iterate through a HashTable.
virtual bool hasCurrent() const
Check if there is a current item.
Efficient key -> value lookups.
Definition HashTable.h:45
virtual const V * get(const K &key) const
Returns the first value for the given key.
Definition HashTable.h:287
virtual bool insert(const K &key, const V &value)
Inserts the given item to the HashTable.
Definition HashTable.h:133
Prints the help info for all known ShellCommands.
Definition HelpCommand.h:38
Iterate through a List.
virtual bool hasCurrent() const
Check if there is a current item on the List.
Simple linked list template class.
Definition List.h:37
POSIX-compatible application.
int runProgram(const char *path, const char **argv)
Runs an external program.
Builtin command for the Shell.
Size getMinimumParams() const
Get the minimum number of parameters required.
const char * getName() const
Get command name.
virtual int execute(const Size nparams, const char **params)=0
Executes the command.
void prompt() const
Output a prompt.
Definition Shell.cpp:289
Result runInteractive()
Executes the Shell by entering an infinite loop.
Definition Shell.cpp:135
void registerCommand(ShellCommand *command)
Register a new ShellCommand.
Definition Shell.cpp:322
ShellCommand * getCommand(const char *name)
Get shell command.
Definition Shell.cpp:317
HashTable< String, ShellCommand * > & getCommands()
Get all shell commands.
Definition Shell.cpp:312
char * getInput() const
Fetch a command text from standard input.
Definition Shell.cpp:239
HashTable< String, ShellCommand * > m_commands
All known ShellCommands.
Definition Shell.h:131
int executeInput(const Size argc, const char **argv, const bool background)
Executes the given input.
Definition Shell.cpp:161
virtual ~Shell()
Destructor.
Definition Shell.cpp:54
Shell(int argc, char **argv)
Constructor.
Definition Shell.cpp:40
virtual Result exec()
Execute the application.
Definition Shell.cpp:62
Size parse(char *cmdline, char **argv, Size maxArgv, bool *background)
Parses an input string into separate pieces.
Definition Shell.cpp:327
Change the standard Input/Output of the shell.
Abstraction of strings.
Definition String.h:42
List< String > split(const char delimiter) const
Split the String into parts separated by a delimiter.
Definition String.cpp:408
Measure the execution time of a program.
Definition TimeCommand.h:38
Vectors are dynamically resizeable Arrays.
Definition Vector.h:42
virtual Size count() const
Returns the number of items inside the Vector.
Definition Vector.h:204
Write data to a file.
C int gethostname(char *name, size_t namelen)
Get name of current host.
C size_t strlen(const char *str)
Calculate the length of a string.
Definition strlen.cpp:21
C char * strerror(int errnum)
The strerror function maps the number in errnum to a message string.
Definition strerror.cpp:20
C int errno
The lvalue errno is used by many functions to return error values.
#define EXIT_SUCCESS
Successful termination.
Definition stdlib.h:33
#define EXIT_FAILURE
Unsuccessful termination.
Definition stdlib.h:36
C int printf(const char *format,...)
Output a formatted string to standard output.
Definition printf.cpp:22
C int fclose(FILE *stream)
Close a stream.
Definition fclose.cpp:23
C ssize_t read(int fildes, void *buf, size_t nbyte)
Read from a file.
Definition read.cpp:22
slong ssize_t
Used for a count of bytes or an error indication.
Definition types.h:38
C size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream)
Binary input.
Definition fread.cpp:24
C FILE * fopen(const char *filename, const char *mode)
Open a stream.
Definition fopen.cpp:24
C char * getcwd(char *buf, size_t size)
Get the pathname of the current working directory.
Definition getcwd.cpp:24
C int snprintf(char *buffer, unsigned int size, const char *fmt,...)
Write a formatted string into a buffer.
Definition snprintf.cpp:22
C pid_t waitpid(pid_t pid, int *stat_loc, int options)
Wait for a child process to stop or terminate.
Definition waitpid.cpp:23
#define NULL
NULL means zero.
Definition Macros.h:39
#define ERROR(msg)
Output an error message.
Definition Log.h:61
unsigned int Size
Any sane size indicator cannot go negative.
Definition Types.h:128
#define ZERO
Zero value.
Definition Macros.h:43
#define DEBUG(msg)
Output a debug message to standard output.
Definition Log.h:89
#define BLUE
#define WHITE
#define GREEN
A structure containing information about a file.
Definition stdio.h:61
The <sys/stat.h> header shall define the stat structure.
Definition stat.h:177
off_t st_size
For regular files, the file size in bytes.
Definition stat.h:226