fixed uninitialized variable warning and indentation
[reactos.git] / reactos / subsys / system / 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 <ekohl@abo.rhein-zeitung.de>)
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 #include "cmd.h"
25
26 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
27
28 VOID CompleteFilename (LPTSTR str, INT charcount)
29 {
30 WIN32_FIND_DATA file;
31 HANDLE hFile;
32 INT curplace = 0;
33 INT start;
34 INT count;
35 INT step;
36 INT c = 0;
37 BOOL found_dot = FALSE;
38 BOOL perfectmatch = TRUE;
39 TCHAR path[MAX_PATH];
40 TCHAR fname[MAX_PATH];
41 TCHAR maxmatch[MAX_PATH] = _T("");
42 TCHAR directory[MAX_PATH];
43 LPCOMMAND cmds_ptr;
44
45 /* expand current file name */
46 count = charcount - 1;
47 if (count < 0)
48 count = 0;
49
50 /* find how many '"'s there is typed already.*/
51 step = count;
52 while (step > 0)
53 {
54 if (str[step] == _T('"'))
55 c++;
56 step--;
57 }
58 /* if c is odd, then user typed " before name, else not.*/
59
60 /* find front of word */
61 if (str[count] == _T('"') || (c % 2))
62 {
63 count--;
64 while (count > 0 && str[count] != _T('"'))
65 count--;
66 }
67 else
68 {
69 while (count > 0 && str[count] != _T(' '))
70 count--;
71 }
72
73 /* if not at beginning, go forward 1 */
74 if (str[count] == _T(' '))
75 count++;
76
77 start = count;
78
79 if (str[count] == _T('"'))
80 count++; /* don't increment start */
81
82 /* extract directory from word */
83 _tcscpy (directory, &str[count]);
84 curplace = _tcslen (directory) - 1;
85
86 if (curplace >= 0 && directory[curplace] == _T('"'))
87 directory[curplace--] = _T('\0');
88
89 _tcscpy (path, directory);
90
91 while (curplace >= 0 && 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 #ifdef __REACTOS__
175 Beep (440, 50);
176 #else
177 MessageBeep (-1);
178 #endif
179 }
180 }
181 else
182 {
183 /* no match found - search for internal command */
184 for (cmds_ptr = cmds; cmds_ptr->name; cmds_ptr++)
185 {
186 if (!_tcsnicmp (&str[start], cmds_ptr->name,
187 _tcslen (&str[start])))
188 {
189 /* return the mach only if it is unique */
190 if (_tcsnicmp (&str[start], (cmds_ptr+1)->name, _tcslen (&str[start])))
191 _tcscpy (&str[start], cmds_ptr->name);
192 break;
193 }
194 }
195
196 #ifdef __REACTOS__
197 Beep (440, 50);
198 #else
199 MessageBeep (-1);
200 #endif
201 }
202 }
203
204
205 /*
206 * returns 1 if at least one match, else returns 0
207 */
208
209 BOOL ShowCompletionMatches (LPTSTR str, INT charcount)
210 {
211 WIN32_FIND_DATA file;
212 HANDLE hFile;
213 BOOL found_dot = FALSE;
214 INT curplace = 0;
215 INT start;
216 UINT count;
217 TCHAR path[MAX_PATH];
218 TCHAR fname[MAX_PATH];
219 TCHAR directory[MAX_PATH];
220 UINT longestfname = 0;
221 SHORT screenwidth;
222
223 /* expand current file name */
224 count = charcount - 1;
225 if (count < 0)
226 count = 0;
227
228 /* find front of word */
229 if (str[count] == _T('"'))
230 {
231 count--;
232 while (count > 0 && str[count] != _T('"'))
233 count--;
234 }
235 else
236 {
237 while (count > 0 && str[count] != _T(' '))
238 count--;
239 }
240
241 /* if not at beginning, go forward 1 */
242 if (str[count] == _T(' '))
243 count++;
244
245 start = count;
246
247 if (str[count] == _T('"'))
248 count++; /* don't increment start */
249
250 /* extract directory from word */
251 _tcscpy (directory, &str[count]);
252 curplace = _tcslen (directory) - 1;
253
254 if (curplace >= 0 && directory[curplace] == _T('"'))
255 directory[curplace--] = _T('\0');
256
257 _tcscpy (path, directory);
258
259 while (curplace >= 0 &&
260 directory[curplace] != _T('\\') &&
261 directory[curplace] != _T(':'))
262 {
263 directory[curplace] = 0;
264 curplace--;
265 }
266
267 /* look for a . in the filename */
268 for (count = _tcslen (directory); path[count] != _T('\0'); count++)
269 {
270 if (path[count] == _T('.'))
271 {
272 found_dot = TRUE;
273 break;
274 }
275 }
276
277 if (found_dot)
278 _tcscat (path, _T("*"));
279 else
280 _tcscat (path, _T("*.*"));
281
282 /* current fname */
283 curplace = 0;
284
285 hFile = FindFirstFile (path, &file);
286 if (hFile != INVALID_HANDLE_VALUE)
287 {
288 /* Get the size of longest filename first. */
289 do
290 {
291 if (_tcslen(file.cFileName) > longestfname)
292 {
293 longestfname = _tcslen(file.cFileName);
294 /* Directories get extra brackets around them. */
295 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
296 longestfname += 2;
297 }
298 }
299 while (FindNextFile (hFile, &file));
300 FindClose (hFile);
301
302 hFile = FindFirstFile (path, &file);
303
304 /* Count the highest number of columns */
305 GetScreenSize(&screenwidth, 0);
306
307 /* For counting columns of output */
308 count = 0;
309
310 /* Increase by the number of spaces behind file name */
311 longestfname += 3;
312
313 /* find anything */
314 ConOutChar (_T('\n'));
315 do
316 {
317 /* ignore . and .. */
318 if (!_tcscmp (file.cFileName, _T(".")) ||
319 !_tcscmp (file.cFileName, _T("..")))
320 continue;
321
322 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
323 _stprintf (fname, _T("[%s]"), file.cFileName);
324 else
325 _tcscpy (fname, file.cFileName);
326
327 ConOutPrintf (_T("%*s"), - longestfname, fname);
328 count++;
329 /* output as much columns as fits on the screen */
330 if (count >= (screenwidth / longestfname))
331 {
332 /* print the new line only if we aren't on the
333 * last column, in this case it wraps anyway */
334 if (count * longestfname != (UINT)screenwidth)
335 ConOutPrintf (_T("\n"));
336 count = 0;
337 }
338 }
339 while (FindNextFile (hFile, &file));
340
341 FindClose (hFile);
342
343 if (count)
344 ConOutChar (_T('\n'));
345 }
346 else
347 {
348 /* no match found */
349 #ifdef __REACTOS__
350 Beep (440, 50);
351 #else
352 MessageBeep (-1);
353 #endif
354 return FALSE;
355 }
356
357 return TRUE;
358 }
359 #endif
360
361 #ifdef FEATURE_4NT_FILENAME_COMPLETION
362
363 typedef struct _FileName
364 {
365 TCHAR Name[MAX_PATH];
366 } FileName;
367
368 VOID FindPrefixAndSuffix(LPTSTR strIN, LPTSTR szPrefix, LPTSTR szSuffix)
369 {
370 /* String that is to be examined */
371 TCHAR str[MAX_PATH];
372 /* temp pointers to used to find needed parts */
373 TCHAR * szSearch;
374 TCHAR * szSearch1;
375 TCHAR * szSearch2;
376 /* number of quotes in the string */
377 INT nQuotes = 0;
378 /* used in for loops */
379 INT i;
380 /* Char number to break the string at */
381 INT PBreak = 0;
382 INT SBreak = 0;
383 /* when phrasing a string, this tells weather
384 you are inside quotes ot not. */
385 BOOL bInside = FALSE;
386
387 szPrefix[0] = _T('\0');
388 szSuffix[0] = _T('\0');
389
390 /* Copy over the string to later be edited */
391 _tcscpy(str,strIN);
392
393 /* Count number of " */
394 for(i = 0; i < _tcslen(str); i++)
395 if(str[i] == _T('\"'))
396 nQuotes++;
397
398 /* Find the prefix and suffix */
399 if(nQuotes % 2 && nQuotes)
400 {
401 /* Odd number of quotes. Just start from the last " */
402 /* THis is the way MS does it, and is an easy way out */
403 szSearch = _tcsrchr(str, _T('\"'));
404 /* Move to the next char past the " */
405 szSearch++;
406 _tcscpy(szSuffix,szSearch);
407 /* Find the one closest to end */
408 szSearch1 = _tcsrchr(str, _T('\"'));
409 szSearch2 = _tcsrchr(str, _T('\\'));
410 if(szSearch2 != NULL && _tcslen(szSearch1) > _tcslen(szSearch2))
411 szSearch = szSearch2;
412 else
413 szSearch = szSearch1;
414 /* Move one char past */
415 szSearch++;
416 szSearch[0] = _T('\0');
417 _tcscpy(szPrefix,str);
418 return;
419
420 }
421
422 if(!_tcschr(str, _T(' ')))
423 {
424 /* No spaces, everything goes to Suffix */
425 _tcscpy(szSuffix,str);
426 /* look for a slash just in case */
427 szSearch = _tcsrchr(str, _T('\\'));
428 if(szSearch)
429 {
430 szSearch++;
431 szSearch[0] = _T('\0');
432 _tcscpy(szPrefix,str);
433 }
434 else
435 {
436 szPrefix[0] = _T('\0');
437 }
438 return;
439 }
440
441 if(!nQuotes)
442 {
443 /* No quotes, and there is a space*/
444 /* Take it after the last space */
445 szSearch = _tcsrchr(str, _T(' '));
446 szSearch++;
447 _tcscpy(szSuffix,szSearch);
448 /* Find the closest to the end space or \ */
449 _tcscpy(str,strIN);
450 szSearch1 = _tcsrchr(str, _T(' '));
451 szSearch2 = _tcsrchr(str, _T('\\'));
452 if(szSearch2 != NULL && _tcslen(szSearch1) > _tcslen(szSearch2))
453 szSearch = szSearch2;
454 else
455 szSearch = szSearch1;
456 szSearch++;
457 szSearch[0] = _T('\0');
458 _tcscpy(szPrefix,str);
459 return;
460 }
461
462 /* All else fails and there is a lot of quotes, spaces and |
463 Then we search through and find the last space or \ that is
464 not inside a quotes */
465 for(i = 0; i < _tcslen(str); i++)
466 {
467 if(str[i] == _T('\"'))
468 bInside = !bInside;
469 if(str[i] == _T(' ') && !bInside)
470 SBreak = i;
471 if((!_tcsncmp(&str[i], _T(" "),1) || !_tcsncmp(&str[i], _T("\\"),1)) && !bInside)
472 PBreak = i;
473
474 }
475 SBreak++;
476 PBreak++;
477 _tcscpy(szSuffix,&strIN[SBreak]);
478 strIN[PBreak] = _T('\0');
479 _tcscpy(szPrefix,strIN);
480 if(!_tcsncmp(&szPrefix[_tcslen(szPrefix) - 2],_T("\""),1))
481 {
482 /* need to remove the " right before a \ at the end to
483 allow the next stuff to stay inside one set of quotes
484 otherwise you would have multiple sets of quotes*/
485 _tcscpy(&szPrefix[_tcslen(szPrefix) - 2],_T("\\"));
486
487 }
488
489 }
490 int compare(const void *arg1,const void *arg2)
491 {
492 FileName * File1;
493 FileName * File2;
494 INT ret;
495
496 File1 = malloc(sizeof(FileName));
497 File2 = malloc(sizeof(FileName));
498 if(!File1 || !File2)
499 return 0;
500
501 memcpy(File1,arg1,sizeof(FileName));
502 memcpy(File2,arg2,sizeof(FileName));
503
504 /* ret = _tcsicmp(File1->Name, File2->Name); */
505 ret = lstrcmpi(File1->Name, File2->Name);
506
507 free(File1);
508 free(File2);
509 return ret;
510 }
511
512 VOID CompleteFilename (LPTSTR strIN, BOOL bNext, LPTSTR strOut, INT cusor)
513 {
514 /* Length of string before we complete it */
515 INT StartLength;
516 /* Length of string after completed */
517 INT EndLength;
518 /* The number of chars added too it */
519 static INT DiffLength = 0;
520 /* Used to find and assemble the string that is returned */
521 TCHAR szBaseWord[MAX_PATH];
522 TCHAR szPrefix[MAX_PATH];
523 TCHAR szOrginal[MAX_PATH];
524 TCHAR szSearchPath[MAX_PATH];
525 /* Save the strings used last time, so if they hit tab again */
526 static TCHAR LastReturned[MAX_PATH];
527 static TCHAR LastSearch[MAX_PATH];
528 static TCHAR LastPrefix[MAX_PATH];
529 /* Used to search for files */
530 HANDLE hFile;
531 WIN32_FIND_DATA file;
532 /* List of all the files */
533 FileName * FileList = NULL;
534 /* Number of files */
535 INT FileListSize = 0;
536 /* Used for loops */
537 INT i;
538 INT ii;
539 /* Editable string of what was passed in */
540 TCHAR str[MAX_PATH];
541 /* Keeps track of what element was last selected */
542 static INT Sel;
543 BOOL NeededQuote = FALSE;
544
545 strOut[0] = _T('\0');
546
547 /* Copy the string, str can be edited and orginal should not be */
548 _tcscpy(str,strIN);
549 _tcscpy(szOrginal,strIN);
550
551 /* Look to see if the cusor is not at the end of the string */
552 if((cusor + 1) < _tcslen(str))
553 str[cusor] = _T('\0');
554
555 /* Look to see if they hit tab again, if so cut off the diff length */
556 if(_tcscmp(str,LastReturned) || !_tcslen(str))
557 {
558 /* We need to know how many chars we added from the start */
559 StartLength = _tcslen(str);
560
561 /* no string, we need all files in that directory */
562 if(!StartLength)
563 {
564 _tcscat(str,_T("*"));
565 }
566
567 /* Zero it out first */
568 szBaseWord[0] = _T('\0');
569 szPrefix[0] = _T('\0');
570
571 /*What comes out of this needs to be:
572 szBaseWord = path no quotes to the object
573 szPrefix = what leads up to the filename
574 no quote at the END of the full name */
575 FindPrefixAndSuffix(str,szPrefix,szBaseWord);
576 /* Strip quotes */
577 for(i = 0; i < _tcslen(szBaseWord); i++)
578 {
579 if(!_tcsncmp(&szBaseWord[i], _T("\""),1))
580 {
581 for(ii = i; ii < (_tcslen(szBaseWord)); ii++)
582 szBaseWord[ii] = szBaseWord[ii + 1];
583 }
584 }
585
586 /* clear it out */
587 memset(szSearchPath, 0, sizeof(szSearchPath));
588
589 /* Start the search for all the files */
590 GetFullPathName(szBaseWord, MAX_PATH, szSearchPath, NULL);
591 if(StartLength > 0)
592 _tcscat(szSearchPath,_T("*"));
593 _tcscpy(LastSearch,szSearchPath);
594 _tcscpy(LastPrefix,szPrefix);
595 }
596 else
597 {
598 _tcscpy(szSearchPath, LastSearch);
599 _tcscpy(szPrefix, LastPrefix);
600 StartLength = 0;
601 }
602 /* search for the files it might be */
603 hFile = FindFirstFile (szSearchPath, &file);
604
605 /* aseemble a list of all files names */
606 do
607 {
608 if(hFile == INVALID_HANDLE_VALUE)
609 {
610 /* Assemble the orginal string and return */
611 _tcscpy(strOut,szOrginal);
612 CloseHandle(hFile);
613 if(FileList != NULL)
614 free(FileList);
615 return;
616 }
617
618 if(!_tcscmp (file.cFileName, _T(".")) ||
619 !_tcscmp (file.cFileName, _T("..")))
620 continue;
621 /* Add the file to the list of files */
622 if(FileList == NULL)
623 {
624 FileListSize = 1;
625 FileList = malloc(FileListSize * sizeof(FileName));
626 }
627 else
628 {
629 FileListSize++;
630 FileList = realloc(FileList, FileListSize * sizeof(FileName));
631 }
632
633 if(FileList == NULL)
634 {
635 /* Assemble the orginal string and return */
636 _tcscpy(strOut,szOrginal);
637 CloseHandle(hFile);
638 ConOutFormatMessage (GetLastError());
639 return;
640 }
641 /* Copies the file name into the struct */
642 _tcscpy(FileList[FileListSize-1].Name,file.cFileName);
643
644 }while(FindNextFile(hFile,&file));
645
646
647 /* Sort the files */
648 qsort(FileList,FileListSize,sizeof(FileName), compare);
649
650 /* Find the next/previous */
651 if(!_tcscmp(szOrginal,LastReturned))
652 {
653 if(bNext)
654 {
655 if(FileListSize - 1 == Sel)
656 Sel = 0;
657 else
658 Sel++;
659 }
660 else
661 {
662 if(!Sel)
663 Sel = FileListSize - 1;
664 else
665 Sel--;
666 }
667 }
668 else
669 {
670 Sel = 0;
671 }
672
673 /* nothing found that matched last time
674 so return the first thing in the list */
675 strOut[0] = _T('\0');
676
677 /* space in the name */
678 if(_tcschr(FileList[Sel].Name, _T(' ')))
679 {
680 /* It needs a " at the end */
681 NeededQuote = TRUE;
682 INT LastSpace = -1;
683 BOOL bInside = FALSE;
684 /* Find the place to put the " at the start */
685 for(i = 0; i < _tcslen(szPrefix); i++)
686 {
687 if(szPrefix[i] == _T('\"'))
688 bInside = !bInside;
689 if(szPrefix[i] == _T(' ') && !bInside)
690 LastSpace = i;
691
692 }
693 /* insert the space and move things around */
694 if(_tcsncmp(&szPrefix[LastSpace + 1],_T("\""),1) && LastSpace != -1)
695 {
696 /* add another char or you will lose a null char ending */
697 _tcsncat(szPrefix,&szPrefix[_tcslen(szPrefix) - 1],1);
698 for(i = _tcslen(szPrefix) - 1; i > LastSpace; i--)
699 {
700 szPrefix[i] = szPrefix[i - 1];
701 }
702
703 if(LastSpace + 1 == _tcslen(szPrefix))
704 {
705 _tcscat(szPrefix,_T("\""));
706 }
707 szPrefix[LastSpace + 1] = _T('\"');
708 }
709 else if(LastSpace == -1)
710 {
711 _tcscpy(szBaseWord,_T("\""));
712 _tcscat(szBaseWord,szPrefix);
713 _tcscpy(szPrefix,szBaseWord);
714
715 }
716 }
717
718 _tcscpy(strOut,szPrefix);
719 _tcscat(strOut,FileList[Sel].Name);
720
721 /* check for odd number of quotes means we need to close them */
722 if(!NeededQuote)
723 {
724 for(i = 0; i < _tcslen(strOut); i++)
725 if(strOut[i] == _T('\"'))
726 NeededQuote = !NeededQuote;
727 }
728
729 if(szPrefix[_tcslen(szPrefix) - 1] == _T('\"') || NeededQuote)
730 _tcscat(strOut,_T("\""));
731
732 _tcscpy(LastReturned,strOut);
733 EndLength = _tcslen(strOut);
734 DiffLength = EndLength - StartLength;
735 CloseHandle(hFile);
736 if(FileList != NULL)
737 free(FileList);
738
739 }
740 #endif