Command line completion fix (uninitialized memory)
[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
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 /* append a space if last word is not a directory */
173 if(perfectmatch)
174 {
175 curplace = _tcslen(&str[start]);
176 if(str[start+curplace-1] == _T('"'))
177 curplace--;
178
179 if(str[start+curplace-1] != _T('\\'))
180 _tcscat(&str[start], _T(" "));
181 }
182 else
183 {
184 #ifdef __REACTOS__
185 Beep (440, 50);
186 #else
187 MessageBeep (-1);
188 #endif
189 }
190 }
191 else
192 {
193 /* no match found - search for internal command */
194 for (cmds_ptr = cmds; cmds_ptr->name; cmds_ptr++)
195 {
196 if (!_tcsnicmp (&str[start], cmds_ptr->name,
197 _tcslen (&str[start])))
198 {
199 /* return the mach only if it is unique */
200 if (_tcsnicmp (&str[start], (cmds_ptr+1)->name, _tcslen (&str[start])))
201 _tcscpy (&str[start], cmds_ptr->name);
202 break;
203 }
204 }
205
206 #ifdef __REACTOS__
207 Beep (440, 50);
208 #else
209 MessageBeep (-1);
210 #endif
211 }
212 }
213
214
215 /*
216 * returns 1 if at least one match, else returns 0
217 */
218
219 BOOL ShowCompletionMatches (LPTSTR str, INT charcount)
220 {
221 WIN32_FIND_DATA file;
222 HANDLE hFile;
223 BOOL found_dot = FALSE;
224 INT curplace = 0;
225 INT start;
226 INT count;
227 TCHAR path[MAX_PATH];
228 TCHAR fname[MAX_PATH];
229 TCHAR directory[MAX_PATH];
230 INT longestfname = 0;
231 SHORT screenwidth;
232
233 /* expand current file name */
234 count = charcount - 1;
235 if (count < 0)
236 count = 0;
237
238 /* find front of word */
239 if (str[count] == _T('"'))
240 {
241 count--;
242 while (count > 0 && str[count] != _T('"'))
243 count--;
244 }
245 else
246 {
247 while (count > 0 && str[count] != _T(' '))
248 count--;
249 }
250
251 /* if not at beginning, go forward 1 */
252 if (str[count] == _T(' '))
253 count++;
254
255 start = count;
256
257 if (str[count] == _T('"'))
258 count++; /* don't increment start */
259
260 /* extract directory from word */
261 _tcscpy (directory, &str[count]);
262 curplace = _tcslen (directory) - 1;
263
264 if (curplace >= 0 && directory[curplace] == _T('"'))
265 directory[curplace--] = _T('\0');
266
267 _tcscpy (path, directory);
268
269 while (curplace >= 0 &&
270 directory[curplace] != _T('\\') &&
271 directory[curplace] != _T(':'))
272 {
273 directory[curplace] = 0;
274 curplace--;
275 }
276
277 /* look for a . in the filename */
278 for (count = _tcslen (directory); path[count] != _T('\0'); count++)
279 {
280 if (path[count] == _T('.'))
281 {
282 found_dot = TRUE;
283 break;
284 }
285 }
286
287 if (found_dot)
288 _tcscat (path, _T("*"));
289 else
290 _tcscat (path, _T("*.*"));
291
292 /* current fname */
293 curplace = 0;
294
295 hFile = FindFirstFile (path, &file);
296 if (hFile != INVALID_HANDLE_VALUE)
297 {
298 /* Get the size of longest filename first. */
299 do
300 {
301 if (_tcslen(file.cFileName) > longestfname)
302 {
303 longestfname = _tcslen(file.cFileName);
304 /* Directories get extra brackets around them. */
305 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
306 longestfname += 2;
307 }
308 }
309 while (FindNextFile (hFile, &file));
310 FindClose (hFile);
311
312 hFile = FindFirstFile (path, &file);
313
314 /* Count the highest number of columns */
315 GetScreenSize(&screenwidth, 0);
316
317 /* For counting columns of output */
318 count = 0;
319
320 /* Increase by the number of spaces behind file name */
321 longestfname += 3;
322
323 /* find anything */
324 ConOutChar (_T('\n'));
325 do
326 {
327 /* ignore . and .. */
328 if (!_tcscmp (file.cFileName, _T(".")) ||
329 !_tcscmp (file.cFileName, _T("..")))
330 continue;
331
332 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
333 _stprintf (fname, _T("[%s]"), file.cFileName);
334 else
335 _tcscpy (fname, file.cFileName);
336
337 ConOutPrintf (_T("%*s"), - longestfname, fname);
338 count++;
339 /* output as much columns as fits on the screen */
340 if (count >= (screenwidth / longestfname))
341 {
342 /* print the new line only if we aren't on the
343 * last column, in this case it wraps anyway */
344 if (count * longestfname != screenwidth)
345 ConOutPrintf (_T("\n"));
346 count = 0;
347 }
348 }
349 while (FindNextFile (hFile, &file));
350
351 FindClose (hFile);
352
353 if (count)
354 ConOutChar (_T('\n'));
355 }
356 else
357 {
358 /* no match found */
359 #ifdef __REACTOS__
360 Beep (440, 50);
361 #else
362 MessageBeep (-1);
363 #endif
364 return FALSE;
365 }
366
367 return TRUE;
368 }
369 #endif
370
371 #ifdef FEATURE_4NT_FILENAME_COMPLETION
372
373 //static VOID BuildFilenameMatchList (...)
374
375 // VOID CompleteFilenameNext (LPTSTR, INT)
376 // VOID CompleteFilenamePrev (LPTSTR, INT)
377
378 // VOID RemoveFilenameMatchList (VOID)
379
380 #endif