VirtualBox

source: kBuild/trunk/src/kShell/kShell.c@ 200

Last change on this file since 200 was 199, checked in by bird, 20 years ago

...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.7 KB
Line 
1/* $Id: kShell.c 199 2004-12-17 04:51:58Z 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 rmdir
164 * Remove directory.
165 *
166 * <b>Syntax: rmdir <directory> </b>
167 *
168 * Specify one directory to remove. The directory must be empty.
169 *
170 * This command failes if directory isn't empty. It will not fail if
171 * the directory doesn't exist.
172 *
173 *
174 *
175 * @subsubsection set
176 * Set environment variable.
177 *
178 * <b>Syntax: set <envvar>=<value> </b>
179 *
180 *
181 *
182 * @subsubsection unset
183 * Unset enviornment variable(s).
184 *
185 * <b>Syntax: unset <envvar pattern> [more envvars] </b>
186 *
187 * Specify on or more environment variable patterns.
188 *
189 *
190 *
191 * @subsubsection pushenv
192 * Pushes a set of environment variables onto the environment stack. The
193 * variables can later be popped back using the popenv command.
194 *
195 * If '*' is specified as pattern the complete enviornment is pushed and
196 * when popped it will <b>replace</b> the enviornment.
197 *
198 * <b>Syntax: pushenv <envvar pattern> [more envvars] </b>
199 * <b>Syntax: pushenv * </b>
200 *
201 *
202 *
203 * @subsubsection popenv
204 * Pop a set of environment variables from the environment stack. If a '*'
205 * push was done, we'll replace the enviornment with the variables poped off
206 * the stack.
207 *
208 * <b>Syntax: popenv </b>
209 *
210 *
211 *
212 */
213
214
215/*******************************************************************************
216* Defined Constants And Macros *
217*******************************************************************************/
218#define KSWORD_FLAGS_ESCAPE 1
219#define KSWORD_FLAGS_QUOTED 2
220#define KSWORD_FLAGS_PATTERN 4
221
222#define KSHELL_MAX_COMMAND 4096
223
224/**
225 * Test if this is an escapable character or not.
226 */
227#define KSHELL_ESCAPABLE(ch) ( (ch) == '"' \
228 || (ch) == '\'' \
229 || (ch) == '`' \
230 || (ch) == 'ï' \
231 )
232
233/**
234 * Test if this is a quote character or not.
235 */
236#define KSHELL_QUOTE(ch) ( (ch) == '"' \
237 || (ch) == '\'' \
238 || (ch) == '`' \
239 || (ch) == 'ï' \
240 )
241
242/**
243 * Test if this is a wildchar character or not.
244 */
245#define KSHELL_WILDCHAR(ch) ( (ch) == '*' || (ch) == '?' )
246
247/**
248 * the a default kShell slash.
249 */
250#define KSHELL_SLASH '/'
251
252/**
253 * Checks if the character is a slash or not.
254 */
255#define KSHELL_ISSLASH(ch) ( (ch) == KSHELL_SLASH )
256
257
258/*******************************************************************************
259* Header Files *
260*******************************************************************************/
261#include "kShell.h"
262#include <kLib/kLib.h>
263#include <kLib/kString.h>
264
265#include <string.h>
266#include <stdlib.h>
267#include <stdio.h>
268
269
270/*******************************************************************************
271* Global Variables *
272*******************************************************************************/
273typedef struct _kshellWord
274{
275 int fFlags;
276 char * pszWord;
277 unsigned cchWord;
278 const char *pszWordOrg;
279 unsigned cchWordOrg;
280} KSHELLWORD, *PKSHELLWORD;
281
282
283typedef struct _kshellWords
284{
285 unsigned cWords;
286 KSHELLWORD aWords[1];
287} KSHELLWORDS, *PKSHELLWORDS;
288
289
290/*******************************************************************************
291* Global Variables *
292*******************************************************************************/
293static const char *pszkshellCurDir = NULL;
294
295/*******************************************************************************
296* Internal Functions *
297*******************************************************************************/
298PKSHELLWORDS kshellWordsParse(const char *pszText, int cWords, PKSHELLWORDS pPrevWords);
299void kshellWordsDestroy(PKSHELLWORDS pWords);
300
301int kshellSyntaxError(const char *pszCmd, const char *pszMessage, ...);
302int kshellError(const char *pszCmd, const char *pszMessage, ...);
303
304int kshellCmd_echo(const char *pszCmd, PKSHELLWORDS pWords);
305int kshellCmd_log(const char *pszCmd, PKSHELLWORDS pWords);
306int kshellCmd_copy(const char *pszCmd, PKSHELLWORDS pWords);
307int kshellCmd_copytree(const char *pszCmd, PKSHELLWORDS pWords);
308int kshellCmd_sync(const char *pszCmd, PKSHELLWORDS pWords);
309int kshellCmd_synctree(const char *pszCmd, PKSHELLWORDS pWords);
310int kshellCmd_rm(const char *pszCmd, PKSHELLWORDS pWords);
311int kshellCmd_rmtree(const char *pszCmd, PKSHELLWORDS pWords);
312int kshellCmd_chdir(const char *pszCmd, PKSHELLWORDS pWords);
313int kshellCmd_mkdir(const char *pszCmd, PKSHELLWORDS pWords);
314int kshellCmd_rmdir(const char *pszCmd, PKSHELLWORDS pWords);
315int kshellCmd_set(const char *pszCmd, PKSHELLWORDS pWords);
316int kshellCmd_unset(const char *pszCmd, PKSHELLWORDS pWords);
317int kshellCmd_pushenv(const char *pszCmd, PKSHELLWORDS pWords);
318int kshellCmd_popenv(const char *pszCmd, PKSHELLWORDS pWords);
319int kshellCmd_write(const char *pszCmd, PKSHELLWORDS pWords);
320int kshellCmd_ExecuteProgram(const char *pszCmd, PKSHELLWORDS pWords);
321
322
323
324/**
325 * Initiate the shell.
326 * Allow us to initiate globals.
327 *
328 * @returns 0 on success.
329 * @returns error code on error.
330 * @param fVerbose If set banner will be printed.
331 */
332int kshellInit(int fVerbose)
333{
334 if (fVerbose)
335 {
336 printf("\n"
337 "kShell v0.0.0\n"
338 "Copyright 2002 knut st. osmundsen <bird@anduin.net>\n"
339 "\n");
340 }
341 return 0;
342}
343
344
345/**
346 * Terminate the shell.
347 * Allow us to cleanup stuff.
348 */
349void kshellTerm(void)
350{
351 return;
352}
353
354
355/**
356 * Start interactive shell interface.
357 * This reads commands from stdin till 'exit' is encountered or end-of-file.
358 *
359 * @returns returncode of last command.
360 */
361int kshellInteractive(void)
362{
363 static char szCmd[KSHELL_MAX_COMMAND];
364 int rc = 0;
365
366 printf("kShell>");
367 fflush(stdout);
368 while (fgets(&szCmd[0], sizeof(szCmd), stdin))
369 {
370 char *pszEnd = &szCmd[strlen(&szCmd[0]) - 1];
371 while (pszEnd >= &szCmd[0] && (*pszEnd == '\n' || *pszEnd == '\r'))
372 *pszEnd-- = '\0';
373
374 if (!strcmp(&szCmd[0], "exit"))
375 break;
376
377 rc = kshellExecute(&szCmd[0]);
378 printf("kShell(rc=%d)>", rc);
379 fflush(stdout);
380 }
381
382 return rc;
383}
384
385/**
386 * Execute a shell command.
387 * @returns 0 on success if command.
388 * @returns Error code if command failed.
389 * @returns Return code from program.
390 * @returns 1742 (KSHELL_ERROR_PROGRAM_NOT_FOUND) if program wasn't found.
391 * @returns 1743 (KSHELL_ERROR_COMMAND_TOO_LONG) if program commandline was too long.
392 * @param pszCmd Command or program to execute.
393 */
394int kshellExecute(const char *pszCmd)
395{
396#define MAX_WORDS (~0)
397 static struct _kshellCommands
398 {
399 const char *pszCmd;
400 unsigned cWords; /* Number of words including the command it self. */
401 int (*pfnCmd)(const char *, PKSHELLWORDS);
402 } aCmds[] =
403 {
404 {"echo", MAX_WORDS, kshellCmd_echo},
405 {"log", 2, kshellCmd_log},
406 {"copy", MAX_WORDS, kshellCmd_copy},
407 {"copytree", 3, kshellCmd_copytree},
408 {"sync", MAX_WORDS, kshellCmd_sync},
409 {"synctree", 3, kshellCmd_synctree},
410 {"rm", MAX_WORDS, kshellCmd_rm},
411 {"rmtree", MAX_WORDS, kshellCmd_rmtree},
412 {"chdir", 2, kshellCmd_chdir},
413 {"mkdir", MAX_WORDS, kshellCmd_mkdir},
414 {"rmdir", MAX_WORDS, kshellCmd_rmdir},
415 {"set", 1, kshellCmd_set},
416 {"unset", MAX_WORDS, kshellCmd_unset},
417 {"pushenv", MAX_WORDS, kshellCmd_pushenv},
418 {"popenv", 1, kshellCmd_popenv},
419 {"write", 2, kshellCmd_write},
420
421 /* last entry */
422 {"", 1, kshellCmd_ExecuteProgram}
423 };
424#undef MAX_WORDS
425
426 PKSHELLWORDS pWords;
427 int i;
428 int rc;
429
430
431 /*
432 * Parse out the first word.
433 */
434 pWords = kshellWordsParse(pszCmd, 1, NULL);
435 if (!pWords)
436 return KSHELL_ERROR_NOT_ENOUGH_MEMORY;
437 if (!pWords->cWords)
438 return 0;
439
440
441 /*
442 * Look for command.
443 * Note! the last entry is the default command (execute program).
444 */
445 for (i = 0; i < (sizeof(aCmds) / sizeof(aCmds[0])) - 1; i++)
446 {
447 if (!strcmp(aCmds[i].pszCmd, pWords->aWords[0].pszWord))
448 break;
449 }
450
451
452 /*
453 * Execute command.
454 */
455 if (aCmds[i].cWords > 1)
456 {
457 pWords = kshellWordsParse(pszCmd, aCmds[i].cWords, pWords);
458 if (!pWords)
459 return KSHELL_ERROR_NOT_ENOUGH_MEMORY;
460 }
461 rc = aCmds[i].pfnCmd(pszCmd, pWords);
462
463
464 return rc;
465}
466
467
468/**
469 * Parses words out of a string.
470 *
471 * @returns Pointer to a words structure.
472 * This must be destroy calling kshellWordsDestroy().
473 * @returns NULL on failure (out of memory).
474 * @param pszText Text string to parse.
475 * @param cWords Number of words to parse. Will stop after cWords.
476 * @param pPrevWords Pointer to structur of previosly parse words from pszText.
477 * Use this to continue parsing a string. The pPrevWords
478 * structure will be destroyed.
479 * If NULL we'll start from the begining.
480 */
481PKSHELLWORDS kshellWordsParse(const char *pszText, int cWords, PKSHELLWORDS pPrevWords)
482{
483 PKSHELLWORDS pWords = pPrevWords;
484
485 /*
486 * If previous work done, skip to end of that.
487 */
488 if (pPrevWords && pPrevWords->cWords)
489 {
490 pszText = pPrevWords->aWords[pPrevWords->cWords - 1].pszWordOrg
491 + pPrevWords->aWords[pPrevWords->cWords - 1].cchWordOrg;
492 cWords -= pPrevWords->cWords;
493 }
494
495 /*
496 * Parse loop
497 */
498 while (cWords-- > 0)
499 {
500 KSHELLWORD word = {0,0,0,0,0};
501 char chEnd = ' ';
502 char ch;
503
504 /*
505 * Skip blanks to find start of word.
506 */
507 while (*pszText == ' ' || *pszText == '\t')
508 pszText++;
509 if (!*pszText)
510 break;
511 word.pszWordOrg = pszText;
512
513
514 /*
515 * Quoted?
516 * Any possible quote!
517 */
518 if (KSHELL_QUOTE(*pszText))
519 {
520 chEnd = *pszText++;
521 word.fFlags |= KSWORD_FLAGS_QUOTED;
522 }
523
524
525 /*
526 * Find end of word and look for escape and pattern characters.
527 * We escape by doubling the character, not by slashing!
528 */
529 while ((ch = *pszText) != '\0' && (ch != chEnd || pszText[1] == chEnd))
530 {
531 if (ch == pszText[1] && KSHELL_ESCAPABLE(ch))
532 {
533 word.fFlags |= KSWORD_FLAGS_ESCAPE;
534 pszText++;
535 }
536 if (KSHELL_WILDCHAR(ch))
537 word.fFlags |= KSWORD_FLAGS_PATTERN;
538 pszText++;
539 }
540 if (word.fFlags & KSWORD_FLAGS_QUOTED)
541 pszText++;
542 word.cchWordOrg = pszText - word.pszWordOrg;
543
544
545 /*
546 * Make a copy of the word and unescape (if needed).
547 */
548 word.pszWord = malloc(word.cchWordOrg + 1);
549 if (!word.pszWord)
550 {
551 kshellWordsDestroy(pWords);
552 return NULL;
553 }
554
555 if (word.fFlags & KSWORD_FLAGS_ESCAPE)
556 {
557 int cch = word.cchWordOrg;
558 const char *pszSrc = word.pszWordOrg;
559 char * pszTrg = word.pszWord;
560 while (cch)
561 {
562 char ch;
563 if ((ch = *pszSrc) == pszSrc[1] && KSHELL_ESCAPABLE(ch))
564 pszSrc++;
565 *pszTrg++ = ch;
566 pszSrc++;
567 }
568 word.cchWord = pszTrg - word.pszWord;
569 *pszTrg = '\0';
570 }
571 else
572 {
573 if (word.fFlags & KSWORD_FLAGS_QUOTED)
574 {
575 word.cchWord = word.cchWordOrg - 2;
576 memcpy(word.pszWord, word.pszWordOrg + 1, word.cchWord);
577 }
578 else
579 {
580 word.cchWord = word.cchWordOrg;
581 memcpy(word.pszWord, word.pszWordOrg, word.cchWord);
582 }
583 word.pszWord[word.cchWord] = '\0';
584 }
585
586
587 /*
588 * Add to words structure.
589 */
590 if (!pWords || ((pWords->cWords + 1) % 32))
591 {
592 void *pv = realloc(pWords, sizeof(KSHELLWORDS) + ((pWords ? pWords->cWords : 0) + 32) * sizeof(KSHELLWORD));
593 if (!pv)
594 {
595 kshellWordsDestroy(pWords);
596 return NULL;
597 }
598 if (pWords)
599 pWords = pv;
600 else
601 {
602 pWords = pv;
603 pWords->cWords = 0;
604 }
605 }
606 pWords->aWords[pWords->cWords++] = word;
607 }
608
609 return pWords;
610}
611
612
613/**
614 * Destroys a words structure freeing it's memory.
615 * @param pWords Pointer to words structure to destroy.
616 */
617void kshellWordsDestroy(PKSHELLWORDS pWords)
618{
619 if (pWords)
620 {
621 int i;
622
623 for (i = 0; i < pWords->cWords; i++)
624 {
625 if (pWords->aWords[i].pszWord)
626 {
627 free(pWords->aWords[i].pszWord);
628 pWords->aWords[i].pszWord = NULL;
629 }
630 }
631
632 pWords->cWords = 0;
633 free(pWords);
634 }
635}
636
637
638/**
639 * Display an syntax message.
640 * @returns KSHELL_ERROR_SYNTAX_ERROR
641 * @param pszCmd The command name.
642 * @param pszMessage Message text.
643 */
644int kshellSyntaxError(const char *pszCmd, const char *pszMessage, ...)
645{
646 va_list args;
647 fflush(stdout);
648 fprintf(stderr, "Syntax error in '%s': ", pszCmd);
649 va_start(args, pszMessage);
650 vfprintf(stderr, pszMessage, args);
651 va_end(args);
652 fputs("\n", stderr);
653 return KSHELL_ERROR_SYNTAX_ERROR;
654}
655
656
657/**
658 * Display an generic message.
659 * @returns KSHELL_ERROR_SYNTAX_ERROR
660 * @param pszCmd The command name.
661 * @param pszMessage Message text.
662 */
663int kshellError(const char *pszCmd, const char *pszMessage, ...)
664{
665 va_list args;
666 fflush(stdout);
667
668 fprintf(stderr, "Error while '%s': ", pszCmd);
669 va_start(args, pszMessage);
670 vfprintf(stderr, pszMessage, args);
671 va_end(args);
672 fputs("\n", stderr);
673 return -1;
674}
675
676
677/**
678 * Execute program.
679 *
680 * @returns program return code.
681 * @returns 1742 (KSHELL_ERROR_PROGRAM_NOT_FOUND) if program wasn't found.
682 * @returns 1743 (KSHELL_ERROR_COMMAND_TOO_LONG) if program commandline was too long.
683 *
684 * @param pszCmd Pointer to commandline.
685 * @param pWords Pointer to 1st word in pszCmd.
686 */
687int kshellCmd_ExecuteProgram(const char *pszCmd, PKSHELLWORDS pWords)
688{
689 return -1;
690}
691
692
693
694/*
695 *
696 * The commands are documented externally.
697 * (Bad idea btw!)
698 *
699 */
700
701
702/** @subsubsection echo
703 * Prints a message to stdout.
704 *
705 * <b>Syntax: echo <level> <message>
706 *
707 * The message is printed word for word normalize with a single space between
708 * the words. It's therefore a good thing to quote the message.
709 *
710 * The message can be empty. Then a blank line will be printed.
711 */
712int kshellCmd_echo(const char *pszCmd, PKSHELLWORDS pWords)
713{
714 int i;
715
716 /* output all the words forcing one space separation */
717 for (i = 2; i < pWords->cWords; i++)
718 {
719 if (i != 2)
720 fputc(' ', stdout);
721 fwrite(pWords->aWords[i].pszWord, pWords->aWords[i].cchWord, 1, stdout);
722 }
723
724 /* new line */
725 fputc('\n', stdout);
726 fflush(stdout);
727
728 return 0;
729}
730
731
732/** @subsubsection log
733 * Prints a message to stdout.
734 *
735 * <b>Syntax: log <level> <message>
736 *
737 * Level is verbosity level of the message. This is compared with the
738 * KBUILD_MSG_LEVEL environment variable. The message is suppressed if the
739 * level is lower that KBUILD_MSG_LEVEL.
740 *
741 * The message is printed word for word normalize with a single space between
742 * the words. It's therefore a good thing to quote the message.
743 *
744 * The message can be empty. Then a blank line will be printed.
745 */
746int kshellCmd_log(const char *pszCmd, PKSHELLWORDS pWords)
747{
748 int rc = KSHELL_ERROR_SYNTAX_ERROR;
749
750 /*
751 * Get the message level from the message.
752 */
753 if (pWords->cWords >= 2)
754 {
755 unsigned uMsgLevel = kStrToUDef(pWords->aWords[1].pszWord, -2, 0);
756 if (uMsgLevel != -2)
757 {
758 if (uMsgLevel <= kEnvGetUDef("KBUILD_MSG_LEVEL", 0, 0))
759 {
760 /* output all the words forcing one space separation */
761 pWords = kshellWordsParse(pszCmd, -1, pWords);
762 if (pWords)
763 {
764 int i;
765 for (i = 2; i < pWords->cWords; i++)
766 {
767 if (i != 2)
768 fputc(' ', stdout);
769 fwrite(pWords->aWords[i].pszWord, pWords->aWords[i].cchWord, 1, stdout);
770 }
771 }
772
773 /* new line */
774 fputc('\n', stdout);
775 fflush(stdout);
776 }
777 }
778 else
779 kshellSyntaxError("log", "invalid message level!");
780 }
781 else
782 kshellSyntaxError("log", "requires at least one argument!");
783
784 return -1;
785}
786
787
788
789int kshellCmd_copy(const char *pszCmd, PKSHELLWORDS pWords)
790{
791 int iDst = pWords->cWords - 1; /* Last word is destination. */
792 KBOOL fDstDir = -1;
793 int iSrc;
794 int rc;
795
796 /*
797 * Syntax validation.
798 */
799 if (pWords->cWords < 3)
800 return kshellSyntaxError("copy", "too few arguments.");
801
802 /*
803 * Figure out if the destion is a directory or file specification.
804 */
805 if (KSHELL_ISSLASH(pWords->aWords[iDst].pszWord[pWords->aWords[iDst].cchWord - 1]))
806 {
807 fDstDir = TRUE;
808 while (KSHELL_ISSLASH(pWords->aWords[iDst].pszWord[pWords->aWords[iDst].cchWord - 1]))
809 pWords->aWords[iDst].cchWord--;
810 pWords->aWords[iDst].pszWord[pWords->aWords[iDst].cchWord] = '\0';
811 }
812 else
813 fDstDir = kDirExist(pWords->aWords[iDst].pszWord);
814
815 /*
816 * Copy sources to destination.
817 */
818 for (iSrc = 1, rc = 0; iSrc < iDst && !rc; iSrc++)
819 {
820 if (pWords->aWords[iSrc].fFlags & KSWORD_FLAGS_PATTERN)
821 {
822 /*
823 *
824 */
825 }
826 else
827 { /*
828 * Construct destination name.
829 */
830 char *pszDst;
831 KBOOL fDstFree = FALSE;
832 if (fDstDir)
833 {
834 fDstFree = TRUE;
835 pszDst = malloc(pWords->aWords[iDst].cchWord + 1 + pWords->aWords[iSrc].cchWord + 1);
836 if (pszDst)
837 return KSHELL_ERROR_NOT_ENOUGH_MEMORY;
838 kMemCpy(pszDst, pWords->aWords[iDst].pszWord, pWords->aWords[iDst].cchWord);
839 pszDst[pWords->aWords[iDst].cchWord] = KSHELL_SLASH;
840 kMemCpy(pszDst + pWords->aWords[iDst].cchWord + 1,
841 pWords->aWords[iSrc].pszWord,
842 pWords->aWords[iSrc].cchWord + 1);
843 }
844 else
845 pszDst = pWords->aWords[iDst].pszWord;
846
847 /*
848 * Do the copy.
849 */
850 #if 0 /* @todo implement this */
851 rc = kFileCopy(pWords->aWords[iSrc].pszWord, pszDst);
852 #endif
853 if (rc)
854 {
855 kshellError("copy", "failed to copy '%s' to '%s' rc=%d.",
856 pWords->aWords[iSrc].pszWord, pszDst, rc);
857 }
858
859 if (fDstFree)
860 free(pszDst);
861 }
862 }
863
864 return -1;
865}
866
867
868int kshellCmd_copytree(const char *pszCmd, PKSHELLWORDS pWords)
869{
870 return -1;
871}
872
873
874int kshellCmd_sync(const char *pszCmd, PKSHELLWORDS pWords)
875{
876 return -1;
877}
878
879
880int kshellCmd_synctree(const char *pszCmd, PKSHELLWORDS pWords)
881{
882 return -1;
883}
884
885
886int kshellCmd_rm(const char *pszCmd, PKSHELLWORDS pWords)
887{
888 return -1;
889}
890
891
892int kshellCmd_rmtree(const char *pszCmd, PKSHELLWORDS pWords)
893{
894 return -1;
895}
896
897
898int kshellCmd_chdir(const char *pszCmd, PKSHELLWORDS pWords)
899{
900 return -1;
901}
902
903/** @subsubsection mkdir
904 * Create directory.
905 *
906 * <b>Syntax: mkdir <directory> [directory2[..]] </b>
907 *
908 * Specify zero or more directories to create. Necessary parent directories
909 * will be created.
910 *
911 * If the directory exist no error is reported.
912 * The command fails on the first failure, processing directories left to right.
913 *
914 * Mkdir is hence not suitable for making locks. Should such be needed a
915 * dedicated command should be created for that purpose.
916 *
917 */
918int kshellCmd_mkdir(const char *pszCmd, PKSHELLWORDS pWords)
919{
920 int rc = 0;
921 int i;
922
923 for (i = 1; i < pWords->cWords; i++)
924 {
925 /*
926 * Allocate a buffer for canonizing the directory name and work in.
927 */
928 char *pszDir = kHeapAlloc(pWords->aWords[i].cchWord + KFILE_LENGTH);
929 if (pszDir)
930 {
931 /*
932 * Canonify the path to local default slash etc.
933 */
934 rc = kPathCanonifyEx(pWords->aWords[i].pszWord, NULL, KSHELL_SLASH, 0, psz);
935 if (!rc)
936 {
937 kFileState
938 /*
939 * Now we will enumerate the path towards the root and
940 * find the first existing directory. We need the mode
941 * of that directory.
942 */
943
944 char *psz =
945 }
946 }
947
948 rc = kDirCreate(
949 }
950
951 return rc;
952}
953
954
955int kshellCmd_rmdir(const char *pszCmd, PKSHELLWORDS pWords)
956{
957 return -1;
958}
959
960
961int kshellCmd_set(const char *pszCmd, PKSHELLWORDS pWords)
962{
963 return -1;
964}
965
966
967int kshellCmd_unset(const char *pszCmd, PKSHELLWORDS pWords)
968{
969 return -1;
970}
971
972
973int kshellCmd_pushenv(const char *pszCmd, PKSHELLWORDS pWords)
974{
975 return -1;
976}
977
978
979int kshellCmd_popenv(const char *pszCmd, PKSHELLWORDS pWords)
980{
981 return -1;
982}
983
984
985
986
987int kshellCmd_write(const char *pszCmd, PKSHELLWORDS pWords)
988{
989 return -1;
990}
991
992
993
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette