source: trunk/src/kShell/kShell.c@ 21

Last change on this file since 21 was 19, checked in by bird, 23 years ago

Bed time.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: kShell.c 19 2002-10-17 23:02:18Z bird $
2 *
3 * kShell - A mini shell.
4 *
5 * Copyright (c) 2002 knut st. osmundsen <bird@anduin.net>
6 *
7 *
8 * This file is part of kBuild.
9 *
10 * kBuild is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * kBuild is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with kBuild; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26/*******************************************************************************
27* Defined Constants And Macros *
28*******************************************************************************/
29#define KSWORD_FLAGS_ESCAPE 1
30#define KSWORD_FLAGS_QUOTED 2
31#define KSWORD_FLAGS_PATTERN 4
32
33#define KSHELL_MAX_COMMAND 4096
34
35/*******************************************************************************
36* Header Files *
37*******************************************************************************/
38#include "kShell.h"
39#include <string.h>
40#include <stdlib.h>
41#include <stdio.h>
42
43
44/*******************************************************************************
45* Global Variables *
46*******************************************************************************/
47typedef struct _kshellWord
48{
49 int fFlags;
50 char * pszWord;
51 unsigned cchWord;
52 const char *pszWordOrg;
53 unsigned cchWordOrg;
54} KSHELLWORD, *PKSHELLWORD;
55
56
57typedef struct _kshellWords
58{
59 unsigned cWords;
60 KSHELLWORD aWords[1];
61} KSHELLWORDS, *PKSHELLWORDS;
62
63
64/*******************************************************************************
65* Internal Functions *
66*******************************************************************************/
67PKSHELLWORDS kshellWordsParse(const char *pszText, int cWords, PKSHELLWORDS pPrevWords);
68void kshellWordsDestroy(PKSHELLWORDS pWords);
69
70int kshellCmdcopy(const char *pszCmd, PKSHELLWORDS pWords);
71int kshellCmdcopytree(const char *pszCmd, PKSHELLWORDS pWords);
72int kshellCmdrm(const char *pszCmd, PKSHELLWORDS pWords);
73int kshellCmdrmtree(const char *pszCmd, PKSHELLWORDS pWords);
74int kshellCmdchdir(const char *pszCmd, PKSHELLWORDS pWords);
75int kshellCmdmkdir(const char *pszCmd, PKSHELLWORDS pWords);
76int kshellCmdrmdir(const char *pszCmd, PKSHELLWORDS pWords);
77int kshellCmdset(const char *pszCmd, PKSHELLWORDS pWords);
78int kshellCmdunset(const char *pszCmd, PKSHELLWORDS pWords);
79int kshellCmdpushenv(const char *pszCmd, PKSHELLWORDS pWords);
80int kshellCmdpopenv(const char *pszCmd, PKSHELLWORDS pWords);
81int kshellCmdecho(const char *pszCmd, PKSHELLWORDS pWords);
82int kshellCmdwrite(const char *pszCmd, PKSHELLWORDS pWords);
83int kshellCmdExecuteProgram(const char *pszCmd, PKSHELLWORDS pWords);
84
85
86
87/**
88 * Initiate the shell.
89 * Allow us to initiate globals.
90 *
91 * @returns 0 on success.
92 * @returns error code on error.
93 * @param fVerbose If set banner will be printed.
94 */
95int kshellInit(int fVerbose)
96{
97 if (fVerbose)
98 {
99 printf("\n"
100 "kShell v0.0.0\n"
101 "Copyright 2002 knut st. osmundsen <bird@anduin.net>\n"
102 "\n");
103 }
104 return 0;
105}
106
107
108/**
109 * Terminate the shell.
110 * Allow us to cleanup stuff.
111 */
112void kshellTerm(void)
113{
114 return;
115}
116
117
118/**
119 * Start interactive shell interface.
120 * This reads commands from stdin till 'exit' is encountered or end-of-file.
121 *
122 * @returns returncode of last command.
123 */
124int kshellInteractive(void)
125{
126 static char szCmd[KSHELL_MAX_COMMAND];
127 int rc = 0;
128
129 printf("kShell>");
130 fflush(stdout);
131 while (fgets(&szCmd[0], sizeof(szCmd), stdin))
132 {
133 char *pszEnd = &szCmd[strlen(&szCmd[0]) - 1];
134 while (pszEnd >= &szCmd[0] && (*pszEnd == '\n' || *pszEnd == '\r'))
135 *pszEnd-- = '\0';
136
137 if (!strcmp(&szCmd[0], "exit"))
138 break;
139
140 rc = kshellExecute(&szCmd[0]);
141 printf("kShell(rc=%d)>", rc);
142 fflush(stdout);
143 }
144
145 return rc;
146}
147
148/**
149 * Execute a shell command.
150 * @returns 0 on success if command.
151 * @returns Error code if command failed.
152 * @returns Return code from program.
153 * @returns 1742 (KSHELL_ERROR_PROGRAM_NOT_FOUND) if program wasn't found.
154 * @returns 1743 (KSHELL_ERROR_COMMAND_TOO_LONG) if program commandline was too long.
155 * @param pszCmd Command or program to execute.
156 */
157int kshellExecute(const char *pszCmd)
158{
159#define MAX_WORDS (~0)
160 static struct _kshellCommands
161 {
162 const char *pszCmd;
163 unsigned cWords; /* Number of words including the command it self. */
164 int (*pfnCmd)(const char *, PKSHELLWORDS);
165 } aCmds[] =
166 {
167 {"copy", MAX_WORDS, kshellCmdcopy},
168 {"copytree", 3, kshellCmdcopytree},
169 {"rm", MAX_WORDS, kshellCmdrm},
170 {"rmtree", MAX_WORDS, kshellCmdrmtree},
171 {"chdir", 2, kshellCmdchdir},
172 {"mkdir", MAX_WORDS, kshellCmdmkdir},
173 {"rmdir", MAX_WORDS, kshellCmdrmdir},
174 {"set", 1, kshellCmdset},
175 {"unset", MAX_WORDS, kshellCmdunset},
176 {"pushenv", MAX_WORDS, kshellCmdpushenv},
177 {"popenv", 1, kshellCmdpopenv},
178 {"echo", 2, kshellCmdecho},
179 {"write", 2, kshellCmdwrite},
180
181 /* last entry */
182 {"", 1, kshellCmdExecuteProgram}
183 };
184#undef MAX_WORDS
185
186 PKSHELLWORDS pWords;
187 int i;
188 int rc;
189
190
191 /*
192 * Parse out the first word.
193 */
194 pWords = kshellWordsParse(pszCmd, 1, NULL);
195 if (!pWords)
196 return KSHELL_ERROR_NOT_ENOUGHT_MEMORY;
197 if (!pWords->cWords)
198 return 0;
199
200
201 /*
202 * Look for command.
203 * Note! the last entry is the default command (execute program).
204 */
205 for (i = 0; i < (sizeof(aCmds) / sizeof(aCmds[0])) - 1; i++)
206 {
207 if (!strcmp(aCmds[i].pszCmd, pWords->aWords[0].pszWord))
208 break;
209 }
210
211
212 /*
213 * Execute command.
214 */
215 if (aCmds[i].cWords > 1)
216 {
217 pWords = kshellWordsParse(pszCmd, aCmds[i].cWords, pWords);
218 if (!pWords)
219 return KSHELL_ERROR_NOT_ENOUGHT_MEMORY;
220 }
221 rc = aCmds[i].pfnCmd(pszCmd, pWords);
222
223
224 return rc;
225}
226
227
228/**
229 * Parses words out of a string.
230 *
231 * @returns Pointer to a words structure.
232 * This must be destroy calling kshellWordsDestroy().
233 * @returns NULL on failure (out of memory).
234 * @param pszText Text string to parse.
235 * @param cWords Number of words to parse. Will stop after cWords.
236 * @param pPrevWords Pointer to structur of previosly parse words from pszText.
237 * Use this to continue parsing a string. The pPrevWords
238 * structure will be destroyed.
239 * If NULL we'll start from the begining.
240 */
241PKSHELLWORDS kshellWordsParse(const char *pszText, int cWords, PKSHELLWORDS pPrevWords)
242{
243 PKSHELLWORDS pWords = pPrevWords;
244
245 /*
246 * If previous work done, skip to end of that.
247 */
248 if (pPrevWords && pPrevWords->cWords)
249 {
250 pszText = pPrevWords->aWords[pPrevWords->cWords - 1].pszWordOrg
251 + pPrevWords->aWords[pPrevWords->cWords - 1].cchWordOrg;
252 cWords -= pPrevWords->cWords;
253 }
254
255 /*
256 * Parse loop
257 */
258 while (cWords > 0)
259 {
260 KSHELLWORD word = {0,0,0,0,0};
261 char chEnd = ' ';
262
263 /*
264 * Skip blanks to find start of word.
265 */
266 while (*pszText == ' ' || *pszText == '\t')
267 pszText++;
268 if (!*pszText)
269 break;
270 word.pszWordOrg = pszText;
271
272 /*
273 * Quoted?
274 */
275 if (*pszText == '"')
276 {
277 pszText++;
278 word.fFlags |= KSWORD_FLAGS_QUOTED;
279 chEnd = '"';
280 }
281
282 /*
283 * Find end of word and look for escape and pattern characters.
284 */
285 while (*pszText != '\0' && *pszText != chEnd)
286 {
287 if (*pszText == '\\')
288 {
289 word.fFlags |= KSWORD_FLAGS_ESCAPE;
290 pszText++;
291 }
292 if (*pszText == '*' || *pszText == '?')
293 word.fFlags |= KSWORD_FLAGS_PATTERN;
294 pszText++;
295 }
296 if (word.fFlags & KSWORD_FLAGS_QUOTED)
297 pszText++;
298 word.cchWordOrg = pszText - word.pszWordOrg;
299
300 /*
301 * Make a copy of the word and unescape (if required).
302 */
303 word.pszWord = malloc(word.cchWordOrg + 1);
304 if (!word.pszWord)
305 {
306 kshellWordsDestroy(pWords);
307 return NULL;
308 }
309
310 if (word.fFlags & KSWORD_FLAGS_ESCAPE)
311 {
312 int cch = word.cchWordOrg;
313 const char *pszSrc = word.pszWordOrg;
314 char * pszTrg = word.pszWord;
315 while (cch)
316 {
317 if (*pszSrc == '\\')
318 pszSrc++;
319 *pszTrg++ = *pszSrc++;
320 }
321 word.cchWord = pszTrg - word.pszWord;
322 *pszTrg = '\0';
323 }
324 else
325 {
326 if (word.fFlags & KSWORD_FLAGS_QUOTED)
327 {
328 word.cchWord = word.cchWordOrg - 2;
329 memcpy(word.pszWord, word.pszWordOrg + 1, word.cchWord);
330 }
331 else
332 {
333 word.cchWord = word.cchWordOrg;
334 memcpy(word.pszWord, word.pszWordOrg, word.cchWord);
335 }
336 word.pszWord[word.cchWord] = '\0';
337 }
338
339
340 /*
341 * Add to words structure.
342 */
343 if (!pWords || ((pWords->cWords + 1) % 32))
344 {
345 void *pv = realloc(pWords, sizeof(KSHELLWORDS) + ((pWords ? pWords->cWords : 0) + 32) * sizeof(KSHELLWORD));
346 if (!pv)
347 {
348 kshellWordsDestroy(pWords);
349 return NULL;
350 }
351 if (pWords)
352 pWords = pv;
353 else
354 {
355 pWords = pv;
356 pWords->cWords = 0;
357 }
358 }
359 pWords->aWords[pWords->cWords++] = word;
360 }
361
362 return pWords;
363}
364
365
366/**
367 * Destroys a words structure freeing it's memory.
368 * @param pWords Pointer to words structure to destroy.
369 */
370void kshellWordsDestroy(PKSHELLWORDS pWords)
371{
372 if (pWords)
373 {
374 int i;
375
376 for (i = 0; i < pWords->cWords; i++)
377 {
378 if (pWords->aWords[i].pszWord)
379 {
380 free(pWords->aWords[i].pszWord);
381 pWords->aWords[i].pszWord = NULL;
382 }
383 }
384
385 pWords->cWords = 0;
386 free(pWords);
387 }
388}
389
390
391/**
392 * Execute program.
393 *
394 * @returns program return code.
395 * @returns 1742 (KSHELL_ERROR_PROGRAM_NOT_FOUND) if program wasn't found.
396 * @returns 1743 (KSHELL_ERROR_COMMAND_TOO_LONG) if program commandline was too long.
397 *
398 * @param pszCmd Pointer to commandline.
399 * @param pWords Pointer to 1st word in pszCmd.
400 */
401int kshellCmdExecuteProgram(const char *pszCmd, PKSHELLWORDS pWords)
402{
403 return -1;
404}
405
406
407/*
408 *
409 * The commands are documented externally.
410 * (Bad idea btw!)
411 *
412 */
413
414
415int kshellCmdcopy(const char *pszCmd, PKSHELLWORDS pWords)
416{
417 return -1;
418}
419
420
421int kshellCmdcopytree(const char *pszCmd, PKSHELLWORDS pWords)
422{
423 return -1;
424}
425
426
427int kshellCmdrm(const char *pszCmd, PKSHELLWORDS pWords)
428{
429 return -1;
430}
431
432
433int kshellCmdrmtree(const char *pszCmd, PKSHELLWORDS pWords)
434{
435 return -1;
436}
437
438
439int kshellCmdchdir(const char *pszCmd, PKSHELLWORDS pWords)
440{
441 return -1;
442}
443
444
445int kshellCmdmkdir(const char *pszCmd, PKSHELLWORDS pWords)
446{
447 return -1;
448}
449
450
451int kshellCmdrmdir(const char *pszCmd, PKSHELLWORDS pWords)
452{
453 return -1;
454}
455
456
457int kshellCmdset(const char *pszCmd, PKSHELLWORDS pWords)
458{
459 return -1;
460}
461
462
463int kshellCmdunset(const char *pszCmd, PKSHELLWORDS pWords)
464{
465 return -1;
466}
467
468
469int kshellCmdpushenv(const char *pszCmd, PKSHELLWORDS pWords)
470{
471 return -1;
472}
473
474
475int kshellCmdpopenv(const char *pszCmd, PKSHELLWORDS pWords)
476{
477 return -1;
478}
479
480
481int kshellCmdecho(const char *pszCmd, PKSHELLWORDS pWords)
482{
483 return -1;
484}
485
486
487int kshellCmdwrite(const char *pszCmd, PKSHELLWORDS pWords)
488{
489 return -1;
490}
491
Note: See TracBrowser for help on using the repository browser.