97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
|
|
//go:build windows
|
|||
|
|
|
|||
|
|
package cursor
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"os"
|
|||
|
|
"path/filepath"
|
|||
|
|
"strings"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// maxScanDirs 限制驱动器扫描的目录总数,避免在超大盘上长时间阻塞。
|
|||
|
|
const maxScanDirs = 8000
|
|||
|
|
|
|||
|
|
// skippedRootNames 是驱动器扫描时跳过的系统目录(大且不可能装 Cursor)。
|
|||
|
|
var skippedRootNames = map[string]struct{}{
|
|||
|
|
"windows": {},
|
|||
|
|
"programdata": {},
|
|||
|
|
"users": {},
|
|||
|
|
"$recycle.bin": {},
|
|||
|
|
"system volume information": {},
|
|||
|
|
"recovery": {},
|
|||
|
|
"perflogs": {},
|
|||
|
|
"dumpstack.log.tmp": {},
|
|||
|
|
"config.msi": {},
|
|||
|
|
"intel": {},
|
|||
|
|
"drivers": {},
|
|||
|
|
"msocache": {},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// scanDriveRootsForCursor 兜底扫描各固定驱动器:
|
|||
|
|
// 根目录与二级目录中查找名称含 cursor 且包含 Cursor.exe 的目录。
|
|||
|
|
// 纯文件系统探测,不依赖注册表枚举或子进程(部分安全软件会拦截)。
|
|||
|
|
func scanDriveRootsForCursor() string {
|
|||
|
|
visited := 0
|
|||
|
|
for letter := 'C'; letter <= 'Z'; letter++ {
|
|||
|
|
drive := string(letter) + `:\`
|
|||
|
|
if _, err := os.Stat(drive); err != nil {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
if dir := scanDirForCursor(drive, &visited); dir != "" {
|
|||
|
|
return dir
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// scanDirForCursor 在目录及其一级子目录中查找 Cursor 安装目录。
|
|||
|
|
func scanDirForCursor(root string, visited *int) string {
|
|||
|
|
entries, err := os.ReadDir(root)
|
|||
|
|
if err != nil {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
var deferred []string
|
|||
|
|
for _, entry := range entries {
|
|||
|
|
if !entry.IsDir() {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
name := entry.Name()
|
|||
|
|
if _, skip := skippedRootNames[strings.ToLower(name)]; skip {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
*visited++
|
|||
|
|
if *visited > maxScanDirs {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
full := filepath.Join(root, name)
|
|||
|
|
if strings.Contains(strings.ToLower(name), "cursor") {
|
|||
|
|
if isCursorInstallDir(full) {
|
|||
|
|
return full
|
|||
|
|
}
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
deferred = append(deferred, full)
|
|||
|
|
}
|
|||
|
|
// 二级目录:仅在一级目录名像程序目录时展开,控制扫描量。
|
|||
|
|
for _, dir := range deferred {
|
|||
|
|
if !looksLikeProgramsDir(filepath.Base(dir)) {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
if dir := scanDirForCursor(dir, visited); dir != "" {
|
|||
|
|
return dir
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// looksLikeProgramsDir 判断目录名是否像程序/工具安装根目录。
|
|||
|
|
func looksLikeProgramsDir(name string) bool {
|
|||
|
|
lower := strings.ToLower(name)
|
|||
|
|
for _, keyword := range []string{"program", "app", "dev", "soft", "tool", "work", "bin"} {
|
|||
|
|
if strings.Contains(lower, keyword) {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|