[CMD]
[reactos.git] / reactos / base / shell / cmd / filecomp.c
1 /*
2 * FILECOMP.C - handles filename completion.
3 *
4 *
5 * Comments:
6 *
7 * 30-Jul-1998 (John P Price <linux-guru@gcfl.net>)
8 * moved from command.c file
9 * made second TAB display list of filename matches
10 * made filename be lower case if last character typed is lower case
11 *
12 * 25-Jan-1999 (Eric Kohl)
13 * Cleanup. Unicode safe!
14 *
15 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
16 * Make the file listing readable when there is a lot of long names.
17 *
18
19 * 05-Jul-2004 (Jens Collin <jens.collin@lakhei.com>)
20 * Now expands lfn even when trailing " is omitted.
21 */
22
23 #include "precomp.h"
24
25 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
26
27 VOID CompleteFilename (LPTSTR str, UINT charcount)
28 {
29 WIN32_FIND_DATA file;
30 HANDLE hFile;
31 INT curplace = 0;
32 INT start;
33 INT count;
34 INT step;
35 INT c = 0;
36 BOOL found_dot = FALSE;
37 BOOL perfectmatch = TRUE;
38 TCHAR path[MAX_PATH];
39 TCHAR fname[MAX_PATH];
40 TCHAR maxmatch[MAX_PATH] = _T("");
41 TCHAR directory[MAX_PATH];
42 LPCOMMAND cmds_ptr;
43
44 /* expand current file name */
45 count = charcount - 1;
46 if (count < 0)
47 count = 0;
48
49 /* find how many '"'s there is typed already. */
50 step = count;
51 while (step > 0)
52 {
53 if (str[step] == _T('"'))
54 c++;
55 step--;
56 }
57 /* if c is odd, then user typed " before name, else not. */
58
59 /* find front of word */
60 if (str[count] == _T('"') || (c % 2))
61 {
62 count--;
63 while (count > 0 && str[count] != _T('"'))
64 count--;
65 }
66 else
67 {
68 while (count > 0 && str[count] != _T(' '))
69 count--;
70 }
71
72 /* if not at beginning, go forward 1 */
73 if (str[count] == _T(' '))
74 count++;
75
76 start = count;
77
78 if (str[count] == _T('"'))
79 count++; /* don't increment start */
80
81 /* extract directory from word */
82 _tcscpy (directory, &str[count]);
83 curplace = _tcslen (directory) - 1;
84
85 if (curplace >= 0 && directory[curplace] == _T('"'))
86 directory[curplace--] = _T('\0');
87
88 _tcscpy (path, directory);
89
90 while (curplace >= 0 && directory[curplace] != _T('\\') &&
91 directory[curplace] != _T('/') &&
92 directory[curplace] != _T(':'))
93 {
94 directory[curplace] = 0;
95 curplace--;
96 }
97
98 /* look for a '.' in the filename */
99 for (count = _tcslen (directory); path[count] != _T('\0'); count++)
100 {
101 if (path[count] == _T('.'))
102 {
103 found_dot = TRUE;
104 break;
105 }
106 }
107
108 if (found_dot)
109 _tcscat (path, _T("*"));
110 else
111 _tcscat (path, _T("*.*"));
112
113 /* current fname */
114 curplace = 0;
115
116 hFile = FindFirstFile (path, &file);
117 if (hFile != INVALID_HANDLE_VALUE)
118 {
119 /* find anything */
120 do
121 {
122 /* ignore "." and ".." */
123 if (!_tcscmp (file.cFileName, _T(".")) ||
124 !_tcscmp (file.cFileName, _T("..")))
125 continue;
126
127 _tcscpy (fname, file.cFileName);
128
129 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
130 _tcscat (fname, _T("\\"));
131
132 if (!maxmatch[0] && perfectmatch)
133 {
134 _tcscpy(maxmatch, fname);
135 }
136 else
137 {
138 for (count = 0; maxmatch[count] && fname[count]; count++)
139 {
140 if (tolower(maxmatch[count]) != tolower(fname[count]))
141 {
142 perfectmatch = FALSE;
143 maxmatch[count] = 0;
144 break;
145 }
146 }
147
148 if (maxmatch[count] == _T('\0') &&
149 fname[count] != _T('\0'))
150 perfectmatch = FALSE;
151 }
152 }
153 while (FindNextFile (hFile, &file));
154
155 FindClose (hFile);
156
157 /* only quote if the filename contains spaces */
158 if (_tcschr(directory, _T(' ')) ||
159 _tcschr(maxmatch, _T(' ')))
160 {
161 str[start] = _T('\"');
162 _tcscpy (&str[start+1], directory);
163 _tcscat (&str[start], maxmatch);
164 _tcscat (&str[start], _T("\"") );
165 }
166 else
167 {
168 _tcscpy (&str[start], directory);
169 _tcscat (&str[start], maxmatch);
170 }
171
172 if (!perfectmatch)
173 {
174 MessageBeep (-1);
175 }
176 }
177 else
178 {
179 /* no match found - search for internal command */
180 for (cmds_ptr = cmds; cmds_ptr->name; cmds_ptr++)
181 {
182 if (!_tcsnicmp (&str[start], cmds_ptr->name,
183 _tcslen (&str[start])))
184 {
185 /* return the mach only if it is unique */
186 if (_tcsnicmp (&str[start], (cmds_ptr+1)->name, _tcslen (&str[start])))
187 _tcscpy (&str[start], cmds_ptr->name);
188 break;
189 }
190 }
191
192 MessageBeep (-1);
193 }
194 }
195
196
197 /*
198 * returns 1 if at least one match, else returns 0
199 */
200 BOOL ShowCompletionMatches (LPTSTR str, INT charcount)
201 {
202 WIN32_FIND_DATA file;
203 HANDLE hFile;
204 BOOL found_dot = FALSE;
205 INT curplace = 0;
206 INT count;
207 TCHAR path[MAX_PATH];
208 TCHAR fname[MAX_PATH];
209 TCHAR directory[MAX_PATH];
210 SHORT screenwidth;
211
212 /* expand current file name */
213 count = charcount - 1;
214 if (count < 0)
215 count = 0;
216
217 /* find front of word */
218 if (str[count] == _T('"'))
219 {
220 count--;
221 while (count > 0 && str[count] != _T('"'))
222 count--;
223 }
224 else
225 {
226 while (count > 0 && str[count] != _T(' '))
227 count--;
228 }
229
230 /* if not at beginning, go forward 1 */
231 if (str[count] == _T(' '))
232 count++;
233
234 if (str[count] == _T('"'))
235 count++;
236
237 /* extract directory from word */
238 _tcscpy (directory, &str[count]);
239 curplace = _tcslen (directory) - 1;
240
241 if (curplace >= 0 && directory[curplace] == _T('"'))
242 directory[curplace--] = _T('\0');
243
244 _tcscpy (path, directory);
245
246 while (curplace >= 0 &&
247 directory[curplace] != _T('\\') &&
248 directory[curplace] != _T(':'))
249 {
250 directory[curplace] = 0;
251 curplace--;
252 }
253
254 /* look for a . in the filename */
255 for (count = _tcslen (directory); path[count] != _T('\0'); count++)
256 {
257 if (path[count] == _T('.'))
258 {
259 found_dot = TRUE;
260 break;
261 }
262 }
263
264 if (found_dot)
265 _tcscat (path, _T("*"));
266 else
267 _tcscat (path, _T("*.*"));
268
269 /* current fname */
270 curplace = 0;
271
272 hFile = FindFirstFile (path, &file);
273 if (hFile != INVALID_HANDLE_VALUE)
274 {
275 UINT longestfname = 0;
276 /* Get the size of longest filename first. */
277 do
278 {
279 if (_tcslen(file.cFileName) > longestfname)
280 {
281 longestfname = _tcslen(file.cFileName);
282 /* Directories get extra brackets around them. */
283 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
284 longestfname += 2;
285 }
286 }
287 while (FindNextFile (hFile, &file));
288 FindClose (hFile);
289
290 hFile = FindFirstFile (path, &file);
291
292 /* Count the highest number of columns */
293 GetScreenSize(&screenwidth, 0);
294
295 /* For counting columns of output */
296 count = 0;
297
298 /* Increase by the number of spaces behind file name */
299 longestfname += 3;
300
301 /* find anything */
302 ConOutChar(_T('\n'));
303 do
304 {
305 /* ignore . and .. */
306 if (!_tcscmp (file.cFileName, _T(".")) ||
307 !_tcscmp (file.cFileName, _T("..")))
308 continue;
309
310 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
311 _stprintf (fname, _T("[%s]"), file.cFileName);
312 else
313 _tcscpy (fname, file.cFileName);
314
315 ConOutPrintf (_T("%*s"), - longestfname, fname);
316 count++;
317 /* output as much columns as fits on the screen */
318 if (count >= (screenwidth / longestfname))
319 {
320 /* print the new line only if we aren't on the
321 * last column, in this case it wraps anyway */
322 if (count * longestfname != (UINT)screenwidth)
323 ConOutChar(_T('\n'));
324 count = 0;
325 }
326 }
327 while (FindNextFile (hFile, &file));
328
329 FindClose (hFile);
330
331 if (count)
332 ConOutChar(_T('\n'));
333 }
334 else
335 {
336 /* no match found */
337 MessageBeep (-1);
338 return FALSE;
339 }
340
341 return TRUE;
342 }
343 #endif
344
345 #ifdef FEATURE_4NT_FILENAME_COMPLETION
346
347 typedef struct _FileName
348 {
349 TCHAR Name[MAX_PATH];
350 } FileName;
351
352 VOID FindPrefixAndSuffix(LPTSTR strIN, LPTSTR szPrefix, LPTSTR szSuffix)
353 {
354 /* String that is to be examined */
355 TCHAR str[MAX_PATH];
356 /* temp pointers to used to find needed parts */
357 TCHAR * szSearch;
358 TCHAR * szSearch1;
359 TCHAR * szSearch2;
360 TCHAR * szSearch3;
361 /* number of quotes in the string */
362 INT nQuotes = 0;
363 /* used in for loops */
364 UINT i;
365 /* Char number to break the string at */
366 INT PBreak = 0;
367 INT SBreak = 0;
368 /* when phrasing a string, this tells weather
369 you are inside quotes ot not. */
370 BOOL bInside = FALSE;
371
372 szPrefix[0] = _T('\0');
373 szSuffix[0] = _T('\0');
374
375 /* Copy over the string to later be edited */
376 _tcscpy(str,strIN);
377
378 /* Count number of " */
379 for(i = 0; i < _tcslen(str); i++)
380 {
381 if (str[i] == _T('\"'))
382 nQuotes++;
383 }
384
385 /* Find the prefix and suffix */
386 if (nQuotes % 2 && nQuotes >= 1)
387 {
388 /* Odd number of quotes. Just start from the last " */
389 /* THis is the way MS does it, and is an easy way out */
390 szSearch = _tcsrchr(str, _T('\"'));
391 /* Move to the next char past the " */
392 szSearch++;
393 _tcscpy(szSuffix,szSearch);
394 /* Find the one closest to end */
395 szSearch1 = _tcsrchr(str, _T('\"'));
396 szSearch2 = _tcsrchr(str, _T('\\'));
397 szSearch3 = _tcsrchr(str, _T('.'));
398 if (szSearch2 != NULL && _tcslen(szSearch1) > _tcslen(szSearch2))
399 szSearch = szSearch2;
400 else if (szSearch3 != NULL && _tcslen(szSearch1) > _tcslen(szSearch3))
401 szSearch = szSearch3;
402 else
403 szSearch = szSearch1;
404 /* Move one char past */
405 szSearch++;
406 szSearch[0] = _T('\0');
407 _tcscpy(szPrefix,str);
408 return;
409
410 }
411
412 if (!_tcschr(str, _T(' ')))
413 {
414 /* No spaces, everything goes to Suffix */
415 _tcscpy(szSuffix,str);
416 /* look for a slash just in case */
417 szSearch = _tcsrchr(str, _T('\\'));
418 if (szSearch)
419 {
420 szSearch++;
421 szSearch[0] = _T('\0');
422 _tcscpy(szPrefix,str);
423 }
424 else
425 {
426 szPrefix[0] = _T('\0');
427 }
428 return;
429 }
430
431 if (!nQuotes)
432 {
433 /* No quotes, and there is a space*/
434 /* Take it after the last space */
435 szSearch = _tcsrchr(str, _T(' '));
436 szSearch++;
437 _tcscpy(szSuffix,szSearch);
438 /* Find the closest to the end space or \ */
439 _tcscpy(str,strIN);
440 szSearch1 = _tcsrchr(str, _T(' '));
441 szSearch2 = _tcsrchr(str, _T('\\'));
442 szSearch3 = _tcsrchr(str, _T('/'));
443 if (szSearch2 != NULL && _tcslen(szSearch1) > _tcslen(szSearch2))
444 szSearch = szSearch2;
445 else if (szSearch3 != NULL && _tcslen(szSearch1) > _tcslen(szSearch3))
446 szSearch = szSearch3;
447 else
448 szSearch = szSearch1;
449 szSearch++;
450 szSearch[0] = _T('\0');
451 _tcscpy(szPrefix,str);
452 return;
453 }
454
455 /* All else fails and there is a lot of quotes, spaces and |
456 Then we search through and find the last space or \ that is
457 not inside a quotes */
458 for(i = 0; i < _tcslen(str); i++)
459 {
460 if (str[i] == _T('\"'))
461 bInside = !bInside;
462 if (str[i] == _T(' ') && !bInside)
463 SBreak = i;
464 if ((str[i] == _T(' ') || str[i] == _T('\\')) && !bInside)
465 PBreak = i;
466 }
467 SBreak++;
468 PBreak++;
469 _tcscpy(szSuffix,&strIN[SBreak]);
470 strIN[PBreak] = _T('\0');
471 _tcscpy(szPrefix,strIN);
472 if (szPrefix[_tcslen(szPrefix) - 2] == _T('\"') &&
473 szPrefix[_tcslen(szPrefix) - 1] != _T(' '))
474 {
475 /* need to remove the " right before a \ at the end to
476 allow the next stuff to stay inside one set of quotes
477 otherwise you would have multiple sets of quotes*/
478 _tcscpy(&szPrefix[_tcslen(szPrefix) - 2],_T("\\"));
479 }
480 }
481
482 int __cdecl compare(const void *arg1,const void *arg2)
483 {
484 FileName * File1;
485 FileName * File2;
486 INT ret;
487
488 File1 = cmd_alloc(sizeof(FileName));
489 if (!File1)
490 return 0;
491
492 File2 = cmd_alloc(sizeof(FileName));
493 if (!File2)
494 {
495 cmd_free(File1);
496 return 0;
497 }
498
499 memcpy(File1,arg1,sizeof(FileName));
500 memcpy(File2,arg2,sizeof(FileName));
501
502 /* ret = _tcsicmp(File1->Name, File2->Name); */
503 ret = lstrcmpi(File1->Name, File2->Name);
504
505 cmd_free(File1);
506 cmd_free(File2);
507 return ret;
508 }
509
510 BOOL
511 FileNameContainsSpecialCharacters(LPTSTR pszFileName)
512 {
513 TCHAR chr;
514
515 while ((chr = *pszFileName++) != _T('\0'))
516 {
517 if ((chr == _T(' ')) ||
518 (chr == _T('!')) ||
519 (chr == _T('%')) ||
520 (chr == _T('&')) ||
521 (chr == _T('(')) ||
522 (chr == _T(')')) ||
523 (chr == _T('{')) ||
524 (chr == _T('}')) ||
525 (chr == _T('[')) ||
526 (chr == _T(']')) ||
527 (chr == _T('=')) ||
528 (chr == _T('\'')) ||
529 (chr == _T('`')) ||
530 (chr == _T(',')) ||
531 (chr == _T(';')) ||
532 (chr == _T('^')) ||
533 (chr == _T('~')) ||
534 (chr == _T('+')) ||
535 (chr == 0xB4)) // 'ยด'
536 {
537 return TRUE;
538 }
539 }
540
541 return FALSE;
542 }
543
544
545 VOID CompleteFilename (LPTSTR strIN, BOOL bNext, LPTSTR strOut, UINT cusor)
546 {
547 /* Length of string before we complete it */
548 INT_PTR StartLength;
549 /* Length of string after completed */
550 //INT EndLength;
551 /* The number of chars added too it */
552 //static INT DiffLength = 0;
553 /* Used to find and assemble the string that is returned */
554 TCHAR szBaseWord[MAX_PATH];
555 TCHAR szPrefix[MAX_PATH];
556 TCHAR szOriginal[MAX_PATH];
557 TCHAR szSearchPath[MAX_PATH];
558 /* Save the strings used last time, so if they hit tab again */
559 static TCHAR LastReturned[MAX_PATH];
560 static TCHAR LastSearch[MAX_PATH];
561 static TCHAR LastPrefix[MAX_PATH];
562 /* Used to search for files */
563 HANDLE hFile;
564 WIN32_FIND_DATA file;
565 /* List of all the files */
566 FileName * FileList = NULL;
567 /* Number of files */
568 INT FileListSize = 0;
569 /* Used for loops */
570 UINT i;
571 /* Editable string of what was passed in */
572 TCHAR str[MAX_PATH];
573 /* Keeps track of what element was last selected */
574 static INT Sel;
575 BOOL NeededQuote = FALSE;
576 BOOL ShowAll = TRUE;
577 TCHAR * line = strIN;
578
579 strOut[0] = _T('\0');
580
581 while (_istspace (*line))
582 line++;
583 if (!_tcsnicmp (line, _T("rd "), 3) || !_tcsnicmp (line, _T("cd "), 3))
584 ShowAll = FALSE;
585
586 /* Copy the string, str can be edited and original should not be */
587 _tcscpy(str,strIN);
588 _tcscpy(szOriginal,strIN);
589
590 /* Look to see if the cusor is not at the end of the string */
591 if ((cusor + 1) < _tcslen(str))
592 str[cusor] = _T('\0');
593
594 /* Look to see if they hit tab again, if so cut off the diff length */
595 if (_tcscmp(str,LastReturned) || !_tcslen(str))
596 {
597 /* We need to know how many chars we added from the start */
598 StartLength = _tcslen(str);
599
600 /* no string, we need all files in that directory */
601 if (!StartLength)
602 {
603 _tcscat(str,_T("*"));
604 }
605
606 /* Zero it out first */
607 szBaseWord[0] = _T('\0');
608 szPrefix[0] = _T('\0');
609
610 /*What comes out of this needs to be:
611 szBaseWord = path no quotes to the object
612 szPrefix = what leads up to the filename
613 no quote at the END of the full name */
614 FindPrefixAndSuffix(str,szPrefix,szBaseWord);
615 /* Strip quotes */
616 for(i = 0; i < _tcslen(szBaseWord); )
617 {
618 if (szBaseWord[i] == _T('\"'))
619 memmove(&szBaseWord[i],&szBaseWord[i + 1], _tcslen(&szBaseWord[i]) * sizeof(TCHAR));
620 else
621 i++;
622 }
623
624 /* clear it out */
625 memset(szSearchPath, 0, sizeof(szSearchPath));
626
627 /* Start the search for all the files */
628 GetFullPathName(szBaseWord, MAX_PATH, szSearchPath, NULL);
629
630 /* Got a device path? Fallback to the the current dir plus the short path */
631 if (szSearchPath[0] == _T('\\') && szSearchPath[1] == _T('\\') &&
632 szSearchPath[2] == _T('.') && szSearchPath[3] == _T('\\'))
633 {
634 GetCurrentDirectory(MAX_PATH, szSearchPath);
635 _tcscat(szSearchPath, _T("\\"));
636 _tcscat(szSearchPath, szBaseWord);
637 }
638
639 if (StartLength > 0)
640 {
641 _tcscat(szSearchPath,_T("*"));
642 }
643 _tcscpy(LastSearch,szSearchPath);
644 _tcscpy(LastPrefix,szPrefix);
645 }
646 else
647 {
648 _tcscpy(szSearchPath, LastSearch);
649 _tcscpy(szPrefix, LastPrefix);
650 StartLength = 0;
651 }
652 /* search for the files it might be */
653 hFile = FindFirstFile (szSearchPath, &file);
654 if (hFile == INVALID_HANDLE_VALUE)
655 {
656 /* Assemble the original string and return */
657 _tcscpy(strOut,szOriginal);
658 return;
659 }
660
661 /* aseemble a list of all files names */
662 do
663 {
664 FileName * oldFileList = FileList;
665
666 if (!_tcscmp (file.cFileName, _T(".")) ||
667 !_tcscmp (file.cFileName, _T("..")))
668 continue;
669
670 /* Don't show files when they are doing 'cd' or 'rd' */
671 if (!ShowAll &&
672 file.dwFileAttributes != 0xFFFFFFFF &&
673 !(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
674 {
675 continue;
676 }
677
678 /* Add the file to the list of files */
679 FileList = cmd_realloc(FileList, ++FileListSize * sizeof(FileName));
680
681 if (FileList == NULL)
682 {
683 /* Don't leak old buffer */
684 cmd_free(oldFileList);
685 /* Assemble the original string and return */
686 _tcscpy(strOut,szOriginal);
687 FindClose(hFile);
688 ConOutFormatMessage (GetLastError());
689 return;
690 }
691 /* Copies the file name into the struct */
692 _tcscpy(FileList[FileListSize-1].Name,file.cFileName);
693
694 } while(FindNextFile(hFile,&file));
695
696 FindClose(hFile);
697
698 /* Check the size of the list to see if we found any matches */
699 if (FileListSize == 0)
700 {
701 _tcscpy(strOut,szOriginal);
702 if (FileList != NULL)
703 cmd_free(FileList);
704 return;
705
706 }
707 /* Sort the files */
708 qsort(FileList,FileListSize,sizeof(FileName), compare);
709
710 /* Find the next/previous */
711 if (_tcslen(szOriginal) && !_tcscmp(szOriginal,LastReturned))
712 {
713 if (bNext)
714 {
715 if (FileListSize - 1 == Sel)
716 Sel = 0;
717 else
718 Sel++;
719 }
720 else
721 {
722 if (!Sel)
723 Sel = FileListSize - 1;
724 else
725 Sel--;
726 }
727 }
728 else
729 {
730 Sel = 0;
731 }
732
733 /* nothing found that matched last time so return the first thing in the list */
734 strOut[0] = _T('\0');
735
736 /* Special character in the name */
737 if (FileNameContainsSpecialCharacters(FileList[Sel].Name))
738 {
739 INT LastSpace;
740 BOOL bInside;
741 /* It needs a " at the end */
742 NeededQuote = TRUE;
743 LastSpace = -1;
744 bInside = FALSE;
745 /* Find the place to put the " at the start */
746 for(i = 0; i < _tcslen(szPrefix); i++)
747 {
748 if (szPrefix[i] == _T('\"'))
749 bInside = !bInside;
750 if (szPrefix[i] == _T(' ') && !bInside)
751 LastSpace = i;
752 }
753
754 /* insert the quotation and move things around */
755 if (szPrefix[LastSpace + 1] != _T('\"') && LastSpace != -1)
756 {
757 memmove ( &szPrefix[LastSpace+1], &szPrefix[LastSpace], (_tcslen(szPrefix)-LastSpace+1) * sizeof(TCHAR) );
758
759 if ((UINT)(LastSpace + 1) == _tcslen(szPrefix))
760 {
761 _tcscat(szPrefix,_T("\""));
762 }
763 szPrefix[LastSpace + 1] = _T('\"');
764 }
765 else if (LastSpace == -1)
766 {
767 /* Add quotation only if none exists already */
768 if (szPrefix[0] != _T('\"'))
769 {
770 _tcscpy(szBaseWord,_T("\""));
771 _tcscat(szBaseWord,szPrefix);
772 _tcscpy(szPrefix,szBaseWord);
773 }
774 }
775 }
776
777 _tcscpy(strOut,szPrefix);
778 _tcscat(strOut,FileList[Sel].Name);
779
780 /* check for odd number of quotes means we need to close them */
781 if (!NeededQuote)
782 {
783 for(i = 0; i < _tcslen(strOut); i++)
784 {
785 if (strOut[i] == _T('\"'))
786 NeededQuote = !NeededQuote;
787 }
788 }
789
790 if (NeededQuote || (_tcslen(szPrefix) && szPrefix[_tcslen(szPrefix) - 1] == _T('\"')))
791 _tcscat(strOut,_T("\""));
792
793 _tcscpy(LastReturned,strOut);
794 //EndLength = _tcslen(strOut);
795 //DiffLength = EndLength - StartLength;
796 if (FileList != NULL)
797 cmd_free(FileList);
798 }
799 #endif