mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 18:32:10 +02:00
Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a94df76bfa | |||
| b2b748b07c | |||
| 77ce6b0e4c | |||
| d28d307b63 | |||
| 3492f9bfa1 | |||
| 79c67ec0d7 | |||
| 6527f03a9b | |||
| a8b629f58c | |||
| 5df00a3394 | |||
| 8a7c83cac6 | |||
| 27bffb3464 | |||
| 875afb7cde | |||
| 3e99a7035b | |||
| 63873763de | |||
| 27c83e88f2 | |||
| f48904805f | |||
| 531cd0909a | |||
| eafcdfec9e | |||
| d6bd855733 | |||
| af7e2d47d1 | |||
| f71c0b0866 | |||
| 1400291ae9 | |||
| 7799dbe481 | |||
| 8426833c36 | |||
| 235a2018fc | |||
| 470e3b4e57 | |||
| 1942dce980 | |||
| efca4f3597 | |||
| 43ddbdca85 | |||
| 5d29e26509 | |||
| 8daec2568c | |||
| 320e1bf4e0 | |||
| 38e98d3d51 | |||
| 50964fcddf | |||
| 6c7a8a8c41 | |||
| 92082eed54 | |||
| 26b3a84b8a | |||
| 0747fc0887 | |||
| 60fbe1fd0b | |||
| f8667d9faa | |||
| 1c89bbc34e | |||
| b37f020270 | |||
| fbc9569e13 | |||
| a923f61115 | |||
| bc0a515998 | |||
| 87d89130ad | |||
| f46aa316cb | |||
| fdb5f30cb8 | |||
| 91f1c4db10 | |||
| 44e2948b1d | |||
| b86acf6c36 | |||
| 9a15666248 | |||
| f5dfec6dcb | |||
| 8c55c06fa7 | |||
| 8159f473cc | |||
| dca3b154ef | |||
| 79b61f1391 | |||
| ba54763dde | |||
| 451bc394ee | |||
| 93f9ed1bf5 | |||
| d9e4389ed4 | |||
| b465052ad2 | |||
| 2e6b9cd503 | |||
| 0557f16c16 | |||
| 08e252a44c | |||
| 90e2d8f8d9 | |||
| 0dd8d80e02 | |||
| 3cddf31987 | |||
| fe1c5c8f6f | |||
| 994bd53d30 | |||
| 383a4190ca | |||
| 42f2e5a745 | |||
| 14b8bb2bb5 | |||
| b68ad3626c | |||
| cd25803e32 | |||
| 3d6bffe7fe | |||
| e694c8ac84 | |||
| dfff57dd85 | |||
| c5141855a2 | |||
| 6cb186f229 | |||
| e91f62aa70 | |||
| ab5cf94099 | |||
| d9eccd70ed | |||
| 025febc78d | |||
| 69cc19d6f5 | |||
| 2e4dd1d262 | |||
| 24ec0e6035 | |||
| 6e50aaf3ff | |||
| e7cd5f618d | |||
| 55e4896524 | |||
| 594c73461e | |||
| 94ceeb4482 | |||
| 4ee7dd72d0 | |||
| 414f7ce067 | |||
| e1dffe794b | |||
| 181b775f73 | |||
| c4c934d23e | |||
| 31c73543e8 | |||
| d79e1d7fbc | |||
| 3883047dce | |||
| 4eebc1b6ee | |||
| 08b79ff4ae | |||
| ffc38eb0f1 | |||
| 37ee193c40 | |||
| c10027f1d7 | |||
| 039fa1ba77 | |||
| 0db820a6c1 | |||
| 8b611899d1 | |||
| ba7e1eb18b | |||
| 6979b1efc5 | |||
| 4a61cdb1c9 | |||
| 75c28ec671 | |||
| 639252ece0 | |||
| a31d8ce87b | |||
| 41143c23f4 | |||
| 3c2e21b7b1 | |||
| 3eaebd3359 | |||
| 768bc154d4 | |||
| 00cc19e28b | |||
| bf0564f9e5 | |||
| dbb060eb62 | |||
| 1314c18a52 | |||
| 05f5e25c66 | |||
| 4b4c37ed88 | |||
| 8970774e5f | |||
| 69c86c95b7 | |||
| 910ba413da | |||
| dbed1b32b4 | |||
| 278a3834ce | |||
| 5ce3220f50 | |||
| c1c6ccce9a | |||
| e2526521a2 | |||
| 1956c66a5e | |||
| c601b68bc6 | |||
| 528f9f152c | |||
| 64d2c4410e | |||
| 6e4de3e9f1 | |||
| ce51dbf7ea | |||
| abc6c0593c | |||
| b63bd5b8c9 | |||
| 4236d830cc | |||
| c851c39837 | |||
| 1cd196b726 | |||
| f8a8864be5 | |||
| 683e90cec6 | |||
| 08de5eabed | |||
| c7ecc33b64 | |||
| ee7f574349 | |||
| 10f58e405e | |||
| d81a7b6278 | |||
| 48daf4ac1e | |||
| 6d01c0b438 | |||
| 3cc0e40e5a | |||
| 679def253d | |||
| 4dc5033c80 | |||
| b3603391eb | |||
| f6a363f7ad | |||
| 3e69361196 | |||
| 04bd5045b9 | |||
| 886bde47d6 | |||
| 254fb82520 | |||
| af884bb638 | |||
| e8e43c10fc | |||
| 9090dbd276 | |||
| f683586623 | |||
| d71d6a679d | |||
| 17eb22dfdb | |||
| c09a788ce3 | |||
| ab32056c3f | |||
| e15049856f | |||
| 16423487fb | |||
| 6b95eb2c74 | |||
| 2b82e9ab02 | |||
| 5fe2cc6350 | |||
| 50739ee890 | |||
| 97ce079488 | |||
| 7045dea284 | |||
| 7887067301 | |||
| 39cfc7aac8 | |||
| 44af8b85ad | |||
| 85594d2056 | |||
| 14cd933969 | |||
| 6550b77c2f | |||
| 7147f23504 | |||
| 94164b873c | |||
| 9cfe7a74bc | |||
| 604aacbae2 | |||
| 04c486d3db | |||
| 152f3cdee1 | |||
| b7890e1e2a | |||
| 8913620aa5 | |||
| 2a103f620d | |||
| 8d0ceff7fa | |||
| ce9f4daec4 | |||
| e639700387 | |||
| 3da344db1f | |||
| 31a35cf845 | |||
| 77c916f747 | |||
| c9dbf990d8 | |||
| ed4d903e69 | |||
| 299451ca62 | |||
| 6319b68c2c | |||
| c37ac4da2b | |||
| 643db92cd1 | |||
| 99795154b5 | |||
| 6cf403da7d | |||
| 4f3e82330b | |||
| 0c401a0112 | |||
| d4b1ae9751 | |||
| 831659966f | |||
| 5f261702a9 | |||
| c2da9b0cb1 | |||
| 239233f4e0 | |||
| b00593ae05 | |||
| b4e6688dfe | |||
| b022babd7e | |||
| 52c7f9f973 | |||
| e0eba8ce21 | |||
| cdd4cee968 | |||
| 1b23edc5c1 |
@@ -0,0 +1,89 @@
|
||||
# find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" -o -name "*.m" -o -name "*.mm" \) -print0 | xargs -0 clang-format -i
|
||||
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
UseTab: Never
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
|
||||
ColumnLimit: 0
|
||||
ReflowComments: true
|
||||
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeCloseBracketBracedList: true
|
||||
BraceWrapping:
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeElse: false
|
||||
BeforeCatch: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
IndentExternBlock: NoIndent
|
||||
|
||||
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
InsertBraces: true
|
||||
|
||||
PointerAlignment: Left
|
||||
DerivePointerAlignment: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpacesInParentheses: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: 1
|
||||
SpacesInContainerLiterals: true
|
||||
|
||||
SortIncludes: Never
|
||||
IndentPPDirectives: BeforeHash
|
||||
PPIndentWidth: 4
|
||||
|
||||
IndentCaseLabels: true
|
||||
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignOperands: DontAlign
|
||||
|
||||
BinPackParameters: false
|
||||
BinPackArguments: false
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
BracedInitializerIndentWidth: 4
|
||||
Cpp11BracedListStyle: Block
|
||||
|
||||
ForEachMacros:
|
||||
- FF_LIST_FOR_EACH
|
||||
- yyjson_obj_foreach
|
||||
- yyjson_arr_foreach
|
||||
- yyjson_mut_obj_foreach
|
||||
- yyjson_mut_arr_foreach
|
||||
|
||||
AttributeMacros:
|
||||
- FF_A_FALLTHROUGH
|
||||
- FF_A_DEPRECATED
|
||||
- FF_A_CLEANUP
|
||||
- FF_A_NODISCARD
|
||||
- FF_A_PRINTF
|
||||
- FF_A_SCANF
|
||||
- FF_A_NONNULL
|
||||
- FF_A_RETURNS_NONNULL
|
||||
- FF_A_UNUSED
|
||||
- FF_A_PACKED
|
||||
- FF_A_WEAK_IMPORT
|
||||
|
||||
MaxEmptyLinesToKeep: 1
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
|
||||
---
|
||||
Language: ObjC
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
@@ -0,0 +1,3 @@
|
||||
src/3rdparty/**
|
||||
build/**
|
||||
src/logo/builtin.c
|
||||
@@ -21,6 +21,10 @@ Closes #
|
||||
|
||||
-
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- Required if this PR introduces visual changes. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have tested my changes locally.
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Reusable DragonFly amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/dragonflybsd-vm@v1
|
||||
with:
|
||||
usesh: yes
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update
|
||||
pkg install -y llvm cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa libelf
|
||||
|
||||
run: |
|
||||
env CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable FreeBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: freebsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '15.0'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg update
|
||||
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-freebsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Reusable Haiku amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: haiku
|
||||
version: 'r1beta5'
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-haiku-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Reusable Linux armv6l
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv6
|
||||
distro: bookworm
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y wget
|
||||
apt-get install -y cmake make gcc libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libefl-all-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-armv6l
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Reusable Linux armv7l
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv7
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y software-properties-common ca-certificates gpg curl
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
echo -e 'Acquire::https::Verify-Peer "false";\nAcquire::https::Verify-Host "false";' >> /etc/apt/apt.conf.d/99ignore-certificates
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libefl-all-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-armv7l
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,103 @@
|
||||
name: Reusable Linux Hosts
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
ffversion:
|
||||
description: fastfetch version from linux host build
|
||||
value: ${{ jobs.build.outputs.ffversion }}
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
outputs:
|
||||
ffversion: ${{ steps.ffversion.outputs.ffversion }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: c
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: get fastfetch version
|
||||
id: ffversion
|
||||
run: echo "ffversion=$(./fastfetch --version-raw)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: polyfill glibc
|
||||
run: |
|
||||
wget https://github.com/CarterLi/polyfill-glibc/releases/download/v0.0.1/polyfill-glibc-${{ inputs.arch }} -O polyfill-glibc
|
||||
chmod +x polyfill-glibc
|
||||
strip fastfetch && ./polyfill-glibc fastfetch --target-glibc=2.17
|
||||
strip flashfetch && ./polyfill-glibc flashfetch --target-glibc=2.17
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-polyfilled")' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_PACKAGE_RELOCATABLE OFF)' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17)")' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_RPM_SPEC_MORE_DEFINE "%global __os_install_post %{nil}")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Reusable Linux i686
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 gcc-13-multilib libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
|
||||
|
||||
- name: cmake version
|
||||
run: cmake --version
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: check deb package
|
||||
run: dpkg -I fastfetch-*.deb
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Reusable Linux VMs
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: ${{ inputs.arch }}
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y software-properties-common
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev libefl-all-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Reusable macOS Hosts
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: install required packages
|
||||
run: |
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: otool -L fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-macos-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Reusable Musl amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: setup alpine linux
|
||||
uses: jirutka/setup-alpine@master
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
cat /etc/alpine-release
|
||||
uname -a
|
||||
apk add cmake samurai vulkan-loader-dev libxcb-dev libxrandr-dev rpm-dev wayland-dev libdrm-dev dconf-dev imagemagick-dev chafa-dev zlib-dev dbus-dev mesa-dev opencl-dev sqlite-dev networkmanager-dev pulseaudio-dev ddcutil-dev elfutils-dev gcc g++
|
||||
shell: alpine.sh --root {0}
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DIS_MUSL=ON -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -GNinja .
|
||||
cmake --build . --target package --verbose -j4
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-musl-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Reusable NetBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: netbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '10.1'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkgin -y install clang cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
|
||||
CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable No Features Test
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_EET=OFF -DENABLE_THREADS=OFF
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable OmniOS amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/omnios-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update --accept || true
|
||||
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja
|
||||
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-omnios-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable OpenBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: openbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '7.8'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg_add -u
|
||||
sudo pkg_add -r llvm-21.1.2p0 cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa
|
||||
CC=clang-21 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Reusable Release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ffversion:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: get latest release version
|
||||
id: get_version_release
|
||||
uses: pozetroninc/github-action-get-latest-release@master
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
- name: download artifacts
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: actions/download-artifact@v8
|
||||
|
||||
- name: create release
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ inputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
artifactErrorsFailBuild: true
|
||||
artifacts: fastfetch-*/fastfetch-*
|
||||
body: "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ inputs.ffversion }}/CHANGELOG.md) for details."
|
||||
|
||||
- name: download source tarballs
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
for i in 1 2 3 4 5; do curl -L --remote-name-all --output-dir fastfetch-source --create-dirs https://github.com/${{ github.repository }}/archive/refs/tags/${{ inputs.ffversion }}.{tar.gz,zip} && break || sleep 5; done
|
||||
ls fastfetch-*/*
|
||||
|
||||
- name: generate release notes
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
echo "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ inputs.ffversion }}/CHANGELOG.md) for details." > fastfetch-release-notes.md
|
||||
echo -e "\n---\n\n<details><summary>SHA256SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha256sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
echo -e "\n<details><summary>SHA512SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha512sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
|
||||
- name: update release body
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ inputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
bodyFile: fastfetch-release-notes.md
|
||||
allowUpdates: true
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Reusable Solaris amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/solaris-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
release: "11.4-gcc-14"
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg install cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja dconf mesa
|
||||
|
||||
run: |
|
||||
export PKG_CONFIG_PATH=/usr/lib/64/pkgconfig
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-solaris-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-solaris-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Reusable Spellcheck
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
spellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install codespell
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y codespell
|
||||
|
||||
- name: Run Spellchecker
|
||||
run: codespell
|
||||
@@ -0,0 +1,106 @@
|
||||
name: Reusable Windows Hosts
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
required: true
|
||||
type: string
|
||||
msystem:
|
||||
required: true
|
||||
type: string
|
||||
msystem-lower:
|
||||
required: true
|
||||
type: string
|
||||
msys-arch:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ inputs.msystem }}
|
||||
update: true
|
||||
install: git mingw-w64-clang-${{ inputs.msys-arch }}-7zip mingw-w64-clang-${{ inputs.msys-arch }}-cmake mingw-w64-clang-${{ inputs.msys-arch }}-clang mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-loader mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-headers mingw-w64-clang-${{ inputs.msys-arch }}-opencl-icd mingw-w64-clang-${{ inputs.msys-arch }}-opencl-headers mingw-w64-clang-${{ inputs.msys-arch }}-cppwinrt mingw-w64-clang-${{ inputs.msys-arch }}-imagemagick mingw-w64-clang-${{ inputs.msys-arch }}-chafa mingw-w64-clang-${{ inputs.msys-arch }}-directx-headers
|
||||
|
||||
- name: print msys version
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: env PKG_CONFIG_PATH=/${{ inputs.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --verbose -j4
|
||||
|
||||
- name: copy necessary dlls
|
||||
run: cp /${{ inputs.msystem-lower }}/bin/{OpenCL,vulkan-1}.dll .
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
id: upload-unsigned-artifact
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ inputs.arch }}
|
||||
path: |
|
||||
*.dll
|
||||
fastfetch.exe
|
||||
flashfetch.exe
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
name: submit signing request
|
||||
uses: signpath/github-action-submit-signing-request@v1
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '${{ vars.SIGNPATH_ORG_ID }}'
|
||||
project-slug: 'fastfetch'
|
||||
signing-policy-slug: ${{ github.ref == 'refs/heads/master' && 'release-signing' || 'test-signing' }}
|
||||
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: '.'
|
||||
|
||||
- name: create zip archive
|
||||
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ inputs.arch }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: create 7z archive
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ inputs.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ inputs.arch }}
|
||||
path: ./fastfetch-windows-${{ inputs.arch }}.*
|
||||
overwrite: true
|
||||
+51
-708
@@ -4,66 +4,20 @@ on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
spellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install codespell
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y codespell
|
||||
|
||||
- name: Run Spellchecker
|
||||
run: codespell
|
||||
uses: ./.github/workflows/build-spellcheck.yml
|
||||
|
||||
no-features-test:
|
||||
name: No-features-test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_THREADS=OFF
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
uses: ./.github/workflows/build-no-features-test.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-hosts:
|
||||
name: Linux-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -74,233 +28,38 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
- arch: aarch64
|
||||
runs-on: ubuntu-22.04-arm
|
||||
outputs:
|
||||
ffversion: ${{ steps.ffversion.outputs.ffversion }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: matrix.arch == 'amd64'
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: c
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
if: matrix.arch == 'amd64'
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: get fastfetch version
|
||||
id: ffversion
|
||||
run: echo "ffversion=$(./fastfetch --version-raw)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: polyfill glibc
|
||||
run: |
|
||||
wget https://github.com/CarterLi/polyfill-glibc/releases/download/v0.0.1/polyfill-glibc-${{ matrix.arch }} -O polyfill-glibc
|
||||
chmod +x polyfill-glibc
|
||||
strip fastfetch && ./polyfill-glibc fastfetch --target-glibc=2.17
|
||||
strip flashfetch && ./polyfill-glibc flashfetch --target-glibc=2.17
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-polyfilled")' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_PACKAGE_RELOCATABLE OFF)' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17)")' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_RPM_SPEC_MORE_DEFINE "%global __os_install_post %{nil}")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-hosts.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
secrets: inherit
|
||||
|
||||
linux-i686:
|
||||
name: Linux-i686
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 gcc-13-multilib libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
|
||||
|
||||
- name: cmake version
|
||||
run: cmake --version
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: check deb package
|
||||
run: dpkg -I fastfetch-*.deb
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-i686.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-armv7l:
|
||||
name: Linux-armv7l
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv7
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y software-properties-common ca-certificates gpg curl
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
echo -e 'Acquire::https::Verify-Peer "false";\nAcquire::https::Verify-Host "false";' >> /etc/apt/apt.conf.d/99ignore-certificates
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-armv7l
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-armv7l.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-armv6l:
|
||||
name: Linux-armv6l
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv6
|
||||
distro: bookworm
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y wget
|
||||
apt-get install -y cmake make gcc libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-armv6l
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-armv6l.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-vms:
|
||||
name: Linux-${{ matrix.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -310,78 +69,18 @@ jobs:
|
||||
- arch: riscv64
|
||||
- arch: ppc64le
|
||||
- arch: s390x
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y software-properties-common
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-vms.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
secrets: inherit
|
||||
|
||||
musl-amd64:
|
||||
name: Musl-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: setup alpine linux
|
||||
uses: jirutka/setup-alpine@master
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
cat /etc/alpine-release
|
||||
uname -a
|
||||
apk add cmake samurai vulkan-loader-dev libxcb-dev libxrandr-dev rpm-dev wayland-dev libdrm-dev dconf-dev imagemagick-dev chafa-dev zlib-dev dbus-dev mesa-dev opencl-dev sqlite-dev networkmanager-dev pulseaudio-dev ddcutil-dev elfutils-dev gcc g++
|
||||
shell: alpine.sh --root {0}
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DIS_MUSL=ON -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -GNinja .
|
||||
cmake --build . --target package --verbose -j4
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-musl-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-musl-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
macos-hosts:
|
||||
name: macOS-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -392,306 +91,64 @@ jobs:
|
||||
runs-on: macos-15-intel
|
||||
- arch: aarch64
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: install required packages
|
||||
run: |
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: otool -L fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-macos-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-macos-hosts.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
secrets: inherit
|
||||
|
||||
omnios-amd64:
|
||||
runs-on: ubuntu-latest
|
||||
name: OmniOS-amd64
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/omnios-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update --accept
|
||||
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja
|
||||
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-omnios-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-omnios-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
solaris-amd64:
|
||||
runs-on: ubuntu-latest
|
||||
name: Solaris-amd64
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/solaris-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
release: "11.4-gcc-14"
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg install cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja dconf mesa
|
||||
|
||||
run: |
|
||||
export PKG_CONFIG_PATH=/usr/lib/64/pkgconfig
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-solaris-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-solaris-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-solaris-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
freebsd-amd64:
|
||||
name: FreeBSD-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: freebsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '15.0'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg update
|
||||
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-freebsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-freebsd-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
openbsd-amd64:
|
||||
name: OpenBSD-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: openbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '7.8'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg_add -u
|
||||
sudo pkg_add -r llvm-21.1.2p0 cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa
|
||||
CC=clang-21 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-openbsd-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
netbsd-amd64:
|
||||
name: NetBSD-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: netbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '10.1'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkgin -y install clang cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
|
||||
CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-netbsd-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
dragonfly-amd64:
|
||||
name: DragonFly-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/dragonflybsd-vm@v1
|
||||
with:
|
||||
usesh: yes
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update
|
||||
pkg install -y llvm cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa libelf
|
||||
|
||||
run: |
|
||||
env CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-dragonfly-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
haiku-amd64:
|
||||
name: Haiku-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: haiku
|
||||
version: 'r1beta5'
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-haiku-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-haiku-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
windows-hosts:
|
||||
name: Windows-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -708,90 +165,18 @@ jobs:
|
||||
msystem: CLANGARM64
|
||||
msystem-lower: clangarm64
|
||||
msys-arch: aarch64
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.msystem }}
|
||||
update: true
|
||||
install: git mingw-w64-clang-${{ matrix.msys-arch }}-7zip mingw-w64-clang-${{ matrix.msys-arch }}-cmake mingw-w64-clang-${{ matrix.msys-arch }}-clang mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-loader mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-headers mingw-w64-clang-${{ matrix.msys-arch }}-opencl-icd mingw-w64-clang-${{ matrix.msys-arch }}-opencl-headers mingw-w64-clang-${{ matrix.msys-arch }}-cppwinrt mingw-w64-clang-${{ matrix.msys-arch }}-imagemagick mingw-w64-clang-${{ matrix.msys-arch }}-chafa mingw-w64-clang-${{ matrix.msys-arch }}-directx-headers
|
||||
|
||||
- name: print msys version
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: env PKG_CONFIG_PATH=/${{ matrix.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --verbose -j4
|
||||
|
||||
- name: copy necessary dlls
|
||||
run: cp /${{ matrix.msystem-lower }}/bin/{OpenCL,vulkan-1}.dll .
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
id: upload-unsigned-artifact
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}
|
||||
path: |
|
||||
*.dll
|
||||
fastfetch.exe
|
||||
flashfetch.exe
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
name: submit signing request
|
||||
uses: signpath/github-action-submit-signing-request@v1
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '${{ vars.SIGNPATH_ORG_ID }}'
|
||||
project-slug: 'fastfetch'
|
||||
signing-policy-slug: ${{ github.ref == 'refs/heads/master' && 'release-signing' || 'test-signing' }}
|
||||
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: '.'
|
||||
|
||||
- name: create zip archive
|
||||
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: create 7z archive
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}
|
||||
path: ./fastfetch-windows-${{ matrix.arch }}.*
|
||||
overwrite: true
|
||||
uses: ./.github/workflows/build-windows-hosts.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
msystem: ${{ matrix.msystem }}
|
||||
msystem-lower: ${{ matrix.msystem-lower }}
|
||||
msys-arch: ${{ matrix.msys-arch }}
|
||||
secrets: inherit
|
||||
|
||||
release:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- linux-hosts
|
||||
- linux-i686
|
||||
@@ -810,49 +195,7 @@ jobs:
|
||||
- windows-hosts
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: get latest release version
|
||||
id: get_version_release
|
||||
uses: pozetroninc/github-action-get-latest-release@master
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
- name: download artifacts
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: actions/download-artifact@v8
|
||||
|
||||
- name: create release
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ needs.linux-hosts.outputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
artifactErrorsFailBuild: true
|
||||
artifacts: fastfetch-*/fastfetch-*
|
||||
body: "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ needs.linux-hosts.outputs.ffversion }}/CHANGELOG.md) for details."
|
||||
|
||||
- name: download source tarballs
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
for i in 1 2 3 4 5; do curl -L --remote-name-all --output-dir fastfetch-source --create-dirs https://github.com/${{ github.repository }}/archive/refs/tags/${{ needs.linux-hosts.outputs.ffversion }}.{tar.gz,zip} && break || sleep 5; done
|
||||
ls fastfetch-*/*
|
||||
|
||||
- name: generate release notes
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
echo "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ needs.linux-hosts.outputs.ffversion }}/CHANGELOG.md) for details." > fastfetch-release-notes.md
|
||||
echo -e "\n---\n\n<details><summary>SHA256SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha256sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
echo -e "\n<details><summary>SHA512SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha512sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
|
||||
- name: update release body
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ needs.linux-hosts.outputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
bodyFile: fastfetch-release-notes.md
|
||||
allowUpdates: true
|
||||
uses: ./.github/workflows/build-release.yml
|
||||
with:
|
||||
ffversion: ${{ needs.linux-hosts.outputs.ffversion }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -1,3 +1,85 @@
|
||||
# 2.63.0
|
||||
|
||||
Changes:
|
||||
* Introduces a new optional dependency, `libefl`, for querying the Enlightenment window manager configuration:
|
||||
* `libefl-all-dev` on Debian/Ubuntu
|
||||
* `libefl-devel` on Fedora
|
||||
* `efl` on Arch Linux
|
||||
* The Windows-specific options `battery.useSetupApi` and `global.wmiTimeout` have been removed. (Windows)
|
||||
|
||||
Features:
|
||||
* Adds wallpaper detection support on Haiku (#2314, Wallpaper, Haiku)
|
||||
* Adds Wi-Fi 6 GHz channel detection and improves protocol reporting (Wifi, Linux)
|
||||
* Improves Deepin version detection using `/etc/os-version` (#2300, OS, Linux)
|
||||
* Adds Ubuntu Kylin and Ubuntu Unity flavor detection (OS, Linux)
|
||||
* Adds NebiDE support (WMTheme, Linux)
|
||||
* Adds package counting for `cards` on NuTyX (#2287, Packages, Linux)
|
||||
* Adds support for the Enlightenment desktop environment (#2165, WM, Linux)
|
||||
* Adds support for playback progress detection (Media)
|
||||
* The module now prints the current playback position and total media duration when supported by the player. If you prefer the previous behavior, you can set `media.percent.type: ["hide-others"]` to hide the new fields.
|
||||
* Adds support for `global.playerName` for Windows (Media, Windows)
|
||||
* This allows detecting a specified media player while ignoring others.
|
||||
* Multi-battery detection is enabled on Windows by default (Battery, Windows)
|
||||
|
||||
Bugfixes:
|
||||
* Improves DisplayServer compatibility on Linux by handling newer `kde-output-device-v2` protocol updates (DisplayServer, Linux)
|
||||
* This fixes a long-standing issue where display detection fails with an `interface 'kde_output_device_mode_v2' has no event X` error.
|
||||
* Improves Wi-Fi reliability on Linux by switching to a netlink implementation (Wifi, Linux)
|
||||
* Improves network interface reliability and default route selection on macOS and Windows (Netif, macOS / Windows)
|
||||
* Validates temperature color thresholds as integers when parsing JSON configs (Temps)
|
||||
* Fixes TDE Konsole version detection (#2319, Terminal, Linux)
|
||||
* Various internal cleanups and optimizations
|
||||
|
||||
Logos:
|
||||
* Adds KibaOS, NebiOS, XJ380, openRuyi, Ximper
|
||||
* Fixes GNOME OS built-in logo detection (#2296)
|
||||
|
||||
# 2.62.1
|
||||
|
||||
Bugfixes:
|
||||
* Fixes the Host module not working on some devices (#2279, Host, Linux)
|
||||
* Regression from v2.61.0
|
||||
|
||||
Logos:
|
||||
* Adds EN-OS, LimeOS, Redrose and Uzbek
|
||||
|
||||
# 2.62.0
|
||||
|
||||
Changes:
|
||||
* Sort package managers alphabetically in output and format arguments (Packages)
|
||||
* The positional order of format arguments has changed — this is a breaking change for users relying on numeric placeholders. Named arguments are always preferred; e.g., use `{pacman}` (refer to `fastfetch -h packages-format`) instead of `{1}` to keep your config future-proof.
|
||||
|
||||
Features:
|
||||
* Reports `Unused` instead of `Disabled` for zero-size swap on macOS (#2248, Swap, macOS)
|
||||
* Improves the robustness of default route detection on Linux (#2252, LocalIP, Linux)
|
||||
* PhysicalDisk module improvements
|
||||
* Detects virtual disks and adds support for hiding them via `hideVirtual` option (defaults to `false`)
|
||||
* Detects unused/disconnected disks and adds support for hiding them via `hideUnused` option (defaults to `true`)
|
||||
* Adds support for OpenBSD and NetBSD (root privileges are required to access disk information)
|
||||
* Improves interconnect type detection on SunOS
|
||||
* Adds floppy drive support on Windows
|
||||
* Rewrites Windows GPU detection with D3DKMT APIs, unifying it with WSL GPU detection (GPU, Windows)
|
||||
* Verifies that the battery and power adapter are present before detection (Battery / PowerAdapter, Linux)
|
||||
* Improves PhysicalMemory reliability on OpenBSD and FreeBSD, including support for x86 UEFI systems on OpenBSD and non-UEFI systems on FreeBSD (PhysicalMemory, OpenBSD / FreeBSD)
|
||||
* Adds `display.common.(ndigits|spaceBeforeUnit)` options to configure common formatting for duration, percent, size, frequency and temperature values (#2234)
|
||||
* Slightly improves performance by skipping probes for ignored device types (Sound)
|
||||
* Adds support for AppImage package detection (#2179, Packages, Linux)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes a potential crash on certain Intel GPU models when using `--gpu-driver-specific` (#2259, GPU, Linux)
|
||||
* Doesn't report partitions as physical disks (DiskIO, NetBSD)
|
||||
* Fixes a typo (#2260, Wifi, macOS)
|
||||
* Corrects documentation for zpool format placeholders (#2261, Zpool)
|
||||
* Restores Windows 8.1 support (Cursor, Windows)
|
||||
* Fixes an out-of-bounds memory access when swaps are disabled (Swap, Windows)
|
||||
* Fixes battery status being incorrectly reported as `Charging` on systems with multiple batteries (#2263, Battery, Linux)
|
||||
* Fixes ash shell version detection (#2271, Shell, Linux)
|
||||
* Uses the `iterm` image protocol automatically on iTerm terminals for auto logo type (Logo)
|
||||
* Adds missing options when generating full config file
|
||||
|
||||
Logos:
|
||||
* Updates AerynOS (#2272)
|
||||
|
||||
# 2.61.0
|
||||
|
||||
Changes:
|
||||
|
||||
+39
-42
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.61.0
|
||||
VERSION 2.63.0
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Fast neofetch-like system information tool"
|
||||
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
|
||||
@@ -77,6 +77,7 @@ cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBS
|
||||
cmake_dependent_option(ENABLE_DRM_AMDGPU "Enable libdrm_amdgpu" ON "LINUX OR FreeBSD OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_DCONF "Enable dconf" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_EET "Enable eet" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_DBUS "Enable dbus-1" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR Haiku OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_SQLITE3 "Enable sqlite3" ON "LINUX OR FreeBSD OR APPLE OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_RPM "Enable rpm" ON "LINUX OR GNU" OFF)
|
||||
@@ -119,7 +120,7 @@ if(NOT BINARY_LINK_TYPE IN_LIST BINARY_LINK_TYPE_OPTIONS)
|
||||
message(FATAL_ERROR "BINARY_LINK_TYPE must be one of ${BINARY_LINK_TYPE_OPTIONS}")
|
||||
endif()
|
||||
|
||||
set(PACKAGE_MANAGERS AM APK BREW CHOCO DPKG EMERGE EOPKG FLATPAK GUIX LINGLONG LPKG LPKGBUILD MACPORTS NIX OPKG PACMAN PACSTALL PALUDIS PISI PKG PKGTOOL RPM SCOOP SNAP SOAR SORCERY WINGET XBPS)
|
||||
set(PACKAGE_MANAGERS AM APPIMAGE APK BREW CHOCO DPKG EMERGE EOPKG FLATPAK GUIX LINGLONG LPKG LPKGBUILD MACPORTS NIX OPKG PACMAN PACSTALL PALUDIS PISI PKG PKGTOOL RPM SCOOP SNAP SOAR SORCERY WINGET XBPS)
|
||||
foreach(package_manager ${PACKAGE_MANAGERS})
|
||||
if(package_manager STREQUAL "WINGET")
|
||||
option(PACKAGES_DISABLE_${package_manager} "Disable ${package_manager} package manager detection by default" ON)
|
||||
@@ -160,7 +161,11 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=incompatible-pointe
|
||||
|
||||
if(WIN32 OR Haiku)
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
if(WIN32)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
endif()
|
||||
|
||||
@@ -401,7 +406,7 @@ set(LIBFASTFETCH_SRC
|
||||
src/common/impl/FFstrbuf.c
|
||||
src/common/impl/path.c
|
||||
src/common/impl/FFPlatform.c
|
||||
src/common/impl/smbiosHelper.c
|
||||
src/common/impl/smbios.c
|
||||
src/detection/bluetoothradio/bluetoothradio.c
|
||||
src/detection/bootmgr/bootmgr.c
|
||||
src/detection/chassis/chassis.c
|
||||
@@ -561,7 +566,7 @@ if(LINUX)
|
||||
src/detection/gpu/gpu_linux.c
|
||||
src/detection/gpu/gpu_drm.c
|
||||
src/detection/gpu/gpu_pci.c
|
||||
src/detection/gpu/gpu_wsl.c
|
||||
src/detection/gpu/gpu_windows.c
|
||||
src/detection/gtk_qt/gtk.c
|
||||
src/detection/host/host_linux.c
|
||||
src/detection/icons/icons_linux.c
|
||||
@@ -793,7 +798,7 @@ elseif(NetBSD)
|
||||
src/detection/cursor/cursor_linux.c
|
||||
src/detection/disk/disk_bsd.c
|
||||
src/detection/dns/dns_linux.c
|
||||
src/detection/physicaldisk/physicaldisk_nosupport.c
|
||||
src/detection/physicaldisk/physicaldisk_nbsd.c
|
||||
src/detection/physicalmemory/physicalmemory_linux.c
|
||||
src/detection/diskio/diskio_nbsd.c
|
||||
src/detection/displayserver/linux/displayserver_linux.c
|
||||
@@ -860,7 +865,7 @@ elseif(OpenBSD)
|
||||
src/common/impl/sysctl.c
|
||||
src/common/impl/FFPlatform_unix.c
|
||||
src/common/impl/binary_linux.c
|
||||
src/common/impl/smbiosHelper.c
|
||||
src/common/impl/smbios.c
|
||||
src/common/impl/kmod_nosupport.c
|
||||
src/detection/battery/battery_obsd.c
|
||||
src/detection/bios/bios_windows.c
|
||||
@@ -877,7 +882,7 @@ elseif(OpenBSD)
|
||||
src/detection/cursor/cursor_linux.c
|
||||
src/detection/disk/disk_bsd.c
|
||||
src/detection/dns/dns_linux.c
|
||||
src/detection/physicaldisk/physicaldisk_nosupport.c
|
||||
src/detection/physicaldisk/physicaldisk_obsd.c
|
||||
src/detection/physicalmemory/physicalmemory_linux.c
|
||||
src/detection/diskio/diskio_obsd.c
|
||||
src/detection/displayserver/linux/displayserver_linux.c
|
||||
@@ -1029,20 +1034,18 @@ elseif(WIN32)
|
||||
src/common/impl/debug_windows.c
|
||||
src/common/impl/kmod_windows.c
|
||||
src/common/windows/getline.c
|
||||
src/common/windows/com.cpp
|
||||
src/common/windows/com.c
|
||||
src/common/windows/registry.c
|
||||
src/common/windows/unicode.c
|
||||
src/common/windows/wmi.cpp
|
||||
src/common/windows/variant.cpp
|
||||
src/common/windows/version.c
|
||||
src/detection/battery/battery_windows.c
|
||||
src/detection/bios/bios_windows.c
|
||||
src/detection/bluetooth/bluetooth_windows.c
|
||||
src/detection/bluetooth/bluetooth_windows.cpp
|
||||
src/detection/bluetoothradio/bluetoothradio_windows.c
|
||||
src/detection/board/board_windows.c
|
||||
src/detection/bootmgr/bootmgr_windows.c
|
||||
src/detection/brightness/brightness_windows.cpp
|
||||
src/detection/brightness/brightness_windows.c
|
||||
src/detection/btrfs/btrfs_nosupport.c
|
||||
src/detection/chassis/chassis_windows.c
|
||||
src/detection/cpu/cpu_windows.c
|
||||
@@ -1067,7 +1070,7 @@ elseif(WIN32)
|
||||
src/detection/locale/locale_windows.c
|
||||
src/detection/localip/localip_windows.c
|
||||
src/detection/gamepad/gamepad_windows.c
|
||||
src/detection/media/media_windows.c
|
||||
src/detection/media/media_windows.cpp
|
||||
src/detection/memory/memory_windows.c
|
||||
src/detection/mouse/mouse_windows.c
|
||||
src/detection/physicalmemory/physicalmemory_linux.c
|
||||
@@ -1238,7 +1241,7 @@ elseif(Haiku)
|
||||
src/detection/tpm/tpm_nosupport.c
|
||||
src/detection/uptime/uptime_haiku.c
|
||||
src/detection/users/users_linux.c
|
||||
src/detection/wallpaper/wallpaper_nosupport.c
|
||||
src/detection/wallpaper/wallpaper_haiku.cpp
|
||||
src/detection/wifi/wifi_nosupport.c
|
||||
src/detection/wm/wm_nosupport.c
|
||||
src/detection/de/de_nosupport.c
|
||||
@@ -1334,6 +1337,14 @@ endif()
|
||||
if(WIN32)
|
||||
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_intel.c)
|
||||
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_amd.c)
|
||||
|
||||
include(CheckIncludeFileCXX)
|
||||
CHECK_INCLUDE_FILE_CXX("winrt/Windows.Foundation.h" HAVE_WINRT)
|
||||
if(HAVE_WINRT)
|
||||
message(STATUS "WinRT headers are available, media detection will be enabled")
|
||||
else()
|
||||
message(WARNING "WinRT headers are NOT available, media detection will be disabled")
|
||||
endif()
|
||||
endif()
|
||||
include(CheckFunctionExists)
|
||||
check_function_exists(wcwidth HAVE_WCWIDTH)
|
||||
@@ -1436,7 +1447,7 @@ if(LINUX)
|
||||
elseif(ANDROID)
|
||||
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE _FILE_OFFSET_BITS=64 "$<$<CONFIG:DEBUG>:__BIONIC_FORTIFY>" "$<$<CONFIG:DEBUG>:__BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED>")
|
||||
elseif(WIN32)
|
||||
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 NOMINMAX UNICODE) # "$<$<CONFIG:Release>:_FORTIFY_SOURCE=3>"
|
||||
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN WINRT_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 NOMINMAX UNICODE) # "$<$<CONFIG:Release>:_FORTIFY_SOURCE=3>"
|
||||
elseif(APPLE)
|
||||
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE __STDC_WANT_LIB_EXT1__ _FILE_OFFSET_BITS=64 _DARWIN_C_SOURCE)
|
||||
elseif(OpenBSD)
|
||||
@@ -1604,6 +1615,10 @@ ff_lib_enable(DCONF
|
||||
"dconf"
|
||||
"DConf"
|
||||
)
|
||||
ff_lib_enable(EET
|
||||
"eet"
|
||||
"Eet"
|
||||
)
|
||||
ff_lib_enable(DBUS
|
||||
"dbus-1"
|
||||
"DBus"
|
||||
@@ -1747,6 +1762,11 @@ elseif(WIN32)
|
||||
PRIVATE "mincore"
|
||||
)
|
||||
endif()
|
||||
if(HAVE_WINRT)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "runtimeobject"
|
||||
)
|
||||
endif()
|
||||
elseif(FreeBSD)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "m"
|
||||
@@ -1767,12 +1787,14 @@ elseif(OpenBSD)
|
||||
PRIVATE "m"
|
||||
PRIVATE "kvm"
|
||||
PRIVATE "sndio"
|
||||
PRIVATE "util"
|
||||
)
|
||||
elseif(NetBSD)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "bluetooth"
|
||||
PRIVATE "m"
|
||||
PRIVATE "prop"
|
||||
PRIVATE "util"
|
||||
)
|
||||
elseif(SunOS)
|
||||
target_link_libraries(libfastfetch
|
||||
@@ -1831,20 +1853,6 @@ target_link_libraries(libfastfetch
|
||||
target_compile_options(libfastfetch PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions -fno-rtti>)
|
||||
|
||||
if(WIN32)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
include(CheckIncludeFileCXX)
|
||||
CHECK_INCLUDE_FILE_CXX("winrt/Windows.Foundation.h" HAVE_WINRT)
|
||||
if(HAVE_WINRT)
|
||||
add_library(ffwinrt MODULE src/detection/media/media_windows.dll.cpp)
|
||||
target_link_libraries(ffwinrt PRIVATE "RuntimeObject")
|
||||
target_compile_definitions(ffwinrt PRIVATE WIN32_LEAN_AND_MEAN=1 WINRT_LEAN_AND_MEAN=1)
|
||||
target_link_options(ffwinrt
|
||||
PRIVATE "-static" # stdc++, winpthread, gcc_s, etc.
|
||||
)
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
if(FreeBSD)
|
||||
set(CMAKE_REQUIRED_INCLUDES "/usr/local/include" "/usr/include")
|
||||
endif()
|
||||
@@ -1854,12 +1862,6 @@ if(LINUX OR FreeBSD OR OpenBSD OR NetBSD)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LINUX_VIDEODEV2=1)
|
||||
endif()
|
||||
endif()
|
||||
if(LINUX)
|
||||
CHECK_INCLUDE_FILE("linux/wireless.h" HAVE_LINUX_WIRELESS)
|
||||
if(HAVE_LINUX_WIRELESS)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LINUX_WIRELESS=1)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT WIN32)
|
||||
CHECK_INCLUDE_FILE("utmpx.h" HAVE_UTMPX)
|
||||
if(HAVE_UTMPX)
|
||||
@@ -1891,6 +1893,8 @@ if(NOT WIN32)
|
||||
message(WARNING "pthread_timedjoin_np was not found; networking timeout will not work")
|
||||
endif()
|
||||
endif()
|
||||
elseif(HAVE_WINRT)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WINRT=1)
|
||||
endif()
|
||||
|
||||
set(PACKAGES_DISABLE_LIST "")
|
||||
@@ -2037,13 +2041,6 @@ if (TARGET flashfetch)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (TARGET ffwinrt)
|
||||
install(
|
||||
TARGETS ffwinrt
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
install(
|
||||
FILES "${CMAKE_SOURCE_DIR}/completions/${CMAKE_PROJECT_NAME}.bash"
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions"
|
||||
|
||||
Vendored
+12
@@ -1,3 +1,15 @@
|
||||
fastfetch (2.62.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.62.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 22 Apr 2026 09:49:55 +0800
|
||||
|
||||
fastfetch (2.61.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.61.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 30 Mar 2026 09:51:18 +0800
|
||||
|
||||
fastfetch (2.58.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.58.0
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ Source: fastfetch
|
||||
Section: universe/utils
|
||||
Priority: optional
|
||||
Maintainer: Carter Li <zhangsongcui@live.cn>
|
||||
Build-Depends: libelf-dev, libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build, python3-setuptools
|
||||
Build-Depends: libelf-dev, libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build, python3-setuptools
|
||||
Standards-Version: 4.0.0
|
||||
Homepage: https://github.com/fastfetch-cli/fastfetch
|
||||
|
||||
|
||||
+312
-289
File diff suppressed because it is too large
Load Diff
+141
-88
@@ -21,7 +21,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
/// \file adl_defines.h
|
||||
/// \brief Contains all definitions exposed by ADL for \ALL platforms.\n <b>Included in ADL SDK</b>
|
||||
/// \brief Contains all definitions exposed by ADL for \WIN platforms.\n <b>Included in ADL SDK</b>
|
||||
///
|
||||
/// This file contains all definitions used by ADL.
|
||||
/// The ADL definitions include the following:
|
||||
@@ -61,6 +61,8 @@
|
||||
#define ADL_ADAPTER_INDEX_ALL -1
|
||||
/// Defines APIs with iOption none
|
||||
#define ADL_MAIN_API_OPTION_NONE 0
|
||||
/// Defines the maximum of RPC endpoint
|
||||
#define ADL_MAX_RPC_ENDPOINT 32
|
||||
/// @}
|
||||
|
||||
/// \name Definitions for iOption parameter used by
|
||||
@@ -141,6 +143,12 @@
|
||||
#define ADL_ERR_FEATURESYNC_NOT_STARTED -24
|
||||
/// Adapter is in an invalid power state
|
||||
#define ADL_ERR_INVALID_POWER_STATE -25
|
||||
/// The RPC server is in busy state
|
||||
#define ADL_ERR_SERVER_BUSY -26
|
||||
/// The GPU is occupied by application which cannot be powered off
|
||||
#define ADL_ERR_GPU_IN_USE -27
|
||||
/// The general RPC connection error
|
||||
#define ADL_ERR_RPC -28
|
||||
|
||||
/// @}
|
||||
/// </A>
|
||||
@@ -380,6 +388,7 @@
|
||||
#define ADL_BUSTYPE_PCIE_GEN2 3 /* PCI Express 2nd generation bus */
|
||||
#define ADL_BUSTYPE_PCIE_GEN3 4 /* PCI Express 3rd generation bus */
|
||||
#define ADL_BUSTYPE_PCIE_GEN4 5 /* PCI Express 4th generation bus */
|
||||
#define ADL_BUSTYPE_PCIE_GEN5 6 /* PCI Express 5th generation bus */
|
||||
/// @}
|
||||
|
||||
/// \defgroup define_ws_caps Workstation Capabilities
|
||||
@@ -872,7 +881,7 @@
|
||||
typedef enum ADLThreadingModel
|
||||
{
|
||||
ADL_THREADING_UNLOCKED = 0, /*!< Default behavior. ADL will not enforce serialization of ADL API executions by multiple threads. Multiple threads will be allowed to enter to ADL at the same time. Note that ADL library is not guaranteed to be thread-safe. Client that calls ADL_Main_Control_Create have to provide its own mechanism for ADL calls serialization. */
|
||||
ADL_THREADING_LOCKED /*!< ADL will enforce serialization of ADL API when called by multiple threads. Only single thread will be allowed to enter ADL API at the time. This option makes ADL calls thread-safe. You shouldn't use this option if ADL calls will be executed on Linux on x-server rendering thread. It can cause the application to hung. */
|
||||
ADL_THREADING_LOCKED /*!< ADL will enforce serialization of ADL API when called by multiple threads. Only single thread will be allowed to enter ADL API at the time. This option makes ADL calls thread-safe. */
|
||||
}ADLThreadingModel;
|
||||
|
||||
/// @}
|
||||
@@ -1844,12 +1853,12 @@ enum ADLOD8FeatureControl
|
||||
ADL_OD8_TEMPERATURE_FAN = 1 << 6, //FanTargetTemperature
|
||||
ADL_OD8_TEMPERATURE_SYSTEM = 1 << 7, //MaxOpTemp
|
||||
ADL_OD8_MEMORY_TIMING_TUNE = 1 << 8,
|
||||
ADL_OD8_FAN_ZERO_RPM_CONTROL = 1 << 9 ,
|
||||
ADL_OD8_AUTO_UV_ENGINE = 1 << 10, //Auto under voltage
|
||||
ADL_OD8_AUTO_OC_ENGINE = 1 << 11, //Auto overclock engine
|
||||
ADL_OD8_AUTO_OC_MEMORY = 1 << 12, //Auto overclock memory
|
||||
ADL_OD8_FAN_CURVE = 1 << 13, //Fan curve
|
||||
ADL_OD8_WS_AUTO_FAN_ACOUSTIC_LIMIT = 1 << 14, //Workstation Manual Fan controller
|
||||
ADL_OD8_FAN_ZERO_RPM_CONTROL = 1 << 9,
|
||||
ADL_OD8_AUTO_UV_ENGINE = 1 << 10, //Auto under voltage
|
||||
ADL_OD8_AUTO_OC_ENGINE = 1 << 11, //Auto overclock engine
|
||||
ADL_OD8_AUTO_OC_MEMORY = 1 << 12, //Auto overclock memory
|
||||
ADL_OD8_FAN_CURVE = 1 << 13, //Fan curve
|
||||
ADL_OD8_WS_AUTO_FAN_ACOUSTIC_LIMIT = 1 << 14, //Workstation Manual Fan controller
|
||||
ADL_OD8_GFXCLK_QUADRATIC_CURVE = 1 << 15,
|
||||
ADL_OD8_OPTIMIZED_GPU_POWER_MODE = 1 << 16,
|
||||
ADL_OD8_ODVOLTAGE_LIMIT = 1 << 17,
|
||||
@@ -1860,43 +1869,45 @@ enum ADLOD8FeatureControl
|
||||
ADL_OD8_TDC_LIMIT = 1 << 22, //TDC slider
|
||||
ADL_OD8_FULL_CONTROL_MODE = 1 << 23, //Full control
|
||||
ADL_OD8_POWER_SAVING_FEATURE_CONTROL = 1 << 24, //Power saving feature control
|
||||
ADL_OD8_POWER_GAUGE = 1 << 25 //Power Gauge
|
||||
ADL_OD8_ACTIMING_PARAMETERS_TUNE = 1 << 25, // AC Timing Parameters Tuning
|
||||
ADL_OD8_OVERDRIVE_INTERFACE = 1 << 26, // Version info feature ID for RSX, do not expose in ADL.
|
||||
ADL_OD8_AUTO_UV_ENGINE_V2 = 1 << 27, // Auto UV 2.0 with actual stress testing.
|
||||
ADL_OD8_POWER_GAUGE = 1 << 28 //Power Gauge
|
||||
};
|
||||
|
||||
|
||||
typedef enum ADLOD8SettingId
|
||||
{
|
||||
OD8_GFXCLK_FMAX = 0,
|
||||
OD8_GFXCLK_FMIN,
|
||||
OD8_GFXCLK_FREQ1,
|
||||
OD8_GFXCLK_VOLTAGE1,
|
||||
OD8_GFXCLK_FREQ2,
|
||||
OD8_GFXCLK_VOLTAGE2,
|
||||
OD8_GFXCLK_FREQ3,
|
||||
OD8_GFXCLK_VOLTAGE3,
|
||||
OD8_UCLK_FMAX,
|
||||
OD8_POWER_PERCENTAGE,
|
||||
OD8_FAN_MIN_SPEED,
|
||||
OD8_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_FAN_TARGET_TEMP,
|
||||
OD8_OPERATING_TEMP_MAX,
|
||||
OD8_AC_TIMING,
|
||||
OD8_FAN_ZERORPM_CONTROL,
|
||||
OD8_AUTO_UV_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_MEMORY_CONTROL,
|
||||
OD8_FAN_CURVE_TEMPERATURE_1,
|
||||
OD8_FAN_CURVE_SPEED_1,
|
||||
OD8_FAN_CURVE_TEMPERATURE_2,
|
||||
OD8_FAN_CURVE_SPEED_2,
|
||||
OD8_FAN_CURVE_TEMPERATURE_3,
|
||||
OD8_FAN_CURVE_SPEED_3,
|
||||
OD8_FAN_CURVE_TEMPERATURE_4,
|
||||
OD8_FAN_CURVE_SPEED_4,
|
||||
OD8_FAN_CURVE_TEMPERATURE_5,
|
||||
OD8_FAN_CURVE_SPEED_5,
|
||||
OD8_WS_FAN_AUTO_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_A, // As part of the agreement with UI team, the min/max voltage limits for the
|
||||
OD8_GFXCLK_FMAX = 0,
|
||||
OD8_GFXCLK_FMIN,
|
||||
OD8_GFXCLK_FREQ1,
|
||||
OD8_GFXCLK_VOLTAGE1,
|
||||
OD8_GFXCLK_FREQ2,
|
||||
OD8_GFXCLK_VOLTAGE2,
|
||||
OD8_GFXCLK_FREQ3,
|
||||
OD8_GFXCLK_VOLTAGE3,
|
||||
OD8_UCLK_FMAX,
|
||||
OD8_POWER_PERCENTAGE,
|
||||
OD8_FAN_MIN_SPEED, //10
|
||||
OD8_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_FAN_TARGET_TEMP,
|
||||
OD8_OPERATING_TEMP_MAX,
|
||||
OD8_AC_TIMING,
|
||||
OD8_FAN_ZERORPM_CONTROL,
|
||||
OD8_AUTO_UV_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_MEMORY_CONTROL,
|
||||
OD8_FAN_CURVE_TEMPERATURE_1,
|
||||
OD8_FAN_CURVE_SPEED_1, //20
|
||||
OD8_FAN_CURVE_TEMPERATURE_2,
|
||||
OD8_FAN_CURVE_SPEED_2,
|
||||
OD8_FAN_CURVE_TEMPERATURE_3,
|
||||
OD8_FAN_CURVE_SPEED_3,
|
||||
OD8_FAN_CURVE_TEMPERATURE_4,
|
||||
OD8_FAN_CURVE_SPEED_4,
|
||||
OD8_FAN_CURVE_TEMPERATURE_5,
|
||||
OD8_FAN_CURVE_SPEED_5,
|
||||
OD8_WS_FAN_AUTO_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_A, // 30 As part of the agreement with UI team, the min/max voltage limits for the
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_B, // quadratic curve graph will be stored in the min and max limits of
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_C, // coefficient a, b and c. A, b and c themselves do not have limits.
|
||||
OD8_GFXCLK_CURVE_VFT_FMIN,
|
||||
@@ -1906,7 +1917,7 @@ typedef enum ADLOD8SettingId
|
||||
OD8_OD_VOLTAGE,// RSX - voltage offset feature
|
||||
OD8_ADV_OC_LIMITS_SETTING,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_1,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_2,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_2, //40
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_3,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_4,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_5,
|
||||
@@ -1915,8 +1926,33 @@ typedef enum ADLOD8SettingId
|
||||
OD8_GFX_VOLTAGE_LIMIT_SETTING,
|
||||
OD8_TDC_PERCENTAGE,
|
||||
OD8_FULL_CONTROL_MODE_SETTING,
|
||||
OD8_FULL_CONTROL_MODE_GFXCLK,
|
||||
OD8_FULL_CONTROL_MODE_UCLK, // 50,
|
||||
OD8_IDLE_POWER_SAVING_FEATURE_CONTROL,
|
||||
OD8_RUNTIME_POWER_SAVING_FEATURE_CONTROL,
|
||||
OD8_FULL_CONTROL_MODE_FEATURE_CONTROL,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_1,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_2,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_3,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_4,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_5,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_6,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_VOLTAGE_LIMIT, //60
|
||||
OD8_ACTIMING_PARAMETER_TRRDS,
|
||||
OD8_ACTIMING_PARAMETER_TCL,
|
||||
OD8_ACTIMING_PARAMETER_TCWL,
|
||||
OD8_ACTIMING_PARAMETER_TRCDRD,
|
||||
OD8_ACTIMING_PARAMETER_TRCDWR,
|
||||
OD8_ACTIMING_PARAMETER_TRAS,
|
||||
OD8_ACTIMING_PARAMETER_TRPAB,
|
||||
OD8_ACTIMING_PARAMETER_TRFC,
|
||||
OD8_ACTIMING_PARAMETER_TRFCPB,
|
||||
OD8_ACTIMING_PARAMETER_TRREFD, //70
|
||||
OD8_ACTIMING_PARAMETER_TREF,
|
||||
OD8_ACTIMING_PARAMETER_TWR,
|
||||
OD8_ACTIMING_PARAMETER_TWTRS,
|
||||
OD8_OVERDRIVE_INTERFACE_ID, //74
|
||||
OD8_AUTO_UV_ENGINE_V2_ID, //75
|
||||
OD8_POWER_GAUGE,
|
||||
OD8_COUNT
|
||||
} ADLOD8SettingId;
|
||||
@@ -1980,28 +2016,28 @@ typedef enum ADLSensorType
|
||||
PMLOG_SSDGPU_POWERLIMIT = 49, // DGPU Power limit
|
||||
PMLOG_TEMPERATURE_HOTSPOT_GCD = 50,
|
||||
PMLOG_TEMPERATURE_HOTSPOT_MCD = 51,
|
||||
PMLOG_THROTTLER_TEMP_EDGE_PERCENTAGE = 52,
|
||||
PMLOG_THROTTLER_TEMP_HOTSPOT_PERCENTAGE = 53,
|
||||
PMLOG_THROTTLER_TEMP_HOTSPOT_GCD_PERCENTAGE = 54,
|
||||
PMLOG_THROTTLER_TEMP_HOTSPOT_MCD_PERCENTAGE = 55,
|
||||
PMLOG_THROTTLER_TEMP_MEM_PERCENTAGE = 56,
|
||||
PMLOG_THROTTLER_TEMP_VR_GFX_PERCENTAGE = 57,
|
||||
PMLOG_THROTTLER_TEMP_VR_MEM0_PERCENTAGE = 58,
|
||||
PMLOG_THROTTLER_TEMP_VR_MEM1_PERCENTAGE = 59,
|
||||
PMLOG_THROTTLER_TEMP_VR_SOC_PERCENTAGE = 60,
|
||||
PMLOG_THROTTLER_TEMP_LIQUID0_PERCENTAGE = 61,
|
||||
PMLOG_THROTTLER_TEMP_LIQUID1_PERCENTAGE = 62,
|
||||
PMLOG_THROTTLER_TEMP_PLX_PERCENTAGE = 63,
|
||||
PMLOG_THROTTLER_TDC_GFX_PERCENTAGE = 64,
|
||||
PMLOG_THROTTLER_TDC_SOC_PERCENTAGE = 65,
|
||||
PMLOG_THROTTLER_TDC_USR_PERCENTAGE = 66,
|
||||
PMLOG_THROTTLER_PPT0_PERCENTAGE = 67,
|
||||
PMLOG_THROTTLER_PPT1_PERCENTAGE = 68,
|
||||
PMLOG_THROTTLER_PPT2_PERCENTAGE = 69,
|
||||
PMLOG_THROTTLER_PPT3_PERCENTAGE = 70,
|
||||
PMLOG_THROTTLER_FIT_PERCENTAGE = 71,
|
||||
PMLOG_THROTTLER_GFX_APCC_PLUS_PERCENTAGE = 72,
|
||||
PMLOG_BOARD_POWER = 73,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TEMP_GFX = 52,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TEMP_MEM = 53,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TEMP_VR = 54,
|
||||
PMLOG_THROTTLE_PERCENTAGE_POWER = 55,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TDC = 56,
|
||||
PMLOG_THROTTLE_PERCENTAGE_VMAX = 57,
|
||||
PMLOG_BUS_CURR_MAX_SPEED = 58,
|
||||
PMLOG_RESERVED_1 = 59, //Currently Unused
|
||||
PMLOG_RESERVED_2 = 60, //Currently Unused
|
||||
PMLOG_RESERVED_3 = 61, //Currently Unused
|
||||
PMLOG_RESERVED_4 = 62, //Currently Unused
|
||||
PMLOG_RESERVED_5 = 63, //Currently Unused
|
||||
PMLOG_RESERVED_6 = 64, //Currently Unused
|
||||
PMLOG_RESERVED_7 = 65, //Currently Unused
|
||||
PMLOG_RESERVED_8 = 66, //Currently Unused
|
||||
PMLOG_RESERVED_9 = 67, //Currently Unused
|
||||
PMLOG_RESERVED_10 = 68, //Currently Unused
|
||||
PMLOG_RESERVED_11 = 69, //Currently Unused
|
||||
PMLOG_RESERVED_12 = 70, //Currently Unused
|
||||
PMLOG_RESERVED_13 = 71, //Currently Unused
|
||||
PMLOG_RESERVED_14 = 72,
|
||||
PMLOG_BOARD_POWER = 73,
|
||||
PMLOG_MAX_SENSORS_REAL
|
||||
} ADLSensorType;
|
||||
|
||||
@@ -2068,28 +2104,29 @@ typedef enum ADL_PMLOG_SENSORS
|
||||
ADL_PMLOG_SSDGPU_POWERLIMIT = 49, // DGPU Power limit
|
||||
ADL_PMLOG_TEMPERATURE_HOTSPOT_GCD = 50,
|
||||
ADL_PMLOG_TEMPERATURE_HOTSPOT_MCD = 51,
|
||||
ADL_PMLOG_THROTTLER_TEMP_EDGE_PERCENTAGE = 52,
|
||||
ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_PERCENTAGE = 53,
|
||||
ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_GCD_PERCENTAGE = 54,
|
||||
ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_MCD_PERCENTAGE = 55,
|
||||
ADL_PMLOG_THROTTLER_TEMP_MEM_PERCENTAGE = 56,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_GFX_PERCENTAGE = 57,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_MEM0_PERCENTAGE = 58,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_MEM1_PERCENTAGE = 59,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_SOC_PERCENTAGE = 60,
|
||||
ADL_PMLOG_THROTTLER_TEMP_LIQUID0_PERCENTAGE = 61,
|
||||
ADL_PMLOG_THROTTLER_TEMP_LIQUID1_PERCENTAGE = 62,
|
||||
ADL_PMLOG_THROTTLER_TEMP_PLX_PERCENTAGE = 63,
|
||||
ADL_PMLOG_THROTTLER_TDC_GFX_PERCENTAGE = 64,
|
||||
ADL_PMLOG_THROTTLER_TDC_SOC_PERCENTAGE = 65,
|
||||
ADL_PMLOG_THROTTLER_TDC_USR_PERCENTAGE = 66,
|
||||
ADL_PMLOG_THROTTLER_PPT0_PERCENTAGE = 67,
|
||||
ADL_PMLOG_THROTTLER_PPT1_PERCENTAGE = 68,
|
||||
ADL_PMLOG_THROTTLER_PPT2_PERCENTAGE = 69,
|
||||
ADL_PMLOG_THROTTLER_PPT3_PERCENTAGE = 70,
|
||||
ADL_PMLOG_THROTTLER_FIT_PERCENTAGE = 71,
|
||||
ADL_PMLOG_THROTTLER_GFX_APCC_PLUS_PERCENTAGE = 72,
|
||||
ADL_PMLOG_BOARD_POWER = 73,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TEMP_GFX = 52,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TEMP_MEM = 53,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TEMP_VR = 54,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_POWER = 55,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TDC = 56,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_VMAX = 57,
|
||||
ADL_PMLOG_BUS_CURR_MAX_SPEED = 58,
|
||||
ADL_PMLOG_RESERVED_1 = 59, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_2 = 60, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_3 = 61, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_4 = 62, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_5 = 63, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_6 = 64, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_7 = 65, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_8 = 66, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_9 = 67, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_10 = 68, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_11 = 69, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_12 = 70, //Currently Unused
|
||||
ADL_PMLOG_CLK_NPUCLK = 71, //NPU frequency
|
||||
ADL_PMLOG_NPU_BUSY_AVG = 72, //NPU busy
|
||||
ADL_PMLOG_BOARD_POWER = 73,
|
||||
ADL_PMLOG_TEMPERATURE_INTAKE = 74,
|
||||
ADL_PMLOG_MAX_SENSORS_REAL
|
||||
} ADL_PMLOG_SENSORS;
|
||||
|
||||
@@ -2393,6 +2430,9 @@ typedef enum ADL_PMLOG_SENSORS
|
||||
/// Indicates there is crossGPU clone
|
||||
#define ADL_CROSSGPUDISPLAYCLONE 0x2
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \defgroup define_D3DKMT_HANDLE
|
||||
@@ -2493,7 +2533,11 @@ typedef enum ADL_UIFEATURES_GROUP
|
||||
ADL_UIFEATURES_GROUP_PROVSR = 12,
|
||||
ADL_UIFEATURES_GROUP_SMA = 13,
|
||||
ADL_UIFEATURES_GROUP_CAMERA = 14,
|
||||
ADL_UIFEATURES_GROUP_FRTCPRO = 15
|
||||
ADL_UIFEATURES_GROUP_FRTCPRO = 15,
|
||||
ADL_UIFEATURES_GROUP_DELAGNEXT = 16,
|
||||
ADL_UIFEATURES_GROUP_RTBOOST = 17,
|
||||
ADL_UIFEATURES_GROUP_UAI = 18
|
||||
|
||||
} ADL_UIFEATURES_GROUP;
|
||||
|
||||
|
||||
@@ -2587,9 +2631,18 @@ typedef enum ADL_USER_SETTINGS
|
||||
ADL_USER_SETTINGS_USU_PROFILE = 1 << 4, //notify USU settings change
|
||||
ADL_USER_SETTINGS_CVDC_PROFILE = 1 << 5, //notify Color Vision Deficiency Corretion settings change
|
||||
ADL_USER_SETTINGS_SCE_PROFILE = 1 << 6,
|
||||
ADL_USER_SETTINGS_PROVSR = 1 << 7
|
||||
ADL_USER_SETTINGS_PROVSR = 1 << 7,
|
||||
ADL_USER_SETTINGS_RTBOOST_PROFILE=1 << 8,
|
||||
ADL_USER_SETTINGS_USU2_PROFILE = 1 << 9, //notify USU2 settings change
|
||||
} ADL_USER_SETTINGS;
|
||||
|
||||
//FRAME_TIMESTAMPS_SHARED_MEMORY type
|
||||
typedef enum FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE
|
||||
{
|
||||
FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE_UNKNOWN = 0,
|
||||
FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE_LEGACY,
|
||||
FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE_INDEXED
|
||||
}FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE;
|
||||
#define ADL_REG_DEVICE_FUNCTION_1 0x00000001
|
||||
#endif /* ADL_DEFINES_H_ */
|
||||
|
||||
|
||||
+2
-6
@@ -33,14 +33,10 @@
|
||||
|
||||
#include "adl_structures.h"
|
||||
|
||||
#if defined (LINUX)
|
||||
#define __stdcall
|
||||
#endif /* (LINUX) */
|
||||
|
||||
/// Memory Allocation Call back
|
||||
typedef void* ( __stdcall *ADL_MAIN_MALLOC_CALLBACK )( int );
|
||||
|
||||
#define ADL_SDK_MAJOR_VERSION 17
|
||||
#define ADL_SDK_MINOR_VERSION 1
|
||||
#define ADL_SDK_MAJOR_VERSION 18
|
||||
#define ADL_SDK_MINOR_VERSION 0
|
||||
|
||||
#endif /* ADL_SDK_H_ */
|
||||
|
||||
+29
-36
@@ -21,7 +21,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
/// \file adl_structures.h
|
||||
///\brief This file contains the structure declarations that are used by the public ADL interfaces for \ALL platforms.\n <b>Included in ADL SDK</b>
|
||||
///\brief This file contains the structure declarations that are used by the public ADL interfaces for \WIN platforms.\n <b>Included in ADL SDK</b>
|
||||
///
|
||||
/// All data structures used in AMD Display Library (ADL) public interfaces should be defined in this header file.
|
||||
///
|
||||
@@ -41,7 +41,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct AdapterInfo
|
||||
{
|
||||
/// \ALL_STRUCT_MEM
|
||||
/// \WIN_STRUCT_MEM
|
||||
|
||||
/// Size of the structure.
|
||||
int iSize;
|
||||
@@ -59,7 +59,7 @@ typedef struct AdapterInfo
|
||||
int iVendorID;
|
||||
/// Adapter name.
|
||||
char strAdapterName[ADL_MAX_PATH];
|
||||
/// Display name. For example, "\\\\Display0" for Windows or ":0:0" for Linux.
|
||||
/// Display name. For example, "\\\\Display0" for Windows.
|
||||
char strDisplayName[ADL_MAX_PATH];
|
||||
/// Present or not; 1 if present and 0 if not present.It the logical adapter is present, the display name such as \\\\.\\Display1 can be found from OS
|
||||
int iPresent;
|
||||
@@ -80,38 +80,8 @@ typedef struct AdapterInfo
|
||||
|
||||
#endif /* (_WIN32) || (_WIN64) */
|
||||
|
||||
#if defined (LINUX)
|
||||
/// \LNX_STRUCT_MEM
|
||||
|
||||
/// Internal X screen number from GPUMapInfo (DEPRICATED use XScreenInfo)
|
||||
int iXScreenNum;
|
||||
/// Internal driver index from GPUMapInfo
|
||||
int iDrvIndex;
|
||||
/// \deprecated Internal x config file screen identifier name. Use XScreenInfo instead.
|
||||
char strXScreenConfigName[ADL_MAX_PATH];
|
||||
|
||||
#endif /* (LINUX) */
|
||||
} AdapterInfo, *LPAdapterInfo;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about the Linux X screen information.
|
||||
///
|
||||
/// This structure is used to store the current screen number and xorg.conf ID name assoicated with an adapter index.
|
||||
/// This structure is updated during ADL_Main_Control_Refresh or ADL_ScreenInfo_Update.
|
||||
/// Note: This structure should be used in place of iXScreenNum and strXScreenConfigName in AdapterInfo as they will be
|
||||
/// deprecated.
|
||||
/// \nosubgrouping
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (LINUX)
|
||||
typedef struct XScreenInfo
|
||||
{
|
||||
/// Internal X screen number from GPUMapInfo.
|
||||
int iXScreenNum;
|
||||
/// Internal x config file screen identifier name.
|
||||
char strXScreenConfigName[ADL_MAX_PATH];
|
||||
} XScreenInfo, *LPXScreenInfo;
|
||||
#endif /* (LINUX) */
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about an controller mode
|
||||
///
|
||||
@@ -453,8 +423,12 @@ typedef struct ADLDDCInfo2
|
||||
int ulMaxBacklightMinLuminanceData;
|
||||
int ulMinBacklightMinLuminanceData;
|
||||
|
||||
// Display screen width/height
|
||||
int ulScreenWidth;
|
||||
int ulScreenHeight;
|
||||
|
||||
// Reserved for future use
|
||||
int iReserved[4];
|
||||
int iReserved[2];
|
||||
} ADLDDCInfo2, *LPADLDDCInfo2;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -3614,6 +3588,23 @@ typedef struct ADL_BOOST_SETTINGS
|
||||
int GlobalMinRes_Step; //Gloabl Min Resolution step value
|
||||
}ADL_BOOST_SETTINGS;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about BOOST Settings
|
||||
///
|
||||
/// Elements of BOOST settings.
|
||||
/// \nosubgrouping
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct ADL_BOOST_SETTINGSX2
|
||||
{
|
||||
int Hotkey; // Hotkey value
|
||||
int GlobalEnable; //Global enable value
|
||||
int GlobalMinRes; //Gloabl Min Resolution value
|
||||
int GlobalMinRes_MinLimit; //Gloabl Min Resolution slider min limit value
|
||||
int GlobalMinRes_MaxLimit; //Gloabl Min Resolution slider max limit value
|
||||
int GlobalMinRes_Step; //Gloabl Min Resolution step value
|
||||
int VsrSupported; //Allows for interop with Upscaling/RSR
|
||||
}ADL_BOOST_SETTINGSX2;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about ProVSR Settings change reason
|
||||
@@ -3817,7 +3808,7 @@ typedef struct ADL_RADEON_LED_PATTERN_CONFIG
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct AdapterInfoX2
|
||||
{
|
||||
/// \ALL_STRUCT_MEM
|
||||
/// \WIN_STRUCT_MEM
|
||||
|
||||
/// Size of the structure.
|
||||
int iSize;
|
||||
@@ -4164,6 +4155,7 @@ typedef struct ADLHDCPSettings
|
||||
///
|
||||
/// This structure is used to store the Mantle Driver information
|
||||
/// \nosubgrouping
|
||||
/// \deprecated This structure has been deprecated.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct ADLMantleAppInfo
|
||||
@@ -4286,4 +4278,5 @@ typedef struct ADLSmartShiftSettings
|
||||
int iCurrentValue;
|
||||
int iFlags; //refer to define_smartshift_bits
|
||||
}ADLSmartShiftSettings, *LPADLSmartShiftSettings;
|
||||
#endif /* ADL_STRUCTURES_H_ */
|
||||
|
||||
#endif /* ADL_STRUCTURES_H_ */
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"home": "https://github.com/GPUOpen-LibrariesAndSDKs/display-library",
|
||||
"license": "MIT (embeded in source)",
|
||||
"version": "ADL SDK 17.1",
|
||||
"version": "ADL SDK 18.1",
|
||||
"author": "Advanced Micro Devices, Inc"
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/FFlist.h"
|
||||
|
||||
typedef struct FFPlatformSysinfo
|
||||
{
|
||||
typedef struct FFPlatformSysinfo {
|
||||
FFstrbuf name;
|
||||
FFstrbuf release;
|
||||
FFstrbuf version;
|
||||
@@ -12,8 +11,7 @@ typedef struct FFPlatformSysinfo
|
||||
uint32_t pageSize;
|
||||
} FFPlatformSysinfo;
|
||||
|
||||
typedef struct FFPlatform
|
||||
{
|
||||
typedef struct FFPlatform {
|
||||
FFstrbuf homeDir; // Trailing slash included
|
||||
FFstrbuf cacheDir; // Trailing slash included
|
||||
FFlist configDirs; // List of FFstrbuf, trailing slash included
|
||||
@@ -22,11 +20,11 @@ typedef struct FFPlatform
|
||||
FFstrbuf cwd; // Trailing slash included
|
||||
|
||||
uint32_t pid;
|
||||
#ifndef _WIN32
|
||||
#ifndef _WIN32
|
||||
uint32_t uid;
|
||||
#else
|
||||
#else
|
||||
FFstrbuf sid;
|
||||
#endif
|
||||
#endif
|
||||
FFstrbuf userName;
|
||||
FFstrbuf fullUserName;
|
||||
FFstrbuf hostName;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <sal.h>
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__warn_unused_result__)
|
||||
#define FF_C_NODISCARD __attribute__((__warn_unused_result__))
|
||||
#elif defined(_MSC_VER)
|
||||
#define FF_C_NODISCARD _Check_return_
|
||||
#else
|
||||
#define FF_C_NODISCARD
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__format__)
|
||||
#define FF_C_PRINTF(formatStrIndex, argsStartIndex) __attribute__((__format__ (printf, formatStrIndex, argsStartIndex)))
|
||||
#else
|
||||
#define FF_C_PRINTF(formatStrIndex, argsStartIndex)
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__format__)
|
||||
#define FF_C_SCANF(formatStrIndex, argsStartIndex) __attribute__((__format__ (scanf, formatStrIndex, argsStartIndex)))
|
||||
#else
|
||||
#define FF_C_SCANF(formatStrIndex, argsStartIndex)
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__nonnull__)
|
||||
#define FF_C_NONNULL(argIndex, ...) __attribute__((__nonnull__(argIndex, ##__VA_ARGS__)))
|
||||
#else
|
||||
#define FF_C_NONNULL(argIndex, ...)
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__returns_nonnull__)
|
||||
#define FF_C_RETURNS_NONNULL __attribute__((__returns_nonnull__))
|
||||
#else
|
||||
#define FF_C_RETURNS_NONNULL
|
||||
#endif
|
||||
+66
-69
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "FFcheckmacros.h"
|
||||
#include "common/attributes.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -9,136 +9,133 @@
|
||||
|
||||
#define FF_LIST_DEFAULT_ALLOC 16
|
||||
|
||||
typedef struct FFlist
|
||||
{
|
||||
typedef struct FFlist {
|
||||
uint8_t* data;
|
||||
uint32_t elementSize;
|
||||
uint32_t length;
|
||||
uint32_t capacity;
|
||||
} FFlist;
|
||||
|
||||
void* ffListAdd(FFlist* list);
|
||||
void* ffListAdd(FFlist* list, uint32_t elementSize);
|
||||
|
||||
// Removes the first element, and copy its value to `*result`
|
||||
bool ffListShift(FFlist* list, void* result);
|
||||
bool ffListShift(FFlist* list, uint32_t elementSize, void* __restrict result);
|
||||
// Removes the last element, and copy its value to `*result`
|
||||
bool ffListPop(FFlist* list, void* result);
|
||||
bool ffListPop(FFlist* list, uint32_t elementSize, void* __restrict result);
|
||||
|
||||
static inline void ffListInit(FFlist* list, uint32_t elementSize)
|
||||
{
|
||||
assert(elementSize > 0);
|
||||
list->elementSize = elementSize;
|
||||
static inline void ffListInit(FFlist* list) {
|
||||
list->capacity = 0;
|
||||
list->length = 0;
|
||||
list->data = NULL;
|
||||
}
|
||||
|
||||
static inline void ffListInitA(FFlist* list, uint32_t elementSize, uint32_t capacity)
|
||||
{
|
||||
ffListInit(list, elementSize);
|
||||
static inline void ffListInitA(FFlist* list, uint32_t elementSize, uint32_t capacity) {
|
||||
ffListInit(list);
|
||||
list->capacity = capacity;
|
||||
list->data = __builtin_expect(capacity == 0, 0) ? NULL : (uint8_t*) malloc((size_t)list->capacity * list->elementSize);
|
||||
list->data = __builtin_expect(capacity == 0, 0) ? NULL : (uint8_t*) malloc((size_t) capacity * elementSize);
|
||||
}
|
||||
|
||||
static inline FFlist ffListCreate(uint32_t elementSize)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFlist ffListCreate() {
|
||||
FFlist result;
|
||||
ffListInit(&result, elementSize);
|
||||
ffListInit(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void* ffListGet(const FFlist* list, uint32_t index)
|
||||
{
|
||||
assert(list->capacity > index);
|
||||
return list->data + (index * list->elementSize);
|
||||
FF_A_NODISCARD static inline FFlist ffListCreateA(uint32_t elementSize, uint32_t capacity) {
|
||||
FFlist result;
|
||||
ffListInitA(&result, elementSize, capacity);
|
||||
return result;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline uint32_t ffListFirstIndexComp(const FFlist* list, void* compElement, bool(*compFunc)(const void*, const void*))
|
||||
{
|
||||
for(uint32_t i = 0; i < list->length; i++)
|
||||
{
|
||||
if(compFunc(ffListGet(list, i), compElement))
|
||||
FF_A_NODISCARD static inline void* ffListGet(const FFlist* list, uint32_t elementSize, uint32_t index) {
|
||||
assert(list->capacity > index);
|
||||
return list->data + (index * elementSize);
|
||||
}
|
||||
|
||||
FF_A_NODISCARD static inline uint32_t ffListFirstIndexComp(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
|
||||
for (uint32_t i = 0; i < list->length; i++) {
|
||||
if (compFunc(ffListGet(list, elementSize, i), compElement)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return list->length;
|
||||
}
|
||||
|
||||
static inline bool ffListContains(const FFlist* list, void* compElement, bool(*compFunc)(const void*, const void*))
|
||||
{
|
||||
return ffListFirstIndexComp(list, compElement, compFunc) != list->length;
|
||||
FF_A_NODISCARD static inline bool ffListContains(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
|
||||
return ffListFirstIndexComp(list, elementSize, compElement, compFunc) != list->length;
|
||||
}
|
||||
|
||||
static inline void ffListSort(FFlist* list, int(*compar)(const void*, const void*))
|
||||
{
|
||||
qsort(list->data, list->length, list->elementSize, compar);
|
||||
static inline void ffListSort(FFlist* list, uint32_t elementSize, int (*compar)(const void*, const void*)) {
|
||||
qsort(list->data, list->length, elementSize, compar);
|
||||
}
|
||||
|
||||
// Move the contents of `src` into `list`, and left `src` empty
|
||||
static inline void ffListInitMove(FFlist* list, FFlist* src)
|
||||
{
|
||||
if (src)
|
||||
{
|
||||
list->elementSize = src->elementSize;
|
||||
static inline void ffListInitMove(FFlist* list, FFlist* src) {
|
||||
if (src) {
|
||||
list->capacity = src->capacity;
|
||||
list->length = src->length;
|
||||
list->data = src->data;
|
||||
ffListInit(src, list->elementSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffListInit(list, 0);
|
||||
ffListInit(src);
|
||||
} else {
|
||||
ffListInit(list);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ffListDestroy(FFlist* list)
|
||||
{
|
||||
if (!list->data) return;
|
||||
static inline void ffListDestroy(FFlist* list) {
|
||||
if (!list->data) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Avoid free-after-use. These 3 assignments are cheap so don't remove them
|
||||
// Avoid free-after-use. These 3 assignments are cheap so don't remove them
|
||||
list->capacity = list->length = 0;
|
||||
free(list->data);
|
||||
list->data = NULL;
|
||||
}
|
||||
|
||||
static inline void ffListClear(FFlist* list)
|
||||
{
|
||||
static inline void ffListClear(FFlist* list) {
|
||||
list->length = 0;
|
||||
}
|
||||
|
||||
static inline void ffListReserve(FFlist* list, uint32_t newCapacity)
|
||||
{
|
||||
if (__builtin_expect(newCapacity <= list->capacity, false))
|
||||
static inline void ffListReserve(FFlist* list, uint32_t elementSize, uint32_t newCapacity) {
|
||||
if (__builtin_expect(newCapacity <= list->capacity, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
list->data = (uint8_t*) realloc(list->data, (size_t) newCapacity * list->elementSize);
|
||||
list->data = (uint8_t*) realloc(list->data, (size_t) newCapacity * elementSize);
|
||||
list->capacity = newCapacity;
|
||||
}
|
||||
|
||||
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
|
||||
assert(sizeof(itemType) == (listVar).elementSize); \
|
||||
for(itemType* itemVarName = (itemType*)(listVar).data; \
|
||||
itemVarName - (itemType*)(listVar).data < (intptr_t)(listVar).length; \
|
||||
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
|
||||
for (itemType* itemVarName = (itemType*) (listVar).data; \
|
||||
itemVarName - (itemType*) (listVar).data < (intptr_t) (listVar).length; \
|
||||
++itemVarName)
|
||||
|
||||
#define FF_LIST_AUTO_DESTROY FFlist __attribute__((__cleanup__(ffListDestroy)))
|
||||
#define FF_LIST_AUTO_DESTROY FFlist FF_A_CLEANUP(ffListDestroy)
|
||||
|
||||
#define FF_LIST_GET(itemType, listVar, index) \
|
||||
({ \
|
||||
assert(sizeof(itemType) == (listVar).elementSize); \
|
||||
({ \
|
||||
assert((listVar).capacity > (index)); \
|
||||
(itemType*)(listVar).data + (index); \
|
||||
(itemType*) (listVar).data + (index); \
|
||||
})
|
||||
|
||||
#define FF_LIST_ADD(itemType, listVar) \
|
||||
({ \
|
||||
assert(sizeof(itemType) == (listVar).elementSize); \
|
||||
(itemType*) ffListAdd(&(listVar)); \
|
||||
})
|
||||
#define FF_LIST_ADD(itemType, listVar) (itemType*) ffListAdd(&(listVar), (uint32_t) sizeof(itemType))
|
||||
|
||||
#define FF_LIST_FIRST(itemType, listVar) FF_LIST_GET(itemType, listVar, 0)
|
||||
#define FF_LIST_LAST(itemType, listVar) \
|
||||
({ \
|
||||
assert((listVar).length > 0); \
|
||||
#define FF_LIST_LAST(itemType, listVar) \
|
||||
({ \
|
||||
assert((listVar).length > 0); \
|
||||
FF_LIST_GET(itemType, listVar, ((listVar).length - 1)); \
|
||||
})
|
||||
|
||||
#define FF_LIST_CONTAINS(listVar, pCompElement, compFunc) \
|
||||
({ \
|
||||
typedef __typeof__(*(pCompElement)) compElementType; \
|
||||
typedef bool compFuncType(const compElementType*, const compElementType*); \
|
||||
static_assert(__builtin_types_compatible_p(__typeof__(compFunc), compFuncType), "Incompatible callback function"); \
|
||||
ffListContains(&(listVar), (uint32_t) sizeof(*(pCompElement)), (pCompElement), (bool (*)(const void*, const void*)) compFunc); \
|
||||
})
|
||||
|
||||
#define FF_LIST_SHIFT(listVar, pResult) \
|
||||
ffListShift(&(listVar), (uint32_t) sizeof(*(pResult)), (pResult))
|
||||
#define FF_LIST_POP(listVar, pResult) \
|
||||
ffListPop(&(listVar), (uint32_t) sizeof(*(pResult)), (pResult))
|
||||
|
||||
+133
-200
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "FFcheckmacros.h"
|
||||
#include "common/attributes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
@@ -17,8 +17,8 @@
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// #include <shlwapi.h>
|
||||
__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
|
||||
// #include <shlwapi.h>
|
||||
__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
|
||||
#define strcasestr StrStrIA
|
||||
#endif
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
|
||||
// static string (allocated == 0), chars points to a string literal
|
||||
// dynamic string (allocated > 0), chars points to a heap allocated buffer
|
||||
typedef struct FFstrbuf
|
||||
{
|
||||
typedef struct FFstrbuf {
|
||||
uint32_t allocated;
|
||||
uint32_t length;
|
||||
char* chars;
|
||||
@@ -47,8 +46,8 @@ static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* _
|
||||
void ffStrbufAppendC(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c);
|
||||
void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value);
|
||||
void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int(*transformFunc)(int));
|
||||
FF_C_PRINTF(2, 3) void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...);
|
||||
void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int (*transformFunc)(int));
|
||||
FF_A_PRINTF(2, 3) void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...);
|
||||
void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments);
|
||||
const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until);
|
||||
|
||||
@@ -61,7 +60,7 @@ void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c);
|
||||
// NOTE: Unlike ffStrbufAppend*, ffStrbufSet* functions may NOT reserve extra space
|
||||
void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value);
|
||||
void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value);
|
||||
FF_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
|
||||
FF_A_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
|
||||
|
||||
void ffStrbufTrimLeft(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufTrimRight(FFstrbuf* strbuf, char c);
|
||||
@@ -72,10 +71,10 @@ bool ffStrbufRemoveSubstr(FFstrbuf* strbuf, uint32_t startIndex, uint32_t endInd
|
||||
void ffStrbufRemoveS(FFstrbuf* strbuf, const char* str);
|
||||
void ffStrbufRemoveStrings(FFstrbuf* strbuf, uint32_t numStrings, const char* strings[]);
|
||||
|
||||
FF_C_NODISCARD uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
FF_C_NODISCARD uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str);
|
||||
FF_A_NODISCARD uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
FF_A_NODISCARD uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str);
|
||||
|
||||
FF_C_NODISCARD uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
FF_A_NODISCARD uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
|
||||
void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace);
|
||||
|
||||
@@ -87,7 +86,7 @@ bool ffStrbufSubstrAfterFirstS(FFstrbuf* strbuf, const char* str);
|
||||
bool ffStrbufSubstrAfterLastC(FFstrbuf* strbuf, char c);
|
||||
bool ffStrbufSubstr(FFstrbuf* strbuf, uint32_t start, uint32_t end);
|
||||
|
||||
FF_C_NODISCARD uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
|
||||
FF_A_NODISCARD uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
|
||||
|
||||
bool ffStrbufRemoveIgnCaseEndS(FFstrbuf* strbuf, const char* end);
|
||||
|
||||
@@ -96,9 +95,9 @@ bool ffStrbufEnsureEndsWithC(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file);
|
||||
void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file);
|
||||
|
||||
FF_C_NODISCARD double ffStrbufToDouble(const FFstrbuf* strbuf, double defaultValue);
|
||||
FF_C_NODISCARD int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue);
|
||||
FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue);
|
||||
FF_A_NODISCARD double ffStrbufToDouble(const FFstrbuf* strbuf, double defaultValue);
|
||||
FF_A_NODISCARD int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue);
|
||||
FF_A_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue);
|
||||
|
||||
void ffStrbufUpperCase(FFstrbuf* strbuf);
|
||||
void ffStrbufLowerCase(FFstrbuf* strbuf);
|
||||
@@ -126,16 +125,14 @@ void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf
|
||||
*
|
||||
* @return true if a line has been read, false if the end of the buffer has been reached.
|
||||
*/
|
||||
static inline bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
|
||||
{
|
||||
static inline bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer) {
|
||||
return ffStrbufGetdelim(lineptr, n, '\n', buffer);
|
||||
}
|
||||
/**
|
||||
* @brief Restore the end of a line that was modified by ffStrbufGetline.
|
||||
* @warning This function should be called before breaking an ffStrbufGetline loop if `buffer` will be used later.
|
||||
*/
|
||||
static inline void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer)
|
||||
{
|
||||
static inline void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer) {
|
||||
ffStrbufGetdelimRestore(lineptr, n, '\n', buffer);
|
||||
}
|
||||
bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf);
|
||||
@@ -152,69 +149,58 @@ void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value);
|
||||
// if `precision < 0`, let yyjson decide the precision
|
||||
void ffStrbufAppendDouble(FFstrbuf* strbuf, double value, int8_t precision, bool trailingZeros);
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitA(&strbuf, allocate);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict src)
|
||||
{
|
||||
if (src->allocated == 0) // static string
|
||||
static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict src) {
|
||||
if (src->allocated == 0) { // static string
|
||||
*strbuf = *src;
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufInitA(strbuf, src->allocated);
|
||||
ffStrbufAppend(strbuf, src);
|
||||
}
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitCopy(&strbuf, src);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
// Move the content of `src` into `strbuf`, and left `src` empty
|
||||
static inline void ffStrbufInitMove(FFstrbuf* strbuf, FFstrbuf* src)
|
||||
{
|
||||
if (src)
|
||||
{
|
||||
static inline void ffStrbufInitMove(FFstrbuf* strbuf, FFstrbuf* src) {
|
||||
if (src) {
|
||||
*strbuf = *src;
|
||||
ffStrbufInit(src);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateMove(FFstrbuf* src)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateMove(FFstrbuf* src) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitMove(&strbuf, src);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateVF(const char* format, va_list arguments)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateVF(const char* format, va_list arguments) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitVF(&strbuf, format, arguments);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
FF_C_PRINTF(2, 3)
|
||||
static inline void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...)
|
||||
{
|
||||
FF_A_PRINTF(2, 3) static inline void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
ffStrbufInitVF(strbuf, format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
FF_C_PRINTF(1, 2)
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateF(const char* format, ...)
|
||||
{
|
||||
FF_A_PRINTF(1, 2) FF_A_NODISCARD static inline FFstrbuf
|
||||
ffStrbufCreateF(const char* format, ...) {
|
||||
FFstrbuf strbuf;
|
||||
|
||||
va_list arguments;
|
||||
@@ -225,50 +211,46 @@ FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateF(const char* format, ...)
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr)
|
||||
{
|
||||
static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr) {
|
||||
ffStrbufInitMoveNS(strbuf, (uint32_t) strlen(heapStr), heapStr);
|
||||
}
|
||||
|
||||
// Despite the name, this function resets strbuf to the initial/unallocated state
|
||||
static inline void ffStrbufDestroy(FFstrbuf* strbuf)
|
||||
{
|
||||
if(strbuf->allocated > 0)
|
||||
static inline void ffStrbufDestroy(FFstrbuf* strbuf) {
|
||||
if (strbuf->allocated > 0) {
|
||||
free(strbuf->chars);
|
||||
}
|
||||
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf)
|
||||
{
|
||||
FF_A_NODISCARD static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) {
|
||||
assert(strbuf != NULL);
|
||||
if(strbuf->allocated == 0)
|
||||
if (strbuf->allocated == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strbuf->allocated - strbuf->length - 1; // - 1 for the null byte
|
||||
}
|
||||
|
||||
static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf)
|
||||
{
|
||||
static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf) {
|
||||
strbuf->length = (uint32_t) strlen(strbuf->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) {
|
||||
assert(strbuf != NULL);
|
||||
|
||||
if (value == NULL)
|
||||
if (value == NULL) {
|
||||
ffStrbufClear(strbuf);
|
||||
else
|
||||
} else {
|
||||
ffStrbufSetNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
{
|
||||
static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
assert(strbuf != NULL);
|
||||
|
||||
if (yyjson_is_str(jsonVal))
|
||||
{
|
||||
if (yyjson_is_str(jsonVal)) {
|
||||
ffStrbufSetNS(strbuf, (uint32_t) unsafe_yyjson_get_len(jsonVal), unsafe_yyjson_get_str(jsonVal));
|
||||
return true;
|
||||
}
|
||||
@@ -277,358 +259,309 @@ static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value) {
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufAppendNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufAppendJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
{
|
||||
if (yyjson_is_str(jsonVal))
|
||||
{
|
||||
static inline bool ffStrbufAppendJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
if (yyjson_is_str(jsonVal)) {
|
||||
ffStrbufAppendNS(strbuf, (uint32_t) unsafe_yyjson_get_len(jsonVal), unsafe_yyjson_get_str(jsonVal));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void ffStrbufInit(FFstrbuf* strbuf)
|
||||
{
|
||||
static inline void ffStrbufInit(FFstrbuf* strbuf) {
|
||||
extern char* CHAR_NULL_PTR;
|
||||
strbuf->allocated = strbuf->length = 0;
|
||||
strbuf->chars = CHAR_NULL_PTR;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreate(void)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreate(void) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInit(&strbuf);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitStatic(FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline void ffStrbufInitStatic(FFstrbuf* strbuf, const char* str) {
|
||||
ffStrbufInit(strbuf);
|
||||
if (!str) return;
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
strbuf->allocated = 0;
|
||||
strbuf->length = (uint32_t) strlen(str);
|
||||
strbuf->chars = (char*) str;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateStatic(const char* str)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateStatic(const char* str) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitStatic(&strbuf, str);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufSetStatic(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(strbuf->allocated > 0)
|
||||
static inline void ffStrbufSetStatic(FFstrbuf* strbuf, const char* value) {
|
||||
if (strbuf->allocated > 0) {
|
||||
free(strbuf->chars);
|
||||
}
|
||||
|
||||
if(value != NULL)
|
||||
if (value != NULL) {
|
||||
ffStrbufInitStatic(strbuf, value);
|
||||
else
|
||||
} else {
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitNS(FFstrbuf* strbuf, uint32_t length, const char* str)
|
||||
{
|
||||
static inline void ffStrbufInitNS(FFstrbuf* strbuf, uint32_t length, const char* str) {
|
||||
ffStrbufInit(strbuf);
|
||||
ffStrbufAppendNS(strbuf, length, str);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateNS(uint32_t length, const char* str)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateNS(uint32_t length, const char* str) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitNS(&strbuf, length, str);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline bool ffStrbufInitJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
{
|
||||
static inline bool ffStrbufInitJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
ffStrbufInit(strbuf);
|
||||
return ffStrbufAppendJsonVal(strbuf, jsonVal);
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitS(FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline void ffStrbufInitS(FFstrbuf* strbuf, const char* str) {
|
||||
ffStrbufInit(strbuf);
|
||||
ffStrbufAppendS(strbuf, str);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateS(const char* str)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateS(const char* str) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitS(&strbuf, str);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value)
|
||||
{
|
||||
static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value) {
|
||||
assert(value != strbuf);
|
||||
if(value == NULL)
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufAppendNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufPrepend(FFstrbuf* strbuf, FFstrbuf* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
static inline void ffStrbufPrepend(FFstrbuf* strbuf, FFstrbuf* value) {
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufPrependNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value) {
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufPrependNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
uint32_t length = strbuf->length > comp->length ? comp->length : strbuf->length;
|
||||
return memcmp(strbuf->chars, comp->chars, length + 1);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEqual(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
return ffStrbufComp(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufCompS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufCompS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return strcmp(strbuf->chars, comp);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEqualS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEqualS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return ffStrbufCompS(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufIgnCaseCompS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufIgnCaseCompS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return strcasecmp(strbuf->chars, comp);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufIgnCaseEqualS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufIgnCaseEqualS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return ffStrbufIgnCaseCompS(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufIgnCaseComp(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufIgnCaseComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
return ffStrbufIgnCaseCompS(strbuf, comp->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufIgnCaseEqual(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufIgnCaseEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
return ffStrbufIgnCaseComp(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainC(const FFstrbuf* strbuf, char c) {
|
||||
return memchr(strbuf->chars, c, strbuf->length) != NULL;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str) {
|
||||
return strstr(strbuf->chars, str) != NULL;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContain(const FFstrbuf* strbuf, const FFstrbuf* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContain(const FFstrbuf* strbuf, const FFstrbuf* str) {
|
||||
return ffStrbufContainS(strbuf, str->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainIgnCaseS(const FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainIgnCaseS(const FFstrbuf* strbuf, const char* str) {
|
||||
return strcasestr(strbuf->chars, str) != NULL;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* str) {
|
||||
return ffStrbufContainIgnCaseS(strbuf, str->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, char c) {
|
||||
return ffStrbufNextIndexC(strbuf, 0, c);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufFirstIndex(const FFstrbuf* strbuf, const FFstrbuf* searched)
|
||||
{
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndex(const FFstrbuf* strbuf, const FFstrbuf* searched) {
|
||||
return ffStrbufNextIndexS(strbuf, 0, searched->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufFirstIndexS(const FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndexS(const FFstrbuf* strbuf, const char* str) {
|
||||
return ffStrbufNextIndexS(strbuf, 0, str);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
if(strbuf->length == 0)
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, char c) {
|
||||
if (strbuf->length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ffStrbufPreviousIndexC(strbuf, strbuf->length - 1, c);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSubstrBeforeFirstC(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline bool ffStrbufSubstrBeforeFirstC(FFstrbuf* strbuf, char c) {
|
||||
return ffStrbufSubstrBefore(strbuf, ffStrbufFirstIndexC(strbuf, c));
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c) {
|
||||
return ffStrbufSubstrBefore(strbuf, ffStrbufLastIndexC(strbuf, c));
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithC(const FFstrbuf* strbuf, char c) {
|
||||
return strbuf->chars[0] == c;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithSN(const FFstrbuf* strbuf, const char* start, uint32_t length)
|
||||
{
|
||||
if (length > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithSN(const FFstrbuf* strbuf, const char* start, uint32_t length) {
|
||||
if (length > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return memcmp(strbuf->chars, start, length) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithS(const FFstrbuf* strbuf, const char* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithS(const FFstrbuf* strbuf, const char* start) {
|
||||
return ffStrbufStartsWithSN(strbuf, start, (uint32_t) strlen(start));
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWith(const FFstrbuf* strbuf, const FFstrbuf* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWith(const FFstrbuf* strbuf, const FFstrbuf* start) {
|
||||
return ffStrbufStartsWithSN(strbuf, start->chars, start->length);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t length, const char* start)
|
||||
{
|
||||
if(length > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t length, const char* start) {
|
||||
if (length > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
return strncasecmp(strbuf->chars, start, length) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithIgnCaseS(const FFstrbuf* strbuf, const char* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCaseS(const FFstrbuf* strbuf, const char* start) {
|
||||
return ffStrbufStartsWithIgnCaseNS(strbuf, (uint32_t) strlen(start), start);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* start) {
|
||||
return ffStrbufStartsWithIgnCaseNS(strbuf, start->length, start->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
return strbuf->length == 0 ? false :
|
||||
strbuf->chars[strbuf->length - 1] == c;
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithC(const FFstrbuf* strbuf, char c) {
|
||||
return strbuf->length == 0 ? false : strbuf->chars[strbuf->length - 1] == c;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end)
|
||||
{
|
||||
if(endLength > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
|
||||
if (endLength > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return memcmp(strbuf->chars + strbuf->length - endLength, end, endLength) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end) {
|
||||
return ffStrbufEndsWithNS(strbuf, (uint32_t) strlen(end), end);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*const fn)(int))
|
||||
{
|
||||
return strbuf->length == 0 ? false :
|
||||
fn(strbuf->chars[strbuf->length - 1]);
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*const fn)(int)) {
|
||||
return strbuf->length == 0 ? false : fn(strbuf->chars[strbuf->length - 1]);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWith(const FFstrbuf* strbuf, const FFstrbuf* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWith(const FFstrbuf* strbuf, const FFstrbuf* end) {
|
||||
return ffStrbufEndsWithNS(strbuf, end->length, end->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end)
|
||||
{
|
||||
if(endLength > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
|
||||
if (endLength > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
return strcasecmp(strbuf->chars + strbuf->length - endLength, end) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithIgnCaseS(const FFstrbuf* strbuf, const char* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCaseS(const FFstrbuf* strbuf, const char* end) {
|
||||
return ffStrbufEndsWithIgnCaseNS(strbuf, (uint32_t) strlen(end), end);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* end) {
|
||||
return ffStrbufEndsWithIgnCaseNS(strbuf, end->length, end->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufTrim(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline void ffStrbufTrim(FFstrbuf* strbuf, char c) {
|
||||
ffStrbufTrimRight(strbuf, c);
|
||||
ffStrbufTrimLeft(strbuf, c);
|
||||
}
|
||||
|
||||
static inline void ffStrbufTrimSpace(FFstrbuf* strbuf)
|
||||
{
|
||||
static inline void ffStrbufTrimSpace(FFstrbuf* strbuf) {
|
||||
ffStrbufTrimRightSpace(strbuf);
|
||||
ffStrbufTrimLeftSpace(strbuf);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparatedS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparatedS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparated(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparated(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparatedIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparatedIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedIgnCaseNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparatedIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparatedIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedIgnCaseNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContainS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContainS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufSeparatedContainNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContain(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContain(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufSeparatedContainNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContainIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContainIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufSeparatedContainIgnCaseNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufSeparatedContainIgnCaseNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
// Returns true if the strbuf is modified
|
||||
bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf);
|
||||
|
||||
#define FF_STRBUF_AUTO_DESTROY FFstrbuf __attribute__((__cleanup__(ffStrbufDestroy)))
|
||||
#define FF_STRBUF_AUTO_DESTROY FFstrbuf FF_A_CLEANUP(ffStrbufDestroy)
|
||||
|
||||
+129
-89
@@ -1,185 +1,225 @@
|
||||
#include "cf_helpers.h"
|
||||
|
||||
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result)
|
||||
{
|
||||
if(CFGetTypeID(cf) == CFNumberGetTypeID())
|
||||
{
|
||||
if(!CFNumberGetValue((CFNumberRef)cf, kCFNumberSInt64Type, result))
|
||||
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result) {
|
||||
if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
|
||||
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt64Type, result)) {
|
||||
return "Number type is not SInt64";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else if(CFGetTypeID(cf) == CFDataGetTypeID())
|
||||
{
|
||||
if(CFDataGetLength((CFDataRef)cf) != sizeof(int64_t))
|
||||
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
if (CFDataGetLength((CFDataRef) cf) != sizeof(int64_t)) {
|
||||
return "Data length is not sizeof(int64_t)";
|
||||
CFDataGetBytes((CFDataRef)cf, CFRangeMake(0, sizeof(int64_t)), (uint8_t*)result);
|
||||
}
|
||||
CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(int64_t)), (uint8_t*) result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return "TypeID is neither 'CFNumber' nor 'CFData'";
|
||||
}
|
||||
|
||||
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result)
|
||||
{
|
||||
if(CFGetTypeID(cf) == CFNumberGetTypeID())
|
||||
{
|
||||
if(!CFNumberGetValue((CFNumberRef)cf, kCFNumberSInt32Type, result))
|
||||
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result) {
|
||||
if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
|
||||
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt32Type, result)) {
|
||||
return "Number type is not SInt32";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else if(CFGetTypeID(cf) == CFDataGetTypeID())
|
||||
{
|
||||
if(CFDataGetLength((CFDataRef)cf) != sizeof(int))
|
||||
return "Data length is not sizeof(int)";
|
||||
CFDataGetBytes((CFDataRef)cf, CFRangeMake(0, sizeof(int)), (uint8_t*)result);
|
||||
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
if (CFDataGetLength((CFDataRef) cf) != sizeof(*result)) {
|
||||
return "Data length is not sizeof(int32_t)";
|
||||
}
|
||||
CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(*result)), (uint8_t*) result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return "TypeID is neither 'CFNumber' nor 'CFData'";
|
||||
}
|
||||
|
||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result)
|
||||
{
|
||||
ffStrbufClear(result);
|
||||
if (!cf)
|
||||
const char* ffCfNumGetDouble(CFTypeRef cf, double* result) {
|
||||
if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
|
||||
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberDoubleType, result) &&
|
||||
!CFNumberGetValue((CFNumberRef) cf, kCFNumberFloatType, result)) {
|
||||
return "Number type is not Double or Float";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) == CFStringGetTypeID())
|
||||
{
|
||||
CFStringRef cfStr = (CFStringRef)cf;
|
||||
return "TypeID is neither 'CFNumber'";
|
||||
}
|
||||
|
||||
const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result) {
|
||||
if (CFGetTypeID(cf) != CFDateGetTypeID()) {
|
||||
return "TypeID is not 'CFDate'";
|
||||
}
|
||||
|
||||
CFAbsoluteTime absTime = CFDateGetAbsoluteTime((CFDateRef) cf);
|
||||
// Convert from seconds to milliseconds and add the difference between 1970 and 2001 in milliseconds
|
||||
*result = (uint64_t) ((absTime + 978307200 /*kCFAbsoluteTimeIntervalSince1970*/) * 1000);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
|
||||
ffStrbufClear(result);
|
||||
if (!cf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) == CFStringGetTypeID()) {
|
||||
CFStringRef cfStr = (CFStringRef) cf;
|
||||
|
||||
const char* cstr = CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8);
|
||||
if (cstr)
|
||||
if (cstr) {
|
||||
ffStrbufSetS(result, cstr);
|
||||
else
|
||||
{
|
||||
} else {
|
||||
uint32_t length = (uint32_t) CFStringGetLength(cfStr);
|
||||
if (length == 0)
|
||||
if (length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
ffStrbufEnsureFixedLengthFree(result, (uint32_t) CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8));
|
||||
if(!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8))
|
||||
if (!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) {
|
||||
return "CFStringGetCString() failed";
|
||||
}
|
||||
// CFStringGetCString ensures the buffer is NUL terminated
|
||||
// https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring
|
||||
result->length = (uint32_t) strnlen(result->chars, (uint32_t)result->allocated);
|
||||
result->length = (uint32_t) strnlen(result->chars, (uint32_t) result->allocated);
|
||||
}
|
||||
}
|
||||
else if (CFGetTypeID(cf) == CFDataGetTypeID())
|
||||
{
|
||||
CFDataRef cfData = (CFDataRef)cf;
|
||||
uint32_t length = (uint32_t)CFDataGetLength(cfData);
|
||||
if (length == 0)
|
||||
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
CFDataRef cfData = (CFDataRef) cf;
|
||||
uint32_t length = (uint32_t) CFDataGetLength(cfData);
|
||||
if (length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
ffStrbufEnsureFixedLengthFree(result, length + 1);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars);
|
||||
result->length = (uint32_t)strnlen(result->chars, length);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
|
||||
result->length = (uint32_t) strnlen(result->chars, length);
|
||||
result->chars[result->length] = '\0';
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return "TypeID is neither 'CFString' nor 'CFData'";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result)
|
||||
{
|
||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result) {
|
||||
ffStrbufClear(result);
|
||||
if (!cf)
|
||||
if (!cf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) == CFDataGetTypeID())
|
||||
{
|
||||
CFDataRef cfData = (CFDataRef)cf;
|
||||
uint32_t length = (uint32_t)CFDataGetLength(cfData);
|
||||
if (length == 0)
|
||||
if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
CFDataRef cfData = (CFDataRef) cf;
|
||||
uint32_t length = (uint32_t) CFDataGetLength(cfData);
|
||||
if (length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
ffStrbufEnsureFixedLengthFree(result, length + 1);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
|
||||
result->length = length;
|
||||
result->chars[result->length] = '\0';
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return "TypeID is not 'CFData'";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result)
|
||||
{
|
||||
CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key);
|
||||
if(cf == NULL)
|
||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfStrGetString(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result)
|
||||
{
|
||||
CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key);
|
||||
if(cf == NULL)
|
||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfDataGetDataAsString(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result)
|
||||
{
|
||||
CFBooleanRef cf = (CFBooleanRef)CFDictionaryGetValue(dict, key);
|
||||
if(cf == NULL)
|
||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) {
|
||||
CFBooleanRef cf = (CFBooleanRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
if(CFGetTypeID(cf) != CFBooleanGetTypeID())
|
||||
if (CFGetTypeID(cf) != CFBooleanGetTypeID()) {
|
||||
return "TypeID is not 'CFBoolean'";
|
||||
}
|
||||
|
||||
*result = CFBooleanGetValue(cf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result)
|
||||
{
|
||||
CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key);
|
||||
if(cf == NULL)
|
||||
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfNumGetInt(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result)
|
||||
{
|
||||
CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key);
|
||||
if(cf == NULL)
|
||||
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfNumGetInt64(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length)
|
||||
{
|
||||
CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key);
|
||||
if(cf == NULL)
|
||||
const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
if(CFGetTypeID(cf) != CFDataGetTypeID())
|
||||
return ffCfNumGetDouble(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) != CFDataGetTypeID()) {
|
||||
return "TypeID is not 'CFData'";
|
||||
}
|
||||
|
||||
CFIndex trueLength = CFDataGetLength((CFDataRef)cf);
|
||||
CFIndex trueLength = CFDataGetLength((CFDataRef) cf);
|
||||
|
||||
if(trueLength < offset + size)
|
||||
if (trueLength < offset + size) {
|
||||
return "Data length is less than offset + size";
|
||||
}
|
||||
|
||||
if(length)
|
||||
if (length) {
|
||||
*length = (uint32_t) trueLength;
|
||||
}
|
||||
|
||||
CFDataGetBytes((CFDataRef)cf, CFRangeMake(offset, size), result);
|
||||
CFDataGetBytes((CFDataRef) cf, CFRangeMake(offset, size), result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result)
|
||||
{
|
||||
CFDictionaryRef cf = (CFDictionaryRef)CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL || CFGetTypeID(cf) != CFDictionaryGetTypeID())
|
||||
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) {
|
||||
CFDictionaryRef cf = (CFDictionaryRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL || CFGetTypeID(cf) != CFDictionaryGetTypeID()) {
|
||||
return "TypeID is not 'CFDictionary'";
|
||||
}
|
||||
|
||||
*result = cf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfDateGetEpoch(cf, result);
|
||||
}
|
||||
|
||||
@@ -4,37 +4,40 @@
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
//Return error info if failed, NULL otherwise
|
||||
// Return error info if failed, NULL otherwise
|
||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result);
|
||||
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result);
|
||||
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result);
|
||||
const char* ffCfNumGetDouble(CFTypeRef cf, double* result);
|
||||
const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result);
|
||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result);
|
||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result);
|
||||
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result);
|
||||
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result);
|
||||
const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result);
|
||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length);
|
||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result);
|
||||
const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result);
|
||||
|
||||
static inline CFNumberRef ffCfCreateInt(int value)
|
||||
{
|
||||
static inline CFNumberRef ffCfCreateInt(int value) {
|
||||
return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
|
||||
}
|
||||
|
||||
static inline void cfReleaseWrapper(void* type)
|
||||
{
|
||||
static inline void cfReleaseWrapper(void* type) {
|
||||
assert(type);
|
||||
if (*(CFTypeRef*) type)
|
||||
if (*(CFTypeRef*) type) {
|
||||
CFRelease(*(CFTypeRef*) type);
|
||||
}
|
||||
}
|
||||
|
||||
#define FF_CFTYPE_AUTO_RELEASE __attribute__((__cleanup__(cfReleaseWrapper)))
|
||||
#define FF_CFTYPE_AUTO_RELEASE FF_A_CLEANUP(cfReleaseWrapper)
|
||||
|
||||
static inline void wrapIoObjectRelease(io_object_t* service)
|
||||
{
|
||||
static inline void wrapIoObjectRelease(io_object_t* service) {
|
||||
assert(service);
|
||||
if (*service)
|
||||
if (*service) {
|
||||
IOObjectRelease(*service);
|
||||
}
|
||||
}
|
||||
#define FF_IOOBJECT_AUTO_RELEASE __attribute__((__cleanup__(wrapIoObjectRelease)))
|
||||
#define FF_IOOBJECT_AUTO_RELEASE FF_A_CLEANUP(wrapIoObjectRelease)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "osascript.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
bool ffOsascript(const char* input, FFstrbuf* result)
|
||||
{
|
||||
|
||||
+268
-252
@@ -3,7 +3,6 @@
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
static const char kSmcCmdReadBytes = 5;
|
||||
@@ -60,66 +59,64 @@ typedef struct
|
||||
SmcBytes_t bytes;
|
||||
} SmcVal_t;
|
||||
|
||||
static uint32_t smcStrtoul(const char *str, int size, int base)
|
||||
{
|
||||
static uint32_t smcStrtoul(const char* str, int size, int base) {
|
||||
uint32_t total = 0;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (base == 16)
|
||||
total += (uint32_t)(str[i] << (size - 1 - i) * 8);
|
||||
else
|
||||
total += (uint32_t)((unsigned char)(str[i]) << (size - 1 - i) * 8);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (base == 16) {
|
||||
total += (uint32_t) (str[i] << (size - 1 - i) * 8);
|
||||
} else {
|
||||
total += (uint32_t) ((unsigned char) (str[i]) << (size - 1 - i) * 8);
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static void smcUltostr(char *str, uint32_t val)
|
||||
{
|
||||
str[0] = (char)(val >> 24);
|
||||
str[1] = (char)(val >> 16);
|
||||
str[2] = (char)(val >> 8);
|
||||
str[3] = (char)val;
|
||||
static void smcUltostr(char* str, uint32_t val) {
|
||||
str[0] = (char) (val >> 24);
|
||||
str[1] = (char) (val >> 16);
|
||||
str[2] = (char) (val >> 8);
|
||||
str[3] = (char) val;
|
||||
str[4] = '\0';
|
||||
}
|
||||
|
||||
static const char *smcCall(io_connect_t conn, uint32_t selector, SmcKeyData_t *inputStructure, SmcKeyData_t *outputStructure)
|
||||
{
|
||||
static const char* smcCall(io_connect_t conn, uint32_t selector, SmcKeyData_t* inputStructure, SmcKeyData_t* outputStructure) {
|
||||
size_t size = sizeof(SmcKeyData_t);
|
||||
|
||||
if (IOConnectCallStructMethod(conn, selector, inputStructure, size, outputStructure, &size) != kIOReturnSuccess)
|
||||
if (IOConnectCallStructMethod(conn, selector, inputStructure, size, outputStructure, &size) != kIOReturnSuccess) {
|
||||
return "IOConnectCallStructMethod(conn) failed";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Provides key info, using a cache to dramatically improve the energy impact of smcFanControl
|
||||
static const char *smcGetKeyInfo(io_connect_t conn, const uint32_t key, SmcKeyData_keyInfo_t *key_info)
|
||||
{
|
||||
SmcKeyData_t inputStructure = {0};
|
||||
SmcKeyData_t outputStructure = {0};
|
||||
static const char* smcGetKeyInfo(io_connect_t conn, const uint32_t key, SmcKeyData_keyInfo_t* key_info) {
|
||||
SmcKeyData_t inputStructure = { 0 };
|
||||
SmcKeyData_t outputStructure = { 0 };
|
||||
|
||||
inputStructure.key = key;
|
||||
inputStructure.data8 = kSmcCmdReadKeyInfo;
|
||||
|
||||
const char *error = smcCall(conn, kKernelIndexSmc, &inputStructure, &outputStructure);
|
||||
if (error)
|
||||
const char* error = smcCall(conn, kKernelIndexSmc, &inputStructure, &outputStructure);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
*key_info = outputStructure.keyInfo;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *smcReadSmcVal(io_connect_t conn, const UInt32Char_t key, SmcVal_t *val)
|
||||
{
|
||||
SmcKeyData_t inputStructure = {0};
|
||||
SmcKeyData_t outputStructure = {0};
|
||||
static const char* smcReadSmcVal(io_connect_t conn, const UInt32Char_t key, SmcVal_t* val) {
|
||||
SmcKeyData_t inputStructure = { 0 };
|
||||
SmcKeyData_t outputStructure = { 0 };
|
||||
|
||||
inputStructure.key = smcStrtoul(key, 4, 16);
|
||||
strcpy(val->key, key);
|
||||
|
||||
const char *error = smcGetKeyInfo(conn, inputStructure.key, &outputStructure.keyInfo);
|
||||
if (error)
|
||||
const char* error = smcGetKeyInfo(conn, inputStructure.key, &outputStructure.keyInfo);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
val->dataSize = outputStructure.keyInfo.dataSize;
|
||||
smcUltostr(val->dataType, outputStructure.keyInfo.dataType);
|
||||
@@ -127,132 +124,147 @@ static const char *smcReadSmcVal(io_connect_t conn, const UInt32Char_t key, SmcV
|
||||
inputStructure.data8 = kSmcCmdReadBytes;
|
||||
|
||||
error = smcCall(conn, kKernelIndexSmc, &inputStructure, &outputStructure);
|
||||
if (error)
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *smcOpen(io_connect_t *conn)
|
||||
{
|
||||
static const char* smcOpen(io_connect_t* conn) {
|
||||
FF_IOOBJECT_AUTO_RELEASE io_object_t device = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMC"));
|
||||
if (!device)
|
||||
if (!device) {
|
||||
return "No SMC device found";
|
||||
}
|
||||
|
||||
if (IOServiceOpen(device, mach_task_self(), 0, conn) != kIOReturnSuccess)
|
||||
if (IOServiceOpen(device, mach_task_self(), 0, conn) != kIOReturnSuccess) {
|
||||
return "IOServiceOpen() failed";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, double *value)
|
||||
{
|
||||
SmcVal_t val = {0};
|
||||
static const char* smcReadValue(io_connect_t conn, const UInt32Char_t key, double* value) {
|
||||
SmcVal_t val = { 0 };
|
||||
const char* error = smcReadSmcVal(conn, key, &val);
|
||||
if (error != NULL)
|
||||
if (error != NULL) {
|
||||
return error;
|
||||
if (val.dataSize == 0)
|
||||
}
|
||||
if (val.dataSize == 0) {
|
||||
return "Empty SMC result";
|
||||
}
|
||||
|
||||
switch (val.dataType[0])
|
||||
{
|
||||
switch (val.dataType[0]) {
|
||||
case 'u': // unsigned integer types
|
||||
if (val.dataType[1] == 'i')
|
||||
{
|
||||
switch (val.dataSize)
|
||||
{
|
||||
case 1: *value = *(uint8_t *)(val.bytes); break;
|
||||
case 2: *value = ntohs(*(uint16_t *)(val.bytes)); break;
|
||||
case 4: *value = ntohl(*(uint32_t *)(val.bytes)); break;
|
||||
case 8: *value = (double) ntohll(*(uint64_t *)(val.bytes)); break;
|
||||
if (val.dataType[1] == 'i') {
|
||||
switch (val.dataSize) {
|
||||
case 1:
|
||||
*value = *(uint8_t*) (val.bytes);
|
||||
break;
|
||||
case 2:
|
||||
*value = ntohs(*(uint16_t*) (val.bytes));
|
||||
break;
|
||||
case 4:
|
||||
*value = ntohl(*(uint32_t*) (val.bytes));
|
||||
break;
|
||||
case 8:
|
||||
*value = (double) ntohll(*(uint64_t*) (val.bytes));
|
||||
break;
|
||||
default:
|
||||
return "Unsupported SMC unsigned integer data size";
|
||||
}
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return "Unsupported SMC unsigned data type";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'f': // floating point types
|
||||
if (ffStrEquals(val.dataType, "flt ") && val.dataSize == 4)
|
||||
*value = *(float *)(val.bytes);
|
||||
else if (val.dataType[1] == 'p' && val.dataSize == 2) // fixed point types
|
||||
if (ffStrEquals(val.dataType, "flt ") && val.dataSize == 4) {
|
||||
*value = *(float*) (val.bytes);
|
||||
} else if (val.dataType[1] == 'p' && val.dataSize == 2) // fixed point types
|
||||
{
|
||||
if (ffStrEquals(val.dataType, "fp1f"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 32768.0;
|
||||
else if (ffStrEquals(val.dataType, "fp4c"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 4096.0;
|
||||
else if (ffStrEquals(val.dataType, "fp5b"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 2048.0;
|
||||
else if (ffStrEquals(val.dataType, "fp6a"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 1024.0;
|
||||
else if (ffStrEquals(val.dataType, "fp79"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 512.0;
|
||||
else if (ffStrEquals(val.dataType, "fp88"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 256.0;
|
||||
else if (ffStrEquals(val.dataType, "fpa6"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 64.0;
|
||||
else if (ffStrEquals(val.dataType, "fpc4"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 16.0;
|
||||
else if (ffStrEquals(val.dataType, "fpe2"))
|
||||
*value = ntohs(*(uint16_t *)(val.bytes)) / 4.0;
|
||||
else
|
||||
if (ffStrEquals(val.dataType, "fp1f")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 32768.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp4c")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 4096.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp5b")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 2048.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp6a")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 1024.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp79")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 512.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp88")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 256.0;
|
||||
} else if (ffStrEquals(val.dataType, "fpa6")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 64.0;
|
||||
} else if (ffStrEquals(val.dataType, "fpc4")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 16.0;
|
||||
} else if (ffStrEquals(val.dataType, "fpe2")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 4.0;
|
||||
} else {
|
||||
return "Unsupported SMC floating point data type";
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
return "Unsupported SMC floating point data type";
|
||||
}
|
||||
break;
|
||||
|
||||
case 's': // signed integer types
|
||||
if (val.dataType[1] == 'i')
|
||||
{
|
||||
switch (val.dataSize)
|
||||
{
|
||||
case 1: *value = *(int8_t *)(val.bytes); break;
|
||||
case 2: *value = ntohs(*(int16_t *)(val.bytes)); break;
|
||||
case 4: *value = ntohl(*(int32_t *)(val.bytes)); break;
|
||||
case 8: *value = (double)ntohll(*(int64_t *)(val.bytes)); break;
|
||||
default: return "Unsupported SMC signed integer data size";
|
||||
if (val.dataType[1] == 'i') {
|
||||
switch (val.dataSize) {
|
||||
case 1:
|
||||
*value = *(int8_t*) (val.bytes);
|
||||
break;
|
||||
case 2:
|
||||
*value = ntohs(*(int16_t*) (val.bytes));
|
||||
break;
|
||||
case 4:
|
||||
*value = ntohl(*(int32_t*) (val.bytes));
|
||||
break;
|
||||
case 8:
|
||||
*value = (double) ntohll(*(int64_t*) (val.bytes));
|
||||
break;
|
||||
default:
|
||||
return "Unsupported SMC signed integer data size";
|
||||
}
|
||||
}
|
||||
else if (val.dataType[1] == 'p' && val.dataSize == 2) // signed fixed point types
|
||||
} else if (val.dataType[1] == 'p' && val.dataSize == 2) // signed fixed point types
|
||||
{
|
||||
if (ffStrEquals(val.dataType, "sp1e"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 16384.0;
|
||||
else if (ffStrEquals(val.dataType, "sp3c"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 4096.0;
|
||||
else if (ffStrEquals(val.dataType, "sp4b"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 2048.0;
|
||||
else if (ffStrEquals(val.dataType, "sp5a"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 1024.0;
|
||||
else if (ffStrEquals(val.dataType, "sp69"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 512.0;
|
||||
else if (ffStrEquals(val.dataType, "sp78"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 256.0;
|
||||
else if (ffStrEquals(val.dataType, "sp87"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 128.0;
|
||||
else if (ffStrEquals(val.dataType, "sp96"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 64.0;
|
||||
else if (ffStrEquals(val.dataType, "spb4"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 16.0;
|
||||
else if (ffStrEquals(val.dataType, "spf0"))
|
||||
*value = (int16_t)ntohs(*(int16_t *)(val.bytes)) / 1.0;
|
||||
else
|
||||
if (ffStrEquals(val.dataType, "sp1e")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 16384.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp3c")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 4096.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp4b")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 2048.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp5a")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 1024.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp69")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 512.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp78")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 256.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp87")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 128.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp96")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 64.0;
|
||||
} else if (ffStrEquals(val.dataType, "spb4")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 16.0;
|
||||
} else if (ffStrEquals(val.dataType, "spf0")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 1.0;
|
||||
} else {
|
||||
return "Unsupported SMC signed integer data type";
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
return "Unsupported SMC signed data type";
|
||||
}
|
||||
break;
|
||||
|
||||
case '{': // special types like pwm
|
||||
if (ffStrEquals(val.dataType, "{pwm") && val.dataSize == 2)
|
||||
{
|
||||
*value = (double)ntohs(*(uint16_t *)(val.bytes)) * 100 / 65536.0;
|
||||
}
|
||||
else
|
||||
if (ffStrEquals(val.dataType, "{pwm") && val.dataSize == 2) {
|
||||
*value = (double) ntohs(*(uint16_t*) (val.bytes)) * 100 / 65536.0;
|
||||
} else {
|
||||
return "Unsupported SMC special data type";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -261,187 +273,191 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool detectTemp(io_connect_t conn, const char* sensor, double* sum)
|
||||
{
|
||||
static bool detectTemp(io_connect_t conn, const char* sensor, double* sum) {
|
||||
double temp = 0;
|
||||
const char* error = smcReadValue(conn, sensor, &temp);
|
||||
if (error) return false;
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
// https://github.com/exelban/stats/blob/14e29c4d60229c363cca9c9d25c30c87b7870830/Modules/Sensors/readers.swift#L124
|
||||
if (temp < 10 || temp > 120) return false;
|
||||
if (temp < 10 || temp > 120) {
|
||||
return false;
|
||||
}
|
||||
*sum += temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
static io_connect_t conn;
|
||||
|
||||
const char* ffDetectSmcSpecificTemp(const char* sensor, double* result)
|
||||
{
|
||||
if (!conn)
|
||||
{
|
||||
if (smcOpen(&conn) != NULL)
|
||||
const char* ffDetectSmcSpecificTemp(const char* sensor, double* result) {
|
||||
if (!conn) {
|
||||
if (smcOpen(&conn) != NULL) {
|
||||
conn = (io_connect_t) -1;
|
||||
}
|
||||
}
|
||||
if (conn == (io_connect_t) -1)
|
||||
if (conn == (io_connect_t) -1) {
|
||||
return "Could not open SMC connection";
|
||||
}
|
||||
|
||||
if (!detectTemp(conn, sensor, result))
|
||||
if (!detectTemp(conn, sensor, result)) {
|
||||
return "Could not read SMC temperature";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffDetectSmcTemps(enum FFTempType type, double* result)
|
||||
{
|
||||
if (!conn)
|
||||
{
|
||||
if (smcOpen(&conn) != NULL)
|
||||
const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
|
||||
if (!conn) {
|
||||
if (smcOpen(&conn) != NULL) {
|
||||
conn = (io_connect_t) -1;
|
||||
}
|
||||
}
|
||||
if (conn == (io_connect_t) -1)
|
||||
if (conn == (io_connect_t) -1) {
|
||||
return "Could not open SMC connection";
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
*result = 0;
|
||||
|
||||
// https://github.com/exelban/stats/blob/master/Modules/Sensors/values.swift
|
||||
switch (type)
|
||||
{
|
||||
case FF_TEMP_CPU_X64:
|
||||
count += detectTemp(conn, "TC0D", result); // CPU diode
|
||||
count += detectTemp(conn, "TC0E", result); // CPU diode virtual
|
||||
count += detectTemp(conn, "TC0F", result); // CPU diode filtered
|
||||
count += detectTemp(conn, "TC0P", result); // CPU proximity
|
||||
break;
|
||||
switch (type) {
|
||||
case FF_TEMP_CPU_X64:
|
||||
count += detectTemp(conn, "TC0D", result); // CPU diode
|
||||
count += detectTemp(conn, "TC0E", result); // CPU diode virtual
|
||||
count += detectTemp(conn, "TC0F", result); // CPU diode filtered
|
||||
count += detectTemp(conn, "TC0P", result); // CPU proximity
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M1X:
|
||||
count += detectTemp(conn, "Tp09", result); // CPU efficient core 1
|
||||
count += detectTemp(conn, "Tp0T", result); // CPU efficient core 2
|
||||
case FF_TEMP_CPU_M1X:
|
||||
count += detectTemp(conn, "Tp09", result); // CPU efficient core 1
|
||||
count += detectTemp(conn, "Tp0T", result); // CPU efficient core 2
|
||||
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0H", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0L", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0P", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0X", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 8
|
||||
break;
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0H", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0L", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0P", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0X", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M2X:
|
||||
count += detectTemp(conn, "Tp1h", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Tp1t", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Tp1p", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Tp1l", result); // CPU efficiency core 4
|
||||
case FF_TEMP_CPU_M2X:
|
||||
count += detectTemp(conn, "Tp1h", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Tp1t", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Tp1p", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Tp1l", result); // CPU efficiency core 4
|
||||
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp09", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0X", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0f", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0j", result); // CPU performance core 8
|
||||
break;
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp09", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0X", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0f", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0j", result); // CPU performance core 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M3X:
|
||||
count += detectTemp(conn, "Te05", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Te0L", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Te0P", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Te0S", result); // CPU efficiency core 4
|
||||
count += detectTemp(conn, "Tf04", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tf09", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tf0A", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tf0B", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tf0D", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tf0E", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tf44", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tf49", result); // CPU performance core 8
|
||||
count += detectTemp(conn, "Tf4A", result); // CPU performance core 9
|
||||
count += detectTemp(conn, "Tf4B", result); // CPU performance core 10
|
||||
count += detectTemp(conn, "Tf4D", result); // CPU performance core 11
|
||||
count += detectTemp(conn, "Tf4E", result); // CPU performance core 12
|
||||
break;
|
||||
case FF_TEMP_CPU_M3X:
|
||||
count += detectTemp(conn, "Te05", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Te0L", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Te0P", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Te0S", result); // CPU efficiency core 4
|
||||
count += detectTemp(conn, "Tf04", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tf09", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tf0A", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tf0B", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tf0D", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tf0E", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tf44", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tf49", result); // CPU performance core 8
|
||||
count += detectTemp(conn, "Tf4A", result); // CPU performance core 9
|
||||
count += detectTemp(conn, "Tf4B", result); // CPU performance core 10
|
||||
count += detectTemp(conn, "Tf4D", result); // CPU performance core 11
|
||||
count += detectTemp(conn, "Tf4E", result); // CPU performance core 12
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M4X:
|
||||
count += detectTemp(conn, "Te05", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Te0S", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Te09", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Te0H", result); // CPU efficiency core 4
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp09", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0V", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0Y", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0e", result); // CPU performance core 8
|
||||
break;
|
||||
case FF_TEMP_CPU_M4X:
|
||||
count += detectTemp(conn, "Te05", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Te0S", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Te09", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Te0H", result); // CPU efficiency core 4
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp09", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0V", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0Y", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0e", result); // CPU performance core 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_INTEL:
|
||||
count += detectTemp(conn, "TCGC", result); // GPU Intel Graphics
|
||||
goto gpu_unknown;
|
||||
case FF_TEMP_GPU_INTEL:
|
||||
count += detectTemp(conn, "TCGC", result); // GPU Intel Graphics
|
||||
goto gpu_unknown;
|
||||
|
||||
case FF_TEMP_GPU_AMD:
|
||||
count += detectTemp(conn, "TGDD", result); // GPU AMD Radeon
|
||||
goto gpu_unknown;
|
||||
case FF_TEMP_GPU_AMD:
|
||||
count += detectTemp(conn, "TGDD", result); // GPU AMD Radeon
|
||||
goto gpu_unknown;
|
||||
|
||||
case FF_TEMP_GPU_UNKNOWN: // Nvidia?
|
||||
gpu_unknown:
|
||||
count += detectTemp(conn, "TG0D", result); // GPU diode
|
||||
count += detectTemp(conn, "TG0P", result); // GPU proximity
|
||||
break;
|
||||
case FF_TEMP_GPU_UNKNOWN: // Nvidia?
|
||||
gpu_unknown:
|
||||
count += detectTemp(conn, "TG0D", result); // GPU diode
|
||||
count += detectTemp(conn, "TG0P", result); // GPU proximity
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M1X:
|
||||
count += detectTemp(conn, "Tg05", result); // GPU 1
|
||||
count += detectTemp(conn, "Tg0D", result); // GPU 2
|
||||
count += detectTemp(conn, "Tg0L", result); // GPU 3
|
||||
count += detectTemp(conn, "Tg0T", result); // GPU 4
|
||||
break;
|
||||
case FF_TEMP_GPU_M1X:
|
||||
count += detectTemp(conn, "Tg05", result); // GPU 1
|
||||
count += detectTemp(conn, "Tg0D", result); // GPU 2
|
||||
count += detectTemp(conn, "Tg0L", result); // GPU 3
|
||||
count += detectTemp(conn, "Tg0T", result); // GPU 4
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M2X:
|
||||
count += detectTemp(conn, "Tg0f", result); // GPU 1
|
||||
count += detectTemp(conn, "Tg0j", result); // GPU 2
|
||||
break;
|
||||
case FF_TEMP_GPU_M2X:
|
||||
count += detectTemp(conn, "Tg0f", result); // GPU 1
|
||||
count += detectTemp(conn, "Tg0j", result); // GPU 2
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M3X:
|
||||
count += detectTemp(conn, "Tf14", result); // GPU 1
|
||||
count += detectTemp(conn, "Tf18", result); // GPU 2
|
||||
count += detectTemp(conn, "Tf19", result); // GPU 3
|
||||
count += detectTemp(conn, "Tf1A", result); // GPU 4
|
||||
count += detectTemp(conn, "Tf24", result); // GPU 5
|
||||
count += detectTemp(conn, "Tf28", result); // GPU 6
|
||||
count += detectTemp(conn, "Tf29", result); // GPU 7
|
||||
count += detectTemp(conn, "Tf2A", result); // GPU 8
|
||||
break;
|
||||
case FF_TEMP_GPU_M3X:
|
||||
count += detectTemp(conn, "Tf14", result); // GPU 1
|
||||
count += detectTemp(conn, "Tf18", result); // GPU 2
|
||||
count += detectTemp(conn, "Tf19", result); // GPU 3
|
||||
count += detectTemp(conn, "Tf1A", result); // GPU 4
|
||||
count += detectTemp(conn, "Tf24", result); // GPU 5
|
||||
count += detectTemp(conn, "Tf28", result); // GPU 6
|
||||
count += detectTemp(conn, "Tf29", result); // GPU 7
|
||||
count += detectTemp(conn, "Tf2A", result); // GPU 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M4X:
|
||||
count += detectTemp(conn, "Tg0G", result); // GPU 1 (Basic)
|
||||
count += detectTemp(conn, "Tg0H", result); // GPU 2 (Basic)
|
||||
count += detectTemp(conn, "Tg1U", result); // GPU 1 (Pro / Max)
|
||||
count += detectTemp(conn, "Tg1k", result); // GPU 2 (Pro / Max)
|
||||
count += detectTemp(conn, "Tg0K", result); // GPU 3
|
||||
count += detectTemp(conn, "Tg0L", result); // GPU 4
|
||||
count += detectTemp(conn, "Tg0d", result); // GPU 5
|
||||
count += detectTemp(conn, "Tg0e", result); // GPU 6
|
||||
count += detectTemp(conn, "Tg0j", result); // GPU 7
|
||||
count += detectTemp(conn, "Tg0k", result); // GPU 8
|
||||
break;
|
||||
case FF_TEMP_GPU_M4X:
|
||||
count += detectTemp(conn, "Tg0G", result); // GPU 1 (Basic)
|
||||
count += detectTemp(conn, "Tg0H", result); // GPU 2 (Basic)
|
||||
count += detectTemp(conn, "Tg1U", result); // GPU 1 (Pro / Max)
|
||||
count += detectTemp(conn, "Tg1k", result); // GPU 2 (Pro / Max)
|
||||
count += detectTemp(conn, "Tg0K", result); // GPU 3
|
||||
count += detectTemp(conn, "Tg0L", result); // GPU 4
|
||||
count += detectTemp(conn, "Tg0d", result); // GPU 5
|
||||
count += detectTemp(conn, "Tg0e", result); // GPU 6
|
||||
count += detectTemp(conn, "Tg0j", result); // GPU 7
|
||||
count += detectTemp(conn, "Tg0k", result); // GPU 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_BATTERY:
|
||||
count += detectTemp(conn, "TB1T", result); // Battery
|
||||
count += detectTemp(conn, "TB2T", result); // Battery
|
||||
break;
|
||||
case FF_TEMP_BATTERY:
|
||||
count += detectTemp(conn, "TB1T", result); // Battery
|
||||
count += detectTemp(conn, "TB2T", result); // Battery
|
||||
break;
|
||||
|
||||
case FF_TEMP_MEMORY:
|
||||
count += detectTemp(conn, "Tm02", result); // Memory 1
|
||||
count += detectTemp(conn, "Tm06", result); // Memory 2
|
||||
count += detectTemp(conn, "Tm08", result); // Memory 3
|
||||
count += detectTemp(conn, "Tm09", result); // Memory 4
|
||||
break;
|
||||
case FF_TEMP_MEMORY:
|
||||
count += detectTemp(conn, "Tm02", result); // Memory 1
|
||||
count += detectTemp(conn, "Tm06", result); // Memory 2
|
||||
count += detectTemp(conn, "Tm08", result); // Memory 3
|
||||
count += detectTemp(conn, "Tm09", result); // Memory 4
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
if (count == 0) {
|
||||
return "No temperatures detected";
|
||||
}
|
||||
|
||||
*result /= count;
|
||||
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFTempValue
|
||||
{
|
||||
typedef struct FFTempValue {
|
||||
FFstrbuf name;
|
||||
FFstrbuf deviceClass;
|
||||
double value;
|
||||
} FFTempValue;
|
||||
|
||||
enum FFTempType
|
||||
{
|
||||
enum FFTempType {
|
||||
FF_TEMP_CPU_X64,
|
||||
FF_TEMP_CPU_M1X,
|
||||
FF_TEMP_CPU_M2X,
|
||||
|
||||
+18
-19
@@ -5,14 +5,12 @@
|
||||
// For ffRegReadValue(s)
|
||||
// If length is 0, data will be allocated with malloc() and must be freed by the caller
|
||||
// Otherwise, data must point to a buffer of at least length bytes, and the actual length of the data will be written to length.
|
||||
typedef struct FFArgBuffer
|
||||
{
|
||||
typedef struct FFArgBuffer {
|
||||
void* data;
|
||||
uint32_t length;
|
||||
} FFArgBuffer;
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFArgType
|
||||
{
|
||||
typedef enum FF_A_PACKED FFArgType {
|
||||
FF_ARG_TYPE_NULL = 0,
|
||||
FF_ARG_TYPE_UINT,
|
||||
FF_ARG_TYPE_UINT64,
|
||||
@@ -28,18 +26,19 @@ typedef enum __attribute__((__packed__)) FFArgType
|
||||
FF_ARG_TYPE_BUFFER,
|
||||
} FFArgType;
|
||||
|
||||
#define FF_ARG(variable, var_name) { _Generic((variable), \
|
||||
uint32_t: FF_ARG_TYPE_UINT, \
|
||||
uint64_t: FF_ARG_TYPE_UINT64, \
|
||||
uint16_t: FF_ARG_TYPE_UINT16, \
|
||||
uint8_t: FF_ARG_TYPE_UINT8, \
|
||||
int32_t: FF_ARG_TYPE_INT, \
|
||||
char*: FF_ARG_TYPE_STRING, \
|
||||
const char*: FF_ARG_TYPE_STRING, \
|
||||
FFstrbuf: FF_ARG_TYPE_STRBUF, \
|
||||
float: FF_ARG_TYPE_FLOAT, \
|
||||
double: FF_ARG_TYPE_DOUBLE, \
|
||||
FFlist: FF_ARG_TYPE_LIST, \
|
||||
bool: FF_ARG_TYPE_BOOL, \
|
||||
FFArgBuffer: FF_ARG_TYPE_BUFFER \
|
||||
), _Generic((variable), char*: (variable), const char*: (variable), default: &(variable) ), (var_name) }
|
||||
#define FF_ARG(variable, var_name) { _Generic((variable), \
|
||||
uint32_t: FF_ARG_TYPE_UINT, \
|
||||
uint64_t: FF_ARG_TYPE_UINT64, \
|
||||
uint16_t: FF_ARG_TYPE_UINT16, \
|
||||
uint8_t: FF_ARG_TYPE_UINT8, \
|
||||
int32_t: FF_ARG_TYPE_INT, \
|
||||
char*: FF_ARG_TYPE_STRING, \
|
||||
const char*: FF_ARG_TYPE_STRING, \
|
||||
FFstrbuf: FF_ARG_TYPE_STRBUF, \
|
||||
float: FF_ARG_TYPE_FLOAT, \
|
||||
double: FF_ARG_TYPE_DOUBLE, \
|
||||
FFlist: FF_ARG_TYPE_LIST, \
|
||||
bool: FF_ARG_TYPE_BOOL, \
|
||||
FFArgBuffer: FF_ARG_TYPE_BUFFER), \
|
||||
_Generic((variable), char*: (variable), const char*: (variable), default: &(variable)), \
|
||||
(var_name) }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __has_attribute
|
||||
#error Unsupported compiler
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#define FF_A_FALLTHROUGH __attribute__((__fallthrough__))
|
||||
#define FF_A_DEPRECATED __attribute__((__deprecated__))
|
||||
#define FF_A_CLEANUP(func) __attribute__((__cleanup__(func)))
|
||||
#define FF_A_NODISCARD __attribute__((__warn_unused_result__))
|
||||
#define FF_A_PRINTF(formatStrIndex, argsStartIndex) __attribute__((__format__(printf, formatStrIndex, argsStartIndex)))
|
||||
#define FF_A_SCANF(formatStrIndex, argsStartIndex) __attribute__((__format__(scanf, formatStrIndex, argsStartIndex)))
|
||||
#define FF_A_NONNULL(argIndex, ...) __attribute__((__nonnull__(argIndex, ##__VA_ARGS__)))
|
||||
#define FF_A_RETURNS_NONNULL __attribute__((__returns_nonnull__))
|
||||
#define FF_A_UNUSED __attribute__((__unused__))
|
||||
#define FF_A_PACKED __attribute__((__packed__))
|
||||
#define FF_A_WEAK_IMPORT __attribute__((__weak_import__))
|
||||
+4
-6
@@ -2,9 +2,8 @@
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output);
|
||||
static inline FFstrbuf ffBase64EncodeStrbuf(const FFstrbuf* in)
|
||||
{
|
||||
void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output);
|
||||
static inline FFstrbuf ffBase64EncodeStrbuf(const FFstrbuf* in) {
|
||||
FFstrbuf out = ffStrbufCreateA(10 + in->length * 4 / 3);
|
||||
ffBase64EncodeRaw(in->length, in->chars, &out.length, out.chars);
|
||||
assert(out.length < out.allocated);
|
||||
@@ -12,9 +11,8 @@ static inline FFstrbuf ffBase64EncodeStrbuf(const FFstrbuf* in)
|
||||
return out;
|
||||
}
|
||||
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output);
|
||||
static inline FFstrbuf ffBase64DecodeStrbuf(const FFstrbuf* in)
|
||||
{
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output);
|
||||
static inline FFstrbuf ffBase64DecodeStrbuf(const FFstrbuf* in) {
|
||||
FFstrbuf out = ffStrbufCreateA(10 + in->length * 3 / 4);
|
||||
ffBase64DecodeRaw(in->length, in->chars, &out.length, out.chars);
|
||||
assert(out.length < out.allocated);
|
||||
|
||||
+11
-14
@@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef FF_HAVE_DBUS
|
||||
#include <dbus/dbus.h>
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/library.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/library.h"
|
||||
|
||||
typedef struct FFDBusLibrary
|
||||
{
|
||||
typedef struct FFDBusLibrary {
|
||||
FF_LIBRARY_SYMBOL(dbus_bus_get)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_new_method_call)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_append_args)
|
||||
@@ -22,28 +21,26 @@ typedef struct FFDBusLibrary
|
||||
FF_LIBRARY_SYMBOL(dbus_connection_unref)
|
||||
} FFDBusLibrary;
|
||||
|
||||
typedef struct FFDBusData
|
||||
{
|
||||
typedef struct FFDBusData {
|
||||
const FFDBusLibrary* lib;
|
||||
DBusConnection* connection;
|
||||
} FFDBusData;
|
||||
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data); //Returns an error message or NULL on success
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data); // Returns an error message or NULL on success
|
||||
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result);
|
||||
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result);
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint32_t* result);
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint64_t* result);
|
||||
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg1, const char* arg2);
|
||||
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property);
|
||||
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result);
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int32_t* result);
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result);
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int64_t* result);
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint64_t* result);
|
||||
void ffDBusDestroyData(FFDBusData* data);
|
||||
|
||||
static inline DBusMessage* ffDBusGetAllProperties(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface)
|
||||
{
|
||||
static inline DBusMessage* ffDBusGetAllProperties(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface) {
|
||||
return ffDBusGetMethodReply(dbus, busName, objectPath, "org.freedesktop.DBus.Properties", "GetAll", interface, NULL);
|
||||
}
|
||||
|
||||
#define FF_DBUS_AUTO_DESTROY_DATA __attribute__((__cleanup__(ffDBusDestroyData)))
|
||||
#define FF_DBUS_AUTO_DESTROY_DATA FF_A_CLEANUP(ffDBusDestroyData)
|
||||
|
||||
#endif // FF_HAVE_DBUS
|
||||
|
||||
+12
-10
@@ -3,27 +3,29 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/time.h"
|
||||
|
||||
static inline const char* ffFindFileName(const char* file)
|
||||
{
|
||||
static inline const char* ffFindFileName(const char* file) {
|
||||
const char* lastSlash = __builtin_strrchr(file, '/');
|
||||
#ifdef _WIN32
|
||||
if (lastSlash == NULL)
|
||||
#ifdef _WIN32
|
||||
if (lastSlash == NULL) {
|
||||
lastSlash = __builtin_strrchr(file, '\\');
|
||||
#endif
|
||||
if (lastSlash != NULL)
|
||||
}
|
||||
#endif
|
||||
if (lastSlash != NULL) {
|
||||
return lastSlash + 1;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FF_DEBUG_PRINT(file_, line_, format_, ...) \
|
||||
do { \
|
||||
if (instance.config.display.debugMode) \
|
||||
#define FF_DEBUG_PRINT(file_, line_, format_, ...) \
|
||||
do { \
|
||||
if (instance.config.display.debugMode) \
|
||||
fprintf(stderr, "[%s%4d, %s] " format_ "\n", ffFindFileName(file_), line_, ffTimeToTimeStr(ffTimeGetNow()), ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define FF_DEBUG_PRINT(file_, line_, format_, ...) \
|
||||
do { } while (0)
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define FF_DEBUG(format, ...) FF_DEBUG_PRINT(__FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
+7
-9
@@ -2,8 +2,7 @@
|
||||
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFDataResultDocType
|
||||
{
|
||||
typedef enum FF_A_PACKED FFDataResultDocType {
|
||||
FF_RESULT_DOC_TYPE_DEFAULT = 0,
|
||||
FF_RESULT_DOC_TYPE_JSON,
|
||||
FF_RESULT_DOC_TYPE_CONFIG,
|
||||
@@ -12,13 +11,12 @@ typedef enum __attribute__((__packed__)) FFDataResultDocType
|
||||
|
||||
// FFdata aggregates configuration, generation parameters, and output state used by fastfetch.
|
||||
// It holds the parsed configuration document, a mutable JSON document for results, and related metadata.
|
||||
typedef struct FFdata
|
||||
{
|
||||
yyjson_doc* configDoc; // Parsed JSON configuration document
|
||||
yyjson_mut_doc* resultDoc; // Mutable JSON document for storing results
|
||||
FFstrbuf structure; // Custom output structure from command line
|
||||
FFstrbuf structureDisabled; // Disabled modules in the output structure from command line
|
||||
FFstrbuf genConfigPath; // Path to generate configuration file
|
||||
typedef struct FFdata {
|
||||
yyjson_doc* configDoc; // Parsed JSON configuration document
|
||||
yyjson_mut_doc* resultDoc; // Mutable JSON document for storing results
|
||||
FFstrbuf structure; // Custom output structure from command line
|
||||
FFstrbuf structureDisabled; // Disabled modules in the output structure from command line
|
||||
FFstrbuf genConfigPath; // Path to generate configuration file
|
||||
FFDataResultDocType docType; // Type of result document
|
||||
bool configLoaded;
|
||||
} FFdata;
|
||||
|
||||
+2
-4
@@ -3,8 +3,7 @@
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/FFlist.h"
|
||||
|
||||
typedef struct FFfont
|
||||
{
|
||||
typedef struct FFfont {
|
||||
FFstrbuf pretty;
|
||||
FFstrbuf name;
|
||||
FFstrbuf size;
|
||||
@@ -21,7 +20,6 @@ void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName);
|
||||
void ffFontDestroy(FFfont* font);
|
||||
|
||||
static inline void ffFontInitCopy(FFfont* font, const char* name)
|
||||
{
|
||||
static inline void ffFontInitCopy(FFfont* font, const char* name) {
|
||||
ffFontInitValues(font, name, NULL);
|
||||
}
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@
|
||||
|
||||
#include "common/argType.h"
|
||||
|
||||
typedef struct FFformatarg
|
||||
{
|
||||
typedef struct FFformatarg {
|
||||
FFArgType type;
|
||||
const void* value;
|
||||
const char* name; // argument name, must start with an alphabet
|
||||
|
||||
@@ -5,20 +5,22 @@ extern "C" {
|
||||
#include <File.h>
|
||||
#include <AppFileInfo.h>
|
||||
|
||||
bool ffGetFileVersion(const char* filePath, FFstrbuf* version)
|
||||
{
|
||||
bool ffGetFileVersion(const char* filePath, FFstrbuf* version) {
|
||||
BFile f(filePath, B_READ_ONLY);
|
||||
if (f.InitCheck() != B_OK)
|
||||
if (f.InitCheck() != B_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BAppFileInfo fileInfo(&f);
|
||||
if (f.InitCheck() != B_OK)
|
||||
if (f.InitCheck() != B_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
version_info info;
|
||||
if (fileInfo.GetVersionInfo(&info, B_SYSTEM_VERSION_KIND) != B_OK)
|
||||
if (fileInfo.GetVersionInfo(&info, B_SYSTEM_VERSION_KIND) != B_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ffStrbufSetF(version, "%d.%d.%d", (int)info.major, (int)info.middle, (int)info.minor);
|
||||
ffStrbufSetF(version, "%d.%d.%d", (int) info.major, (int) info.middle, (int) info.minor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
#include "common/io.h"
|
||||
#include "detection/version/version.h"
|
||||
|
||||
void ffPlatformInit(FFPlatform* platform)
|
||||
{
|
||||
void ffPlatformInit(FFPlatform* platform) {
|
||||
ffStrbufInit(&platform->homeDir);
|
||||
ffStrbufInit(&platform->cacheDir);
|
||||
ffListInit(&platform->configDirs, sizeof(FFstrbuf));
|
||||
ffListInit(&platform->dataDirs, sizeof(FFstrbuf));
|
||||
ffListInit(&platform->configDirs);
|
||||
ffListInit(&platform->dataDirs);
|
||||
ffStrbufInit(&platform->exePath);
|
||||
ffStrbufInit(&platform->cwd);
|
||||
|
||||
@@ -17,9 +16,9 @@ void ffPlatformInit(FFPlatform* platform)
|
||||
ffStrbufInit(&platform->hostName);
|
||||
ffStrbufInit(&platform->userShell);
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
ffStrbufInit(&platform->sid);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FFPlatformSysinfo* info = &platform->sysinfo;
|
||||
|
||||
@@ -27,27 +26,31 @@ void ffPlatformInit(FFPlatform* platform)
|
||||
ffStrbufInit(&info->release);
|
||||
ffStrbufInit(&info->version);
|
||||
ffStrbufInit(&info->architecture);
|
||||
info->pageSize = 0;
|
||||
|
||||
ffPlatformInitImpl(platform);
|
||||
|
||||
if(info->name.length == 0)
|
||||
if (info->name.length == 0) {
|
||||
ffStrbufSetStatic(&info->name, ffVersionResult.sysName);
|
||||
}
|
||||
|
||||
if(info->architecture.length == 0)
|
||||
if (info->architecture.length == 0) {
|
||||
ffStrbufSetStatic(&info->architecture, ffVersionResult.architecture);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPlatformDestroy(FFPlatform* platform)
|
||||
{
|
||||
void ffPlatformDestroy(FFPlatform* platform) {
|
||||
ffStrbufDestroy(&platform->homeDir);
|
||||
ffStrbufDestroy(&platform->cacheDir);
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, dir, platform->configDirs)
|
||||
FF_LIST_FOR_EACH (FFstrbuf, dir, platform->configDirs) {
|
||||
ffStrbufDestroy(dir);
|
||||
}
|
||||
ffListDestroy(&platform->configDirs);
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, dir, platform->dataDirs)
|
||||
FF_LIST_FOR_EACH (FFstrbuf, dir, platform->dataDirs) {
|
||||
ffStrbufDestroy(dir);
|
||||
}
|
||||
ffListDestroy(&platform->dataDirs);
|
||||
ffStrbufDestroy(&platform->exePath);
|
||||
ffStrbufDestroy(&platform->cwd);
|
||||
@@ -57,9 +60,9 @@ void ffPlatformDestroy(FFPlatform* platform)
|
||||
ffStrbufDestroy(&platform->userShell);
|
||||
ffStrbufDestroy(&platform->fullUserName);
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
ffStrbufDestroy(&platform->sid);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FFPlatformSysinfo* info = &platform->sysinfo;
|
||||
ffStrbufDestroy(&info->architecture);
|
||||
@@ -68,23 +71,24 @@ void ffPlatformDestroy(FFPlatform* platform)
|
||||
ffStrbufDestroy(&info->version);
|
||||
}
|
||||
|
||||
void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path)
|
||||
{
|
||||
if (!ffPathExists(path, FF_PATHTYPE_DIRECTORY))
|
||||
void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path) {
|
||||
if (!ffPathExists(path, FF_PATHTYPE_DIRECTORY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateS(path);
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (!ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
|
||||
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
|
||||
if (!FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPlatformPathAddHome(FFlist* dirs, const FFPlatform* platform, const char* suffix)
|
||||
{
|
||||
void ffPlatformPathAddHome(FFlist* dirs, const FFPlatform* platform, const char* suffix) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateA(64);
|
||||
ffStrbufAppend(&buffer, &platform->homeDir);
|
||||
ffStrbufAppendS(&buffer, suffix);
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
|
||||
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
|
||||
if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
+152
-173
@@ -18,6 +18,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <kvm.h>
|
||||
#include "common/path.h"
|
||||
#elif defined(__HAIKU__)
|
||||
@@ -25,154 +26,148 @@
|
||||
#include <OS.h>
|
||||
#endif
|
||||
|
||||
static void getExePath(FFPlatform* platform)
|
||||
{
|
||||
static void getExePath(FFPlatform* platform) {
|
||||
char exePath[PATH_MAX];
|
||||
#if defined(__linux__) || defined (__GNU__)
|
||||
ssize_t exePathLen = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
|
||||
if (exePathLen >= 0)
|
||||
exePath[exePathLen] = '\0';
|
||||
#elif defined(__APPLE__)
|
||||
uint32_t exePathLen = sizeof(exePath);
|
||||
if (_NSGetExecutablePath(exePath, &exePathLen) == 0)
|
||||
exePathLen = (uint32_t) strlen(exePath);
|
||||
else
|
||||
exePathLen = 0;
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
size_t exePathLen = sizeof(exePath);
|
||||
if(sysctl(
|
||||
(int[]){CTL_KERN,
|
||||
#ifdef __FreeBSD__
|
||||
KERN_PROC, KERN_PROC_PATHNAME, (pid_t) platform->pid
|
||||
#else
|
||||
KERN_PROC_ARGS, platform->pid, KERN_PROC_PATHNAME
|
||||
#endif
|
||||
}, 4,
|
||||
exePath, &exePathLen,
|
||||
NULL, 0
|
||||
) < 0)
|
||||
exePathLen = 0;
|
||||
else
|
||||
exePathLen--; // remove terminating NUL
|
||||
#elif defined(__OpenBSD__)
|
||||
// OpenBSD doesn't have a reliable way to get the executable path.
|
||||
// Current implementation uses argv[0], which can be easily spoofed.
|
||||
// See #2195
|
||||
size_t exePathLen = 0;
|
||||
kvm_t* kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
|
||||
if (kd)
|
||||
{
|
||||
int kpCount;
|
||||
struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
|
||||
if (kp && kpCount == 1)
|
||||
{
|
||||
char** argv = kvm_getargv(kd, kp, 0);
|
||||
if (argv && argv[0])
|
||||
{
|
||||
char* arg0 = argv[0];
|
||||
if (arg0[0])
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
ssize_t exePathLen = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
|
||||
if (exePathLen >= 0) {
|
||||
exePath[exePathLen] = '\0';
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
uint32_t exePathLen = sizeof(exePath);
|
||||
if (_NSGetExecutablePath(exePath, &exePathLen) == 0) {
|
||||
exePathLen = (uint32_t) strlen(exePath);
|
||||
} else {
|
||||
exePathLen = 0;
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
size_t exePathLen = sizeof(exePath);
|
||||
if (sysctl(
|
||||
(int[]) { CTL_KERN,
|
||||
#ifdef __FreeBSD__
|
||||
KERN_PROC,
|
||||
KERN_PROC_PATHNAME,
|
||||
(pid_t) platform->pid
|
||||
#else
|
||||
KERN_PROC_ARGS,
|
||||
(pid_t) platform->pid,
|
||||
KERN_PROC_PATHNAME
|
||||
#endif
|
||||
},
|
||||
4,
|
||||
exePath,
|
||||
&exePathLen,
|
||||
NULL,
|
||||
0) < 0)
|
||||
exePathLen = 0;
|
||||
else {
|
||||
exePathLen--; // remove terminating NUL
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
// OpenBSD doesn't have a reliable way to get the executable path.
|
||||
// Current implementation uses argv[0], which can be easily spoofed.
|
||||
// See #2195
|
||||
size_t exePathLen = 0;
|
||||
kvm_t* kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
|
||||
if (kd) {
|
||||
int kpCount;
|
||||
struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
|
||||
if (kp && kpCount == 1) {
|
||||
char** argv = kvm_getargv(kd, kp, 0);
|
||||
if (argv && argv[0]) {
|
||||
char* arg0 = argv[0];
|
||||
if (arg0[0]) {
|
||||
if (strchr(arg0, '/') != NULL) // likely a path (absolute or relative)
|
||||
{
|
||||
if (strchr(arg0, '/') != NULL) // likely a path (absolute or relative)
|
||||
{
|
||||
exePathLen = strlen(arg0);
|
||||
if (exePathLen < ARRAY_SIZE(exePath))
|
||||
{
|
||||
memcpy(exePath, arg0, exePathLen);
|
||||
exePath[exePathLen] = '\0';
|
||||
}
|
||||
else
|
||||
exePathLen = 0;
|
||||
exePathLen = strlen(arg0);
|
||||
if (exePathLen < ARRAY_SIZE(exePath)) {
|
||||
memcpy(exePath, arg0, exePathLen);
|
||||
exePath[exePathLen] = '\0';
|
||||
} else {
|
||||
exePathLen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY tmpPath = ffStrbufCreate();
|
||||
if (ffFindExecutableInPath(arg0, &tmpPath) == NULL && tmpPath.length < ARRAY_SIZE(exePath))
|
||||
{
|
||||
memcpy(exePath, tmpPath.chars, tmpPath.length + 1);
|
||||
exePathLen = tmpPath.length;
|
||||
}
|
||||
} else {
|
||||
FF_STRBUF_AUTO_DESTROY tmpPath = ffStrbufCreate();
|
||||
if (ffFindExecutableInPath(arg0, &tmpPath) == NULL && tmpPath.length < ARRAY_SIZE(exePath)) {
|
||||
memcpy(exePath, tmpPath.chars, tmpPath.length + 1);
|
||||
exePathLen = tmpPath.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (exePathLen > 0)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode))
|
||||
{
|
||||
int cntp;
|
||||
struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, platform->pid, sizeof(*kf), &cntp);
|
||||
if (kf)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < cntp; i++)
|
||||
{
|
||||
if (kf[i].fd_fd == KERN_FILE_TEXT)
|
||||
{
|
||||
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
|
||||
if (st.st_dev != (dev_t)kf[i].va_fsid || st.st_ino != (ino_t)kf[i].va_fileid)
|
||||
i = -1;
|
||||
break;
|
||||
if (exePathLen > 0) {
|
||||
struct stat st;
|
||||
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
int cntp;
|
||||
struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, (pid_t) platform->pid, sizeof(*kf), &cntp);
|
||||
if (kf) {
|
||||
int i;
|
||||
for (i = 0; i < cntp; i++) {
|
||||
if (kf[i].fd_fd == KERN_FILE_TEXT) {
|
||||
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
|
||||
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
|
||||
i = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i < 0)
|
||||
exePathLen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we can't get the list of open files, we can't verify that the file is actually the executable
|
||||
// Assume it is
|
||||
if (i < 0) {
|
||||
exePathLen = 0;
|
||||
}
|
||||
} else {
|
||||
// If we can't get the list of open files, we can't verify that the file is actually the executable
|
||||
// Assume it is
|
||||
}
|
||||
else
|
||||
exePathLen = 0;
|
||||
} else {
|
||||
exePathLen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
}
|
||||
#elif defined(__sun)
|
||||
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
|
||||
if (exePathLen >= 0)
|
||||
exePath[exePathLen] = '\0';
|
||||
#elif defined(__HAIKU__)
|
||||
size_t exePathLen = 0;
|
||||
image_info info;
|
||||
int32 cookie = 0;
|
||||
kvm_close(kd);
|
||||
}
|
||||
#elif defined(__sun)
|
||||
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
|
||||
if (exePathLen >= 0) {
|
||||
exePath[exePathLen] = '\0';
|
||||
}
|
||||
#elif defined(__HAIKU__)
|
||||
size_t exePathLen = 0;
|
||||
image_info info;
|
||||
int32 cookie = 0;
|
||||
|
||||
while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
|
||||
if (info.type == B_APP_IMAGE) {
|
||||
exePathLen = strlcpy(exePath, info.name, sizeof(exePath));
|
||||
break;
|
||||
}
|
||||
while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
|
||||
if (info.type == B_APP_IMAGE) {
|
||||
exePathLen = strlcpy(exePath, info.name, sizeof(exePath));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (exePathLen > 0)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
if (exePathLen > 0) {
|
||||
ffStrbufEnsureFree(&platform->exePath, PATH_MAX);
|
||||
if (realpath(exePath, platform->exePath.chars))
|
||||
if (realpath(exePath, platform->exePath.chars)) {
|
||||
ffStrbufRecalculateLength(&platform->exePath);
|
||||
else
|
||||
} else {
|
||||
ffStrbufSetNS(&platform->exePath, (uint32_t) exePathLen, exePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void platformPathAddEnv(FFlist* dirs, const char* env)
|
||||
{
|
||||
static void platformPathAddEnv(FFlist* dirs, const char* env) {
|
||||
const char* envValue = getenv(env);
|
||||
if(!ffStrSet(envValue))
|
||||
if (!ffStrSet(envValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY value = ffStrbufCreateA(64);
|
||||
ffStrbufAppendS(&value, envValue);
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < value.length)
|
||||
{
|
||||
while (startIndex < value.length) {
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&value, startIndex, ':');
|
||||
value.chars[colonIndex] = '\0';
|
||||
|
||||
if(!ffStrSet(value.chars + startIndex))
|
||||
{
|
||||
if (!ffStrSet(value.chars + startIndex)) {
|
||||
startIndex = colonIndex + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -183,61 +178,53 @@ static void platformPathAddEnv(FFlist* dirs, const char* env)
|
||||
}
|
||||
}
|
||||
|
||||
static void getHomeDir(FFPlatform* platform, const struct passwd* pwd)
|
||||
{
|
||||
static void getHomeDir(FFPlatform* platform, const struct passwd* pwd) {
|
||||
const char* home = pwd ? pwd->pw_dir : getenv("HOME");
|
||||
ffStrbufAppendS(&platform->homeDir, home);
|
||||
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
|
||||
}
|
||||
|
||||
static void getCacheDir(FFPlatform* platform)
|
||||
{
|
||||
static void getCacheDir(FFPlatform* platform) {
|
||||
const char* cache = getenv("XDG_CACHE_HOME");
|
||||
if(ffStrSet(cache))
|
||||
{
|
||||
if (ffStrSet(cache)) {
|
||||
ffStrbufAppendS(&platform->cacheDir, cache);
|
||||
ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppend(&platform->cacheDir, &platform->homeDir);
|
||||
ffStrbufAppendS(&platform->cacheDir, ".cache/");
|
||||
}
|
||||
}
|
||||
|
||||
static void getConfigDirs(FFPlatform* platform)
|
||||
{
|
||||
static void getConfigDirs(FFPlatform* platform) {
|
||||
// Always make sure `${XDG_CONFIG_HOME:-$HOME/.config}` is the first entry
|
||||
platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_HOME");
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, ".config/");
|
||||
|
||||
#if defined(__APPLE__)
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Preferences/");
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Application Support/");
|
||||
#endif
|
||||
#if defined(__HAIKU__)
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "config/settings/");
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Preferences/");
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Application Support/");
|
||||
#endif
|
||||
#if defined(__HAIKU__)
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "config/settings/");
|
||||
#endif
|
||||
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "");
|
||||
platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_DIRS");
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/xdg/");
|
||||
#endif
|
||||
#if !defined(__APPLE__)
|
||||
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/xdg/");
|
||||
#endif
|
||||
|
||||
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/");
|
||||
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_INSTALL_SYSCONF "/");
|
||||
}
|
||||
|
||||
static void getDataDirs(FFPlatform* platform)
|
||||
{
|
||||
static void getDataDirs(FFPlatform* platform) {
|
||||
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_HOME");
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/");
|
||||
|
||||
// Add ${currentExePath}/../share
|
||||
if (platform->exePath.length > 0)
|
||||
{
|
||||
if (platform->exePath.length > 0) {
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&platform->exePath);
|
||||
ffStrbufSubstrBeforeLastC(&path, '/');
|
||||
ffStrbufSubstrBeforeLastC(&path, '/');
|
||||
@@ -245,9 +232,9 @@ static void getDataDirs(FFPlatform* platform)
|
||||
ffPlatformPathAddAbsolute(&platform->dataDirs, path.chars);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "Library/Application Support/");
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "Library/Application Support/");
|
||||
#endif
|
||||
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
|
||||
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS");
|
||||
@@ -258,74 +245,66 @@ static void getDataDirs(FFPlatform* platform)
|
||||
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/share/");
|
||||
}
|
||||
|
||||
static void getUserName(FFPlatform* platform, const struct passwd* pwd)
|
||||
{
|
||||
if (pwd)
|
||||
{
|
||||
static void getUserName(FFPlatform* platform, const struct passwd* pwd) {
|
||||
if (pwd) {
|
||||
ffStrbufSetS(&platform->userName, pwd->pw_name);
|
||||
ffStrbufSetS(&platform->fullUserName, pwd->pw_gecos);
|
||||
ffStrbufTrimSpace(&platform->fullUserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufSetS(&platform->userName, getenv("USER"));
|
||||
}
|
||||
}
|
||||
|
||||
static void getHostName(FFPlatform* platform, const struct utsname* uts)
|
||||
{
|
||||
static void getHostName(FFPlatform* platform, const struct utsname* uts) {
|
||||
ffStrbufAppendS(&platform->hostName, uts->nodename);
|
||||
}
|
||||
|
||||
static void getUserShell(FFPlatform* platform, const struct passwd* pwd)
|
||||
{
|
||||
static void getUserShell(FFPlatform* platform, const struct passwd* pwd) {
|
||||
const char* shell = getenv("SHELL");
|
||||
if(!ffStrSet(shell) && pwd)
|
||||
if (!ffStrSet(shell) && pwd) {
|
||||
shell = pwd->pw_shell;
|
||||
}
|
||||
|
||||
ffStrbufAppendS(&platform->userShell, shell);
|
||||
}
|
||||
|
||||
static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts)
|
||||
{
|
||||
static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) {
|
||||
ffStrbufAppendS(&info->name, uts->sysname);
|
||||
ffStrbufAppendS(&info->release, uts->release);
|
||||
ffStrbufAppendS(&info->version, uts->version);
|
||||
#ifdef __HAIKU__
|
||||
#ifdef __HAIKU__
|
||||
/* historical reason */
|
||||
if (ffStrEquals(uts->machine, "BePC"))
|
||||
if (ffStrEquals(uts->machine, "BePC")) {
|
||||
ffStrbufSetStatic(&info->architecture, "i386");
|
||||
else
|
||||
#endif
|
||||
ffStrbufAppendS(&info->architecture, uts->machine);
|
||||
} else
|
||||
#endif
|
||||
ffStrbufAppendS(&info->architecture, uts->machine);
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
size_t length = sizeof(info->pageSize);
|
||||
sysctl((int[]){ CTL_HW, HW_PAGESIZE }, 2, &info->pageSize, &length, NULL, 0);
|
||||
#else
|
||||
sysctl((int[]) { CTL_HW, HW_PAGESIZE }, 2, &info->pageSize, &length, NULL, 0);
|
||||
#else
|
||||
info->pageSize = (uint32_t) sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void getCwd(FFPlatform* platform)
|
||||
{
|
||||
static void getCwd(FFPlatform* platform) {
|
||||
char cwd[PATH_MAX];
|
||||
if (getcwd(cwd, sizeof(cwd)) != NULL)
|
||||
{
|
||||
if (getcwd(cwd, sizeof(cwd)) != NULL) {
|
||||
ffStrbufSetS(&platform->cwd, cwd);
|
||||
ffStrbufEnsureEndsWithC(&platform->cwd, '/');
|
||||
}
|
||||
}
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform)
|
||||
{
|
||||
void ffPlatformInitImpl(FFPlatform* platform) {
|
||||
platform->pid = (uint32_t) getpid();
|
||||
platform->uid = getuid();
|
||||
struct passwd* pwd = getpwuid(platform->uid);
|
||||
|
||||
struct utsname uts;
|
||||
if(uname(&uts) < 0)
|
||||
if (uname(&uts) < 0) {
|
||||
memset(&uts, 0, sizeof(uts));
|
||||
}
|
||||
|
||||
getExePath(platform);
|
||||
getCwd(platform);
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
#define SECURITY_WIN32 1 // For secext.h
|
||||
#include <secext.h>
|
||||
|
||||
static void getExePath(FFPlatform* platform)
|
||||
{
|
||||
static void getExePath(FFPlatform* platform) {
|
||||
wchar_t exePathW[MAX_PATH];
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(
|
||||
@@ -26,19 +25,17 @@ static void getExePath(FFPlatform* platform)
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
if (hPath != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (hPath != INVALID_HANDLE_VALUE) {
|
||||
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_NORMALIZED);
|
||||
if (len > 0 && len < MAX_PATH)
|
||||
{
|
||||
if (len > 0 && len < MAX_PATH) {
|
||||
ffStrbufSetNWS(&platform->exePath, len, exePathW);
|
||||
if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\"))
|
||||
if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\")) {
|
||||
ffStrbufSubstrAfter(&platform->exePath, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (platform->exePath.length == 0)
|
||||
{
|
||||
if (platform->exePath.length == 0) {
|
||||
PCUNICODE_STRING imagePathName = &ffGetPeb()->ProcessParameters->ImagePathName;
|
||||
ffStrbufSetNWS(&platform->exePath, imagePathName->Length / sizeof(wchar_t), imagePathName->Buffer);
|
||||
}
|
||||
@@ -46,17 +43,13 @@ static void getExePath(FFPlatform* platform)
|
||||
ffStrbufReplaceAllC(&platform->exePath, '\\', '/');
|
||||
}
|
||||
|
||||
static void getHomeDir(FFPlatform* platform)
|
||||
{
|
||||
static void getHomeDir(FFPlatform* platform) {
|
||||
PWSTR pPath = NULL;
|
||||
if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pPath)))
|
||||
{
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pPath))) {
|
||||
ffStrbufSetWS(&platform->homeDir, pPath);
|
||||
ffStrbufReplaceAllC(&platform->homeDir, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufSetS(&platform->homeDir, getenv("USERPROFILE"));
|
||||
ffStrbufReplaceAllC(&platform->homeDir, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
|
||||
@@ -64,61 +57,54 @@ static void getHomeDir(FFPlatform* platform)
|
||||
CoTaskMemFree(pPath);
|
||||
}
|
||||
|
||||
static void getCacheDir(FFPlatform* platform)
|
||||
{
|
||||
static void getCacheDir(FFPlatform* platform) {
|
||||
PWSTR pPath = NULL;
|
||||
if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &pPath)))
|
||||
{
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &pPath))) {
|
||||
ffStrbufSetWS(&platform->cacheDir, pPath);
|
||||
ffStrbufReplaceAllC(&platform->cacheDir, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppend(&platform->cacheDir, &platform->homeDir);
|
||||
ffStrbufAppendS(&platform->cacheDir, "AppData/Local/");
|
||||
}
|
||||
CoTaskMemFree(pPath);
|
||||
}
|
||||
|
||||
static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId)
|
||||
{
|
||||
static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId) {
|
||||
PWSTR pPath = NULL;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath)))
|
||||
{
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath))) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath);
|
||||
CoTaskMemFree(pPath);
|
||||
ffStrbufReplaceAllC(&buffer, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (!ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
|
||||
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
|
||||
if (!FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix)
|
||||
{
|
||||
static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix) {
|
||||
const char* value = getenv(env);
|
||||
if(!ffStrSet(value))
|
||||
if (!ffStrSet(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateA(64);
|
||||
ffStrbufAppendS(&buffer, value);
|
||||
ffStrbufReplaceAllC(&buffer, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (suffix)
|
||||
{
|
||||
if (suffix) {
|
||||
ffStrbufAppendS(&buffer, suffix);
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
}
|
||||
|
||||
if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
|
||||
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
|
||||
if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void getConfigDirs(FFPlatform* platform)
|
||||
{
|
||||
if(getenv("MSYSTEM"))
|
||||
{
|
||||
static void getConfigDirs(FFPlatform* platform) {
|
||||
if (getenv("MSYSTEM")) {
|
||||
// We are in MSYS2 / Git Bash
|
||||
platformPathAddEnvSuffix(&platform->configDirs, "HOME", ".config/");
|
||||
platformPathAddEnvSuffix(&platform->configDirs, "HOME", NULL);
|
||||
@@ -132,10 +118,8 @@ static void getConfigDirs(FFPlatform* platform)
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "");
|
||||
}
|
||||
|
||||
static void getDataDirs(FFPlatform* platform)
|
||||
{
|
||||
if(getenv("MSYSTEM") && getenv("HOME"))
|
||||
{
|
||||
static void getDataDirs(FFPlatform* platform) {
|
||||
if (getenv("MSYSTEM") && getenv("HOME")) {
|
||||
// We are in MSYS2 / Git Bash
|
||||
platformPathAddEnvSuffix(&platform->dataDirs, "HOME", ".local/share/");
|
||||
platformPathAddEnvSuffix(&platform->dataDirs, "HOME", NULL);
|
||||
@@ -148,75 +132,82 @@ static void getDataDirs(FFPlatform* platform)
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
|
||||
}
|
||||
|
||||
static void getUserName(FFPlatform* platform)
|
||||
{
|
||||
static void getUserName(FFPlatform* platform) {
|
||||
wchar_t buffer[256];
|
||||
DWORD size = ARRAY_SIZE(buffer);
|
||||
if (GetUserNameExW(NameDisplay, buffer, &size))
|
||||
if (GetUserNameExW(NameDisplay, buffer, &size)) {
|
||||
ffStrbufSetWS(&platform->fullUserName, buffer);
|
||||
}
|
||||
|
||||
size = ARRAY_SIZE(buffer);
|
||||
if (GetUserNameW(buffer, &size)) // GetUserNameExW(10002)?
|
||||
ffStrbufSetWS(&platform->userName, buffer);
|
||||
else
|
||||
NTSYSAPI NTSTATUS NTAPI LsaGetUserName(
|
||||
_Outptr_ PLSA_UNICODE_STRING * UserName,
|
||||
_Outptr_opt_ PLSA_UNICODE_STRING * DomainName);
|
||||
PLSA_UNICODE_STRING userName = NULL;
|
||||
if (NT_SUCCESS(LsaGetUserName(&userName, NULL))) {
|
||||
ffStrbufSetNWS(&platform->userName, userName->Length / sizeof(wchar_t), userName->Buffer);
|
||||
RtlFreeUnicodeString(userName); // Required. userName.Buffer is allocated separately
|
||||
LsaFreeMemory(userName);
|
||||
} else {
|
||||
ffStrbufSetS(&platform->userName, getenv("USERNAME"));
|
||||
}
|
||||
|
||||
alignas(TOKEN_USER) char buf[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_USER)];
|
||||
if (NT_SUCCESS(NtQueryInformationToken(NtCurrentProcessToken(), TokenUser, buf, sizeof(buf), &size)))
|
||||
{
|
||||
if (NT_SUCCESS(NtQueryInformationToken(NtCurrentProcessToken(), TokenUser, buf, sizeof(buf), &size))) {
|
||||
TOKEN_USER* tokenUser = (TOKEN_USER*) buf;
|
||||
UNICODE_STRING sidString = { .Buffer = buffer, .Length = 0, .MaximumLength = sizeof(buffer) };
|
||||
if (NT_SUCCESS(RtlConvertSidToUnicodeString(&sidString, tokenUser->User.Sid, FALSE)))
|
||||
if (NT_SUCCESS(RtlConvertSidToUnicodeString(&sidString, tokenUser->User.Sid, FALSE))) {
|
||||
ffStrbufSetNWS(&platform->sid, sidString.Length / sizeof(wchar_t), sidString.Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getHostName(FFPlatform* platform)
|
||||
{
|
||||
static void getHostName(FFPlatform* platform) {
|
||||
wchar_t buffer[256];
|
||||
DWORD len = ARRAY_SIZE(buffer);
|
||||
if (GetComputerNameExW(ComputerNameDnsHostname, buffer, &len) && len > 0)
|
||||
if (GetComputerNameExW(ComputerNameDnsHostname, buffer, &len) && len > 0) {
|
||||
ffStrbufSetNWS(&platform->hostName, len, buffer);
|
||||
else
|
||||
{
|
||||
} else {
|
||||
len = ARRAY_SIZE(buffer);
|
||||
if (GetComputerNameExW(ComputerNameNetBIOS, buffer, &len) && len > 0)
|
||||
if (GetComputerNameExW(ComputerNameNetBIOS, buffer, &len) && len > 0) {
|
||||
ffStrbufSetNWS(&platform->hostName, len, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getUserShell(FFPlatform* platform)
|
||||
{
|
||||
static void getUserShell(FFPlatform* platform) {
|
||||
// Works in MSYS2
|
||||
const char* userShell = getenv("SHELL");
|
||||
if (userShell)
|
||||
{
|
||||
if (userShell) {
|
||||
ffStrbufAppendS(&platform->userShell, userShell);
|
||||
ffStrbufReplaceAllC(&platform->userShell, '\\', '/');
|
||||
}
|
||||
}
|
||||
|
||||
static const char* detectWine(void)
|
||||
{
|
||||
const char * __cdecl wine_get_version(void);
|
||||
static const char* detectWine(void) {
|
||||
const char* __cdecl wine_get_version(void);
|
||||
void* hntdll = ffLibraryGetModule(L"ntdll.dll");
|
||||
if (!hntdll) return NULL;
|
||||
if (!hntdll) {
|
||||
return NULL;
|
||||
}
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(hntdll, wine_get_version);
|
||||
if (!ffwine_get_version) return NULL;
|
||||
if (!ffwine_get_version) {
|
||||
return NULL;
|
||||
}
|
||||
return ffwine_get_version();
|
||||
}
|
||||
|
||||
static void getSystemReleaseAndVersion(FFPlatformSysinfo* info)
|
||||
{
|
||||
static void getSystemReleaseAndVersion(FFPlatformSysinfo* info) {
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
|
||||
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t ubr = 0;
|
||||
ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
|
||||
FF_ARG(ubr, L"UBR"),
|
||||
FF_ARG(info->version, L"BuildLabEx"),
|
||||
}, NULL);
|
||||
FF_ARG(ubr, L"UBR"),
|
||||
FF_ARG(info->version, L"BuildLabEx"),
|
||||
},
|
||||
NULL);
|
||||
|
||||
PPEB_FULL peb = ffGetPeb();
|
||||
|
||||
@@ -228,87 +219,82 @@ static void getSystemReleaseAndVersion(FFPlatformSysinfo* info)
|
||||
(unsigned) ubr);
|
||||
|
||||
const char* wineVersion = detectWine();
|
||||
if (wineVersion)
|
||||
if (wineVersion) {
|
||||
ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
|
||||
else
|
||||
} else {
|
||||
ffStrbufSetStatic(&info->name, "WIN32_NT");
|
||||
}
|
||||
}
|
||||
|
||||
static void getSystemPageSize(FFPlatformSysinfo* info)
|
||||
{
|
||||
static void getSystemPageSize(FFPlatformSysinfo* info) {
|
||||
SYSTEM_BASIC_INFORMATION sbi;
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL)))
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL))) {
|
||||
info->pageSize = sbi.PhysicalPageSize;
|
||||
else
|
||||
} else {
|
||||
info->pageSize = 4096;
|
||||
}
|
||||
}
|
||||
|
||||
static void getSystemArchitecture(FFPlatformSysinfo* info)
|
||||
{
|
||||
static void getSystemArchitecture(FFPlatformSysinfo* info) {
|
||||
SYSTEM_PROCESSOR_INFORMATION spi;
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorInformation, &spi, sizeof(spi), NULL)))
|
||||
{
|
||||
switch (spi.ProcessorArchitecture)
|
||||
{
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
ffStrbufSetStatic(&info->architecture, "x86_64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_IA64:
|
||||
ffStrbufSetStatic(&info->architecture, "ia64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
switch (spi.ProcessorLevel)
|
||||
{
|
||||
case 4:
|
||||
ffStrbufSetStatic(&info->architecture, "i486");
|
||||
break;
|
||||
case 5:
|
||||
ffStrbufSetStatic(&info->architecture, "i586");
|
||||
break;
|
||||
case 6:
|
||||
ffStrbufSetStatic(&info->architecture, "i686");
|
||||
break;
|
||||
default:
|
||||
ffStrbufSetStatic(&info->architecture, "i386");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM64:
|
||||
ffStrbufSetStatic(&info->architecture, "aarch64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM:
|
||||
ffStrbufSetStatic(&info->architecture, "arm");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_PPC:
|
||||
ffStrbufSetStatic(&info->architecture, "ppc");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_MIPS:
|
||||
ffStrbufSetStatic(&info->architecture, "mips");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ALPHA:
|
||||
ffStrbufSetStatic(&info->architecture, "alpha");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ALPHA64:
|
||||
ffStrbufSetStatic(&info->architecture, "alpha64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_UNKNOWN:
|
||||
default:
|
||||
ffStrbufSetStatic(&info->architecture, "unknown");
|
||||
break;
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorInformation, &spi, sizeof(spi), NULL))) {
|
||||
switch (spi.ProcessorArchitecture) {
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
ffStrbufSetStatic(&info->architecture, "x86_64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_IA64:
|
||||
ffStrbufSetStatic(&info->architecture, "ia64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
switch (spi.ProcessorLevel) {
|
||||
case 4:
|
||||
ffStrbufSetStatic(&info->architecture, "i486");
|
||||
break;
|
||||
case 5:
|
||||
ffStrbufSetStatic(&info->architecture, "i586");
|
||||
break;
|
||||
case 6:
|
||||
ffStrbufSetStatic(&info->architecture, "i686");
|
||||
break;
|
||||
default:
|
||||
ffStrbufSetStatic(&info->architecture, "i386");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM64:
|
||||
ffStrbufSetStatic(&info->architecture, "aarch64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM:
|
||||
ffStrbufSetStatic(&info->architecture, "arm");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_PPC:
|
||||
ffStrbufSetStatic(&info->architecture, "ppc");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_MIPS:
|
||||
ffStrbufSetStatic(&info->architecture, "mips");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ALPHA:
|
||||
ffStrbufSetStatic(&info->architecture, "alpha");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ALPHA64:
|
||||
ffStrbufSetStatic(&info->architecture, "alpha64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_UNKNOWN:
|
||||
default:
|
||||
ffStrbufSetStatic(&info->architecture, "unknown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getCwd(FFPlatform* platform)
|
||||
{
|
||||
static void getCwd(FFPlatform* platform) {
|
||||
PCURDIR cwd = &ffGetPeb()->ProcessParameters->CurrentDirectory;
|
||||
ffStrbufSetNWS(&platform->cwd, cwd->DosPath.Length / sizeof(WCHAR), cwd->DosPath.Buffer);
|
||||
ffStrbufReplaceAllC(&platform->cwd, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->cwd, '/');
|
||||
}
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform)
|
||||
{
|
||||
void ffPlatformInitImpl(FFPlatform* platform) {
|
||||
platform->pid = (uint32_t) (uintptr_t) ffGetTeb()->ClientId.UniqueProcess;
|
||||
getExePath(platform);
|
||||
getCwd(platform);
|
||||
|
||||
+14
-14
@@ -3,32 +3,32 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void* ffListAdd(FFlist* list)
|
||||
{
|
||||
if(list->length == list->capacity)
|
||||
ffListReserve(list, list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2);
|
||||
void* ffListAdd(FFlist* list, uint32_t elementSize) {
|
||||
if (list->length == list->capacity) {
|
||||
ffListReserve(list, elementSize, list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2);
|
||||
}
|
||||
|
||||
++list->length;
|
||||
return ffListGet(list, list->length - 1);
|
||||
return ffListGet(list, elementSize, list->length - 1);
|
||||
}
|
||||
|
||||
bool ffListShift(FFlist* list, void* result)
|
||||
{
|
||||
if(list->length == 0)
|
||||
bool ffListShift(FFlist* list, uint32_t elementSize, void* __restrict result) {
|
||||
if (list->length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(result, list->data, list->elementSize);
|
||||
memmove(list->data, list->data + list->elementSize, (size_t) list->elementSize * (list->length - 1));
|
||||
memcpy(result, list->data, elementSize);
|
||||
memmove(list->data, list->data + elementSize, (size_t) elementSize * (list->length - 1));
|
||||
--list->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffListPop(FFlist* list, void* result)
|
||||
{
|
||||
if(list->length == 0)
|
||||
bool ffListPop(FFlist* list, uint32_t elementSize, void* __restrict result) {
|
||||
if (list->length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(result, ffListGet(list, list->length - 1), list->elementSize);
|
||||
memcpy(result, ffListGet(list, elementSize, list->length - 1), elementSize);
|
||||
--list->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
+298
-328
File diff suppressed because it is too large
Load Diff
+45
-47
@@ -1,14 +1,12 @@
|
||||
#include "common/base64.h"
|
||||
|
||||
// https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145
|
||||
void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output)
|
||||
{
|
||||
void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
|
||||
static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
char *out = output;
|
||||
const char *ends = str + (size - size % 3);
|
||||
while (str != ends)
|
||||
{
|
||||
uint32_t n = __builtin_bswap32(*(uint32_t *)str);
|
||||
char* out = output;
|
||||
const char* ends = str + (size - size % 3);
|
||||
while (str != ends) {
|
||||
uint32_t n = __builtin_bswap32(*(uint32_t*) str);
|
||||
*out++ = chars[(n >> 26) & 63];
|
||||
*out++ = chars[(n >> 20) & 63];
|
||||
*out++ = chars[(n >> 14) & 63];
|
||||
@@ -16,94 +14,94 @@ void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char
|
||||
str += 3;
|
||||
}
|
||||
|
||||
if (size % 3 == 1)
|
||||
{
|
||||
uint64_t n = (uint64_t)*str << 16;
|
||||
if (size % 3 == 1) {
|
||||
uint64_t n = (uint64_t) *str << 16;
|
||||
*out++ = chars[(n >> 18) & 63];
|
||||
*out++ = chars[(n >> 12) & 63];
|
||||
*out++ = '=';
|
||||
*out++ = '=';
|
||||
}
|
||||
else if (size % 3 == 2)
|
||||
{
|
||||
uint64_t n = (uint64_t)*str++ << 16;
|
||||
n |= (uint64_t)*str << 8;
|
||||
} else if (size % 3 == 2) {
|
||||
uint64_t n = (uint64_t) *str++ << 16;
|
||||
n |= (uint64_t) *str << 8;
|
||||
*out++ = chars[(n >> 18) & 63];
|
||||
*out++ = chars[(n >> 12) & 63];
|
||||
*out++ = chars[(n >> 6) & 63];
|
||||
*out++ = '=';
|
||||
}
|
||||
*out = '\0';
|
||||
*out_size = (uint32_t)(out - output);
|
||||
*out_size = (uint32_t) (out - output);
|
||||
}
|
||||
|
||||
static uint8_t decode_table[256];
|
||||
|
||||
void init_decode_table()
|
||||
{
|
||||
void init_decode_table() {
|
||||
uint8_t ch = 0;
|
||||
do
|
||||
{
|
||||
do {
|
||||
int32_t code = -1;
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
code = ch - 0x41;
|
||||
if (ch >= 'a' && ch <= 'z')
|
||||
}
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
code = ch - 0x47;
|
||||
if (ch >= '0' && ch <= '9')
|
||||
}
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
code = ch + 0x04;
|
||||
if (ch == '+' || ch == '-')
|
||||
}
|
||||
if (ch == '+' || ch == '-') {
|
||||
code = 0x3E;
|
||||
if (ch == '/' || ch == '_')
|
||||
}
|
||||
if (ch == '/' || ch == '_') {
|
||||
code = 0x3F;
|
||||
}
|
||||
decode_table[ch] = (uint8_t) code;
|
||||
} while (ch++ < 0xFF);
|
||||
}
|
||||
|
||||
#define next_char(x) uint8_t x = decode_table[(uint8_t) *str++];
|
||||
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output)
|
||||
{
|
||||
if (*(uint64_t*) decode_table == 0)
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
|
||||
if (*(uint64_t*) decode_table == 0) {
|
||||
init_decode_table();
|
||||
}
|
||||
|
||||
char *out = output;
|
||||
while (size > 0 && (str[size - 1] == '\n' || str[size - 1] == '\r' || str[size - 1] == '='))
|
||||
char* out = output;
|
||||
while (size > 0 && (str[size - 1] == '\n' || str[size - 1] == '\r' || str[size - 1] == '=')) {
|
||||
size--;
|
||||
}
|
||||
|
||||
const char *ends = str + size - 4;
|
||||
while (true)
|
||||
{
|
||||
if (str > ends)
|
||||
const char* ends = str + size - 4;
|
||||
while (true) {
|
||||
if (str > ends) {
|
||||
break;
|
||||
while (*str == '\n' || *str == '\r')
|
||||
}
|
||||
while (*str == '\n' || *str == '\r') {
|
||||
str++;
|
||||
}
|
||||
|
||||
if (str > ends)
|
||||
if (str > ends) {
|
||||
break;
|
||||
}
|
||||
next_char(a);
|
||||
next_char(b);
|
||||
next_char(c);
|
||||
next_char(d);
|
||||
|
||||
*out++ = (char)(a << 2 | b >> 4);
|
||||
*out++ = (char)(b << 4 | c >> 2);
|
||||
*out++ = (char)(c << 6 | d >> 0);
|
||||
*out++ = (char) (a << 2 | b >> 4);
|
||||
*out++ = (char) (b << 4 | c >> 2);
|
||||
*out++ = (char) (c << 6 | d >> 0);
|
||||
}
|
||||
|
||||
uint8_t mod = (uint8_t) (ends - str + 4) % 4;
|
||||
if (mod == 2)
|
||||
{
|
||||
if (mod == 2) {
|
||||
next_char(a);
|
||||
next_char(b);
|
||||
*out++ = (char)(a << 2 | b >> 4);
|
||||
}
|
||||
else if (mod == 3)
|
||||
{
|
||||
*out++ = (char) (a << 2 | b >> 4);
|
||||
} else if (mod == 3) {
|
||||
next_char(a);
|
||||
next_char(b);
|
||||
next_char(c);
|
||||
*out++ = (char)(a << 2 | b >> 4);
|
||||
*out++ = (char)(b << 4 | c >> 2);
|
||||
*out++ = (char) (a << 2 | b >> 4);
|
||||
*out++ = (char) (b << 4 | c >> 2);
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
|
||||
+178
-119
@@ -1,11 +1,10 @@
|
||||
#include "common/binary.h"
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/mallocHelper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/swap.h>
|
||||
#include <mach-o/fat.h>
|
||||
@@ -14,19 +13,39 @@
|
||||
|
||||
// Ref: https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
|
||||
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
size_t length;
|
||||
} FFMemoryMapping;
|
||||
|
||||
static inline void wrapMunmap(FFMemoryMapping* mapping) {
|
||||
assert(mapping);
|
||||
if (mapping->data == NULL || mapping->data == MAP_FAILED) {
|
||||
return;
|
||||
}
|
||||
munmap((void*) mapping->data, mapping->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to read data from a file at a specific offset
|
||||
* Helper function to access data from a memory-mapped file at a specific offset
|
||||
*/
|
||||
static inline bool readData(FILE *objFile, void *buf, size_t size, off_t offset)
|
||||
{
|
||||
fseek(objFile, offset, SEEK_SET);
|
||||
return fread(buf, 1, size, objFile) == size;
|
||||
static inline const void* readData(const FFMemoryMapping* mapping, size_t size, off_t offset) {
|
||||
if (offset < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t start = (size_t) offset;
|
||||
if (start > mapping->length || size > mapping->length - start) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mapping->data + start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a Mach-O section by extracting strings from the __cstring section
|
||||
*
|
||||
* @param objFile File handle to the Mach-O object file
|
||||
* @param mapping Memory mapping of the Mach-O object file
|
||||
* @param name Section name to check
|
||||
* @param offset Offset of the section in the file
|
||||
* @param size Size of the section
|
||||
@@ -36,23 +55,30 @@ static inline bool readData(FILE *objFile, void *buf, size_t size, off_t offset)
|
||||
*
|
||||
* @return true to continue processing, false to stop
|
||||
*/
|
||||
static bool handleMachSection(FILE *objFile, const char *name, off_t offset, size_t size, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength)
|
||||
{
|
||||
if (!ffStrEquals(name, "__cstring")) return true;
|
||||
|
||||
FF_AUTO_FREE char* data = (char*) malloc(size);
|
||||
if (!readData(objFile, data, size, offset))
|
||||
static bool handleMachSection(const FFMemoryMapping* mapping, const char* name, off_t offset, size_t size, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
if (!ffStrEquals(name, "__cstring")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t off = 0; off < size; ++off)
|
||||
{
|
||||
const char* p = (const char*) data + off;
|
||||
if (*p == '\0') continue;
|
||||
uint32_t len = (uint32_t) strlen(p);
|
||||
if (len < minLength) continue;
|
||||
if (*p >= ' ' && *p <= '~') // Ignore control characters
|
||||
{
|
||||
if (!cb(p, len, userdata)) return false;
|
||||
const char* data = readData(mapping, size, offset);
|
||||
if (!data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t off = 0; off < size; ++off) {
|
||||
const char* p = data + off;
|
||||
if (*p == '\0') {
|
||||
continue;
|
||||
}
|
||||
uint32_t len = (uint32_t) strnlen(p, size - off);
|
||||
if (len < minLength) {
|
||||
off += len; // Skip short strings
|
||||
continue;
|
||||
}
|
||||
if (*p >= ' ' && *p <= '~') { // Ignore control characters
|
||||
if (!cb(p, len, userdata)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
off += len;
|
||||
}
|
||||
@@ -66,7 +92,7 @@ static bool handleMachSection(FILE *objFile, const char *name, off_t offset, siz
|
||||
* LC_SEGMENT or LC_SEGMENT_64 commands that contain the __TEXT segment.
|
||||
* It then processes the sections within that segment to extract strings.
|
||||
*
|
||||
* @param objFile File handle to the Mach-O object file
|
||||
* @param mapping Memory mapping of the Mach-O object file
|
||||
* @param offset Offset of the Mach header in the file
|
||||
* @param is_64 Whether this is a 64-bit Mach-O header
|
||||
* @param cb Callback function to process strings
|
||||
@@ -75,75 +101,83 @@ static bool handleMachSection(FILE *objFile, const char *name, off_t offset, siz
|
||||
*
|
||||
* @return NULL on success, error message on failure
|
||||
*/
|
||||
static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength)
|
||||
{
|
||||
static const char* dumpMachHeader(const FFMemoryMapping* mapping, off_t offset, bool is_64, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
uint32_t ncmds;
|
||||
off_t loadCommandsOffset = offset;
|
||||
|
||||
if (is_64)
|
||||
{
|
||||
struct mach_header_64 header;
|
||||
if (!readData(objFile, &header, sizeof(header), offset))
|
||||
if (is_64) {
|
||||
const struct mach_header_64* header = readData(mapping, sizeof(struct mach_header_64), offset);
|
||||
if (!header) {
|
||||
return "read mach header failed";
|
||||
}
|
||||
|
||||
ncmds = header.ncmds;
|
||||
loadCommandsOffset += sizeof(header);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct mach_header header;
|
||||
if (!readData(objFile, &header, sizeof(header), offset))
|
||||
ncmds = header->ncmds;
|
||||
loadCommandsOffset += sizeof(*header);
|
||||
} else {
|
||||
const struct mach_header* header = readData(mapping, sizeof(struct mach_header), offset);
|
||||
if (!header) {
|
||||
return "read mach header failed";
|
||||
}
|
||||
|
||||
ncmds = header.ncmds;
|
||||
loadCommandsOffset += sizeof(header);
|
||||
ncmds = header->ncmds;
|
||||
loadCommandsOffset += sizeof(*header);
|
||||
}
|
||||
|
||||
off_t commandOffset = loadCommandsOffset;
|
||||
struct load_command cmd = {};
|
||||
for (uint32_t i = 0U; i < ncmds; i++, commandOffset += cmd.cmdsize)
|
||||
{
|
||||
if (!readData(objFile, &cmd, sizeof(cmd), commandOffset))
|
||||
continue;
|
||||
|
||||
if (cmd.cmd == LC_SEGMENT_64)
|
||||
{
|
||||
struct segment_command_64 segment;
|
||||
if (!readData(objFile, &segment, sizeof(segment), commandOffset))
|
||||
continue;
|
||||
|
||||
if (!ffStrEquals(segment.segname, "__TEXT")) continue;
|
||||
|
||||
for (uint32_t j = 0U; j < segment.nsects; j++)
|
||||
{
|
||||
struct section_64 section;
|
||||
if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section))))
|
||||
continue;
|
||||
|
||||
if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata, minLength))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (cmd.cmd == LC_SEGMENT)
|
||||
{
|
||||
struct segment_command segment;
|
||||
if (!readData(objFile, &segment, sizeof(segment), commandOffset))
|
||||
continue;
|
||||
|
||||
if (!ffStrEquals(segment.segname, "__TEXT")) continue;
|
||||
|
||||
for (uint32_t j = 0; j < segment.nsects; j++)
|
||||
{
|
||||
struct section section;
|
||||
if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section))))
|
||||
continue;
|
||||
|
||||
if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata, minLength))
|
||||
return NULL;
|
||||
}
|
||||
const struct load_command* cmd = NULL;
|
||||
for (uint32_t i = 0U; i < ncmds; i++, commandOffset += cmd->cmdsize) {
|
||||
cmd = readData(mapping, sizeof(*cmd), commandOffset);
|
||||
if (!cmd) {
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
if (cmd->cmdsize < sizeof(*cmd)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64* segment = readData(mapping, sizeof(struct segment_command_64), commandOffset);
|
||||
if (!segment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ffStrEquals(segment->segname, "__TEXT")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0U; j < segment->nsects; j++) {
|
||||
off_t sectionOffset = commandOffset + (off_t) sizeof(*segment) + (off_t) (j * sizeof(struct section_64));
|
||||
const struct section_64* section = readData(mapping, sizeof(struct section_64), sectionOffset);
|
||||
if (!section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!handleMachSection(mapping, section->sectname, (off_t) section->offset, (size_t) section->size, cb, userdata, minLength)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else if (cmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command* segment = readData(mapping, sizeof(struct segment_command), commandOffset);
|
||||
if (!segment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ffStrEquals(segment->segname, "__TEXT")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < segment->nsects; j++) {
|
||||
off_t sectionOffset = commandOffset + (off_t) sizeof(*segment) + (off_t) (j * sizeof(struct section));
|
||||
const struct section* section = readData(mapping, sizeof(struct section), sectionOffset);
|
||||
if (!section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!handleMachSection(mapping, section->sectname, (off_t) section->offset, (size_t) section->size, cb, userdata, minLength)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -156,56 +190,65 @@ static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool
|
||||
* multiple Mach-O binaries for different architectures. It extracts and processes
|
||||
* each embedded Mach-O file.
|
||||
*
|
||||
* @param objFile File handle to the universal binary
|
||||
* @param mapping Memory mapping of the universal binary
|
||||
* @param cb Callback function to process strings
|
||||
* @param userdata User data for the callback
|
||||
* @param minLength Minimum string length to extract
|
||||
*
|
||||
* @return NULL on success, error message on failure
|
||||
*/
|
||||
static const char* dumpFatHeader(FILE *objFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength)
|
||||
{
|
||||
struct fat_header header;
|
||||
if (!readData(objFile, &header, sizeof(header), 0))
|
||||
static const char* dumpFatHeader(const FFMemoryMapping* mapping, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
const struct fat_header* headerRaw = readData(mapping, sizeof(struct fat_header), 0);
|
||||
if (!headerRaw) {
|
||||
return "read fat header failed";
|
||||
}
|
||||
|
||||
struct fat_header header = *headerRaw;
|
||||
|
||||
bool needSwap = header.magic == FAT_CIGAM || header.magic == FAT_CIGAM_64;
|
||||
|
||||
if (needSwap) swap_fat_header(&header, NX_UnknownByteOrder);
|
||||
if (needSwap) {
|
||||
swap_fat_header(&header, NX_UnknownByteOrder);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0U; i < header.nfat_arch; i++)
|
||||
{
|
||||
for (uint32_t i = 0U; i < header.nfat_arch; i++) {
|
||||
off_t machHeaderOffset = 0;
|
||||
if (header.magic == FAT_MAGIC)
|
||||
{
|
||||
struct fat_arch arch;
|
||||
if (!readData(objFile, &arch, sizeof(arch), (off_t) (sizeof(header) + i * sizeof(arch))))
|
||||
if (header.magic == FAT_MAGIC) {
|
||||
off_t archOffset = (off_t) sizeof(struct fat_header) + (off_t) (i * sizeof(struct fat_arch));
|
||||
const struct fat_arch* archRaw = readData(mapping, sizeof(struct fat_arch), archOffset);
|
||||
if (!archRaw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (needSwap)
|
||||
struct fat_arch arch = *archRaw;
|
||||
|
||||
if (needSwap) {
|
||||
swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
|
||||
machHeaderOffset = (off_t)arch.offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct fat_arch_64 arch;
|
||||
if (!readData(objFile, &arch, sizeof(arch), (off_t) (sizeof(header) + i * sizeof(arch))))
|
||||
}
|
||||
machHeaderOffset = (off_t) arch.offset;
|
||||
} else {
|
||||
off_t archOffset = (off_t) sizeof(struct fat_header) + (off_t) (i * sizeof(struct fat_arch_64));
|
||||
const struct fat_arch_64* archRaw = readData(mapping, sizeof(struct fat_arch_64), archOffset);
|
||||
if (!archRaw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (needSwap)
|
||||
struct fat_arch_64 arch = *archRaw;
|
||||
|
||||
if (needSwap) {
|
||||
swap_fat_arch_64(&arch, 1, NX_UnknownByteOrder);
|
||||
}
|
||||
|
||||
machHeaderOffset = (off_t)arch.offset;
|
||||
machHeaderOffset = (off_t) arch.offset;
|
||||
}
|
||||
|
||||
uint32_t magic;
|
||||
if (!readData(objFile, &magic, sizeof(magic), machHeaderOffset))
|
||||
const uint32_t* magic = readData(mapping, sizeof(uint32_t), machHeaderOffset);
|
||||
if (!magic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (magic == MH_MAGIC_64 || magic == MH_MAGIC)
|
||||
{
|
||||
dumpMachHeader(objFile, machHeaderOffset, magic == MH_MAGIC_64, cb, userdata, minLength);
|
||||
return NULL;
|
||||
if (*magic == MH_MAGIC_64 || *magic == MH_MAGIC) {
|
||||
return dumpMachHeader(mapping, machHeaderOffset, *magic == MH_MAGIC_64, cb, userdata, minLength);
|
||||
}
|
||||
}
|
||||
return "Unsupported fat header";
|
||||
@@ -219,26 +262,42 @@ static const char* dumpFatHeader(FILE *objFile, bool (*cb)(const char *str, uint
|
||||
* It locates the __cstring section in the __TEXT segment which contains
|
||||
* the string literals used in the program.
|
||||
*/
|
||||
const char *ffBinaryExtractStrings(const char *machoFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength)
|
||||
{
|
||||
FF_AUTO_CLOSE_FILE FILE *objFile = fopen(machoFile, "rb");
|
||||
if (objFile == NULL)
|
||||
const char* ffBinaryExtractStrings(const char* machoFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
FF_AUTO_CLOSE_FD int fd = open(machoFile, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return "File could not be opened";
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0 || st.st_size <= 0) {
|
||||
return "Failed to stat file";
|
||||
}
|
||||
|
||||
FF_A_CLEANUP(wrapMunmap) FFMemoryMapping mapping = {
|
||||
.data = mmap(NULL, (size_t) st.st_size, PROT_READ, MAP_PRIVATE, fd, 0),
|
||||
.length = (size_t) st.st_size,
|
||||
};
|
||||
if (mapping.data == MAP_FAILED) {
|
||||
return "mmap failed";
|
||||
}
|
||||
|
||||
// Read the magic number to determine the type of binary
|
||||
uint32_t magic;
|
||||
if (!readData(objFile, &magic, sizeof(magic), 0))
|
||||
const uint32_t* magic = readData(&mapping, sizeof(uint32_t), 0);
|
||||
if (!magic) {
|
||||
return "read magic number failed";
|
||||
}
|
||||
|
||||
// Check for supported formats
|
||||
// MH_CIGAM and MH_CIGAM_64 seem to be no longer used, as `swap_mach_header` is marked as deprecated.
|
||||
// However FAT_CIGAM and FAT_CIGAM_64 are still used (/usr/bin/vim).
|
||||
if (magic != MH_MAGIC && magic != MH_MAGIC_64 && magic != FAT_CIGAM && magic != FAT_CIGAM_64 && magic != FAT_MAGIC && magic != FAT_MAGIC_64)
|
||||
if (*magic != MH_MAGIC && *magic != MH_MAGIC_64 && *magic != FAT_CIGAM && *magic != FAT_CIGAM_64 && *magic != FAT_MAGIC && *magic != FAT_MAGIC_64) {
|
||||
return "Unsupported format or big endian mach-o file";
|
||||
}
|
||||
|
||||
// Process either a fat binary or a regular Mach-O binary
|
||||
if (magic == FAT_MAGIC || magic == FAT_MAGIC_64 || magic == FAT_CIGAM || magic == FAT_CIGAM_64)
|
||||
return dumpFatHeader(objFile, cb, userdata, minLength);
|
||||
else
|
||||
return dumpMachHeader(objFile, 0, magic == MH_MAGIC_64, cb, userdata, minLength);
|
||||
if (*magic == FAT_MAGIC || *magic == FAT_MAGIC_64 || *magic == FAT_CIGAM || *magic == FAT_CIGAM_64) {
|
||||
return dumpFatHeader(&mapping, cb, userdata, minLength);
|
||||
} else {
|
||||
return dumpMachHeader(&mapping, 0, *magic == MH_MAGIC_64, cb, userdata, minLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
#if defined(FF_HAVE_ELF) || defined(__sun) || (defined(__FreeBSD__) && !defined(__DragonFly__)) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
|
||||
#include "common/io.h"
|
||||
#include "common/library.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/io.h"
|
||||
#include "common/library.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <libelf.h> // #1254
|
||||
#include <fcntl.h>
|
||||
#include <libelf.h> // #1254
|
||||
#include <fcntl.h>
|
||||
|
||||
/**
|
||||
* Structure to hold dynamically loaded libelf function pointers
|
||||
@@ -36,15 +36,15 @@ struct FFElfData {
|
||||
*
|
||||
* The function supports both 32-bit and 64-bit ELF formats.
|
||||
*/
|
||||
const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength)
|
||||
{
|
||||
const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
// Initialize libelf if not already done
|
||||
if (!elfData.inited)
|
||||
{
|
||||
if (!elfData.inited) {
|
||||
elfData.inited = true;
|
||||
FF_LIBRARY_LOAD_MESSAGE(libelf, "libelf" FF_LIBRARY_EXTENSION, 1);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_version)
|
||||
if (elfData.ffelf_version(EV_CURRENT) == EV_NONE) return "elf_version() failed";
|
||||
if (elfData.ffelf_version(EV_CURRENT) == EV_NONE) {
|
||||
return "elf_version() failed";
|
||||
}
|
||||
|
||||
// Load all required libelf functions
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_begin)
|
||||
@@ -59,56 +59,70 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
|
||||
libelf = NULL;
|
||||
}
|
||||
|
||||
if (elfData.ffelf_end == NULL)
|
||||
if (elfData.ffelf_end == NULL) {
|
||||
return "load libelf failed";
|
||||
}
|
||||
|
||||
// Open the ELF file
|
||||
FF_AUTO_CLOSE_FD int fd = open(elfFile, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) return "open() failed";
|
||||
if (fd < 0) {
|
||||
return "open() failed";
|
||||
}
|
||||
|
||||
Elf* elf = elfData.ffelf_begin(fd, ELF_C_READ, NULL);
|
||||
if (elf == NULL) return "elf_begin() failed";
|
||||
if (elf == NULL) {
|
||||
return "elf_begin() failed";
|
||||
}
|
||||
|
||||
// Get the section header string table index
|
||||
size_t shstrndx = 0;
|
||||
if (elfData.ffelf_getshdrstrndx(elf, &shstrndx) < 0)
|
||||
{
|
||||
if (elfData.ffelf_getshdrstrndx(elf, &shstrndx) < 0) {
|
||||
elfData.ffelf_end(elf);
|
||||
return "elf_getshdrstrndx() failed";
|
||||
}
|
||||
|
||||
// Iterate through all sections, looking for .rodata which contains string literals
|
||||
Elf_Scn* scn = NULL;
|
||||
while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL)
|
||||
{
|
||||
while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL) {
|
||||
// Try 64-bit section header first, then 32-bit if that fails
|
||||
Elf64_Shdr* shdr64 = elfData.ffelf64_getshdr(scn);
|
||||
Elf32_Shdr* shdr32 = NULL;
|
||||
if (shdr64 == NULL)
|
||||
{
|
||||
if (shdr64 == NULL) {
|
||||
shdr32 = elfData.ffelf32_getshdr(scn);
|
||||
if (shdr32 == NULL) continue;
|
||||
if (shdr32 == NULL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the section name and check if it's .rodata
|
||||
const char* name = elfData.ffelf_strptr(elf, shstrndx, shdr64 ? shdr64->sh_name : shdr32->sh_name);
|
||||
if (name == NULL || !ffStrEquals(name, ".rodata")) continue;
|
||||
if (name == NULL || !ffStrEquals(name, ".rodata")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the section data
|
||||
Elf_Data* data = elfData.ffelf_getdata(scn, NULL);
|
||||
if (data == NULL) continue;
|
||||
if (data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scan the section for string literals
|
||||
for (size_t off = 0; off < data->d_size; ++off)
|
||||
{
|
||||
for (size_t off = 0; off < data->d_size; ++off) {
|
||||
const char* p = (const char*) data->d_buf + off;
|
||||
if (*p == '\0') continue;
|
||||
uint32_t len = (uint32_t) strlen(p);
|
||||
if (len < minLength) continue;
|
||||
if (*p == '\0') {
|
||||
continue;
|
||||
}
|
||||
uint32_t len = (uint32_t) strnlen(p, data->d_size - off);
|
||||
if (len < minLength) {
|
||||
off += len;
|
||||
continue;
|
||||
}
|
||||
// Only process printable ASCII characters
|
||||
if (*p >= ' ' && *p <= '~') // Ignore control characters
|
||||
{
|
||||
if (!cb(p, len, userdata)) break;
|
||||
if (!cb(p, len, userdata)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
off += len;
|
||||
}
|
||||
@@ -125,8 +139,7 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
|
||||
/**
|
||||
* Fallback implementation when libelf is not available
|
||||
*/
|
||||
const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength)
|
||||
{
|
||||
const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
FF_UNUSED(file, cb, userdata, minLength);
|
||||
return "Fastfetch was built without libelf support";
|
||||
}
|
||||
|
||||
@@ -14,48 +14,52 @@
|
||||
* (which typically contains string literals), and scans it for valid strings.
|
||||
* Each string found is passed to the callback function for processing.
|
||||
*/
|
||||
const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileA(peFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
const char* ffBinaryExtractStrings(const char* peFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileA(peFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return "CreateFileA() failed";
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hSection = NULL;
|
||||
if (!NT_SUCCESS(NtCreateSection(&hSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, SEC_COMMIT, hFile)))
|
||||
if (!NT_SUCCESS(NtCreateSection(&hSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, SEC_COMMIT, hFile))) {
|
||||
return "NtCreateSection() failed";
|
||||
}
|
||||
|
||||
PVOID base = NULL;
|
||||
SIZE_T viewSize = 0;
|
||||
if (!NT_SUCCESS(NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READONLY)))
|
||||
if (!NT_SUCCESS(NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READONLY))) {
|
||||
return "NtMapViewOfSection() failed";
|
||||
}
|
||||
|
||||
PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(base);
|
||||
if (!ntHeaders)
|
||||
{
|
||||
if (!ntHeaders) {
|
||||
NtUnmapViewOfSection(NtCurrentProcess(), base);
|
||||
return "RtlImageNtHeader() failed";
|
||||
}
|
||||
|
||||
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
|
||||
for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i, ++section)
|
||||
{
|
||||
for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i, ++section) {
|
||||
// Look for initialized data sections with the name ".rdata" which typically contains string literals
|
||||
if ((section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) && ffStrEquals((const char*) section->Name, ".rdata"))
|
||||
{
|
||||
uint8_t *data = (uint8_t *) base + section->PointerToRawData;
|
||||
if ((section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) && ffStrEquals((const char*) section->Name, ".rdata")) {
|
||||
uint8_t* data = (uint8_t*) base + section->PointerToRawData;
|
||||
|
||||
// Scan the section for string literals
|
||||
for (size_t off = 0; off < section->SizeOfRawData; ++off)
|
||||
{
|
||||
for (size_t off = 0; off < section->SizeOfRawData; ++off) {
|
||||
const char* p = (const char*) data + off;
|
||||
if (*p == '\0') continue;
|
||||
uint32_t len = (uint32_t) strlen(p);
|
||||
if (len < minLength) continue;
|
||||
if (*p == '\0') {
|
||||
continue;
|
||||
}
|
||||
uint32_t len = (uint32_t) strnlen(p, section->SizeOfRawData - off);
|
||||
if (len < minLength) {
|
||||
off += len;
|
||||
continue;
|
||||
}
|
||||
// Only process printable ASCII characters
|
||||
if (*p >= ' ' && *p <= '~') // Ignore control characters
|
||||
{
|
||||
if (!cb(p, len, userdata)) break;
|
||||
if (!cb(p, len, userdata)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
off += len;
|
||||
}
|
||||
|
||||
+100
-106
@@ -10,16 +10,17 @@
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
bool ffParseModuleOptions(const char* key, const char* value)
|
||||
{
|
||||
if (!ffStrStartsWith(key, "--") || !ffCharIsEnglishAlphabet(key[2])) return false;
|
||||
if (value && !*value) value = NULL;
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(key[2]) - 'A']; *modules; ++modules)
|
||||
{
|
||||
bool ffParseModuleOptions(const char* key, const char* value) {
|
||||
if (!ffStrStartsWith(key, "--") || !ffCharIsEnglishAlphabet(key[2])) {
|
||||
return false;
|
||||
}
|
||||
if (value && !*value) {
|
||||
value = NULL;
|
||||
}
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(key[2]) - 'A']; *modules; ++modules) {
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
const char* subKey = ffOptionTestPrefix(key, baseInfo->name);
|
||||
if (subKey != NULL)
|
||||
{
|
||||
if (subKey != NULL) {
|
||||
if (subKey[0] == '\0' || subKey[0] == '-') // Key is exactly the module name or has a leading '-'
|
||||
{
|
||||
fprintf(stderr, "Error: unknown module key %s\n", key);
|
||||
@@ -31,32 +32,25 @@ bool ffParseModuleOptions(const char* key, const char* value)
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY jsonKey = ffStrbufCreate();
|
||||
bool flag = false;
|
||||
for (const char* p = subKey; *p; ++p)
|
||||
{
|
||||
if (*p == '-')
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
for (const char* p = subKey; *p; ++p) {
|
||||
if (*p == '-') {
|
||||
if (flag) {
|
||||
fprintf(stderr, "Error: invalid double `-` in module key %s\n", key);
|
||||
exit(477);
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isalpha((unsigned char)*p) && !isdigit((unsigned char)*p))
|
||||
{
|
||||
} else {
|
||||
if (!isalpha((unsigned char) *p) && !isdigit((unsigned char) *p)) {
|
||||
fprintf(stderr, "Error: invalid character `%c` in module key %s\n", *p, key);
|
||||
exit(477);
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
if (flag) {
|
||||
flag = false;
|
||||
ffStrbufAppendC(&jsonKey, (char) toupper((unsigned char) *p));
|
||||
}
|
||||
else
|
||||
} else {
|
||||
ffStrbufAppendC(&jsonKey, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: Unsupported module option: %s\n", key);
|
||||
@@ -69,188 +63,188 @@ bool ffParseModuleOptions(const char* key, const char* value)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffPrepareCommandOption(FFdata* data)
|
||||
{
|
||||
void ffPrepareCommandOption(FFdata* data) {
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
|
||||
{
|
||||
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (moduleLen == strlen(moduleNameConstant) \
|
||||
&& ffStrEqualsIgnCase(moduleType, moduleNameConstant) \
|
||||
&& !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
|
||||
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (moduleLen == strlen(moduleNameConstant) && ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
|
||||
|
||||
switch (moduleType[0])
|
||||
{
|
||||
case 'C': case 'c':
|
||||
switch (moduleType[0]) {
|
||||
case 'C':
|
||||
case 'c':
|
||||
FF_IF_MODULE_MATCH(FF_CPUUSAGE_MODULE_NAME)
|
||||
ffPrepareCPUUsage();
|
||||
ffPrepareCPUUsage();
|
||||
break;
|
||||
|
||||
case 'D': case 'd':
|
||||
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
|
||||
case 'D':
|
||||
case 'd':
|
||||
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyDiskIOOptions) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N': case 'n':
|
||||
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
|
||||
case 'N':
|
||||
case 'n':
|
||||
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyNetIOOptions) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'P': case 'p':
|
||||
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
|
||||
case 'P':
|
||||
case 'p':
|
||||
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyPublicIpOptions) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'W': case 'w':
|
||||
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
|
||||
case 'W':
|
||||
case 'w':
|
||||
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyWeatherOptions) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#undef FF_IF_MODULE_MATCH
|
||||
#undef FF_IF_MODULE_MATCH
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonConfig(FFdata* data, FFModuleBaseInfo* baseInfo, void* options)
|
||||
{
|
||||
static void genJsonConfig(FFdata* data, FFModuleBaseInfo* baseInfo, void* options) {
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
|
||||
yyjson_mut_val* modules = yyjson_mut_obj_get(doc->root, "modules");
|
||||
if (!modules)
|
||||
if (!modules) {
|
||||
modules = yyjson_mut_obj_add_arr(doc, doc->root, "modules");
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreateS(baseInfo->name);
|
||||
ffStrbufLowerCase(&type);
|
||||
|
||||
if (data->docType == FF_RESULT_DOC_TYPE_CONFIG_FULL)
|
||||
{
|
||||
if (data->docType == FF_RESULT_DOC_TYPE_CONFIG_FULL) {
|
||||
yyjson_mut_val* module = yyjson_mut_obj(doc);
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
|
||||
|
||||
if (baseInfo->generateJsonConfig)
|
||||
if (baseInfo->generateJsonConfig) {
|
||||
baseInfo->generateJsonConfig(options, doc, module);
|
||||
}
|
||||
|
||||
if (yyjson_mut_obj_size(module) > 1)
|
||||
if (yyjson_mut_obj_size(module) > 1) {
|
||||
yyjson_mut_arr_add_val(modules, module);
|
||||
else
|
||||
} else {
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonResult(FFdata* data, FFModuleBaseInfo* baseInfo, void* options)
|
||||
{
|
||||
static void genJsonResult(FFdata* data, FFModuleBaseInfo* baseInfo, void* options) {
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
|
||||
if (baseInfo->generateJsonResult)
|
||||
if (baseInfo->generateJsonResult) {
|
||||
baseInfo->generateJsonResult(options, doc, module);
|
||||
else
|
||||
} else {
|
||||
yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format");
|
||||
}
|
||||
}
|
||||
|
||||
static void parseStructureCommand(
|
||||
static bool parseStructureCommand(
|
||||
FFdata* data,
|
||||
const char* line,
|
||||
void (*fn)(FFdata*, FFModuleBaseInfo* baseInfo, void* options)
|
||||
)
|
||||
{
|
||||
if(ffCharIsEnglishAlphabet(line[0]))
|
||||
{
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(line[0]) - 'A']; *modules; ++modules)
|
||||
{
|
||||
void (*fn)(FFdata*, FFModuleBaseInfo* baseInfo, void* options)) {
|
||||
if (ffCharIsEnglishAlphabet(line[0])) {
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(line[0]) - 'A']; *modules; ++modules) {
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
if (ffStrEqualsIgnCase(line, baseInfo->name))
|
||||
{
|
||||
if (ffStrEqualsIgnCase(line, baseInfo->name)) {
|
||||
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
|
||||
baseInfo->initOptions(optionBuf);
|
||||
if (__builtin_expect(data->resultDoc != NULL, false))
|
||||
if (data->resultDoc != NULL) {
|
||||
fn(data, baseInfo, optionBuf);
|
||||
else
|
||||
} else {
|
||||
baseInfo->printModule(optionBuf);
|
||||
}
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffPrintError(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
|
||||
if (fn == genJsonResult) {
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", line);
|
||||
yyjson_mut_obj_add_str(doc, module, "error", "Unknown module type");
|
||||
} else {
|
||||
ffPrintError(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffPrintCommandOption(FFdata* data)
|
||||
{
|
||||
//Parse the structure and call the modules
|
||||
void ffPrintCommandOption(FFdata* data) {
|
||||
// Parse the structure and call the modules
|
||||
int32_t thres = instance.config.display.stat;
|
||||
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
|
||||
{
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':'))
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double ms = 0;
|
||||
if(thres >= 0)
|
||||
if (thres >= 0) {
|
||||
ms = ffTimeGetTick();
|
||||
}
|
||||
|
||||
parseStructureCommand(data, moduleType, genJsonResult);
|
||||
|
||||
if(thres >= 0)
|
||||
{
|
||||
if (thres >= 0) {
|
||||
ms = ffTimeGetTick() - ms;
|
||||
|
||||
if (data->resultDoc)
|
||||
{
|
||||
if (data->resultDoc) {
|
||||
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(data->resultDoc->root);
|
||||
yyjson_mut_obj_add_real(data->resultDoc, moduleJson, "stat", ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
char str[64];
|
||||
int len = snprintf(str, sizeof str, "%.3fms", ms);
|
||||
if (thres > 0)
|
||||
snprintf(str, sizeof str, "\e[%sm%.3fms\e[m", (ms <= thres ? FF_COLOR_FG_GREEN : ms <= 2 * thres ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_RED), ms);
|
||||
if (thres > 0) {
|
||||
snprintf(str, sizeof str, "\e[%sm%.3fms\e[m", (ms <= thres ? FF_COLOR_FG_GREEN : ms <= 2 * thres ? FF_COLOR_FG_YELLOW
|
||||
: FF_COLOR_FG_RED),
|
||||
ms);
|
||||
}
|
||||
printf("\e7\e[1A\e[9999999C\e[%dD%s\e8", len, str); // Save; Up 1; Right 9999999; Left <len>; Print <str>; Load
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (!data->resultDoc && !instance.config.display.noBuffer) fflush(stdout);
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
if (!data->resultDoc && !instance.config.display.noBuffer) {
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data)
|
||||
{
|
||||
//If we don't have a custom structure, use the default one
|
||||
if(data->structure.length == 0)
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data) {
|
||||
// If we don't have a custom structure, use the default one
|
||||
if (data->structure.length == 0) {
|
||||
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
|
||||
}
|
||||
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
|
||||
{
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':'))
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseStructureCommand(data, moduleType, genJsonConfig);
|
||||
}
|
||||
|
||||
+118
-86
@@ -2,11 +2,10 @@
|
||||
|
||||
#ifdef FF_HAVE_DBUS
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
static bool loadLibSymbols(FFDBusLibrary* lib)
|
||||
{
|
||||
static bool loadLibSymbols(FFDBusLibrary* lib) {
|
||||
FF_LIBRARY_LOAD(dbus, false, "libdbus-1" FF_LIBRARY_EXTENSION, 4);
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_bus_get, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_new_method_call, false)
|
||||
@@ -24,14 +23,12 @@ static bool loadLibSymbols(FFDBusLibrary* lib)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const FFDBusLibrary* loadLib(void)
|
||||
{
|
||||
static const FFDBusLibrary* loadLib(void) {
|
||||
static FFDBusLibrary lib;
|
||||
static bool loaded = false;
|
||||
static bool loadSuccess = false;
|
||||
|
||||
if(!loaded)
|
||||
{
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
loadSuccess = loadLibSymbols(&lib);
|
||||
}
|
||||
@@ -39,199 +36,234 @@ static const FFDBusLibrary* loadLib(void)
|
||||
return loadSuccess ? &lib : NULL;
|
||||
}
|
||||
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data)
|
||||
{
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data) {
|
||||
data->lib = loadLib();
|
||||
if(data->lib == NULL)
|
||||
if (data->lib == NULL) {
|
||||
return "Failed to load DBus library";
|
||||
}
|
||||
|
||||
data->connection = data->lib->ffdbus_bus_get(busType, NULL);
|
||||
if(data->connection == NULL)
|
||||
if (data->connection == NULL) {
|
||||
return "Failed to connect to DBus";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ffDBusDestroyData(FFDBusData* data)
|
||||
{
|
||||
if (data->connection != NULL)
|
||||
{
|
||||
void ffDBusDestroyData(FFDBusData* data) {
|
||||
if (data->connection != NULL) {
|
||||
data->lib->ffdbus_connection_unref(data->connection);
|
||||
data->connection = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result)
|
||||
{
|
||||
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_STRING || argType == DBUS_TYPE_OBJECT_PATH)
|
||||
{
|
||||
if (argType == DBUS_TYPE_STRING || argType == DBUS_TYPE_OBJECT_PATH) {
|
||||
const char* value = NULL;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
|
||||
if(!ffStrSet(value))
|
||||
if (!ffStrSet(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ffStrbufAppendS(result, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType == DBUS_TYPE_BYTE)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BYTE) {
|
||||
uint8_t value;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
ffStrbufAppendC(result, (char) value);
|
||||
return false; // Don't append a comma
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY)
|
||||
if (argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
|
||||
if(argType == DBUS_TYPE_VARIANT)
|
||||
if (argType == DBUS_TYPE_VARIANT) {
|
||||
return ffDBusGetString(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
//At this point we have an array
|
||||
// At this point we have an array
|
||||
|
||||
int subArgType = dbus->lib->ffdbus_message_iter_get_arg_type(&subIter);
|
||||
if (subArgType == DBUS_TYPE_INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (subArgType == DBUS_TYPE_BYTE) {
|
||||
while (true) {
|
||||
uint8_t value;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(&subIter, &value);
|
||||
ffStrbufAppendC(result, (char) value);
|
||||
|
||||
if (!dbus->lib->ffdbus_message_iter_next(&subIter)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool foundAValue = false;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(ffDBusGetString(dbus, &subIter, result))
|
||||
{
|
||||
while (true) {
|
||||
if (ffDBusGetString(dbus, &subIter, result)) {
|
||||
foundAValue = true;
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
|
||||
if(!dbus->lib->ffdbus_message_iter_next(&subIter))
|
||||
if (!dbus->lib->ffdbus_message_iter_next(&subIter)) {
|
||||
break;
|
||||
else
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(foundAValue)
|
||||
if (foundAValue) {
|
||||
ffStrbufSubstrBefore(result, result->length - 2);
|
||||
}
|
||||
|
||||
return foundAValue;
|
||||
}
|
||||
|
||||
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result)
|
||||
{
|
||||
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_BOOLEAN)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BOOLEAN) {
|
||||
dbus_bool_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT)
|
||||
if (argType != DBUS_TYPE_VARIANT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
return ffDBusGetBool(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint32_t* result)
|
||||
{
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint64_t* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_BYTE)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BYTE) {
|
||||
uint8_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT16)
|
||||
{
|
||||
if (argType == DBUS_TYPE_UINT16) {
|
||||
uint16_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT32)
|
||||
{
|
||||
if (argType == DBUS_TYPE_UINT32) {
|
||||
uint32_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType == DBUS_TYPE_UINT64) {
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT)
|
||||
if (argType != DBUS_TYPE_VARIANT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
return ffDBusGetUint(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int32_t* result)
|
||||
{
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int64_t* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_INT16)
|
||||
{
|
||||
if (argType == DBUS_TYPE_INT16) {
|
||||
int16_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_INT32)
|
||||
{
|
||||
if (argType == DBUS_TYPE_INT32) {
|
||||
int32_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType == DBUS_TYPE_INT64) {
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_BYTE)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BYTE) {
|
||||
uint8_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT16)
|
||||
{
|
||||
if (argType == DBUS_TYPE_UINT16) {
|
||||
uint16_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = (int16_t) value;
|
||||
*result = (int32_t) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT32)
|
||||
{
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, result);
|
||||
if (argType == DBUS_TYPE_UINT32) {
|
||||
uint32_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = (int32_t) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT)
|
||||
if (argType == DBUS_TYPE_UINT64) {
|
||||
uint64_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
if (value > INT64_MAX) {
|
||||
return false;
|
||||
}
|
||||
*result = (int64_t) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType != DBUS_TYPE_VARIANT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
return ffDBusGetInt(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg1, const char* arg2)
|
||||
{
|
||||
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg1, const char* arg2) {
|
||||
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, interface, method);
|
||||
if(message == NULL)
|
||||
if (message == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (arg1)
|
||||
{
|
||||
if (arg2)
|
||||
if (arg1) {
|
||||
if (arg2) {
|
||||
dbus->lib->ffdbus_message_append_args(message, DBUS_TYPE_STRING, &arg1, DBUS_TYPE_STRING, &arg2, DBUS_TYPE_INVALID);
|
||||
else
|
||||
} else {
|
||||
dbus->lib->ffdbus_message_append_args(message, DBUS_TYPE_STRING, &arg1, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL);
|
||||
@@ -241,15 +273,17 @@ DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const c
|
||||
return reply;
|
||||
}
|
||||
|
||||
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property)
|
||||
{
|
||||
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property) {
|
||||
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, "org.freedesktop.DBus.Properties", "Get");
|
||||
if(message == NULL)
|
||||
if (message == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbus->lib->ffdbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_STRING, &property,
|
||||
DBUS_TYPE_STRING,
|
||||
&interface,
|
||||
DBUS_TYPE_STRING,
|
||||
&property,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL);
|
||||
@@ -259,15 +293,14 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result)
|
||||
{
|
||||
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result) {
|
||||
DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property);
|
||||
if(reply == NULL)
|
||||
if (reply == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
if (!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator)) {
|
||||
dbus->lib->ffdbus_message_unref(reply);
|
||||
return false;
|
||||
}
|
||||
@@ -279,15 +312,14 @@ bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char*
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result)
|
||||
{
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint64_t* result) {
|
||||
DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property);
|
||||
if(reply == NULL)
|
||||
if (reply == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
if (!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator)) {
|
||||
dbus->lib->ffdbus_message_unref(reply);
|
||||
return false;
|
||||
}
|
||||
@@ -299,4 +331,4 @@ bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* ob
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif //FF_HAVE_DBUS
|
||||
#endif // FF_HAVE_DBUS
|
||||
|
||||
@@ -1,50 +1,55 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
const char* ffDebugWin32Error(DWORD errorCode)
|
||||
{
|
||||
static char buffer[256];
|
||||
const char* ffDebugWin32Error(DWORD errorCode) {
|
||||
static char buffer[512];
|
||||
|
||||
DWORD len = FormatMessageA(
|
||||
wchar_t bufferW[256];
|
||||
ULONG len = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
(DWORD) errorCode,
|
||||
0,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
bufferW,
|
||||
ARRAY_SIZE(bufferW),
|
||||
NULL);
|
||||
|
||||
if (len == 0) {
|
||||
snprintf(buffer, sizeof(buffer), "Unknown error code (%lu)", errorCode);
|
||||
} else {
|
||||
// Remove trailing newline
|
||||
while (len > 0 && (buffer[len - 1] == '\r' || buffer[len - 1] == '\n')) {
|
||||
buffer[--len] = '\0';
|
||||
while (len > 0 && (bufferW[len - 1] == '\r' || bufferW[len - 1] == '\n')) {
|
||||
--len;
|
||||
}
|
||||
|
||||
if (NT_SUCCESS(RtlUnicodeToUTF8N(buffer, sizeof(buffer), &len, bufferW, len * sizeof(wchar_t)))) {
|
||||
snprintf(buffer + len, sizeof(buffer) - len, " (%lu)", errorCode);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Unknown error (%lu)", errorCode);
|
||||
}
|
||||
snprintf(buffer + len, sizeof(buffer) - len + 2, " (%lu)", errorCode);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char* ffDebugNtStatus(NTSTATUS status)
|
||||
{
|
||||
const char* ffDebugNtStatus(NTSTATUS status) {
|
||||
return ffDebugWin32Error(RtlNtStatusToDosError(status));
|
||||
}
|
||||
|
||||
static inline DWORD HRESULTToWin32Error(HRESULT hr)
|
||||
{
|
||||
if (SUCCEEDED(hr))
|
||||
static inline DWORD HRESULTToWin32Error(HRESULT hr) {
|
||||
if (SUCCEEDED(hr)) {
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
|
||||
if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
|
||||
return HRESULT_CODE(hr);
|
||||
}
|
||||
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const char* ffDebugHResult(HRESULT hr)
|
||||
{
|
||||
const char* ffDebugHResult(HRESULT hr) {
|
||||
return ffDebugWin32Error(HRESULTToWin32Error(hr));
|
||||
}
|
||||
|
||||
+38
-33
@@ -1,25 +1,27 @@
|
||||
#include "common/duration.h"
|
||||
|
||||
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result)
|
||||
{
|
||||
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result) {
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
|
||||
bool spaceBeforeUnit = options->durationSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
|
||||
if (totalSeconds < 60)
|
||||
{
|
||||
if (totalSeconds < 60) {
|
||||
ffStrbufAppendUInt(result, totalSeconds);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppendS(result, options->durationAbbreviation ? "sec" : "second");
|
||||
if (totalSeconds != 1)
|
||||
if (totalSeconds != 1) {
|
||||
ffStrbufAppendC(result, 's');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t seconds = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
if (seconds >= 30)
|
||||
if (seconds >= 30) {
|
||||
totalSeconds++;
|
||||
}
|
||||
|
||||
uint32_t minutes = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
@@ -27,56 +29,59 @@ void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result)
|
||||
totalSeconds /= 24;
|
||||
uint32_t days = (uint32_t) totalSeconds;
|
||||
|
||||
if (days > 0)
|
||||
{
|
||||
if (days > 0) {
|
||||
ffStrbufAppendUInt(result, days);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (options->durationAbbreviation)
|
||||
{
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
if (options->durationAbbreviation) {
|
||||
ffStrbufAppendC(result, 'd');
|
||||
|
||||
if (hours > 0 || minutes > 0)
|
||||
if (hours > 0 || minutes > 0) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
ffStrbufAppendS(result, days == 1 ? "day" : "days");
|
||||
|
||||
if (days >= 100)
|
||||
if (days >= 100) {
|
||||
ffStrbufAppendS(result, "(!)");
|
||||
}
|
||||
|
||||
if (hours > 0 || minutes > 0)
|
||||
if (hours > 0 || minutes > 0) {
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hours > 0)
|
||||
{
|
||||
if (hours > 0) {
|
||||
ffStrbufAppendUInt(result, hours);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (options->durationAbbreviation)
|
||||
{
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
if (options->durationAbbreviation) {
|
||||
ffStrbufAppendC(result, 'h');
|
||||
|
||||
if (minutes > 0)
|
||||
if (minutes > 0) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
ffStrbufAppendS(result, hours == 1 ? "hour" : "hours");
|
||||
|
||||
if (minutes > 0)
|
||||
if (minutes > 0) {
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minutes > 0)
|
||||
{
|
||||
if (minutes > 0) {
|
||||
ffStrbufAppendUInt(result, minutes);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (options->durationAbbreviation)
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
if (options->durationAbbreviation) {
|
||||
ffStrbufAppendC(result, 'm');
|
||||
else
|
||||
} else {
|
||||
ffStrbufAppendS(result, minutes == 1 ? "min" : "mins");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,39 @@
|
||||
#include "common/edidHelper.h"
|
||||
|
||||
void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint32_t* height)
|
||||
{
|
||||
void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint32_t* height) {
|
||||
const int dtd = 54;
|
||||
*width = (((uint32_t) edid[dtd + 4] >> 4) << 8) | edid[dtd + 2];
|
||||
*height = (((uint32_t) edid[dtd + 7] >> 4) << 8) | edid[dtd + 5];
|
||||
}
|
||||
|
||||
void ffEdidGetPreferredResolutionAndRefreshRate(const uint8_t edid[128], uint32_t* width, uint32_t* height, double* refreshRate)
|
||||
{
|
||||
for (uint32_t i = 0x36; i < 0x7E; i += 0x12)
|
||||
{ // read through descriptor blocks...
|
||||
if (edid[i] != 0x00 && edid[i + 1] != 0x00)
|
||||
{ // a dtd
|
||||
uint32_t hactive = edid[i+2] + (uint32_t) ((edid[i + 4] & 0xf0) << 4);
|
||||
uint32_t hblank = edid[i+3] + (uint32_t) ((edid[i + 4] & 0x0f) << 8);
|
||||
uint32_t vactive = edid[i+5] + (uint32_t) ((edid[i + 7] & 0xf0) << 4);
|
||||
uint32_t vblank = edid[i+6] + (uint32_t) ((edid[i + 7] & 0x0f) << 8);
|
||||
uint32_t pixclk = ((uint32_t) edid[i+1] << 8) | (edid[i]);
|
||||
void ffEdidGetPreferredResolutionAndRefreshRate(const uint8_t edid[128], uint32_t* width, uint32_t* height, double* refreshRate) {
|
||||
for (uint32_t i = 0x36; i < 0x7E; i += 0x12) { // read through descriptor blocks...
|
||||
if (edid[i] != 0x00 && edid[i + 1] != 0x00) { // a dtd
|
||||
uint32_t hactive = edid[i + 2] + (uint32_t) ((edid[i + 4] & 0xf0) << 4);
|
||||
uint32_t hblank = edid[i + 3] + (uint32_t) ((edid[i + 4] & 0x0f) << 8);
|
||||
uint32_t vactive = edid[i + 5] + (uint32_t) ((edid[i + 7] & 0xf0) << 4);
|
||||
uint32_t vblank = edid[i + 6] + (uint32_t) ((edid[i + 7] & 0x0f) << 8);
|
||||
uint32_t pixclk = ((uint32_t) edid[i + 1] << 8) | (edid[i]);
|
||||
*width = hactive;
|
||||
*height = vactive;
|
||||
*refreshRate = (double)pixclk * 10000 / (double)(hactive+hblank) / (double)(vactive+vblank);
|
||||
*refreshRate = (double) pixclk * 10000 / (double) (hactive + hblank) / (double) (vactive + vblank);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ffEdidGetVendorAndModel(const uint8_t edid[128], FFstrbuf* result)
|
||||
{
|
||||
void ffEdidGetVendorAndModel(const uint8_t edid[128], FFstrbuf* result) {
|
||||
// https://github.com/jinksong/read_edid/blob/master/parse-edid/parse-edid.c
|
||||
ffStrbufAppendF(result, "%c%c%c%04X",
|
||||
(char) (((uint32_t)edid[8] >> 2 & 0x1f) + 'A' - 1),
|
||||
(char) (((((uint32_t)edid[8] & 0x3) << 3) | (((uint32_t)edid[9] & 0xe0) >> 5)) + 'A' - 1),
|
||||
(char) (((uint32_t)edid[9] & 0x1f) + 'A' - 1),
|
||||
(uint32_t) (edid[10] + (uint32_t) (edid[11] << 8))
|
||||
);
|
||||
ffStrbufAppendF(result, "%c%c%c%04X", (char) (((uint32_t) edid[8] >> 2 & 0x1f) + 'A' - 1), (char) (((((uint32_t) edid[8] & 0x3) << 3) | (((uint32_t) edid[9] & 0xe0) >> 5)) + 'A' - 1), (char) (((uint32_t) edid[9] & 0x1f) + 'A' - 1), (uint32_t) (edid[10] + (uint32_t) (edid[11] << 8)));
|
||||
}
|
||||
|
||||
bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name)
|
||||
{
|
||||
bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name) {
|
||||
// https://github.com/jinksong/read_edid/blob/master/parse-edid/parse-edid.c
|
||||
for (uint32_t i = 0x36; i < 0x7E; i += 0x12)
|
||||
{ // read through descriptor blocks...
|
||||
if (edid[i] == 0x00)
|
||||
{ // not a timing descriptor
|
||||
if (edid[i + 3] == 0xfc)
|
||||
{ // Model Name tag
|
||||
for (uint32_t j = 0; j < 13; j++)
|
||||
{
|
||||
if (edid[i + 5 + j] == 0x0a)
|
||||
{
|
||||
for (uint32_t i = 0x36; i < 0x7E; i += 0x12) { // read through descriptor blocks...
|
||||
if (edid[i] == 0x00) { // not a timing descriptor
|
||||
if (edid[i + 3] == 0xfc) { // Model Name tag
|
||||
for (uint32_t j = 0; j < 13; j++) {
|
||||
if (edid[i + 5 + j] == 0x0a) {
|
||||
ffStrbufAppendNS(name, j, (const char*) &edid[i + 5]);
|
||||
return true;
|
||||
}
|
||||
@@ -63,8 +47,7 @@ bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height)
|
||||
{
|
||||
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height) {
|
||||
// Detailed Timing Descriptors
|
||||
uint32_t dw = (((uint32_t) edid[68] & 0xF0) << 4) + edid[66];
|
||||
uint32_t dh = (((uint32_t) edid[68] & 0x0F) << 8) + edid[67];
|
||||
@@ -74,53 +57,54 @@ void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* h
|
||||
uint32_t bh = edid[22] * 10;
|
||||
|
||||
// Some monitors report invalid data in DTD. See #1406
|
||||
if (abs((int)dw - (int)bw) < 10 && abs((int)dh - (int)bh) < 10)
|
||||
{
|
||||
if (abs((int) dw - (int) bw) < 10 && abs((int) dh - (int) bh) < 10) {
|
||||
*width = dw;
|
||||
*height = dh;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
*width = bw;
|
||||
*height = bh;
|
||||
}
|
||||
}
|
||||
|
||||
void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week)
|
||||
{
|
||||
if (edid[17] > 0 && edid[17] < 0xFF)
|
||||
{
|
||||
void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week) {
|
||||
if (edid[17] > 0 && edid[17] < 0xFF) {
|
||||
*year = (uint16_t) (edid[17] + 1990);
|
||||
*week = (uint16_t) edid[16];
|
||||
if (*week == 0xFF) *week = 0;
|
||||
}
|
||||
else
|
||||
if (*week == 0xFF) {
|
||||
*week = 0;
|
||||
}
|
||||
} else {
|
||||
*year = *week = 0;
|
||||
}
|
||||
|
||||
*serial = *(uint32_t*) &edid[12];
|
||||
}
|
||||
|
||||
bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length)
|
||||
{
|
||||
if (length <= 128) return false;
|
||||
for (const uint8_t* cta = &edid[128]; cta < &edid[length]; cta += 128)
|
||||
{
|
||||
bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length) {
|
||||
if (length <= 128) {
|
||||
return false;
|
||||
}
|
||||
for (const uint8_t* cta = &edid[128]; cta < &edid[length]; cta += 128) {
|
||||
// https://en.wikipedia.org/wiki/Extended_Display_Identification_Data#CTA_EDID_Timing_Extension_Block
|
||||
if (cta[0] != 0x02 /* CTA EDID */) continue;
|
||||
if (cta[1] < 0x03 /* Version 3 */) continue;
|
||||
if (cta[0] != 0x02 /* CTA EDID */) {
|
||||
continue;
|
||||
}
|
||||
if (cta[1] < 0x03 /* Version 3 */) {
|
||||
continue;
|
||||
}
|
||||
const uint8_t offset = cta[2];
|
||||
if (offset <= 4) continue;
|
||||
for (uint8_t i = 4; i < offset;)
|
||||
{
|
||||
if (offset <= 4) {
|
||||
continue;
|
||||
}
|
||||
for (uint8_t i = 4; i < offset;) {
|
||||
uint8_t blkLen = cta[i] & 0x1f;
|
||||
if (blkLen > 0)
|
||||
{
|
||||
if (blkLen > 0) {
|
||||
uint8_t blkTag = (cta[i] & 0xe0) >> 5;
|
||||
if (blkTag == 0x07 /* Extended Block Type Tag */)
|
||||
{
|
||||
if (blkTag == 0x07 /* Extended Block Type Tag */) {
|
||||
uint8_t extendedTag = cta[i + 1];
|
||||
if (extendedTag == 6 /* HDR SMDB */ || extendedTag == 7 /* HDR DMDB */)
|
||||
if (extendedTag == 6 /* HDR SMDB */ || extendedTag == 7 /* HDR DMDB */) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += (uint8_t) (blkLen + 1);
|
||||
@@ -129,15 +113,20 @@ bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length)
|
||||
{
|
||||
if (length < 128 || length % 128 != 0) return false;
|
||||
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length) {
|
||||
if (length < 128 || length % 128 != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const uint8_t edidHeader[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
|
||||
if (memcmp(edid, edidHeader, sizeof(edidHeader)) != 0) return false;
|
||||
if (memcmp(edid, edidHeader, sizeof(edidHeader)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t sum = 0;
|
||||
for (uint32_t i = 0; i < 128; i++) sum += edid[i];
|
||||
for (uint32_t i = 0; i < 128; i++) {
|
||||
sum += edid[i];
|
||||
}
|
||||
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
+167
-174
@@ -7,93 +7,98 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void ffFontInit(FFfont* font)
|
||||
{
|
||||
void ffFontInit(FFfont* font) {
|
||||
// Ensure no memory allocates
|
||||
ffStrbufInit(&font->pretty);
|
||||
ffStrbufInit(&font->name);
|
||||
ffStrbufInit(&font->size);
|
||||
ffListInit(&font->styles, sizeof(FFstrbuf));
|
||||
ffListInit(&font->styles);
|
||||
}
|
||||
|
||||
static void strbufAppendNSExcludingC(FFstrbuf* strbuf, uint32_t length, const char* value, char exclude)
|
||||
{
|
||||
if(value == NULL || length == 0)
|
||||
static void strbufAppendNSExcludingC(FFstrbuf* strbuf, uint32_t length, const char* value, char exclude) {
|
||||
if (value == NULL || length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufEnsureFree(strbuf, length);
|
||||
|
||||
for(uint32_t i = 0; i < length; i++)
|
||||
{
|
||||
if(value[i] != exclude)
|
||||
strbuf->chars[strbuf->length++] = value[i];
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (value[i] != exclude) {
|
||||
strbuf->chars[strbuf->length++] = value[i];
|
||||
}
|
||||
}
|
||||
|
||||
strbuf->chars[strbuf->length] = '\0';
|
||||
}
|
||||
|
||||
static void fontInitPretty(FFfont* font)
|
||||
{
|
||||
static void fontInitPretty(FFfont* font) {
|
||||
ffStrbufAppend(&font->pretty, &font->name);
|
||||
|
||||
if(font->size.length == 0 && font->styles.length == 0)
|
||||
if (font->size.length == 0 && font->styles.length == 0) {
|
||||
return;
|
||||
else if(font->pretty.length == 0)
|
||||
} else if (font->pretty.length == 0) {
|
||||
ffStrbufAppendS(&font->pretty, "default");
|
||||
}
|
||||
|
||||
ffStrbufAppendS(&font->pretty, " (");
|
||||
|
||||
if(font->size.length > 0)
|
||||
{
|
||||
if (font->size.length > 0) {
|
||||
ffStrbufAppend(&font->pretty, &font->size);
|
||||
if (!ffStrbufEndsWithS(&font->size, "pt") && !ffStrbufEndsWithS(&font->size, "px"))
|
||||
if (!ffStrbufEndsWithS(&font->size, "pt") && !ffStrbufEndsWithS(&font->size, "px")) {
|
||||
ffStrbufAppendS(&font->pretty, "pt");
|
||||
}
|
||||
|
||||
if(font->styles.length > 0)
|
||||
if (font->styles.length > 0) {
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < font->styles.length; i++)
|
||||
{
|
||||
for (uint32_t i = 0; i < font->styles.length; i++) {
|
||||
ffStrbufAppend(&font->pretty, FF_LIST_GET(FFstrbuf, font->styles, i));
|
||||
|
||||
if(i < font->styles.length - 1)
|
||||
if (i < font->styles.length - 1) {
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufAppendC(&font->pretty, ')');
|
||||
}
|
||||
|
||||
void ffFontInitQt(FFfont* font, const char* data)
|
||||
{
|
||||
void ffFontInitQt(FFfont* font, const char* data) {
|
||||
ffFontInit(font);
|
||||
|
||||
//See https://doc.qt.io/qt-5/qfont.html#toString
|
||||
// See https://doc.qt.io/qt-5/qfont.html#toString
|
||||
|
||||
//Family
|
||||
// Family
|
||||
data = ffStrbufAppendSUntilC(&font->name, data, ',');
|
||||
ffStrbufTrim(&font->name, ' ');
|
||||
if (!data) goto exit;
|
||||
if (!data) {
|
||||
goto exit;
|
||||
}
|
||||
data++;
|
||||
|
||||
//Size
|
||||
// Size
|
||||
data = ffStrbufAppendSUntilC(&font->size, data, ',');
|
||||
ffStrbufTrim(&font->size, ' ');
|
||||
if (!data) goto exit;
|
||||
if (!data) {
|
||||
goto exit;
|
||||
}
|
||||
data++;
|
||||
|
||||
//Style
|
||||
// Style
|
||||
data = strrchr(data, ',');
|
||||
if (!data) goto exit;
|
||||
if (!data) {
|
||||
goto exit;
|
||||
}
|
||||
data++;
|
||||
if (isalpha(*data))
|
||||
{
|
||||
do
|
||||
{
|
||||
FFstrbuf* style = (FFstrbuf*) ffListAdd(&font->styles);
|
||||
if (isalpha(*data)) {
|
||||
do {
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInit(style);
|
||||
data = ffStrbufAppendSUntilC(style, data, ' ');
|
||||
if (data) data++;
|
||||
if (data) {
|
||||
data++;
|
||||
}
|
||||
} while (data);
|
||||
}
|
||||
|
||||
@@ -101,34 +106,37 @@ exit:
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* alternativeBuffer)
|
||||
{
|
||||
while(**data == ' ' || **data == '\t' || **data == ',')
|
||||
static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* alternativeBuffer) {
|
||||
while (**data == ' ' || **data == '\t' || **data == ',') {
|
||||
++(*data);
|
||||
}
|
||||
|
||||
const char* wordStart = *data;
|
||||
|
||||
while(**data != ' ' && **data != '\t' && **data != ',' && **data != '\0' && **data != '`' && **data != '\\')
|
||||
while (**data != ' ' && **data != '\t' && **data != ',' && **data != '\0' && **data != '`' && **data != '\\') {
|
||||
++(*data);
|
||||
}
|
||||
|
||||
uint32_t wordLength = (uint32_t) (*data - wordStart);
|
||||
if(wordLength == 0)
|
||||
if (wordLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(**data == '\0' || **data == '`' || **data == '\\')
|
||||
{
|
||||
if (**data == '\0' || **data == '`' || **data == '\\') {
|
||||
ffStrbufAppendNS(&font->size, wordLength, wordStart);
|
||||
if(ffStrbufEndsWithS(&font->size, "px"))
|
||||
if (ffStrbufEndsWithS(&font->size, "px")) {
|
||||
ffStrbufSubstrBefore(&font->size, font->size.length - 2);
|
||||
}
|
||||
|
||||
double dummy;
|
||||
if(sscanf(font->size.chars, "%lf", &dummy) == 1)
|
||||
if (sscanf(font->size.chars, "%lf", &dummy) == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufClear(&font->size);
|
||||
}
|
||||
|
||||
if(
|
||||
if (
|
||||
ffStrStartsWithIgnCase(wordStart, "Ultra") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Extra") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Semi") ||
|
||||
@@ -142,51 +150,47 @@ static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* altern
|
||||
ffStrStartsWithIgnCase(wordStart, "Bold") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Black") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Condensed") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Expanded")
|
||||
) {
|
||||
if(alternativeBuffer == NULL)
|
||||
{
|
||||
alternativeBuffer = (FFstrbuf*) ffListAdd(&font->styles);
|
||||
ffStrStartsWithIgnCase(wordStart, "Expanded")) {
|
||||
if (alternativeBuffer == NULL) {
|
||||
alternativeBuffer = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInit(alternativeBuffer);
|
||||
}
|
||||
|
||||
strbufAppendNSExcludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
|
||||
if(
|
||||
if (
|
||||
ffStrStartsWithIgnCase(wordStart, "Ultra ") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Extra ") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Semi ") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Demi ")
|
||||
) {
|
||||
ffStrStartsWithIgnCase(wordStart, "Demi ")) {
|
||||
fontPangoParseWord(data, font, alternativeBuffer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(alternativeBuffer != NULL)
|
||||
{
|
||||
if (alternativeBuffer != NULL) {
|
||||
strbufAppendNSExcludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
return;
|
||||
}
|
||||
|
||||
if(font->name.length > 0)
|
||||
if (font->name.length > 0) {
|
||||
ffStrbufAppendC(&font->name, ' ');
|
||||
}
|
||||
ffStrbufAppendNS(&font->name, wordLength, wordStart);
|
||||
}
|
||||
|
||||
void ffFontInitPango(FFfont* font, const char* data)
|
||||
{
|
||||
void ffFontInitPango(FFfont* font, const char* data) {
|
||||
ffFontInit(font);
|
||||
|
||||
while(*data != '\0' && *data != '`' && *data != '\\')
|
||||
while (*data != '\0' && *data != '`' && *data != '\\') {
|
||||
fontPangoParseWord(&data, font, NULL);
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitValues(FFfont* font, const char* name, const char* size)
|
||||
{
|
||||
void ffFontInitValues(FFfont* font, const char* name, const char* size) {
|
||||
ffFontInit(font);
|
||||
|
||||
ffStrbufAppendS(&font->name, name);
|
||||
@@ -196,8 +200,7 @@ void ffFontInitValues(FFfont* font, const char* name, const char* size)
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitXlfd(FFfont* font, const char* xlfd)
|
||||
{
|
||||
void ffFontInitXlfd(FFfont* font, const char* xlfd) {
|
||||
assert(xlfd && *xlfd);
|
||||
|
||||
// https://en.wikipedia.org/wiki/X_logical_font_description
|
||||
@@ -205,80 +208,78 @@ void ffFontInitXlfd(FFfont* font, const char* xlfd)
|
||||
|
||||
// XLFD: -foundry-family-weight-slant-setwidth-addstyle-pixelsize-pointsize-xres-yres-spacing-averagewidth-charsetregistry-charsetencoding
|
||||
// It often starts with '-', which would create an empty first field. Skip it to align indexes.
|
||||
if(*xlfd == '-')
|
||||
if (*xlfd == '-') {
|
||||
xlfd++;
|
||||
}
|
||||
|
||||
const char* pstart = xlfd;
|
||||
|
||||
for(int field = 0; field < 14; field++)
|
||||
{
|
||||
for (int field = 0; field < 14; field++) {
|
||||
const char* pend = strchr(pstart, '-');
|
||||
uint32_t length = pend ? (uint32_t)(pend - pstart) : (uint32_t) strlen(pstart);
|
||||
uint32_t length = pend ? (uint32_t) (pend - pstart) : (uint32_t) strlen(pstart);
|
||||
|
||||
if(length > 0)
|
||||
{
|
||||
if(field == 1) // family
|
||||
if (length > 0) {
|
||||
if (field == 1) // family
|
||||
{
|
||||
ffStrbufAppendNS(&font->name, length, pstart);
|
||||
}
|
||||
else if(field == 7) // pointsize (decipoints, preferred)
|
||||
} else if (field == 7) // pointsize (decipoints, preferred)
|
||||
{
|
||||
// parse positive integer from substring
|
||||
long deciPt = 0;
|
||||
bool ok = true;
|
||||
for(uint32_t i = 0; i < length; i++)
|
||||
{
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
char c = pstart[i];
|
||||
if(c < '0' || c > '9') { ok = false; break; }
|
||||
if (c < '0' || c > '9') {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
deciPt = deciPt * 10 + (c - '0');
|
||||
}
|
||||
|
||||
if(ok && deciPt > 0)
|
||||
{
|
||||
if (ok && deciPt > 0) {
|
||||
ffStrbufClear(&font->size);
|
||||
|
||||
char tmp[32];
|
||||
if(deciPt % 10 == 0)
|
||||
if (deciPt % 10 == 0) {
|
||||
snprintf(tmp, sizeof(tmp), "%ldpt", deciPt / 10);
|
||||
else
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "%ld.%ldpt", deciPt / 10, deciPt % 10);
|
||||
}
|
||||
|
||||
ffStrbufAppendS(&font->size, tmp);
|
||||
}
|
||||
}
|
||||
else if(field == 6) // pixelsize (fallback if pointsize missing/invalid)
|
||||
} else if (field == 6) // pixelsize (fallback if pointsize missing/invalid)
|
||||
{
|
||||
if(font->size.length == 0)
|
||||
{
|
||||
if (font->size.length == 0) {
|
||||
long px = 0;
|
||||
bool ok = true;
|
||||
for(uint32_t i = 0; i < length; i++)
|
||||
{
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
char c = pstart[i];
|
||||
if(c < '0' || c > '9') { ok = false; break; }
|
||||
if (c < '0' || c > '9') {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
px = px * 10 + (c - '0');
|
||||
}
|
||||
|
||||
if(ok && px > 0)
|
||||
{
|
||||
if (ok && px > 0) {
|
||||
ffStrbufAppendNS(&font->size, length, pstart);
|
||||
ffStrbufAppendS(&font->size, "px");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(field >= 2 && field <= 5) // weight/slant/setwidth/addstyle
|
||||
} else if (field >= 2 && field <= 5) // weight/slant/setwidth/addstyle
|
||||
{
|
||||
// ignore "normal" (case-insensitive)
|
||||
if(!(length == 6 && ffStrStartsWithIgnCase(pstart, "normal")))
|
||||
{
|
||||
FFstrbuf* style = (FFstrbuf*) ffListAdd(&font->styles);
|
||||
if (!(length == 6 && ffStrStartsWithIgnCase(pstart, "normal"))) {
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInitNS(style, length, pstart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!pend)
|
||||
if (!pend) {
|
||||
break;
|
||||
}
|
||||
|
||||
pstart = pend + 1;
|
||||
}
|
||||
@@ -286,8 +287,7 @@ void ffFontInitXlfd(FFfont* font, const char* xlfd)
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitXft(FFfont* font, const char* xft)
|
||||
{
|
||||
void ffFontInitXft(FFfont* font, const char* xft) {
|
||||
assert(xft);
|
||||
|
||||
// https://en.wikipedia.org/wiki/Xft
|
||||
@@ -302,26 +302,28 @@ void ffFontInitXft(FFfont* font, const char* xft)
|
||||
// 1) Parse "head" part before first ':' => usually "family[-size]" (may include commas)
|
||||
const char* p = xft;
|
||||
|
||||
while(*p == ' ' || *p == '\t')
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
++p;
|
||||
}
|
||||
|
||||
const char* headStart = p;
|
||||
while(*p != '\0' && *p != ':')
|
||||
while (*p != '\0' && *p != ':') {
|
||||
++p;
|
||||
}
|
||||
const char* headEnd = p;
|
||||
|
||||
// trim tail spaces
|
||||
while(headEnd > headStart && (headEnd[-1] == ' ' || headEnd[-1] == '\t'))
|
||||
while (headEnd > headStart && (headEnd[-1] == ' ' || headEnd[-1] == '\t')) {
|
||||
--headEnd;
|
||||
}
|
||||
|
||||
// If multiple families are listed, take the first one (up to comma)
|
||||
for(const char* q = headStart; q < headEnd; ++q)
|
||||
{
|
||||
if(*q == ',')
|
||||
{
|
||||
for (const char* q = headStart; q < headEnd; ++q) {
|
||||
if (*q == ',') {
|
||||
headEnd = q;
|
||||
while(headEnd > headStart && (headEnd[-1] == ' ' || headEnd[-1] == '\t'))
|
||||
while (headEnd > headStart && (headEnd[-1] == ' ' || headEnd[-1] == '\t')) {
|
||||
--headEnd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -330,56 +332,51 @@ void ffFontInitXft(FFfont* font, const char* xft)
|
||||
const char* dashPos = NULL;
|
||||
const char* sizeStart = NULL;
|
||||
|
||||
for(const char* q = headEnd; q > headStart; )
|
||||
{
|
||||
for (const char* q = headEnd; q > headStart;) {
|
||||
--q;
|
||||
if(*q == '-' && (q + 1) < headEnd && ffCharIsDigit(q[1]))
|
||||
{
|
||||
if (*q == '-' && (q + 1) < headEnd && ffCharIsDigit(q[1])) {
|
||||
dashPos = q;
|
||||
sizeStart = q + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dashPos)
|
||||
{
|
||||
if (dashPos) {
|
||||
bool ok = true;
|
||||
bool seenDigit = false;
|
||||
for(const char* q = sizeStart; q < headEnd; ++q)
|
||||
{
|
||||
if(ffCharIsDigit(*q))
|
||||
for (const char* q = sizeStart; q < headEnd; ++q) {
|
||||
if (ffCharIsDigit(*q)) {
|
||||
seenDigit = true;
|
||||
else if(*q == '.')
|
||||
} else if (*q == '.') {
|
||||
continue;
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok && seenDigit)
|
||||
{
|
||||
if (ok && seenDigit) {
|
||||
const char* nameEnd = dashPos;
|
||||
while(nameEnd > headStart && (nameEnd[-1] == ' ' || nameEnd[-1] == '\t'))
|
||||
while (nameEnd > headStart && (nameEnd[-1] == ' ' || nameEnd[-1] == '\t')) {
|
||||
--nameEnd;
|
||||
}
|
||||
|
||||
if(nameEnd > headStart)
|
||||
if (nameEnd > headStart) {
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (nameEnd - headStart), headStart);
|
||||
}
|
||||
|
||||
if(headEnd > sizeStart)
|
||||
if (headEnd > sizeStart) {
|
||||
ffStrbufAppendNS(&font->size, (uint32_t) (headEnd - sizeStart), sizeStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(headEnd > headStart)
|
||||
}
|
||||
} else {
|
||||
if (headEnd > headStart) {
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (headEnd - headStart), headStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(headEnd > headStart)
|
||||
} else {
|
||||
if (headEnd > headStart) {
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (headEnd - headStart), headStart);
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufTrim(&font->name, ' ');
|
||||
@@ -387,28 +384,27 @@ void ffFontInitXft(FFfont* font, const char* xft)
|
||||
|
||||
// 2) Parse key=value fields after ':' (Fontconfig-like). Fields separated by ':'.
|
||||
// Common keys: size, pixelsize, pointsize, style, weight, slant, width
|
||||
while(*p == ':')
|
||||
{
|
||||
while (*p == ':') {
|
||||
++p;
|
||||
|
||||
// key
|
||||
const char* keyStart = p;
|
||||
while(*p != '\0' && *p != '=' && *p != ':')
|
||||
while (*p != '\0' && *p != '=' && *p != ':') {
|
||||
++p;
|
||||
}
|
||||
const char* keyEnd = p;
|
||||
|
||||
if(*p != '=')
|
||||
if (*p != '=') {
|
||||
continue; // skip tokens without '='
|
||||
}
|
||||
|
||||
++p; // skip '='
|
||||
|
||||
// value (until next ':', allow backslash-escaping)
|
||||
FF_STRBUF_AUTO_DESTROY value = ffStrbufCreate();
|
||||
|
||||
while(*p != '\0' && *p != ':')
|
||||
{
|
||||
if(*p == '\\' && p[1] != '\0')
|
||||
{
|
||||
while (*p != '\0' && *p != ':') {
|
||||
if (*p == '\\' && p[1] != '\0') {
|
||||
++p;
|
||||
ffStrbufAppendC(&value, *p);
|
||||
++p;
|
||||
@@ -426,49 +422,46 @@ void ffFontInitXft(FFfont* font, const char* xft)
|
||||
|
||||
// helper: set numeric size if not set yet
|
||||
const bool sizeEmpty = (font->size.length == 0);
|
||||
if(value.length > 0)
|
||||
{
|
||||
if(
|
||||
if (value.length > 0) {
|
||||
if (
|
||||
(keyLen == 4 && ffStrStartsWithIgnCase(keyStart, "size")) ||
|
||||
(keyLen == 9 && ffStrStartsWithIgnCase(keyStart, "pixelsize"))
|
||||
)
|
||||
{
|
||||
if(sizeEmpty && ffCharIsDigit(value.chars[0]))
|
||||
{
|
||||
(keyLen == 9 && (ffStrStartsWithIgnCase(keyStart, "pixelsize") || ffStrStartsWithIgnCase(keyStart, "pointsize")))) {
|
||||
if (sizeEmpty && ffCharIsDigit(value.chars[0])) {
|
||||
ffStrbufAppend(&font->size, &value);
|
||||
ffStrbufAppendS(&font->size, keyLen == 4 ? "pt" : "px");
|
||||
ffStrbufAppendS(&font->size,
|
||||
(keyLen == 9 && ffStrStartsWithIgnCase(keyStart, "pixelsize")) ? "px" : "pt");
|
||||
}
|
||||
}
|
||||
else if(keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "style"))
|
||||
{
|
||||
} else if (keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "style")) {
|
||||
// style may contain multiple words: "Bold Italic"
|
||||
const char* s = value.chars;
|
||||
while(*s != '\0')
|
||||
{
|
||||
while(*s == ' ' || *s == '\t' || *s == ',')
|
||||
while (*s != '\0') {
|
||||
while (*s == ' ' || *s == '\t' || *s == ',') {
|
||||
++s;
|
||||
}
|
||||
|
||||
const char* w = s;
|
||||
while(*s != '\0' && *s != ' ' && *s != '\t' && *s != ',')
|
||||
while (*s != '\0' && *s != ' ' && *s != '\t' && *s != ',') {
|
||||
++s;
|
||||
}
|
||||
|
||||
if(s > w)
|
||||
{
|
||||
if (s > w) {
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInitNS(style, (uint32_t) (s - w), w);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(
|
||||
} else if (
|
||||
(keyLen == 6 && ffStrStartsWithIgnCase(keyStart, "weight")) ||
|
||||
(keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "slant")) ||
|
||||
(keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "width"))
|
||||
) {
|
||||
(keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "width"))) {
|
||||
// normalize: remove '-' to align with other parsers ("Semi-Bold" -> "SemiBold")
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInit(style);
|
||||
strbufAppendNSExcludingC(style, value.length, value.chars, '-');
|
||||
ffStrbufTrim(style, ' ');
|
||||
if (style->length == 0) {
|
||||
ffStrbufDestroy(style);
|
||||
--font->styles.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,14 +469,16 @@ void ffFontInitXft(FFfont* font, const char* xft)
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style)
|
||||
{
|
||||
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style) {
|
||||
ffFontInit(font);
|
||||
|
||||
if (name) ffStrbufInitMove(&font->name, name);
|
||||
if (size) ffStrbufInitMove(&font->size, size);
|
||||
if (style)
|
||||
{
|
||||
if (name) {
|
||||
ffStrbufInitMove(&font->name, name);
|
||||
}
|
||||
if (size) {
|
||||
ffStrbufInitMove(&font->size, size);
|
||||
}
|
||||
if (style) {
|
||||
FFstrbuf* styleBuf = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInitMove(styleBuf, style);
|
||||
}
|
||||
@@ -491,30 +486,28 @@ void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName)
|
||||
{
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName) {
|
||||
const char* pspace = strrchr(rawName, ' ');
|
||||
if(pspace == NULL)
|
||||
{
|
||||
if (pspace == NULL) {
|
||||
ffFontInitCopy(font, rawName);
|
||||
return;
|
||||
}
|
||||
|
||||
ffFontInit(font);
|
||||
|
||||
ffStrbufAppendNS(&font->name, (uint32_t)(pspace - rawName), rawName);
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (pspace - rawName), rawName);
|
||||
ffStrbufAppendS(&font->size, pspace + 1);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontDestroy(FFfont* font)
|
||||
{
|
||||
void ffFontDestroy(FFfont* font) {
|
||||
ffStrbufDestroy(&font->pretty);
|
||||
ffStrbufDestroy(&font->name);
|
||||
ffStrbufDestroy(&font->size);
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, str, font->styles)
|
||||
FF_LIST_FOR_EACH (FFstrbuf, str, font->styles) {
|
||||
ffStrbufDestroy(str);
|
||||
}
|
||||
ffListDestroy(&font->styles);
|
||||
}
|
||||
|
||||
+114
-156
@@ -6,61 +6,58 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
{
|
||||
switch(formatarg->type)
|
||||
{
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) {
|
||||
switch (formatarg->type) {
|
||||
case FF_ARG_TYPE_INT:
|
||||
ffStrbufAppendSInt(buffer, *(int32_t*)formatarg->value);
|
||||
ffStrbufAppendSInt(buffer, *(int32_t*) formatarg->value);
|
||||
break;
|
||||
case FF_ARG_TYPE_UINT:
|
||||
ffStrbufAppendUInt(buffer, *(uint32_t*)formatarg->value);
|
||||
ffStrbufAppendUInt(buffer, *(uint32_t*) formatarg->value);
|
||||
break;
|
||||
case FF_ARG_TYPE_UINT64:
|
||||
ffStrbufAppendUInt(buffer, *(uint64_t*)formatarg->value);
|
||||
ffStrbufAppendUInt(buffer, *(uint64_t*) formatarg->value);
|
||||
break;
|
||||
case FF_ARG_TYPE_UINT16:
|
||||
ffStrbufAppendUInt(buffer, *(uint16_t*)formatarg->value);
|
||||
ffStrbufAppendUInt(buffer, *(uint16_t*) formatarg->value);
|
||||
break;
|
||||
case FF_ARG_TYPE_UINT8:
|
||||
ffStrbufAppendUInt(buffer, *(uint8_t*)formatarg->value);
|
||||
ffStrbufAppendUInt(buffer, *(uint8_t*) formatarg->value);
|
||||
break;
|
||||
case FF_ARG_TYPE_STRING:
|
||||
ffStrbufAppendS(buffer, (const char*)formatarg->value);
|
||||
ffStrbufAppendS(buffer, (const char*) formatarg->value);
|
||||
break;
|
||||
case FF_ARG_TYPE_STRBUF:
|
||||
ffStrbufAppend(buffer, (const FFstrbuf*)formatarg->value);
|
||||
ffStrbufAppend(buffer, (const FFstrbuf*) formatarg->value);
|
||||
break;
|
||||
case FF_ARG_TYPE_FLOAT:
|
||||
ffStrbufAppendDouble(buffer, *(float*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
ffStrbufAppendDouble(buffer, *(float*) formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
break;
|
||||
case FF_ARG_TYPE_DOUBLE:
|
||||
ffStrbufAppendDouble(buffer, *(double*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
ffStrbufAppendDouble(buffer, *(double*) formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
break;
|
||||
case FF_ARG_TYPE_BOOL:
|
||||
ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false");
|
||||
ffStrbufAppendS(buffer, *(bool*) formatarg->value ? "true" : "false");
|
||||
break;
|
||||
case FF_ARG_TYPE_LIST:
|
||||
{
|
||||
case FF_ARG_TYPE_LIST: {
|
||||
const FFlist* list = (const FFlist*) formatarg->value;
|
||||
for(uint32_t i = 0; i < list->length; i++)
|
||||
{
|
||||
for (uint32_t i = 0; i < list->length; i++) {
|
||||
ffStrbufAppend(buffer, FF_LIST_GET(FFstrbuf, *list, i));
|
||||
if(i < list->length - 1)
|
||||
if (i < list->length - 1) {
|
||||
ffStrbufAppendS(buffer, ", ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FF_ARG_TYPE_BUFFER:
|
||||
{
|
||||
case FF_ARG_TYPE_BUFFER: {
|
||||
// Placeholder for binary data, just print the size for now
|
||||
const FFArgBuffer* argBuffer = (const FFArgBuffer*) formatarg->value;
|
||||
ffStrbufAppendF(buffer, "buffer(%u bytes)", argBuffer->length);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if(formatarg->type != FF_ARG_TYPE_NULL)
|
||||
if (formatarg->type != FF_ARG_TYPE_NULL) {
|
||||
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -73,62 +70,48 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
* @param placeholderValue the string to parse
|
||||
* @return uint32_t the parsed value
|
||||
*/
|
||||
static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
char firstChar = placeholderValue[0];
|
||||
if (firstChar == '\0')
|
||||
if (firstChar == '\0') {
|
||||
return 0; // use arg counter
|
||||
}
|
||||
|
||||
if (firstChar >= '0' && firstChar <= '9')
|
||||
{
|
||||
if (firstChar >= '0' && firstChar <= '9') {
|
||||
char* pEnd = NULL;
|
||||
uint32_t result = (uint32_t) strtoul(placeholderValue, &pEnd, 10);
|
||||
if (result > numArgs)
|
||||
if (result > numArgs) {
|
||||
return UINT32_MAX;
|
||||
if (*pEnd != '\0')
|
||||
}
|
||||
if (*pEnd != '\0') {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (ffCharIsEnglishAlphabet(firstChar))
|
||||
{
|
||||
for (uint32_t i = 0; i < numArgs; ++i)
|
||||
{
|
||||
} else if (ffCharIsEnglishAlphabet(firstChar)) {
|
||||
for (uint32_t i = 0; i < numArgs; ++i) {
|
||||
const FFformatarg* arg = &arguments[i];
|
||||
if (arg->name && ffStrEqualsIgnCase(placeholderValue, arg->name))
|
||||
if (arg->name && ffStrEqualsIgnCase(placeholderValue, arg->name)) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength)
|
||||
{
|
||||
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength) {
|
||||
ffStrbufAppendS(buffer, start);
|
||||
ffStrbufAppend(buffer, placeholderValue);
|
||||
|
||||
if(index < formatStringLength)
|
||||
if (index < formatStringLength) {
|
||||
ffStrbufAppendC(buffer, '}');
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool formatArgSet(const FFformatarg* arg)
|
||||
{
|
||||
return arg->value != NULL && (
|
||||
(arg->type == FF_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
|
||||
(arg->type == FF_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
|
||||
(arg->type == FF_ARG_TYPE_INT && *(int*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
|
||||
(arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
|
||||
(arg->type == FF_ARG_TYPE_BOOL && *(bool*)arg->value) ||
|
||||
(arg->type == FF_ARG_TYPE_LIST && ((FFlist*)arg->value)->length > 0)
|
||||
);
|
||||
static inline bool formatArgSet(const FFformatarg* arg) {
|
||||
return arg->value != NULL && ((arg->type == FF_ARG_TYPE_DOUBLE && *(double*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_FLOAT && *(float*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_INT && *(int32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*) arg->value)->length > 0) || (arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*) arg->value)) || (arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT && *(uint32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT64 && *(uint64_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_BOOL && *(bool*) arg->value) || (arg->type == FF_ARG_TYPE_LIST && ((FFlist*) arg->value)->length > 0));
|
||||
}
|
||||
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
uint32_t argCounter = 0;
|
||||
|
||||
uint32_t numOpenIfs = 0;
|
||||
@@ -136,11 +119,9 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate();
|
||||
|
||||
for(uint32_t i = 0; i < formatstr->length; ++i)
|
||||
{
|
||||
for (uint32_t i = 0; i < formatstr->length; ++i) {
|
||||
// if we don't have a placeholder start just copy the chars over to output buffer
|
||||
if(formatstr->chars[i] != '{')
|
||||
{
|
||||
if (formatstr->chars[i] != '{') {
|
||||
ffStrbufAppendC(buffer, formatstr->chars[i]);
|
||||
continue;
|
||||
}
|
||||
@@ -149,8 +130,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
++i;
|
||||
|
||||
// double {{ elvaluates to a single { and doesn't count as start
|
||||
if(formatstr->chars[i] == '{')
|
||||
{
|
||||
if (formatstr->chars[i] == '{') {
|
||||
ffStrbufAppendC(buffer, '{');
|
||||
continue;
|
||||
}
|
||||
@@ -165,61 +145,58 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
|
||||
char firstChar = placeholderValue.chars[0];
|
||||
|
||||
if (placeholderValue.length == 1)
|
||||
{
|
||||
if (placeholderValue.length == 1) {
|
||||
// test if for stop, if so break the loop
|
||||
if (firstChar == '-')
|
||||
if (firstChar == '-') {
|
||||
break;
|
||||
}
|
||||
|
||||
// test for end of an if, if so do nothing
|
||||
if (firstChar == '?')
|
||||
{
|
||||
if(numOpenIfs == 0)
|
||||
if (firstChar == '?') {
|
||||
if (numOpenIfs == 0) {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
} else {
|
||||
--numOpenIfs;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for end of a not if, if so do nothing
|
||||
if (firstChar == '/')
|
||||
{
|
||||
if(numOpenNotIfs == 0)
|
||||
if (firstChar == '/') {
|
||||
if (numOpenNotIfs == 0) {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
} else {
|
||||
--numOpenNotIfs;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for end of a color, if so do nothing
|
||||
if (firstChar == '#')
|
||||
{
|
||||
if (!instance.config.display.pipe)
|
||||
if (firstChar == '#') {
|
||||
if (!instance.config.display.pipe) {
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// test for if, if so evaluate it
|
||||
if (firstChar == '?')
|
||||
{
|
||||
if (firstChar == '?') {
|
||||
ffStrbufSubstrAfter(&placeholderValue, 0);
|
||||
|
||||
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||
|
||||
// testing for an invalid index
|
||||
if (index > numArgs || index < 1)
|
||||
{
|
||||
if (index > numArgs || index < 1) {
|
||||
appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// continue normally if an format arg is set and the value is > 0
|
||||
if (formatArgSet(&arguments[index - 1]))
|
||||
{
|
||||
if (formatArgSet(&arguments[index - 1])) {
|
||||
++numOpenIfs;
|
||||
continue;
|
||||
}
|
||||
@@ -230,22 +207,19 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
}
|
||||
|
||||
// test for not if, if so evaluate it
|
||||
if (firstChar == '/')
|
||||
{
|
||||
if (firstChar == '/') {
|
||||
ffStrbufSubstrAfter(&placeholderValue, 0);
|
||||
|
||||
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||
|
||||
// testing for an invalid index
|
||||
if (index > numArgs || index < 1)
|
||||
{
|
||||
if (index > numArgs || index < 1) {
|
||||
appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
//continue normally if an format arg is not set or the value is 0
|
||||
if (!formatArgSet(&arguments[index - 1]))
|
||||
{
|
||||
// continue normally if an format arg is not set or the value is 0
|
||||
if (!formatArgSet(&arguments[index - 1])) {
|
||||
++numOpenNotIfs;
|
||||
continue;
|
||||
}
|
||||
@@ -255,11 +229,9 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
continue;
|
||||
}
|
||||
|
||||
//test for color, if so evaluate it
|
||||
if (firstChar == '#')
|
||||
{
|
||||
if (!instance.config.display.pipe)
|
||||
{
|
||||
// test for color, if so evaluate it
|
||||
if (firstChar == '#') {
|
||||
if (!instance.config.display.pipe) {
|
||||
ffStrbufAppendS(buffer, "\e[");
|
||||
ffOptionParseColorNoClear(placeholderValue.chars + 1, buffer);
|
||||
ffStrbufAppendC(buffer, 'm');
|
||||
@@ -267,29 +239,25 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
continue;
|
||||
}
|
||||
|
||||
//test for constant or env var, if so evaluate it
|
||||
if (firstChar == '$')
|
||||
{
|
||||
// test for constant or env var, if so evaluate it
|
||||
if (firstChar == '$') {
|
||||
char* pend = NULL;
|
||||
int32_t indexSigned = (int32_t) strtol(placeholderValue.chars + 1, &pend, 10);
|
||||
if (pend == placeholderValue.chars + 1)
|
||||
{
|
||||
if (pend == placeholderValue.chars + 1) {
|
||||
// treat placeholder as an environment variable
|
||||
char* envValue = getenv(placeholderValue.chars + 1);
|
||||
if (envValue)
|
||||
if (envValue) {
|
||||
ffStrbufAppendS(buffer, envValue);
|
||||
else
|
||||
} else {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
// treat placeholder as a constant
|
||||
uint32_t index = (uint32_t) (indexSigned < 0 ? (int32_t) instance.config.display.constants.length + indexSigned : indexSigned - 1);
|
||||
|
||||
if (*pend != '\0' || instance.config.display.constants.length <= index)
|
||||
if (*pend != '\0' || instance.config.display.constants.length <= index) {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
{
|
||||
} else {
|
||||
FFstrbuf* item = FF_LIST_GET(FFstrbuf, instance.config.display.constants, index);
|
||||
ffStrbufAppend(buffer, item);
|
||||
}
|
||||
@@ -299,115 +267,104 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
|
||||
char* pSep = placeholderValue.chars;
|
||||
char cSep = '\0';
|
||||
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '~')
|
||||
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '~') {
|
||||
++pSep;
|
||||
if (*pSep)
|
||||
{
|
||||
}
|
||||
if (*pSep) {
|
||||
cSep = *pSep;
|
||||
*pSep = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
pSep = NULL;
|
||||
}
|
||||
|
||||
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||
|
||||
// test for invalid index
|
||||
if (index == 0)
|
||||
if (index == 0) {
|
||||
index = ++argCounter;
|
||||
}
|
||||
|
||||
if (index > numArgs)
|
||||
{
|
||||
if (pSep) *pSep = cSep;
|
||||
if (index > numArgs) {
|
||||
if (pSep) {
|
||||
*pSep = cSep;
|
||||
}
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cSep)
|
||||
if (!cSep) {
|
||||
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
|
||||
else if (cSep == '~')
|
||||
{
|
||||
} else if (cSep == '~') {
|
||||
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
||||
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
||||
|
||||
char* pEnd = NULL;
|
||||
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
||||
if (start < 0)
|
||||
if (start < 0) {
|
||||
start = (int32_t) tempString.length + start;
|
||||
if (start >= 0 && (uint32_t) start < tempString.length)
|
||||
{
|
||||
if (*pEnd == '\0')
|
||||
}
|
||||
if (start >= 0 && (uint32_t) start < tempString.length) {
|
||||
if (*pEnd == '\0') {
|
||||
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
||||
else if (*pEnd == ',')
|
||||
{
|
||||
} else if (*pEnd == ',') {
|
||||
int32_t end = (int32_t) strtol(pEnd + 1, &pEnd, 10);
|
||||
if (!*pEnd)
|
||||
{
|
||||
if (end < 0)
|
||||
if (!*pEnd) {
|
||||
if (end < 0) {
|
||||
end = (int32_t) tempString.length + end;
|
||||
if ((uint32_t) end > tempString.length)
|
||||
}
|
||||
if ((uint32_t) end > tempString.length) {
|
||||
end = (int32_t) tempString.length;
|
||||
if (end > start)
|
||||
}
|
||||
if (end > start) {
|
||||
ffStrbufAppendNS(buffer, (uint32_t) (end - start), &tempString.chars[start]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*pEnd)
|
||||
{
|
||||
if (*pEnd) {
|
||||
*pSep = cSep;
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
char* pEnd = NULL;
|
||||
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
||||
if (*pEnd != '\0')
|
||||
{
|
||||
if (*pEnd != '\0') {
|
||||
*pSep = cSep;
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ellipsis = false;
|
||||
if (truncLength < 0)
|
||||
{
|
||||
if (truncLength < 0) {
|
||||
ellipsis = true;
|
||||
truncLength = -truncLength;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
||||
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
||||
if (tempString.length == (uint32_t) truncLength)
|
||||
if (tempString.length == (uint32_t) truncLength) {
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
else if (tempString.length > (uint32_t) truncLength)
|
||||
{
|
||||
if (cSep == ':')
|
||||
{
|
||||
} else if (tempString.length > (uint32_t) truncLength) {
|
||||
if (cSep == ':') {
|
||||
ffStrbufSubstrBefore(&tempString, (uint32_t) truncLength);
|
||||
ffStrbufTrimRightSpace(&tempString);
|
||||
} else {
|
||||
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis ? truncLength : truncLength - 1));
|
||||
}
|
||||
else
|
||||
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
|
||||
if (ellipsis)
|
||||
if (ellipsis) {
|
||||
ffStrbufAppendS(buffer, "…");
|
||||
}
|
||||
else if (cSep == ':')
|
||||
}
|
||||
} else if (cSep == ':') {
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
else
|
||||
{
|
||||
if (cSep == '<')
|
||||
{
|
||||
} else {
|
||||
if (cSep == '<') {
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
}
|
||||
@@ -415,6 +372,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
}
|
||||
}
|
||||
|
||||
if (!instance.config.display.pipe)
|
||||
if (!instance.config.display.pipe) {
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -1,24 +1,25 @@
|
||||
#include "common/frequency.h"
|
||||
|
||||
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result)
|
||||
{
|
||||
if (mhz == 0)
|
||||
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result) {
|
||||
if (mhz == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
bool spaceBeforeUnit = options->freqSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
int8_t ndigits = options->freqNdigits;
|
||||
|
||||
if (ndigits >= 0)
|
||||
{
|
||||
if (ndigits >= 0) {
|
||||
ffStrbufAppendDouble(result, mhz / 1000., ndigits, true);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppendS(result, "GHz");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppendUInt(result, mhz);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppendS(result, "MHz");
|
||||
}
|
||||
return true;
|
||||
|
||||
+157
-152
@@ -19,8 +19,7 @@
|
||||
|
||||
FFinstance instance; // Global singleton
|
||||
|
||||
static void initState(FFstate* state)
|
||||
{
|
||||
static void initState(FFstate* state) {
|
||||
state->logoWidth = 0;
|
||||
state->logoHeight = 0;
|
||||
state->keysHeight = 0;
|
||||
@@ -33,94 +32,100 @@ static void initState(FFstate* state)
|
||||
{
|
||||
// don't enable bright color if the terminal is in light mode
|
||||
FFTerminalThemeResult result;
|
||||
if (ffDetectTerminalTheme(&result, true /* forceEnv for performance */) && !result.bg.dark)
|
||||
if (ffDetectTerminalTheme(&result, true /* forceEnv for performance */) && !result.bg.dark) {
|
||||
state->terminalLightTheme = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void defaultConfig(void)
|
||||
{
|
||||
static void defaultConfig(void) {
|
||||
ffOptionsInitLogo(&instance.config.logo);
|
||||
ffOptionsInitGeneral(&instance.config.general);
|
||||
ffOptionsInitDisplay(&instance.config.display);
|
||||
}
|
||||
|
||||
void ffInitInstance(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
#else
|
||||
// Never use `setlocale(LC_ALL, "")`
|
||||
setlocale(LC_TIME, "");
|
||||
#endif
|
||||
void ffInitInstance(void) {
|
||||
#ifdef _WIN32
|
||||
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
#else
|
||||
// Never use `setlocale(LC_ALL, "")`
|
||||
setlocale(LC_TIME, "");
|
||||
#endif
|
||||
|
||||
defaultConfig();
|
||||
initState(&instance.state);
|
||||
}
|
||||
|
||||
static volatile bool ffDisableLinewrap = true;
|
||||
static volatile bool ffHideCursor = true;
|
||||
#if _WIN32
|
||||
static volatile bool ffDisableLinewrap = false;
|
||||
static volatile bool ffHideCursor = false;
|
||||
#ifdef _WIN32
|
||||
static volatile UINT oldCp = CP_UTF8;
|
||||
#endif
|
||||
|
||||
static void resetConsole(void)
|
||||
{
|
||||
if(ffDisableLinewrap)
|
||||
static void resetConsole(void) {
|
||||
if (ffDisableLinewrap) {
|
||||
fputs("\033[?7h", stdout);
|
||||
}
|
||||
|
||||
if(ffHideCursor)
|
||||
if (ffHideCursor) {
|
||||
fputs("\033[?25h", stdout);
|
||||
}
|
||||
|
||||
if(instance.state.dynamicInterval > 0)
|
||||
if (instance.state.dynamicInterval > 0) {
|
||||
fputs("\033[?1049l", stdout); // Disable alternate buffer
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
fflush(stdout);
|
||||
#if defined(_WIN32)
|
||||
fflush(stdout);
|
||||
|
||||
if(oldCp != CP_UTF8)
|
||||
SetConsoleOutputCP(oldCp);
|
||||
#endif
|
||||
if (oldCp != CP_UTF8) {
|
||||
SetConsoleOutputCP(oldCp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOL WINAPI consoleHandler(FF_MAYBE_UNUSED DWORD signal)
|
||||
{
|
||||
BOOL WINAPI consoleHandler(FF_A_UNUSED DWORD signal) {
|
||||
resetConsole();
|
||||
exit(0);
|
||||
}
|
||||
#else
|
||||
static void exitSignalHandler(FF_MAYBE_UNUSED int signal)
|
||||
{
|
||||
static void exitSignalHandler(FF_A_UNUSED int signal) {
|
||||
resetConsole();
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ffStart(void)
|
||||
{
|
||||
void ffStart(void) {
|
||||
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe;
|
||||
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe;
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
if (instance.config.display.noBuffer)
|
||||
if (instance.config.display.noBuffer) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
else
|
||||
} else {
|
||||
setvbuf(stdout, NULL, _IOFBF, 4096);
|
||||
}
|
||||
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
||||
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD mode = 0;
|
||||
if (GetConsoleMode(hStdout, &mode))
|
||||
{
|
||||
if (GetConsoleMode(hStdout, &mode)) {
|
||||
SetConsoleMode(hStdout, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
oldCp = GetConsoleOutputCP();
|
||||
if (oldCp != CP_UTF8) SetConsoleOutputCP(CP_UTF8);
|
||||
if (oldCp != CP_UTF8) {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (instance.config.display.noBuffer) setvbuf(stdout, NULL, _IONBF, 0);
|
||||
struct sigaction action = { .sa_handler = exitSignalHandler };
|
||||
#else
|
||||
if (instance.config.display.noBuffer) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
}
|
||||
struct sigaction action;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = exitSignalHandler;
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
sigaction(SIGQUIT, &action, NULL);
|
||||
@@ -128,142 +133,142 @@ void ffStart(void)
|
||||
sigemptyset(&newmask);
|
||||
sigaddset(&newmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &newmask, NULL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//reset everything to default before we start printing
|
||||
if(!instance.config.display.pipe)
|
||||
// reset everything to default before we start printing
|
||||
if (!instance.config.display.pipe) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
|
||||
if(ffHideCursor)
|
||||
if (ffHideCursor) {
|
||||
fputs("\033[?25l", stdout);
|
||||
}
|
||||
|
||||
if(ffDisableLinewrap)
|
||||
if (ffDisableLinewrap) {
|
||||
fputs("\033[?7l", stdout);
|
||||
}
|
||||
|
||||
if(instance.state.dynamicInterval > 0)
|
||||
{
|
||||
if (instance.state.dynamicInterval > 0) {
|
||||
fputs("\033[?1049h\033[H", stdout); // Enable alternate buffer
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void ffFinish(void)
|
||||
{
|
||||
void ffFinish(void) {
|
||||
resetConsole();
|
||||
}
|
||||
|
||||
static void destroyConfig(void)
|
||||
{
|
||||
static void destroyConfig(void) {
|
||||
ffOptionsDestroyLogo(&instance.config.logo);
|
||||
ffOptionsDestroyGeneral(&instance.config.general);
|
||||
ffOptionsDestroyDisplay(&instance.config.display);
|
||||
}
|
||||
|
||||
static void destroyState(void)
|
||||
{
|
||||
static void destroyState(void) {
|
||||
ffPlatformDestroy(&instance.state.platform);
|
||||
}
|
||||
|
||||
void ffDestroyInstance(void)
|
||||
{
|
||||
void ffDestroyInstance(void) {
|
||||
destroyConfig();
|
||||
destroyState();
|
||||
}
|
||||
|
||||
//Must be in a file compiled with the libfastfetch target, because the FF_HAVE* macros are not defined for the executable targets
|
||||
void ffListFeatures(void)
|
||||
{
|
||||
// Must be in a file compiled with the libfastfetch target, because the FF_HAVE* macros are not defined for the executable targets
|
||||
void ffListFeatures(void) {
|
||||
fputs(
|
||||
#if FF_HAVE_THREADS
|
||||
"threads\n"
|
||||
#endif
|
||||
#if FF_HAVE_VULKAN
|
||||
"vulkan\n"
|
||||
#endif
|
||||
#if FF_HAVE_WAYLAND
|
||||
"wayland\n"
|
||||
#endif
|
||||
#if FF_HAVE_XCB_RANDR
|
||||
"xcb-randr\n"
|
||||
#endif
|
||||
#if FF_HAVE_XRANDR
|
||||
"xrandr\n"
|
||||
#endif
|
||||
#if FF_HAVE_DRM
|
||||
"drm\n"
|
||||
#endif
|
||||
#if FF_HAVE_DRM_AMDGPU
|
||||
"drm_amdgpu\n"
|
||||
#endif
|
||||
#if FF_HAVE_GIO
|
||||
"gio\n"
|
||||
#endif
|
||||
#if FF_HAVE_DCONF
|
||||
"dconf\n"
|
||||
#endif
|
||||
#if FF_HAVE_DBUS
|
||||
"dbus\n"
|
||||
#endif
|
||||
#if FF_HAVE_IMAGEMAGICK7
|
||||
"imagemagick7\n"
|
||||
#endif
|
||||
#if FF_HAVE_IMAGEMAGICK6
|
||||
"imagemagick6\n"
|
||||
#endif
|
||||
#if FF_HAVE_CHAFA
|
||||
"chafa\n"
|
||||
#endif
|
||||
#if FF_HAVE_ZLIB
|
||||
"zlib\n"
|
||||
#endif
|
||||
#if FF_HAVE_SQLITE3
|
||||
"sqlite3\n"
|
||||
#endif
|
||||
#if FF_HAVE_RPM
|
||||
"rpm\n"
|
||||
#endif
|
||||
#if FF_HAVE_EGL
|
||||
"egl\n"
|
||||
#endif
|
||||
#if FF_HAVE_GLX
|
||||
"glx\n"
|
||||
#endif
|
||||
#if FF_HAVE_OPENCL
|
||||
"opencl\n"
|
||||
#endif
|
||||
#if FF_HAVE_FREETYPE
|
||||
"freetype\n"
|
||||
#endif
|
||||
#if FF_HAVE_PULSE
|
||||
"libpulse\n"
|
||||
#endif
|
||||
#if FF_HAVE_DDCUTIL
|
||||
"libddcutil\n"
|
||||
#endif
|
||||
#if FF_HAVE_ELF || __sun || (__FreeBSD__ && !__DragonFly__) || __OpenBSD__ || __NetBSD__
|
||||
"libelf\n"
|
||||
#endif
|
||||
#if FF_HAVE_LIBZFS
|
||||
"libzfs\n"
|
||||
#endif
|
||||
#if FF_USE_SYSTEM_YYJSON
|
||||
"System yyjson\n"
|
||||
#endif
|
||||
#if FF_HAVE_LINUX_VIDEODEV2
|
||||
"linux/videodev2\n"
|
||||
#endif
|
||||
#if FF_HAVE_LINUX_WIRELESS
|
||||
"linux/wireless\n"
|
||||
#endif
|
||||
#if FF_HAVE_EMBEDDED_PCIIDS
|
||||
"Embedded pciids\n"
|
||||
#endif
|
||||
#if FF_WIN81_COMPAT
|
||||
"Windows 8.1 Compatibility\n"
|
||||
#endif
|
||||
#if FF_APPLE_MEMSIZE_USABLE
|
||||
"Apple memsize_usable\n"
|
||||
#endif
|
||||
""
|
||||
, stdout);
|
||||
#if FF_HAVE_THREADS
|
||||
"threads\n"
|
||||
#endif
|
||||
#if FF_HAVE_VULKAN
|
||||
"vulkan\n"
|
||||
#endif
|
||||
#if FF_HAVE_WAYLAND
|
||||
"wayland\n"
|
||||
#endif
|
||||
#if FF_HAVE_XCB_RANDR
|
||||
"xcb-randr\n"
|
||||
#endif
|
||||
#if FF_HAVE_XRANDR
|
||||
"xrandr\n"
|
||||
#endif
|
||||
#if FF_HAVE_DRM
|
||||
"drm\n"
|
||||
#endif
|
||||
#if FF_HAVE_DRM_AMDGPU
|
||||
"drm_amdgpu\n"
|
||||
#endif
|
||||
#if FF_HAVE_GIO
|
||||
"gio\n"
|
||||
#endif
|
||||
#if FF_HAVE_DCONF
|
||||
"dconf\n"
|
||||
#endif
|
||||
#if FF_HAVE_EET
|
||||
"eet\n"
|
||||
#endif
|
||||
#if FF_HAVE_DBUS
|
||||
"dbus\n"
|
||||
#endif
|
||||
#if FF_HAVE_IMAGEMAGICK7
|
||||
"imagemagick7\n"
|
||||
#endif
|
||||
#if FF_HAVE_IMAGEMAGICK6
|
||||
"imagemagick6\n"
|
||||
#endif
|
||||
#if FF_HAVE_CHAFA
|
||||
"chafa\n"
|
||||
#endif
|
||||
#if FF_HAVE_ZLIB
|
||||
"zlib\n"
|
||||
#endif
|
||||
#if FF_HAVE_SQLITE3
|
||||
"sqlite3\n"
|
||||
#endif
|
||||
#if FF_HAVE_RPM
|
||||
"rpm\n"
|
||||
#endif
|
||||
#if FF_HAVE_EGL
|
||||
"egl\n"
|
||||
#endif
|
||||
#if FF_HAVE_GLX
|
||||
"glx\n"
|
||||
#endif
|
||||
#if FF_HAVE_OPENCL
|
||||
"opencl\n"
|
||||
#endif
|
||||
#if FF_HAVE_FREETYPE
|
||||
"freetype\n"
|
||||
#endif
|
||||
#if FF_HAVE_PULSE
|
||||
"libpulse\n"
|
||||
#endif
|
||||
#if FF_HAVE_DDCUTIL
|
||||
"libddcutil\n"
|
||||
#endif
|
||||
#if FF_HAVE_ELF || __sun || (__FreeBSD__ && !__DragonFly__) || __OpenBSD__ || __NetBSD__
|
||||
"libelf\n"
|
||||
#endif
|
||||
#if FF_HAVE_LIBZFS
|
||||
"libzfs\n"
|
||||
#endif
|
||||
#if FF_USE_SYSTEM_YYJSON
|
||||
"System yyjson\n"
|
||||
#endif
|
||||
#if FF_HAVE_LINUX_VIDEODEV2
|
||||
"linux/videodev2\n"
|
||||
#endif
|
||||
#if FF_HAVE_EMBEDDED_PCIIDS
|
||||
"Embedded pciids\n"
|
||||
#endif
|
||||
#if FF_HAVE_WINRT
|
||||
"WinRT headers\n"
|
||||
#endif
|
||||
#if FF_WIN81_COMPAT
|
||||
"Windows 8.1 Compatibility\n"
|
||||
#endif
|
||||
#if FF_APPLE_MEMSIZE_USABLE
|
||||
"Apple memsize_usable\n"
|
||||
#endif
|
||||
"",
|
||||
stdout);
|
||||
}
|
||||
|
||||
+107
-116
@@ -8,9 +8,9 @@
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#ifndef __APPLE__
|
||||
#include <poll.h>
|
||||
#include <poll.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#if FF_HAVE_WORDEXP
|
||||
@@ -19,170 +19,166 @@
|
||||
#include <glob.h>
|
||||
#endif
|
||||
|
||||
static void createSubfolders(const char* fileName)
|
||||
{
|
||||
static void createSubfolders(const char* fileName) {
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
||||
|
||||
const char *token = NULL;
|
||||
while((token = strchr(fileName, '/')) != NULL)
|
||||
{
|
||||
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
|
||||
const char* token = NULL;
|
||||
while ((token = strchr(fileName, '/')) != NULL) {
|
||||
ffStrbufAppendNS(&path, (uint32_t) (token - fileName + 1), fileName);
|
||||
mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH);
|
||||
fileName = token + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
|
||||
{
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data) {
|
||||
int openFlagsModes = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
|
||||
mode_t openFlagsRights = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
int FF_AUTO_CLOSE_FD fd = open(fileName, openFlagsModes, openFlagsRights);
|
||||
if(fd == -1)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
createSubfolders(fileName);
|
||||
fd = open(fileName, openFlagsModes, openFlagsRights);
|
||||
if(fd == -1)
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return write(fd, data, dataSize) > 0;
|
||||
}
|
||||
|
||||
static inline void readWithLength(int fd, FFstrbuf* buffer, uint32_t length)
|
||||
{
|
||||
static inline void readWithLength(int fd, FFstrbuf* buffer, uint32_t length) {
|
||||
ffStrbufEnsureFixedLengthFree(buffer, length);
|
||||
ssize_t bytesRead = 0;
|
||||
while(
|
||||
length > 0 && (bytesRead = read(fd, buffer->chars + buffer->length, length)) > 0
|
||||
) {
|
||||
while (
|
||||
length > 0 && (bytesRead = read(fd, buffer->chars + buffer->length, length)) > 0) {
|
||||
buffer->length += (uint32_t) bytesRead;
|
||||
length -= (uint32_t) bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void readUntilEOF(int fd, FFstrbuf* buffer)
|
||||
{
|
||||
static inline void readUntilEOF(int fd, FFstrbuf* buffer) {
|
||||
ffStrbufEnsureFree(buffer, 31);
|
||||
uint32_t available = ffStrbufGetFree(buffer);
|
||||
ssize_t bytesRead = 0;
|
||||
while(
|
||||
(bytesRead = read(fd, buffer->chars + buffer->length, available)) > 0
|
||||
) {
|
||||
while (
|
||||
(bytesRead = read(fd, buffer->chars + buffer->length, available)) > 0) {
|
||||
buffer->length += (uint32_t) bytesRead;
|
||||
if((uint32_t) bytesRead == available)
|
||||
if ((uint32_t) bytesRead == available) {
|
||||
ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte.
|
||||
}
|
||||
available = ffStrbufGetFree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
|
||||
{
|
||||
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer) {
|
||||
struct stat fileInfo;
|
||||
if(fstat(fd, &fileInfo) != 0)
|
||||
if (fstat(fd, &fileInfo) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileInfo.st_size > 0)
|
||||
readWithLength(fd, buffer, (uint32_t)fileInfo.st_size);
|
||||
else
|
||||
if (fileInfo.st_size > 0) {
|
||||
readWithLength(fd, buffer, (uint32_t) fileInfo.st_size);
|
||||
} else {
|
||||
readUntilEOF(fd, buffer);
|
||||
}
|
||||
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
|
||||
return buffer->length > 0;
|
||||
}
|
||||
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out)
|
||||
{
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out) {
|
||||
bool result = false;
|
||||
|
||||
#if FF_HAVE_WORDEXP
|
||||
#if FF_HAVE_WORDEXP
|
||||
|
||||
wordexp_t exp;
|
||||
if (wordexp(in, &exp, 0) != 0) // WARN: 0 = no safety flags; command substitution allowed
|
||||
if (wordexp(in, &exp, 0) != 0) { // WARN: 0 = no safety flags; command substitution allowed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (exp.we_wordc >= 1)
|
||||
{
|
||||
if (exp.we_wordc >= 1) {
|
||||
result = true;
|
||||
ffStrbufSetS(out, exp.we_wordv[exp.we_wordc > 1 ? ffTimeGetNow() % exp.we_wordc : 0]);
|
||||
}
|
||||
|
||||
wordfree(&exp);
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
glob_t gb;
|
||||
if (glob(in, GLOB_NOSORT
|
||||
#ifdef GLOB_TILDE
|
||||
| GLOB_TILDE
|
||||
#endif
|
||||
#ifdef GLOB_BRACE
|
||||
| GLOB_BRACE
|
||||
#endif
|
||||
, NULL, &gb) != 0)
|
||||
#ifdef GLOB_TILDE
|
||||
| GLOB_TILDE
|
||||
#endif
|
||||
#ifdef GLOB_BRACE
|
||||
| GLOB_BRACE
|
||||
#endif
|
||||
,
|
||||
NULL,
|
||||
&gb) != 0)
|
||||
return false;
|
||||
|
||||
if (gb.gl_pathc >= 1)
|
||||
{
|
||||
if (gb.gl_pathc >= 1) {
|
||||
result = true;
|
||||
ffStrbufSetS(out, gb.gl_pathv[gb.gl_pathc > 1 ? ffTimeGetNow() % (unsigned) gb.gl_pathc : 0]);
|
||||
}
|
||||
|
||||
globfree(&gb);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ftty = -1;
|
||||
static struct termios oldTerm;
|
||||
void restoreTerm(void)
|
||||
{
|
||||
void restoreTerm(void) {
|
||||
tcsetattr(ftty, TCSAFLUSH, &oldTerm);
|
||||
}
|
||||
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...)
|
||||
{
|
||||
if (ftty < 0)
|
||||
{
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
||||
if (ftty < 0) {
|
||||
ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||
if (ftty < 0)
|
||||
if (ftty < 0) {
|
||||
return "open(\"/dev/tty\", O_RDWR | O_NOCTTY | O_CLOEXEC) failed";
|
||||
}
|
||||
|
||||
if(tcgetattr(ftty, &oldTerm) == -1)
|
||||
if (tcgetattr(ftty, &oldTerm) == -1) {
|
||||
return "tcgetattr(STDIN_FILENO, &oldTerm) failed";
|
||||
}
|
||||
|
||||
struct termios newTerm = oldTerm;
|
||||
newTerm.c_lflag &= (tcflag_t) ~(ICANON | ECHO);
|
||||
if(tcsetattr(ftty, TCSAFLUSH, &newTerm) == -1)
|
||||
if (tcsetattr(ftty, TCSAFLUSH, &newTerm) == -1) {
|
||||
return "tcsetattr(STDIN_FILENO, TCSAFLUSH, &newTerm)";
|
||||
}
|
||||
atexit(restoreTerm);
|
||||
}
|
||||
|
||||
ffWriteFDData(ftty, strlen(request), request);
|
||||
|
||||
//Give the terminal some time to respond
|
||||
#ifndef __APPLE__
|
||||
if(poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0)
|
||||
// Give the terminal some time to respond
|
||||
#ifndef __APPLE__
|
||||
if (poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0) {
|
||||
return "poll(/dev/tty) timeout or failed";
|
||||
#else
|
||||
}
|
||||
#else
|
||||
{
|
||||
// On macOS, poll(/dev/tty) always returns immediately
|
||||
// See also https://nathancraddock.com/blog/macos-dev-tty-polling/
|
||||
fd_set rd;
|
||||
FD_ZERO(&rd);
|
||||
FD_SET(ftty, &rd);
|
||||
if(select(ftty + 1, &rd, NULL, NULL, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 }) <= 0)
|
||||
if (select(ftty + 1, &rd, NULL, NULL, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 }) <= 0) {
|
||||
return "select(/dev/tty) timeout or failed";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
char buffer[1024];
|
||||
size_t bytesRead = 0;
|
||||
@@ -190,12 +186,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
ssize_t nRead = read(ftty, buffer + bytesRead, sizeof(buffer) - bytesRead - 1);
|
||||
|
||||
if (nRead <= 0)
|
||||
{
|
||||
if (nRead <= 0) {
|
||||
va_end(args);
|
||||
return "read(STDIN_FILENO, buffer, sizeof(buffer) - 1) failed";
|
||||
}
|
||||
@@ -208,13 +202,13 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
int ret = vsscanf(buffer, format, cargs);
|
||||
va_end(cargs);
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
if (ret <= 0) {
|
||||
va_end(args);
|
||||
return "vsscanf(buffer, format, args) failed";
|
||||
}
|
||||
if (ret >= nParams)
|
||||
if (ret >= nParams) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
@@ -222,22 +216,22 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffSuppressIO(bool suppress)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (instance.config.display.debugMode)
|
||||
bool ffSuppressIO(bool suppress) {
|
||||
#ifndef NDEBUG
|
||||
if (instance.config.display.debugMode) {
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool init = false;
|
||||
static int origOut = -1;
|
||||
static int origErr = -1;
|
||||
static int nullFile = -1;
|
||||
|
||||
if(!init)
|
||||
{
|
||||
if(!suppress)
|
||||
if (!init) {
|
||||
if (!suppress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
origOut = dup(STDOUT_FILENO);
|
||||
origErr = dup(STDERR_FILENO);
|
||||
@@ -245,8 +239,9 @@ bool ffSuppressIO(bool suppress)
|
||||
init = true;
|
||||
}
|
||||
|
||||
if(nullFile == -1)
|
||||
if (nullFile == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
@@ -256,47 +251,49 @@ bool ffSuppressIO(bool suppress)
|
||||
return true;
|
||||
}
|
||||
|
||||
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD int dfd = open(folder->chars, O_RDONLY | O_CLOEXEC);
|
||||
if (dfd < 0)
|
||||
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty) {
|
||||
int dfd = open(folder->chars, O_RDONLY | O_CLOEXEC | O_DIRECTORY); // Ownership of dfd will be transformed to dir
|
||||
if (dfd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DIR* dir = fdopendir(dfd);
|
||||
if(dir == NULL)
|
||||
return;
|
||||
FF_AUTO_CLOSE_DIR DIR* dir = fdopendir(dfd);
|
||||
if (dir == NULL) {
|
||||
close(dfd);
|
||||
return; // Should not happen
|
||||
}
|
||||
|
||||
uint32_t folderLength = folder->length;
|
||||
|
||||
if(pretty && folderName != NULL)
|
||||
{
|
||||
for(uint8_t i = 0; i < indentation - 1; i++)
|
||||
if (pretty && folderName != NULL) {
|
||||
for (uint8_t i = 0; i < indentation - 1; i++) {
|
||||
fputs(" | ", stdout);
|
||||
}
|
||||
printf("%s/\n", folderName);
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
|
||||
while((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if(entry->d_name[0] == '.') // skip hidden files
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] == '.') { // skip hidden files
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isDir = false;
|
||||
#if !defined(__sun) && !defined(__HAIKU__)
|
||||
if(entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK)
|
||||
if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK) {
|
||||
isDir = entry->d_type == DT_DIR;
|
||||
else
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct stat stbuf;
|
||||
if (fstatat(dfd, entry->d_name, &stbuf, 0) < 0)
|
||||
if (fstatat(dfd, entry->d_name, &stbuf, 0) < 0) {
|
||||
isDir = false;
|
||||
else
|
||||
} else {
|
||||
isDir = S_ISDIR(stbuf.st_mode);
|
||||
}
|
||||
}
|
||||
if (isDir)
|
||||
{
|
||||
if (isDir) {
|
||||
ffStrbufAppendS(folder, entry->d_name);
|
||||
ffStrbufAppendC(folder, '/');
|
||||
listFilesRecursively(baseLength, folder, (uint8_t) (indentation + 1), entry->d_name, pretty);
|
||||
@@ -304,39 +301,33 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pretty)
|
||||
{
|
||||
for(uint8_t i = 0; i < indentation; i++)
|
||||
if (pretty) {
|
||||
for (uint8_t i = 0; i < indentation; i++) {
|
||||
fputs(" | ", stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
fputs(folder->chars + baseLength, stdout);
|
||||
}
|
||||
|
||||
puts(entry->d_name);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void ffListFilesRecursively(const char* path, bool pretty)
|
||||
{
|
||||
void ffListFilesRecursively(const char* path, bool pretty) {
|
||||
FF_STRBUF_AUTO_DESTROY folder = ffStrbufCreateS(path);
|
||||
ffStrbufEnsureEndsWithC(&folder, '/');
|
||||
listFilesRecursively(folder.length, &folder, 0, NULL, pretty);
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void)
|
||||
{
|
||||
FFNativeFD ffGetNullFD(void) {
|
||||
static FFNativeFD hNullFile = -1;
|
||||
if (hNullFile != -1)
|
||||
if (hNullFile != -1) {
|
||||
return hNullFile;
|
||||
}
|
||||
hNullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
bool ffRemoveFile(const char* fileName)
|
||||
{
|
||||
bool ffRemoveFile(const char* fileName) {
|
||||
return unlink(fileName) == 0;
|
||||
}
|
||||
|
||||
+145
-150
@@ -6,15 +6,13 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static bool createSubfolders(wchar_t* fileName)
|
||||
{
|
||||
static bool createSubfolders(wchar_t* fileName) {
|
||||
HANDLE hRoot = ffGetPeb()->ProcessParameters->CurrentDirectory.Handle;
|
||||
bool closeRoot = false;
|
||||
wchar_t* ptr = fileName;
|
||||
|
||||
// Absolute drive path: C:\...
|
||||
if (ffCharIsEnglishAlphabet((char)ptr[0]) && ptr[1] == L':' && ptr[2] == L'\\')
|
||||
{
|
||||
if (ffCharIsEnglishAlphabet((char) ptr[0]) && ptr[1] == L':' && ptr[2] == L'\\') {
|
||||
wchar_t saved = ptr[3];
|
||||
ptr[3] = L'\0';
|
||||
|
||||
@@ -25,26 +23,27 @@ static bool createSubfolders(wchar_t* fileName)
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL
|
||||
);
|
||||
NULL);
|
||||
|
||||
ptr[3] = saved;
|
||||
if (hRoot == INVALID_HANDLE_VALUE)
|
||||
if (hRoot == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closeRoot = true;
|
||||
ptr += 3; // skip "C:\"
|
||||
}
|
||||
// UNC path: \\server\share\...
|
||||
else if (ptr[0] == L'\\' && ptr[1] == L'\\')
|
||||
{
|
||||
else if (ptr[0] == L'\\' && ptr[1] == L'\\') {
|
||||
wchar_t* serverEnd = wcschr(ptr + 2, L'\\');
|
||||
if (serverEnd == NULL)
|
||||
if (serverEnd == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t* shareEnd = wcschr(serverEnd + 1, L'\\');
|
||||
if (shareEnd == NULL)
|
||||
if (shareEnd == NULL) {
|
||||
return true; // no parent subfolder exists before file name
|
||||
}
|
||||
|
||||
wchar_t saved = *shareEnd;
|
||||
*shareEnd = L'\0';
|
||||
@@ -56,19 +55,18 @@ static bool createSubfolders(wchar_t* fileName)
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL
|
||||
);
|
||||
NULL);
|
||||
|
||||
*shareEnd = saved;
|
||||
if (hRoot == INVALID_HANDLE_VALUE)
|
||||
if (hRoot == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closeRoot = true;
|
||||
ptr = shareEnd + 1; // first component under share
|
||||
}
|
||||
// Rooted path on current drive: \foo\bar
|
||||
else if (ptr[0] == L'\\')
|
||||
{
|
||||
else if (ptr[0] == L'\\') {
|
||||
UNICODE_STRING* dosPath = &ffGetPeb()->ProcessParameters->CurrentDirectory.DosPath;
|
||||
wchar_t driveRoot[] = { dosPath->Buffer[0], L':', L'\\', L'\0' };
|
||||
hRoot = CreateFileW(
|
||||
@@ -78,23 +76,22 @@ static bool createSubfolders(wchar_t* fileName)
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL
|
||||
);
|
||||
if (hRoot == INVALID_HANDLE_VALUE)
|
||||
NULL);
|
||||
if (hRoot == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
closeRoot = true;
|
||||
ptr++; // skip leading '\'
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
wchar_t* token = wcschr(ptr, L'\\');
|
||||
if (token == NULL)
|
||||
if (token == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip empty path segments caused by duplicated '\'
|
||||
if (token == ptr)
|
||||
{
|
||||
if (token == ptr) {
|
||||
ptr = token + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -110,8 +107,8 @@ static bool createSubfolders(wchar_t* fileName)
|
||||
.RootDirectory = hRoot,
|
||||
.ObjectName = &(UNICODE_STRING) {
|
||||
.Buffer = ptr,
|
||||
.Length = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)),
|
||||
.MaximumLength = (USHORT)((USHORT)(token - ptr) * sizeof(wchar_t)),
|
||||
.Length = (USHORT) ((USHORT) (token - ptr) * sizeof(wchar_t)),
|
||||
.MaximumLength = (USHORT) ((USHORT) (token - ptr) * sizeof(wchar_t)),
|
||||
},
|
||||
.Attributes = OBJ_CASE_INSENSITIVE,
|
||||
},
|
||||
@@ -122,155 +119,153 @@ static bool createSubfolders(wchar_t* fileName)
|
||||
FILE_OPEN_IF,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
||||
NULL,
|
||||
0
|
||||
);
|
||||
0);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
|
||||
if (!NT_SUCCESS(status)) {
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE) {
|
||||
NtClose(hRoot);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE) {
|
||||
NtClose(hRoot);
|
||||
}
|
||||
hRoot = hNew;
|
||||
closeRoot = true;
|
||||
|
||||
ptr = token + 1;
|
||||
}
|
||||
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE)
|
||||
if (closeRoot && hRoot != INVALID_HANDLE_VALUE) {
|
||||
NtClose(hRoot);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
|
||||
{
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data) {
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
ULONG len = 0;
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG)strlen(fileName) + 1)))
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG) strlen(fileName) + 1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < len / sizeof(wchar_t); ++i)
|
||||
{
|
||||
if (fileNameW[i] == L'/')
|
||||
for (ULONG i = 0; i < len / sizeof(wchar_t); ++i) {
|
||||
if (fileNameW[i] == L'/') {
|
||||
fileNameW[i] = L'\\';
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetLastError() == ERROR_PATH_NOT_FOUND)
|
||||
{
|
||||
if (!createSubfolders(fileNameW))
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
if (GetLastError() == ERROR_PATH_NOT_FOUND) {
|
||||
if (!createSubfolders(fileNameW)) {
|
||||
return false;
|
||||
}
|
||||
handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD written;
|
||||
return !!WriteFile(handle, data, (DWORD)dataSize, &written, NULL);
|
||||
return !!WriteFile(handle, data, (DWORD) dataSize, &written, NULL);
|
||||
}
|
||||
|
||||
static inline void readWithLength(HANDLE handle, FFstrbuf* buffer, uint32_t length)
|
||||
{
|
||||
static inline void readWithLength(HANDLE handle, FFstrbuf* buffer, uint32_t length) {
|
||||
ffStrbufEnsureFree(buffer, length);
|
||||
DWORD bytesRead = 0;
|
||||
while(
|
||||
while (
|
||||
length > 0 &&
|
||||
ReadFile(handle, buffer->chars + buffer->length, length, &bytesRead, NULL) != FALSE &&
|
||||
bytesRead > 0
|
||||
) {
|
||||
bytesRead > 0) {
|
||||
buffer->length += (uint32_t) bytesRead;
|
||||
length -= (uint32_t) bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void readUntilEOF(HANDLE handle, FFstrbuf* buffer)
|
||||
{
|
||||
static inline void readUntilEOF(HANDLE handle, FFstrbuf* buffer) {
|
||||
ffStrbufEnsureFree(buffer, 31);
|
||||
uint32_t available = ffStrbufGetFree(buffer);
|
||||
DWORD bytesRead = 0;
|
||||
while(
|
||||
while (
|
||||
ReadFile(handle, buffer->chars + buffer->length, available, &bytesRead, NULL) != FALSE &&
|
||||
bytesRead > 0
|
||||
) {
|
||||
bytesRead > 0) {
|
||||
buffer->length += (uint32_t) bytesRead;
|
||||
if((uint32_t) bytesRead == available)
|
||||
if ((uint32_t) bytesRead == available) {
|
||||
ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte.
|
||||
}
|
||||
available = ffStrbufGetFree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer)
|
||||
{
|
||||
bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer) {
|
||||
FILE_STANDARD_INFORMATION fileInfo;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
if(!NT_SUCCESS(NtQueryInformationFile(handle, &iosb, &fileInfo, sizeof(fileInfo), FileStandardInformation)))
|
||||
if (!NT_SUCCESS(NtQueryInformationFile(handle, &iosb, &fileInfo, sizeof(fileInfo), FileStandardInformation))) {
|
||||
fileInfo.EndOfFile.QuadPart = 0;
|
||||
}
|
||||
|
||||
if (fileInfo.EndOfFile.QuadPart > 0)
|
||||
readWithLength(handle, buffer, (uint32_t)fileInfo.EndOfFile.QuadPart);
|
||||
else
|
||||
if (fileInfo.EndOfFile.QuadPart > 0) {
|
||||
readWithLength(handle, buffer, (uint32_t) fileInfo.EndOfFile.QuadPart);
|
||||
} else {
|
||||
readUntilEOF(handle, buffer);
|
||||
}
|
||||
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
|
||||
return buffer->length > 0;
|
||||
}
|
||||
|
||||
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory)
|
||||
{
|
||||
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory) {
|
||||
assert(fileNameLen <= 0x7FFF);
|
||||
|
||||
HANDLE hFile;
|
||||
IO_STATUS_BLOCK iosb = {};
|
||||
if(!NT_SUCCESS(NtOpenFile(&hFile,
|
||||
(directory ? FILE_LIST_DIRECTORY | FILE_TRAVERSE : FILE_READ_DATA | FILE_READ_EA) | FILE_READ_ATTRIBUTES | SYNCHRONIZE, &(OBJECT_ATTRIBUTES) {
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = dfd,
|
||||
.ObjectName = &(UNICODE_STRING) {
|
||||
.Buffer = (PWSTR) fileName,
|
||||
.Length = fileNameLen * (USHORT) sizeof(wchar_t),
|
||||
.MaximumLength = (fileNameLen + 1) * (USHORT) sizeof(wchar_t),
|
||||
if (!NT_SUCCESS(NtOpenFile(&hFile,
|
||||
(directory ? FILE_LIST_DIRECTORY | FILE_TRAVERSE : FILE_READ_DATA | FILE_READ_EA) | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||||
&(OBJECT_ATTRIBUTES) {
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = dfd,
|
||||
.ObjectName = &(UNICODE_STRING) {
|
||||
.Buffer = (PWSTR) fileName,
|
||||
.Length = fileNameLen * (USHORT) sizeof(wchar_t),
|
||||
.MaximumLength = (fileNameLen + 1) * (USHORT) sizeof(wchar_t),
|
||||
},
|
||||
.Attributes = OBJ_CASE_INSENSITIVE,
|
||||
},
|
||||
.Attributes = OBJ_CASE_INSENSITIVE,
|
||||
},
|
||||
&iosb,
|
||||
FILE_SHARE_READ | (directory ? FILE_SHARE_WRITE | FILE_SHARE_DELETE : 0),
|
||||
FILE_SYNCHRONOUS_IO_NONALERT | (directory ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE)
|
||||
)))
|
||||
&iosb,
|
||||
FILE_SHARE_READ | (directory ? FILE_SHARE_WRITE | FILE_SHARE_DELETE : 0),
|
||||
FILE_SYNCHRONOUS_IO_NONALERT | (directory ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE)))) {
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
return hFile;
|
||||
}
|
||||
|
||||
HANDLE openat(HANDLE dfd, const char* fileName, int oflag)
|
||||
{
|
||||
HANDLE openat(HANDLE dfd, const char* fileName, int oflag) {
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
ULONG len;
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG)strlen(fileName) + 1)))
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &len, fileName, (ULONG) strlen(fileName) + 1))) {
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
// Implies `fileNameW[len] = L'\0';` and `len` includes the null terminator
|
||||
len /= sizeof(wchar_t); // convert from bytes to characters
|
||||
|
||||
for (uint32_t i = 0; i < len - 1; ++i)
|
||||
{
|
||||
if (fileNameW[i] == L'/')
|
||||
for (uint32_t i = 0; i < len - 1; ++i) {
|
||||
if (fileNameW[i] == L'/') {
|
||||
fileNameW[i] = L'\\';
|
||||
}
|
||||
}
|
||||
|
||||
return openatW(dfd, fileNameW, (uint16_t)(len - 1), !!(oflag & O_DIRECTORY));
|
||||
return openatW(dfd, fileNameW, (uint16_t) (len - 1), !!(oflag & O_DIRECTORY));
|
||||
}
|
||||
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out)
|
||||
{
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out) {
|
||||
if (in[0] == '~') {
|
||||
if ((in[1] == '/' || in[1] == '\\' || in[1] == '\0') && !ffStrContainsC(in, '%')) {
|
||||
ffStrbufSet(out, &instance.state.platform.homeDir);
|
||||
@@ -281,24 +276,26 @@ bool ffPathExpandEnv(const char* in, FFstrbuf* out)
|
||||
|
||||
wchar_t pathInW[MAX_PATH], pathOutW[MAX_PATH];
|
||||
ULONG len = (ULONG) strlen(in);
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathInW, (ULONG) sizeof(pathInW), &len, in, len)))
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathInW, (ULONG) sizeof(pathInW), &len, in, len))) {
|
||||
return false;
|
||||
}
|
||||
len /= sizeof(wchar_t); // convert from bytes to characters
|
||||
|
||||
size_t outLen; // in characters, including null terminator
|
||||
if (!NT_SUCCESS(RtlExpandEnvironmentStrings(NULL, pathInW, len, pathOutW, ARRAY_SIZE(pathOutW), &outLen)))
|
||||
SIZE_T outLen; // in characters, including null terminator
|
||||
if (!NT_SUCCESS(RtlExpandEnvironmentStrings(NULL, pathInW, len, pathOutW, ARRAY_SIZE(pathOutW), &outLen))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ffStrbufSetNWS(out, (uint32_t) outLen - 1, pathOutW);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffSuppressIO(bool suppress)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (instance.config.display.debugMode)
|
||||
bool ffSuppressIO(bool suppress) {
|
||||
#ifndef NDEBUG
|
||||
if (instance.config.display.debugMode) {
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool init = false;
|
||||
static HANDLE hOrigOut = INVALID_HANDLE_VALUE;
|
||||
@@ -308,10 +305,10 @@ bool ffSuppressIO(bool suppress)
|
||||
static int fOrigErr = -1;
|
||||
static int fNullFile = -1;
|
||||
|
||||
if (!init)
|
||||
{
|
||||
if(!suppress)
|
||||
if (!init) {
|
||||
if (!suppress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hOrigOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
hOrigErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
@@ -321,8 +318,9 @@ bool ffSuppressIO(bool suppress)
|
||||
|
||||
init = true;
|
||||
}
|
||||
if (hNullFile == INVALID_HANDLE_VALUE || fNullFile == -1)
|
||||
if (hNullFile == INVALID_HANDLE_VALUE || fNullFile == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
@@ -335,14 +333,13 @@ bool ffSuppressIO(bool suppress)
|
||||
return true;
|
||||
}
|
||||
|
||||
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty)
|
||||
{
|
||||
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty) {
|
||||
uint32_t folderLength = folder->length;
|
||||
|
||||
if(pretty && folderName != NULL)
|
||||
{
|
||||
for(uint8_t i = 0; i < indentation - 1; i++)
|
||||
if (pretty && folderName != NULL) {
|
||||
for (uint8_t i = 0; i < indentation - 1; i++) {
|
||||
fputs(" | ", stdout);
|
||||
}
|
||||
printf("%s/\n", folderName);
|
||||
}
|
||||
|
||||
@@ -350,15 +347,15 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
WIN32_FIND_DATAA entry;
|
||||
HANDLE hFind = FindFirstFileA(folder->chars, &entry);
|
||||
ffStrbufTrimRight(folder, '*');
|
||||
if(hFind == INVALID_HANDLE_VALUE)
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
if(ffStrEquals(entry.cFileName, ".") || ffStrEquals(entry.cFileName, ".."))
|
||||
do {
|
||||
if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (ffStrEquals(entry.cFileName, ".") || ffStrEquals(entry.cFileName, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(folder, folderLength);
|
||||
ffStrbufAppendS(folder, entry.cFileName);
|
||||
@@ -368,13 +365,11 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pretty)
|
||||
{
|
||||
for(uint8_t i = 0; i < indentation; i++)
|
||||
if (pretty) {
|
||||
for (uint8_t i = 0; i < indentation; i++) {
|
||||
fputs(" | ", stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
fputs(folder->chars + baseLength, stdout);
|
||||
}
|
||||
|
||||
@@ -383,22 +378,21 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
void ffListFilesRecursively(const char* path, bool pretty)
|
||||
{
|
||||
void ffListFilesRecursively(const char* path, bool pretty) {
|
||||
FF_STRBUF_AUTO_DESTROY folder = ffStrbufCreateS(path);
|
||||
ffStrbufEnsureEndsWithC(&folder, '/');
|
||||
listFilesRecursively(folder.length, &folder, 0, NULL, pretty);
|
||||
}
|
||||
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...)
|
||||
{
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
||||
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
FF_AUTO_CLOSE_FD HANDLE hConin = INVALID_HANDLE_VALUE;
|
||||
DWORD inputMode;
|
||||
if (!GetConsoleMode(hInput, &inputMode))
|
||||
{
|
||||
DWORD inputMode = 0;
|
||||
bool hasInputMode = !!GetConsoleMode(hInput, &inputMode);
|
||||
if (!hasInputMode) {
|
||||
hConin = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, NULL);
|
||||
hInput = hConin;
|
||||
hasInputMode = !!GetConsoleMode(hInput, &inputMode);
|
||||
}
|
||||
SetConsoleMode(hInput, 0);
|
||||
|
||||
@@ -409,18 +403,15 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
FF_AUTO_CLOSE_FD HANDLE hConout = INVALID_HANDLE_VALUE;
|
||||
DWORD outputMode;
|
||||
if (!GetConsoleMode(hOutput, &outputMode))
|
||||
{
|
||||
if (!GetConsoleMode(hOutput, &outputMode)) {
|
||||
hConout = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, NULL);
|
||||
hOutput = hConout;
|
||||
}
|
||||
WriteFile(hOutput, request, (DWORD) strlen(request), &bytes, NULL);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 }) != STATUS_WAIT_0)
|
||||
{
|
||||
while (true) {
|
||||
if (NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 }) != STATUS_WAIT_0) {
|
||||
SetConsoleMode(hInput, inputMode);
|
||||
return "NtWaitForSingleObject() failed or timeout";
|
||||
}
|
||||
@@ -428,17 +419,18 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
// Ignore all unexpected input events
|
||||
INPUT_RECORD record;
|
||||
DWORD len = 0;
|
||||
if (!PeekConsoleInputW(hInput, &record, 1, &len))
|
||||
if (!PeekConsoleInputW(hInput, &record, 1, &len)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
record.EventType == KEY_EVENT &&
|
||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\r' &&
|
||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\n'
|
||||
)
|
||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\n') {
|
||||
break;
|
||||
else
|
||||
} else {
|
||||
ReadConsoleInputW(hInput, &record, 1, &len);
|
||||
}
|
||||
}
|
||||
|
||||
va_list args;
|
||||
@@ -447,16 +439,18 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
char buffer[1024];
|
||||
uint32_t bytesRead = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
DWORD bytes = 0;
|
||||
if (!ReadFile(hInput, buffer, sizeof(buffer) - 1, &bytes, NULL) || bytes == 0)
|
||||
{
|
||||
if (!ReadFile(hInput, buffer + bytesRead, (DWORD) (sizeof(buffer) - 1 - bytesRead), &bytes, NULL) || bytes == 0) {
|
||||
va_end(args);
|
||||
return "ReadFile() failed";
|
||||
}
|
||||
|
||||
bytesRead += bytes;
|
||||
if (__builtin_expect(bytesRead >= sizeof(buffer) - 1, false)) {
|
||||
va_end(args);
|
||||
return "terminal response buffer overflow";
|
||||
}
|
||||
buffer[bytesRead] = '\0';
|
||||
|
||||
va_list cargs;
|
||||
@@ -464,27 +458,29 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
int ret = vsscanf(buffer, format, cargs);
|
||||
va_end(cargs);
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
if (ret <= 0) {
|
||||
va_end(args);
|
||||
return "vsscanf(buffer, format, args) failed";
|
||||
}
|
||||
if (ret >= nParams)
|
||||
if (ret >= nParams) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetConsoleMode(hInput, inputMode);
|
||||
if (hasInputMode) {
|
||||
SetConsoleMode(hInput, inputMode);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void)
|
||||
{
|
||||
FFNativeFD ffGetNullFD(void) {
|
||||
static FFNativeFD hNullFile = INVALID_HANDLE_VALUE;
|
||||
if (hNullFile != INVALID_HANDLE_VALUE)
|
||||
if (hNullFile != INVALID_HANDLE_VALUE) {
|
||||
return hNullFile;
|
||||
}
|
||||
hNullFile = CreateFileW(
|
||||
L"NUL",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
@@ -492,7 +488,7 @@ FFNativeFD ffGetNullFD(void)
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
&(SECURITY_ATTRIBUTES){
|
||||
&(SECURITY_ATTRIBUTES) {
|
||||
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||
.lpSecurityDescriptor = NULL,
|
||||
.bInheritHandle = TRUE,
|
||||
@@ -500,7 +496,6 @@ FFNativeFD ffGetNullFD(void)
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
bool ffRemoveFile(const char* fileName)
|
||||
{
|
||||
bool ffRemoveFile(const char* fileName) {
|
||||
return DeleteFileA(fileName) != FALSE;
|
||||
}
|
||||
|
||||
+155
-155
@@ -12,135 +12,117 @@
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs)
|
||||
{
|
||||
if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition"))
|
||||
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs) {
|
||||
if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (unsafe_yyjson_equals_str(key, "key"))
|
||||
{
|
||||
if (unsafe_yyjson_equals_str(key, "key")) {
|
||||
ffStrbufSetJsonVal(&moduleArgs->key, val);
|
||||
return true;
|
||||
}
|
||||
else if (unsafe_yyjson_equals_str(key, "format"))
|
||||
{
|
||||
} else if (unsafe_yyjson_equals_str(key, "format")) {
|
||||
ffStrbufSetJsonVal(&moduleArgs->outputFormat, val);
|
||||
return true;
|
||||
}
|
||||
else if (unsafe_yyjson_equals_str(key, "outputColor"))
|
||||
{
|
||||
} else if (unsafe_yyjson_equals_str(key, "outputColor")) {
|
||||
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->outputColor);
|
||||
return true;
|
||||
}
|
||||
else if (unsafe_yyjson_equals_str(key, "keyColor"))
|
||||
{
|
||||
} else if (unsafe_yyjson_equals_str(key, "keyColor")) {
|
||||
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->keyColor);
|
||||
return true;
|
||||
}
|
||||
else if (unsafe_yyjson_equals_str(key, "keyWidth"))
|
||||
{
|
||||
} else if (unsafe_yyjson_equals_str(key, "keyWidth")) {
|
||||
moduleArgs->keyWidth = (uint32_t) yyjson_get_uint(val);
|
||||
return true;
|
||||
}
|
||||
else if (unsafe_yyjson_equals_str(key, "keyIcon"))
|
||||
{
|
||||
} else if (unsafe_yyjson_equals_str(key, "keyIcon")) {
|
||||
ffStrbufSetJsonVal(&moduleArgs->keyIcon, val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs)
|
||||
{
|
||||
if (moduleArgs->key.length > 0)
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs) {
|
||||
if (moduleArgs->key.length > 0) {
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "key", &moduleArgs->key);
|
||||
if (moduleArgs->outputFormat.length > 0)
|
||||
}
|
||||
if (moduleArgs->outputFormat.length > 0) {
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "format", &moduleArgs->outputFormat);
|
||||
if (moduleArgs->outputColor.length > 0)
|
||||
}
|
||||
if (moduleArgs->outputColor.length > 0) {
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "outputColor", &moduleArgs->outputColor);
|
||||
if (moduleArgs->keyColor.length > 0)
|
||||
}
|
||||
if (moduleArgs->keyColor.length > 0) {
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "keyColor", &moduleArgs->keyColor);
|
||||
if (moduleArgs->keyWidth > 0)
|
||||
}
|
||||
if (moduleArgs->keyWidth > 0) {
|
||||
yyjson_mut_obj_add_uint(doc, module, "keyWidth", moduleArgs->keyWidth);
|
||||
if (moduleArgs->keyIcon.length > 0)
|
||||
}
|
||||
if (moduleArgs->keyIcon.length > 0) {
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "keyIcon", &moduleArgs->keyIcon);
|
||||
}
|
||||
}
|
||||
|
||||
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[])
|
||||
{
|
||||
if (yyjson_is_int(val))
|
||||
{
|
||||
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]) {
|
||||
if (yyjson_is_int(val)) {
|
||||
int intVal = yyjson_get_int(val);
|
||||
|
||||
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair)
|
||||
{
|
||||
if (intVal == pPair->value)
|
||||
{
|
||||
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
|
||||
if (intVal == pPair->value) {
|
||||
*result = pPair->value;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return "Invalid enum integer";
|
||||
}
|
||||
else if (yyjson_is_str(val))
|
||||
{
|
||||
} else if (yyjson_is_str(val)) {
|
||||
const char* strVal = yyjson_get_str(val);
|
||||
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair)
|
||||
{
|
||||
if (ffStrEqualsIgnCase(strVal, pPair->key))
|
||||
{
|
||||
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
|
||||
if (ffStrEqualsIgnCase(strVal, pPair->key)) {
|
||||
*result = pPair->value;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return "Invalid enum string";
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return "Invalid enum value type; must be a string or integer";
|
||||
}
|
||||
}
|
||||
|
||||
static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_mut_doc* jsonDoc)
|
||||
{
|
||||
if(!ffCharIsEnglishAlphabet(type[0])) return false;
|
||||
static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_mut_doc* jsonDoc) {
|
||||
if (!ffCharIsEnglishAlphabet(type[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(type[0]) - 'A']; *modules; ++modules)
|
||||
{
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(type[0]) - 'A']; *modules; ++modules) {
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
if (ffStrEqualsIgnCase(type, baseInfo->name))
|
||||
{
|
||||
if (ffStrEqualsIgnCase(type, baseInfo->name)) {
|
||||
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
|
||||
baseInfo->initOptions(optionBuf);
|
||||
if (jsonVal) baseInfo->parseJsonObject(optionBuf, jsonVal);
|
||||
if (jsonVal) {
|
||||
baseInfo->parseJsonObject(optionBuf, jsonVal);
|
||||
}
|
||||
bool succeeded;
|
||||
if (jsonDoc)
|
||||
{
|
||||
if (jsonDoc) {
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(jsonDoc, jsonDoc->root);
|
||||
yyjson_mut_obj_add_str(jsonDoc, module, "type", baseInfo->name);
|
||||
if (baseInfo->generateJsonResult)
|
||||
if (baseInfo->generateJsonResult) {
|
||||
succeeded = baseInfo->generateJsonResult(optionBuf, jsonDoc, module);
|
||||
else
|
||||
{
|
||||
} else {
|
||||
yyjson_mut_obj_add_str(jsonDoc, module, "error", "Unsupported for JSON format");
|
||||
succeeded = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
} else {
|
||||
succeeded = baseInfo->printModule(optionBuf);
|
||||
}
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
return succeeded;
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonDoc)
|
||||
{
|
||||
if (jsonDoc) {
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(jsonDoc, jsonDoc->root);
|
||||
yyjson_mut_obj_add_strcpy(jsonDoc, module, "type", type);
|
||||
yyjson_mut_obj_add_str(jsonDoc, module, "error", "Unknown module type");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
FFModuleArgs moduleArgs;
|
||||
ffOptionInitModuleArg(&moduleArgs, "");
|
||||
ffPrintError(type, 0, &moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown module type");
|
||||
@@ -149,61 +131,66 @@ static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_
|
||||
return false;
|
||||
}
|
||||
|
||||
static void prepareModuleJsonObject(const char* type, yyjson_val* module)
|
||||
{
|
||||
switch (type[0])
|
||||
{
|
||||
case 'b': case 'B': {
|
||||
if (ffStrEqualsIgnCase(type, FF_CPUUSAGE_MODULE_NAME))
|
||||
static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
|
||||
switch (type[0]) {
|
||||
case 'c':
|
||||
case 'C': {
|
||||
if (ffStrEqualsIgnCase(type, FF_CPUUSAGE_MODULE_NAME)) {
|
||||
ffPrepareCPUUsage();
|
||||
break;
|
||||
}
|
||||
case 'c': case 'C': {
|
||||
if (ffStrEqualsIgnCase(type, FF_COMMAND_MODULE_NAME))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyCommandOptions))) FFCommandOptions options;
|
||||
} else if (ffStrEqualsIgnCase(type, FF_COMMAND_MODULE_NAME)) {
|
||||
FF_A_CLEANUP(ffDestroyCommandOptions) FFCommandOptions options;
|
||||
ffInitCommandOptions(&options);
|
||||
if (module) ffCommandModuleInfo.parseJsonObject(&options, module);
|
||||
if (module) {
|
||||
ffCommandModuleInfo.parseJsonObject(&options, module);
|
||||
}
|
||||
ffPrepareCommand(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'd': case 'D': {
|
||||
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
|
||||
case 'd':
|
||||
case 'D': {
|
||||
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME)) {
|
||||
FF_A_CLEANUP(ffDestroyDiskIOOptions) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
if (module) ffDiskIOModuleInfo.parseJsonObject(&options, module);
|
||||
if (module) {
|
||||
ffDiskIOModuleInfo.parseJsonObject(&options, module);
|
||||
}
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'n': case 'N': {
|
||||
if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
|
||||
case 'n':
|
||||
case 'N': {
|
||||
if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME)) {
|
||||
FF_A_CLEANUP(ffDestroyNetIOOptions) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
if (module) ffNetIOModuleInfo.parseJsonObject(&options, module);
|
||||
if (module) {
|
||||
ffNetIOModuleInfo.parseJsonObject(&options, module);
|
||||
}
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': case 'P': {
|
||||
if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
|
||||
case 'p':
|
||||
case 'P': {
|
||||
if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME)) {
|
||||
FF_A_CLEANUP(ffDestroyPublicIpOptions) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
if (module) ffPublicIPModuleInfo.parseJsonObject(&options, module);
|
||||
if (module) {
|
||||
ffPublicIPModuleInfo.parseJsonObject(&options, module);
|
||||
}
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'w': case 'W': {
|
||||
if (ffStrEqualsIgnCase(type, FF_WEATHER_MODULE_NAME))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
|
||||
case 'w':
|
||||
case 'W': {
|
||||
if (ffStrEqualsIgnCase(type, FF_WEATHER_MODULE_NAME)) {
|
||||
FF_A_CLEANUP(ffDestroyWeatherOptions) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
if (module) ffWeatherModuleInfo.parseJsonObject(&options, module);
|
||||
if (module) {
|
||||
ffWeatherModuleInfo.parseJsonObject(&options, module);
|
||||
}
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
break;
|
||||
@@ -211,141 +198,154 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module)
|
||||
}
|
||||
}
|
||||
|
||||
static bool matchesJsonArray(const char* str, yyjson_val* val)
|
||||
{
|
||||
static bool matchesJsonArray(const char* str, yyjson_val* val) {
|
||||
assert(val);
|
||||
|
||||
if (unsafe_yyjson_is_str(val))
|
||||
if (unsafe_yyjson_is_str(val)) {
|
||||
return ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(val));
|
||||
}
|
||||
|
||||
if (!unsafe_yyjson_is_arr(val)) return false;
|
||||
if (!unsafe_yyjson_is_arr(val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t idx, max;
|
||||
yyjson_val* item;
|
||||
yyjson_arr_foreach(val, idx, max, item)
|
||||
{
|
||||
if (yyjson_is_str(item) && ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(item)))
|
||||
yyjson_arr_foreach (val, idx, max, item) {
|
||||
if (yyjson_is_str(item) && ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(item))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char* printJsonConfig(FFdata* data, bool prepare)
|
||||
{
|
||||
static const char* printJsonConfig(FFdata* data, bool prepare) {
|
||||
yyjson_mut_doc* jsonDoc = data->resultDoc;
|
||||
yyjson_val* const root = yyjson_doc_get_root(data->configDoc);
|
||||
assert(root);
|
||||
|
||||
if (!yyjson_is_obj(root))
|
||||
if (!yyjson_is_obj(root)) {
|
||||
return "Invalid JSON config format. Root value must be an object";
|
||||
}
|
||||
|
||||
yyjson_val* modules = yyjson_obj_get(root, "modules");
|
||||
if (!modules) return NULL;
|
||||
if (!yyjson_is_arr(modules)) return "Property 'modules' must be an array of strings or objects";
|
||||
if (!modules) {
|
||||
return NULL;
|
||||
}
|
||||
if (!yyjson_is_arr(modules)) {
|
||||
return "Property 'modules' must be an array of strings or objects";
|
||||
}
|
||||
|
||||
bool succeeded = true;
|
||||
int32_t thres = instance.config.display.stat;
|
||||
yyjson_val* item;
|
||||
size_t idx, max;
|
||||
yyjson_arr_foreach(modules, idx, max, item)
|
||||
{
|
||||
yyjson_arr_foreach (modules, idx, max, item) {
|
||||
double ms = 0;
|
||||
if(!prepare && thres >= 0)
|
||||
if (!prepare && thres >= 0) {
|
||||
ms = ffTimeGetTick();
|
||||
}
|
||||
|
||||
yyjson_val* module = item;
|
||||
const char* type = yyjson_get_str(module);
|
||||
if (type)
|
||||
if (type) {
|
||||
module = NULL;
|
||||
else if (yyjson_is_obj(module))
|
||||
{
|
||||
} else if (yyjson_is_obj(module)) {
|
||||
yyjson_val* conditions = yyjson_obj_get(module, "condition");
|
||||
if (conditions)
|
||||
{
|
||||
if (!yyjson_is_obj(conditions))
|
||||
return "Property 'conditions' must be an object";
|
||||
if (conditions) {
|
||||
if (!yyjson_is_obj(conditions)) {
|
||||
return "Property 'condition' must be an object";
|
||||
}
|
||||
|
||||
yyjson_val* system = yyjson_obj_get(conditions, "system");
|
||||
if (system && !matchesJsonArray(ffVersionResult.sysName, system))
|
||||
if (system && !matchesJsonArray(ffVersionResult.sysName, system)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
system = yyjson_obj_get(conditions, "!system");
|
||||
if (system && matchesJsonArray(ffVersionResult.sysName, system))
|
||||
if (system && matchesJsonArray(ffVersionResult.sysName, system)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
yyjson_val* arch = yyjson_obj_get(conditions, "arch");
|
||||
if (arch && !matchesJsonArray(ffVersionResult.architecture, arch))
|
||||
if (arch && !matchesJsonArray(ffVersionResult.architecture, arch)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
arch = yyjson_obj_get(conditions, "!arch");
|
||||
if (arch && matchesJsonArray(ffVersionResult.architecture, arch))
|
||||
if (arch && matchesJsonArray(ffVersionResult.architecture, arch)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
yyjson_val* previousSucceeded = yyjson_obj_get(conditions, "succeeded");
|
||||
if (previousSucceeded && !unsafe_yyjson_is_null(previousSucceeded))
|
||||
{
|
||||
if (!unsafe_yyjson_is_bool(previousSucceeded))
|
||||
if (previousSucceeded && !unsafe_yyjson_is_null(previousSucceeded)) {
|
||||
if (!unsafe_yyjson_is_bool(previousSucceeded)) {
|
||||
return "Property 'succeeded' in 'condition' must be a boolean";
|
||||
if (succeeded != unsafe_yyjson_get_bool(previousSucceeded))
|
||||
}
|
||||
if (succeeded != unsafe_yyjson_get_bool(previousSucceeded)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = yyjson_get_str(yyjson_obj_get(module, "type"));
|
||||
if (!type) return "module object must contain a \"type\" key ( case sensitive )";
|
||||
if (yyjson_obj_size(module) == 1) // contains only Property type
|
||||
if (!type) {
|
||||
return "module object must contain a \"type\" key ( case sensitive )";
|
||||
}
|
||||
if (yyjson_obj_size(module) == 1) { // contains only Property type
|
||||
module = NULL;
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
return "modules must be an array of strings or objects";
|
||||
}
|
||||
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, type, ':'))
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, type, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(prepare)
|
||||
if (prepare) {
|
||||
prepareModuleJsonObject(type, module);
|
||||
else
|
||||
} else {
|
||||
succeeded = parseModuleJsonObject(type, module, jsonDoc);
|
||||
}
|
||||
|
||||
if(!prepare && thres >= 0)
|
||||
{
|
||||
if (!prepare && thres >= 0) {
|
||||
ms = ffTimeGetTick() - ms;
|
||||
if (jsonDoc)
|
||||
{
|
||||
if (jsonDoc) {
|
||||
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(jsonDoc->root);
|
||||
yyjson_mut_obj_add_real(jsonDoc, moduleJson, "stat", ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
char str[64];
|
||||
int len = snprintf(str, sizeof str, "%.3fms", ms);
|
||||
if (thres > 0)
|
||||
snprintf(str, sizeof str, "\e[%sm%.3fms\e[m", (ms <= thres ? FF_COLOR_FG_GREEN : ms <= 2 * thres ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_RED), ms);
|
||||
if (thres > 0) {
|
||||
snprintf(str, sizeof str, "\e[%sm%.3fms\e[m", (ms <= thres ? FF_COLOR_FG_GREEN : ms <= 2 * thres ? FF_COLOR_FG_YELLOW
|
||||
: FF_COLOR_FG_RED),
|
||||
ms);
|
||||
}
|
||||
printf("\e7\e[1A\e[9999999C\e[%dD%s\e8", len, str); // Save; Up 1; Right 9999999; Left <len>; Print <str>; Load
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (!instance.config.display.noBuffer && !jsonDoc) fflush(stdout);
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
if (!instance.config.display.noBuffer && !jsonDoc) {
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ffPrintJsonConfig(FFdata* data, bool prepare)
|
||||
{
|
||||
void ffPrintJsonConfig(FFdata* data, bool prepare) {
|
||||
yyjson_mut_doc* jsonDoc = data->resultDoc;
|
||||
const char* error = printJsonConfig(data, prepare);
|
||||
if (error)
|
||||
{
|
||||
if (jsonDoc)
|
||||
{
|
||||
if (error) {
|
||||
if (jsonDoc) {
|
||||
yyjson_mut_val* obj = yyjson_mut_obj(jsonDoc);
|
||||
yyjson_mut_obj_add_str(jsonDoc, obj, "error", error);
|
||||
yyjson_mut_doc_set_root(jsonDoc, obj);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
ffPrintError("JsonConfig", 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "%s", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include <IOKit/kext/KextManager.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
bool ffKmodLoaded(const char* modName)
|
||||
{
|
||||
bool ffKmodLoaded(const char* modName) {
|
||||
FF_CFTYPE_AUTO_RELEASE CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, modName, kCFStringEncodingUTF8);
|
||||
FF_CFTYPE_AUTO_RELEASE CFArrayRef identifiers = CFArrayCreate(kCFAllocatorDefault, (const void**) &name, 1, &kCFTypeArrayCallBacks);
|
||||
FF_CFTYPE_AUTO_RELEASE CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/module.h>
|
||||
|
||||
bool ffKmodLoaded(const char* modName)
|
||||
{
|
||||
bool ffKmodLoaded(const char* modName) {
|
||||
return modfind(modName) >= 0;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
#include "common/kmod.h"
|
||||
#include "common/io.h"
|
||||
|
||||
bool ffKmodLoaded(const char* modName)
|
||||
{
|
||||
bool ffKmodLoaded(const char* modName) {
|
||||
static FFstrbuf modules;
|
||||
if (modules.chars == NULL)
|
||||
{
|
||||
if (modules.chars == NULL) {
|
||||
ffStrbufInitS(&modules, "\n");
|
||||
ffAppendFileBuffer("/proc/modules", &modules);
|
||||
}
|
||||
|
||||
if (modules.length == 0) return false;
|
||||
if (modules.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t len = (uint32_t) strlen(modName);
|
||||
if (len > 250) return false;
|
||||
if (len > 250) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char temp[256];
|
||||
temp[0] = '\n';
|
||||
|
||||
+11
-14
@@ -4,39 +4,36 @@
|
||||
#include <sys/module.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
typedef struct __attribute__((__packed__)) FFNbsdModList
|
||||
{
|
||||
typedef struct FF_A_PACKED FFNbsdModList {
|
||||
int len;
|
||||
modstat_t mods[];
|
||||
} FFNbsdModList;
|
||||
|
||||
bool ffKmodLoaded(const char* modName)
|
||||
{
|
||||
bool ffKmodLoaded(const char* modName) {
|
||||
static FFNbsdModList* list = NULL;
|
||||
|
||||
if (list == NULL)
|
||||
{
|
||||
if (list == NULL) {
|
||||
struct iovec iov = {};
|
||||
|
||||
for (size_t len = 8192;; len = iov.iov_len)
|
||||
{
|
||||
for (size_t len = 8192;; len = iov.iov_len) {
|
||||
iov.iov_len = len;
|
||||
iov.iov_base = realloc(iov.iov_base, len);
|
||||
if (modctl(MODCTL_STAT, &iov) < 0)
|
||||
{
|
||||
if (modctl(MODCTL_STAT, &iov) < 0) {
|
||||
free(iov.iov_base);
|
||||
return true; // ignore errors
|
||||
}
|
||||
|
||||
if (len >= iov.iov_len) break;
|
||||
if (len >= iov.iov_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
list = (FFNbsdModList*) iov.iov_base;
|
||||
}
|
||||
|
||||
for (int i = 0; i < list->len; i++)
|
||||
{
|
||||
if (ffStrEquals(list->mods[i].ms_name, modName))
|
||||
for (int i = 0; i < list->len; i++) {
|
||||
if (ffStrEquals(list->mods[i].ms_name, modName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "common/kmod.h"
|
||||
|
||||
bool ffKmodLoaded(FF_MAYBE_UNUSED const char* modName)
|
||||
{
|
||||
bool ffKmodLoaded(FF_A_UNUSED const char* modName) {
|
||||
return true; // Don't generate kernel module related errors
|
||||
}
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
#include <sys/modctl.h>
|
||||
#include <errno.h>
|
||||
|
||||
bool ffKmodLoaded(const char* modName)
|
||||
{
|
||||
bool ffKmodLoaded(const char* modName) {
|
||||
struct modinfo modinfo = {
|
||||
.mi_id = -1,
|
||||
.mi_nextid = -1,
|
||||
.mi_info = MI_INFO_ALL,
|
||||
};
|
||||
|
||||
for (int id = -1; modctl(MODINFO, id, &modinfo) == 0; id = modinfo.mi_id)
|
||||
{
|
||||
for (int id = -1; modctl(MODINFO, id, &modinfo) == 0; id = modinfo.mi_id) {
|
||||
modinfo.mi_name[MODMAXNAMELEN - 1] = '\0';
|
||||
|
||||
if (ffStrEquals(modinfo.mi_name, modName))
|
||||
if (ffStrEquals(modinfo.mi_name, modName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return !(errno == EINVAL || errno == ENOENT);
|
||||
|
||||
@@ -3,24 +3,25 @@
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
bool ffKmodLoaded(const char* modName)
|
||||
{
|
||||
bool ffKmodLoaded(const char* modName) {
|
||||
ULONG bufferSize = 0;
|
||||
NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &bufferSize);
|
||||
if (bufferSize == 0)
|
||||
if (bufferSize == 0) {
|
||||
return true; // ignore errors
|
||||
}
|
||||
|
||||
FF_AUTO_FREE RTL_PROCESS_MODULES* buffer = malloc(bufferSize);
|
||||
|
||||
if (!NT_SUCCESS(NtQuerySystemInformation(SystemModuleInformation, buffer, bufferSize, &bufferSize)))
|
||||
if (!NT_SUCCESS(NtQuerySystemInformation(SystemModuleInformation, buffer, bufferSize, &bufferSize))) {
|
||||
return true; // ignore errors
|
||||
}
|
||||
|
||||
for (ULONG i = 0; i < buffer->NumberOfModules; i++)
|
||||
{
|
||||
for (ULONG i = 0; i < buffer->NumberOfModules; i++) {
|
||||
const char* name = (const char*) buffer->Modules[i].FullPathName + buffer->Modules[i].OffsetToFileName;
|
||||
|
||||
if (ffStrEqualsIgnCase(name, modName))
|
||||
if (ffStrEqualsIgnCase(name, modName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
+60
-65
@@ -2,46 +2,47 @@
|
||||
#include "common/library.h"
|
||||
|
||||
#if _WIN32
|
||||
#include "common/debug.h"
|
||||
#include "common/windows/nt.h"
|
||||
#include <errno.h>
|
||||
#include <ntstatus.h>
|
||||
#include "common/debug.h"
|
||||
#include "common/windows/nt.h"
|
||||
#include <errno.h>
|
||||
#include <ntstatus.h>
|
||||
#endif
|
||||
|
||||
#ifndef FF_DISABLE_DLOPEN
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
//Clang doesn't define __SANITIZE_ADDRESS__ but defines __has_feature(address_sanitizer)
|
||||
#if !defined(__SANITIZE_ADDRESS__) && defined(__has_feature)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#define __SANITIZE_ADDRESS__
|
||||
// Clang doesn't define __SANITIZE_ADDRESS__ but defines __has_feature(address_sanitizer)
|
||||
#if !defined(__SANITIZE_ADDRESS__) && defined(__has_feature)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#define __SANITIZE_ADDRESS__
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef FF_DLOPEN_FLAGS
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
#define FF_DLOPEN_FLAGS RTLD_LAZY | RTLD_NODELETE
|
||||
#else
|
||||
#define FF_DLOPEN_FLAGS RTLD_LAZY
|
||||
#ifndef FF_DLOPEN_FLAGS
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
#define FF_DLOPEN_FLAGS RTLD_LAZY | RTLD_NODELETE
|
||||
#else
|
||||
#define FF_DLOPEN_FLAGS RTLD_LAZY
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void* libraryLoad(const char* path, int maxVersion)
|
||||
{
|
||||
static void* libraryLoad(const char* path, int maxVersion) {
|
||||
void* result = dlopen(path, FF_DLOPEN_FLAGS);
|
||||
|
||||
#ifdef _WIN32
|
||||
#if _WIN32
|
||||
|
||||
// libX.dll.1 never exists on Windows, while libX-1.dll may exist
|
||||
FF_UNUSED(maxVersion)
|
||||
|
||||
if(result != NULL)
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t pathLen = ffStrbufLastIndexC(&instance.state.platform.exePath, '/');
|
||||
if (pathLen == instance.state.platform.exePath.length)
|
||||
if (pathLen == instance.state.platform.exePath.length) {
|
||||
return result;
|
||||
}
|
||||
|
||||
char absPath[MAX_PATH * 2];
|
||||
strcpy(mempcpy(absPath, instance.state.platform.exePath.chars, pathLen + 1), path);
|
||||
@@ -49,21 +50,22 @@ static void* libraryLoad(const char* path, int maxVersion)
|
||||
|
||||
#else
|
||||
|
||||
if(result != NULL || maxVersion < 0)
|
||||
if (result != NULL || maxVersion < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY pathbuf = ffStrbufCreateA(64);
|
||||
ffStrbufAppendS(&pathbuf, path);
|
||||
ffStrbufAppendC(&pathbuf, '.');
|
||||
|
||||
for(int i = maxVersion; i >= 0; --i)
|
||||
{
|
||||
for (int i = maxVersion; i >= 0; --i) {
|
||||
uint32_t originalLength = pathbuf.length;
|
||||
ffStrbufAppendSInt(&pathbuf, i);
|
||||
|
||||
result = dlopen(pathbuf.chars, FF_DLOPEN_FLAGS);
|
||||
if(result != NULL)
|
||||
if (result != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(&pathbuf, originalLength);
|
||||
}
|
||||
@@ -73,20 +75,18 @@ static void* libraryLoad(const char* path, int maxVersion)
|
||||
return result;
|
||||
}
|
||||
|
||||
void* ffLibraryLoad(const char* path, int maxVersion, ...)
|
||||
{
|
||||
void* ffLibraryLoad(const char* path, int maxVersion, ...) {
|
||||
void* result = libraryLoad(path, maxVersion);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
if (!result) {
|
||||
va_list defaultNames;
|
||||
va_start(defaultNames, maxVersion);
|
||||
|
||||
do
|
||||
{
|
||||
do {
|
||||
const char* pathRest = va_arg(defaultNames, const char*);
|
||||
if(pathRest == NULL)
|
||||
if (pathRest == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
int maxVersionRest = va_arg(defaultNames, int);
|
||||
result = libraryLoad(pathRest, maxVersionRest);
|
||||
@@ -102,27 +102,25 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...)
|
||||
|
||||
#if _WIN32
|
||||
|
||||
void* dlopen(const char* path, FF_MAYBE_UNUSED int mode)
|
||||
{
|
||||
void* dlopen(const char* path, FF_A_UNUSED int mode) {
|
||||
wchar_t pathW[MAX_PATH + 1];
|
||||
ULONG pathWBytes = 0;
|
||||
|
||||
NTSTATUS status = RtlUTF8ToUnicodeN(pathW, sizeof(pathW), &pathWBytes, path, (uint32_t)strlen(path) + 1);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
NTSTATUS status = RtlUTF8ToUnicodeN(pathW, sizeof(pathW), &pathWBytes, path, (uint32_t) strlen(path) + 1);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FF_DEBUG("RtlUTF8ToUnicodeN failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PVOID module = NULL;
|
||||
status = LdrLoadDll(NULL, NULL, &(UNICODE_STRING) {
|
||||
.Length = (USHORT) pathWBytes - sizeof(wchar_t), // Exclude null terminator
|
||||
.MaximumLength = (USHORT) pathWBytes,
|
||||
.Buffer = pathW,
|
||||
}, &module);
|
||||
.Length = (USHORT) (pathWBytes - sizeof(wchar_t)), // Exclude null terminator
|
||||
.MaximumLength = (USHORT) pathWBytes,
|
||||
.Buffer = pathW,
|
||||
},
|
||||
&module);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FF_DEBUG("LdrLoadDll failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
@@ -130,47 +128,44 @@ void* dlopen(const char* path, FF_MAYBE_UNUSED int mode)
|
||||
return module;
|
||||
}
|
||||
|
||||
int dlclose(void* handle)
|
||||
{
|
||||
int dlclose(void* handle) {
|
||||
NTSTATUS status = LdrUnloadDll(handle);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FF_DEBUG("LdrUnloadDll failed for handle %p with status 0x%08lX: %s", handle, status, ffDebugNtStatus(status));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* dlsym(void* handle, const char* symbol)
|
||||
{
|
||||
void* dlsym(void* handle, const char* symbol) {
|
||||
void* address;
|
||||
USHORT symbolBytes = (USHORT) strlen(symbol) + 1;
|
||||
USHORT symbolBytes = (USHORT) (strlen(symbol) + 1);
|
||||
NTSTATUS status = LdrGetProcedureAddress(handle, &(ANSI_STRING) {
|
||||
.Length = symbolBytes - sizeof(char),
|
||||
.MaximumLength = symbolBytes,
|
||||
.Buffer = (char*) symbol,
|
||||
}, 0, &address);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
.Length = symbolBytes - sizeof(char),
|
||||
.MaximumLength = symbolBytes,
|
||||
.Buffer = (char*) symbol,
|
||||
},
|
||||
0,
|
||||
&address);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FF_DEBUG("LdrGetProcedureAddress failed for symbol %s with status 0x%08lX: %s", symbol, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
void* ffLibraryGetModule(const wchar_t* libraryFileName)
|
||||
{
|
||||
void* ffLibraryGetModule(const wchar_t* libraryFileName) {
|
||||
assert(libraryFileName != NULL && "Use \"ffGetPeb()->ImageBaseAddress\" instead");
|
||||
|
||||
void* module = NULL;
|
||||
USHORT libraryFileNameBytes = (USHORT) (wcslen(libraryFileName) * sizeof(wchar_t)) + sizeof(wchar_t);
|
||||
USHORT libraryFileNameBytes = (USHORT) (wcslen(libraryFileName) * sizeof(wchar_t) + sizeof(wchar_t));
|
||||
NTSTATUS status = LdrGetDllHandle(NULL, NULL, &(UNICODE_STRING) {
|
||||
.Length = libraryFileNameBytes - sizeof(wchar_t),
|
||||
.MaximumLength = libraryFileNameBytes,
|
||||
.Buffer = (wchar_t*) libraryFileName,
|
||||
}, &module);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
.Length = libraryFileNameBytes - sizeof(wchar_t),
|
||||
.MaximumLength = libraryFileNameBytes,
|
||||
.Buffer = (wchar_t*) libraryFileName,
|
||||
},
|
||||
&module);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FF_DEBUG("LdrGetDllHandle failed for library %ls with status 0x%08lX: %s", libraryFileName, status, ffDebugNtStatus(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void* memrchr(const void* s, int c, size_t n)
|
||||
{
|
||||
if (n == 0) return NULL;
|
||||
void* memrchr(const void* s, int c, size_t n) {
|
||||
if (n == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const uint8_t uc = (uint8_t) c;
|
||||
|
||||
const uint8_t* p = (const uint8_t*) s + n;
|
||||
|
||||
while (n--)
|
||||
{
|
||||
if (*--p == uc) return (void*) p;
|
||||
while (n--) {
|
||||
if (*--p == uc) {
|
||||
return (void*) p;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV4(void)
|
||||
{
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV4(void) {
|
||||
static FFNetifDefaultRouteResult result;
|
||||
if (result.status == FF_NETIF_UNINITIALIZED) {
|
||||
result.status = ffNetifGetDefaultRouteImplV4(&result) ? FF_NETIF_OK : FF_NETIF_INVALID;
|
||||
@@ -14,8 +13,7 @@ const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV4(void)
|
||||
return &result;
|
||||
}
|
||||
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV6(void)
|
||||
{
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV6(void) {
|
||||
static FFNetifDefaultRouteResult result;
|
||||
if (result.status == FF_NETIF_UNINITIALIZED) {
|
||||
result.status = ffNetifGetDefaultRouteImplV6(&result) ? FF_NETIF_OK : FF_NETIF_INVALID;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "common/netif.h"
|
||||
#include "common/io.h"
|
||||
#include "common/mallocHelper.h"
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
@@ -8,68 +7,66 @@
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
|
||||
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
|
||||
|
||||
#if __APPLE__
|
||||
// https://github.com/apple-oss-distributions/network_cmds/blob/8f38231438e6a4d16ef8015e97e12c2c05105644/rtsol.tproj/if.c#L243
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint32_t))
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint32_t))
|
||||
#elif __sun
|
||||
// https://github.com/illumos/illumos-gate/blob/95b8c88950fa7b19af46bc63230137cf96b0bff7/usr/src/cmd/cmd-inet/usr.sbin/route.c#L339
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#else
|
||||
#error unknown platform
|
||||
#endif
|
||||
|
||||
static struct sockaddr *
|
||||
get_rt_address(struct rt_msghdr *rtm, int desired)
|
||||
{
|
||||
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
|
||||
static struct sockaddr*
|
||||
get_rt_address(struct rt_msghdr* rtm, int desired) {
|
||||
struct sockaddr* sa = (struct sockaddr*) (rtm + 1);
|
||||
|
||||
for (int i = 0; i < RTAX_MAX; i++)
|
||||
{
|
||||
if (rtm->rtm_addrs & (1 << i))
|
||||
{
|
||||
if ((1 << i) == desired)
|
||||
for (int i = 0; i < RTAX_MAX; i++) {
|
||||
if (rtm->rtm_addrs & (1 << i)) {
|
||||
if ((1 << i) == desired) {
|
||||
return sa;
|
||||
}
|
||||
|
||||
#ifndef __sun
|
||||
#ifndef __sun
|
||||
uint32_t salen = sa->sa_len;
|
||||
#else
|
||||
#else
|
||||
uint32_t salen;
|
||||
// https://github.com/illumos/illumos-gate/blob/95b8c88950fa7b19af46bc63230137cf96b0bff7/usr/src/cmd/cmd-inet/usr.sbin/route.c#L2941
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
salen = sizeof (struct sockaddr_in);
|
||||
break;
|
||||
case AF_LINK:
|
||||
salen = sizeof (struct sockaddr_dl);
|
||||
break;
|
||||
case AF_INET6:
|
||||
salen = sizeof (struct sockaddr_in6);
|
||||
break;
|
||||
default:
|
||||
salen = sizeof (struct sockaddr);
|
||||
break;
|
||||
case AF_INET:
|
||||
salen = sizeof(struct sockaddr_in);
|
||||
break;
|
||||
case AF_LINK:
|
||||
salen = sizeof(struct sockaddr_dl);
|
||||
break;
|
||||
case AF_INET6:
|
||||
salen = sizeof(struct sockaddr_in6);
|
||||
break;
|
||||
default:
|
||||
salen = sizeof(struct sockaddr);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
sa = (struct sockaddr *)(ROUNDUP(salen) + (char *)sa);
|
||||
#endif
|
||||
sa = (struct sockaddr*) (ROUNDUP(salen) + (char*) sa);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
|
||||
// https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
|
||||
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET);
|
||||
if (pfRoute < 0)
|
||||
if (pfRoute < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
struct timeval timeout = {1, 0};
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
struct timeval timeout = { 1, 0 };
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char*) &timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
@@ -90,38 +87,49 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
},
|
||||
.dst = {
|
||||
.sin_family = AF_INET,
|
||||
#ifndef __sun
|
||||
#ifndef __sun
|
||||
.sin_len = sizeof(rtmsg.dst),
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == (pid_t) pid))
|
||||
;
|
||||
bool gotResponse = false;
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0) {
|
||||
if (rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == (pid_t) pid) {
|
||||
gotResponse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!gotResponse) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
if ((rtmsg.hdr.rtm_flags & (RTF_UP | RTF_GATEWAY)) == (RTF_UP | RTF_GATEWAY))
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl*) get_rt_address(&rtmsg.hdr, RTA_IFP);
|
||||
if (sdl
|
||||
#ifndef __sun
|
||||
#ifndef __sun
|
||||
&& sdl->sdl_len
|
||||
#endif
|
||||
)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
#endif
|
||||
&& sdl->sdl_family == AF_LINK) {
|
||||
if (sdl->sdl_nlen > IF_NAMESIZE) {
|
||||
return false;
|
||||
}
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
// Get the preferred source address
|
||||
struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(&rtmsg.hdr, RTA_IFA);
|
||||
if (src && src->sin_family == AF_INET)
|
||||
struct sockaddr_in* src = (struct sockaddr_in*) get_rt_address(&rtmsg.hdr, RTA_IFA);
|
||||
if (src && src->sin_family == AF_INET) {
|
||||
result->preferredSourceAddrV4 = src->sin_addr.s_addr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -131,18 +139,18 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
|
||||
// https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
|
||||
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
|
||||
if (pfRoute < 0)
|
||||
if (pfRoute < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
struct timeval timeout = {1, 0};
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
struct timeval timeout = { 1, 0 };
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char*) &timeout, sizeof(timeout));
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char*) &timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
@@ -163,30 +171,40 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
},
|
||||
.dst = {
|
||||
.sin6_family = AF_INET6,
|
||||
#ifndef __sun
|
||||
#ifndef __sun
|
||||
.sin6_len = sizeof(rtmsg.dst),
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == (pid_t) pid))
|
||||
;
|
||||
bool gotResponse = false;
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0) {
|
||||
if (rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == (pid_t) pid) {
|
||||
gotResponse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!gotResponse) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
if ((rtmsg.hdr.rtm_flags & (RTF_UP | RTF_GATEWAY)) == (RTF_UP | RTF_GATEWAY))
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl*) get_rt_address(&rtmsg.hdr, RTA_IFP);
|
||||
if (sdl
|
||||
#ifndef __sun
|
||||
#ifndef __sun
|
||||
&& sdl->sdl_len
|
||||
#endif
|
||||
)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
#endif
|
||||
&& sdl->sdl_family == AF_LINK) {
|
||||
if (sdl->sdl_nlen > IF_NAMESIZE) {
|
||||
return false;
|
||||
}
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
+46
-47
@@ -9,75 +9,74 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
|
||||
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
|
||||
|
||||
#if __DragonFly__
|
||||
// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cf0aa2f1e47a3f0a6055fe427563cb3f3e627064/sys/net/route.h#L315C9-L315C19
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#elif __FreeBSD__
|
||||
// https://github.com/freebsd/freebsd-src/blob/e4c0ecba44b20ebb2e4d80978c2cb6d16b730cb9/sys/net/route.h#L368C9-L368C16
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#elif __NetBSD__
|
||||
// https://github.com/NetBSD/src/blob/29beb637d057520c0ed37ac2cde966f7cc0cadf4/sys/net/route.h#L330
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t))
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t))
|
||||
#elif __OpenBSD__
|
||||
// https://github.com/openbsd/src/blob/ca647cfa4ec3ccb8360714bc0ebc32a394f7fb6a/regress/sys/netinet/bindconnect/bindconnect.c#L250
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#else
|
||||
#error unknown platform
|
||||
#endif
|
||||
|
||||
static struct sockaddr *
|
||||
get_rt_address(struct rt_msghdr *rtm, int desired)
|
||||
{
|
||||
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
|
||||
static struct sockaddr*
|
||||
get_rt_address(struct rt_msghdr* rtm, int desired) {
|
||||
struct sockaddr* sa = (struct sockaddr*) (rtm + 1);
|
||||
|
||||
for (int i = 0; i < RTAX_MAX; i++)
|
||||
{
|
||||
if (rtm->rtm_addrs & (1 << i))
|
||||
{
|
||||
if ((1 << i) == desired)
|
||||
for (int i = 0; i < RTAX_MAX; i++) {
|
||||
if (rtm->rtm_addrs & (1 << i)) {
|
||||
if ((1 << i) == desired) {
|
||||
return sa;
|
||||
sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
|
||||
}
|
||||
sa = (struct sockaddr*) (ROUNDUP(sa->sa_len) + (char*) sa);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY};
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
|
||||
int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY };
|
||||
size_t needed;
|
||||
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0)
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_AUTO_FREE char* buf = malloc(needed);
|
||||
|
||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
|
||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* lim = buf + needed;
|
||||
struct rt_msghdr* rtm;
|
||||
for (char* next = buf; next < lim; next += rtm->rtm_msglen)
|
||||
{
|
||||
rtm = (struct rt_msghdr *)next;
|
||||
struct sockaddr* sa = (struct sockaddr *)(rtm + 1);
|
||||
for (char* next = buf; next < lim; next += rtm->rtm_msglen) {
|
||||
rtm = (struct rt_msghdr*) next;
|
||||
struct sockaddr* sa = (struct sockaddr*) (rtm + 1);
|
||||
|
||||
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET))
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP);
|
||||
if (sdl && sdl->sdl_family == AF_LINK)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET)) {
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl*) get_rt_address(rtm, RTA_IFP);
|
||||
if (sdl && sdl->sdl_family == AF_LINK) {
|
||||
if (sdl->sdl_nlen > IF_NAMESIZE) {
|
||||
continue;
|
||||
}
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
// Get the preferred source address
|
||||
struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(rtm, RTA_IFA);
|
||||
if (src && src->sin_family == AF_INET)
|
||||
struct sockaddr_in* src = (struct sockaddr_in*) get_rt_address(rtm, RTA_IFA);
|
||||
if (src && src->sin_family == AF_INET) {
|
||||
result->preferredSourceAddrV4 = src->sin_addr.s_addr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -86,32 +85,32 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_FLAGS, RTF_GATEWAY};
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
|
||||
int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_FLAGS, RTF_GATEWAY };
|
||||
size_t needed;
|
||||
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0)
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_AUTO_FREE char* buf = malloc(needed);
|
||||
|
||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
|
||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* lim = buf + needed;
|
||||
struct rt_msghdr* rtm;
|
||||
for (char* next = buf; next < lim; next += rtm->rtm_msglen)
|
||||
{
|
||||
rtm = (struct rt_msghdr *)next;
|
||||
struct sockaddr* sa = (struct sockaddr *)(rtm + 1);
|
||||
for (char* next = buf; next < lim; next += rtm->rtm_msglen) {
|
||||
rtm = (struct rt_msghdr*) next;
|
||||
struct sockaddr* sa = (struct sockaddr*) (rtm + 1);
|
||||
|
||||
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET6))
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP);
|
||||
if (sdl && sdl->sdl_family == AF_LINK)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET6)) {
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl*) get_rt_address(rtm, RTA_IFP);
|
||||
if (sdl && sdl->sdl_family == AF_LINK) {
|
||||
if (sdl->sdl_nlen > IF_NAMESIZE) {
|
||||
continue;
|
||||
}
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
@@ -7,28 +7,29 @@
|
||||
#define FF_STR_INDIR(x) #x
|
||||
#define FF_STR(x) FF_STR_INDIR(x)
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
|
||||
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/route", "r");
|
||||
|
||||
if (!netRoute) return false;
|
||||
if (!netRoute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip first line
|
||||
FF_UNUSED(fscanf(netRoute, "%*[^\n]\n"));
|
||||
unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, ...
|
||||
while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", result->ifName, &destination) == 2)
|
||||
{
|
||||
if (destination != 0) continue;
|
||||
while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", result->ifName, &destination) == 2) {
|
||||
if (destination != 0) {
|
||||
continue;
|
||||
}
|
||||
result->ifIndex = if_nametoindex(result->ifName);
|
||||
// TODO: Get the preferred source address
|
||||
return true;
|
||||
}
|
||||
result->ifName[0] = '0';
|
||||
result->ifName[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
|
||||
// TODO: AF_INET6
|
||||
FF_UNUSED(result);
|
||||
return false;
|
||||
|
||||
@@ -11,85 +11,95 @@
|
||||
|
||||
// loosely based on Haiku's src/bin/network/route/route.cpp
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET, SOCK_RAW, AF_INET);
|
||||
if (pfRoute < 0)
|
||||
if (pfRoute < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ifconf config;
|
||||
config.ifc_len = sizeof(config.ifc_value);
|
||||
if (ioctl(pfRoute, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
|
||||
if (ioctl(pfRoute, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = config.ifc_value;
|
||||
if (size <= 0)
|
||||
if (size <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_AUTO_FREE void *buffer = malloc((size_t) size);
|
||||
FF_AUTO_FREE void* buffer = malloc((size_t) size);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config.ifc_len = size;
|
||||
config.ifc_buf = buffer;
|
||||
if (ioctl(pfRoute, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
|
||||
if (ioctl(pfRoute, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ifreq *interface = (struct ifreq*)buffer;
|
||||
struct ifreq *end = (struct ifreq*)((uint8_t*)buffer + size);
|
||||
struct ifreq* interface = (struct ifreq*) buffer;
|
||||
struct ifreq* end = (struct ifreq*) ((uint8_t*) buffer + size);
|
||||
|
||||
while (interface < end) {
|
||||
if (interface->ifr_route.flags & RTF_DEFAULT) {
|
||||
// interface->ifr_metric?
|
||||
strlcpy(result->ifName, interface->ifr_name, IF_NAMESIZE);
|
||||
result->ifIndex = if_nametoindex(interface->ifr_name);
|
||||
if (interface->ifr_route.source)
|
||||
result->preferredSourceAddrV4 = ((struct sockaddr_in*)interface->ifr_route.source)->sin_addr.s_addr;
|
||||
if (interface->ifr_route.source) {
|
||||
result->preferredSourceAddrV4 = ((struct sockaddr_in*) interface->ifr_route.source)->sin_addr.s_addr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t addressSize = 0;
|
||||
if (interface->ifr_route.destination != NULL)
|
||||
if (interface->ifr_route.destination != NULL) {
|
||||
addressSize += interface->ifr_route.destination->sa_len;
|
||||
if (interface->ifr_route.mask != NULL)
|
||||
}
|
||||
if (interface->ifr_route.mask != NULL) {
|
||||
addressSize += interface->ifr_route.mask->sa_len;
|
||||
if (interface->ifr_route.gateway != NULL)
|
||||
}
|
||||
if (interface->ifr_route.gateway != NULL) {
|
||||
addressSize += interface->ifr_route.gateway->sa_len;
|
||||
}
|
||||
|
||||
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
|
||||
interface = (struct ifreq*) ((addr_t) interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET, SOCK_RAW, AF_INET6);
|
||||
if (pfRoute < 0)
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET6, SOCK_RAW, AF_INET6);
|
||||
if (pfRoute < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ifconf config;
|
||||
config.ifc_len = sizeof(config.ifc_value);
|
||||
if (ioctl(pfRoute, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
|
||||
if (ioctl(pfRoute, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = config.ifc_value;
|
||||
if (size <= 0)
|
||||
if (size <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_AUTO_FREE void *buffer = malloc((size_t) size);
|
||||
FF_AUTO_FREE void* buffer = malloc((size_t) size);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config.ifc_len = size;
|
||||
config.ifc_buf = buffer;
|
||||
if (ioctl(pfRoute, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
|
||||
if (ioctl(pfRoute, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ifreq *interface = (struct ifreq*)buffer;
|
||||
struct ifreq *end = (struct ifreq*)((uint8_t*)buffer + size);
|
||||
struct ifreq* interface = (struct ifreq*) buffer;
|
||||
struct ifreq* end = (struct ifreq*) ((uint8_t*) buffer + size);
|
||||
|
||||
while (interface < end) {
|
||||
if (interface->ifr_route.flags & RTF_DEFAULT) {
|
||||
@@ -99,14 +109,17 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
|
||||
size_t addressSize = 0;
|
||||
if (interface->ifr_route.destination != NULL)
|
||||
if (interface->ifr_route.destination != NULL) {
|
||||
addressSize += interface->ifr_route.destination->sa_len;
|
||||
if (interface->ifr_route.mask != NULL)
|
||||
}
|
||||
if (interface->ifr_route.mask != NULL) {
|
||||
addressSize += interface->ifr_route.mask->sa_len;
|
||||
if (interface->ifr_route.gateway != NULL)
|
||||
}
|
||||
if (interface->ifr_route.gateway != NULL) {
|
||||
addressSize += interface->ifr_route.gateway->sa_len;
|
||||
}
|
||||
|
||||
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
|
||||
interface = (struct ifreq*) ((addr_t) interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,35 +7,44 @@
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/if.h>
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
static uint32_t ffNetifGetNetlinkPortId(int sock_fd) {
|
||||
struct sockaddr_nl addr = {};
|
||||
socklen_t addrLen = sizeof(addr);
|
||||
if (getsockname(sock_fd, (struct sockaddr*) &addr, &addrLen) < 0) {
|
||||
FF_DEBUG("Failed to query netlink socket address (use PID instead): %s", strerror(errno));
|
||||
return instance.state.platform.pid;
|
||||
} else {
|
||||
FF_DEBUG("Netlink port ID: %u", addr.nl_pid);
|
||||
return addr.nl_pid;
|
||||
}
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
|
||||
FF_DEBUG("Starting IPv4 default route detection");
|
||||
|
||||
FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (sock_fd < 0)
|
||||
{
|
||||
if (sock_fd < 0) {
|
||||
FF_DEBUG("Failed to create netlink socket: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
|
||||
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
FF_DEBUG("Process PID: %u", pid);
|
||||
|
||||
// Bind socket
|
||||
struct sockaddr_nl addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = 0, // Let kernel choose PID
|
||||
.nl_groups = 0, // No multicast groups
|
||||
.nl_pid = 0, // Let kernel choose PID
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
if (bind(sock_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
|
||||
FF_DEBUG("Failed to bind socket: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Successfully bound socket");
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
uint32_t pid = ffNetifGetNetlinkPortId(sock_fd);
|
||||
|
||||
struct FF_A_PACKED {
|
||||
struct nlmsghdr nlh;
|
||||
struct rtmsg rtm;
|
||||
struct rtattr rta;
|
||||
@@ -52,8 +61,8 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
// Route message
|
||||
.rtm = {
|
||||
.rtm_family = AF_INET,
|
||||
.rtm_dst_len = 0, // Match all destinations
|
||||
.rtm_src_len = 0, // Match all sources
|
||||
.rtm_dst_len = 0, // Match all destinations
|
||||
.rtm_src_len = 0, // Match all sources
|
||||
.rtm_tos = 0,
|
||||
.rtm_table = RT_TABLE_UNSPEC,
|
||||
.rtm_protocol = RTPROT_UNSPEC,
|
||||
@@ -71,12 +80,11 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
|
||||
struct sockaddr_nl dest_addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = 0, // Kernel
|
||||
.nl_groups = 0, // No multicast groups
|
||||
.nl_pid = 0, // Kernel
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0,
|
||||
(struct sockaddr*)&dest_addr, sizeof(dest_addr));
|
||||
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0, (struct sockaddr*) &dest_addr, sizeof(dest_addr));
|
||||
|
||||
if (sent != sizeof(req)) {
|
||||
FF_DEBUG("Failed to send netlink request: sent=%zd, expected=%zu", sent, sizeof(req));
|
||||
@@ -89,19 +97,17 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
|
||||
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
|
||||
uint32_t minMetric = UINT32_MAX;
|
||||
FF_MAYBE_UNUSED int routeCount = 0;
|
||||
FF_A_UNUSED int routeCount = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0,
|
||||
(struct sockaddr*)&src_addr, &src_addr_len);
|
||||
while (true) {
|
||||
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0, (struct sockaddr*) &src_addr, &src_addr_len);
|
||||
|
||||
if (received < 0) {
|
||||
FF_DEBUG("Failed to receive netlink response: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (received >= (ssize_t)sizeof(buffer)) {
|
||||
if (received >= (ssize_t) sizeof(buffer)) {
|
||||
FF_DEBUG("Received truncated message: received %zd, bufsize %zu", received, sizeof(buffer));
|
||||
return false;
|
||||
}
|
||||
@@ -117,20 +123,19 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
uint32_t prefsrc;
|
||||
} entry;
|
||||
|
||||
for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
|
||||
for (const struct nlmsghdr* nlh = (struct nlmsghdr*) buffer;
|
||||
NLMSG_OK(nlh, received);
|
||||
nlh = NLMSG_NEXT(nlh, received))
|
||||
{
|
||||
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
|
||||
nlh = NLMSG_NEXT(nlh, received)) {
|
||||
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid) {
|
||||
continue;
|
||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
}
|
||||
if (nlh->nlmsg_type == NLMSG_DONE) {
|
||||
FF_DEBUG("Received NLMSG_DONE, processed %d routes", routeCount);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (nlh->nlmsg_type == NLMSG_ERROR) {
|
||||
FF_DEBUG("Netlink reports error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(nlh))->error));
|
||||
FF_DEBUG("Netlink reports error: %s", strerror(-((struct nlmsgerr*) NLMSG_DATA(nlh))->error));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -140,7 +145,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
|
||||
routeCount++;
|
||||
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
|
||||
struct rtmsg* rtm = (struct rtmsg*) NLMSG_DATA(nlh);
|
||||
if (rtm->rtm_family != AF_INET) {
|
||||
FF_DEBUG("Skipping non-IPv4 route #%d (family=%d)", routeCount, rtm->rtm_family);
|
||||
continue;
|
||||
@@ -158,16 +163,16 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
|
||||
FF_DEBUG("Processing IPv4 default route candidate #%d", routeCount);
|
||||
entry = (__typeof__(entry)) { }; // Default to zero metric (no RTA_PRIORITY found)
|
||||
entry = (__typeof__(entry)) {}; // Default to zero metric (no RTA_PRIORITY found)
|
||||
|
||||
// Parse route attributes
|
||||
size_t rtm_len = RTM_PAYLOAD(nlh);
|
||||
for (struct rtattr* rta = RTM_RTA(rtm);
|
||||
RTA_OK(rta, rtm_len);
|
||||
rta = RTA_NEXT(rta, rtm_len))
|
||||
{
|
||||
if (RTA_PAYLOAD(rta) < sizeof(uint32_t))
|
||||
rta = RTA_NEXT(rta, rtm_len)) {
|
||||
if (RTA_PAYLOAD(rta) < sizeof(uint32_t)) {
|
||||
continue; // Skip invalid attributes
|
||||
}
|
||||
|
||||
uint32_t rta_data = *(uint32_t*) RTA_DATA(rta);
|
||||
switch (rta->rta_type) {
|
||||
@@ -179,23 +184,26 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
FF_DEBUG("Found interface index: %u", entry.ifindex);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
FF_DEBUG("Found gateway: %s", inet_ntoa(*(struct in_addr*)&rta_data));
|
||||
if (rta_data == 0) goto next;
|
||||
FF_DEBUG("Found gateway: %s", inet_ntoa(*(struct in_addr*) &rta_data));
|
||||
if (rta_data == 0) {
|
||||
goto next;
|
||||
}
|
||||
break;
|
||||
case RTA_PRIORITY:
|
||||
FF_DEBUG("Found metric: %u", rta_data);
|
||||
if (rta_data >= minMetric) goto next;
|
||||
if (rta_data >= minMetric) {
|
||||
goto next;
|
||||
}
|
||||
entry.metric = rta_data;
|
||||
break;
|
||||
case RTA_PREFSRC:
|
||||
entry.prefsrc = rta_data;
|
||||
FF_DEBUG("Found preferred source: %s", inet_ntoa(*(struct in_addr*)&rta_data));
|
||||
FF_DEBUG("Found preferred source: %s", inet_ntoa(*(struct in_addr*) &rta_data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.ifindex == 0 || entry.metric >= minMetric)
|
||||
{
|
||||
if (entry.ifindex == 0 || entry.metric >= minMetric) {
|
||||
next:
|
||||
FF_DEBUG("Skipping route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
|
||||
continue;
|
||||
@@ -204,8 +212,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
result->ifIndex = entry.ifindex;
|
||||
FF_DEBUG("Updated best route: ifindex=%u, metric=%u, prefsrc=%x", entry.ifindex, entry.metric, entry.prefsrc);
|
||||
result->preferredSourceAddrV4 = entry.prefsrc;
|
||||
if (minMetric == 0)
|
||||
{
|
||||
if (minMetric == 0) {
|
||||
FF_DEBUG("Found zero metric route, stopping further processing");
|
||||
break; // Stop processing if we found a zero metric route
|
||||
}
|
||||
@@ -213,8 +220,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
|
||||
exit:
|
||||
if (minMetric < UINT32_MAX)
|
||||
{
|
||||
if (minMetric < UINT32_MAX) {
|
||||
if_indextoname(result->ifIndex, result->ifName);
|
||||
FF_DEBUG("Found default IPv4 route: interface=%s, index=%u, metric=%u", result->ifName, result->ifIndex, minMetric);
|
||||
return true;
|
||||
@@ -223,35 +229,32 @@ exit:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
|
||||
FF_DEBUG("Starting IPv6 default route detection");
|
||||
|
||||
FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (sock_fd < 0)
|
||||
{
|
||||
if (sock_fd < 0) {
|
||||
FF_DEBUG("Failed to create netlink socket: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
|
||||
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
FF_DEBUG("Process PID: %u", pid);
|
||||
|
||||
// Bind socket
|
||||
struct sockaddr_nl addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = 0, // Let kernel choose PID
|
||||
.nl_groups = 0, // No multicast groups
|
||||
.nl_pid = 0, // Let kernel choose PID
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
if (bind(sock_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
|
||||
FF_DEBUG("Failed to bind socket: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Successfully bound socket");
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
uint32_t pid = ffNetifGetNetlinkPortId(sock_fd);
|
||||
|
||||
struct FF_A_PACKED {
|
||||
struct nlmsghdr nlh;
|
||||
struct rtmsg rtm;
|
||||
struct rtattr rta;
|
||||
@@ -267,9 +270,9 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
},
|
||||
// Route message
|
||||
.rtm = {
|
||||
.rtm_family = AF_INET6, // IPv6 instead of IPv4
|
||||
.rtm_dst_len = 0, // Match all destinations
|
||||
.rtm_src_len = 0, // Match all sources
|
||||
.rtm_family = AF_INET6, // IPv6 instead of IPv4
|
||||
.rtm_dst_len = 0, // Match all destinations
|
||||
.rtm_src_len = 0, // Match all sources
|
||||
.rtm_tos = 0,
|
||||
.rtm_table = RT_TABLE_UNSPEC,
|
||||
.rtm_protocol = RTPROT_UNSPEC,
|
||||
@@ -287,12 +290,11 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
|
||||
struct sockaddr_nl dest_addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = 0, // Kernel
|
||||
.nl_groups = 0, // No multicast groups
|
||||
.nl_pid = 0, // Kernel
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0,
|
||||
(struct sockaddr*)&dest_addr, sizeof(dest_addr));
|
||||
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0, (struct sockaddr*) &dest_addr, sizeof(dest_addr));
|
||||
|
||||
if (sent != sizeof(req)) {
|
||||
FF_DEBUG("Failed to send netlink request: sent=%zd, expected=%zu", sent, sizeof(req));
|
||||
@@ -305,19 +307,17 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
|
||||
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
|
||||
uint32_t minMetric = UINT32_MAX;
|
||||
FF_MAYBE_UNUSED int routeCount = 0;
|
||||
FF_A_UNUSED int routeCount = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0,
|
||||
(struct sockaddr*)&src_addr, &src_addr_len);
|
||||
while (true) {
|
||||
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0, (struct sockaddr*) &src_addr, &src_addr_len);
|
||||
|
||||
if (received < 0) {
|
||||
FF_DEBUG("Failed to receive netlink response: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (received >= (ssize_t)sizeof(buffer)) {
|
||||
if (received >= (ssize_t) sizeof(buffer)) {
|
||||
FF_DEBUG("Received truncated message: received %zd, bufsize %zu", received, sizeof(buffer));
|
||||
return false;
|
||||
}
|
||||
@@ -332,20 +332,19 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
uint32_t ifindex;
|
||||
} entry;
|
||||
|
||||
for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
|
||||
for (const struct nlmsghdr* nlh = (struct nlmsghdr*) buffer;
|
||||
NLMSG_OK(nlh, received);
|
||||
nlh = NLMSG_NEXT(nlh, received))
|
||||
{
|
||||
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
|
||||
nlh = NLMSG_NEXT(nlh, received)) {
|
||||
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid) {
|
||||
continue;
|
||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
}
|
||||
if (nlh->nlmsg_type == NLMSG_DONE) {
|
||||
FF_DEBUG("Received NLMSG_DONE, processed %d routes", routeCount);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (nlh->nlmsg_type == NLMSG_ERROR) {
|
||||
FF_DEBUG("Netlink reports error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(nlh))->error));
|
||||
FF_DEBUG("Netlink reports error: %s", strerror(-((struct nlmsgerr*) NLMSG_DATA(nlh))->error));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -355,7 +354,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
|
||||
routeCount++;
|
||||
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
|
||||
struct rtmsg* rtm = (struct rtmsg*) NLMSG_DATA(nlh);
|
||||
if (rtm->rtm_family != AF_INET6) {
|
||||
FF_DEBUG("Skipping non-IPv6 route #%d (family=%d)", routeCount, rtm->rtm_family);
|
||||
continue;
|
||||
@@ -373,18 +372,17 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
|
||||
FF_DEBUG("Processing IPv6 default route candidate #%d", routeCount);
|
||||
entry = (__typeof__(entry)) { }; // Default to zero metric (no RTA_PRIORITY found)
|
||||
entry = (__typeof__(entry)) {}; // Default to zero metric (no RTA_PRIORITY found)
|
||||
|
||||
// Parse route attributes
|
||||
size_t rtm_len = RTM_PAYLOAD(nlh);
|
||||
for (struct rtattr* rta = RTM_RTA(rtm);
|
||||
RTA_OK(rta, rtm_len);
|
||||
rta = RTA_NEXT(rta, rtm_len))
|
||||
{
|
||||
rta = RTA_NEXT(rta, rtm_len)) {
|
||||
switch (rta->rta_type) {
|
||||
case RTA_DST:
|
||||
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
|
||||
FF_MAYBE_UNUSED char str[INET6_ADDRSTRLEN];
|
||||
FF_A_UNUSED char str[INET6_ADDRSTRLEN];
|
||||
FF_DEBUG("Unexpected RTA_DST: %s", inet_ntop(AF_INET6, RTA_DATA(rta), str, sizeof(str)));
|
||||
goto next;
|
||||
}
|
||||
@@ -398,8 +396,10 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
case RTA_GATEWAY:
|
||||
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
|
||||
struct in6_addr* gw = (struct in6_addr*) RTA_DATA(rta);
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(gw)) goto next;
|
||||
FF_MAYBE_UNUSED char str[INET6_ADDRSTRLEN];
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(gw)) {
|
||||
goto next;
|
||||
}
|
||||
FF_A_UNUSED char str[INET6_ADDRSTRLEN];
|
||||
FF_DEBUG("Found gateway: %s", inet_ntop(AF_INET6, gw, str, sizeof(str)));
|
||||
}
|
||||
break;
|
||||
@@ -407,15 +407,16 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
if (RTA_PAYLOAD(rta) >= sizeof(uint32_t)) {
|
||||
uint32_t metric = *(uint32_t*) RTA_DATA(rta);
|
||||
FF_DEBUG("Found metric: %u", metric);
|
||||
if (metric >= minMetric) goto next;
|
||||
if (metric >= minMetric) {
|
||||
goto next;
|
||||
}
|
||||
entry.metric = metric;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.ifindex == 0 || entry.metric >= minMetric)
|
||||
{
|
||||
if (entry.ifindex == 0 || entry.metric >= minMetric) {
|
||||
next:
|
||||
FF_DEBUG("Skipping route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
|
||||
continue;
|
||||
@@ -424,8 +425,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
result->ifIndex = entry.ifindex;
|
||||
FF_DEBUG("Updated best route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
|
||||
|
||||
if (minMetric == 0)
|
||||
{
|
||||
if (minMetric == 0) {
|
||||
FF_DEBUG("Found zero metric route, stopping further processing");
|
||||
break; // Stop processing if we found a zero metric route
|
||||
}
|
||||
@@ -433,8 +433,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
|
||||
exit:
|
||||
if (minMetric < UINT32_MAX)
|
||||
{
|
||||
if (minMetric < UINT32_MAX) {
|
||||
if_indextoname(result->ifIndex, result->ifName);
|
||||
FF_DEBUG("Found default IPv6 route: interface=%s, index=%u, metric=%u", result->ifName, result->ifIndex, minMetric);
|
||||
return true;
|
||||
|
||||
@@ -1,32 +1,27 @@
|
||||
#include "common/netif.h"
|
||||
#include "common/mallocHelper.h"
|
||||
|
||||
#include <ws2tcpip.h> // AF_INET6, IN6_IS_ADDR_UNSPECIFIED
|
||||
#include <iphlpapi.h>
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
|
||||
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
|
||||
|
||||
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable)))
|
||||
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_INET, &pIpForwardTable))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundDefault = false;
|
||||
uint32_t smallestMetric = UINT32_MAX;
|
||||
|
||||
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i)
|
||||
{
|
||||
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i) {
|
||||
MIB_IPFORWARD_ROW2* row = &pIpForwardTable->Table[i];
|
||||
|
||||
if (row->DestinationPrefix.PrefixLength == 0 &&
|
||||
row->DestinationPrefix.Prefix.Ipv4.sin_family == AF_INET &&
|
||||
row->DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr == 0)
|
||||
{
|
||||
row->DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr == 0) {
|
||||
MIB_IF_ROW2 ifRow = {
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
};
|
||||
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp)
|
||||
{
|
||||
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp) {
|
||||
MIB_IPINTERFACE_ROW ipInterfaceRow = {
|
||||
.Family = AF_INET,
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
@@ -34,15 +29,14 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
|
||||
uint32_t realMetric = row->Metric /* Metric offset */;
|
||||
|
||||
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow)))
|
||||
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow))) {
|
||||
realMetric += ipInterfaceRow.Metric /* Interface metric */;
|
||||
}
|
||||
|
||||
if (realMetric < smallestMetric)
|
||||
{
|
||||
if (realMetric < smallestMetric) {
|
||||
smallestMetric = realMetric;
|
||||
result->ifIndex = row->InterfaceIndex;
|
||||
foundDefault = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,30 +47,25 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
return foundDefault;
|
||||
}
|
||||
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
|
||||
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
|
||||
|
||||
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable)))
|
||||
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_INET6, &pIpForwardTable))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundDefault = false;
|
||||
uint32_t smallestMetric = UINT32_MAX;
|
||||
|
||||
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i)
|
||||
{
|
||||
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i) {
|
||||
MIB_IPFORWARD_ROW2* row = &pIpForwardTable->Table[i];
|
||||
|
||||
if (row->DestinationPrefix.PrefixLength == 0 &&
|
||||
row->DestinationPrefix.Prefix.Ipv6.sin6_family == AF_INET6 &&
|
||||
IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr))
|
||||
{
|
||||
IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr)) {
|
||||
MIB_IF_ROW2 ifRow = {
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
};
|
||||
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp)
|
||||
{
|
||||
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp) {
|
||||
MIB_IPINTERFACE_ROW ipInterfaceRow = {
|
||||
.Family = AF_INET6,
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
@@ -84,11 +73,11 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
|
||||
uint32_t realMetric = row->Metric /* Metric offset */;
|
||||
|
||||
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow)))
|
||||
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow))) {
|
||||
realMetric += ipInterfaceRow.Metric /* Interface metric */;
|
||||
}
|
||||
|
||||
if (realMetric < smallestMetric)
|
||||
{
|
||||
if (realMetric < smallestMetric) {
|
||||
smallestMetric = realMetric;
|
||||
result->ifIndex = row->InterfaceIndex;
|
||||
foundDefault = true;
|
||||
|
||||
@@ -5,10 +5,9 @@
|
||||
#include "common/debug.h"
|
||||
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
#include <zlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
struct FFZlibLibrary
|
||||
{
|
||||
struct FFZlibLibrary {
|
||||
FF_LIBRARY_SYMBOL(inflateInit2_)
|
||||
FF_LIBRARY_SYMBOL(inflate)
|
||||
FF_LIBRARY_SYMBOL(inflateEnd)
|
||||
@@ -16,18 +15,17 @@ struct FFZlibLibrary
|
||||
bool inited;
|
||||
} zlibData;
|
||||
|
||||
const char* ffNetworkingLoadZlibLibrary(void)
|
||||
{
|
||||
if (!zlibData.inited)
|
||||
{
|
||||
const char* ffNetworkingLoadZlibLibrary(void) {
|
||||
if (!zlibData.inited) {
|
||||
zlibData.inited = true;
|
||||
FF_LIBRARY_LOAD_MESSAGE(zlib,
|
||||
#ifdef _WIN32
|
||||
"zlib1"
|
||||
#else
|
||||
"libz"
|
||||
#endif
|
||||
FF_LIBRARY_EXTENSION, 2)
|
||||
#ifdef _WIN32
|
||||
"zlib1"
|
||||
#else
|
||||
"libz"
|
||||
#endif
|
||||
FF_LIBRARY_EXTENSION,
|
||||
2)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateInit2_)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflate)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateEnd)
|
||||
@@ -37,16 +35,16 @@ const char* ffNetworkingLoadZlibLibrary(void)
|
||||
}
|
||||
|
||||
// Try to pre-read gzip header to determine uncompressed size
|
||||
static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize)
|
||||
{
|
||||
static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize) {
|
||||
// gzip file format: http://www.zlib.org/rfc-gzip.html
|
||||
if (dataSize < 10 || ((const uint8_t*)data)[0] != 0x1f || ((const uint8_t*)data)[1] != 0x8b)
|
||||
if (dataSize < 10 || ((const uint8_t*) data)[0] != 0x1f || ((const uint8_t*) data)[1] != 0x8b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Uncompressed size in gzip format is stored in the last 4 bytes, but only valid if data is less than 4GB
|
||||
if (dataSize > 18) {
|
||||
// Get ISIZE value from the end of file (little endian)
|
||||
const uint8_t* tail = (const uint8_t*)data + dataSize - 4;
|
||||
const uint8_t* tail = (const uint8_t*) data + dataSize - 4;
|
||||
uint32_t uncompressedSize = (uint32_t) tail[0] | ((uint32_t) tail[1] << 8u) | ((uint32_t) tail[2] << 16u) | ((uint32_t) tail[3] << 24u);
|
||||
|
||||
// For valid gzip files, this value is the length of the uncompressed data modulo 2^32
|
||||
@@ -65,8 +63,7 @@ static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize)
|
||||
}
|
||||
|
||||
// Decompress gzip content
|
||||
bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd)
|
||||
{
|
||||
bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd) {
|
||||
assert(headerEnd != NULL && *headerEnd == '\r');
|
||||
|
||||
// Calculate header size
|
||||
@@ -96,7 +93,7 @@ bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd)
|
||||
uint32_t compressedSize = buffer->length - headerSize - 4;
|
||||
|
||||
// Check if content is actually in gzip format (gzip header magic is 0x1f 0x8b)
|
||||
if (compressedSize < 2 || (uint8_t)bodyStart[0] != 0x1f || (uint8_t)bodyStart[1] != 0x8b) {
|
||||
if (compressedSize < 2 || (uint8_t) bodyStart[0] != 0x1f || (uint8_t) bodyStart[1] != 0x8b) {
|
||||
FF_DEBUG("Content is not valid gzip format, skipping decompression");
|
||||
return false;
|
||||
}
|
||||
@@ -113,14 +110,14 @@ bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd)
|
||||
.zalloc = Z_NULL,
|
||||
.zfree = Z_NULL,
|
||||
.opaque = Z_NULL,
|
||||
.avail_in = (uInt)compressedSize,
|
||||
.next_in = (Bytef*)bodyStart,
|
||||
.avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer),
|
||||
.next_out = (Bytef*)decompressedBuffer.chars,
|
||||
.avail_in = (uInt) compressedSize,
|
||||
.next_in = (Bytef*) bodyStart,
|
||||
.avail_out = (uInt) ffStrbufGetFree(&decompressedBuffer),
|
||||
.next_out = (Bytef*) decompressedBuffer.chars,
|
||||
};
|
||||
|
||||
// Initialize decompression engine
|
||||
if (zlibData.ffinflateInit2_(&zs, 16 + MAX_WBITS, ZLIB_VERSION, (int)sizeof(z_stream)) != Z_OK) {
|
||||
if (zlibData.ffinflateInit2_(&zs, 16 + MAX_WBITS, ZLIB_VERSION, (int) sizeof(z_stream)) != Z_OK) {
|
||||
FF_DEBUG("Failed to initialize decompression engine");
|
||||
return false;
|
||||
}
|
||||
@@ -130,52 +127,53 @@ bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd)
|
||||
int result = zlibData.ffinflate(&zs, Z_FINISH);
|
||||
|
||||
// If output buffer is insufficient, try to extend buffer
|
||||
while (result == Z_BUF_ERROR || (result != Z_STREAM_END && zs.avail_out == 0))
|
||||
{
|
||||
while (result == Z_BUF_ERROR || (result != Z_STREAM_END && zs.avail_out == 0)) {
|
||||
FF_DEBUG("Output buffer insufficient, trying to extend");
|
||||
|
||||
// Save already decompressed data amount
|
||||
uint32_t alreadyDecompressed = (uint32_t)(availableOut - zs.avail_out);
|
||||
uint32_t alreadyDecompressed = (uint32_t) (availableOut - zs.avail_out);
|
||||
decompressedBuffer.length += alreadyDecompressed;
|
||||
decompressedBuffer.chars[decompressedBuffer.length] = '\0';
|
||||
|
||||
ffStrbufEnsureFree(&decompressedBuffer, decompressedBuffer.length / 2);
|
||||
|
||||
// Set output parameters to point to new buffer
|
||||
zs.avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer);
|
||||
zs.next_out = (Bytef*)(decompressedBuffer.chars + decompressedBuffer.length);
|
||||
zs.avail_out = (uInt) ffStrbufGetFree(&decompressedBuffer);
|
||||
zs.next_out = (Bytef*) (decompressedBuffer.chars + decompressedBuffer.length);
|
||||
availableOut = zs.avail_out;
|
||||
|
||||
// Decompress again
|
||||
result = zlibData.ffinflate(&zs, Z_FINISH);
|
||||
}
|
||||
|
||||
// Check for decompression errors before using result
|
||||
if (result != Z_STREAM_END) {
|
||||
FF_DEBUG("Decompression failed with zlib error: %d", result);
|
||||
zlibData.ffinflateEnd(&zs);
|
||||
return false;
|
||||
}
|
||||
|
||||
zlibData.ffinflateEnd(&zs);
|
||||
|
||||
// Calculate decompressed size
|
||||
uint32_t decompressedSize = (uint32_t)(availableOut - zs.avail_out);
|
||||
// Calculate decompressed size (from the last inflate call)
|
||||
uint32_t decompressedSize = (uint32_t) (availableOut - zs.avail_out);
|
||||
decompressedBuffer.length += decompressedSize;
|
||||
decompressedBuffer.chars[decompressedBuffer.length] = '\0';
|
||||
FF_DEBUG("Successfully decompressed %u bytes compressed data to %u bytes", compressedSize, decompressedBuffer.length);
|
||||
|
||||
// Modify Content-Length header and remove Content-Encoding header
|
||||
FF_STRBUF_AUTO_DESTROY newBuffer = ffStrbufCreateA(headerSize + decompressedSize + 64);
|
||||
// Use decompressedBuffer.length (total) not decompressedSize (last chunk only)
|
||||
FF_STRBUF_AUTO_DESTROY newBuffer = ffStrbufCreateA(headerSize + decompressedBuffer.length + 64);
|
||||
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
while (ffStrbufGetline(&line, &len, buffer))
|
||||
{
|
||||
if (ffStrStartsWithIgnCase(line, "Content-Encoding:"))
|
||||
{
|
||||
while (ffStrbufGetline(&line, &len, buffer)) {
|
||||
if (ffStrStartsWithIgnCase(line, "Content-Encoding:")) {
|
||||
continue;
|
||||
}
|
||||
else if (ffStrStartsWithIgnCase(line, "Content-Length:"))
|
||||
{
|
||||
} else if (ffStrStartsWithIgnCase(line, "Content-Length:")) {
|
||||
ffStrbufAppendF(&newBuffer, "Content-Length: %u\r\n", decompressedBuffer.length);
|
||||
continue;
|
||||
}
|
||||
else if (line[0] == '\r')
|
||||
{
|
||||
} else if (line[0] == '\r') {
|
||||
ffStrbufAppendS(&newBuffer, "\r\n");
|
||||
ffStrbufGetlineRestore(&line, &len, buffer);
|
||||
break;
|
||||
|
||||
+176
-187
@@ -16,132 +16,137 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static const char* tryNonThreadingFastPath(FFNetworkingState* state)
|
||||
{
|
||||
#if defined(TCP_FASTOPEN) || __APPLE__
|
||||
static const char* tryNonThreadingFastPath(FFNetworkingState* state) {
|
||||
#if defined(TCP_FASTOPEN) || __APPLE__
|
||||
|
||||
if (!state->tfo)
|
||||
{
|
||||
#if __linux__ || __GNU__
|
||||
// Linux doesn't support sendto() on unconnected sockets
|
||||
FF_DEBUG("TCP Fast Open disabled, skipping");
|
||||
return "TCP Fast Open disabled";
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Attempting to use TCP Fast Open to connect");
|
||||
if (!state->tfo) {
|
||||
#if __linux__ || __GNU__
|
||||
// Linux doesn't support sendto() on unconnected sockets
|
||||
FF_DEBUG("TCP Fast Open disabled, skipping");
|
||||
return "TCP Fast Open disabled";
|
||||
#endif
|
||||
} else {
|
||||
FF_DEBUG("Attempting to use TCP Fast Open to connect");
|
||||
|
||||
#ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed
|
||||
// Set TCP Fast Open
|
||||
int flag = 1;
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP,
|
||||
#ifdef __APPLE__
|
||||
#ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed
|
||||
// Set TCP Fast Open
|
||||
int flag = 1;
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP,
|
||||
#ifdef __APPLE__
|
||||
// https://github.com/rust-lang/libc/pull/3135
|
||||
0x218 // TCP_FASTOPEN_FORCE_ENABLE
|
||||
#else
|
||||
TCP_FASTOPEN
|
||||
#endif
|
||||
, &flag, sizeof(flag)) != 0) {
|
||||
FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno));
|
||||
return "setsockopt(TCP_FASTOPEN) failed";
|
||||
} else {
|
||||
#if __linux__ || __GNU__
|
||||
FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", flag);
|
||||
#elif defined(__APPLE__)
|
||||
FF_DEBUG("Successfully set TCP_FASTOPEN_FORCE_ENABLE option");
|
||||
#else
|
||||
FF_DEBUG("Successfully set TCP_FASTOPEN option");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
FF_DEBUG("Using sendto() + MSG_DONTWAIT to send %u bytes of data", state->command.length);
|
||||
ssize_t sent = sendto(state->sockfd,
|
||||
state->command.chars,
|
||||
state->command.length,
|
||||
#ifdef MSG_FASTOPEN
|
||||
MSG_FASTOPEN |
|
||||
#endif
|
||||
#ifdef MSG_NOSIGNAL
|
||||
MSG_NOSIGNAL |
|
||||
#endif
|
||||
MSG_DONTWAIT,
|
||||
state->addr->ai_addr,
|
||||
state->addr->ai_addrlen);
|
||||
#else
|
||||
if (fcntl(state->sockfd, F_SETFL, O_NONBLOCK) == -1) {
|
||||
FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno));
|
||||
return "fcntl(F_SETFL) failed";
|
||||
TCP_FASTOPEN
|
||||
#endif
|
||||
,
|
||||
&flag,
|
||||
sizeof(flag)) != 0) {
|
||||
FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno));
|
||||
return "setsockopt(TCP_FASTOPEN) failed";
|
||||
} else {
|
||||
#if __linux__ || __GNU__
|
||||
FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", flag);
|
||||
#elif defined(__APPLE__)
|
||||
FF_DEBUG("Successfully set TCP_FASTOPEN_FORCE_ENABLE option");
|
||||
#else
|
||||
FF_DEBUG("Successfully set TCP_FASTOPEN option");
|
||||
#endif
|
||||
}
|
||||
FF_DEBUG("Using connectx() to send %u bytes of data", state->command.length);
|
||||
// Use connectx to establish connection and send data in one call
|
||||
size_t sent;
|
||||
if (connectx(state->sockfd,
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
FF_DEBUG("Using sendto() + MSG_DONTWAIT to send %u bytes of data", state->command.length);
|
||||
ssize_t sent = sendto(state->sockfd,
|
||||
state->command.chars,
|
||||
state->command.length,
|
||||
#ifdef MSG_FASTOPEN
|
||||
MSG_FASTOPEN |
|
||||
#endif
|
||||
#ifdef MSG_NOSIGNAL
|
||||
MSG_NOSIGNAL |
|
||||
#endif
|
||||
MSG_DONTWAIT,
|
||||
state->addr->ai_addr,
|
||||
state->addr->ai_addrlen);
|
||||
#else
|
||||
if (fcntl(state->sockfd, F_SETFL, O_NONBLOCK) == -1) {
|
||||
FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno));
|
||||
return "fcntl(F_SETFL) failed";
|
||||
}
|
||||
FF_DEBUG("Using connectx() to send %u bytes of data", state->command.length);
|
||||
// Use connectx to establish connection and send data in one call
|
||||
size_t sent;
|
||||
if (connectx(state->sockfd,
|
||||
&(sa_endpoints_t) {
|
||||
.sae_dstaddr = state->addr->ai_addr,
|
||||
.sae_dstaddrlen = state->addr->ai_addrlen,
|
||||
},
|
||||
SAE_ASSOCID_ANY, state->tfo ? CONNECT_DATA_IDEMPOTENT : 0,
|
||||
SAE_ASSOCID_ANY,
|
||||
state->tfo ? CONNECT_DATA_IDEMPOTENT : 0,
|
||||
&(struct iovec) {
|
||||
.iov_base = state->command.chars,
|
||||
.iov_len = state->command.length,
|
||||
}, 1, &sent, NULL) != 0) sent = 0;
|
||||
if (fcntl(state->sockfd, F_SETFL, 0) == -1) {
|
||||
FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno));
|
||||
return "fcntl(F_SETFL) failed";
|
||||
}
|
||||
#endif
|
||||
if (sent > 0 || (errno == EAGAIN || errno == EWOULDBLOCK
|
||||
#ifdef __APPLE__
|
||||
// On macOS EINPROGRESS means the connection cannot be completed immediately
|
||||
// On Linux, it means the TFO cookie is not available locally
|
||||
|| errno == EINPROGRESS
|
||||
#endif
|
||||
))
|
||||
{
|
||||
FF_DEBUG(
|
||||
#ifdef __APPLE__
|
||||
"connectx()"
|
||||
#else
|
||||
"sendto()"
|
||||
#endif
|
||||
" %s (sent=%zd, %s)", errno == 0 ? "succeeded" : "was in progress", sent, strerror(errno));
|
||||
freeaddrinfo(state->addr);
|
||||
state->addr = NULL;
|
||||
ffStrbufDestroy(&state->command);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG(
|
||||
#ifdef __APPLE__
|
||||
"connectx()"
|
||||
#else
|
||||
"sendto()"
|
||||
#endif
|
||||
" failed: %s", strerror(errno));
|
||||
#ifdef __APPLE__
|
||||
return "connectx() failed";
|
||||
#else
|
||||
return "sendto() failed";
|
||||
#endif
|
||||
#else
|
||||
FF_UNUSED(state);
|
||||
return "TFO support is not available";
|
||||
},
|
||||
1,
|
||||
&sent,
|
||||
NULL) != 0) {
|
||||
sent = 0;
|
||||
}
|
||||
if (fcntl(state->sockfd, F_SETFL, 0) == -1) {
|
||||
FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno));
|
||||
return "fcntl(F_SETFL) failed";
|
||||
}
|
||||
#endif
|
||||
if (sent > 0 || (errno == EAGAIN || errno == EWOULDBLOCK
|
||||
#ifdef __APPLE__
|
||||
// On macOS EINPROGRESS means the connection cannot be completed immediately
|
||||
// On Linux, it means the TFO cookie is not available locally
|
||||
|| errno == EINPROGRESS
|
||||
#endif
|
||||
)) {
|
||||
FF_DEBUG(
|
||||
#ifdef __APPLE__
|
||||
"connectx()"
|
||||
#else
|
||||
"sendto()"
|
||||
#endif
|
||||
" %s (sent=%zd, %s)",
|
||||
errno == 0 ? "succeeded" : "was in progress",
|
||||
sent,
|
||||
strerror(errno));
|
||||
freeaddrinfo(state->addr);
|
||||
state->addr = NULL;
|
||||
ffStrbufDestroy(&state->command);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG(
|
||||
#ifdef __APPLE__
|
||||
"connectx()"
|
||||
#else
|
||||
"sendto()"
|
||||
#endif
|
||||
" failed: %s",
|
||||
strerror(errno));
|
||||
#ifdef __APPLE__
|
||||
return "connectx() failed";
|
||||
#else
|
||||
return "sendto() failed";
|
||||
#endif
|
||||
#else
|
||||
FF_UNUSED(state);
|
||||
return "TFO support is not available";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Traditional connect and send function
|
||||
static const char* connectAndSend(FFNetworkingState* state)
|
||||
{
|
||||
static const char* connectAndSend(FFNetworkingState* state) {
|
||||
const char* ret = NULL;
|
||||
FF_DEBUG("Using traditional connection method to connect");
|
||||
|
||||
FF_DEBUG("Attempting connect() to server...");
|
||||
if(connect(state->sockfd, state->addr->ai_addr, state->addr->ai_addrlen) == -1)
|
||||
{
|
||||
if (connect(state->sockfd, state->addr->ai_addr, state->addr->ai_addrlen) == -1) {
|
||||
FF_DEBUG("connect() failed: %s", strerror(errno));
|
||||
ret = "connect() failed";
|
||||
goto error;
|
||||
@@ -149,8 +154,7 @@ static const char* connectAndSend(FFNetworkingState* state)
|
||||
FF_DEBUG("connect() succeeded");
|
||||
|
||||
FF_DEBUG("Attempting to send %u bytes of data...", state->command.length);
|
||||
if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0)
|
||||
{
|
||||
if (send(state->sockfd, state->command.chars, state->command.length, 0) < 0) {
|
||||
FF_DEBUG("send() failed: %s", strerror(errno));
|
||||
ret = "send() failed";
|
||||
goto error;
|
||||
@@ -176,8 +180,7 @@ exit:
|
||||
FF_THREAD_ENTRY_DECL_WRAPPER(connectAndSend, FFNetworkingState*);
|
||||
|
||||
// Parallel DNS resolution and socket creation
|
||||
static const char* initNetworkingState(FFNetworkingState* state, const char* host, const char* path, const char* headers)
|
||||
{
|
||||
static const char* initNetworkingState(FFNetworkingState* state, const char* host, const char* path, const char* headers) {
|
||||
FF_DEBUG("Initializing network connection state: host=%s, path=%s", host, path);
|
||||
|
||||
// Initialize command and host information
|
||||
@@ -197,10 +200,10 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
|
||||
ffStrbufAppendS(&state->command, headers);
|
||||
ffStrbufAppendS(&state->command, "\r\n");
|
||||
|
||||
#ifdef FF_HAVE_THREADS
|
||||
#ifdef FF_HAVE_THREADS
|
||||
state->thread = 0;
|
||||
FF_DEBUG("Thread ID initialized to 0");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const char* ret = NULL;
|
||||
|
||||
@@ -214,8 +217,7 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
|
||||
// Use AI_NUMERICSERV flag to indicate the service is a numeric port, reducing parsing time
|
||||
|
||||
int gaiRes = getaddrinfo(host, "80", &hints, &state->addr);
|
||||
if(gaiRes != 0)
|
||||
{
|
||||
if (gaiRes != 0) {
|
||||
FF_DEBUG("getaddrinfo() failed: %s (res=%d)", gai_strerror(gaiRes), gaiRes);
|
||||
ret = "getaddrinfo() failed";
|
||||
goto error;
|
||||
@@ -224,8 +226,7 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
|
||||
|
||||
FF_DEBUG("Creating socket");
|
||||
state->sockfd = socket(state->addr->ai_family, state->addr->ai_socktype, state->addr->ai_protocol);
|
||||
if(state->sockfd == -1)
|
||||
{
|
||||
if (state->sockfd == -1) {
|
||||
FF_DEBUG("socket() failed: %s", strerror(errno));
|
||||
ret = "socket() failed";
|
||||
goto error;
|
||||
@@ -233,57 +234,56 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
|
||||
FF_DEBUG("Socket creation successful: fd=%d", state->sockfd);
|
||||
|
||||
int flag = 1;
|
||||
#ifdef TCP_NODELAY
|
||||
#ifdef TCP_NODELAY
|
||||
// Disable Nagle's algorithm to reduce small packet transmission delay
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) != 0) {
|
||||
FF_DEBUG("Failed to set TCP_NODELAY: %s", strerror(errno));
|
||||
} else {
|
||||
FF_DEBUG("Successfully disabled Nagle's algorithm");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TCP_QUICKACK
|
||||
#ifdef TCP_QUICKACK
|
||||
// Set TCP_QUICKACK option to avoid delayed acknowledgments
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(flag)) != 0) {
|
||||
FF_DEBUG("Failed to set TCP_QUICKACK: %s", strerror(errno));
|
||||
} else {
|
||||
FF_DEBUG("Successfully enabled TCP quick acknowledgment");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (state->timeout > 0)
|
||||
{
|
||||
if (state->timeout > 0) {
|
||||
FF_DEBUG("Setting connection timeout: %u ms", state->timeout);
|
||||
FF_MAYBE_UNUSED uint32_t sec = state->timeout / 1000;
|
||||
if (sec == 0) sec = 1;
|
||||
FF_A_UNUSED uint32_t sec = state->timeout / 1000;
|
||||
if (sec == 0) {
|
||||
sec = 1;
|
||||
}
|
||||
|
||||
#ifdef TCP_CONNECTIONTIMEOUT
|
||||
#ifdef TCP_CONNECTIONTIMEOUT
|
||||
FF_DEBUG("Using TCP_CONNECTIONTIMEOUT: %u seconds", sec);
|
||||
setsockopt(state->sockfd, IPPROTO_TCP, TCP_CONNECTIONTIMEOUT, &sec, sizeof(sec));
|
||||
#elif defined(TCP_KEEPINIT)
|
||||
#elif defined(TCP_KEEPINIT)
|
||||
FF_DEBUG("Using TCP_KEEPINIT: %u seconds", sec);
|
||||
setsockopt(state->sockfd, IPPROTO_TCP, TCP_KEEPINIT, &sec, sizeof(sec));
|
||||
#elif defined(TCP_USER_TIMEOUT)
|
||||
#elif defined(TCP_USER_TIMEOUT)
|
||||
FF_DEBUG("Using TCP_USER_TIMEOUT: %u milliseconds", state->timeout);
|
||||
setsockopt(state->sockfd, IPPROTO_TCP, TCP_USER_TIMEOUT, &state->timeout, sizeof(state->timeout));
|
||||
#else
|
||||
#else
|
||||
FF_DEBUG("Current platform does not support TCP connection timeout");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
error:
|
||||
FF_DEBUG("Error occurred during initialization");
|
||||
if (state->addr != NULL)
|
||||
{
|
||||
if (state->addr != NULL) {
|
||||
FF_DEBUG("Releasing address information");
|
||||
freeaddrinfo(state->addr);
|
||||
state->addr = NULL;
|
||||
}
|
||||
|
||||
if (state->sockfd > 0)
|
||||
{
|
||||
if (state->sockfd > 0) {
|
||||
FF_DEBUG("Closing socket: fd=%d", state->sockfd);
|
||||
close(state->sockfd);
|
||||
state->sockfd = -1;
|
||||
@@ -291,31 +291,26 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers)
|
||||
{
|
||||
const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) {
|
||||
FF_DEBUG("Preparing to send HTTP request: host=%s, path=%s", host, path);
|
||||
|
||||
if (state->compression)
|
||||
{
|
||||
if (state->compression) {
|
||||
FF_DEBUG("Compression enabled, checking if zlib is available");
|
||||
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
const char* zlibError = ffNetworkingLoadZlibLibrary();
|
||||
// Only enable compression if zlib library is successfully loaded
|
||||
if (zlibError == NULL)
|
||||
{
|
||||
if (zlibError == NULL) {
|
||||
FF_DEBUG("Successfully loaded zlib library, compression enabled");
|
||||
} else {
|
||||
FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError);
|
||||
state->compression = false;
|
||||
}
|
||||
#else
|
||||
#else
|
||||
FF_DEBUG("zlib not supported at build time, compression disabled");
|
||||
state->compression = false;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
} else {
|
||||
FF_DEBUG("Compression disabled");
|
||||
}
|
||||
|
||||
@@ -333,33 +328,30 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
}
|
||||
FF_DEBUG("TryNonThreadingFastPath() failed: %s, trying traditional connection", tfoResult);
|
||||
|
||||
#ifdef FF_HAVE_THREADS
|
||||
if (instance.config.general.multithreading)
|
||||
{
|
||||
#ifdef FF_HAVE_THREADS
|
||||
if (instance.config.general.multithreading) {
|
||||
FF_DEBUG("Multithreading mode enabled, creating connection thread");
|
||||
state->thread = ffThreadCreate(connectAndSendThreadMain, state);
|
||||
if (state->thread) {
|
||||
FF_DEBUG("Thread creation successful: thread=%p", (void*)(uintptr_t)state->thread);
|
||||
FF_DEBUG("Thread creation successful: thread=%p", (void*) (uintptr_t) state->thread);
|
||||
return NULL;
|
||||
}
|
||||
FF_DEBUG("Thread creation failed");
|
||||
} else {
|
||||
FF_DEBUG("Multithreading mode disabled, connecting in main thread");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return connectAndSend(state);
|
||||
}
|
||||
|
||||
const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer)
|
||||
{
|
||||
const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer) {
|
||||
assert(buffer->allocated > 0);
|
||||
FF_DEBUG("Preparing to receive HTTP response");
|
||||
uint32_t timeout = state->timeout;
|
||||
|
||||
#ifdef FF_HAVE_THREADS
|
||||
if (state->thread)
|
||||
{
|
||||
#ifdef FF_HAVE_THREADS
|
||||
if (state->thread) {
|
||||
FF_DEBUG("Connection thread is running, waiting for it to complete (timeout=%u ms)", timeout);
|
||||
if (!ffThreadJoin(state->thread, timeout)) {
|
||||
FF_DEBUG("Thread join failed or timed out");
|
||||
@@ -368,10 +360,9 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
FF_DEBUG("Thread completed successfully");
|
||||
state->thread = 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if(state->sockfd == -1)
|
||||
{
|
||||
if (state->sockfd == -1) {
|
||||
FF_DEBUG("Invalid socket, HTTP request might have failed");
|
||||
return "ffNetworkingSendHttpRequest() failed";
|
||||
}
|
||||
@@ -380,24 +371,22 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
int rcvbuf = 65536; // 64KB
|
||||
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef __APPLE__
|
||||
// poll for the socket to be readable.
|
||||
// Because of the non-blocking connectx() call, the connection might not be established yet
|
||||
FF_DEBUG("Using poll() to check if socket is readable");
|
||||
{
|
||||
int pollRes = poll(&(struct pollfd) {
|
||||
.fd = state->sockfd,
|
||||
.events = POLLIN
|
||||
}, 1, timeout > 0 ? (int) timeout : -1);
|
||||
if (pollRes == 0)
|
||||
{
|
||||
.fd = state->sockfd,
|
||||
.events = POLLIN },
|
||||
1,
|
||||
timeout > 0 ? (int) timeout : -1);
|
||||
if (pollRes == 0) {
|
||||
FF_DEBUG("poll() timed out after %u ms", timeout);
|
||||
close(state->sockfd);
|
||||
state->sockfd = -1;
|
||||
return "poll() timeout";
|
||||
}
|
||||
else if (pollRes == -1)
|
||||
{
|
||||
} else if (pollRes == -1) {
|
||||
FF_DEBUG("poll() failed: %s", strerror(errno));
|
||||
close(state->sockfd);
|
||||
state->sockfd = -1;
|
||||
@@ -405,31 +394,31 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
}
|
||||
}
|
||||
FF_DEBUG("Socket is readable, proceeding to receive data");
|
||||
#else
|
||||
if(timeout > 0)
|
||||
{
|
||||
#else
|
||||
if (timeout > 0) {
|
||||
FF_DEBUG("Setting receive timeout: %u ms", timeout);
|
||||
struct timeval timev;
|
||||
timev.tv_sec = timeout / 1000;
|
||||
timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); //milliseconds to microseconds
|
||||
timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); // milliseconds to microseconds
|
||||
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (shutdown(state->sockfd, SHUT_WR) == -1)
|
||||
{
|
||||
if (shutdown(state->sockfd, SHUT_WR) == -1) {
|
||||
FF_DEBUG("Failed to shutdown socket send: %s", strerror(errno));
|
||||
// Not a critical error, continue anyway
|
||||
}
|
||||
|
||||
FF_DEBUG("Starting data reception");
|
||||
FF_MAYBE_UNUSED int recvCount = 0;
|
||||
FF_A_UNUSED int recvCount = 0;
|
||||
uint32_t contentLength = 0;
|
||||
uint32_t headerEnd = 0;
|
||||
|
||||
do {
|
||||
FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %u",
|
||||
++recvCount, buffer->length, ffStrbufGetFree(buffer));
|
||||
++recvCount,
|
||||
buffer->length,
|
||||
ffStrbufGetFree(buffer));
|
||||
|
||||
// We set `Connection: close`, so the server will close the connection when done.
|
||||
// Thus we can use MSG_WAITALL to wait until the buffer is full or the connection is closed.
|
||||
@@ -453,13 +442,13 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
if (headerEnd == 0) {
|
||||
char* pHeaderEnd = memmem(buffer->chars, buffer->length, "\r\n\r\n", 4);
|
||||
if (pHeaderEnd) {
|
||||
headerEnd = (uint32_t)(pHeaderEnd - buffer->chars);
|
||||
headerEnd = (uint32_t) (pHeaderEnd - buffer->chars);
|
||||
FF_DEBUG("Found HTTP header end marker, position: %u", headerEnd);
|
||||
|
||||
// Check for Content-Length header to pre-allocate enough memory
|
||||
const char* clHeader = strcasestr(buffer->chars, "Content-Length:");
|
||||
if (clHeader) {
|
||||
contentLength = (uint32_t) strtoul(clHeader + 16, NULL, 10);
|
||||
contentLength = (uint32_t) strtoul(clHeader + 15, NULL, 10);
|
||||
if (contentLength > 0) {
|
||||
FF_DEBUG("Detected Content-Length: %u, pre-allocating buffer", contentLength);
|
||||
// Ensure buffer is large enough, adding header size and some margin
|
||||
@@ -484,20 +473,20 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
FF_DEBUG("No HTTP header end marker found");
|
||||
return "No HTTP header end found";
|
||||
}
|
||||
|
||||
if (!ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n")) {
|
||||
FF_DEBUG("Invalid response: %.40s...", buffer->chars);
|
||||
return "Invalid response";
|
||||
}
|
||||
FF_DEBUG("Received valid HTTP 200 response, content %u bytes, total %u bytes", contentLength, buffer->length);
|
||||
|
||||
if (contentLength > 0 && buffer->length != contentLength + headerEnd + 4) {
|
||||
FF_DEBUG("Received content length mismatches: %u != %u", buffer->length, contentLength + headerEnd + 4);
|
||||
return "Content length mismatch";
|
||||
}
|
||||
|
||||
if (ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n")) {
|
||||
FF_DEBUG("Received valid HTTP 200 response, content %u bytes, total %u bytes", contentLength, buffer->length);
|
||||
} else {
|
||||
FF_DEBUG("Invalid response: %.40s...", buffer->chars);
|
||||
return "Invalid response";
|
||||
}
|
||||
|
||||
// If compression was used, try to decompress
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
// If compression was used, try to decompress
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
if (state->compression) {
|
||||
FF_DEBUG("Content received, checking if compressed");
|
||||
if (!ffNetworkingDecompressGzip(buffer, buffer->chars + headerEnd)) {
|
||||
@@ -507,7 +496,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
FF_DEBUG("Decompression successful or no decompression needed, total length after decompression: %u bytes", buffer->length);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <mswsock.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
//Must be included after <mswsock.h>
|
||||
// Must be included after <mswsock.h>
|
||||
#include "fastfetch.h"
|
||||
#include "common/networking.h"
|
||||
#include "common/stringUtils.h"
|
||||
@@ -9,23 +9,22 @@
|
||||
|
||||
static LPFN_CONNECTEX ConnectEx;
|
||||
|
||||
static const char* initWsaData(WSADATA* wsaData)
|
||||
{
|
||||
static const char* initWsaData(WSADATA* wsaData) {
|
||||
FF_DEBUG("Initializing WinSock");
|
||||
if(WSAStartup(MAKEWORD(2, 2), wsaData) != 0) {
|
||||
if (WSAStartup(MAKEWORD(2, 2), wsaData) != 0) {
|
||||
FF_DEBUG("WSAStartup() failed");
|
||||
return "WSAStartup() failed";
|
||||
}
|
||||
|
||||
if(LOBYTE(wsaData->wVersion) != 2 || HIBYTE(wsaData->wVersion) != 2) {
|
||||
if (LOBYTE(wsaData->wVersion) != 2 || HIBYTE(wsaData->wVersion) != 2) {
|
||||
FF_DEBUG("Invalid wsaData version found: %d.%d", LOBYTE(wsaData->wVersion), HIBYTE(wsaData->wVersion));
|
||||
WSACleanup();
|
||||
return "Invalid wsaData version found";
|
||||
}
|
||||
|
||||
//Dummy socket needed for WSAIoctl
|
||||
// Dummy socket needed for WSAIoctl
|
||||
SOCKET sockfd = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
|
||||
if(sockfd == INVALID_SOCKET) {
|
||||
if (sockfd == INVALID_SOCKET) {
|
||||
FF_DEBUG("WSASocketW(AF_INET, SOCK_STREAM) failed");
|
||||
WSACleanup();
|
||||
return "WSASocketW(AF_INET, SOCK_STREAM) failed";
|
||||
@@ -33,10 +32,7 @@ static const char* initWsaData(WSADATA* wsaData)
|
||||
|
||||
DWORD dwBytes;
|
||||
GUID guid = WSAID_CONNECTEX;
|
||||
if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
&guid, sizeof(guid),
|
||||
&ConnectEx, sizeof(ConnectEx),
|
||||
&dwBytes, NULL, NULL) != 0) {
|
||||
if (WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx), &dwBytes, NULL, NULL) != 0) {
|
||||
FF_DEBUG("WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER) failed");
|
||||
closesocket(sockfd);
|
||||
WSACleanup();
|
||||
@@ -49,45 +45,36 @@ static const char* initWsaData(WSADATA* wsaData)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers)
|
||||
{
|
||||
const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) {
|
||||
FF_DEBUG("Preparing to send HTTP request: host=%s, path=%s", host, path);
|
||||
|
||||
if (state->compression)
|
||||
{
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
if (state->compression) {
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
const char* zlibError = ffNetworkingLoadZlibLibrary();
|
||||
// Only enable compression if zlib library is successfully loaded
|
||||
if (zlibError == NULL)
|
||||
{
|
||||
if (zlibError == NULL) {
|
||||
FF_DEBUG("Successfully loaded zlib library, compression enabled");
|
||||
} else {
|
||||
FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError);
|
||||
state->compression = false;
|
||||
}
|
||||
#else
|
||||
#else
|
||||
FF_DEBUG("zlib not supported at build time, compression disabled");
|
||||
state->compression = false;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
} else {
|
||||
FF_DEBUG("Compression disabled");
|
||||
}
|
||||
|
||||
static WSADATA wsaData;
|
||||
if (wsaData.wVersion == 0)
|
||||
{
|
||||
if (wsaData.wVersion == 0) {
|
||||
const char* error = initWsaData(&wsaData);
|
||||
if (error != NULL)
|
||||
{
|
||||
if (error != NULL) {
|
||||
wsaData.wVersion = (WORD) -1;
|
||||
FF_DEBUG("WinSock initialization failed: %s", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else if (wsaData.wVersion == (WORD) -1)
|
||||
{
|
||||
} else if (wsaData.wVersion == (WORD) -1) {
|
||||
FF_DEBUG("WinSock initialization previously failed");
|
||||
return "initWsaData() failed before";
|
||||
}
|
||||
@@ -100,54 +87,52 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
};
|
||||
|
||||
wchar_t hostW[256];
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(hostW, (ULONG) sizeof(hostW), NULL, host, (ULONG) strlen(host) + 1)))
|
||||
{
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(hostW, (ULONG) sizeof(hostW), NULL, host, (ULONG) strlen(host) + 1))) {
|
||||
FF_DEBUG("Failed to convert host to wide string: %s", host);
|
||||
return "Failed to convert host to wide string";
|
||||
}
|
||||
|
||||
FF_DEBUG("Resolving address: %s (%s)", host, state->ipv6 ? "IPv6" : "IPv4");
|
||||
if(GetAddrInfoW(hostW, L"80", &hints, &addr) != 0)
|
||||
{
|
||||
if (GetAddrInfoW(hostW, L"80", &hints, &addr) != 0) {
|
||||
FF_DEBUG("GetAddrInfoW() failed");
|
||||
return "GetAddrInfoW() failed";
|
||||
}
|
||||
|
||||
state->sockfd = WSASocketW(addr->ai_family, addr->ai_socktype, addr->ai_protocol, NULL, 0, 0);
|
||||
if(state->sockfd == INVALID_SOCKET)
|
||||
{
|
||||
if (state->sockfd == INVALID_SOCKET) {
|
||||
FF_DEBUG("WSASocketW() failed");
|
||||
FreeAddrInfoW(addr);
|
||||
return "WSASocketW() failed";
|
||||
}
|
||||
|
||||
DWORD flag = 1;
|
||||
#ifdef TCP_NODELAY
|
||||
#ifdef TCP_NODELAY
|
||||
// Enable TCP_NODELAY to disable Nagle's algorithm
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)) != 0) {
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)) != 0) {
|
||||
FF_DEBUG("Failed to set TCP_NODELAY: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
} else {
|
||||
FF_DEBUG("Successfully disabled Nagle's algorithm");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Set timeout if needed
|
||||
if (state->timeout > 0) {
|
||||
FF_DEBUG("Setting connection timeout: %u ms", state->timeout);
|
||||
setsockopt(state->sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&state->timeout, sizeof(state->timeout));
|
||||
setsockopt(state->sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*) &state->timeout, sizeof(state->timeout));
|
||||
}
|
||||
|
||||
//ConnectEx requires the socket to be initially bound
|
||||
if((state->ipv6
|
||||
? bind(state->sockfd, (SOCKADDR *) &(struct sockaddr_in6) {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = in6addr_any,
|
||||
}, sizeof(struct sockaddr_in6))
|
||||
: bind(state->sockfd, (SOCKADDR *) &(struct sockaddr_in) {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = INADDR_ANY,
|
||||
}, sizeof(struct sockaddr_in))) != 0)
|
||||
{
|
||||
// ConnectEx requires the socket to be initially bound
|
||||
if ((state->ipv6
|
||||
? bind(state->sockfd, (SOCKADDR*) &(struct sockaddr_in6) {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = in6addr_any,
|
||||
},
|
||||
sizeof(struct sockaddr_in6))
|
||||
: bind(state->sockfd, (SOCKADDR*) &(struct sockaddr_in) {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = INADDR_ANY,
|
||||
},
|
||||
sizeof(struct sockaddr_in))) != 0) {
|
||||
FF_DEBUG("bind() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
closesocket(state->sockfd);
|
||||
FreeAddrInfoW(addr);
|
||||
@@ -156,7 +141,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
}
|
||||
|
||||
// Initialize overlapped structure with WSA event for asynchronous I/O
|
||||
state->overlapped = (OVERLAPPED){
|
||||
state->overlapped = (OVERLAPPED) {
|
||||
.hEvent = WSACreateEvent()
|
||||
};
|
||||
|
||||
@@ -185,49 +170,39 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
ffStrbufAppendS(&state->command, headers);
|
||||
ffStrbufAppendS(&state->command, "\r\n");
|
||||
|
||||
#ifdef TCP_FASTOPEN
|
||||
if (state->tfo)
|
||||
{
|
||||
#ifdef TCP_FASTOPEN
|
||||
if (state->tfo) {
|
||||
// Set TCP Fast Open
|
||||
flag = 1;
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&flag, sizeof(flag)) != 0) {
|
||||
if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*) &flag, sizeof(flag)) != 0) {
|
||||
FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
} else {
|
||||
FF_DEBUG("Successfully set TCP_FASTOPEN option");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
FF_DEBUG("TCP Fast Open disabled");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FF_DEBUG("Using ConnectEx to send %u bytes of data", state->command.length);
|
||||
DWORD sent = 0;
|
||||
BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen,
|
||||
state->command.chars, state->command.length, &sent, &state->overlapped);
|
||||
BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int) addr->ai_addrlen, state->command.chars, state->command.length, &sent, &state->overlapped);
|
||||
|
||||
FreeAddrInfoW(addr);
|
||||
addr = NULL;
|
||||
|
||||
if(!result)
|
||||
{
|
||||
if (WSAGetLastError() != WSA_IO_PENDING)
|
||||
{
|
||||
if (!result) {
|
||||
if (WSAGetLastError() != WSA_IO_PENDING) {
|
||||
FF_DEBUG("ConnectEx() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
WSACloseEvent(state->overlapped.hEvent);
|
||||
closesocket(state->sockfd);
|
||||
state->sockfd = INVALID_SOCKET;
|
||||
ffStrbufDestroy(&state->command);
|
||||
return "ConnectEx() failed";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
FF_DEBUG("ConnectEx() pending");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
FF_DEBUG("ConnectEx() succeeded, sent %u bytes of data", (unsigned) sent);
|
||||
}
|
||||
|
||||
@@ -235,31 +210,28 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer)
|
||||
{
|
||||
const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer) {
|
||||
assert(buffer->allocated > 0);
|
||||
FF_DEBUG("Preparing to receive HTTP response");
|
||||
|
||||
if (state->sockfd == INVALID_SOCKET)
|
||||
{
|
||||
if (state->sockfd == INVALID_SOCKET) {
|
||||
FF_DEBUG("Invalid socket, HTTP request might have failed");
|
||||
return "ffNetworkingSendHttpRequest() failed";
|
||||
}
|
||||
|
||||
uint32_t timeout = state->timeout;
|
||||
if (timeout > 0)
|
||||
{
|
||||
if (timeout > 0) {
|
||||
FF_DEBUG("WSAWaitForMultipleEvents with timeout: %u ms", timeout);
|
||||
DWORD result = WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, timeout, FALSE);
|
||||
if (result != WSA_WAIT_EVENT_0)
|
||||
{
|
||||
if (result != WSA_WAIT_EVENT_0) {
|
||||
if (result == WSA_WAIT_TIMEOUT) {
|
||||
FF_DEBUG("WSAWaitForMultipleEvents timed out");
|
||||
} else {
|
||||
FF_DEBUG("WSAWaitForMultipleEvents failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
}
|
||||
if (CancelIoEx((HANDLE) state->sockfd, &state->overlapped))
|
||||
if (CancelIoEx((HANDLE) state->sockfd, &state->overlapped)) {
|
||||
WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, 10, TRUE);
|
||||
}
|
||||
WSACloseEvent(state->overlapped.hEvent);
|
||||
closesocket(state->sockfd);
|
||||
ffStrbufDestroy(&state->command);
|
||||
@@ -268,8 +240,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
}
|
||||
|
||||
DWORD transfer, flags;
|
||||
if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags))
|
||||
{
|
||||
if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags)) {
|
||||
FF_DEBUG("WSAGetOverlappedResult failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
closesocket(state->sockfd);
|
||||
WSACloseEvent(state->overlapped.hEvent);
|
||||
@@ -281,46 +252,49 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
WSACloseEvent(state->overlapped.hEvent);
|
||||
state->overlapped.hEvent = NULL;
|
||||
|
||||
if (setsockopt(state->sockfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0)
|
||||
{
|
||||
if (setsockopt(state->sockfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) {
|
||||
FF_DEBUG("Failed to update connect context: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
// Not a critical error, continue anyway
|
||||
}
|
||||
|
||||
if (shutdown(state->sockfd, SD_SEND) == SOCKET_ERROR)
|
||||
{
|
||||
if (shutdown(state->sockfd, SD_SEND) == SOCKET_ERROR) {
|
||||
FF_DEBUG("Failed to shutdown socket send: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
// Not a critical error, continue anyway
|
||||
}
|
||||
|
||||
if(timeout > 0)
|
||||
{
|
||||
if (timeout > 0) {
|
||||
FF_DEBUG("Setting receive timeout: %u ms", timeout);
|
||||
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
|
||||
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
// Set larger receive buffer for better performance
|
||||
int rcvbuf = 65536; // 64KB
|
||||
if (setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&rcvbuf, sizeof(rcvbuf)))
|
||||
{
|
||||
if (setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, (const char*) &rcvbuf, sizeof(rcvbuf))) {
|
||||
FF_DEBUG("Failed to set SO_RCVBUF: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
|
||||
// Not a critical error, continue anyway
|
||||
}
|
||||
|
||||
FF_DEBUG("Starting data reception");
|
||||
FF_MAYBE_UNUSED int recvCount = 0;
|
||||
FF_A_UNUSED int recvCount = 0;
|
||||
uint32_t contentLength = 0;
|
||||
uint32_t headerEnd = 0;
|
||||
|
||||
do {
|
||||
FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %u",
|
||||
++recvCount, buffer->length, ffStrbufGetFree(buffer));
|
||||
++recvCount,
|
||||
buffer->length,
|
||||
ffStrbufGetFree(buffer));
|
||||
|
||||
DWORD received = 0, recvFlags = 0;
|
||||
int recvResult = WSARecv(state->sockfd, &(WSABUF) {
|
||||
.buf = buffer->chars + buffer->length,
|
||||
.len = (ULONG) ffStrbufGetFree(buffer),
|
||||
}, 1, &received, &recvFlags, NULL, NULL);
|
||||
.buf = buffer->chars + buffer->length,
|
||||
.len = (ULONG) ffStrbufGetFree(buffer),
|
||||
},
|
||||
1,
|
||||
&received,
|
||||
&recvFlags,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
if (recvResult == SOCKET_ERROR || received == 0) {
|
||||
if (recvResult == 0 && received == 0) {
|
||||
@@ -340,13 +314,13 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
if (headerEnd == 0) {
|
||||
char* pHeaderEnd = strstr(buffer->chars, "\r\n\r\n");
|
||||
if (pHeaderEnd) {
|
||||
headerEnd = (uint32_t)(pHeaderEnd - buffer->chars);
|
||||
headerEnd = (uint32_t) (pHeaderEnd - buffer->chars);
|
||||
FF_DEBUG("Found HTTP header end marker, position: %u", headerEnd);
|
||||
|
||||
// Check for Content-Length header to pre-allocate enough memory
|
||||
const char* clHeader = strcasestr(buffer->chars, "Content-Length:");
|
||||
if (clHeader) {
|
||||
contentLength = (uint32_t) strtoul(clHeader + 16, NULL, 10);
|
||||
contentLength = (uint32_t) strtoul(clHeader + 15, NULL, 10);
|
||||
if (contentLength > 0) {
|
||||
FF_DEBUG("Detected Content-Length: %u, pre-allocating buffer", contentLength);
|
||||
// Ensure buffer is large enough, adding header size and some margin
|
||||
@@ -358,7 +332,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
}
|
||||
} while (ffStrbufGetFree(buffer) > 0);
|
||||
|
||||
FF_DEBUG("Closing socket: fd=%u", (unsigned)state->sockfd);
|
||||
FF_DEBUG("Closing socket: fd=%u", (unsigned) state->sockfd);
|
||||
closesocket(state->sockfd);
|
||||
state->sockfd = INVALID_SOCKET;
|
||||
|
||||
@@ -371,21 +345,22 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
FF_DEBUG("No HTTP header end marker found");
|
||||
return "No HTTP header end found";
|
||||
}
|
||||
|
||||
if (!ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n")) {
|
||||
FF_DEBUG("Invalid response: %.40s...", buffer->chars);
|
||||
return "Invalid response";
|
||||
}
|
||||
FF_DEBUG("Received valid HTTP 200 response, content length: %u bytes, total length: %u bytes",
|
||||
contentLength,
|
||||
buffer->length);
|
||||
|
||||
if (contentLength > 0 && buffer->length != contentLength + headerEnd + 4) {
|
||||
FF_DEBUG("Received content length mismatches: %u != %u", buffer->length, contentLength + headerEnd + 4);
|
||||
return "Content length mismatch";
|
||||
}
|
||||
|
||||
if (ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n")) {
|
||||
FF_DEBUG("Received valid HTTP 200 response, content length: %u bytes, total length: %u bytes",
|
||||
contentLength, buffer->length);
|
||||
} else {
|
||||
FF_DEBUG("Invalid response: %.40s...", buffer->chars);
|
||||
return "Invalid response";
|
||||
}
|
||||
|
||||
// If compression was used, try to decompress
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
// If compression was used, try to decompress
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
if (state->compression) {
|
||||
FF_DEBUG("Content received, checking if compressed");
|
||||
if (!ffNetworkingDecompressGzip(buffer, buffer->chars + headerEnd)) {
|
||||
@@ -395,7 +370,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
|
||||
FF_DEBUG("Decompression successful or no decompression needed, total length after decompression: %u bytes", buffer->length);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+64
-99
@@ -3,35 +3,40 @@
|
||||
#include "common/color.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// Return start position of the inner key if the argument key belongs to the module specified, NULL otherwise
|
||||
const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName)
|
||||
{
|
||||
const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName) {
|
||||
assert(argumentKey && moduleName);
|
||||
|
||||
const char* subKey = argumentKey;
|
||||
if(!(subKey[0] == '-' && subKey[1] == '-'))
|
||||
if (!(subKey[0] == '-' && subKey[1] == '-')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
subKey += 2;
|
||||
uint32_t moduleNameLen = (uint32_t)strlen(moduleName);
|
||||
if(strncasecmp(subKey, moduleName, moduleNameLen) != 0)
|
||||
uint32_t moduleNameLen = (uint32_t) strlen(moduleName);
|
||||
if (strncasecmp(subKey, moduleName, moduleNameLen) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
subKey += moduleNameLen;
|
||||
|
||||
if(subKey[0] == '\0')
|
||||
if (subKey[0] == '\0') {
|
||||
return subKey;
|
||||
}
|
||||
|
||||
if(subKey[0] != '-')
|
||||
if (subKey[0] != '-') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
subKey += 1;
|
||||
|
||||
return subKey;
|
||||
}
|
||||
|
||||
void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer)
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer) {
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Error: usage: %s <str>\n", argumentKey);
|
||||
exit(477);
|
||||
}
|
||||
@@ -39,80 +44,70 @@ void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* b
|
||||
ffStrbufSetS(buffer, value);
|
||||
}
|
||||
|
||||
uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value) {
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
|
||||
exit(480);
|
||||
}
|
||||
|
||||
char* end;
|
||||
uint32_t num = (uint32_t) strtoul(value, &end, 10);
|
||||
if(*end != '\0')
|
||||
{
|
||||
unsigned long num = strtoul(value, &end, 10);
|
||||
if (value[0] == '-' || *end != '\0' || num > UINT32_MAX) {
|
||||
fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
|
||||
exit(479);
|
||||
}
|
||||
|
||||
return num;
|
||||
return (uint32_t) num;
|
||||
}
|
||||
|
||||
int32_t ffOptionParseInt32(const char* argumentKey, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
int32_t ffOptionParseInt32(const char* argumentKey, const char* value) {
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
|
||||
exit(480);
|
||||
}
|
||||
|
||||
char* end;
|
||||
int32_t num = (int32_t) strtol(value, &end, 10);
|
||||
if(*end != '\0')
|
||||
{
|
||||
long num = strtol(value, &end, 10);
|
||||
if (*end != '\0' || num < INT32_MIN || num > INT32_MAX) {
|
||||
fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
|
||||
exit(479);
|
||||
}
|
||||
|
||||
return num;
|
||||
return (int32_t) num;
|
||||
}
|
||||
|
||||
int ffOptionParseEnum(const char* argumentKey, const char* requestedKey, FFKeyValuePair pairs[])
|
||||
{
|
||||
if(requestedKey == NULL)
|
||||
{
|
||||
int ffOptionParseEnum(const char* argumentKey, const char* requestedKey, FFKeyValuePair pairs[]) {
|
||||
if (requestedKey == NULL) {
|
||||
fprintf(stderr, "Error: usage: %s <value>\n", argumentKey);
|
||||
exit(476);
|
||||
}
|
||||
|
||||
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair)
|
||||
{
|
||||
if(ffStrEqualsIgnCase(requestedKey, pPair->key))
|
||||
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
|
||||
if (ffStrEqualsIgnCase(requestedKey, pPair->key)) {
|
||||
return pPair->value;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Error: unknown %s value: %s\n", argumentKey, requestedKey);
|
||||
exit(478);
|
||||
}
|
||||
|
||||
bool ffOptionParseBoolean(const char* str)
|
||||
{
|
||||
bool ffOptionParseBoolean(const char* str) {
|
||||
return (
|
||||
!ffStrSet(str) ||
|
||||
ffStrEqualsIgnCase(str, "true") ||
|
||||
ffStrEqualsIgnCase(str, "yes") ||
|
||||
ffStrEqualsIgnCase(str, "on") ||
|
||||
ffStrEqualsIgnCase(str, "1")
|
||||
);
|
||||
ffStrEqualsIgnCase(str, "yes") ||
|
||||
ffStrEqualsIgnCase(str, "on") ||
|
||||
ffStrEqualsIgnCase(str, "1"));
|
||||
}
|
||||
|
||||
void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer)
|
||||
{
|
||||
if (!value || value[0] == '\0') return;
|
||||
void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) {
|
||||
if (!value || value[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If value is already an ANSI escape code, use it
|
||||
if (value[0] == '\e' && value[1] == '[')
|
||||
{
|
||||
if (value[0] == '\e' && value[1] == '[') {
|
||||
ffStrbufAppendS(buffer, value + 2);
|
||||
ffStrbufTrimRight(buffer, 'm');
|
||||
return;
|
||||
@@ -120,54 +115,28 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer)
|
||||
|
||||
ffStrbufEnsureFree(buffer, 63);
|
||||
|
||||
while(*value != '\0')
|
||||
{
|
||||
#define FF_APPEND_COLOR_CODE_COND(prefix, code) \
|
||||
if(ffStrStartsWithIgnCase(value, #prefix)) { ffStrbufAppendS(buffer, code); value += strlen(#prefix); continue; }
|
||||
#define FF_APPEND_COLOR_PROP_COND(prefix, prop) \
|
||||
if(ffStrStartsWithIgnCase(value, #prefix)) { if (instance.config.display.prop.length) ffStrbufAppend(buffer, &instance.config.display.prop); else ffStrbufAppendS(buffer, FF_COLOR_FG_DEFAULT); value += strlen(#prefix); continue; }
|
||||
while (*value != '\0') {
|
||||
#define FF_APPEND_COLOR_CODE_COND(prefix, code) \
|
||||
if (ffStrStartsWithIgnCase(value, #prefix)) { \
|
||||
ffStrbufAppendS(buffer, code); \
|
||||
value += strlen(#prefix); \
|
||||
continue; \
|
||||
}
|
||||
#define FF_APPEND_COLOR_PROP_COND(prefix, prop) \
|
||||
if (ffStrStartsWithIgnCase(value, #prefix)) { \
|
||||
if (instance.config.display.prop.length) ffStrbufAppend(buffer, &instance.config.display.prop); \
|
||||
else ffStrbufAppendS(buffer, FF_COLOR_FG_DEFAULT); \
|
||||
value += strlen(#prefix); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
if (ffCharIsEnglishAlphabet(value[0]))
|
||||
{
|
||||
if (ffCharIsEnglishAlphabet(value[0])) {
|
||||
FF_APPEND_COLOR_CODE_COND(reset_, FF_COLOR_MODE_RESET)
|
||||
else FF_APPEND_COLOR_CODE_COND(bold_, FF_COLOR_MODE_BOLD)
|
||||
else FF_APPEND_COLOR_CODE_COND(bright_, FF_COLOR_MODE_BOLD)
|
||||
else FF_APPEND_COLOR_CODE_COND(dim_, FF_COLOR_MODE_DIM)
|
||||
else FF_APPEND_COLOR_CODE_COND(italic_, FF_COLOR_MODE_ITALIC)
|
||||
else FF_APPEND_COLOR_CODE_COND(underline_, FF_COLOR_MODE_UNDERLINE)
|
||||
else FF_APPEND_COLOR_CODE_COND(blink_, FF_COLOR_MODE_BLINK)
|
||||
else FF_APPEND_COLOR_CODE_COND(inverse_, FF_COLOR_MODE_INVERSE)
|
||||
else FF_APPEND_COLOR_CODE_COND(hidden_, FF_COLOR_MODE_HIDDEN)
|
||||
else FF_APPEND_COLOR_CODE_COND(strike_, FF_COLOR_MODE_STRIKETHROUGH)
|
||||
else FF_APPEND_COLOR_CODE_COND(black, FF_COLOR_FG_BLACK)
|
||||
else FF_APPEND_COLOR_CODE_COND(red, FF_COLOR_FG_RED)
|
||||
else FF_APPEND_COLOR_CODE_COND(green, FF_COLOR_FG_GREEN)
|
||||
else FF_APPEND_COLOR_CODE_COND(yellow, FF_COLOR_FG_YELLOW)
|
||||
else FF_APPEND_COLOR_CODE_COND(blue, FF_COLOR_FG_BLUE)
|
||||
else FF_APPEND_COLOR_CODE_COND(magenta, FF_COLOR_FG_MAGENTA)
|
||||
else FF_APPEND_COLOR_CODE_COND(cyan, FF_COLOR_FG_CYAN)
|
||||
else FF_APPEND_COLOR_CODE_COND(white, FF_COLOR_FG_WHITE)
|
||||
else FF_APPEND_COLOR_CODE_COND(default, FF_COLOR_FG_DEFAULT)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_black, FF_COLOR_FG_LIGHT_BLACK)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_red, FF_COLOR_FG_LIGHT_RED)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_green, FF_COLOR_FG_LIGHT_GREEN)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_yellow, FF_COLOR_FG_LIGHT_YELLOW)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_blue, FF_COLOR_FG_LIGHT_BLUE)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_magenta, FF_COLOR_FG_LIGHT_MAGENTA)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_cyan, FF_COLOR_FG_LIGHT_CYAN)
|
||||
else FF_APPEND_COLOR_CODE_COND(light_white, FF_COLOR_FG_LIGHT_WHITE)
|
||||
else FF_APPEND_COLOR_PROP_COND(keys, colorKeys)
|
||||
else FF_APPEND_COLOR_PROP_COND(title, colorTitle)
|
||||
else FF_APPEND_COLOR_PROP_COND(output, colorOutput)
|
||||
else FF_APPEND_COLOR_PROP_COND(separator, colorSeparator)
|
||||
else
|
||||
{
|
||||
else FF_APPEND_COLOR_CODE_COND(bold_, FF_COLOR_MODE_BOLD) else FF_APPEND_COLOR_CODE_COND(bright_, FF_COLOR_MODE_BOLD) else FF_APPEND_COLOR_CODE_COND(dim_, FF_COLOR_MODE_DIM) else FF_APPEND_COLOR_CODE_COND(italic_, FF_COLOR_MODE_ITALIC) else FF_APPEND_COLOR_CODE_COND(underline_, FF_COLOR_MODE_UNDERLINE) else FF_APPEND_COLOR_CODE_COND(blink_, FF_COLOR_MODE_BLINK) else FF_APPEND_COLOR_CODE_COND(inverse_, FF_COLOR_MODE_INVERSE) else FF_APPEND_COLOR_CODE_COND(hidden_, FF_COLOR_MODE_HIDDEN) else FF_APPEND_COLOR_CODE_COND(strike_, FF_COLOR_MODE_STRIKETHROUGH) else FF_APPEND_COLOR_CODE_COND(black, FF_COLOR_FG_BLACK) else FF_APPEND_COLOR_CODE_COND(red, FF_COLOR_FG_RED) else FF_APPEND_COLOR_CODE_COND(green, FF_COLOR_FG_GREEN) else FF_APPEND_COLOR_CODE_COND(yellow, FF_COLOR_FG_YELLOW) else FF_APPEND_COLOR_CODE_COND(blue, FF_COLOR_FG_BLUE) else FF_APPEND_COLOR_CODE_COND(magenta, FF_COLOR_FG_MAGENTA) else FF_APPEND_COLOR_CODE_COND(cyan, FF_COLOR_FG_CYAN) else FF_APPEND_COLOR_CODE_COND(white, FF_COLOR_FG_WHITE) else FF_APPEND_COLOR_CODE_COND(default, FF_COLOR_FG_DEFAULT) else FF_APPEND_COLOR_CODE_COND(light_black, FF_COLOR_FG_LIGHT_BLACK) else FF_APPEND_COLOR_CODE_COND(light_red, FF_COLOR_FG_LIGHT_RED) else FF_APPEND_COLOR_CODE_COND(light_green, FF_COLOR_FG_LIGHT_GREEN) else FF_APPEND_COLOR_CODE_COND(light_yellow, FF_COLOR_FG_LIGHT_YELLOW) else FF_APPEND_COLOR_CODE_COND(light_blue, FF_COLOR_FG_LIGHT_BLUE) else FF_APPEND_COLOR_CODE_COND(light_magenta, FF_COLOR_FG_LIGHT_MAGENTA) else FF_APPEND_COLOR_CODE_COND(light_cyan, FF_COLOR_FG_LIGHT_CYAN) else FF_APPEND_COLOR_CODE_COND(light_white, FF_COLOR_FG_LIGHT_WHITE) else FF_APPEND_COLOR_PROP_COND(keys, colorKeys) else FF_APPEND_COLOR_PROP_COND(title, colorTitle) else FF_APPEND_COLOR_PROP_COND(output, colorOutput) else FF_APPEND_COLOR_PROP_COND(separator, colorSeparator) else {
|
||||
fprintf(stderr, "Error: invalid color code found: %s\n", value);
|
||||
exit(479);
|
||||
}
|
||||
}
|
||||
else if (value[0] == '@')
|
||||
{
|
||||
} else if (value[0] == '@') {
|
||||
// Xterm 256 color
|
||||
++value;
|
||||
char* pend = NULL;
|
||||
@@ -181,9 +150,7 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer)
|
||||
ffStrbufAppendUInt(buffer, color);
|
||||
value = pend;
|
||||
continue;
|
||||
}
|
||||
else if (value[0] == '#')
|
||||
{
|
||||
} else if (value[0] == '#') {
|
||||
// RGB color
|
||||
++value;
|
||||
char* pend = NULL;
|
||||
@@ -195,13 +162,11 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer)
|
||||
if (pend - value > 6) {
|
||||
fprintf(stderr, "Error: RGB color code too long: %s\n", value);
|
||||
exit(479);
|
||||
}
|
||||
else if (pend - value == 3) {
|
||||
} else if (pend - value == 3) {
|
||||
rgb = ((rgb & 0xF00) >> 8) * 0x110000 +
|
||||
((rgb & 0x0F0) >> 4) * 0x001100 +
|
||||
((rgb & 0x00F) >> 0) * 0x000011;
|
||||
}
|
||||
else if (pend - value != 6) {
|
||||
((rgb & 0x0F0) >> 4) * 0x001100 +
|
||||
((rgb & 0x00F) >> 0) * 0x000011;
|
||||
} else if (pend - value != 6) {
|
||||
fprintf(stderr, "Error: invalid RGB color code length: %s\n", value);
|
||||
exit(479);
|
||||
}
|
||||
@@ -214,7 +179,7 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer)
|
||||
ffStrbufAppendC(buffer, *value);
|
||||
++value;
|
||||
|
||||
#undef FF_APPEND_COLOR_CODE_COND
|
||||
#undef FF_APPEND_COLOR_PROP_COND
|
||||
#undef FF_APPEND_COLOR_CODE_COND
|
||||
#undef FF_APPEND_COLOR_PROP_COND
|
||||
}
|
||||
}
|
||||
|
||||
+47
-63
@@ -1,97 +1,87 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/parsing.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat"
|
||||
#endif
|
||||
|
||||
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch)
|
||||
{
|
||||
if(major->length > 0)
|
||||
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch) {
|
||||
if (major->length > 0) {
|
||||
ffStrbufAppend(buffer, major);
|
||||
else if(minor->length > 0 || patch->length > 0)
|
||||
} else if (minor->length > 0 || patch->length > 0) {
|
||||
ffStrbufAppendC(buffer, '1');
|
||||
}
|
||||
|
||||
if(minor->length == 0 && patch->length == 0)
|
||||
if (minor->length == 0 && patch->length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
|
||||
if(minor->length > 0)
|
||||
if (minor->length > 0) {
|
||||
ffStrbufAppend(buffer, minor);
|
||||
else if(patch->length > 0)
|
||||
} else if (patch->length > 0) {
|
||||
ffStrbufAppendC(buffer, '0');
|
||||
}
|
||||
|
||||
if(patch->length == 0)
|
||||
if (patch->length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufAppendC(buffer, '.');
|
||||
|
||||
ffStrbufAppend(buffer, patch);
|
||||
}
|
||||
|
||||
int8_t ffVersionCompare(const FFVersion* version1, const FFVersion* version2)
|
||||
{
|
||||
if(version1->major != version2->major)
|
||||
int8_t ffVersionCompare(const FFVersion* version1, const FFVersion* version2) {
|
||||
if (version1->major != version2->major) {
|
||||
return version1->major > version2->major ? 1 : -1;
|
||||
}
|
||||
|
||||
if(version1->minor != version2->minor)
|
||||
if (version1->minor != version2->minor) {
|
||||
return version1->minor > version2->minor ? 1 : -1;
|
||||
}
|
||||
|
||||
if(version1->patch != version2->patch)
|
||||
if (version1->patch != version2->patch) {
|
||||
return version1->patch > version2->patch ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ffVersionToPretty(const FFVersion* version, FFstrbuf* pretty)
|
||||
{
|
||||
if(version->major > 0 || version->minor > 0 || version->patch > 0)
|
||||
{
|
||||
void ffVersionToPretty(const FFVersion* version, FFstrbuf* pretty) {
|
||||
if (version->major > 0 || version->minor > 0 || version->patch > 0) {
|
||||
ffStrbufAppendUInt(pretty, version->major);
|
||||
}
|
||||
|
||||
if(version->minor > 0 || version->patch > 0)
|
||||
{
|
||||
if (version->minor > 0 || version->patch > 0) {
|
||||
ffStrbufAppendC(pretty, '.');
|
||||
ffStrbufAppendUInt(pretty, version->minor);
|
||||
}
|
||||
|
||||
if(version->patch > 0)
|
||||
{
|
||||
if (version->patch > 0) {
|
||||
ffStrbufAppendC(pretty, '.');
|
||||
ffStrbufAppendUInt(pretty, version->patch);
|
||||
}
|
||||
}
|
||||
|
||||
void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4)
|
||||
{
|
||||
if(gtk2->length > 0 && gtk3->length > 0 && gtk4->length > 0)
|
||||
{
|
||||
if((ffStrbufIgnCaseEqual(gtk2, gtk3)) && (ffStrbufIgnCaseEqual(gtk2, gtk4)))
|
||||
{
|
||||
void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4) {
|
||||
if (gtk2->length > 0 && gtk3->length > 0 && gtk4->length > 0) {
|
||||
if ((ffStrbufIgnCaseEqual(gtk2, gtk3)) && (ffStrbufIgnCaseEqual(gtk2, gtk4))) {
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK2/3/4]");
|
||||
}
|
||||
else if(ffStrbufIgnCaseEqual(gtk2, gtk3))
|
||||
{
|
||||
} else if (ffStrbufIgnCaseEqual(gtk2, gtk3)) {
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK2/3], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
else if(ffStrbufIgnCaseEqual(gtk3, gtk4))
|
||||
{
|
||||
} else if (ffStrbufIgnCaseEqual(gtk3, gtk4)) {
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK3/4]");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2], ");
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
@@ -99,49 +89,43 @@ void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, co
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
}
|
||||
else if(gtk2->length > 0 && gtk3->length > 0)
|
||||
{
|
||||
if(ffStrbufIgnCaseEqual(gtk2, gtk3))
|
||||
{
|
||||
} else if (gtk2->length > 0 && gtk3->length > 0) {
|
||||
if (ffStrbufIgnCaseEqual(gtk2, gtk3)) {
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK2/3]");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2], ");
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK3]");
|
||||
}
|
||||
}
|
||||
else if(gtk3->length > 0 && gtk4->length > 0)
|
||||
{
|
||||
if(ffStrbufIgnCaseEqual(gtk3, gtk4))
|
||||
{
|
||||
} else if (gtk2->length > 0 && gtk4->length > 0) {
|
||||
if (ffStrbufIgnCaseEqual(gtk2, gtk4)) {
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK2/4]");
|
||||
} else {
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
} else if (gtk3->length > 0 && gtk4->length > 0) {
|
||||
if (ffStrbufIgnCaseEqual(gtk3, gtk4)) {
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK3/4]");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK3], ");
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
}
|
||||
else if(gtk2->length > 0)
|
||||
{
|
||||
} else if (gtk2->length > 0) {
|
||||
ffStrbufAppend(buffer, gtk2);
|
||||
ffStrbufAppendS(buffer, " [GTK2]");
|
||||
}
|
||||
else if(gtk3->length > 0)
|
||||
{
|
||||
} else if (gtk3->length > 0) {
|
||||
ffStrbufAppend(buffer, gtk3);
|
||||
ffStrbufAppendS(buffer, " [GTK3]");
|
||||
}
|
||||
else if(gtk4->length > 0)
|
||||
{
|
||||
} else if (gtk4->length > 0) {
|
||||
ffStrbufAppend(buffer, gtk4);
|
||||
ffStrbufAppendS(buffer, " [GTK4]");
|
||||
}
|
||||
|
||||
+71
-118
@@ -3,44 +3,49 @@
|
||||
#include "common/arrayUtils.h"
|
||||
|
||||
#if !_WIN32
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
{
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) {
|
||||
char* path = getenv("PATH");
|
||||
if(!path)
|
||||
if (!path) {
|
||||
return "$PATH not set";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const bool appendExe = !ffStrEndsWithIgnCase(name, ".exe");
|
||||
#endif
|
||||
|
||||
for (char* token = path; *token; path = token + 1)
|
||||
{
|
||||
for (char* token = path; *token; path = token + 1) {
|
||||
token = strchr(path,
|
||||
#ifdef _WIN32
|
||||
';'
|
||||
#else
|
||||
':'
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
';'
|
||||
#else
|
||||
':'
|
||||
#endif
|
||||
);
|
||||
if (!token) token = path + strlen(path);
|
||||
if (!token) {
|
||||
token = path + strlen(path);
|
||||
}
|
||||
|
||||
ffStrbufSetNS(result, (uint32_t)(token - path), path);
|
||||
ffStrbufSetNS(result, (uint32_t) (token - path), path);
|
||||
ffStrbufEnsureEndsWithC(result,
|
||||
#ifdef _WIN32
|
||||
'\\'
|
||||
#else
|
||||
'/'
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
'\\'
|
||||
#else
|
||||
'/'
|
||||
#endif
|
||||
);
|
||||
ffStrbufAppendS(result, name);
|
||||
#ifdef _WIN32
|
||||
if (appendExe) ffStrbufAppendS(result, ".exe");
|
||||
if (!ffPathExists(result->chars, FF_PATHTYPE_FILE))
|
||||
#ifdef _WIN32
|
||||
if (appendExe) {
|
||||
ffStrbufAppendS(result, ".exe");
|
||||
}
|
||||
if (!ffPathExists(result->chars, FF_PATHTYPE_FILE)) {
|
||||
continue;
|
||||
#else
|
||||
if (access(result->chars, X_OK) != 0)
|
||||
}
|
||||
#else
|
||||
if (access(result->chars, X_OK) != 0) {
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -48,17 +53,15 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
return "Executable not found";
|
||||
}
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
#include <errno.h>
|
||||
#include <stdalign.h>
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
#include <errno.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
{
|
||||
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) {
|
||||
char buffer[MAX_PATH + 1];
|
||||
DWORD length = SearchPathA(NULL, name, ".exe", sizeof(buffer), buffer, NULL);
|
||||
if (length == 0)
|
||||
{
|
||||
if (length == 0) {
|
||||
ffStrbufClear(result);
|
||||
return "Executable not found";
|
||||
}
|
||||
@@ -66,10 +69,8 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int winerr2Errno(DWORD err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
static inline int winerr2Errno(DWORD err) {
|
||||
switch (err) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
case ERROR_INVALID_NAME:
|
||||
@@ -89,24 +90,20 @@ static inline int winerr2Errno(DWORD err)
|
||||
}
|
||||
}
|
||||
|
||||
char* frealpath(HANDLE hFile, char* resolved_name)
|
||||
{
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !hFile, false))
|
||||
{
|
||||
char* frealpath(HANDLE hFile, char* resolved_name) {
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !hFile, false)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t resolvedNameW[MAX_PATH + 4]; /* +4 for "\\\\?\\" prefix */
|
||||
DWORD lenW = GetFinalPathNameByHandleW(hFile, resolvedNameW, (DWORD)ARRAY_SIZE(resolvedNameW), FILE_NAME_NORMALIZED);
|
||||
DWORD lenW = GetFinalPathNameByHandleW(hFile, resolvedNameW, (DWORD) ARRAY_SIZE(resolvedNameW), FILE_NAME_NORMALIZED);
|
||||
|
||||
if (lenW == 0)
|
||||
{
|
||||
if (lenW == 0) {
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
if (lenW >= ARRAY_SIZE(resolvedNameW))
|
||||
{
|
||||
if (lenW >= ARRAY_SIZE(resolvedNameW)) {
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
@@ -115,66 +112,46 @@ char* frealpath(HANDLE hFile, char* resolved_name)
|
||||
wchar_t* srcW = resolvedNameW;
|
||||
DWORD srcLenW = lenW;
|
||||
|
||||
if (srcLenW >= 8 && wcsncmp(resolvedNameW, L"\\\\?\\UNC\\", 8) == 0)
|
||||
{
|
||||
if (srcLenW >= 8 && wcsncmp(resolvedNameW, L"\\\\?\\UNC\\", 8) == 0) {
|
||||
/* Convert "\\?\UNC\server\share" to "\\server\share" */
|
||||
srcW += 6;
|
||||
srcLenW -= 6;
|
||||
*srcW = L'\\';
|
||||
}
|
||||
else if (srcLenW >= 4 && wcsncmp(resolvedNameW, L"\\\\?\\", 4) == 0)
|
||||
{
|
||||
} else if (srcLenW >= 4 && wcsncmp(resolvedNameW, L"\\\\?\\", 4) == 0) {
|
||||
srcW += 4;
|
||||
srcLenW -= 4;
|
||||
}
|
||||
|
||||
if (resolved_name)
|
||||
{
|
||||
if (resolved_name) {
|
||||
ULONG outBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(resolved_name, MAX_PATH, &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
|
||||
{
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(resolved_name, MAX_PATH, &outBytes, srcW, (ULONG) (srcLenW * sizeof(wchar_t))))) {
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (outBytes > MAX_PATH)
|
||||
{
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return resolved_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
/* UTF-8 worst-case: up to 4 bytes per UTF-16 code unit */
|
||||
char tmp[(MAX_PATH + 4) * 4];
|
||||
ULONG outBytes = 0;
|
||||
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(tmp, (ULONG)sizeof(tmp), &outBytes, srcW, (ULONG)(srcLenW * sizeof(wchar_t)))))
|
||||
{
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(tmp, (ULONG) sizeof(tmp), &outBytes, srcW, (ULONG) (srcLenW * sizeof(wchar_t))))) {
|
||||
errno = E2BIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resolved_name = (char*)malloc(outBytes);
|
||||
if (!resolved_name)
|
||||
{
|
||||
resolved_name = (char*) malloc(outBytes);
|
||||
if (!resolved_name) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(resolved_name, tmp, outBytes);
|
||||
return resolved_name;
|
||||
}
|
||||
|
||||
return resolved_name;
|
||||
}
|
||||
|
||||
char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
|
||||
{
|
||||
if (!file_name)
|
||||
{
|
||||
char* realpath(const char* __restrict file_name, char* __restrict resolved_name) {
|
||||
if (!file_name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
@@ -182,8 +159,7 @@ char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
|
||||
wchar_t fileNameW[MAX_PATH];
|
||||
ULONG lenBytes = 0;
|
||||
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG)sizeof(fileNameW), &lenBytes, file_name, (ULONG)strlen(file_name) + 1)))
|
||||
{
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &lenBytes, file_name, (ULONG) strlen(file_name) + 1))) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
@@ -197,8 +173,7 @@ char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
@@ -206,18 +181,15 @@ char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
|
||||
return frealpath(hFile, resolved_name);
|
||||
}
|
||||
|
||||
ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz)
|
||||
{
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !buf || bufsiz == 0, false))
|
||||
{
|
||||
ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz) {
|
||||
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !buf || bufsiz == 0, false)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
alignas(REPARSE_DATA_BUFFER) BYTE reparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
DWORD bytesReturned = 0;
|
||||
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, NULL))
|
||||
{
|
||||
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, NULL)) {
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
@@ -226,59 +198,45 @@ ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz)
|
||||
const wchar_t* targetW = NULL;
|
||||
USHORT targetBytes = 0;
|
||||
|
||||
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
{
|
||||
if (rp->SymbolicLinkReparseBuffer.PrintNameLength > 0)
|
||||
{
|
||||
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
|
||||
if (rp->SymbolicLinkReparseBuffer.PrintNameLength > 0) {
|
||||
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
(rp->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->SymbolicLinkReparseBuffer.PrintNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
targetW = rp->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
(rp->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (targetBytes >= 8 &&
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0)
|
||||
{
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0) {
|
||||
targetW += 4;
|
||||
targetBytes -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
|
||||
{
|
||||
if (rp->MountPointReparseBuffer.PrintNameLength > 0)
|
||||
{
|
||||
} else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
|
||||
if (rp->MountPointReparseBuffer.PrintNameLength > 0) {
|
||||
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
||||
(rp->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->MountPointReparseBuffer.PrintNameLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
targetW = rp->MountPointReparseBuffer.PathBuffer +
|
||||
(rp->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t));
|
||||
targetBytes = rp->MountPointReparseBuffer.SubstituteNameLength;
|
||||
|
||||
if (targetBytes >= 8 &&
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0)
|
||||
{
|
||||
wcsncmp(targetW, L"\\??\\", 4) == 0) {
|
||||
targetW += 4;
|
||||
targetBytes -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ULONG outBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(buf, (ULONG) bufsiz, &outBytes, targetW, targetBytes)))
|
||||
{
|
||||
if (!NT_SUCCESS(RtlUnicodeToUTF8N(buf, (ULONG) bufsiz, &outBytes, targetW, targetBytes))) {
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
@@ -287,18 +245,15 @@ ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz)
|
||||
return (ssize_t) outBytes;
|
||||
}
|
||||
|
||||
ssize_t readlink(const char* path, char* buf, size_t bufsiz)
|
||||
{
|
||||
if (!path || !buf || bufsiz == 0)
|
||||
{
|
||||
ssize_t readlink(const char* path, char* buf, size_t bufsiz) {
|
||||
if (!path || !buf || bufsiz == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
wchar_t pathW[MAX_PATH];
|
||||
ULONG pathWBytes = 0;
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), &pathWBytes, path, (ULONG) strlen(path) + 1)))
|
||||
{
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), &pathWBytes, path, (ULONG) strlen(path) + 1))) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
@@ -310,11 +265,9 @@ ssize_t readlink(const char* path, char* buf, size_t bufsiz)
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL
|
||||
);
|
||||
NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
errno = winerr2Errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
+110
-136
@@ -6,44 +6,42 @@
|
||||
#include "common/textModifier.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
static void appendOutputColor(FFstrbuf* buffer, const FFModuleArgs* module)
|
||||
{
|
||||
if (module->outputColor.length)
|
||||
static void appendOutputColor(FFstrbuf* buffer, const FFModuleArgs* module) {
|
||||
if (module->outputColor.length) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", module->outputColor.chars);
|
||||
else if (instance.config.display.colorOutput.length)
|
||||
} else if (instance.config.display.colorOutput.length) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", instance.config.display.colorOutput.chars);
|
||||
}
|
||||
}
|
||||
|
||||
const char* ffPercentParseTypeJsonConfig(yyjson_val* jsonVal, FFPercentageTypeFlags* result)
|
||||
{
|
||||
if (yyjson_is_uint(jsonVal))
|
||||
{
|
||||
const char* ffPercentParseTypeJsonConfig(yyjson_val* jsonVal, FFPercentageTypeFlags* result) {
|
||||
if (yyjson_is_uint(jsonVal)) {
|
||||
*result = (FFPercentageTypeFlags) yyjson_get_uint(jsonVal);
|
||||
return NULL;
|
||||
}
|
||||
if (yyjson_is_arr(jsonVal))
|
||||
{
|
||||
if (yyjson_is_arr(jsonVal)) {
|
||||
FFPercentageTypeFlags flags = 0;
|
||||
|
||||
yyjson_val* item;
|
||||
size_t idx, max;
|
||||
yyjson_arr_foreach(jsonVal, idx, max, item)
|
||||
{
|
||||
yyjson_arr_foreach (jsonVal, idx, max, item) {
|
||||
const char* flag = yyjson_get_str(item);
|
||||
if (!flag)
|
||||
if (!flag) {
|
||||
return "Error: percent.type: invalid flag string";
|
||||
if (ffStrEqualsIgnCase(flag, "num"))
|
||||
}
|
||||
if (ffStrEqualsIgnCase(flag, "num")) {
|
||||
flags |= FF_PERCENTAGE_TYPE_NUM_BIT;
|
||||
else if (ffStrEqualsIgnCase(flag, "bar"))
|
||||
} else if (ffStrEqualsIgnCase(flag, "bar")) {
|
||||
flags |= FF_PERCENTAGE_TYPE_BAR_BIT;
|
||||
else if (ffStrEqualsIgnCase(flag, "hide-others"))
|
||||
} else if (ffStrEqualsIgnCase(flag, "hide-others")) {
|
||||
flags |= FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT;
|
||||
else if (ffStrEqualsIgnCase(flag, "num-color"))
|
||||
} else if (ffStrEqualsIgnCase(flag, "num-color")) {
|
||||
flags |= FF_PERCENTAGE_TYPE_NUM_COLOR_BIT;
|
||||
else if (ffStrEqualsIgnCase(flag, "bar-monochrome"))
|
||||
} else if (ffStrEqualsIgnCase(flag, "bar-monochrome")) {
|
||||
flags |= FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT;
|
||||
else
|
||||
} else {
|
||||
return "Error: percent.type: unknown flag string";
|
||||
}
|
||||
}
|
||||
|
||||
*result = flags;
|
||||
@@ -53,8 +51,7 @@ const char* ffPercentParseTypeJsonConfig(yyjson_val* jsonVal, FFPercentageTypeFl
|
||||
return "Error: usage: percent.type must be a number or an array of strings";
|
||||
}
|
||||
|
||||
void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConfig config, const FFModuleArgs* module)
|
||||
{
|
||||
void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConfig config, const FFModuleArgs* module) {
|
||||
uint8_t green = config.green, yellow = config.yellow;
|
||||
assert(green <= 100 && yellow <= 100);
|
||||
|
||||
@@ -62,112 +59,100 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
|
||||
const bool borderAsValue = options->barBorderLeftElapsed.length && options->barBorderRightElapsed.length;
|
||||
|
||||
uint8_t blocksPercent = (uint8_t) (percent / 100.0 * options->barWidth + 0.5);
|
||||
assert(blocksPercent <= options->barWidth);
|
||||
|
||||
if(!borderAsValue && options->barBorderLeft.length)
|
||||
{
|
||||
if(!options->pipe && options->barColorBorder.length > 0)
|
||||
if (!borderAsValue && options->barBorderLeft.length) {
|
||||
if (!options->pipe && options->barColorBorder.length > 0) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
|
||||
}
|
||||
ffStrbufAppend(buffer, &options->barBorderLeft);
|
||||
}
|
||||
|
||||
if (percent == -DBL_MAX)
|
||||
{
|
||||
if (percent == -DBL_MAX) {
|
||||
// Use total color for simplification
|
||||
if(!options->pipe && options->barColorTotal.length > 0)
|
||||
if (!options->pipe && options->barColorTotal.length > 0) {
|
||||
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
|
||||
|
||||
for (uint8_t i = 0; i < options->barWidth; ++i)
|
||||
{
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0
|
||||
? &options->barBorderLeft
|
||||
: borderAsValue && i == options->barWidth - 1
|
||||
? &options->barBorderRight
|
||||
: &options->barCharTotal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (uint8_t i = 0; i < options->barWidth; ++i) {
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0 ? &options->barBorderLeft : borderAsValue && i == options->barWidth - 1 ? &options->barBorderRight
|
||||
: &options->barCharTotal);
|
||||
}
|
||||
} else {
|
||||
const char* colorGreen = options->percentColorGreen.chars;
|
||||
const char* colorYellow = options->percentColorYellow.chars;
|
||||
const char* colorRed = options->percentColorRed.chars;
|
||||
|
||||
FFPercentageTypeFlags percentType = config.type == 0 ? options->percentType : config.type;
|
||||
|
||||
uint8_t blocksPercent = (uint8_t) (percent / 100.0 * options->barWidth + 0.5);
|
||||
assert(blocksPercent <= options->barWidth);
|
||||
|
||||
bool autoColorElapsed = ffStrbufIgnCaseEqualS(&options->barColorElapsed, "auto");
|
||||
|
||||
bool monochrome = (percentType & FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT) || !autoColorElapsed;
|
||||
if (!options->pipe && options->barColorElapsed.length > 0 && monochrome)
|
||||
{
|
||||
if (!options->pipe && options->barColorElapsed.length > 0 && monochrome) {
|
||||
const char* color = NULL;
|
||||
if (!autoColorElapsed)
|
||||
if (!autoColorElapsed) {
|
||||
color = options->barColorElapsed.chars;
|
||||
else if (green <= yellow)
|
||||
{
|
||||
if (percent < green) color = colorGreen;
|
||||
else if (percent < yellow) color = colorYellow;
|
||||
else color = colorRed;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (percent < yellow) color = colorRed;
|
||||
else if (percent < green) color = colorYellow;
|
||||
else color = colorGreen;
|
||||
} else if (green <= yellow) {
|
||||
if (percent < green) {
|
||||
color = colorGreen;
|
||||
} else if (percent < yellow) {
|
||||
color = colorYellow;
|
||||
} else {
|
||||
color = colorRed;
|
||||
}
|
||||
} else {
|
||||
if (percent < yellow) {
|
||||
color = colorRed;
|
||||
} else if (percent < green) {
|
||||
color = colorYellow;
|
||||
} else {
|
||||
color = colorGreen;
|
||||
}
|
||||
}
|
||||
ffStrbufAppendF(buffer, "\e[%sm", color);
|
||||
}
|
||||
for (uint8_t i = 0; i < blocksPercent; ++i)
|
||||
{
|
||||
if (!options->pipe && options->barColorElapsed.length > 0 && !monochrome)
|
||||
{
|
||||
for (uint8_t i = 0; i < blocksPercent; ++i) {
|
||||
if (!options->pipe && options->barColorElapsed.length > 0 && !monochrome) {
|
||||
uint32_t section1Begin = (uint32_t) ((green <= yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
|
||||
uint32_t section2Begin = (uint32_t) ((green > yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
|
||||
if (i == section2Begin)
|
||||
if (i == section2Begin) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", (green > yellow ? colorGreen : colorRed));
|
||||
else if (i == section1Begin)
|
||||
} else if (i == section1Begin) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
|
||||
else if (i == 0)
|
||||
} else if (i == 0) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", (green <= yellow ? colorGreen : colorRed));
|
||||
}
|
||||
}
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0
|
||||
? &options->barBorderLeftElapsed
|
||||
: borderAsValue && i == options->barWidth - 1
|
||||
? &options->barBorderRightElapsed
|
||||
: &options->barCharElapsed);
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0 ? &options->barBorderLeftElapsed : borderAsValue && i == options->barWidth - 1 ? &options->barBorderRightElapsed
|
||||
: &options->barCharElapsed);
|
||||
}
|
||||
|
||||
if (blocksPercent < options->barWidth)
|
||||
{
|
||||
if(!options->pipe && options->barColorTotal.length > 0)
|
||||
if (blocksPercent < options->barWidth) {
|
||||
if (!options->pipe && options->barColorTotal.length > 0) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", options->barColorTotal.chars);
|
||||
for (uint8_t i = blocksPercent; i < options->barWidth; ++i)
|
||||
{
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0
|
||||
? &options->barBorderLeft
|
||||
: borderAsValue && i == options->barWidth - 1
|
||||
? &options->barBorderRight
|
||||
: &options->barCharTotal);
|
||||
}
|
||||
for (uint8_t i = blocksPercent; i < options->barWidth; ++i) {
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0 ? &options->barBorderLeft : borderAsValue && i == options->barWidth - 1 ? &options->barBorderRight
|
||||
: &options->barCharTotal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!borderAsValue && options->barBorderRight.length)
|
||||
{
|
||||
if(!options->pipe && options->barColorBorder.length > 0)
|
||||
if (!borderAsValue && options->barBorderRight.length) {
|
||||
if (!options->pipe && options->barColorBorder.length > 0) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
|
||||
}
|
||||
ffStrbufAppend(buffer, &options->barBorderRight);
|
||||
}
|
||||
|
||||
if(!options->pipe && (options->barColorElapsed.length > 0 || options->barColorTotal.length > 0 || options->barColorBorder.length > 0))
|
||||
{
|
||||
if (!options->pipe && (options->barColorElapsed.length > 0 || options->barColorTotal.length > 0 || options->barColorBorder.length > 0)) {
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
appendOutputColor(buffer, module);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConfig config, bool parentheses, const FFModuleArgs* module)
|
||||
{
|
||||
void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConfig config, bool parentheses, const FFModuleArgs* module) {
|
||||
uint8_t green = config.green, yellow = config.yellow;
|
||||
assert(green <= 100 && yellow <= 100);
|
||||
|
||||
@@ -176,61 +161,61 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
|
||||
bool colored = !!(percentType & FF_PERCENTAGE_TYPE_NUM_COLOR_BIT);
|
||||
|
||||
if (parentheses)
|
||||
if (parentheses) {
|
||||
ffStrbufAppendC(buffer, '(');
|
||||
}
|
||||
|
||||
if (colored && !options->pipe)
|
||||
{
|
||||
if (colored && !options->pipe) {
|
||||
const char* colorGreen = options->percentColorGreen.chars;
|
||||
const char* colorYellow = options->percentColorYellow.chars;
|
||||
const char* colorRed = options->percentColorRed.chars;
|
||||
|
||||
if(percent == -DBL_MAX)
|
||||
if (percent == -DBL_MAX) {
|
||||
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
|
||||
else if(green <= yellow)
|
||||
{
|
||||
if (percent > yellow)
|
||||
} else if (green <= yellow) {
|
||||
if (percent > yellow) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorRed);
|
||||
else if (percent > green)
|
||||
} else if (percent > green) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
|
||||
else
|
||||
} else {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (percent < yellow)
|
||||
}
|
||||
} else {
|
||||
if (percent < yellow) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorRed);
|
||||
else if (percent < green)
|
||||
} else if (percent < green) {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
|
||||
else
|
||||
} else {
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
ffStrbufAppendF(buffer, "%*.*f%s%%", options->percentWidth, options->percentNdigits, percent,
|
||||
options->percentSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
|
||||
if (percent == -DBL_MAX) {
|
||||
ffStrbufAppendS(buffer, "-");
|
||||
} else {
|
||||
ffStrbufAppendF(buffer, "%*.*f%s%%", options->percentWidth, options->percentNdigits, percent, options->percentSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
|
||||
}
|
||||
|
||||
if (colored && !options->pipe)
|
||||
{
|
||||
if (colored && !options->pipe) {
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
appendOutputColor(buffer, module);
|
||||
}
|
||||
|
||||
if (parentheses)
|
||||
if (parentheses) {
|
||||
ffStrbufAppendC(buffer, ')');
|
||||
}
|
||||
}
|
||||
|
||||
bool ffPercentParseCommandOptions(const char* key, const char* subkey, const char* value, FFPercentageModuleConfig* config)
|
||||
{
|
||||
if (!ffStrStartsWithIgnCase(subkey, "percent-"))
|
||||
bool ffPercentParseCommandOptions(const char* key, const char* subkey, const char* value, FFPercentageModuleConfig* config) {
|
||||
if (!ffStrStartsWithIgnCase(subkey, "percent-")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
subkey += strlen("percent-");
|
||||
|
||||
if (ffStrEqualsIgnCase(subkey, "green"))
|
||||
{
|
||||
if (ffStrEqualsIgnCase(subkey, "green")) {
|
||||
uint32_t num = ffOptionParseUInt32(key, value);
|
||||
if (num > 100)
|
||||
{
|
||||
if (num > 100) {
|
||||
fprintf(stderr, "Error: usage: %s must be between 0 and 100\n", key);
|
||||
exit(480);
|
||||
}
|
||||
@@ -238,11 +223,9 @@ bool ffPercentParseCommandOptions(const char* key, const char* subkey, const cha
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffStrEqualsIgnCase(subkey, "yellow"))
|
||||
{
|
||||
if (ffStrEqualsIgnCase(subkey, "yellow")) {
|
||||
uint32_t num = ffOptionParseUInt32(key, value);
|
||||
if (num > 100)
|
||||
{
|
||||
if (num > 100) {
|
||||
fprintf(stderr, "Error: usage: %s must be between 0 and 100\n", key);
|
||||
exit(480);
|
||||
}
|
||||
@@ -250,8 +233,7 @@ bool ffPercentParseCommandOptions(const char* key, const char* subkey, const cha
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffStrEqualsIgnCase(subkey, "type"))
|
||||
{
|
||||
if (ffStrEqualsIgnCase(subkey, "type")) {
|
||||
config->type = (FFPercentageTypeFlags) ffOptionParseUInt32(key, value);
|
||||
return true;
|
||||
}
|
||||
@@ -259,25 +241,22 @@ bool ffPercentParseCommandOptions(const char* key, const char* subkey, const cha
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config)
|
||||
{
|
||||
bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config) {
|
||||
assert(key);
|
||||
|
||||
if (!unsafe_yyjson_equals_str(key, "percent"))
|
||||
if (!unsafe_yyjson_equals_str(key, "percent")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!yyjson_is_obj(value))
|
||||
{
|
||||
if (!yyjson_is_obj(value)) {
|
||||
fprintf(stderr, "Error: usage: %s must be an object\n", unsafe_yyjson_get_str(key));
|
||||
exit(480);
|
||||
}
|
||||
|
||||
yyjson_val* greenVal = yyjson_obj_get(value, "green");
|
||||
if (greenVal)
|
||||
{
|
||||
if (greenVal) {
|
||||
int num = yyjson_get_int(greenVal);
|
||||
if (num < 0 || num > 100)
|
||||
{
|
||||
if (num < 0 || num > 100) {
|
||||
fputs("Error: usage: percent.green must be between 0 and 100\n", stderr);
|
||||
exit(480);
|
||||
}
|
||||
@@ -285,11 +264,9 @@ bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageMo
|
||||
}
|
||||
|
||||
yyjson_val* yellowVal = yyjson_obj_get(value, "yellow");
|
||||
if (yellowVal)
|
||||
{
|
||||
if (yellowVal) {
|
||||
int num = yyjson_get_int(yellowVal);
|
||||
if (num < 0 || num > 100)
|
||||
{
|
||||
if (num < 0 || num > 100) {
|
||||
fputs("Error: usage: percent.yellow must be between 0 and 100\n", stderr);
|
||||
exit(480);
|
||||
}
|
||||
@@ -297,11 +274,9 @@ bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageMo
|
||||
}
|
||||
|
||||
yyjson_val* typeVal = yyjson_obj_get(value, "type");
|
||||
if (typeVal)
|
||||
{
|
||||
if (typeVal) {
|
||||
const char* error = ffPercentParseTypeJsonConfig(typeVal, &config->type);
|
||||
if (error)
|
||||
{
|
||||
if (error) {
|
||||
fputs(error, stderr);
|
||||
exit(480);
|
||||
}
|
||||
@@ -310,8 +285,7 @@ bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageMo
|
||||
return true;
|
||||
}
|
||||
|
||||
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig config)
|
||||
{
|
||||
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig config) {
|
||||
yyjson_mut_val* percent = yyjson_mut_obj_add_obj(doc, module, "percent");
|
||||
yyjson_mut_obj_add_uint(doc, percent, "green", config.green);
|
||||
yyjson_mut_obj_add_uint(doc, percent, "yellow", config.yellow);
|
||||
|
||||
+58
-57
@@ -3,145 +3,146 @@
|
||||
#include "common/textModifier.h"
|
||||
#include "logo/logo.h"
|
||||
|
||||
void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType)
|
||||
{
|
||||
void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType) {
|
||||
ffLogoPrintLine();
|
||||
|
||||
//This is used by --set-keyless, in this case we want neither the module name nor the separator
|
||||
if(moduleName == NULL)
|
||||
// This is used by --set-keyless, in this case we want neither the module name nor the separator
|
||||
if (moduleName == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
//This is used as a magic value for hiding keys
|
||||
if (!(moduleArgs && ffStrbufEqualS(&moduleArgs->key, " ")) && instance.config.display.keyType != FF_MODULE_KEY_TYPE_NONE)
|
||||
{
|
||||
// This is used as a magic value for hiding keys
|
||||
if (!(moduleArgs && ffStrbufEqualS(&moduleArgs->key, " ")) && instance.config.display.keyType != FF_MODULE_KEY_TYPE_NONE) {
|
||||
ffPrintCharTimes(' ', instance.config.display.keyPaddingLeft);
|
||||
|
||||
if(!instance.config.display.pipe)
|
||||
{
|
||||
if (!instance.config.display.pipe) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
if (instance.config.display.brightColor)
|
||||
if (instance.config.display.brightColor) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
|
||||
}
|
||||
|
||||
if(moduleArgs && !(printType & FF_PRINT_TYPE_NO_CUSTOM_KEY_COLOR) && moduleArgs->keyColor.length > 0)
|
||||
if (moduleArgs && !(printType & FF_PRINT_TYPE_NO_CUSTOM_KEY_COLOR) && moduleArgs->keyColor.length > 0) {
|
||||
ffPrintColor(&moduleArgs->keyColor);
|
||||
else
|
||||
} else {
|
||||
ffPrintColor(&instance.config.display.colorKeys);
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_ICON && moduleArgs && moduleArgs->keyIcon.length > 0)
|
||||
if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_ICON && moduleArgs && moduleArgs->keyIcon.length > 0) {
|
||||
ffStrbufWriteTo(&moduleArgs->keyIcon, stdout);
|
||||
}
|
||||
|
||||
if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_STRING)
|
||||
{
|
||||
if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_STRING) {
|
||||
ffPrintCharTimes(' ', instance.config.display.keyType >> FF_MODULE_KEY_TYPE_SPACE_SHIFT);
|
||||
|
||||
//NULL check is required for modules with custom keys, e.g. disk with the folder path
|
||||
if((printType & FF_PRINT_TYPE_NO_CUSTOM_KEY) || !moduleArgs || moduleArgs->key.length == 0)
|
||||
{
|
||||
// NULL check is required for modules with custom keys, e.g. disk with the folder path
|
||||
if ((printType & FF_PRINT_TYPE_NO_CUSTOM_KEY) || !moduleArgs || moduleArgs->key.length == 0) {
|
||||
fputs(moduleName, stdout);
|
||||
|
||||
if(moduleIndex > 0)
|
||||
if (moduleIndex > 0) {
|
||||
printf(" %hhu", moduleIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
|
||||
FF_PARSE_FORMAT_STRING_CHECKED(&key, &moduleArgs->key, ((FFformatarg[]) {
|
||||
FF_ARG(moduleIndex, "index"),
|
||||
FF_ARG(moduleArgs->keyIcon, "icon"),
|
||||
}));
|
||||
FF_ARG(moduleIndex, "index"),
|
||||
FF_ARG(moduleArgs->keyIcon, "icon"),
|
||||
}));
|
||||
ffStrbufWriteTo(&key, stdout);
|
||||
}
|
||||
}
|
||||
|
||||
if(!instance.config.display.pipe)
|
||||
{
|
||||
if (!instance.config.display.pipe) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
ffPrintColor(&instance.config.display.colorSeparator);
|
||||
}
|
||||
|
||||
ffStrbufWriteTo(&instance.config.display.keyValueSeparator, stdout);
|
||||
|
||||
if(!instance.config.display.pipe && instance.config.display.colorSeparator.length)
|
||||
if (!instance.config.display.pipe && instance.config.display.colorSeparator.length) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
|
||||
if (!(printType & FF_PRINT_TYPE_NO_CUSTOM_KEY_WIDTH))
|
||||
{
|
||||
if (!(printType & FF_PRINT_TYPE_NO_CUSTOM_KEY_WIDTH)) {
|
||||
uint32_t keyWidth = moduleArgs && moduleArgs->keyWidth > 0 ? moduleArgs->keyWidth : instance.config.display.keyWidth;
|
||||
if (keyWidth > 0)
|
||||
if (keyWidth > 0) {
|
||||
printf("\e[%uG", (unsigned) (keyWidth + instance.state.logoWidth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!instance.config.display.pipe)
|
||||
{
|
||||
if (!instance.config.display.pipe) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
if (moduleArgs && moduleArgs->outputColor.length)
|
||||
if (moduleArgs && moduleArgs->outputColor.length) {
|
||||
ffPrintColor(&moduleArgs->outputColor);
|
||||
else if (instance.config.display.colorOutput.length)
|
||||
} else if (instance.config.display.colorOutput.length) {
|
||||
ffPrintColor(&instance.config.display.colorOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ffPrintFormat(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
void ffPrintFormat(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
if (moduleArgs)
|
||||
if (moduleArgs) {
|
||||
ffParseFormatString(&buffer, &moduleArgs->outputFormat, numArgs, arguments);
|
||||
else
|
||||
} else {
|
||||
ffStrbufAppendS(&buffer, "unknown");
|
||||
}
|
||||
|
||||
ffPrintLogoAndKey(moduleName, moduleIndex, moduleArgs, printType);
|
||||
ffStrbufPutTo(&buffer, stdout);
|
||||
}
|
||||
|
||||
void ffPrintError(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, const char* message, ...)
|
||||
{
|
||||
if(!instance.config.display.showErrors)
|
||||
void ffPrintError(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, const char* message, ...) {
|
||||
if (!instance.config.display.showErrors) {
|
||||
return;
|
||||
}
|
||||
|
||||
ffPrintLogoAndKey(moduleName, moduleIndex, moduleArgs, printType);
|
||||
|
||||
if(!instance.config.display.pipe)
|
||||
if (!instance.config.display.pipe) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stdout);
|
||||
}
|
||||
|
||||
va_list arguments;
|
||||
va_start(arguments, message);
|
||||
vprintf(message, arguments);
|
||||
va_end(arguments);
|
||||
|
||||
if(!instance.config.display.pipe)
|
||||
if (!instance.config.display.pipe) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void ffPrintColor(const FFstrbuf* colorValue)
|
||||
{
|
||||
//If the color is not set, this would reset in \033[m, which resets everything.
|
||||
//So we only print it, if the main color is at least one char.
|
||||
if(colorValue->length == 0)
|
||||
void ffPrintColor(const FFstrbuf* colorValue) {
|
||||
// If the color is not set, this would reset in \033[m, which resets everything.
|
||||
// So we only print it, if the main color is at least one char.
|
||||
if (colorValue->length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\e[%sm", colorValue->chars);
|
||||
}
|
||||
|
||||
void ffPrintCharTimes(char c, uint32_t times)
|
||||
{
|
||||
if(times == 0)
|
||||
void ffPrintCharTimes(char c, uint32_t times) {
|
||||
if (times == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(times == 1)
|
||||
{
|
||||
if (times == 1) {
|
||||
putchar(c);
|
||||
return;
|
||||
}
|
||||
|
||||
char str[32];
|
||||
memset(str, c, sizeof(str)); //2 instructions when compiling with AVX2 enabled
|
||||
for(uint32_t i = sizeof(str); i <= times; i += (uint32_t)sizeof(str))
|
||||
memset(str, c, sizeof(str)); // 2 instructions when compiling with AVX2 enabled
|
||||
for (uint32_t i = sizeof(str); i <= times; i += (uint32_t) sizeof(str)) {
|
||||
fwrite(str, 1, sizeof(str), stdout);
|
||||
}
|
||||
uint32_t remaining = times % sizeof(str);
|
||||
if(remaining > 0)
|
||||
if (remaining > 0) {
|
||||
fwrite(str, 1, remaining, stdout);
|
||||
}
|
||||
}
|
||||
|
||||
+266
-251
@@ -43,31 +43,30 @@ extern char** environ;
|
||||
|
||||
enum { FF_PIPE_BUFSIZ = 8192 };
|
||||
|
||||
static inline int ffPipe2(int* fds, int flags)
|
||||
{
|
||||
#ifndef FF_HAVE_PIPE2
|
||||
if(pipe(fds) == -1)
|
||||
return -1;
|
||||
fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | flags);
|
||||
fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | flags);
|
||||
return 0;
|
||||
#else
|
||||
return pipe2(fds, flags);
|
||||
#endif
|
||||
static inline int ffPipe2(int* fds, int flags) {
|
||||
#ifndef FF_HAVE_PIPE2
|
||||
if (pipe(fds) == -1) {
|
||||
return -1;
|
||||
}
|
||||
fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | flags);
|
||||
fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | flags);
|
||||
return 0;
|
||||
#else
|
||||
return pipe2(fds, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Not thread-safe
|
||||
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle)
|
||||
{
|
||||
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle) {
|
||||
int pipes[2];
|
||||
if(ffPipe2(pipes, O_CLOEXEC) == -1)
|
||||
if (ffPipe2(pipes, O_CLOEXEC) == -1) {
|
||||
return "pipe() failed";
|
||||
}
|
||||
|
||||
pid_t childPid = -1;
|
||||
int nullFile = ffGetNullFD();
|
||||
|
||||
#if !(__ANDROID__ || __OpenBSD__)
|
||||
#if !(__ANDROID__ || __OpenBSD__)
|
||||
|
||||
// NetBSD / Darwin: native syscall
|
||||
// Linux (glibc): clone3-execve
|
||||
@@ -83,33 +82,29 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
static char* oldLang = NULL;
|
||||
static int langIndex = -1;
|
||||
|
||||
if (langIndex >= 0)
|
||||
{
|
||||
if (langIndex >= 0) {
|
||||
// Found before
|
||||
if (oldLang) // oldLang was set only if it needed to be changed
|
||||
{
|
||||
if (environ[langIndex] != oldLang)
|
||||
{
|
||||
if (environ[langIndex] != oldLang) {
|
||||
// environ is changed outside of this function
|
||||
langIndex = -1;
|
||||
}
|
||||
else
|
||||
} else {
|
||||
environ[langIndex] = (char*) "LANG=C.UTF-8";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (langIndex < 0)
|
||||
{
|
||||
for (int i = 0; environ[i] != NULL; i++)
|
||||
{
|
||||
if (ffStrStartsWith(environ[i], "LANG="))
|
||||
{
|
||||
if (langIndex < 0) {
|
||||
for (int i = 0; environ[i] != NULL; i++) {
|
||||
if (ffStrStartsWith(environ[i], "LANG=")) {
|
||||
langIndex = i;
|
||||
const char* langValue = environ[i] + 5; // Skip "LANG="
|
||||
if (ffStrEqualsIgnCase(langValue, "C") ||
|
||||
ffStrStartsWithIgnCase(environ[i], "C.") ||
|
||||
ffStrEqualsIgnCase(langValue, "en_US") ||
|
||||
ffStrStartsWithIgnCase(langValue, "en_US."))
|
||||
ffStrStartsWithIgnCase(langValue, "en_US.")) {
|
||||
break; // No need to change LANG
|
||||
}
|
||||
oldLang = environ[i];
|
||||
environ[i] = (char*) "LANG=C.UTF-8"; // Set LANG to C.UTF-8 for consistent output
|
||||
break;
|
||||
@@ -119,33 +114,32 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
|
||||
int ret = posix_spawnp(&childPid, argv[0], &file_actions, NULL, argv, environ);
|
||||
|
||||
if (oldLang)
|
||||
if (oldLang) {
|
||||
environ[langIndex] = oldLang;
|
||||
}
|
||||
|
||||
posix_spawn_file_actions_destroy(&file_actions);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
if (ret != 0) {
|
||||
close(pipes[0]);
|
||||
close(pipes[1]);
|
||||
if (ret == ENOENT)
|
||||
if (ret == ENOENT) {
|
||||
return "command not found";
|
||||
}
|
||||
return "posix_spawnp() failed";
|
||||
}
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
// https://github.com/termux/termux-packages/issues/25369
|
||||
childPid = fork();
|
||||
if(childPid == -1)
|
||||
{
|
||||
if (childPid == -1) {
|
||||
close(pipes[0]);
|
||||
close(pipes[1]);
|
||||
return "fork() failed";
|
||||
}
|
||||
|
||||
if(childPid == 0)
|
||||
{
|
||||
if (childPid == 0) {
|
||||
// Child process
|
||||
dup2(pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO);
|
||||
dup2(nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO);
|
||||
@@ -154,7 +148,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
close(pipes[1]);
|
||||
outHandle->pid = childPid;
|
||||
@@ -162,8 +156,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
{
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
|
||||
assert(handle->pipeRead != -1);
|
||||
assert(handle->pid != -1);
|
||||
|
||||
@@ -174,20 +167,15 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
handle->pid = -1;
|
||||
char str[FF_PIPE_BUFSIZ];
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (timeout >= 0)
|
||||
{
|
||||
for (;;) {
|
||||
if (timeout >= 0) {
|
||||
struct pollfd pollfd = { childPipeFd, POLLIN, 0 };
|
||||
int pollret = poll(&pollfd, 1, timeout);
|
||||
if (pollret == 0)
|
||||
{
|
||||
if (pollret == 0) {
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)";
|
||||
}
|
||||
else if (pollret < 0 || (pollfd.revents & POLLERR))
|
||||
{
|
||||
} else if (pollret < 0 || (pollfd.revents & POLLERR)) {
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return pollret < 0
|
||||
@@ -197,63 +185,65 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
}
|
||||
|
||||
ssize_t nRead = read(childPipeFd, str, FF_PIPE_BUFSIZ);
|
||||
if (nRead > 0)
|
||||
if (nRead > 0) {
|
||||
ffStrbufAppendNS(buffer, (uint32_t) nRead, str);
|
||||
else if (nRead == 0)
|
||||
{
|
||||
} else if (nRead == 0) {
|
||||
int stat_loc = 0;
|
||||
if (childPid > 0 && waitpid(childPid, &stat_loc, 0) == childPid)
|
||||
{
|
||||
if (!WIFEXITED(stat_loc))
|
||||
if (childPid > 0 && waitpid(childPid, &stat_loc, 0) == childPid) {
|
||||
if (!WIFEXITED(stat_loc)) {
|
||||
return "child process exited abnormally";
|
||||
if (WEXITSTATUS(stat_loc) == 127)
|
||||
}
|
||||
if (WEXITSTATUS(stat_loc) == 127) {
|
||||
return "command not found";
|
||||
}
|
||||
// We only handle 127 as an error. See `getTerminalVersionUrxvt` in `terminalshell.c`
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else if (nRead < 0)
|
||||
} else if (nRead < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed";
|
||||
}
|
||||
|
||||
void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath)
|
||||
{
|
||||
void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath) {
|
||||
assert(processName->length > 0);
|
||||
ffStrbufClear(exe);
|
||||
if (exePath) ffStrbufClear(exePath);
|
||||
if (exePath) {
|
||||
ffStrbufClear(exePath);
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
|
||||
char filePath[64];
|
||||
snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", (int)pid);
|
||||
snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", (int) pid);
|
||||
|
||||
if(ffReadFileBuffer(filePath, exe))
|
||||
{
|
||||
if (ffReadFileBuffer(filePath, exe)) {
|
||||
const char* p = exe->chars;
|
||||
uint32_t len = (uint32_t) strlen(p);
|
||||
|
||||
if (len + 1 < exe->length)
|
||||
{
|
||||
if (len + 1 < exe->length) {
|
||||
const char* name = memrchr(p, '/', len);
|
||||
if (name) name++; else name = p;
|
||||
if (name) {
|
||||
name++;
|
||||
} else {
|
||||
name = p;
|
||||
}
|
||||
|
||||
// For interpreters, try to find the real script path in the arguments
|
||||
if (ffStrStartsWith(name, "python")
|
||||
#ifndef __ANDROID__
|
||||
#ifndef __ANDROID__
|
||||
|| ffStrEquals(name, "guile") // for shepherd
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#endif
|
||||
) {
|
||||
// `cmdline` always ends with a trailing '\0', and ffReadFileBuffer appends another \0
|
||||
// So `exe->chars` is always double '\0' terminated
|
||||
for (p = p + len + 1; *p && *p == '-'; p += strlen(p) + 1) // Skip arguments
|
||||
for (p = p + len + 1; *p && *p == '-'; p += strlen(p) + 1) { // Skip arguments
|
||||
assert(p - exe->chars < exe->allocated);
|
||||
if (*p)
|
||||
{
|
||||
}
|
||||
if (*p) {
|
||||
len = (uint32_t) strlen(p);
|
||||
memmove(exe->chars, p, len + 1);
|
||||
}
|
||||
@@ -262,12 +252,11 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
|
||||
|
||||
assert(len < exe->allocated);
|
||||
exe->length = len;
|
||||
ffStrbufTrimLeft(exe, '-'); //Login shells start with a dash
|
||||
ffStrbufTrimLeft(exe, '-'); // Login shells start with a dash
|
||||
}
|
||||
|
||||
if (exePath)
|
||||
{
|
||||
snprintf(filePath, sizeof(filePath), "/proc/%d/exe", (int)pid);
|
||||
if (exePath) {
|
||||
snprintf(filePath, sizeof(filePath), "/proc/%d/exe", (int) pid);
|
||||
char buf[PATH_MAX];
|
||||
ssize_t length = readlink(filePath, buf, PATH_MAX - 1);
|
||||
if (length > 0) // doesn't contain trailing NUL
|
||||
@@ -275,64 +264,68 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
|
||||
buf[length] = '\0';
|
||||
// When the process is a deleted executable, the resolved path is like `/usr/bin/app (deleted)`
|
||||
// But we can still access the binary via `/proc/pid/exe`. See #2136
|
||||
if (ffPathExists(buf, FF_PATHTYPE_ANY))
|
||||
ffStrbufSetNS(exePath, (uint32_t)length, buf);
|
||||
if (ffPathExists(buf, FF_PATHTYPE_ANY)) {
|
||||
ffStrbufSetNS(exePath, (uint32_t) length, buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (exePath->length == 0)
|
||||
if (exePath->length == 0) {
|
||||
ffStrbufSetS(exePath, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
size_t len = 0;
|
||||
int mibs[] = { CTL_KERN, KERN_PROCARGS2, pid };
|
||||
if (sysctl(mibs, ARRAY_SIZE(mibs), NULL, &len, NULL, 0) == 0)
|
||||
{// try get arg0
|
||||
//don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version.
|
||||
if (sysctl(mibs, ARRAY_SIZE(mibs), NULL, &len, NULL, 0) == 0) { // try get arg0
|
||||
// don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version.
|
||||
len++;
|
||||
FF_AUTO_FREE char* const procArgs2 = malloc(len);
|
||||
if (sysctl(mibs, ARRAY_SIZE(mibs), procArgs2, &len, NULL, 0) == 0)
|
||||
{
|
||||
if (sysctl(mibs, ARRAY_SIZE(mibs), procArgs2, &len, NULL, 0) == 0) {
|
||||
// https://gist.github.com/nonowarn/770696#file-getargv-c-L46
|
||||
uint32_t argc = *(uint32_t*) procArgs2;
|
||||
const char* realExePath = procArgs2 + sizeof(argc);
|
||||
|
||||
const char* arg0 = memchr(realExePath, '\0', len - (size_t) (realExePath - procArgs2));
|
||||
if (exePath)
|
||||
if (exePath) {
|
||||
ffStrbufSetNS(exePath, (uint32_t) (arg0 - realExePath), realExePath);
|
||||
|
||||
do arg0++; while (*arg0 == '\0');
|
||||
assert(arg0 < procArgs2 + len);
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
// #977
|
||||
const char* p = strrchr(arg0, '/');
|
||||
if (p)
|
||||
p++;
|
||||
else
|
||||
p = arg0;
|
||||
if (ffStrStartsWithIgnCase(p, "python")) // /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python /Users/carter/.local/bin/xonsh
|
||||
arg0 = p + strlen(p) + 1;
|
||||
}
|
||||
|
||||
if (*arg0 == '-') arg0++; // Login shells
|
||||
do {
|
||||
arg0++;
|
||||
} while (*arg0 == '\0');
|
||||
assert(arg0 < procArgs2 + len);
|
||||
|
||||
if (argc > 1) {
|
||||
// #977
|
||||
const char* p = strrchr(arg0, '/');
|
||||
if (p) {
|
||||
p++;
|
||||
} else {
|
||||
p = arg0;
|
||||
}
|
||||
if (ffStrStartsWithIgnCase(p, "python")) { // /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python /Users/carter/.local/bin/xonsh
|
||||
arg0 = p + strlen(p) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*arg0 == '-') {
|
||||
arg0++; // Login shells
|
||||
}
|
||||
|
||||
ffStrbufSetS(exe, arg0);
|
||||
}
|
||||
}
|
||||
|
||||
if (exePath || exe->length == 0)
|
||||
{
|
||||
if (exePath || exe->length == 0) {
|
||||
char buf[PROC_PIDPATHINFO_MAXSIZE];
|
||||
int length = proc_pidpath(pid, buf, ARRAY_SIZE(buf));
|
||||
if (length > 0)
|
||||
{
|
||||
if (exe->length == 0)
|
||||
if (length > 0) {
|
||||
if (exe->length == 0) {
|
||||
ffStrbufSetNS(exe, (uint32_t) length, buf);
|
||||
if (exePath)
|
||||
{
|
||||
}
|
||||
if (exePath) {
|
||||
// We don't use exec_path above as exePath because it's a relative path and can be different
|
||||
// from the actual executable being run (for example, when the original file is moved)
|
||||
ffStrbufSetNS(exePath, (uint32_t) length, buf);
|
||||
@@ -340,142 +333,159 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
|
||||
size_t size = ARG_MAX;
|
||||
FF_AUTO_FREE char* args = malloc(size);
|
||||
|
||||
static_assert(ARG_MAX > PATH_MAX, "");
|
||||
|
||||
if(exePath && sysctl(
|
||||
(int[]){CTL_KERN,
|
||||
#if __FreeBSD__
|
||||
KERN_PROC, KERN_PROC_PATHNAME, pid
|
||||
#else
|
||||
KERN_PROC_ARGS, pid, KERN_PROC_PATHNAME
|
||||
#endif
|
||||
}, 4,
|
||||
args, &size,
|
||||
NULL, 0
|
||||
) == 0)
|
||||
if (exePath && sysctl((int[]) { CTL_KERN,
|
||||
#if __FreeBSD__
|
||||
KERN_PROC,
|
||||
KERN_PROC_PATHNAME,
|
||||
pid
|
||||
#else
|
||||
KERN_PROC_ARGS,
|
||||
pid,
|
||||
KERN_PROC_PATHNAME
|
||||
#endif
|
||||
},
|
||||
4,
|
||||
args,
|
||||
&size,
|
||||
NULL,
|
||||
0) == 0)
|
||||
ffStrbufSetNS(exePath, (uint32_t) (size - 1), args);
|
||||
|
||||
size = ARG_MAX;
|
||||
if(sysctl(
|
||||
(int[]){CTL_KERN,
|
||||
#if __FreeBSD__
|
||||
KERN_PROC, KERN_PROC_ARGS, pid
|
||||
#else
|
||||
KERN_PROC_ARGS, pid, KERN_PROC_ARGV,
|
||||
#endif
|
||||
}, 4,
|
||||
args, &size,
|
||||
NULL, 0
|
||||
) == 0)
|
||||
{
|
||||
if (sysctl(
|
||||
(int[]) { CTL_KERN,
|
||||
#if __FreeBSD__
|
||||
KERN_PROC,
|
||||
KERN_PROC_ARGS,
|
||||
pid
|
||||
#else
|
||||
KERN_PROC_ARGS,
|
||||
pid,
|
||||
KERN_PROC_ARGV,
|
||||
#endif
|
||||
},
|
||||
4,
|
||||
args,
|
||||
&size,
|
||||
NULL,
|
||||
0) == 0) {
|
||||
char* arg0 = args;
|
||||
size_t arg0Len = strlen(args);
|
||||
if (size > arg0Len + 1)
|
||||
{
|
||||
if (size > arg0Len + 1) {
|
||||
char* p = (char*) memrchr(args, '/', arg0Len);
|
||||
if (p)
|
||||
if (p) {
|
||||
p++;
|
||||
else
|
||||
} else {
|
||||
p = arg0;
|
||||
}
|
||||
if (ffStrStartsWith(p, "python")) // /usr/local/bin/python3.9 /home/carter/.local/bin/xonsh
|
||||
{
|
||||
arg0 += arg0Len + 1;
|
||||
}
|
||||
}
|
||||
if (arg0[0] == '-') arg0++;
|
||||
if (arg0[0] == '-') {
|
||||
arg0++;
|
||||
}
|
||||
ffStrbufSetS(exe, arg0);
|
||||
}
|
||||
|
||||
#elif defined(__sun)
|
||||
#elif defined(__sun)
|
||||
|
||||
char filePath[128];
|
||||
snprintf(filePath, sizeof(filePath), "/proc/%d/psinfo", (int) pid);
|
||||
psinfo_t proc;
|
||||
if (ffReadFileData(filePath, sizeof(proc), &proc) == sizeof(proc))
|
||||
{
|
||||
if (ffReadFileData(filePath, sizeof(proc), &proc) == sizeof(proc)) {
|
||||
const char* args = proc.pr_psargs;
|
||||
if (args[0] == '-') ++args;
|
||||
if (args[0] == '-') {
|
||||
++args;
|
||||
}
|
||||
const char* end = strchr(args, ' ');
|
||||
ffStrbufSetNS(exe, end ? (uint32_t) (end - args) : (uint32_t) strlen(args), args);
|
||||
}
|
||||
|
||||
if (exePath)
|
||||
{
|
||||
if (exePath) {
|
||||
snprintf(filePath, sizeof(filePath), "/proc/%d/path/a.out", (int) pid);
|
||||
char buf[PATH_MAX];
|
||||
ssize_t length = readlink(filePath, buf, PATH_MAX - 1);
|
||||
if (length > 0) // doesn't contain trailing NUL
|
||||
{
|
||||
buf[length] = '\0';
|
||||
ffStrbufSetNS(exePath, (uint32_t)length, buf);
|
||||
ffStrbufSetNS(exePath, (uint32_t) length, buf);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
#elif defined(__OpenBSD__)
|
||||
|
||||
kvm_t* kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
|
||||
int count = 0;
|
||||
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &count);
|
||||
if (proc)
|
||||
{
|
||||
if (proc) {
|
||||
char** argv = kvm_getargv(kd, proc, 0);
|
||||
if (argv)
|
||||
{
|
||||
if (argv) {
|
||||
const char* arg0 = argv[0];
|
||||
if (arg0[0] == '-') arg0++;
|
||||
if (arg0[0] == '-') {
|
||||
arg0++;
|
||||
}
|
||||
ffStrbufSetS(exe, arg0);
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
|
||||
#elif defined(__HAIKU__)
|
||||
#elif defined(__HAIKU__)
|
||||
|
||||
image_info info;
|
||||
int32 cookie = 0;
|
||||
|
||||
while (get_next_image_info(pid, &cookie, &info) == B_OK)
|
||||
{
|
||||
if (info.type != B_APP_IMAGE) continue;
|
||||
while (get_next_image_info(pid, &cookie, &info) == B_OK) {
|
||||
if (info.type != B_APP_IMAGE) {
|
||||
continue;
|
||||
}
|
||||
ffStrbufSetS(exe, info.name);
|
||||
|
||||
if (exePath)
|
||||
if (exePath) {
|
||||
ffStrbufSet(exePath, exe);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if(exe->length == 0)
|
||||
if (exe->length == 0) {
|
||||
ffStrbufSet(exe, processName);
|
||||
}
|
||||
|
||||
assert(exe->length > 0);
|
||||
uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/');
|
||||
if(lastSlashIndex < exe->length)
|
||||
if (lastSlashIndex < exe->length) {
|
||||
*exeName = exe->chars + lastSlashIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, int32_t* tty)
|
||||
{
|
||||
if (pid <= 0)
|
||||
const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, int32_t* tty) {
|
||||
if (pid <= 0) {
|
||||
return "Invalid pid";
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
|
||||
char procFilePath[64];
|
||||
#if __linux__
|
||||
if (ppid || tty)
|
||||
#endif
|
||||
{
|
||||
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/stat", (int)pid);
|
||||
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/stat", (int) pid);
|
||||
char buf[PROC_FILE_BUFFSIZ];
|
||||
ssize_t nRead = ffReadFileData(procFilePath, sizeof(buf) - 1, buf);
|
||||
if(nRead <= 8)
|
||||
if (nRead <= 8) {
|
||||
return "ffReadFileData(/proc/pid/stat, PROC_FILE_BUFFSIZ-1, buf) failed";
|
||||
}
|
||||
buf[nRead] = '\0'; // pid (comm) state ppid pgrp session tty
|
||||
|
||||
const char* pState = NULL;
|
||||
@@ -483,66 +493,70 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
|
||||
{
|
||||
// comm in `/proc/pid/stat` is not encoded, and may contain ' ', ')' or even `\n`
|
||||
const char* start = memchr(buf, '(', (size_t) nRead);
|
||||
if (!start)
|
||||
if (!start) {
|
||||
return "memchr(stat, '(') failed";
|
||||
}
|
||||
start++;
|
||||
const char* end = memrchr(start, ')', (size_t) nRead - (size_t) (start - buf));
|
||||
if (!end)
|
||||
if (!end) {
|
||||
return "memrchr(stat, ')') failed";
|
||||
}
|
||||
ffStrbufSetNS(name, (uint32_t) (end - start), start);
|
||||
ffStrbufTrimRightSpace(name);
|
||||
if (name->chars[0] == '\0')
|
||||
if (name->chars[0] == '\0') {
|
||||
return "process name is empty";
|
||||
}
|
||||
pState = end + 2; // skip ") "
|
||||
}
|
||||
|
||||
#if !__linux__
|
||||
#if !__linux__
|
||||
if (ppid || tty)
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
int ppid_, tty_;
|
||||
if(sscanf(pState + 2, "%d %*d %*d %d", &ppid_, &tty_) < 2)
|
||||
if (sscanf(pState + 2, "%d %*d %*d %d", &ppid_, &tty_) < 2) {
|
||||
return "sscanf(stat) failed";
|
||||
}
|
||||
|
||||
if (ppid)
|
||||
if (ppid) {
|
||||
*ppid = (pid_t) ppid_;
|
||||
if (tty)
|
||||
}
|
||||
if (tty) {
|
||||
*tty = tty_ & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if __linux__
|
||||
else
|
||||
{
|
||||
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/comm", (int)pid);
|
||||
else {
|
||||
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/comm", (int) pid);
|
||||
ssize_t nRead = ffReadFileBuffer(procFilePath, name);
|
||||
if(nRead <= 0)
|
||||
if (nRead <= 0) {
|
||||
return "ffReadFileBuffer(/proc/pid/comm, name) failed";
|
||||
}
|
||||
ffStrbufTrimRightSpace(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
struct kinfo_proc proc;
|
||||
size_t size = sizeof(proc);
|
||||
if(sysctl(
|
||||
(int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4,
|
||||
&proc, &size,
|
||||
NULL, 0
|
||||
))
|
||||
if (sysctl(
|
||||
(int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, NULL, 0)) {
|
||||
return "sysctl(KERN_PROC_PID) failed";
|
||||
}
|
||||
|
||||
ffStrbufSetS(name, proc.kp_proc.p_comm); //trancated to 16 chars
|
||||
if (ppid)
|
||||
*ppid = (pid_t)proc.kp_eproc.e_ppid;
|
||||
if (tty)
|
||||
{
|
||||
ffStrbufSetS(name, proc.kp_proc.p_comm); // trancated to 16 chars
|
||||
if (ppid) {
|
||||
*ppid = (pid_t) proc.kp_eproc.e_ppid;
|
||||
}
|
||||
if (tty) {
|
||||
*tty = ((proc.kp_eproc.e_tdev >> 24) & 0xFF) == 0x10
|
||||
? proc.kp_eproc.e_tdev & 0xFFFFFF
|
||||
: -1;
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__)
|
||||
#elif defined(__FreeBSD__)
|
||||
|
||||
#ifdef __DragonFly__
|
||||
#define ki_comm kp_comm
|
||||
@@ -553,105 +567,106 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
|
||||
|
||||
struct kinfo_proc proc;
|
||||
size_t size = sizeof(proc);
|
||||
if(sysctl(
|
||||
(int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4,
|
||||
&proc, &size,
|
||||
NULL, 0
|
||||
))
|
||||
if (sysctl(
|
||||
(int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, NULL, 0)) {
|
||||
return "sysctl(KERN_PROC_PID) failed";
|
||||
|
||||
ffStrbufSetS(name, proc.ki_comm);
|
||||
if (ppid)
|
||||
*ppid = (pid_t)proc.ki_ppid;
|
||||
if (tty)
|
||||
{
|
||||
if (proc.ki_tdev != NODEV && proc.ki_flag & P_CONTROLT)
|
||||
{
|
||||
const char* ttyName = devname(proc.ki_tdev, S_IFCHR);
|
||||
if (ffStrStartsWith(ttyName, "pts/"))
|
||||
*tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10);
|
||||
else
|
||||
*tty = -1;
|
||||
}
|
||||
else
|
||||
*tty = -1;
|
||||
}
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
ffStrbufSetS(name, proc.ki_comm);
|
||||
if (ppid) {
|
||||
*ppid = (pid_t) proc.ki_ppid;
|
||||
}
|
||||
if (tty) {
|
||||
if (proc.ki_tdev != NODEV && proc.ki_flag & P_CONTROLT) {
|
||||
const char* ttyName = devname(proc.ki_tdev, S_IFCHR);
|
||||
if (ffStrStartsWith(ttyName, "pts/")) {
|
||||
*tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10);
|
||||
} else {
|
||||
*tty = -1;
|
||||
}
|
||||
} else {
|
||||
*tty = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
|
||||
struct kinfo_proc2 proc;
|
||||
size_t size = sizeof(proc);
|
||||
if(sysctl(
|
||||
(int[]){CTL_KERN, KERN_PROC2, KERN_PROC_PID, pid, sizeof(proc), 1}, 6,
|
||||
&proc, &size,
|
||||
NULL, 0
|
||||
) != 0)
|
||||
if (sysctl(
|
||||
(int[]) { CTL_KERN, KERN_PROC2, KERN_PROC_PID, pid, sizeof(proc), 1 }, 6, &proc, &size, NULL, 0) != 0) {
|
||||
return "sysctl(KERN_PROC_PID) failed";
|
||||
|
||||
ffStrbufSetS(name, proc.p_comm);
|
||||
if (ppid)
|
||||
*ppid = (pid_t)proc.p_ppid;
|
||||
if (tty)
|
||||
{
|
||||
if (proc.p_flag & P_CONTROLT)
|
||||
{
|
||||
const char* ttyName = devname(proc.p_tdev, S_IFCHR);
|
||||
if (ffStrStartsWith(ttyName, "pts/"))
|
||||
*tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10);
|
||||
else
|
||||
*tty = -1;
|
||||
}
|
||||
else
|
||||
*tty = -1;
|
||||
}
|
||||
|
||||
#elif defined(__sun)
|
||||
ffStrbufSetS(name, proc.p_comm);
|
||||
if (ppid) {
|
||||
*ppid = (pid_t) proc.p_ppid;
|
||||
}
|
||||
if (tty) {
|
||||
if (proc.p_flag & P_CONTROLT) {
|
||||
const char* ttyName = devname(proc.p_tdev, S_IFCHR);
|
||||
if (ffStrStartsWith(ttyName, "pts/")) {
|
||||
*tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10);
|
||||
} else {
|
||||
*tty = -1;
|
||||
}
|
||||
} else {
|
||||
*tty = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__sun)
|
||||
char path[128];
|
||||
snprintf(path, sizeof(path), "/proc/%d/psinfo", (int) pid);
|
||||
psinfo_t proc;
|
||||
if (ffReadFileData(path, sizeof(proc), &proc) != sizeof(proc))
|
||||
if (ffReadFileData(path, sizeof(proc), &proc) != sizeof(proc)) {
|
||||
return "ffReadFileData(psinfo) failed";
|
||||
}
|
||||
|
||||
ffStrbufSetS(name, proc.pr_fname);
|
||||
if (ppid)
|
||||
if (ppid) {
|
||||
*ppid = proc.pr_ppid;
|
||||
if (tty)
|
||||
}
|
||||
if (tty) {
|
||||
*tty = (int) proc.pr_ttydev;
|
||||
}
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
#elif defined(__OpenBSD__)
|
||||
|
||||
kvm_t* kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
|
||||
int count = 0;
|
||||
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &count);
|
||||
if (proc)
|
||||
{
|
||||
if (proc) {
|
||||
ffStrbufSetS(name, proc->p_comm);
|
||||
if (ppid)
|
||||
if (ppid) {
|
||||
*ppid = proc->p_ppid;
|
||||
if (tty)
|
||||
}
|
||||
if (tty) {
|
||||
*tty = (int) proc->p_tdev;
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
if (!proc)
|
||||
if (!proc) {
|
||||
return "kvm_getprocs() failed";
|
||||
}
|
||||
|
||||
#elif defined(__HAIKU__)
|
||||
#elif defined(__HAIKU__)
|
||||
|
||||
team_info info;
|
||||
if (get_team_info(pid, &info) == B_OK)
|
||||
{
|
||||
if (get_team_info(pid, &info) == B_OK) {
|
||||
ffStrbufSetS(name, info.name);
|
||||
if (ppid)
|
||||
if (ppid) {
|
||||
*ppid = info.parent;
|
||||
}
|
||||
}
|
||||
|
||||
FF_UNUSED(tty);
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
return "Unsupported platform";
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -11,45 +11,44 @@
|
||||
|
||||
enum { FF_PIPE_BUFSIZ = 8192 };
|
||||
|
||||
static void argvToCmdline(char* const argv[], FFstrbuf* result)
|
||||
{
|
||||
static void argvToCmdline(char* const argv[], FFstrbuf* result) {
|
||||
// From https://gist.github.com/jin-x/cdd641d98887524b091fb1f82a68717d
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY temp = ffStrbufCreate();
|
||||
for (int i = 0; argv[i] != NULL; i++)
|
||||
{
|
||||
for (int i = 0; argv[i] != NULL; i++) {
|
||||
ffStrbufSetS(&temp, argv[i]);
|
||||
// Add slash (\) before double quotes (") and duplicate slashes before it
|
||||
for (
|
||||
uint32_t pos = ffStrbufFirstIndexC(&temp, '"'), cnt;
|
||||
pos != temp.length;
|
||||
pos = ffStrbufNextIndexC(&temp, pos + cnt * 2, '"')
|
||||
) {
|
||||
pos = ffStrbufNextIndexC(&temp, pos + cnt * 2, '"')) {
|
||||
cnt = 1;
|
||||
while (pos > 0 && temp.chars[pos - 1] == '\\') { ++cnt, --pos; }
|
||||
ffStrbufInsertNC(&temp, pos, cnt, '\\');
|
||||
}
|
||||
|
||||
// Add quotes around string if whitespace chars are present (with slash duplicating at the end of string)
|
||||
if (ffStrbufFirstIndexS(&temp, " \t") != temp.length)
|
||||
{
|
||||
if (ffStrbufFirstIndexS(&temp, " \t") != temp.length) {
|
||||
uint32_t pos = temp.length;
|
||||
uint32_t cnt = 0;
|
||||
while (pos > 0 && temp.chars[pos - 1] == '\\') { ++cnt, --pos; }
|
||||
if (cnt > 0) ffStrbufAppendNC(&temp, cnt, '\\');
|
||||
if (cnt > 0) {
|
||||
ffStrbufAppendNC(&temp, cnt, '\\');
|
||||
}
|
||||
ffStrbufPrependC(&temp, '"');
|
||||
ffStrbufAppendC(&temp, '"');
|
||||
}
|
||||
|
||||
// Add space delimiter
|
||||
if (i > 0) ffStrbufAppendC(result, ' ');
|
||||
if (i > 0) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppend(result, &temp);
|
||||
ffStrbufClear(&temp);
|
||||
}
|
||||
}
|
||||
|
||||
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle)
|
||||
{
|
||||
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle) {
|
||||
const int32_t timeout = instance.config.general.processingTimeout;
|
||||
|
||||
wchar_t pipeName[32];
|
||||
@@ -64,39 +63,36 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
FF_PIPE_BUFSIZ,
|
||||
FF_PIPE_BUFSIZ,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
if (hChildPipeRead == INVALID_HANDLE_VALUE)
|
||||
NULL);
|
||||
if (hChildPipeRead == INVALID_HANDLE_VALUE) {
|
||||
return "CreateNamedPipeW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
|
||||
}
|
||||
|
||||
HANDLE hChildPipeWrite = CreateFileW(
|
||||
pipeName,
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
&(SECURITY_ATTRIBUTES){
|
||||
&(SECURITY_ATTRIBUTES) {
|
||||
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||
.lpSecurityDescriptor = NULL,
|
||||
.bInheritHandle = TRUE,
|
||||
},
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
if (hChildPipeWrite == INVALID_HANDLE_VALUE)
|
||||
NULL);
|
||||
if (hChildPipeWrite == INVALID_HANDLE_VALUE) {
|
||||
return "CreateFileW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION piProcInfo = {};
|
||||
STARTUPINFOW siStartInfo = {
|
||||
.cb = sizeof(siStartInfo),
|
||||
.dwFlags = STARTF_USESTDHANDLES,
|
||||
};
|
||||
if (useStdErr)
|
||||
{
|
||||
if (useStdErr) {
|
||||
siStartInfo.hStdOutput = ffGetNullFD();
|
||||
siStartInfo.hStdError = hChildPipeWrite;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
siStartInfo.hStdOutput = hChildPipeWrite;
|
||||
siStartInfo.hStdError = ffGetNullFD();
|
||||
}
|
||||
@@ -107,52 +103,51 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
argvToCmdline(argv, &buf);
|
||||
uint32_t cmdlineBytes = (buf.length + 1) * sizeof(wchar_t);
|
||||
cmdline = malloc(cmdlineBytes);
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(cmdline, cmdlineBytes, NULL, buf.chars, buf.length + 1)))
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(cmdline, cmdlineBytes, NULL, buf.chars, buf.length + 1))) {
|
||||
return "RtlUTF8ToUnicodeN() failed";
|
||||
}
|
||||
}
|
||||
|
||||
BOOL success = CreateProcessW(
|
||||
NULL, // application name
|
||||
cmdline, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
NULL, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&piProcInfo // receives PROCESS_INFORMATION
|
||||
NULL, // application name
|
||||
cmdline, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
NULL, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&piProcInfo // receives PROCESS_INFORMATION
|
||||
);
|
||||
|
||||
NtClose(hChildPipeWrite);
|
||||
if(!success)
|
||||
{
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||
if (!success) {
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||
return "command not found";
|
||||
}
|
||||
return "CreateProcessW() failed";
|
||||
}
|
||||
|
||||
NtClose(piProcInfo.hThread); // we don't need the thread handle
|
||||
outHandle->pid = piProcInfo.hProcess;
|
||||
outHandle->pipeRead = hChildPipeRead;
|
||||
outHandle->pid = piProcInfo.hProcess;
|
||||
outHandle->pipeRead = hChildPipeRead;
|
||||
hChildPipeRead = INVALID_HANDLE_VALUE; // ownership transferred, don't close it
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void terminateChildProcess(HANDLE hProcess, HANDLE hChildPipeRead, HANDLE hReadEvent, IO_STATUS_BLOCK* piosb)
|
||||
{
|
||||
static void terminateChildProcess(HANDLE hProcess, HANDLE hChildPipeRead, HANDLE hReadEvent, IO_STATUS_BLOCK* piosb) {
|
||||
IO_STATUS_BLOCK cancelIosb = {};
|
||||
if (NT_SUCCESS(NtCancelIoFileEx(hChildPipeRead, piosb, &cancelIosb)))
|
||||
{
|
||||
if (hReadEvent)
|
||||
if (NT_SUCCESS(NtCancelIoFileEx(hChildPipeRead, piosb, &cancelIosb))) {
|
||||
if (hReadEvent) {
|
||||
NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = -100000 }); // wait for cancellation to complete
|
||||
}
|
||||
}
|
||||
NtTerminateProcess(hProcess, 1);
|
||||
}
|
||||
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
{
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
|
||||
assert(handle->pipeRead != INVALID_HANDLE_VALUE);
|
||||
assert(handle->pid != INVALID_HANDLE_VALUE);
|
||||
|
||||
@@ -163,14 +158,14 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
handle->pid = INVALID_HANDLE_VALUE;
|
||||
handle->pipeRead = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (timeout >= 0 && !NT_SUCCESS(NtCreateEvent(&hReadEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE)))
|
||||
if (timeout >= 0 && !NT_SUCCESS(NtCreateEvent(&hReadEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) {
|
||||
return "NtCreateEvent() failed";
|
||||
}
|
||||
|
||||
char str[FF_PIPE_BUFSIZ];
|
||||
uint32_t nRead = 0;
|
||||
IO_STATUS_BLOCK iosb = {};
|
||||
do
|
||||
{
|
||||
do {
|
||||
NTSTATUS status = NtReadFile(
|
||||
hChildPipeRead,
|
||||
hReadEvent,
|
||||
@@ -180,33 +175,29 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
str,
|
||||
(ULONG) sizeof(str),
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
if (status == STATUS_PENDING)
|
||||
{
|
||||
switch (NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 }))
|
||||
{
|
||||
case STATUS_WAIT_0:
|
||||
status = iosb.Status;
|
||||
break;
|
||||
NULL);
|
||||
if (status == STATUS_PENDING) {
|
||||
switch (NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 })) {
|
||||
case STATUS_WAIT_0:
|
||||
status = iosb.Status;
|
||||
break;
|
||||
|
||||
case STATUS_TIMEOUT:
|
||||
{
|
||||
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
|
||||
return "NtReadFile(hChildPipeRead) timed out";
|
||||
}
|
||||
case STATUS_TIMEOUT: {
|
||||
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
|
||||
return "NtReadFile(hChildPipeRead) timed out";
|
||||
}
|
||||
|
||||
default:
|
||||
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
|
||||
return "NtWaitForSingleObject(hReadEvent) failed";
|
||||
default:
|
||||
terminateChildProcess(hProcess, hChildPipeRead, hReadEvent, &iosb);
|
||||
return "NtWaitForSingleObject(hReadEvent) failed";
|
||||
}
|
||||
}
|
||||
|
||||
if (status == STATUS_PIPE_BROKEN || status == STATUS_END_OF_FILE)
|
||||
if (status == STATUS_PIPE_BROKEN || status == STATUS_END_OF_FILE) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
if (!NT_SUCCESS(status)) {
|
||||
terminateChildProcess(hProcess, hChildPipeRead, NULL, &iosb);
|
||||
return "NtReadFile(hChildPipeRead) failed";
|
||||
}
|
||||
@@ -215,81 +206,75 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
ffStrbufAppendNS(buffer, nRead, str);
|
||||
} while (nRead > 0);
|
||||
|
||||
exit:
|
||||
{
|
||||
PROCESS_BASIC_INFORMATION info = {};
|
||||
ULONG size;
|
||||
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size)))
|
||||
{
|
||||
assert(size == sizeof(info));
|
||||
if (info.ExitStatus != STILL_ACTIVE && info.ExitStatus != 0)
|
||||
return "Child process exited with an error";
|
||||
exit: {
|
||||
PROCESS_BASIC_INFORMATION info = {};
|
||||
ULONG size;
|
||||
if (NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size))) {
|
||||
assert(size == sizeof(info));
|
||||
if (info.ExitStatus != STILL_ACTIVE && info.ExitStatus != 0) {
|
||||
return "Child process exited with an error";
|
||||
}
|
||||
else
|
||||
return "NtQueryInformationProcess(ProcessBasicInformation) failed";
|
||||
} else {
|
||||
return "NtQueryInformationProcess(ProcessBasicInformation) failed";
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui)
|
||||
{
|
||||
bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui) {
|
||||
FF_AUTO_CLOSE_FD HANDLE hProcess = NtCurrentProcess();
|
||||
if(pid != 0)
|
||||
{
|
||||
if (pid != 0) {
|
||||
if (!NT_SUCCESS(NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &(OBJECT_ATTRIBUTES) {
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
}, &(CLIENT_ID) { .UniqueProcess = (HANDLE)(uintptr_t) pid })))
|
||||
.Length = sizeof(OBJECT_ATTRIBUTES),
|
||||
},
|
||||
&(CLIENT_ID) { .UniqueProcess = (HANDLE) (uintptr_t) pid }))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(ppid)
|
||||
{
|
||||
if (ppid) {
|
||||
PROCESS_BASIC_INFORMATION info = {};
|
||||
ULONG size;
|
||||
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size)))
|
||||
{
|
||||
if (NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size))) {
|
||||
assert(size == sizeof(info));
|
||||
*ppid = (uint32_t)info.InheritedFromUniqueProcessId;
|
||||
}
|
||||
else
|
||||
*ppid = (uint32_t) info.InheritedFromUniqueProcessId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(exe)
|
||||
{
|
||||
if (exe) {
|
||||
// TODO: It's possible to query the command line with `NtQueryInformationProcess(60/*ProcessCommandLineInformation*/)` since Windows 8.1
|
||||
|
||||
alignas(UNICODE_STRING) uint8_t buffer[4096];
|
||||
ULONG size;
|
||||
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, &buffer, sizeof(buffer), &size)))
|
||||
{
|
||||
UNICODE_STRING* imagePath = (UNICODE_STRING*)buffer;
|
||||
if (NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, &buffer, sizeof(buffer), &size))) {
|
||||
UNICODE_STRING* imagePath = (UNICODE_STRING*) buffer;
|
||||
ffStrbufSetNWS(exe, imagePath->Length / sizeof(wchar_t), imagePath->Buffer);
|
||||
|
||||
if (exePath) ffStrbufSet(exePath, exe);
|
||||
if (exePath) {
|
||||
ffStrbufSet(exePath, exe);
|
||||
}
|
||||
|
||||
if (pname && exeName)
|
||||
{
|
||||
if (pname && exeName) {
|
||||
*exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1;
|
||||
ffStrbufSetS(pname, *exeName);
|
||||
}
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (gui)
|
||||
{
|
||||
if (gui) {
|
||||
SECTION_IMAGE_INFORMATION info = {};
|
||||
ULONG size;
|
||||
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageInformation, &info, sizeof(info), &size)))
|
||||
{
|
||||
if (NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageInformation, &info, sizeof(info), &size))) {
|
||||
assert(size == sizeof(info));
|
||||
*gui = info.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -9,58 +9,60 @@
|
||||
#include "common/windows/getline.h"
|
||||
#endif
|
||||
|
||||
bool ffParsePropLinePointer(const char** line, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
if(**line == '\0')
|
||||
bool ffParsePropLinePointer(const char** line, const char* start, FFstrbuf* buffer) {
|
||||
if (**line == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Skip any amount of whitespace at the begin of line
|
||||
while(**line == ' ' || **line == '\t')
|
||||
// Skip any amount of whitespace at the begin of line
|
||||
while (**line == ' ' || **line == '\t') {
|
||||
++(*line);
|
||||
}
|
||||
|
||||
while(*start != '\0')
|
||||
{
|
||||
while (*start != '\0') {
|
||||
// Any amount of whitespace in the format string matches any amount of whitespace in the line, even none
|
||||
if(*start == ' ' || *start == '\t')
|
||||
{
|
||||
while(*start == ' ' || *start == '\t')
|
||||
if (*start == ' ' || *start == '\t') {
|
||||
while (*start == ' ' || *start == '\t') {
|
||||
++start;
|
||||
}
|
||||
|
||||
while(**line == ' ' || **line == '\t')
|
||||
while (**line == ' ' || **line == '\t') {
|
||||
++(*line);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//Line doesn't match start, skip it
|
||||
if(tolower(**line) != tolower(*start) || **line == '\0')
|
||||
// Line doesn't match start, skip it
|
||||
if (tolower(**line) != tolower(*start) || **line == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Line and start match, continue testing
|
||||
// Line and start match, continue testing
|
||||
++(*line);
|
||||
++start;
|
||||
}
|
||||
|
||||
char valueEnd = '\n';
|
||||
|
||||
//Allow faster parsing of XML
|
||||
if(*(*line - 1) == '>')
|
||||
// Allow faster parsing of XML
|
||||
if (*(*line - 1) == '>') {
|
||||
valueEnd = '<';
|
||||
}
|
||||
|
||||
//Skip any amount of whitespace at the begin of the value
|
||||
while(**line == ' ' || **line == '\t')
|
||||
// Skip any amount of whitespace at the begin of the value
|
||||
while (**line == ' ' || **line == '\t') {
|
||||
++(*line);
|
||||
}
|
||||
|
||||
//Allow faster parsing of quotet values
|
||||
if(**line == '"' || **line == '\'')
|
||||
{
|
||||
// Allow faster parsing of quoted values
|
||||
if (**line == '"' || **line == '\'') {
|
||||
valueEnd = **line;
|
||||
++(*line);
|
||||
}
|
||||
|
||||
//Copy the value to the buffer
|
||||
while(**line != valueEnd && **line != '\n' && **line != '\0')
|
||||
{
|
||||
// Copy the value to the buffer
|
||||
while (**line != valueEnd && **line != '\n' && **line != '\0') {
|
||||
ffStrbufAppendC(buffer, **line);
|
||||
++(*line);
|
||||
}
|
||||
@@ -70,17 +72,17 @@ bool ffParsePropLinePointer(const char** line, const char* start, FFstrbuf* buff
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffParsePropLines(const char* lines, const char* start, FFstrbuf* buffer)
|
||||
{
|
||||
while(!ffParsePropLinePointer(&lines, start, buffer))
|
||||
{
|
||||
while(*lines != '\0' && *lines != '\n')
|
||||
bool ffParsePropLines(const char* lines, const char* start, FFstrbuf* buffer) {
|
||||
while (!ffParsePropLinePointer(&lines, start, buffer)) {
|
||||
while (*lines != '\0' && *lines != '\n') {
|
||||
++lines;
|
||||
}
|
||||
|
||||
if(*lines == '\0')
|
||||
if (*lines == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Skip '\n'
|
||||
// Skip '\n'
|
||||
++lines;
|
||||
}
|
||||
|
||||
@@ -91,81 +93,79 @@ bool ffParsePropLines(const char* lines, const char* start, FFstrbuf* buffer)
|
||||
// Buffers which already contain content are not overwritten
|
||||
// The last occurrence of start in the first file will be the one used
|
||||
|
||||
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries) {
|
||||
FF_AUTO_CLOSE_FILE FILE* file = fopen(filename, "r");
|
||||
if (file == NULL)
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool valueStorage[32];
|
||||
bool* unsetValues = valueStorage;
|
||||
|
||||
if (numQueries > ARRAY_SIZE(valueStorage))
|
||||
if (numQueries > ARRAY_SIZE(valueStorage)) {
|
||||
unsetValues = malloc(sizeof(bool) * numQueries);
|
||||
|
||||
bool allSet = true;
|
||||
for (uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
unsetValues[i] = queries[i].buffer->length == 0;
|
||||
if (unsetValues[i])
|
||||
allSet = false;
|
||||
}
|
||||
|
||||
if (!allSet)
|
||||
{
|
||||
bool allSet = true;
|
||||
for (uint32_t i = 0; i < numQueries; i++) {
|
||||
unsetValues[i] = queries[i].buffer->length == 0;
|
||||
if (unsetValues[i]) {
|
||||
allSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allSet) {
|
||||
FF_AUTO_FREE char* line = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
while (getline(&line, &len, file) != -1)
|
||||
{
|
||||
for(uint32_t i = 0; i < numQueries; i++)
|
||||
{
|
||||
if(!unsetValues[i])
|
||||
while (getline(&line, &len, file) != -1) {
|
||||
for (uint32_t i = 0; i < numQueries; i++) {
|
||||
if (!unsetValues[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t currentLength = queries[i].buffer->length;
|
||||
queries[i].buffer->length = 0;
|
||||
if(!ffParsePropLine(line, queries[i].start, queries[i].buffer))
|
||||
if (!ffParsePropLine(line, queries[i].start, queries[i].buffer)) {
|
||||
queries[i].buffer->length = currentLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(unsetValues != valueStorage)
|
||||
if (unsetValues != valueStorage) {
|
||||
free(unsetValues);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffParsePropFileHomeValues(const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
bool ffParsePropFileHomeValues(const char* relativeFile, uint32_t numQueries, FFpropquery* queries) {
|
||||
FF_STRBUF_AUTO_DESTROY absolutePath = ffStrbufCreateF("%s/%s", instance.state.platform.homeDir.chars, relativeFile);
|
||||
return ffParsePropFileValues(absolutePath.chars, numQueries, queries);
|
||||
}
|
||||
|
||||
bool ffParsePropFileListValues(const FFlist* list, const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
|
||||
{
|
||||
bool ffParsePropFileListValues(const FFlist* list, const char* relativeFile, uint32_t numQueries, FFpropquery* queries) {
|
||||
bool foundAFile = false;
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, dirPrefix, *list)
|
||||
{
|
||||
FF_LIST_FOR_EACH (FFstrbuf, dirPrefix, *list) {
|
||||
const uint32_t dirPrefixLength = dirPrefix->length;
|
||||
ffStrbufAppendS(dirPrefix, relativeFile);
|
||||
if(ffParsePropFileValues(dirPrefix->chars, numQueries, queries))
|
||||
if (ffParsePropFileValues(dirPrefix->chars, numQueries, queries)) {
|
||||
foundAFile = true;
|
||||
}
|
||||
ffStrbufSubstrBefore(dirPrefix, dirPrefixLength);
|
||||
|
||||
bool allSet = true;
|
||||
for(uint32_t k = 0; k < numQueries; k++)
|
||||
{
|
||||
if(queries[k].buffer->length == 0)
|
||||
{
|
||||
for (uint32_t k = 0; k < numQueries; k++) {
|
||||
if (queries[k].buffer->length == 0) {
|
||||
allSet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(allSet)
|
||||
if (allSet) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundAFile;
|
||||
|
||||
+275
-156
@@ -7,39 +7,38 @@
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FF_HAVE_GIO
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
typedef struct GVariantGetters
|
||||
{
|
||||
typedef struct GVariantGetters {
|
||||
FF_LIBRARY_SYMBOL(g_variant_dup_string)
|
||||
FF_LIBRARY_SYMBOL(g_variant_get_boolean)
|
||||
FF_LIBRARY_SYMBOL(g_variant_get_int32)
|
||||
FF_LIBRARY_SYMBOL(g_variant_unref)
|
||||
} GVariantGetters;
|
||||
|
||||
static FFvariant getGVariantValue(GVariant* variant, FFvarianttype type, const GVariantGetters* variantGetters)
|
||||
{
|
||||
static FFvariant getGVariantValue(GVariant* variant, FFvarianttype type, const GVariantGetters* variantGetters) {
|
||||
FFvariant result;
|
||||
|
||||
if(variant == NULL)
|
||||
if (variant == NULL) {
|
||||
result = FF_VARIANT_NULL;
|
||||
else if(type == FF_VARIANT_TYPE_STRING)
|
||||
result = (FFvariant) {.strValue = variantGetters->ffg_variant_dup_string(variant, NULL)}; // Dup string, so that variant itself can be freed
|
||||
else if(type == FF_VARIANT_TYPE_BOOL)
|
||||
result = (FFvariant) {.boolValue = (bool) variantGetters->ffg_variant_get_boolean(variant), .boolValueSet = true};
|
||||
else if(type == FF_VARIANT_TYPE_INT)
|
||||
result = (FFvariant) {.intValue = variantGetters->ffg_variant_get_int32(variant)};
|
||||
else
|
||||
} else if (type == FF_VARIANT_TYPE_STRING) {
|
||||
result = (FFvariant) { .strValue = variantGetters->ffg_variant_dup_string(variant, NULL) }; // Dup string, so that variant itself can be freed
|
||||
} else if (type == FF_VARIANT_TYPE_BOOL) {
|
||||
result = (FFvariant) { .boolValue = (bool) variantGetters->ffg_variant_get_boolean(variant), .boolValueSet = true };
|
||||
} else if (type == FF_VARIANT_TYPE_INT) {
|
||||
result = (FFvariant) { .intValue = variantGetters->ffg_variant_get_int32(variant) };
|
||||
} else {
|
||||
result = FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
if(variant)
|
||||
if (variant) {
|
||||
variantGetters->ffg_variant_unref(variant);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct GSettingsData
|
||||
{
|
||||
typedef struct GSettingsData {
|
||||
FF_LIBRARY_SYMBOL(g_settings_schema_source_lookup)
|
||||
FF_LIBRARY_SYMBOL(g_settings_schema_has_key)
|
||||
FF_LIBRARY_SYMBOL(g_settings_new_full)
|
||||
@@ -53,12 +52,10 @@ typedef struct GSettingsData
|
||||
bool inited;
|
||||
} GSettingsData;
|
||||
|
||||
static const GSettingsData* getGSettingsData(void)
|
||||
{
|
||||
static const GSettingsData* getGSettingsData(void) {
|
||||
static GSettingsData data;
|
||||
|
||||
if (!data.inited)
|
||||
{
|
||||
if (!data.inited) {
|
||||
data.inited = true;
|
||||
FF_LIBRARY_LOAD(libgsettings, NULL, "libgio-2.0" FF_LIBRARY_EXTENSION, 1);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR(libgsettings, data, g_settings_schema_source_lookup, NULL)
|
||||
@@ -75,56 +72,61 @@ static const GSettingsData* getGSettingsData(void)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR(libgsettings, data.variantGetters, g_variant_unref, NULL);
|
||||
|
||||
data.schemaSource = data.ffg_settings_schema_source_get_default();
|
||||
if (data.schemaSource)
|
||||
if (data.schemaSource) {
|
||||
libgsettings = NULL;
|
||||
}
|
||||
}
|
||||
if(!data.schemaSource)
|
||||
if (!data.schemaSource) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &data;
|
||||
}
|
||||
|
||||
FFvariant ffSettingsGetGSettings(const char* schemaName, const char* path, const char* key, FFvarianttype type)
|
||||
{
|
||||
FFvariant ffSettingsGetGSettings(const char* schemaName, const char* path, const char* key, FFvarianttype type) {
|
||||
const GSettingsData* data = getGSettingsData();
|
||||
if(data == NULL)
|
||||
if (data == NULL) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
GSettingsSchema* schema = data->ffg_settings_schema_source_lookup(data->schemaSource, schemaName, true);
|
||||
if(schema == NULL)
|
||||
if (schema == NULL) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
if(data->ffg_settings_schema_has_key(schema, key) == false)
|
||||
if (data->ffg_settings_schema_has_key(schema, key) == false) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
GSettings* settings = data->ffg_settings_new_full(schema, NULL, path);
|
||||
if(settings == NULL)
|
||||
if (settings == NULL) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
GVariant* variant = data->ffg_settings_get_value(settings, key);
|
||||
if(variant != NULL)
|
||||
if (variant != NULL) {
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
|
||||
variant = data->ffg_settings_get_user_value(settings, key);
|
||||
if(variant != NULL)
|
||||
if (variant != NULL) {
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
|
||||
variant = data->ffg_settings_get_default_value(settings, key);
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
#else //FF_HAVE_GIO
|
||||
FFvariant ffSettingsGetGSettings(const char* schemaName, const char* path, const char* key, FFvarianttype type)
|
||||
{
|
||||
#else // FF_HAVE_GIO
|
||||
FFvariant ffSettingsGetGSettings(const char* schemaName, const char* path, const char* key, FFvarianttype type) {
|
||||
FF_UNUSED(schemaName, path, key, type)
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#endif //FF_HAVE_GIO
|
||||
#endif // FF_HAVE_GIO
|
||||
|
||||
#ifdef FF_HAVE_DCONF
|
||||
#include <dconf.h>
|
||||
#include <dconf.h>
|
||||
|
||||
typedef struct DConfData
|
||||
{
|
||||
typedef struct DConfData {
|
||||
FF_LIBRARY_SYMBOL(dconf_client_read_full)
|
||||
FF_LIBRARY_SYMBOL(dconf_client_new)
|
||||
GVariantGetters variantGetters;
|
||||
@@ -133,12 +135,10 @@ typedef struct DConfData
|
||||
bool inited;
|
||||
} DConfData;
|
||||
|
||||
static const DConfData* getDConfData(void)
|
||||
{
|
||||
static const DConfData* getDConfData(void) {
|
||||
static DConfData data;
|
||||
|
||||
if (!data.inited)
|
||||
{
|
||||
if (!data.inited) {
|
||||
data.inited = true;
|
||||
|
||||
FF_LIBRARY_LOAD(libdconf, NULL, "libdconf" FF_LIBRARY_EXTENSION, 2);
|
||||
@@ -150,127 +150,127 @@ static const DConfData* getDConfData(void)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR(libdconf, data.variantGetters, g_variant_unref, NULL)
|
||||
|
||||
data.client = data.ffdconf_client_new();
|
||||
if (data.client)
|
||||
if (data.client) {
|
||||
libdconf = NULL;
|
||||
}
|
||||
}
|
||||
if(!data.client)
|
||||
if (!data.client) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &data;
|
||||
}
|
||||
|
||||
FFvariant ffSettingsGetDConf(const char* key, FFvarianttype type)
|
||||
{
|
||||
FFvariant ffSettingsGetDConf(const char* key, FFvarianttype type) {
|
||||
const DConfData* data = getDConfData();
|
||||
if(data == NULL)
|
||||
if (data == NULL) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
GVariant* variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_FLAGS_NONE, NULL);
|
||||
if(variant != NULL)
|
||||
if (variant != NULL) {
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
|
||||
variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_USER_VALUE, NULL);
|
||||
if(variant != NULL)
|
||||
if (variant != NULL) {
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
|
||||
variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_DEFAULT_VALUE, NULL);
|
||||
return getGVariantValue(variant, type, &data->variantGetters);
|
||||
}
|
||||
#else //FF_HAVE_DCONF
|
||||
FFvariant ffSettingsGetDConf(const char* key, FFvarianttype type)
|
||||
{
|
||||
#else // FF_HAVE_DCONF
|
||||
FFvariant ffSettingsGetDConf(const char* key, FFvarianttype type) {
|
||||
FF_UNUSED(key, type)
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#endif //FF_HAVE_DCONF
|
||||
#endif // FF_HAVE_DCONF
|
||||
|
||||
FFvariant ffSettingsGetGnome(const char* dconfKey, const char* gsettingsSchemaName, const char* gsettingsPath, const char* gsettingsKey, FFvarianttype type)
|
||||
{
|
||||
FFvariant ffSettingsGetGnome(const char* dconfKey, const char* gsettingsSchemaName, const char* gsettingsPath, const char* gsettingsKey, FFvarianttype type) {
|
||||
FFvariant gsettings = ffSettingsGetGSettings(gsettingsSchemaName, gsettingsPath, gsettingsKey, type);
|
||||
|
||||
if(
|
||||
if (
|
||||
(type == FF_VARIANT_TYPE_BOOL && gsettings.boolValueSet) ||
|
||||
(type != FF_VARIANT_TYPE_BOOL && gsettings.strValue != NULL)
|
||||
) return gsettings;
|
||||
(type != FF_VARIANT_TYPE_BOOL && gsettings.strValue != NULL)) {
|
||||
return gsettings;
|
||||
}
|
||||
|
||||
return ffSettingsGetDConf(dconfKey, type);
|
||||
}
|
||||
|
||||
#ifdef FF_HAVE_DBUS
|
||||
#include "common/dbus.h"
|
||||
#include "common/dbus.h"
|
||||
|
||||
FFvariant ffSettingsGetXFConf(const char* channelName, const char* propertyName, FFvarianttype type)
|
||||
{
|
||||
FFvariant ffSettingsGetXFConf(const char* channelName, const char* propertyName, FFvarianttype type) {
|
||||
FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
|
||||
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL)
|
||||
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
DBusMessage* reply = ffDBusGetMethodReply(&dbus, "org.xfce.Xfconf", "/org/xfce/Xfconf", "org.xfce.Xfconf", "GetProperty", channelName, propertyName);
|
||||
if(!reply)
|
||||
if (!reply) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!dbus.lib->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
if (!dbus.lib->ffdbus_message_iter_init(reply, &rootIterator)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
if(type == FF_VARIANT_TYPE_INT)
|
||||
{
|
||||
int32_t value;
|
||||
if (ffDBusGetInt(&dbus, &rootIterator, &value))
|
||||
{
|
||||
if (type == FF_VARIANT_TYPE_INT) {
|
||||
int64_t value;
|
||||
if (ffDBusGetInt(&dbus, &rootIterator, &value)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return (FFvariant) { .intValue = value };
|
||||
return (FFvariant) { .intValue = (int32_t) value };
|
||||
}
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
if(type == FF_VARIANT_TYPE_STRING)
|
||||
{
|
||||
if (type == FF_VARIANT_TYPE_STRING) {
|
||||
FFstrbuf value = ffStrbufCreate();
|
||||
if (ffDBusGetString(&dbus, &rootIterator, &value))
|
||||
{
|
||||
if (ffDBusGetString(&dbus, &rootIterator, &value)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return (FFvariant) { .strValue = value.chars }; // Leaks value.chars
|
||||
}
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
if(type == FF_VARIANT_TYPE_BOOL)
|
||||
{
|
||||
if (type == FF_VARIANT_TYPE_BOOL) {
|
||||
bool value;
|
||||
if (ffDBusGetBool(&dbus, &rootIterator, &value))
|
||||
{
|
||||
if (ffDBusGetBool(&dbus, &rootIterator, &value)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return (FFvariant) { .boolValue = value, .boolValueSet = true };
|
||||
}
|
||||
}
|
||||
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
#define FF_DBUS_ITER_CONTINUE(dbus, iterator) \
|
||||
{ \
|
||||
if(!(dbus).lib->ffdbus_message_iter_next(iterator)) \
|
||||
break; \
|
||||
continue; \
|
||||
#define FF_DBUS_ITER_CONTINUE(dbus, iterator) \
|
||||
{ \
|
||||
if (!(dbus).lib->ffdbus_message_iter_next(iterator)) \
|
||||
break; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
FFvariant ffSettingsGetXFConfFirstMatch(const char* channelName, const char* propertyPrefix, FFvarianttype type, void* data, FFTestXfconfPropCallback* cb) {
|
||||
FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
|
||||
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
FFvariant ffSettingsGetXFConfFirstMatch(const char* channelName, const char* propertyPrefix, FFvarianttype type, void* data, FFTestXfconfPropCallback* cb)
|
||||
{
|
||||
FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
|
||||
if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL)
|
||||
return FF_VARIANT_NULL;
|
||||
|
||||
DBusMessage* reply = ffDBusGetMethodReply(&dbus, "org.xfce.Xfconf", "/org/xfce/Xfconf", "org.xfce.Xfconf", "GetAllProperties", channelName, propertyPrefix);
|
||||
if(!reply)
|
||||
if (!reply) {
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!dbus.lib->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
if (!dbus.lib->ffdbus_message_iter_init(reply, &rootIterator)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
@@ -278,10 +278,10 @@ FFvariant ffSettingsGetXFConfFirstMatch(const char* channelName, const char* pro
|
||||
DBusMessageIter arrayIterator;
|
||||
dbus.lib->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator);
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(dbus.lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY)
|
||||
while (true) {
|
||||
if (dbus.lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY) {
|
||||
FF_DBUS_ITER_CONTINUE(dbus, &arrayIterator)
|
||||
}
|
||||
|
||||
DBusMessageIter dictIterator;
|
||||
dbus.lib->ffdbus_message_iter_recurse(&arrayIterator, &dictIterator);
|
||||
@@ -289,64 +289,61 @@ FFvariant ffSettingsGetXFConfFirstMatch(const char* channelName, const char* pro
|
||||
const char* key;
|
||||
dbus.lib->ffdbus_message_iter_get_basic(&dictIterator, &key);
|
||||
|
||||
if (cb(data, key)) FF_DBUS_ITER_CONTINUE(dbus, &arrayIterator)
|
||||
if (cb(data, key)) {
|
||||
FF_DBUS_ITER_CONTINUE(dbus, &arrayIterator)
|
||||
}
|
||||
dbus.lib->ffdbus_message_iter_next(&dictIterator);
|
||||
|
||||
if(type == FF_VARIANT_TYPE_INT)
|
||||
{
|
||||
int32_t value;
|
||||
if (ffDBusGetInt(&dbus, &dictIterator, &value))
|
||||
{
|
||||
if (type == FF_VARIANT_TYPE_INT) {
|
||||
int64_t value;
|
||||
if (ffDBusGetInt(&dbus, &dictIterator, &value)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return (FFvariant) { .intValue = value };
|
||||
return (FFvariant) { .intValue = (int32_t) value };
|
||||
}
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
if(type == FF_VARIANT_TYPE_STRING)
|
||||
{
|
||||
if (type == FF_VARIANT_TYPE_STRING) {
|
||||
FFstrbuf value = ffStrbufCreate();
|
||||
if (ffDBusGetString(&dbus, &dictIterator, &value))
|
||||
{
|
||||
if (ffDBusGetString(&dbus, &dictIterator, &value)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return (FFvariant) { .strValue = value.chars }; // Leaks value.chars
|
||||
}
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
if(type == FF_VARIANT_TYPE_BOOL)
|
||||
{
|
||||
if (type == FF_VARIANT_TYPE_BOOL) {
|
||||
bool value;
|
||||
if (ffDBusGetBool(&dbus, &dictIterator, &value))
|
||||
{
|
||||
if (ffDBusGetBool(&dbus, &dictIterator, &value)) {
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return (FFvariant) { .boolValue = value, .boolValueSet = true };
|
||||
}
|
||||
}
|
||||
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
|
||||
dbus.lib->ffdbus_message_unref(reply);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#else //FF_HAVE_DBUS
|
||||
FFvariant ffSettingsGetXFConf(const char* channelName, const char* propertyName, FFvarianttype type)
|
||||
{
|
||||
#else // FF_HAVE_DBUS
|
||||
FFvariant ffSettingsGetXFConf(const char* channelName, const char* propertyName, FFvarianttype type) {
|
||||
FF_UNUSED(channelName, propertyName, type)
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
FFvariant ffSettingsGetXFConfFirstMatch(const char* channelName, const char* propertyPrefix, FFvarianttype type, void* data, FFTestXfconfPropCallback* cb)
|
||||
{
|
||||
FFvariant ffSettingsGetXFConfFirstMatch(const char* channelName, const char* propertyPrefix, FFvarianttype type, void* data, FFTestXfconfPropCallback* cb) {
|
||||
FF_UNUSED(channelName, propertyPrefix, type, data, cb);
|
||||
return FF_VARIANT_NULL;
|
||||
}
|
||||
#endif //FF_HAVE_DBUS
|
||||
#endif // FF_HAVE_DBUS
|
||||
|
||||
#ifdef FF_HAVE_SQLITE3
|
||||
#include <sqlite3.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
typedef struct SQLiteData
|
||||
{
|
||||
typedef struct SQLiteData {
|
||||
FF_LIBRARY_SYMBOL(sqlite3_open_v2)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_prepare_v2)
|
||||
FF_LIBRARY_SYMBOL(sqlite3_step)
|
||||
@@ -359,12 +356,10 @@ typedef struct SQLiteData
|
||||
bool inited;
|
||||
} SQLiteData;
|
||||
|
||||
static const SQLiteData* getSQLiteData(void)
|
||||
{
|
||||
static const SQLiteData* getSQLiteData(void) {
|
||||
static SQLiteData data;
|
||||
|
||||
if (!data.inited)
|
||||
{
|
||||
if (!data.inited) {
|
||||
data.inited = true;
|
||||
FF_LIBRARY_LOAD(libsqlite, NULL, "libsqlite3" FF_LIBRARY_EXTENSION, 1);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR(libsqlite, data, sqlite3_open_v2, NULL)
|
||||
@@ -378,34 +373,35 @@ static const SQLiteData* getSQLiteData(void)
|
||||
libsqlite = NULL;
|
||||
}
|
||||
|
||||
if (!data.ffsqlite3_close)
|
||||
if (!data.ffsqlite3_close) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &data;
|
||||
}
|
||||
|
||||
int ffSettingsGetSQLite3Int(const char* dbPath, const char* query)
|
||||
{
|
||||
if(!ffPathExists(dbPath, FF_PATHTYPE_FILE))
|
||||
int ffSettingsGetSQLite3Int(const char* dbPath, const char* query) {
|
||||
if (!ffPathExists(dbPath, FF_PATHTYPE_FILE)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const SQLiteData* data = getSQLiteData();
|
||||
if(data == NULL)
|
||||
if (data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3* db;
|
||||
if(data->ffsqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK)
|
||||
if (data->ffsqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if(data->ffsqlite3_prepare_v2(db, query, (int) strlen(query), &stmt, NULL) != SQLITE_OK)
|
||||
{
|
||||
if (data->ffsqlite3_prepare_v2(db, query, (int) strlen(query), &stmt, NULL) != SQLITE_OK) {
|
||||
data->ffsqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(data->ffsqlite3_step(stmt) != SQLITE_ROW || data->ffsqlite3_data_count(stmt) < 1)
|
||||
{
|
||||
if (data->ffsqlite3_step(stmt) != SQLITE_ROW || data->ffsqlite3_data_count(stmt) < 1) {
|
||||
data->ffsqlite3_finalize(stmt);
|
||||
data->ffsqlite3_close(db);
|
||||
return 0;
|
||||
@@ -419,72 +415,195 @@ int ffSettingsGetSQLite3Int(const char* dbPath, const char* query)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ffSettingsGetSQLite3String(const char* dbPath, const char* query, FFstrbuf* result)
|
||||
{
|
||||
if(!ffPathExists(dbPath, FF_PATHTYPE_FILE))
|
||||
bool ffSettingsGetSQLite3String(const char* dbPath, const char* query, FFstrbuf* result) {
|
||||
if (!ffPathExists(dbPath, FF_PATHTYPE_FILE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SQLiteData* data = getSQLiteData();
|
||||
if(data == NULL)
|
||||
if (data == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3* db;
|
||||
if(data->ffsqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK)
|
||||
if (data->ffsqlite3_open_v2(dbPath, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
if(data->ffsqlite3_prepare_v2(db, query, (int) strlen(query), &stmt, NULL) != SQLITE_OK)
|
||||
{
|
||||
if (data->ffsqlite3_prepare_v2(db, query, (int) strlen(query), &stmt, NULL) != SQLITE_OK) {
|
||||
data->ffsqlite3_close(db);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(data->ffsqlite3_step(stmt) != SQLITE_ROW || data->ffsqlite3_data_count(stmt) < 1)
|
||||
{
|
||||
if (data->ffsqlite3_step(stmt) != SQLITE_ROW || data->ffsqlite3_data_count(stmt) < 1) {
|
||||
data->ffsqlite3_finalize(stmt);
|
||||
data->ffsqlite3_close(db);
|
||||
return false;
|
||||
}
|
||||
|
||||
ffStrbufSetS(result, (const char *) data->ffsqlite3_column_text(stmt, 0));
|
||||
ffStrbufSetS(result, (const char*) data->ffsqlite3_column_text(stmt, 0));
|
||||
|
||||
data->ffsqlite3_finalize(stmt);
|
||||
data->ffsqlite3_close(db);
|
||||
|
||||
return true;
|
||||
}
|
||||
#else //FF_HAVE_SQLITE3
|
||||
int ffSettingsGetSQLite3Int(const char* dbPath, const char* query)
|
||||
{
|
||||
#else // FF_HAVE_SQLITE3
|
||||
int ffSettingsGetSQLite3Int(const char* dbPath, const char* query) {
|
||||
FF_UNUSED(dbPath, query)
|
||||
return 0;
|
||||
}
|
||||
bool ffSettingsGetSQLite3String(const char* dbPath, const char* query, FFstrbuf* result)
|
||||
{
|
||||
bool ffSettingsGetSQLite3String(const char* dbPath, const char* query, FFstrbuf* result) {
|
||||
FF_UNUSED(dbPath, query, result)
|
||||
return false;
|
||||
}
|
||||
#endif //FF_HAVE_SQLITE3
|
||||
#endif // FF_HAVE_SQLITE3
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <sys/system_properties.h>
|
||||
#include <sys/system_properties.h>
|
||||
bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result) {
|
||||
ffStrbufEnsureFree(result, PROP_VALUE_MAX);
|
||||
int len = __system_property_get(propName, result->chars + result->length);
|
||||
if (len <= 0) return false;
|
||||
if (len <= 0) {
|
||||
return false;
|
||||
}
|
||||
result->length += (uint32_t) len;
|
||||
result->chars[result->length] = '\0';
|
||||
return true;
|
||||
}
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <kenv.h>
|
||||
bool ffSettingsGetFreeBSDKenv(const char* propName, FFstrbuf* result)
|
||||
{
|
||||
//https://wiki.ghostbsd.org/index.php/Kenv
|
||||
#include <kenv.h>
|
||||
bool ffSettingsGetFreeBSDKenv(const char* propName, FFstrbuf* result) {
|
||||
// https://wiki.ghostbsd.org/index.php/Kenv
|
||||
ffStrbufEnsureFree(result, KENV_MVALLEN);
|
||||
int len = kenv(KENV_GET, propName, result->chars + result->length, KENV_MVALLEN);
|
||||
if (len <= 1) return false; // number of bytes copied, including NUL terminator
|
||||
if (len <= 1) {
|
||||
return false; // number of bytes copied, including NUL terminator
|
||||
}
|
||||
result->length += (uint32_t) len - 1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FF_HAVE_EET
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#pragma GCC diagnostic ignored "-Wfloat-conversion"
|
||||
#include <Eet.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
typedef struct E_Font_Default {
|
||||
char* text_class;
|
||||
char* font;
|
||||
int size;
|
||||
} E_Font_Default;
|
||||
|
||||
typedef struct E_Config {
|
||||
char* theme_default_border_style;
|
||||
char* icon_theme;
|
||||
int use_e_cursor;
|
||||
int cursor_size;
|
||||
char* desktop_default_background;
|
||||
Eina_List* font_defaults;
|
||||
} E_Config; // Must be the same name as the top level struct in e.cfg
|
||||
|
||||
#define FF_EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(clas, type) \
|
||||
(ffeet_eina_file_data_descriptor_class_set(clas, sizeof(*(clas)), #type, sizeof(type)))
|
||||
#define FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, struct_type, member, type) \
|
||||
do { \
|
||||
struct_type ___ett; \
|
||||
ffeet_data_descriptor_element_add(edd, #member, type, EET_G_UNKNOWN, (char*) (&(___ett.member)) - (char*) (&(___ett)), 0, /* 0, */ NULL, NULL); \
|
||||
} while (0)
|
||||
#define FF_EET_DATA_DESCRIPTOR_ADD_LIST(edd, struct_type, member, subtype) \
|
||||
do { \
|
||||
struct_type ___ett; \
|
||||
ffeet_data_descriptor_element_add(edd, #member, EET_T_UNKNOW, EET_G_LIST, (char*) (&(___ett.member)) - (char*) (&(___ett)), 0, /* 0, */ NULL, subtype); \
|
||||
} while (0)
|
||||
|
||||
bool ffSettingsGetEnlightenmentProperty(ffEnlightenmentSettings* result) {
|
||||
FF_LIBRARY_LOAD(libeet, false, "libeet" FF_LIBRARY_EXTENSION, 1);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_init, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_open, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_data_descriptor_file_new, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_data_read, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_close, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_shutdown, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_data_descriptor_free, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_eina_file_data_descriptor_class_set, false);
|
||||
FF_LIBRARY_LOAD_SYMBOL(libeet, eet_data_descriptor_element_add, false);
|
||||
|
||||
if (ffeet_init() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY fileName = ffStrbufCreateCopy(&instance.state.platform.homeDir);
|
||||
ffStrbufAppendS(&fileName, ".e/e/config/standard/e.cfg");
|
||||
|
||||
Eet_File* ef = ffeet_open(fileName.chars, EET_FILE_MODE_READ);
|
||||
if (!ef) {
|
||||
ffeet_shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
Eet_Data_Descriptor_Class fontDdc;
|
||||
FF_EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&fontDdc, E_Font_Default);
|
||||
Eet_Data_Descriptor* fontDdd = ffeet_data_descriptor_file_new(&fontDdc);
|
||||
if (!fontDdd) {
|
||||
ffeet_close(ef);
|
||||
ffeet_shutdown();
|
||||
return false;
|
||||
}
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(fontDdd, E_Font_Default, text_class, EET_T_STRING);
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(fontDdd, E_Font_Default, font, EET_T_STRING);
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(fontDdd, E_Font_Default, size, EET_T_INT);
|
||||
|
||||
Eet_Data_Descriptor_Class eddc;
|
||||
FF_EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, E_Config);
|
||||
Eet_Data_Descriptor* edd = ffeet_data_descriptor_file_new(&eddc);
|
||||
if (!edd) {
|
||||
ffeet_data_descriptor_free(fontDdd);
|
||||
ffeet_close(ef);
|
||||
ffeet_shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, E_Config, theme_default_border_style, EET_T_STRING);
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, E_Config, icon_theme, EET_T_STRING);
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, E_Config, use_e_cursor, EET_T_INT);
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, E_Config, cursor_size, EET_T_INT);
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, E_Config, desktop_default_background, EET_T_STRING);
|
||||
FF_EET_DATA_DESCRIPTOR_ADD_LIST(edd, E_Config, font_defaults, fontDdd);
|
||||
|
||||
E_Config* parsed = ffeet_data_read(ef, edd, "config");
|
||||
|
||||
if (parsed) {
|
||||
// TODO: find a better method to get the main theme name
|
||||
result->theme = parsed->theme_default_border_style;
|
||||
result->icon_theme = parsed->icon_theme;
|
||||
result->use_e_cursor = !!parsed->use_e_cursor;
|
||||
result->cursor_size = parsed->cursor_size;
|
||||
result->desktop_default_background = parsed->desktop_default_background;
|
||||
|
||||
E_Font_Default* firstFont = eina_list_data_get(parsed->font_defaults);
|
||||
if (firstFont) {
|
||||
result->font = firstFont->font;
|
||||
}
|
||||
}
|
||||
|
||||
ffeet_close(ef);
|
||||
ffeet_data_descriptor_free(edd);
|
||||
ffeet_data_descriptor_free(fontDdd);
|
||||
if (!parsed) {
|
||||
// We don't shutdown eet so that `result->*` are not freed
|
||||
ffeet_shutdown();
|
||||
}
|
||||
|
||||
return !!parsed;
|
||||
}
|
||||
#else
|
||||
bool ffSettingsGetEnlightenmentProperty(FF_A_UNUSED ffEnlightenmentSettings* result) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
+13
-15
@@ -2,43 +2,41 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static void appendNum(FFstrbuf* result, uint64_t bytes, uint32_t base, const char** prefixes)
|
||||
{
|
||||
static void appendNum(FFstrbuf* result, uint64_t bytes, uint32_t base, const char** prefixes) {
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
double size = (double) bytes;
|
||||
uint8_t counter = 0;
|
||||
|
||||
while(size >= base && counter < options->sizeMaxPrefix && prefixes[counter + 1])
|
||||
{
|
||||
while (size >= base && counter < options->sizeMaxPrefix && prefixes[counter + 1]) {
|
||||
size /= base;
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (counter == 0)
|
||||
if (counter == 0) {
|
||||
ffStrbufAppendUInt(result, bytes);
|
||||
else
|
||||
} else {
|
||||
ffStrbufAppendDouble(result, size, (int8_t) options->sizeNdigits, true);
|
||||
if (options->sizeSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER)
|
||||
}
|
||||
if (options->sizeSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppendS(result, prefixes[counter]);
|
||||
}
|
||||
|
||||
void ffSizeAppendNum(uint64_t bytes, FFstrbuf* result)
|
||||
{
|
||||
void ffSizeAppendNum(uint64_t bytes, FFstrbuf* result) {
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
switch (options->sizeBinaryPrefix)
|
||||
{
|
||||
switch (options->sizeBinaryPrefix) {
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_IEC:
|
||||
appendNum(result, bytes, 1024, (const char*[]) {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", NULL});
|
||||
appendNum(result, bytes, 1024, (const char*[]) { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", NULL });
|
||||
break;
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_SI:
|
||||
appendNum(result, bytes, 1000, (const char*[]) {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL});
|
||||
appendNum(result, bytes, 1000, (const char*[]) { "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL });
|
||||
break;
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_JEDEC:
|
||||
appendNum(result, bytes, 1024, (const char*[]) {"B", "KB", "MB", "GB", "TB", NULL});
|
||||
appendNum(result, bytes, 1024, (const char*[]) { "B", "KB", "MB", "GB", "TB", NULL });
|
||||
break;
|
||||
default:
|
||||
appendNum(result, bytes, 1024, (const char*[]) {"B", NULL});
|
||||
appendNum(result, bytes, 1024, (const char*[]) { "B", NULL });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,805 @@
|
||||
#include "common/smbios.h"
|
||||
#include "common/io.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
bool ffIsSmbiosValueSet(FFstrbuf* value) {
|
||||
ffStrbufTrimRightSpace(value);
|
||||
return value->length > 0 &&
|
||||
!ffStrbufStartsWithIgnCaseS(value, "To be filled") &&
|
||||
!ffStrbufStartsWithIgnCaseS(value, "To be set") &&
|
||||
!ffStrbufStartsWithIgnCaseS(value, "OEM") &&
|
||||
!ffStrbufStartsWithIgnCaseS(value, "O.E.M.") &&
|
||||
!ffStrbufStartsWithIgnCaseS(value, "System Product") &&
|
||||
!ffStrbufStartsWithIgnCaseS(value, "Unknown Product") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "None") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "System Name") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "System Version") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "System SKU#") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Default string") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Undefined") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Not Specified") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Not Applicable") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Not Defined") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Not Available") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "INVALID") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Type1ProductConfigId") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "TBD by OEM") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "No Enclosure") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Chassis Version") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "All Series") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "N/A") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Unknown") &&
|
||||
!ffStrbufIgnCaseEqualS(value, "Standard") && ({
|
||||
// Some SMBIOS implementations use "0x0000" to indicate an unset value, even for strings.
|
||||
bool zero = ffStrbufStartsWithS(value, "0x0");
|
||||
if (zero) {
|
||||
for (size_t i = 2; i < value->length; i++) {
|
||||
char c = value->chars[i];
|
||||
if (c != '0') {
|
||||
zero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
!zero;
|
||||
});
|
||||
}
|
||||
|
||||
static bool smbiosTableInitialized = false;
|
||||
static FFSmbiosHeaderTable smbiosTable;
|
||||
|
||||
const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header) {
|
||||
const char* p = ((const char*) header) + header->Length;
|
||||
if (*p) {
|
||||
do {
|
||||
p += strlen(p) + 1;
|
||||
} while (*p);
|
||||
} else { // The terminator is always double 0 even if there is no string
|
||||
p++;
|
||||
}
|
||||
|
||||
return (const FFSmbiosHeader*) (p + 1);
|
||||
}
|
||||
|
||||
static bool parseSmbiosTable(const uint8_t* data, uint32_t length) {
|
||||
const FFSmbiosHeader* endOfTable = NULL;
|
||||
|
||||
FF_DEBUG("Parsing SMBIOS table structures with length %u bytes", length);
|
||||
FF_A_UNUSED int structureCount = 0, totalCount = 0;
|
||||
for (
|
||||
const FFSmbiosHeader* header = (const FFSmbiosHeader*) data;
|
||||
(const uint8_t*) header + sizeof(FFSmbiosHeader) < (const uint8_t*) data + length;
|
||||
header = ffSmbiosNextEntry(header)) {
|
||||
++totalCount;
|
||||
endOfTable = header;
|
||||
|
||||
if (header->Length < sizeof(FFSmbiosHeader)) {
|
||||
FF_DEBUG("Invalid SMBIOS structure length %u at offset 0x%lx",
|
||||
header->Length,
|
||||
(unsigned long) ((const uint8_t*) header - data));
|
||||
break;
|
||||
}
|
||||
|
||||
if (header->Handle >= 0xFF00) {
|
||||
FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx",
|
||||
header->Handle,
|
||||
(unsigned long) ((const uint8_t*) header - data));
|
||||
break;
|
||||
}
|
||||
|
||||
// This doesn't verify the entire structure (e.g. string section can still be truncated),
|
||||
// but at least ensures the formatted section is valid and prevents infinite loops
|
||||
// when the table is severely malformed.
|
||||
if (__builtin_expect((const uint8_t*) header + header->Length > (const uint8_t*) data + length, false)) {
|
||||
FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small",
|
||||
(unsigned long) ((const uint8_t*) header - data),
|
||||
header->Length);
|
||||
break;
|
||||
}
|
||||
|
||||
if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE) {
|
||||
if (!smbiosTable[header->Type]) {
|
||||
smbiosTable[header->Type] = header;
|
||||
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
|
||||
header->Type,
|
||||
header->Handle,
|
||||
header->Length);
|
||||
structureCount++;
|
||||
} else {
|
||||
FF_DEBUG("Duplicate SMBIOS structure type %u, handle 0x%04X, length %u",
|
||||
header->Type,
|
||||
header->Handle,
|
||||
header->Length);
|
||||
}
|
||||
} else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
|
||||
FF_DEBUG("Reached SMBIOS end of type %u, handle 0x%04X, length %u",
|
||||
header->Type,
|
||||
header->Handle,
|
||||
header->Length);
|
||||
break;
|
||||
} else {
|
||||
FF_DEBUG("Found custom SMBIOS structure type %u, handle 0x%04X, length %u; ignoring",
|
||||
header->Type,
|
||||
header->Handle,
|
||||
header->Length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!endOfTable) {
|
||||
FF_DEBUG("No SMBIOS structures found in table");
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_DEBUG("Parsed %d/%d SMBIOS structures, end-of-table (Type 127) %s",
|
||||
structureCount,
|
||||
totalCount,
|
||||
endOfTable->Type == FF_SMBIOS_TYPE_END_OF_TABLE ? "found." : "not found! SMBIOS data may be malformed.");
|
||||
smbiosTable[FF_SMBIOS_TYPE_END_OF_TABLE] = endOfTable;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || defined(__HAIKU__) || defined(__OpenBSD__) || defined(__GNU__)
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include "common/properties.h"
|
||||
#elif defined(__FreeBSD__)
|
||||
#include "common/settings.h"
|
||||
#elif defined(__NetBSD__)
|
||||
#include "common/sysctl.h"
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) {
|
||||
// /sys/class/dmi/id/* are all pseudo-files with very small content
|
||||
// so reading the whole file at once is efficient
|
||||
ffStrbufEnsureFixedLengthFree(buffer, 127);
|
||||
|
||||
ssize_t len = ffReadFileData(devicesPath, buffer->allocated - 1, buffer->chars);
|
||||
if (len > 0) {
|
||||
assert(len < buffer->allocated);
|
||||
buffer->chars[len] = '\0';
|
||||
buffer->length = (uint32_t) len;
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
if (ffIsSmbiosValueSet(buffer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
len = ffReadFileData(classPath, buffer->allocated - 1, buffer->chars);
|
||||
if (len > 0) {
|
||||
assert(len < buffer->allocated);
|
||||
buffer->chars[len] = '\0';
|
||||
buffer->length = (uint32_t) len;
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
if (ffIsSmbiosValueSet(buffer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufClear(buffer);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool readPhysicalMemory(int fd, off_t address, size_t length, void* buffer) {
|
||||
#if !defined(__FreeBSD__) // Either causes kernel panic or returns EFAULT
|
||||
// -1: unknown, 0: failed before (stop trying), 1: succeeded before
|
||||
static int preadState = -1;
|
||||
if (preadState != 0) {
|
||||
ssize_t bytesRead = pread(fd, buffer, length, address);
|
||||
if (bytesRead == (ssize_t) length) {
|
||||
preadState = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_DEBUG("pread failed at address 0x%lx for %zu bytes: %s. Falling back to mmap%s",
|
||||
(unsigned long) address,
|
||||
length,
|
||||
strerror(errno),
|
||||
preadState < 0 ? " and caching failure" : "");
|
||||
preadState = 0;
|
||||
} else {
|
||||
FF_DEBUG("Skipping pread due to cached failure; using mmap");
|
||||
}
|
||||
#endif
|
||||
|
||||
off_t alignedAddress = address & ~((off_t) instance.state.platform.sysinfo.pageSize - 1);
|
||||
size_t pageOffset = (size_t) (address - alignedAddress);
|
||||
size_t mapLength = pageOffset + length;
|
||||
|
||||
void* p = mmap(NULL, mapLength, PROT_READ, MAP_SHARED, fd, alignedAddress);
|
||||
if (p == MAP_FAILED) {
|
||||
FF_DEBUG("mmap failed at aligned address 0x%lx for %zu bytes: %s",
|
||||
(unsigned long) alignedAddress,
|
||||
mapLength,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(buffer, (const uint8_t*) p + pageOffset, length);
|
||||
munmap(p, mapLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct FFSmbios20EntryPoint {
|
||||
uint8_t AnchorString[4];
|
||||
uint8_t EntryPointStructureChecksum;
|
||||
uint8_t EntryPointLength;
|
||||
uint8_t SmbiosMajorVersion;
|
||||
uint8_t SmbiosMinorVersion;
|
||||
uint16_t MaximumStructureSize;
|
||||
uint8_t EntryPointRevision;
|
||||
uint8_t FormattedArea[5];
|
||||
uint8_t IntermediateAnchorString[5];
|
||||
uint8_t IntermediateChecksum;
|
||||
uint16_t StructureTableLength;
|
||||
uint32_t StructureTableAddress;
|
||||
uint16_t NumberOfSmbiosStructures;
|
||||
uint8_t SmbiosBcdRevision;
|
||||
} FF_A_PACKED FFSmbios20EntryPoint;
|
||||
static_assert(offsetof(FFSmbios20EntryPoint, SmbiosBcdRevision) == 0x1E,
|
||||
"FFSmbios20EntryPoint: Wrong struct alignment");
|
||||
|
||||
typedef struct FFSmbios30EntryPoint {
|
||||
uint8_t AnchorString[5];
|
||||
uint8_t EntryPointStructureChecksum;
|
||||
uint8_t EntryPointLength;
|
||||
uint8_t SmbiosMajorVersion;
|
||||
uint8_t SmbiosMinorVersion;
|
||||
uint8_t SmbiosDocrev;
|
||||
uint8_t EntryPointRevision;
|
||||
uint8_t Reversed;
|
||||
uint32_t StructureTableMaximumSize;
|
||||
uint64_t StructureTableAddress;
|
||||
} FF_A_PACKED FFSmbios30EntryPoint;
|
||||
|
||||
static_assert(offsetof(FFSmbios30EntryPoint, StructureTableAddress) == 0x10,
|
||||
"FFSmbios30EntryPoint: Wrong struct alignment");
|
||||
|
||||
typedef union FFSmbiosEntryPoint {
|
||||
FFSmbios20EntryPoint Smbios20;
|
||||
FFSmbios30EntryPoint Smbios30;
|
||||
} FFSmbiosEntryPoint;
|
||||
|
||||
static bool fillTableBufferFallback(FFstrbuf* buffer) {
|
||||
const char* devMem =
|
||||
#if __HAIKU__
|
||||
"/dev/misc/mem";
|
||||
#else
|
||||
"/dev/mem"; // kern.securelevel must be -1
|
||||
#endif
|
||||
FF_DEBUG("Using physical memory searching implementation: %s", devMem);
|
||||
|
||||
uint32_t tableLength = 0;
|
||||
off_t tableAddress = 0;
|
||||
FF_AUTO_CLOSE_FD int fd = open(devMem, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
FF_DEBUG("Failed to open memory device: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Memory device opened successfully with fd=%d", fd);
|
||||
|
||||
// Works on legacy BIOS only
|
||||
// See: https://wiki.osdev.org/System_Management_BIOS#UEFI_systems
|
||||
// On BSD systems, we can get EFI system resource table (ESRT) via EFIIOC_GET_TABLE
|
||||
// However, to acquire SMBIOS entry point, we need EFI configuration table (provided by EFI system table)
|
||||
// which is not available via EFIIOC_GET_TABLE.
|
||||
FF_AUTO_FREE uint8_t* smBiosBase = malloc(0x10000);
|
||||
if (!readPhysicalMemory(fd, 0xF0000, 0x10000, smBiosBase)) {
|
||||
FF_DEBUG("Failed to read SMBIOS memory region");
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Successfully read 0x10000 bytes from physical address 0xF0000");
|
||||
|
||||
for (off_t offset = 0; offset <= 0xffe0; offset += 0x10) {
|
||||
FFSmbiosEntryPoint* p = (void*) (smBiosBase + offset);
|
||||
if (memcmp(p, "_SM3_", sizeof(p->Smbios30.AnchorString)) == 0) {
|
||||
FF_DEBUG("Found SMBIOS 3.0 entry point at phyaddr 0x%05lX", (unsigned long) (0xF0000 + offset));
|
||||
if (p->Smbios30.EntryPointLength != sizeof(p->Smbios30)) {
|
||||
FF_DEBUG("Invalid SMBIOS 3.0 entry point length: %u (expected %zu)",
|
||||
p->Smbios30.EntryPointLength,
|
||||
sizeof(p->Smbios30));
|
||||
return false;
|
||||
}
|
||||
tableLength = p->Smbios30.StructureTableMaximumSize;
|
||||
tableAddress = (off_t) p->Smbios30.StructureTableAddress;
|
||||
FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u",
|
||||
tableLength,
|
||||
(unsigned long) tableAddress,
|
||||
p->Smbios30.SmbiosMajorVersion,
|
||||
p->Smbios30.SmbiosMinorVersion,
|
||||
p->Smbios30.SmbiosDocrev);
|
||||
break;
|
||||
} else if (memcmp(p, "_SM_", sizeof(p->Smbios20.AnchorString)) == 0) {
|
||||
FF_DEBUG("Found SMBIOS 2.0 entry point at phyaddr 0x%05lX", (unsigned long) (0xF0000 + offset));
|
||||
if (p->Smbios20.EntryPointLength != sizeof(p->Smbios20)) {
|
||||
FF_DEBUG("Invalid SMBIOS 2.0 entry point length: %u (expected %zu)",
|
||||
p->Smbios20.EntryPointLength,
|
||||
sizeof(p->Smbios20));
|
||||
return false;
|
||||
}
|
||||
tableLength = p->Smbios20.StructureTableLength;
|
||||
tableAddress = (off_t) p->Smbios20.StructureTableAddress;
|
||||
FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u",
|
||||
tableLength,
|
||||
(unsigned long) tableAddress,
|
||||
p->Smbios20.SmbiosMajorVersion,
|
||||
p->Smbios20.SmbiosMinorVersion);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tableLength == 0) {
|
||||
FF_DEBUG("No valid SMBIOS entry point found in memory region");
|
||||
return false;
|
||||
}
|
||||
|
||||
ffStrbufClear(buffer);
|
||||
ffStrbufEnsureFixedLengthFree(buffer, tableLength);
|
||||
FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress);
|
||||
if (readPhysicalMemory(fd, (off_t) tableAddress, tableLength, buffer->chars)) {
|
||||
buffer->length = tableLength;
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength);
|
||||
} else {
|
||||
FF_DEBUG("Failed to read SMBIOS table data");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
static bool detectSmbiosTableLength(const uint8_t* data, uint32_t bufferLength, uint32_t* tableLength) {
|
||||
const uint8_t* p = data;
|
||||
const uint8_t* end = data + bufferLength;
|
||||
|
||||
while (p + sizeof(FFSmbiosHeader) <= end) {
|
||||
const FFSmbiosHeader* header = (const FFSmbiosHeader*) p;
|
||||
if (header->Length < sizeof(*header)) {
|
||||
FF_DEBUG("Invalid SMBIOS structure length %u at offset 0x%lx",
|
||||
header->Length,
|
||||
(unsigned long) (p - data));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header->Handle >= 0xFF00) {
|
||||
FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx",
|
||||
header->Handle,
|
||||
(unsigned long) (p - data));
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* formattedEnd = p + header->Length;
|
||||
if (formattedEnd > end) {
|
||||
FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small",
|
||||
(unsigned long) (p - data),
|
||||
header->Length);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* string = (const char*) formattedEnd;
|
||||
const char* stringEnd = (const char*) end;
|
||||
while (true) {
|
||||
size_t remaining = (size_t) (stringEnd - string);
|
||||
if (remaining == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* nul = memchr(string, '\0', remaining);
|
||||
if (!nul) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string = nul + 1;
|
||||
if (string >= stringEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*string == '\0') {
|
||||
++string;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
|
||||
*tableLength = (uint32_t) (string - (const char*) data);
|
||||
return true;
|
||||
}
|
||||
|
||||
p = (const uint8_t*) string;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool fillTableBufferPlatform(FFstrbuf* buffer) {
|
||||
FF_DEBUG("Using OpenBSD /var/run/dmesg.boot implementation");
|
||||
|
||||
char dmesg[2048];
|
||||
ssize_t size = ffReadFileData("/var/run/dmesg.boot", sizeof(dmesg) - 1, dmesg);
|
||||
if (size <= 0) {
|
||||
FF_DEBUG("Failed to read /var/run/dmesg.boot");
|
||||
return false;
|
||||
} else {
|
||||
FF_DEBUG("Successfully read %zd bytes from /var/run/dmesg.boot", size);
|
||||
}
|
||||
dmesg[size] = '\0';
|
||||
|
||||
const char* const needle = "\nbios0 at mainbus0: SMBIOS rev. ";
|
||||
size_t needleLen = strlen(needle);
|
||||
char* line = memmem(dmesg, (size_t) size, needle, needleLen);
|
||||
if (!line) {
|
||||
FF_DEBUG("Failed to find SMBIOS line in /var/run/dmesg.boot");
|
||||
return false;
|
||||
}
|
||||
line += needleLen;
|
||||
|
||||
char* lineEnd = memchr(line, '\n', (size_t) ((dmesg + size) - line));
|
||||
if (!lineEnd) {
|
||||
lineEnd = dmesg + size;
|
||||
}
|
||||
|
||||
char* address = memchr(line, '@', (size_t) (lineEnd - line));
|
||||
if (!address) {
|
||||
FF_DEBUG("Failed to find SMBIOS table address in dmesg line");
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
++address;
|
||||
} while (address < lineEnd && (*address == ' ' || *address == '\t'));
|
||||
|
||||
errno = 0;
|
||||
char* addressEnd = NULL;
|
||||
unsigned long long parsedAddress = strtoull(address, &addressEnd, 16);
|
||||
if (errno != 0 || addressEnd == address || parsedAddress == 0) {
|
||||
FF_DEBUG("Failed to parse OpenBSD SMBIOS table address from line: %.*s",
|
||||
(int) (lineEnd - line),
|
||||
line);
|
||||
return false;
|
||||
}
|
||||
|
||||
off_t tableAddress = (off_t) parsedAddress;
|
||||
FF_DEBUG("Parsed OpenBSD SMBIOS table address: 0x%llx", parsedAddress);
|
||||
|
||||
FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
FF_DEBUG("Failed to open /dev/mem: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t readLength = 0x10000;
|
||||
ffStrbufClear(buffer);
|
||||
ffStrbufEnsureFixedLengthFree(buffer, readLength);
|
||||
|
||||
FF_DEBUG("Attempting to read OpenBSD SMBIOS table data: %u bytes at 0x%lx",
|
||||
readLength,
|
||||
(unsigned long) tableAddress);
|
||||
|
||||
if (!readPhysicalMemory(fd, tableAddress, readLength, buffer->chars)) {
|
||||
FF_DEBUG("Failed to read OpenBSD SMBIOS table data");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t detectedLength = 0;
|
||||
if (detectSmbiosTableLength((const uint8_t*) buffer->chars, readLength, &detectedLength)) {
|
||||
buffer->length = detectedLength;
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
FF_DEBUG("Determined OpenBSD SMBIOS table length: %u bytes", detectedLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_DEBUG("SMBIOS end-of-table marker not found within first %u bytes; give up", readLength);
|
||||
ffStrbufClear(buffer);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
static bool fillTableBufferPlatform(FFstrbuf* buffer) {
|
||||
#if __HAIKU__ && __GNU__
|
||||
return false;
|
||||
#elif defined(__linux__)
|
||||
FF_DEBUG("Using Linux implementation - trying /sys/firmware/dmi/tables/DMI");
|
||||
if (ffAppendFileBuffer("/sys/firmware/dmi/tables/DMI", buffer)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_DEBUG("Failed to read /sys/firmware/dmi/tables/DMI, falling back to memory-mapped implementation");
|
||||
#endif
|
||||
|
||||
{
|
||||
#if !defined(__sun) && !defined(__NetBSD__)
|
||||
FF_DEBUG("Using memory-mapped implementation");
|
||||
FF_STRBUF_AUTO_DESTROY strEntryAddress = ffStrbufCreate();
|
||||
#ifdef __FreeBSD__
|
||||
FF_DEBUG("Using FreeBSD kenv implementation");
|
||||
if (!ffSettingsGetFreeBSDKenv("hint.smbios.0.mem", &strEntryAddress)) {
|
||||
FF_DEBUG("Failed to get SMBIOS address from FreeBSD kenv");
|
||||
return false; // non-UEFI systems
|
||||
}
|
||||
FF_DEBUG("Got SMBIOS address from kenv: %s", strEntryAddress.chars);
|
||||
#elif defined(__linux__)
|
||||
{
|
||||
FF_DEBUG("Using Linux EFI systab implementation");
|
||||
FF_STRBUF_AUTO_DESTROY systab = ffStrbufCreate();
|
||||
if (!ffAppendFileBuffer("/sys/firmware/efi/systab", &systab)) {
|
||||
FF_DEBUG("Failed to read /sys/firmware/efi/systab");
|
||||
return false;
|
||||
}
|
||||
if (!ffParsePropLines(systab.chars, "SMBIOS3=", &strEntryAddress) &&
|
||||
!ffParsePropLines(systab.chars, "SMBIOS=", &strEntryAddress)) {
|
||||
FF_DEBUG("Failed to find SMBIOS entry in systab");
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Found SMBIOS entry in systab: %s", strEntryAddress.chars);
|
||||
}
|
||||
#endif
|
||||
|
||||
off_t entryAddress = (off_t) strtol(strEntryAddress.chars, NULL, 16);
|
||||
if (entryAddress == 0) {
|
||||
FF_DEBUG("Invalid SMBIOS entry address: 0");
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Parsed SMBIOS entry address: 0x%lx", (unsigned long) entryAddress);
|
||||
|
||||
FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
FF_DEBUG("Failed to open /dev/mem: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("/dev/mem opened successfully with fd=%d", fd);
|
||||
|
||||
FFSmbiosEntryPoint entryPoint;
|
||||
FF_DEBUG("Attempting to read %zu bytes from physical address 0x%lx",
|
||||
sizeof(entryPoint),
|
||||
(unsigned long) entryAddress);
|
||||
if (!readPhysicalMemory(fd, entryAddress, sizeof(entryPoint), &entryPoint)) {
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Successfully read SMBIOS entry point data");
|
||||
#else
|
||||
// Sun or NetBSD
|
||||
FF_DEBUG("Using %s specific implementation",
|
||||
#ifdef __NetBSD__
|
||||
"NetBSD"
|
||||
#else
|
||||
"SunOS"
|
||||
#endif
|
||||
);
|
||||
|
||||
FF_AUTO_CLOSE_FD int fd = open("/dev/smbios", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
FF_DEBUG("Failed to open /dev/smbios: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("/dev/smbios opened successfully with fd=%d", fd);
|
||||
|
||||
FFSmbiosEntryPoint entryPoint;
|
||||
#ifdef __NetBSD__
|
||||
off_t addr = (off_t) ffSysctlGetInt64("machdep.smbios", 0);
|
||||
if (addr == 0) {
|
||||
FF_DEBUG("Failed to get SMBIOS address from sysctl");
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Got SMBIOS address from sysctl: 0x%lx", (unsigned long) addr);
|
||||
|
||||
if (pread(fd, &entryPoint, sizeof(entryPoint), addr) < 1) {
|
||||
FF_DEBUG("Failed to read SMBIOS entry point: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Successfully read SMBIOS entry point");
|
||||
#else
|
||||
FF_DEBUG("Reading SMBIOS entry point from /dev/smbios");
|
||||
if (ffReadFDData(fd, sizeof(entryPoint), &entryPoint) < 1) {
|
||||
FF_DEBUG("Failed to read SMBIOS entry point: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FF_DEBUG("Successfully read SMBIOS entry point");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
uint32_t tableLength = 0;
|
||||
off_t tableAddress = 0;
|
||||
if (memcmp(entryPoint.Smbios20.AnchorString, "_SM_", sizeof(entryPoint.Smbios20.AnchorString)) == 0) {
|
||||
FF_DEBUG("Found SMBIOS 2.0 entry point");
|
||||
if (entryPoint.Smbios20.EntryPointLength != sizeof(entryPoint.Smbios20)) {
|
||||
FF_DEBUG("Invalid SMBIOS 2.0 entry point length: %u (expected %zu)",
|
||||
entryPoint.Smbios20.EntryPointLength,
|
||||
sizeof(entryPoint.Smbios20));
|
||||
return false;
|
||||
}
|
||||
tableLength = entryPoint.Smbios20.StructureTableLength;
|
||||
tableAddress = (off_t) entryPoint.Smbios20.StructureTableAddress;
|
||||
FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u",
|
||||
tableLength,
|
||||
(unsigned long) tableAddress,
|
||||
entryPoint.Smbios20.SmbiosMajorVersion,
|
||||
entryPoint.Smbios20.SmbiosMinorVersion);
|
||||
} else if (memcmp(entryPoint.Smbios30.AnchorString, "_SM3_", sizeof(entryPoint.Smbios30.AnchorString)) == 0) {
|
||||
FF_DEBUG("Found SMBIOS 3.0 entry point");
|
||||
if (entryPoint.Smbios30.EntryPointLength != sizeof(entryPoint.Smbios30)) {
|
||||
FF_DEBUG("Invalid SMBIOS 3.0 entry point length: %u (expected %zu)",
|
||||
entryPoint.Smbios30.EntryPointLength,
|
||||
sizeof(entryPoint.Smbios30));
|
||||
return false;
|
||||
}
|
||||
tableLength = entryPoint.Smbios30.StructureTableMaximumSize;
|
||||
tableAddress = (off_t) entryPoint.Smbios30.StructureTableAddress;
|
||||
FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u",
|
||||
tableLength,
|
||||
(unsigned long) tableAddress,
|
||||
entryPoint.Smbios30.SmbiosMajorVersion,
|
||||
entryPoint.Smbios30.SmbiosMinorVersion,
|
||||
entryPoint.Smbios30.SmbiosDocrev);
|
||||
} else {
|
||||
FF_DEBUG("Unknown SMBIOS entry point format");
|
||||
return false; // Dragonfly goes here
|
||||
}
|
||||
|
||||
ffStrbufClear(buffer);
|
||||
ffStrbufEnsureFixedLengthFree(buffer, tableLength);
|
||||
FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress);
|
||||
if (readPhysicalMemory(fd, tableAddress, tableLength, buffer->chars)) {
|
||||
buffer->length = tableLength;
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength);
|
||||
} else {
|
||||
ffStrbufClear(buffer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
|
||||
static FFstrbuf buffer;
|
||||
if (!smbiosTableInitialized) {
|
||||
smbiosTableInitialized = true;
|
||||
FF_DEBUG("Initializing SMBIOS buffer");
|
||||
ffStrbufInit(&buffer);
|
||||
|
||||
if (!fillTableBufferPlatform(&buffer)) {
|
||||
FF_DEBUG("Platform specific SMBIOS retrieval failed, trying fallback method");
|
||||
if (!fillTableBufferFallback(&buffer)) {
|
||||
FF_DEBUG("Fallback SMBIOS retrieval also failed");
|
||||
ffStrbufDestroy(&buffer);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parseSmbiosTable((const uint8_t*) buffer.chars, buffer.length)) {
|
||||
ffStrbufClear(&buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.length == 0) {
|
||||
FF_DEBUG("No valid SMBIOS data available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &smbiosTable;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmultichar"
|
||||
|
||||
typedef struct FFRawSmbiosData {
|
||||
uint8_t Used20CallingMethod;
|
||||
uint8_t SMBIOSMajorVersion;
|
||||
uint8_t SMBIOSMinorVersion;
|
||||
uint8_t DmiRevision;
|
||||
uint32_t Length;
|
||||
uint8_t SMBIOSTableData[];
|
||||
} FFRawSmbiosData;
|
||||
|
||||
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
|
||||
static SYSTEM_FIRMWARE_TABLE_INFORMATION* buffer;
|
||||
|
||||
if (!smbiosTableInitialized) {
|
||||
smbiosTableInitialized = true;
|
||||
FF_DEBUG("Initializing Windows SMBIOS buffer");
|
||||
|
||||
FF_DEBUG("Querying system firmware table size with signature 'RSMB'");
|
||||
SYSTEM_FIRMWARE_TABLE_INFORMATION sfti = {
|
||||
.ProviderSignature = 'RSMB',
|
||||
.Action = SystemFirmwareTableGet,
|
||||
};
|
||||
ULONG bufSize = 0;
|
||||
NtQuerySystemInformation(SystemFirmwareTableInformation, &sfti, sizeof(sfti), &bufSize);
|
||||
if (bufSize <= sizeof(FFRawSmbiosData) + sizeof(sfti)) {
|
||||
FF_DEBUG("Invalid firmware table size: %lu (must be > %zu)", bufSize, sizeof(FFRawSmbiosData) + sizeof(sfti));
|
||||
return NULL;
|
||||
}
|
||||
if (bufSize != sfti.TableBufferLength + (ULONG) sizeof(sfti)) {
|
||||
FF_DEBUG("Firmware table size mismatch: NtQuerySystemInformation returned %lu but expected %lu",
|
||||
bufSize,
|
||||
sfti.TableBufferLength + (ULONG) sizeof(sfti));
|
||||
return NULL;
|
||||
}
|
||||
FF_DEBUG("Firmware table size: %lu bytes", bufSize);
|
||||
|
||||
buffer = malloc(bufSize);
|
||||
*buffer = sfti;
|
||||
FF_DEBUG("Allocated buffer for SMBIOS data");
|
||||
|
||||
if (!NT_SUCCESS(NtQuerySystemInformation(SystemFirmwareTableInformation, buffer, bufSize, &bufSize))) {
|
||||
FF_DEBUG("NtQuerySystemInformation(SystemFirmwareTableInformation) failed");
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
return NULL;
|
||||
}
|
||||
FFRawSmbiosData* rawData = (FFRawSmbiosData*) buffer->TableBuffer;
|
||||
|
||||
FF_DEBUG("Successfully retrieved SMBIOS data: version %u.%u, length %u bytes",
|
||||
rawData->SMBIOSMajorVersion,
|
||||
rawData->SMBIOSMinorVersion,
|
||||
rawData->Length);
|
||||
|
||||
if (!parseSmbiosTable(rawData->SMBIOSTableData, rawData->Length)) {
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
FF_DEBUG("No valid SMBIOS data available");
|
||||
return NULL;
|
||||
}
|
||||
return &smbiosTable;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#include "common/apple/cf_helpers.h"
|
||||
|
||||
const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
|
||||
static CFDataRef smbiosDataBuffer;
|
||||
|
||||
if (!smbiosTableInitialized) {
|
||||
smbiosTableInitialized = true;
|
||||
FF_DEBUG("Initializing SMBIOS buffer on Apple platform");
|
||||
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMBIOS"));
|
||||
|
||||
if (!registryEntry) {
|
||||
FF_DEBUG("IOServiceGetMatchingService() failed to find AppleSMBIOS");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("AppleSMBIOS service found, retrieving SMBIOS data");
|
||||
smbiosDataBuffer = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("SMBIOS"), kCFAllocatorDefault, kNilOptions);
|
||||
if (!smbiosDataBuffer) {
|
||||
FF_DEBUG("IORegistryEntryCreateCFProperty() failed to get SMBIOS data");
|
||||
return NULL;
|
||||
}
|
||||
if (CFGetTypeID(smbiosDataBuffer) != CFDataGetTypeID()) {
|
||||
FF_DEBUG("Unexpected SMBIOS data type: expected CFData");
|
||||
CFRelease(smbiosDataBuffer);
|
||||
smbiosDataBuffer = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("Successfully retrieved SMBIOS data: %lu bytes", CFDataGetLength(smbiosDataBuffer));
|
||||
if (!parseSmbiosTable((const uint8_t*) CFDataGetBytePtr(smbiosDataBuffer), (uint32_t) CFDataGetLength(smbiosDataBuffer))) {
|
||||
CFRelease(smbiosDataBuffer);
|
||||
smbiosDataBuffer = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!smbiosDataBuffer) {
|
||||
FF_DEBUG("No valid SMBIOS data available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &smbiosTable;
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user