From 0947690677bf9f696a768cf26a898dc0239a2cf8 Mon Sep 17 00:00:00 2001 From: Simone Date: Wed, 29 Apr 2026 23:19:40 +0200 Subject: [PATCH] initial: nsJson plugin 1.1.3 --- .editorconfig | 24 + .gitattributes | 52 + .github/ISSUE_TEMPLATE/bug_report.yml | 76 + .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.yml | 32 + .github/PULL_REQUEST_TEMPLATE.md | 31 + .github/workflows/build.yml | 66 + .github/workflows/release.yml | 99 ++ .gitignore | 63 + CHANGELOG.md | 87 + CONTRIBUTING.md | 137 ++ LICENSE | 21 + LICENSE.txt | 15 + README.md | 435 +++++ README_IT.md | 270 +++ SECURITY.md | 40 + VERSION | 1 + build_plugin.py | 679 ++++++++ dist/amd64-unicode/nsJSON.dll | Bin 0 -> 30208 bytes dist/x86-ansi/nsJSON.dll | Bin 0 -> 23040 bytes dist/x86-unicode/nsJSON.dll | Bin 0 -> 24064 bytes examples/ConsoleExec.nsi | 193 +++ examples/Example.nsi | 509 ++++++ examples/HttpWebRequest.nsi | 127 ++ examples/Input/Example1.json | 22 + examples/Input/Example1_Unicode.json | Bin 0 -> 990 bytes examples/Input/Example1_Unicode_UTF16BE.json | Bin 0 -> 992 bytes examples/Input/Example1_Unicode_UTF16LE.json | Bin 0 -> 992 bytes examples/Input/Example1_Unicode_UTF8.json | 22 + examples/Input/Example2.json | 14 + examples/Input/Example3.json | 28 + examples/Input/Example4.json | 99 ++ examples/Input/Example5.json | 29 + examples/Input/Preferences.json | 992 +++++++++++ examples/Input/Preferences2.json | 988 +++++++++++ examples/MultiTree.nsi | 26 + examples/Quote.nsi | 46 + examples/Sort.nsi | 36 + examples/Syntax.nsi | 35 + examples/UnicodeNSIS.nsi | 25 + src/ConsoleApp/ConsoleApp.vcxproj | 141 ++ src/ConsoleApp/ConsoleApp.vcxproj.filters | 22 + src/ConsoleApp/main.c | 30 + src/ConsoleAppDotNet/ConsoleAppDotNet.csproj | 55 + src/ConsoleAppDotNet/Program.cs | 25 + .../Properties/AssemblyInfo.cs | 36 + src/JSON.c | 1542 +++++++++++++++++ src/JSON.h | 86 + src/LinkedList.c | 90 + src/LinkedList.h | 26 + src/api.h | 83 + src/nsJSON.c | 1474 ++++++++++++++++ src/nsJSON.h | 7 + src/nsJSON.rc | Bin 0 -> 4558 bytes src/nsJSON.sln | 97 ++ src/nsJSON.vcxproj | 449 +++++ src/nsJSON.vcxproj.filters | 56 + src/nsis_tchar.h | 229 +++ src/pluginapi.c | 294 ++++ src/pluginapi.h | 104 ++ src/resource.h | Bin 0 -> 810 bytes 61 files changed, 10173 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 README_IT.md create mode 100644 SECURITY.md create mode 100644 VERSION create mode 100644 build_plugin.py create mode 100644 dist/amd64-unicode/nsJSON.dll create mode 100644 dist/x86-ansi/nsJSON.dll create mode 100644 dist/x86-unicode/nsJSON.dll create mode 100644 examples/ConsoleExec.nsi create mode 100644 examples/Example.nsi create mode 100644 examples/HttpWebRequest.nsi create mode 100644 examples/Input/Example1.json create mode 100644 examples/Input/Example1_Unicode.json create mode 100644 examples/Input/Example1_Unicode_UTF16BE.json create mode 100644 examples/Input/Example1_Unicode_UTF16LE.json create mode 100644 examples/Input/Example1_Unicode_UTF8.json create mode 100644 examples/Input/Example2.json create mode 100644 examples/Input/Example3.json create mode 100644 examples/Input/Example4.json create mode 100644 examples/Input/Example5.json create mode 100644 examples/Input/Preferences.json create mode 100644 examples/Input/Preferences2.json create mode 100644 examples/MultiTree.nsi create mode 100644 examples/Quote.nsi create mode 100644 examples/Sort.nsi create mode 100644 examples/Syntax.nsi create mode 100644 examples/UnicodeNSIS.nsi create mode 100644 src/ConsoleApp/ConsoleApp.vcxproj create mode 100644 src/ConsoleApp/ConsoleApp.vcxproj.filters create mode 100644 src/ConsoleApp/main.c create mode 100644 src/ConsoleAppDotNet/ConsoleAppDotNet.csproj create mode 100644 src/ConsoleAppDotNet/Program.cs create mode 100644 src/ConsoleAppDotNet/Properties/AssemblyInfo.cs create mode 100644 src/JSON.c create mode 100644 src/JSON.h create mode 100644 src/LinkedList.c create mode 100644 src/LinkedList.h create mode 100644 src/api.h create mode 100644 src/nsJSON.c create mode 100644 src/nsJSON.h create mode 100644 src/nsJSON.rc create mode 100644 src/nsJSON.sln create mode 100644 src/nsJSON.vcxproj create mode 100644 src/nsJSON.vcxproj.filters create mode 100644 src/nsis_tchar.h create mode 100644 src/pluginapi.c create mode 100644 src/pluginapi.h create mode 100644 src/resource.h diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..55629c5 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..20b175b --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..7599f2a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -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.) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..94b31f1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Domande generali + url: https://gitea.emulab.it/Simone/nsis-plugin-nsjson/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-nsjson/security/advisories/new + about: Segnala vulnerabilità di security in privato (non aprire issue pubbliche) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..fae6d8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -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 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3758b50 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ +## Descrizione + + + +## 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 + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..4102e1e --- /dev/null +++ b/.github/workflows/build.yml @@ -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 --config ${{ 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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e4cf67d --- /dev/null +++ b/.github/workflows/release.yml @@ -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. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c3a730 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cba4853 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,87 @@ +**1.1.1.1** - 10th October 2024 +- When deleting an element from an array, NSIS no longer crashes (was only working for the last element of an array before) +- Github migration (repository maintained by Pieter Dewachter) + +**1.1.1.0** - 21st November 2017 +- Fixed JSON with syntax errors still being parsed without setting the error flag. +- Fixed Set function not replacing the root value if the value was an array. +- Fixed Delete function not deleting the root node and tree. + +**1.1.0.9** - 9th August 2017 +- Fixed access violation and stack overflow crashes in JSON_Delete, which could occur on plug-in unload, Delete function call or when overwriting existing nodes with the Set function. +- Fixed DoHttpWebRequest failing when POST data exceeded 2048 characters. +- Fixed DoCreateProcess failing to read process output when the output exceeded 1024 characters. +- Delete function will now delete a complete node tree if no path is given. + +**1.1.0.8** - 8th January 2017 +- Added ErrorCode and ErrorMessage for HTTP requests for getting WinINet or Win32 errors. +- Added Sort function. + +**1.1.0.7** - 7th November 2016 +- Fixed /index for adding array elements with the Set function. +- Fixed file name being left on the stack after Serialize with /file. +- Added access type, proxy, authentication and timeout options for HTTP requests. +- Added POST data encoding options for HTTP requests. +- HTTP request headers can be given as raw headers or as JSON key/value pairs. +- Fixed a memory leak in JSON_SerializeAlloc and EscapePostData. +- POST parameters given as a JSON array was not escaped using EscapePostData. + +**1.1.0.6** - 14th October 2016 +- Output and ExitCode/StatusCode are always cleared for /exec and /http calls on the Set function. + +**1.1.0.5** - 10th August 2016 +- Empty value keys ("") were not included in serialized JSON. +- Serialize function popped one extra value from the stack. + +**1.1.0.4** - 7th June 2016 +- Fixed incorrect flags used for HTTP requests. +- Added support for negative indices. +- Added console application execution for input and output. +- Added support for asynchronous HTTP requests and console application execution. + +**1.1.0.3** - 4th March 2016 +- Fixed crash for the Delete function when specifying a tree/path that does not exist. +- Fixed Unicode build. +- Added /keys switch to Get function. + +**1.1.0.2** - 9th December 2015 +- Get function /type switch returns an empty string if the node does not exist. +- Added /always switch to Quote function which surrounds the input string in quotes even if it is already quoted. + +**1.1.0.1** - 23rd November 2015 +- Added Quote function. +- Added amd64-unicode build. + +**1.1.0.0** - 19th April 2015 +- Support for multiple JSON trees. +- JSON via HTTP web requests. + +**1.0.1.3** - 18th October 2014 +- Added UTF-16LE BOM, UTF-16BE BOM and UTF-8 signature detection for input files (with UTF-16BE conversion to UTF-16LE). +- Fixed formatting errors for the Serialize function. +- Fixed closing bracket or curly brace not being included on Serialize to stack when not using /format. +- Moved plug-in DLLs to x86-ansi and x86-unicode respectively for NSIS 3.0. + +**1.0.1.2** - 12th July 2014 +- Fixed crash on serialization to file for node values larger than 64KB. +- Fixed crash on serialization to stack for JSON larger than NSIS_MAX_STRLEN. The JSON will now be truncated. + +**1.0.1.1** - 29th March 2014 +- Fixed incorrect handling of escape character (\). + +**1.0.1.0** - 28th August 2012 +- Added /unicode switch to the Serialize function. Output files for both plug-in builds are now encoded in ANSI by default. +- Removed the Parse function in favour of Set /file [/unicode]. +- Added /type, /key, /exists, /count, /isempty to the Get function. +- Added /index switch for referencing nodes by index. + +**1.0.0.2** - 15th August 2012 +- Fixed Unicode build parsing and serializing. + +**1.0.0.1** - 1st July 2012 +- Fixed parsing of single digit numbers. +- Fixed Serialize not writing the output file when the stack isn't + empty. + +**1.0.0.0** - 25th June 2012 +- First version. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ac26cdd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,137 @@ +# Contribuire a nsJson + +Grazie per il tuo interesse a contribuire! + +> Repository primario: [https://gitea.emulab.it/Simone/nsis-plugin-nsjson](https://gitea.emulab.it/Simone/nsis-plugin-nsjson) +> Mirror GitHub: [https://github.com/systempal/nsis-plugin-nsjson](https://github.com/systempal/nsis-plugin-nsjson) (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-nsjson.git +cd nsis-plugin-nsjson +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 `/{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-nsjson/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-nsjson/issues) con label `question`. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bc3fdc5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +zlib License + +Copyright (c) 2026 Simone + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0226c9a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,15 @@ +This plug-in is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this plug-in. + +Permission is granted to anyone to use this plug-in for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this plug-in must not be misrepresented; you must not + claim that you wrote the original plug-in. + If you use this plug-in in a product, an acknowledgment in the + product documentation would be appreciated but is not required. +2. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original plug-in. +3. This notice may not be removed or altered from any distribution. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3831a0 --- /dev/null +++ b/README.md @@ -0,0 +1,435 @@ +# nsJSON NSIS plug-in + +Authors: Stuart 'Afrow UK' Welch, Pieter Dewachter + +Date: 10th October 2024 + +Version: 1.1.2 + +A JSON (JavaScript Object Notation) parser, manipulator and generator plug-in for NSIS. + +See `examples/*.nsi`. + +## About JSON + +See http://www.json.org/ for information about JSON along with the syntax and string escape sequences. + +## Important information + +All plug-in functions set the NSIS error flag on error. For functions that return a value on the stack, no item will be returned if the error flag has been set. You can use `IfErrors` or `${If} ${Errors}` to check that a call has succeeded before Pop-ping a value, if any. + +The plug-in supports all escape sequences in string values: +`\r`, `\n`, `\t`, `\b`, `\f`, `\"`, `\\` and `\uXXXX` + +`[NodePath]`, used throughout this readme, is any space-delimited list of mixed node keys and indices (prefixed with /index). For example: + +``` +{ + "node1": { + "node2": false, + "node3": [ "Stuart", 1, { "node4": "Welch", "node5": "" }, 32.5, [ 0, "test", false ] ] + } +} +``` + +This is a JSON snippet. "node2" is a child node of "node1". "node2" has a Boolean value of false. "node3" is an array (denoted by square brackets). The 3rd element within the array is another node. The 5th element within the array is another array. +These are examples of node paths within the JSON snippet: + +"node1" "node2" +- The path for "node2" (value: false) +- `nsJSON::Get "node1" "node2" /end` + +"node1" "node3" /index 0 +- The path for the first array element in "node3" (value: "Stuart") +- `nsJSON::Get "node1" "node3" /index 0 /end` + +"node1" "node3" /index 2 "node4" +- The path for "node4" (value: "Welch") +- `nsJSON::Get "node1" "node3" /index 3 "node4" /end` + +/index 0 "node3" /index 2 /index 0 +- Also the path for "node4" (value: "Welch") +- `nsJSON::Get /index 0 /index 1 /index 3 /index 0 /end` + +"node1" "node3" /index 4 /index -2 +- The path for value "test" in the array; note the negative index +- `nsJSON::Get "node1" "node3" /index 4 /index -2 /end` + +Node names containing double quotes are automatically escaped. If no node path is given, the root node is used. + +Multiple JSON trees can be manipulated at the same time. Add `/tree Tree` (where Tree is the tree name) before all other plug-in arguments to specify which JSON tree you are manipulating. + +## Reading JSON files + +`nsJSON::Set [/tree Tree] [NodePath] /file [/unicode] "path\to\input.json"` + +Loads the JSON from the given file into the given node. Specify `/unicode` if the input file is Unicode. + +--- + +`nsJSON::Set [/tree Tree] /file [/unicode] "path\to\input.json"` + +Loads the JSON from the given file into memory, overwriting any existing JSON tree. Specify `/unicode` if the input file is Unicode. + +## JSON from HTTP web requests + +`nsJSON::Set [/tree Tree] [NodePath] /http ConfigTree` + +Executes an HTTP web request and loads the JSON response into the given node. The ConfigTree JSON tree specifies the web request configuration to execute, described using JSON. Its possible values are as follows... + +"**Url**": "http://..." +- The web request URL. This is required. + +"**Verb**": "GET" +- The request verb, such as GET or POST. Default is GET. + +"**Params**": "..." +- Optional parameters to append to the URL (after ?). + +"**ParamsType**": "..." +- The parameters type. Possible values are... + + Empty string/omitted (default) + - Parameters are given as JSON and will be sent as a standard key/value pair string. Special characters will be URL-encoded automatically. Arrays will be serialized as multiple keys of the same name with a [] suffix. + + For example `{"a": "Value1", "b": true, "c": "Value3"}` becomes `a=Value1&b&c=Value3`, and `{"Array": [1, 2]}` becomes `Array[]=1&Array[]=2`. + + "Raw" + - Raw parameters are given as a string. + +"**Data**": "..."` +- Optional request data to send (typically used with POST). + +"**DataType**": "..." +- The request data type. Possible values are... + + Empty string/omitted (default) + - Data is given as JSON and will be sent as a standard POST key/value pair string. Special characters will be URL-encoded automatically. Arrays will be serialized as multiple keys of the + same name with a [] suffix. + + For example `{"a": "Value1", "b": true, "c": "Value3"}` becomes `a=Value1&b&c=Value3`, and `{"Array": [1, 2]}` becomes + `Array[]=1&Array[]=2`. + + "JSON" + - POST data is JSON and will be sent as-is (serialized JSON). + + "Raw" + - Raw POST data is given as a string. + +"**DataEncoding**": "..." +- Specifies the encoding to use for the POST data. Possible values + are... + + Empty string/omitted (default) + - Data is encoded as 1 byte per character. + + "Unicode" + - Data is encoded as 2 bytes per character. + + "Headers": "..." + - Additional headers to send, which will override these defaults... + * "Content-Type: application/x-www-form-urlencoded" is sent if "Verb" is "POST" and "DataType" is "Raw". + * "Content-Type: application/json" is sent if "Verb" is "POST" and "DataType" is "JSON". + * "Accept-Encoding: gzip,deflate" is sent if "Decoding" is set to true. + + Specify headers as JSON, for example: `{"Content-Type": "text/plain", "a": "b"}` + + + "Agent": "nsJSON NSIS plug-in/1.0.x.x" + - The request agent string. Default is shown. + + "Decoding": false + - Enables automatic GZIP decompression by sending the "Accept-Encoding: gzip,deflate" request header. If the server responds with a valid "Content-Encoding" header, the compressed data will be decompressed automatically. + + "AccessType": "..." + - The connection access type. Possible values are... + + Empty string/omitted (default) + - Direct access (bypasses any proxy). + + "PreConfig" + - Use the internet configuration defined in the Windows registry. + + "Proxy" + - Use defined proxy configuration (see below). + +"**Proxy**": { "Server": "...", "Bypass": "...", Username": "...", "Password": "..." } +- Specifies the proxy configuration. The "Bypass" value is optional. + +"**Username**": "..." +- Username for HTTP authentication. + +"**Password**": "..." +- Password for HTTP authentication. + +"**ConnectTimeout**": "..." +- The initial connection timeout. A value of `0XFFFFFFFF` will disable the timeout. + +"**SendTimeout**": N +- The request send timeout. + +"**ReceiveTimeout**": N +- The response receive timeout. + +"**UnicodeOutput**": true +- The response must be read as Unicode rather than ASCII. + +"**RawOutput**": true +- The response must be read as raw text rather than JSON. + +"**Async**": true +- Perform the request asynchronously. You must use the Wait function to wait or check if the request has finished. + +"**UIAsync**": true +- For performing the request in a page callback function. This will ensure window messages are processed so that the UI does not become unresponsive. After the request has finished, the node specified by NodePath will contain the following values: + + "Output": ... + - The HTTP response as JSON. + + "StatusCode": N + - The HTTP status code. + + "ErrorCode": N + - The WinINet/Win32 error code on error. + + "ErrorMessage": "..." + - The WinINet/Win32 error message on error. + +## JSON from console application execution + +`nsJSON::Set [/tree Tree] [NodePath] /exec ConfigTree` + +Executes a console application and loads the output into the given node. The ConfigTree JSON tree specifies the console application to execute, described using JSON. Its possible values are as follows... + + "Path": "$INSTDIR\ConsoleApp.exe" + - The path to the executable to be executed. This is required. + + "Arguments": ... + - The command line arguments. The value can be a string or an array of strings. + + "WorkingDir": "..." + - The working/current directory. If not specified, the installer's working directory will be used, which is $OUTDIR. + + "Input": ... + - The input to write to STDIN. The value can be a string or an array of strings. + + "UnicodeInput": true + - The standard input must be written as Unicode rather than ASCII. + + "UnicodeOutput": true + - The standard output must be read as Unicode rather than ASCII. + + "RawOutput": true + - The standard output must be read as raw text rather than JSON. + + "Async": true + - Execute asynchronously. You must use the Wait function to wait or check if the process has finished. + + "UIAsync": true + - For performing the execution in a page callback function. This will + ensure window messages are processed so that the UI does not become unresponsive. + + After execution has finished, the node specified by NodePath will + contain the following values: + + "Output": ... + - The output of the console application, depending on the RawOutput setting. + + "ExitCode": X + - The process exit code. + +## Modifying JSON + +`nsJSON::Set [/tree Tree] [NodePath] /value "Value"` + +Sets the value of the given node. The value can be any single value or it can be JSON code. The node will be created if it does not exist. + +--- + +`nsJSON::Delete [/tree Tree] [NodePath] /end` + +Deletes the given tree or node. `/end` must be added to the end of the list to prevent stack corruption. + +--- + +``` +nsJSON::Quote [/unicode] [/always] Value +Pop $Var +``` + +Surrounds the given value with quotes if necessary and escapes any characters that require it. The quoted value will be returned on the stack. + +Optionally specify `/unicode` to escape non ASCII characters as well although it is perfectly legal to include Unicode characters in JSON. + +Specify `/always` to always surround the input string in quotes even if it is already quoted. + +--- + +`nsJSON::Sort [/tree Tree] [NodePath] [/options Options] /end` + +Sorts the given node. Add up the following values for the optional sort options: + +1. Sort in descending order. +2. Use numeric sort. +4. Sort case sensitively. +8. Sort by keys rather than by values. +16. Sort all nodes in the tree recursively. + +## Reading JSON values + +``` +nsJSON::Get [/tree Tree] [/noexpand] [NodePath] /end +Pop $Var +``` + +Gets the value of the given node. The value returned will be JSON code if the node is not a value-only node. Specify `/noexpand` when reading quoted string values to stop escape sequences being expanded. `/end` must be added to the end of the list to prevent stack corruption. + +--- + +``` +nsJSON::Get [/tree Tree] /type [NodePath] /end +Pop $Var +``` + +Gets the value-type of the given node. It can be one of "node", "array", "string" (quoted strings), "value" (which is all non-string values such as integers, floats and Booleans) or an empty string if the node does not exist. + +--- + +``` +nsJSON::Get [/tree Tree] /key [NodePath] /end +Pop $Var +``` + +Gets the name (key) of the given node. + +--- + +``` +nsJSON::Get [/tree Tree] /keys [NodePath] /end +Pop $VarKeyCount +Pop $VarKey1 +Pop $VarKey2 +Pop $VarKeyN +``` + +Gets the keys of the given node. `$VarKeyCount` will be the number of keys pushed onto the stack (to be removed via Pop). + +--- + +``` +nsJSON::Get [/tree Tree] /exists [NodePath] /end +Pop $Var +``` + +Determines whether or not the given node exists. `$Var` will be "yes" or "no". + +--- + +``` +nsJSON::Get [/tree Tree] /count [NodePath] /end +Pop $Var +``` + +Gets the number of child nodes for the given node or the number of elements if the node is an array. + +--- + +``` +nsJSON::Get [/tree Tree] /isempty [NodePath] /end +Pop $Var +``` + +Determines whether or not the given node is empty. `$Var` will be "yes" or "no". An empty node is for example: `"node": { }` or `"array": [ ]`. + +## Generating JSON + +``` +nsJSON::Serialize [/tree Tree] [/format] +Pop $Var +``` + +Serializes the current JSON tree into $Var. Add `/format` to apply formatting to the output. The error flag is set if an error occurs. + +--- + +`nsJSON::Serialize [/tree Tree] [/format] /file [/unicode] "path\to\output.json"` + +Serializes the current JSON tree into the given file. Add `/format` to apply formatting to the output. Add `/unicode` to generate a Unicode output file (applies to both ANSI and Unicode NSIS). + +## Waiting for asynchronous tasks to finish + +``` +nsJSON::Wait Tree [/timeout TimeoutInMilliseconds] +Pop $Var +``` + +Checks whether or not the asynchonous task that is being ran under the given JSON tree has finished. + +If `/timeout` is specified, the function will return after the specified number of milliseconds; after which `$Var` will be "wait" if the task has not finished; or an empty string otherwise. + +When `/timeout` is omitted, the function will wait indefinitely and nothing will be pushed onto the stack when the task finishes. + +--- + +## ⚠️ Differences in the Personal Version + +This version is based on the official v1.1.1.1 release from [GitHub](https://github.com/Pieter-Dewachter/nsJSON/releases/tag/v1.1.1.1). + +### New supported architecture: x64 (amd64-unicode) + +The original v1.1.1.1 supports only: +- x86-ansi +- x86-unicode + +The personal version adds support for: +- **amd64-unicode** (x64) + +### Visual Studio Project + +The original included: +- `Contrib/nsJSON/nsJSON.sln` - VS2012 Solution +- `Contrib/nsJSON/nsJSON.vcxproj` - VS2012 Project (x86 only) +- `Contrib/nsJSON/ConsoleApp/ConsoleApp.vcxproj` - Test application + +The personal version uses the same project updated for VS2022 with x64 support. + +### Added Files (build infrastructure) + +- `build_plugin.py` - Python script to compile the plugin for all architectures +- `.gitignore` - Git exclusion file + +### Removed Files + +Pre-compiled DLL files have been removed from the distribution (they are generated by the build): + +- `Plugins/x86-ansi/nsJSON.dll` +- `Plugins/x86-unicode/nsJSON.dll` + +### Functional Changes + +No functional changes compared to the original. Modifications are limited to the build infrastructure and x64 support. + +### Build + +```cmd +cd nsJson +python build_plugin.py +``` + +DLLs are copied to `plugins/{platform}/nsJSON.dll`. + +### 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 +``` + +--- + +*See [README_IT.md](README_IT.md) for the Italian version.* diff --git a/README_IT.md b/README_IT.md new file mode 100644 index 0000000..d893217 --- /dev/null +++ b/README_IT.md @@ -0,0 +1,270 @@ +# nsJSON NSIS plug-in + +Autori: Stuart 'Afrow UK' Welch, Pieter Dewachter + +Data: 10 Ottobre 2024 + +Versione: 1.1.1.1 + +Plugin NSIS per il parsing, la manipolazione e la generazione di JSON (JavaScript Object Notation). + +Vedi Examples\nsJSON\*. + +## Informazioni su JSON + +Vedi http://www.json.org/ per informazioni su JSON, sintassi e sequenze di escape. + +## Informazioni importanti + +Tutte le funzioni del plug-in impostano il flag di errore NSIS in caso di errore. Per le funzioni che restituiscono un valore sullo stack, nessun elemento viene restituito se il flag di errore è stato impostato. Usare `IfErrors` o `${If} ${Errors}` per verificare che una chiamata sia riuscita prima di fare Pop di un valore. + +Il plug-in supporta tutte le sequenze di escape nei valori stringa: +`\r`, `\n`, `\t`, `\b`, `\f`, `\"`, `\\` e `\uXXXX` + +`[NodePath]`, usato in questo readme, è qualsiasi lista di chiavi e indici separati da spazi (gli indici sono preceduti da /index). Per esempio: + +``` +{ + "node1": { + "node2": false, + "node3": [ "Stuart", 1, { "node4": "Welch", "node5": "" }, 32.5, [ 0, "test", false ] ] + } +} +``` + +Esempi di percorsi nodo in questo JSON: + +"node1" "node2" +- Percorso per "node2" (valore: false) +- `nsJSON::Get "node1" "node2" /end` + +"node1" "node3" /index 0 +- Percorso per il primo elemento di "node3" (valore: "Stuart") +- `nsJSON::Get "node1" "node3" /index 0 /end` + +Più alberi JSON possono essere manipolati contemporaneamente. Aggiungere `/tree NomeAlbero` prima degli altri argomenti per specificare quale albero JSON si sta manipolando. + +## Lettura di file JSON + +`nsJSON::Set [/tree Albero] [NodePath] /file [/unicode] "percorso\input.json"` + +Carica il JSON dal file dato nel nodo specificato. Specificare `/unicode` se il file è Unicode. + +--- + +`nsJSON::Set [/tree Albero] /file [/unicode] "percorso\input.json"` + +Carica il JSON dal file dato in memoria, sovrascrivendo qualsiasi albero JSON esistente. + +## JSON da richieste HTTP + +`nsJSON::Set [/tree Albero] [NodePath] /http AlberoConfig` + +Esegue una richiesta HTTP e carica la risposta JSON nel nodo dato. L'AlberoConfig specifica la configurazione della richiesta in formato JSON. I valori possibili sono: + +**"Url"**: "http://..." +- L'URL della richiesta. Obbligatorio. + +**"Verb"**: "GET" +- Il verbo della richiesta (GET, POST, ecc.). Default: GET. + +**"Params"**: "..." +- Parametri opzionali da aggiungere all'URL (dopo ?). + +**"Data"**: "..." +- Dati opzionali da inviare (tipicamente con POST). + +**"Headers"**: "..." +- Header aggiuntivi da inviare. + +**"Agent"**: "nsJSON NSIS plug-in/1.0.x.x" +- La stringa dell'agente della richiesta. + +**"Decoding"**: false +- Abilita la decompressione GZIP automatica. + +**"Async"**: true +- Esegue la richiesta in modo asincrono. Usare la funzione Wait per attendere il completamento. + +## JSON dall'esecuzione di applicazioni console + +`nsJSON::Set [/tree Albero] [NodePath] /exec AlberoConfig` + +Esegue un'applicazione console e carica l'output nel nodo dato. I valori possibili della configurazione: + +**"Path"**: "$INSTDIR\ConsoleApp.exe" +- Percorso all'eseguibile. Obbligatorio. + +**"Arguments"**: ... +- Argomenti della riga di comando. + +**"WorkingDir"**: "..." +- Directory di lavoro. + +**"Input"**: ... +- Input da scrivere su STDIN. + +**"Async"**: true +- Esegue in modo asincrono. + +## Modifica del JSON + +`nsJSON::Set [/tree Albero] [NodePath] /value "Valore"` + +Imposta il valore del nodo dato. Il valore può essere un singolo valore o codice JSON. Il nodo viene creato se non esiste. + +--- + +`nsJSON::Delete [/tree Albero] [NodePath] /end` + +Elimina l'albero o il nodo dato. `/end` deve essere aggiunto alla fine della lista. + +--- + +``` +nsJSON::Quote [/unicode] [/always] Valore +Pop $Var +``` + +Racchiude il valore dato tra virgolette se necessario e fa l'escape dei caratteri che lo richiedono. + +--- + +`nsJSON::Sort [/tree Albero] [NodePath] [/options Opzioni] /end` + +Ordina il nodo dato. Sommare i seguenti valori per le opzioni di ordinamento: + +1. Ordine decrescente. +2. Ordinamento numerico. +4. Ordinamento case-sensitive. +8. Ordina per chiavi anziché per valori. +16. Ordina ricorsivamente tutti i nodi nell'albero. + +## Lettura di valori JSON + +``` +nsJSON::Get [/tree Albero] [/noexpand] [NodePath] /end +Pop $Var +``` + +Ottiene il valore del nodo dato. `/end` deve essere aggiunto alla fine della lista. + +--- + +``` +nsJSON::Get [/tree Albero] /type [NodePath] /end +Pop $Var +``` + +Ottiene il tipo di valore del nodo dato: "node", "array", "string", "value" o stringa vuota se il nodo non esiste. + +--- + +``` +nsJSON::Get [/tree Albero] /keys [NodePath] /end +Pop $VarKeyCount +Pop $VarKey1 +Pop $VarKeyN +``` + +Ottiene le chiavi del nodo dato. + +--- + +``` +nsJSON::Get [/tree Albero] /count [NodePath] /end +Pop $Var +``` + +Ottiene il numero di nodi figli o elementi di un array. + +## Generazione JSON + +``` +nsJSON::Serialize [/tree Albero] [/format] +Pop $Var +``` + +Serializza l'albero JSON corrente in `$Var`. Aggiungere `/format` per formattare l'output. + +--- + +`nsJSON::Serialize [/tree Albero] [/format] /file [/unicode] "percorso\output.json"` + +Serializza l'albero JSON corrente nel file dato. + +## Attesa di task asincroni + +``` +nsJSON::Wait Albero [/timeout TimeoutInMillisecondi] +Pop $Var +``` + +Verifica se il task asincrono in esecuzione nell'albero JSON dato è terminato. + +--- + +## ⚠️ Differenze nella versione personale + +Questa versione è basata sul rilascio ufficiale v1.1.1.1 da [GitHub](https://github.com/Pieter-Dewachter/nsJSON/releases/tag/v1.1.1.1). + +### Nuova architettura supportata: x64 (amd64-unicode) + +L'originale v1.1.1.1 supporta solo: +- x86-ansi +- x86-unicode + +La versione personale aggiunge il supporto per: +- **amd64-unicode** (x64) + +### Progetto Visual Studio + +L'originale includeva: +- `Contrib/nsJSON/nsJSON.sln` - Solution VS2012 +- `Contrib/nsJSON/nsJSON.vcxproj` - Progetto VS2012 (solo x86) +- `Contrib/nsJSON/ConsoleApp/ConsoleApp.vcxproj` - App di test + +La versione personale usa lo stesso progetto aggiornato per VS2022 con supporto x64. + +### File aggiunti (infrastruttura di build) + +- `build_plugin.cmd` - Script per compilare il plugin +- `build_plugin.py` - Script Python per compilare il plugin per tutte le architetture +- `BUILD_README.md` - Documentazione di compilazione +- `.gitignore` - File di esclusione Git + +### File rimossi + +I file DLL precompilati sono stati rimossi dalla distribuzione (vengono generati dalla compilazione): + +- `Plugins/x86-ansi/nsJSON.dll` +- `Plugins/x86-unicode/nsJSON.dll` + +### Modifiche funzionali + +Nessuna modifica funzionale rispetto all'originale. Le modifiche riguardano solo l'infrastruttura di build e il supporto x64. + +### Compilazione + +```cmd +cd nsJson +python build_plugin.py +``` + +I DLL vengono copiati in `plugins/{platform}/nsJSON.dll`. + +### 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 +``` + +--- + +*See [README.md](README.md) for the English version.* diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..f5df862 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policy + +## Versioni Supportate + +Solo l'ultima versione minor riceve fix di security. + +| Versione | Supporto | +|----------|----------| +| Latest minor (es. 1.1.1) | ✅ | +| 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-nsjson/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. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..764d6eb --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.1.3 \ No newline at end of file diff --git a/build_plugin.py b/build_plugin.py new file mode 100644 index 0000000..943d8fd --- /dev/null +++ b/build_plugin.py @@ -0,0 +1,679 @@ +#!/usr/bin/env python3 +""" +Build script for nsJSON 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 = 'nsJSON.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 / 'nsJSON.vcxproj' + dist_dir = script_dir / 'dist' + return project_dir, project_file, dist_dir + + +def get_plugins_dir() -> Optional[Path]: + """Return workspace-level plugins/ dir (3 levels above src/modules//).""" + 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// to plugins//.""" + 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 nsJSON 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 / 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 nsJSON 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()) diff --git a/dist/amd64-unicode/nsJSON.dll b/dist/amd64-unicode/nsJSON.dll new file mode 100644 index 0000000000000000000000000000000000000000..e411443b70d90acb11023a63d78af725473a18c2 GIT binary patch literal 30208 zcmeI5dwf*YwfN8E4J1r>j06FR7!->_B&fuoX2Oh|!3jj(DvyMaKs1DyoB>os+DUGm z9>;6F+S+@2TibeD>+ezf6d!E^C1XYaMwUTf{O*Is+=eTLfW?$)w3P0QhH+nTnGR6f<}@8AA&fa;;+A0Mjy zV$j~RxA~%b&tAB;rL(lXWBr(;Msno?9W zxXeX8?)w#m*X~@t()*UyEL*WcJuh3ik#z5zYga5+>GBmzRr>vvPP$S(&u?j5D{VbW z>%yA0`XBPN84r(Y@yd>A6H13<4ZT3Cqhx|pT3$(7tnyoZl1M&F)ACf&K&tgnAj093 zrPTxKH1d)!K076kv}xLdv{|@bQ{8G0Xxa=B^xc|vFa4xHk7!yU51&Ep*mU84B2y14 zMfmAqrx;C}45~)Y<&ThD$Jgb9ocfe%TK$xc)eT02rfnWcQ9a)?_%7s|@u@}~rZ`{< zMB7TG@Nfg)j8CVMe5w^(Q|o#F zMMl~qe5GGuEaOwHX*C&-f6xA3NTB)dXyf3k{Ev=|1mmBSZY+sBp`EK~qFA;5A3Wb# z5KIPriLBHs&Ecv8Ms`KdPN!g-K*=O~{va$j_(OOo#cu8=v9)G)3H$TzM_Z}|= zAZ#8>&MCD6Q&B!W5h`1%n}_x89)DK@&4b%+1w=Ow>E=EwX4`gmk8x$VqNg{TV$J#i zC3<333GhZBxb3z9*ocCSt*$tzC!=Lct=e2otLW)3RyePhw&yw6M)hV3nN}fx1yyj{ zavoFLqDdj8mp?peRz#}JR*JgcG3x2AG=9QojEJAeij56-ANP0v9H{;~_|uy$+Ly}f z%@!J|GY0928%z4{;VHQ7GXA>k{P1(dPB5tCWF6vBH~U~EHdHtF*nuhYz^Fr|sU@;? zGl&Fd6_Az)nRdWWHHeE-XFM()Cg)sX2U1LAYKx`A;I<0}`nt^NYZ#)l+N2{v1sC7Y z&7HcrtgKpi)AjgMrNI>|S7eNtQC7S}2FxMS88+(zMXHRFan?!QJpUKP(jeFh%)pI4=n12&M0F4`k44Q_qULj`TJx;<%sHhS$IbFTnlnd#%u_bq|AXym zWjkE)P9!l&ZyX#%FXuGJKba6)t~YKEp^D)|w)ru|%WATkVn*&B6H0 znq{#P^SOxmjP)vFL)e3ZW?ysnk=Tw@elS_>3nsEdW^XWgjZa`+!Q$zOyo#RG?R0UO zvDkc0Z`@%%6Na)H(4Dxzm?g<=#!N|;8y8DbYD|%2ka3D<_3VRo;M>&b<}t?E-?f|Sh0EC-peIKzl996m3#Ci*fXtUA zdiU`y^O=TGvo9FmV+WHzXi4w#6FZdWvemNx;?Rbj;1}U6llM2 znl|nOlUX-MJElVKKH~4XPoTOF8t3bk*A>LD3D}-QO+oxJ&B%>k=8MsxPp_;gk*aE` zD$&iWi*@rxOw?{8&kmH)p*e*7n0UX>I79yA#y|GO#sP;V%%wUtOix~2qQ@&WBfsLH zwLa2et{~#$($i zz+7_eYgHd^IiopPnHPiZm&k-Rd>+dUnSV*;g)5-srQq?Wvy5W?=EQQt&_LZEQrS$< zaQB(`Vq zD*mn?c$tLCWEq_@Sj&a#kWhUk67U&QF|Zm>Bl|DyL0P27au>xl>OT&b9*v zq}qF&uCfoOI`zh#&7nlj>{N@BE1ZqF$Z2a%gqCEb7I8qToP?OB5US#$aa~(`&$CgxHGxH$9RQmvXoLy z@OQmPd2sTx{;p@q#QQJwckNV}Jb%|OR3^vYCEJqVr^Wjfc>6Iq{vj^v1{()<8t)x`ITsY?63iFz;{GW@@PF7)G+n=2pD@_^?U!F z)oQvfLu%$Ov0&{Y$F;h-xoi`1tX7738G_Frq@~V670yeQ@GzKJPg1C*22*xrDu=)M z<}0Zmole%`c2R(nhDtv&`bnB?tbEy?QUpBaPR>Z9^t?xpy!avvxnHhQ|)vMzQ0p8Z{wvXf3p}_RL5(l&5leGWRAnBML_h?}o&pZ^=(wfnSsw zqO8;`FkyThB@!o7X%OtMRqTsHrX$S)c$k5S5X7Z|Sj1Uv-6XmAQ!{poxf~R)%#P*g z<~1169jYC^xAL$=gFY6J>7r|rnaub-Lf3ZS8AX(Frf$wFq3{Q)@Ykx)f8S2%%yP*4 z>ponjjTA=O!xis@&DX=`2~qCuBSv;8F}W5`xoZkC)y<0{=3jI?tb0$0rM8q=@>3J4 zoB|J5`25``FiyI;r9@0pjc)F>x~~+;-8?2}zQ`&n>(pFto!S9Au@AF%1kJt3b}?4o zxX#`l5gFfJP}R5jPpKLZ`@4oY2G8F;T99|!{;qq-#IMX^LAf$pRwDQ}U$g^{%~I=b zp^HM6jh6+!dzFi!>sm5uQ4A$ll?Kgc?Z7(PDF0}Z@XH1O2<-{^Q+bxZ`*BiO@pliU zB)MR`YH}G(qRHD!B8mJ^Vp4Oca-C*e0)W?P>oc=CnhQZiTK*gV%%9P7sZhyb*hcXI2=pQeW zGO?v3VqTeAkMv@LWF!V9&PvTA6Px2udy%5{VX!J{JyIJe&=a$vfvplWva_)RzX-BW z4_Dh2#r;ed$R#~bk)9)cDx?=mwb|?G9`-doHSkz6ytNQB3yZq2ta#A^wZk;umf2o$ zBy7HySgyNje<6D(MNe6CWr;C9xu7(8b#=1S=g=jD4uJr=K9ML%Wts2G)@_w;*3Jkz z4mf^JIg?M^%dL#1jAym=s2Z3V);UECo@^*(-5Cld zvsSkQ?^L5<%XR;~OjO5StXy&Za&PX73&WDLxvXSSdX6gj)H6eS!$-9OCY||4D6vjw zramrlprCm;n7pl8I{6m3^+foXnxDyRG1--`6G5;c-U?B)1Olj4NvZ8eRW^dRMBJ98gPG^pfG8|YnL-%CK48^&a z>=S)hS&Ye~+1qwty1*m5sJX`#U-(V9WznfUhRlwN?5uC*s|g;%BUQF-6k@XjuPa;P z@46J^dNPdovDMLJQ7*NDBAN`psZg+|5_DD_Xaqy3a%!wRXg*CPjTWjgdmH~iHDW7x$xTomm%XZ)&=<*b5-*%}zArx;{tg(N#?L_!(rtDoN z{721NfYa)78h(EShJJ}b>gGnQJO+2JddQI5XIFfIk~c-IffEV`WOqHdL^{fdc_P{c zAEKh3h-5KhtW-5Q=b2)l34LuS~Mtw5IwDxV8{NTW$ z5S@VMqpJIfgVYLQ3{r-~4+b^;|1?wh2p^M8Z9QAsa<;Q9iL(-&r{NNlWsBg2Dzl!nQL65HB~9ILG+nd@o?WQ-s# zh*cipwU{YWq<2;cylMS2cnFGfzt?(6>4;;H&ZSuGLT?sfga#y1AdOv#uv^_wpD zyo`bbF5c{t)cyP|CXZ!&S5Mqp(tj{Rj%VYqp-Tr1zi!sBJ$*)3(Sq1}dgA7S{)d2+ z7+~sGQN$cEFA)i^5i5+C=VA`qXsg8hDd9ym`@SgVxrq69REwDB4v6_;W{e7p^iUV6 zx+In#aW~55hq6chjv6(!+a9jRZ!gx2Ax`WSS0Odr;VqINSinf#1_~9j6911?w^g=8 z2HtGkS*_i;3Vy7_B~a6s(~fNLF=WVSi97rK4b_B#h`an^Beg6!ZK9ZBQ z?QiU-N>L(~G<>>BU+kJ_@+O3KMa4U{=I1OQ$m)nOGs;q#>_pUu@IwyR44RBkgyMt{9M>WsU%H+6DK0tqxKomjD3dk{DxEGZZ*I(G-^IAI|{d zD1u+T6xk7%9AgKbAR8ov#hTe8Wa0H^}kXq;|l7PiRy>T~jyPSxf zFY!XZ#0y_c{XFx0a2Tc_V7}GsrV_^T{%07Y>`hD0=_^^M96! zJY*otYt4Pm`q2x`vQkDcW&bEa`*_M1`<;q@It#^(EEJuY3x(2)sQJ4{;`)Lxwp8gZ zJ4t-#Fno+c)REp<7CkZ2SEF@H5lD(ibB~x>=0cyfMVXYtGKD9}6t0Uht(S^L6Hh@} zDr+&GhRQd%N@Zk9rS2=m?wrO7Ucx zqID86XQbQ{4yNdU{b(;;Btdgo4xMcSLAj9mNhr}Y!~7&{en8+n7=H`*^XpI^P1N^= z2{QY7t9!Qc%zof;lC8bf+r$O30~cK^Tsb>|>(wG-2PRTx_9bd(AdxG~-m7UCT;0L0(QO5xPLOllOc zPSQTb@-vDw5uSne`MVB56H9W()~e5ARa))knKj`eh1XxzPPo*&au4Uk&?j%xvZiuNRQ!eCAsR-t#7<2O=Wl`ppY!mK+f{>VCaZt?{m4`^Qe)6a6 zp|N+Y$vD0DRd@|#!!iKOEPyZTb6EHA)trn^CJN39!@b`+1el^L%*o?9UW3smH}D8} z7~r5;SSG&QPe6bL_ILfz$rv{%|LsLqS`SMv6lPK;jxJrU28*zP1%!=#)4T?!W#dv| zBU!W=REo_0rF1aR`tHBo`s0sX?)opMS+H_!nV!t8P75wsG(+s_*un55zp`vtRg6Wr8dW=ev|S|9GYLjBQU zxA%Qx4xaNIU9;~)P%m2lghL_}%=aj`%SG+#MJ5G)kAh$_Y&+d2i<*IF^*%+7-Uiea zMMT7xEV@b(q~P=;WTQ?QWdDpPS_HBSqUg40b*8cwolKOcD_dv-@&2wcaN#kq_o9ooZ#P289d=YdE<^k{@0Z#AEoVw-I-V@Y<{u1E(Ultxb$T|fHh0pgtMR~aL3MR;SzMV~pp$`qPz&ctMcLvKG@kvax&NcX;W0g-bGA#} zZFmiEKixt26Iu))eDNdO7F{;?gHL==aNg(QtOTb+>sO^D8R(M4AyjaI`WrMSiiFBa zbjeukbWsnS%S;Ra(NDQ<;8aefa-jc3r|CZ+-GKZt>iXYT+ii6+lMy*Am&l>x|Nb>U zqvCk2xEQaPA1B*<$F~<4HNndASaJ37-eP009MSh#o8On!>1rvBi z7E57HjL6)r7b=&0iTPn_L9lYthU+T7WHeV!GghfqEZ%cUte-e7(Tl^@56D*?PMr}k z>v+DpMDQ3x?Z9M(r`rGBT`4@*iR#He??ZD}#naVYfANQQrG^74v>g&3=qTG-FJAxG z{GZ1Q(5B>^8Jy?-@&fNX_eYeSZe!)%*@0jV5RMovx;ZAAy!bKbs5s69jrzlXmhx+G ztktR5jm380cCa$6MY{REd*1qbs$6!i1bEw+gMPhobgak@%mz;HCAubE!`-FwEbGn` zm0|Pn*L^sw%7#4j6c)M<>i+O2sgZQZcGC_tgBiip>E`v2 zb0qc_^Gi?OSZ(f2E?|>>o}z>qa@Y*{!++( z$$?(%LjMdORfv6qRET}_uMs;=h)p>AFNc31)d~}Q%pS+O2V22-A9nj*lxzRF7LVwOhu9NkY`$6uqsW#5KS+q$9 zapNmUv!(==#?|v2O%z?DlH3}S2;nRHK$w|M7$h6f9SSBlRLe$W^CTfkwk-axH${r( zAt`QB#g5ASmSTk9gxhlHLC*SEE84}As83x>zdw}4OeTCS?GVrc%D(TFRjRUBZnE7c zVRRbOn^7i*pkS$I-zSJlB}#-4>#Zi)GzfkncaL>dnL_!kbj$Pgs;Ak+aG@|+w0t%f z`*LvEb#o!%2HLk<6MxU5?uK_6&WF!)Y5N`-4uoQZIeY7^mWUrTXRa)AQtPcOdZOrT zaI09D+H1K2>-v}9en{c^v!~i{|14$$q2c!}R-$~_+V!?_vS;(? z!8uC(Q*5v>`fmU@C6B6-eb%1Wm6{V5{R25?k>%KrfTkI^^(xlUC#&h0=$GCbevKT~ zF5K&;vTu~<{gT>&d(QfOHOKpV^aGX=dW(%Wlp1BuhEa)@(Gly*qt3X zPEpi7K^{VJatSd4j$;KsM_2E9F7bb`(Q`!fpjuh>Sq~i*p)veoko;Xgg|+yMEXR3{ z&&c+7{fjCg==}{cy1BFjVA)rHRm!X6(6}mFZfnXdq#x0p-J5*^vo0aV*lW1HtIR{5 zeXCPcs*P?`PPJ22=~HJ8CYO{%l7%bnz&&NskA%dR0-t>tWtfWaoyr)M+?jBW9b!GH zoTw?smCM%y)z8Wh?9CxA<}hi7SwZ$hWR7q)uwODu$-E|+x*$3rIYNJxt2_fWcTy8F(a_pnJMbuElH1QUh{ST9f5dah%x#~A_9SaJQgw)m z{bDam5$ao@wgVk5Z*p(I3hT>}cHkx&Ktr-FOGQo@ zM+)2kN4Vl(<)V!nMtm*jOq-hu`-1kaJ8~+k7H#Z!2SVN=TA3yUb0z|&9|Zi)1SR0^ zTW#A;onr?+RFV~YDw5rfO_K%hJ(a4e`A!us z=YwwL3#1>3zRjbGl~y_!vpfEJ&PPYa^nfLPG;2s7XVl1MzjdhvC0sJOZ4JZA^0jEA z9r);MWoqjlKLetZbG~E;mMc1Cc`z;`qO}p^HA=r(&%aI`$F{;`7VwVVyh+!B{_tBu zFe}1jpri*o@G;nkfZc{8;%e}$b&bZA~*q*hPrhDp8IW_d>I53l3nCWSlxhi)I zpB1^))^S~zLm6?J9`_*_JMg)5bsS~{jlb)SL8#T@waK~Fb|9N7XHxmQenYv_#fwT0 z_FK=rf>aXWKF2jNYMt6%xAx~!%ZXAl@G)4dzVxGvn;qyIFHFa9&jK$~-y4>27M<;} zz5`@R?If-gi(hdtl&tYt_rGe}00AS5%>idEiIv%bc_0GGTGj17CaDz(;E(1(uAYb; zNW_++roH)VX=n#-0Jvhex{aGyS(;c`r&reYaUBb74>VT}^k#+ zvMU%>sbg{@5%K$(B{zeTdmtjx}42hAIi`~zLRw*gv{93q6gjwyJ(K(#&jlz!*qv+<~om3cWTo}2vMbKu9Kd4?lS9lsE;0Y`l4nu5jT5-jEp4Y1$)IOnOV@Kk8Yn~_ z!a|)>PsumHZZ*kcGLT2D7#`W!uNtGQOkOBi?@NuP@?%8?N~u#L0k}7VAH5KaznSIG zGtKS!_=_wEN4y1LiccKY>=>bix)PXFn^7?oCP*rmKV_U5e2ft<1^*xrq^n!0W_BN? z{36Qj!1HG+a?Vo_$-o5y?6ky7iGSuZ@&RpDpyjj;Rxa24-H(8})?8OoD>1qs@W4uq z6b-YmE-es(&$~Dl}M$APRIA-ZT5r&{AdC5`D(C2xk z5>td^b~%-k74ba(if7!4Iz{ONvI7*>?^bxJewS*?)*uvZ?TlhZG zgSjmKSGpjNZu?c!KI@k+$nZLLZC{p<;@*GzM+fd{v8Y1sx9Nhl;#Ni8k9feNVZ5@{ zc6T3ohM_#6sI)M!-M^$c@huAI>~4_YPRqJ*k)L(pe!*?(C1LYvt^)=A_kzT#QkFnb z(!Bu|up1?+^>8HAzZ?cs*B5|N>B%CQn~2%^@TeZ9D&=(YU9#%9K;jK@xmPCDqB;ks9nioitH`JDVJ@muLvHWD z|D~C?1N(?N88O^k2syyjC!q(w4H#+rZR!;tfqUGUl=r-vY1SpAPR;#J4W8>C;gMmm z&ZJIxuJ=*JdFE-oRtbxK85cLT0J_!fG0;g=g68ph`s5@v9u-Pel{ z_S4X22X1jHs|G}6P^m<9J5?#9xx@jf9ss!p5PE}M;k-_*pmZEgZW^IB!h30f8a3O2 zG34SWZi!uw1e5wMIUZ)P0)kQLqfwJa5H)2Nt>{mx_Qtp`_|qT2gD`N1awwS-NAS&6 zlYdqym!A-Ve<+2-^BiDN$Oj4q4ERpKQs>!$xC8P#KvINk4d?Nl0TH#S7P4>MM4j9; zpT|<&DsYv{`wR?J?E&$vhj809hBrQ6YQ)Eks7&o)JCHAZfnb!BBm>(9$Qh=Pf4i)Y zA;1~mN;2S#%RSdG_~KvvKvV+;{)o$!;ftRp-tWDG!C~NPf=g!68qUw7jh{fmMY*Uz z;_|stkSMxRCAsA!a?(afj5iP&f$W3H+G;z{9uSUX&GmQ5o1d z3FZovLyg0Av|{KB@cjDkb?T;ibyqlbxyhwjsat7uu2(TJU6GwyMa4L;qA*>NlZsF= z(yMrXr0ND#xy0a;0r3Y^|MGq#{Zo;b8bw8xSFzox2qc$sTV|j2d9lDb*T_@#QgZ(= zhHEiR-eRB>uDetdV=iU??v>r*pkbXOem6-r%xnh;-N~gLcJJa#;@>%;w}ffEg|M4;aDtr&+Nx#*}!DI@61PnjO2Sa;m>uYza5fSk|s4RJ7a3 zwO&GFc#=l)^2XIA)(rUXzYvQgVWKDH<%A33pZbiS35SpD5lJ}K|Dgs&GzfNgyI z)7;n~p2o2DriR5o%`x5-X1GdN)DD1UvV#2@K|lfaGzLcQ$#E2vQ+C=|X}#RVNe4{T zmAQ;}#N9hkPMo?rnA|%4IXP(Mh*#cYz5HM3X5*RkypNvo-_XO5`SfGj5b6CuinsqB z)2Lxn$vvG->OK@60-l{ZGIti`IE_r+;`HBj7uayw?0`iP(;0KAH)4$j@mQOjxq>=` zvtD)eAtH|di5GzOZ6aj1uxt}AfX*p30$lClZC7hhbNockhI}cU5*udkYfj9ZqoYhW zq1UM1kaNrnzHdA)j8??tsDFs?DR z6IW30L&*WA8{6mpoO;1WH*>#MF2+XA!a4fti4|G_IqXQ}c2KVFNU>t{S=H*e<^&MN zDE45S#4Rm|4YEe>5?Jm&$|XtXwjR+&Ir}1(A^o{_HhFDUlAx=vG>ae9c2vRGs<=&#}p=fc3Jh zDapWbr=1-b?H2?^bDRdaUSmCY+nB%s-I;pj%7WNP>%*s989`58Z=R#`A})2!%oUfq zZg#TFBt{f*CwsrhC3CVX;q_kyNtt7saVIkhMqIn1JyW1k9oF2(^h}KVInOLj52_^h zJ`xVpWo5#IJ})bi1WR5TDy$t=E7LuwdvZ%w>JoTwR!c97j{yPbE^wm*k|3S zpctp?z~`Q>fITYM)tO3OhMg$?!cEbW&#+1c!f4#9jhYXZZG||b^S?Oxh&KKa`ZxZC zB_MI(y;6`U>Q+ha1`==OJ}w(US3bWg8jw#4Ock~`x5v{9_*{@h%@e6PAfCVq3xjsx zdo)5YxyemF-Y-oCMtc=^col1XDG4H#coplNis7;pij4jTfgt4gaH^2o>`InlWGGS1 zWojTooNG<$w!_X13&ztQvam*C#H}GL0JI(9wH!iACDv3ag*m(DkCmYdtfgPP0yaAAZrvAxtIyCR!Cuy8z5eF+clM6!T1;k^B&q z2QG%1P;xy|ag_8DSyFjrTIxnq=bc?i5T5vhF7N-L zRKKDp#)XJdy`H$OPF<5n;A#Z;M$gv8- zDRihJ9x7y@MY7_C)R{alU}9AUhLGP!5)HUS^%%PcT+n*Z*hUA_ut!8*tyf;-%8VEY ztJtBdd#$3&fxHXIdvU8%FIey8DYCmGQk@K(B@TpWRnoBmIAMFNy)Mi|g`$?uxRE%U zhfuOQJ9QrWfqHhjsk5vHUBJ)8-=I_hp5*{$iO6QLjEInq01!0)(w{}0+}|ax?py~` zI_Y@}PnN2YkazUuWJX?OBcdz|{C*WDW`XM}yF*|YscPQSgMt60#K?aBGeDvova9-J z;9n_M`!+DhHY%tbRHxbw{3F20xcDZHOM?4cR7DHyDS=d65#^Gk{gEi{2d=l%xw^jR zcqKxDI6M1!z44&h59Ug#vaZghWL?b%_Oa4$ za{5M`Ft~bx(XazS%968ey|J&mXVX;OyjL;DJ9}kwPVcyk;3aC;Z{en+1I`;GxGu-h z3ik>IW9DwuzfLbAn~D2m_z?3Z#k?y+$BVg76^TY-UNTUy&xoGIsl|DJZ#j?EqRTK>s&y8`OJ~+quW)%mw#7ic<9+2Le>@ zP!xUhTu*=v$3=j*^Nax7Rg&94avGoE`*{Q6n<$15d;1BYFtoEw{ zO2W>S3}%dsWGUi$K~`3EEo0wy)B`oIQ(nj}z~!L5``2V7=w&^u`VjBT-}R6x+!Nnt zTVGXO#@fL(Lpf;3<~&6jraLylTI1AnuWFCAMxi(+**aBh>`vuc?@9LfrYV?G(a!7G zj9;DwB&YVtqR;R@to=Zwrt$Pg5L?^jsWASGqs)^5SM6?58TXb*on=3seg_bWx~cr4 z8&fUos4hDlc>$_iUU*Tu?!Hm+v0VP+ilO`@5D}IZ)xm-i_g#to>Equ}GKXpWXZ)`e z8-yAK#>;o|vIUYBky=H+mANOnZcH9{;Ub*flSYCyQf&MW&>$c?a8PA3-?`xAhugW= zaDgmU!mkxoZY)XWY)w{M!DLP~b^P?sjx5T{JKxy(EYHKD$zWE+!Kk@Al)SE-AO3ni zHkKbFnX$>JmSm|>DK7&x2Pf}jSCqd&exu}*3-x61bM6ZErB=sJT)J@sC_jm)bDfa~ z1Ugu?v-3YvE9DI$9t)De1N?^4#@zV!1F4c^@VoI7xjghQNexK`e=83c$NMkUx4)4i z@8@J6Nc94J`zZZ>j*0H!btPSX8wWf-Xt?=J)jzK*@Y03)S%haff1QLLJib-54IM4D zZXkyUbCAGbr(`5c4FM;C+v&xa0+*gc1C~zq7o5)>G0vC08av?y1^88Hk8TD=3W%AF zr1b;`6iv)`kmf44PWk%jy;PKQ{|Vyt@n%TGED?OX-M8B~iwglSQPF)cl^st-0>8205 z{d*&5xz8WC>0raDIm=C_xoM4?&UaI<{XI81xdIpe2k!GLZo11&A92%iH{If|@no|B=&-YfoMH(I&AACk_Ip5p(vMW=c>nXXM z+_~=36g^g8`vT|*cHc@Ll=_{bifSO3{bn4~vC2cBSjlY7S$;E(Ofj)!pLhWXL zwo|BdK6A7M+B|Kp_o&SWW+P7PYMcNd& zodUP=<@7;eYlAoE6GTRhZcmxK)+y>X!M*d5=O#sKo7N2OHE#I=N;|X->aP^lD0wv~ z%H_EZ`aAfpqvX`*%>F!HR>Fl~Y@tmne{bO_NUjwrHG*I^l3Pf+SV65)IIraIBJfMQ zHr3a_cuD^v#f?hVs};Qb2?5=xBrkIExY1k=+f=PVPc2a11YFFOm{8Zoc%51^U$tlg zUSx4NwTk(_99 zlRKO0(B1_~f(|~>KG6o@N-%n3BA6P$BKp((D9l*Bh%vlxMn5k+G2#tQxk$i+Y`P@v2;GO8gO1YbJzzZ;EH+)o+cLt!q=T_0I2-Pic|g0WZ5) z%I>mYY#X-ovwboJOur_vD$+0tzjyy2D zr?}l$GFU5FJwz*6NIHFxRx)kmvEfH4ADpEPo}X9l8L@-sw7saluzX0JZ+xLP z-WaKkUp+z_KYh41{%2`ib-rb-v;pt(bPj^9$P@`UgRO0rclXf1Wma8uXt%SUVe@ z&W5M6;py!82SyPjKSI& z^nV8ZpV2Y=7^B;x((J|7S((0VRcc#$4%Hf#`$Nr!SzLactz#;RlL)(t4rf zJPbJ(Yr}u$%30|hJktMI`X4(#gI6`i=m@eKhwR27yK(c~F&;Ho8wG8n<`*9W(}BS~ z1?~Cud36py@B(}Z@FnwIew0oOK_>>G6X?c#bVBhC&!@uXXKDGqi87_qA3`^_JeBA z%U3XFY1v#V$mRcnXzQG_VBTD{X$dv8HW^LY>?T9ICbpj3f~Jm^hSru_Bxh&~)^`}% zl7<$sxChz=kMlW`<$F?tV|%=^SHz0cW1}+Q+G%J}_SZgt-={m0`LEume^-mN8BHB* zWoLCXG~T$Vqjib4J6$%fy{T=9c3=8=R_prCCcU9;b!(IM?SazuZEa1B#u822moA&% z)Uf)h7QnP;(q-4gnmTR{v5W>cG_>Wt?O4c zv<6${zx%k!eVX6oJm26xU)9ls48EM#+G%t&u4{+NE-&A*MEk0fZ@-!HJ5|25sV&GM znU|-0LQZY0)o7`?*=Sn0eo4#frdewnI<%|vyzIjD>3Xe0XmmzTWYV#&LBy?TAG^}1 zYiWm?vOJmgSFP_@(9*W1wQ1g}8yH)yS@;caYB6TkR(h!dB` z=RUgt(DyxQSG#_7tku;U==bGB*Ecq_D(3h5=m|nv`g`OE7>`$V!t7~n&vbirK_~9vey3n^WRGX)y10j z-Y|R-f~Fsk)(+?Yts-AX`mGWCzfk1AP5RzQ_E_tHA^jEio&;ta=^ywS>a+F z*sC2Qtr^W8N%n1|k8uC=GxCytWeoc=;JZj4;(I6MPm&JflCA9XMv{J)_a~(71Ejy= z&6ES=w~;<|j;0+YFKL9p_9Nt@r1$XcBQNi=PdSf1!Cy}LBHwcIa?e2Cwy!6@lyn85 z<^p)BCzT(PC?$W4blsP@r3Xwq>2rJ!lYfEqkqb2K7vv?qx!i%@O#0$9+EIR(^tlQL z{!P;Rro#(3?MMgt|tA)Jmd+?{iJzy&KMPtp3hg{FCd-Ew+om$(#!CT zg`SzDH}Dl0&tDcbl!N;zM zxpbQHLBw@*>gpe0oQZdw`gtLXNx*5u0&OLK!^CCfBt}k%R+7#oE{wAB`E|~_pUd9s z)PJbH`sA=9I~DzO3Vh%jh^To{W{HPN{E@w&f+=TRD`#bsbFEdv2l3F6wDsUataY8N zc@mi$h>}Ui|G9G{kxfAcKIinM(~Y(X9yw_*%|3dj16+3j!7)~xL)qbp%DoMp?)j^-7+7fWI!t+{A zLQ0WG3(pQMr9_XLR0L6Ky|QWaJPrA2+7*iSd2XK)%M?mHSq_{qWy&s-2VKeDiAb*| zHrhxZ1Dv1C`(Ho1WB;yXelng@B|ji@kw5l}uMbTWDR>kQXbavvdM5t?qQr28e&=+^ hiJDTO4Jvp<0RH=YwmSc};-m-u`>^zPPX6-}_&;6TzhVFY literal 0 HcmV?d00001 diff --git a/dist/x86-ansi/nsJSON.dll b/dist/x86-ansi/nsJSON.dll new file mode 100644 index 0000000000000000000000000000000000000000..ae524028a151de3ad7302e0aa3cb6c18d02fc67b GIT binary patch literal 23040 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 Bg`xle literal 0 HcmV?d00001 diff --git a/dist/x86-unicode/nsJSON.dll b/dist/x86-unicode/nsJSON.dll new file mode 100644 index 0000000000000000000000000000000000000000..cd28d6465ab63520d00ad0eb24e8332e7db7afbc GIT binary patch literal 24064 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 literal 0 HcmV?d00001 diff --git a/examples/ConsoleExec.nsi b/examples/ConsoleExec.nsi new file mode 100644 index 0000000..b5eea0b --- /dev/null +++ b/examples/ConsoleExec.nsi @@ -0,0 +1,193 @@ +!include MUI2.nsh + +Name `nsJSON plug-in` +OutFile nsJSON_ConsoleExec.exe +RequestExecutionLevel user +ShowInstDetails show + +!define MUI_COMPONENTSPAGE_SMALLDESC +!define MUI_PAGE_CUSTOMFUNCTION_LEAVE ComponentsPage_Leave +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_LANGUAGE English + +Section + + CreateDirectory $EXEDIR\Output + +SectionEnd + +Section /o `Console App` NATIVE + + InitPluginsDir + StrCpy $R0 $PLUGINSDIR\ConsoleApp.exe + File /oname=$R0 Bin\ConsoleApp.exe + + nsJSON::Set /tree ConsoleExec /value `{ "Path": "$R0", "WorkingDir": "$PLUGINSDIR", "RawOutput": true }` + + ; Provide arguments as a single string or an array. + nsJSON::Set /tree ConsoleExec Arguments /value `[]` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg1"` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg2"` + + ; Provide input as a single string or an array. + nsJSON::Set /tree ConsoleExec Input /value `"hello"` + + ; Export the tree to file for debugging. + DetailPrint `Generate: $EXEDIR\Output\ConsoleExecInput.json from ConsoleExec` + nsJSON::Serialize /tree ConsoleExec /format /file $EXEDIR\Output\ConsoleExecInput.json + + ; Execute the console application. + DetailPrint `Exec: $R0` + nsJSON::Set /tree ConsoleExecOutput /exec ConsoleExec + + ; Save the output to file. + DetailPrint `Generate: $EXEDIR\Output\ConsoleExecOutput.json from ConsoleExecOutput` + nsJSON::Serialize /tree ConsoleExecOutput /format /file $EXEDIR\Output\ConsoleExecOutput.json + +SectionEnd + +Section /o `Async Console App` NATIVEASYNC + + InitPluginsDir + StrCpy $R0 $PLUGINSDIR\ConsoleApp.exe + File /oname=$R0 Bin\ConsoleApp.exe + + nsJSON::Set /tree ConsoleExec /value `{ "Async": true, "Path": "$R0", "WorkingDir": "$PLUGINSDIR", "RawOutput": true }` + + ; Provide arguments as a single string or an array. + nsJSON::Set /tree ConsoleExec Arguments /value `[]` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg1"` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg2"` + + ; Provide input as a single string or an array. + nsJSON::Set /tree ConsoleExec Input /value `"hello"` + + ; Export the tree to file for debugging. + DetailPrint `Generate: $EXEDIR\Output\AsyncConsoleExecInput.json from ConsoleExec` + nsJSON::Serialize /tree ConsoleExec /format /file $EXEDIR\Output\AsyncConsoleExecInput.json + + ; Execute the console application. + DetailPrint `Exec: $R0` + nsJSON::Set /tree AsyncConsoleExecOutput /exec ConsoleExec + + ; Wait until done. + ${Do} + Sleep 1000 + nsJSON::Wait ConsoleExec /timeout 0 + Pop $R0 + ${If} $R0 != wait + ${Break} + ${EndIf} + DetailPrint `Waiting...` + ${Loop} + + DetailPrint `Finished...` + + ; Save the output to file. + DetailPrint `Generate: $EXEDIR\Output\AsyncConsoleExecOutput.json from AsyncConsoleExecOutput` + nsJSON::Serialize /tree AsyncConsoleExecOutput /format /file $EXEDIR\Output\AsyncConsoleExecOutput.json + +SectionEnd + +Section /o `.NET Console App` DOTNET + + InitPluginsDir + StrCpy $R0 $PLUGINSDIR\ConsoleAppDotNet.exe + File /oname=$R0 Bin\ConsoleAppDotNet.exe + + nsJSON::Set /tree ConsoleExec /value `{ "Path": "$R0", "WorkingDir": "$PLUGINSDIR" }` + + ; Provide arguments as a single string or an array. + nsJSON::Set /tree ConsoleExec Arguments /value `[]` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg1"` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg2"` + + ; Provide input as a single string or an array. + nsJSON::Set /tree ConsoleExec Input /value `"hello"` + + ; Export the tree to file for debugging. + DetailPrint `Generate: $EXEDIR\Output\ConsoleExecDotNetInput.json from ConsoleExec` + nsJSON::Serialize /tree ConsoleExec /format /file $EXEDIR\Output\ConsoleExecDotNetInput.json + + ; Execute the console application. + DetailPrint `Exec: $R0` + nsJSON::Set /tree ConsoleExecOutput /exec ConsoleExec + + ; Save the output to file. + DetailPrint `Generate: $EXEDIR\Output\ConsoleExecDotNetOutput.json from ConsoleExecOutput` + nsJSON::Serialize /tree ConsoleExecOutput /format /file $EXEDIR\Output\ConsoleExecDotNetOutput.json + +SectionEnd + +Section /o `UI Thread Console App` UINATIVE + +SectionEnd + +Function ComponentsPage_Leave + + ${IfNot} ${SectionIsSelected} ${UINATIVE} + Return + ${EndIf} + + GetDlgItem $R0 $HWNDPARENT 1 + EnableWindow $R0 0 + GetDlgItem $R0 $HWNDPARENT 2 + EnableWindow $R0 0 + GetDlgItem $R0 $HWNDPARENT 3 + EnableWindow $R0 0 + FindWindow $R0 `#32770` `` $HWNDPARENT + GetDlgItem $R0 $R0 1032 + EnableWindow $R0 0 + + Banner::Show /set 76 `Executing ConsoleApp.exe` `Please wait...` + + InitPluginsDir + StrCpy $R0 $PLUGINSDIR\ConsoleApp.exe + File /oname=$R0 Bin\ConsoleApp.exe + + nsJSON::Set /tree ConsoleExec /value `{ "UIAsync": true, "Path": "$R0", "WorkingDir": "$PLUGINSDIR", "RawOutput": true }` + + ; Provide arguments as a single string or an array. + nsJSON::Set /tree ConsoleExec Arguments /value `[]` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg1"` + nsJSON::Set /tree ConsoleExec Arguments /value `"arg2"` + + ; Provide input as a single string or an array. + nsJSON::Set /tree ConsoleExec Input /value `"hello"` + + ; Export the tree to file for debugging. + nsJSON::Serialize /tree ConsoleExec /format /file $EXEDIR\Output\UIConsoleExecInput.json + + ; Execute the console application. + nsJSON::Set /tree UIConsoleExecOutput /exec ConsoleExec + + ; Save the output to file. + nsJSON::Serialize /tree UIConsoleExecOutput /format /file $EXEDIR\Output\UIConsoleExecOutput.json + + Banner::Destroy + + GetDlgItem $R0 $HWNDPARENT 1 + EnableWindow $R0 1 + GetDlgItem $R0 $HWNDPARENT 2 + EnableWindow $R0 1 + GetDlgItem $R0 $HWNDPARENT 3 + EnableWindow $R0 1 + FindWindow $R0 `#32770` `` $HWNDPARENT + GetDlgItem $R0 $R0 1032 + EnableWindow $R0 1 + +FunctionEnd + +LangString NATIVEDesc ${LANG_ENGLISH} `Executes native console app with STDIN data and reads JSON from STDOUT` +LangString NATIVEASYNCDesc ${LANG_ENGLISH} `Executes native console app asynchronously with STDIN data and reads JSON from STDOUT` +LangString DOTNETDesc ${LANG_ENGLISH} `Executes .NET console app with STDIN data and reads JSON from STDOUT` +LangString UINATIVEDesc ${LANG_ENGLISH} `Executes native console app asynchronously while processing window messages` + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${NATIVE} $(NATIVEDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${NATIVEASYNC} $(NATIVEASYNCDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${DOTNET} $(DOTNETDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${UINATIVE} $(UINATIVEDesc) +!insertmacro MUI_FUNCTION_DESCRIPTION_END \ No newline at end of file diff --git a/examples/Example.nsi b/examples/Example.nsi new file mode 100644 index 0000000..246fae0 --- /dev/null +++ b/examples/Example.nsi @@ -0,0 +1,509 @@ +!include MUI2.nsh + +Name `nsJSON plug-in` +OutFile nsJSON_Example.exe +RequestExecutionLevel user +ShowInstDetails show + +!define MUI_COMPONENTSPAGE_SMALLDESC +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_LANGUAGE English + +Section + + CreateDirectory $EXEDIR\Output + +SectionEnd + +Section /o `Parse Example1.json` EXAMPLE1 + + ; Input: Example1.json; output: Example1.json. + nsJSON::Set /file $EXEDIR\Input\Example1.json + nsJSON::Serialize /format /file $EXEDIR\Output\Example1.json + DetailPrint `Generate: $EXEDIR\Output\Example1.json` + +SectionEnd + +Section /o `Convert Example1_Unicode.json (no BOM)` EXAMPLE1B + + ; Use the /unicode switch if the input file is Unicode. + nsJSON::Set /file /unicode $EXEDIR\Input\Example1_Unicode.json + + ; Generate an ANSII output file. + nsJSON::Serialize /format /file $EXEDIR\Output\Example1B_ASCII.json + DetailPrint `Generate: $EXEDIR\Output\Example1B_ASCII.json` + + ; Generate a Unicode output file. + nsJSON::Serialize /format /file /unicode $EXEDIR\Output\Example1B_Unicode.json + DetailPrint `Generate: $EXEDIR\Output\Example1B_Unicode.json` + +SectionEnd + +Section /o `Convert Example1_Unicode_UTF16BE.json (BOM)` EXAMPLE1C + + ; Use the /unicode switch if the input file is Unicode. + nsJSON::Set /file /unicode $EXEDIR\Input\Example1_Unicode_UTF16BE.json + + ; Generate an ANSII output file. + nsJSON::Serialize /format /file $EXEDIR\Output\Example1C_ASCII.json + DetailPrint `Generate: $EXEDIR\Output\Example1C_ASCII.json` + + ; Generate a Unicode output file. + nsJSON::Serialize /format /file /unicode $EXEDIR\Output\Example1C_Unicode.json + DetailPrint `Generate: $EXEDIR\Output\Example1C_Unicode.json` + +SectionEnd + +Section /o `Convert Example1_Unicode_UTF16LE.json (BOM)` EXAMPLE1D + + ; Use the /unicode switch if the input file is Unicode. + nsJSON::Set /file /unicode $EXEDIR\Input\Example1_Unicode_UTF16LE.json + + ; Generate an ANSII output file. + nsJSON::Serialize /format /file $EXEDIR\Output\Example1D_ASCII.json + DetailPrint `Generate: $EXEDIR\Output\Example1D_ASCII.json` + + ; Generate a Unicode output file. + nsJSON::Serialize /format /file /unicode $EXEDIR\Output\Example1D_Unicode.json + DetailPrint `Generate: $EXEDIR\Output\Example1D_Unicode.json` + +SectionEnd + +Section /o `Convert Example1_Unicode_UTF8.json (sig)` EXAMPLE1E + + ; No /unicode switch is used for UTF8. + nsJSON::Set /file $EXEDIR\Input\Example1_Unicode_UTF8.json + + ; Generate an ANSII output file. + nsJSON::Serialize /format /file $EXEDIR\Output\Example1_ASCII.json + DetailPrint `Generate: $EXEDIR\Output\Example1_ASCII.json` + + ; Generate a Unicode output file. + nsJSON::Serialize /format /file /unicode $EXEDIR\Output\Example1_Unicode.json + DetailPrint `Generate: $EXEDIR\Output\Example1_Unicode.json` + +SectionEnd + +Section /o `Parse Example2.json` EXAMPLE2 + + ; Input: Example2.json; output: Example2.json. + nsJSON::Set /file $EXEDIR\Input\Example2.json + nsJSON::Serialize /format /file $EXEDIR\Output\Example2.json + DetailPrint `Generate: $EXEDIR\Output\Example2.json` + +SectionEnd + +Section /o `Parse Example3.json` EXAMPLE3 + + ; Input: Example3.json; output: Example3.json. + nsJSON::Set /file $EXEDIR\Input\Example3.json + nsJSON::Serialize /format /file $EXEDIR\Output\Example3.json + DetailPrint `Generate: $EXEDIR\Output\Example3.json` + +SectionEnd + +Section /o `Parse Example4.json` EXAMPLE4 + + ; Input: Example4.json; output: Example4.json. + nsJSON::Set /file $EXEDIR\Input\Example4.json + nsJSON::Serialize /format /file $EXEDIR\Output\Example4.json + DetailPrint `Generate: $EXEDIR\Output\Example4.json` + +SectionEnd + +Section /o `Parse Example5.json` EXAMPLE5 + + ; Input: Example5.json; output: Example5.json. + nsJSON::Set /file $EXEDIR\Input\Example5.json + nsJSON::Serialize /format /file $EXEDIR\Output\Example5.json + DetailPrint `Generate: $EXEDIR\Output\Example5.json` + +SectionEnd + +Section /o `Parse Example5.json (via $$R0)` EXAMPLE5B + + ; Input: Example5.json; output: Example5.json. + nsJSON::Set /file $EXEDIR\Input\Example5.json + nsJSON::Serialize /format + Pop $R0 + FileOpen $R1 $EXEDIR\Output\Example5B.json w + FileWrite $R1 $R0 + FileClose $R1 + DetailPrint `Generate: $EXEDIR\Output\Example5B.json` + +SectionEnd + +Section /o `Generate Example6.json` EXAMPLE6 + + nsJSON::Set /value `{}` + nsJSON::Set `html` `head` `title` /value `"Example6"` + + ; Build an array using individual calls. + nsJSON::Set `html` `body` `h1` /value `[]` + nsJSON::Set `html` `body` `h1` /value `"Hello,\tmy name is"` + nsJSON::Set `html` `body` `h1` `i` /value `"Stuart."` + nsJSON::Set `html` `body` `h1` /value `"Howdy"` + nsJSON::Set `html` `body` `h1` /value `"!"` + nsJSON::Set `html` `body` `h1` /value `[ true, "hello", false ]` + + ; Build an array using a JSON string. + nsJSON::Set `html` `body` `h2` /value `[ "I like", { "u": { "i" : "programming" } }, "very much!" ]` + + ; Quotes in node keys are allowed; they are escaped automatically. + nsJSON::Set `html` `body` `a href="http://www.afrowsoft.co.uk"` /value `"My website!"` + + ; Open the file below in Notepad. + nsJSON::Serialize /format /file $EXEDIR\Output\Example6.json + DetailPrint `Generate: $EXEDIR\Output\Example6.json` + +SectionEnd + +Section /o `Reading from Example1.json` EXAMPLE7 + + nsJSON::Set /file $EXEDIR\Input\Example1.json + + ; Read quoted string value. + ClearErrors + nsJSON::Get `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossTerm` /end + ${IfNot} ${Errors} + Pop $R0 + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossTerm = $R0` + ${EndIf} + + ; Read quoted string value with escaping. + ClearErrors + nsJSON::Set `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossDef` `para2` /value `"A meta-markup language, used to create markup languages\r\nsuch as DocBook."` + nsJSON::Get `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossDef` `para2` /end + ${IfNot} ${Errors} + Pop $R0 + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossDef->para2 = $R0` + ${EndIf} + + ; Read quoted string value without expanding escape sequences. + ClearErrors + nsJSON::Set `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossDef` `para3` /value `"A meta-markup language, used to create markup languages\r\nsuch as DocBook."` + nsJSON::Get /noexpand `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossDef` `para3` /end + ${IfNot} ${Errors} + Pop $R0 + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossDef->para3 = $R0` + ${EndIf} + + ; Read the value of an array (returns a comma delimited list). + ClearErrors + nsJSON::Get `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossDef` `GlossSeeAlso` /end + ${IfNot} ${Errors} + Pop $R0 + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossDef->GlossSeeAlso = $R0` + ${EndIf} + + ; Try reading a node that does not exist. + ClearErrors + nsJSON::Get `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossDef` `GlossSeeAlso2` /end + ${IfNot} ${Errors} + Pop $R0 + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossDef->GlossSeeAlso2 = $R0` + ${Else} + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossDef->GlossSeeAlso2 = Does not exist!` + ${EndIf} + + ; Try reading an array element that does not exist. + ClearErrors + nsJSON::Get `glossary` `GlossDiv` `GlossList` `GlossEntry` `GlossDef` `GlossSeeAlso` 99 /end + ${IfNot} ${Errors} + Pop $R0 + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossDef->GlossSeeAlso->#99 = $R0` + ${Else} + DetailPrint `glossary->GlossDiv->GlossList->GlossEntry->GlossDef->GlossSeeAlso->#99 = Does not exist!` + ${EndIf} + +SectionEnd + +Section /o `Arrays test` EXAMPLE8 + + nsJSON::Set /value `{}` + + ; You can add an array this way. + nsJSON::Set `array1` /value `[ "value 1", "value 2", { "value 3": "node value" }, "value 4", 5 ]` + + ; Inspect array1. + nsJSON::Get `array1` /end + Pop $R0 + DetailPrint `array1 = [$R0]` + + ; Or you can build it this way. + nsJSON::Set `array2` /value `[]` + nsJSON::Set `array2` /value `"value 1"` + nsJSON::Set `array2` /value `"value 2"` + nsJSON::Set `array2` `value 3` /value `"node value"` + nsJSON::Set `array2` /value `"value 4"` + nsJSON::Set `array2` /value `5` + + ; You cannot add the same value again. + nsJSON::Set `array2` /value `5` + + ; More. + nsJSON::Set `array2` /value `blah1` + nsJSON::Set `array2` /value `blah2` + + ; Inspect array2. + nsJSON::Get `array2` /end + Pop $R0 + DetailPrint `array2 = [$R0]` + + ; Does an array element exist at the given index? + nsJSON::Get /exists `array2` /index 0 /end + Pop $R0 + DetailPrint `array2[0] exists? = $R0` + ${If} $R0 == yes + nsJSON::Get `array2` /index 0 /end + Pop $R0 + DetailPrint `array2[0] = $R0` + ${EndIf} + + ; Does an array element exist at the given index? + nsJSON::Get /exists `array2` /index -2 /end + Pop $R0 + DetailPrint `array2[-2] exists? = $R0` + ${If} $R0 == yes + nsJSON::Get `array2` /index -2 /end + Pop $R0 + DetailPrint `array2[-2] = $R0` + ${EndIf} + + ; Does an array element exist that matches the value? + nsJSON::Get /exists `array2` `5` /end + Pop $R0 + DetailPrint `array2->5 exists? = $R0` + + ; Does an array element exist at the given index? + nsJSON::Get /exists `array2` /index 6 /end + Pop $R0 + DetailPrint `array2[6] exists? = $R0` + + ; Open Example8_1.json to see what it now looks like. + nsJSON::Serialize /format /file $EXEDIR\Output\Example8_1.json + DetailPrint `Generate: $EXEDIR\Output\Example8_1.json` + + ; Now delete the element at the given index. + nsJSON::Delete `array1` /index 2 `value 3` /end + DetailPrint `Delete: array1[2]->value 3` + nsJSON::Delete `array2` /index 5 /end + DetailPrint `Delete: array2[5]` + + ; Now delete the elements with the given values. + nsJSON::Delete `array1` `value 1` /end + DetailPrint `Delete: array1->value 1` + nsJSON::Delete `array2` `value 2` /end + DetailPrint `Delete: array2->value 2` + + ; Inspect array1. + nsJSON::Get `array1` /end + Pop $R0 + DetailPrint `array1 = [$R0]` + + ; Inspect array2. + nsJSON::Get `array2` /end + Pop $R0 + DetailPrint `array2 = [$R0]` + + ; Open Example8_2.json to see what it now looks like. + nsJSON::Serialize /format /file $EXEDIR\Output\Example8_2.json + DetailPrint `Generate: $EXEDIR\Output\Example8_2.json` + +SectionEnd + +Section /o `Node iteration test` EXAMPLE9 + + nsJSON::Set /file $EXEDIR\Input\Example4.json + + ; Get the node count. + nsJSON::Get /count `web-app` `servlet` /index 0 `init-param` /end + Pop $R0 + + DetailPrint `Node web-app->servlet[0]->init-param contains $R0 children:` + ${For} $R1 0 $R0 + + nsJSON::Get /key `web-app` `servlet` /index 0 `init-param` /index $R1 /end + Pop $R2 + nsJSON::Get `web-app` `servlet` /index 0 `init-param` /index $R1 /end + Pop $R3 + nsJSON::Get /type `web-app` `servlet` /index 0 `init-param` /index $R1 /end + Pop $R4 + DetailPrint `$R2 = $R3 (type: $R4)` + + ${Next} + +SectionEnd + +Section /o `Load Example5.json into Example4.json` EXAMPLE10 + + ; Input: Example5.json; output: Example5.json. + nsJSON::Set /file $EXEDIR\Input\Example5.json + nsJSON::Set `menu` `example4` /file $EXEDIR\Input\Example4.json + nsJSON::Serialize /format /file $EXEDIR\Output\Example10.json + DetailPrint `Generate: $EXEDIR\Output\Example10.json` + +SectionEnd + +Section /o `Copies Preferences.json into PreferencesNew.json` EXAMPLE11 + + ; Input: Preferences.json; output: PreferencesNew.json. + nsJSON::Set /file $EXEDIR\Input\Preferences.json + nsJSON::Serialize /format /file $EXEDIR\Output\PreferencesNew.json + DetailPrint `Generate: $EXEDIR\Output\PreferencesNew.json` + +SectionEnd + +Section /o `Copies Preferences2.json into Preferences2New.json` EXAMPLE12 + + ; Input: Preferences2.json; output: Preferences2New.json. + nsJSON::Set /file $EXEDIR\Input\Preferences2.json + nsJSON::Serialize /format /file $EXEDIR\Output\Preferences2New.json + DetailPrint `Generate: $EXEDIR\Output\Preferences2New.json` + +SectionEnd + +Section /o `Buffer overflow` EXAMPLE13 + + ; Input: Example2.json. + nsJSON::Set /file $EXEDIR\Input\Example2.json + nsJSON::Serialize /format + Pop $R0 + StrLen $R1 $R0 + DetailPrint `Output length: $R1` + MessageBox MB_OK $R0 + +SectionEnd + +Section /o `Get tests` EXAMPLE14 + + nsJSON::Set /value `{ "a": "a", "b": 1, "c": {} }` + nsJSON::Serialize /format + Pop $R0 + DetailPrint `Test: $R0` + + nsJSON::Get /key /index 0 /end + Pop $R0 + DetailPrint `Key at index 0: $R0` + + nsJSON::Get /keys /end + Pop $R0 + DetailPrint `Total keys: $R0` + StrCpy $R1 0 + ${DoWhile} $R0 > 0 + Pop $R2 + DetailPrint `Key at index $R1: $R2` + IntOp $R0 $R0 - 1 + IntOp $R1 $R1 + 1 + ${Loop} + + nsJSON::Get /type /index 0 /end + Pop $R0 + DetailPrint `Type at index 0: $R0` + + nsJSON::Get /exists /index 0 /end + Pop $R0 + DetailPrint `Index 0 exists?: $R0` + + nsJSON::Get /count /end + Pop $R0 + DetailPrint `Count: $R0` + + nsJSON::Get /isempty /end + Pop $R0 + DetailPrint `Is empty?: $R0` + + nsJSON::Get /isempty /index 2 /end + Pop $R0 + DetailPrint `Is empty at index 2?: $R0` + + ClearErrors + nsJSON::Get /isempty /index 99 /end + IfErrors 0 +2 + DetailPrint `Is empty at index 99?: Error flag is set` + +SectionEnd + +Section /o `Delete tests` EXAMPLE15 + + nsJSON::Set /value `{ "a": "a", "b": 1, "c": {} }` + nsJSON::Serialize /format + Pop $R0 + DetailPrint `Test: $R0` + + nsJSON::Delete a /end + Pop $R0 + DetailPrint `Delete key "a": $R0` + + nsJSON::Serialize /format + Pop $R0 + DetailPrint `Now: $R0` + + ClearErrors + nsJSON::Delete /index 99 /end + IfErrors 0 +2 + DetailPrint `Delete at index 99: Error flag is set` + +SectionEnd + +Section /o `Empty keys` EXAMPLE16 + + nsJSON::Set /value `{ "": "a", 1, "c": {} }` + nsJSON::Serialize /format + Pop $R0 + DetailPrint `Test: $R0` + + nsJSON::Set a "" /value `"abc"` + nsJSON::Serialize /format + Pop $R0 + DetailPrint `Test: $R0` + +SectionEnd + +LangString Example1Desc ${LANG_ENGLISH} `Parses Example1.json and then generates Output\Example1.json` +LangString Example1BDesc ${LANG_ENGLISH} `Parses Example1_Unicode.json (no BOM) and then generates Unicode and ASCII copies` +LangString Example1CDesc ${LANG_ENGLISH} `Parses Example1_Unicode_UTF16BE.json (BOM) and then generates Unicode and ASCII copies` +LangString Example1DDesc ${LANG_ENGLISH} `Parses Example1_Unicode_UTF16LE.json (BOM) and then generates Unicode and ASCII copies` +LangString Example1EDesc ${LANG_ENGLISH} `Parses Example1_Unicode_UTF8.json (sig) and then generates Unicode and ASCII copies` +LangString Example2Desc ${LANG_ENGLISH} `Parses Example2.json and then generates Output\Example2.json` +LangString Example3Desc ${LANG_ENGLISH} `Parses Example3.json and then generates Output\Example3.json` +LangString Example4Desc ${LANG_ENGLISH} `Parses Example4.json and then generates Output\Example4.json` +LangString Example5Desc ${LANG_ENGLISH} `Parses Example5.json and then generates Output\Example5.json` +LangString Example5BDesc ${LANG_ENGLISH} `Parses Example5.json and then generates Output\Example5B.json using $$R0` +LangString Example6Desc ${LANG_ENGLISH} `Generates Output\Example6.json using Parse, Set and Serialize` +LangString Example7Desc ${LANG_ENGLISH} `Parses Example1.json and then reads values from the tree using Get` +LangString Example8Desc ${LANG_ENGLISH} `Tests JSON array manipulation while generating Output\Example8.json` +LangString Example9Desc ${LANG_ENGLISH} `Iterates through some nodes in Example4.json by index` +LangString Example10Desc ${LANG_ENGLISH} `Parses Example5.json into Example4.json and then generates Output\Example10.json` +LangString Example11Desc ${LANG_ENGLISH} `Parses Preferences.json and then generates Output\PreferencesNew.json` +LangString Example12Desc ${LANG_ENGLISH} `Parses Preferences2.json and then generates Output\Preferences2New.json` +LangString Example13Desc ${LANG_ENGLISH} `Parses Example2.json (size>NSIS_MAX_STRLEN) and outputs the result.` +LangString Example14Desc ${LANG_ENGLISH} `Simple Get function tests.` +LangString Example15Desc ${LANG_ENGLISH} `Simple Delete function tests.` +LangString Example16Desc ${LANG_ENGLISH} `Empty/missing value keys tests.` + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE1} $(Example1Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE1B} $(Example1BDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE1C} $(Example1CDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE1D} $(Example1DDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE1E} $(Example1EDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE2} $(Example2Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE3} $(Example3Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE4} $(Example4Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE5} $(Example5Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE5B} $(Example5BDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE6} $(Example6Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE7} $(Example7Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE8} $(Example8Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE9} $(Example9Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE10} $(Example10Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE11} $(Example11Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE12} $(Example12Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE13} $(Example13Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE14} $(Example14Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE15} $(Example15Desc) + !insertmacro MUI_DESCRIPTION_TEXT ${EXAMPLE16} $(Example16Desc) +!insertmacro MUI_FUNCTION_DESCRIPTION_END \ No newline at end of file diff --git a/examples/HttpWebRequest.nsi b/examples/HttpWebRequest.nsi new file mode 100644 index 0000000..e6cb83e --- /dev/null +++ b/examples/HttpWebRequest.nsi @@ -0,0 +1,127 @@ +!include MUI2.nsh + +!define HttpWebRequestURL `http://www.afrowsoft.co.uk/test/HttpWebRequest.php` + +Name `nsJSON plug-in` +OutFile nsJSON_HttpWebRequest.exe +RequestExecutionLevel user +ShowInstDetails show + +!define MUI_COMPONENTSPAGE_SMALLDESC +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_LANGUAGE English + +Section + + CreateDirectory $EXEDIR\Output + +SectionEnd + +Section /o `POST` DATAPOST + + StrCpy $R0 `{ "Name": "Jonathan Doe", "Age": 23, "Formula": "a + b == 13%!", "ViewTimes": [ "10:00", "13:43", "21:19", "03:10" ] }` + + nsJSON::Set /tree HttpWebRequest /value `{ "Url": "${HttpWebRequestURL}", "Verb": "POST", "Agent": "Mozilla/5.0 (Windows NT 10.0; rv:10.0) Gecko/20100101 Firefox/10.0" }` + DetailPrint `Send: $R0` + DetailPrint `Send to: ${HttpWebRequestURL}` + nsJSON::Set /tree HttpWebRequest Data /value $R0 + + nsJSON::Set /tree HttpWebRequest Params /value `{ "a": "string", "b": true, "c": 2 }` + + DetailPrint `Generate: $EXEDIR\Output\HttpWebRequest.json from HttpWebRequest` + nsJSON::Serialize /tree HttpWebRequest /format /file $EXEDIR\Output\HttpWebRequest.json + + DetailPrint `Download: ${HttpWebRequestURL}` + nsJSON::Set /tree HttpWebResponse /http HttpWebRequest + + DetailPrint `Generate: $EXEDIR\Output\HttpWebResponse.json from HttpWebResponse` + nsJSON::Serialize /tree HttpWebResponse /format /file $EXEDIR\Output\HttpWebResponse.json + +SectionEnd + +Section /o `Async POST` DATAPOSTASYNC + + StrCpy $R0 `{ "Name": "Jonathan Doe", "Age": 23, "Formula": "a + b == 13%!", "ViewTimes": [ "10:00", "13:43", "21:19", "03:10" ] }` + + nsJSON::Set /tree HttpWebRequest /value `{ "Url": "${HttpWebRequestURL}", "Verb": "POST", "Async": true }` + DetailPrint `Send: $R0` + DetailPrint `Send to: ${HttpWebRequestURL}` + nsJSON::Set /tree HttpWebRequest Data /value $R0 + + nsJSON::Set /tree HttpWebRequest Params /value `{ "a": "string", "b": true, "c": 2 }` + + DetailPrint `Generate: $EXEDIR\Output\HttpWebRequest.json from HttpWebRequest` + nsJSON::Serialize /tree HttpWebRequest /format /file $EXEDIR\Output\HttpWebRequest.json + + DetailPrint `Download: ${HttpWebRequestURL}` + nsJSON::Set /tree AsyncHttpWebResponse /http HttpWebRequest + + ; Wait until done. + ${Do} + Sleep 1000 + nsJSON::Wait HttpWebRequest /timeout 0 + Pop $R0 + ${If} $R0 != wait + ${Break} + ${EndIf} + DetailPrint `Waiting...` + ${Loop} + + DetailPrint `Finished...` + + DetailPrint `Generate: $EXEDIR\Output\AsyncHttpWebResponse.json from AsyncHttpWebResponse` + nsJSON::Serialize /tree AsyncHttpWebResponse /format /file $EXEDIR\Output\AsyncHttpWebResponse.json + +SectionEnd + +Section /o `Raw POST` RAWDATAPOST + + StrCpy $R0 `Name=Jonathan+Doe&Age=23&Formula=a+%2B+b+%3D%3D+13%25%21&ViewTimes[]=10%3A00&ViewTimes[]=13%3A43&ViewTimes[]=21%3A19&ViewTimes[]=03%3A10` + + nsJSON::Set /tree HttpWebRequest /value `{ "Url": "${HttpWebRequestURL}", "Verb": "POST", "DataType": "Raw" }` + DetailPrint `Send: $R0` + DetailPrint `Send to: ${HttpWebRequestURL}` + nsJSON::Set /tree HttpWebRequest Data /value `"$R0"` + + DetailPrint `Generate: $EXEDIR\Output\HttpWebRequest_Raw.json from HttpWebRequest` + nsJSON::Serialize /tree HttpWebRequest /format /file $EXEDIR\Output\HttpWebRequest_Raw.json + + DetailPrint `Download: ${HttpWebRequestURL}` + nsJSON::Set /tree HttpWebResponse /http HttpWebRequest + + DetailPrint `Generate: $EXEDIR\Output\HttpWebResponse_Raw.json from HttpWebResponse` + nsJSON::Serialize /tree HttpWebResponse /format /file $EXEDIR\Output\HttpWebResponse_Raw.json + +SectionEnd + +Section /o `JSON POST` JSONPOST + + nsJSON::Set /tree HttpWebRequest /value `{ "Url": "${HttpWebRequestURL}", "Verb": "POST", "DataType": "JSON" }` + DetailPrint `Send: $EXEDIR\Input\Example1.json` + DetailPrint `Send to: ${HttpWebRequestURL}` + nsJSON::Set /tree HttpWebRequest Data /file $EXEDIR\Input\Example1.json + + DetailPrint `Generate: $EXEDIR\Output\HttpWebRequest_JSON.json from HttpWebRequest` + nsJSON::Serialize /tree HttpWebRequest /format /file $EXEDIR\Output\HttpWebRequest_JSON.json + + DetailPrint `Download: ${HttpWebRequestURL}` + nsJSON::Set /tree HttpWebResponse /http HttpWebRequest + + DetailPrint `Generate: $EXEDIR\Output\HttpWebResponse_JSON.json from HttpWebResponse` + nsJSON::Serialize /tree HttpWebResponse /format /file $EXEDIR\Output\HttpWebResponse_JSON.json + +SectionEnd + +LangString DataPOSTDesc ${LANG_ENGLISH} `Sends POST data and parses the JSON response` +LangString DataPOSTAsyncDesc ${LANG_ENGLISH} `Asynchronously sends POST data and parses the JSON response` +LangString RawDataPOSTDesc ${LANG_ENGLISH} `Sends raw POST data and parses the JSON response` +LangString JSONPOSTDesc ${LANG_ENGLISH} `Sends Example1.json and parses the JSON response` + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${DATAPOST} $(DataPOSTDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${DATAPOSTASYNC} $(DataPOSTAsyncDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${RAWDATAPOST} $(RawDataPOSTDesc) + !insertmacro MUI_DESCRIPTION_TEXT ${JSONPOST} $(JSONPOSTDesc) +!insertmacro MUI_FUNCTION_DESCRIPTION_END \ No newline at end of file diff --git a/examples/Input/Example1.json b/examples/Input/Example1.json new file mode 100644 index 0000000..ac2fc3c --- /dev/null +++ b/examples/Input/Example1.json @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} \ No newline at end of file diff --git a/examples/Input/Example1_Unicode.json b/examples/Input/Example1_Unicode.json new file mode 100644 index 0000000000000000000000000000000000000000..9b465ef22548993c290ef0a5bb53df3884128b15 GIT binary patch literal 990 zcmaKrK~KU!6oluBiT`2KGn#l&^oGF@6Nra;Ffkq~R3uhP+G0e)fA`yN%ND6*L;H3+ zJM(6C+2~X!>L}1miBjc^NSW5G+{kECeYc20bAE~9>RUkd4f<2da@DkObQ}AJJB0yE zUk3{98X-8(Vlkd)1W!&+e|znejySs7yYEYfn`Yf!NS3Q-tq#Js8Na3L9GaP`r3 zs>kuZhey)N$ja|3$Znx&nfH{ehaeUuDk7=5eyHvGxI}ztMb3u4?5-KJh^IY^!!G-p zpp}AK?Gy@a*ZbRnt2i!17l4^!LXlZL?mMexeEy zkfou^m{cR3QQ7-fyG@ui-FCsQ$lfJW;L(&`V{iqlmg(6397GPoMDJ7)wc+Gb=C?dk zbkAGd?ccf(otUl@cBhq#XWl+t!|bWLLod63fJOUF7=N4{o?l_+CHO10tSN?!flAac$5#hRAie4-D~3$2P%uZ5#q@|*lY ztD_1vs;w)7G@Q5Umn^+KB7%q3=zMOZKyPA>RUi=2nc4z0} IDzfkGFIne*U;qFB literal 0 HcmV?d00001 diff --git a/examples/Input/Example1_Unicode_UTF16LE.json b/examples/Input/Example1_Unicode_UTF16LE.json new file mode 100644 index 0000000000000000000000000000000000000000..023c0dbcaabadfb210f750a7f9e12c89b83be1be GIT binary patch literal 992 zcmaKrK~KU!6oluBiT|PL8BM$>dc$Cd3B*G^m>3TgDiSLtZ84(ZudCm7TegKtHneZI zvomjIm*1an9qULP`I;$KqKpwL)tZ$nd8)n}M6Nl%SW)@Sr}_l_u4S?6wQzJxezSkL zRT#kZv8T|k5rA_nO`Z`v89nXx+9@14y&Ek+wYf4#!+EPg$>J-Z)3HW7x)v9ZQ3_Wd zU8{N+?|Qf=t(2_XuAJ-^nihFa$$AK4QJ_MSn(K?&td9%Cn^xrP(U;jZV;1r>XHieb zcG=eitpwbYAzMdH=eAT&_Oak4Z*kfGilKXJRv#0Y`P3Lp~%S-9g-5;B@$$DA% z2}^`emL6S3q#Ejk%G|%oZOp9hwhMMe_BNRuk6z(50++LDm=5jFKx8mX^hy;{dz^g2 z{F-Ni?rDp==B*3RiRe0JcT&1| $R0` + IfErrors 0 +2 + DetailPrint `Error flag is set!` + DetailPrint `` +!macroend + +Section + + !insertmacro DoTest `{ "Input": [ { "test1": false, } ] }` `Trailing comma` + + !insertmacro DoTest `{ "Input": [ { "test1": false } .? ] }` `Junk characters` + + !insertmacro DoTest `{ "Input": [ { "test1": false } }` `Missing square bracket` + + !insertmacro DoTest `{ "Input": [ { "test1": false ] }` `Missing curly brace` + +SectionEnd \ No newline at end of file diff --git a/examples/UnicodeNSIS.nsi b/examples/UnicodeNSIS.nsi new file mode 100644 index 0000000..479da30 --- /dev/null +++ b/examples/UnicodeNSIS.nsi @@ -0,0 +1,25 @@ +!include MUI2.nsh + +Name `nsJSON plug-in` +OutFile nsJSON_UnicodeNSIS.exe +RequestExecutionLevel user +ShowInstDetails show +Unicode true + +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_LANGUAGE English + +Section + + nsJSON::Set /tree testTree testNode /value testValue + + nsJSON::Get /tree testTree testNode /end + Pop $R0 + DetailPrint $R0 + + nsJSON::Serialize /tree testTree + Pop $R0 + DetailPrint $R0 + +SectionEnd \ No newline at end of file diff --git a/src/ConsoleApp/ConsoleApp.vcxproj b/src/ConsoleApp/ConsoleApp.vcxproj new file mode 100644 index 0000000..4cccade --- /dev/null +++ b/src/ConsoleApp/ConsoleApp.vcxproj @@ -0,0 +1,141 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D} + ConsoleApp + 8.1 + + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + false + + + false + + + + Level3 + Disabled + true + + + + + Debug + + + + + Level3 + Disabled + true + + + + + Debug + + + + + Level3 + MaxSpeed + true + true + false + + + true + true + No + true + libc.lib + main + + + + + Level3 + MaxSpeed + true + true + false + + + true + true + No + true + libc.lib + main + + + + + + + + + \ No newline at end of file diff --git a/src/ConsoleApp/ConsoleApp.vcxproj.filters b/src/ConsoleApp/ConsoleApp.vcxproj.filters new file mode 100644 index 0000000..6827613 --- /dev/null +++ b/src/ConsoleApp/ConsoleApp.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/src/ConsoleApp/main.c b/src/ConsoleApp/main.c new file mode 100644 index 0000000..6aaf1df --- /dev/null +++ b/src/ConsoleApp/main.c @@ -0,0 +1,30 @@ +#include + +int main() +{ + CHAR szInBuffer[32], szOutBuffer[128]; + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE), hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwBytesRead, dwBytesWritten; + int i, cchOutBuffer; + + if (ReadFile(hStdIn, szInBuffer, sizeof(szInBuffer), &dwBytesRead, NULL) && dwBytesRead) + { + for (i = lstrlenA(szInBuffer) - 1; i >= 0 && (szInBuffer[i] == '\n' || szInBuffer[i] == '\r' || szInBuffer[i] == '\t' || szInBuffer[i] == ' '); i--) + szInBuffer[i] = '\0'; + + // Pretend to do some work. + Sleep(5000); + + // Return JSON. + //cchOutBuffer = wsprintfA(szOutBuffer, "{\"Input\": \"%s\", \"Output\": \"blah!\"}", szInBuffer); + + // Or return a string. + lstrcpyA(szOutBuffer, "blah!!!!"); + cchOutBuffer = lstrlenA(szOutBuffer); + + WriteFile(hStdOut, szOutBuffer, cchOutBuffer, &dwBytesWritten, NULL); + } + + ExitProcess(0); + return 0; +} \ No newline at end of file diff --git a/src/ConsoleAppDotNet/ConsoleAppDotNet.csproj b/src/ConsoleAppDotNet/ConsoleAppDotNet.csproj new file mode 100644 index 0000000..57aa371 --- /dev/null +++ b/src/ConsoleAppDotNet/ConsoleAppDotNet.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8} + Exe + Properties + ConsoleAppDotNet + ConsoleAppDotNet + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ConsoleAppDotNet/Program.cs b/src/ConsoleAppDotNet/Program.cs new file mode 100644 index 0000000..611c9b5 --- /dev/null +++ b/src/ConsoleAppDotNet/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading; + +namespace ConsoleAppDotNet +{ + class Program + { + static void Main() + { + string input = Console.ReadLine(); + if (input == null) + return; + + input = input.Trim(); + if (input == string.Empty) + return; + + // Pretend to do some work. + Thread.Sleep(TimeSpan.FromSeconds(5)); + + // Return JSON. + Console.Write("{{\"Input\": \"{0}\", \"Output\":\"blah!\"}}", input); + } + } +} diff --git a/src/ConsoleAppDotNet/Properties/AssemblyInfo.cs b/src/ConsoleAppDotNet/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c6d08d1 --- /dev/null +++ b/src/ConsoleAppDotNet/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ConsoleAppDotNet")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ConsoleAppDotNet")] +[assembly: AssemblyCopyright("Copyright © Stuart Welch 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("dbaa5865-5ef1-4a17-bc19-b68d5e5c13f8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/JSON.c b/src/JSON.c new file mode 100644 index 0000000..d8aa257 --- /dev/null +++ b/src/JSON.c @@ -0,0 +1,1542 @@ +/* + JSON parser & writer by Stuart Welch + v1.1.1.1 - 10th October 2024 +*/ + +#include +#include "JSON.h" +#include "pluginapi.h" + +#define IsWhitespace(c) c == TEXT(' ') || c == TEXT('\t') || c == TEXT('\r') || c == TEXT('\n') +#define ntohs(n) (((((unsigned short)(n) & 0xFF)) << 8) | (((unsigned short)(n) & 0xFF00) >> 8)) +static TCHAR hex[] = TEXT("0123456789abcdef"); + +struct JSON_NODE *JSON_Create() +{ + return (struct JSON_NODE *)GlobalAlloc(GPTR, sizeof(struct JSON_NODE)); +} + +PTCHAR JSON_GetQuotedValue(struct JSON_NODE *pNode, const PTCHAR pszDefaultValue) +{ + return pNode && pNode->eType == JNT_QUOTED_VALUE ? pNode->pszValue : pszDefaultValue; +} + +BOOL JSON_IsTrue(struct JSON_NODE *pNode) +{ + if (!pNode || !pNode->pszValue || lstrcmp(pNode->pszValue, TEXT("0")) == 0 || lstrcmpi(pNode->pszValue, TEXT("false")) == 0) + return FALSE; + return TRUE; +} + +static void EatWhitespace(PTCHAR pszBuffer, int *piPos) +{ + while (IsWhitespace(pszBuffer[*piPos])) + (*piPos)++; +} + +static PTCHAR MakeCopy(PTCHAR pszCopyFrom, int iStart, int iEnd) +{ + PTCHAR pszCopy = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * (iEnd - iStart + 2)); + if (pszCopy) + { + int i = 0; + for (i = 0; iStart <= iEnd; i++, iStart++) + pszCopy[i] = pszCopyFrom[iStart]; + } + + return pszCopy; +} + +static BOOL IsChar(PTCHAR pszBuffer, int *piPos, TCHAR chExpected) +{ + EatWhitespace(pszBuffer, piPos); + + if (pszBuffer[*piPos] != chExpected) + return FALSE; + + return TRUE; +} + +/*static BOOL IsNumeric(PTCHAR pszBuffer) +{ + int cch = lstrlen(pszBuffer), i; + if (cch == 0) + return FALSE; + + if (pszBuffer[0] == TEXT('0')) + { + if (cch == 1) + return TRUE; + return FALSE; + } + + for (i = 0; i < cch; i++) + if (pszBuffer[i] < TEXT('0') || pszBuffer[i] > TEXT('9')) + return FALSE; + + return TRUE; +}*/ + +static BOOL EatChar(PTCHAR pszBuffer, int *piPos, TCHAR chExpected) +{ + if (!IsChar(pszBuffer, piPos, chExpected)) + return FALSE; + + (*piPos)++; + return TRUE; +} + +static BOOL IsEscaped(PTCHAR pszBuffer, int iPos) +{ + return iPos > 0 && pszBuffer[iPos - 1] == TEXT('\\') && !IsEscaped(pszBuffer, iPos - 1); +} + +static enum JSON_WORD_TYPE EatWord(PTCHAR pszBuffer, int *piPos, PTCHAR *ppszWord) +{ + int iStart, iEnd; + + EatWhitespace(pszBuffer, piPos); + + iStart = *piPos; + iEnd = 0; + *ppszWord = NULL; + + if (pszBuffer[*piPos] == TEXT('"')) + { + iStart++; + + while (TRUE) + { + (*piPos)++; + if (pszBuffer[*piPos] == TEXT('"') && !IsEscaped(pszBuffer, *piPos) || pszBuffer[*piPos] == 0) + { + iEnd = *piPos - 1; + if (pszBuffer[*piPos] == TEXT('"')) + (*piPos)++; + break; + } + } + + *ppszWord = MakeCopy(pszBuffer, iStart, iEnd); + return JWT_STRING; + } + + while (TRUE) + { + if (pszBuffer[*piPos] == TEXT(':') || pszBuffer[*piPos] == TEXT(',') || pszBuffer[*piPos] == TEXT('}') || pszBuffer[*piPos] == TEXT(']') || IsWhitespace(pszBuffer[*piPos]) || pszBuffer[*piPos] == 0) + { + iEnd = *piPos - 1; + break; + } + (*piPos)++; + } + + if (iStart <= iEnd) + { + *ppszWord = MakeCopy(pszBuffer, iStart, iEnd); + return JWT_OTHER; + } + + return JWT_NONE; +} + +static struct JSON_NODE *EatNode(PTCHAR pszBuffer, int *piPos, BOOL bIsValue); + +static struct JSON_NODE *EatNodeArray(PTCHAR pszBuffer, int *piPos) +{ + struct JSON_NODE *pNode = EatNode(pszBuffer, piPos, TRUE); + if (pNode && EatChar(pszBuffer, piPos, TEXT(','))) + pNode->pNext = EatNodeArray(pszBuffer, piPos); + return pNode; +} + +static struct JSON_NODE *EatNode(PTCHAR pszBuffer, int *piPos, BOOL bIsValue) +{ + struct JSON_NODE *pNode = JSON_Create(); + + if (pNode) + { + if (EatChar(pszBuffer, piPos, TEXT('{'))) + { + pNode->eType = JNT_NODE; + pNode->pValue = EatNode(pszBuffer, piPos, FALSE); + if (!pNode->pValue || !EatChar(pszBuffer, piPos, TEXT('}'))) + { + JSON_Delete(&pNode, NULL); + return NULL; + } + } + else if (EatChar(pszBuffer, piPos, TEXT('['))) + { + pNode->eType = JNT_ARRAY; + pNode->pValue = EatNodeArray(pszBuffer, piPos); + // Allow empty arrays: pValue can be NULL + if (!EatChar(pszBuffer, piPos, TEXT(']'))) + { + JSON_Delete(&pNode, NULL); + return NULL; + } + } + else + { + PTCHAR pszValue = NULL; + enum JSON_WORD_TYPE eType = EatWord(pszBuffer, piPos, &pszValue); + + if (pszValue) + { + switch (eType) + { + case JWT_STRING: + pNode->eType = JNT_QUOTED_VALUE; + break; + case JWT_OTHER: + pNode->eType = JNT_VALUE; + break; + } + + if (eType != JWT_NONE) + { + // Node is just a value. + if (bIsValue) + { + pNode->pszValue = pszValue; + } + // Node is a key: value pair. + else if (EatChar(pszBuffer, piPos, TEXT(':'))) + { + struct JSON_NODE *pChildNode = EatNode(pszBuffer, piPos, TRUE); + if (!pChildNode) + { + GlobalFree(pNode); + return NULL; + } + + pNode->eType = pChildNode->eType; + pNode->pNext = pChildNode->pNext; + pNode->pValue = pChildNode->pValue; + GlobalFree(pChildNode); + + pNode->pszKey = pszValue; + } + // No key was given; use an empty string. + else + { + pNode->pszKey = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR)); + pNode->pszValue = pszValue; + } + } + } + else + { + GlobalFree(pNode); + return NULL; + } + } + + // Commas are allowed; eat next node. + if (!bIsValue && EatChar(pszBuffer, piPos, TEXT(','))) + { + pNode->pNext = EatNode(pszBuffer, piPos, FALSE); + if (pNode->pNext == NULL) + { + GlobalFree(pNode); + return NULL; + } + } + } + + return pNode; +} + +static struct JSON_NODE *EatRoot(PTCHAR pszBuffer) +{ + int iPos = 0; + return EatNode(pszBuffer, &iPos, FALSE); +} + +static PTCHAR EscapeQuotes(PTCHAR pszStr) +{ + int nQuotes = 0, i, cchLen = lstrlen(pszStr); + PTCHAR pszStrNew; + + for (i = 0; i < cchLen; i++) + if (pszStr[i] == TEXT('"')) + nQuotes++; + + pszStrNew = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * (cchLen + nQuotes + 1)); + if (pszStrNew) + { + int j; + + for (i = 0, j = 0; i < cchLen; i++, j++) + { + if (pszStr[i] == TEXT('"') && !IsEscaped(pszStr, i)) + { + pszStrNew[j] = TEXT('\\'); + j++; + } + + pszStrNew[j] = pszStr[i]; + } + } + + return pszStrNew; +} + +void JSON_Delete(struct JSON_NODE **ppNode, struct JSON_NODE *pPrev) +{ + if (ppNode && *ppNode) + { + struct JSON_NODE *pNext = *ppNode; + + if (pPrev) + { + if (pPrev->pNext == *ppNode) + pPrev->pNext = (*ppNode)->pNext; + + if ((pPrev->eType == JNT_NODE || pPrev->eType == JNT_ARRAY) && pPrev->pValue == *ppNode) + pPrev->pValue = (*ppNode)->pNext; + } + + while (pNext) + { + struct JSON_NODE *pNextNext = pNext->pNext; + + if (pNext->pszKey) + GlobalFree(pNext->pszKey); + + switch (pNext->eType) + { + case JNT_NODE: + case JNT_ARRAY: + + if (pNext->pValue) + JSON_Delete(&pNext->pValue, NULL); + + break; + case JNT_VALUE: + case JNT_QUOTED_VALUE: + + GlobalFree(pNext->pszValue); + + break; + } + + GlobalFree(pNext); + + if (pNext == *ppNode) + *ppNode = NULL; + + if (pPrev) + break; + + pNext = pNextNext; + } + } +} + +static PCHAR HandleUTF8Input(PBYTE pbBuffer, int *pcbBuffer) +{ + if (pbBuffer[0] == 0xEF && pbBuffer[1] == 0xBB && pbBuffer[2] == 0xBF) // UTF-8 sig + { + if (pcbBuffer) + *pcbBuffer -= 3; + return (PCHAR)(pbBuffer + 3); + } + + return (PCHAR)pbBuffer; +} + +static PWCHAR HandleUTF16Input(PBYTE pbBuffer, int *pcchBuffer) +{ + if (pbBuffer[0] == 0xFE && pbBuffer[1] == 0xFF) // UTF-16BE BOM + { + int i; + PWCHAR pwszBuffer = (PWCHAR)(pbBuffer + 2); + for (i = 0; pwszBuffer[i] != L'\0'; i++) + pwszBuffer[i] = ntohs(pwszBuffer[i]); + if (pcchBuffer) + *pcchBuffer -= 2; + return pwszBuffer; + } + + if (pbBuffer[0] == 0xFF && pbBuffer[1] == 0xFE && (pbBuffer[2] != 0x00 || pbBuffer[3] != 0x00)) // UTF-16LE BOM + { + if (pcchBuffer) + *pcchBuffer -= 2; + return (PWCHAR)(pbBuffer + 2); + } + + return (PWCHAR)pbBuffer; +} + +static struct JSON_NODE *Parse(PBYTE pbSource, enum JSON_SET_FLAGS eFlags) +{ + struct JSON_NODE *pNode = NULL; + PBYTE pbBuffer = NULL; + DWORD dwSize = 0; + + if (eFlags & JSF_IS_FILE) + { + HANDLE hFile = CreateFile((PTCHAR)pbSource, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + dwSize = GetFileSize(hFile, NULL); + if (dwSize > 0) + { + pbBuffer = (PBYTE)GlobalAlloc(GPTR, dwSize + ((eFlags & JSF_IS_UNICODE) ? sizeof(WCHAR) : sizeof(CHAR))); + if (pbBuffer) + ReadFile(hFile, pbBuffer, dwSize, &dwSize, NULL); + } + CloseHandle(hFile); + } + } + else + { + pbBuffer = pbSource; + dwSize = (eFlags & JSF_IS_UNICODE) ? lstrlenW((PWCHAR)pbSource) : lstrlenA((PCHAR)pbSource); + } + + if (pbBuffer) + { +#ifdef UNICODE + if (eFlags & JSF_IS_UNICODE) + { + pNode = EatRoot(HandleUTF16Input(pbBuffer, NULL)); + } + else + { + int cbBuffer = dwSize; + PWCHAR pwszBuffer = JSON_ToUnicode(HandleUTF8Input(pbBuffer, &cbBuffer), &cbBuffer); + if (pwszBuffer) + { + pNode = EatRoot(pwszBuffer); + GlobalFree(pwszBuffer); + } + } +#else + if (eFlags & JSF_IS_UNICODE) + { + int cchBuffer = dwSize / sizeof(WCHAR); + PCHAR pszBuffer = JSON_FromUnicode(HandleUTF16Input(pbBuffer, &cchBuffer), &cchBuffer, CP_ACP); + if (pszBuffer) + { + pNode = EatRoot(pszBuffer); + GlobalFree(pszBuffer); + } + } + else + { + pNode = EatRoot(HandleUTF8Input(pbBuffer, NULL)); + } +#endif + + if (eFlags & JSF_IS_FILE) + GlobalFree(pbBuffer); + } + + return pNode; +} + +int JSON_Count(struct JSON_NODE *pNode) +{ + int i = 0; + + if (pNode) + { + pNode = pNode->pValue; + for (; pNode != NULL; i++) + pNode = pNode->pNext; + } + + return i; +} + +struct JSON_NODE *JSON_Get(struct JSON_NODE *pNode, PTCHAR pszKey, BOOL bKeyIsIndex) +{ + return JSON_Next(&pNode, pszKey, bKeyIsIndex, FALSE, NULL); +} + +struct JSON_NODE *JSON_GetEx(struct JSON_NODE *pNode, PTCHAR pszKey, BOOL bKeyIsIndex, BOOL bCreate, BOOL *pbCreated) +{ + return JSON_Next(&pNode, pszKey, bKeyIsIndex, bCreate, pbCreated); +} + +struct JSON_NODE *JSON_Next(struct JSON_NODE **ppNode, PTCHAR pszKey, BOOL bKeyIsIndex, BOOL bCreate, BOOL *pbCreated) +{ + if (pbCreated) + *pbCreated = FALSE; + + if (ppNode) + { + struct JSON_NODE *pParent = *ppNode; + struct JSON_NODE *pNext = NULL; + + pszKey = EscapeQuotes(pszKey); + if (pszKey) + { + // We can only get a child node if the parent is also a node or array. + if (pParent->eType == JNT_NODE || pParent->eType == JNT_ARRAY) + { + pNext = pParent->pValue; + + // Get the child node by index. + if (bKeyIsIndex) + { + int i, j = myatoi(pszKey); + + // Negative index? + if (j < 0) + j = JSON_Count(pParent) + j; + + for (i = 0; i < j && pNext != NULL; i++) + { + *ppNode = pNext; + pNext = pNext->pNext; + } + } + // Get the child node by key. + else if (pParent->eType == JNT_NODE) + { + while (pNext != NULL && !(!pNext->pszKey && !*pszKey || lstrcmp(pNext->pszKey, pszKey) == 0)) + { + *ppNode = pNext; + pNext = pNext->pNext; + } + } + // Seek to the end of the array if we're going to add a new node element. + else if (bCreate) + { + while (pNext != NULL) + { + *ppNode = pNext; + pNext = pNext->pNext; + } + } + // Otherwise find an array element that matches. + else + { + while (pNext != NULL) + { + if ((pNext->eType == JNT_VALUE || pNext->eType == JNT_QUOTED_VALUE) && lstrcmp(pNext->pszValue, pszKey) == 0) + break; + + *ppNode = pNext; + pNext = pNext->pNext; + } + } + + // No existing child node found and the caller wants to create one. + if (!pNext && bCreate) + { + struct JSON_NODE *pNew = (struct JSON_NODE *)GlobalAlloc(GPTR, sizeof(struct JSON_NODE)); + pNew->eType = JNT_NODE; + + if (pbCreated) + *pbCreated = TRUE; + + // Adding a new node to an array. + if (pParent->eType == JNT_ARRAY && !bKeyIsIndex) + { + pNew->pValue = (struct JSON_NODE *)GlobalAlloc(GPTR, sizeof(struct JSON_NODE)); + pNew->pValue->eType = JNT_NODE; + + if (*pszKey) + { + pNew->pValue->pszKey = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * (lstrlen(pszKey) + 1)); + if (pNew->pValue->pszKey) + lstrcpy(pNew->pValue->pszKey, pszKey); + } + + if (*ppNode != pParent) + (*ppNode)->pNext = pNew; + else + pParent->pValue = pNew; + + pNext = pNew->pValue; + } + // Change parent to an array. + else if (pParent->eType == JNT_NODE && bKeyIsIndex) + { + pParent->eType = JNT_ARRAY; + + if (*ppNode != pParent) + (*ppNode)->pNext = pNew; + else + pParent->pValue = pNew; + + pNext = pNew; + } + // Adding a new node to a node. + else + { + if (!bKeyIsIndex) + { + if (*pszKey) + { + pNew->pszKey = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * (lstrlen(pszKey) + 1)); + if (pNew->pszKey) + lstrcpy(pNew->pszKey, pszKey); + } + else + { + pNew->pszKey = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR)); + } + } + + if (*ppNode != pParent) + (*ppNode)->pNext = pNew; + else + pParent->pValue = pNew; + + pNext = pNew; + } + } + } + + GlobalFree(pszKey); + } + + return pNext; + } + + return NULL; +} + +BOOL JSON_Set(struct JSON_NODE *pNode, PBYTE pbValue, enum JSON_SET_FLAGS eFlags) +{ + if (pNode) + { + if (eFlags & JSF_IS_RAW) + { + // We are overwriting the node's value. + switch (pNode->eType) + { + case JNT_NODE: + case JNT_ARRAY: + + if (pNode->pValue) + JSON_Delete(&pNode->pValue, NULL); + + break; + case JNT_VALUE: + case JNT_QUOTED_VALUE: + + if (pNode->pszValue) + GlobalFree(pNode->pszValue); + + break; + } + + pNode->eType = JNT_QUOTED_VALUE; + +#ifdef UNICODE + if (eFlags & JSF_IS_UNICODE) + { + pNode->pszValue = JSON_Escape((PWCHAR)pbValue, JEF_NONE); + } + else + { + int cchValue = lstrlenA((PCHAR)pbValue); + PWCHAR pwszConverted = JSON_ToUnicode((PCHAR)pbValue, &cchValue); + if (pwszConverted) + { + pNode->pszValue = JSON_Escape(pwszConverted, JEF_NONE); + GlobalFree(pwszConverted); + } + else + { + pNode->pszValue = NULL; + } + } +#else + if (eFlags & JSF_IS_UNICODE) + { + int cchValue = lstrlenW((PWCHAR)pbValue); + PCHAR pszConverted = JSON_FromUnicode((PWCHAR)pbValue, &cchValue, CP_ACP); + if (pszConverted) + { + pNode->pszValue = JSON_Escape(pszConverted, JEF_NONE); + GlobalFree(pszConverted); + } + else + { + pNode->pszValue = NULL; + } + } + else + { + pNode->pszValue = JSON_Escape((PCHAR)pbValue, JEF_NONE); + } +#endif + } + else + { + struct JSON_NODE *pChildNode = Parse(pbValue, eFlags); + if (!pChildNode) + return FALSE; + + switch (pNode->eType) + { + case JNT_NODE: + + // We are overwriting the node's value. + if (pNode->pValue) + JSON_Delete(&pNode->pValue, NULL); + + switch (pChildNode->eType) + { + case JNT_NODE: + case JNT_ARRAY: + + // Child node is a key: value pair; set it as the parent node's value. + if (pChildNode->pszKey) + { + pNode->eType = JNT_NODE; + pNode->pValue = pChildNode; + } + // Child node is a node without a key; overwrite the parent node's value with the child node's value. + else + { + pNode->eType = pChildNode->eType; + pNode->pValue = pChildNode->pValue; + GlobalFree(pChildNode); + } + + break; + default: + + // Replace the node's value. + pNode->eType = pChildNode->eType; + pNode->pszValue = pChildNode->pszValue; + GlobalFree(pChildNode); + + break; + } + + break; + case JNT_ARRAY: + + if (pNode->pValue) + { + struct JSON_NODE *pArrayNode = pNode->pValue; + + // Seek to the end of the array and then append to the end. + while (TRUE) + { + if (pArrayNode->pNext == NULL) + break; + + pArrayNode = pArrayNode->pNext; + } + + pArrayNode->pNext = pChildNode; + } + // Replacing node. + else + { + pNode->eType = pChildNode->eType; + pNode->pValue = pChildNode->pValue; + GlobalFree(pChildNode); + } + + break; + case JNT_VALUE: + case JNT_QUOTED_VALUE: + + // We are overwriting the node's value. + if (pNode->pszValue) + GlobalFree(pNode->pszValue); + + switch (pChildNode->eType) + { + case JNT_NODE: + case JNT_ARRAY: + + // Child node is a key: value pair; set it as the parent node's value. + if (pChildNode->pszKey) + { + pNode->eType = pChildNode->eType; + pNode->pValue = pChildNode; + } + // Child node is a node without a key; overwrite the parent node's value with the child node's value. + else + { + pNode->eType = pChildNode->eType; + pNode->pValue = pChildNode->pValue; + GlobalFree(pChildNode); + } + + break; + default: + + // Replace the node's value. + pNode->eType = pChildNode->eType; + pNode->pszValue = pChildNode->pszValue; + GlobalFree(pChildNode); + + break; + } + + break; + } + } + } + + return TRUE; +} + +BOOL JSON_SetEx(struct JSON_NODE *pNode, PTCHAR pszKey, BOOL bKeyIsIndex, PBYTE pbValue, enum JSON_SET_FLAGS eFlags) +{ + struct JSON_NODE *pNew = JSON_Next(&pNode, pszKey, bKeyIsIndex, TRUE, NULL); + if (pNew) + return JSON_Set(pNew, pbValue, eFlags); + return FALSE; +} + +static void MyWriteFile(HANDLE hFile, PTCHAR pszText, int cchText, BOOL bAsUnicode) +{ + DWORD dwBytes; + +#ifdef UNICODE + if (bAsUnicode) + { + WriteFile(hFile, pszText, sizeof(WCHAR) * cchText, &dwBytes, NULL); + } + else + { + PCHAR pszConverted = JSON_FromUnicode(pszText, &cchText, CP_ACP); + if (pszConverted) + { + WriteFile(hFile, pszConverted, cchText, &dwBytes, NULL); + GlobalFree(pszConverted); + } + } +#else + if (bAsUnicode) + { + PWCHAR pwszConverted = JSON_ToUnicode(pszText, &cchText); + if (pwszConverted) + { + WriteFile(hFile, pwszConverted, cchText, &dwBytes, NULL); + GlobalFree(pwszConverted); + } + } + else + { + WriteFile(hFile, pszText, cchText, &dwBytes, NULL); + } +#endif +} + +static void MyWriteFileEx(HANDLE hFile, PTCHAR pszText, int cchText, BOOL bAsUnicode, int iRepeat) +{ + int i = 0; + + for (; i < iRepeat; i++) + { + MyWriteFile(hFile, pszText, cchText, bAsUnicode); + } +} + +static void SerializeToFile(HANDLE hFile, struct JSON_NODE *pNode, int iIndent, BOOL bAsUnicode) +{ + if (!pNode) + return; + + if (pNode->pszKey) + { + MyWriteFile(hFile, TEXT("\""), 1, bAsUnicode); + MyWriteFile(hFile, pNode->pszKey, lstrlen(pNode->pszKey), bAsUnicode); + MyWriteFile(hFile, TEXT("\":"), 2, bAsUnicode); + + if (iIndent) + { + MyWriteFile(hFile, TEXT(" "), 1, bAsUnicode); + } + } + + switch (pNode->eType) + { + case JNT_NODE: + case JNT_ARRAY: + + MyWriteFile(hFile, pNode->eType == JNT_ARRAY ? TEXT("[") : TEXT("{"), 1, bAsUnicode); + + if (iIndent) + { + MyWriteFile(hFile, TEXT("\r\n"), 2, bAsUnicode); + MyWriteFileEx(hFile, TEXT(JSON_INDENT_CHAR), 1, bAsUnicode, iIndent); + } + + SerializeToFile(hFile, pNode->pValue, iIndent ? iIndent + JSON_INDENT : 0, bAsUnicode); + + if (iIndent) + { + MyWriteFile(hFile, TEXT("\r\n"), 2, bAsUnicode); + MyWriteFileEx(hFile, TEXT(JSON_INDENT_CHAR), 1, bAsUnicode, iIndent - 1); + } + + MyWriteFile(hFile, pNode->eType == JNT_ARRAY ? TEXT("]") : TEXT("}"), 1, bAsUnicode); + + break; + case JNT_VALUE: + + MyWriteFile(hFile, pNode->pszValue, lstrlen(pNode->pszValue), bAsUnicode); + + break; + case JNT_QUOTED_VALUE: + + MyWriteFile(hFile, TEXT("\""), 1, bAsUnicode); + MyWriteFile(hFile, pNode->pszValue, lstrlen(pNode->pszValue), bAsUnicode); + MyWriteFile(hFile, TEXT("\""), 1, bAsUnicode); + + break; + } + + if (pNode->pNext) + { + pNode = pNode->pNext; + + MyWriteFile(hFile, TEXT(","), 1, bAsUnicode); + + if (iIndent) + { + MyWriteFile(hFile, TEXT("\r\n"), 2, bAsUnicode); + MyWriteFileEx(hFile, TEXT(JSON_INDENT_CHAR), 1, bAsUnicode, iIndent - 1); + } + + SerializeToFile(hFile, pNode, iIndent, bAsUnicode); + } +} + +static void MyStrCpy(PTCHAR *ppszBuffer, int *pcchBuffer, int *piPos, PTCHAR pszCopy, int cchCopyLen, BOOL bResize, DWORD *pdwLastError) +{ + if (*piPos + cchCopyLen <= *pcchBuffer) + { + if (ppszBuffer && *ppszBuffer) + lstrcpy(*ppszBuffer + *piPos, pszCopy); + *piPos += cchCopyLen; + } + else if (bResize) + { + *pcchBuffer *= 2; + + if (ppszBuffer && *ppszBuffer) + { + PTCHAR pszNewBuffer = (PTCHAR)GlobalReAlloc(*ppszBuffer, *pcchBuffer, GMEM_ZEROINIT | GMEM_MOVEABLE); + if (!pszNewBuffer) + { + *pdwLastError = GetLastError(); + GlobalFree(*ppszBuffer); + *ppszBuffer = NULL; + } + else + { + *ppszBuffer = pszNewBuffer; + } + } + + MyStrCpy(ppszBuffer, pcchBuffer, piPos, pszCopy, cchCopyLen, TRUE, pdwLastError); + } + else + { + int i = 0; + for (; i < cchCopyLen && *piPos < *pcchBuffer; i++, (*piPos)++) + { + if (ppszBuffer && *ppszBuffer) + (*ppszBuffer)[*piPos] = pszCopy[i]; + } + } +} + +static void MyStrCpyEx(PTCHAR *ppszBuffer, int *pcchBuffer, int *piPos, PTCHAR pszCopy, int cchCopyLen, int iRepeat, BOOL bResize, DWORD *pdwLastError) +{ + int i = 0; + for (; i < iRepeat; i++) + { + MyStrCpy(ppszBuffer, pcchBuffer, piPos, pszCopy, cchCopyLen, bResize, pdwLastError); + } +} + +static void SerializeToBuffer(PTCHAR *ppszBuffer, int *pcchBuffer, struct JSON_NODE *pNode, int *piPos, int iIndent, BOOL bResize, DWORD *pdwLastError) +{ + if (!pNode) + { + if (ppszBuffer && *ppszBuffer) + lstrcpy(*ppszBuffer + *piPos, TEXT("")); + return; + } + + if (pNode->pszKey) + { + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT("\""), 1, bResize, pdwLastError); + MyStrCpy(ppszBuffer, pcchBuffer, piPos, pNode->pszKey, lstrlen(pNode->pszKey), bResize, pdwLastError); + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT("\":"), 2, bResize, pdwLastError); + + if (iIndent) + { + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT(" "), 1, bResize, pdwLastError); + } + } + + switch (pNode->eType) + { + case JNT_NODE: + case JNT_ARRAY: + + MyStrCpy(ppszBuffer, pcchBuffer, piPos, pNode->eType == JNT_ARRAY ? TEXT("[") : TEXT("{"), 1, bResize, pdwLastError); + + if (iIndent) + { + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT("\r\n"), 2, bResize, pdwLastError); + MyStrCpyEx(ppszBuffer, pcchBuffer, piPos, TEXT(JSON_INDENT_CHAR), 1, iIndent, bResize, pdwLastError); + } + + SerializeToBuffer(ppszBuffer, pcchBuffer, pNode->pValue, piPos, iIndent ? iIndent + JSON_INDENT : 0, bResize, pdwLastError); + + if (iIndent) + { + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT("\r\n"), 2, bResize, pdwLastError); + MyStrCpyEx(ppszBuffer, pcchBuffer, piPos, TEXT(JSON_INDENT_CHAR), 1, iIndent - 1, bResize, pdwLastError); + } + + MyStrCpy(ppszBuffer, pcchBuffer, piPos, pNode->eType == JNT_ARRAY ? TEXT("]") : TEXT("}"), 1, bResize, pdwLastError); + + break; + case JNT_VALUE: + + MyStrCpy(ppszBuffer, pcchBuffer, piPos, pNode->pszValue, lstrlen(pNode->pszValue), bResize, pdwLastError); + + break; + case JNT_QUOTED_VALUE: + + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT("\""), 1, bResize, pdwLastError); + MyStrCpy(ppszBuffer, pcchBuffer, piPos, pNode->pszValue, lstrlen(pNode->pszValue), bResize, pdwLastError); + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT("\""), 1, bResize, pdwLastError); + + break; + } + + if (pNode->pNext) + { + pNode = pNode->pNext; + + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT(","), 1, bResize, pdwLastError); + + if (iIndent) + { + MyStrCpy(ppszBuffer, pcchBuffer, piPos, TEXT("\r\n"), 2, bResize, pdwLastError); + MyStrCpyEx(ppszBuffer, pcchBuffer, piPos, TEXT(JSON_INDENT_CHAR), 1, iIndent - 1, bResize, pdwLastError); + } + + SerializeToBuffer(ppszBuffer, pcchBuffer, pNode, piPos, iIndent, bResize, pdwLastError); + } +} + +static BOOL AddRoot(struct JSON_NODE **ppNode) +{ + struct JSON_NODE *pRoot = JSON_Create(); + if (pRoot) + { + pRoot->eType = JNT_NODE; + pRoot->pValue = *ppNode; + *ppNode = pRoot; + return TRUE; + } + + return FALSE; +} + +static TCHAR i2a(TCHAR code) +{ + return hex[code & 15]; +} + +static int EscapePostData(PTCHAR *ppszPostData, int *pcchPostData, DWORD *pdwLastError) +{ + int cchLength = 0; + if (ppszPostData && *ppszPostData) + { + PTCHAR pszBuffer = (PTCHAR)GlobalAlloc(GPTR, (lstrlen(*ppszPostData) * 3 + 1) * sizeof(TCHAR)); + + if (pszBuffer) + { + PTCHAR pszBufferPtr = pszBuffer, pszPostDataPtr = *ppszPostData; + int iPos = 0; + + while (*pszPostDataPtr) + { + if (IsCharAlphaNumeric(*pszPostDataPtr) || *pszPostDataPtr == TEXT('-') || *pszPostDataPtr == TEXT('_') || *pszPostDataPtr == TEXT('.') || *pszPostDataPtr == TEXT('~')) + { + *pszBufferPtr++ = *pszPostDataPtr; + } + else if (*pszPostDataPtr == TEXT(' ')) + { + *pszBufferPtr++ = TEXT('+'); + } + else + { + *pszBufferPtr++ = TEXT('%'); + *pszBufferPtr++ = i2a(*pszPostDataPtr >> 4); + *pszBufferPtr++ = i2a(*pszPostDataPtr & 15); + } + + pszPostDataPtr++; + } + + if ((cchLength = lstrlen(pszBuffer)) > 0) + MyStrCpy(ppszPostData, pcchPostData, &iPos, pszBuffer, cchLength, TRUE, pdwLastError); + + GlobalFree(pszBuffer); + } + } + + return cchLength; +} + +BOOL JSON_Serialize(struct JSON_NODE *pNode, PTCHAR pszBuffer, int cchBuffer, BOOL bIsFile, BOOL bAsUnicode, BOOL bFormat) +{ + DWORD dwLastError = 0; + + BOOL bAddRoot = pNode && pNode->pszKey && *pNode->pszKey; + if (bAddRoot && !AddRoot(&pNode)) + bAddRoot = FALSE; + + if (bIsFile) + { + HANDLE hFile = CreateFile(pszBuffer, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + SerializeToFile(hFile, pNode, bFormat ? JSON_INDENT : 0, bAsUnicode); + CloseHandle(hFile); + } + else + { + dwLastError = GetLastError(); + } + } + else + { + int iPos = 0; + SerializeToBuffer(&pszBuffer, &cchBuffer, pNode, &iPos, bFormat ? JSON_INDENT : 0, FALSE, &dwLastError); + } + + if (bAddRoot) + GlobalFree(pNode); + + SetLastError(dwLastError); + return dwLastError == 0; +} + +PTCHAR JSON_SerializeAlloc(struct JSON_NODE *pNode, BOOL bFormat, BOOL bAsPostData) +{ + DWORD dwLastError = 0; + int cchBuffer = 2048; + PTCHAR pszBuffer = (PTCHAR)GlobalAlloc(GPTR, cchBuffer * sizeof(TCHAR)); + + if (pszBuffer) + { + BOOL bAddRoot = pNode && pNode->pszKey && *pNode->pszKey; + if (bAddRoot && !AddRoot(&pNode)) + bAddRoot = FALSE; + + if (bAsPostData) + { + struct JSON_NODE *pNext = pNode->pValue; + int iPos = 0; + int cchValueBuffer = 2048; + PTCHAR pszValueBuffer = (PTCHAR)GlobalAlloc(GPTR, cchValueBuffer * sizeof(TCHAR)); + if (pszValueBuffer) + { + while (pNext) + { + switch (pNext->eType) + { + case JNT_ARRAY: + { + struct JSON_NODE *pArrayNext = pNext->pValue; + while (pArrayNext) + { + int iValuePos = 0; + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, pNext->pszKey, lstrlen(pNext->pszKey), TRUE, &dwLastError); + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, TEXT("[]="), 3, TRUE, &dwLastError); + + switch (pArrayNext->eType) + { + case JNT_NODE: + case JNT_ARRAY: + SerializeToBuffer(&pszValueBuffer, &cchValueBuffer, pArrayNext, &iValuePos, 0, TRUE, &dwLastError); + break; + default: + MyStrCpy(&pszValueBuffer, &cchValueBuffer, &iValuePos, pArrayNext->pszValue, lstrlen(pArrayNext->pszValue), TRUE, &dwLastError); + break; + } + + iValuePos = EscapePostData(&pszValueBuffer, &cchValueBuffer, &dwLastError); + if (iValuePos > 0) + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, pszValueBuffer, iValuePos, TRUE, &dwLastError); + + pArrayNext = pArrayNext->pNext; + if (pArrayNext) + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, TEXT("&"), 1, TRUE, &dwLastError); + } + } + break; + + default: + { + int iValuePos = 0; + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, pNext->pszKey, lstrlen(pNext->pszKey), TRUE, &dwLastError); + + if (pNext->eType != JNT_VALUE || pNext->eType == JNT_VALUE && lstrcmpi(pNext->pszValue, TEXT("true")) != 0) + { + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, TEXT("="), 1, TRUE, &dwLastError); + + if (pNext->eType == JNT_NODE) + SerializeToBuffer(&pszValueBuffer, &cchValueBuffer, pNext->pValue, &iValuePos, bFormat ? JSON_INDENT : 0, TRUE, &dwLastError); + else + MyStrCpy(&pszValueBuffer, &cchValueBuffer, &iValuePos, pNext->pszValue, lstrlen(pNext->pszValue), TRUE, &dwLastError); + + iValuePos = EscapePostData(&pszValueBuffer, &cchValueBuffer, &dwLastError); + if (iValuePos > 0) + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, pszValueBuffer, iValuePos, TRUE, &dwLastError); + } + } + break; + } + + pNext = pNext->pNext; + if (pNext) + MyStrCpy(&pszBuffer, &cchBuffer, &iPos, TEXT("&"), 1, TRUE, &dwLastError); + } + + GlobalFree(pszValueBuffer); + } + } + else + { + int iPos = 0; + SerializeToBuffer(&pszBuffer, &cchBuffer, pNode, &iPos, bFormat ? JSON_INDENT : 0, TRUE, &dwLastError); + } + + if (bAddRoot) + GlobalFree(pNode); + } + + SetLastError(dwLastError); + return pszBuffer; +} + +PTCHAR JSON_Expand(struct JSON_NODE *pNode) +{ + PTCHAR pszExpanded = NULL; + int i, j, cch = lstrlen(pNode->pszValue); + TCHAR szUnicode[7]; + + pszExpanded = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * (cch + 1)); + if (pszExpanded) + { + for (i = 0, j = 0; i < cch; i++, j++) + { + if (pNode->pszValue[i] == TEXT('\\')) + { + switch (pNode->pszValue[i + 1]) + { + case TEXT('"'): + case TEXT('\\'): + case TEXT('/'): + pszExpanded[j] = pNode->pszValue[i + 1]; + i++; + break; + case TEXT('b'): + pszExpanded[j] = TEXT('\b'); + i++; + break; + case TEXT('f'): + pszExpanded[j] = TEXT('\f'); + i++; + break; + case TEXT('n'): + pszExpanded[j] = TEXT('\n'); + i++; + break; + case TEXT('r'): + pszExpanded[j] = TEXT('\r'); + i++; + break; + case TEXT('t'): + pszExpanded[j] = TEXT('\t'); + i++; + break; + case TEXT('u'): + wsprintf(szUnicode, TEXT("0x%c%c%c%c"), pNode->pszValue[i + 2], pNode->pszValue[i + 3], pNode->pszValue[i + 4], pNode->pszValue[i + 5]); +#ifdef UNICODE + pszExpanded[j] = (WCHAR)myatoi(szUnicode); +#else + wsprintfW((PWCHAR)szUnicode, L"%c", myatoi(szUnicode)); + if (WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, (PWCHAR)szUnicode, 1, NULL, 0, NULL, NULL) == 1) + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, (PWCHAR)szUnicode, 1, pszExpanded + j, 1, NULL, NULL); + else + lstrcpyA(pszExpanded + j, "?"); +#endif + i += 5; + break; + } + } + else + { + pszExpanded[j] = pNode->pszValue[i]; + } + } + } + + return pszExpanded; +} + +PTCHAR JSON_Escape(PTCHAR pszValue, enum JSON_ESCAPE_FLAGS eFlags) +{ + PTCHAR pszEscaped; + int i, cch = lstrlen(pszValue), cchNew = cch; + BOOL bAlreadyQuoted = cch > 1 && pszValue[0] == TEXT('"') && pszValue[cch - 1] == TEXT('"'); + BOOL bQuote = (eFlags & JEF_QUOTE) && ((eFlags & JEF_ALWAYS_QUOTE) || !bAlreadyQuoted); + if (bQuote) + cchNew += 2; + + for (i = 0; i < cch; i++) + { + switch (pszValue[i]) + { + case TEXT('\b'): + case TEXT('\f'): + case TEXT('\n'): + case TEXT('\r'): + case TEXT('\t'): + case TEXT('\\'): + case TEXT('"'): + cchNew++; + break; + default: + if ((eFlags & JEF_ESCAPE_UNICODE) && (pszValue[i] < 32 || pszValue[i] > 126)) + cchNew += 6; + break; + } + } + + pszEscaped = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * (cchNew + 1)); + + if (pszEscaped && cch < cchNew) + { + int j; + + if (bQuote) + { + pszEscaped[0] = TEXT('"'); + i = 0; + j = 1; + } + else if (bAlreadyQuoted) + { + pszEscaped[0] = TEXT('"'); + i = 1; + j = 1; + cch--; + } + else + { + i = 0; + j = 0; + } + + for (; i < cch; i++, j++) + { + switch (pszValue[i]) + { + case TEXT('\b'): + pszEscaped[j++] = TEXT('\\'); + pszEscaped[j] = TEXT('b'); + break; + case TEXT('\f'): + pszEscaped[j++] = TEXT('\\'); + pszEscaped[j] = TEXT('f'); + break; + case TEXT('\n'): + pszEscaped[j++] = TEXT('\\'); + pszEscaped[j] = TEXT('n'); + break; + case TEXT('\r'): + pszEscaped[j++] = TEXT('\\'); + pszEscaped[j] = TEXT('r'); + break; + case TEXT('\t'): + pszEscaped[j++] = TEXT('\\'); + pszEscaped[j] = TEXT('t'); + break; + case TEXT('\\'): + case TEXT('"'): + pszEscaped[j++] = TEXT('\\'); + pszEscaped[j] = pszValue[i]; + break; + default: + if ((eFlags & JEF_ESCAPE_UNICODE) && (pszValue[i] < 32 || pszValue[i] > 126)) + { + j += wsprintf(pszEscaped + j, TEXT("\\u%04x"), (unsigned char)pszValue[i]) - 1; + } + else + { + pszEscaped[j] = pszValue[i]; + } + break; + } + } + + if (bQuote || bAlreadyQuoted) + { + pszEscaped[j] = TEXT('"'); + } + } + else + { + lstrcpy(pszEscaped, pszValue); + } + + return pszEscaped; +} + +PCHAR JSON_FromUnicode(PWCHAR pwszText, int *pcchText, UINT nCodePage) +{ + int cbConverted = WideCharToMultiByte(nCodePage, WC_COMPOSITECHECK, pwszText, *pcchText, NULL, 0, NULL, NULL); + if (cbConverted > 0) + { + PCHAR pszBuffer = (PCHAR)GlobalAlloc(GPTR, sizeof(CHAR) * (cbConverted + 1)); + if (pszBuffer) + { + if (WideCharToMultiByte(nCodePage, WC_COMPOSITECHECK, pwszText, *pcchText, pszBuffer, cbConverted, NULL, NULL) > 0) + { + *pcchText = cbConverted; + return pszBuffer; + } + + GlobalFree(pszBuffer); + } + } + + return NULL; +} + +PWCHAR JSON_ToUnicode(PCHAR pszText, int *pcbText) +{ + int cchConverted = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszText, *pcbText, NULL, 0); + if (cchConverted > 0) + { + PWCHAR pwszBuffer = (PWCHAR)GlobalAlloc(GPTR, sizeof(WCHAR) * (cchConverted + 1)); + if (pwszBuffer) + { + if (MultiByteToWideChar(CP_ACP, MB_COMPOSITE, pszText, *pcbText, pwszBuffer, cchConverted) > 0) + { + *pcbText = cchConverted; + return pwszBuffer; + } + + GlobalFree(pwszBuffer); + } + } + + return NULL; +} + +static BOOL _Sort(enum JSON_SORT_FLAGS eFlags, struct JSON_NODE *pCurrent, struct JSON_NODE *pNext) +{ + BOOL bMove = FALSE; + + if (eFlags & JSF_BY_KEYS) + { + if (pNext->eType == JNT_NODE) + { + if (eFlags & JSF_RECURSIVE) + JSON_Sort(pNext, eFlags); + } + + if (eFlags & JSF_NUMERIC) + { + if (myatoi(pCurrent->pszKey) > myatoi(pNext->pszKey)) + bMove = TRUE; + } + else if (eFlags & JSF_CASE_SENSITIVE) + { + if (lstrcmp(pCurrent->pszKey, pNext->pszKey) > 0) + bMove = TRUE; + } + else + { + if (lstrcmpi(pCurrent->pszKey, pNext->pszKey) > 0) + bMove = TRUE; + } + } + else + { + if (pNext->eType == JNT_ARRAY || pNext->eType == JNT_NODE) + { + if (eFlags & JSF_RECURSIVE) + JSON_Sort(pNext, eFlags); + return FALSE; + } + + if (pCurrent->eType == JNT_ARRAY || pCurrent->eType == JNT_NODE) + { + return FALSE; + } + + if (eFlags & JSF_NUMERIC) + { + if (myatoi(pCurrent->pszValue) > myatoi(pNext->pszValue)) + bMove = TRUE; + } + else if (eFlags & JSF_CASE_SENSITIVE) + { + if (lstrcmp(pCurrent->pszValue, pNext->pszValue) > 0) + bMove = TRUE; + } + else + { + if (lstrcmpi(pCurrent->pszValue, pNext->pszValue) > 0) + bMove = TRUE; + } + } + + if (eFlags & JSF_DESCENDING) + bMove = !bMove; + + return bMove; +} + +void JSON_Sort(struct JSON_NODE *pNode, enum JSON_SORT_FLAGS eFlags) +{ + if (pNode->eType != JNT_NODE && pNode->eType != JNT_ARRAY) + return; + + while (TRUE) + { + BOOL bSwapped = FALSE; + struct JSON_NODE **ppLink = &pNode->pValue; + struct JSON_NODE *pCurrent = pNode->pValue; + struct JSON_NODE *pNext; + + while (pNext = pCurrent->pNext) + { + if (_Sort(eFlags, pCurrent, pNext)) + { + pCurrent->pNext = pNext->pNext; + pNext->pNext = pCurrent; + *ppLink = pCurrent = pNext; + bSwapped |= TRUE; + } + + ppLink = &pCurrent->pNext; + pCurrent = pNext; + } + + if (!bSwapped) + break; + } +} \ No newline at end of file diff --git a/src/JSON.h b/src/JSON.h new file mode 100644 index 0000000..68ffd9d --- /dev/null +++ b/src/JSON.h @@ -0,0 +1,86 @@ +#ifndef __JSON_H__ +#define __JSON_H__ + +#define JSON_INDENT 1 +#define JSON_INDENT_CHAR "\t" + +enum JSON_WORD_TYPE +{ + JWT_STRING, + JWT_OTHER, + JWT_NONE +}; + +enum JSON_NODE_TYPE +{ + // node = key: [node] + JNT_NODE, + // node = key: [ array ] + JNT_ARRAY, + // node = key: value + JNT_VALUE, + // node = key: "value" + JNT_QUOTED_VALUE +}; + +enum JSON_SET_FLAGS +{ + JSF_NONE = 0, + JSF_IS_FILE = 1, + JSF_IS_UNICODE = 2, + JSF_IS_RAW = 4 +}; + +enum JSON_ESCAPE_FLAGS +{ + JEF_NONE = 0, + JEF_ESCAPE_UNICODE = 1, + JEF_QUOTE = 2, + JEF_ALWAYS_QUOTE = 4 +}; + +struct JSON_NODE +{ + enum JSON_NODE_TYPE eType; + struct JSON_NODE* pNext; + PTCHAR pszKey; + union + { + PTCHAR pszValue; + struct JSON_NODE* pValue; + }; +}; + +enum JSON_SORT_FLAGS +{ + // Sort descending instead of ascending. + JSF_DESCENDING = 1, + // Numeric comparison rather than string. + JSF_NUMERIC = 2, + // Sort case sensitive. + JSF_CASE_SENSITIVE = 4, + // Sort by keys rather than by values. + JSF_BY_KEYS = 8, + // Sort recursively. + JSF_RECURSIVE = 16 +}; + +struct JSON_NODE* JSON_Create(); +BOOL JSON_IsTrue(struct JSON_NODE* pNode); +PTCHAR JSON_GetQuotedValue(struct JSON_NODE* pNode, const PTCHAR pszDefaultValue); +void JSON_Delete(struct JSON_NODE** ppNode, struct JSON_NODE* pPrev); +int JSON_Count(struct JSON_NODE* pNode); +struct JSON_NODE* JSON_Get(struct JSON_NODE* pNode, PTCHAR pszKey, BOOL bKeyIsIndex); +struct JSON_NODE* JSON_GetEx(struct JSON_NODE* pNode, PTCHAR pszKey, BOOL bKeyIsIndex, BOOL bCreate, BOOL* pbCreated); +struct JSON_NODE* JSON_Next(struct JSON_NODE** ppNode, PTCHAR pszKey, BOOL bKeyIsIndex, BOOL bCreate, BOOL* pbCreated); +BOOL JSON_Set(struct JSON_NODE* pNode, PBYTE pbValue, enum JSON_SET_FLAGS eFlags); +BOOL JSON_SetEx(struct JSON_NODE* pNode, PTCHAR pszKey, BOOL bKeyIsIndex, PBYTE pbValue, enum JSON_SET_FLAGS eFlags); +BOOL JSON_Serialize(struct JSON_NODE* pNode, PTCHAR pszBuffer, int cchBuffer, BOOL bIsFile, BOOL bAsUnicode, BOOL bFormat); +PTCHAR JSON_SerializeAlloc(struct JSON_NODE* pNode, BOOL bFormat, BOOL bAsPostData); +PTCHAR JSON_Expand(struct JSON_NODE* pNode); +PTCHAR JSON_Escape(PTCHAR pszValue, enum JSON_ESCAPE_FLAGS eFlags); +PCHAR JSON_FromUnicode(PWCHAR pwszText, int* pcchText, UINT nCodePage); +PWCHAR JSON_ToUnicode(PCHAR pszText, int* pcbText); +void JSON_Sort(struct JSON_NODE* pNode, enum JSON_SORT_FLAGS eFlags); + +#endif \ No newline at end of file diff --git a/src/LinkedList.c b/src/LinkedList.c new file mode 100644 index 0000000..29cd1d8 --- /dev/null +++ b/src/LinkedList.c @@ -0,0 +1,90 @@ +#include +#include "LinkedList.h" + +struct LinkedList* LinkedListCreate() +{ + return (struct LinkedList*)GlobalAlloc(GPTR, sizeof(struct LinkedList)); +} + +void LinkedListDestroy(struct LinkedList** ppList, LinkedListDestroyCallback callback) +{ + if (ppList && *ppList) + { + struct LinkedListNode* pNode = (*ppList)->First; + + while (pNode) + { + struct LinkedListNode* pNext = pNode->Next; + if (callback) + callback(pNode); + GlobalFree(pNode->Key); + GlobalFree(pNode); + pNode = pNext; + } + + GlobalFree(*ppList); + *ppList = NULL; + } +} + +void LinkedListDelete(struct LinkedList** ppList, const PTCHAR szKey) +{ + if (ppList && *ppList) + { + struct LinkedListNode* pNode = (*ppList)->First; + struct LinkedListNode* pPrev = NULL; + PTCHAR pszKey = szKey ? szKey : TEXT(""); + + while (pNode) + { + struct LinkedListNode* pNext = pNode->Next; + + if (lstrcmpi(pNode->Key, pszKey) == 0) + { + if (pPrev) + pPrev->Next = pNext; + + if ((*ppList)->First == pNode) + { + GlobalFree(*ppList); + *ppList = NULL; + } + + GlobalFree(pNode->Key); + GlobalFree(pNode); + break; + } + + pPrev = pNode; + pNode = pNext; + } + } +} + +struct LinkedListNode* LinkedListGet(struct LinkedList* pList, const PTCHAR szKey, const BOOL bCreate) +{ + if (pList) + { + struct LinkedListNode* pNode = pList->First; + PTCHAR pszKey = szKey ? szKey : TEXT(""); + + while (pNode) + { + if (lstrcmpi(pNode->Key, pszKey) == 0) + return pNode; + pNode = pNode->Next; + } + + if (bCreate) + { + pNode = (struct LinkedListNode*)GlobalAlloc(GPTR, sizeof(struct LinkedListNode)); + pNode->Key = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * (lstrlen(pszKey) + 1)); + lstrcpy(pNode->Key, pszKey); + pNode->Next = pList->First; + pList->First = pNode; + return pNode; + } + } + + return NULL; +} \ No newline at end of file diff --git a/src/LinkedList.h b/src/LinkedList.h new file mode 100644 index 0000000..06a1ece --- /dev/null +++ b/src/LinkedList.h @@ -0,0 +1,26 @@ +#ifndef __LinkedList_H__ +#define __LinkedList_H__ + +struct LinkedListNode +{ + PTCHAR Key; + PVOID Value; + struct LinkedListNode* Next; +}; + +struct LinkedList +{ + struct LinkedListNode* First; +}; + +struct LinkedList* LinkedListCreate(); + +typedef void (*LinkedListDestroyCallback)(struct LinkedListNode* pListNode); + +void LinkedListDestroy(struct LinkedList** ppList, LinkedListDestroyCallback callback); + +void LinkedListDelete(struct LinkedList** ppList, const PTCHAR szKey); + +struct LinkedListNode* LinkedListGet(struct LinkedList* pList, const PTCHAR szKey, const BOOL bCreate); + +#endif \ No newline at end of file diff --git a/src/api.h b/src/api.h new file mode 100644 index 0000000..85d41c4 --- /dev/null +++ b/src/api.h @@ -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_ */ diff --git a/src/nsJSON.c b/src/nsJSON.c new file mode 100644 index 0000000..f3d2721 --- /dev/null +++ b/src/nsJSON.c @@ -0,0 +1,1474 @@ +/* + nsJSON NSIS plug-in by Stuart Welch + v1.1.1.1 - 10th October 2024 +*/ + +#include +#include +#include "LinkedList.h" +#include "nsJSON.h" +#include "JSON.h" +#include "pluginapi.h" + +HANDLE g_hInstance; +struct LinkedList* g_pList = NULL; + +struct THREAD_PARAM +{ + struct JSON_NODE* pNode; + struct JSON_NODE* pRootNode; +}; + +static void NodeDelete(struct LinkedListNode* pListNode) +{ + JSON_Delete(&((struct JSON_NODE*)pListNode->Value), NULL); + pListNode->Value = NULL; +} + +static UINT_PTR PluginCallback(enum NSPIM msg) +{ + if (msg == NSPIM_UNLOAD) + { + LinkedListDestroy(&g_pList, NodeDelete); + } + return 0; +} + +BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + g_hInstance = hInst; + return TRUE; +} + +static struct JSON_NODE* GetTreeNode(PTCHAR pszTree, BOOL bCreate) +{ + struct LinkedListNode* pListNode; + + if (!g_pList) + g_pList = LinkedListCreate(); + + pListNode = LinkedListGet(g_pList, pszTree, bCreate); + + if (bCreate && pListNode && !pListNode->Value) + pListNode->Value = JSON_Create(); + + return pListNode ? (struct JSON_NODE*)pListNode->Value : NULL; +} + +static struct JSON_NODE* PopTreeNode(PTCHAR pszArg, BOOL bCreate, BOOL* pbCreated) +{ + struct LinkedListNode* pListNode = NULL; + + if (!g_pList) + g_pList = LinkedListCreate(); + + if (pbCreated) + *pbCreated = FALSE; + + if (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/tree")) == 0) + { + if (popstring(pszArg) == 0) + pListNode = LinkedListGet(g_pList, pszArg, bCreate); + } + else + { + pushstring(pszArg); + lstrcpy(pszArg, TEXT("")); + } + } + + if (!pListNode) + pListNode = LinkedListGet(g_pList, NULL, bCreate); + + if (bCreate && pListNode && !pListNode->Value) + { + pListNode->Value = JSON_Create(); + if (pbCreated) + *pbCreated = TRUE; + } + + return pListNode ? (struct JSON_NODE*)pListNode->Value : NULL; +} + +enum HttpWebRequestDataType +{ + WRDT_Default = 0, + WRDT_JSON = 1, + WRDT_Raw = 2, +}; + +static enum HttpWebRequestDataType GetHttpWebRequestDataType(struct JSON_NODE* pRootNode) +{ + PTCHAR pszDataType = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("DataType"), FALSE), TEXT("")); + if (lstrcmpi(pszDataType, TEXT("JSON")) == 0) + return WRDT_JSON; + if (lstrcmpi(pszDataType, TEXT("Raw")) == 0) + return WRDT_Raw; + return WRDT_Default; +} + +#define _trim(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '.') + +static void GetLastErrorMessage(PTCHAR pszErrorMessage, const PTCHAR szWin32Func, DWORD dwLastError) +{ + PTCHAR pszError; + int cchLastError; + + if (dwLastError >= 12001 && dwLastError <= 12156) + cchLastError = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandleA("wininet.dll"), dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszError, 0, NULL); + else + cchLastError = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszError, 0, NULL); + + if (cchLastError > 0) + { + int cchWin32Func = lstrlen(szWin32Func); + lstrcpy(pszErrorMessage, szWin32Func); + lstrcpy(pszErrorMessage + cchWin32Func, TEXT(": ")); + + for (cchLastError--; cchLastError >= 0 && _trim(pszError[cchLastError]); cchLastError--) + pszError[cchLastError] = '\0'; + lstrcpy(pszErrorMessage + cchWin32Func + 2, pszError); + + wsprintf(pszErrorMessage + cchWin32Func + 2 + cchLastError, TEXT(" (%lu)"), dwLastError); + } + + if (pszError) + LocalFree(pszError); +} + +static void SetLastErrorNode(struct JSON_NODE* pNode, const PTCHAR szWin32Func, DWORD dwLastError) +{ + PTCHAR pszError = (PTCHAR)GlobalAlloc(GPTR, 16 * sizeof(TCHAR)); + int cchLastError; + + if (pszError) + { + wsprintf(pszError, TEXT("%lu"), dwLastError); + JSON_SetEx(pNode, TEXT("ErrorCode"), FALSE, (PBYTE)pszError, JSF_NONE); + GlobalFree(pszError); + } + + if (dwLastError >= 12001 && dwLastError <= 12156) + cchLastError = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandleA("wininet.dll"), dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszError, 0, NULL); + else + cchLastError = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszError, 0, NULL); + + if (cchLastError > 0) + { + int cchWin32Func = lstrlen(szWin32Func); + PTCHAR pszErrorMessage = (PTCHAR)GlobalAlloc(GPTR, (cchLastError + cchWin32Func + 3) * sizeof(TCHAR)); + if (pszErrorMessage) + { + lstrcpy(pszErrorMessage, szWin32Func); + lstrcpy(pszErrorMessage + cchWin32Func, TEXT(": ")); + + for (cchLastError--; cchLastError >= 0 && _trim(pszError[cchLastError]); cchLastError--) + pszError[cchLastError] = '\0'; + lstrcpy(pszErrorMessage + cchWin32Func + 2, pszError); +#ifdef UNICODE + JSON_SetEx(pNode, TEXT("ErrorMessage"), FALSE, (PBYTE)pszErrorMessage, JSF_IS_RAW | JSF_IS_UNICODE); +#else + JSON_SetEx(pNode, TEXT("ErrorMessage"), FALSE, (PBYTE)pszErrorMessage, JSF_IS_RAW); +#endif + GlobalFree(pszErrorMessage); + } + } + + if (pszError) + LocalFree(pszError); +} + +static void DoHttpWebRequest(struct JSON_NODE* pNode, struct JSON_NODE* pRootNode) +{ + static PTCHAR accept[2] = { TEXT("*/*"), NULL }; + + PTCHAR pszUrl = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("Url"), FALSE), NULL); + if (pszUrl) + { + PTCHAR pszAgentNode = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("Agent"), FALSE), TEXT("nsJSON NSIS plug-in/1.0.x.x")), + pszAccessType = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("AccessType"), FALSE), NULL), + pszProxyName = NULL, + pszProxyBypass = NULL; + struct JSON_NODE* pProxyNode = JSON_Get(pRootNode, TEXT("Proxy"), FALSE); + BOOL bSuccess = FALSE; + + DWORD dwAccessType = INTERNET_OPEN_TYPE_DIRECT; + if (pszAccessType) + { + if (lstrcmpi(pszAccessType, TEXT("PreConfig")) == 0) + { + dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG; + } + else if (lstrcmpi(pszAccessType, TEXT("Proxy")) == 0) + { + if (pProxyNode) + { + pszProxyName = JSON_GetQuotedValue(JSON_Get(pProxyNode, TEXT("Server"), FALSE), NULL); + if (pszProxyName) + { + dwAccessType = INTERNET_OPEN_TYPE_PROXY; + pszProxyBypass = JSON_GetQuotedValue(JSON_Get(pProxyNode, TEXT("Bypass"), FALSE), NULL); + } + } + } + } + + HINTERNET hSession = InternetOpen(pszAgentNode, INTERNET_OPEN_TYPE_DIRECT, pszProxyName, pszProxyBypass, 0); + if (hSession != NULL) + { + LPURL_COMPONENTS pstUrlComp = (LPURL_COMPONENTS)GlobalAlloc(GPTR, sizeof(URL_COMPONENTS)); + if (pstUrlComp != NULL) + { + struct JSON_NODE* pParamsNode = JSON_Get(pRootNode, TEXT("Params"), FALSE); + BOOL bRawParams = pParamsNode && lstrcmpi(JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("ParamsType"), FALSE), TEXT("")), TEXT("Raw")) == 0; + PTCHAR pszParams = pParamsNode ? (bRawParams ? JSON_GetQuotedValue(pParamsNode, TEXT("")) : JSON_SerializeAlloc(pParamsNode->pValue, FALSE, TRUE)) : NULL; + + if (pParamsNode && !pszParams) + { + SetLastErrorNode(pNode, TEXT("JSON_SerializeAlloc"), GetLastError()); + } + else + { + pstUrlComp->dwStructSize = sizeof(URL_COMPONENTS); + pstUrlComp->dwUrlPathLength = pstUrlComp->dwHostNameLength = lstrlen(pszUrl) + 1; + + if (pParamsNode && pszParams && *pszParams) + pstUrlComp->dwUrlPathLength += lstrlen(pszParams) + 1; + + pstUrlComp->lpszUrlPath = (PTCHAR)GlobalAlloc(GPTR, pstUrlComp->dwUrlPathLength * sizeof(TCHAR)); + if (pstUrlComp->lpszUrlPath) + { + pstUrlComp->lpszHostName = (PTCHAR)GlobalAlloc(GPTR, pstUrlComp->dwHostNameLength * sizeof(TCHAR)); + if (pstUrlComp->lpszHostName) + { + if (InternetCrackUrl(pszUrl, 0, 0, pstUrlComp)) + { + HINTERNET hConnect = InternetConnect(hSession, pstUrlComp->lpszHostName, pstUrlComp->nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + if (hConnect) + { + PTCHAR pszVerb = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("Verb"), FALSE), TEXT("GET")); + PTCHAR pszValue; + HINTERNET hRequest; + BOOL bEncoding; + + if (pParamsNode && pszParams && *pszParams) + { + lstrcpy(pstUrlComp->lpszUrlPath + pstUrlComp->dwUrlPathLength, TEXT("?")); + lstrcpy(pstUrlComp->lpszUrlPath + pstUrlComp->dwUrlPathLength + 1, pszParams); + } + + if (pProxyNode) + { + pszValue = JSON_GetQuotedValue(JSON_Get(pProxyNode, TEXT("Username"), FALSE), NULL); + if (pszValue) + InternetSetOption(hConnect, INTERNET_OPTION_PROXY_USERNAME, pszValue, lstrlen(pszValue)); + + pszValue = JSON_GetQuotedValue(JSON_Get(pProxyNode, TEXT("Password"), FALSE), NULL); + if (pszValue) + InternetSetOption(hConnect, INTERNET_OPTION_PROXY_PASSWORD, pszValue, lstrlen(pszValue)); + } + + if (bEncoding = JSON_IsTrue(JSON_Get(pRootNode, TEXT("Decoding"), FALSE))) + { + InternetSetOption(hConnect, INTERNET_OPTION_HTTP_DECODING, &bEncoding, sizeof(bEncoding)); + } + + pszValue = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("Username"), FALSE), NULL); + if (pszValue) + { + InternetSetOption(hConnect, INTERNET_OPTION_USERNAME, pszValue, lstrlen(pszValue)); + } + + pszValue = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("Password"), FALSE), NULL); + if (pszValue) + { + InternetSetOption(hConnect, INTERNET_OPTION_PASSWORD, pszValue, lstrlen(pszValue)); + } + + pszValue = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("ConnectTimeout"), FALSE), NULL); + if (pszValue) + { + ULONG ulValue = myatou(pszValue); + InternetSetOption(hConnect, INTERNET_OPTION_CONNECT_TIMEOUT, &ulValue, sizeof(ulValue)); + } + + pszValue = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("SendTimeout"), FALSE), NULL); + if (pszValue) + { + ULONG ulValue = myatou(pszValue); + InternetSetOption(hConnect, INTERNET_OPTION_SEND_TIMEOUT, &ulValue, sizeof(ulValue)); + } + + pszValue = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("ReceiveTimeout"), FALSE), NULL); + if (pszValue) + { + ULONG ulValue = myatou(pszValue); + InternetSetOption(hConnect, INTERNET_OPTION_RECEIVE_TIMEOUT, &ulValue, sizeof(ulValue)); + } + + hRequest = HttpOpenRequest(hConnect, pszVerb, pstUrlComp->lpszUrlPath, NULL, NULL, accept, + INTERNET_FLAG_NO_CACHE_WRITE | + INTERNET_FLAG_NO_COOKIES | + INTERNET_FLAG_NO_UI | + INTERNET_FLAG_RELOAD | + INTERNET_FLAG_KEEP_CONNECTION | + (pstUrlComp->nScheme == INTERNET_SCHEME_HTTPS ? + INTERNET_FLAG_SECURE | + INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | + INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS : 0), 0); + if (hRequest) + { + struct JSON_NODE* pDataNode = JSON_Get(pRootNode, TEXT("Data"), FALSE); + PTCHAR pszDataEncoding = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("DataEncoding"), FALSE), NULL); + enum HttpWebRequestDataType eDataType = GetHttpWebRequestDataType(pRootNode); + struct JSON_NODE* pHeadersNode = JSON_Get(pRootNode, TEXT("Headers"), FALSE); + BOOL bRequestSent = FALSE; + + if (pHeadersNode) + { + if (pHeadersNode->eType == JNT_QUOTED_VALUE) + { + HttpAddRequestHeaders(hRequest, pHeadersNode->pszValue, -1L, 0); + } + + if (bEncoding) + { + HttpAddRequestHeaders(hRequest, TEXT("Accept-Encoding: gzip,deflate\r\n"), -1L, HTTP_ADDREQ_FLAG_ADD); + } + + if (lstrcmpi(pszVerb, TEXT("POST")) == 0) + { + if (eDataType == WRDT_JSON) + { + HttpAddRequestHeaders(hRequest, TEXT("Content-Type: application/json\r\n"), -1L, HTTP_ADDREQ_FLAG_ADD); + } + else + { + HttpAddRequestHeaders(hRequest, TEXT("Content-Type: application/x-www-form-urlencoded\r\n"), -1L, HTTP_ADDREQ_FLAG_ADD); + } + } + + if (pHeadersNode->eType == JNT_NODE) + { + pHeadersNode = pHeadersNode->pValue; + while (pHeadersNode) + { + int cchHeader = lstrlen(pHeadersNode->pszKey) + lstrlen(pHeadersNode->pszValue) + 4; + PTCHAR pszHeader = (PTCHAR)GlobalAlloc(GPTR, (cchHeader + 1) * sizeof(TCHAR)); + if (pszHeader) + { + lstrcpy(pszHeader, pHeadersNode->pszKey); + lstrcat(pszHeader, TEXT(": ")); + lstrcat(pszHeader, pHeadersNode->pszValue); + lstrcat(pszHeader, TEXT("\r\n")); + HttpAddRequestHeaders(hRequest, pszHeader, cchHeader, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); + GlobalFree(pszHeader); + } + + pHeadersNode = pHeadersNode->pNext; + } + } + } + + if (pDataNode) + { + PTCHAR pszBuffer; + + if (eDataType == WRDT_JSON || eDataType == WRDT_Default) + { + pszBuffer = JSON_SerializeAlloc(pDataNode->pValue, FALSE, eDataType == WRDT_Default); + if (!pszBuffer) + { + SetLastErrorNode(pNode, TEXT("JSON_SerializeAlloc"), GetLastError()); + } + } + else + { + pszBuffer = JSON_GetQuotedValue(pDataNode, TEXT("")); + } + + if (pszBuffer) + { +#ifdef UNICODE + if (pszDataEncoding && lstrcmpi(pszDataEncoding, TEXT("Unicode")) == 0) + { + bRequestSent = HttpSendRequest(hRequest, NULL, 0, pszBuffer, lstrlen(pszBuffer) * sizeof(TCHAR)); + if (!bRequestSent) + SetLastErrorNode(pNode, TEXT("HttpSendRequest"), GetLastError()); + } + else + { + int cchConverted = lstrlen(pszBuffer); + PCHAR pszConverted = JSON_FromUnicode(pszBuffer, &cchConverted, CP_ACP); + if (pszConverted) + { + bRequestSent = HttpSendRequest(hRequest, NULL, 0, pszConverted, cchConverted); + if (!bRequestSent) + SetLastErrorNode(pNode, TEXT("HttpSendRequest"), GetLastError()); + GlobalFree(pszConverted); + } + } +#else + if (pszDataEncoding && lstrcmpi(pszDataEncoding, TEXT("Unicode")) == 0) + { + int cbUnicode = lstrlen(pszBuffer); + PWCHAR pszUnicode = JSON_ToUnicode(pszBuffer, &cbUnicode); + if (pszUnicode) + { + bRequestSent = HttpSendRequest(hRequest, NULL, 0, pszUnicode, cbUnicode); + if (!bRequestSent) + SetLastErrorNode(pNode, TEXT("HttpSendRequest"), GetLastError()); + GlobalFree(pszUnicode); + } + } + else + { + bRequestSent = HttpSendRequest(hRequest, NULL, 0, pszBuffer, lstrlen(pszBuffer) * sizeof(TCHAR)); + if (!bRequestSent) + SetLastErrorNode(pNode, TEXT("HttpSendRequest"), GetLastError()); + } +#endif + + GlobalFree(pszBuffer); + } + } + else + { + bRequestSent = HttpSendRequest(hRequest, NULL, 0, NULL, 0); + if (!bRequestSent) + SetLastErrorNode(pNode, TEXT("HttpSendRequest"), GetLastError()); + } + + if (bRequestSent) + { + DWORD dwSize; + PTCHAR pszStatusCode = NULL; + + if (InternetQueryDataAvailable(hRequest, &dwSize, 0, 0)) + { + PBYTE pbResponseBuffer = (PBYTE)GlobalAlloc(GPTR, dwSize + 1); + if (pbResponseBuffer) + { + DWORD dwBytesRead = 0; + if (InternetReadFile(hRequest, pbResponseBuffer, dwSize, &dwBytesRead)) + { + JSON_SetEx(pNode, TEXT("Output"), FALSE, pbResponseBuffer, + (JSON_IsTrue(JSON_Get(pRootNode, TEXT("UnicodeOutput"), FALSE)) ? JSF_IS_UNICODE : 0) | + (JSON_IsTrue(JSON_Get(pRootNode, TEXT("RawOutput"), FALSE)) ? JSF_IS_RAW : 0)); + } + else + { + SetLastErrorNode(pNode, TEXT("InternetReadFile"), GetLastError()); + } + + GlobalFree(pbResponseBuffer); + } + } + else + { + SetLastErrorNode(pNode, TEXT("InternetQueryDataAvailable"), GetLastError()); + } + + dwSize = 0; + if (!HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, pszStatusCode, &dwSize, NULL) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + pszStatusCode = GlobalAlloc(GPTR, (dwSize + 1) * sizeof(TCHAR)); + if (pszStatusCode) + { + if (HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, pszStatusCode, &dwSize, NULL)) + { +#ifdef UNICODE + JSON_SetEx(pNode, TEXT("StatusCode"), FALSE, (PBYTE)pszStatusCode, JSF_IS_UNICODE); +#else + JSON_SetEx(pNode, TEXT("StatusCode"), FALSE, (PBYTE)pszStatusCode, JSF_NONE); +#endif + } + + GlobalFree(pszStatusCode); + } + } + } + + InternetCloseHandle(hRequest); + } + else + { + SetLastErrorNode(pNode, TEXT("HttpOpenRequest"), GetLastError()); + } + + InternetCloseHandle(hConnect); + } + else + { + SetLastErrorNode(pNode, TEXT("InternetConnect"), GetLastError()); + } + } + else + { + SetLastErrorNode(pNode, TEXT("InternetCrackUrl"), GetLastError()); + } + + GlobalFree(pstUrlComp->lpszHostName); + } + + GlobalFree(pstUrlComp->lpszUrlPath); + } + + if (pParamsNode && pszParams && !bRawParams) + GlobalFree(pszParams); + } + + GlobalFree(pstUrlComp); + } + + InternetCloseHandle(hSession); + } + else + { + SetLastErrorNode(pNode, TEXT("InternetOpen"), GetLastError()); + } + } +} + +static DWORD WINAPI DoHttpWebRequestThreadProc(LPVOID lpParameter) +{ + struct THREAD_PARAM* ptp = (struct THREAD_PARAM*)lpParameter; + DoHttpWebRequest(ptp->pNode, ptp->pRootNode); + GlobalFree(ptp); + return 0; +} + +static void WriteInput(HANDLE hStdIn, struct JSON_NODE* pNode, BOOL bIsUnicode) +{ + DWORD dwBytesWritten; +#ifdef UNICODE + if (bIsUnicode) + { + WriteFile(hStdIn, pNode->pszValue, lstrlen(pNode->pszValue) * sizeof(TCHAR), &dwBytesWritten, NULL); + } + else + { + int cchValue = lstrlen(pNode->pszValue); + PCHAR pszConverted = JSON_FromUnicode(pNode->pszValue, &cchValue, CP_ACP); + if (pszConverted) + { + WriteFile(hStdIn, pszConverted, cchValue * sizeof(TCHAR), &dwBytesWritten, NULL); + GlobalFree(pszConverted); + } + } +#else + if (bIsUnicode) + { + int cchValue = lstrlenA(pNode->pszValue); + PWCHAR pwszConverted = JSON_ToUnicode(pNode->pszValue, &cchValue); + if (pwszConverted) + { + WriteFile(hStdIn, pNode->pszValue, lstrlen(pNode->pszValue) * sizeof(TCHAR), &dwBytesWritten, NULL); + GlobalFree(pwszConverted); + } + } + else + { + WriteFile(hStdIn, pNode->pszValue, lstrlen(pNode->pszValue) * sizeof(TCHAR), &dwBytesWritten, NULL); + } +#endif +} + +static void DoCreateProcess(struct JSON_NODE* pNode, struct JSON_NODE* pRootNode) +{ +#ifdef UNICODE + JSON_SetEx(pNode, TEXT("Output"), FALSE, (PBYTE)TEXT(""), JSF_IS_UNICODE); + JSON_SetEx(pNode, TEXT("ExitCode"), FALSE, (PBYTE)TEXT(""), JSF_IS_UNICODE); +#else + JSON_SetEx(pNode, TEXT("Output"), FALSE, (PBYTE)TEXT(""), JSF_NONE); + JSON_SetEx(pNode, TEXT("ExitCode"), FALSE, (PBYTE)TEXT(""), JSF_NONE); +#endif + + PTCHAR pszPath = JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("Path"), FALSE), NULL); + if (pszPath) + { + HANDLE hStdInRead = NULL, hStdInWrite = NULL, hStdOutRead = NULL, hStdOutWrite = NULL; + PBYTE pbOutputBuffer = (PBYTE)GlobalAlloc(GPTR, g_stringsize); + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (pbOutputBuffer) + { + if (CreatePipe(&hStdOutRead, &hStdOutWrite, &saAttr, 0) && + SetHandleInformation(hStdOutRead, HANDLE_FLAG_INHERIT, 0) && + CreatePipe(&hStdInRead, &hStdInWrite, &saAttr, 0) && + SetHandleInformation(hStdInWrite, HANDLE_FLAG_INHERIT, 0)) + { + PPROCESS_INFORMATION ppiProcInfo = (PPROCESS_INFORMATION)GlobalAlloc(GPTR, sizeof(PROCESS_INFORMATION)); + if (ppiProcInfo) + { + LPSTARTUPINFO psiStartInfo = (LPSTARTUPINFO)GlobalAlloc(GPTR, sizeof(STARTUPINFO)); + if (psiStartInfo) + { + struct JSON_NODE* pArgumentsNode = JSON_Get(pRootNode, TEXT("Arguments"), FALSE); + PTCHAR pszArguments = NULL; + + if (pArgumentsNode) + { + if (pArgumentsNode->eType == JNT_VALUE || pArgumentsNode->eType == JNT_QUOTED_VALUE) + { + pszArguments = pArgumentsNode->pszValue; + } + else if (pArgumentsNode->eType == JNT_ARRAY) + { + DWORD cchAlloc = 0; + struct JSON_NODE* pArrayNext = pArgumentsNode->pValue; + while (pArrayNext) + { + if (pArrayNext->eType == JNT_VALUE || pArrayNext->eType == JNT_QUOTED_VALUE) + cchAlloc += lstrlen(pArrayNext->pszValue) + 1; + pArrayNext = pArrayNext->pNext; + } + + if (cchAlloc > 0) + { + pszArguments = GlobalAlloc(GPTR, sizeof(TCHAR) * cchAlloc); + if (pszArguments) + { + DWORD dwPos = 0; + + pArrayNext = pArgumentsNode->pValue; + while (pArrayNext) + { + if (pArrayNext->eType == JNT_VALUE || pArrayNext->eType == JNT_QUOTED_VALUE) + { + lstrcpy(pszArguments + dwPos, pArrayNext->pszValue); + dwPos += lstrlen(pArrayNext->pszValue); + lstrcpy(pszArguments + dwPos, TEXT(" ")); + dwPos++; + } + pArrayNext = pArrayNext->pNext; + } + } + } + } + } + + psiStartInfo->cb = sizeof(STARTUPINFO); + psiStartInfo->hStdError = hStdOutWrite; + psiStartInfo->hStdOutput = hStdOutWrite; + psiStartInfo->hStdInput = hStdInRead; + psiStartInfo->dwFlags = STARTF_USESTDHANDLES; + + if (CreateProcess(pszPath, pszArguments, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, JSON_GetQuotedValue(JSON_Get(pRootNode, TEXT("WorkingDir"), FALSE), NULL), psiStartInfo, ppiProcInfo)) + { + struct JSON_NODE* pInputNode = JSON_Get(pRootNode, TEXT("Input"), FALSE); + DWORD dwBytesRead, dwTotalBytesRead = 0, dwValueBufferPos = 0; + PBYTE pbValueBuffer = NULL; + + CloseHandle(ppiProcInfo->hThread); + + if (pInputNode) + { + BOOL bIsUnicode = JSON_IsTrue(JSON_Get(pRootNode, TEXT("UnicodeInput"), FALSE)); + + if (pInputNode->eType == JNT_VALUE || pInputNode->eType == JNT_QUOTED_VALUE) + { + WriteInput(hStdInWrite, pInputNode, bIsUnicode); + } + else if (pInputNode->eType == JNT_ARRAY) + { + struct JSON_NODE* pArrayNext = pArgumentsNode->pValue; + while (pArrayNext) + { + if (pArrayNext->eType == JNT_VALUE || pArrayNext->eType == JNT_QUOTED_VALUE) + WriteInput(hStdInWrite, pArrayNext, bIsUnicode); + pArrayNext = pArrayNext->pNext; + } + } + } + + CloseHandle(hStdInWrite); + hStdInWrite = NULL; + CloseHandle(hStdOutWrite); + hStdOutWrite = NULL; + + while (ReadFile(hStdOutRead, pbOutputBuffer, g_stringsize, &dwBytesRead, NULL) && dwBytesRead != 0) + { + DWORD i; + + dwTotalBytesRead += dwBytesRead; + + if (pbValueBuffer) + { + pbValueBuffer = (PBYTE)GlobalReAlloc(pbValueBuffer, dwTotalBytesRead + sizeof(TCHAR), GMEM_ZEROINIT | GMEM_MOVEABLE); + } + else + { + pbValueBuffer = (PBYTE)GlobalAlloc(GPTR, dwBytesRead + sizeof(TCHAR)); + } + + if (!pbValueBuffer) + { + SetLastErrorNode(pNode, TEXT("DoCreateProcess"), GetLastError()); + break; + } + + for (i = 0; i < dwBytesRead; i++, dwValueBufferPos++) + { + pbValueBuffer[dwValueBufferPos] = pbOutputBuffer[i]; + } + } + + if (pbValueBuffer) + { + JSON_SetEx(pNode, TEXT("Output"), FALSE, pbValueBuffer, + (JSON_IsTrue(JSON_Get(pRootNode, TEXT("UnicodeOutput"), FALSE)) ? JSF_IS_UNICODE : 0) | + (JSON_IsTrue(JSON_Get(pRootNode, TEXT("RawOutput"), FALSE)) ? JSF_IS_RAW : 0)); + } + + if (GetExitCodeProcess(ppiProcInfo->hProcess, &dwBytesRead)) + { + PTCHAR pszExitCode = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * 11); + if (pszExitCode) + { + wsprintf(pszExitCode, TEXT("%lu"), dwBytesRead); +#ifdef UNICODE + JSON_SetEx(pNode, TEXT("ExitCode"), FALSE, (PBYTE)pszExitCode, JSF_IS_UNICODE); +#else + JSON_SetEx(pNode, TEXT("ExitCode"), FALSE, (PBYTE)pszExitCode, JSF_NONE); +#endif + GlobalFree(pszExitCode); + } + } + + CloseHandle(ppiProcInfo->hProcess); + } + + if (pArgumentsNode && pArgumentsNode->eType == JNT_ARRAY && pszArguments) + GlobalFree(pszArguments); + + GlobalFree(psiStartInfo); + } + + GlobalFree(ppiProcInfo); + } + } + + if (hStdInWrite != NULL) + CloseHandle(hStdInWrite); + if (hStdOutWrite != NULL) + CloseHandle(hStdOutWrite); + if (hStdInRead != NULL) + CloseHandle(hStdInRead); + if (hStdOutRead != NULL) + CloseHandle(hStdOutRead); + + GlobalFree(pbOutputBuffer); + } + } +} + +static DWORD WINAPI DoCreateProcessThreadProc(LPVOID lpParameter) +{ + struct THREAD_PARAM* ptp = (struct THREAD_PARAM*)lpParameter; + DoCreateProcess(ptp->pNode, ptp->pRootNode); + GlobalFree(ptp); + return 0; +} + +static BOOL CallFunc(LPTHREAD_START_ROUTINE func, struct JSON_NODE* pNode, PTCHAR pszTree) +{ + struct JSON_NODE* pRootNode = GetTreeNode(pszTree, FALSE); + BOOL bSuccess = FALSE; + + if (pRootNode) + { + struct THREAD_PARAM* ptp = (struct THREAD_PARAM*)GlobalAlloc(GPTR, sizeof(struct THREAD_PARAM)); + if (ptp) + { + ptp->pNode = pNode; + ptp->pRootNode = pRootNode; + + if (JSON_IsTrue(JSON_Get(pRootNode, TEXT("Async"), FALSE))) + { + HANDLE hThread = CreateThread(NULL, 0, func, ptp, 0, NULL); + if (hThread) + { + PTCHAR pszHandle = (PTCHAR)GlobalAlloc(GPTR, g_stringsize * sizeof(TCHAR)); + if (pszHandle) + { +#ifdef _WIN64 + wsprintf(pszHandle, TEXT("%Id"), hThread); +#else + wsprintf(pszHandle, TEXT("%d"), hThread); +#endif + +#ifdef UNICODE + JSON_SetEx(pRootNode, TEXT("Handle"), FALSE, (PBYTE)pszHandle, JSF_IS_UNICODE); +#else + JSON_SetEx(pRootNode, TEXT("Handle"), FALSE, (PBYTE)pszHandle, JSF_NONE); +#endif + GlobalFree(pszHandle); + bSuccess = TRUE; + } + } + else + { + GlobalFree(ptp); + } + } + else if (JSON_IsTrue(JSON_Get(pRootNode, TEXT("UIAsync"), FALSE))) + { + HANDLE hThread = CreateThread(NULL, 0, func, ptp, 0, NULL); + if (hThread) + { + BOOL bLoop = TRUE; + while (bLoop) + { + MSG msg; + if (MsgWaitForMultipleObjectsEx(1, &hThread, INFINITE, QS_ALLINPUT | QS_ALLPOSTMESSAGE, 0) != WAIT_OBJECT_0 + 1) + break; + + while (bLoop && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + { + PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam); + bLoop = FALSE; + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + + CloseHandle(hThread); + bSuccess = TRUE; + } + } + else + { + func(ptp); + bSuccess = TRUE; + } + } + } + + return bSuccess; +} + +NSISFUNC(Serialize) +{ + DLL_INIT(); + { + BOOL bOK = FALSE; + + PTCHAR pszArg = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszArg) + { + BOOL bIsFile = FALSE, bAsUnicode = FALSE, bFormat = FALSE; + struct JSON_NODE* pNode = PopTreeNode(pszArg, FALSE, NULL); + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/file")) == 0) + { + bIsFile = TRUE; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/unicode")) == 0) + { + bAsUnicode = TRUE; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/format")) == 0) + { + bFormat = TRUE; + continue; + } + + if (!bIsFile) + pushstring(pszArg); + break; + } + + if (pNode) + { +#ifdef UNICODE + if (!bIsFile) + bAsUnicode = TRUE; +#endif + if (JSON_Serialize(pNode, pszArg, string_size - 1, bIsFile, bAsUnicode, bFormat)) + { + if (!bIsFile) + pushstring(pszArg); + bOK = TRUE; + } + else + { + GetLastErrorMessage(pszArg, TEXT("JSON_Serialize"), GetLastError()); + pushstring(pszArg); + } + } + + GlobalFree(pszArg); + } + + if (!bOK) + extra->exec_flags->exec_error = 1; + } +} + +static void PushKeys(struct JSON_NODE* pNode) +{ + if (pNode->pNext) + PushKeys(pNode->pNext); + + if (pNode->pszKey) + pushstring(pNode->pszKey); + else + pushstring(pNode->pszValue); +} + +#define GET_ACTION_NOEXPAND 1 +#define GET_ACTION_KEY 2 +#define GET_ACTION_KEYS 3 +#define GET_ACTION_TYPE 4 +#define GET_ACTION_EXISTS 5 +#define GET_ACTION_COUNT 6 +#define GET_ACTION_ISEMPTY 7 + +NSISFUNC(Get) +{ + DLL_INIT(); + { + BOOL bOK = FALSE; + + PTCHAR pszArg = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszArg) + { + struct JSON_NODE* pNode = PopTreeNode(pszArg, FALSE, NULL); + struct JSON_NODE* pPrev; + int nAction = 0; + BOOL bKeyIsIndex = FALSE; + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/noexpand")) == 0) + { + nAction = GET_ACTION_NOEXPAND; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/key")) == 0) + { + nAction = GET_ACTION_KEY; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/keys")) == 0) + { + nAction = GET_ACTION_KEYS; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/type")) == 0) + { + nAction = GET_ACTION_TYPE; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/exists")) == 0) + { + nAction = GET_ACTION_EXISTS; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/count")) == 0) + { + nAction = GET_ACTION_COUNT; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/isempty")) == 0) + { + nAction = GET_ACTION_ISEMPTY; + continue; + } + + pushstring(pszArg); + break; + } + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/end")) == 0) + break; + + if (lstrcmpi(pszArg, TEXT("/index")) == 0) + { + bKeyIsIndex = TRUE; + continue; + } + + if (pNode) + { + pPrev = pNode; + pNode = JSON_Next(&pPrev, pszArg, bKeyIsIndex, FALSE, NULL); + bKeyIsIndex = FALSE; + } + } + + if (pNode) + { + if (nAction == GET_ACTION_ISEMPTY) + { + pushstring(pNode->pValue ? TEXT("no") : TEXT("yes")); + } + else if (nAction == GET_ACTION_COUNT) + { + wsprintf(pszArg, TEXT("%d"), JSON_Count(pNode)); + pushstring(pszArg); + } + else if (nAction == GET_ACTION_EXISTS) + { + pushstring(TEXT("yes")); + } + else if (nAction == GET_ACTION_TYPE) + { + switch (pNode->eType) + { + case JNT_NODE: + pushstring(TEXT("node")); + break; + case JNT_ARRAY: + pushstring(TEXT("array")); + break; + case JNT_VALUE: + pushstring(TEXT("value")); + break; + case JNT_QUOTED_VALUE: + pushstring(TEXT("string")); + break; + } + } + else if (nAction == GET_ACTION_KEY) + { + if (pNode->pszKey) + pushstring(pNode->pszKey); + else + pushstring(pNode->pszValue); + } + else if (nAction == GET_ACTION_KEYS) + { + if (pNode->eType == JNT_NODE) + { + PushKeys(pNode->pValue); + wsprintf(pszArg, TEXT("%d"), JSON_Count(pNode)); + pushstring(pszArg); + } + else + { + pushstring(TEXT("0")); + } + } + else if (pNode->eType == JNT_VALUE) + { + pushstring(pNode->pszValue); + } + else if (pNode->eType == JNT_QUOTED_VALUE) + { + if (nAction == GET_ACTION_NOEXPAND) + { + pushstring(pNode->pszValue); + } + else + { + PTCHAR pszExpanded = JSON_Expand(pNode); + if (pszExpanded) + { + pushstring(pszExpanded); + GlobalFree(pszExpanded); + } + } + } + else if (pNode->pValue) + { + if (JSON_Serialize(pNode->pValue, pszArg, string_size - 1, FALSE, FALSE, FALSE)) + { + pushstring(pszArg); + bOK = TRUE; + } + else + { + GetLastErrorMessage(pszArg, TEXT("JSON_Serialize"), GetLastError()); + pushstring(pszArg); + } + } + else + { + pushstring(TEXT("")); + } + + bOK = TRUE; + } + else if (nAction == GET_ACTION_EXISTS) + { + pushstring(TEXT("no")); + bOK = TRUE; + } + else if (nAction == GET_ACTION_TYPE) + { + pushstring(TEXT("")); + bOK = TRUE; + } + else if (nAction == GET_ACTION_KEYS) + { + pushstring(TEXT("0")); + bOK = TRUE; + } + + GlobalFree(pszArg); + } + + if (!bOK) + extra->exec_flags->exec_error = 1; + } +} + +NSISFUNC(Set) +{ + DLL_INIT(); + { + BOOL bOK = FALSE; + + PTCHAR pszArg = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszArg) + { + PTCHAR pszTree = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszTree) + { + BOOL bCreated; + struct JSON_NODE* pTreeNode = PopTreeNode(pszTree, TRUE, &bCreated); + struct JSON_NODE* pNode = pTreeNode; + struct JSON_NODE* pPrev = NULL; + BOOL bKeyIsIndex = FALSE; + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/index")) == 0) + { + bKeyIsIndex = TRUE; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/value")) == 0) + { + if (popstring(pszArg) == 0 && pNode) + { +#ifdef UNICODE + if (JSON_Set(pNode, (PBYTE)pszArg, JSF_IS_UNICODE)) +#else + if (JSON_Set(pNode, (PBYTE)pszArg, JSF_NONE)) +#endif + bOK = TRUE; + } + break; + } + + if (lstrcmpi(pszArg, TEXT("/file")) == 0) + { + if (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/unicode")) == 0) + { + if (popstring(pszArg) == 0 && pNode) + { + if (JSON_Set(pNode, (PBYTE)pszArg, JSF_IS_FILE | JSF_IS_UNICODE)) + bOK = TRUE; + } + } + else + { + if (JSON_Set(pNode, (PBYTE)pszArg, JSF_IS_FILE)) + bOK = TRUE; + } + } + break; + } + + if (lstrcmpi(pszArg, TEXT("/http")) == 0) + { + if (popstring(pszArg) == 0 && pNode) + { + if (CallFunc(DoHttpWebRequestThreadProc, pNode, pszArg)) + bOK = TRUE; + } + break; + } + + if (lstrcmpi(pszArg, TEXT("/exec")) == 0) + { + if (popstring(pszArg) == 0 && pNode) + { + if (CallFunc(DoCreateProcessThreadProc, pNode, pszArg)) + bOK = TRUE; + } + break; + } + + if (pNode) + { + pPrev = pNode; + pNode = JSON_Next(&pPrev, pszArg, bKeyIsIndex, TRUE, &bCreated); + bKeyIsIndex = FALSE; + } + } + + if (!bOK && pNode && bCreated) + { + if (pNode == pTreeNode) + LinkedListDelete(&g_pList, pszTree); + JSON_Delete(&pNode, pPrev); + } + + GlobalFree(pszTree); + } + + GlobalFree(pszArg); + } + + if (!bOK) + extra->exec_flags->exec_error = 1; + } +} + +NSISFUNC(Delete) +{ + DLL_INIT(); + { + BOOL bOK = FALSE; + + PTCHAR pszArg = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszArg) + { + PTCHAR pszTree = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszTree) + { + struct JSON_NODE* pTreeNode = PopTreeNode(pszTree, FALSE, NULL); + struct JSON_NODE* pNode = pTreeNode; + struct JSON_NODE* pPrev = NULL; + BOOL bKeyIsIndex = FALSE; + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/end")) == 0) + break; + + if (lstrcmpi(pszArg, TEXT("/index")) == 0) + { + bKeyIsIndex = TRUE; + continue; + } + + if (pNode) + { + pPrev = pNode; + pNode = JSON_Next(&pPrev, pszArg, bKeyIsIndex, FALSE, NULL); + bKeyIsIndex = FALSE; + } + } + + if (pNode) + { + if (pNode == pTreeNode) + LinkedListDelete(&g_pList, pszTree); + JSON_Delete(&pNode, pPrev); + bOK = TRUE; + } + + GlobalFree(pszTree); + } + + GlobalFree(pszArg); + } + + if (!bOK) + extra->exec_flags->exec_error = 1; + } +} + +NSISFUNC(Quote) +{ + DLL_INIT(); + { + BOOL bOK = FALSE; + + PTCHAR pszArg = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszArg) + { + enum JSON_ESCAPE_FLAGS eFlags = JEF_QUOTE; + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/unicode")) == 0) + { + eFlags |= JEF_ESCAPE_UNICODE; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/always")) == 0) + { + eFlags |= JEF_ALWAYS_QUOTE; + continue; + } + + pushstring(pszArg); + break; + } + + if (popstring(pszArg) == 0) + { + PTCHAR pszQuoted = JSON_Escape(pszArg, eFlags); + if (pszQuoted) + { + pushstring(pszQuoted); + GlobalFree(pszQuoted); + bOK = TRUE; + } + } + + GlobalFree(pszArg); + } + + if (!bOK) + extra->exec_flags->exec_error = 1; + } +} + +NSISFUNC(Wait) +{ + DLL_INIT(); + { + BOOL bOK = FALSE; + + PTCHAR pszArg = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszArg) + { + if (popstring(pszArg) == 0) + { + struct JSON_NODE* pNode = GetTreeNode(pszArg, FALSE); + if (pNode) + { + struct JSON_NODE* pPrev = pNode; + struct JSON_NODE* pHandleNode = JSON_Next(&pPrev, TEXT("Handle"), FALSE, FALSE, NULL); + if (pHandleNode && pHandleNode->eType == JNT_VALUE) + { + HANDLE hThread = (HANDLE)nsishelper_str_to_ptr(pHandleNode->pszValue); + if (hThread) + { + int nTimeout = -1; + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/timeout")) == 0) + { + if (popstring(pszArg) == 0) + nTimeout = myatoi(pszArg); + continue; + } + + pushstring(pszArg); + break; + } + + if (nTimeout >= 0) + { + if (WaitForSingleObject(hThread, nTimeout) == WAIT_TIMEOUT) + { + pushstring(TEXT("wait")); + } + else + { + pushstring(TEXT("")); + CloseHandle(hThread); + JSON_Delete(&pHandleNode, pPrev); + } + } + else + { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + JSON_Delete(&pHandleNode, pPrev); + } + + bOK = TRUE; + } + } + } + } + + GlobalFree(pszArg); + } + + if (!bOK) + extra->exec_flags->exec_error = 1; + } +} + +NSISFUNC(Sort) +{ + DLL_INIT(); + { + BOOL bOK = FALSE; + + PTCHAR pszArg = (PTCHAR)GlobalAlloc(GPTR, sizeof(TCHAR) * string_size); + if (pszArg) + { + struct JSON_NODE* pNode = PopTreeNode(pszArg, FALSE, NULL); + struct JSON_NODE* pPrev = NULL; + BOOL bKeyIsIndex = FALSE; + enum JSON_SORT_FLAGS eFlags = 0; + + while (popstring(pszArg) == 0) + { + if (lstrcmpi(pszArg, TEXT("/end")) == 0) + break; + + if (lstrcmpi(pszArg, TEXT("/index")) == 0) + { + bKeyIsIndex = TRUE; + continue; + } + + if (lstrcmpi(pszArg, TEXT("/options")) == 0) + { + eFlags = (enum JSON_SORT_FLAGS)popint(); + continue; + } + + if (pNode) + { + pPrev = pNode; + pNode = JSON_Next(&pPrev, pszArg, bKeyIsIndex, FALSE, NULL); + bKeyIsIndex = FALSE; + } + } + + if (pNode) + { + JSON_Sort(pNode, eFlags); + bOK = TRUE; + } + + GlobalFree(pszArg); + } + + if (!bOK) + extra->exec_flags->exec_error = 1; + } +} \ No newline at end of file diff --git a/src/nsJSON.h b/src/nsJSON.h new file mode 100644 index 0000000..a521d3d --- /dev/null +++ b/src/nsJSON.h @@ -0,0 +1,7 @@ +#ifndef __NSJSON_H__ +#define __NSJSON_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 \ No newline at end of file diff --git a/src/nsJSON.rc b/src/nsJSON.rc new file mode 100644 index 0000000000000000000000000000000000000000..57233df9b6fb54fed204d833b46ca3e7a22c429d GIT binary patch literal 4558 zcmdUyTW=CU6vxlAiQi$|zSvk>X#1d#El{kbK*~i;nvhy9C85x8No(R4@=L0}GsCdE zu%KWcFq_%gnK^T=|8r*c*Utkv5F?k;m0KCgKt{B-jG3!5Hacx}*UtAw^7v)m$8Eec zkbCSO;~n)w6{|nKaNJ@XU|RxjpkS&jkCkvf)KAT|5n)q~_r_o&cbTN^GOJRlx^-dj>9KO4~G$n%Olhl`BW z@-y;GcSl$JyzVwooRinNIT{0k_ae>Fmf>Flfi)^n^{;k#?q{}kTZ_K(j^}l_C#g31Ro~Z%quz7W2f<9oEP6)on+~smJwpQYAIV2-)4QRe zDOT0j5B3Jyk+;_3WUW@qjpv7#m*(yiwsrWbuk{=s((9eO$ornL=Nz?U9~AX?*5O}( zSI2VB5YY$uW~1HV9x~r0Hqo>|> zeXIS^=FX>8|2B&^jqS21s*PHvW8eSlf3DvAR{!UEUq-dqu5Zk GE2rNTQ5~lM literal 0 HcmV?d00001 diff --git a/src/nsJSON.sln b/src/nsJSON.sln new file mode 100644 index 0000000..9a2466a --- /dev/null +++ b/src/nsJSON.sln @@ -0,0 +1,97 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsJSON", "nsJSON.vcxproj", "{EC3375F9-B492-470B-8EA6-8F5F4A804BB7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConsoleApp", "ConsoleApp\ConsoleApp.vcxproj", "{6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppDotNet", "ConsoleAppDotNet\ConsoleAppDotNet.csproj", "{DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Unicode|Any CPU = Debug Unicode|Any CPU + Debug Unicode|Win32 = Debug Unicode|Win32 + Debug Unicode|x64 = Debug Unicode|x64 + Debug|Any CPU = Debug|Any CPU + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release Unicode|Any CPU = Release Unicode|Any CPU + Release Unicode|Win32 = Release Unicode|Win32 + Release Unicode|x64 = Release Unicode|x64 + Release|Any CPU = Release|Any CPU + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug Unicode|Any CPU.ActiveCfg = Debug Unicode|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug Unicode|Win32.ActiveCfg = Debug Unicode|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug Unicode|Win32.Build.0 = Debug Unicode|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug Unicode|x64.ActiveCfg = Debug Unicode|x64 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug Unicode|x64.Build.0 = Debug Unicode|x64 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug|Win32.ActiveCfg = Debug|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug|Win32.Build.0 = Debug|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug|x64.ActiveCfg = Debug|x64 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Debug|x64.Build.0 = Debug|x64 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release Unicode|Any CPU.ActiveCfg = Release Unicode|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release Unicode|Win32.ActiveCfg = Release Unicode|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release Unicode|Win32.Build.0 = Release Unicode|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release Unicode|x64.ActiveCfg = Release Unicode|x64 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release Unicode|x64.Build.0 = Release Unicode|x64 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release|Any CPU.ActiveCfg = Release|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release|Win32.ActiveCfg = Release|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release|Win32.Build.0 = Release|Win32 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release|x64.ActiveCfg = Release|x64 + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7}.Release|x64.Build.0 = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug Unicode|Any CPU.ActiveCfg = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug Unicode|Any CPU.Build.0 = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug Unicode|Win32.ActiveCfg = Debug|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug Unicode|Win32.Build.0 = Debug|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug Unicode|x64.ActiveCfg = Debug|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug Unicode|x64.Build.0 = Debug|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug|Win32.ActiveCfg = Debug|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug|Win32.Build.0 = Debug|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug|x64.ActiveCfg = Debug|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Debug|x64.Build.0 = Debug|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release Unicode|Any CPU.ActiveCfg = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release Unicode|Any CPU.Build.0 = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release Unicode|Win32.ActiveCfg = Release|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release Unicode|Win32.Build.0 = Release|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release Unicode|x64.ActiveCfg = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release Unicode|x64.Build.0 = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release|Any CPU.ActiveCfg = Release|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release|Win32.ActiveCfg = Release|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release|Win32.Build.0 = Release|Win32 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release|x64.ActiveCfg = Release|x64 + {6ECB05AC-F1A5-41F9-8880-431EEB8E2D7D}.Release|x64.Build.0 = Release|x64 + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug Unicode|Any CPU.ActiveCfg = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug Unicode|Any CPU.Build.0 = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug Unicode|Win32.ActiveCfg = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug Unicode|Win32.Build.0 = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug Unicode|x64.ActiveCfg = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug Unicode|x64.Build.0 = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug|Win32.ActiveCfg = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug|Win32.Build.0 = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug|x64.ActiveCfg = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Debug|x64.Build.0 = Debug|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release Unicode|Any CPU.ActiveCfg = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release Unicode|Any CPU.Build.0 = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release Unicode|Win32.ActiveCfg = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release Unicode|Win32.Build.0 = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release Unicode|x64.ActiveCfg = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release Unicode|x64.Build.0 = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release|Any CPU.Build.0 = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release|Win32.ActiveCfg = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release|Win32.Build.0 = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release|x64.ActiveCfg = Release|Any CPU + {DBAA5865-5EF1-4A17-BC19-B68D5E5C13F8}.Release|x64.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/nsJSON.vcxproj b/src/nsJSON.vcxproj new file mode 100644 index 0000000..854b2fe --- /dev/null +++ b/src/nsJSON.vcxproj @@ -0,0 +1,449 @@ + + + + + Debug Unicode + Win32 + + + Debug Unicode + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release Unicode + Win32 + + + Release Unicode + x64 + + + Release + Win32 + + + Release + x64 + + + + {EC3375F9-B492-470B-8EA6-8F5F4A804BB7} + 10.0.15063.0 + + + + DynamicLibrary + Unicode + v140 + + + DynamicLibrary + Unicode + v140 + + + DynamicLibrary + MultiByte + v140 + + + DynamicLibrary + MultiByte + v140 + + + DynamicLibrary + Unicode + v140 + + + DynamicLibrary + Unicode + v140 + + + DynamicLibrary + MultiByte + v140 + + + DynamicLibrary + MultiByte + v140 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)..\..\Plugins\x86-ansi\ + false + false + $(SolutionDir)..\..\Plugins\x86-ansi\ + $(SolutionDir)..\..\Plugins\x86-unicode\ + false + false + false + false + $(SolutionDir)..\..\Plugins\x86-unicode\ + false + false + + + $(SolutionDir)..\..\Plugins\amd64-unicode\ + + + $(SolutionDir)..\..\Plugins\amd64-unicode\ + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + + + + + MultiThreadedDLL + false + + + Level3 + true + MinSpace + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + DllMain + + + MachineX86 + false + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + No + + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + + + + + MultiThreadedDLL + false + + + Level3 + true + MinSpace + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + DllMain + + + false + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + No + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + + + + + WIN32;_DEBUG;_WINDOWS;_USRDLL;nsJSON_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + + + Level3 + true + EditAndContinue + Disabled + MultiThreadedDebug + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + MachineX86 + + + false + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + + + + + WIN32;_DEBUG;_WINDOWS;_USRDLL;nsJSON_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + + + Level3 + true + ProgramDatabase + Disabled + MultiThreadedDebug + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + + + false + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + + + + + WIN32;_DEBUG;_WINDOWS;_USRDLL;nsJSON_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + + + Level3 + true + EditAndContinue + Disabled + MultiThreadedDebug + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + MachineX86 + + + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + + + + + WIN32;_DEBUG;_WINDOWS;_USRDLL;nsJSON_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + + + Level3 + true + ProgramDatabase + Disabled + MultiThreadedDebug + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + + + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + + + + + MultiThreadedDLL + false + + + MinSpace + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + DllMain + + + MachineX86 + false + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + No + + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + + + + + MultiThreadedDLL + false + + + MinSpace + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + DllMain + + + false + wininet.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + No + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/nsJSON.vcxproj.filters b/src/nsJSON.vcxproj.filters new file mode 100644 index 0000000..77298fe --- /dev/null +++ b/src/nsJSON.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/nsis_tchar.h b/src/nsis_tchar.h new file mode 100644 index 0000000..210bab1 --- /dev/null +++ b/src/nsis_tchar.h @@ -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__) diff --git a/src/pluginapi.c b/src/pluginapi.c new file mode 100644 index 0000000..c507e31 --- /dev/null +++ b/src/pluginapi.c @@ -0,0 +1,294 @@ +#include + +#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); +} diff --git a/src/pluginapi.h b/src/pluginapi.h new file mode 100644 index 0000000..ca671a8 --- /dev/null +++ b/src/pluginapi.h @@ -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___ diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 0000000000000000000000000000000000000000..97a43f859b6751ce7fda8ad22f9c21597c0a6f5e GIT binary patch literal 810 zcmb7?%}T>i5QWcL@Erm!g~oy}5YsNIO)9M^x(YT;q*7978h;R9UH#@pY;+MU!%b#} zGiS~{$>)bJUvEULiBb(T)HNq|S*%bas+r!2V;zC@t1WB zP^W~a2jwa`TlzcROL{9h8K{~nr!MHM$$G5~30w_&p;s^^^(8tcDC@zN{a{ViURli4 zi_>G=1zT&lP2ZDKR@1HA&J8Tao9mhPMhiWXXJD%qpD~}HuW(umkOBV8bfW}^M$WSl zn&xCzI5lvy%|zKTGX)!fw)>2Ahx0R@kuKtPxN)B{=b<+SR*1}+n|%z(>cr*hq1@pVCwGMrP_n_Fz&`l`xf22=1