首次入库,远端 https://git.gvsds.com/GVSDS/Viper_VET 。 19 个文件全部为源码/配置,无构建产物: App/main.vp、App/lib/*.vp(VCache / VCheck / VCli / VDirs / VFs / VGuard / VGuid / VJson / VLog / VPe / VProc / VScan / __init__) 1.json、LICENSE、README.md、project.vpj、.gitignore 既有 .gitignore 已覆盖本仓的产物目录(**/output/、**/temp/、**/.tpv_build/) 与 *.exe / *.obj / *.o / *.img / *.iso / __pycache__/ / .cache/; 实测 `git add -A --dry-run` 仅 19 个文件、零产物混入,故未改动 .gitignore。
539 lines
29 KiB
Plaintext
539 lines
29 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import string
|
||
import sys
|
||
import lib.VLog as VLog
|
||
import lib.VDirs as VDirs
|
||
|
||
|
||
# ============================================================
|
||
# VCli - 命令行解析(scan/ver、--cache/--once/--sleep/--pace/--pace-units/--need-confirm/
|
||
# --need-confirm-timeout/--path/--verbose/--search-paths;手写解析,不引入 argparse)
|
||
#
|
||
# 从原 App/main.vp 的 RunScan 参数解析段平移,并新增可重复的 --path(需求 2):
|
||
# 一旦出现 --path 即置 VDirs 覆盖模式(仅收集路径、保序);存在性检查与
|
||
# "不存在则告警并跳过该条"在 VDirs.BuildTable([0/3] 段内)执行。
|
||
# --need-confirm <秒>:心跳确认间隔,缺省/0 = 关闭该功能(仅 scan 模式生效)。
|
||
# 本轮改为"独立看门狗子进程"实现(见 VGuard.vp):父进程在本模块解析出
|
||
# NeedConfirm/NeedConfirmTimeout 后由 App/main.vp 拉起子进程,扫盘侧不再参与确认。
|
||
# --sleep <毫秒>:手动指定扫盘轮间隔(缺省 DEFAULT_SLEEP_MS=10000;0 = 不 sleep)。
|
||
# --pace <毫秒>:扫盘节流点的睡眠时长(缺省 DEFAULT_PACE_SLEEP_MS=20;0 = 关闭节流)。
|
||
# --pace-units <N>:文件侧节流粒度——每 N 个文件一个节流点(缺省 DEFAULT_PACE_FILE_UNITS=64;1 = 每个文件)。
|
||
# 两者"显式给出即启用":只要命令行出现其一,VScan 便打开节流(--once 下也生效);
|
||
# 未给出时 VScan 完全沿用其自身缺省(常驻启用/20ms/64,--once 关闭),行为逐字不变。
|
||
# 目录侧粒度固定为 VScan.PACE_DIR_UNITS=16,不受 --pace-units 影响;--pace 0 时两个节流点都不 Sleep。
|
||
# 取值经 PaceSet()/PaceSleepMs()/PaceFileUnits() 暴露给 VScan.RunScan。
|
||
# --need-confirm-timeout <毫秒>:看门狗确认等待超时(缺省 DEFAULT_CONFIRM_TIMEOUT_MS=5000)。
|
||
# --verbose(无值):打开详细过程日志(缺省 = 安静)。仅改变 stderr 日志详细度,
|
||
# 不影响 stdout 的单行 JSON 与缓存文件内容。取值经 Verbose() 暴露,由 App/main.vp
|
||
# 在参数解析后、任何日志之前设置到 VLog(VLog.SetVerbose)。
|
||
# --search-paths(无值):**查询开关**——建好"会搜索的目录表"后逐行打印(stderr)并直接退出,
|
||
# 不扫盘、不拉起看门狗、不读写缓存,因此与 --cache 相互独立:未给 --cache 也可运行
|
||
# (见 ParseScanArgs 末尾的必填校验)。取值经 SearchPaths() 暴露,由 App/main.vp 在 scan
|
||
# 分派中"拉起看门狗之前"分派到 VScan.RunSearchPathsQuery。
|
||
# confirm-watch(内部隐藏子命令)的参数解析在 ParseWatchArgs(),不出现在用法里;
|
||
# 父进程为 verbose 时为子进程追加 --verbose,使看门狗按同一规则输出(WatchVerbose())。
|
||
# ver 子命令的取参(sys._argv[2])在 VPe.RunVer 内,保持原样;ver 不解析开关,
|
||
# 其 verbose 由 HasVerbose()(argv 中出现 --verbose 即置位)识别。
|
||
# 取参方式依据:sys._init_argv/_argc/_argv(includes/sys.vp:60-67)。
|
||
# ============================================================
|
||
|
||
VET_VERSION: t.CDefine = "0.2.0"
|
||
SECS_MAX: t.CDefine = 86400 # --need-confirm 秒数上界(1 天;超出视为参数错误)
|
||
DEFAULT_SLEEP_MS: t.CDefine = 10000 # --sleep 缺省值(沿用原 SCAN_INTERVAL_MS)
|
||
SLEEP_MAX_MS: t.CDefine = 86400000 # --sleep 毫秒上界(1 天;超出视为参数错误)
|
||
DEFAULT_CONFIRM_TIMEOUT_MS: t.CDefine = 5000 # --need-confirm-timeout 缺省值
|
||
CONFIRM_TIMEOUT_MAX_MS: t.CDefine = 86400000 # --need-confirm-timeout 毫秒上界(1 天)
|
||
# --pace <毫秒>:节流点睡眠时长,取值域 0..60000。0 = 关闭节流(两个节流点都不 Sleep)。
|
||
# 上界取 1 分钟:节流只为降低扫盘对机器的持续压力,睡眠远超 1 分钟只会无谓拖慢单轮而无收益,
|
||
# 与 --sleep(轮间隔,上界 1 天)的用途不同,故不共用其宽松上界。
|
||
DEFAULT_PACE_SLEEP_MS: t.CDefine = 20 # --pace 缺省值(沿用 VScan.PACE_SLEEP_MS,未给出时逐字保持现状)
|
||
PACE_SLEEP_MAX_MS: t.CDefine = 60000 # --pace 毫秒上界(1 分钟)
|
||
# --pace-units <N>:文件粒度,取值域 1..1000000(每 N 个文件一个节流点;1 = 每个文件都停)。
|
||
# 0 无意义("每 0 个文件"),与非法输入一并报错;上界 1000000 已足够稀疏(等同关闭文件侧节流)。
|
||
DEFAULT_PACE_FILE_UNITS: t.CDefine = 64 # --pace-units 缺省值(沿用 VScan.PACE_FILE_UNITS,未给出时逐字保持现状)
|
||
PACE_UNITS_MAX: t.CDefine = 1000000 # --pace-units 文件数上界
|
||
# --parent-pid 上界(DWORD 全域 4294967295)。
|
||
# 注意:这里不能用 `t.CDefine = 4294967295`。CDefine 的物化规则(TPV
|
||
# ExprGenCdef._const_int_for_cdefine)把落在 u32 范围内的值一律按 **i32** 常量生成
|
||
# (0xFFFFFFFF → i32 -1),上一轮构建 IR 现场:
|
||
# VET/.tpv_build/temp/df/df86f78f0f0481a1.ll:1214
|
||
# %108 = call i32 @...ParseUint(i8* %107, i32 -1)
|
||
# 即 PID_MAX 传到 `Max: int` 时已成 -1。改宽类型全局常量后该值以 i64 物化
|
||
# (字面量对 >32 位目标走 const_int64,ExprGenConst.translate_constant:105-107)。
|
||
PID_MAX: LONGLONG = 4294967295 # --parent-pid 上界(DWORD 全域)
|
||
|
||
Cache_Path: str = None
|
||
Once_Flag: int = 0
|
||
Need_Confirm: int = 0
|
||
Sleep_Ms: int = DEFAULT_SLEEP_MS
|
||
Need_Confirm_Timeout: int = DEFAULT_CONFIRM_TIMEOUT_MS
|
||
Verbose_Flag: int = 0 # --verbose(scan);1=详细日志
|
||
# 顶层写法(`VET.exe --search-paths ...`,子命令位置直接写该开关)时由 App/main.vp 置位:
|
||
# ParseScanArgs 从 argv[2] 起扫并先把 Search_Paths_Flag 清零,故"子命令位置上的那个
|
||
# --search-paths"它扫不到;用本标记在解析后重新并入,两种写法语义完全一致。
|
||
Search_Paths_Implicit: int = 0
|
||
Search_Paths_Flag: int = 0 # --search-paths(scan);1=打印将搜索的路径后直接退出
|
||
# --pace/--pace-units(scan):显式给出即启用节流(--once 下也生效)。
|
||
# Pace_Set 记"是否显式给出过两者之一";未给出时 VScan 完全沿用其缺省(常驻启用/20ms/64,--once 关闭)。
|
||
Pace_Set: int = 0 # 1=命令行显式给出 --pace 或 --pace-units
|
||
Pace_Ms: int = DEFAULT_PACE_SLEEP_MS # --pace <毫秒>(0 = 关闭节流)
|
||
Pace_Units: int = DEFAULT_PACE_FILE_UNITS # --pace-units <N>(每 N 个文件一个节流点;1 = 每个文件)
|
||
# --first-no-pace(scan):第一轮扫盘不启用节流(之后各轮按常规规则)。
|
||
# 只影响"第 1 轮"这一件事:不改变 Pace_Set/Pace_Ms/Pace_Units,也不改变常驻/--once 的常规节流规则;
|
||
# 与 --pace 0(所有轮都不节流)互不替代——前者是"首轮抢速度",后者是"全程不限速"。
|
||
First_No_Pace_Flag: int = 0 # 1=第一轮不启用节流
|
||
|
||
# confirm-watch(隐藏内部子命令)参数
|
||
Watch_Interval: int = 0 # 秒(父进程由 --need-confirm 传入)
|
||
Watch_Timeout: int = -1 # 毫秒;-1 = 未给出(之后取 DEFAULT_CONFIRM_TIMEOUT_MS)
|
||
Watch_ParentPid: LONGLONG = 0 # 父进程 pid(宽类型:承载 DWORD 全域,见 PID_MAX)
|
||
Watch_IpcWrite: ULONGLONG = 0 # check 通道管道**写端句柄数值**(0 = 未给出 ⇒ 无通道)
|
||
Watch_Verbose: int = 0 # --verbose(由父进程在 verbose 时追加传入)
|
||
|
||
# ParseUint64 的出参(模块全局,与 VPe.Pe_RawPtr / VFs.LastReadSize 同一惯例)
|
||
U64_Val: ULONGLONG = 0
|
||
|
||
|
||
# 版本号经函数暴露给 VPe/VScan(跨模块"函数调用"路径明确支持;
|
||
# 避免依赖跨模块字符串 t.CDefine 取值——本仓已证实的是跨模块整型 t.CDefine)
|
||
def Version() -> str:
|
||
return VET_VERSION
|
||
|
||
|
||
def CachePath() -> str:
|
||
return Cache_Path
|
||
|
||
|
||
def Once() -> int:
|
||
return Once_Flag
|
||
|
||
|
||
def NeedConfirm() -> int:
|
||
return Need_Confirm
|
||
|
||
|
||
def SleepMs() -> int:
|
||
return Sleep_Ms
|
||
|
||
|
||
def NeedConfirmTimeout() -> int:
|
||
return Need_Confirm_Timeout
|
||
|
||
|
||
def Verbose() -> int:
|
||
return Verbose_Flag
|
||
|
||
|
||
def SearchPaths() -> int:
|
||
return Search_Paths_Flag
|
||
|
||
|
||
# --pace/--pace-units 取值:PaceSet()==0 时调用方(VScan.RunScan)应完全忽略后两者、沿用自身缺省,
|
||
# 以保证"未给出参数时行为逐字不变";==1 时三者的组合语义见 RunScan 的注释。
|
||
def PaceSet() -> int:
|
||
return Pace_Set
|
||
|
||
|
||
def PaceSleepMs() -> int:
|
||
return Pace_Ms
|
||
|
||
|
||
def PaceFileUnits() -> int:
|
||
return Pace_Units
|
||
|
||
|
||
# --first-no-pace:是否要求"第 1 轮不启用节流"。1=是(仅第 1 轮;之后各轮按常规规则)。
|
||
def FirstNoPace() -> int:
|
||
return First_No_Pace_Flag
|
||
|
||
|
||
# 顶层写法专用:`VET.exe --search-paths ...`(子命令位置就是该开关)。
|
||
# 必须在 ParseScanArgs 之前调用——它把该意图记进 Search_Paths_Implicit,解析结束时再并入
|
||
# Search_Paths_Flag;这样解析里"Search_Paths_Flag == 0 才要求 --cache"的放宽也一并生效。
|
||
def SetSearchPathsImplicit():
|
||
global Search_Paths_Implicit
|
||
Search_Paths_Implicit = 1
|
||
|
||
|
||
def WatchInterval() -> int:
|
||
return Watch_Interval
|
||
|
||
|
||
def WatchTimeout() -> int:
|
||
return Watch_Timeout
|
||
|
||
|
||
def WatchParentPid() -> LONGLONG:
|
||
return Watch_ParentPid
|
||
|
||
|
||
def WatchIpcWrite() -> ULONGLONG:
|
||
return Watch_IpcWrite
|
||
|
||
|
||
def WatchVerbose() -> int:
|
||
return Watch_Verbose
|
||
|
||
|
||
# ver 子命令专用:ver 不解析开关(目标路径固定在 sys._argv[2]),
|
||
# 只要 argv 中出现 --verbose 即视为要求详细日志(argv 起点与 scan 一致,从下标 2 扫)。
|
||
def HasVerbose() -> int:
|
||
i: int = 2
|
||
while i < sys._argc:
|
||
A: str = sys._argv[i]
|
||
if A is not None:
|
||
if string.strcmp(A, "--verbose") == 0:
|
||
return 1
|
||
i += 1
|
||
return 0
|
||
|
||
|
||
# 严格解析十进制非负整数:仅接受 '0'-'9'(空串/空白/正负号/小数点/其它字符 → -1=非法),
|
||
# 上界 Max(超出 → -1,避免无意义超大间隔与运算溢出)。
|
||
# 不用 string.atoi(includes/string.vp:271):它对非法尾字符静默截断("3x" → 3),
|
||
# 与本参数"整数字面量"的约定不符。
|
||
#
|
||
# 宽度(本函数修的就是这一条):Max / 累加器 v / 返回值统一用 LONGLONG(有符号 64 位)。
|
||
# * 原 `Max: int`(i32)配 PID_MAX=4294967295 会被截断成 -1,于是 `v > Max`
|
||
# 对任何首位数字恒真 ⇒ 任何 --parent-pid 都判非法(看门狗启动即 exit 1)。
|
||
# IR 证据:本仓上一轮构建 IR
|
||
# VET/.tpv_build/temp/df/df86f78f0f0481a1.ll:521
|
||
# define i32 @df86f78f0f0481a1.ParseUint(i8* %S, i32 %Max) ; Max 是 i32
|
||
# VET/.tpv_build/temp/df/df86f78f0f0481a1.ll:583
|
||
# %35 = icmp sgt i32 %34, %Max ; 有符号比较,Max=-1
|
||
# VET/.tpv_build/temp/df/df86f78f0f0481a1.ll:1214
|
||
# %108 = call i32 @df86f78f0f0481a1.ParseUint(i8* %107, i32 -1)
|
||
# 后跟 :1217 `%110 = icmp sle i32 %109, 0` ⇒ P<=0 恒真 ⇒ 打错误并 ret 1。
|
||
# * 不用 t.CSizeT/ULONGLONG:无符号返回承载不了 -1 非法哨兵,且各调用点的
|
||
# `if X < 0`(本文件 :165/:180/:196/:253/:264/:275)在无符号比较下恒假 ⇒ 非法值被放行。
|
||
# * 溢出(20 位数字也不会):循环顶部 `if v > Max: return -1` 是不变量守卫——
|
||
# 进入每轮时必有 v <= Max,故 v*10 <= Max*10;本模块全部上界 <= 4294967295,
|
||
# 远小于 i64 上限/10,`v*10 + d` 不可能溢出;末尾 `or v < 0` 再兜一层 64 位
|
||
# 回绕(防御性,按当前上界不可达)。
|
||
def ParseUint(S: str, Max: LONGLONG) -> LONGLONG:
|
||
if S is None:
|
||
return -1
|
||
ub: BYTEPTR = BYTEPTR(S)
|
||
if ub[0] == 0:
|
||
return -1
|
||
v: LONGLONG = 0
|
||
i: int = 0
|
||
while ub[i] != 0:
|
||
ch: int = int(ub[i])
|
||
if ch < 48 or ch > 57:
|
||
return -1
|
||
if v > Max: # 上界短路:兼作 v*10 的溢出守卫(不变量兜底)
|
||
return -1
|
||
v = v * 10 + LONGLONG(ch - 48)
|
||
if v > Max or v < 0: # 越界 / 64 位回绕(后者理论不可达)
|
||
return -1
|
||
i += 1
|
||
return v
|
||
|
||
|
||
# --need-confirm 的秒数解析:等价 ParseUint(S, SECS_MAX)(保持原判定口径与错误码 -1)
|
||
def ParseSecs(S: str) -> int:
|
||
return ParseUint(S, SECS_MAX)
|
||
|
||
|
||
# 严格解析十进制**无符号 64 位**字面量:仅接受 '0'-'9'(空串/空白/正负号/小数点/其它字符/溢出 ⇒ 失败)。
|
||
# 成功返回 1 并把值写入 U64_Val;失败返回 0(U64_Val 置 0)。
|
||
#
|
||
# 为什么不复用 ParseUint:`--ipc-write` 传的是**句柄的位模式**(父进程 ptrtoint 得到)。
|
||
# 64 位 Windows 上内核句柄按符号扩展存放(最高位为 1),其无符号读数超过 INT64_MAX,
|
||
# 而 ParseUint 的返回域(LONGLONG)与 -1 非法哨兵都承载不了它;若截成 32 位传,符号扩展型
|
||
# 句柄在子进程侧就还原不回原值(inttoptr 得到另一个地址)。故这里按 u64 全域解析。
|
||
# 溢出守卫:不变量 v*10 + d ≤ 2^64-1 = 18446744073709551615 ⇒ v ≤ 1844674407370955161,
|
||
# 且 v 取该上界时 d 必须 ≤ 5。
|
||
def ParseUint64(S: str) -> int:
|
||
global U64_Val
|
||
U64_Val = 0
|
||
if S is None:
|
||
return 0
|
||
ub: BYTEPTR = BYTEPTR(S)
|
||
if ub[0] == 0:
|
||
return 0
|
||
v: ULONGLONG = 0
|
||
i: int = 0
|
||
while ub[i] != 0:
|
||
ch: int = int(ub[i])
|
||
if ch < 48 or ch > 57:
|
||
return 0
|
||
d: ULONGLONG = ULONGLONG(ch - 48)
|
||
if v > ULONGLONG(1844674407370955161): # 再乘 10 必溢出
|
||
return 0
|
||
if v == ULONGLONG(1844674407370955161) and d > ULONGLONG(5):
|
||
return 0
|
||
v = v * ULONGLONG(10) + d
|
||
i += 1
|
||
U64_Val = v
|
||
return 1
|
||
|
||
|
||
def PrintUsage():
|
||
VLog.LogStr("VET - Viper Environment Tool\n")
|
||
VLog.LogStr("用法:\n")
|
||
VLog.LogStr(" VET.exe ver <exe> [--verbose]\n")
|
||
VLog.LogStr(" 单次校验指定 exe(常用目录表 + .PRVERSN 段 + --version 前缀);\n")
|
||
VLog.LogStr(" --verbose 为与 scan 同名的无值开关(输出目标路径与解析/探测细节)。\n")
|
||
VLog.LogStr(" VET.exe scan --cache <path> [--once] [--sleep <毫秒>] [--pace <毫秒>] [--pace-units <N>]\n")
|
||
VLog.LogStr(" [--first-no-pace] [--need-confirm <秒> [--need-confirm-timeout <毫秒>]]\n")
|
||
VLog.LogStr(" [--verbose] [--path <dir>]...\n")
|
||
VLog.LogStr(" 常驻扫盘发现器;结果 JSON 写 stdout,缓存写 <path>。\n")
|
||
VLog.LogStr(" VET.exe scan --search-paths [--path <dir>]... [--verbose]\n")
|
||
VLog.LogStr(" VET.exe --search-paths [--path <dir>]... [--verbose] (同上,可省 scan:纯查询)\n")
|
||
VLog.LogStr(" --once 只跑一轮(便于验证)。\n")
|
||
VLog.LogStr(" --sleep <毫秒> 每轮间隔(缺省 10000;0 = 不 sleep;上限 86400000)。\n")
|
||
VLog.LogStr(" --pace <毫秒> 节流点睡眠时长(缺省 20;0 = 关闭节流;上限 60000)。\n")
|
||
VLog.LogStr(" --pace-units <N> 每 N 个文件一个节流点(缺省 64;1 = 每个文件都停;上限 1000000)。\n")
|
||
VLog.LogStr(" 显式给出 --pace 或 --pace-units 即启用节流,--once 下也生效;\n")
|
||
VLog.LogStr(" 未给出时沿用缺省(常驻启用;--once 关闭)。目录侧粒度恒为 16。\n")
|
||
VLog.LogStr(" --first-no-pace 第 1 轮扫盘不启用节流(只影响第 1 轮;之后各轮按常规规则)。\n")
|
||
VLog.LogStr(" 与 --pace 0 的区别:后者是全程不限速。仅常驻多轮时才有意义。\n")
|
||
VLog.LogStr(" --need-confirm <秒> 心跳确认:独立看门狗子进程每 <秒> 秒要求 stdin 输入一行\n")
|
||
VLog.LogStr(" {\"ok\": 1};超时内未输入或 ok!=1 则输出 {\"exit\":1, ...}\n")
|
||
VLog.LogStr(" 并终止本进程(退出码 1)。缺省/0 = 关闭;仅 scan 模式生效。\n")
|
||
VLog.LogStr(" --need-confirm-timeout <毫秒>\n")
|
||
VLog.LogStr(" 确认等待超时(缺省 5000;仅在给出 --need-confirm 时有意义)。\n")
|
||
VLog.LogStr(" --verbose 输出详细过程日志(进度/扫描根/逐条过滤/探测细节);\n")
|
||
VLog.LogStr(" 缺省安静:只留启动摘要、心跳确认提示、警告与轮末一行。\n")
|
||
VLog.LogStr(" --path <dir> 强制替换常用目录表(可重复);一出现即不做任何自动建表。\n")
|
||
VLog.LogStr(" --search-paths 打印将搜索的路径后退出:建好常用目录表即逐行列出\n")
|
||
VLog.LogStr(" (序号 + 路径 + 目录表来源,输出到 stderr),不扫盘、\n")
|
||
VLog.LogStr(" 不拉起看门狗、不读写缓存;此时 --cache 非必填。\n")
|
||
|
||
|
||
# 解析 scan 参数。返回 0=成功,1=参数错误(已打印用法/错误)
|
||
# 说明:本轮起 ParseScanArgs 由 App/main.vp 在分派 scan 时调用(看门狗生命周期需要
|
||
# 在 RunScan 之前拿到 NeedConfirm),VScan.RunScan 不再自行解析。
|
||
def ParseScanArgs() -> int:
|
||
global Cache_Path, Once_Flag, Need_Confirm, Sleep_Ms, Need_Confirm_Timeout, Verbose_Flag
|
||
global Search_Paths_Flag, Pace_Set, Pace_Ms, Pace_Units, First_No_Pace_Flag
|
||
Cache_Path = None
|
||
Once_Flag = 0
|
||
Need_Confirm = 0
|
||
Sleep_Ms = DEFAULT_SLEEP_MS
|
||
Need_Confirm_Timeout = DEFAULT_CONFIRM_TIMEOUT_MS
|
||
Verbose_Flag = 0
|
||
Search_Paths_Flag = 0
|
||
Pace_Set = 0
|
||
Pace_Ms = DEFAULT_PACE_SLEEP_MS
|
||
Pace_Units = DEFAULT_PACE_FILE_UNITS
|
||
First_No_Pace_Flag = 0
|
||
if Search_Paths_Implicit != 0:
|
||
# 顶层写法(`VET.exe --search-paths ...`):该开关位于 argv[1](子命令位置),
|
||
# 本函数从 argv[2] 起扫,扫不到它 ⇒ 由 main.vp 预先置位,这里并入(见其声明处)。
|
||
Search_Paths_Flag = 1
|
||
Has_Timeout: int = 0
|
||
i: int = 2
|
||
while i < sys._argc:
|
||
A: str = sys._argv[i]
|
||
if A is not None:
|
||
if string.strcmp(A, "--cache") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
Cache_Path = sys._argv[i + 1]
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("错误: --cache 需要一个路径参数\n")
|
||
return 1
|
||
elif string.strcmp(A, "--once") == 0:
|
||
Once_Flag = 1
|
||
elif string.strcmp(A, "--verbose") == 0:
|
||
Verbose_Flag = 1 # 无值开关:只改 stderr 详细度
|
||
elif string.strcmp(A, "--search-paths") == 0:
|
||
Search_Paths_Flag = 1 # 无值开关:查询"会搜索的路径"后直接退出(不扫盘/不碰缓存)
|
||
elif string.strcmp(A, "--sleep") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
M: int = ParseUint(sys._argv[i + 1], SLEEP_MAX_MS)
|
||
if M < 0:
|
||
VLog.LogFmt1("错误: --sleep 需要一个 0..%u 的整数字面量毫秒数,实际为: ",
|
||
t.CSizeT(SLEEP_MAX_MS))
|
||
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
|
||
PrintUsage()
|
||
return 1
|
||
Sleep_Ms = M
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("错误: --sleep 需要一个毫秒数参数\n")
|
||
PrintUsage()
|
||
return 1
|
||
elif string.strcmp(A, "--pace") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
P: int = ParseUint(sys._argv[i + 1], PACE_SLEEP_MAX_MS)
|
||
if P < 0:
|
||
VLog.LogFmt1("错误: --pace 需要一个 0..%u 的整数字面量毫秒数,实际为: ",
|
||
t.CSizeT(PACE_SLEEP_MAX_MS))
|
||
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
|
||
PrintUsage()
|
||
return 1
|
||
Pace_Ms = P
|
||
Pace_Set = 1 # 显式给出 ⇒ 节流启用(--once 下也生效)
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("错误: --pace 需要一个毫秒数参数\n")
|
||
PrintUsage()
|
||
return 1
|
||
elif string.strcmp(A, "--pace-units") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
U: int = ParseUint(sys._argv[i + 1], PACE_UNITS_MAX)
|
||
# 0 与非法同为错误(ParseUint 非法返回 -1):0 会让"每 0 个文件一个节流点"无意义
|
||
if U <= 0:
|
||
VLog.LogFmt1("错误: --pace-units 需要一个 1..%u 的整数字面量文件数,实际为: ",
|
||
t.CSizeT(PACE_UNITS_MAX))
|
||
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
|
||
PrintUsage()
|
||
return 1
|
||
Pace_Units = U
|
||
Pace_Set = 1 # 显式给出 ⇒ 节流启用(--once 下也生效)
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("错误: --pace-units 需要一个文件数参数\n")
|
||
PrintUsage()
|
||
return 1
|
||
elif string.strcmp(A, "--first-no-pace") == 0:
|
||
# 无值开关:只要求"第 1 轮不节流",不改变节流参数本身、也不置 Pace_Set
|
||
# (第 2 轮起的节流规则与未给该开关时逐字相同)。
|
||
First_No_Pace_Flag = 1
|
||
elif string.strcmp(A, "--need-confirm") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
S: int = ParseSecs(sys._argv[i + 1])
|
||
if S < 0:
|
||
# 两个固定元数调用拼一行(LogFmt1 只带整数,LogFmtS1 只带字符串)
|
||
VLog.LogFmt1("错误: --need-confirm 需要一个 0..%u 的整数字面量秒数,实际为: ",
|
||
t.CSizeT(SECS_MAX))
|
||
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
|
||
PrintUsage()
|
||
return 1
|
||
Need_Confirm = S
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("错误: --need-confirm 需要一个秒数参数\n")
|
||
PrintUsage()
|
||
return 1
|
||
elif string.strcmp(A, "--need-confirm-timeout") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
T: int = ParseUint(sys._argv[i + 1], CONFIRM_TIMEOUT_MAX_MS)
|
||
if T < 0:
|
||
VLog.LogFmt1("错误: --need-confirm-timeout 需要一个 0..%u 的整数字面量毫秒数,实际为: ",
|
||
t.CSizeT(CONFIRM_TIMEOUT_MAX_MS))
|
||
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
|
||
PrintUsage()
|
||
return 1
|
||
Need_Confirm_Timeout = T
|
||
Has_Timeout = 1
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("错误: --need-confirm-timeout 需要一个毫秒数参数\n")
|
||
PrintUsage()
|
||
return 1
|
||
elif string.strcmp(A, "--path") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
VDirs.BeginOverride()
|
||
VDirs.AddOverridePath(sys._argv[i + 1])
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("错误: --path 需要一个目录参数\n")
|
||
return 1
|
||
else:
|
||
VLog.LogFmtS1("错误: 未知参数 %s\n", A)
|
||
PrintUsage()
|
||
return 1
|
||
i += 1
|
||
# --search-paths 是纯查询开关(只建表 + 打印,不读写缓存、不扫盘):未给 --cache 时放行。
|
||
# 其余校验一律不放宽——给了 --cache 仍按原样接受(该查询只是不使用它)。
|
||
if Cache_Path is None and Search_Paths_Flag == 0:
|
||
VLog.LogStr("错误: scan 模式必须提供 --cache <path>\n")
|
||
PrintUsage()
|
||
return 1
|
||
# 取舍:--need-confirm-timeout 单独出现时不报错(仅告警忽略)——它只是给
|
||
# --need-confirm 的从属参数,报错会因标志顺序/复用脚本而无谓失败;告警避免静默无效。
|
||
if Has_Timeout != 0 and Need_Confirm <= 0:
|
||
VLog.LogStr("警告: --need-confirm-timeout 未与 --need-confirm 一起使用,已忽略\n")
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# confirm-watch(内部隐藏子命令)参数解析
|
||
# 命令行形如:VET.exe confirm-watch --interval <秒> --timeout <毫秒> --parent-pid <pid>
|
||
# --ipc-write <句柄数值> [--verbose]
|
||
# --interval 必填且 >=1;--timeout 缺省 DEFAULT_CONFIRM_TIMEOUT_MS;--parent-pid 必填。
|
||
# --ipc-write 由父进程在建立 check 通道管道时传入(写端句柄的数值);缺省 0 = 无通道。
|
||
# --verbose 由父进程在自身为 verbose 时追加,使看门狗按同一规则输出细节。
|
||
# 该命令不出现在用法文本里,由父进程(scan + --need-confirm)自动拉起。
|
||
# ============================================================
|
||
|
||
def ParseWatchArgs() -> int:
|
||
global Watch_Interval, Watch_Timeout, Watch_ParentPid, Watch_IpcWrite, Watch_Verbose
|
||
Watch_Interval = 0
|
||
Watch_Timeout = -1
|
||
Watch_ParentPid = 0
|
||
Watch_IpcWrite = 0
|
||
Watch_Verbose = 0
|
||
Has_Pid: int = 0
|
||
i: int = 2
|
||
while i < sys._argc:
|
||
A: str = sys._argv[i]
|
||
if A is not None:
|
||
if string.strcmp(A, "--interval") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
S: int = ParseUint(sys._argv[i + 1], SECS_MAX)
|
||
if S <= 0:
|
||
VLog.LogStr("confirm-watch: --interval 需要一个 >=1 的整数字面量秒数\n")
|
||
return 1
|
||
Watch_Interval = S
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("confirm-watch: --interval 需要一个秒数参数\n")
|
||
return 1
|
||
elif string.strcmp(A, "--timeout") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
T: int = ParseUint(sys._argv[i + 1], CONFIRM_TIMEOUT_MAX_MS)
|
||
if T < 0:
|
||
VLog.LogStr("confirm-watch: --timeout 需要一个非负整数字面量毫秒数\n")
|
||
return 1
|
||
Watch_Timeout = T
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("confirm-watch: --timeout 需要一个毫秒数参数\n")
|
||
return 1
|
||
elif string.strcmp(A, "--parent-pid") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
P: LONGLONG = ParseUint(sys._argv[i + 1], PID_MAX)
|
||
if P <= 0:
|
||
VLog.LogStr("confirm-watch: --parent-pid 需要一个正整数\n")
|
||
return 1
|
||
Watch_ParentPid = P
|
||
Has_Pid = 1
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("confirm-watch: --parent-pid 需要一个 pid 参数\n")
|
||
return 1
|
||
elif string.strcmp(A, "--ipc-write") == 0:
|
||
if i + 1 < sys._argc and sys._argv[i + 1] is not None:
|
||
if ParseUint64(sys._argv[i + 1]) == 0:
|
||
VLog.LogFmtS1("confirm-watch: --ipc-write 需要一个十进制非负整数字面量句柄值,实际为: ",
|
||
sys._argv[i + 1])
|
||
VLog.LogStr("\n")
|
||
return 1
|
||
Watch_IpcWrite = U64_Val
|
||
i += 1
|
||
else:
|
||
VLog.LogStr("confirm-watch: --ipc-write 需要一个句柄数值参数\n")
|
||
return 1
|
||
elif string.strcmp(A, "--verbose") == 0:
|
||
Watch_Verbose = 1 # 无值开关:由父进程按自身 verbose 追加
|
||
else:
|
||
VLog.LogFmtS1("confirm-watch: 未知参数 %s\n", A)
|
||
return 1
|
||
i += 1
|
||
if Watch_Interval <= 0:
|
||
VLog.LogStr("confirm-watch: 缺少 --interval <秒>\n")
|
||
return 1
|
||
if Has_Pid == 0:
|
||
VLog.LogStr("confirm-watch: 缺少 --parent-pid <pid>\n")
|
||
return 1
|
||
if Watch_Timeout < 0:
|
||
Watch_Timeout = DEFAULT_CONFIRM_TIMEOUT_MS
|
||
return 0
|