initial: nsParser plugin 1.0.1
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{yml,yaml,json}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{cmd,bat,ps1}]
|
||||||
|
end_of_line = crlf
|
||||||
|
|
||||||
|
[*.{nsi,nsh,sln,vcxproj,filters}]
|
||||||
|
end_of_line = crlf
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Normalizzazione line endings
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# File di codice
|
||||||
|
*.c text eol=lf
|
||||||
|
*.cpp text eol=lf
|
||||||
|
*.h text eol=lf
|
||||||
|
*.hpp text eol=lf
|
||||||
|
*.def text eol=lf
|
||||||
|
*.py text eol=lf
|
||||||
|
*.ps1 text eol=lf
|
||||||
|
*.cmd text eol=crlf
|
||||||
|
*.bat text eol=crlf
|
||||||
|
|
||||||
|
# NSIS
|
||||||
|
*.nsi text eol=crlf
|
||||||
|
*.nsh text eol=crlf
|
||||||
|
|
||||||
|
# Markdown / docs / config
|
||||||
|
*.md text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.txt text eol=lf
|
||||||
|
*.xml text eol=lf
|
||||||
|
|
||||||
|
# Visual Studio (CRLF richiesto)
|
||||||
|
*.sln text eol=crlf
|
||||||
|
*.vcxproj text eol=crlf merge=union
|
||||||
|
*.filters text eol=crlf merge=union
|
||||||
|
*.rc binary
|
||||||
|
*.rc2 binary
|
||||||
|
|
||||||
|
# Binari
|
||||||
|
*.dll binary
|
||||||
|
*.exe binary
|
||||||
|
*.lib binary
|
||||||
|
*.obj binary
|
||||||
|
*.pdb binary
|
||||||
|
*.zip binary
|
||||||
|
*.7z binary
|
||||||
|
*.png binary
|
||||||
|
*.ico binary
|
||||||
|
*.gif binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.bmp binary
|
||||||
|
*.pdf binary
|
||||||
|
|
||||||
|
# Linguistic detection (GitHub Linguist / Gitea analogo)
|
||||||
|
*.nsi linguist-language=NSIS
|
||||||
|
*.nsh linguist-language=NSIS
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Segnala un bug riproducibile
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: ["bug", "triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Grazie per la segnalazione. Compila i campi sotto per aiutarci a riprodurre il problema.
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: plugin-version
|
||||||
|
attributes:
|
||||||
|
label: Versione plugin
|
||||||
|
description: Contenuto del file VERSION o tag della release scaricata
|
||||||
|
placeholder: "1.6.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: architecture
|
||||||
|
attributes:
|
||||||
|
label: Architettura DLL usata
|
||||||
|
options:
|
||||||
|
- x86-ansi
|
||||||
|
- x86-unicode
|
||||||
|
- amd64-unicode
|
||||||
|
- non so / multiple
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: nsis-version
|
||||||
|
attributes:
|
||||||
|
label: Versione NSIS
|
||||||
|
placeholder: "3.10"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Sistema operativo
|
||||||
|
placeholder: "Windows 11 23H2 x64"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: repro
|
||||||
|
attributes:
|
||||||
|
label: Passi per riprodurre
|
||||||
|
description: Script .nsi minimale che riproduce il problema
|
||||||
|
render: nsis
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Comportamento atteso
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: Comportamento osservato
|
||||||
|
description: Includi messaggi di errore esatti, output del log NSIS, screenshot
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional
|
||||||
|
attributes:
|
||||||
|
label: Note aggiuntive
|
||||||
|
description: Qualunque altra informazione utile (workaround tentati, ecc.)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Domande generali
|
||||||
|
url: https://gitea.emulab.it/Simone/nsis-plugin-nsparser/issues?type=question
|
||||||
|
about: Per domande di utilizzo che non sono bug, apri una issue con label "question"
|
||||||
|
- name: Security
|
||||||
|
url: https://gitea.emulab.it/Simone/nsis-plugin-nsparser/security/advisories/new
|
||||||
|
about: Segnala vulnerabilità di security in privato (non aprire issue pubbliche)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: Suggerisci una nuova funzionalità o miglioramento
|
||||||
|
title: "[Feature]: "
|
||||||
|
labels: ["enhancement"]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: Problema / Caso d'uso
|
||||||
|
description: Quale problema vorresti risolvere? Quale workflow vuoi abilitare?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: proposal
|
||||||
|
attributes:
|
||||||
|
label: Proposta
|
||||||
|
description: Come dovrebbe funzionare? Includi esempio di API/uso negli script NSIS se possibile.
|
||||||
|
render: nsis
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternative considerate
|
||||||
|
description: Altri approcci che hai valutato e perché sono stati scartati
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional
|
||||||
|
attributes:
|
||||||
|
label: Note aggiuntive
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
## Descrizione
|
||||||
|
|
||||||
|
<!-- Descrivi cosa cambia questa PR e perché. Riferisci issue se applicabile (Closes #XXX). -->
|
||||||
|
|
||||||
|
## Tipo di Modifica
|
||||||
|
|
||||||
|
- [ ] Bug fix (modifica non breaking che risolve un problema)
|
||||||
|
- [ ] Feature (modifica non breaking che aggiunge funzionalità)
|
||||||
|
- [ ] Breaking change (fix o feature che rompe la compatibilità)
|
||||||
|
- [ ] Documentazione
|
||||||
|
- [ ] Build / CI
|
||||||
|
|
||||||
|
## Architetture Testate
|
||||||
|
|
||||||
|
- [ ] `x86-ansi`
|
||||||
|
- [ ] `x86-unicode`
|
||||||
|
- [ ] `amd64-unicode`
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Build completa senza warning: `python build_plugin.py`
|
||||||
|
- [ ] CHANGELOG.md aggiornato (sezione `[Unreleased]`)
|
||||||
|
- [ ] README / docs aggiornati se l'API è cambiata
|
||||||
|
- [ ] Esempi aggiornati se l'API è cambiata
|
||||||
|
- [ ] Conventional Commits rispettato
|
||||||
|
- [ ] Nessun file in `dist/` o `__pycache__/` committato
|
||||||
|
- [ ] Self-review completata
|
||||||
|
|
||||||
|
## Note Aggiuntive
|
||||||
|
|
||||||
|
<!-- Eventuali considerazioni per i reviewer, screenshot, output di test, ecc. -->
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Build (GitHub) — pipeline CI primaria
|
||||||
|
#
|
||||||
|
# Strategia hosting:
|
||||||
|
# - Repo primario su Gitea (push iniziale)
|
||||||
|
# - Push mirror automatico Gitea -> GitHub
|
||||||
|
# - GitHub esegue TUTTA la CI (Gitea non esegue Actions)
|
||||||
|
#
|
||||||
|
# Trigger:
|
||||||
|
# - push su main (incluso quello propagato dal mirror Gitea)
|
||||||
|
# - PR aperte su GitHub
|
||||||
|
# - run manuale via UI
|
||||||
|
|
||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'examples/**'
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
config: [x86-ansi, x86-unicode, x64-unicode]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v2
|
||||||
|
|
||||||
|
- name: Read VERSION
|
||||||
|
id: version
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$v = (Get-Content VERSION -Raw).Trim()
|
||||||
|
"version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.config }}
|
||||||
|
run: python build_plugin.py --configs ${{ matrix.config }} --verbosity minimal
|
||||||
|
|
||||||
|
- name: Upload DLL
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ github.event.repository.name }}-${{ steps.version.outputs.version }}-${{ matrix.config }}
|
||||||
|
path: dist/**
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# Release (GitHub) — pipeline release primaria
|
||||||
|
#
|
||||||
|
# Triggerata quando un tag v* viene propagato dal mirror Gitea.
|
||||||
|
# Costruisce gli artifact per tutte le architetture e crea la GitHub Release.
|
||||||
|
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ['v*']
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Tag versione (es. v1.7.0)'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
config: [x86-ansi, x86-unicode, x64-unicode]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- uses: microsoft/setup-msbuild@v2
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.config }}
|
||||||
|
run: python build_plugin.py --config ${{ matrix.config }}
|
||||||
|
|
||||||
|
- name: Determine version & arch
|
||||||
|
id: meta
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$tag = "${{ github.ref_name }}"
|
||||||
|
if ([string]::IsNullOrEmpty($tag) -or $tag -eq "main") { $tag = "${{ inputs.tag }}" }
|
||||||
|
$v = $tag -replace '^v',''
|
||||||
|
$map = @{ 'x86-ansi' = 'x86-ansi'; 'x86-unicode' = 'x86-unicode'; 'x64-unicode' = 'amd64-unicode' }
|
||||||
|
$dir = $map['${{ matrix.config }}']
|
||||||
|
echo "version=$v" >> $env:GITHUB_OUTPUT
|
||||||
|
echo "tag=$tag" >> $env:GITHUB_OUTPUT
|
||||||
|
echo "dir=$dir" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Package
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$name = "${{ github.event.repository.name }}-${{ steps.meta.outputs.version }}-${{ steps.meta.outputs.dir }}"
|
||||||
|
$stage = "stage/$name"
|
||||||
|
New-Item -ItemType Directory -Force -Path $stage | Out-Null
|
||||||
|
Copy-Item "dist/${{ steps.meta.outputs.dir }}/*" $stage -Recurse
|
||||||
|
Copy-Item README.md, LICENSE, VERSION, CHANGELOG.md $stage
|
||||||
|
if (Test-Path "include") { Copy-Item "include" $stage -Recurse }
|
||||||
|
if (Test-Path "examples") { Copy-Item "examples" $stage -Recurse }
|
||||||
|
Compress-Archive -Path "$stage/*" -DestinationPath "$name.zip" -Force
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: pkg-${{ matrix.config }}
|
||||||
|
path: '*.zip'
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: SHA256SUMS
|
||||||
|
run: |
|
||||||
|
cd artifacts
|
||||||
|
sha256sum *.zip > SHA256SUMS.txt
|
||||||
|
cat SHA256SUMS.txt
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
artifacts/*.zip
|
||||||
|
artifacts/SHA256SUMS.txt
|
||||||
|
generate_release_notes: true
|
||||||
|
body: |
|
||||||
|
**Repository primario**: vedi link Gitea nel README.
|
||||||
|
|
||||||
|
Il mirror GitHub è generato automaticamente dal repo Gitea (sorgente di verità).
|
||||||
|
Per issue e PR aprire ticket sul repository primario Gitea.
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
# Build output (src/ subdirs only — dist/ is tracked, contains pre-built DLLs)
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
Build/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Visual Studio
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
.vs/
|
||||||
|
ipch/
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
*.opendb
|
||||||
|
*.aps
|
||||||
|
*.tlog
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
*.ilk
|
||||||
|
*.exp
|
||||||
|
*.lib
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.pytest_cache/
|
||||||
|
*.egg-info/
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# Editor / IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# NSIS
|
||||||
|
*.nsh.bak
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
.logs/
|
||||||
|
|
||||||
|
# Sensitive
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
|
||||||
|
# Migration/automation scripts (temporary, contain tokens — never commit)
|
||||||
|
tools/t*.py
|
||||||
|
tools/check_*.py
|
||||||
|
tools/legacy/
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
Tutte le modifiche rilevanti a questo progetto sono documentate in questo file.
|
||||||
|
|
||||||
|
Il formato segue [Keep a Changelog](https://keepachangelog.com/it/1.0.0/),
|
||||||
|
e il progetto aderisce al [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.0.0] — 2026-04-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Prima release come repository indipendente
|
||||||
|
- Build script unificato (`build_plugin.py`) con CLI canonica
|
||||||
|
- CI/CD via GitHub Actions (mirror automatico Gitea → GitHub)
|
||||||
|
- Documentazione completa (README, CONTRIBUTING, SECURITY)
|
||||||
|
|
||||||
|
[Unreleased]: https://gitea.emulab.it/Simone/nsis-plugin-nsparser/compare/v1.0.0...HEAD
|
||||||
|
[1.0.0]: https://gitea.emulab.it/Simone/nsis-plugin-nsparser/releases/tag/v1.0.0
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
# Contribuire a nsParser
|
||||||
|
|
||||||
|
Grazie per il tuo interesse a contribuire!
|
||||||
|
|
||||||
|
> Repository primario: [https://gitea.emulab.it/Simone/nsis-plugin-nsparser](https://gitea.emulab.it/Simone/nsis-plugin-nsparser)
|
||||||
|
> Mirror GitHub: [https://github.com/systempal/nsis-plugin-nsparser](https://github.com/systempal/nsis-plugin-nsparser) (read-only — apri PR su Gitea)
|
||||||
|
|
||||||
|
## Prerequisiti
|
||||||
|
|
||||||
|
| Strumento | Versione | Note |
|
||||||
|
|-----------|----------|------|
|
||||||
|
| Visual Studio | 2022 / 2026 | Workload: Desktop development with C++ (toolset v143/v145) |
|
||||||
|
| Python | 3.7+ | Per `build_plugin.py` |
|
||||||
|
| Git | 2.30+ | Con supporto submoduli se applicabile |
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git clone https://gitea.emulab.it/Simone/nsis-plugin-nsparser.git
|
||||||
|
cd nsis-plugin-nsparser
|
||||||
|
python build_plugin.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Output in `dist/{x86-ansi,x86-unicode,amd64-unicode}/`.
|
||||||
|
|
||||||
|
## Workflow Sviluppo
|
||||||
|
|
||||||
|
### 1. Branch
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git checkout -b feat/nome-feature # nuova feature
|
||||||
|
git checkout -b fix/descrizione-bug # bugfix
|
||||||
|
git checkout -b docs/aggiornamento # solo doc
|
||||||
|
git checkout -b chore/manutenzione # build, dipendenze
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Commit
|
||||||
|
|
||||||
|
Segui [Conventional Commits](https://www.conventionalcommits.org/it/v1.0.0/):
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: aggiunge supporto per formato XYZ
|
||||||
|
fix: corregge crash su Windows 11 ARM
|
||||||
|
docs: aggiorna esempio di utilizzo
|
||||||
|
chore: aggiorna toolset MSVC
|
||||||
|
refactor: estrae logica parsing in funzione separata
|
||||||
|
test: aggiunge test per edge case
|
||||||
|
build: aggiorna script di build
|
||||||
|
ci: aggiorna workflow GitHub Actions
|
||||||
|
```
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat!: rinomina API Foo() in Bar()
|
||||||
|
|
||||||
|
BREAKING CHANGE: Foo() rinominata in Bar(); aggiornare tutti i chiamanti.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Pull Request
|
||||||
|
|
||||||
|
PR aperte verso il branch `main` su Gitea. Verifica:
|
||||||
|
|
||||||
|
- [ ] Build completa senza warning per tutte le architetture: `python build_plugin.py`
|
||||||
|
- [ ] Esempi aggiornati se cambia l'API
|
||||||
|
- [ ] `CHANGELOG.md` aggiornato (sezione `[Unreleased]`)
|
||||||
|
- [ ] Documentazione aggiornata (README, docs/API.md se esiste)
|
||||||
|
- [ ] Conventional Commits rispettato
|
||||||
|
- [ ] Nessun file in `dist/` o `__pycache__/` committato
|
||||||
|
|
||||||
|
## Build Script
|
||||||
|
|
||||||
|
```
|
||||||
|
python build_plugin.py [opzioni]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Opzione | Default | Descrizione |
|
||||||
|
|---------|---------|-------------|
|
||||||
|
| `--config` | `all` | Architettura: `x86-ansi`, `x86-unicode`, `x64-unicode`, `all` |
|
||||||
|
| `--toolset` | `auto` | Visual Studio: `2022`, `2026`, `auto` (rileva il più recente) |
|
||||||
|
| `--jobs` | numero CPU | Thread paralleli per la build |
|
||||||
|
| `--clean` | false | Pulisce `dist/` prima di compilare |
|
||||||
|
| `--install-dir` | none | Copia DLL anche in `<dir>/{arch}/` (es. NSIS plugin folder) |
|
||||||
|
| `--verbose` | false | Output dettagliato (mostra comandi MSBuild) |
|
||||||
|
|
||||||
|
Exit codes:
|
||||||
|
- `0` — Successo
|
||||||
|
- `1` — Errore di build
|
||||||
|
- `2` — Prerequisiti mancanti
|
||||||
|
- `3` — Argomenti invalidi
|
||||||
|
|
||||||
|
## Release Workflow
|
||||||
|
|
||||||
|
Le release sono automatiche via tag Git:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Aggiorna VERSION e CHANGELOG.md
|
||||||
|
echo "1.7.0" > VERSION
|
||||||
|
# (modifica CHANGELOG.md spostando entry da [Unreleased] a [1.7.0])
|
||||||
|
|
||||||
|
# 2. Commit
|
||||||
|
git add VERSION CHANGELOG.md
|
||||||
|
git commit -m "chore(release): v1.7.0"
|
||||||
|
|
||||||
|
# 3. Tag annotato
|
||||||
|
git tag -a v1.7.0 -m "Release v1.7.0"
|
||||||
|
|
||||||
|
# 4. Push
|
||||||
|
git push origin main
|
||||||
|
git push origin v1.7.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Il workflow `.github/workflows/release.yml` compilerà tutte le architetture
|
||||||
|
e creerà la release su Gitea con gli artifact ZIP allegati. Il mirror GitHub
|
||||||
|
riceverà automaticamente il tag.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- C/C++: stile coerente con il file modificato
|
||||||
|
- Python: PEP 8 (4 spazi indent, line length flessibile)
|
||||||
|
- Header file: `#include` ordinati: stdlib → Win32 → progetto
|
||||||
|
- Encoding: UTF-8 senza BOM (eccetto `.rc` che richiede UTF-16 LE)
|
||||||
|
- Line endings: LF in repo (`.gitattributes` normalizza)
|
||||||
|
|
||||||
|
## Reporting Bug
|
||||||
|
|
||||||
|
Apri una [issue](https://gitea.emulab.it/Simone/nsis-plugin-nsparser/issues/new) usando il template "Bug Report".
|
||||||
|
Includi:
|
||||||
|
- Versione plugin e architettura DLL usata
|
||||||
|
- Versione NSIS
|
||||||
|
- Sistema operativo
|
||||||
|
- Script `.nsi` minimale che riproduce il problema
|
||||||
|
- Output / errore esatto
|
||||||
|
|
||||||
|
## Domande
|
||||||
|
|
||||||
|
Per domande generali, apri una [issue](https://gitea.emulab.it/Simone/nsis-plugin-nsparser/issues) con label `question`.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Simone
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# nsParser NSIS Plugin
|
||||||
|
|
||||||
|
Native C plugin for NSIS to extract values from large files, bypassing the NSIS_MAX_STRLEN limit (1 KB).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem Solved
|
||||||
|
|
||||||
|
NSIS's built-in string functions are limited to `NSIS_MAX_STRLEN` (typically 1024 bytes). When reading configuration files, JSON, or INI files larger than this limit, the string is silently truncated.
|
||||||
|
|
||||||
|
`nsParser` solves this by reading only the specific value requested, without loading the entire file into a string.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- **Visual Studio 2022** (with "Desktop development with C++" workload)
|
||||||
|
- **Python 3.x**
|
||||||
|
|
||||||
|
### Build script
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python build_plugin.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build options
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python build_plugin.py --configs x86-unicode # Single architecture (x86-ansi|x86-unicode|x64-unicode|all)
|
||||||
|
python build_plugin.py --vs-version 2026 # Specific toolset (auto|2022|2026)
|
||||||
|
python build_plugin.py --clean # Clean dist/ before build
|
||||||
|
python build_plugin.py --rebuild # Force full rebuild
|
||||||
|
python build_plugin.py --verbosity detailed # Extended MSBuild output
|
||||||
|
python build_plugin.py --install-dir "C:\NSIS\Plugins" # Copy to additional NSIS directory
|
||||||
|
python build_plugin.py --list # List available configurations
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage in NSIS
|
||||||
|
|
||||||
|
```nsis
|
||||||
|
!addplugindir "plugins\x86-unicode"
|
||||||
|
|
||||||
|
Section
|
||||||
|
nsParser::Extract "C:\config.ini" "key" $0
|
||||||
|
DetailPrint "Value: $0"
|
||||||
|
SectionEnd
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `nsParser::Extract`
|
||||||
|
|
||||||
|
```nsis
|
||||||
|
nsParser::Extract "FilePath" "Key" $OutputVar
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
|-----------|-------------|
|
||||||
|
| `FilePath` | Full path to the file to parse |
|
||||||
|
| `Key` | Key to search for (e.g. `version`, `path`) |
|
||||||
|
| `$OutputVar` | Variable to receive the extracted value |
|
||||||
|
|
||||||
|
**Search logic:**
|
||||||
|
|
||||||
|
The plugin searches for the pattern `Key=Value` or `Key = Value` in the file. If found, returns the value trimmed of leading/trailing whitespace. If not found, returns an empty string.
|
||||||
|
|
||||||
|
**Buffer:**
|
||||||
|
|
||||||
|
The value is extracted with a buffer of 32768 bytes, well beyond the NSIS_MAX_STRLEN limit. This allows reading long paths and embedded JSON strings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advantages
|
||||||
|
|
||||||
|
- No dependency on the NSIS string size limit
|
||||||
|
- Reads only the requested value (efficient on large files)
|
||||||
|
- Handles `key=value` and `key = value` formats
|
||||||
|
- Thread-safe (no global state)
|
||||||
|
- Small binary size
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
The plugin is written in C and uses a direct file reader via `ReadFile` (Win32). The algorithm:
|
||||||
|
|
||||||
|
1. Opens the file with `CreateFile` (read-only, shared)
|
||||||
|
2. Reads the file in 8192-byte blocks
|
||||||
|
3. Scans each block for the pattern `Key=` or `Key =`
|
||||||
|
4. Extracts the value up to the first newline (`\n` or `\r\n`)
|
||||||
|
5. Trims leading/trailing whitespace
|
||||||
|
6. Returns the result via the NSIS stack
|
||||||
|
|
||||||
|
### Visual Studio configurations
|
||||||
|
|
||||||
|
| Configuration | Architecture | Output |
|
||||||
|
|---------------|--------------|--------|
|
||||||
|
| Release-x86-ansi | x86 ANSI | `plugins/x86-ansi/nsParser.dll` |
|
||||||
|
| Release-x86-unicode | x86 Unicode | `plugins/x86-unicode/nsParser.dll` |
|
||||||
|
| Release-amd64-unicode | x64 Unicode | `plugins/amd64-unicode/nsParser.dll` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
nsParser/
|
||||||
|
build_plugin.py # Unified build script
|
||||||
|
src/
|
||||||
|
nsParser.c # Plugin source code
|
||||||
|
nsParser.vcxproj # VS2022 project (x86-ansi, x86-unicode, amd64-unicode)
|
||||||
|
nsParser.sln # VS2022 solution
|
||||||
|
dist/
|
||||||
|
x86-ansi/
|
||||||
|
x86-unicode/
|
||||||
|
amd64-unicode/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*See [README_IT.md](README_IT.md) for the Italian version.*
|
||||||
+288
@@ -0,0 +1,288 @@
|
|||||||
|
# nsParser NSIS Plugin
|
||||||
|
|
||||||
|
Plugin NSIS nativo in C per estrarre velocemente valori da file di grandi dimensioni, bypassando i limiti di NSIS_MAX_STRLEN (1KB).
|
||||||
|
|
||||||
|
## Problema Risolto
|
||||||
|
|
||||||
|
NSIS ha un limite di **1024 byte** per le stringhe (`NSIS_MAX_STRLEN`). File HTML/JSON con linee molto lunghe (>1KB) non possono essere processati con `FileRead` o operazioni su stringhe native NSIS.
|
||||||
|
|
||||||
|
**Esempio**: Il file HTML di Remote Desktop Manager contiene una linea di 39.830 caratteri (135KB totali) che causa il troncamento di `FileRead` a 1KB, impedendo l'estrazione della versione.
|
||||||
|
|
||||||
|
**Soluzioni fallite**:
|
||||||
|
- `FileRead` → Tronca a 1KB
|
||||||
|
- Concatenazione stringhe → Limite 1023 byte totali
|
||||||
|
- `NScurl /MEMORY` → Limite ~8KB
|
||||||
|
- Lettura byte-by-byte → Troppo lenta (10-30 secondi per 135KB)
|
||||||
|
|
||||||
|
**Soluzione nsParser**: Plugin nativo in C che legge file in chunk di 8KB con buffer sliding window di 16KB, parsing in <1 secondo.
|
||||||
|
|
||||||
|
## Compilazione
|
||||||
|
|
||||||
|
### Requisiti
|
||||||
|
- Visual Studio 2022 (MSBuild 17.x, PlatformToolset v143)
|
||||||
|
- Python 3.x (per build automation)
|
||||||
|
|
||||||
|
### Build Automatica
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
cd nsParser
|
||||||
|
python build_plugin.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Compila tutte le configurazioni e copia i DLL finali in `../plugins/`:
|
||||||
|
- `x86-ansi` → 85-87 KB
|
||||||
|
- `x86-unicode` → 85-87 KB
|
||||||
|
- `x64-ansi` → 104 KB
|
||||||
|
- `amd64-unicode` → 104 KB
|
||||||
|
|
||||||
|
File intermedi (`.lib`, `.exp`, `.obj`) rimangono in `nsParser/Plugins/{platform}/` per debugging.
|
||||||
|
|
||||||
|
### Opzioni build
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python build_plugin.py --config x86-unicode # Solo un'architettura (x86-ansi|x86-unicode|amd64-unicode|all)
|
||||||
|
python build_plugin.py --toolset 2026 # Toolset specifico (2022|2026|auto)
|
||||||
|
python build_plugin.py --jobs 4 # Numero di job MSBuild paralleli (default: CPU count)
|
||||||
|
python build_plugin.py --clean # Pulizia dist/ prima della build
|
||||||
|
python build_plugin.py --install-dir "C:\NSIS\Plugins" # Copia in directory NSIS aggiuntiva
|
||||||
|
python build_plugin.py --verbose # Output MSBuild esteso
|
||||||
|
python build_plugin.py --version # Stampa versione ed esce
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Manuale
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
cd nsParser
|
||||||
|
build.cmd
|
||||||
|
```
|
||||||
|
|
||||||
|
O con MSBuild direttamente:
|
||||||
|
```cmd
|
||||||
|
msbuild nsParser.vcxproj /p:Configuration="Release" /p:Platform="Win32"
|
||||||
|
msbuild nsParser.vcxproj /p:Configuration="Release Unicode" /p:Platform="Win32"
|
||||||
|
msbuild nsParser.vcxproj /p:Configuration="Release" /p:Platform="x64"
|
||||||
|
msbuild nsParser.vcxproj /p:Configuration="Release Unicode" /p:Platform="x64"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Utilizzo in NSIS
|
||||||
|
|
||||||
|
```nsis
|
||||||
|
!addplugindir "plugins\x86-unicode"
|
||||||
|
|
||||||
|
Section
|
||||||
|
; Download HTML file
|
||||||
|
NScurl::http GET "https://devolutions.net/..." "$PLUGINSDIR\rdm.html" /END
|
||||||
|
Pop $0
|
||||||
|
|
||||||
|
; Extract version from HTML
|
||||||
|
nsParser::Extract "$PLUGINSDIR\rdm.html" "RDM7zX64.Version"
|
||||||
|
Pop $0 ; Status: "OK" or "ERROR"
|
||||||
|
Pop $1 ; Version string (es: "2025.3.21.0")
|
||||||
|
|
||||||
|
${If} $0 == "OK"
|
||||||
|
DetailPrint "Version found: $1"
|
||||||
|
${Else}
|
||||||
|
DetailPrint "Version not found"
|
||||||
|
${EndIf}
|
||||||
|
SectionEnd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Esempio Reale: RDM_Functions.nsh
|
||||||
|
|
||||||
|
```nsis
|
||||||
|
!macro _GetLatestVersion
|
||||||
|
NScurl::http GET "https://devolutions.net/..." "$R8" /END
|
||||||
|
Pop $0
|
||||||
|
|
||||||
|
${If} $0 == "OK"
|
||||||
|
nsParser::Extract "$R8" "RDM7zX64.Version"
|
||||||
|
Pop $0 ; Status
|
||||||
|
Pop $1 ; Version: "2025.3.21.0"
|
||||||
|
|
||||||
|
${If} $0 == "OK"
|
||||||
|
StrCpy $Latest_Version "$1"
|
||||||
|
StrCpy $DOWNLOAD_LINK "https://cdn.devolutions.net/download/Devolutions.RemoteDesktopManager.win-x64.$Latest_Version.7z"
|
||||||
|
${EndIf}
|
||||||
|
${EndIf}
|
||||||
|
!macroend
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### nsParser::Extract
|
||||||
|
|
||||||
|
```nsis
|
||||||
|
nsParser::Extract <filepath> <pattern>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parametri**:
|
||||||
|
- `<filepath>`: Percorso assoluto del file da analizzare
|
||||||
|
- `<pattern>`: Pattern da cercare (es: "RDM7zX64.Version")
|
||||||
|
|
||||||
|
**Stack Output**:
|
||||||
|
1. Status: `"OK"` o `"ERROR"`
|
||||||
|
2. Value: Stringa estratta (o vuota se errore)
|
||||||
|
|
||||||
|
**Logica di estrazione**:
|
||||||
|
1. Cerca `<pattern>` nel file
|
||||||
|
2. Trova il primo `:` dopo il pattern
|
||||||
|
3. Trova il primo `"` dopo i due punti
|
||||||
|
4. Estrae tutto fino al `"` di chiusura
|
||||||
|
5. Restituisce il valore trovato
|
||||||
|
|
||||||
|
**Esempio**: Nel file con contenuto `{"RDM7zX64.Version":"2025.3.21.0"}`, il pattern `"RDM7zX64.Version"` estrae `"2025.3.21.0"`.
|
||||||
|
|
||||||
|
## Vantaggi
|
||||||
|
|
||||||
|
- **Veloce**: ~1000x più veloce del byte-reading NSIS (<1s vs 10-30s per 135KB)
|
||||||
|
- **No Limiti**: Bypassa NSIS_MAX_STRLEN, gestisce file di qualsiasi dimensione
|
||||||
|
- **Efficiente**: Lettura in chunk di 8KB con sliding window buffer di 16KB
|
||||||
|
- **Pattern Spanning**: Buffer sliding window preserva pattern tra chunk (ultimi 2KB)
|
||||||
|
- **Multi-Arch**: 4 configurazioni (x86/x64, ANSI/Unicode)
|
||||||
|
- **Semplice**: Singola funzione `Extract` parametrica
|
||||||
|
- **Generico**: Non specifico per RDM, usabile per qualsiasi estrazione pattern
|
||||||
|
|
||||||
|
## Implementazione Tecnica
|
||||||
|
|
||||||
|
### Algoritmo
|
||||||
|
|
||||||
|
```c
|
||||||
|
1. CreateFile() → Apre file in lettura
|
||||||
|
2. Loop:
|
||||||
|
a. ReadFile() → Legge 8KB chunk
|
||||||
|
b. Append a search buffer (16KB max)
|
||||||
|
c. strstr() → Cerca pattern
|
||||||
|
d. Se trovato:
|
||||||
|
- Trova ':' dopo pattern
|
||||||
|
- Trova '"' dopo ':'
|
||||||
|
- Estrae fino a '"' chiusura
|
||||||
|
- Return value
|
||||||
|
e. Se buffer pieno:
|
||||||
|
- Copia ultimi 2KB all'inizio (sliding window)
|
||||||
|
- Azzera resto buffer
|
||||||
|
- Continue reading
|
||||||
|
3. CloseHandle()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caratteristiche
|
||||||
|
|
||||||
|
- **Buffer Size**: 16KB totale (8KB chunk + 8KB overlap)
|
||||||
|
- **Sliding Window**: Preserva ultimi 2KB per pattern spanning
|
||||||
|
- **String Search**: Native `strstr()` su dati ANSI
|
||||||
|
- **Unicode Support**: `MultiByteToWideChar()` per output Unicode
|
||||||
|
- **Windows API**: `CreateFile`, `ReadFile`, `CloseHandle`
|
||||||
|
- **No CRT**: Runtime library statica (MultiThreaded)
|
||||||
|
|
||||||
|
### Configurazioni Visual Studio
|
||||||
|
|
||||||
|
| Configuration | Platform | CharacterSet | Output Size | Plugin Dir |
|
||||||
|
|---------------|----------|--------------|-------------|------------|
|
||||||
|
| Release | Win32 | MultiByte | 85-87 KB | x86-ansi |
|
||||||
|
| Release Unicode | Win32 | Unicode | 85-87 KB | x86-unicode |
|
||||||
|
| Release | x64 | MultiByte | 104 KB | x64-ansi |
|
||||||
|
| Release Unicode | x64 | Unicode | 104 KB | amd64-unicode |
|
||||||
|
|
||||||
|
### Struttura File
|
||||||
|
|
||||||
|
```
|
||||||
|
nsParser/
|
||||||
|
├── nsParser.c # Implementazione plugin (140 righe)
|
||||||
|
├── nsParser.h # Header NSISFUNC macro
|
||||||
|
├── nsParser.def # Export definitions
|
||||||
|
├── nsParser.vcxproj # Visual Studio project (4 configs)
|
||||||
|
├── build_plugin.py # Build automation + DLL copy
|
||||||
|
├── build.cmd # Batch wrapper
|
||||||
|
├── .gitignore # Ignora Plugins/*, .user, __pycache__
|
||||||
|
├── README.md # Questa documentazione
|
||||||
|
└── Plugins/ # Build intermedi (gitignored)
|
||||||
|
├── x86-ansi/ # DLL + .lib + .exp + .obj
|
||||||
|
├── x86-unicode/
|
||||||
|
├── x64-ansi/
|
||||||
|
└── amd64-unicode/
|
||||||
|
|
||||||
|
../plugins/ # Output finale (copiato da Python)
|
||||||
|
├── x86-ansi/nsParser.dll
|
||||||
|
├── x86-unicode/nsParser.dll
|
||||||
|
├── x64-ansi/nsParser.dll
|
||||||
|
└── amd64-unicode/nsParser.dll
|
||||||
|
```
|
||||||
|
|
||||||
|
## Note Tecniche
|
||||||
|
|
||||||
|
### NSIS_MAX_STRLEN Limit
|
||||||
|
|
||||||
|
Il limite di 1KB è **assoluto** in NSIS Unicode e non può essere aumentato senza ricompilare NSIS stesso. Questo plugin è l'unica soluzione pratica per processare file con linee >1KB.
|
||||||
|
|
||||||
|
### VersionCompare Bug
|
||||||
|
|
||||||
|
**ATTENZIONE**: `${VersionCompare}` ha un bug noto con versioni a 4 parti. Esempio:
|
||||||
|
|
||||||
|
```nsis
|
||||||
|
${VersionCompare} "2024.3.18.0" "2025.3.21.0" $R0
|
||||||
|
; $R0 = "2" (SBAGLIATO! Dovrebbe essere "1")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Workaround** (usato in `RDM_Installer.nsi`):
|
||||||
|
|
||||||
|
```nsis
|
||||||
|
${VersionCompare} "$Current_Version" "$Latest_Version" $R0
|
||||||
|
|
||||||
|
${If} $R0 == "2"
|
||||||
|
; Estrai anno e confronta numericamente
|
||||||
|
${WordFind} "$Current_Version" "." "+1{" $R1 ; Anno corrente
|
||||||
|
${WordFind} "$Latest_Version" "." "+1{" $R2 ; Anno remoto
|
||||||
|
|
||||||
|
${If} $R1 < $R2
|
||||||
|
; Anno vecchio → forzare selezione
|
||||||
|
!insertmacro SelectSection 0
|
||||||
|
${Else}
|
||||||
|
!insertmacro UnselectSection 0
|
||||||
|
${EndIf}
|
||||||
|
${EndIf}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nota**: In `RDM.nsi` (launcher) il workaround non serve perché si verifica solo uguaglianza (`$R0 == "0"`), non maggiore/minore.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
### Benchmark: File HTML 135KB (linea 39.830 caratteri)
|
||||||
|
|
||||||
|
| Metodo | Tempo | Note |
|
||||||
|
|--------|-------|------|
|
||||||
|
| NSIS FileRead | ❌ Fallisce | Tronca a 1KB |
|
||||||
|
| NSIS Byte-reading | 10-30 secondi | Troppo lento |
|
||||||
|
| NScurl /MEMORY | ❌ Fallisce | Limite ~8KB |
|
||||||
|
| **nsParser plugin** | **<1 secondo** | ✅ Soluzione ottimale |
|
||||||
|
|
||||||
|
### Perché è così veloce?
|
||||||
|
|
||||||
|
1. **Native C**: Compiled code vs NSIS interpreted
|
||||||
|
2. **Bulk Reading**: 8KB chunks vs 1-byte iterations
|
||||||
|
3. **Efficient Search**: `strstr()` CPU-optimized vs NSIS string ops
|
||||||
|
4. **No String Limits**: Direct memory access vs NSIS_MAX_STRLEN
|
||||||
|
|
||||||
|
## Casi d'Uso
|
||||||
|
|
||||||
|
- ✅ Estrazione versioni da HTML/JSON con linee lunghe
|
||||||
|
- ✅ Parsing file di configurazione con valori embedded
|
||||||
|
- ✅ Lettura metadata da file generati (build info, manifests)
|
||||||
|
- ✅ Download + parsing di risorse remote in un unico passaggio
|
||||||
|
- ✅ Qualsiasi pattern `"key":"value"` o `key: "value"`
|
||||||
|
|
||||||
|
## Licenza
|
||||||
|
|
||||||
|
Sviluppato per uso interno nel workspace Launchers.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### v1.0 (2025-11-10)
|
||||||
|
- ✅ Rinominato da RDMVersion a nsParser (generico)
|
||||||
|
- ✅ Supporto multi-architettura (4 configurazioni)
|
||||||
|
- ✅ Build automation con Python
|
||||||
|
- ✅ Struttura directory pulita (Plugins/ + ../plugins/)
|
||||||
|
- ✅ Fixed amd64-unicode configuration bug
|
||||||
|
- ✅ Integrato in RDM_Installer.nsi e RDM_Functions.nsh
|
||||||
|
- ✅ Performance validated: <1s per 135KB file
|
||||||
|
|
||||||
|
### v0.1 (Initial)
|
||||||
|
- Plugin iniziale RDMVersion specifico per Remote Desktop Manager
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Versioni Supportate
|
||||||
|
|
||||||
|
Solo l'ultima versione minor riceve fix di security.
|
||||||
|
|
||||||
|
| Versione | Supporto |
|
||||||
|
|----------|----------|
|
||||||
|
| Latest minor (es. 1.0.0) | ✅ |
|
||||||
|
| Versioni precedenti | ❌ |
|
||||||
|
|
||||||
|
## Reporting di una Vulnerabilità
|
||||||
|
|
||||||
|
**Non aprire issue pubbliche per vulnerabilità di security.**
|
||||||
|
|
||||||
|
Per segnalare una vulnerabilità:
|
||||||
|
|
||||||
|
1. **Preferito**: usa la funzione [Security Advisory privato](https://gitea.emulab.it/Simone/nsis-plugin-nsparser/security/advisories/new) di Gitea
|
||||||
|
2. **Alternativa**: invia email a `security@example.com` (sostituire con email reale prima del primo release)
|
||||||
|
|
||||||
|
Includi:
|
||||||
|
- Descrizione della vulnerabilità
|
||||||
|
- Passi per riprodurla
|
||||||
|
- Versione/i affette
|
||||||
|
- Possibile mitigazione (se nota)
|
||||||
|
|
||||||
|
## Tempo di Risposta
|
||||||
|
|
||||||
|
- Acknowledgment iniziale: entro 7 giorni
|
||||||
|
- Valutazione: entro 14 giorni
|
||||||
|
- Patch (se confermata): tempistiche dipendono dalla severità
|
||||||
|
|
||||||
|
## Hardening
|
||||||
|
|
||||||
|
Questo plugin gira con i permessi del processo NSIS chiamante (tipicamente
|
||||||
|
l'utente che esegue l'installer). Non richiede privilegi elevati per la
|
||||||
|
compilazione né per l'esecuzione, salvo dove esplicitamente documentato.
|
||||||
|
|
||||||
|
DLL distribuite tramite Releases sono compilate da CI GitHub Actions con
|
||||||
|
toolchain pinnata. Verificare hash SHA256 pubblicati nelle release.
|
||||||
+679
@@ -0,0 +1,679 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Build script for nsParser plugin - All configurations
|
||||||
|
Supports multiple build configurations with flexible parameters
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import errno
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
|
# Ensure stdout/stderr handle Unicode on Windows CI (cp1252 default breaks non-ASCII)
|
||||||
|
if sys.stdout.encoding and sys.stdout.encoding.lower() not in ('utf-8', 'utf_8'):
|
||||||
|
import io
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||||
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Filesystem helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _on_rmtree_error(func, path, exc_info):
|
||||||
|
if func in (os.unlink, os.rmdir) and exc_info[1].errno == errno.EACCES:
|
||||||
|
try:
|
||||||
|
os.chmod(path, stat.S_IWRITE)
|
||||||
|
func(path)
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for i in range(5):
|
||||||
|
try:
|
||||||
|
time.sleep(0.1 * (i + 1))
|
||||||
|
if os.path.isdir(path):
|
||||||
|
shutil.rmtree(path)
|
||||||
|
else:
|
||||||
|
os.unlink(path)
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
raise exc_info[1]
|
||||||
|
|
||||||
|
|
||||||
|
def robust_rmtree(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
shutil.rmtree(path, onexc=_on_rmtree_error)
|
||||||
|
except TypeError:
|
||||||
|
try:
|
||||||
|
shutil.rmtree(path, onerror=_on_rmtree_error)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
shutil.rmtree(path, ignore_errors=True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Colors & Spinner
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Colors:
|
||||||
|
CYAN = "\033[36m"
|
||||||
|
GREEN = "\033[32m"
|
||||||
|
YELLOW = "\033[33m"
|
||||||
|
RED = "\033[31m"
|
||||||
|
GRAY = "\033[90m"
|
||||||
|
BLUE = "\033[34m"
|
||||||
|
RESET = "\033[0m"
|
||||||
|
BOLD = "\033[1m"
|
||||||
|
BRIGHT_GREEN = "\033[92m"
|
||||||
|
BRIGHT_CYAN = "\033[96m"
|
||||||
|
BRIGHT_WHITE = "\033[97m"
|
||||||
|
BRIGHT_YELLOW = "\033[93m"
|
||||||
|
BRIGHT_RED = "\033[91m"
|
||||||
|
|
||||||
|
|
||||||
|
class Spinner:
|
||||||
|
def __init__(self, message: str = "Building...", delay: float = 0.1, total: int = 0):
|
||||||
|
self.spinner = ['\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f']
|
||||||
|
self.delay = delay
|
||||||
|
self.message = message
|
||||||
|
self.running = False
|
||||||
|
self.thread = None
|
||||||
|
self.start_time = time.time()
|
||||||
|
|
||||||
|
def update(self, current=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _spin(self):
|
||||||
|
idx = 0
|
||||||
|
_block = '\u28ff'
|
||||||
|
while self.running:
|
||||||
|
elapsed = time.time() - self.start_time
|
||||||
|
n_blocks = int(elapsed // 2)
|
||||||
|
time_blocks = f"{Colors.YELLOW}{_block * n_blocks}{Colors.RESET}"
|
||||||
|
spin_char = f"{Colors.YELLOW}{self.spinner[idx % len(self.spinner)]}{Colors.RESET}"
|
||||||
|
msg = f"{Colors.BOLD}{Colors.CYAN}{self.message}{Colors.RESET}"
|
||||||
|
time_str = f"{Colors.GREEN}{int(elapsed)}s{Colors.RESET}"
|
||||||
|
sys.stdout.write(f"\r{msg} {time_str} {time_blocks}{spin_char} ")
|
||||||
|
sys.stdout.flush()
|
||||||
|
idx += 1
|
||||||
|
time.sleep(self.delay)
|
||||||
|
sys.stdout.write("\r" + " " * (len(self.message) + 80) + "\r")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if sys.stdout.isatty():
|
||||||
|
self.running = True
|
||||||
|
self.thread = threading.Thread(target=self._spin, daemon=True)
|
||||||
|
self.thread.start()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if self.running:
|
||||||
|
self.running = False
|
||||||
|
if self.thread:
|
||||||
|
self.thread.join()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Build configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class BuildConfig:
|
||||||
|
def __init__(self, name: str, config: str, platform: str, dest_dir: str):
|
||||||
|
self.name = name
|
||||||
|
self.config = config
|
||||||
|
self.platform = platform
|
||||||
|
self.dest_dir = dest_dir
|
||||||
|
|
||||||
|
|
||||||
|
CONFIGS = {
|
||||||
|
'x86-ansi': BuildConfig('x86-ansi', 'Release', 'Win32', 'x86-ansi'),
|
||||||
|
'x86-unicode': BuildConfig('x86-unicode', 'Release Unicode', 'Win32', 'x86-unicode'),
|
||||||
|
'x64-unicode': BuildConfig('x64-unicode', 'Release Unicode', 'x64', 'amd64-unicode'),
|
||||||
|
}
|
||||||
|
|
||||||
|
DLL_NAME = 'nsParser.dll'
|
||||||
|
_VSWHERE = Path(r'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe')
|
||||||
|
_VS_RANGES = {'2026': '[18.0,19.0)', '2022': '[17.0,18.0)'}
|
||||||
|
_VS_TOOLSETS = {'2026': 'v145', '2022': 'v143'}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# MSBuild discovery
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _find_msbuild_vswhere(vs_version: str) -> Optional[Tuple[Path, str, str]]:
|
||||||
|
if not _VSWHERE.exists():
|
||||||
|
return None
|
||||||
|
to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version]
|
||||||
|
for ver in to_try:
|
||||||
|
if ver not in _VS_RANGES:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[str(_VSWHERE), '-version', _VS_RANGES[ver], '-latest',
|
||||||
|
'-requires', 'Microsoft.Component.MSBuild',
|
||||||
|
'-find', r'MSBuild\**\Bin\MSBuild.exe'],
|
||||||
|
capture_output=True, text=True, timeout=15,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
lines = [ln.strip() for ln in result.stdout.splitlines() if ln.strip()]
|
||||||
|
if lines:
|
||||||
|
p = Path(lines[0])
|
||||||
|
if p.exists():
|
||||||
|
return p, _VS_TOOLSETS[ver], ver
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_msbuild(vs_version: str = 'auto') -> Optional[Tuple[Path, str, str]]:
|
||||||
|
result = _find_msbuild_vswhere(vs_version)
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
# Fallback: well-known paths
|
||||||
|
to_try = ['2026', '2022'] if vs_version == 'auto' else [vs_version]
|
||||||
|
for ver in to_try:
|
||||||
|
for edition in ('Community', 'Professional', 'Enterprise', 'BuildTools'):
|
||||||
|
p = Path(rf'C:\Program Files\Microsoft Visual Studio\{ver}\{edition}\MSBuild\Current\Bin\MSBuild.exe')
|
||||||
|
if p.exists():
|
||||||
|
return p, _VS_TOOLSETS[ver], ver
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Project paths & version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_project_paths() -> Tuple[Path, Path, Path]:
|
||||||
|
script_dir = Path(__file__).parent.absolute()
|
||||||
|
project_dir = script_dir / 'src'
|
||||||
|
project_file = project_dir / 'nsParser.vcxproj'
|
||||||
|
dist_dir = script_dir / 'dist'
|
||||||
|
return project_dir, project_file, dist_dir
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugins_dir() -> Optional[Path]:
|
||||||
|
"""Return workspace-level plugins/ dir (4 levels above src/modules/<plugin>/)."""
|
||||||
|
p = Path(__file__).parent.parent.parent.parent / 'plugins'
|
||||||
|
return p if p.is_dir() else None
|
||||||
|
|
||||||
|
|
||||||
|
def read_version() -> str:
|
||||||
|
vf = Path(__file__).parent / 'VERSION'
|
||||||
|
try:
|
||||||
|
return vf.read_text(encoding='utf-8-sig').strip()
|
||||||
|
except Exception:
|
||||||
|
return '0.0.0'
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# CPU info & build optimizations
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_cpu_info() -> dict:
|
||||||
|
try:
|
||||||
|
import psutil
|
||||||
|
logical = psutil.cpu_count(logical=True)
|
||||||
|
physical = psutil.cpu_count(logical=False)
|
||||||
|
return {'logical_cores': logical, 'physical_cores': physical,
|
||||||
|
'has_hyperthreading': logical > physical, 'has_psutil': True}
|
||||||
|
except ImportError:
|
||||||
|
n = multiprocessing.cpu_count()
|
||||||
|
return {'logical_cores': n, 'physical_cores': n,
|
||||||
|
'has_hyperthreading': False, 'has_psutil': False}
|
||||||
|
|
||||||
|
|
||||||
|
def get_optimal_threads() -> int:
|
||||||
|
info = get_cpu_info()
|
||||||
|
return info['physical_cores'] if info['has_hyperthreading'] and info['physical_cores'] > 1 else info['logical_cores']
|
||||||
|
|
||||||
|
|
||||||
|
def get_build_optimizations() -> List[str]:
|
||||||
|
return [
|
||||||
|
'/p:BuildInParallel=true',
|
||||||
|
'/p:MultiProcessorCompilation=true',
|
||||||
|
'/p:PreferredToolArchitecture=x64',
|
||||||
|
'/p:UseSharedCompilation=true',
|
||||||
|
'/nodeReuse:true',
|
||||||
|
'/p:GenerateResourceUsePreserializedResources=true',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_memory_optimizations() -> List[str]:
|
||||||
|
try:
|
||||||
|
import psutil
|
||||||
|
gb = psutil.virtual_memory().total / (1024 ** 3)
|
||||||
|
if gb >= 16:
|
||||||
|
return ['/p:DisableFastUpToDateCheck=false', '/p:BuildProjectReferences=true',
|
||||||
|
'/p:UseCommonOutputDirectory=false']
|
||||||
|
elif gb >= 8:
|
||||||
|
return ['/p:DisableFastUpToDateCheck=false']
|
||||||
|
return []
|
||||||
|
except ImportError:
|
||||||
|
return ['/p:DisableFastUpToDateCheck=false']
|
||||||
|
|
||||||
|
|
||||||
|
def print_cpu_info(use_parallel: bool, use_optimizations: bool = True) -> None:
|
||||||
|
if not use_parallel:
|
||||||
|
print(f"{Colors.CYAN}Build mode: {Colors.RESET} {Colors.BOLD}Single-threaded{Colors.RESET}")
|
||||||
|
if use_optimizations:
|
||||||
|
print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.GREEN}ENABLED{Colors.RESET} {Colors.GRAY}(memory, caching){Colors.RESET}")
|
||||||
|
return
|
||||||
|
|
||||||
|
info = get_cpu_info()
|
||||||
|
optimal = get_optimal_threads()
|
||||||
|
print(f"{Colors.CYAN}Build mode: {Colors.RESET} {Colors.BRIGHT_WHITE}{'Parallel' if use_parallel else 'Sequential'}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Logical cores: {Colors.RESET} {Colors.BRIGHT_WHITE}{info['logical_cores']}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Physical cores: {Colors.RESET} {Colors.BRIGHT_WHITE}{info['physical_cores']}{Colors.RESET}")
|
||||||
|
if info['has_hyperthreading']:
|
||||||
|
print(f"{Colors.CYAN}Hyperthreading: {Colors.RESET} {Colors.BRIGHT_GREEN}ENABLED{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Optimal threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET} {Colors.GRAY}(using physical cores){Colors.RESET}")
|
||||||
|
else:
|
||||||
|
print(f"{Colors.CYAN}Hyperthreading: {Colors.RESET} {Colors.GRAY}NOT AVAILABLE{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Optimal threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}MSBuild threads: {Colors.RESET} {Colors.BRIGHT_WHITE}{optimal}{Colors.RESET}")
|
||||||
|
if use_optimizations:
|
||||||
|
print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.BRIGHT_GREEN}ENABLED{Colors.RESET} {Colors.GRAY}(parallel, memory, caching){Colors.RESET}")
|
||||||
|
try:
|
||||||
|
import psutil
|
||||||
|
gb = psutil.virtual_memory().total / (1024 ** 3)
|
||||||
|
print(f"{Colors.CYAN}Available memory: {Colors.RESET} {Colors.BRIGHT_WHITE}{gb:.1f} GB{Colors.RESET}")
|
||||||
|
except ImportError:
|
||||||
|
print(f"{Colors.CYAN}Available memory: {Colors.RESET} {Colors.GRAY}Unknown (install psutil){Colors.RESET}")
|
||||||
|
else:
|
||||||
|
print(f"{Colors.CYAN}Optimizations: {Colors.RESET} {Colors.BRIGHT_RED}DISABLED{Colors.RESET}")
|
||||||
|
if not info['has_psutil']:
|
||||||
|
print(f"{Colors.GRAY}Note: Install 'psutil' for detailed CPU/memory info (pip install psutil){Colors.RESET}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Build helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def format_time(seconds: float) -> str:
|
||||||
|
if seconds < 60:
|
||||||
|
return f"{seconds:.2f}s"
|
||||||
|
elif seconds < 3600:
|
||||||
|
return f"{int(seconds // 60)}m {seconds % 60:.1f}s"
|
||||||
|
return f"{int(seconds // 3600)}h {int((seconds % 3600) // 60)}m {seconds % 60:.0f}s"
|
||||||
|
|
||||||
|
|
||||||
|
def build_configuration(
|
||||||
|
msbuild_path: Path,
|
||||||
|
project_file: Path,
|
||||||
|
config: BuildConfig,
|
||||||
|
platform_toolset: str,
|
||||||
|
*,
|
||||||
|
rebuild: bool = True,
|
||||||
|
verbosity: str = 'quiet',
|
||||||
|
parallel: bool = True,
|
||||||
|
optimizations: bool = True,
|
||||||
|
counter: str = "",
|
||||||
|
capture_output: bool = False,
|
||||||
|
) -> Tuple[bool, float, str]:
|
||||||
|
optimal = get_optimal_threads()
|
||||||
|
cmd = [
|
||||||
|
str(msbuild_path),
|
||||||
|
str(project_file),
|
||||||
|
f'/t:{"Rebuild" if rebuild else "Build"}',
|
||||||
|
f'/p:Configuration={config.config}',
|
||||||
|
f'/p:Platform={config.platform}',
|
||||||
|
f'/p:OutDir=Build\\{config.name}\\',
|
||||||
|
f'/p:IntDir=Build\\{config.name}\\obj\\',
|
||||||
|
'/p:WindowsTargetPlatformVersion=10.0',
|
||||||
|
f'/p:PlatformToolset={platform_toolset}',
|
||||||
|
f'/v:{verbosity}',
|
||||||
|
]
|
||||||
|
if parallel:
|
||||||
|
cmd += [f'/maxcpucount:{optimal}', '/p:UseMultiToolTask=true', f'/p:CL_MPCount={optimal}']
|
||||||
|
cmd += ['/p:Optimization=MaxSpeed', '/p:ExceptionHandling=Sync']
|
||||||
|
if optimizations:
|
||||||
|
cmd += get_build_optimizations() + get_memory_optimizations()
|
||||||
|
|
||||||
|
if not capture_output:
|
||||||
|
color = Colors.BRIGHT_YELLOW
|
||||||
|
print(f"\n{color}{'='*60}{Colors.RESET}")
|
||||||
|
label = f"[{counter}] " if counter else ""
|
||||||
|
print(f"{color}Building {Colors.BRIGHT_WHITE}{config.name:15s}{Colors.RESET} {color}{label}({'rebuild' if rebuild else 'incremental'}){Colors.RESET}")
|
||||||
|
print(f"{color}{'='*60}{Colors.RESET}")
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
try:
|
||||||
|
if capture_output:
|
||||||
|
result = subprocess.run(cmd, check=False, capture_output=True, text=True)
|
||||||
|
return result.returncode == 0, time.time() - start, result.stdout + result.stderr
|
||||||
|
else:
|
||||||
|
result = subprocess.run(cmd, check=False)
|
||||||
|
return result.returncode == 0, time.time() - start, ""
|
||||||
|
except Exception as e:
|
||||||
|
elapsed = time.time() - start
|
||||||
|
msg = f"ERROR: Build failed with exception: {e}"
|
||||||
|
if capture_output:
|
||||||
|
return False, elapsed, msg
|
||||||
|
print(msg)
|
||||||
|
return False, elapsed, ""
|
||||||
|
|
||||||
|
|
||||||
|
def copy_output(project_dir: Path, dist_dir: Path, config: BuildConfig) -> Tuple[bool, int, Optional[Path]]:
|
||||||
|
output_file = project_dir / 'Build' / config.name / DLL_NAME
|
||||||
|
if not output_file.exists():
|
||||||
|
print(f"ERROR: DLL not found: {output_file}")
|
||||||
|
return False, 0, None
|
||||||
|
dest = dist_dir / config.dest_dir
|
||||||
|
dest.mkdir(parents=True, exist_ok=True)
|
||||||
|
try:
|
||||||
|
dst = dest / DLL_NAME
|
||||||
|
shutil.copy2(output_file, dst)
|
||||||
|
return True, output_file.stat().st_size, dst
|
||||||
|
except Exception as e:
|
||||||
|
print(f"ERROR: Failed to copy {config.name}: {e}")
|
||||||
|
return False, 0, None
|
||||||
|
|
||||||
|
|
||||||
|
def copy_to_plugins(dist_dir: Path, config: BuildConfig, plugins_dir: Path) -> Optional[Path]:
|
||||||
|
"""Copy DLL from dist/<dest_dir>/ to plugins/<dest_dir>/."""
|
||||||
|
src = dist_dir / config.dest_dir / DLL_NAME
|
||||||
|
if not src.exists():
|
||||||
|
return None
|
||||||
|
dst_dir = plugins_dir / config.dest_dir
|
||||||
|
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
dst = dst_dir / DLL_NAME
|
||||||
|
shutil.copy2(src, dst)
|
||||||
|
return dst
|
||||||
|
|
||||||
|
|
||||||
|
def clean_build_artifacts(project_dir: Path, configs: List[BuildConfig]) -> None:
|
||||||
|
print(f"\n{Colors.CYAN}Cleaning build artifacts...{Colors.RESET}")
|
||||||
|
build_base = project_dir / 'Build'
|
||||||
|
if not build_base.exists():
|
||||||
|
return
|
||||||
|
for cfg in configs:
|
||||||
|
d = build_base / cfg.name
|
||||||
|
if d.exists():
|
||||||
|
try:
|
||||||
|
robust_rmtree(d)
|
||||||
|
print(f" {Colors.GRAY}- Cleaned:{Colors.RESET} {cfg.name}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" {Colors.RED}- Failed to clean {cfg.name}: {e}{Colors.RESET}")
|
||||||
|
try:
|
||||||
|
if build_base.exists() and not any(build_base.iterdir()):
|
||||||
|
build_base.rmdir()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
print(f"{Colors.BRIGHT_GREEN}Build artifacts cleaned successfully.{Colors.RESET}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_available_configurations(project_file: Path) -> None:
|
||||||
|
try:
|
||||||
|
tree = ET.parse(project_file)
|
||||||
|
root = tree.getroot()
|
||||||
|
ns = {'ms': 'http://schemas.microsoft.com/developer/msbuild/2003'}
|
||||||
|
configs = []
|
||||||
|
for ig in root.findall('.//ms:ItemGroup', ns):
|
||||||
|
for pc in ig.findall('ms:ProjectConfiguration', ns):
|
||||||
|
inc = pc.get('Include', '')
|
||||||
|
parts = inc.split('|')
|
||||||
|
if len(parts) == 2:
|
||||||
|
configs.append(tuple(parts))
|
||||||
|
print("Available configurations in project:")
|
||||||
|
print("-" * 60)
|
||||||
|
from collections import defaultdict
|
||||||
|
grouped: dict = defaultdict(list)
|
||||||
|
for cfg, plat in configs:
|
||||||
|
grouped[cfg].append(plat)
|
||||||
|
for cfg_name in sorted(grouped):
|
||||||
|
print(f" {cfg_name:25s} - {', '.join(sorted(grouped[cfg_name]))}")
|
||||||
|
print(f"\nTotal: {len(configs)} configuration(s)\n")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Could not parse project file: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
_print_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
def _build_configs_parallel(
|
||||||
|
msbuild_path: Path,
|
||||||
|
project_file: Path,
|
||||||
|
configs: List[BuildConfig],
|
||||||
|
platform_toolset: str,
|
||||||
|
*,
|
||||||
|
rebuild: bool,
|
||||||
|
verbosity: str,
|
||||||
|
parallel: bool,
|
||||||
|
optimizations: bool,
|
||||||
|
project_dir: Path,
|
||||||
|
dist_dir: Path,
|
||||||
|
) -> list:
|
||||||
|
n = len(configs)
|
||||||
|
print(f"\nParallel: launching {n} builds simultaneously...")
|
||||||
|
print("=" * 50)
|
||||||
|
total_start = time.time()
|
||||||
|
results_by_idx: dict = {}
|
||||||
|
idx_lock = threading.Lock()
|
||||||
|
|
||||||
|
def _build_one(idx: int, config: BuildConfig):
|
||||||
|
import contextlib
|
||||||
|
import io as _io
|
||||||
|
success, build_time, captured = build_configuration(
|
||||||
|
msbuild_path, project_file, config, platform_toolset,
|
||||||
|
rebuild=rebuild, verbosity=verbosity,
|
||||||
|
parallel=parallel, optimizations=optimizations,
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
copy_buf = _io.StringIO()
|
||||||
|
if success:
|
||||||
|
with contextlib.redirect_stdout(copy_buf):
|
||||||
|
copy_ok, file_size, dest_path = copy_output(project_dir, dist_dir, config)
|
||||||
|
else:
|
||||||
|
copy_ok, file_size, dest_path = False, 0, None
|
||||||
|
|
||||||
|
with _print_lock:
|
||||||
|
sys.stdout.write("\r" + " " * 80 + "\r")
|
||||||
|
sys.stdout.flush()
|
||||||
|
tag = "OK" if (success and copy_ok) else "FAILED"
|
||||||
|
size_str = f"{file_size:,} bytes" if file_size > 0 else "N/A"
|
||||||
|
copy_out = copy_buf.getvalue()
|
||||||
|
if copy_out.strip():
|
||||||
|
print(copy_out.rstrip())
|
||||||
|
if captured.strip():
|
||||||
|
lines = [ln for ln in captured.splitlines() if "MSBuild" not in ln and "Copyright" not in ln]
|
||||||
|
filtered = "\n".join(lines).strip()
|
||||||
|
if filtered:
|
||||||
|
print(filtered)
|
||||||
|
color = Colors.BRIGHT_GREEN if (success and copy_ok) else Colors.RED
|
||||||
|
print(f"{color}[{tag}]{Colors.RESET} {config.name} ({format_time(build_time)}) {size_str}")
|
||||||
|
if dest_path:
|
||||||
|
print(f" -> {dest_path}")
|
||||||
|
|
||||||
|
with idx_lock:
|
||||||
|
results_by_idx[idx] = (config, success and copy_ok, build_time, file_size, dest_path)
|
||||||
|
|
||||||
|
with Spinner("Building configurations...", total=n) as s:
|
||||||
|
with ThreadPoolExecutor(max_workers=n) as executor:
|
||||||
|
futures = {executor.submit(_build_one, i, cfg): i for i, cfg in enumerate(configs)}
|
||||||
|
for fut in as_completed(futures):
|
||||||
|
s.update()
|
||||||
|
exc = fut.exception()
|
||||||
|
if exc:
|
||||||
|
idx = futures[fut]
|
||||||
|
with _print_lock:
|
||||||
|
print(f"ERROR: worker for config index {idx} raised: {exc}")
|
||||||
|
with idx_lock:
|
||||||
|
results_by_idx[idx] = (configs[idx], False, 0.0, 0, None)
|
||||||
|
|
||||||
|
wall = time.time() - total_start
|
||||||
|
print(f"\nAll {n} configs finished in {format_time(wall)} (wall clock)")
|
||||||
|
return [results_by_idx[i] for i in range(n)]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# main
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description='Build nsParser plugin for NSIS')
|
||||||
|
parser.add_argument('--configs', nargs='+',
|
||||||
|
choices=list(CONFIGS.keys()) + ['all'], default=None)
|
||||||
|
parser.add_argument('--config', # singular alias used by CI
|
||||||
|
choices=list(CONFIGS.keys()) + ['all'], default=None)
|
||||||
|
parser.add_argument('--rebuild', action='store_true', default=True)
|
||||||
|
parser.add_argument('--no-rebuild', action='store_false', dest='rebuild')
|
||||||
|
parser.add_argument('--parallel', action='store_true', default=True)
|
||||||
|
parser.add_argument('--no-parallel', action='store_false', dest='parallel')
|
||||||
|
parser.add_argument('--verbosity',
|
||||||
|
choices=['quiet', 'minimal', 'normal', 'detailed', 'diagnostic'],
|
||||||
|
default='quiet')
|
||||||
|
parser.add_argument('--clean', action='store_true', default=False)
|
||||||
|
parser.add_argument('--list', action='store_true')
|
||||||
|
parser.add_argument('--list-project', action='store_true')
|
||||||
|
parser.add_argument('--no-optimizations', action='store_true')
|
||||||
|
parser.add_argument('--vs-version', choices=['auto', '2026', '2022'], default='auto')
|
||||||
|
parser.add_argument('--final', action='store_true', default=False,
|
||||||
|
help='Force rebuild + clean (pre-release build)')
|
||||||
|
parser.add_argument('--install-dir', type=Path, default=None,
|
||||||
|
help='Copy built DLLs to this directory (in <config>/ subdirs)')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Merge --config (singular) into --configs list
|
||||||
|
if args.config and not args.configs:
|
||||||
|
args.configs = [args.config]
|
||||||
|
|
||||||
|
if args.final:
|
||||||
|
args.rebuild = True
|
||||||
|
args.clean = True
|
||||||
|
|
||||||
|
project_dir, project_file, dist_dir = get_project_paths()
|
||||||
|
|
||||||
|
if args.list_project:
|
||||||
|
print_available_configurations(project_file)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.list:
|
||||||
|
for name, config in CONFIGS.items():
|
||||||
|
print(f" {name:15s} -> {config.config} / {config.platform}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if not project_file.exists():
|
||||||
|
print(f"ERROR: project file not found: {project_file}", file=sys.stderr)
|
||||||
|
return 3
|
||||||
|
|
||||||
|
msbuild_result = find_msbuild(args.vs_version)
|
||||||
|
if not msbuild_result:
|
||||||
|
print("ERROR: MSBuild not found. Install Visual Studio 2022 or 2026.", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
msbuild_path, platform_toolset, vs_version_name = msbuild_result
|
||||||
|
|
||||||
|
version = read_version()
|
||||||
|
|
||||||
|
if not args.configs:
|
||||||
|
configs_to_build = list(CONFIGS.values())
|
||||||
|
elif 'all' in args.configs:
|
||||||
|
configs_to_build = list(CONFIGS.values())
|
||||||
|
else:
|
||||||
|
configs_to_build = [CONFIGS[n] for n in args.configs]
|
||||||
|
|
||||||
|
# Banner
|
||||||
|
print(f"{Colors.BOLD}{Colors.BRIGHT_CYAN}{'='*60}")
|
||||||
|
print(f"Building nsParser plugin v{version} - {Colors.BRIGHT_WHITE}{len(configs_to_build)}{Colors.RESET} {Colors.BOLD}{Colors.BRIGHT_CYAN}configuration(s)")
|
||||||
|
print(f"{'='*60}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}VS: {Colors.RESET} {Colors.BRIGHT_WHITE}{vs_version_name}{Colors.RESET} {Colors.GRAY}({platform_toolset}){Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}MSBuild: {Colors.RESET} {Colors.BRIGHT_WHITE}{msbuild_path}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Project: {Colors.RESET} {Colors.BRIGHT_WHITE}{project_file}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Dist: {Colors.RESET} {Colors.BRIGHT_WHITE}{dist_dir}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Rebuild: {Colors.RESET} {Colors.BRIGHT_WHITE}{args.rebuild}{Colors.RESET}")
|
||||||
|
print(f"{Colors.CYAN}Verbosity: {Colors.RESET} {Colors.BRIGHT_WHITE}{args.verbosity}{Colors.RESET}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
use_optimizations = not args.no_optimizations
|
||||||
|
print_cpu_info(args.parallel, use_optimizations)
|
||||||
|
|
||||||
|
build_results = []
|
||||||
|
total_start = time.time()
|
||||||
|
|
||||||
|
if args.parallel and len(configs_to_build) > 1:
|
||||||
|
build_results = _build_configs_parallel(
|
||||||
|
msbuild_path, project_file, configs_to_build, platform_toolset,
|
||||||
|
rebuild=args.rebuild, verbosity=args.verbosity,
|
||||||
|
parallel=True, optimizations=use_optimizations,
|
||||||
|
project_dir=project_dir, dist_dir=dist_dir,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
for i, config in enumerate(configs_to_build, 1):
|
||||||
|
success, b_time, _ = build_configuration(
|
||||||
|
msbuild_path, project_file, config, platform_toolset,
|
||||||
|
rebuild=args.rebuild, verbosity=args.verbosity,
|
||||||
|
parallel=args.parallel, optimizations=use_optimizations,
|
||||||
|
counter=f"{i}/{len(configs_to_build)}",
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
ok, size, path = copy_output(project_dir, dist_dir, config)
|
||||||
|
build_results.append((config, ok, b_time, size, path))
|
||||||
|
else:
|
||||||
|
build_results.append((config, False, b_time, 0, None))
|
||||||
|
|
||||||
|
total_time = time.time() - total_start
|
||||||
|
all_success = all(res[1] for res in build_results)
|
||||||
|
|
||||||
|
print(f"\n{Colors.BOLD}{Colors.BRIGHT_GREEN if all_success else Colors.RED}{'='*50}")
|
||||||
|
print("ALL BUILDS SUCCESSFUL!" if all_success else "SOME BUILDS FAILED!")
|
||||||
|
print(f"{'='*50}{Colors.RESET}")
|
||||||
|
|
||||||
|
# Copy to workspace plugins/ dir if present
|
||||||
|
plugins_dir = get_plugins_dir()
|
||||||
|
plugins_copied = []
|
||||||
|
if plugins_dir and all_success:
|
||||||
|
for cfg, ok, _, _, _ in build_results:
|
||||||
|
if ok:
|
||||||
|
dst = copy_to_plugins(dist_dir, cfg, plugins_dir)
|
||||||
|
if dst:
|
||||||
|
plugins_copied.append(str(dst))
|
||||||
|
if plugins_copied:
|
||||||
|
print(f"\n{Colors.CYAN}Installed to plugins/:{Colors.RESET}")
|
||||||
|
for p in plugins_copied:
|
||||||
|
print(f" {Colors.GRAY}->{Colors.RESET} {p}")
|
||||||
|
|
||||||
|
if args.install_dir and all_success:
|
||||||
|
print(f"\n{Colors.CYAN}Installing to {args.install_dir}...{Colors.RESET}")
|
||||||
|
for cfg, ok, _, _, path in build_results:
|
||||||
|
if ok and path:
|
||||||
|
dest = args.install_dir / cfg.dest_dir
|
||||||
|
dest.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(path, dest / DLL_NAME)
|
||||||
|
print(f" {Colors.GRAY}- Installed:{Colors.RESET} {dest / DLL_NAME}")
|
||||||
|
|
||||||
|
print(f"\n{Colors.BOLD}{Colors.BRIGHT_CYAN}{'-'*50}")
|
||||||
|
print(f"Build Summary - VS {vs_version_name} ({platform_toolset}):")
|
||||||
|
for cfg, ok, b_time, size, _ in build_results:
|
||||||
|
color = Colors.GREEN if ok else Colors.RED
|
||||||
|
tag = "OK " if ok else "FAIL"
|
||||||
|
print(f" {color}{tag}{Colors.RESET} {cfg.name:15s} - {format_time(b_time):8s} - {size:11,d} bytes")
|
||||||
|
print(f"{Colors.BOLD}{Colors.BRIGHT_CYAN}{'-'*50}{Colors.RESET}")
|
||||||
|
print(f"Total time: {format_time(total_time)}\n")
|
||||||
|
|
||||||
|
if args.clean:
|
||||||
|
clean_build_artifacts(project_dir, configs_to_build)
|
||||||
|
|
||||||
|
return 0 if all_success else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* apih
|
||||||
|
*
|
||||||
|
* This file is a part of NSIS.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2013 Nullsoft and Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the zlib/libpng license (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NSIS_EXEHEAD_API_H_
|
||||||
|
#define _NSIS_EXEHEAD_API_H_
|
||||||
|
|
||||||
|
// Starting with NSIS 2.42, you can check the version of the plugin API in exec_flags->plugin_api_version
|
||||||
|
// The format is 0xXXXXYYYY where X is the major version and Y is the minor version (MAKELONG(y,x))
|
||||||
|
// When doing version checks, always remember to use >=, ex: if (pX->exec_flags->plugin_api_version >= NSISPIAPIVER_1_0) {}
|
||||||
|
|
||||||
|
#define NSISPIAPIVER_1_0 0x00010000
|
||||||
|
#define NSISPIAPIVER_CURR NSISPIAPIVER_1_0
|
||||||
|
|
||||||
|
// NSIS Plug-In Callback Messages
|
||||||
|
enum NSPIM
|
||||||
|
{
|
||||||
|
NSPIM_UNLOAD, // This is the last message a plugin gets, do final cleanup
|
||||||
|
NSPIM_GUIUNLOAD, // Called after .onGUIEnd
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prototype for callbacks registered with extra_parameters->RegisterPluginCallback()
|
||||||
|
// Return NULL for unknown messages
|
||||||
|
// Should always be __cdecl for future expansion possibilities
|
||||||
|
typedef UINT_PTR (*NSISPLUGINCALLBACK)(enum NSPIM);
|
||||||
|
|
||||||
|
// extra_parameters data structures containing other interesting stuff
|
||||||
|
// but the stack, variables and HWND passed on to plug-ins.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int autoclose;
|
||||||
|
int all_user_var;
|
||||||
|
int exec_error;
|
||||||
|
int abort;
|
||||||
|
int exec_reboot; // NSIS_SUPPORT_REBOOT
|
||||||
|
int reboot_called; // NSIS_SUPPORT_REBOOT
|
||||||
|
int XXX_cur_insttype; // depreacted
|
||||||
|
int plugin_api_version; // see NSISPIAPIVER_CURR
|
||||||
|
// used to be XXX_insttype_changed
|
||||||
|
int silent; // NSIS_CONFIG_SILENT_SUPPORT
|
||||||
|
int instdir_error;
|
||||||
|
int rtl;
|
||||||
|
int errlvl;
|
||||||
|
int alter_reg_view;
|
||||||
|
int status_update;
|
||||||
|
} exec_flags_t;
|
||||||
|
|
||||||
|
#ifndef NSISCALL
|
||||||
|
# define NSISCALL __stdcall
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
exec_flags_t *exec_flags;
|
||||||
|
int (NSISCALL *ExecuteCodeSegment)(int, HWND);
|
||||||
|
void (NSISCALL *validate_filename)(TCHAR *);
|
||||||
|
int (NSISCALL *RegisterPluginCallback)(HMODULE, NSISPLUGINCALLBACK); // returns 0 on success, 1 if already registered and < 0 on errors
|
||||||
|
} extra_parameters;
|
||||||
|
|
||||||
|
// Definitions for page showing plug-ins
|
||||||
|
// See Ui.c to understand better how they're used
|
||||||
|
|
||||||
|
// sent to the outer window to tell it to go to the next inner window
|
||||||
|
#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8)
|
||||||
|
|
||||||
|
// custom pages should send this message to let NSIS know they're ready
|
||||||
|
#define WM_NOTIFY_CUSTOM_READY (WM_USER+0xd)
|
||||||
|
|
||||||
|
// sent as wParam with WM_NOTIFY_OUTER_NEXT when user cancels - heed its warning
|
||||||
|
#define NOTIFY_BYE_BYE 'x'
|
||||||
|
|
||||||
|
#endif /* _PLUGIN_H_ */
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
nsParser NSIS plugin
|
||||||
|
Extract patterns from text/HTML files
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include "pluginapi.h"
|
||||||
|
#include "nsParser.h"
|
||||||
|
|
||||||
|
HANDLE g_hInstance;
|
||||||
|
|
||||||
|
static UINT_PTR PluginCallback(enum NSPIM msg)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
g_hInstance = hInst;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find pattern in file and extract value between quotes
|
||||||
|
static BOOL ExtractVersion(PTCHAR pszFilename, PCHAR pszPattern, PTCHAR pszVersion, DWORD dwVersionSize)
|
||||||
|
{
|
||||||
|
HANDLE hFile;
|
||||||
|
DWORD dwBytesRead;
|
||||||
|
CHAR szBuffer[8192];
|
||||||
|
CHAR szSearchBuffer[16384] = {0};
|
||||||
|
DWORD dwSearchBufferLen = 0;
|
||||||
|
DWORD dwPatternLen = lstrlenA(pszPattern);
|
||||||
|
BOOL bFound = FALSE;
|
||||||
|
|
||||||
|
// Open file
|
||||||
|
hFile = CreateFile(pszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Read file in chunks
|
||||||
|
while (ReadFile(hFile, szBuffer, sizeof(szBuffer), &dwBytesRead, NULL) && dwBytesRead > 0)
|
||||||
|
{
|
||||||
|
// Append to search buffer
|
||||||
|
if (dwSearchBufferLen + dwBytesRead < sizeof(szSearchBuffer))
|
||||||
|
{
|
||||||
|
CopyMemory(szSearchBuffer + dwSearchBufferLen, szBuffer, dwBytesRead);
|
||||||
|
dwSearchBufferLen += dwBytesRead;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Buffer full, keep last 2KB and continue
|
||||||
|
MoveMemory(szSearchBuffer, szSearchBuffer + dwSearchBufferLen - 2048, 2048);
|
||||||
|
CopyMemory(szSearchBuffer + 2048, szBuffer, dwBytesRead);
|
||||||
|
dwSearchBufferLen = 2048 + dwBytesRead;
|
||||||
|
}
|
||||||
|
szSearchBuffer[dwSearchBufferLen] = '\0';
|
||||||
|
|
||||||
|
// Search for pattern
|
||||||
|
PCHAR pszFound = strstr(szSearchBuffer, pszPattern);
|
||||||
|
if (pszFound)
|
||||||
|
{
|
||||||
|
// Found! Extract value between quotes
|
||||||
|
// Format: "RDM7zX64.Version":"2025.3.21.0"
|
||||||
|
PCHAR pszColon = strchr(pszFound + dwPatternLen, ':');
|
||||||
|
if (pszColon)
|
||||||
|
{
|
||||||
|
PCHAR pszOpenQuote = strchr(pszColon, '"');
|
||||||
|
if (pszOpenQuote)
|
||||||
|
{
|
||||||
|
PCHAR pszCloseQuote = strchr(pszOpenQuote + 1, '"');
|
||||||
|
if (pszCloseQuote)
|
||||||
|
{
|
||||||
|
// Extract version
|
||||||
|
DWORD dwLen = (DWORD)(pszCloseQuote - pszOpenQuote - 1);
|
||||||
|
if (dwLen < dwVersionSize)
|
||||||
|
{
|
||||||
|
// Convert to TCHAR
|
||||||
|
#ifdef UNICODE
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, pszOpenQuote + 1, dwLen, pszVersion, dwVersionSize);
|
||||||
|
pszVersion[dwLen] = 0;
|
||||||
|
#else
|
||||||
|
CopyMemory(pszVersion, pszOpenQuote + 1, dwLen);
|
||||||
|
pszVersion[dwLen] = 0;
|
||||||
|
#endif
|
||||||
|
bFound = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return bFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSIS Plugin function: Extract
|
||||||
|
// Usage: nsParser::Extract "filepath" "pattern"
|
||||||
|
// Returns: "OK" or "ERROR", then extracted value
|
||||||
|
NSISFUNC(Extract)
|
||||||
|
{
|
||||||
|
DLL_INIT();
|
||||||
|
{
|
||||||
|
TCHAR szFilepath[1024];
|
||||||
|
TCHAR szPattern[256];
|
||||||
|
CHAR szPatternA[256];
|
||||||
|
TCHAR szVersion[256] = {0};
|
||||||
|
|
||||||
|
// Pop filepath
|
||||||
|
if (popstring(szFilepath) != 0)
|
||||||
|
{
|
||||||
|
pushstring(TEXT(""));
|
||||||
|
pushstring(TEXT("ERROR"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop pattern
|
||||||
|
if (popstring(szPattern) != 0)
|
||||||
|
{
|
||||||
|
pushstring(TEXT(""));
|
||||||
|
pushstring(TEXT("ERROR"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert pattern to ANSI for file searching
|
||||||
|
#ifdef UNICODE
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, szPattern, -1, szPatternA, sizeof(szPatternA), NULL, NULL);
|
||||||
|
#else
|
||||||
|
lstrcpyA(szPatternA, szPattern);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Extract version
|
||||||
|
if (ExtractVersion(szFilepath, szPatternA, szVersion, sizeof(szVersion) / sizeof(TCHAR)))
|
||||||
|
{
|
||||||
|
pushstring(szVersion);
|
||||||
|
pushstring(TEXT("OK"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushstring(TEXT(""));
|
||||||
|
pushstring(TEXT("ERROR"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
; nsParser.def - Export definitions for NSIS plugin
|
||||||
|
LIBRARY nsParser
|
||||||
|
EXPORTS
|
||||||
|
Extract
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef __NSPARSER_H__
|
||||||
|
#define __NSPARSER_H__
|
||||||
|
|
||||||
|
#define NSISFUNC(name) void __declspec(dllexport) name(HWND hWndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
||||||
|
#define DLL_INIT() \
|
||||||
|
EXDLL_INIT(); \
|
||||||
|
extra->RegisterPluginCallback((HMODULE)g_hInstance, PluginCallback)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0"
|
||||||
|
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release Unicode|Win32">
|
||||||
|
<Configuration>Release Unicode</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release Unicode|x64">
|
||||||
|
<Configuration>Release Unicode</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>nsParser</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'"
|
||||||
|
Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'"
|
||||||
|
Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
|
||||||
|
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
|
||||||
|
Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets"
|
||||||
|
Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
|
||||||
|
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
|
||||||
|
Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
|
||||||
|
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
|
||||||
|
Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets"
|
||||||
|
Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
|
||||||
|
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
|
||||||
|
Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)Plugins\x86-ansi\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)Plugins\x86-ansi\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)Plugins\x86-unicode\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)Plugins\x86-unicode\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)Plugins\x64-ansi\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)Plugins\x64-ansi\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)Plugins\amd64-unicode\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)Plugins\amd64-unicode\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<ModuleDefinitionFile>nsParser.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<ImportLibrary></ImportLibrary>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>
|
||||||
|
WIN32;NDEBUG;_WINDOWS;_USRDLL;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<ModuleDefinitionFile>nsParser.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<ModuleDefinitionFile>nsParser.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>
|
||||||
|
WIN32;NDEBUG;_WINDOWS;_USRDLL;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<ModuleDefinitionFile>nsParser.def</ModuleDefinitionFile>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="pluginapi.c" />
|
||||||
|
<ClCompile Include="nsParser.c" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="api.h" />
|
||||||
|
<ClInclude Include="nsis_tchar.h" />
|
||||||
|
<ClInclude Include="nsParser.h" />
|
||||||
|
<ClInclude Include="pluginapi.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="nsParser.def" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* nsis_tchar.h
|
||||||
|
*
|
||||||
|
* This file is a part of NSIS.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2013 Nullsoft and Contributors
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty.
|
||||||
|
*
|
||||||
|
* For Unicode support by Jim Park -- 08/30/2007
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Jim Park: Only those we use are listed here.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _UNICODE
|
||||||
|
|
||||||
|
#ifndef _T
|
||||||
|
#define __T(x) L ## x
|
||||||
|
#define _T(x) __T(x)
|
||||||
|
#define _TEXT(x) __T(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _TCHAR_DEFINED
|
||||||
|
#define _TCHAR_DEFINED
|
||||||
|
#if !defined(_NATIVE_WCHAR_T_DEFINED) && !defined(_WCHAR_T_DEFINED)
|
||||||
|
typedef unsigned short TCHAR;
|
||||||
|
#else
|
||||||
|
typedef wchar_t TCHAR;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// program
|
||||||
|
#define _tenviron _wenviron
|
||||||
|
#define __targv __wargv
|
||||||
|
|
||||||
|
// printfs
|
||||||
|
#define _ftprintf fwprintf
|
||||||
|
#define _sntprintf _snwprintf
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER<=1310)) || defined(__MINGW32__)
|
||||||
|
# define _stprintf swprintf
|
||||||
|
#else
|
||||||
|
# define _stprintf _swprintf
|
||||||
|
#endif
|
||||||
|
#define _tprintf wprintf
|
||||||
|
#define _vftprintf vfwprintf
|
||||||
|
#define _vsntprintf _vsnwprintf
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER<=1310)
|
||||||
|
# define _vstprintf vswprintf
|
||||||
|
#else
|
||||||
|
# define _vstprintf _vswprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// scanfs
|
||||||
|
#define _tscanf wscanf
|
||||||
|
#define _stscanf swscanf
|
||||||
|
|
||||||
|
// string manipulations
|
||||||
|
#define _tcscat wcscat
|
||||||
|
#define _tcschr wcschr
|
||||||
|
#define _tcsclen wcslen
|
||||||
|
#define _tcscpy wcscpy
|
||||||
|
#define _tcsdup _wcsdup
|
||||||
|
#define _tcslen wcslen
|
||||||
|
#define _tcsnccpy wcsncpy
|
||||||
|
#define _tcsncpy wcsncpy
|
||||||
|
#define _tcsrchr wcsrchr
|
||||||
|
#define _tcsstr wcsstr
|
||||||
|
#define _tcstok wcstok
|
||||||
|
|
||||||
|
// string comparisons
|
||||||
|
#define _tcscmp wcscmp
|
||||||
|
#define _tcsicmp _wcsicmp
|
||||||
|
#define _tcsncicmp _wcsnicmp
|
||||||
|
#define _tcsncmp wcsncmp
|
||||||
|
#define _tcsnicmp _wcsnicmp
|
||||||
|
|
||||||
|
// upper / lower
|
||||||
|
#define _tcslwr _wcslwr
|
||||||
|
#define _tcsupr _wcsupr
|
||||||
|
#define _totlower towlower
|
||||||
|
#define _totupper towupper
|
||||||
|
|
||||||
|
// conversions to numbers
|
||||||
|
#define _tcstoi64 _wcstoi64
|
||||||
|
#define _tcstol wcstol
|
||||||
|
#define _tcstoul wcstoul
|
||||||
|
#define _tstof _wtof
|
||||||
|
#define _tstoi _wtoi
|
||||||
|
#define _tstoi64 _wtoi64
|
||||||
|
#define _ttoi _wtoi
|
||||||
|
#define _ttoi64 _wtoi64
|
||||||
|
#define _ttol _wtol
|
||||||
|
|
||||||
|
// conversion from numbers to strings
|
||||||
|
#define _itot _itow
|
||||||
|
#define _ltot _ltow
|
||||||
|
#define _i64tot _i64tow
|
||||||
|
#define _ui64tot _ui64tow
|
||||||
|
|
||||||
|
// file manipulations
|
||||||
|
#define _tfopen _wfopen
|
||||||
|
#define _topen _wopen
|
||||||
|
#define _tremove _wremove
|
||||||
|
#define _tunlink _wunlink
|
||||||
|
|
||||||
|
// reading and writing to i/o
|
||||||
|
#define _fgettc fgetwc
|
||||||
|
#define _fgetts fgetws
|
||||||
|
#define _fputts fputws
|
||||||
|
#define _gettchar getwchar
|
||||||
|
|
||||||
|
// directory
|
||||||
|
#define _tchdir _wchdir
|
||||||
|
|
||||||
|
// environment
|
||||||
|
#define _tgetenv _wgetenv
|
||||||
|
#define _tsystem _wsystem
|
||||||
|
|
||||||
|
// time
|
||||||
|
#define _tcsftime wcsftime
|
||||||
|
|
||||||
|
#else // ANSI
|
||||||
|
|
||||||
|
#ifndef _T
|
||||||
|
#define _T(x) x
|
||||||
|
#define _TEXT(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _TCHAR_DEFINED
|
||||||
|
#define _TCHAR_DEFINED
|
||||||
|
typedef char TCHAR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// program
|
||||||
|
#define _tenviron environ
|
||||||
|
#define __targv __argv
|
||||||
|
|
||||||
|
// printfs
|
||||||
|
#define _ftprintf fprintf
|
||||||
|
#define _sntprintf _snprintf
|
||||||
|
#define _stprintf sprintf
|
||||||
|
#define _tprintf printf
|
||||||
|
#define _vftprintf vfprintf
|
||||||
|
#define _vsntprintf _vsnprintf
|
||||||
|
#define _vstprintf vsprintf
|
||||||
|
|
||||||
|
// scanfs
|
||||||
|
#define _tscanf scanf
|
||||||
|
#define _stscanf sscanf
|
||||||
|
|
||||||
|
// string manipulations
|
||||||
|
#define _tcscat strcat
|
||||||
|
#define _tcschr strchr
|
||||||
|
#define _tcsclen strlen
|
||||||
|
#define _tcscnlen strnlen
|
||||||
|
#define _tcscpy strcpy
|
||||||
|
#define _tcsdup _strdup
|
||||||
|
#define _tcslen strlen
|
||||||
|
#define _tcsnccpy strncpy
|
||||||
|
#define _tcsrchr strrchr
|
||||||
|
#define _tcsstr strstr
|
||||||
|
#define _tcstok strtok
|
||||||
|
|
||||||
|
// string comparisons
|
||||||
|
#define _tcscmp strcmp
|
||||||
|
#define _tcsicmp _stricmp
|
||||||
|
#define _tcsncmp strncmp
|
||||||
|
#define _tcsncicmp _strnicmp
|
||||||
|
#define _tcsnicmp _strnicmp
|
||||||
|
|
||||||
|
// upper / lower
|
||||||
|
#define _tcslwr _strlwr
|
||||||
|
#define _tcsupr _strupr
|
||||||
|
|
||||||
|
#define _totupper toupper
|
||||||
|
#define _totlower tolower
|
||||||
|
|
||||||
|
// conversions to numbers
|
||||||
|
#define _tcstol strtol
|
||||||
|
#define _tcstoul strtoul
|
||||||
|
#define _tstof atof
|
||||||
|
#define _tstoi atoi
|
||||||
|
#define _tstoi64 _atoi64
|
||||||
|
#define _tstoi64 _atoi64
|
||||||
|
#define _ttoi atoi
|
||||||
|
#define _ttoi64 _atoi64
|
||||||
|
#define _ttol atol
|
||||||
|
|
||||||
|
// conversion from numbers to strings
|
||||||
|
#define _i64tot _i64toa
|
||||||
|
#define _itot _itoa
|
||||||
|
#define _ltot _ltoa
|
||||||
|
#define _ui64tot _ui64toa
|
||||||
|
|
||||||
|
// file manipulations
|
||||||
|
#define _tfopen fopen
|
||||||
|
#define _topen _open
|
||||||
|
#define _tremove remove
|
||||||
|
#define _tunlink _unlink
|
||||||
|
|
||||||
|
// reading and writing to i/o
|
||||||
|
#define _fgettc fgetc
|
||||||
|
#define _fgetts fgets
|
||||||
|
#define _fputts fputs
|
||||||
|
#define _gettchar getchar
|
||||||
|
|
||||||
|
// directory
|
||||||
|
#define _tchdir _chdir
|
||||||
|
|
||||||
|
// environment
|
||||||
|
#define _tgetenv getenv
|
||||||
|
#define _tsystem system
|
||||||
|
|
||||||
|
// time
|
||||||
|
#define _tcsftime strftime
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// is functions (the same in Unicode / ANSI)
|
||||||
|
#define _istgraph isgraph
|
||||||
|
#define _istascii __isascii
|
||||||
|
|
||||||
|
#define __TFILE__ _T(__FILE__)
|
||||||
|
#define __TDATE__ _T(__DATE__)
|
||||||
|
#define __TTIME__ _T(__TIME__)
|
||||||
+294
@@ -0,0 +1,294 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "pluginapi.h"
|
||||||
|
|
||||||
|
#ifdef _countof
|
||||||
|
#define COUNTOF _countof
|
||||||
|
#else
|
||||||
|
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned int g_stringsize;
|
||||||
|
stack_t **g_stacktop;
|
||||||
|
TCHAR *g_variables;
|
||||||
|
|
||||||
|
// utility functions (not required but often useful)
|
||||||
|
|
||||||
|
int NSISCALL popstring(TCHAR *str)
|
||||||
|
{
|
||||||
|
stack_t *th;
|
||||||
|
if (!g_stacktop || !*g_stacktop) return 1;
|
||||||
|
th=(*g_stacktop);
|
||||||
|
if (str) lstrcpy(str,th->text);
|
||||||
|
*g_stacktop = th->next;
|
||||||
|
GlobalFree((HGLOBAL)th);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NSISCALL popstringn(TCHAR *str, int maxlen)
|
||||||
|
{
|
||||||
|
stack_t *th;
|
||||||
|
if (!g_stacktop || !*g_stacktop) return 1;
|
||||||
|
th=(*g_stacktop);
|
||||||
|
if (str) lstrcpyn(str,th->text,maxlen?maxlen:g_stringsize);
|
||||||
|
*g_stacktop = th->next;
|
||||||
|
GlobalFree((HGLOBAL)th);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL pushstring(const TCHAR *str)
|
||||||
|
{
|
||||||
|
stack_t *th;
|
||||||
|
if (!g_stacktop) return;
|
||||||
|
th=(stack_t*)GlobalAlloc(GPTR,(sizeof(stack_t)+(g_stringsize)*sizeof(TCHAR)));
|
||||||
|
lstrcpyn(th->text,str,g_stringsize);
|
||||||
|
th->next=*g_stacktop;
|
||||||
|
*g_stacktop=th;
|
||||||
|
}
|
||||||
|
|
||||||
|
TCHAR* NSISCALL getuservariable(const int varnum)
|
||||||
|
{
|
||||||
|
if (varnum < 0 || varnum >= __INST_LAST) return NULL;
|
||||||
|
return g_variables+varnum*g_stringsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL setuservariable(const int varnum, const TCHAR *var)
|
||||||
|
{
|
||||||
|
if (var != NULL && varnum >= 0 && varnum < __INST_LAST)
|
||||||
|
lstrcpy(g_variables + varnum*g_stringsize, var);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _UNICODE
|
||||||
|
int NSISCALL PopStringA(char* ansiStr)
|
||||||
|
{
|
||||||
|
wchar_t* wideStr = (wchar_t*) GlobalAlloc(GPTR, g_stringsize*sizeof(wchar_t));
|
||||||
|
int rval = popstring(wideStr);
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL);
|
||||||
|
GlobalFree((HGLOBAL)wideStr);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NSISCALL PopStringNA(char* ansiStr, int maxlen)
|
||||||
|
{
|
||||||
|
int realLen = maxlen ? maxlen : g_stringsize;
|
||||||
|
wchar_t* wideStr = (wchar_t*) GlobalAlloc(GPTR, realLen*sizeof(wchar_t));
|
||||||
|
int rval = popstringn(wideStr, realLen);
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, realLen, NULL, NULL);
|
||||||
|
GlobalFree((HGLOBAL)wideStr);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL PushStringA(const char* ansiStr)
|
||||||
|
{
|
||||||
|
wchar_t* wideStr = (wchar_t*) GlobalAlloc(GPTR, g_stringsize*sizeof(wchar_t));
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize);
|
||||||
|
pushstring(wideStr);
|
||||||
|
GlobalFree((HGLOBAL)wideStr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr)
|
||||||
|
{
|
||||||
|
lstrcpyW(wideStr, getuservariable(varnum));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL GetUserVariableA(const int varnum, char* ansiStr)
|
||||||
|
{
|
||||||
|
wchar_t* wideStr = getuservariable(varnum);
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL SetUserVariableA(const int varnum, const char* ansiStr)
|
||||||
|
{
|
||||||
|
if (ansiStr != NULL && varnum >= 0 && varnum < __INST_LAST)
|
||||||
|
{
|
||||||
|
wchar_t* wideStr = g_variables + varnum * g_stringsize;
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
// ANSI defs
|
||||||
|
int NSISCALL PopStringW(wchar_t* wideStr)
|
||||||
|
{
|
||||||
|
char* ansiStr = (char*) GlobalAlloc(GPTR, g_stringsize);
|
||||||
|
int rval = popstring(ansiStr);
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize);
|
||||||
|
GlobalFree((HGLOBAL)ansiStr);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NSISCALL PopStringNW(wchar_t* wideStr, int maxlen)
|
||||||
|
{
|
||||||
|
int realLen = maxlen ? maxlen : g_stringsize;
|
||||||
|
char* ansiStr = (char*) GlobalAlloc(GPTR, realLen);
|
||||||
|
int rval = popstringn(ansiStr, realLen);
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, realLen);
|
||||||
|
GlobalFree((HGLOBAL)ansiStr);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL PushStringW(wchar_t* wideStr)
|
||||||
|
{
|
||||||
|
char* ansiStr = (char*) GlobalAlloc(GPTR, g_stringsize);
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL);
|
||||||
|
pushstring(ansiStr);
|
||||||
|
GlobalFree((HGLOBAL)ansiStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr)
|
||||||
|
{
|
||||||
|
char* ansiStr = getuservariable(varnum);
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, wideStr, g_stringsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL GetUserVariableA(const int varnum, char* ansiStr)
|
||||||
|
{
|
||||||
|
lstrcpyA(ansiStr, getuservariable(varnum));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL SetUserVariableW(const int varnum, const wchar_t* wideStr)
|
||||||
|
{
|
||||||
|
if (wideStr != NULL && varnum >= 0 && varnum < __INST_LAST)
|
||||||
|
{
|
||||||
|
char* ansiStr = g_variables + varnum * g_stringsize;
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, wideStr, -1, ansiStr, g_stringsize, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// playing with integers
|
||||||
|
|
||||||
|
INT_PTR NSISCALL nsishelper_str_to_ptr(const TCHAR *s)
|
||||||
|
{
|
||||||
|
INT_PTR v=0;
|
||||||
|
if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X')))
|
||||||
|
{
|
||||||
|
s++;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int c=*(++s);
|
||||||
|
if (c >= _T('0') && c <= _T('9')) c-=_T('0');
|
||||||
|
else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10;
|
||||||
|
else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10;
|
||||||
|
else break;
|
||||||
|
v<<=4;
|
||||||
|
v+=c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0'))
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int c=*(++s);
|
||||||
|
if (c >= _T('0') && c <= _T('7')) c-=_T('0');
|
||||||
|
else break;
|
||||||
|
v<<=3;
|
||||||
|
v+=c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int sign=0;
|
||||||
|
if (*s == _T('-')) sign++; else s--;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int c=*(++s) - _T('0');
|
||||||
|
if (c < 0 || c > 9) break;
|
||||||
|
v*=10;
|
||||||
|
v+=c;
|
||||||
|
}
|
||||||
|
if (sign) v = -v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NSISCALL myatou(const TCHAR *s)
|
||||||
|
{
|
||||||
|
unsigned int v=0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned int c=*s++;
|
||||||
|
if (c >= _T('0') && c <= _T('9')) c-=_T('0');
|
||||||
|
else break;
|
||||||
|
v*=10;
|
||||||
|
v+=c;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int NSISCALL myatoi_or(const TCHAR *s)
|
||||||
|
{
|
||||||
|
int v=0;
|
||||||
|
if (*s == _T('0') && (s[1] == _T('x') || s[1] == _T('X')))
|
||||||
|
{
|
||||||
|
s++;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int c=*(++s);
|
||||||
|
if (c >= _T('0') && c <= _T('9')) c-=_T('0');
|
||||||
|
else if (c >= _T('a') && c <= _T('f')) c-=_T('a')-10;
|
||||||
|
else if (c >= _T('A') && c <= _T('F')) c-=_T('A')-10;
|
||||||
|
else break;
|
||||||
|
v<<=4;
|
||||||
|
v+=c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*s == _T('0') && s[1] <= _T('7') && s[1] >= _T('0'))
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int c=*(++s);
|
||||||
|
if (c >= _T('0') && c <= _T('7')) c-=_T('0');
|
||||||
|
else break;
|
||||||
|
v<<=3;
|
||||||
|
v+=c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int sign=0;
|
||||||
|
if (*s == _T('-')) sign++; else s--;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int c=*(++s) - _T('0');
|
||||||
|
if (c < 0 || c > 9) break;
|
||||||
|
v*=10;
|
||||||
|
v+=c;
|
||||||
|
}
|
||||||
|
if (sign) v = -v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support for simple ORed expressions
|
||||||
|
if (*s == _T('|'))
|
||||||
|
{
|
||||||
|
v |= myatoi_or(s+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT_PTR NSISCALL popintptr()
|
||||||
|
{
|
||||||
|
TCHAR buf[128];
|
||||||
|
if (popstringn(buf,COUNTOF(buf)))
|
||||||
|
return 0;
|
||||||
|
return nsishelper_str_to_ptr(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int NSISCALL popint_or()
|
||||||
|
{
|
||||||
|
TCHAR buf[128];
|
||||||
|
if (popstringn(buf,COUNTOF(buf)))
|
||||||
|
return 0;
|
||||||
|
return myatoi_or(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NSISCALL pushintptr(INT_PTR value)
|
||||||
|
{
|
||||||
|
TCHAR buffer[30];
|
||||||
|
wsprintf(buffer, sizeof(void*) > 4 ? _T("%Id") : _T("%d"), value);
|
||||||
|
pushstring(buffer);
|
||||||
|
}
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
#ifndef ___NSIS_PLUGIN__H___
|
||||||
|
#define ___NSIS_PLUGIN__H___
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "api.h"
|
||||||
|
#include "nsis_tchar.h"
|
||||||
|
|
||||||
|
#ifndef NSISCALL
|
||||||
|
# define NSISCALL __stdcall
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXDLL_INIT() { \
|
||||||
|
g_stringsize=string_size; \
|
||||||
|
g_stacktop=stacktop; \
|
||||||
|
g_variables=variables; }
|
||||||
|
|
||||||
|
typedef struct _stack_t {
|
||||||
|
struct _stack_t *next;
|
||||||
|
TCHAR text[1]; // this should be the length of string_size
|
||||||
|
} stack_t;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
INST_0, // $0
|
||||||
|
INST_1, // $1
|
||||||
|
INST_2, // $2
|
||||||
|
INST_3, // $3
|
||||||
|
INST_4, // $4
|
||||||
|
INST_5, // $5
|
||||||
|
INST_6, // $6
|
||||||
|
INST_7, // $7
|
||||||
|
INST_8, // $8
|
||||||
|
INST_9, // $9
|
||||||
|
INST_R0, // $R0
|
||||||
|
INST_R1, // $R1
|
||||||
|
INST_R2, // $R2
|
||||||
|
INST_R3, // $R3
|
||||||
|
INST_R4, // $R4
|
||||||
|
INST_R5, // $R5
|
||||||
|
INST_R6, // $R6
|
||||||
|
INST_R7, // $R7
|
||||||
|
INST_R8, // $R8
|
||||||
|
INST_R9, // $R9
|
||||||
|
INST_CMDLINE, // $CMDLINE
|
||||||
|
INST_INSTDIR, // $INSTDIR
|
||||||
|
INST_OUTDIR, // $OUTDIR
|
||||||
|
INST_EXEDIR, // $EXEDIR
|
||||||
|
INST_LANG, // $LANGUAGE
|
||||||
|
__INST_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
extern unsigned int g_stringsize;
|
||||||
|
extern stack_t **g_stacktop;
|
||||||
|
extern TCHAR *g_variables;
|
||||||
|
|
||||||
|
void NSISCALL pushstring(const TCHAR *str);
|
||||||
|
void NSISCALL pushintptr(INT_PTR value);
|
||||||
|
#define pushint(v) pushintptr((INT_PTR)(v))
|
||||||
|
int NSISCALL popstring(TCHAR *str); // 0 on success, 1 on empty stack
|
||||||
|
int NSISCALL popstringn(TCHAR *str, int maxlen); // with length limit, pass 0 for g_stringsize
|
||||||
|
INT_PTR NSISCALL popintptr();
|
||||||
|
#define popint() ( (int) popintptr() )
|
||||||
|
int NSISCALL popint_or(); // with support for or'ing (2|4|8)
|
||||||
|
INT_PTR NSISCALL nsishelper_str_to_ptr(const TCHAR *s);
|
||||||
|
#define myatoi(s) ( (int) nsishelper_str_to_ptr(s) ) // converts a string to an integer
|
||||||
|
unsigned int NSISCALL myatou(const TCHAR *s); // converts a string to an unsigned integer, decimal only
|
||||||
|
int NSISCALL myatoi_or(const TCHAR *s); // with support for or'ing (2|4|8)
|
||||||
|
TCHAR* NSISCALL getuservariable(const int varnum);
|
||||||
|
void NSISCALL setuservariable(const int varnum, const TCHAR *var);
|
||||||
|
|
||||||
|
#ifdef _UNICODE
|
||||||
|
#define PopStringW(x) popstring(x)
|
||||||
|
#define PushStringW(x) pushstring(x)
|
||||||
|
#define SetUserVariableW(x,y) setuservariable(x,y)
|
||||||
|
|
||||||
|
int NSISCALL PopStringA(char* ansiStr);
|
||||||
|
void NSISCALL PushStringA(const char* ansiStr);
|
||||||
|
void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr);
|
||||||
|
void NSISCALL GetUserVariableA(const int varnum, char* ansiStr);
|
||||||
|
void NSISCALL SetUserVariableA(const int varnum, const char* ansiStr);
|
||||||
|
|
||||||
|
#else
|
||||||
|
// ANSI defs
|
||||||
|
|
||||||
|
#define PopStringA(x) popstring(x)
|
||||||
|
#define PushStringA(x) pushstring(x)
|
||||||
|
#define SetUserVariableA(x,y) setuservariable(x,y)
|
||||||
|
|
||||||
|
int NSISCALL PopStringW(wchar_t* wideStr);
|
||||||
|
void NSISCALL PushStringW(wchar_t* wideStr);
|
||||||
|
void NSISCALL GetUserVariableW(const int varnum, wchar_t* wideStr);
|
||||||
|
void NSISCALL GetUserVariableA(const int varnum, char* ansiStr);
|
||||||
|
void NSISCALL SetUserVariableW(const int varnum, const wchar_t* wideStr);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif//!___NSIS_PLUGIN__H___
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
@echo off@echo off
|
||||||
|
|
||||||
|
:: Build script for nsParser plugin - All configurations:: Build script for RDMVersion NSIS plugin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansionsetlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:: Project pathsset PROJECT_FILE=%~dp0RDMVersion.vcxproj
|
||||||
|
|
||||||
|
set PROJECT_DIR=%~dp0set PLUGINS_DIR=%~dp0..\plugins
|
||||||
|
|
||||||
|
set PROJECT_FILE=%PROJECT_DIR%nsParser.vcxproj
|
||||||
|
|
||||||
|
set PLUGINS_DIR=%~dp0..\plugins:: Find MSBuild
|
||||||
|
|
||||||
|
set MSBUILD_PATH=
|
||||||
|
|
||||||
|
:: Find MSBuild
|
||||||
|
|
||||||
|
set MSBUILD_PATH=if exist "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" (
|
||||||
|
|
||||||
|
set MSBUILD_PATH=C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe
|
||||||
|
|
||||||
|
if exist "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" ()
|
||||||
|
|
||||||
|
set MSBUILD_PATH=C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exeif exist "C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe" (
|
||||||
|
|
||||||
|
) set MSBUILD_PATH=C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe
|
||||||
|
|
||||||
|
if exist "C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe" ()
|
||||||
|
|
||||||
|
set MSBUILD_PATH=C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exeif exist "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" (
|
||||||
|
|
||||||
|
) set MSBUILD_PATH=C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe
|
||||||
|
|
||||||
|
if exist "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" ()
|
||||||
|
|
||||||
|
set MSBUILD_PATH=C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exeif exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" (
|
||||||
|
|
||||||
|
) set MSBUILD_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%MSBUILD_PATH%"=="" (if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe" (
|
||||||
|
|
||||||
|
echo ERROR: MSBuild not found! set MSBUILD_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe
|
||||||
|
|
||||||
|
echo Please install Visual Studio 2022 with C++ workload.)
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
exit /b 1if "%MSBUILD_PATH%"=="" (
|
||||||
|
|
||||||
|
) echo ERROR: MSBuild not found!
|
||||||
|
|
||||||
|
echo Please install Visual Studio 2019 or 2022 with C++ workload.
|
||||||
|
|
||||||
|
echo ============================================ pause
|
||||||
|
|
||||||
|
echo Building nsParser plugin - All configurations exit /b 1
|
||||||
|
|
||||||
|
echo ============================================)
|
||||||
|
|
||||||
|
echo MSBuild: %MSBUILD_PATH%
|
||||||
|
|
||||||
|
echo Project: %PROJECT_FILE%echo ============================================
|
||||||
|
|
||||||
|
echo.echo Building RDMVersion plugin
|
||||||
|
|
||||||
|
echo ============================================
|
||||||
|
|
||||||
|
set BUILD_FAILED=0echo MSBuild: %MSBUILD_PATH%
|
||||||
|
|
||||||
|
echo Project: %PROJECT_FILE%
|
||||||
|
|
||||||
|
:: Build x86-ansi (Release Win32)echo.
|
||||||
|
|
||||||
|
echo.
|
||||||
|
|
||||||
|
echo Building x86-ansi...:: Build x86-unicode
|
||||||
|
|
||||||
|
echo ----------------------------------------echo Building x86-unicode...
|
||||||
|
|
||||||
|
"%MSBUILD_PATH%" "%PROJECT_FILE%" /t:Rebuild /p:Configuration="Release" /p:Platform=Win32 /p:WindowsTargetPlatformVersion=10.0 /p:PlatformToolset=v143 /maxcpucount /p:UseMultiToolTask=true /p:CL_MPCount=0 /v:minimalecho ----------------------------------------
|
||||||
|
|
||||||
|
if errorlevel 1 ("%MSBUILD_PATH%" "%PROJECT_FILE%" /t:Rebuild /p:Configuration="Release Unicode" /p:Platform=Win32 /p:WindowsTargetPlatformVersion=10.0 /maxcpucount /v:minimal
|
||||||
|
|
||||||
|
echo ERROR: x86-ansi build failed!
|
||||||
|
|
||||||
|
set BUILD_FAILED=1if errorlevel 1 (
|
||||||
|
|
||||||
|
) else ( echo.
|
||||||
|
|
||||||
|
echo SUCCESS: x86-ansi DLL created echo ERROR: Build failed!
|
||||||
|
|
||||||
|
) pause
|
||||||
|
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:: Build x86-unicode (Release Unicode Win32))
|
||||||
|
|
||||||
|
echo.
|
||||||
|
|
||||||
|
echo Building x86-unicode...:: Copy to plugins directory
|
||||||
|
|
||||||
|
echo ----------------------------------------set OUTPUT_FILE=%~dp0Plugins\x86-unicode\RDMVersion.dll
|
||||||
|
|
||||||
|
"%MSBUILD_PATH%" "%PROJECT_FILE%" /t:Rebuild /p:Configuration="Release Unicode" /p:Platform=Win32 /p:WindowsTargetPlatformVersion=10.0 /p:PlatformToolset=v143 /maxcpucount /p:UseMultiToolTask=true /p:CL_MPCount=0 /v:minimalset DEST_DIR=%PLUGINS_DIR%\x86-unicode
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
|
||||||
|
echo ERROR: x86-unicode build failed!if exist "%OUTPUT_FILE%" (
|
||||||
|
|
||||||
|
set BUILD_FAILED=1 if not exist "%DEST_DIR%" mkdir "%DEST_DIR%"
|
||||||
|
|
||||||
|
) else ( copy /Y "%OUTPUT_FILE%" "%DEST_DIR%\" >nul
|
||||||
|
|
||||||
|
echo SUCCESS: x86-unicode DLL created echo.
|
||||||
|
|
||||||
|
) echo ============================================
|
||||||
|
|
||||||
|
echo Build successful!
|
||||||
|
|
||||||
|
:: Build x64-ansi (Release x64) echo Plugin copied to: %DEST_DIR%\RDMVersion.dll
|
||||||
|
|
||||||
|
echo. echo ============================================
|
||||||
|
|
||||||
|
echo Building x64-ansi...) else (
|
||||||
|
|
||||||
|
echo ---------------------------------------- echo.
|
||||||
|
|
||||||
|
"%MSBUILD_PATH%" "%PROJECT_FILE%" /t:Rebuild /p:Configuration="Release" /p:Platform=x64 /p:WindowsTargetPlatformVersion=10.0 /p:PlatformToolset=v143 /maxcpucount /p:UseMultiToolTask=true /p:CL_MPCount=0 /v:minimal echo ERROR: Output file not found: %OUTPUT_FILE%
|
||||||
|
|
||||||
|
if errorlevel 1 ( pause
|
||||||
|
|
||||||
|
echo ERROR: x64-ansi build failed! exit /b 1
|
||||||
|
|
||||||
|
set BUILD_FAILED=1)
|
||||||
|
|
||||||
|
) else (
|
||||||
|
|
||||||
|
echo SUCCESS: x64-ansi DLL createdpause
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Build amd64-unicode (Release Unicode x64)
|
||||||
|
echo.
|
||||||
|
echo Building amd64-unicode...
|
||||||
|
echo ----------------------------------------
|
||||||
|
"%MSBUILD_PATH%" "%PROJECT_FILE%" /t:Rebuild /p:Configuration="Release Unicode" /p:Platform=x64 /p:WindowsTargetPlatformVersion=10.0 /p:PlatformToolset=v143 /maxcpucount /p:UseMultiToolTask=true /p:CL_MPCount=0 /v:minimal
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: amd64-unicode build failed!
|
||||||
|
set BUILD_FAILED=1
|
||||||
|
) else (
|
||||||
|
echo SUCCESS: amd64-unicode DLL created
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
if %BUILD_FAILED%==1 (
|
||||||
|
echo BUILD FAILED - Check errors above
|
||||||
|
echo ============================================
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
) else (
|
||||||
|
echo BUILD SUCCESSFUL - All configurations built
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
echo Output files:
|
||||||
|
echo - %PLUGINS_DIR%\x86-ansi\nsParser.dll
|
||||||
|
echo - %PLUGINS_DIR%\x86-unicode\nsParser.dll
|
||||||
|
echo - %PLUGINS_DIR%\x64-ansi\nsParser.dll
|
||||||
|
echo - %PLUGINS_DIR%\amd64-unicode\nsParser.dll
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
Reference in New Issue
Block a user