adacf9092fbcec622bcffad0139702a5f8650041
[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 start;
207 INT count;
208 TCHAR path[MAX_PATH];
209 TCHAR fname[MAX_PATH];
210 TCHAR directory[MAX_PATH];
211 SHORT screenwidth;
212
213 /* expand current file name */
214 count = charcount - 1;
215 if (count < 0)
216 count = 0;
217
218 /* find front of word */
219 if (str[count] == _T('"'))
220 {
221 count--;
222 while (count > 0 && str[count] != _T('"'))
223 count--;
224 }
225 else
226 {
227 while (count > 0 && str[count] != _T(' '))
228 count--;
229 }
230
231 /* if not at beginning, go forward 1 */
232 if (str[count] == _T(' '))
233 count++;
234
235 start = count;
236
237 if (str[count] == _T('"'))
238 count++; /* don't increment start */
239
240 /* extract directory from word */
241 _tcscpy (directory, &str[count]);
242 curplace = _tcslen (directory) - 1;
243
244 if (curplace >= 0 && directory[curplace] == _T('"'))
245 directory[curplace--] = _T('\0');
246
247 _tcscpy (path, directory);
248
249 while (curplace >= 0 &&
250 directory[curplace] != _T('\\') &&
251 directory[curplace] != _T(':'))
252 {
253 directory[curplace] = 0;
254 curplace--;
255 }
256
257 /* look for a . in the filename */
258 for (count = _tcslen (directory); path[count] != _T('\0'); count++)
259 {
260 if (path[count] == _T('.'))
261 {
262 found_dot = TRUE;
263 break;
264 }
265 }
266
267 if (found_dot)
268 _tcscat (path, _T("*"));
269 else
270 _tcscat (path, _T("*.*"));
271
272 /* current fname */
273 curplace = 0;
274
275 hFile = FindFirstFile (path, &file);
276 if (hFile != INVALID_HANDLE_VALUE)
277 {
278 UINT longestfname = 0;
279 /* Get the size of longest filename first. */
280 do
281 {
282 if (_tcslen(file.cFileName) > longestfname)
283 {
284 longestfname = _tcslen(file.cFileName);
285 /* Directories get extra brackets around them. */
286 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
287 longestfname += 2;
288 }
289 }
290 while (FindNextFile (hFile, &file));
291 FindClose (hFile);
292
293 hFile = FindFirstFile (path, &file);
294
295 /* Count the highest number of columns */
296 GetScreenSize(&screenwidth, 0);
297
298 /* For counting columns of output */
299 count = 0;
300
301 /* Increase by the number of spaces behind file name */
302 longestfname += 3;
303
304 /* find anything */
305 ConOutChar(_T('\n'));
306 do
307 {
308 /* ignore . and .. */
309 if (!_tcscmp (file.cFileName, _T(".")) ||
310 !_tcscmp (file.cFileName, _T("..")))
311 continue;
312
313 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
314 _stprintf (fname, _T("[%s]"), file.cFileName);
315 else
316 _tcscpy (fname, file.cFileName);
317
318 ConOutPrintf (_T("%*s"), - longestfname, fname);
319 count++;
320 /* output as much columns as fits on the screen */
321 if (count >= (screenwidth / longestfname))
322 {
323 /* print the new line only if we aren't on the
324 * last column, in this case it wraps anyway */
325 if (count * longestfname != (UINT)screenwidth)
326 ConOutChar(_T('\n'));
327 count = 0;
328 }
329 }
330 while (FindNextFile (hFile, &file));
331
332 FindClose (hFile);
333
334 if (count)
335 ConOutChar(_T('\n'));
336 }
337 else
338 {
339 /* no match found */
340 MessageBeep (-1);
341 return FALSE;
342 }
343
344 return TRUE;
345 }
346 #endif
347
348 #ifdef FEATURE_4NT_FILENAME_COMPLETION
349
350 typedef struct _FileName
351 {
352 TCHAR Name[MAX_PATH];
353 } FileName;
354
355 VOID FindPrefixAndSuffix(LPTSTR strIN, LPTSTR szPrefix, LPTSTR szSuffix)
356 {
357 /* String that is to be examined */
358 TCHAR str[MAX_PATH];
359 /* temp pointers to used to find needed parts */
360 TCHAR * szSearch;
361 TCHAR * szSearch1;
362 TCHAR * szSearch2;
363 TCHAR * szSearch3;
364 /* number of quotes in the string */
365 INT nQuotes = 0;
366 /* used in for loops */
367 UINT i;
368 /* Char number to break the string at */
369 INT PBreak = 0;
370 INT SBreak = 0;
371 /* when phrasing a string, this tells weather
372 you are inside quotes ot not. */
373 BOOL bInside = FALSE;
374
375 szPrefix[0] = _T('\0');
376 szSuffix[0] = _T('\0');
377
378 /* Copy over the string to later be edited */
379 _tcscpy(str,strIN);
380
381 /* Count number of " */
382 for(i = 0; i < _tcslen(str); i++)
383 {
384 if (str[i] == _T('\"'))
385 nQuotes++;
386 }
387
388 /* Find the prefix and suffix */
389 if (nQuotes % 2 && nQuotes >= 1)
390 {
391 /* Odd number of quotes. Just start from the last " */
392 /* THis is the way MS does it, and is an easy way out */
393 szSearch = _tcsrchr(str, _T('\"'));
394 /* Move to the next char past the " */
395 szSearch++;
396 _tcscpy(szSuffix,szSearch);
397 /* Find the one closest to end */
398 szSearch1 = _tcsrchr(str, _T('\"'));
399 szSearch2 = _tcsrchr(str, _T('\\'));
400 szSearch3 = _tcsrchr(str, _T('.'));
401 if (szSearch2 != NULL && _tcslen(szSearch1) > _tcslen(szSearch2))
402 szSearch = szSearch2;
403 else if (szSearch3 != NULL && _tcslen(szSearch1) > _tcslen(szSearch3))
404 szSearch = szSearch3;
405 else
406 szSearch = szSearch1;
407 /* Move one char past */
408 szSearch++;
409 szSearch[0] = _T('\0');
410 _tcscpy(szPrefix,str);
411 return;
412
413 }
414
415 if (!_tcschr(str, _T(' ')))
416 {
417 /* No spaces, everything goes to Suffix */
418 _tcscpy(szSuffix,str);
419 /* look for a slash just in case */
420 szSearch = _tcsrchr(str, _T('\\'));
421 if (szSearch)
422 {
423 szSearch++;
424 szSearch[0] = _T('\0');
425 _tcscpy(szPrefix,str);
426 }
427 else
428 {
429 szPrefix[0] = _T('\0');
430 }
431 return;
432 }
433
434 if (!nQuotes)
435 {
436 /* No quotes, and there is a space*/
437 /* Take it after the last space */
438 szSearch = _tcsrchr(str, _T(' '));
439 szSearch++;
440 _tcscpy(szSuffix,szSearch);
441 /* Find the closest to the end space or \ */
442 _tcscpy(str,strIN);
443 szSearch1 = _tcsrchr(str, _T(' '));
444 szSearch2 = _tcsrchr(str, _T('\\'));
445 szSearch3 = _tcsrchr(str, _T('/'));
446 if (szSearch2 != NULL && _tcslen(szSearch1) > _tcslen(szSearch2))
447 szSearch = szSearch2;
448 else if (szSearch3 != NULL && _tcslen(szSearch1) > _tcslen(szSearch3))
449 szSearch = szSearch3;
450 else
451 szSearch = szSearch1;
452 szSearch++;
453 szSearch[0] = _T('\0');
454 _tcscpy(szPrefix,str);
455 return;
456 }
457
458 /* All else fails and there is a lot of quotes, spaces and |
459 Then we search through and find the last space or \ that is
460 not inside a quotes */
461 for(i = 0; i < _tcslen(str); i++)
462 {
463 if (str[i] == _T('\"'))
464 bInside = !bInside;
465 if (str[i] == _T(' ') && !bInside)
466 SBreak = i;
467 if ((str[i] == _T(' ') || str[i] == _T('\\')) && !bInside)
468 PBreak = i;
469 }
470 SBreak++;
471 PBreak++;
472 _tcscpy(szSuffix,&strIN[SBreak]);
473 strIN[PBreak] = _T('\0');
474 _tcscpy(szPrefix,strIN);
475 if (szPrefix[_tcslen(szPrefix) - 2] == _T('\"') &&
476 szPrefix[_tcslen(szPrefix) - 1] != _T(' '))
477 {
478 /* need to remove the " right before a \ at the end to
479 allow the next stuff to stay inside one set of quotes
480 otherwise you would have multiple sets of quotes*/
481 _tcscpy(&szPrefix[_tcslen(szPrefix) - 2],_T("\\"));
482 }
483 }
484
485 int __cdecl compare(const void *arg1,const void *arg2)
486 {
487 FileName * File1;
488 FileName * File2;
489 INT ret;
490
491 File1 = cmd_alloc(sizeof(FileName));
492 if (!File1)
493 return 0;
494
495 File2 = cmd_alloc(sizeof(FileName));
496 if (!File2)
497 {
498 cmd_free(File1);
499 return 0;
500 }
501
502 memcpy(File1,arg1,sizeof(FileName));
503 memcpy(File2,arg2,sizeof(FileName));
504
505 /* ret = _tcsicmp(File1->Name, File2->Name); */
506 ret = lstrcmpi(File1->Name, File2->Name);
507
508 cmd_free(File1);
509 cmd_free(File2);
510 return ret;
511 }
512
513 BOOL
514 FileNameContainsSpecialCharacters(LPTSTR pszFileName)
515 {
516 TCHAR chr;
517
518 while ((chr = *pszFileName++) != _T('\0'))
519 {
520 if ((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 == _T('^')) ||
536 (chr == _T('~')) ||
537 (chr == _T('+')) ||
538 (chr == 0xB4)) // 'ยด'
539 {
540 return TRUE;
541 }
542 }
543
544 return FALSE;
545 }
546
547
548 VOID CompleteFilename (LPTSTR strIN, BOOL bNext, LPTSTR strOut, UINT cusor)
549 {
550 /* Length of string before we complete it */
551 INT_PTR StartLength;
552 /* Length of string after completed */
553 //INT EndLength;
554 /* The number of chars added too it */
555 //static INT DiffLength = 0;
556 /* Used to find and assemble the string that is returned */
557 TCHAR szBaseWord[MAX_PATH];
558 TCHAR szPrefix[MAX_PATH];
559 TCHAR szOriginal[MAX_PATH];
560 TCHAR szSearchPath[MAX_PATH];
561 /* Save the strings used last time, so if they hit tab again */
562 static TCHAR LastReturned[MAX_PATH];
563 static TCHAR LastSearch[MAX_PATH];
564 static TCHAR LastPrefix[MAX_PATH];
565 /* Used to search for files */
566 HANDLE hFile;
567 WIN32_FIND_DATA file;
568 /* List of all the files */
569 FileName * FileList = NULL;
570 /* Number of files */
571 INT FileListSize = 0;
572 /* Used for loops */
573 UINT i;
574 /* Editable string of what was passed in */
575 TCHAR str[MAX_PATH];
576 /* Keeps track of what element was last selected */
577 static INT Sel;
578 BOOL NeededQuote = FALSE;
579 BOOL ShowAll = TRUE;
580 TCHAR * line = strIN;
581
582 strOut[0] = _T('\0');
583
584 while (_istspace (*line))
585 line++;
586 if (!_tcsnicmp (line, _T("rd "), 3) || !_tcsnicmp (line, _T("cd "), 3))
587 ShowAll = FALSE;
588
589 /* Copy the string, str can be edited and original should not be */
590 _tcscpy(str,strIN);
591 _tcscpy(szOriginal,strIN);
592
593 /* Look to see if the cusor is not at the end of the string */
594 if ((cusor + 1) < _tcslen(str))
595 str[cusor] = _T('\0');
596
597 /* Look to see if they hit tab again, if so cut off the diff length */
598 if (_tcscmp(str,LastReturned) || !_tcslen(str))
599 {
600 /* We need to know how many chars we added from the start */
601 StartLength = _tcslen(str);
602
603 /* no string, we need all files in that directory */
604 if (!StartLength)
605 {
606 _tcscat(str,_T("*"));
607 }
608
609 /* Zero it out first */
610 szBaseWord[0] = _T('\0');
611 szPrefix[0] = _T('\0');
612
613 /*What comes out of this needs to be:
614 szBaseWord = path no quotes to the object
615 szPrefix = what leads up to the filename
616 no quote at the END of the full name */
617 FindPrefixAndSuffix(str,szPrefix,szBaseWord);
618 /* Strip quotes */
619 for(i = 0; i < _tcslen(szBaseWord); )
620 {
621 if (szBaseWord[i] == _T('\"'))
622 memmove(&szBaseWord[i],&szBaseWord[i + 1], _tcslen(&szBaseWord[i]) * sizeof(TCHAR));
623 else
624 i++;
625 }
626
627 /* clear it out */
628 memset(szSearchPath, 0, sizeof(szSearchPath));
629
630 /* Start the search for all the files */
631 GetFullPathName(szBaseWord, MAX_PATH, szSearchPath, NULL);
632
633 /* Got a device path? Fallback to the the current dir plus the short path */
634 if (szSearchPath[0] == _T('\\') && szSearchPath[1] == _T('\\') &&
635 szSearchPath[2] == _T('.') && szSearchPath[3] == _T('\\'))
636 {
637 GetCurrentDirectory(MAX_PATH, szSearchPath);
638 _tcscat(szSearchPath, _T("\\"));
639 _tcscat(szSearchPath, szBaseWord);
640 }
641
642 if (StartLength > 0)
643 {
644 _tcscat(szSearchPath,_T("*"));
645 }
646 _tcscpy(LastSearch,szSearchPath);
647 _tcscpy(LastPrefix,szPrefix);
648 }
649 else
650 {
651 _tcscpy(szSearchPath, LastSearch);
652 _tcscpy(szPrefix, LastPrefix);
653 StartLength = 0;
654 }
655 /* search for the files it might be */
656 hFile = FindFirstFile (szSearchPath, &file);
657 if (hFile == INVALID_HANDLE_VALUE)
658 {
659 /* Assemble the original string and return */
660 _tcscpy(strOut,szOriginal);
661 return;
662 }
663
664 /* aseemble a list of all files names */
665 do
666 {
667 FileName * oldFileList = FileList;
668
669 if (!_tcscmp (file.cFileName, _T(".")) ||
670 !_tcscmp (file.cFileName, _T("..")))
671 continue;
672
673 /* Don't show files when they are doing 'cd' or 'rd' */
674 if (!ShowAll &&
675 file.dwFileAttributes != 0xFFFFFFFF &&
676 !(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
677 {
678 continue;
679 }
680
681 /* Add the file to the list of files */
682 FileList = cmd_realloc(FileList, ++FileListSize * sizeof(FileName));
683
684 if (FileList == NULL)
685 {
686 /* Don't leak old buffer */
687 cmd_free(oldFileList);
688 /* Assemble the original string and return */
689 _tcscpy(strOut,szOriginal);
690 FindClose(hFile);
691 ConOutFormatMessage (GetLastError());
692 return;
693 }
694 /* Copies the file name into the struct */
695 _tcscpy(FileList[FileListSize-1].Name,file.cFileName);
696
697 } while(FindNextFile(hFile,&file));
698
699 FindClose(hFile);
700
701 /* Check the size of the list to see if we found any matches */
702 if (FileListSize == 0)
703 {
704 _tcscpy(strOut,szOriginal);
705 if (FileList != NULL)
706 cmd_free(FileList);
707 return;
708
709 }
710 /* Sort the files */
711 qsort(FileList,FileListSize,sizeof(FileName), compare);
712
713 /* Find the next/previous */
714 if (_tcslen(szOriginal) && !_tcscmp(szOriginal,LastReturned))
715 {
716 if (bNext)
717 {
718 if (FileListSize - 1 == Sel)
719 Sel = 0;
720 else
721 Sel++;
722 }
723 else
724 {
725 if (!Sel)
726 Sel = FileListSize - 1;
727 else
728 Sel--;
729 }
730 }
731 else
732 {
733 Sel = 0;
734 }
735
736 /* nothing found that matched last time so return the first thing in the list */
737 strOut[0] = _T('\0');
738
739 /* Special character in the name */
740 if (FileNameContainsSpecialCharacters(FileList[Sel].Name))
741 {
742 INT LastSpace;
743 BOOL bInside;
744 /* It needs a " at the end */
745 NeededQuote = TRUE;
746 LastSpace = -1;
747 bInside = FALSE;
748 /* Find the place to put the " at the start */
749 for(i = 0; i < _tcslen(szPrefix); i++)
750 {
751 if (szPrefix[i] == _T('\"'))
752 bInside = !bInside;
753 if (szPrefix[i] == _T(' ') && !bInside)
754 LastSpace = i;
755 }
756
757 /* insert the quotation and move things around */
758 if (szPrefix[LastSpace + 1] != _T('\"') && LastSpace != -1)
759 {
760 memmove ( &szPrefix[LastSpace+1], &szPrefix[LastSpace], (_tcslen(szPrefix)-LastSpace+1) * sizeof(TCHAR) );
761
762 if ((UINT)(LastSpace + 1) == _tcslen(szPrefix))
763 {
764 _tcscat(szPrefix,_T("\""));
765 }
766 szPrefix[LastSpace + 1] = _T('\"');
767 }
768 else if (LastSpace == -1)
769 {
770 /* Add quotation only if none exists already */
771 if (szPrefix[0] != _T('\"'))
772 {
773 _tcscpy(szBaseWord,_T("\""));
774 _tcscat(szBaseWord,szPrefix);
775 _tcscpy(szPrefix,szBaseWord);
776 }
777 }
778 }
779
780 _tcscpy(strOut,szPrefix);
781 _tcscat(strOut,FileList[Sel].Name);
782
783 /* check for odd number of quotes means we need to close them */
784 if (!NeededQuote)
785 {
786 for(i = 0; i < _tcslen(strOut); i++)
787 {
788 if (strOut[i] == _T('\"'))
789 NeededQuote = !NeededQuote;
790 }
791 }
792
793 if (NeededQuote || (_tcslen(szPrefix) && szPrefix[_tcslen(szPrefix) - 1] == _T('\"')))
794 _tcscat(strOut,_T("\""));
795
796 _tcscpy(LastReturned,strOut);
797 //EndLength = _tcslen(strOut);
798 //DiffLength = EndLength - StartLength;
799 if (FileList != NULL)
800 cmd_free(FileList);
801 }
802 #endif