Windows NT 4.0 source code leak
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.

683 lines
24 KiB

4 years ago
  1. #include "cmd.h"
  2. extern unsigned tywild; /* type is wild flag */
  3. extern TCHAR CurDrvDir[], *SaveDir, PathChar, Delimiters[] ;
  4. extern TCHAR VolSrch[] ;
  5. extern TCHAR BSlash ;
  6. extern TCHAR TmpBuf[] ;
  7. extern unsigned DosErr ;
  8. extern BOOL CtrlCSeen;
  9. static TCHAR szNull[] = TEXT("");
  10. /*** TokStr - tokenize argument strings
  11. *
  12. * Purpose:
  13. * Tokenize a string.
  14. * Allocate space for a new string and copy each token in src into the
  15. * new string and null terminate it. Tokens are whitespace delimited
  16. * unless changed by specialdelims and/or tsflags. The entire tokenized
  17. * string ends with 2 null bytes.
  18. *
  19. * TCHAR *TokStr(TCHAR *src, TCHAR *specialdelims, unsigned tsflags)
  20. *
  21. * Args:
  22. * src - the string to be tokenized
  23. * specialdelims - a string of other characters which are to be comsidered
  24. * as token delimiters
  25. * tsflags - bit 0 nonzero if whitespace are NOT delimiters
  26. * bit 1 nonzero if special delimiters are tokens themselves,
  27. * eg "foo=bar" => "foo0=0bar00"
  28. *
  29. * Returns:
  30. * A pointer to the new string.
  31. * A pointer to a null string if src is NULL
  32. * NULL if unable to allocate memory.
  33. *
  34. * Notes:
  35. * The format of the tokenized string dictates the way code is written
  36. * to process the tokens in that string. For instance, the code
  37. * "s += mystrlen(s)+1" is the way to update s to point to the next token
  38. * in the string.
  39. *
  40. * Command considers "=", ",", and ";" to be token delimiters just like
  41. * whitespace. The only time they are not treated like whitespace is
  42. * when they are included in specialdelims.
  43. *
  44. * *** W A R N I N G ! ***
  45. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  46. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  47. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  48. *
  49. */
  50. TCHAR *TokStr(src, specialdelims, tsflags)
  51. TCHAR *src ;
  52. TCHAR *specialdelims ;
  53. unsigned tsflags ;
  54. {
  55. TCHAR *ts ; /* Tokenized string pointer */
  56. TCHAR *tscpy, /* Copy of ts */
  57. delist[5], /* List of non-whitespace delimiter/separators */
  58. c; /* Work variable */
  59. int first, /* Flag, true if first time through the loop */
  60. lctdelim, /* Flag, true if last byte was token delimiter */
  61. i, j ; /* Index/loop counter */
  62. DEBUG((CTGRP, TSLVL, "TOKSTR: Entered &src = %04x, src = %ws",src,src));
  63. DEBUG((CTGRP, TSLVL, "TOKSTR: Making copy str of len %d",(mystrlen(src)*2+2))) ;
  64. if (src == NULL) {
  65. return(szNull); // This routine returns a doubly null terminated string
  66. } else {
  67. ts = tscpy = gmkstr((mystrlen(src)*2+2)*sizeof(TCHAR)) ; /*WARNING*/
  68. DEBUG((CTGRP, TSLVL, "TOKSTR: &tscpy = %04x",tscpy)) ;
  69. for (i = j = 0 ; c = *(&Delimiters[i]) ; i++)
  70. if (!mystrchr(specialdelims, c))
  71. delist[j++] = c ;
  72. delist[j] = NULLC ;
  73. DEBUG((CTGRP, TSLVL, "TOKSTR: Delimiter string built as `%ws'",delist)) ;
  74. for (first = TRUE, lctdelim = TRUE ; *src ; src++) {
  75. if (
  76. (*src != QUOTE) &&
  77. (_istspace(*src) || mystrchr(delist, *src)) &&
  78. (!(tsflags & TS_WSPACE) || first) &&
  79. (!(tsflags & TS_SDTOKENS) || !mystrchr(specialdelims, *src)) &&
  80. (!(tsflags & TS_NWSPACE) || !mystrchr(specialdelims, *src)) ) {
  81. while ( *src &&
  82. (*src != QUOTE) &&
  83. (_istspace(*src) || mystrchr(delist, *src)) &&
  84. (!(tsflags & TS_SDTOKENS) || !mystrchr(specialdelims, *src)) &&
  85. (!(tsflags & TS_NWSPACE) || !mystrchr(specialdelims, *src)) )
  86. src++ ;
  87. if (!(*src))
  88. break ;
  89. if (!first && !lctdelim)
  90. ts++ ;
  91. lctdelim = TRUE ;
  92. } ;
  93. first = FALSE ;
  94. if (specialdelims && mystrchr(specialdelims, *src)) {
  95. if (tsflags & TS_SDTOKENS) {
  96. if (lctdelim)
  97. *ts = *src ;
  98. else
  99. *++ts = *src ;
  100. lctdelim = TRUE ;
  101. ts++ ;
  102. } else {
  103. if ( tsflags & TS_NWSPACE )
  104. *ts = *src ;
  105. lctdelim = FALSE ;
  106. }
  107. ts++ ;
  108. continue ;
  109. } ;
  110. *ts++ = *src ;
  111. if ( *src == QUOTE ) {
  112. do {
  113. *ts++ = *(++src);
  114. } while ( src[0] && src[0] != QUOTE && src[1] );
  115. if ( !src[0] ) {
  116. src--;
  117. }
  118. }
  119. lctdelim = FALSE ;
  120. } ;
  121. DEBUG((CTGRP, TSLVL, "TOKSTR: String complete, resizing to %d",ts-tscpy+2)) ;
  122. return(resize(tscpy, (ts-tscpy+2)*sizeof(TCHAR))) ;
  123. DEBUG((CTGRP, TSLVL, "TOKSTR: Resizing done, returning")) ;
  124. }
  125. }
  126. /******************************************************************************/
  127. /* */
  128. /* LoopThroughArgs - call a function on all args in a list */
  129. /* */
  130. /* Purpose: */
  131. /* This is function is called by many of the commands that take */
  132. /* multiple arguments. This function will parse the argument string, */
  133. /* complain if no args were given, and call func on each of the ards */
  134. /* in argstr. Optionally, it will also expand any wildcards in the */
  135. /* arguments. Execution stops if func ever returns FAILURE. */
  136. /* */
  137. /* int LoopThroughArgs(TCHAR *argstr, int (*func)(), int ltaflags) */
  138. /* */
  139. /* Args: */
  140. /* argstr - argument string */
  141. /* func - the function to pass each element of argstr */
  142. /* ltaflags - bit 0 on if wildcards are to be expanded */
  143. /* bit 1 on if it's ok for argstr to be empty (nothing but whitespace) */
  144. /* bit 2 on if file names should be passed through un changed */
  145. /* when the wildcard expansion fails to find any matches.*/
  146. /* */
  147. /* Returns: */
  148. /* The value returned by func the last time it is run. */
  149. /* */
  150. /******************************************************************************/
  151. int LoopThroughArgs(argstr, func, ltaflags)
  152. TCHAR *argstr ;
  153. PLOOP_THROUGH_ARGS_ROUTINE func ;
  154. int ltaflags ;
  155. {
  156. TCHAR *tas ; /* Tokenized argument string */
  157. TCHAR fspec[MAX_PATH] ; /* Holds filespec when expanding */
  158. WIN32_FIND_DATA buf ; /* Use for ffirst/fnext */
  159. HANDLE hnFirst ;
  160. struct cpyinfo fsinfo ;
  161. int catspot ; /* Fspec index where fname should be added */
  162. unsigned final_code = SUCCESS;
  163. unsigned error_code = SUCCESS;
  164. int multargs = FALSE;
  165. unsigned attr;/* attribute for ffirst for because of TYPE wild */
  166. unsigned taslen;
  167. tywild = FALSE; /* global type wild card flag ret */
  168. GetDir(CurDrvDir, GD_DEFAULT);
  169. if (*(tas = TokStr(argstr, NULL, TS_NOFLAGS)) == NULLC)
  170. {
  171. if (ltaflags & LTA_NULLOK)
  172. {
  173. /* return((*func)(tas)) ; */
  174. return((*func)( stripit(tas) )) ;
  175. }
  176. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  177. return(FAILURE) ;
  178. }
  179. if (*(tas + mystrlen(tas) + 1) ) /* Check for multiple args */
  180. {
  181. multargs = TRUE;
  182. }
  183. for ( ; *tas ; tas += taslen+1 )
  184. {
  185. if (CtrlCSeen) {
  186. return(FAILURE);
  187. }
  188. taslen = mystrlen( tas );
  189. mystrcpy( tas, stripit( tas ) );
  190. if (ltaflags & LTA_EXPAND)
  191. {
  192. if (cmdfound == TYTYP) /* if TYPE cmd then only files */
  193. {
  194. attr = A_RO|A_A; /* find Read-only, arch & hidden */
  195. }
  196. else /* else */
  197. {
  198. attr = A_AEV; /* find all */
  199. }
  200. //
  201. // this is used to detect an error other then can not
  202. // find file. It is set in ffirst
  203. //
  204. DosErr = 0;
  205. if (!ffirst(tas, attr, &buf, &hnFirst))
  206. {
  207. //
  208. // Check that failure was not do to some system error such
  209. // as an abort on access to floppy
  210. //
  211. if (DosErr) {
  212. if ((DosErr != ERROR_FILE_NOT_FOUND) &&
  213. (DosErr != ERROR_NO_MORE_FILES)) {
  214. PutStdErr(DosErr, NOARGS);
  215. return( FAILURE );
  216. }
  217. }
  218. if (ltaflags & LTA_NOMATCH)
  219. {
  220. if ( error_code = ((*func)(tas)) )
  221. {
  222. final_code = FAILURE;
  223. if (multargs) /* if cmd failed then (TYPE)*/
  224. { /* display arg failed msg too */
  225. PutStdErr(MSG_ERR_PROC_ARG, ONEARG,
  226. argstr1(TEXT("%s"), (unsigned long)((int)tas)));
  227. }
  228. }
  229. if ( error_code && !(ltaflags & LTA_CONT))
  230. {
  231. return(FAILURE) ;
  232. }
  233. else
  234. {
  235. continue;
  236. }
  237. }
  238. PutStdErr(((DosErr == ERROR_PATH_NOT_FOUND) ?
  239. MSG_REN_INVAL_PATH_FILENAME :
  240. ERROR_FILE_NOT_FOUND),
  241. NOARGS);
  242. return(FAILURE) ;
  243. }
  244. if (buf.dwFileAttributes & A_D)
  245. {
  246. PutStdErr(MSG_REN_INVAL_PATH_FILENAME, NOARGS);
  247. return(FAILURE) ;
  248. }
  249. fsinfo.fspec = tas ;
  250. ScanFSpec(&fsinfo) ;
  251. catspot = fsinfo.fnptr-tas ;
  252. mystrcpy(fspec, tas) ;
  253. do
  254. {
  255. fspec[catspot] = NULLC ;
  256. tywild |= multargs; /* if multiple args or wild then wild for TYPE */
  257. if ( error_code = ((*func)(mystrcat(fspec, buf.cFileName))) )
  258. {
  259. final_code = FAILURE;
  260. }
  261. if ( error_code && !(ltaflags & LTA_CONT))
  262. {
  263. return(FAILURE) ;
  264. }
  265. } while(fnext(&buf, attr, hnFirst));
  266. findclose(hnFirst) ;
  267. }
  268. else
  269. {
  270. tywild |= multargs; /* if multiple args or wild then wild for TYPE */
  271. /* if ( error_code = ((*func)(mystrcpy(fspec,tas))) ) */
  272. if ( error_code = ((*func)(tas)) )
  273. {
  274. final_code = FAILURE;
  275. }
  276. if ( error_code && !(ltaflags & LTA_CONT))
  277. {
  278. return(FAILURE) ;
  279. }
  280. }
  281. if (error_code && multargs) /* error this time through */
  282. {
  283. PutStdErr(MSG_ERR_PROC_ARG, ONEARG,
  284. argstr1(TEXT("%s"), (unsigned long)((int)tas)));
  285. }
  286. }
  287. return( final_code ) ;
  288. }
  289. BOOLEAN
  290. IsDriveNameOnly (
  291. IN PTCHAR psz
  292. )
  293. {
  294. //
  295. // If it does not have any path character, is 2 chars long and
  296. // has a ':' it must be a drive
  297. //
  298. if (!mystrrchr(psz,PathChar)) {
  299. if ((mystrlen(psz) == 2) && psz[1] == COLON) {
  300. return( TRUE );
  301. }
  302. }
  303. return( FALSE );
  304. }
  305. /*** ScanFSpec - parse a path string
  306. *
  307. * Purpose:
  308. * To scan the filespec in cis to find the information needed to set the
  309. * pathend, fnptr, extptr, and flags field of the structure. Pathend is
  310. * a ptr to the end of the path and can be NULL. Fnptr is a ptr to the
  311. * filename and may point to a null character. Extptr is a ptr to the
  312. * extension (including ".") and may point to a null character.
  313. *
  314. * ScanFSpec(struct cpyinfo *cis)
  315. *
  316. * Arg:
  317. * cis - the copy information structure to fill
  318. *
  319. * Notes:
  320. * This function needs to be rewritten and cleanup more than any other
  321. * function in the entire program!!!
  322. *
  323. * *** W A R N I N G ! ***
  324. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  325. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  326. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  327. *
  328. */
  329. void ScanFSpec(cis)
  330. struct cpyinfo *cis ;
  331. {
  332. unsigned att ;
  333. UINT OldErrorMode;
  334. TCHAR *sds = &VolSrch[2] ; /* "\*.*" Added to dir's */
  335. TCHAR *fspec ; /* Work Vars - Holds filespec */
  336. TCHAR *wptr ; /* - General string pointer */
  337. TCHAR c ; /* - Temp byte holder */
  338. TCHAR c2 = NULLC ; /* Another if two are needed */
  339. int cbPath, /* - Length of incoming fspec */
  340. dirflag = FALSE ; /* - FSpec is directory flag */
  341. CRTHANDLE hn;
  342. PWIN32_FIND_DATA pfdSave;
  343. DosErr = NO_ERROR;
  344. DEBUG((CTGRP, SFLVL, "SCANFSPEC: cis = %04x fspec = %04x `%ws'",
  345. cis, cis->fspec, cis->fspec)) ;
  346. cbPath = mystrlen(cis->fspec) ; /* Get length of filespec */
  347. if (*(wptr = lastc(cis->fspec)) == COLON && cbPath > 2) {
  348. *wptr-- = NULLC ; /* Zap colon if device name */
  349. OldErrorMode = SetErrorMode( 0 );
  350. hn = Copen(cis->fspec, O_RDONLY|O_BINARY );
  351. if ((hn == BADHANDLE) || (!FileIsDevice(hn) && !FileIsPipe(hn))) {
  352. *++wptr = COLON;
  353. if (cmdfound == CPYTYP) {
  354. if (cpydest == FALSE) {
  355. PutStdErr(MSG_CMD_NOT_RECOGNIZED,
  356. ONEARG, cis->fspec);
  357. }
  358. cdevfail = TRUE;
  359. }
  360. else {
  361. PutStdErr(MSG_CMD_NOT_RECOGNIZED,
  362. ONEARG, cis->fspec);
  363. }
  364. if (hn != BADHANDLE) {
  365. Cclose( hn );
  366. }
  367. }
  368. else {
  369. if ( FileIsDevice(hn) || FileIsPipe(hn) ) {
  370. Cclose( hn );
  371. }
  372. }
  373. SetErrorMode( OldErrorMode );
  374. }
  375. cis->buf = (PWIN32_FIND_DATA)gmkstr(sizeof(WIN32_FIND_DATA)) ; /*WARNING*/
  376. /* First it must be determined if this is a file or directory and if directory
  377. * a "\*.*" appended. Filespec's that are "." or "\" or those ending in "\",
  378. * ":.", ".." or "\." are assumed to be directories. Note that ROOT will fit
  379. * one of these patterns if explicitly named. If no such pattern is found,
  380. * a Get Attributes system call is performed as a final test. Note that
  381. * wildcards are not tested for, since the DOS call will fail defaulting them
  382. * to filenames. Success of any test assumes directory with the "\*.*" being
  383. * appended, while failure of all tests assumes filename.
  384. */
  385. /* If the filespec ends in a '\' set dirflag. Otherwise find where the
  386. * actual filename begins (by looking for last PathChar if there is one).
  387. * If there is no pathchar, then check if a drive and colon has been
  388. * specified. Update the pointer to point to the actual file name spec.
  389. * If it is a "." or ".." then set dirflag.
  390. */
  391. c = *wptr;
  392. if ( c == PathChar ) {
  393. dirflag = TRUE ;
  394. } else {
  395. wptr = mystrrchr(cis->fspec, PathChar);
  396. if (wptr == NULL) {
  397. wptr = cis->fspec ;
  398. if ((mystrlen(wptr) >= 2) && (wptr[1] == COLON)) {
  399. wptr = &wptr[2];
  400. }
  401. } else {
  402. wptr++ ;
  403. }
  404. if ((_tcsicmp(wptr, TEXT(".")) == 0) || (_tcsicmp(wptr, TEXT("..")) == 0)) {
  405. dirflag = TRUE ;
  406. }
  407. }
  408. if (!dirflag)
  409. {
  410. if (cmdfound == CPYTYP) /* bypass if COPY cmd */
  411. {
  412. if (cpydflag == TRUE)
  413. {
  414. att = GetFileAttributes(cis->fspec);
  415. DosErr = (att != -1 ? NO_ERROR : GetLastError());
  416. if (att != -1 )
  417. {
  418. if (att & A_D)
  419. { dirflag = TRUE; }
  420. }
  421. }
  422. else
  423. {
  424. if (cpyfirst == TRUE) /* and !first time */
  425. {
  426. cpyfirst = FALSE; /* and !first time */
  427. att = GetFileAttributes(cis->fspec);
  428. DosErr = (att != -1 ? NO_ERROR : GetLastError());
  429. if (att != -1 )
  430. {
  431. if (att & A_D)
  432. { dirflag = TRUE; }
  433. }
  434. }
  435. }
  436. }
  437. else
  438. {
  439. att = GetFileAttributes(cis->fspec);
  440. DosErr = (att != -1 ? NO_ERROR : GetLastError());
  441. if (att != -1 )
  442. {
  443. if (att & A_D)
  444. { dirflag = TRUE; }
  445. }
  446. }
  447. }
  448. /* Note that in the following conditional, the directory attribute is set
  449. * in cis->buf->attributes. Some functions calling ScanFSpec() require this
  450. * knowledge.
  451. */
  452. if (dirflag)
  453. {
  454. #ifdef DOSWIN32
  455. // Treat "" as path ending with '\'. Returning '\*' is
  456. // wrong because '\*' is INVALID
  457. if (c == PathChar || c == NULLC)
  458. #else
  459. if (c == PathChar) /* If ending in "\"... */
  460. #endif
  461. {
  462. sds = &VolSrch[3] ; /* ...add only "*.*" */
  463. }
  464. //
  465. // If was a drive then don't put wild card stuff on end or
  466. // dir c: will be the same as dir c:\*
  467. // Otherwise append wild card spec.
  468. //
  469. if (!IsDriveNameOnly(cis->fspec)) {
  470. cis->fspec = mystrcpy(gmkstr((cbPath+5)*sizeof(TCHAR)), cis->fspec) ;
  471. mystrcat(cis->fspec, sds) ;
  472. }
  473. cis->buf->dwFileAttributes = A_D ; /* Fixup attribute */
  474. DEBUG((CTGRP, SFLVL, "SCANFSPEC: changed fspec to fspec = `%ws'",cis->fspec)) ;
  475. }
  476. /* Get a pointer to the end of the path in fspec. Everytime a PathChar or
  477. * a correctly placed COLON is found, the pointer is updated. "." and ".."
  478. * are not looked for because they should be caught above.
  479. */
  480. for (cbPath=1,wptr=NULL,fspec=cis->fspec; c=*fspec; fspec++,cbPath++) {
  481. if (c == PathChar || (c == COLON && cbPath == 2)) {
  482. wptr = fspec ;
  483. }
  484. }
  485. cis->pathend = wptr ;
  486. if (wptr) /* Load ptr to fspec's filename */
  487. {
  488. cis->fnptr = (*wptr) ? wptr+1 : wptr ;
  489. }
  490. else
  491. {
  492. wptr = cis->fnptr = cis->fspec ;
  493. }
  494. if (mystrchr(wptr, STAR) || mystrchr(wptr, QMARK)) { /* has wildcards*/
  495. cis->flags |= CI_NAMEWILD;
  496. tywild = TRUE; /* global type wild */
  497. }
  498. cis->extptr = mystrchr(wptr, DOT); /* look for extension */
  499. DEBUG((CTGRP, SFLVL,
  500. "SCANFSPEC: pathend = %04x fnptr = %04x extptr = %04x flags = %04x",
  501. cis->pathend, cis->fnptr, cis->extptr, cis->flags)) ;
  502. }
  503. /*** SetFsSetSaveDir - save current directory and change to another one
  504. *
  505. * Purpose:
  506. * Parse fspec.
  507. * Save the current directory and change to the new directory
  508. * specified in fspec.
  509. *
  510. * struct cpyinfo *SetFsSetSaveDir(TCHAR *fspec)
  511. *
  512. * Args:
  513. * fspec - the filespec to use
  514. *
  515. * Returns:
  516. * A ptr to the cpyinfo struct fsinfo.
  517. * FAILURE otherwise.
  518. * SaveDir will contain what default dir was when SetFsSetSaveDir was
  519. * called.
  520. * CurDrvDir will contain the directory to execute the command in.
  521. *
  522. * *** W A R N I N G ! ***
  523. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  524. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  525. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  526. *
  527. */
  528. struct cpyinfo *SetFsSetSaveDir(fspec)
  529. TCHAR *fspec ;
  530. {
  531. TCHAR *tmpptr;
  532. TCHAR *buftemp;
  533. TCHAR buft[MAX_PATH];
  534. TCHAR *pathend ; /* Ptr to the end of the path in fspec */
  535. TCHAR c ; /* Work variable */
  536. struct cpyinfo *fsinfo ;/* Filespec information struct */
  537. unsigned attr; /* work variable */
  538. PWIN32_FIND_DATA pfdSave;
  539. fsinfo = (struct cpyinfo *)gmkstr(sizeof(struct cpyinfo)) ; /*WARNING*/
  540. fsinfo->fspec = fspec ;
  541. ScanFSpec(fsinfo) ;
  542. pfdSave = fsinfo->buf; /* save original find buffer */
  543. fspec = fsinfo->fspec ;
  544. pathend = fsinfo->pathend ;
  545. DEBUG((CTGRP, SSLVL, "SFSSD: pathend = `%ws' fnptr = `%ws'",
  546. fsinfo->pathend, fsinfo->fnptr)) ;
  547. SaveDir = gmkstr(MAX_PATH*sizeof(TCHAR)) ; /*WARNING*/
  548. GetDir(SaveDir, GD_DEFAULT); /* SaveDir will be current default */
  549. DEBUG((CTGRP, SSLVL, "SFSSD: SaveDir = `%ws'", SaveDir)) ;
  550. /* Added new test to second conditional below to test for the byte
  551. * preceeding pathend to also be a PathChar. In this way, "\\"
  552. * in the root position will case a ChangeDir() call on "\\" which
  553. * will fail and cause an invalid directory error as do similar
  554. * sequences in other positions in the filespec.
  555. */
  556. if (FullPath(buft,fspec,MAX_PATH))
  557. {
  558. return((struct cpyinfo *) FAILURE) ;
  559. }
  560. buftemp = mystrrchr(buft,PathChar) + 1;
  561. *buftemp = NULLC;
  562. mystrcpy(CurDrvDir,buft);
  563. if (pathend && *pathend != COLON) {
  564. if (*pathend == PathChar &&
  565. (pathend == fspec ||
  566. *(tmpptr = prevc(fspec, pathend)) == COLON ||
  567. *tmpptr == PathChar)) {
  568. pathend++ ;
  569. }
  570. c = *pathend;
  571. *pathend = NULLC;
  572. DEBUG((CTGRP, SSLVL, "SFSSD: path = `%ws'", fspec)) ;
  573. attr = GetFileAttributes(fspec);
  574. DosErr = (attr != -1 ? NO_ERROR : GetLastError());
  575. *pathend = c;
  576. if (DosErr) {
  577. return((struct cpyinfo *) FAILURE) ;
  578. }
  579. }
  580. ScanFSpec(fsinfo) ; /* re-scan in case quotes disappeared */
  581. fsinfo->buf = pfdSave; /* reset original find buffer */
  582. /* the original is not freed, because */
  583. /* it will be freed by command cleanup */
  584. return(fsinfo) ;
  585. }