Compare commits

..

349 Commits

Author SHA1 Message Date
Carter Li 021ece1681 Merge pull request #2370 from fastfetch-cli/dev
Release: v2.64.1
2026-06-04 23:45:19 +08:00
李通洲 9787b56460 Release: v2.64.1 2026-06-04 20:15:54 +08:00
李通洲 54792e61ff Common (Lua): detects circular reference in encode_json 2026-06-04 20:15:54 +08:00
Carter Li b34e2a4055 Common (Base64): adds static to internal functions 2026-06-04 17:22:24 +08:00
Carter Li 3b56200ee4 Packages: adds option PACKAGES_REMOVE_DISABLED to remove disabled package managers from the build entirely 2026-06-04 17:22:24 +08:00
Carter Li de95a62e2f Codec (macOS): removes usage of JPEG XL; replaces enums with raw constants 2026-06-04 10:48:01 +08:00
Carter Li 72cb64a500 Common (Lua): optimises code; fixes incompatibility against lua 5.3 2026-06-04 10:48:01 +08:00
Carter Li d48bd733f0 Merge pull request #2368 from fastfetch-cli/dev
Release v2.64.0
2026-06-03 19:18:31 +08:00
李通洲 0585ea7797 Doc: update changelog 2026-06-03 18:34:09 +08:00
李通洲 794035f8d9 Shell: sets pretty name of busybox to ash 2026-06-03 18:29:33 +08:00
李通洲 723552f68b Shell: be more robust on busybox version detection 2026-06-03 18:29:10 +08:00
Carter Li 8dd38f5744 Release: v2.64.0 2026-06-03 16:04:00 +08:00
Carter Li 616a5c7aea Codec (Vulkan): be compatible with old Vulkan headers
Fixes #2367
2026-06-03 15:23:42 +08:00
Carter Li 5e1e94473e Bootmgr (macOS): don't hard-code the firmware name 2026-06-03 15:23:42 +08:00
CicaDATA 4eae8ac244 Logo (Builtin): updates NurOS logo and colors (#2366) 2026-06-03 02:21:23 -05:00
李通洲 8aeb7c2f10 Shell (Linux): improves performance of busybox (ash) version detection 2026-06-02 17:44:55 +08:00
Carter Li c20b532c7a Common (Format): adds | for center aligning 2026-06-02 16:03:04 +08:00
Carter Li 3c59161aca Common (Format): handles ANSI escapes when padding/truncating variable strings
Fixes #2364
2026-06-02 15:55:10 +08:00
Carter Li bd454632c3 FFstrbuf: inlines more functions 2026-06-02 15:42:40 +08:00
Carter Li e73adc27a0 Common: fixes wcwidth handling 2026-06-02 15:05:13 +08:00
Carter Li 15c6014c04 Common: don't inline ffUtf8CharLenWidth 2026-06-02 14:01:59 +08:00
Carter Li 54f18982ac Common: renames arrayUtil to arrutil 2026-06-02 10:59:18 +08:00
Carter Li 99c2f2a2c8 JsonSchema: adds codec 2026-06-02 10:58:57 +08:00
Carter Li 45f35b1c4d Library (Linux): adds simple debug log 2026-06-02 09:27:38 +08:00
Carter Li 428c05b9de Codec (Linux): optimises code 2026-06-02 09:10:55 +08:00
李通洲 23ca6d94ab CI: run codec module 2026-06-02 00:15:42 +08:00
李通洲 9e4e0cb838 Codec (Linux): removes VAEntrypointEncPicture 2026-06-01 23:45:17 +08:00
李通洲 95a370e5a2 Codec (macOS): fixes building 2026-06-01 20:48:59 +08:00
李通洲 e013005f34 Code (Vulkan): fixes compiling 2026-06-01 20:16:30 +08:00
李通洲 92b86f7cbb Codec: shows types only specifed by showType 2026-06-01 19:27:37 +08:00
李通洲 2e4b7baf44 Codec (Android): adds showType support 2026-06-01 19:27:37 +08:00
李通洲 a47ff0b2dc Codec (macOS): adds showType support 2026-06-01 19:27:37 +08:00
李通洲 1f13f6c8f5 Codec (Windows): adds showType support 2026-06-01 19:27:37 +08:00
李通洲 b0bb6fb58e Codec: don't show Unknown 2026-06-01 19:27:37 +08:00
Carter Li 37029dbbe3 Codec (Vulkan): adds showType support 2026-06-01 19:27:37 +08:00
Carter Li 35428606e7 Codec (Linux): adds FFCodecShowOptions; code cleanup 2026-06-01 19:27:37 +08:00
Carter Li 2c65eee00f Common (Lua): adds json_encode 2026-06-01 19:27:37 +08:00
Carter Li a1bf014352 Codec: normalizes platformApi string 2026-06-01 10:34:17 +08:00
Carter Li 412fc94dab WM (Linux): fixes possible segfault when detecting sway version
Fixes #2360
2026-06-01 10:16:26 +08:00
Carter Li b65de28805 Logo (Builtin): adds Quasar
Fixes #2338 #2323
2026-06-01 09:34:32 +08:00
李通洲 bc86c66cdd Codec (Windows): detects encoders detection support via MFT on Win10 or older 2026-06-01 09:16:22 +08:00
李通洲 9afa01812d Codec (Vulkan): disables impl if the vulkan headers are too old to use 2026-06-01 00:15:27 +08:00
李通洲 30a8a94cd2 Chore (Haiku): silences compiler warnings 2026-05-31 23:56:45 +08:00
李通洲 3a54617196 Codec: adds vulkan support 2026-05-31 23:45:20 +08:00
李通洲 3e3a03a233 Codec (Android): adds support 2026-05-31 23:37:50 +08:00
李通洲 b407669b69 JsonSchema: update 2026-05-31 23:37:49 +08:00
李通洲 e14ea926a5 Codec (macOS): adds platformApi 2026-05-31 23:37:49 +08:00
李通洲 9d8f794cb9 Codec (Windows): detects encoder support via d3d12va 2026-05-31 23:37:49 +08:00
Carter Li 825b8b8715 Codec (Linux): adds vdpau fallback impl 2026-05-31 23:37:49 +08:00
Carter Li 0c94fd87db Codec: updates default format 2026-05-31 23:37:49 +08:00
Carter Li 473a08d85c Decoder: renames to Codec and adds encoders detection support 2026-05-31 23:37:49 +08:00
Carter Li ae5132c388 Decoder (macOS): adds support 2026-05-31 23:37:49 +08:00
李通洲 1addcec7a2 Decoder (Linux): adds *nix support 2026-05-31 23:37:49 +08:00
李通洲 e75c37c101 Decoder (Windows): adds support 2026-05-31 23:37:49 +08:00
李通洲 b9ab823f77 Decoder: inits module 2026-05-31 23:37:48 +08:00
François Revol a63d854681 OS (Haiku): Fix warnings 2026-05-31 23:37:35 +08:00
François Revol 576548b15e WMTheme (Haiku): implement 2026-05-31 23:37:35 +08:00
François Revol 3043971259 Brightness (Haiku): Implement 2026-05-31 23:37:35 +08:00
François Revol ccf4f22f8b Bootmgr (Haiku): Preliminary implementation 2026-05-31 23:37:35 +08:00
LibereCode f932db713c Logo (Builtin): Minor tweak NixOS_small logo (#2357)
* [fix]: markdown-link to wiki

* [fix]: fix color at 1 spot

* [fix]: fix color at 1 spot, again

* Revert "[fix]: markdown-link to wiki"

This reverts commit 72f4d11c90.

"Remove unrelated changes"
2026-05-31 01:11:53 +08:00
李通洲 d2e8a8fd9e CMake: renames DISABLE_MODULE_* for MODULE_DISABLE_* to align with PACKAGE_DISABLE_* 2026-05-30 18:37:42 +08:00
李通洲 4c1f7c114a Chore: reports if embedded wcwidth is enabled 2026-05-30 18:27:17 +08:00
SirSputnikWiki 90bbfe195f Terminal (Linux): improves terminal name detection for nix packages (#2352)
* Improved terminal name detection for nix packages

* Improved terminal name detection for nix packages

* Update terminalshell_linux.c

Move ffStrbufStartsWithC(&result->processName, '.') before string comparation, as it's much cheaper.
2026-05-30 10:16:12 +08:00
Carter Li 1689fc81e9 Logo (Builtin): adds Origami and Origami_small
Fixes #2321 #2322
2026-05-29 16:05:53 +08:00
Carter Li 4190dd5a6e Logo (Builtin): adds BerserkArch
Fixes #2324 #2310
2026-05-29 16:05:52 +08:00
Carter Li 074f882d7e Wallpaper (Linux): adds COSMIC support 2026-05-29 15:40:27 +08:00
Carter Li 53102380b8 WMTheme (Linux): adds COSMIC support 2026-05-29 15:40:02 +08:00
李通洲 822bc158ba CMake: changes FF_MODULE_XX to FF_DISABLE_MODULE_XX 2026-05-28 23:14:49 +08:00
李通洲 8fbc29a70c Common (strutil): fixes unit tests 2026-05-28 22:41:47 +08:00
李通洲 0ef677a98c Logo (Builtin): add NixOS2
Fixes #2346
2026-05-28 20:12:46 +08:00
Carter Li 10b0d0ca3e CPU (Android): adds Exynos 2600 2026-05-28 19:20:21 +08:00
Carter Li ec4e820e92 Logo: exports ffLogoPrintDetected 2026-05-28 19:18:35 +08:00
Carter Li 6c09fe49e4 Logo (Builtin): warps logo definitions into #ifdef blocks 2026-05-28 18:42:10 +08:00
Carter Li 1ce6398391 Logo (Builtin): splits built-in logos into individual files 2026-05-28 18:42:10 +08:00
Carter Li d1bf208a92 Chore: renames stringUtils to strutil 2026-05-28 15:58:08 +08:00
Carter Li 7ce1e553b8 CMake: makes default structure configurable via CMake 2026-05-28 15:55:43 +08:00
Carter Li 9c878b6943 Chore: moves /src/data/help.json to /doc 2026-05-28 15:54:17 +08:00
Carter Li 6b9a6fd103 CMake: adds MODULE_<module> options to disable modules 2026-05-28 15:41:14 +08:00
Carter Li 896fe60461 CMake: adds option FF_ENABLE_WCWIDTH 2026-05-28 14:47:09 +08:00
Carter Li dadcccdf26 TerminalShell: code refactor 2026-05-28 10:31:35 +08:00
李通洲 0746af7549 Wifi (Linux): adds comments [ci skip] 2026-05-27 17:11:59 +08:00
Carter Li 88c3b7d118 Common (stringUtil): adds ffUtf8CharLenWidth 2026-05-26 16:11:30 +08:00
Carter Li 173d9695b6 Common: removes using of built-in wcwidth, which is locale-dependent 2026-05-26 16:10:01 +08:00
Carter Li 4b265cd56a Common: adds Attr FF_A_ALWAYS_INLINE 2026-05-26 15:37:17 +08:00
李通洲 1f211cf8d1 Brightness: adds ddcciSleep: null to skip DDC/CI detection 2026-05-24 10:58:10 +08:00
李通洲 bac60357c6 Logo: fixes paddingLeft not working with position: top 2026-05-24 08:39:25 +08:00
李通洲 3a0e188570 CMake (Windows): disables usage of WinRT on GCC
Fixes #2341
2026-05-23 22:49:04 +08:00
李通洲 9d5b40686b Global: really prints --stat at the right border 2026-05-23 09:50:49 +08:00
李通洲 a321cd877e Logo: resets color state after printing logo line 2026-05-23 09:44:03 +08:00
李通洲 5f810de1e0 Logo: overrides the auto detected max-width with the configured width, if set 2026-05-23 09:33:58 +08:00
李通洲 3f0a93d057 Logo: reworks position: top; adds a new option --logo-padding-bottom 2026-05-22 20:26:18 +08:00
Carter Li cbca6a965c Logo: prints ASCII logo line by line 2026-05-22 19:09:34 +08:00
Carter Li fde1195c27 TerminalFont (macOS): fixes fallback font detection of Ghostty; adds Muxy terminal support 2026-05-22 14:20:39 +08:00
Carter Li 9856d97bd1 Common (macOS): unifies AppBundle version detection code 2026-05-22 13:37:48 +08:00
Carter Li 2751737ebe CI: builds with quickjs-ng support 2026-05-22 10:24:25 +08:00
Carter Li 834292df29 Brightness (DFBSD): fixes compiling 2026-05-22 10:09:39 +08:00
Carter Li a9a29869a4 Format: tidy 2026-05-22 10:09:39 +08:00
Carter Li 242fb50461 Format: traits undefined result as an error 2026-05-22 09:26:52 +08:00
Carter Li b5c4ffcc85 Format (Lua): simplifies error messages 2026-05-21 09:25:41 +08:00
李通洲 2c3628c630 Format (QJS): converts UINT64 to double and drops usage of BigInt 2026-05-20 23:01:28 +08:00
李通洲 4cca9bf198 Presets: removes invalid comments 2026-05-20 18:40:36 +08:00
李通洲 0ed27d9700 Format: fixes a possible OOB read 2026-05-20 18:40:20 +08:00
李通洲 9bca4eb48f Format: normalizes arg name before pass to script language 2026-05-20 18:39:12 +08:00
李通洲 462b80fc36 CMake: corrects the package name of quickjs-ng 2026-05-20 18:17:38 +08:00
Carter Li f49620a95b CMake: hardens Lua handling 2026-05-20 16:18:50 +08:00
Carter Li 6e0949c739 CMake: fixes finding quickjs 2026-05-20 09:32:02 +08:00
Carter Li 4b18ab0de7 Brightness (macOS): fixes building 2026-05-20 09:31:35 +08:00
Carter Li 8abd75a746 Brightness: refactor 2026-05-20 00:09:09 +08:00
Carter Li d5b215e074 Brightness (FreeBSD): improves DDC/CI handling 2026-05-20 00:07:20 +08:00
Carter Li 0b22427091 WM (Linux): fixes swayfx version detection on NixOS 2026-05-19 17:20:56 +08:00
Carter Li aa34910b6c CMake: actually compiles with quickjs-ng definitions 2026-05-19 15:52:01 +08:00
李通洲 52972ed833 CI (Windows): bundles dlls of lua & qjs 2026-05-18 11:06:30 +08:00
李通洲 68e75effab CMake: adds comments [ci skip] 2026-05-18 11:01:37 +08:00
Carter Li bb79370d03 Brightness (FreeBSD): adds ddc-ci detection support (WIP) 2026-05-18 10:08:31 +08:00
李通洲 9613cdd146 CI: builds with quickjs support 2026-05-18 01:42:13 +08:00
李通洲 724b816252 CI: builds with lua support 2026-05-17 23:21:49 +08:00
李通洲 7c89a10add Common: adds Lua script support for custom format 2026-05-17 20:12:03 +08:00
李通洲 fb5ec76eb3 Global: skips module if format buffer is empty 2026-05-17 19:55:30 +08:00
Carter Li 7141214f95 WM (Linux): fixes swayfx version detection 2026-05-17 09:51:19 +08:00
Carter Li 7d6074968f Packaging: update debian stuff [ci skip] 2026-05-15 09:40:40 +08:00
Carter Li 3bd380802b Merge remote-tracking branch 'origin/master' into dev 2026-05-15 09:40:24 +08:00
李通洲 ec1200dc06 Media (Windows): removes using of C++20 std::bind_front 2026-05-14 19:50:38 +08:00
Carter Li 5c18c061b3 GPU (Linux): silences a warning in Release mode 2026-05-14 09:39:17 +08:00
Carter Li acf610416c Merge pull request #2331 from fastfetch-cli/dev
Release: v2.63.1
2026-05-13 19:54:48 +08:00
Carter Li 90d3b3d49f Release: v2.63.1 2026-05-13 19:41:21 +08:00
Carter Li bbc5b4de98 Wifi (Linux): disables usage of Netlink on big-endian platforms
Until some can confirm it works
2026-05-13 19:29:33 +08:00
Carter Li 7f9b6187a6 CommandOption: fixes segfault with unknown module 2026-05-13 19:28:36 +08:00
Carter Li 8067e43631 Media (Linux): be compatible with signed media length
Fixes Chrome support
2026-05-13 19:07:14 +08:00
Carter Li 61dba1ea05 Packaging: update debian stuff [ci skip] 2026-05-13 16:04:51 +08:00
Carter Li a94df76bfa Merge pull request #2330 from fastfetch-cli/dev
Release v2.63.0
2026-05-13 16:04:31 +08:00
Carter Li b2b748b07c DisplayServer (Linux): fixes a memory leak 2026-05-13 15:44:00 +08:00
Carter Li 77ce6b0e4c Settings (Linux): shuts libeet down if applicate 2026-05-13 15:33:55 +08:00
Carter Li d28d307b63 Release: v2.63.0 2026-05-13 14:56:22 +08:00
Carter Li 3492f9bfa1 Common (Linux): tidy 2026-05-13 14:38:16 +08:00
李通洲 79c67ec0d7 CI (Linux): installs libefl 2026-05-13 14:37:39 +08:00
Carter Li 6527f03a9b Logo (Builtin): add Ximper Linux
Fixes #2283
2026-05-13 13:53:00 +08:00
Carter Li a8b629f58c DE (Linux): fixes an inverted test 2026-05-13 13:23:23 +08:00
Carter Li 5df00a3394 Global: adds missing #endifs back 2026-05-13 13:14:21 +08:00
Carter Li 8a7c83cac6 Doc: update changelog 2026-05-13 11:08:02 +08:00
Carter Li 27bffb3464 Windows: removes unused config wmiTimeout 2026-05-13 11:07:48 +08:00
李通洲 875afb7cde Media (Windows): adds global.playerName selection support 2026-05-13 10:19:45 +08:00
李通洲 3e99a7035b Media (Windows): adds length & position detection support 2026-05-13 10:19:31 +08:00
Carter Li 63873763de Doc: update JsonSchema [ci skip] 2026-05-13 10:17:06 +08:00
Carter Li 27c83e88f2 Media: prints hours if the song is longer than 1 hour 2026-05-13 10:12:07 +08:00
Carter Li f48904805f Media: fixes percentage format 2026-05-13 10:12:07 +08:00
Carter Li 531cd0909a Media (Linux): don't use playerctld as a media player 2026-05-13 10:12:07 +08:00
李通洲 eafcdfec9e WM (Linux): fixes building 2026-05-12 23:10:09 +08:00
李通洲 d6bd855733 DateTime: adds AM/PM format support to date and time output
Fixes #2325
2026-05-12 22:54:49 +08:00
李通洲 af7e2d47d1 OS (Windows): adds support for Embedded variant in OS detection 2026-05-12 22:54:31 +08:00
Carter Li f71c0b0866 Media: adds progress number & bar format support 2026-05-12 19:14:56 +08:00
Carter Li 1400291ae9 Media (macOS): adds length & position detection support 2026-05-12 15:07:22 +08:00
Carter Li 7799dbe481 WM: adds full support of Enlightenment
Fixes #2165
2026-05-12 10:56:14 +08:00
Carter Li 8426833c36 DE (Linux): adds Enlightenment version detection support 2026-05-12 08:36:50 +08:00
Carter Li 235a2018fc Chore: update .clang-format [ci skip] 2026-05-12 08:36:50 +08:00
李通洲 470e3b4e57 IO (Linux): fixes a possible fd leak 2026-05-12 00:08:27 +08:00
Carter Li 1942dce980 Media (Linux): detects song length & play position 2026-05-11 23:25:52 +08:00
Carter Li efca4f3597 DBus: expands Get(U)Int to (u)int64 2026-05-11 23:24:45 +08:00
Carter Li 43ddbdca85 Logo (Builtin): tidy
Fk `clang-format`
2026-05-11 11:06:01 +08:00
Carter Li 5d29e26509 Logo (Builtin): cleans up Kylin 2026-05-11 11:03:15 +08:00
Carter Li 8daec2568c Common (Windows): adds a missing #include 2026-05-11 10:13:48 +08:00
Carter Li 320e1bf4e0 Doc: update changelog [ci skip] 2026-05-11 09:42:48 +08:00
Carter Li 38e98d3d51 Logo (Builtin): cleans up xj380 2026-05-11 09:37:57 +08:00
HNO₃ 50964fcddf Logo (Builtin): adds openRuyi (#2318)
* Logo: Add openRuyi

* Update ASCII art in openruyi.txt

* Fix casing of 'openRuyi' in builtin.c

---------

Co-authored-by: Z572 <z572@z572.online>
Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-05-11 09:37:02 +08:00
国企鱼 6c7a8a8c41 Logo (Builtin): adds XJ380 OS Logo (#2309) 2026-05-11 09:36:32 +08:00
Carter Li 92082eed54 Doc: update PR template [ci skip] 2026-05-11 08:56:31 +08:00
Carter Li 26b3a84b8a Terminal: fixes TDE konsole version detection
Fixes #2319
2026-05-11 08:51:46 +08:00
Carter Li 0747fc0887 CI (OmniOS): fixes build 2026-05-10 17:35:33 +08:00
Carter Li 60fbe1fd0b Common (Windows): uses WinRT APIs only when FF_HAVE_WINRT is defined 2026-05-10 16:46:20 +08:00
Carter Li f8667d9faa Doc: updates changelog 2026-05-10 16:20:48 +08:00
Carter Li 1c89bbc34e Chore: clang-format 2026-05-10 16:00:22 +08:00
Carter Li b37f020270 DisplayServer (Linux): binds minimal version to avoid breakage 2026-05-10 15:43:13 +08:00
Carter Li fbc9569e13 DisplayServer (Linux): updates kde-output-device-v2 again 2026-05-10 15:37:03 +08:00
François Revol a923f61115 Wallpaper (Haiku): adds support (#2314)
* Wallpaper (Haiku): Add support

* Refactor wallpaper detection logic and memory handling

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-05-09 20:35:25 -05:00
李通洲 bc0a515998 Common (Windows): works around a compiler optimise bug 2026-05-09 15:43:15 +08:00
李通洲 87d89130ad Common (Windows): fixes Windows 11 detection code 2026-05-09 15:43:15 +08:00
李通洲 f46aa316cb Wifi (Linux): improves protocol reporting 2026-05-09 14:34:23 +08:00
Carter Li fdb5f30cb8 CMake (Linux): removes linux/wireless detection
Always required now
2026-05-09 11:04:39 +08:00
Carter Li 91f1c4db10 Wifi: fixes 60 GHz band detection 2026-05-09 11:01:50 +08:00
Carter Li 44e2948b1d Wifi: adds 6 GHz channels detection 2026-05-09 11:01:05 +08:00
Carter Li b86acf6c36 Wifi (Linux): fixes bugs when multiple BSS exists; adds ioctl fallback 2026-05-09 11:00:40 +08:00
Carter Li 9a15666248 Wifi (Linux): switches to netlink impl 2026-05-08 22:43:16 +08:00
李通洲 f5dfec6dcb Battery (Windows): tidy
Co-authored-by: Copilot <copilot@github.com>
2026-05-08 14:31:42 +08:00
Carter Li 8c55c06fa7 OS (Linux): detects bedrock only on Linux 2026-05-06 18:15:57 +08:00
李通洲 8159f473cc Common (Windows): allows COM be used in C sources 2026-05-06 15:01:40 +08:00
李通洲 dca3b154ef Global (Windows): fixes possible use-before-init errors 2026-05-06 15:00:52 +08:00
李通洲 79b61f1391 Common (Windows): inits WinRT/COM globally 2026-05-06 14:38:10 +08:00
李通洲 ba54763dde Camera (Windows): adds support for Display P3 color space 2026-05-06 11:06:07 +08:00
Oliver Lin 451bc394ee OS (Linux): enhances Deepin version detection using /etc/os-version (#2300)
* feat(os): enhance Deepin detection with os-version data

Add a Deepin-specific enhancement in Linux OS detection that reads
`/etc/os-version` for `MinorVersion` and `EditionName`.

When available, it now:
- sets `versionID` to `MinorVersion`
- updates `prettyName` to include version and optional edition

This improves Deepin version accuracy and provides a clearer OS name
than relying solely on generic `os-release` fields.feat(os): enhance Deepin detection with os-version data

Add a Deepin-specific enhancement in Linux OS detection that reads
`/etc/os-version` for `MinorVersion` and `EditionName`.

When available, it now:
- sets `versionID` to `MinorVersion`
- updates `prettyName` to include version and optional edition

This improves Deepin version accuracy and provides a clearer OS name
than relying solely on generic `os-release` fields.

* fix(os): use distro name when formatting Deepin prettyName

Update Deepin enhancement formatting to build `prettyName` from
`result->name` instead of hardcoding "Deepin". This keeps output
consistent with the detected distro name while still appending version
and edition details.fix(os): use distro name when formatting Deepin prettyName

Update Deepin enhancement formatting to build `prettyName` from
`result->name` instead of hardcoding "Deepin". This keeps output
consistent with the detected distro name while still appending version
and edition details.

* fix(os/linux): guard Deepin minor version override

Only apply Deepin’s `minor` value to `versionID` when an edition is
present and `prettyName` does not already include parenthesized details.
This avoids incorrectly overriding parsed version info in enhanced names.fix(os/linux): guard Deepin minor version override

Only apply Deepin’s `minor` value to `versionID` when an edition is
present and `prettyName` does not already include parenthesized details.
This avoids incorrectly overriding parsed version info in enhanced names.

Co-authored-by: Copilot <copilot@github.com>

* Refactor Deepin enhancement detection logic

* Refactor detectDeepinEnhancement function

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-05-05 20:43:17 -05:00
李通洲 93f9ed1bf5 Wmtheme (Linux): adds NebiDE support
Ref: #2295
2026-05-04 19:44:42 +08:00
Sarp Mateson d9e4389ed4 Logo (Builtin): add NebiOS (#2295)
* added logo with nebisoft's color palette, updated builtin.c

* forgot to update the nebios logo with    color placeholders
2026-05-04 19:42:51 +08:00
李通洲 b465052ad2 Logo (Builtin): removes Ubuntu KDE 2026-05-02 22:19:26 +08:00
李通洲 2e6b9cd503 OS (Linux): adds comments 2026-05-02 22:18:08 +08:00
李通洲 0557f16c16 OS (Linux): adds Ubuntu Unity detection 2026-05-02 22:06:18 +08:00
李通洲 08e252a44c OS (Linux): adds Ubuntu Kylin detection 2026-05-02 21:59:22 +08:00
Oliver Lin 90e2d8f8d9 OS (Linux): detects Ubuntu Studio before Kubuntu on KDE systems (#2298)
* fix(os): detect Ubuntu Studio before KDE-based flavor checks

Add an explicit Ubuntu Studio detection in `getUbuntuFlavour()` by
checking for `/usr/share/doc/ubuntustudio-desktop` and setting
`name`, `id`, and `idLike` accordingly.

This prevents Ubuntu Studio systems from being misidentified as
Kubuntu when KDE/Plasma-related XDG config paths are present.fix(os): detect Ubuntu Studio before KDE-based flavor checks

Add an explicit Ubuntu Studio detection in `getUbuntuFlavour()` by
checking for `/usr/share/doc/ubuntustudio-desktop` and setting
`name`, `id`, and `idLike` accordingly.

This prevents Ubuntu Studio systems from being misidentified as
Kubuntu when KDE/Plasma-related XDG config paths are present.

* refactor(os): detect Ubuntu Studio via package presence
2026-05-02 21:54:33 +08:00
Angelo Verlain 0dd8d80e02 Logo (Builtin): correctly detects GNOME OS (#2296)
* Correctly detect GNOME OS

* Update builtin.c

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-05-02 01:06:47 +08:00
Christopher L Fox Junior 3cddf31987 Add ASCII logo for KibaOS (#2294)
* Add ASCII logo for KibaOS

* Add KibaOS logo to builtin.c

* Update builtin.c

* Update builtin.c

* Add KibaOS logo to builtin logo configurations

Added KibaOS logo configuration to the logo data structure.

* Fix KibaOS Syntax
2026-05-01 17:13:32 +08:00
李通洲 fe1c5c8f6f Media (Windows): improves readability
Co-authored-by: Copilot <copilot@github.com>
2026-05-01 11:58:19 +08:00
李通洲 994bd53d30 Media (Windows): major code refactor
moves detection code into fastfetch binary

Co-authored-by: Copilot <copilot@github.com>
2026-04-30 20:26:57 +08:00
李通洲 383a4190ca Comon (macOS): tidy
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 13:26:46 +08:00
李通洲 42f2e5a745 Netif (macOS): improves reliability 2026-04-30 11:01:07 +08:00
Carter Li 14b8bb2bb5 Temps: validates temp color thresholds as integers in JSON parsing
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 09:33:48 +08:00
Carter Li b68ad3626c Sysctl: returns error on failed string fetch and removes malloc null check
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 09:31:28 +08:00
Carter Li cd25803e32 Option: adds a missing validation 2026-04-30 09:27:05 +08:00
Carter Li 3d6bffe7fe Settings: fixes DBusMessage reply leaks in XFConf functions
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 09:04:02 +08:00
Carter Li e694c8ac84 Properties: fixes a typo
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 08:50:23 +08:00
李通洲 dfff57dd85 Percent: improve reliability
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 08:40:30 +08:00
李通洲 c5141855a2 Path: removes dead code 2026-04-30 08:40:30 +08:00
李通洲 6cb186f229 Parsing: improves reliablity
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 08:40:30 +08:00
李通洲 e91f62aa70 Option: improve reliablity 2026-04-30 08:40:30 +08:00
李通洲 ab5cf94099 Networking: improves reliablity
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 08:40:30 +08:00
李通洲 d9eccd70ed Battery (Windows): removes option useSetupApi 2026-04-29 18:54:18 +08:00
李通洲 025febc78d Battery (Windows): switches to query WMI with NtApi as a fast path
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 18:50:04 +08:00
李通洲 69cc19d6f5 Brightness (Windows): cleanups debug log 2026-04-29 18:42:12 +08:00
李通洲 2e4dd1d262 Chore: silenses compiler warnings 2026-04-29 17:36:37 +08:00
李通洲 24ec0e6035 Common (Windows): removes old COM based WMI query implementation 2026-04-29 00:11:29 +08:00
李通洲 6e50aaf3ff Brightness (Windows): uses COM-less method to query WMI
Co-authored-by: Copilot <copilot@github.com>
2026-04-29 00:11:29 +08:00
李通洲 e7cd5f618d Bluetooth (Windows): uses CM API to detect BTH battery detection
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 19:52:38 +08:00
李通洲 55e4896524 Chore: runs clang-format 2026-04-28 19:45:19 +08:00
李通洲 594c73461e Netif: fixes compiler warnings 2026-04-28 15:19:44 +08:00
Or-simen 94ceeb4482 Chore: fixes typo from FF_UNITIALIZED to FF_UNINITIALIZED (#2290)
Co-authored-by: or sim <you@example.com>
2026-04-27 21:23:42 -05:00
李通洲 4ee7dd72d0 IO: improves code quality & reliablity 2026-04-28 09:03:24 +08:00
李通洲 414f7ce067 Netif: improves code quality
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 08:51:03 +08:00
李通洲 e1dffe794b Library: improves code quality
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 08:51:03 +08:00
李通洲 181b775f73 Init: don't print unecessary ANSI escape codes
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 23:15:30 +08:00
李通洲 c4c934d23e Format: fixes UINT64 handling
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 23:00:52 +08:00
李通洲 31c73543e8 Font: improves pt handling
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 22:57:29 +08:00
李通洲 d79e1d7fbc FFstrbuf: improves robustness of ffStrbufMatchSeparatedNS
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 22:49:31 +08:00
李通洲 3883047dce Platform: initializes pageSize
Noop, because the whole platform is statically initialized

Co-authored-by: Copilot <copilot@github.com>
2026-04-27 22:35:11 +08:00
李通洲 4eebc1b6ee Platform (Windows): adds comments
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 22:30:48 +08:00
李通洲 08b79ff4ae Platform (OpenBSD): silences a compiler warning
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 22:27:10 +08:00
李通洲 ffc38eb0f1 FFlist: add __restrict 2026-04-27 22:21:45 +08:00
李通洲 37ee193c40 DBus: improves message handling 2026-04-27 22:11:34 +08:00
李通洲 c10027f1d7 JsonConfig: fixes CPUUsage preparation never executed
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 21:56:52 +08:00
李通洲 039fa1ba77 CommandOption: generates JSON result entry even for unknown modules
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 20:28:11 +08:00
李通洲 0db820a6c1 Binary: skips short string entirely to improve performance
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 19:51:14 +08:00
tnut 8b611899d1 Packages (Linux): adds cards counting (NuTyX) (#2287)
* Packages:add cards counting (NuTyX)

* package.c
no need to add counts.catds

* Sort packages print alphabetically

* ditto

* ditto

* packages.c
update FF_ARG and ffpackagesModule.info

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-04-27 19:31:08 +08:00
李通洲 ba7e1eb18b Netif (Windows): correctly selects lowest-metric default route on IPv4
... and improve performance slightly
2026-04-26 16:57:38 +08:00
李通洲 6979b1efc5 Dep (3rdparty): upgrades display-library 2026-04-26 16:00:01 +08:00
Carter Li 4a61cdb1c9 Merge pull request #2280 from fastfetch-cli/dev
Release v2.62.1
2026-04-23 20:12:45 +08:00
李通洲 75c28ec671 Release: v2.62.1 2026-04-23 19:09:24 +08:00
李通洲 639252ece0 Logo (Builtin): adds uzbek
Fixes #2159
2026-04-23 15:19:33 +08:00
lost a31d8ce87b Logo (Builtin): adds support for Redrose (#2236)
* Added support for Redrose.

* Remove 'redrose' OS identification logic

* Remove dollar signs from ASCII art in redrose.txt

* Update logo name from 'redrose' to 'Redrose'

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-04-23 02:19:19 -05:00
Uzair Mughal 41143c23f4 Logo (Builtin): adds LimeOS logo (#2137)
* Logo (Builtin): adds LimeOS logo

Adds support for LimeOS, a Debian-based distribution.

- Adds ASCII logo (white color scheme)
- Adds OS detection for `ID=limeos`

Homepage: https://limeos.org

* Logo (Builtin): adds LimeOS logo

Adds support for LimeOS, a Debian-based distribution.

Homepage: https://limeos.org

* Logo (Builtin): adds LimeOS logo

* Update builtin.c
2026-04-23 01:53:11 -05:00
Endscape 3c2e21b7b1 Logo (Builtin): adds EN-OS
[LOGO] Add EN-OS
2026-04-23 01:51:50 -05:00
Carter Li 3eaebd3359 Host (Linux): fixes host is missing in some machine
Fixes #2279
2026-04-23 09:35:03 +08:00
Carter Li 768bc154d4 Packaging: updates debian stuff [ci skip] 2026-04-22 10:02:21 +08:00
Carter Li 00cc19e28b Merge pull request #2277 from fastfetch-cli/dev
Release v2.62.0
2026-04-22 09:35:08 +08:00
李通洲 bf0564f9e5 Terminal (Windows): removes using of Win32 registry APIs 2026-04-21 23:41:34 +08:00
Carter Li dbb060eb62 Global: addresses AI's comments 2026-04-21 19:43:11 +08:00
Carter Li 1314c18a52 Release: v2.62.0 2026-04-21 17:15:23 +08:00
李通洲 05f5e25c66 Packages: rearranges the packages output in alphabet order
... and improves string format logic
2026-04-21 14:41:27 +08:00
李通洲 4b4c37ed88 Packages (Linux): adds AppImage packages support
Fixes #2179
2026-04-21 14:16:55 +08:00
李通洲 8970774e5f Doc: update changelog [ci skip] 2026-04-21 10:53:19 +08:00
Carter Li 69c86c95b7 GPU: hard codes QEMU virtual GPU 2026-04-20 22:30:51 +08:00
李通洲 910ba413da Modules: adds missing options when generating full config file 2026-04-20 16:14:58 +08:00
李通洲 dbed1b32b4 Style: runs clang-format 2026-04-20 15:22:10 +08:00
李通洲 278a3834ce Logo (Image): uses iterm image protocol on iTerm terminal
Fixes #2246
2026-04-18 23:30:20 +08:00
李通洲 5ce3220f50 Logo (Builtin): updates AerynOS
Fixes #2272
2026-04-18 23:16:37 +08:00
李通洲 c1c6ccce9a Shell (Linux): fixes ash version detection
Fixes #2271
2026-04-17 18:50:35 +08:00
李通洲 e2526521a2 Smbios: fixes typo 2026-04-16 23:18:56 +08:00
李通洲 1956c66a5e PhysicalMemory (Android): fixes compiling 2026-04-16 22:42:59 +08:00
李通洲 c601b68bc6 Doc: improves overall English grammer 2026-04-16 18:27:11 +08:00
李通洲 528f9f152c Config: adds display.common.(ndigits|spaceBeforeUnit) 2026-04-16 15:45:12 +08:00
李通洲 64d2c4410e Binary (macOS): improve performance with mmap(2) 2026-04-16 15:15:50 +08:00
李通洲 6e4de3e9f1 Platform (Windows): uses LsaGetUserName to acquire user name 2026-04-16 15:03:08 +08:00
Carter Li ce51dbf7ea Smbios (Linux): fixes compiling errors 2026-04-16 15:03:08 +08:00
李通洲 abc6c0593c Chore: IndentPPDirectives: BeforeHash [ci skip] 2026-04-15 16:17:08 +08:00
李通洲 b63bd5b8c9 Smbios: refactors code 2026-04-15 16:13:56 +08:00
李通洲 4236d830cc Options: uses enums instead of hard code number [ci skip] 2026-04-15 10:09:44 +08:00
李通洲 c851c39837 Options: fixes copy&paste errors 2026-04-15 09:56:23 +08:00
Carter Li 1cd196b726 Smbios: uses the standard type off_t 2026-04-15 09:02:35 +08:00
李通洲 f8a8864be5 Smbios (OpenBSD): reads table address from dmesg 2026-04-15 09:02:35 +08:00
Carter Li 683e90cec6 Smbios (FreeBSD): unifies pread/mmap handling
Don't try `pread(/dev/mem)` on FreeBSD, which causes kernel panic
2026-04-15 09:02:35 +08:00
李通洲 08de5eabed Sound: merges isMain and isActive to type 2026-04-15 09:02:35 +08:00
李通洲 c7ecc33b64 Global: removes FFlist::elementSize
Improves performance of module Sound and PhysicalMemory
2026-04-14 15:47:26 +08:00
Carter Li ee7f574349 Smbios (OpenBSD): reads table address from dmesg 2026-04-14 15:47:26 +08:00
Carter Li 10f58e405e Smbios (FreeBSD): adds fallback method for non-UEFI systems 2026-04-14 00:20:01 +08:00
Carter Li d81a7b6278 Smbios: improves logging 2026-04-13 18:20:19 +08:00
李通洲 48daf4ac1e Camera (macOS): fixes a compiler warning 2026-04-13 16:16:57 +08:00
李通洲 6d01c0b438 PhysicalDisk (NetBSD/OpenBSD): adds debug logs 2026-04-13 14:25:42 +08:00
李通洲 3cc0e40e5a Smbios: updates EMTF standard version 2026-04-13 09:38:47 +08:00
李通洲 679def253d Global: formats code [ci skip] 2026-04-12 19:19:55 +08:00
李通洲 4dc5033c80 PowerAdapter (Linux): checks if the device is present 2026-04-10 08:48:55 +08:00
Carter Li b3603391eb Battery: refactors status field from string to bit-fields 2026-04-09 20:02:04 +08:00
Carter Li f6a363f7ad Global: unifies GCC attributes usage 2026-04-09 20:02:04 +08:00
Carter Li 3e69361196 Battery (OpenBSD): adds Unknown state 2026-04-09 15:49:41 +08:00
Carter Li 04bd5045b9 PowerAdapter (Linux): tidy 2026-04-09 15:20:36 +08:00
李通洲 886bde47d6 Battery (Linux): checks if battery's present before parsing 2026-04-09 14:01:24 +08:00
李通洲 254fb82520 Battery (Linux): fixes AC status can be incorrectly detected as Connected
... in machine that has multiple batteries

Fixes #2263
2026-04-09 13:54:50 +08:00
李通洲 af884bb638 FFstrbuf: silence a compiler warning 2026-04-09 13:54:50 +08:00
李通洲 e8e43c10fc PhysicalDisk: renames hideUnknown for hideUnused 2026-04-09 13:54:50 +08:00
李通洲 9090dbd276 PhysicalDisk (macOS): detects 0-size disks 2026-04-09 13:54:50 +08:00
李通洲 f683586623 Swap (Windows): fixes out-of-bound memory access when swaps are disabled 2026-04-09 13:54:50 +08:00
Carter Li d71d6a679d Swap (macOS): fixes a compiler warning 2026-04-09 13:54:50 +08:00
李通洲 17eb22dfdb PhysicalDisk: adds option hideVirtual (defaults to false) and hideUnknown (defaults to true) 2026-04-09 13:54:50 +08:00
Carter Li c09a788ce3 PhysicalDisk (OpenBSD): honors namePrefix 2026-04-09 09:35:48 +08:00
Carter Li ab32056c3f PhysicalDisk (NetBSD): honors namePrefix 2026-04-09 09:35:48 +08:00
李通洲 e15049856f GPU (Windows): improves support of non-PCI devices 2026-04-09 09:35:48 +08:00
李通洲 16423487fb GPU (Windows): updates comments 2026-04-09 09:35:48 +08:00
李通洲 6b95eb2c74 GPU: fixes the vendor name of Parallels 2026-04-09 09:35:48 +08:00
Faisal Ahmed Moshiur 2b82e9ab02 Fix potential use-after-free and overlapping memory copy in ffStrbufSetNS 2026-04-09 09:33:58 +08:00
李通洲 5fe2cc6350 PhysicalDisk (Windows): adds simple debug log 2026-04-06 10:36:46 +08:00
李通洲 50739ee890 Cursor (Windows): fixes Win8.1 support 2026-04-06 10:00:40 +08:00
李通洲 97ce079488 GPU (Windows): adds fallback method for GPU vendor detection 2026-04-06 09:33:23 +08:00
Carter Li 7045dea284 Global (Windows): fixes building on i686 2026-04-05 14:40:38 +08:00
李通洲 7887067301 PhysicalDisk (Windows): switches to CMAPI; adds floppy support 2026-04-05 20:37:23 +08:00
李通洲 39cfc7aac8 Debug (Windows): ensures UTF-8 output 2026-04-05 20:21:50 +08:00
李通洲 44af8b85ad Battery (Windows): tidy 2026-04-05 20:21:50 +08:00
李通洲 85594d2056 GPU (Windows): restores Win8.1 support 2026-04-05 14:26:22 +08:00
Данил Голдышев 14cd933969 fix(help): align format placeholders dynamically 2026-04-05 10:32:23 +08:00
Данил Голдышев 6550b77c2f fix(zpool): correct documented format placeholders 2026-04-05 10:32:23 +08:00
李通洲 7147f23504 GPU (Windows): ignores invalid D3DKMT responses 2026-04-05 01:31:14 +08:00
李通洲 94164b873c GPU (WSL): unifies code with the windows version 2026-04-05 00:37:58 +08:00
李通洲 9cfe7a74bc GPU (Windows): rewrites with D3DKMT APIs
... which aligns to WSL impl
2026-04-05 00:37:58 +08:00
Eli Sher 604aacbae2 Wifi (macOS): corrects typo in WPA Personal Mixed security string (#2260) 2026-04-04 23:39:53 +08:00
Carter Li 04c486d3db PhysicalDisk (SunOS): detects unknown type 2026-04-04 00:29:08 +08:00
Carter Li 152f3cdee1 Disk (SunOS): trims disk name 2026-04-04 00:18:42 +08:00
李通洲 b7890e1e2a PhysicalDisk (SunOS): better device type detection 2026-04-04 00:08:25 +08:00
李通洲 8913620aa5 PhysicalDisk (NetBSD): fixes building on older systems 2026-04-03 22:32:35 +08:00
李通洲 2a103f620d PhysicalDisk (NetBSD): adds support
Requires root permission
2026-04-03 21:01:49 +08:00
李通洲 8d0ceff7fa PhysicalDisk (OpenBSD): fixes issues found later 2026-04-03 21:01:15 +08:00
李通洲 ce9f4daec4 PhysicalDisk: adds unknown in JSON result 2026-04-03 15:45:10 +08:00
李通洲 e639700387 PhysicalDisk (OpenBSD): fix potencial out-of-bound read 2026-04-03 15:44:47 +08:00
李通洲 3da344db1f DiskIO (NetBSD): ignores partitions 2026-04-03 10:53:51 +08:00
Carter Li 31a35cf845 Bios (NetBSD): adds fall back method 2026-04-03 10:25:04 +08:00
李通洲 77c916f747 CI: splits different jobs into different yml files 2026-04-03 08:55:38 +08:00
李通洲 c9dbf990d8 PhysicalDisk (OpenBSD): adds support 2026-04-02 20:10:14 +08:00
李通洲 ed4d903e69 Sysctl (OpenBSD): improves error messages 2026-04-02 20:06:23 +08:00
李通洲 299451ca62 GPU (Linux): ensures we don't pass invalid length to calloc
Fixes #2259
2026-04-01 23:31:36 +08:00
李通洲 6319b68c2c PhysicalDisk (FreeBSD): adds type virtual and type unknown detection 2026-04-01 15:08:44 +08:00
李通洲 c37ac4da2b PhysicalDisk (Windows): adds type virtual and type unknown detection 2026-04-01 15:08:26 +08:00
李通洲 643db92cd1 PhysicalDisk (Linux): adds type unknown detection 2026-04-01 15:08:02 +08:00
李通洲 99795154b5 PhysicalDisk (macOS): adds type unknown detection 2026-04-01 15:06:15 +08:00
Carter Li 6cf403da7d Global: renames smbiosHelper to smbios 2026-03-31 20:01:25 +08:00
Carter Li 4f3e82330b Smbios: improves log 2026-03-31 19:58:47 +08:00
李通洲 0c401a0112 PhysicalDisk: detects virtual disks 2026-03-31 19:48:54 +08:00
李通洲 d4b1ae9751 PhysicalDisk / DiskIO (macOS): improves performance by query less IO entries 2026-03-31 14:20:21 +08:00
李通洲 831659966f Style: adds Cpp11BracedListStyle: Block 2026-03-31 14:20:21 +08:00
李通洲 5f261702a9 Style: adds BreakBeforeCloseBracketBracedList: true 2026-03-31 14:20:21 +08:00
李通洲 c2da9b0cb1 Chore: tidy 2026-03-31 14:20:21 +08:00
李通洲 239233f4e0 Swap (macOS): don't check if swap is enabled on macOS 26 or later 2026-03-31 14:20:21 +08:00
Eitan b00593ae05 JsonSchema: adds "command-raw" to schema (#2255)
* Add "command-raw" to json_schema.json

* I spelled it wrong
2026-03-31 13:11:14 +08:00
Roshan Warrier b4e6688dfe Localip: uses assigned netlink port id on Linux (#2253)
Fixes #2252
2026-03-30 18:39:43 +08:00
Carter Li b022babd7e Packaging: update debian stuff [ci skip] 2026-03-30 10:33:58 +08:00
Carter Li 52c7f9f973 Swap (macOS): display Unused instead of Disabled for 0/0
Fixes #2248
2026-03-29 11:19:07 +08:00
李通洲 e0eba8ce21 Chore: run clang-format
```bash
find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" \) ! -path "src/3rdparty/*" -print0 | xargs -0 clang-format -i
```
2026-03-29 10:03:32 +08:00
李通洲 cdd4cee968 Chore: add clang-format configuration 2026-03-29 10:03:22 +08:00
李通洲 1b23edc5c1 Flashfetch: reduces duplicated code 2026-03-29 09:30:00 +08:00
1315 changed files with 47541 additions and 40882 deletions
+89
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
src/3rdparty/**
build/**
src/logo/builtin.c
+4
View File
@@ -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 lua54 libva libvdpau
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-*.*
+42
View File
@@ -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 lua54 libva libvdpau
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-*.*
+40
View File
@@ -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 lua
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-*.*
+40
View File
@@ -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 liblua5.4-dev libvdpau-dev libva-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-*.*
+44
View File
@@ -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 liblua5.4-dev libvdpau-dev libva-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-*.*
+103
View File
@@ -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 liblua5.4-dev libvdpau-dev libva-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 quickjs-ng --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 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew 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-*.*
+70
View File
@@ -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 liblua5.4-dev libvdpau-dev libva-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 quickjs-ng --ignore-dependencies
- name: cmake version
run: cmake --version
- name: configure project
run: CC=gcc-13 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew 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-*.*
+45
View File
@@ -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 liblua5.4-dev libvdpau-dev libva-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-*.*
+59
View File
@@ -0,0 +1,59 @@
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 update
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa lua quickjs-ng
- 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-*.*
+47
View File
@@ -0,0 +1,47 @@
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
with:
branch: edge
- 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 lua5.4-dev quickjs-ng-dev libvdpau-dev libva-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-*.*
+41
View File
@@ -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 libva libvdpau
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
+42
View File
@@ -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-*.*
+42
View File
@@ -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 wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa lua-5.4.7 # pkg-config is preinstalled
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-*.*
+58
View File
@@ -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
+43
View File
@@ -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 lua
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-*.*
+24
View File
@@ -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
+106
View File
@@ -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 }}-lua mingw-w64-clang-${{ inputs.msys-arch }}-quickjs-ng 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/{lua55,libqjs-0}.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
View File
@@ -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
+161
View File
@@ -1,3 +1,164 @@
# 2.64.1
Features:
* Adds a CMake option `-DPACKAGES_REMOVE_DISABLED` to remove detection code of disabled packages (`-DPACKAGES_DISABLE_<PACKAGE_NAME>`) for slightly smaller binary size (Packages)
Bugfixes:
* Fixes compatibility issues with Lua 5.3
* Avoid possible infinite recursion in `encode_json` when encoding deeply nested tables or circular references
* Fixes compilation issues when building with old macOS SDKs
# 2.64.0
New **optional build** dependencies:
* [`libva`](https://github.com/intel/libva) and [`libvdpau`](https://www.freedesktop.org/wiki/Software/VDPAU) for the new `Codec` module.
* [Lua 5.3 to 5.5](https://www.lua.org/) for Lua scripting support, or [QuickJS](https://github.com/quickjs-ng/quickjs) v0.15.0 or newer for JavaScript scripting support.
Features:
* Adds `Codec` module support for Windows, macOS, Linux, and Android for hardware-accelerated video codec detection (Codec)
* Backends used (results may vary depending on the backend due to driver differences):
* Linux and BSD: VA-API (default) and VDPAU (fallback for NVIDIA). Fastfetch must be built with `libva` and `libvdpau` support to enable these backends.
* Windows: D3D12VA (Windows 11) and D3D11VA+MFT (Windows 10 or older)
* macOS: VideoToolbox
* Android: AMediaCodec
* Common: Vulkan Video (disabled by default; can be enabled with `"useVulkan": true`)
* By default, both encoders and decoders are reported. If no codecs are detected for a given type, `None` is reported. This behavior can be configured using the `"showType": "encoder|decoder"` option.
* Adds experimental Lua script support for custom formats (Global)
* Basic usage (using `Title` as an example): `{ "type": "title", "format": "lua:return string.format('Hello %s@%s', (...).userName, (...).hostName)" }`. The `lua:` prefix indicates a Lua script. A `return` statement is required to pass the final result back to the fastfetch module; otherwise, `nil` is returned implicitly and the entire module output is skipped.
* Parameters are passed via variable arguments `(...)`. Users can assign them to named variables for better readability. For example: `lua:local args = ...; string.format('Hello %s@%s', args.userName, args.hostName)`.
* The Lua interpreter instance is shared across all modules. This allows users to store and manipulate data across modules. For example:
* `{ "type": "shell", "format": "lua:shell = ..." } // Note: no "return" here`
* `{ "type": "terminal", "format": "lua:return shell.prettyName .. ' in ' .. (...).prettyName" } // This will print the detected shell and terminal names`
* A `json_encode(table, is_pretty)` function has been added to the Lua API for easier debugging. For example, `lua:return json_encode(...)` will print all available variables and their values in JSON format.
* Supported Lua versions: Lua 5.3 to 5.5 (Lua 5.1 and LuaJIT are not supported). The Lua version is auto-detected at build time. Once built, users can check the configured Lua version using `fastfetch --list-features`.
* Adds experimental QuickJS script support for custom formats as an alternative to Lua (Global)
* Basic usage: `{ "type": "title", "format": "qjs:`Hello ${this.userName}@${this.hostName}`" }`. The `qjs:` prefix indicates a JavaScript script. No `return` statement is needed; the final result is simply the evaluated value of the script.
* Parameters are passed via the `this` object. Usage is mostly the same as Lua, but using JavaScript syntax.
* Requires [quickjs-ng v0.15.0](https://github.com/quickjs-ng/quickjs/releases/tag/v0.15.0) or newer.
* Adds CMake options `-DMODULE_DISABLE_<MODULE_NAME>=ON` to disable modules at compile time for a smaller binary size
* Use `cmake -L . | grep MODULE_DISABLE_` to list all available options.
* These options rely on LTO for dead code elimination (`-DCMAKE_BUILD_TYPE=Release` should enable LTO by default).
* Adds the ability to remove unneeded ASCII logos without modifying the fastfetch source code, further reducing binary size
* Simply remove the corresponding logo file from the logo directory (`src/logo/ascii/[a-z]/*`) and re-run `cmake`.
* Improves string manipulation specifiers in custom formats
* They are now ANSI escape-aware and work correctly with colored output (e.g., percentages with `num-color`) (#2364). Limitations:
* They are still not wide-character aware and treat CJK and emoji characters as raw bytes.
* Escape sequences in the middle of strings are not supported.
* A new `|` specifier has been added to center strings. For example, `{user-name|20}` will center the username in a 20-character-wide field.
* Adds preliminary `Bootmgr`, `Brightness`, and `WMTheme` detection support for Haiku (#2358, Haiku)
* Adds `Wallpaper` and `WMTheme` detection support for the COSMIC desktop environment (Linux)
* Improves terminal name detection for Nix packages (#2352, Terminal, Linux)
* Adds DDC/CI brightness detection support for FreeBSD (Brightness, FreeBSD)
* DDC/CI communication can be very slow. Users can set the `ddcciSleep: null` option to skip DDC/CI detection.
* Reworks the built-in logo printing logic. ASCII logos and modules are now printed line by line, avoiding issues like #2239
* Note: Image logos are not affected by this change and still print the entire image first before printing any modules.
* Adds a new `--logo-padding-bottom` option to control the padding between the bottom of the logo and the first module when `--logo-position top` is used
* Previously, `--logo-padding-right` was used for both right and bottom padding.
* Adds `Samsung Exynos 2600` to CPU detection (CPU, Android)
* Adds terminal font detection support for the Muxy terminal (TerminalFont, macOS)
* Improves the performance of BusyBox (ash) version detection and always reports `ash` as the pretty name (Shell, Linux)
Bugfixes:
* Fixes SwayFX version detection (WM, Linux)
* Fixes fallback font detection for Ghostty (TerminalFont, macOS)
* Fixes `--stat` output to correctly align with the right border (Global)
* Fixes boot manager on macOS 26 (mBoot) is incorrectly reported as `iBoot` (Bootmgr, macOS)
Logos:
* Adds Quasar (#2338, #2323), Origami, Origami_small (#2321, #2322), BerserkArch (#2324, #2310), and NixOS2 (#2346)
* Minor tweaks and color fixes for the NixOS_small logo (#2357)
* Updates NurOS (#2366)
# 2.63.1
Bugfixes:
* Fixes media length detection for Chrome on Linux (Media, Linux)
* Fixes segmentation fault when specifying unsupported modules on command line
* Disables usage of Netlink for Wi-Fi detection on s390x architectures (Wifi, Linux)
# 2.63.0
Changes:
* Introduces a new **build-only** 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 `{am-pm}` to custom format for 12-hour time with am/pm (DateTime)
* 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:
+201 -64
View File
@@ -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.64.1
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -74,9 +74,12 @@ cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeB
cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_VA "Enable libva" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_VDPAU "Enable vdpau" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
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)
@@ -104,7 +107,11 @@ option(INSTALL_LICENSE "Install license into /usr/share/licenses" ON)
option(ENABLE_EMBEDDED_PCIIDS "Embed pci.ids into fastfetch, requires `python`" OFF)
option(ENABLE_EMBEDDED_AMDGPUIDS "Embed amdgpu.ids into fastfetch, requires `python`" OFF)
option(ENABLE_WORDEXP "Enable using of wordexp(3) if available, instead of glob(3)" ON)
option(ENABLE_LUA "Enable lua scripting for format strings" ON)
option(ENABLE_QUICKJS "Enable quickjs scripting for format strings" ON)
option(ENABLE_LIBZFS "Enable libzfs" ON)
option(ENABLE_WCWIDTH "Detect wide-width characters with wcwidth when calculating logo and separator width. Disable for a smaller and slightly faster build, but logos containing CJK/emoji may be measured too narrow and render incorrectly. Such usecases are uncommon." ON)
set(DEFAULT_STRUCTURE "Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Display:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Swap:Disk:LocalIp:Battery:PowerAdapter:Locale:Break:Colors" CACHE STRING "Default module structure for detection")
if(WIN32 AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
option(ENABLE_WIN81_COMPAT "Enable legacy Windows compatibility (Windows 8.1 and later; Windows 7 unsupported)" ON)
endif()
@@ -112,6 +119,14 @@ if(APPLE)
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
endif()
file(GLOB FF_MODULE_DIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/")
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/${FF_MODULE_DIR}")
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
endif()
endforeach()
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch")
set_property(CACHE BINARY_LINK_TYPE PROPERTY STRINGS ${BINARY_LINK_TYPE_OPTIONS})
@@ -119,7 +134,25 @@ 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 APK APPIMAGE
BREW
CARDS CHOCO
DPKG
EMERGE EOPKG
FLATPAK
GUIX
HPKG
KISS
LINGLONG LPKG LPKGBUILD
MACPORTS MOSS MPORT
NIX
OPKG
PACMAN PACSTALL PALUDIS PISI PKG PKGSRC 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)
@@ -127,6 +160,7 @@ foreach(package_manager ${PACKAGE_MANAGERS})
option(PACKAGES_DISABLE_${package_manager} "Disable ${package_manager} package manager detection by default" OFF)
endif()
endforeach()
option(PACKAGES_REMOVE_DISABLED "Remove disabled package managers from the build entirely, instead of just skipping them at runtime" OFF)
if (LINUX)
set(CUSTOM_PCI_IDS_PATH "" CACHE STRING "Custom path to file pci.ids, defaults to `/usr/share/hwdata/pci.ids`")
@@ -160,7 +194,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()
@@ -282,14 +320,14 @@ find_package(Python)
if(Python_FOUND)
message(STATUS "Minifying 'help.json'")
execute_process(COMMAND ${Python_EXECUTABLE} -c "import json,sys;json.dump(json.load(sys.stdin),sys.stdout,separators=(',',':'))"
INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/data/help.json"
INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/doc/help.json"
OUTPUT_VARIABLE DATATEXT_JSON_HELP)
if(DATATEXT_JSON_HELP STREQUAL "")
message(ERROR "DATATEXT_JSON_HELP is empty, which should not happen!")
endif()
else()
message(WARNING "Python3 is not found, 'help.json' will not be minified")
file(READ "src/data/help.json" DATATEXT_JSON_HELP)
file(READ "doc/help.json" DATATEXT_JSON_HELP)
endif()
if(ENABLE_EMBEDDED_PCIIDS AND NOT EXISTS "${PROJECT_BINARY_DIR}/fastfetch_pciids.c.inc")
@@ -349,7 +387,6 @@ else()
endif()
fastfetch_encode_c_string("${DATATEXT_JSON_HELP}" DATATEXT_JSON_HELP)
fastfetch_load_text(src/data/structure.txt DATATEXT_STRUCTURE)
configure_file(src/fastfetch_config.h.in fastfetch_config.h @ONLY)
configure_file(src/fastfetch_datatext.h.in fastfetch_datatext.h @ONLY)
@@ -361,7 +398,7 @@ endif()
# Ascii image data #
####################
file(GLOB LOGO_FILES CONFIGURE_DEPENDS "src/logo/ascii/*.txt")
file(GLOB LOGO_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/logo/ascii/[a-z_]/*.txt")
set(LOGO_BUILTIN_H "#pragma once\n#pragma GCC diagnostic ignored \"-Wtrigraphs\"\n\n")
foreach(file ${LOGO_FILES})
fastfetch_load_text("${file}" content)
@@ -384,6 +421,7 @@ set(LIBFASTFETCH_SRC
src/common/impl/init.c
src/common/impl/jsonconfig.c
src/common/impl/library.c
src/common/impl/lua.c
src/common/impl/netif.c
src/common/impl/networking_common.c
src/common/impl/option.c
@@ -401,10 +439,13 @@ 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/common/impl/strutil.c
src/detection/bluetoothradio/bluetoothradio.c
src/detection/bootmgr/bootmgr.c
src/detection/chassis/chassis.c
src/detection/codec/codec.c
src/detection/codec/codec_vulkan.c
src/detection/cpu/cpu.c
src/detection/cpuusage/cpuusage.c
src/detection/command/command.c
@@ -446,6 +487,7 @@ set(LIBFASTFETCH_SRC
src/modules/btrfs/btrfs.c
src/modules/camera/camera.c
src/modules/chassis/chassis.c
src/modules/codec/codec.c
src/modules/colors/colors.c
src/modules/cpu/cpu.c
src/modules/cpucache/cpucache.c
@@ -532,6 +574,7 @@ if(LINUX)
src/detection/brightness/brightness_linux.c
src/detection/btrfs/btrfs_linux.c
src/detection/chassis/chassis_linux.c
src/detection/codec/codec_linux.c
src/detection/cpu/cpu_linux.c
src/detection/cpucache/cpucache_linux.c
src/detection/cpuusage/cpuusage_linux.c
@@ -561,7 +604,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
@@ -619,6 +662,7 @@ elseif(ANDROID)
src/detection/brightness/brightness_nosupport.c
src/detection/btrfs/btrfs_nosupport.c
src/detection/chassis/chassis_nosupport.c
src/detection/codec/codec_android.c
src/detection/cpu/cpu_linux.c
src/detection/cpucache/cpucache_linux.c
src/detection/cursor/cursor_linux.c
@@ -693,6 +737,7 @@ elseif(FreeBSD)
src/detection/brightness/brightness_bsd.c
src/detection/btrfs/btrfs_nosupport.c
src/detection/chassis/chassis_bsd.c
src/detection/codec/codec_linux.c
src/detection/cpu/cpu_bsd.c
src/detection/cpucache/cpucache_shared.c
src/detection/cpuusage/cpuusage_bsd.c
@@ -787,13 +832,14 @@ elseif(NetBSD)
src/detection/brightness/brightness_nbsd.c
src/detection/btrfs/btrfs_nosupport.c
src/detection/chassis/chassis_nbsd.c
src/detection/codec/codec_linux.c
src/detection/cpu/cpu_nbsd.c
src/detection/cpucache/cpucache_shared.c
src/detection/cpuusage/cpuusage_bsd.c
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 +906,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
@@ -871,13 +917,14 @@ elseif(OpenBSD)
src/detection/brightness/brightness_obsd.c
src/detection/btrfs/btrfs_nosupport.c
src/detection/chassis/chassis_windows.c
src/detection/codec/codec_linux.c
src/detection/cpu/cpu_obsd.c
src/detection/cpucache/cpucache_shared.c
src/detection/cpuusage/cpuusage_bsd.c
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
@@ -947,12 +994,14 @@ elseif(APPLE)
src/common/apple/cf_helpers.c
src/common/apple/osascript.m
src/common/apple/smc_temps.c
src/common/apple/version.m
src/detection/battery/battery_apple.c
src/detection/bluetooth/bluetooth_apple.m
src/detection/bluetoothradio/bluetoothradio_apple.m
src/detection/bootmgr/bootmgr_apple.c
src/detection/brightness/brightness_apple.c
src/detection/btrfs/btrfs_nosupport.c
src/detection/codec/codec_apple.c
src/detection/cpu/cpu_apple.c
src/detection/cpucache/cpucache_apple.c
src/detection/cpuusage/cpuusage_apple.c
@@ -1029,22 +1078,21 @@ 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/codec/codec_windows.cpp
src/detection/cpu/cpu_windows.c
src/detection/cpucache/cpucache_windows.c
src/detection/cpuusage/cpuusage_windows.c
@@ -1067,7 +1115,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
@@ -1116,6 +1164,7 @@ elseif(SunOS)
src/detection/cursor/cursor_linux.c
src/detection/bluetooth/bluetooth_nosupport.c
src/detection/bluetoothradio/bluetoothradio_nosupport.c
src/detection/codec/codec_linux.c
src/detection/disk/disk_sunos.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_sunos.c
@@ -1189,8 +1238,8 @@ elseif(Haiku)
src/detection/battery/battery_haiku.c
src/detection/bios/bios_windows.c
src/detection/board/board_windows.c
src/detection/bootmgr/bootmgr_nosupport.c
src/detection/brightness/brightness_nosupport.c
src/detection/bootmgr/bootmgr_haiku.cpp
src/detection/brightness/brightness_haiku.cpp
src/detection/btrfs/btrfs_nosupport.c
src/detection/chassis/chassis_windows.c
src/detection/cpu/cpu_haiku.c
@@ -1199,6 +1248,7 @@ elseif(Haiku)
src/detection/cursor/cursor_nosupport.c
src/detection/bluetooth/bluetooth_haiku.cpp
src/detection/bluetoothradio/bluetoothradio_nosupport.c
src/detection/codec/codec_linux.c
src/detection/disk/disk_haiku.cpp
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_haiku.c
@@ -1238,11 +1288,11 @@ 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
src/detection/wmtheme/wmtheme_nosupport.c
src/detection/wmtheme/wmtheme_haiku.cpp
src/detection/camera/camera_nosupport.c
)
elseif(GNU)
@@ -1268,6 +1318,7 @@ elseif(GNU)
src/detection/cursor/cursor_linux.c
src/detection/bluetooth/bluetooth_linux.c
src/detection/bluetoothradio/bluetoothradio_linux.c
src/detection/codec/codec_linux.c
src/detection/disk/disk_linux.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_nosupport.c
@@ -1334,12 +1385,21 @@ endif()
if(WIN32)
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_intel.c)
list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_amd.c)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "WinRT support is disabled due to known issues with g++")
set(HAVE_WINRT FALSE)
else()
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()
endif()
include(CheckFunctionExists)
check_function_exists(wcwidth HAVE_WCWIDTH)
if(NOT HAVE_WCWIDTH)
list(APPEND LIBFASTFETCH_SRC src/common/impl/wcwidth.c)
endif()
if(NOT WIN32)
check_function_exists(pipe2 HAVE_PIPE2)
endif()
@@ -1347,6 +1407,9 @@ check_function_exists(memrchr HAVE_MEMRCHR)
if(NOT HAVE_MEMRCHR)
list(APPEND LIBFASTFETCH_SRC src/common/impl/memrchr.c)
endif()
if(ENABLE_WCWIDTH)
list(APPEND LIBFASTFETCH_SRC src/common/impl/wcwidth.c)
endif()
if(ENABLE_SYSTEM_YYJSON)
find_package(yyjson)
@@ -1365,6 +1428,15 @@ add_library(libfastfetch OBJECT
${LIBFASTFETCH_SRC}
)
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/${FF_MODULE_DIR}")
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
if(MODULE_DISABLE_${FF_MODULE_UPPER})
target_compile_definitions(libfastfetch PUBLIC FF_MODULE_DISABLE_${FF_MODULE_UPPER}=1)
endif()
endif()
endforeach()
include(CheckCSourceCompiles)
check_c_source_compiles("int main(void){int arr[1];return _Countof(arr);}" COMPILER_SUPPORTS_COUNT_OF)
if(COMPILER_SUPPORTS_COUNT_OF)
@@ -1436,7 +1508,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)
@@ -1498,10 +1570,6 @@ if(FreeBSD OR OpenBSD OR NetBSD)
unset(CMAKE_REQUIRED_DEFINITIONS)
endif()
if(HAVE_WCWIDTH)
target_compile_definitions(libfastfetch PUBLIC FF_HAVE_WCWIDTH)
endif()
if(HAVE_PIPE2)
target_compile_definitions(libfastfetch PUBLIC FF_HAVE_PIPE2)
endif()
@@ -1531,22 +1599,52 @@ if(NOT BINARY_LINK_TYPE STREQUAL "dlopen")
endif()
endif()
function(ff_lib_enable VARNAME PKGCONFIG_NAMES CMAKE_NAME)
function(ff_lib_enable VARNAME PKGCONFIG_NAMES CMAKE_NAME) # [CMAKE_TARGET_NAME]
if(NOT ENABLE_${VARNAME})
return()
endif()
if(PKG_CONFIG_FOUND)
pkg_search_module(${VARNAME} QUIET ${PKGCONFIG_NAMES})
if(${VARNAME}_FOUND)
set(${VARNAME}_FOUND "pkg-config")
endif()
endif()
if(NOT ${VARNAME}_FOUND)
find_package(${CMAKE_NAME} QUIET)
set(${VARNAME}_FOUND ${${CMAKE_NAME}_FOUND})
set(${VARNAME}_INCLUDE_DIRS ${${CMAKE_NAME}_INCLUDE_DIRS})
set(${VARNAME}_LIBRARIES ${${CMAKE_NAME}_LIBRARIES})
set(${VARNAME}_CFLAGS_OTHER ${${CMAKE_NAME}_CFLAGS_OTHER})
if(${VARNAME}_FOUND)
set(${VARNAME}_FOUND "CMake")
# Some CMake packages (e.g. quickjs) are target-based only and
# don't expose *_INCLUDE_DIRS/*_LIBRARIES variables.
if(ARGC GREATER 3)
set(_FF_CMAKE_TARGET ${ARGV3})
if(NOT _FF_CMAKE_TARGET STREQUAL "")
if(NOT DEFINED ${VARNAME}_INCLUDE_DIRS OR "${${VARNAME}_INCLUDE_DIRS}" STREQUAL "")
get_target_property(_FF_CMAKE_TARGET_INCLUDES ${_FF_CMAKE_TARGET} INTERFACE_INCLUDE_DIRECTORIES)
if(_FF_CMAKE_TARGET_INCLUDES)
set(${VARNAME}_INCLUDE_DIRS ${_FF_CMAKE_TARGET_INCLUDES})
endif()
endif()
if(NOT DEFINED ${VARNAME}_LIBRARIES OR "${${VARNAME}_LIBRARIES}" STREQUAL "")
get_target_property(_FF_CMAKE_TARGET_LIBRARIES ${_FF_CMAKE_TARGET} INTERFACE_LINK_LIBRARIES)
if(_FF_CMAKE_TARGET_LIBRARIES)
set(${VARNAME}_LIBRARIES ${_FF_CMAKE_TARGET_LIBRARIES})
else()
set(${VARNAME}_LIBRARIES ${_FF_CMAKE_TARGET})
endif()
endif()
endif()
else()
set(${VARNAME}_INCLUDE_DIRS ${${CMAKE_NAME}_INCLUDE_DIRS})
set(${VARNAME}_LIBRARIES ${${CMAKE_NAME}_LIBRARIES})
set(${VARNAME}_CFLAGS_OTHER ${${CMAKE_NAME}_CFLAGS_OTHER})
endif()
endif()
endif()
if(NOT ${VARNAME}_FOUND)
@@ -1554,7 +1652,7 @@ function(ff_lib_enable VARNAME PKGCONFIG_NAMES CMAKE_NAME)
return()
endif()
message(STATUS "Library: found ${VARNAME}")
message(STATUS "Library: found ${VARNAME} by ${${VARNAME}_FOUND}")
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_${VARNAME}=1)
target_include_directories(libfastfetch PRIVATE ${${VARNAME}_INCLUDE_DIRS})
@@ -1592,6 +1690,14 @@ ff_lib_enable(DRM
"libdrm"
"Libdrm"
)
ff_lib_enable(VA
"libva"
"Libva"
)
ff_lib_enable(VDPAU
"vdpau"
"vdpau"
)
ff_lib_enable(DRM_AMDGPU
"libdrm_amdgpu"
"Libdrm_Amdgpu"
@@ -1604,6 +1710,10 @@ ff_lib_enable(DCONF
"dconf"
"DConf"
)
ff_lib_enable(EET
"eet"
"Eet"
)
ff_lib_enable(DBUS
"dbus-1"
"DBus"
@@ -1667,6 +1777,36 @@ ff_lib_enable(ELF
"libelf"
"libelf"
)
ff_lib_enable(QUICKJS
"qjs"
"qjs"
"qjs"
)
if(ENABLE_LUA)
# Lua is a special case since its CMake config does not follow the usual pattern
find_package(Lua QUIET)
if(Lua_FOUND AND NOT DEFINED Lua_VERSION) # For CMake 4.2- compatible
set(Lua_VERSION ${LUA_VERSION_STRING})
set(Lua_VERSION_MAJOR ${LUA_VERSION_MAJOR})
set(Lua_VERSION_MINOR ${LUA_VERSION_MINOR})
endif()
if(Lua_FOUND)
if((NOT Lua_VERSION_MAJOR EQUAL 5) OR Lua_VERSION_MINOR LESS 3 OR Lua_VERSION_MINOR GREATER 5)
message(STATUS "Library: Lua version ${Lua_VERSION} is not supported, requires 5.3-5.5")
unset(Lua_FOUND)
else()
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LUA=1)
target_include_directories(libfastfetch PRIVATE ${LUA_INCLUDE_DIR})
if(NOT BINARY_LINK_TYPE STREQUAL "dlopen")
target_link_libraries(libfastfetch PRIVATE ${LUA_LIBRARIES})
endif()
message(STATUS "Library: found LUA ${Lua_VERSION} by CMake")
endif()
else()
message(STATUS "Library: missing: LUA")
endif()
endif()
if(ENABLE_THREADS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
@@ -1690,8 +1830,11 @@ if(ENABLE_LIBZFS)
)
endif()
endif()
if(ENABLE_WCWIDTH)
target_compile_definitions(libfastfetch PUBLIC FF_ENABLE_WCWIDTH=1)
endif()
if(NOT HAVE_MEMRCHR)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_CUSTOM_MEMRCHR=1)
target_compile_definitions(libfastfetch PUBLIC FF_HAVE_CUSTOM_MEMRCHR=1)
endif()
if(LINUX)
@@ -1721,6 +1864,7 @@ elseif(APPLE)
PRIVATE "-framework OpenGL"
PRIVATE "-framework OpenCL"
PRIVATE "-framework SystemConfiguration"
PRIVATE "-framework VideoToolbox"
PRIVATE "-weak_framework CoreDisplay"
PRIVATE "-F /System/Library/PrivateFrameworks"
@@ -1747,6 +1891,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 +1916,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 +1982,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 +1991,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 +2022,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 "")
@@ -1907,7 +2040,10 @@ else()
list(TRANSFORM PACKAGES_DISABLE_LIST APPEND "_BIT")
list(JOIN PACKAGES_DISABLE_LIST " | " PACKAGES_DISABLE_LIST)
endif()
target_compile_definitions(libfastfetch PRIVATE FF_PACKAGES_DISABLE_LIST=${PACKAGES_DISABLE_LIST})
target_compile_definitions(libfastfetch PRIVATE "FF_PACKAGES_DISABLE_LIST=${PACKAGES_DISABLE_LIST}")
if(PACKAGES_REMOVE_DISABLED)
target_compile_definitions(libfastfetch PRIVATE FF_PACKAGES_REMOVE_DISABLED=1)
endif()
######################
# Executable targets #
@@ -2011,12 +2147,20 @@ if (BUILD_TESTS)
PRIVATE libfastfetch
)
add_executable(fastfetch-test-strutil
tests/strutil.c
)
target_link_libraries(fastfetch-test-strutil
PRIVATE libfastfetch
)
enable_testing()
add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf)
add_test(NAME test-list COMMAND fastfetch-test-list)
add_test(NAME test-format COMMAND fastfetch-test-format)
add_test(NAME test-color COMMAND fastfetch-test-color)
add_test(NAME test-duration COMMAND fastfetch-test-duration)
add_test(NAME test-strutil COMMAND fastfetch-test-strutil)
endif()
##################
@@ -2037,13 +2181,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"
+30
View File
@@ -1,3 +1,33 @@
fastfetch (2.63.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.63.1
-- Carter Li <zhangsongcui@live.cn> Fri, 15 May 2026 09:31:54 +0800
fastfetch (2.63.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.63.0
-- Carter Li <zhangsongcui@live.cn> Wed, 13 May 2026 16:02:28 +0800
fastfetch (2.62.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.62.1
-- Carter Li <zhangsongcui@live.cn> Fri, 24 Apr 2026 15:45:15 +0800
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
+1 -1
View File
@@ -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, libefl-all-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
+53 -53
View File
@@ -3,7 +3,7 @@
{
"short": "h",
"long": "help",
"desc": "Display this help message or help for a specific command",
"desc": "Show this help message or help for a specific command",
"arg": {
"type": "command",
"optional": true
@@ -12,11 +12,11 @@
{
"short": "v",
"long": "version",
"desc": "Show the full version of fastfetch"
"desc": "Show the full Fastfetch version"
},
{
"long": "version-raw",
"desc": "Display the raw version string (major.minor.patch)"
"desc": "Show the raw version string (major.minor.patch)"
},
{
"long": "list-config-paths",
@@ -36,7 +36,7 @@
},
{
"long": "list-presets",
"desc": "List presets that fastfetch knows about",
"desc": "List available presets",
"remark": "Presets can be loaded with \"--config <preset-name>\""
},
{
@@ -46,15 +46,15 @@
},
{
"long": "print-logos",
"desc": "Display available logos"
"desc": "Show available logos"
},
{
"long": "print-structure",
"desc": "Display the default structure"
"desc": "Show the default structure"
},
{
"long": "format",
"desc": "Set output format",
"desc": "Set the output format",
"arg": {
"type": "enum",
"enum": {
@@ -77,8 +77,8 @@
},
{
"long": "dynamic-interval",
"desc": "Keep fastfetch open and update the output every <num> milliseconds",
"remark": "0 (default) to disable the behavior; don't work with --json",
"desc": "Keep Fastfetch running and refresh the output every <num> milliseconds",
"remark": "Set to 0 (default) to disable this behavior; does not work with --json",
"arg": {
"type": "num",
"default": 0
@@ -90,7 +90,7 @@
"short": "c",
"long": "config",
"desc": "Specify the config file or preset to load",
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"-\" to read config from stdin or \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
"remark": "The file is searched in the order shown by \"fastfetch --list-config-paths\". Use \"-\" to read config from stdin, or \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for details",
"arg": {
"type": "config"
}
@@ -98,7 +98,7 @@
{
"long": "gen-config",
"desc": "Generate a minimal config file at the specified path",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Will print the generated config if <path> is \"-\"",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"arg": {
"type": "path",
"optional": true
@@ -106,8 +106,8 @@
},
{
"long": "gen-config-full",
"desc": "Generate a full config file with all optional options at the specified path",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Will print the generated config if <path> is \"-\"",
"desc": "Generate a full config file with all optional settings at the specified path",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"arg": {
"type": "path",
"optional": true
@@ -152,7 +152,7 @@
},
{
"long": "ds-force-drm",
"desc": "Specify whether only DRM should be used to detect displays",
"desc": "Specify whether display detection should use DRM only",
"remark": [
"Use this option if you encounter problems with other detection methods.",
"Linux only"
@@ -170,7 +170,7 @@
},
{
"long": "detect-version",
"desc": "Specify whether to detect and display versions of terminal, shell, editor, and others",
"desc": "Specify whether to detect and display versions for terminal, shell, editor, and others",
"remark": "Mainly for benchmarking",
"arg": {
"type": "bool",
@@ -183,31 +183,31 @@
{
"short": "l",
"long": "logo",
"desc": "Set the logo source. Use \"none\" to disable the logo",
"remark": "Should be the name of a built-in logo or a path to an image file. See also https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options",
"desc": "Set the logo source. Use \"none\" to disable logo output",
"remark": "Use either a built-in logo name or a path to an image file. See also https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options",
"arg": {
"type": "logo"
}
},
{
"long": "logo-type",
"desc": "Set the type of the logo specified in \"--logo\"",
"desc": "Set the type of logo specified by \"--logo\"",
"remark": "See also https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options",
"arg": {
"type": "enum",
"enum": {
"auto": "If something is given, first try built-in, then file. Otherwise detect logo",
"auto": "If a value is provided, try built-in first, then file. Otherwise auto-detect the logo",
"builtin": "Built-in ASCII art",
"small": "Built-in ASCII art, small version",
"file": "Text file, printed with color code replacement",
"file-raw": "Text file, printed as is",
"data": "Text data, printed with color code replacement",
"data-raw": "Text data, printed as is",
"sixel": "Image file, printed as sixel codes",
"kitty": "Image file, printed using kitty graphics protocol",
"sixel": "Image file, rendered as sixel",
"kitty": "Image file, rendered using the Kitty graphics protocol",
"kitty-direct": "Image file, tells the terminal emulator to read image data from the specified file",
"kitty-icat": "Image file, uses `kitten icat` to display the image. Requires binary `kitten` to be installed",
"iterm": "Image file, printed using iterm graphics protocol",
"kitty-icat": "Image file, displayed using `kitten icat`. Requires the `kitten` binary",
"iterm": "Image file, rendered using the iTerm graphics protocol",
"chafa": "Image file, printed as ASCII art using libchafa",
"raw": "Image file, printed as raw binary string",
"none": "Disable logo printing"
@@ -279,7 +279,7 @@
},
{
"long": "logo-print-remaining",
"desc": "Specify whether to print the remaining logo if it has more lines than modules to display",
"desc": "Specify whether to print remaining logo lines when the logo is taller than the module list",
"arg": {
"type": "bool",
"optional": true,
@@ -288,7 +288,7 @@
},
{
"long": "logo-position",
"desc": "Set the position where the logo should be displayed",
"desc": "Set the display position of the logo",
"arg": {
"type": "enum",
"enum": {
@@ -398,7 +398,7 @@
{
"long": "chafa-fg-only",
"desc": "Produce character-cell output using foreground colors only",
"remark": "See chafa document for detail",
"remark": "See the Chafa documentation for details",
"arg": {
"type": "bool",
"optional": true,
@@ -408,7 +408,7 @@
{
"long": "chafa-symbols",
"desc": "Specify character symbols to employ in final output",
"remark": "See chafa document for detail",
"remark": "See the Chafa documentation for details",
"arg": {
"type": "str"
}
@@ -416,7 +416,7 @@
{
"long": "chafa-canvas-mode",
"desc": "Determine how colors are used in the output",
"remark": "This value maps the int value of enum ChafaCanvasMode. See chafa document for detail",
"remark": "This value maps to the integer value of enum ChafaCanvasMode. See the Chafa documentation for details",
"arg": {
"type": "enum",
"enum": {
@@ -433,8 +433,8 @@
},
{
"long": "chafa-color-space",
"desc": "Set color space used for quantization",
"remark": "This value maps the int value of enum ChafaColorSpace. See chafa document for detail",
"desc": "Set the color space used for quantization",
"remark": "This value maps to the integer value of enum ChafaColorSpace. See the Chafa documentation for details",
"arg": {
"type": "enum",
"enum": {
@@ -445,8 +445,8 @@
},
{
"long": "chafa-dither-mode",
"desc": "Set output dither mode (No effect with 24-bit color)",
"remark": "This value maps the int value of enum ChafaDitherMode. See chafa document for detail",
"desc": "Set the output dithering mode (no effect with 24-bit color)",
"remark": "This value maps to the integer value of enum ChafaDitherMode. See the Chafa documentation for details",
"arg": {
"type": "enum",
"enum": {
@@ -462,7 +462,7 @@
"short": "s",
"long": "structure",
"desc": "Set the structure of the fetch",
"remark": "Must be a colon-separated list of keys. Use \"fastfetch --list-modules\" to see available options",
"remark": "Must be a colon-separated list of module keys. Use \"fastfetch --list-modules\" to see available options",
"arg": {
"type": "structure",
"default": "\"fastfetch --print-structure\""
@@ -478,7 +478,7 @@
},
{
"long": "stat",
"desc": "Show time usage (in ms) for individual modules",
"desc": "Show execution time (ms) for individual modules",
"arg": {
"type": "bool",
"optional": true,
@@ -541,7 +541,7 @@
{
"long": "duration-abbreviation",
"desc": "Specify whether to abbreviate duration values",
"remark": "If true, the output will be in the form of \"1h 2m\" instead of \"1 hour, 2 mins\"",
"remark": "If true, output is shown as \"1h 2m\" instead of \"1 hour, 2 mins\"",
"arg": {
"type": "bool",
"optional": true,
@@ -582,7 +582,7 @@
"enum": {
"none": "Disable keys",
"string": "Show string",
"icon": "Show icon (requires newest nerd font)",
"icon": "Show icon (requires a recent Nerd Font)",
"both": "Show both icon and string (alias of `both-1`)",
"both-0": "Show both icon and string with no spaces between them",
"both-1": "Show both icon and string with a space between them",
@@ -639,7 +639,7 @@
},
{
"long": "percent-type",
"desc": "Set the percentage output type",
"desc": "Set the percentage output style",
"remark": [
"1 for percentage number",
"2 for multi-color bar",
@@ -655,7 +655,7 @@
},
{
"long": "percent-ndigits",
"desc": "Set the number of digits to keep after the decimal point when formatting percentage numbers",
"desc": "Set the number of decimal places to use when formatting percentages",
"arg": {
"type": "num",
"default": 0
@@ -702,7 +702,7 @@
},
{
"long": "percent-width",
"desc": "Specify the width of the percentage number, in number of characters",
"desc": "Specify the width of the percentage number, in characters",
"remark": "This option affects only percentage numbers, not bars",
"arg": {
"type": "num",
@@ -711,7 +711,7 @@
},
{
"long": "bar-char-elapsed",
"desc": "Set the character to use in the elapsed part of percentage bars",
"desc": "Set the character used for the elapsed part of percentage bars",
"arg": {
"type": "str",
"default": "\u25a0"
@@ -719,7 +719,7 @@
},
{
"long": "bar-char-total",
"desc": "Set the character to use in the total part of percentage bars",
"desc": "Set the character used for the total part of percentage bars",
"arg": {
"type": "str",
"default": "-"
@@ -743,7 +743,7 @@
},
{
"long": "bar-border-left-elapsed",
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border will be used as parts of bar content",
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border is used as part of the bar content",
"arg": {
"type": "string",
"default": ""
@@ -751,7 +751,7 @@
},
{
"long": "bar-border-right-elapsed",
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border will be used as parts of bar content",
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border is used as part of the bar content",
"arg": {
"type": "string",
"default": ""
@@ -759,8 +759,8 @@
},
{
"long": "bar-color-elapsed",
"desc": "Set the color to use in the elapsed part of percentage bars",
"remark": "By default, auto selected by percent-color-{green,yellow,red}",
"desc": "Set the color used for the elapsed part of percentage bars",
"remark": "By default, this is auto-selected by percent-color-{green,yellow,red}",
"arg": {
"type": "color",
"default": "<auto>"
@@ -768,7 +768,7 @@
},
{
"long": "bar-color-total",
"desc": "Set the color to use in the total part of percentage bars",
"desc": "Set the color used for the total part of percentage bars",
"arg": {
"type": "color",
"default": "light_white"
@@ -776,7 +776,7 @@
},
{
"long": "bar-color-border",
"desc": "Set the color to use in the borders of percentage bars",
"desc": "Set the color used for percentage bar borders",
"arg": {
"type": "color",
"default": "light_white"
@@ -792,7 +792,7 @@
},
{
"long": "no-buffer",
"desc": "Specify whether the stdout application buffer should be disabled",
"desc": "Specify whether stdout buffering should be disabled",
"arg": {
"type": "bool",
"optional": true,
@@ -801,7 +801,7 @@
},
{
"long": "size-ndigits",
"desc": "Set the number of digits to keep after the decimal point when formatting sizes",
"desc": "Set the number of decimal places to use when formatting sizes",
"arg": {
"type": "num"
}
@@ -852,7 +852,7 @@
},
{
"long": "freq-ndigits",
"desc": "Set the number of digits to keep after the decimal point when printing CPU/GPU frequency in GHz",
"desc": "Set the number of decimal places to use when printing CPU/GPU frequency in GHz",
"arg": {
"type": "num",
"default": 2
@@ -872,7 +872,7 @@
},
{
"long": "fraction-ndigits",
"desc": "Set the number of digits to keep after the decimal point when printing ordinary fraction numbers",
"desc": "Set the number of decimal places to use when printing fractional values",
"remark": "If negative, the number of digits will be automatically determined based on the value",
"arg": {
"type": "num",
@@ -885,7 +885,7 @@
"arg": {
"type": "enum",
"enum": {
"default": "Use the behavior defined internally",
"default": "Use the built-in behavior",
"always": "Always keep trailing zeros",
"never": "Never keep trailing zeros"
}
@@ -907,7 +907,7 @@
},
{
"long": "temp-ndigits",
"desc": "Set the number of digits to keep after the decimal point when printing temperature",
"desc": "Set the number of decimal places to use when printing temperatures",
"arg": {
"type": "num",
"default": 2
+394 -292
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -50,6 +50,10 @@
"driverSpecific": true,
"temp": true
},
{
"type": "codec",
"splitGPU": true
},
"memory",
"physicalmemory",
{
+4
View File
@@ -52,6 +52,10 @@
"driverSpecific": true,
"temp": true
},
{
"type": "codec",
"splitGPU": true
},
"memory",
"physicalmemory",
{
-3
View File
@@ -1,6 +1,3 @@
// Load with --config examples/2.jsonc
// Note that you must replace the image path to an existing image to display it.
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
"logo": {
-3
View File
@@ -1,6 +1,3 @@
// Load with --config examples/2.jsonc
// Note that you must replace the image path to an existing image to display it.
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
"display": {
-3
View File
@@ -1,6 +1,3 @@
// Load with --config examples/2.jsonc
// Note that you must replace the image path to an existing image to display it.
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
"logo": {
+1 -1
View File
@@ -33,7 +33,7 @@ endBold = r"\fR" # end bold text tag
# path to the current directory
pathToCurrentDir = path.dirname(__file__)
# path to the JSON option file
pathToHelpFile = path.join(pathToCurrentDir, "../src/data/help.json")
pathToHelpFile = path.join(pathToCurrentDir, "../doc/help.json")
# man page section
manSection = 1
# title (center header)
+141 -88
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"
}
+5 -7
View File
@@ -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;
-37
View File
@@ -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
View File
@@ -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))
+152 -202
View File
@@ -1,6 +1,6 @@
#pragma once
#include "FFcheckmacros.h"
#include "common/attributes.h"
#include <stdint.h>
#include <stdarg.h>
@@ -9,6 +9,7 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "common/memrchr.h"
#ifdef FF_USE_SYSTEM_YYJSON
#include <yyjson.h>
@@ -17,8 +18,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 +27,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 +47,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 +61,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,11 +72,6 @@ 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_C_NODISCARD uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace);
// Returns true if the strbuf is modified
@@ -87,7 +82,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 +91,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 +121,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 +145,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 +207,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 +255,330 @@ 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)
{
FF_A_NODISCARD static inline uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
assert(start <= strbuf->length);
const char* ptr = (const char*) memchr(strbuf->chars + start, c, strbuf->length - start);
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
}
FF_A_NODISCARD static inline uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str) {
assert(start <= strbuf->length);
const char* ptr = strstr(strbuf->chars + start, str);
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
}
FF_A_NODISCARD static inline uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
assert(start <= strbuf->length);
const char* ptr = (const char*) memrchr(strbuf->chars, c, start + 1);
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
}
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
View File
@@ -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);
}
+14 -11
View File
@@ -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)
-2
View File
@@ -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)
{
+269 -253
View File
@@ -1,9 +1,8 @@
#include "smc_temps.h"
#include "common/apple/cf_helpers.h"
#include "common/stringUtils.h"
#include "common/strutil.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 -4
View File
@@ -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,
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "fastfetch.h"
bool ffGetAppNameAndVersion(const char* exePath, FFstrbuf* retName, FFstrbuf* retVersion);
+43
View File
@@ -0,0 +1,43 @@
#include "common/apple/version.h"
#include "common/strutil.h"
#import <Foundation/Foundation.h>
#define APP_CONTENTS_MACOS ".app/Contents/MacOS"
bool ffGetAppNameAndVersion(const char* exePath, FFstrbuf* retName, FFstrbuf* retVersion) {
char* lastSlash = strrchr(exePath, '/');
if (!lastSlash) {
return false;
}
if ((size_t) (lastSlash - exePath) > strlen("X" APP_CONTENTS_MACOS) && memcmp(lastSlash - strlen(APP_CONTENTS_MACOS), APP_CONTENTS_MACOS, strlen(APP_CONTENTS_MACOS)) != 0) {
return false;
}
lastSlash -= strlen("MacOS");
char infoPlistPath[PATH_MAX];
memcpy(infoPlistPath, exePath, lastSlash - exePath);
memcpy(infoPlistPath + (lastSlash - exePath), "Info.plist", sizeof("Info.plist")); // X.app/Contents/Info.plist
NSError* error;
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@(infoPlistPath)]
error:&error];
if (!dict) {
return false;
}
if (retName) {
NSString* bundleName = dict[@"CFBundleDisplayName"] ?: dict[@"CFBundleName"];
if (bundleName) {
ffStrbufSetS(retName, bundleName.UTF8String);
}
}
if (retVersion) {
NSString* bundleVersion = dict[@"CFBundleShortVersionString"];
if (bundleVersion) {
ffStrbufSetS(retVersion, bundleVersion.UTF8String);
}
}
return true;
}
+18 -19
View File
@@ -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) }
+19
View File
@@ -0,0 +1,19 @@
#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__))
#define FF_A_ALWAYS_INLINE __attribute__((__always_inline__))
+4 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+2 -3
View File
@@ -2,14 +2,13 @@
#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
} FFformatarg;
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg);
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments);
bool ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments);
#define FF_PARSE_FORMAT_STRING_CHECKED(buffer, formatstr, arguments) \
ffParseFormatString((buffer), (formatstr), sizeof(arguments) / sizeof(*arguments), (arguments));
+8 -6
View File
@@ -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;
}
+28 -24
View File
@@ -1,14 +1,13 @@
#include "FFPlatform_private.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#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);
}
}
+154 -175
View File
@@ -1,7 +1,7 @@
#include "FFPlatform_private.h"
#include "common/FFstrbuf.h"
#include "common/arrayUtils.h"
#include "common/stringUtils.h"
#include "common/arrutil.h"
#include "common/strutil.h"
#include "common/io.h"
#include "fastfetch_config.h"
@@ -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);
+123 -137
View File
@@ -1,7 +1,7 @@
#include "FFPlatform_private.h"
#include "common/io.h"
#include "common/library.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "common/windows/unicode.h"
#include "common/windows/registry.h"
#include "common/windows/nt.h"
@@ -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
View File
@@ -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;
}
+291 -347
View File
File diff suppressed because it is too large Load Diff
+45 -47
View File
@@ -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()
{
static 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';
+179 -120
View File
@@ -1,11 +1,10 @@
#include "common/binary.h"
#include "common/io.h"
#include "common/stringUtils.h"
#include "common/mallocHelper.h"
#include "common/strutil.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, &section, 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, &section, 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);
}
}
+43 -30
View File
@@ -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/strutil.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";
}
+25 -21
View File
@@ -1,6 +1,6 @@
#include "common/binary.h"
#include "common/io.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "common/windows/nt.h"
#include <windows.h>
@@ -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;
}
+112 -108
View File
@@ -3,23 +3,24 @@
#include "common/printing.h"
#include "common/time.h"
#include "common/jsonconfig.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "fastfetch_datatext.h"
#include "modules/modules.h"
#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,198 @@ 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]) {
#if !FF_MODULE_DISABLE_CPUUSAGE
case 'C':
case 'c':
FF_IF_MODULE_MATCH(FF_CPUUSAGE_MODULE_NAME)
ffPrepareCPUUsage();
ffPrepareCPUUsage();
break;
#endif
case 'D': case 'd':
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
#if !FF_MODULE_DISABLE_DISKIO
case 'D':
case 'd':
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyDiskIOOptions) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
ffPrepareDiskIO(&options);
}
break;
#endif
case 'N': case 'n':
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
#if !FF_MODULE_DISABLE_NETIO
case 'N':
case 'n':
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyNetIOOptions) FFNetIOOptions options;
ffInitNetIOOptions(&options);
ffPrepareNetIO(&options);
}
break;
#endif
case 'P': case 'p':
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
#if !FF_MODULE_DISABLE_PUBLICIP
case 'P':
case 'p':
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyPublicIpOptions) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
ffPreparePublicIp(&options);
}
break;
#endif
case 'W': case 'w':
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
#if !FF_MODULE_DISABLE_WEATHER
case 'W':
case 'w':
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyWeatherOptions) FFWeatherOptions options;
ffInitWeatherOptions(&options);
ffPrepareWeather(&options);
}
break;
#endif
}
#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 (data->resultDoc) {
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);
printf("\e7\e[1A\e[9999999C\e[%dD%s\e8", len, str); // Save; Up 1; Right 9999999; Left <len>; Print <str>; Load
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 - 1, str); // Save; Up 1; Right 9999999; Left <len - 1>; 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
View File
@@ -2,11 +2,10 @@
#ifdef FF_HAVE_DBUS
#include "common/thread.h"
#include "common/stringUtils.h"
#include "common/thread.h"
#include "common/strutil.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
+22 -17
View File
@@ -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
View File
@@ -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");
}
}
}
+57 -68
View File
@@ -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;
}
+168 -175
View File
@@ -1,99 +1,104 @@
#include "fastfetch.h"
#include "common/FFlist.h"
#include "common/FFstrbuf.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "common/font.h"
#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);
}
+498 -157
View File
@@ -2,65 +2,63 @@
#include "common/format.h"
#include "common/parsing.h"
#include "common/textModifier.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "common/library.h"
#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 +71,387 @@ 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)
{
FF_A_UNUSED static inline void normalizeArgName(FFstrbuf* dst, const char* src) {
ffStrbufClear(dst);
bool flag = false;
for (const char* p = src; *p; ++p) {
if (*p == '-') {
flag = true;
} else if (flag) {
ffStrbufAppendC(dst, (char) toupper((unsigned char) *p));
flag = false;
} else {
ffStrbufAppendC(dst, *p);
}
}
}
#if FF_HAVE_LUA
#include "common/lua.h"
static void appendLuaError(FFstrbuf* buffer, const char* prefix, lua_State* L) {
const char* err = lua_tolstring(L, -1, NULL);
if (err) {
const char* tmp = strchr(err, ':');
if (tmp) {
err = tmp + 1;
while (*err == ' ') {
++err;
}
}
}
ffStrbufAppendF(buffer, "%s: %s", prefix, err ? err : "unknown");
}
static bool parseLuaString(FFstrbuf* buffer, const char* script, uint32_t scriptLen, uint32_t numArgs, const FFformatarg* arguments) {
const char* err = ffLuaLoadState();
if (err) {
ffStrbufAppendF(buffer, "Lua init error: %s", err);
return false;
}
FF_STRBUF_AUTO_DESTROY argNameBuf = ffStrbufCreate();
bool ret = false;
lua_State* L = luaData.L;
// Clear stack and load chunk
lua_settop(L, 0);
if (luaL_loadbuffer(L, script, scriptLen, "") != LUA_OK) {
appendLuaError(buffer, "Lua load error", L);
} else {
// Build args table for name lookup only.
lua_createtable(L, 0, 0);
for (uint32_t i = 0; i < numArgs; ++i) {
const FFformatarg* arg = &arguments[i];
switch (arg->type) {
case FF_ARG_TYPE_INT:
lua_pushinteger(L, (lua_Integer) * (int32_t*) arg->value);
break;
case FF_ARG_TYPE_UINT:
lua_pushinteger(L, (lua_Integer) * (uint32_t*) arg->value);
break;
case FF_ARG_TYPE_UINT64:
lua_pushinteger(L, (lua_Integer) * (uint64_t*) arg->value);
break;
case FF_ARG_TYPE_UINT16:
lua_pushinteger(L, (lua_Integer) * (uint16_t*) arg->value);
break;
case FF_ARG_TYPE_UINT8:
lua_pushinteger(L, (lua_Integer) * (uint8_t*) arg->value);
break;
case FF_ARG_TYPE_FLOAT:
lua_pushnumber(L, (lua_Number) * (float*) arg->value);
break;
case FF_ARG_TYPE_DOUBLE:
lua_pushnumber(L, (lua_Number) * (double*) arg->value);
break;
case FF_ARG_TYPE_BOOL:
lua_pushboolean(L, *(bool*) arg->value);
break;
case FF_ARG_TYPE_STRING:
lua_pushlstring(L, (const char*) arg->value, strlen((const char*) arg->value));
break;
case FF_ARG_TYPE_STRBUF: {
const FFstrbuf* sb = (const FFstrbuf*) arg->value;
lua_pushlstring(L, sb->chars, sb->length);
break;
}
case FF_ARG_TYPE_LIST: {
const FFlist* list = (const FFlist*) arg->value;
lua_createtable(L, 0, 0);
for (uint32_t li = 0; li < list->length; ++li) {
const FFstrbuf* item = FF_LIST_GET(FFstrbuf, *list, li);
lua_pushlstring(L, item->chars, item->length);
lua_seti(L, -2, (lua_Integer) (li + 1));
}
break;
}
default:
lua_pushnil(L);
break;
}
if (arg->name && arg->name[0]) {
normalizeArgName(&argNameBuf, arg->name);
} else {
ffStrbufSetF(&argNameBuf, "arg%" PRIu32, i + 1);
}
lua_setfield(L, -2, argNameBuf.chars);
}
if (lua_pcall(L, 1, LUA_MULTRET, 0) != LUA_OK) {
appendLuaError(buffer, "Lua runtime error", L);
} else {
int nresults = lua_gettop(L);
if (nresults == 0) {
ffStrbufAppendS(buffer, "Lua result error: no result");
} else {
// Convert first result to string
const char* res = lua_tolstring(L, 1, NULL);
if (res) {
ffStrbufAppendS(buffer, res);
} else {
luaL_tolstring(L, 1, NULL);
const char* sval = lua_tolstring(L, -1, NULL);
if (sval) {
ffStrbufAppendS(buffer, sval);
}
}
ret = true;
}
}
}
lua_settop(L, 0);
return ret;
}
#endif
#if FF_HAVE_QUICKJS
#include <quickjs.h>
struct FFQuickJSData {
FF_LIBRARY_SYMBOL(JS_NewRuntime)
FF_LIBRARY_SYMBOL(JS_NewContext)
FF_LIBRARY_SYMBOL(JS_FreeRuntime)
FF_LIBRARY_SYMBOL(JS_EvalThis)
FF_LIBRARY_SYMBOL(JS_GetException)
FF_LIBRARY_SYMBOL(JS_ToCStringLen2)
FF_LIBRARY_SYMBOL(JS_FreeCString)
FF_LIBRARY_SYMBOL(JS_NewStringLen)
FF_LIBRARY_SYMBOL(JS_NewArray)
FF_LIBRARY_SYMBOL(JS_NewBigUint64)
FF_LIBRARY_SYMBOL(JS_SetPropertyUint32)
FF_LIBRARY_SYMBOL(JS_NewObject)
FF_LIBRARY_SYMBOL(JS_SetPropertyStr)
FF_LIBRARY_SYMBOL(JS_FreeValue)
JSRuntime* rt;
JSContext* ctx;
bool inited;
} qjsData;
static const char* loadQuickJSState(void) {
if (qjsData.inited) {
if (qjsData.ctx == NULL) {
return "QuickJS is not available";
}
return NULL;
}
qjsData.inited = true;
#ifdef _WIN32
FF_LIBRARY_LOAD_MESSAGE(libqjs, "libqjs-0" FF_LIBRARY_EXTENSION, 0)
#else
FF_LIBRARY_LOAD_MESSAGE(libqjs, "libqjs" FF_LIBRARY_EXTENSION, 0)
#endif
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_NewRuntime)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_NewContext)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_FreeRuntime)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_EvalThis)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_GetException)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_ToCStringLen2)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_FreeCString)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_NewStringLen)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_NewArray)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_SetPropertyUint32)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_NewObject)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_SetPropertyStr)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_FreeValue)
qjsData.rt = qjsData.ffJS_NewRuntime();
if (qjsData.rt == NULL) {
return "JS_NewRuntime() failed";
}
qjsData.ctx = qjsData.ffJS_NewContext(qjsData.rt);
if (qjsData.ctx == NULL) {
qjsData.ffJS_FreeRuntime(qjsData.rt);
qjsData.rt = NULL;
return "JS_NewContext() failed";
}
libqjs = NULL; // don't close quickjs
return NULL;
}
static bool parseQuickJSString(FFstrbuf* buffer, const char* script, uint32_t scriptLen, uint32_t numArgs, const FFformatarg* arguments) {
const char* err = loadQuickJSState();
if (err) {
ffStrbufAppendF(buffer, "Qjs init error: %s", err);
return false;
}
JSContext* ctx = qjsData.ctx;
JSValue argsObj = qjsData.ffJS_NewObject(ctx);
FF_STRBUF_AUTO_DESTROY argNameBuf = ffStrbufCreate();
for (uint32_t i = 0; i < numArgs; ++i) {
const FFformatarg* arg = &arguments[i];
JSValue value;
switch (arg->type) {
case FF_ARG_TYPE_INT:
value = JS_NewInt32(ctx, *(int32_t*) arg->value);
break;
case FF_ARG_TYPE_UINT:
value = JS_NewUint32(ctx, *(uint32_t*) arg->value);
break;
case FF_ARG_TYPE_UINT64: {
uint64_t val = *(uint64_t*) arg->value;
if (val <= INT32_MAX) {
value = JS_NewInt32(ctx, (int32_t) val);
} else {
value = JS_NewFloat64(ctx, (double) val);
}
break;
}
case FF_ARG_TYPE_UINT16:
value = JS_NewUint32(ctx, *(uint16_t*) arg->value);
break;
case FF_ARG_TYPE_UINT8:
value = JS_NewUint32(ctx, *(uint8_t*) arg->value);
break;
case FF_ARG_TYPE_FLOAT:
value = JS_NewFloat64(ctx, *(float*) arg->value);
break;
case FF_ARG_TYPE_DOUBLE:
value = JS_NewFloat64(ctx, *(double*) arg->value);
break;
case FF_ARG_TYPE_BOOL:
value = JS_NewBool(ctx, *(bool*) arg->value);
break;
case FF_ARG_TYPE_STRING:
value = qjsData.ffJS_NewStringLen(ctx, (const char*) arg->value, strlen((const char*) arg->value));
break;
case FF_ARG_TYPE_STRBUF: {
const FFstrbuf* sb = (const FFstrbuf*) arg->value;
value = qjsData.ffJS_NewStringLen(ctx, sb->chars, sb->length);
break;
}
case FF_ARG_TYPE_LIST: {
const FFlist* list = (const FFlist*) arg->value;
JSValue arr = qjsData.ffJS_NewArray(ctx);
for (uint32_t li = 0; li < list->length; ++li) {
const FFstrbuf* item = FF_LIST_GET(FFstrbuf, *list, li);
JSValue itemValue = qjsData.ffJS_NewStringLen(ctx, item->chars, item->length);
qjsData.ffJS_SetPropertyUint32(ctx, arr, li, itemValue);
}
value = arr;
break;
}
default:
value = JS_UNDEFINED;
break;
}
if (arg->name && arg->name[0]) {
normalizeArgName(&argNameBuf, arg->name);
} else {
ffStrbufSetF(&argNameBuf, "arg%" PRIu32, i + 1);
}
qjsData.ffJS_SetPropertyStr(ctx, argsObj, argNameBuf.chars, value);
}
JSValue result = qjsData.ffJS_EvalThis(ctx, argsObj, script, scriptLen, "", JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRICT);
qjsData.ffJS_FreeValue(ctx, argsObj);
bool ret = false;
if (JS_IsException(result)) {
JSValue exc = qjsData.ffJS_GetException(ctx);
const char* message = qjsData.ffJS_ToCStringLen2(ctx, NULL, exc, false);
qjsData.ffJS_FreeValue(ctx, exc);
ffStrbufAppendF(buffer, "Qjs runtime error: %s", message ?: "unknown");
if (message) {
qjsData.ffJS_FreeCString(ctx, message);
}
} else if (JS_IsUndefined(result)) {
ffStrbufAppendS(buffer, "Qjs result error: undefined result");
} else {
size_t len;
const char* res = qjsData.ffJS_ToCStringLen2(ctx, &len, result, false);
if (res) {
ffStrbufAppendNS(buffer, (uint32_t) len, res);
qjsData.ffJS_FreeCString(ctx, res);
}
ret = true;
}
qjsData.ffJS_FreeValue(ctx, result);
return ret;
}
#endif
static bool skipAnsiEscape(FFstrbuf* in, FFstrbuf* out, FFstrbuf* trailingEscape) {
if (__builtin_expect(in->chars[0] == '\e' && in->chars[1] == '[', false)) {
// skip ANSI escape codes at the start of the string for truncation
const char* p = in->chars + 2;
while (*p && (ffCharIsDigit(*p) || *p == ';')) {
++p;
}
if (*p && isascii(*p)) {
++p;
}
uint32_t prefixLen = (uint32_t) (p - in->chars);
ffStrbufAppendNS(out, prefixLen, in->chars);
ffStrbufSubstrAfter(in, prefixLen - 1);
if (trailingEscape) {
// likely have a `CSI m` reset at the end of the string
uint32_t iLastEscape = ffStrbufLastIndexC(in, '\e');
if (iLastEscape != in->length) {
ffStrbufSetNS(trailingEscape, in->length - iLastEscape, in->chars + iLastEscape);
ffStrbufSubstrBefore(in, iLastEscape);
}
}
return true;
}
return false;
}
static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
uint32_t argCounter = 0;
uint32_t numOpenIfs = 0;
@@ -136,11 +459,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;
}
@@ -148,9 +469,14 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
// jump to next char, the start of the placeholder value
++i;
// unmatched trailing '{'
if (i >= formatstr->length) {
ffStrbufAppendC(buffer, '{');
break;
}
// double {{ elvaluates to a single { and doesn't count as start
if(formatstr->chars[i] == '{')
{
if (formatstr->chars[i] == '{') {
ffStrbufAppendC(buffer, '{');
continue;
}
@@ -165,61 +491,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 +553,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 +575,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 +585,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,122 +613,149 @@ 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 != '~') {
++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]);
FF_STRBUF_AUTO_DESTROY trailingEscape = ffStrbufCreate();
skipAnsiEscape(&tempString, buffer, &trailingEscape);
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 (trailingEscape.length > 0) {
ffStrbufAppend(buffer, &trailingEscape);
}
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)
FF_STRBUF_AUTO_DESTROY trailingEscape = ffStrbufCreate();
skipAnsiEscape(&tempString, buffer, &trailingEscape);
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 == '<') { // left align
ffStrbufAppend(buffer, &tempString);
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
}
else
{
} else if (cSep == '>') { // right align
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
ffStrbufAppend(buffer, &tempString);
} else if (cSep == '|') { // center align
uint32_t padding = ((uint32_t) truncLength - tempString.length) / 2;
ffStrbufAppendNC(buffer, padding, ' ');
ffStrbufAppend(buffer, &tempString);
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length - padding, ' ');
}
}
if (trailingEscape.length > 0) {
ffStrbufAppend(buffer, &trailingEscape);
}
}
}
if (!instance.config.display.pipe)
if (!instance.config.display.pipe) {
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
}
return true;
}
bool ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
#if FF_HAVE_QUICKJS
if (ffStrbufStartsWithS(formatstr, "qjs:")) {
return parseQuickJSString(buffer, formatstr->chars + 4, formatstr->length - 4, numArgs, arguments);
}
#endif
#if FF_HAVE_LUA
if (ffStrbufStartsWithS(formatstr, "lua:")) {
// If outputFormat starts with "lua:", treat the rest as a Lua script
return parseLuaString(buffer, formatstr->chars + 4, formatstr->length - 4, numArgs, arguments);
}
#endif
return parseFormatString(buffer, formatstr, numArgs, arguments);
}
+11 -10
View File
@@ -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;
+183 -152
View File
@@ -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;
@@ -30,97 +29,105 @@ static void initState(FFstate* state)
ffPlatformInit(&state->platform);
state->dynamicInterval = 0;
#if !FF_MODULE_DISABLE_TERMINALTHEME
{
// 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;
}
}
#endif
}
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 +135,166 @@ 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)
{
#if FF_HAVE_LUA
#include <lua.h>
#endif
#if FF_HAVE_QUICKJS
#include <quickjs.h>
#define FF_STR_INDIR(x) #x
#define FF_STR(x) FF_STR_INDIR(x)
#endif
// 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_HAVE_VA
"va\n"
#endif
#if FF_HAVE_VDPAU
"vdpau\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_ENABLE_WCWIDTH
"Embedded wcwidth\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
#if FF_HAVE_LUA
LUA_VERSION "\n"
#endif
#if FF_HAVE_QUICKJS
"QuickJS " FF_STR(QJS_VERSION_MAJOR) "." FF_STR(QJS_VERSION_MINOR) "." FF_STR(QJS_VERSION_PATCH) QJS_VERSION_SUFFIX "\n"
#endif
"",
stdout);
}
+108 -117
View File
@@ -1,6 +1,6 @@
#include "common/io.h"
#include "fastfetch.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "common/time.h"
#include <fcntl.h>
@@ -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;
}
+146 -151
View File
@@ -1,20 +1,18 @@
#include "fastfetch.h"
#include "common/io.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "common/windows/nt.h"
#include "common/windows/unicode.h"
#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;
}
+171 -157
View File
@@ -4,7 +4,7 @@
#include "common/printing.h"
#include "common/io.h"
#include "common/time.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "detection/version/version.h"
#include "modules/modules.h"
@@ -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,203 +131,235 @@ 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]) {
#if !FF_MODULE_DISABLE_CPUUSAGE
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;
#endif
#if !FF_MODULE_DISABLE_DISKIO
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;
#endif
#if !FF_MODULE_DISABLE_NETIO
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;
#endif
#if !FF_MODULE_DISABLE_PUBLICIP
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;
#endif
#if !FF_MODULE_DISABLE_WEATHER
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;
}
#endif
}
}
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);
printf("\e7\e[1A\e[9999999C\e[%dD%s\e8", len, str); // Save; Up 1; Right 9999999; Left <len>; Print <str>; Load
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 - 1, str); // Save; Up 1; Right 9999999; Left <len - 1>; 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);
}
}
}
+1 -2
View File
@@ -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);
+1 -2
View File
@@ -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;
}
+8 -6
View File
@@ -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';
+12 -15
View File
@@ -1,42 +1,39 @@
#include "common/kmod.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#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 -2
View File
@@ -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
}
+5 -6
View File
@@ -1,23 +1,22 @@
#include "common/kmod.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#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);
+9 -8
View File
@@ -1,26 +1,27 @@
#include "common/kmod.h"
#include "common/windows/nt.h"
#include "common/mallocHelper.h"
#include "common/stringUtils.h"
#include "common/strutil.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;
+66 -65
View File
@@ -1,47 +1,48 @@
#include "fastfetch.h"
#include "common/library.h"
#include "common/debug.h"
#if _WIN32
#include "common/debug.h"
#include "common/windows/nt.h"
#include <errno.h>
#include <ntstatus.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,28 @@ static void* libraryLoad(const char* path, int maxVersion)
#else
if(result != NULL || maxVersion < 0)
if (result == NULL) {
FF_DEBUG("dlopen(\"%s\"): %s", path, dlerror());
}
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;
} else {
FF_DEBUG("dlopen(\"%s\"): %s", pathbuf.chars, dlerror());
}
ffStrbufSubstrBefore(&pathbuf, originalLength);
}
@@ -73,20 +81,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 +108,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 +134,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;
}
+232
View File
@@ -0,0 +1,232 @@
#if FF_HAVE_LUA
#include "common/lua.h"
#include "common/mallocHelper.h"
struct FFLuaData luaData;
static yyjson_mut_val* lua2yyjson(lua_State* L, int idx, yyjson_mut_doc* doc, int depth) {
if (__builtin_expect(depth > 15, false)) {
yyjson_mut_doc_free(doc);
lua_pushlstring(
L, "yyjson: recursion depth exceeded; possible circular reference",
strlen("yyjson: recursion depth exceeded; possible circular reference")
);
lua_error(L); // noreturn
__builtin_unreachable();
}
if (idx < 0) {
idx = lua_gettop(L) + idx + 1;
}
int type = lua_type(L, idx);
switch (type) {
case LUA_TNIL:
return yyjson_mut_null(doc);
case LUA_TBOOLEAN:
return yyjson_mut_bool(doc, lua_toboolean(L, idx));
case LUA_TNUMBER: {
if (lua_isinteger(L, idx)) {
lua_Integer i = lua_tointeger(L, idx);
return yyjson_mut_sint(doc, (int64_t) i);
} else {
return yyjson_mut_real(doc, lua_tonumber(L, idx));
}
}
case LUA_TSTRING: {
size_t len;
const char* str = lua_tolstring(L, idx, &len);
return yyjson_mut_strncpy(doc, str, len);
}
case LUA_TTABLE: {
lua_Unsigned len = lua_rawlen(L, idx);
int is_array = 1;
if (len == 0) {
lua_pushnil(L);
is_array = 0;
if (lua_next(L, idx) != 0) {
lua_pop(L, 2);
}
} else {
lua_pushnil(L);
while (lua_next(L, idx) != 0) {
if (lua_type(L, -2) != LUA_TNUMBER) {
is_array = 0;
lua_pop(L, 2);
break;
}
int isnum = false;
lua_Integer k = lua_tointegerx(L, -2, &isnum);
if (!isnum || (lua_Unsigned) k > len) {
is_array = 0;
lua_pop(L, 2);
break;
}
lua_pop(L, 1);
}
}
if (is_array) {
yyjson_mut_val* arr = yyjson_mut_arr(doc);
for (lua_Unsigned i = 1; i <= len; i++) {
lua_rawgeti(L, idx, (lua_Integer) i);
yyjson_mut_val* val = lua2yyjson(L, -1, doc, depth + 1);
yyjson_mut_arr_append(arr, val);
lua_pop(L, 1);
}
return arr;
} else {
yyjson_mut_val* obj = yyjson_mut_obj(doc);
lua_pushnil(L);
while (lua_next(L, idx) != 0) {
size_t klen;
const char* key_str = luaL_tolstring(L, -2, &klen);
yyjson_mut_val* key = yyjson_mut_strncpy(doc, key_str, klen);
lua_pop(L, 1);
yyjson_mut_val* val = lua2yyjson(L, -1, doc, depth + 1);
yyjson_mut_obj_add(obj, key, val);
lua_pop(L, 1);
}
return obj;
}
}
default:
return yyjson_mut_null(doc);
}
}
static int yyjsonEncode(lua_State* L) {
luaL_checkany(L, 1);
bool pretty = false;
if (lua_isboolean(L, 2)) {
pretty = (int) lua_toboolean(L, 2);
}
yyjson_mut_doc* doc = yyjson_mut_doc_new(NULL);
if (__builtin_expect(!doc, false)) {
lua_pushlstring(L, "yyjson: yyjson_mut_doc_new() failed", strlen("yyjson: yyjson_mut_doc_new() failed"));
return lua_error(L);
}
yyjson_mut_val* root = lua2yyjson(L, 1, doc, 0);
yyjson_mut_doc_set_root(doc, root);
size_t jsonLen;
yyjson_write_err err = {};
FF_AUTO_FREE const char* jsonStr = yyjson_mut_write_opts(doc, YYJSON_WRITE_ALLOW_INF_AND_NAN | (pretty ? YYJSON_WRITE_PRETTY_TWO_SPACES : 0), NULL, &jsonLen, &err);
if (__builtin_expect(jsonStr != NULL, true)) {
lua_pushlstring(L, jsonStr, jsonLen);
yyjson_mut_doc_free(doc);
return 1;
} else {
yyjson_mut_doc_free(doc);
{
FF_STRBUF_AUTO_DESTROY errBuf = ffStrbufCreateStatic("yyjson: yyjson_mut_write_opts() failed: ");
ffStrbufAppendS(&errBuf, err.msg);
lua_pushlstring(L, errBuf.chars, errBuf.length);
}
return lua_error(L); // longjmp
}
}
const char* ffLuaLoadState() {
if (luaData.inited) {
if (luaData.L == NULL) {
return "Lua library is not available";
}
return NULL;
}
luaData.inited = true;
// clang-format off
#ifdef _WIN32
#define FF_LOAD_LIBLUA(version) FF_LIBRARY_LOAD_MESSAGE(liblua, \
"lua5" #version FF_LIBRARY_EXTENSION, 0)
#else
#define FF_LOAD_LIBLUA(version) FF_LIBRARY_LOAD_MESSAGE(liblua, \
"liblua5." #version FF_LIBRARY_EXTENSION, 0, \
"liblua-5." #version FF_LIBRARY_EXTENSION, 0, \
"liblua5." #version FF_LIBRARY_EXTENSION ".5." #version, 0)
#endif
// clang-format on
#if LUA_VERSION_NUM == 505
FF_LOAD_LIBLUA(5)
#elif LUA_VERSION_NUM == 504
FF_LOAD_LIBLUA(4)
#elif LUA_VERSION_NUM == 503
FF_LOAD_LIBLUA(3)
#else
#error "Unsupported Lua version"
#endif
#undef FF_LOAD_LIBLUA
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaL_newstate)
#if LUA_VERSION_NUM >= 505
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaL_openselectedlibs)
#else
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaL_requiref)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_base)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_math)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_string)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_table)
#endif
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_checkany)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_loadbufferx)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_tolstring)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_callk)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_createtable)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_error)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_gettop)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_isinteger)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_next)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pcallk)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushboolean)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushcclosure)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushinteger)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushlstring)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushnil)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushnumber)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushvalue)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_rawgeti)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_rawlen)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_setfield)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_setglobal)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_seti)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_settop)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_toboolean)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tointegerx)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tolstring)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tonumberx)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_type)
lua_State* L = ffluaL_newstate();
if (L == NULL) {
return "luaL_newstate() failed";
}
#if LUA_VERSION_NUM >= 505
ffluaL_openselectedlibs(L, LUA_GLIBK | LUA_MATHLIBK | LUA_STRLIBK | LUA_TABLIBK, 0);
#else
ffluaL_requiref(L, LUA_GNAME, ffluaopen_base, 1);
ffluaL_requiref(L, LUA_MATHLIBNAME, ffluaopen_math, 1);
ffluaL_requiref(L, LUA_STRLIBNAME, ffluaopen_string, 1);
ffluaL_requiref(L, LUA_TABLIBNAME, ffluaopen_table, 1);
lua_settop(L, 0);
#endif
lua_pushcfunction(L, yyjsonEncode);
lua_setglobal(L, "json_encode");
luaData.L = L;
liblua = NULL; // don't close lua
return NULL;
}
#endif
+8 -6
View File
@@ -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;
+2 -4
View File
@@ -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;
+89 -71
View File
@@ -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
View File
@@ -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;
+10 -9
View File
@@ -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;
+42 -29
View File
@@ -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;
}
+89 -90
View File
@@ -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;
+18 -29
View File
@@ -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;
+42 -44
View File
@@ -1,14 +1,13 @@
#include "fastfetch.h"
#include "common/library.h"
#include "common/networking.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#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;
+177 -188
View File
@@ -2,7 +2,7 @@
#include "common/networking.h"
#include "common/time.h"
#include "common/library.h"
#include "common/stringUtils.h"
#include "common/strutil.h"
#include "common/mallocHelper.h"
#include "common/debug.h"
@@ -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;
}
+87 -112
View File
@@ -1,31 +1,30 @@
#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"
#include "common/strutil.h"
#include "common/debug.h"
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;
}

Some files were not shown because too many files have changed in this diff Show More