tclwinfile.c

来自「tcl是工具命令语言」· C语言 代码 · 共 2,275 行 · 第 1/5 页

C
2,275
字号
	    Tcl_Obj *retVal;	    Tcl_DString ds;	    CONST char *copy;	    int len;	    	    Tcl_WinTCharToUtf( 		(CONST char*)reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, 		(int)reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength, 		&ds);		    copy = Tcl_DStringValue(&ds);	    len = Tcl_DStringLength(&ds);	    /* 	     * Certain native path representations on Windows have this special	     * prefix to indicate that they are to be treated specially.  For	     * example extremely long paths, or symlinks 	     */	    if (*copy == '\\') {		if (0 == strncmp(copy,"\\??\\",4)) {		    copy += 4;		    len -= 4;		} else if (0 == strncmp(copy,"\\\\?\\",4)) {		    copy += 4;		    len -= 4;		}	    }	    retVal = Tcl_NewStringObj(copy,len);	    Tcl_IncrRefCount(retVal);	    Tcl_DStringFree(&ds);	    return retVal;	}    }    Tcl_SetErrno(EINVAL);    return NULL;}/* *-------------------------------------------------------------------- * * NativeReadReparse * * Read the junction/reparse information from a given NTFS directory. * * Assumption that LinkDirectory is a valid, existing directory. *  * Returns zero on success. *-------------------------------------------------------------------- */static int NativeReadReparse(LinkDirectory, buffer)    CONST TCHAR* LinkDirectory;   /* The junction to read */    REPARSE_DATA_BUFFER* buffer;  /* Pointer to buffer. Cannot be NULL */{    HANDLE hFile;    DWORD returnedLength;       hFile = (*tclWinProcs->createFileProc)(LinkDirectory, GENERIC_READ, 0,	NULL, OPEN_EXISTING, 	FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);    if (hFile == INVALID_HANDLE_VALUE) {	/* Error creating directory */	TclWinConvertError(GetLastError());	return -1;    }    /* Get the link */    if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 			 0, buffer, sizeof(DUMMY_REPARSE_BUFFER), 			 &returnedLength, NULL)) {		/* Error setting junction */	TclWinConvertError(GetLastError());	CloseHandle(hFile);	return -1;    }    CloseHandle(hFile);        if (!IsReparseTagValid(buffer->ReparseTag)) {	Tcl_SetErrno(EINVAL);	return -1;    }    return 0;}/* *-------------------------------------------------------------------- * * NativeWriteReparse * * Write the reparse information for a given directory. *  * Assumption that LinkDirectory does not exist. *-------------------------------------------------------------------- */static int NativeWriteReparse(LinkDirectory, buffer)    CONST TCHAR* LinkDirectory;    REPARSE_DATA_BUFFER* buffer;{    HANDLE hFile;    DWORD returnedLength;        /* Create the directory - it must not already exist */    if ((*tclWinProcs->createDirectoryProc)(LinkDirectory, NULL) == 0) {	/* Error creating directory */	TclWinConvertError(GetLastError());	return -1;    }    hFile = (*tclWinProcs->createFileProc)(LinkDirectory, GENERIC_WRITE, 0,	NULL, OPEN_EXISTING, 	FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);    if (hFile == INVALID_HANDLE_VALUE) {	/* Error creating directory */	TclWinConvertError(GetLastError());	return -1;    }    /* Set the link */    if (!DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, buffer, 			 (DWORD) buffer->ReparseDataLength 			 + REPARSE_MOUNTPOINT_HEADER_SIZE,			 NULL, 0, &returnedLength, NULL)) {		/* Error setting junction */	TclWinConvertError(GetLastError());	CloseHandle(hFile);	(*tclWinProcs->removeDirectoryProc)(LinkDirectory);	return -1;    }    CloseHandle(hFile);    /* We succeeded */    return 0;}/* *--------------------------------------------------------------------------- * * TclpFindExecutable -- * *	This procedure computes the absolute path name of the current *	application, given its argv[0] value. * * Results: *	A dirty UTF string that is the path to the executable.  At this *	point we may not know the system encoding.  Convert the native *	string value to UTF using the default encoding.  The assumption *	is that we will still be able to parse the path given the path *	name contains ASCII string and '/' chars do not conflict with *	other UTF chars. * * Side effects: *	The variable tclNativeExecutableName gets filled in with the file *	name for the application, if we figured it out.  If we couldn't *	figure it out, tclNativeExecutableName is set to NULL. * *--------------------------------------------------------------------------- */char *TclpFindExecutable(argv0)    CONST char *argv0;		/* The value of the application's argv[0]				 * (native). */{    Tcl_DString ds;    WCHAR wName[MAX_PATH];    if (argv0 == NULL) {	return NULL;    }    if (tclNativeExecutableName != NULL) {	return tclNativeExecutableName;    }    /*     * Under Windows we ignore argv0, and return the path for the file used to     * create this process.     */    (*tclWinProcs->getModuleFileNameProc)(NULL, wName, MAX_PATH);    Tcl_WinTCharToUtf((CONST TCHAR *) wName, -1, &ds);    tclNativeExecutableName = ckalloc((unsigned) (Tcl_DStringLength(&ds) + 1));    strcpy(tclNativeExecutableName, Tcl_DStringValue(&ds));    Tcl_DStringFree(&ds);    TclWinNoBackslash(tclNativeExecutableName);    return tclNativeExecutableName;}/* *---------------------------------------------------------------------- * * TclpMatchInDirectory -- * *	This routine is used by the globbing code to search a *	directory for all files which match a given pattern. * * Results:  *	 *	The return value is a standard Tcl result indicating whether an *	error occurred in globbing.  Errors are left in interp, good *	results are lappended to resultPtr (which must be a valid object) * * Side effects: *	None. * *---------------------------------------------------------------------- */intTclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types)    Tcl_Interp *interp;		/* Interpreter to receive errors. */    Tcl_Obj *resultPtr;		/* List object to lappend results. */    Tcl_Obj *pathPtr;	        /* Contains path to directory to search. */    CONST char *pattern;	/* Pattern to match against. */    Tcl_GlobTypeData *types;	/* Object containing list of acceptable types.				 * May be NULL. In particular the directory				 * flag is very important. */{    CONST TCHAR *native;    if (pattern == NULL || (*pattern == '\0')) {	Tcl_Obj *norm = Tcl_FSGetNormalizedPath(NULL, pathPtr);	if (norm != NULL) {	    /* Match a single file directly */	    int len;	    DWORD attr;	    CONST char *str = Tcl_GetStringFromObj(norm,&len);	    native = (CONST TCHAR*) Tcl_FSGetNativePath(pathPtr);	    	    if (tclWinProcs->getFileAttributesExProc == NULL) {		attr = (*tclWinProcs->getFileAttributesProc)(native);		if (attr == 0xffffffff) {		    return TCL_OK;		}	    } else {		WIN32_FILE_ATTRIBUTE_DATA data;		if ((*tclWinProcs->getFileAttributesExProc)(native,			GetFileExInfoStandard, &data) != TRUE) {		    return TCL_OK;		}		attr = data.dwFileAttributes;	    }	    if (NativeMatchType(WinIsDrive(str,len), attr, 				native, types)) {		Tcl_ListObjAppendElement(interp, resultPtr, pathPtr);	    }	}	return TCL_OK;    } else {	DWORD attr;	HANDLE handle;	WIN32_FIND_DATAT data;	CONST char *dirName;	int dirLength;	int matchSpecialDots;	Tcl_DString ds;        /* native encoding of dir */	Tcl_DString dsOrig;    /* utf-8 encoding of dir */	Tcl_DString dirString; /* utf-8 encoding of dir with \'s */	Tcl_Obj *fileNamePtr;	/*	 * Convert the path to normalized form since some interfaces only	 * accept backslashes.  Also, ensure that the directory ends with a	 * separator character.	 */	fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr);	if (fileNamePtr == NULL) {	    return TCL_ERROR;	}	Tcl_DStringInit(&dsOrig);	dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength);	Tcl_DStringAppend(&dsOrig, dirName, dirLength);		Tcl_DStringInit(&dirString);	if (dirLength == 0) {	    Tcl_DStringAppend(&dirString, ".\\", 2);	} else {	    char *p;	    Tcl_DStringAppend(&dirString, dirName, dirLength);	    for (p = Tcl_DStringValue(&dirString); *p != '\0'; p++) {		if (*p == '/') {		    *p = '\\';		}	    }	    p--;	    /* Make sure we have a trailing directory delimiter */	    if ((*p != '\\') && (*p != ':')) {		Tcl_DStringAppend(&dirString, "\\", 1);		Tcl_DStringAppend(&dsOrig, "/", 1);		dirLength++;	    }	}	dirName = Tcl_DStringValue(&dirString);	/*	 * First verify that the specified path is actually a directory.	 */	native = Tcl_WinUtfToTChar(dirName, Tcl_DStringLength(&dirString),		&ds);	attr = (*tclWinProcs->getFileAttributesProc)(native);	Tcl_DStringFree(&ds);	if ((attr == 0xffffffff) || ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)) {	    Tcl_DStringFree(&dirString);	    return TCL_OK;	}	/*	 * We need to check all files in the directory, so append a *.*	 * to the path. 	 */	dirName = Tcl_DStringAppend(&dirString, "*.*", 3);	native = Tcl_WinUtfToTChar(dirName, -1, &ds);	handle = (*tclWinProcs->findFirstFileProc)(native, &data);	Tcl_DStringFree(&ds);	if (handle == INVALID_HANDLE_VALUE) {	    Tcl_DStringFree(&dirString);	    TclWinConvertError(GetLastError());	    Tcl_ResetResult(interp);	    Tcl_AppendResult(interp, "couldn't read directory \"",		    Tcl_DStringValue(&dsOrig), "\": ", 		    Tcl_PosixError(interp), (char *) NULL);	    Tcl_DStringFree(&dsOrig);	    return TCL_ERROR;	}	/*	 * Check to see if the pattern should match the special	 * . and .. names, referring to the current directory,	 * or the directory above.  We need a special check for	 * this because paths beginning with a dot are not considered	 * hidden on Windows, and so otherwise a relative glob like	 * 'glob -join * *' will actually return './. ../..' etc.	 */	if ((pattern[0] == '.')		|| ((pattern[0] == '\\') && (pattern[1] == '.'))) {	    matchSpecialDots = 1;	} else {	    matchSpecialDots = 0;	}	/*	 * Now iterate over all of the files in the directory, starting	 * with the first one we found.	 */	do {	    CONST char *utfname;	    int checkDrive = 0;	    int isDrive;	    DWORD attr;	    	    if (tclWinProcs->useWide) {		native = (CONST TCHAR *) data.w.cFileName;		attr = data.w.dwFileAttributes;	    } else {		native = (CONST TCHAR *) data.a.cFileName;		attr = data.a.dwFileAttributes;	    }	    	    utfname = Tcl_WinTCharToUtf(native, -1, &ds);	    if (!matchSpecialDots) {		/* If it is exactly '.' or '..' then we ignore it */		if ((utfname[0] == '.') && (utfname[1] == '\0' 			|| (utfname[1] == '.' && utfname[2] == '\0'))) {		    Tcl_DStringFree(&ds);		    continue;		}	    } else if (utfname[0] == '.' && utfname[1] == '.'		    && utfname[2] == '\0') {		/* 		 * Have to check if this is a drive below, so we can		 * correctly match 'hidden' and not hidden files.		 */		checkDrive = 1;	    }	    	    /*	     * Check to see if the file matches the pattern.  Note that	     * we are ignoring the case sensitivity flag because Windows	     * doesn't honor case even if the volume is case sensitive.	     * If the volume also doesn't preserve case, then we	     * previously returned the lower case form of the name.  This	     * didn't seem quite right since there are	     * non-case-preserving volumes that actually return mixed	     * case.  So now we are returning exactly what we get from	     * the system.	     */	    if (Tcl_StringCaseMatch(utfname, pattern, 1)) {		/*		 * If the file matches, then we need to process the remainder		 * of the path.		 */		if (checkDrive) {		    CONST char *fullname = Tcl_DStringAppend(&dsOrig, utfname,			    Tcl_DStringLength(&ds));		    isDrive = WinIsDrive(fullname, Tcl_DStringLength(&dsOrig));		    Tcl_DStringSetLength(&dsOrig, dirLength);		} else {		    isDrive = 0;		}		if (NativeMatchType(isDrive, attr, native, types)) {		    Tcl_ListObjAppendElement(interp, resultPtr, 			    TclNewFSPathObj(pathPtr, utfname,				    Tcl_DStringLength(&ds)));		}	    }	    /*	     * Free ds here to ensure that native is valid above.	     */	    Tcl_DStringFree(&ds);	} while ((*tclWinProcs->findNextFileProc)(handle, &data) == TRUE);	FindClose(handle);	Tcl_DStringFree(&dirString);	Tcl_DStringFree(&dsOrig);	return TCL_OK;    }}/*  * Does the given path represent a root volume?  We need this special * case because for NTFS root volumes, the getFileAttributesProc returns * a 'hidden' attribute when it should not. */static intWinIsDrive(    CONST char *name,     /* Name (UTF-8) */    int len)              /* Length of name */{    int remove = 0;    while (len > 4) {        if ((name[len-1] != '.' || name[len-2] != '.') 	    || (name[len-3] != '/' && name[len-3] != '\\')) {            /* We don't have '/..' at the end */	    if (remove == 0) {	        break;	    }	    remove--;	    while (len > 0) {		len--;		if (name[len] == '/' || name[len] == '\\') {		    break;		}	    }	    if (len < 4) {	        len++;		break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?