6f74ab50434f29ee53e2d41396de4660acda2aa8
[reactos.git] / subsys / system / cmd / internal.c
1 /*
2  *  INTERNAL.C - command.com internal commands.
3  *
4  *
5  *  History:
6  *
7  *  17/08/94 (Tim Norman)
8  *    started.
9  *
10  *  08/08/95 (Matt Rains)
11  *    i have cleaned up the source code. changes now bring this source into
12  *    guidelines for recommended programming practice.
13  *
14  *  cd()
15  *    started.
16  *
17  *  dir()
18  *    i have added support for file attributes to the DIR() function. the
19  *    routine adds "d" (directory) and "r" (read only) output. files with the
20  *    system attribute have the filename converted to lowercase. files with
21  *    the hidden attribute are not displayed.
22  *
23  *    i have added support for directorys. now if the directory attribute is
24  *    detected the file size if replaced with the string "<dir>".
25  *
26  *  ver()
27  *    started.
28  *
29  *  md()
30  *    started.
31  *
32  *  rd()
33  *    started.
34  *
35  *  del()
36  *    started.
37  *
38  *  does not support wildcard selection.
39  *
40  *  todo: add delete directory support.
41  *        add recursive directory delete support.
42  *
43  *  ren()
44  *    started.
45  *
46  *  does not support wildcard selection.
47  *
48  *    todo: add rename directory support.
49  *
50  *  a general structure has been used for the cd, rd and md commands. this
51  *  will be better in the long run. it is too hard to maintain such diverse
52  *  functions when you are involved in a group project like this.
53  *
54  *  12/14/95 (Tim Norman)
55  *    fixed DIR so that it will stick \*.* if a directory is specified and
56  *    that it will stick on .* if a file with no extension is specified or
57  *    *.* if it ends in a \
58  *
59  *  1/6/96 (Tim Norman)
60  *    added an isatty call to DIR so it won't prompt for keypresses unless
61  *    stdin and stdout are the console.
62  *
63  *    changed parameters to be mutually consistent to make calling the
64  *    functions easier
65  *
66  *  rem()
67  *    started.
68  *
69  *  doskey()
70  *    started.
71  *
72  *    01/22/96 (Oliver Mueller)
73  *        error messages are now handled by perror.
74  *
75  *    02/05/96 (Tim Norman)
76  *        converted all functions to accept first/rest parameters
77  *
78  *    07/26/96 (Tim Norman)
79  *        changed return values to int instead of void
80  *
81  *        path() started.
82  *
83  *    12/23/96 (Aaron Kaufman)
84  *        rewrote dir() to mimic MS-DOS's dir
85  *
86  *    01/28/97 (Tim Norman)
87  *        cleaned up Aaron's DIR code
88  *
89  *    06/13/97 (Tim Norman)
90  *        moved DIR code to dir.c
91  *        re-implemented Aaron's DIR code
92  *
93  *    06/14/97 (Steffan Kaiser)
94  *        ctrl-break handling
95  *        bug fixes
96  *
97  *    27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
98  *        added config.h include
99  *
100  *    03-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
101  *        Replaced DOS calls by Win32 calls.
102  *
103  *    08-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
104  *        Added help texts ("/?").
105  *
106  *    18-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
107  *        Added support for quoted arguments (cd "program files").
108  *
109  *    07-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
110  *        Clean up.
111  *
112  *    26-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
113  *        Replaced remaining CRT io functions by Win32 io functions.
114  *        Unicode safe!
115  *
116  *    30-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
117  *        Added "cd -" feature. Changes to the previous directory.
118  *
119  *    15-Mar-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
120  *        Fixed bug in "cd -" feature. If the previous directory was a root
121  *        directory, it was ignored.
122  *
123  *    23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
124  *        Improved chdir/cd command.
125  */
126
127 #include "config.h"
128
129 #include <windows.h>
130 #include <tchar.h>
131 #include <string.h>
132 #include <stdlib.h>
133 #include <ctype.h>
134
135 #include "cmd.h"
136
137
138 #ifdef INCLUDE_CMD_CHDIR
139
140 static LPTSTR lpLastPath;
141
142
143 VOID InitLastPath (VOID)
144 {
145         lpLastPath = NULL;
146 }
147
148
149 VOID FreeLastPath (VOID)
150 {
151         if (lpLastPath)
152                 free (lpLastPath);
153 }
154
155 /*
156  * CD / CHDIR
157  *
158  */
159 INT cmd_chdir (LPTSTR cmd, LPTSTR param)
160 {
161         LPTSTR dir;             /* pointer to the directory to change to */
162         LPTSTR lpOldPath;
163         LPTSTR endofstring; /* pointer to the null character in the directory to change to */
164         LPTSTR lastquote; /* pointer to the last quotation mark in the directory to change to */
165
166         /*Should we better declare a variable containing _tsclen(dir) ? It's used a few times,
167           but on the other hand paths are generally not very long*/
168
169         if (!_tcsncmp (param, _T("/?"), 2))
170         {
171                 ConOutPuts (_T("Changes the current directory or displays it's name\n\n"
172                                            "CHDIR [drive:][path]\n"
173                                            "CHDIR[..|-]\n"
174                                            "CD [drive:][path]\n"
175                                            "CD[..|-]\n\n"
176                                            "  ..   parent directory\n"
177                                            "  -    previous directory\n\n"
178                                            "Type CD drive: to display the current directory on the specified drive.\n"
179                                            "Type CD without a parameter to display the current drive and directory."));
180                 return 0;
181         }
182
183         /* The whole param string is our parameter these days. The only thing we do is eliminating every quotation mark */
184         /* Is it safe to change the characters param is pointing to? I presume it is, as there doesn't seem to be any
185         post-processing of it after the function call (what would that accomplish?) */
186
187         dir=param;
188         endofstring=dir+_tcslen(dir);
189
190         while ((lastquote = _tcsrchr(dir,'\"')))
191         {
192                 endofstring--;
193                 memmove(lastquote,lastquote+1,endofstring-lastquote);
194                 *endofstring=_T('\0');
195         }
196
197         /* if doing a CD and no parameters given, print out current directory */
198         if (!dir || !dir[0])
199         {
200                 TCHAR szPath[MAX_PATH];
201
202                 GetCurrentDirectory (MAX_PATH, szPath);
203
204                 ConOutPuts (szPath);
205
206
207                 return 0;
208         }
209
210         if (dir && _tcslen (dir) == 1 && *dir == _T('-'))
211         {
212                 if (lpLastPath)
213                         dir = lpLastPath;
214                 else
215                         return 0;
216         }
217         else if (dir && _tcslen (dir)==2 && dir[1] == _T(':'))
218         {
219                 TCHAR szRoot[3] = _T("A:");
220                 TCHAR szPath[MAX_PATH];
221
222                 szRoot[0] = _totupper (dir[0]);
223                 GetFullPathName (szRoot, MAX_PATH, szPath, NULL);
224
225                 /* PathRemoveBackslash */
226                 if (_tcslen (szPath) > 3)
227                 {
228                         LPTSTR p = _tcsrchr (szPath, _T('\\'));
229                         *p = _T('\0');
230                 }
231
232                 ConOutPuts (szPath);
233
234
235                 return 0;
236         }
237
238         /* remove trailing \ if any, but ONLY if dir is not the root dir */
239         if (_tcslen (dir) > 3 && dir[_tcslen (dir) - 1] == _T('\\'))
240                 dir[_tcslen(dir) - 1] = _T('\0');
241
242
243         /* store current directory */
244         lpOldPath = (LPTSTR)malloc (MAX_PATH * sizeof(TCHAR));
245         GetCurrentDirectory (MAX_PATH, lpOldPath);
246
247         if (!SetCurrentDirectory (dir))
248         {
249                 ErrorMessage (GetLastError(), _T("CD"));
250
251                 /* throw away current directory */
252                 free (lpOldPath);
253                 lpOldPath = NULL;
254
255                 return 1;
256         }
257         else
258         {
259                 GetCurrentDirectory(MAX_PATH, dir);
260                 if (dir[0]!=lpOldPath[0])
261                 {
262                         SetCurrentDirectory(lpOldPath);
263                         free(lpOldPath);
264                 }
265                 else
266                 {
267                         if (lpLastPath)
268                                 free (lpLastPath);
269                         lpLastPath = lpOldPath;
270                 }
271         }
272
273
274         return 0;
275 }
276 #endif
277
278
279
280 #ifdef INCLUDE_CMD_MKDIR
281 /*
282  * MD / MKDIR
283  *
284  */
285 INT cmd_mkdir (LPTSTR cmd, LPTSTR param)
286 {
287         LPTSTR dir;             /* pointer to the directory to change to */
288         LPTSTR place;   /* used to search for the \ when no space is used */
289         LPTSTR *p = NULL;
290         INT argc;
291
292
293         if (!_tcsncmp (param, _T("/?"), 2))
294         {
295                 ConOutPuts (_T("Creates a directory.\n\n"
296                                            "MKDIR [drive:]path\nMD [drive:]path"));
297                 return 0;
298         }
299
300
301         /* check if there is no space between the command and the path */
302         if (param[0] == _T('\0'))
303         {
304                 /* search for the \ or . so that both short & long names will work */
305                 for (place = cmd; *place; place++)
306                         if (*place == _T('.') || *place == _T('\\'))
307                                 break;
308
309                 if (*place)
310                         dir = place;
311                 else
312                         /* signal that there are no parameters */
313                         dir = NULL;
314         }
315         else
316         {
317                 p = split (param, &argc, FALSE);
318                 if (argc > 1)
319                 {
320                         /*JPP 20-Jul-1998 use standard error message */
321                         error_too_many_parameters (param);
322                         freep (p);
323                         return 1;
324                 }
325                 else
326                         dir = p[0];
327         }
328
329         if (!dir)
330         {
331                 ConErrPrintf (_T("Required parameter missing\n"));
332                 return 1;
333         }
334
335         /* remove trailing \ if any, but ONLY if dir is not the root dir */
336         if (_tcslen (dir) >= 2 && dir[_tcslen (dir) - 1] == _T('\\'))
337                 dir[_tcslen(dir) - 1] = _T('\0');
338
339         if (!CreateDirectory (dir, NULL))
340         {
341                 ErrorMessage (GetLastError(), _T("MD"));
342
343                 freep (p);
344                 return 1;
345         }
346
347         freep (p);
348
349         return 0;
350 }
351 #endif
352
353
354 #ifdef INCLUDE_CMD_RMDIR
355 /*
356  * RD / RMDIR
357  *
358  */
359 INT cmd_rmdir (LPTSTR cmd, LPTSTR param)
360 {
361         LPTSTR dir;             /* pointer to the directory to change to */
362         LPTSTR place;   /* used to search for the \ when no space is used */
363
364         LPTSTR *p = NULL;
365         INT argc;
366
367         if (!_tcsncmp (param, _T("/?"), 2))
368         {
369                 ConOutPuts (_T("Removes a directory.\n\n"
370                                            "RMDIR [drive:]path\nRD [drive:]path"));
371                 return 0;
372         }
373
374         /* check if there is no space between the command and the path */
375         if (param[0] == _T('\0'))
376         {
377                 /* search for the \ or . so that both short & long names will work */
378                 for (place = cmd; *place; place++)
379                         if (*place == _T('.') || *place == _T('\\'))
380                                 break;
381
382                 if (*place)
383                         dir = place;
384                 else
385                         /* signal that there are no parameters */
386                         dir = NULL;
387         }
388         else
389         {
390                 p = split (param, &argc, FALSE);
391                 if (argc > 1)
392                 {
393                         /*JPP 20-Jul-1998 use standard error message */
394                         error_too_many_parameters (param);
395                         freep (p);
396                         return 1;
397                 }
398                 else
399                         dir = p[0];
400         }
401
402         if (!dir)
403         {
404                 ConErrPrintf (_T("Required parameter missing\n"));
405                 return 1;
406         }
407
408         /* remove trailing \ if any, but ONLY if dir is not the root dir */
409         if (_tcslen (dir) >= 2 && dir[_tcslen (dir) - 1] == _T('\\'))
410                 dir[_tcslen(dir) - 1] = _T('\0');
411
412         if (!RemoveDirectory (dir))
413         {
414                 ErrorMessage (GetLastError(), _T("RD"));
415                 freep (p);
416
417                 return 1;
418         }
419
420         freep (p);
421
422         return 0;
423 }
424 #endif
425
426
427 /*
428  * set the exitflag to true
429  *
430  */
431 INT CommandExit (LPTSTR cmd, LPTSTR param)
432 {
433         if (!_tcsncmp (param, _T("/?"), 2))
434         {
435                 ConOutPuts (_T("Exits the command line interpreter.\n\nEXIT"));
436                 return 0;
437         }
438
439         bExit = TRUE;
440         return 0;
441 }
442
443
444 #ifdef INCLUDE_CMD_REM
445 /*
446  * does nothing
447  *
448  */
449 INT CommandRem (LPTSTR cmd, LPTSTR param)
450 {
451         if (!_tcsncmp (param, _T("/?"), 2))
452         {
453                 ConOutPuts (_T("Starts a comment line in a batch file.\n\n"
454                                "REM [Comment]"));
455         }
456
457         return 0;
458 }
459 #endif /* INCLUDE_CMD_REM */
460
461
462 INT CommandShowCommands (LPTSTR cmd, LPTSTR param)
463 {
464         PrintCommandList ();
465         return 0;
466 }
467
468 /* EOF */