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

Last change on this file since 46 was 44, checked in by bird, 22 years ago

Playing with the shell code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.3 KB
Line 
1/* $Id: kShell.c 44 2003-03-28 11:22: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/** @design kShell (Micro Shell)
27 *
28 * The micro shell provides the basic shell functionality kBuild need - no more,
29 * no less. It is intended to be as simple as possible.
30 *
31 * The shell commands are case sensitive - all lowercase.
32 *
33 * The shell environment variables are case sensitive or insensitive according to
34 * host os.
35 *
36 *
37 *
38 * @subsection Command Separators
39 *
40 * There is one command separator '&&'. This works like splitting the command line
41 * into several makefile lines. This splitting isn't done by the micro shell but
42 * the makefile interpreter.
43 *
44 * You might thing this is limiting, but no, you can use all the makefile command
45 * prefixes.
46 *
47 *
48 *
49 * @subsection Path Component Separator (/)
50 *
51 * The shell uses '/' as path component separator.
52 * For host OSes with the notion of drive letters or similar, ':' is
53 * used to separate the drive letter and the path.
54 *
55 *
56 *
57 * @subsection UNC paths
58 *
59 * For host OSes which supports UNC paths these are supported but for the chdir
60 * command.
61 *
62 * The Path Component Separator is still '/' for UNC paths.
63 *
64 *
65 *
66 * @subsection Wildchars
67 *
68 * '*' and '?' are accepted as wildchars.
69 *
70 * '*' means 0 or more characters. <br>
71 * '?' means 1 character.
72 *
73 * When the term 'pattern' is use in command description this means that
74 * wildchars are accepted.
75 *
76 *
77 *
78 * @subsection Quoting
79 *
80 * Use double quotes (") to quote filenames or executables containing spaces.
81 *
82 *
83 *
84 * @subsection Execute Program
85 *
86 * If the first, possibly quoted, word of a commandline if not found as an
87 * internal command will be tried executed. If no path it will be searched
88 * for in the PATH environment variable.
89 *
90 *
91 *
92 * @subsection Commands
93 *
94 * This section will describe the commands implemented by the shell.
95 *
96 *
97 *
98 * @subsubsection copy
99 * Copies one or more files to a target file or directory.
100 *
101 * <b>Syntax: copy <source file pattern> [more sources] <target> </b>
102 *
103 * Specify one or more source file patterns.
104 *
105 * Specify exactly one target. The target may be a directory or a file.
106 * If it's a file and multiple source files specified either thru pattern or
107 * multiple source file specifications, the target file will be a copy of the
108 * last one.
109 *
110 * The command fails if a source file isn't found. It also fails on read or
111 * write errors.
112 *
113 *
114 *
115 * @subsubsection copytree
116 * Copies one or more files to a target file or directory.
117 *
118 * <b>Syntax: copytree <source directory> <target directory> </b>
119 *
120 * Specify exactly one source directory.
121 *
122 * Specify exactly one target directory. The target directory path will be
123 * created if doesn't exist.
124 *
125 * The command fails if source directory isn't found. It also fails on read or
126 * write errors.
127 *
128 *
129 *
130 * @subsubsection rm
131 * Deletes one or more files.
132 *
133 * <b>Syntax: rm [file pattern] [more files] </b>
134 *
135 * Specify 0 or more file patterns for deletion.
136 *
137 * This command fails if it cannot delete a file. It will not fail if a file
138 * doesn't exist. It will neither fail if no files are specified.
139 *
140 *
141 *
142 * @subsubsection rmtree
143 * Deletes one or more directory trees.
144 *
145 * <b>Syntax: rmtree [directory pattern] [directories] </b>
146 *
147 * Specify 0 or more directory patterns for deletion.
148 *
149 * This command fails if it cannot delete a file or directory. It will not fail
150 * if a directory doesn't exist. It will neither fail if no files are specified.
151 *
152 *
153 *
154 * @subsubsection chdir
155 * Changes the current directory.
156 *
157 * This updates the .CWD macro to the new current directory path.
158 *
159 * <b>Syntax: chdir <directory> </b>
160 *
161 *
162 *
163 * @subsubsection mkdir
164 * Create directory.
165 *
166 * <b>Syntax: mkdir <directory> </b>
167 *
168 * Specify one directory to create.
169 *
170 *
171 *
172 * @subsubsection rmdir
173 * Remove directory.
174 *
175 * <b>Syntax: rmdir <directory> </b>
176 *
177 * Specify one directory to remove. The directory must be empty.
178 *
179 * This command failes if directory isn't empty. It will not fail if
180 * the directory doesn't exist.
181 *
182 *
183 *
184 * @subsubsection set
185 * Set environment variable.
186 *
187 * <b>Syntax: set <envvar>=<value> </b>
188 *
189 *
190 *
191 * @subsubsection unset
192 * Unset enviornment variable(s).
193 *
194 * <b>Syntax: unset <envvar pattern> [more envvars] </b>
195 *
196 * Specify on or more environment variable patterns.
197 *
198 *
199 *
200 * @subsubsection pushenv
201 * Pushes a set of environment variables onto the environment stack. The
202 * variables can later be popped back using the popenv command.
203 *
204 * If '*' is specified as pattern the complete enviornment is pushed and
205 * when popped it will <b>replace</b> the enviornment.
206 *
207 * <b>Syntax: pushenv <envvar pattern> [more envvars] </b>
208 * <b>Syntax: pushenv * </b>
209 *
210 *
211 *
212 * @subsubsection popenv
213 * Pop a set of environment variables from the environment stack. If a '*'
214 * push was done, we'll replace the enviornment with the variables poped off
215 * the stack.
216 *
217 * <b>Syntax: popenv </b>
218 *
219 *
220 *
221 */
222
223
224/*******************************************************************************
225* Defined Constants And Macros *
226*******************************************************************************/
227#define KSWORD_FLAGS_ESCAPE 1
228#define KSWORD_FLAGS_QUOTED 2
229#define KSWORD_FLAGS_PATTERN 4
230
231#define KSHELL_MAX_COMMAND 4096
232
233/**
234 * Test if this is an escapable character or not.
235 */
236#define KSHELL_ESCAPABLE(ch) ( (ch) == '"' \
237 || (ch) == '\'' \
238 || (ch) == '`' \
239 || (ch) == 'ï' \
240 )
241
242/**
243 * Test if this is a quote character or not.
244 */
245#define KSHELL_QUOTE(ch) ( (ch) == '"' \
246 || (ch) == '\'' \
247 || (ch) == '`' \
248 || (ch) == 'ï' \
249 )
250
251/**
252 * Test if this is a wildchar character or not.
253 */
254#define KSHELL_WILDCHAR(ch) ( (ch) == '*' || (ch) == '?' )
255
256/**
257 * the a default kShell slash.
258 */
259#define KSHELL_SLASH '/'
260
261/**
262 * Checks if the character is a slash or not.
263 */
264#define KSHELL_ISSLASH(ch) ( (ch) == KSHELL_SLASH )
265
266
267/*******************************************************************************
268* Header Files *
269*******************************************************************************/
270#include "kShell.h"
271#include <kLib/kLib.h>
272#include <kLib/kString.h>
273
274#include <string.h>
275#include <stdlib.h>
276#include <stdio.h>
277
278
279/*******************************************************************************
280* Global Variables *
281*******************************************************************************/
282typedef struct _kshellWord
283{
284 int fFlags;
285 char * pszWord;
286 unsigned cchWord;
287 const char *pszWordOrg;
288 unsigned cchWordOrg;
289} KSHELLWORD, *PKSHELLWORD;
290
291
292typedef struct _kshellWords
293{
294 unsigned cWords;
295 KSHELLWORD aWords[1];
296} KSHELLWORDS, *PKSHELLWORDS;
297
298
299/*******************************************************************************
300* Global Variables *
301*******************************************************************************/
302static const char *pszkshellCurDir = NULL;
303
304/*******************************************************************************
305* Internal Functions *
306*******************************************************************************/
307PKSHELLWORDS kshellWordsParse(const char *pszText, int cWords, PKSHELLWORDS pPrevWords);
308void kshellWordsDestroy(PKSHELLWORDS pWords);
309
310int kshellSyntaxError(const char *pszCmd, const char *pszMessage);
311int kshellError(const char *pszCmd, const char *pszMessage);
312
313int kshellCmd_copy(const char *pszCmd, PKSHELLWORDS pWords);
314int kshellCmd_copytree(const char *pszCmd, PKSHELLWORDS pWords);
315int kshellCmd_sync(const char *pszCmd, PKSHELLWORDS pWords);
316int kshellCmd_synctree(const char *pszCmd, PKSHELLWORDS pWords);
317int kshellCmd_rm(const char *pszCmd, PKSHELLWORDS pWords);
318int kshellCmd_rmtree(const char *pszCmd, PKSHELLWORDS pWords);
319int kshellCmd_chdir(const char *pszCmd, PKSHELLWORDS pWords);
320int kshellCmd_mkdir(const char *pszCmd, PKSHELLWORDS pWords);
321int kshellCmd_rmdir(const char *pszCmd, PKSHELLWORDS pWords);
322int kshellCmd_set(const char *pszCmd, PKSHELLWORDS pWords);
323int kshellCmd_unset(const char *pszCmd, PKSHELLWORDS pWords);
324int kshellCmd_pushenv(const char *pszCmd, PKSHELLWORDS pWords);
325int kshellCmd_popenv(const char *pszCmd, PKSHELLWORDS pWords);
326int kshellCmd_echo(const char *pszCmd, PKSHELLWORDS pWords);
327int kshellCmd_write(const char *pszCmd, PKSHELLWORDS pWords);
328int kshellCmd_ExecuteProgram(const char *pszCmd, PKSHELLWORDS pWords);
329
330
331
332/**
333 * Initiate the shell.
334 * Allow us to initiate globals.
335 *
336 * @returns 0 on success.
337 * @returns error code on error.
338 * @param fVerbose If set banner will be printed.
339 */
340int kshellInit(int fVerbose)
341{
342 if (fVerbose)
343 {
344 printf("\n"
345 "kShell v0.0.0\n"
346 "Copyright 2002 knut st. osmundsen <bird@anduin.net>\n"
347 "\n");
348 }
349 return 0;
350}
351
352
353/**
354 * Terminate the shell.
355 * Allow us to cleanup stuff.
356 */
357void kshellTerm(void)
358{
359 return;
360}
361
362
363/**
364 * Start interactive shell interface.
365 * This reads commands from stdin till 'exit' is encountered or end-of-file.
366 *
367 * @returns returncode of last command.
368 */
369int kshellInteractive(void)
370{
371 static char szCmd[KSHELL_MAX_COMMAND];
372 int rc = 0;
373
374 printf("kShell>");
375 fflush(stdout);
376 while (fgets(&szCmd[0], sizeof(szCmd), stdin))
377 {
378 char *pszEnd = &szCmd[strlen(&szCmd[0]) - 1];
379 while (pszEnd >= &szCmd[0] && (*pszEnd == '\n' || *pszEnd == '\r'))
380 *pszEnd-- = '\0';
381
382 if (!strcmp(&szCmd[0], "exit"))
383 break;
384
385 rc = kshellExecute(&szCmd[0]);
386 printf("kShell(rc=%d)>", rc);
387 fflush(stdout);
388 }
389
390 return rc;
391}
392
393/**
394 * Execute a shell command.
395 * @returns 0 on success if command.
396 * @returns Error code if command failed.
397 * @returns Return code from program.
398 * @returns 1742 (KSHELL_ERROR_PROGRAM_NOT_FOUND) if program wasn't found.
399 * @returns 1743 (KSHELL_ERROR_COMMAND_TOO_LONG) if program commandline was too long.
400 * @param pszCmd Command or program to execute.
401 */
402int kshellExecute(const char *pszCmd)
403{
404#define MAX_WORDS (~0)
405 static struct _kshellCommands
406 {
407 const char *pszCmd;
408 unsigned cWords; /* Number of words including the command it self. */
409 int (*pfnCmd)(const char *, PKSHELLWORDS);
410 } aCmds[] =
411 {
412 {"copy", MAX_WORDS, kshellCmd_copy},
413 {"copytree", 3, kshellCmd_copytree},
414 {"sync", MAX_WORDS, kshellCmd_sync},
415 {"synctree", 3, kshellCmd_synctree},
416 {"rm", MAX_WORDS, kshellCmd_rm},
417 {"rmtree", MAX_WORDS, kshellCmd_rmtree},
418 {"chdir", 2, kshellCmd_chdir},
419 {"mkdir", MAX_WORDS, kshellCmd_mkdir},
420 {"rmdir", MAX_WORDS, kshellCmd_rmdir},
421 {"set", 1, kshellCmd_set},
422 {"unset", MAX_WORDS, kshellCmd_unset},
423 {"pushenv", MAX_WORDS, kshellCmd_pushenv},
424 {"popenv", 1, kshellCmd_popenv},
425 {"echo", 2, kshellCmd_echo},
426 {"write", 2, kshellCmd_write},
427
428 /* last entry */
429 {"", 1, kshellCmd_ExecuteProgram}
430 };
431#undef MAX_WORDS
432
433 PKSHELLWORDS pWords;
434 int i;
435 int rc;
436
437
438 /*
439 * Parse out the first word.
440 */
441 pWords = kshellWordsParse(pszCmd, 1, NULL);
442 if (!pWords)
443 return KSHELL_ERROR_NOT_ENOUGH_MEMORY;
444 if (!pWords->cWords)
445 return 0;
446
447
448 /*
449 * Look for command.
450 * Note! the last entry is the default command (execute program).
451 */
452 for (i = 0; i < (sizeof(aCmds) / sizeof(aCmds[0])) - 1; i++)
453 {
454 if (!strcmp(aCmds[i].pszCmd, pWords->aWords[0].pszWord))
455 break;
456 }
457
458
459 /*
460 * Execute command.
461 */
462 if (aCmds[i].cWords > 1)
463 {
464 pWords = kshellWordsParse(pszCmd, aCmds[i].cWords, pWords);
465 if (!pWords)
466 return KSHELL_ERROR_NOT_ENOUGH_MEMORY;
467 }
468 rc = aCmds[i].pfnCmd(pszCmd, pWords);
469
470
471 return rc;
472}
473
474
475/**
476 * Parses words out of a string.
477 *
478 * @returns Pointer to a words structure.
479 * This must be destroy calling kshellWordsDestroy().
480 * @returns NULL on failure (out of memory).
481 * @param pszText Text string to parse.
482 * @param cWords Number of words to parse. Will stop after cWords.
483 * @param pPrevWords Pointer to structur of previosly parse words from pszText.
484 * Use this to continue parsing a string. The pPrevWords
485 * structure will be destroyed.
486 * If NULL we'll start from the begining.
487 */
488PKSHELLWORDS kshellWordsParse(const char *pszText, int cWords, PKSHELLWORDS pPrevWords)
489{
490 PKSHELLWORDS pWords = pPrevWords;
491
492 /*
493 * If previous work done, skip to end of that.
494 */
495 if (pPrevWords && pPrevWords->cWords)
496 {
497 pszText = pPrevWords->aWords[pPrevWords->cWords - 1].pszWordOrg
498 + pPrevWords->aWords[pPrevWords->cWords - 1].cchWordOrg;
499 cWords -= pPrevWords->cWords;
500 }
501
502 /*
503 * Parse loop
504 */
505 while (cWords-- > 0)
506 {
507 KSHELLWORD word = {0,0,0,0,0};
508 char chEnd = ' ';
509 char ch;
510
511 /*
512 * Skip blanks to find start of word.
513 */
514 while (*pszText == ' ' || *pszText == '\t')
515 pszText++;
516 if (!*pszText)
517 break;
518 word.pszWordOrg = pszText;
519
520
521 /*
522 * Quoted?
523 * Any possible quote!
524 */
525 if (KSHELL_QUOTE(*pszText))
526 {
527 chEnd = *pszText++;
528 word.fFlags |= KSWORD_FLAGS_QUOTED;
529 }
530
531
532 /*
533 * Find end of word and look for escape and pattern characters.
534 * We escape by doubling the character, not by slashing!
535 */
536 while ((ch = *pszText) != '\0' && (ch != chEnd || pszText[1] == chEnd))
537 {
538 if (ch == pszText[1] && KSHELL_ESCAPABLE(ch))
539 {
540 word.fFlags |= KSWORD_FLAGS_ESCAPE;
541 pszText++;
542 }
543 if (KSHELL_WILDCHAR(ch))
544 word.fFlags |= KSWORD_FLAGS_PATTERN;
545 pszText++;
546 }
547 if (word.fFlags & KSWORD_FLAGS_QUOTED)
548 pszText++;
549 word.cchWordOrg = pszText - word.pszWordOrg;
550
551
552 /*
553 * Make a copy of the word and unescape (if needed).
554 */
555 word.pszWord = malloc(word.cchWordOrg + 1);
556 if (!word.pszWord)
557 {
558 kshellWordsDestroy(pWords);
559 return NULL;
560 }
561
562 if (word.fFlags & KSWORD_FLAGS_ESCAPE)
563 {
564 int cch = word.cchWordOrg;
565 const char *pszSrc = word.pszWordOrg;
566 char * pszTrg = word.pszWord;
567 while (cch)
568 {
569 char ch;
570 if ((ch = *pszSrc) == pszSrc[1] && KSHELL_ESCAPABLE(ch))
571 pszSrc++;
572 *pszTrg++ = ch;
573 pszSrc++;
574 }
575 word.cchWord = pszTrg - word.pszWord;
576 *pszTrg = '\0';
577 }
578 else
579 {
580 if (word.fFlags & KSWORD_FLAGS_QUOTED)
581 {
582 word.cchWord = word.cchWordOrg - 2;
583 memcpy(word.pszWord, word.pszWordOrg + 1, word.cchWord);
584 }
585 else
586 {
587 word.cchWord = word.cchWordOrg;
588 memcpy(word.pszWord, word.pszWordOrg, word.cchWord);
589 }
590 word.pszWord[word.cchWord] = '\0';
591 }
592
593
594 /*
595 * Add to words structure.
596 */
597 if (!pWords || ((pWords->cWords + 1) % 32))
598 {
599 void *pv = realloc(pWords, sizeof(KSHELLWORDS) + ((pWords ? pWords->cWords : 0) + 32) * sizeof(KSHELLWORD));
600 if (!pv)
601 {
602 kshellWordsDestroy(pWords);
603 return NULL;
604 }
605 if (pWords)
606 pWords = pv;
607 else
608 {
609 pWords = pv;
610 pWords->cWords = 0;
611 }
612 }
613 pWords->aWords[pWords->cWords++] = word;
614 }
615
616 return pWords;
617}
618
619
620/**
621 * Destroys a words structure freeing it's memory.
622 * @param pWords Pointer to words structure to destroy.
623 */
624void kshellWordsDestroy(PKSHELLWORDS pWords)
625{
626 if (pWords)
627 {
628 int i;
629
630 for (i = 0; i < pWords->cWords; i++)
631 {
632 if (pWords->aWords[i].pszWord)
633 {
634 free(pWords->aWords[i].pszWord);
635 pWords->aWords[i].pszWord = NULL;
636 }
637 }
638
639 pWords->cWords = 0;
640 free(pWords);
641 }
642}
643
644
645/**
646 * Display an syntax message.
647 * @returns KSHELL_ERROR_SYNTAX_ERROR
648 * @param pszCmd The command name.
649 * @param pszMessage Message text.
650 */
651int kshellSyntaxError(const char *pszCmd, const char *pszMessage)
652{
653 fflush(stdout);
654 fprintf(stderr, "Syntax error while executing command '%s': %s\n", pszCmd, pszMessage);
655 return KSHELL_ERROR_SYNTAX_ERROR;
656}
657
658
659/**
660 * Display an generic message.
661 * @returns KSHELL_ERROR_SYNTAX_ERROR
662 * @param pszCmd The command name.
663 * @param pszMessage Message text.
664 */
665int kshellError(const char *pszCmd, const char *pszMessage)
666{
667 fflush(stdout);
668 fprintf(stderr, "Error while executing command '%s': %s\n", pszCmd, pszMessage);
669 return -1;
670}
671
672
673/**
674 * Execute program.
675 *
676 * @returns program return code.
677 * @returns 1742 (KSHELL_ERROR_PROGRAM_NOT_FOUND) if program wasn't found.
678 * @returns 1743 (KSHELL_ERROR_COMMAND_TOO_LONG) if program commandline was too long.
679 *
680 * @param pszCmd Pointer to commandline.
681 * @param pWords Pointer to 1st word in pszCmd.
682 */
683int kshellCmd_ExecuteProgram(const char *pszCmd, PKSHELLWORDS pWords)
684{
685 return -1;
686}
687
688
689/*
690 *
691 * The commands are documented externally.
692 * (Bad idea btw!)
693 *
694 */
695
696
697int kshellCmd_copy(const char *pszCmd, PKSHELLWORDS pWords)
698{
699 int iDst = pWords->cWords - 1; /* Last word is destination. */
700 KBOOL fDstDir = -1;
701 int iSrc;
702
703 /*
704 * Syntax validation.
705 */
706 if (pWords->cWords < 3)
707 return kshellSyntaxError("copy", "too few arguments.");
708
709 /*
710 * Figure out if the destion is a directory or file specification.
711 */
712 if (KSHELL_ISSLASH(pWords->aWords[iDst].pszWord[pWords->aWords[iDst].cchWord - 1]))
713 {
714 fDstDir = TRUE;
715 while (KSHELL_ISSLASH(pWords->aWords[iDst].pszWord[pWords->aWords[iDst].cchWord - 1]))
716 pWords->aWords[iDst].cchWord--;
717 pWords->aWords[iDst].pszWord[pWords->aWords[iDst].cchWord] = '\0';
718 }
719 else
720 fDstDir = kDirExist(pWords->aWords[iDst].pszWord);
721
722 /*
723 * Copy sources to destination.
724 */
725 for (iSrc = 1; iSrc < iDst && !rc; iSrc++)
726 {
727 if (pWords->aWords[iSrc].fFlags & KSWORD_FLAGS_PATTERN)
728 {
729 /*
730 *
731 */
732 }
733 else
734 { /*
735 * Construct destination name.
736 */
737 char *pszDst;
738 KBOOL fDstFree = FALSE;
739 if (fDstDir)
740 {
741 fDstFree = TRUE;
742 pszDst = malloc(pWords->aWords[iDst].cchWord + 1 + pWords->aWords[iSrc].cchWord + 1);
743 if (pszDst)
744 return KSHELL_ERROR_NOT_ENOUGH_MEMORY;
745 kMemCpy(pszDst, pWords->aWords[iDst].pszWord, pWords->aWords[iDst].cchWord);
746 pszDst[pWords->aWords[iDst].cchWord] = KSHELL_SLASH;
747 kMemCpy(pszDst + pWords->aWords[iDst].cchWord + 1,
748 pWords->aWords[iSrc].pszWord
749 pWords->aWords[iSrc].cchWord + 1);
750 }
751 else
752 pszDst = pWords->aWords[iDst].pszWord;
753
754 /*
755 * Do the copy.
756 */
757 rc = kFileCopy(pWords->aWords[iSrc].pszWord, pszDst);
758 if (rc)
759 {
760 kshellError("copy", "failed to copy '%s' to '%s' rc=%d.",
761 pWords->aWords[iSrc].pszWord,
762 pDst, rc);
763 }
764
765 if (fDstFree)
766 free(pszDst);
767 }
768 }
769
770 return -1;
771}
772
773
774int kshellCmd_copytree(const char *pszCmd, PKSHELLWORDS pWords)
775{
776 return -1;
777}
778
779
780int kshellCmd_sync(const char *pszCmd, PKSHELLWORDS pWords)
781{
782 return -1;
783}
784
785
786int kshellCmd_synctree(const char *pszCmd, PKSHELLWORDS pWords)
787{
788 return -1;
789}
790
791
792int kshellCmd_rm(const char *pszCmd, PKSHELLWORDS pWords)
793{
794 return -1;
795}
796
797
798int kshellCmd_rmtree(const char *pszCmd, PKSHELLWORDS pWords)
799{
800 return -1;
801}
802
803
804int kshellCmd_chdir(const char *pszCmd, PKSHELLWORDS pWords)
805{
806 return -1;
807}
808
809
810int kshellCmd_mkdir(const char *pszCmd, PKSHELLWORDS pWords)
811{
812 return -1;
813}
814
815
816int kshellCmd_rmdir(const char *pszCmd, PKSHELLWORDS pWords)
817{
818 return -1;
819}
820
821
822int kshellCmd_set(const char *pszCmd, PKSHELLWORDS pWords)
823{
824 return -1;
825}
826
827
828int kshellCmd_unset(const char *pszCmd, PKSHELLWORDS pWords)
829{
830 return -1;
831}
832
833
834int kshellCmd_pushenv(const char *pszCmd, PKSHELLWORDS pWords)
835{
836 return -1;
837}
838
839
840int kshellCmd_popenv(const char *pszCmd, PKSHELLWORDS pWords)
841{
842 return -1;
843}
844
845
846/** @subsubsection echo
847 * Prints a message to stdout.
848 *
849 * <b>Syntax: echo <level> <message>
850 *
851 * Level is verbosity level of the message. This is compared with the
852 * KBUILD_MSG_LEVEL environment variable. The message is suppressed if the
853 * level is lower that KBUILD_MSG_LEVEL.
854 *
855 * The message is printed word for word normalize with a single space between
856 * the words. It's therefore a good thing to quote the message.
857 *
858 * The message can be empty. Then a blank line will be printed.
859 */
860int kshellCmd_echo(const char *pszCmd, PKSHELLWORDS pWords)
861{
862 int rc = KSHELL_ERROR_SYNTAX_ERROR;
863
864 /*
865 * Get the message level from the message.
866 */
867 if (pWords->cWords >= 2)
868 {
869 unsigned uMsgLevel = kStrToUDef(pWords->aWords[1].pszWord, -2, 0);
870 if (uMsgLevel != -2)
871 {
872 if (uMsgLevel <= kEnvGetUDef("KBUILD_MSG_LEVEL", 0, 0))
873 {
874 /* output all the words forcing one space separation */
875 pWords = kshellWordsParse(pszCmd, -1, pWords);
876 if (pWords)
877 {
878 int i;
879 for (i = 2; i < pWords->cWords; i++)
880 fwrite(pWords->aWords[i].pszWord, pWords->aWords[i].cchWord, 1, stdout);
881 }
882
883 /* new line */
884 fputc('\n', stdout);
885 fflush(stdout);
886 }
887 }
888 else
889 kshellSyntaxError("echo", "invalid message level!");
890 }
891 else
892 kshellSyntaxError("echo", "requires at least one argument!");
893
894 return -1;
895}
896
897
898int kshellCmd_write(const char *pszCmd, PKSHELLWORDS pWords)
899{
900 return -1;
901}
902
903
904
Note: See TracBrowser for help on using the repository browser.