Fluxul complet bin->text->edit->bin pentru .vcx/.scx (faza 1; .mnx/.frx raman read-only): regenerare+compilare in staging unic per rulare, guard-uri (COMUN cu -AllowComun, staleness, binar lipsa, folder foxbin2prg), fidelity check pe octeti inainte de orice copiere, copiere .vct->.vcx cu restaurare la esec partial, refresh cache. Harta de extensii extrasa in vfp_filemap.ps1, dot-sourced si de vcx2txt.ps1 (comportament identic). Invoke-FoxBin2PrgSafe: wrapper cu timeout+taskkill pentru toate invocarile exe-ului - la text stricat Prg2Bin intoarce ErrorLevel 0 dar binarul corupt poate ridica MessageBox modal VFP nesuprimabil la reconversie. test_roundtrip.ps1: faze fidelitate/editare/smoke siblings/negativ+guard-uri, 41 PASS pe ROAGEST + smoke ROAIMOB/ROAACNPRO. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
154 lines
6.6 KiB
PowerShell
154 lines
6.6 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Converteste in TEXT (.vc2/.sc2/.fr2/.mn2/.dc2/.lb2) librariile/formele VFP binare,
|
|
ca sa poata fi cautat codul din proceduri/metode cu grep.
|
|
|
|
.DESCRIPTION
|
|
Implicit ("project mode"), converteste DOAR fisierele incluse in proiect (citite din .pjx),
|
|
nu tot folderul COMUN (care contine multe librarii nefolosite in ROAACNPRO).
|
|
|
|
Pentru fiecare fisier copiaza binarul + memo-ul (.vct/.sct/...) intr-un folder de cache care
|
|
oglindeste structura proiectului (ca sa NU murdareasca arborele SVN), ruleaza FoxBin2Prg.exe
|
|
acolo, si pastreaza doar fisierul TEXT. Incremental: sare peste ce e deja la zi.
|
|
|
|
NU e nevoie de IDE-ul VFP deschis (se foloseste FoxBin2Prg.exe compilat).
|
|
|
|
.PARAMETER Source
|
|
Optional. Daca e dat, ignora proiectul si converteste exact: un fisier .vcx/.scx/... SAU un
|
|
folder (recursiv). Folosit pentru conversii punctuale.
|
|
|
|
.PARAMETER Project
|
|
Fisierul .pjx din care se citeste lista de fisiere (implicit roaacnpro.PJX).
|
|
|
|
.PARAMETER Types
|
|
Ce extensii sa includa in project mode (implicit vcx,scx = cod). Ex: -Types vcx,scx,frx,mnx
|
|
|
|
.PARAMETER ProjectRoot
|
|
Radacina proiectului (implicit D:\ROA\ROAACNPRO). Caile din .pjx sunt relative la ea.
|
|
|
|
.PARAMETER CacheRoot
|
|
Unde se scrie textul (implicit D:\ROA\_vfp_textcache\roacnpro).
|
|
IMPORTANT: tine cache-ul in AFARA folderului foxbin2prg, altfel ReCreate_FoxBin2Prg.prg
|
|
(care face Prg2Bin recursiv pe tot folderul tool-ului) reconverteste .??2-urile din cache in binare.
|
|
|
|
.PARAMETER Clean
|
|
Sterge tot cache-ul inainte de conversie (util dupa ce s-au convertit accidental fisiere extra).
|
|
|
|
.PARAMETER Force
|
|
Reconverteste chiar daca textul exista si e mai nou decat sursa.
|
|
|
|
.EXAMPLE
|
|
# doar fisierele proiectului (recomandat, rapid)
|
|
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1
|
|
|
|
.EXAMPLE
|
|
# include si rapoartele si meniurile
|
|
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Types vcx,scx,frx,mnx
|
|
|
|
.EXAMPLE
|
|
# conversie punctuala a unei librarii (chiar daca nu e in proiect)
|
|
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Source D:\ROA\ROAACNPRO\COMUN\clase\caut.vcx
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$Source,
|
|
[string]$Project = 'D:\ROA\ROAACNPRO\roaacnpro.PJX',
|
|
[string[]]$Types = @('vcx','scx'),
|
|
[string]$ProjectRoot = 'D:\ROA\ROAACNPRO',
|
|
[string]$CacheRoot = 'D:\ROA\_vfp_textcache\roacnpro',
|
|
[switch]$Clean,
|
|
[switch]$Force
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$exe = 'D:\ROA\UTIL\foxbin2prg\FoxBin2Prg.EXE'
|
|
if (-not (Test-Path $exe)) {
|
|
throw "Nu gasesc $exe. Compileaza intai FoxBin2Prg (deschide foxbin2prg.pj2 in VFP si Build)."
|
|
}
|
|
|
|
# binar -> (memo, extensie text) -- harta comuna, partajata cu txt2vcx.ps1
|
|
. (Join-Path $PSScriptRoot 'vfp_filemap.ps1')
|
|
$map = $script:VfpFileMap
|
|
|
|
function Convert-One($fullPath) {
|
|
$f = Get-Item -LiteralPath $fullPath
|
|
$ext = $f.Extension.ToLower()
|
|
if (-not $map.ContainsKey($ext)) { return 'skip' }
|
|
$memoX = $map[$ext][0]; $txtX = $map[$ext][1]
|
|
$memo = [IO.Path]::ChangeExtension($f.FullName, $memoX)
|
|
|
|
$rel = $f.FullName
|
|
if ($rel.ToLower().StartsWith($ProjectRoot.ToLower())) {
|
|
$rel = $rel.Substring($ProjectRoot.Length).TrimStart('\')
|
|
} else { $rel = $f.Name }
|
|
$relDir = Split-Path $rel -Parent
|
|
$workDir = if ($relDir) { Join-Path $CacheRoot $relDir } else { $CacheRoot }
|
|
$txtOut = Join-Path $workDir ([IO.Path]::GetFileNameWithoutExtension($f.Name) + $txtX)
|
|
|
|
if (-not $Force -and (Test-Path $txtOut) -and ((Get-Item $txtOut).LastWriteTime -ge $f.LastWriteTime)) {
|
|
return 'uptodate'
|
|
}
|
|
if (-not (Test-Path $workDir)) { New-Item -ItemType Directory -Force -Path $workDir | Out-Null }
|
|
|
|
$binCopy = Join-Path $workDir $f.Name
|
|
Copy-Item -LiteralPath $f.FullName -Destination $binCopy -Force
|
|
if (Test-Path $memo) { Copy-Item -LiteralPath $memo -Destination (Join-Path $workDir (Split-Path $memo -Leaf)) -Force }
|
|
|
|
& $exe $binCopy '' '' '' '1' '0' '1' | Out-Null
|
|
|
|
Remove-Item -LiteralPath $binCopy -Force -ErrorAction SilentlyContinue
|
|
$memoCopy = Join-Path $workDir (Split-Path $memo -Leaf)
|
|
if (Test-Path $memoCopy) { Remove-Item -LiteralPath $memoCopy -Force -ErrorAction SilentlyContinue }
|
|
|
|
if (Test-Path $txtOut) { return 'ok' } else { return 'fail' }
|
|
}
|
|
|
|
# --- Construieste lista de fisiere ---
|
|
$list = @()
|
|
if (-not [string]::IsNullOrWhiteSpace($Source)) {
|
|
if (Test-Path $Source -PathType Leaf) { $list = @((Get-Item -LiteralPath $Source).FullName) }
|
|
elseif (Test-Path $Source -PathType Container) {
|
|
$list = Get-ChildItem -LiteralPath $Source -Recurse -File |
|
|
Where-Object { $map.ContainsKey($_.Extension.ToLower()) } |
|
|
ForEach-Object { $_.FullName }
|
|
} else { throw "Sursa nu exista: $Source" }
|
|
} else {
|
|
# PROJECT MODE: citeste .pjx -> pj2 si extrage fisierele
|
|
if (-not (Test-Path $Project)) { throw "Nu gasesc proiectul: $Project" }
|
|
$tmp = Join-Path $env:TEMP ('pjx_' + [IO.Path]::GetFileNameWithoutExtension($Project))
|
|
New-Item -ItemType Directory -Force -Path $tmp | Out-Null
|
|
$pjxName = [IO.Path]::GetFileName($Project)
|
|
Copy-Item -LiteralPath $Project -Destination (Join-Path $tmp $pjxName) -Force
|
|
$pjt = [IO.Path]::ChangeExtension($Project, '.pjt')
|
|
if (Test-Path $pjt) { Copy-Item -LiteralPath $pjt -Destination (Join-Path $tmp ([IO.Path]::GetFileName($pjt))) -Force }
|
|
& $exe (Join-Path $tmp $pjxName) '' '' '' '1' '0' '1' | Out-Null
|
|
$pj2 = Join-Path $tmp ([IO.Path]::GetFileNameWithoutExtension($Project) + '.pj2')
|
|
if (-not (Test-Path $pj2)) { throw "Conversia .pjx a esuat: $pj2" }
|
|
|
|
$extPat = ($Types | ForEach-Object { [regex]::Escape($_) }) -join '|'
|
|
$rx = [regex]("(?i)\.ADD\('([^']*\.(?:$extPat))'\)")
|
|
foreach ($m in $rx.Matches((Get-Content -Raw $pj2))) {
|
|
$relp = $m.Groups[1].Value
|
|
$abs = Join-Path $ProjectRoot $relp
|
|
if (Test-Path $abs) { $list += (Get-Item -LiteralPath $abs).FullName }
|
|
else { Write-Warning "lipsa: $relp" }
|
|
}
|
|
$list = $list | Sort-Object -Unique
|
|
}
|
|
|
|
if ($Clean -and (Test-Path $CacheRoot)) { Remove-Item -Recurse -Force $CacheRoot }
|
|
if (-not $list) { Write-Host 'Nimic de convertit.'; return }
|
|
|
|
$ok=0;$up=0;$fail=0
|
|
foreach ($p in $list) {
|
|
switch (Convert-One $p) {
|
|
'ok' { $ok++; $r = $p; if ($r.ToLower().StartsWith($ProjectRoot.ToLower())) { $r = $r.Substring($ProjectRoot.Length).TrimStart('\') }; Write-Host "OK $r" }
|
|
'uptodate' { $up++ }
|
|
'fail' { $fail++; Write-Warning "ESEC $p" }
|
|
}
|
|
}
|
|
Write-Host ''
|
|
Write-Host "Gata. Convertite: $ok Sarite(la zi): $up Esuate: $fail (tipuri: $($Types -join ','))"
|
|
Write-Host "Text in: $CacheRoot"
|
|
Write-Host "Cauta cu: grep -rn 'expresie' `"$CacheRoot`""
|