|
|
/*++
Copyright (c) 1988-1999 Microsoft Corporation
Module Name:
ctools1.c
Abstract:
Low level utilities
--*/
#include "cmd.h"
extern unsigned tywild; /* type is wild flag */ extern TCHAR CurDrvDir[], *SaveDir, PathChar, Delimiters[] ;
extern TCHAR VolSrch[] ;
extern TCHAR BSlash ;
extern unsigned DosErr ;
extern BOOL CtrlCSeen;
static TCHAR szNull[] = TEXT("");
/*** TokStr - tokenize argument strings
* * Purpose: * Tokenize a string. * Allocate space for a new string and copy each token in src into the * new string and null terminate it. Tokens are whitespace delimited * unless changed by specialdelims and/or tsflags. The entire tokenized * string ends with 2 null bytes. * * TCHAR *TokStr(TCHAR *src, TCHAR *specialdelims, unsigned tsflags) * * Args: * src - the string to be tokenized * specialdelims - a string of other characters which are to be comsidered * as token delimiters * tsflags - bit 0 nonzero if whitespace are NOT delimiters * bit 1 nonzero if special delimiters are tokens themselves, * eg "foo=bar" => "foo0=0bar00" * * Returns: * A pointer to the new string. * A pointer to a null string if src is NULL * NULL if unable to allocate memory. * * Notes: * The format of the tokenized string dictates the way code is written * to process the tokens in that string. For instance, the code * "s += mystrlen(s)+1" is the way to update s to point to the next token * in the string. * * Command considers "=", ",", and ";" to be token delimiters just like * whitespace. The only time they are not treated like whitespace is * when they are included in specialdelims. * * *** W A R N I N G ! *** * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT * */
TCHAR *TokStr(src, specialdelims, tsflags) TCHAR *src ; TCHAR *specialdelims ; unsigned tsflags ; { TCHAR *ts ; /* Tokenized string pointer */ TCHAR *tscpy, /* Copy of ts */ delist[5], /* List of non-whitespace delimiter/separators */ c; /* Work variable */
int first, /* Flag, true if first time through the loop */ lctdelim, /* Flag, true if last byte was token delimiter */ i, j ; /* Index/loop counter */
DEBUG((CTGRP, TSLVL, "TOKSTR: Entered &src = %04x, src = %ws",src,src)); DEBUG((CTGRP, TSLVL, "TOKSTR: Making copy str of len %d",(mystrlen(src)*2+2))) ;
if (src == NULL) { return(szNull); // This routine returns a doubly null terminated string
} else { ts = tscpy = gmkstr((mystrlen(src)*2+2)*sizeof(TCHAR)) ; /*WARNING*/
DEBUG((CTGRP, TSLVL, "TOKSTR: &tscpy = %04x",tscpy)) ;
for (i = j = 0 ; c = *(&Delimiters[i]) ; i++) if (!mystrchr(specialdelims, c)) delist[j++] = c ; delist[j] = NULLC ;
DEBUG((CTGRP, TSLVL, "TOKSTR: Delimiter string built as `%ws'",delist)) ;
for (first = TRUE, lctdelim = TRUE ; *src ; src++) {
if ( (*src != QUOTE) && (_istspace(*src) || mystrchr(delist, *src)) && (!(tsflags & TS_WSPACE) || first) && (!(tsflags & TS_SDTOKENS) || !mystrchr(specialdelims, *src)) && (!(tsflags & TS_NWSPACE) || !mystrchr(specialdelims, *src)) ) {
while ( *src && (*src != QUOTE) && (_istspace(*src) || mystrchr(delist, *src)) && (!(tsflags & TS_SDTOKENS) || !mystrchr(specialdelims, *src)) && (!(tsflags & TS_NWSPACE) || !mystrchr(specialdelims, *src)) ) src++ ;
if (!(*src)) break ;
if (!first && !lctdelim) ts++ ;
lctdelim = TRUE ; } ;
first = FALSE ;
if (specialdelims && mystrchr(specialdelims, *src)) { if (tsflags & TS_SDTOKENS) { if (lctdelim) *ts = *src ; else *++ts = *src ; lctdelim = TRUE ; ts++ ; } else { if ( tsflags & TS_NWSPACE ) *ts = *src ; lctdelim = FALSE ; }
ts++ ; continue ; } ;
*ts++ = *src ; if ( *src == QUOTE ) { do { *ts++ = *(++src); } while ( src[0] && src[0] != QUOTE && src[1] ); if ( !src[0] ) { src--; } }
lctdelim = FALSE ; } ;
DEBUG((CTGRP, TSLVL, "TOKSTR: String complete, resizing to %d",ts-tscpy+2)) ;
return(resize(tscpy, ((UINT)(ts-tscpy)+2)*sizeof(TCHAR))) ;
DEBUG((CTGRP, TSLVL, "TOKSTR: Resizing done, returning")) ; } }
/******************************************************************************/ /* */ /* LoopThroughArgs - call a function on all args in a list */ /* */ /* Purpose: */ /* This is function is called by many of the commands that take */ /* multiple arguments. This function will parse the argument string, */ /* complain if no args were given, and call func on each of the ards */ /* in argstr. Optionally, it will also expand any wildcards in the */ /* arguments. Execution stops if func ever returns FAILURE. */ /* */ /* int LoopThroughArgs(TCHAR *argstr, int (*func)(), int ltaflags) */ /* */ /* Args: */ /* argstr - argument string */ /* func - the function to pass each element of argstr */ /* ltaflags - bit 0 on if wildcards are to be expanded */ /* bit 1 on if it's ok for argstr to be empty (nothing but whitespace) */ /* bit 2 on if file names should be passed through un changed */ /* when the wildcard expansion fails to find any matches.*/ /* */ /* Returns: */ /* The value returned by func the last time it is run. */ /* */ /******************************************************************************/
int LoopThroughArgs(argstr, func, ltaflags) TCHAR *argstr ; PLOOP_THROUGH_ARGS_ROUTINE func ; int ltaflags ; { TCHAR *tas ; /* Tokenized argument string */ TCHAR fspec[MAX_PATH] ; /* Holds filespec when expanding */ WIN32_FIND_DATA buf ; /* Use for ffirst/fnext */ HANDLE hnFirst ; CPYINFO fsinfo ; int catspot ; /* Fspec index where fname should be added */ unsigned final_code = SUCCESS; unsigned error_code = SUCCESS; int multargs = FALSE; unsigned attr;/* attribute for ffirst for because of TYPE wild */ unsigned taslen;
tywild = FALSE; /* global type wild card flag ret */
GetDir(CurDrvDir, GD_DEFAULT);
if (*(tas = TokStr(argstr, NULL, TS_NOFLAGS)) == NULLC) { if (ltaflags & LTA_NULLOK) { /* return((*func)(tas)) ; */ return((*func)( StripQuotes(tas) )) ; }
PutStdErr(MSG_BAD_SYNTAX, NOARGS); return(FAILURE) ; }
if (*(tas + mystrlen(tas) + 1) ) /* Check for multiple args */ { multargs = TRUE; }
for ( ; *tas ; tas += taslen+1 ) { if (CtrlCSeen) { return(FAILURE); } taslen = mystrlen( tas ); mystrcpy( tas, StripQuotes( tas ) );
if (ltaflags & LTA_EXPAND) { if (cmdfound == TYTYP) /* if TYPE cmd then only files */ { attr = FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE; } else /* else */ { attr = A_ALL; /* find all */ } //
// this is used to detect an error other then can not
// find file. It is set in ffirst
//
DosErr = 0; if (!ffirst(tas, attr, &buf, &hnFirst)) { //
// Check that failure was not do to some system error such
// as an abort on access to floppy
//
if (DosErr) {
if ((DosErr != ERROR_FILE_NOT_FOUND) && (DosErr != ERROR_NO_MORE_FILES)) {
PutStdErr(DosErr, NOARGS);
return( FAILURE );
} }
if (ltaflags & LTA_NOMATCH) { if ( error_code = ((*func)(tas)) ) { final_code = FAILURE; if (multargs) /* if cmd failed then (TYPE)*/ { /* display arg failed msg too */ PutStdErr( MSG_ERR_PROC_ARG, ONEARG, tas ); } } if ( error_code && !(ltaflags & LTA_CONT)) { return(FAILURE) ; } else { continue; } } PutStdErr(((DosErr == ERROR_PATH_NOT_FOUND) ? MSG_REN_INVAL_PATH_FILENAME : ERROR_FILE_NOT_FOUND), NOARGS); return(FAILURE) ; }
if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { PutStdErr(MSG_REN_INVAL_PATH_FILENAME, NOARGS); return(FAILURE) ; }
fsinfo.fspec = tas ; ScanFSpec(&fsinfo) ; catspot = (int)(fsinfo.fnptr-tas) ; mystrcpy(fspec, tas) ;
do { fspec[catspot] = NULLC ; tywild |= multargs; /* if multiple args or wild then wild for TYPE */ if ( error_code = ((*func)(mystrcat(fspec, buf.cFileName))) ) { final_code = FAILURE; } if ( error_code && !(ltaflags & LTA_CONT)) { return(FAILURE) ; } } while(fnext(&buf, attr, hnFirst));
findclose(hnFirst) ; } else { tywild |= multargs; /* if multiple args or wild then wild for TYPE */ /* if ( error_code = ((*func)(mystrcpy(fspec,tas))) ) */ if ( error_code = ((*func)(tas)) ) { final_code = FAILURE; } if ( error_code && !(ltaflags & LTA_CONT)) { return(FAILURE) ; } }
if (error_code && multargs) /* error this time through */ { PutStdErr(MSG_ERR_PROC_ARG, ONEARG, tas ); } } return( final_code ) ; }
BOOLEAN IsDriveNameOnly ( IN PTCHAR psz ) {
//
// If it does not have any path character, is 2 chars long and
// has a ':' it must be a drive
//
if (!mystrrchr(psz,PathChar)) { if ((mystrlen(psz) == 2) && psz[1] == COLON) { return( TRUE ); } } return( FALSE ); }
/*** ScanFSpec - parse a path string
* * Purpose: * To scan the filespec in cis to find the information needed to set the * pathend, fnptr, extptr, and flags field of the structure. Pathend is * a ptr to the end of the path and can be NULL. Fnptr is a ptr to the * filename and may point to a null character. Extptr is a ptr to the * extension (including ".") and may point to a null character. * * ScanFSpec(PCPYINFO cis) * * Arg: * cis - the copy information structure to fill * * Notes: * This function needs to be rewritten and cleanup more than any other * function in the entire program!!! * * *** W A R N I N G ! *** * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT * */
BOOL ScanFSpec(cis) PCPYINFO cis ; { unsigned att ; UINT OldErrorMode;
TCHAR *sds = &VolSrch[2] ; /* "\*.*" Added to dir's */
TCHAR *fspec ; /* Work Vars - Holds filespec */ TCHAR *wptr ; /* - General string pointer */ TCHAR c ; /* - Temp byte holder */ TCHAR c2 = NULLC ; /* Another if two are needed */ int cbPath, /* - Length of incoming fspec */ dirflag = FALSE ; /* - FSpec is directory flag */ CRTHANDLE hn; PWIN32_FIND_DATA pfdSave;
DosErr = NO_ERROR; DEBUG((CTGRP, SFLVL, "SCANFSPEC: cis = %04x fspec = %04x `%ws'", cis, cis->fspec, cis->fspec)) ;
cbPath = mystrlen(cis->fspec) ; /* Get length of filespec */
if (*(wptr = lastc(cis->fspec)) == COLON && cbPath > 2) { *wptr-- = NULLC ; /* Zap colon if device name */
OldErrorMode = SetErrorMode( 0 ); hn = Copen(cis->fspec, O_RDONLY|O_BINARY ); if ((hn == BADHANDLE) || (!FileIsDevice(hn) && !FileIsPipe(hn))) { *++wptr = COLON; if (cmdfound == CPYTYP) { if (cpydest == FALSE) { PutStdErr( MSG_CMD_NOT_RECOGNIZED, ONEARG, cis->fspec); } cdevfail = TRUE; } else { PutStdErr( MSG_CMD_NOT_RECOGNIZED, ONEARG, cis->fspec); } if (hn != BADHANDLE) { Cclose( hn ); }
} else { if ( FileIsDevice(hn) || FileIsPipe(hn) ) { Cclose( hn ); } } SetErrorMode( OldErrorMode ); }
cis->buf = (PWIN32_FIND_DATA)gmkstr(sizeof(WIN32_FIND_DATA)) ; /*WARNING*/
/* First it must be determined if this is a file or directory and if directory
* a "\*.*" appended. Filespec's that are "." or "\" or those ending in "\", * ":.", ".." or "\." are assumed to be directories. Note that ROOT will fit * one of these patterns if explicitly named. If no such pattern is found, * a Get Attributes system call is performed as a final test. Note that * wildcards are not tested for, since the DOS call will fail defaulting them * to filenames. Success of any test assumes directory with the "\*.*" being * appended, while failure of all tests assumes filename. */
/* If the filespec ends in a '\' set dirflag. Otherwise find where the
* actual filename begins (by looking for last PathChar if there is one). * If there is no pathchar, then check if a drive and colon has been * specified. Update the pointer to point to the actual file name spec. * If it is a "." or ".." then set dirflag. */ c = *wptr; if ( c == PathChar ) { dirflag = TRUE ; } else { wptr = mystrrchr(cis->fspec, PathChar); if (wptr == NULL) { wptr = cis->fspec ; if ((mystrlen(wptr) >= 2) && (wptr[1] == COLON)) { wptr = &wptr[2]; } } else { wptr++ ; } if ((_tcsicmp(wptr, TEXT(".")) == 0) || (_tcsicmp(wptr, TEXT("..")) == 0)) { dirflag = TRUE ; } }
if (!dirflag) { if (cmdfound == CPYTYP) { /* bypass if COPY cmd */ if (cpydflag == TRUE) { att = GetFileAttributes(cis->fspec); DosErr = (att != -1 ? NO_ERROR : GetLastError()); if (att != -1 ) { if (att & FILE_ATTRIBUTE_DIRECTORY) { dirflag = TRUE; } } } else { if (cpyfirst == TRUE) { /* and !first time */ cpyfirst = FALSE; /* and !first time */ att = GetFileAttributes(cis->fspec); DosErr = (att != -1 ? NO_ERROR : GetLastError()); if (att != -1 ) { if (att & FILE_ATTRIBUTE_DIRECTORY) { dirflag = TRUE; } } } } } else { att = GetFileAttributes(cis->fspec); DosErr = (att != -1 ? NO_ERROR : GetLastError()); if (att != -1 ) { if (att & FILE_ATTRIBUTE_DIRECTORY) { dirflag = TRUE; } } } }
/* Note that in the following conditional, the directory attribute is set
* in cis->buf->attributes. Some functions calling ScanFSpec() require this * knowledge. */ if (dirflag) { if (c == PathChar) /* If ending in "\"... */ { sds = &VolSrch[3] ; /* ...add only "*.*" */ }
//
// If was a drive then don't put wild card stuff on end or
// dir c: will be the same as dir c:\*
// Otherwise append wild card spec.
//
if (!IsDriveNameOnly(cis->fspec)) { cis->fspec = mystrcpy(gmkstr((cbPath+5)*sizeof(TCHAR)), cis->fspec) ; mystrcat(cis->fspec, sds) ; }
cis->buf->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY ; /* Fixup attribute */ DEBUG((CTGRP, SFLVL, "SCANFSPEC: changed fspec to fspec = `%ws'",cis->fspec)) ; }
/* Get a pointer to the end of the path in fspec. Everytime a PathChar or
* a correctly placed COLON is found, the pointer is updated. "." and ".." * are not looked for because they should be caught above. */
for (cbPath=1,wptr=NULL,fspec=cis->fspec; c=*fspec; fspec++,cbPath++) { if (c == PathChar || (c == COLON && cbPath == 2)) { wptr = fspec ; } }
cis->pathend = wptr ;
if (wptr) { /* Load ptr to fspec's filename */ cis->fnptr = (*wptr) ? wptr+1 : wptr ; } else { wptr = cis->fnptr = cis->fspec ; }
if (mystrchr(wptr, STAR) || mystrchr(wptr, QMARK)) { /* has wildcards*/ cis->flags |= CI_NAMEWILD; tywild = TRUE; /* global type wild */ } cis->extptr = mystrchr(wptr, DOT); /* look for extension */
DEBUG((CTGRP, SFLVL, "SCANFSPEC: pathend = %04x fnptr = %04x extptr = %04x flags = %04x", cis->pathend, cis->fnptr, cis->extptr, cis->flags)) ;
return TRUE; }
/*** SetFsSetSaveDir - save current directory and change to another one
* * Purpose: * Parse fspec. * Save the current directory and change to the new directory * specified in fspec. * * PCPYINFO SetFsSetSaveDir(TCHAR *fspec) * * Args: * fspec - the filespec to use * * Returns: * A ptr to the cpyinfo struct fsinfo. * FAILURE otherwise. * SaveDir will contain what default dir was when SetFsSetSaveDir was * called. * CurDrvDir will contain the directory to execute the command in. * * *** W A R N I N G ! *** * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT * */
PCPYINFO SetFsSetSaveDir(fspec) TCHAR *fspec ; { TCHAR *tmpptr; TCHAR *buftemp; TCHAR buft[MAX_PATH];
TCHAR *pathend ; /* Ptr to the end of the path in fspec */ TCHAR c ; /* Work variable */ PCPYINFO fsinfo ;/* Filespec information struct */ unsigned attr; /* work variable */ PWIN32_FIND_DATA pfdSave;
fsinfo = (PCPYINFO)gmkstr(sizeof(CPYINFO)) ; /*WARNING*/
fsinfo->fspec = fspec ;
ScanFSpec(fsinfo) ;
pfdSave = fsinfo->buf; /* save original find buffer */ fspec = fsinfo->fspec ; pathend = fsinfo->pathend ; DEBUG((CTGRP, SSLVL, "SFSSD: pathend = `%ws' fnptr = `%ws'", fsinfo->pathend, fsinfo->fnptr)) ;
SaveDir = gmkstr(MAX_PATH*sizeof(TCHAR)) ; /*WARNING*/ GetDir(SaveDir, GD_DEFAULT); /* SaveDir will be current default */ DEBUG((CTGRP, SSLVL, "SFSSD: SaveDir = `%ws'", SaveDir)) ;
/* Added new test to second conditional below to test for the byte
* preceeding pathend to also be a PathChar. In this way, "\\" * in the root position will case a ChangeDir() call on "\\" which * will fail and cause an invalid directory error as do similar * sequences in other positions in the filespec. */ if (FullPath(buft,fspec,MAX_PATH)) { return((PCPYINFO) FAILURE) ; }
buftemp = mystrrchr(buft,PathChar) + 1;
*buftemp = NULLC;
mystrcpy(CurDrvDir,buft);
if (pathend && *pathend != COLON) { if (*pathend == PathChar && (pathend == fspec || *(tmpptr = prevc(fspec, pathend)) == COLON || *tmpptr == PathChar)) { pathend++ ; } c = *pathend; *pathend = NULLC; DEBUG((CTGRP, SSLVL, "SFSSD: path = `%ws'", fspec)) ; attr = GetFileAttributes(fspec); DosErr = (attr != -1 ? NO_ERROR : GetLastError()); *pathend = c; if (DosErr) { return((PCPYINFO) FAILURE) ; } }
ScanFSpec(fsinfo) ; /* re-scan in case quotes disappeared */ fsinfo->buf = pfdSave; /* reset original find buffer */ /* the original is not freed, because */ /* it will be freed by command cleanup */ return(fsinfo) ; }
|