Leaked source code of windows server 2003
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.

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