import json,hashlib,shutil,sys,subprocess,os,re
from pathlib import Path
root=Path(sys.argv[1]).resolve();dm=Path('/home/dmdba/dmdbms');native=dm/'bin';files={}
def add(src,dst):
 src=src.resolve()
 if not src.is_file():raise SystemExit('现场客户端文件缺失：'+str(src))
 if dst in files and files[dst]!=src:raise SystemExit('客户端SONAME目标冲突：'+str(dst))
 files[dst]=src
add(dm/'drivers/php_pdo/php73_pdo_dm.so',root/'dm-client/php73_pdo_dm.so')
for n in ['libdmdpi.so','libdmcrypt.so','libdmcvt.so','libdmzip2.so']:
 src=native/n
 if n=='libdmzip2.so' and not src.is_file():continue
 add(src,root/'dm-client/lib'/n)
 env=dict(os.environ,LD_LIBRARY_PATH=str(native))
 p=subprocess.run(['ldd',str(src)],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=True,env=env)
 if p.returncode or 'not found'in p.stdout:raise SystemExit('现场客户端依赖不完整：'+str(src))
 for soname,path in re.findall(r'^\s*(\S+)\s+=>\s+(/\S+)\s',p.stdout,re.M):
  q=Path(path).resolve()
  if str(q).startswith(str(native)+'/'):
   if not re.fullmatch(r'[A-Za-z0-9_.+-]+',soname):raise SystemExit('Invalid native SONAME')
   add(q,root/'dm-client/lib'/soname)
# Preserve the vendor DPI's own dependency search layout; never replace PHP/system SSL.
for name in ['libcrypto.so','libssl.so']:add(native/'dependencies'/name,root/'dm-client/lib/dependencies'/name)
for dst in files:
 if dst.exists() or dst.is_symlink():raise SystemExit('目标已存在，停止，不覆盖：'+str(dst))
report=[]
for dst,src in files.items():
 dst.parent.mkdir(parents=True,exist_ok=True);shutil.copyfile(src,dst);dst.chmod(0o644)
 report.append({'source':str(src),'destination':str(dst),'sha256':hashlib.sha256(dst.read_bytes()).hexdigest()})
(root/'dm-client/SOURCE.json').write_text(json.dumps(report,indent=2)+'\n')
from repair_native_sonames import repair
repair(root)
print('现场达梦PDO扩展、DPI与其加密/字符转换依赖已复制：'+str(len(report))+'个文件；不含配置、口令或授权文件')
