Files
foxbin2prg/vcx2txt.ps1
Marius Mutu f36a26a9c6 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>
2026-07-09 18:30:21 +03:00

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`""