1 ///////////////////////////////////////////////////////////
2 // Betriebssysteme I - Windows 2000
3 // Assignment 3.3: An extended command shell
4 //
5 // author: Stephan Brumme
6 // last changes: November 27, 2001
7
8 #include <windows.h>
9 #include <stdio.h>
10
11
12 ////////////////////////////////////////////////////////////////////
13 // read a single command
14 BOOL ReadLine(const HANDLE hFile, char* buffer)
15 {
16 // FALSE, if error
17 BOOL bRead;
18 // determines end of line
19 BOOL bCRLF = FALSE;
20 // number of read bytes per read access (only 0 or 1 !)
21 unsigned long nRead = 0;
22 // total amount of read bytes
23 unsigned long nIndex = 0;
24
25 // read in command byte by byte
26 do
{
27 // read a single byte
28 bRead = ReadFile(hFile, &buffer[nIndex], 1, &nRead, NULL);
29 // strip EOL
30 if (buffer[nIndex] == 0x0D)
31 nIndex--;
32 if (buffer[nIndex] == 0x0A)
33 bCRLF = TRUE;
34
35 // next byte
36 nIndex++;
37 }
38 while (nRead == 1 && bCRLF == FALSE);
39
40 // terminate string, we moved one too far
41 buffer[nIndex-1] = 0;
42
43 // return last status
44 return bRead;
45 }
46
47
48 ////////////////////////////////////////////////////////////////////
49 // parse a string and looks for a given command
50 // return a pointer to the parameter list
51 // or NULL if command not found
52 char* SearchCommand(const char* command, const char* text)
53 {
54 // look for command
55 char* pReturn = strstr(text, command);
56 // must be at the beginning of the line
57 if (pReturn != text)
58 return NULL;
59
60 // jump to parameter list
61 pReturn += strlen(command);
62
63 // remove whitespaces
64 while (*pReturn == ' ')
65 pReturn++;
66
67 return pReturn;
68 }
69
70
71 ////////////////////////////////////////////////////////////////////
72 const MAX_HANDLES = 255;
73 HANDLE arHandles[MAX_HANDLES];
74
75 ////////////////////////////////////////////////////////////////////
76 // add handle to internal list
77 // no checking !
78 void AddHandle(HANDLE handle)
79 {
80 for (int i=0; i<MAX_HANDLES; i++)
81 if (arHandles[i] == 0)
82 {
83 arHandles[i] = handle;
84 break;
85 }
86 }
87
88
89 ////////////////////////////////////////////////////////////////////
90 // remove handle from internal list
91 // no checking !
92 void RemoveHandle(HANDLE handle)
93 {
94 for (int i=0; i<MAX_HANDLES; i++)
95 if (arHandles[i] == handle)
96 {
97 arHandles[i] = 0;
98 break;
99 }
100 }
101
102
103 ////////////////////////////////////////////////////////////////////
104 // change directory
105 // return TRUE if successful
106 BOOL ChangeDirectory(const char* dir)
107 {
108 return SetCurrentDirectory(dir);
109 }
110
111
112 ////////////////////////////////////////////////////////////////////
113 // wait for a single process
114 // return TRUE if successful
115 BOOL Wait(HANDLE hProcess)
116 {
117 printf("... waiting for %d ...\n", hProcess);
118
119 // wait for Godot
120 DWORD dwResult = WaitForSingleObject(hProcess, INFINITE);
121 CloseHandle(hProcess);
122
123 // remove handle from list
124 RemoveHandle(hProcess);
125
126 return (dwResult != WAIT_FAILED);
127 }
128
129
130 ////////////////////////////////////////////////////////////////////
131 // create a new process
132 BOOL NewProcess(char* name, BOOL bWait = TRUE)
133 {
134 // fill in start-up info values
135 STARTUPINFO StartupInfo;
136 ZeroMemory(&StartupInfo, sizeof StartupInfo);
137 StartupInfo.cb = sizeof StartupInfo;
138
139 // CreateProcess returns some process information
140 PROCESS_INFORMATION ProcessInfo;
141 ZeroMemory(&ProcessInfo, sizeof ProcessInfo);
142
143 if (CreateProcess(NULL, // application name is first token of the command line
144 name, // contains application name, too
145 NULL, // default process security settings
146 NULL, // default thread security settings
147 FALSE, // process DOES NOT inherit from minshell.exe
148 0, // child has no access to minshell.exe
149 NULL, // no special environment
150 NULL, // same directory
151 &StartupInfo,
152 &ProcessInfo) == 0)
153 {
154 // error handling
155 const int MAX_ERROR_LEN = 255;
156 char strError[MAX_ERROR_LEN+1];
157
158 DWORD nError = GetLastError();
159 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
160 NULL,
161 nError,
162 0,
163 strError,
164 MAX_ERROR_LEN,
165 NULL);
166
167 printf("Failed to create \"%s===>\<===": (%d) %s\n", name, nError, strError);
168
169 return FALSE;
170 }
171
172 printf("\"%s===>\<===" (PID: %d, handle: %d) started ", name, ProcessInfo.dwProcessId,
173 ProcessInfo.hProcess);
174
175 // wait until process terminates and clean up
176 if (bWait == TRUE)
177 {
178 printf("synchronously\n");
179
180 // wait
181 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
182
183 CloseHandle(ProcessInfo.hProcess);
184 CloseHandle(ProcessInfo.hThread);
185 }
186 else
{
187 printf("asynchronously\n");
188 CloseHandle(ProcessInfo.hThread);
189
190 // store handle
191 AddHandle(ProcessInfo.hProcess);
192 }
193
194 return TRUE;
195 }
196
197
198
199 ////////////////////////////////////////////////////////////////////
200 ////////////////////////////////////////////////////////////////////
201 ////////////////////////////////////////////////////////////////////
202 // here we go !
203
204 int main(int argc, char* argv[])
205 {
206 // a shell command file must be supplied
207 if (argc > 2)
208 {
209 printf("usage: \"MinShell ShellCommands.txt===>\<==="\n");
210 printf("or just \"MinShell===>\<===" if you want to type in the commands.\n");
211
212 // wrong parameter count
213 return 1;
214 }
215 printf("Please remember that this extremely cool shell is case sensitive.\n" ===>\<===
216 "Available internal commands:\n\n" ===>\<===
217 "\"cd===>\<===" Directory change current directory\n" ===>\<===
218 "\"wait===>\<===" Handle|\"all===>\<===" wait for a single process / all processes\n" ===>\<===
219 "\"exit===>\<===" quit shell\n\n" ===>\<===
220 "or just enter a program (+parameters) that is available through %%PATH%% !\n\n");
221
222 // file handle to access the shell commands
223 HANDLE hFile;
224
225 if (argc == 2)
226 {
227 // open file (read shared !)
228 hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
229 if (hFile == INVALID_HANDLE_VALUE)
230 {
231 printf("Cannot find %s.\n", argv[1]);
232 // failed
233 return 2;
234 }
235 }
236 else
// read from console
237 hFile = GetStdHandle(STD_INPUT_HANDLE);
238
239 // clear HANDLE list
240 ZeroMemory(&arHandles, sizeof arHandles);
241
242
243 //
244 // DA MAIN LOOP
245 //
246
247 // read and execute shell commands
248 for (;;)
249 {
250 // at most 512 characters per line
251 const int MAX_LINE_LENGTH = 512;
252 char cmdLine[MAX_LINE_LENGTH+1];
253
254 // prompt
255 printf("MinShell:");
256 // show current directoy
257 static char strDirectory[MAX_LINE_LENGTH];
258 if (GetCurrentDirectory(sizeof strDirectory, strDirectory))
259 printf(strDirectory);
260 printf("$ ");
261
262
263 // read the command
264 BOOL bDone = ReadLine(hFile, cmdLine);
265
266 // error or exit ?
267 if (cmdLine[0] == 0 || SearchCommand("exit", cmdLine))
268 {
269 printf("\n... winke, winke, Lala !\n");
270 return 0;
271 }
272
273 // show shell command (if not typed in from console)
274 if (hFile != GetStdHandle(STD_INPUT_HANDLE))
275 printf("%s\n", cmdLine);
276
277 //
278 // now the internal commands
279 // "exit" was already handled
280 //
281
282 // "cd [directory]"
283 if (char* directory = SearchCommand("cd", cmdLine))
284 {
285 if (!ChangeDirectory(directory))
286 printf("Unable to change directory to \"%s===>\<===".\n", directory);
287 // read next command
288 continue;
289 }
290
291 // "wait"
292 if (char* strHandle = SearchCommand("wait", cmdLine))
293 {
294 // wait for all ?
295 if (strstr(strHandle, "all"))
296 {
297 // take a look at each handle
298 for (int i=0; i<MAX_HANDLES; i++)
299 if (arHandles[i] != 0)
300 // wait and remove from list
301 Wait(arHandles[i]);
302 }
303 else
{
304 // get numerical value
305 HANDLE dwHandle = (HANDLE) atoi(strHandle);
306 // wait and wait and wait ...
307 if (!Wait(dwHandle))
308 printf("Unable to use handle %d (converted from %s).\n", dwHandle, strHandle);
309 }
310
311 // read next command
312 continue;
313 }
314
315
316 //
317 // no internal command found ? let's try to launch new process !
318 //
319 if (cmdLine[strlen(cmdLine)-1] == '&')
320 {
321 // remove ampersand
322 cmdLine[strlen(cmdLine)-1] = 0;
323 // launch synchronously
324 NewProcess(cmdLine, FALSE);
325 }
326 else
// launch asynchronously
327 NewProcess(cmdLine, TRUE);
328 }
329
330 CloseHandle(hFile);
331
332 // successful
333 return 0;
334 }
335