Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

379 lines
9.7 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. complete.c
  5. Abstract:
  6. File/path name completion support
  7. --*/
  8. #include "cmd.h"
  9. //
  10. // Upon the first completion, pCompleteBuffer is a pointer to an array
  11. // of matching full path names
  12. //
  13. TCHAR **pCompleteBuffer;
  14. //
  15. // The count of strings stored in pCompleteBuffer
  16. //
  17. int nBufSize;
  18. //
  19. // The index in pCompleteBuffer of the current match displayed
  20. //
  21. int nCurrentSpec;
  22. //
  23. // There are two types of completion matching, path and directory. This is the current
  24. // matching being done.
  25. //
  26. int bCurrentSpecType;
  27. //
  28. // When called for completion, the location of the beginning of the path is found
  29. // and stored in nCurrentSpecPathStart. This is relative to the buffer passed
  30. // in to DoComplete
  31. //
  32. int nCurrentSpecPathStart;
  33. int CompleteDir( TCHAR *pFileSpecBuffer, int, int );
  34. //
  35. // Characters that CMD uses for its own grammar. To use these in filenames
  36. // requires quoting
  37. //
  38. TCHAR szSpecialFileCharsToQuote[] = TEXT(" &()[]{}^=;!%'+,`~");
  39. void
  40. DoCompleteInitialize( VOID )
  41. {
  42. pCompleteBuffer = NULL;
  43. nBufSize = 0;
  44. bCurrentSpecType = 0;
  45. nCurrentSpecPathStart = 0;
  46. nCurrentSpec = 0;
  47. return;
  48. }
  49. int
  50. DoComplete(
  51. TCHAR *buffer,
  52. int len,
  53. int maxlen,
  54. int bForward,
  55. int bPathCompletion,
  56. int bTouched)
  57. /*++
  58. Routine Description:
  59. This is used whenever a path completion character is seen on input. It updates the
  60. input buffer with the next matching text and returns the updated size.
  61. Arguments:
  62. buffer - input string that contains the prefix of the path to match at the end.
  63. len - length of the input string.
  64. maxlen - maximum string length that can be stored in buffer.
  65. bForward - TRUE => matching goes forward through the storted match list. Otherwise
  66. move backwards through the list.
  67. bPathCompletion - TRUE => we match ONLY directories and not files+directories.
  68. bTouched - TRUE => the user has edited the path. This usually means that we need to
  69. begin the matching process anew.
  70. Return Value:
  71. Zero if no matching entries were found, otherwise the length of the updated buffer.
  72. --*/
  73. {
  74. TCHAR pFileSpecBuffer[512];
  75. int nBufPos;
  76. int nPathStart;
  77. int nFileStart;
  78. int k;
  79. BOOL bWildSeen;
  80. //
  81. // If the user edited the previous match or if the form of completion (dir vs
  82. // dir/file) changed, then we must rebuild the matching information
  83. //
  84. if ( bTouched || (bCurrentSpecType != bPathCompletion)) {
  85. BOOL InQuotes = FALSE;
  86. //
  87. // The following code was shipped in NT 4 and Windows 2000. It presented
  88. // a usability problem when changing matching forms in mid-stream. We will
  89. // now treat a simple change of completion type the same as being touched
  90. // by the user: rebuild the matching database from where we are presently.
  91. //
  92. // //
  93. // // if the buffer was left alone but the matching style
  94. // // was changed, then start the matching process at the
  95. // // beginning of the path
  96. // //
  97. //
  98. // if (!bTouched && (bCurrentSpecType != bPathCompletion)) {
  99. // buffer[nCurrentSpecPathStart] = NULLC;
  100. // len = nCurrentSpecPathStart;
  101. // }
  102. //
  103. // Determine the beginning of the path and file name. We
  104. // need to take into account the presence of quotes and the
  105. // need for CMD to introduce quoting as well
  106. //
  107. nPathStart = 0;
  108. nFileStart = -1;
  109. bWildSeen = FALSE;
  110. for ( k = 0; k < len; k++ ) {
  111. if (buffer[k] == SWITCHAR) {
  112. nPathStart = k + 1;
  113. bWildSeen = FALSE;
  114. } else if ( buffer[k] == QUOTE ) {
  115. if ( !InQuotes )
  116. nPathStart = k;
  117. InQuotes = !InQuotes;
  118. } else if ( !InQuotes &&
  119. _tcschr(szSpecialFileCharsToQuote, buffer[k]) != NULL
  120. ) {
  121. nPathStart = k+1;
  122. bWildSeen = FALSE;
  123. } else if (buffer[k] == COLON ||
  124. buffer[k] == BSLASH
  125. ) {
  126. nFileStart = k+1;
  127. bWildSeen = FALSE;
  128. } else if (buffer[k] == STAR || buffer[k] == QMARK) {
  129. bWildSeen = TRUE;
  130. }
  131. }
  132. if (nFileStart == -1 || nFileStart < nPathStart)
  133. nFileStart = nPathStart;
  134. _tcsncpy( pFileSpecBuffer, &(buffer[nPathStart]), len-nPathStart );
  135. if (!bWildSeen) {
  136. pFileSpecBuffer[len-nPathStart+0] = TEXT('*');
  137. pFileSpecBuffer[len-nPathStart+1] = TEXT('\0');
  138. } else {
  139. pFileSpecBuffer[len-nPathStart+0] = TEXT('\0');
  140. }
  141. // do the DIR into a buffer
  142. nBufSize = CompleteDir( pFileSpecBuffer, bPathCompletion, nFileStart - nPathStart );
  143. // reset the current completion string
  144. nCurrentSpec = nBufSize;
  145. nCurrentSpecPathStart = nPathStart;
  146. bCurrentSpecType = bPathCompletion;
  147. }
  148. // if no matches, do nothing.
  149. if ( nBufSize == 0 ) {
  150. return 0;
  151. }
  152. // find our postion in the completion buffer.
  153. if ( bForward ) {
  154. nCurrentSpec++;
  155. if ( nCurrentSpec >= nBufSize )
  156. nCurrentSpec = 0;
  157. } else {
  158. nCurrentSpec--;
  159. if ( nCurrentSpec < 0 )
  160. nCurrentSpec = nBufSize - 1;
  161. }
  162. // Return nothing if buffer not big enough
  163. if ((int)(nCurrentSpecPathStart+_tcslen(pCompleteBuffer[nCurrentSpec])) >= maxlen)
  164. return 0;
  165. // copy the completion path onto the end of the command line
  166. _tcscpy(&buffer[nCurrentSpecPathStart], pCompleteBuffer[nCurrentSpec] );
  167. return nBufSize;
  168. }
  169. int
  170. CompleteDir(
  171. TCHAR *pFileSpecBuffer,
  172. int bPathCompletion,
  173. int nFileStart
  174. )
  175. {
  176. PFS pfsCur;
  177. PSCREEN pscr;
  178. DRP drpCur = {0, 0, 0, 0,
  179. {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
  180. 0, 0, NULL, 0, 0, 0, 0} ;
  181. int hits = 0;
  182. int i, j, nFileLen;
  183. unsigned Err;
  184. TCHAR *s, *d, *pszFileStart;
  185. BOOLEAN bNeedQuotes;
  186. ULONG rgfAttribs, rgfAttribsOnOff;
  187. if (pCompleteBuffer != NULL) {
  188. // free the old buffer
  189. for( i=0; i<nBufSize; i++ ){
  190. free( pCompleteBuffer[i] );
  191. }
  192. free( pCompleteBuffer );
  193. pCompleteBuffer = NULL;
  194. }
  195. // fake up a screen to print into
  196. pscr = (PSCREEN)gmkstr(sizeof(SCREEN));
  197. pscr->ccol = 2048;
  198. rgfAttribs = 0;
  199. rgfAttribsOnOff = 0;
  200. if (bPathCompletion) {
  201. rgfAttribs = FILE_ATTRIBUTE_DIRECTORY;
  202. rgfAttribsOnOff = FILE_ATTRIBUTE_DIRECTORY;
  203. }
  204. ParseDirParms(pFileSpecBuffer, &drpCur);
  205. if ( (drpCur.patdscFirst.pszPattern == NULL) ||
  206. (SetFsSetSaveDir(drpCur.patdscFirst.pszPattern) == (PCPYINFO) FAILURE) ||
  207. (BuildFSFromPatterns(&drpCur, FALSE, TRUE, &pfsCur) == FAILURE) ) {
  208. RestoreSavedDirectory( );
  209. return( 0 );
  210. }
  211. Err =
  212. ExpandAndApplyToFS( pfsCur,
  213. pscr,
  214. rgfAttribs,
  215. rgfAttribsOnOff,
  216. NULL,
  217. NULL,
  218. NULL,
  219. NULL,
  220. NULL );
  221. if (Err) {
  222. RestoreSavedDirectory( );
  223. return 0;
  224. }
  225. //
  226. // Make sure there is something to sort, then sort
  227. //
  228. if (pfsCur->cff) {
  229. qsort( pfsCur->prgpff,
  230. pfsCur->cff,
  231. sizeof(PTCHAR),
  232. CmpName
  233. );
  234. }
  235. s = pFileSpecBuffer;
  236. d = s;
  237. bNeedQuotes = FALSE;
  238. while (*s) {
  239. if (*s == QUOTE) {
  240. bNeedQuotes = TRUE;
  241. s += 1;
  242. if (nFileStart >= (s-pFileSpecBuffer))
  243. nFileStart -= 1;
  244. if (*s == QUOTE)
  245. *d++ = *s++;
  246. }
  247. else {
  248. if (_tcschr(szSpecialFileCharsToQuote, *s) != NULL)
  249. bNeedQuotes = TRUE;
  250. *d++ = *s++;
  251. }
  252. }
  253. *d = NULLC;
  254. hits = pfsCur->cff;
  255. pCompleteBuffer = calloc( sizeof(TCHAR *), hits );
  256. if (pCompleteBuffer == NULL) {
  257. RestoreSavedDirectory( );
  258. return 0;
  259. }
  260. for(i=0, j=0; i<hits; i++) {
  261. if (!_tcscmp((TCHAR *)(pfsCur->prgpff[i]->data.cFileName), TEXT(".") )
  262. || !_tcscmp((TCHAR *)(pfsCur->prgpff[i]->data.cFileName), TEXT("..") )) {
  263. continue;
  264. }
  265. nFileLen = _tcslen( (TCHAR *)(pfsCur->prgpff[i]->data.cFileName) );
  266. pCompleteBuffer[j] = (TCHAR *)calloc( (nFileStart + nFileLen + 4) , sizeof( TCHAR ));
  267. if (pCompleteBuffer[j] == NULL) {
  268. continue;
  269. }
  270. if (!bNeedQuotes) {
  271. s = (TCHAR *)(pfsCur->prgpff[i]->data.cFileName);
  272. while (*s) {
  273. if (_tcschr(szSpecialFileCharsToQuote, *s) != NULL)
  274. bNeedQuotes = TRUE;
  275. s += 1;
  276. }
  277. }
  278. else
  279. s = NULL;
  280. d = pCompleteBuffer[j];
  281. if (bNeedQuotes)
  282. *d++ = QUOTE;
  283. _tcsncpy( d, pFileSpecBuffer, nFileStart );
  284. d += nFileStart;
  285. _tcsncpy( d, (TCHAR *)(pfsCur->prgpff[i]->data.cFileName), nFileLen );
  286. d += nFileLen;
  287. if (bNeedQuotes) {
  288. *d++ = QUOTE;
  289. if (s)
  290. bNeedQuotes = FALSE;
  291. }
  292. *d++ = NULLC;
  293. j++;
  294. }
  295. hits = j;
  296. FreeStr((PTCHAR)(pfsCur->pff));
  297. FreeStr(pfsCur->pszDir);
  298. FreeStr((PTCHAR)pfsCur);
  299. RestoreSavedDirectory( );
  300. return hits;
  301. }