Adauga write-back text->binar VFP: txt2vcx.ps1 + vfp_filemap.ps1 + test_roundtrip.ps1

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>
This commit is contained in:
2026-07-09 18:30:21 +03:00
parent b449e0aa2f
commit f36a26a9c6
6 changed files with 873 additions and 6 deletions

80
vfp_filemap.ps1 Normal file
View File

@@ -0,0 +1,80 @@
<#
.SYNOPSIS
Harta comuna bin<->memo<->text pentru conversiile VFP (FoxBin2Prg), dot-sourced de
vcx2txt.ps1 (Bin2Prg) si txt2vcx.ps1 (Prg2Bin), ca sa nu existe doua copii ale hartii.
.DESCRIPTION
Defineste $script:VfpFileMap: extensie-binar (lowercase, cu punct) -> @(extensie-memo, extensie-text).
Si o functie inversa Get-VfpBinExtensionForTextExtension pentru txt2vcx.ps1 (text -> binar).
#>
$script:VfpFileMap = @{
'.vcx' = @('.vct', '.vc2'); '.scx' = @('.sct', '.sc2'); '.frx' = @('.frt', '.fr2')
'.lbx' = @('.lbt', '.lb2'); '.mnx' = @('.mnt', '.mn2'); '.dbc' = @('.dct', '.dc2')
}
function Get-VfpBinExtensionForTextExtension {
<#
.SYNOPSIS
Data o extensie text (.vc2, .sc2, ...), intoarce @(extensie-binar, extensie-memo), sau $null daca nu e cunoscuta.
#>
param([Parameter(Mandatory)][string]$TextExtension)
$t = $TextExtension.ToLower()
foreach ($binExt in $script:VfpFileMap.Keys) {
if ($script:VfpFileMap[$binExt][1] -eq $t) {
return @($binExt, $script:VfpFileMap[$binExt][0])
}
}
return $null
}
function Invoke-FoxBin2PrgSafe {
<#
.SYNOPSIS
Ruleaza FoxBin2Prg.EXE cu un watchdog de timp, ca sa nu ramana blocat la infinit.
.DESCRIPTION
FoxBin2Prg.EXE poate afisa un MessageBox MODAL nativ al motorului VFP (nu al
propriului sau error-handler) cand recompilarea textului esueaza (ex. text
stricat, ENDPROC lipsa) - vazut concret: "Error 1098, Procedure not closed...".
Parametrul cDontShowErrors='1' NU suprima acest dialog (acela controleaza doar
raportarea de erori proprie a FoxBin2Prg, nu eroarea nativa de compilare VFP).
Fara acest watchdog, un rulaj automat/CI ar ramane agatat la infinit asteptand
un om sa apese OK. Orice timeout se trateaza ca ESEC pentru fisierul respectiv.
.PARAMETER ExePath
Calea catre FoxBin2Prg.EXE.
.PARAMETER ExeArgs
Lista de argumente pozitionale, in ordinea folosita de FoxBin2Prg (inclusiv
string-urile goale intre ele, ca in apelul cu "&").
.PARAMETER TimeoutSeconds
Cat asteapta procesul sa termine normal inainte sa-l omoare (implicit 60s -
conversiile uzuale dureaza sub o secunda; un timeout mare inseamna aproape
sigur un dialog modal blocat).
.OUTPUTS
PSCustomObject cu TimedOut (bool) si ExitCode (int; -1 daca a fost omorat la timeout).
#>
param(
[Parameter(Mandatory)][string]$ExePath,
[Parameter(Mandatory)][AllowEmptyCollection()][AllowEmptyString()][string[]]$ExeArgs,
[int]$TimeoutSeconds = 60
)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $ExePath
$psi.Arguments = ($ExeArgs | ForEach-Object { '"' + ($_ -replace '"', '\"') + '"' }) -join ' '
$psi.UseShellExecute = $false
$proc = [System.Diagnostics.Process]::Start($psi)
$finished = $proc.WaitForExit($TimeoutSeconds * 1000)
if (-not $finished) {
Write-Warning "Invoke-FoxBin2PrgSafe: TIMEOUT dupa $TimeoutSeconds s (probabil MessageBox modal blocat) - omor procesul (PID $($proc.Id)) si eventualele lui ferestre/copii."
try { & taskkill /PID $proc.Id /T /F 2>&1 | Out-Null } catch {}
try { if (-not $proc.HasExited) { $proc.Kill() } } catch {}
Start-Sleep -Milliseconds 300
return [PSCustomObject]@{ TimedOut = $true; ExitCode = -1 }
}
return [PSCustomObject]@{ TimedOut = $false; ExitCode = $proc.ExitCode }
}