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>
81 lines
3.4 KiB
PowerShell
81 lines
3.4 KiB
PowerShell
<#
|
|
.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 }
|
|
}
|