diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 715ebbcdc..c6832bdcf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,24 +13,29 @@ jobs: fail-fast: false matrix: os: [ubuntu-22.04, ubuntu-20.04] - compiler: [gcc, clang, "musl-gcc"] + compiler: [ + { cc: gcc, cxx: "g++" }, + { cc: clang, cxx: "clang++" }, + { cc: "musl-gcc", cxx: "musl-g++" } + ] enableFeatures: [ON, OFF] exclude: # The feature libraries are all build against glibc, so they can't be used with musl - - compiler: "musl-gcc" + - compiler: { cc: "musl-gcc", cxx: "musl-g++" } enableFeatures: ON runs-on: ${{ matrix.os }} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install required packages - run: sudo apt-get update && sudo apt-get install -y musl-dev musl-tools linux-headers-generic libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev + run: sudo apt-get update && sudo apt-get install -y musl-dev musl-tools linux-headers-generic libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev directx-headers-dev - name: configure project env: - CC: ${{ matrix.compiler }} - run: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_LIBPCI=${{ matrix.enableFeatures }} -DENABLE_VULKAN=${{ matrix.enableFeatures }} -DENABLE_WAYLAND=${{ matrix.enableFeatures }} -DENABLE_XCB_RANDR=${{ matrix.enableFeatures }} -DENABLE_XCB=${{ matrix.enableFeatures }} -DENABLE_XRANDR=${{ matrix.enableFeatures }} -DENABLE_X11=${{ matrix.enableFeatures }} -DENABLE_GIO=${{ matrix.enableFeatures }} -DENABLE_DCONF=${{ matrix.enableFeatures }} -DENABLE_DBUS=${{ matrix.enableFeatures }} -DENABLE_XFCONF=${{ matrix.enableFeatures }} -DENABLE_SQLITE3=${{ matrix.enableFeatures }} -DENABLE_RPM=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK7=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK6=${{ matrix.enableFeatures }} -DENABLE_CHAFA=${{ matrix.enableFeatures }} -DENABLE_ZLIB=${{ matrix.enableFeatures }} -DENABLE_EGL=${{ matrix.enableFeatures }} -DENABLE_GLX=${{ matrix.enableFeatures }} -DENABLE_OSMESA=${{ matrix.enableFeatures }} -DENABLE_OPENCL=${{ matrix.enableFeatures }} -DENABLE_LIBNM=${{ matrix.enableFeatures }} -DENABLE_PULSE=${{ matrix.enableFeatures }} -DENABLE_JSONC=${{ matrix.enableFeatures }} . + CC: ${{ matrix.compiler.cc }} + CXX: ${{ matrix.compiler.cxx }} + run: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_LIBPCI=${{ matrix.enableFeatures }} -DENABLE_VULKAN=${{ matrix.enableFeatures }} -DENABLE_WAYLAND=${{ matrix.enableFeatures }} -DENABLE_XCB_RANDR=${{ matrix.enableFeatures }} -DENABLE_XCB=${{ matrix.enableFeatures }} -DENABLE_XRANDR=${{ matrix.enableFeatures }} -DENABLE_X11=${{ matrix.enableFeatures }} -DENABLE_GIO=${{ matrix.enableFeatures }} -DENABLE_DCONF=${{ matrix.enableFeatures }} -DENABLE_DBUS=${{ matrix.enableFeatures }} -DENABLE_XFCONF=${{ matrix.enableFeatures }} -DENABLE_SQLITE3=${{ matrix.enableFeatures }} -DENABLE_RPM=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK7=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK6=${{ matrix.enableFeatures }} -DENABLE_CHAFA=${{ matrix.enableFeatures }} -DENABLE_ZLIB=${{ matrix.enableFeatures }} -DENABLE_EGL=${{ matrix.enableFeatures }} -DENABLE_GLX=${{ matrix.enableFeatures }} -DENABLE_OSMESA=${{ matrix.enableFeatures }} -DENABLE_OPENCL=${{ matrix.enableFeatures }} -DENABLE_LIBNM=${{ matrix.enableFeatures }} -DENABLE_PULSE=${{ matrix.enableFeatures }} -DENABLE_JSONC=${{ matrix.enableFeatures }} -DENABLE_DIRECTX_HEADERS=${{ matrix.enableFeatures }} . - name: build project run: cmake --build . @@ -39,7 +44,7 @@ jobs: run: ctest - name: run fastfetch - run: ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: ./fastfetch --stat --show-errors --no-buffer --load-config presets/all - name: run flashfetch run: ./flashfetch @@ -54,10 +59,10 @@ jobs: ffversion: ${{ steps.ffversion.outputs.ffversion }} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev + run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev directx-headers-dev - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -77,11 +82,14 @@ jobs: run: ./fastfetch --list-features - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: ldd fastfetch + - name: run tests run: ctest @@ -103,7 +111,7 @@ jobs: contents: read steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install required packages run: brew install vulkan-loader molten-vk imagemagick chafa @@ -126,11 +134,14 @@ jobs: run: ./fastfetch --list-features - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: otool -L fastfetch + - name: run tests run: ctest @@ -148,7 +159,7 @@ jobs: contents: read steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: run VM uses: vmactions/freebsd-vm@v0 @@ -159,8 +170,9 @@ jobs: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On . cmake --build . --target package ./fastfetch --list-features - time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + time ./fastfetch -c presets/ci time ./flashfetch + ldd fastfetch ctest - name: upload artifacts @@ -180,7 +192,7 @@ jobs: shell: msys2 {0} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: setup-msys2 uses: msys2/setup-msys2@v2 @@ -213,11 +225,14 @@ jobs: run: ./fastfetch --list-features - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: ldd fastfetch + - name: run tests run: ctest @@ -244,7 +259,7 @@ jobs: shell: msys2 {0} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: setup-msys2 uses: msys2/setup-msys2@v2 @@ -274,11 +289,14 @@ jobs: run: cp /clang32/bin/{OpenCL,vulkan-1}.dll . - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: ldd fastfetch + - name: run tests run: ctest diff --git a/CHANGELOG.md b/CHANGELOG.md index 9defef35e..feeaeb472 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,57 @@ +# 2.1.0 + +This release introduces a new output format: JSON result + +Changes: +* Users module detects and prints user login time by default. Specifying `--users-compact` to disable it +* Fastfetch now requires yyjson 0.8.0 or later, which is embeded in fastfetch source tree. If you build fastfetch with `-DENABLE_SYSTEM_YYJSON` cmake option, you must upgrade your yyjson package +* Reduced information supported by `--terminal-format`, `--shell-format` +* Some config presets (`devinfo` and `verbose`) are obseleted and removed. They are barely maintained and can be replaced with `--format json` now. +* Custom strings in `--module-key` and `--module-format` are no longer trimmed. +* `/boot` is hidden by default (FreeBSD, Disk) + +Features: +* Add `--format json`, which prints system information as JSON format +* Add fast path for xfce4 version detection (DE, FreeBSD) +* Support contour terminal version and font detection (Terminal / TerminalFont) +* Support `kitty-direct` / `iterm` without specifying logo width / height. Note: in this case, the entre screen will be cleared. +* Support new flag `--logo-separate`. If true, print modules at bottom of the logo +* Support Apple Silicon CPU frequency detection (CPU, macOS) +* Support user login time detection (Users) +* Support winget package manager detection, guarded behind `--allow-slow-operations` (Packages, Windows) +* Print monitor type (built-in or external) (Display) +* Support full GPU detection in WSL (Linux, GPU) +* Add `--module-key " "` as a special case for hiding keys +* Support `--title-format`. See `fastfetch --help title-format` for detail +* Support `--colors-key` (Colors) +* Add `-c` as a shortcut of `--load-config`. Note it was used as the shortcut of `--color` before 2.0.5 +* Support Windows Service Pack version detection (Kernel, Windows) +* Support Debian point releases detection (OS, Linux) +* Add new module `NetIO` which prints network throughput (usage) of specified interface. Note this module costs about 1 second to finish. +* Use `lscpu` to detect CPU name for ARM CPUs (CPU, Linux) + +Bugfixes: +* Fix fastfetch hanging in specific environment (#561) +* Fix short read when reading from stdin (Logo) +* Fix `poll() timeout or failed` error when image is very large (Logo) +* Fix Termux Monet terminal version detection (Terminal) +* Fix zpool volumes detection (Disk, Linux) +* Fix external volumes detection (Disk, Linux) +* Fix snap package number detection on systems other than Ubuntu (Packages, Linux) +* Fix dpkg / apt package number detection (Packages, Linux) +* Fix bluetooth mac address detection (Bluetooth, Windows) + +Logo: +* Add Afterglow +* Add Elbrus +* Update EvolutionOS +* Update AOSC OS +* Update Ubuntu_old +* Update Windows 11_small +* Add Amazon Linux +* Add LainOS +* Fix colors of Slackware + # 2.0.5 Bugfixes: @@ -11,7 +65,7 @@ Features: # 2.0.4 Bugfixes: -* Fix building on 32-bit FreeBSD (Memory, BSD) +* Fix building on 32-bit FreeBSD (Memory, FreeBSD) * Fix `--file-raw` doesn't work (Logo) Features: diff --git a/CMakeLists.txt b/CMakeLists.txt index a799086ba..04eebf679 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url project(fastfetch - VERSION 2.0.5 + VERSION 2.1.0 LANGUAGES C DESCRIPTION "Fast system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -67,6 +67,7 @@ cmake_dependent_option(ENABLE_LIBNM "Enable libnm" ON "LINUX" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF) +cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF) cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF) cmake_dependent_option(ENABLE_PCI_MEMORY "Enable detecting GPU memory size with libpci" OFF "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embeded) yyjson library" OFF "LINUX OR APPLE OR BSD OR WIN32 OR ANDROID" OFF) @@ -98,10 +99,13 @@ set(WARNING_FLAGS "-Wall -Wextra -Wconversion -Werror=uninitialized -Werror=retu set(CMAKE_C_STANDARD 11) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=incompatible-pointer-types -Werror=implicit-function-declaration") -if(WIN32) +if(WIN32 OR ENABLE_DIRECTX_HEADERS) enable_language(CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -fno-exceptions -fno-rtti") +endif() + +if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--tsaware -Wl,--build-id -Wl,--subsystem,console:6.1,--major-os-version,6,--minor-os-version,1") endif() @@ -112,8 +116,8 @@ if(APPLE AND DEFINED ENV{HOMEBREW_PREFIX}) endif() set(FASTFETCH_FLAGS_DEBUG "-fno-omit-frame-pointer") -if(NOT WIN32) - set(FASTFETCH_FLAGS_DEBUG "${FASTFETCH_FLAGS_DEBUG} -fsanitize=address -fsanitize=undefined") +if(NOT WIN32 AND NOT APPLE) + set(FASTFETCH_FLAGS_DEBUG "${FASTFETCH_FLAGS_DEBUG} -fsanitize=address") endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG}") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG}") @@ -121,8 +125,8 @@ if(NOT WIN32) set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -rdynamic") endif() - if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "Enabling LTO") include(CheckIPOSupported) check_ipo_supported(RESULT IPO_SUPPORTED) if(IPO_SUPPORTED) @@ -259,6 +263,7 @@ set(LIBFASTFETCH_SRC src/common/jsonconfig.c src/common/library.c src/common/modules.c + src/common/netif/netif.c src/common/option.c src/common/parsing.c src/common/printing.c @@ -267,20 +272,22 @@ set(LIBFASTFETCH_SRC src/detection/chassis/chassis.c src/detection/cpu/cpu.c src/detection/cpuusage/cpuusage.c - src/detection/datetime/datetime.c src/detection/disk/disk.c src/detection/displayserver/displayserver.c src/detection/font/font.c src/detection/gpu/gpu.c src/detection/locale/locale.c src/detection/media/media.c + src/detection/netio/netio.c src/detection/opencl/opencl.c src/detection/os/os.c src/detection/packages/packages.c + src/detection/publicip/publicip.c src/detection/terminalfont/terminalfont.c src/detection/terminalshell/terminalshell.c src/detection/version/version.c src/detection/vulkan/vulkan.c + src/detection/weather/weather.c src/logo/builtin.c src/logo/image/im6.c src/logo/image/im7.c @@ -314,6 +321,7 @@ set(LIBFASTFETCH_SRC src/modules/localip/localip.c src/modules/memory/memory.c src/modules/monitor/monitor.c + src/modules/netio/netio.c src/modules/opencl/opencl.c src/modules/opengl/opengl.c src/modules/os/os.c @@ -354,6 +362,7 @@ if(LINUX) list(APPEND LIBFASTFETCH_SRC src/common/dbus.c src/common/io/io_unix.c + src/common/netif/netif_linux.c src/common/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_linux.c @@ -382,6 +391,7 @@ if(LINUX) src/detection/media/media_linux.c src/detection/memory/memory_linux.c src/detection/monitor/monitor_linux.c + src/detection/netio/netio_linux.c src/detection/opengl/opengl_linux.c src/detection/os/os_linux.c src/detection/packages/packages_linux.c @@ -405,6 +415,7 @@ if(LINUX) elseif(ANDROID) list(APPEND LIBFASTFETCH_SRC src/common/io/io_unix.c + src/common/netif/netif_linux.c src/common/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_android.c @@ -428,6 +439,7 @@ elseif(ANDROID) src/detection/media/media_nosupport.c src/detection/memory/memory_linux.c src/detection/monitor/monitor_nosupport.c + src/detection/netio/netio_linux.c src/detection/opengl/opengl_linux.c src/detection/os/os_android.c src/detection/packages/packages_linux.c @@ -451,6 +463,7 @@ elseif(BSD) list(APPEND LIBFASTFETCH_SRC src/common/dbus.c src/common/io/io_unix.c + src/common/netif/netif_bsd.c src/common/networking_linux.c src/common/processing_linux.c src/common/sysctl.c @@ -480,6 +493,7 @@ elseif(BSD) src/detection/media/media_linux.c src/detection/memory/memory_bsd.c src/detection/monitor/monitor_nosupport.c + src/detection/netio/netio_bsd.c src/detection/opengl/opengl_linux.c src/detection/os/os_linux.c src/detection/packages/packages_linux.c @@ -503,6 +517,7 @@ elseif(BSD) elseif(APPLE) list(APPEND LIBFASTFETCH_SRC src/common/io/io_unix.c + src/common/netif/netif_bsd.c src/common/networking_linux.c src/common/processing_linux.c src/common/sysctl.c @@ -515,7 +530,6 @@ elseif(APPLE) src/detection/cpu/cpu_apple.c src/detection/cpuusage/cpuusage_apple.c src/detection/cursor/cursor_apple.m - src/detection/disk/disk_apple.m src/detection/disk/disk_bsd.c src/detection/displayserver/displayserver_apple.c src/detection/font/font_apple.m @@ -528,6 +542,7 @@ elseif(APPLE) src/detection/media/media_apple.m src/detection/memory/memory_apple.c src/detection/monitor/monitor_apple.m + src/detection/netio/netio_bsd.c src/detection/opengl/opengl_apple.c src/detection/os/os_apple.m src/detection/packages/packages_apple.c @@ -552,6 +567,7 @@ elseif(APPLE) elseif(WIN32) list(APPEND LIBFASTFETCH_SRC src/common/io/io_windows.c + src/common/netif/netif_windows.c src/common/networking_windows.c src/common/processing_windows.c src/detection/battery/battery_windows.c @@ -575,6 +591,7 @@ elseif(WIN32) src/detection/media/media_nosupport.c src/detection/memory/memory_windows.c src/detection/monitor/monitor_windows.c + src/detection/netio/netio_windows.c src/detection/opengl/opengl_windows.c src/detection/os/os_windows.cpp src/detection/packages/packages_windows.c @@ -601,6 +618,11 @@ elseif(WIN32) ) endif() +if(ENABLE_DIRECTX_HEADERS) + message(STATUS "Enabling DirectX headers for WSL") + list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_wsl.cpp) +endif() + include(CheckFunctionExists) check_function_exists(wcwidth HAVE_WCWIDTH) if(NOT HAVE_WCWIDTH) @@ -623,14 +645,17 @@ endif() add_library(libfastfetch OBJECT ${LIBFASTFETCH_SRC} ) + if(yyjson_FOUND) target_compile_definitions(libfastfetch PRIVATE FF_USE_SYSTEM_YYJSON) target_link_libraries(libfastfetch PRIVATE yyjson) endif() -target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE) +target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE __STDC_WANT_LIB_EXT1__=1) if(WIN32) target_compile_definitions(libfastfetch PUBLIC WIN32_LEAN_AND_MEAN=1) +elseif(APPLE) + target_compile_definitions(libfastfetch PUBLIC _DARWIN_C_SOURCE) endif() if(HAVE_WCWIDTH) @@ -773,6 +798,10 @@ ff_lib_enable(DDCUTIL "ddcutil" "Ddcutil" ) +ff_lib_enable(DIRECTX_HEADERS + "DirectX-Headers" + "DirectX-Headers" +) if(ENABLE_THREADS) target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS) @@ -877,6 +906,12 @@ target_link_libraries(flashfetch PRIVATE libfastfetch ) +# Prevent fastfetch from linking to libstdc++ +set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") +set_target_properties(fastfetch PROPERTIES LINKER_LANGUAGE C) +set_target_properties(flashfetch PROPERTIES LINKER_LANGUAGE C) + if(WIN32) set(TARGET_NAME fastfetch) target_sources(fastfetch diff --git a/README.md b/README.md index 4f1a1ebc3..3148465d5 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ The following libraries are used if present at runtime: * [`libnm`](https://networkmanager.dev/docs/libnm/latest/): Used for Wifi detection. * [`libpulse`](https://freedesktop.org/software/pulseaudio/doxygen/): Used for Sound detection. * [`libddcutil`](https://github.com/rockowitz/ddcutil): Used for brightness detection of external displays +* [`DirectX-Headers`](https://github.com/microsoft/DirectX-Headers): Used for GPU detection in WSL ### macOS @@ -91,7 +92,7 @@ For the image logo, iTerm with iterm image protocol should work. Apple Terminal Note: In Windows 7, 8 and 8.1, [ConEmu](https://conemu.github.io/en/AnsiEscapeCodes.html) is required to run fastfetch due to [the lack of ASCII escape code native support](https://en.wikipedia.org/wiki/ANSI_escape_code#DOS,_OS/2,_and_Windows). In addition, as fastfetch for Windows targets [UCRT](https://learn.microsoft.com/en-us/cpp/windows/universal-crt-deployment) C runtime library, [it must be installed manually](https://support.microsoft.com/en-us/topic/update-for-universal-c-runtime-in-windows-c0514201-7fe6-95a3-b0a5-287930f3560c) as UCRT is only pre-installed in Windows 10 and later. -For the image logo, only chafa is supported due to [a design flaw of ConPTY](https://github.com/microsoft/terminal/issues/1173). In addition, chafa support is not included by default due to the massive dependencies of imagemagick. You must built it yourself. +For the image logo, WezTerm with iterm image protocol is known to work, surprisingly. ### Android @@ -100,19 +101,21 @@ For the image logo, only chafa is supported due to [a design flaw of ConPTY](htt * [`termux-api`](https://github.com/termux/termux-api-package): Used for Wifi / Battery detection. Read [the official doc](https://wiki.termux.com/wiki/Termux:API) for detail or if you hang on these modules (IMPORTANT). [`termux-api`](https://github.com/termux/termux-packages/tree/master/packages/termux-api) * [`libandroid-wordexp-static`](https://github.com/termux/termux-packages/tree/master/packages/libandroid-wordexp): `wordexp.h` support for Android. +For the image logo, [Termux Monet](https://github.com/HardcodedCat/termux-monet) supports iterm image protocol. + ## Support status All categories not listed here should work without needing a specific implementation. ##### Available Modules ``` -Battery, Bios, Bluetooth, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, Gamepad, GPU, Host, Icons, Kernel, LM, Locale, LocalIP, Media, Memory, Monitor, OpenCL, OpenGL, OS, Packages, Player, Power Adapter, Processes, PublicIP, Separator, Shell, Sound, Swap, Terminal, Terminal Font, Terminal Size, Theme, Time, Title, Uptime, Version, Vulkan, Wallpaper, Weather, Wifi, WM, WMTheme +Battery, Bios, Bluetooth, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, Gamepad, GPU, Host, Icons, Kernel, LM, Locale, LocalIP, Media, Memory, Monitor, NetIO, OpenCL, OpenGL, OS, Packages, Player, Power Adapter, Processes, PublicIP, Separator, Shell, Sound, Swap, Terminal, Terminal Font, Terminal Size, Theme, Time, Title, Uptime, Version, Vulkan, Wallpaper, Weather, Wifi, WM, WMTheme ``` ##### Builtin logos ``` -AerOS, AIX, AlmaLinux, Alpine, Alpine2Small, AlpineSmall, Alter, Amazon, AmogOS, Anarchy, Android, AndroidSmall, Antergos, Antix, AoscOS, AoscOsRetro, AoscOsRetro_small, Aperture, Apple, AppleSmall, Apricity, Arch, Arch2, ArchBox, Archcraft, Archcraft2, Archlabs, ArchSmall, ArchStrike, ArcoLinux, ArcoLinuxSmall, ArseLinux, Artix, Artix2Small, ArtixSmall, Arya, Asahi, Aster, AsteroidOS, AstOS, Astra, Ataraxia, Athena, Bedrock, BigLinux, Bitrig, BlackArch, BlackPanther, BLAG, BlankOn, BlueLight, Bodhi, Bonsai, BSD, BunsenLabs, CachyOS, CachyOSSmall, Calculate, CalinixOS, CalinixOSSmall, Carbs, CBL-Mariner, CelOS, Center, CentOS, CentOSSmall, Chakra, ChaletOS, Chapeau, ChonkySealOS, Chrom, Cleanjaro, CleanjaroSmall, ClearLinux, ClearOS, Clover, Cobalt, Condres, ContainerLinux, CRUX, CRUXSmall, CrystalLinux, Cucumber, CutefishOS, CuteOS, CyberOS, Dahlia, DarkOS, Debian, DebianSmall, Deepin, DesaOS, Devuan, DevuanSmall, DietPi, DracOS, DragonFly, DragonFlyOld, DragonFlySmall, Drauger, Droidian, Elementary, ElementarySmall, Elive, EncryptOS, Endeavour, Endless, Enso, EuroLinux, EvolutionOS, Exherbo, ExodiaPredator, Fedora, FedoraOld, FedoraSmall, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeBSDSmall, FreeMiNT, Frugalware, Funtoo, GalliumOS, Garuda, GarudaDragon, GarudaSmall, Gentoo, GentooSmall, GhostBSD, Glaucus, GNewSense, Gnome, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, GuixSmall, Haiku, HaikuSmall, HamoniKR, HarDClanZ, HardenedBSD, Hash, Huayra, Hybrid, HydroOS, Hyperbola, HyperbolaSmall, Iglunix, InstantOS, IRIX, Itc, Januslinux, Kaisen, Kali, KaliSmall, KaOS, KDENeon, Kibojoe, KISSLinux, Kogaion, Korora, KrassOS, KSLinux, Kubuntu, LangitKetujuh, Laxeros, LEDE, LibreELEC, Linspire, Linux, LinuxLight, LinuxLightSmall, LinuxMint, LinuxMintOld, LinuxMintSmall, LinuxSmall, Live_Raizo, LMDE, Lunar, MacOS, MacOS2, MacOS2Small, MacOSSmall, Mageia, MageiaSmall, MagpieOS, Mandriva, Manjaro, ManjaroSmall, MassOS, MatuusOS, MaUI, Meowix, Mer, Minix, Mint, MintOld, MintSmall, MiracleLinux, MOS, Msys2, MX, MXSmall, Namib, Nekos, Neptune, NetBSD, NetRunner, Nitrux, NixOS, NixOS_small, NixOsOld, NixOsSmall, Nobara, NomadBSD, Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, OpenBSD, OpenBSDSmall, OpenEuler, OpenIndiana, OpenKylin, OpenMamba, OpenMandriva, OpenStage, OpenSuse, OpenSuseLeap, OpenSuseSmall, OpenSuseTumbleweed, OpenWrt, OPNsense, Oracle, Orchid, OrchidSmall, OS_Elbrus, OSMC, OSX, OSXSmall, PacBSD, Panwah, Parabola, ParabolaSmall, Parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS, PearOS, Pengwin, Pentoo, Peppermint, PhyOS, Pisi, PNMLinux, Pop, PopSmall, Porteus, PostMarketOS, PostMarketOSSmall, Proxmox, PuffOS, Puppy, PureOS, PureOSSmall, Q4OS, Qubes, Qubyt, Quibian, Radix, Raspbian, RaspbianSmall, RavynOS, Reborn, RebornSmall, RedCore, RedHatEnterpriseLinux, RedHatEnterpriseLinux_old, RedstarOS, Refracted Devuan, Regata, Regolith, RhaymOS, RockyLinux, RockyLinuxSmall, RosaLinux, Sabayon, Sabotage, Sailfish, SalentOS, SalientOS, Salix, SambaBOX, Sasanqua, Scientific, Semc, Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel, Slackware, SlackwareSmall, Slitaz, SmartOS, Soda, Solaris, SolarisSmall, Solus, SourceMage, Sparky, Star, SteamOS, StockLinux, Sulin, Suse, SuseSmall, Swagarch, T2, Tails, TeArch, TorizonCore, Trisquel, TuxedoOS, Twister, Ubuntu, Ubuntu2Old, Ubuntu2Small, UbuntuBudgie, UbuntuCinnamon, UbuntuGnome, UbuntuKde, UbuntuKylin, UbuntuMate, UbuntuOld, UbuntuSmall, UbuntuStudio, UbuntuSway, UbuntuTouch, UbuntuUnity, Ultramarine, Univalent, Univention, UOS, UrukOS, Uwuntu, Vanilla, Venom, Vnux, Void, VoidSmall, Vzlinux, WiiLinuxNgx, Windows, Windows11, Windows11Small, Windows8, Windows95, Xferience, YiffOS, Zorin +AerOS, Afterglow, AIX, AlmaLinux, Alpine, Alpine2Small, AlpineSmall, Alter, Amazon, AmazonLinux, AmogOS, Anarchy, Android, AndroidSmall, Antergos, Antix, AoscOS, AoscOS_old, AoscOsRetro, AoscOsRetro_small, Aperture, Apple, AppleSmall, Apricity, Arch, Arch2, ArchBox, Archcraft, Archcraft2, Archlabs, ArchSmall, ArchStrike, ArcoLinux, ArcoLinuxSmall, ArseLinux, Artix, Artix2Small, ArtixSmall, Arya, Asahi, Aster, AsteroidOS, AstOS, Astra, Ataraxia, Athena, Bedrock, BigLinux, Bitrig, BlackArch, BlackPanther, BLAG, BlankOn, BlueLight, Bodhi, Bonsai, BSD, BunsenLabs, CachyOS, CachyOSSmall, Calculate, CalinixOS, CalinixOSSmall, Carbs, CBL-Mariner, CelOS, Center, CentOS, CentOSSmall, Chakra, ChaletOS, Chapeau, ChonkySealOS, Chrom, Cleanjaro, CleanjaroSmall, ClearLinux, ClearOS, Clover, Cobalt, Condres, ContainerLinux, CRUX, CRUXSmall, CrystalLinux, Cucumber, CutefishOS, CuteOS, CyberOS, Dahlia, DarkOS, Debian, DebianSmall, Deepin, DesaOS, Devuan, DevuanSmall, DietPi, DracOS, DragonFly, DragonFlyOld, DragonFlySmall, Drauger, Droidian, Elbrus, Elementary, ElementarySmall, Elive, EncryptOS, Endeavour, Endless, Enso, EuroLinux, EvolutionOS, EvolutionOS_old, EvolutionOSSmall, Exherbo, ExodiaPredator, Fedora, FedoraOld, FedoraSmall, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeBSDSmall, FreeMiNT, Frugalware, Funtoo, GalliumOS, Garuda, GarudaDragon, GarudaSmall, Gentoo, GentooSmall, GhostBSD, Glaucus, GNewSense, Gnome, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, GuixSmall, Haiku, HaikuSmall, HamoniKR, HarDClanZ, HardenedBSD, Hash, Huayra, Hybrid, HydroOS, Hyperbola, HyperbolaSmall, Iglunix, InstantOS, IRIX, Itc, Januslinux, Kaisen, Kali, KaliSmall, KaOS, KDENeon, Kibojoe, KISSLinux, Kogaion, Korora, KrassOS, KSLinux, Kubuntu, LangitKetujuh, Laxeros, LEDE, LibreELEC, Linspire, Linux, LinuxLight, LinuxLightSmall, LinuxMint, LinuxMintOld, LinuxMintSmall, LinuxSmall, Live_Raizo, LMDE, Lunar, MacOS, MacOS2, MacOS2Small, MacOSSmall, Mageia, MageiaSmall, MagpieOS, Mandriva, Manjaro, ManjaroSmall, MassOS, MatuusOS, MaUI, Meowix, Mer, Minix, Mint, MintOld, MintSmall, MiracleLinux, MOS, Msys2, MX, MXSmall, Namib, Nekos, Neptune, NetBSD, NetRunner, Nitrux, NixOS, NixOS_small, NixOsOld, NixOsSmall, Nobara, NomadBSD, Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, OpenBSD, OpenBSDSmall, OpenEuler, OpenIndiana, OpenKylin, OpenMamba, OpenMandriva, OpenStage, OpenSuse, OpenSuseLeap, OpenSuseSmall, OpenSuseTumbleweed, OpenWrt, OPNsense, Oracle, Orchid, OrchidSmall, OS_Elbrus, OSMC, OSX, OSXSmall, PacBSD, Panwah, Parabola, ParabolaSmall, Parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS, PearOS, Pengwin, Pentoo, Peppermint, PhyOS, Pisi, PNMLinux, Pop, PopSmall, Porteus, PostMarketOS, PostMarketOSSmall, Proxmox, PuffOS, Puppy, PureOS, PureOSSmall, Q4OS, Qubes, Qubyt, Quibian, Radix, Raspbian, RaspbianSmall, RavynOS, Reborn, RebornSmall, RedCore, RedHatEnterpriseLinux, RedHatEnterpriseLinux_old, RedstarOS, Refracted Devuan, Regata, Regolith, RhaymOS, RockyLinux, RockyLinuxSmall, RosaLinux, Sabayon, Sabotage, Sailfish, SalentOS, SalientOS, Salix, SambaBOX, Sasanqua, Scientific, Semc, Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel, Slackware, SlackwareSmall, Slitaz, SmartOS, Soda, Solaris, SolarisSmall, Solus, SourceMage, Sparky, Star, SteamOS, StockLinux, Sulin, Suse, SuseSmall, Swagarch, T2, Tails, TeArch, TorizonCore, Trisquel, TuxedoOS, Twister, Ubuntu, Ubuntu2Old, Ubuntu2Small, UbuntuBudgie, UbuntuCinnamon, UbuntuGnome, UbuntuKde, UbuntuKylin, UbuntuMate, UbuntuOld, UbuntuSmall, UbuntuStudio, UbuntuSway, UbuntuTouch, UbuntuUnity, Ultramarine, Univalent, Univention, UOS, UrukOS, Uwuntu, Vanilla, Venom, VenomSmall, Vnux, Void, VoidSmall, Vzlinux, WiiLinuxNgx, Windows, Windows11, Windows11Small, Windows8, Windows95, Xferience, YiffOS, Zorin ``` Run `fastfetch --print-logos` to print them diff --git a/completions/bash b/completions/bash index a8db1c600..9a59d7116 100644 --- a/completions/bash +++ b/completions/bash @@ -24,7 +24,7 @@ __fastfetch_complete_help() "terminal-format" "terminalfont-format" "cpu-format" - "cpu-usage-format" + "cpuusage-format" "gpu-format" "memory-format" "swap-format" diff --git a/doc/json_schema.json b/doc/json_schema.json index 60c3c5259..d7e681566 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -166,6 +166,11 @@ "title": "If true, regenerate image logo cache", "default": false }, + "separate": { + "type": "boolean", + "title": "If true, print modules at bottom of the logo", + "default": false + }, "chafa": { "type": "object", "title": "Chafa configuration. See chafa document for details", @@ -269,6 +274,11 @@ "type": "integer", "title": "Set the timeout (ms) for WMI queries, `-1` for no timeout. Windows only", "default": 5000 + }, + "processingTimeout": { + "type": "integer", + "title": "Set the timeout (ms) when waiting for child processes, `-1` for no timeout", + "default": 1000 } }, "additionalProperties": false @@ -548,6 +558,7 @@ "media", "memory", "monitor", + "netio", "opencl", "opengl", "os", @@ -989,7 +1000,37 @@ "title": "Show IPs with given name prefix only", "type": "string" }, - "showDefaultRouteOnly": { + "defaultRouteOnly": { + "title": "Show ips that are used for default routing only", + "type": "boolean", + "default": false + }, + "key": { + "$ref": "#/$defs/key" + }, + "keyColor": { + "$ref": "#/$defs/keyColor" + }, + "keyWidth": { + "$ref": "#/$defs/keyWidth" + }, + "format": { + "$ref": "#/$defs/format" + } + }, + "additionalProperties": false + }, + { + "title": "Network throughput (usage)", + "properties": { + "type": { + "const": "netio" + }, + "namePrefix": { + "title": "Show IPs with given name prefix only", + "type": "string" + }, + "defaultRouteOnly": { "title": "Show ips that are used for default routing only", "type": "boolean", "default": false diff --git a/presets/all b/presets/all index 1841c47ca..b0ed3cab5 100644 --- a/presets/all +++ b/presets/all @@ -1 +1 @@ ---structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:Monitor:LM:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Wallpaper:Terminal:TerminalFont:TerminalSize:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Bluetooth:Sound:Gamepad:Weather:Version:Break:Colors +--structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:Monitor:LM:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Wallpaper:Terminal:TerminalFont:TerminalSize:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Bluetooth:Sound:Gamepad:Weather:NetIO:Version:Break:Colors diff --git a/presets/all.jsonc b/presets/all.jsonc index b3e9c30d9..f8d4cc82e 100644 --- a/presets/all.jsonc +++ b/presets/all.jsonc @@ -51,6 +51,7 @@ "sound", "gamepad", "weather", + "netio", "version", "break", "colors" diff --git a/presets/ci b/presets/ci new file mode 100644 index 000000000..d8327be7a --- /dev/null +++ b/presets/ci @@ -0,0 +1,7 @@ +-c all +--pipe +--show-errors +--no-buffer +--publicip-timeout 1000 +--weather-timeout 1000 +--stat diff --git a/presets/devinfo b/presets/devinfo deleted file mode 100644 index 2ddf030b5..000000000 --- a/presets/devinfo +++ /dev/null @@ -1,3 +0,0 @@ ---disable-linewrap false ---multithreading false ---show-errors diff --git a/presets/devinfo-verbose b/presets/devinfo-verbose deleted file mode 100644 index 6f79f97e7..000000000 --- a/presets/devinfo-verbose +++ /dev/null @@ -1,3 +0,0 @@ ---load-config devinfo ---load-config all ---load-config verbose diff --git a/presets/devinfo.jsonc b/presets/devinfo.jsonc deleted file mode 100644 index 450adb926..000000000 --- a/presets/devinfo.jsonc +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", - "logo": { - "type": "builtin", - "source": "arch" - }, - "display": { - "disableLinewrap": false, - "showErrors": true - }, - "general": { - "multithreading": false - }, - "modules": [ - "title", - "separator", - { - "type": "os", - "format": "Arch Linux ({12})" - }, - "host", - "kernel", - "uptime", - { - "type": "packages", - "format": "{} (pacman)" - }, - "shell", - "display", - "de", - "wm", - "wmtheme", - "theme", - "icons", - "font", - "cursor", - "terminal", - "terminalfont", - "cpu", - "gpu", - "memory", - "disk", - "battery", - "poweradapter", - "locale", - "break", - "colors" - ] -} diff --git a/presets/examples/10.jsonc b/presets/examples/10.jsonc new file mode 100644 index 000000000..96ec3bb06 --- /dev/null +++ b/presets/examples/10.jsonc @@ -0,0 +1,150 @@ +// Load with --load-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": { + "padding": { + "top": 2 + } + }, + "display": { + "separator": " -> " + }, + "modules": [ + { + "type": "custom", + "format": "\u001b[90m┌────────────────────────────────────────────────────────────┐" + }, + { + "type": "title", + "keyWidth": 10 + }, + { + "type": "custom", + "format": "\u001b[90m└────────────────────────────────────────────────────────────┘" + }, + { + "type": "custom", + "format": " \u001b[90m \u001b[31m \u001b[32m \u001b[33m \u001b[34m \u001b[35m \u001b[36m \u001b[37m \u001b[38m \u001b[39m  \u001b[38m \u001b[37m \u001b[36m \u001b[35m \u001b[34m \u001b[33m \u001b[32m \u001b[31m \u001b[90m" + }, + { + "type": "custom", + "format": "\u001b[90m┌────────────────────────────────────────────────────────────┐" + }, + { + "type": "os", + "key": " OS", + "keyColor": "yellow" + }, + { + "type": "kernel", + "key": "│ ├", + "keyColor": "yellow" + }, + { + "type": "packages", + "key": "│ ├󰏖", + "keyColor": "yellow" + }, + { + "type": "shell", + "key": "│ └", + "keyColor": "yellow" + }, + { + "type": "wm", + "key": " DE/WM", + "keyColor": "blue" + }, + { + "type": "lm", + "key": "│ ├󰧨", + "keyColor": "blue" + }, + { + "type": "wmtheme", + "key": "│ ├󰉼", + "keyColor": "blue" + }, + { + "type": "icons", + "key": "│ ├󰀻", + "keyColor": "blue" + }, + { + "type": "terminal", + "key": "│ ├", + "keyColor": "blue" + }, + { + "type": "wallpaper", + "key": "│ └󰸉", + "keyColor": "blue" + }, + { + "type": "host", + "key": " PC", + "keyColor": "green" + }, + { + "type": "cpu", + "key": "│ ├", + "keyColor": "green" + }, + { + "type": "gpu", + "key": "│ ├﬙", + "keyColor": "green" + }, + { + "type": "disk", + "key": "│ ├", + "keyColor": "green" + }, + { + "type": "memory", + "key": "│ ├󰑭", + "keyColor": "green" + }, + { + "type": "swap", + "key": "│ ├󰓡", + "keyColor": "green" + }, + { + "type": "uptime", + "key": "│ ├󰅐", + "keyColor": "green" + }, + { + "type": "display", + "key": "│ └󰍹", + "keyColor": "green" + }, + { + "type": "sound", + "key": " SND", + "keyColor": "cyan" + }, + { + "type": "player", + "key": "│ ├󰥠", + "keyColor": "cyan" + }, + { + "type": "media", + "key": "│ └󰝚", + "keyColor": "cyan" + }, + { + "type": "custom", + "format": "\u001b[90m└────────────────────────────────────────────────────────────┘" + }, + "break", + { + "type": "custom", + "format": " \u001b[90m \u001b[31m \u001b[32m \u001b[33m \u001b[34m \u001b[35m \u001b[36m \u001b[37m \u001b[38m \u001b[39m  \u001b[38m \u001b[37m \u001b[36m \u001b[35m \u001b[34m \u001b[33m \u001b[32m \u001b[31m \u001b[90m" + } + ] +} diff --git a/presets/examples/11.jsonc b/presets/examples/11.jsonc new file mode 100644 index 000000000..13215add1 --- /dev/null +++ b/presets/examples/11.jsonc @@ -0,0 +1,38 @@ +{ + "$schema": "file:///C:/msys64/home/zhang/fastfetch/doc/json_schema.json", + "logo": { + "type": "small" + }, + "display": { + "percentType": 9, + "separator": "\u001b[31m-> " + }, + "modules": [ + { + "key": "Distro ", + "type": "os" + }, + { + "key": "Shell ", + "type": "shell" + }, + { + "key": "Terminal ", + "type": "terminal" + }, + { + "key": "Display ", + "type": "display" + }, + { + "key": "Backlight ", + "type": "brightness" + }, + "break", + { + "type": "colors", + "paddingLeft": 6, + "symbol": "circle" + } + ] +} diff --git a/presets/verbose b/presets/verbose deleted file mode 100644 index 3ed020f19..000000000 --- a/presets/verbose +++ /dev/null @@ -1,31 +0,0 @@ ---os-format System: {}; Name: {}; Pretty name: {}; ID: {}; ID like: {}; Variant: {}; Variant ID: {}; Version: {}; Version ID: {}; Version codename: {}; Build ID: {}; Architecture: {} ---host-format Family: product_family: {}; product_name: {}; product_version: {}; product_sku: {}; sys_vendor: {} ---kernel-format Sysname: {}; Release: {}; Version: {} ---uptime-format Days: {}; Hours: {}; Minutes: {}; Seconds: {} ---processes-format Count: {} ---packages-format All: {}; pacman: {}; pacman branch: {}; dpkg: {}; rpm: {}; emerge: {}; eopkg: {}; xbps: {}; apk: {}; flatpak-system: {}; flatpak-user: {}; snap: {}; brew: {}; brew-cask: {}; port: {}; scoop: {}; choco: {}; pkgtool: {}; paludis {} ---shell-format Process name: {}; Process path: {}; Process exe: {}; Process version: {}; User path: {}; User exe: {}; User version: {} ---display-format Width: {}; Height: {}; Refresh rate: {}; ScaledWith: {}; ScaledHeight: {} ---de-format Process name: {}; Pretty name: {}; Version: {} ---wm-format Process name: {}; Pretty name: {}; Protocol: {} ---wmtheme-format Name: {} ---theme-format Plasma: {}; Plasma colors: {}; Plasma colors pretty: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {} ---icons-format Plasma: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {} ---font-format Font1: {}; Font2: {}; Font3: {}; Font4: {} ---cursor-format Theme: {}; Size: {} ---terminal-format Process: {}; Path: {}; Exe: {} ---terminalfont-format Pretty: {}; Name: {}; Size: {}; Styles: {} ---cpu-format Name: {}, Vendor: {}, CoresPhysical: {}, CoresLogical: {}, CoresOnline: {}, FrequencyMin: {}, FrequencyMax: {}, Temperature: {} ---cpu-usage-format Percentage: {} ---gpu-format Vendor: {}; Name: {}; Driver: {}; Temperature: {}; CoreCount: {}; Type: {} ---memory-format Used: {}; Total: {}; Percentage: {} ---disk-format SizeUsed: {}; SizeTotal: {}; SizePercentage: {}; FilesUsed: {}; FilesTotal: {}; FilesPercentage: {}; External: {}; Hidden: {}; Filesystem: {}; Name: {} ---battery-format Manufactor: {}; Model: {}; Technology: {}; Capacity: {}; Status: {} ---poweradapter-format Watts: {}; Name: {}; Manufactor: {}; Model: {}; Description: {} ---player-format Pretty: {}; Name: {}; Bus: {}; Url: {} ---media-format Pretty: {}; Name: {}; Artist: {}; Album: {} ---datetime-format year: {}; yearShort: {}; month: {}; monthPretty: {}; monthName: {}; monthNameShort: {}; weekNumber: {}; weekday: {}; weekdayShort: {}; dayInYear: {}; dayInMonth: {}; dayInWeek: {}; hour: {}; hourPretty: {}; hour12: {}; hour12Pretty: {}; minute: {}; minutePretty: {}; second: {}; secondPretty: {} ---vulkan-format driver: {}; Api Version: {}; Conformance Version: {} ---opengl-format version: {}; renderer: {}; vendor: {} ---opencl-format version: {}; device: {}; vendor: {} ---bluetooth-format Name: {}; Address: {}; Type: {}; Battery: {} diff --git a/src/3rdparty/yyjson/repo.json b/src/3rdparty/yyjson/repo.json index d9edbde01..4e8e1106d 100644 --- a/src/3rdparty/yyjson/repo.json +++ b/src/3rdparty/yyjson/repo.json @@ -1,6 +1,6 @@ { "home": "https://github.com/ibireme/yyjson", "license": "MIT ( embed in source )", - "version": "5e3b26d2659287d31e2f8e10f95f95feb7e5ab3a", + "version": "0.8.0", "author": "ibireme" } diff --git a/src/3rdparty/yyjson/yyjson.c b/src/3rdparty/yyjson/yyjson.c index c669bab9b..3e74166c1 100644 --- a/src/3rdparty/yyjson/yyjson.c +++ b/src/3rdparty/yyjson/yyjson.c @@ -307,6 +307,9 @@ uint32_t yyjson_version(void) { #define YYJSON_MUT_DOC_VAL_POOL_INIT_SIZE (0x10 * sizeof(yyjson_mut_val)) #define YYJSON_MUT_DOC_VAL_POOL_MAX_SIZE (0x1000000 * sizeof(yyjson_mut_val)) +/* The minimum size of the dynamic allocator's chunk. */ +#define YYJSON_ALC_DYN_MIN_SIZE 0x1000 + /* Default value for compile-time options. */ #ifndef YYJSON_DISABLE_READER #define YYJSON_DISABLE_READER 0 @@ -966,6 +969,15 @@ static const yyjson_alc YYJSON_DEFAULT_ALC = { NULL }; + + +/*============================================================================== + * Null Memory Allocator + * + * This allocator is just a placeholder to ensure that the internal + * malloc/realloc/free function pointers are not null. + *============================================================================*/ + static void *null_malloc(void *ctx, usize size) { return NULL; } @@ -989,29 +1001,37 @@ static const yyjson_alc YYJSON_NULL_ALC = { /*============================================================================== * Pool Memory Allocator - * This is a simple memory allocator that uses linked list memory chunk. - * The following code will be executed only when the library user creates - * this allocator manually. + * + * This allocator is initialized with a fixed-size buffer. + * The buffer is split into multiple memory chunks for memory allocation. *============================================================================*/ -/** chunk header */ +/** memory chunk header */ typedef struct pool_chunk { - usize size; /* chunk memory size (include chunk header) */ - struct pool_chunk *next; + usize size; /* chunk memory size, include chunk header */ + struct pool_chunk *next; /* linked list, nullable */ + /* char mem[]; flexible array member */ } pool_chunk; -/** ctx header */ +/** allocator ctx header */ typedef struct pool_ctx { - usize size; /* total memory size (include ctx header) */ - pool_chunk *free_list; + usize size; /* total memory size, include ctx header */ + pool_chunk *free_list; /* linked list, nullable */ + /* pool_chunk chunks[]; flexible array member */ } pool_ctx; +/** align up the input size to chunk size */ +static_inline void pool_size_align(usize *size) { + *size = size_align_up(*size, sizeof(pool_chunk)) + sizeof(pool_chunk); +} + static void *pool_malloc(void *ctx_ptr, usize size) { + /* assert(size != 0) */ pool_ctx *ctx = (pool_ctx *)ctx_ptr; pool_chunk *next, *prev = NULL, *cur = ctx->free_list; - if (unlikely(size == 0 || size >= ctx->size)) return NULL; - size = size_align_up(size, sizeof(pool_chunk)) + sizeof(pool_chunk); + if (unlikely(size >= ctx->size)) return NULL; + pool_size_align(&size); while (cur) { if (cur->size < size) { @@ -1038,6 +1058,7 @@ static void *pool_malloc(void *ctx_ptr, usize size) { } static void pool_free(void *ctx_ptr, void *ptr) { + /* assert(ptr != NULL) */ pool_ctx *ctx = (pool_ctx *)ctx_ptr; pool_chunk *cur = ((pool_chunk *)ptr) - 1; pool_chunk *prev = NULL, *next = ctx->free_list; @@ -1064,25 +1085,15 @@ static void pool_free(void *ctx_ptr, void *ptr) { static void *pool_realloc(void *ctx_ptr, void *ptr, usize old_size, usize size) { + /* assert(ptr != NULL && size != 0 && old_size < size) */ pool_ctx *ctx = (pool_ctx *)ctx_ptr; pool_chunk *cur = ((pool_chunk *)ptr) - 1, *prev, *next, *tmp; - usize free_size; - void *new_ptr; - if (unlikely(size == 0 || size >= ctx->size)) return NULL; - size = size_align_up(size, sizeof(pool_chunk)) + sizeof(pool_chunk); - - /* reduce size */ - if (unlikely(size <= cur->size)) { - free_size = cur->size - size; - if (free_size >= sizeof(pool_chunk) * 2) { - tmp = (pool_chunk *)(void *)((u8 *)cur + cur->size - free_size); - tmp->size = free_size; - pool_free(ctx_ptr, (void *)(tmp + 1)); - cur->size -= free_size; - } - return ptr; - } + /* check size */ + if (unlikely(size >= ctx->size)) return NULL; + pool_size_align(&old_size); + pool_size_align(&size); + if (unlikely(old_size == size)) return ptr; /* find next and prev chunk */ prev = NULL; @@ -1092,10 +1103,9 @@ static void *pool_realloc(void *ctx_ptr, void *ptr, next = next->next; } - /* merge to higher chunk if they are contiguous */ - if ((u8 *)cur + cur->size == (u8 *)next && - cur->size + next->size >= size) { - free_size = cur->size + next->size - size; + if ((u8 *)cur + cur->size == (u8 *)next && cur->size + next->size >= size) { + /* merge to higher chunk if they are contiguous */ + usize free_size = cur->size + next->size - size; if (free_size > sizeof(pool_chunk) * 2) { tmp = (pool_chunk *)(void *)((u8 *)cur + size); if (prev) prev->next = tmp; @@ -1109,15 +1119,15 @@ static void *pool_realloc(void *ctx_ptr, void *ptr, cur->size += next->size; } return ptr; + } else { + /* fallback to malloc and memcpy */ + void *new_ptr = pool_malloc(ctx_ptr, size - sizeof(pool_chunk)); + if (new_ptr) { + memcpy(new_ptr, ptr, cur->size - sizeof(pool_chunk)); + pool_free(ctx_ptr, ptr); + } + return new_ptr; } - - /* fallback to malloc and memcpy */ - new_ptr = pool_malloc(ctx_ptr, size - sizeof(pool_chunk)); - if (new_ptr) { - memcpy(new_ptr, ptr, cur->size - sizeof(pool_chunk)); - pool_free(ctx_ptr, ptr); - } - return new_ptr; } bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, usize size) { @@ -1147,6 +1157,161 @@ bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, usize size) { +/*============================================================================== + * Dynamic Memory Allocator + * + * This allocator allocates memory on demand and does not immediately release + * unused memory. Instead, it places the unused memory into a freelist for + * potential reuse in the future. It is only when the entire allocator is + * destroyed that all previously allocated memory is released at once. + *============================================================================*/ + +/** memory chunk header */ +typedef struct dyn_chunk { + usize size; /* chunk size, include header */ + struct dyn_chunk *next; + /* char mem[]; flexible array member */ +} dyn_chunk; + +/** allocator ctx header */ +typedef struct { + dyn_chunk free_list; /* dummy header, sorted from small to large */ + dyn_chunk used_list; /* dummy header */ +} dyn_ctx; + +/** align up the input size to chunk size */ +static_inline bool dyn_size_align(usize *size) { + usize alc_size = *size + sizeof(dyn_chunk); + alc_size = size_align_up(alc_size, YYJSON_ALC_DYN_MIN_SIZE); + if (unlikely(alc_size < *size)) return false; /* overflow */ + *size = alc_size; + return true; +} + +/** remove a chunk from list (the chunk must already be in the list) */ +static_inline void dyn_chunk_list_remove(dyn_chunk *list, dyn_chunk *chunk) { + dyn_chunk *prev = list, *cur; + for (cur = prev->next; cur; cur = cur->next) { + if (cur == chunk) { + prev->next = cur->next; + cur->next = NULL; + return; + } + prev = cur; + } +} + +/** add a chunk to list header (the chunk must not be in the list) */ +static_inline void dyn_chunk_list_add(dyn_chunk *list, dyn_chunk *chunk) { + chunk->next = list->next; + list->next = chunk; +} + +static void *dyn_malloc(void *ctx_ptr, usize size) { + /* assert(size != 0) */ + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *chunk, *prev, *next; + if (unlikely(!dyn_size_align(&size))) return NULL; + + /* freelist is empty, create new chunk */ + if (!ctx->free_list.next) { + chunk = (dyn_chunk *)def.malloc(def.ctx, size); + if (unlikely(!chunk)) return NULL; + chunk->size = size; + chunk->next = NULL; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + + /* find a large enough chunk, or resize the largest chunk */ + prev = &ctx->free_list; + while (true) { + chunk = prev->next; + if (chunk->size >= size) { /* enough size, reuse this chunk */ + prev->next = chunk->next; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + if (!chunk->next) { /* resize the largest chunk */ + chunk = (dyn_chunk *)def.realloc(def.ctx, chunk, chunk->size, size); + if (unlikely(!chunk)) return NULL; + prev->next = NULL; + chunk->size = size; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + prev = chunk; + } +} + +static void *dyn_realloc(void *ctx_ptr, void *ptr, + usize old_size, usize size) { + /* assert(ptr != NULL && size != 0 && old_size < size) */ + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *prev, *next, *new_chunk; + dyn_chunk *chunk = (dyn_chunk *)ptr - 1; + if (unlikely(!dyn_size_align(&size))) return NULL; + if (chunk->size >= size) return ptr; + + dyn_chunk_list_remove(&ctx->used_list, chunk); + new_chunk = (dyn_chunk *)def.realloc(def.ctx, chunk, chunk->size, size); + if (likely(new_chunk)) { + new_chunk->size = size; + chunk = new_chunk; + } + dyn_chunk_list_add(&ctx->used_list, chunk); + return new_chunk ? (void *)(new_chunk + 1) : NULL; +} + +static void dyn_free(void *ctx_ptr, void *ptr) { + /* assert(ptr != NULL) */ + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *chunk = (dyn_chunk *)ptr - 1, *prev; + + dyn_chunk_list_remove(&ctx->used_list, chunk); + for (prev = &ctx->free_list; prev; prev = prev->next) { + if (!prev->next || prev->next->size >= chunk->size) { + chunk->next = prev->next; + prev->next = chunk; + break; + } + } +} + +yyjson_alc *yyjson_alc_dyn_new(void) { + const yyjson_alc def = YYJSON_DEFAULT_ALC; + usize hdr_len = sizeof(yyjson_alc) + sizeof(dyn_ctx); + yyjson_alc *alc = (yyjson_alc *)def.malloc(def.ctx, hdr_len); + dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + if (unlikely(!alc)) return NULL; + alc->malloc = dyn_malloc; + alc->realloc = dyn_realloc; + alc->free = dyn_free; + alc->ctx = alc + 1; + memset(ctx, 0, sizeof(*ctx)); + return alc; +} + +void yyjson_alc_dyn_free(yyjson_alc *alc) { + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + dyn_chunk *chunk, *next; + if (unlikely(!alc)) return; + for (chunk = ctx->free_list.next; chunk; chunk = next) { + next = chunk->next; + def.free(def.ctx, chunk); + } + for (chunk = ctx->used_list.next; chunk; chunk = next) { + next = chunk->next; + def.free(def.ctx, chunk); + } + def.free(def.ctx, alc); +} + + + /*============================================================================== * JSON document and value *============================================================================*/ @@ -1305,7 +1470,6 @@ yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *m_doc, We copy them to another contiguous memory as mutable values, then reconnect the mutable values with the original relationship. */ - usize i_vals_len; yyjson_mut_val *m_vals, *m_val; yyjson_val *i_val, *i_end; @@ -1375,7 +1539,6 @@ static yyjson_mut_val *unsafe_yyjson_mut_val_mut_copy(yyjson_mut_doc *m_doc, second to last item, which needs to be linked to the last item to close the circle. */ - yyjson_mut_val *m_val = unsafe_yyjson_mut_val(m_doc, 1); if (unlikely(!m_val)) return NULL; m_val->tag = m_vals->tag; @@ -1540,12 +1703,13 @@ static_inline bool unsafe_yyjson_num_equals(void *lhs, void *rhs) { yyjson_val_uni *runi = &((yyjson_val *)rhs)->uni; yyjson_subtype lt = unsafe_yyjson_get_subtype(lhs); yyjson_subtype rt = unsafe_yyjson_get_subtype(rhs); - if (lt == rt) - return luni->u64 == runi->u64; - if (lt == YYJSON_SUBTYPE_SINT && rt == YYJSON_SUBTYPE_UINT) + if (lt == rt) return luni->u64 == runi->u64; + if (lt == YYJSON_SUBTYPE_SINT && rt == YYJSON_SUBTYPE_UINT) { return luni->i64 >= 0 && luni->u64 == runi->u64; - if (lt == YYJSON_SUBTYPE_UINT && rt == YYJSON_SUBTYPE_SINT) + } + if (lt == YYJSON_SUBTYPE_UINT && rt == YYJSON_SUBTYPE_SINT) { return runi->i64 >= 0 && luni->u64 == runi->u64; + } return false; } @@ -1571,8 +1735,8 @@ bool unsafe_yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { while (len-- > 0) { rhs = yyjson_obj_iter_getn(&iter, lhs->uni.str, unsafe_yyjson_get_len(lhs)); - if (!rhs || !unsafe_yyjson_equals(lhs + 1, rhs)) - return false; + if (!rhs) return false; + if (!unsafe_yyjson_equals(lhs + 1, rhs)) return false; lhs = unsafe_yyjson_get_next(lhs + 1); } } @@ -1626,8 +1790,8 @@ bool unsafe_yyjson_mut_equals(yyjson_mut_val *lhs, yyjson_mut_val *rhs) { while (len-- > 0) { rhs = yyjson_mut_obj_iter_getn(&iter, lhs->uni.str, unsafe_yyjson_get_len(lhs)); - if (!rhs || !unsafe_yyjson_mut_equals(lhs->next, rhs)) - return false; + if (!rhs) return false; + if (!unsafe_yyjson_mut_equals(lhs->next, rhs)) return false; lhs = lhs->next->next; } } @@ -2506,6 +2670,7 @@ yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, builder = yyjson_mut_obj(doc); if (unlikely(!builder)) return NULL; + memset(&local_orig, 0, sizeof(local_orig)); if (!yyjson_is_obj(orig)) { orig = &local_orig; orig->tag = builder->tag; @@ -2557,6 +2722,7 @@ yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc, builder = yyjson_mut_obj(doc); if (unlikely(!builder)) return NULL; + memset(&local_orig, 0, sizeof(local_orig)); if (!yyjson_mut_is_obj(orig)) { orig = &local_orig; orig->tag = builder->tag; @@ -3318,8 +3484,6 @@ static_inline void pow10_table_get_exp(i32 exp10, i32 *exp2) { -#if !YYJSON_DISABLE_READER - /*============================================================================== * JSON Character Matcher *============================================================================*/ @@ -3513,6 +3677,8 @@ static_inline bool digi_is_digit_or_fp(u8 d) { +#if !YYJSON_DISABLE_READER + /*============================================================================== * Hex Character Reader * This function is used by JSON reader to read escaped characters. diff --git a/src/3rdparty/yyjson/yyjson.h b/src/3rdparty/yyjson/yyjson.h index a9740045a..97b86fb96 100644 --- a/src/3rdparty/yyjson/yyjson.h +++ b/src/3rdparty/yyjson/yyjson.h @@ -527,16 +527,16 @@ extern "C" { #define YYJSON_VERSION_MAJOR 0 /** The minor version of yyjson. */ -#define YYJSON_VERSION_MINOR 7 +#define YYJSON_VERSION_MINOR 8 /** The patch version of yyjson. */ #define YYJSON_VERSION_PATCH 0 /** The version of yyjson in hex: `(major << 16) | (minor << 8) | (patch)`. */ -#define YYJSON_VERSION_HEX 0x000700 +#define YYJSON_VERSION_HEX 0x000800 /** The version string of yyjson. */ -#define YYJSON_VERSION_STRING "0.7.0" +#define YYJSON_VERSION_STRING "0.8.0" /** The version of yyjson in hex, same as `YYJSON_VERSION_HEX`. */ yyjson_api uint32_t yyjson_version(void); @@ -635,11 +635,11 @@ typedef struct yyjson_alc { function, but the amount of memory required to write a JSON cannot be directly calculated. - This is not a general-purpose allocator. If used to read multiple JSON - documents and only some of them are released, it may cause memory - fragmentation, leading to performance degradation and memory waste. Therefore, - it is recommended to use this allocator only for reading or writing a single - JSON document. + This is not a general-purpose allocator. It is designed to handle a single JSON + data at a time. If it is used for overly complex memory tasks, such as parsing + multiple JSON documents using the same allocator but releasing only a few of + them, it may cause memory fragmentation, resulting in performance degradation + and memory waste. @param alc The allocator to be initialized. If this parameter is NULL, the function will fail and return false. @@ -662,9 +662,31 @@ typedef struct yyjson_alc { yyjson_doc *doc = yyjson_read_opts(json, strlen(json), 0, &alc, NULL); // the memory of `doc` is on the stack @endcode + + @warning This Allocator is not thread-safe. */ yyjson_api bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, size_t size); +/** + A dynamic allocator. + + This allocator has a similar usage to the pool allocator above. However, when + there is not enough memory, this allocator will dynamically request more memory + using libc's `malloc` function, and frees it all at once when it is destroyed. + + @return A new dynamic allocator, or NULL if memory allocation failed. + @note The returned value should be freed with `yyjson_alc_dyn_free()`. + + @warning This Allocator is not thread-safe. + */ +yyjson_api yyjson_alc *yyjson_alc_dyn_new(void); + +/** + Free a dynamic allocator which is created by `yyjson_alc_dyn_new()`. + @param alc The dynamic allocator to be destroyed. + */ +yyjson_api void yyjson_alc_dyn_free(yyjson_alc *alc); + /*============================================================================== @@ -3556,7 +3578,7 @@ yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3566,7 +3588,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_true(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3576,7 +3598,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_true(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_false(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3586,7 +3608,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_false(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3596,7 +3618,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_uint(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3606,7 +3628,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_uint(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_sint(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3616,7 +3638,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_sint(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3626,7 +3648,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_real(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3636,7 +3658,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_real(yyjson_mut_doc *doc, The `key` and `val` should be null-terminated UTF-8 strings. This function allows duplicated key in one object. - @warning The key/value string are not copied, you should keep these strings + @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3648,7 +3670,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, The `len` should be the length of the `val`, in bytes. This function allows duplicated key in one object. - @warning The key/value string are not copied, you should keep these strings + @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_strn(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3660,7 +3682,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_strn(yyjson_mut_doc *doc, The value string is copied. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3673,18 +3695,44 @@ yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, The `len` should be the length of the `val`, in bytes. This function allows duplicated key in one object. - @warning The key/value string are not copied, you should keep these strings + @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, const char *val, size_t len); +/** + Creates and adds a new array to the target object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep these strings + unmodified for the lifetime of this JSON document. + @return The new array, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + +/** + Creates and adds a new object to the target object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep these strings + unmodified for the lifetime of this JSON document. + @return The new object, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + /** Adds a JSON value at the end of the object. The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -6920,6 +6968,22 @@ yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, }); } +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_val *key = yyjson_mut_str(doc, _key); + yyjson_mut_val *val = yyjson_mut_arr(doc); + return yyjson_mut_obj_add(obj, key, val) ? val : NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_val *key = yyjson_mut_str(doc, _key); + yyjson_mut_val *val = yyjson_mut_obj(doc); + return yyjson_mut_obj_add(obj, key, val) ? val : NULL; +} + yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *_key, diff --git a/src/common/commandoption.c b/src/common/commandoption.c index 510ac61a9..e8a7e8001 100644 --- a/src/common/commandoption.c +++ b/src/common/commandoption.c @@ -1,7 +1,26 @@ #include "commandoption.h" +#include "common/printing.h" +#include "common/time.h" +#include "fastfetch_datatext.h" +#include "modules/modules.h" #include "util/stringUtils.h" #include +#include + +static inline yyjson_mut_val* genJson(FFModuleBaseInfo* baseInfo) +{ + yyjson_mut_doc* doc = instance.state.resultDoc; + if (__builtin_expect(!doc, true)) return NULL; + + yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root); + yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name); + if (baseInfo->generateJson) + baseInfo->generateJson(baseInfo, doc, module); + else + yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format"); + return module; +} bool ffParseModuleCommand(const char* type) { @@ -12,7 +31,8 @@ bool ffParseModuleCommand(const char* type) FFModuleBaseInfo* baseInfo = *modules; if (ffStrEqualsIgnCase(type, baseInfo->name)) { - baseInfo->printModule(baseInfo); + if (!genJson(baseInfo)) + baseInfo->printModule(baseInfo); return true; } } @@ -30,3 +50,91 @@ bool ffParseModuleOptions(const char* key, const char* value) } return false; } + +void ffPrepareCommandOption(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); + + if(ffStrbufContainIgnCaseS(&data->structure, FF_CPUUSAGE_MODULE_NAME)) + ffPrepareCPUUsage(); + + if(ffStrbufContainIgnCaseS(&data->structure, FF_NETIO_MODULE_NAME)) + ffPrepareNetIO(&instance.config.netIo); + + if(instance.config.multithreading) + { + if(ffStrbufContainIgnCaseS(&data->structure, FF_PUBLICIP_MODULE_NAME)) + ffPreparePublicIp(&instance.config.publicIP); + + if(ffStrbufContainIgnCaseS(&data->structure, FF_WEATHER_MODULE_NAME)) + ffPrepareWeather(&instance.config.weather); + } +} + +static void parseStructureCommand(const char* line, FFlist* customValues) +{ + // handle `--set` and `--set-keyless` + FF_LIST_FOR_EACH(FFCustomValue, customValue, *customValues) + { + if (ffStrbufEqualS(&customValue->key, line)) + { + __attribute__((__cleanup__(ffDestroyCustomOptions))) FFCustomOptions options; + ffInitCustomOptions(&options); + if (customValue->printKey) + ffStrbufAppend(&options.moduleArgs.key, &customValue->key); + ffStrbufAppend(&options.moduleArgs.outputFormat, &customValue->value); + ffPrintCustom(&options); + return; + } + } + + if(!ffParseModuleCommand(line)) + ffPrintErrorString(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, ""); +} + +void ffPrintCommandOption(FFdata* data) +{ + yyjson_mut_doc* resultDoc = instance.state.resultDoc; + + //Parse the structure and call the modules + uint32_t startIndex = 0; + while (startIndex < data->structure.length) + { + uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':'); + data->structure.chars[colonIndex] = '\0'; + + uint64_t ms = 0; + if(instance.config.stat) + ms = ffTimeGetTick(); + + parseStructureCommand(data->structure.chars + startIndex, &data->customValues); + + if(instance.config.stat) + { + ms = ffTimeGetTick() - ms; + + if (resultDoc) + { + yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(resultDoc->root); + yyjson_mut_obj_add_uint(resultDoc, moduleJson, "stat", ms); + } + else + { + char str[32]; + int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ms); + if(instance.config.pipe) + puts(str); + else + printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load + } + } + + #if defined(_WIN32) + if (!instance.config.noBuffer) fflush(stdout); + #endif + + startIndex = colonIndex + 1; + } +} diff --git a/src/common/commandoption.h b/src/common/commandoption.h index 3ae2a8311..c938fe2a8 100644 --- a/src/common/commandoption.h +++ b/src/common/commandoption.h @@ -5,7 +5,24 @@ #include "fastfetch.h" +typedef struct FFCustomValue +{ + bool printKey; + FFstrbuf key; + FFstrbuf value; +} FFCustomValue; + +// Things only needed by fastfetch +typedef struct FFdata +{ + FFstrbuf structure; + FFlist customValues; // List of FFCustomValue + bool loadUserConfig; +} FFdata; + bool ffParseModuleCommand(const char* type); bool ffParseModuleOptions(const char* key, const char* value); +void ffPrepareCommandOption(FFdata* data); +void ffPrintCommandOption(FFdata* data); #endif diff --git a/src/common/format.c b/src/common/format.c index 4ed096dc1..b5092c6c7 100644 --- a/src/common/format.c +++ b/src/common/format.c @@ -12,6 +12,8 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) ffStrbufAppendF(buffer, "%i", *(int*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT) ffStrbufAppendF(buffer, "%" PRIu32, *(uint32_t*)formatarg->value); + else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT64) + ffStrbufAppendF(buffer, "%" PRIu64, *(uint64_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT16) ffStrbufAppendF(buffer, "%" PRIu16, *(uint16_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT8) @@ -254,7 +256,5 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n ffFormatAppendFormatArg(buffer, &arguments[index - 1]); } - ffStrbufTrimRight(buffer, ' '); - ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET); } diff --git a/src/common/format.h b/src/common/format.h index 57c8c680f..3e7c751f2 100644 --- a/src/common/format.h +++ b/src/common/format.h @@ -7,6 +7,7 @@ typedef enum FFformatargtype { FF_FORMAT_ARG_TYPE_NULL = 0, FF_FORMAT_ARG_TYPE_UINT, + FF_FORMAT_ARG_TYPE_UINT64, FF_FORMAT_ARG_TYPE_UINT16, FF_FORMAT_ARG_TYPE_UINT8, FF_FORMAT_ARG_TYPE_INT, diff --git a/src/common/init.c b/src/common/init.c index dfe8b4d19..35f1467fc 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -27,6 +27,7 @@ static void initState(FFstate* state) ffPlatformInit(&state->platform); state->configDoc = NULL; + state->resultDoc = NULL; } static void defaultConfig(void) @@ -105,6 +106,7 @@ static void defaultConfig(void) ffInitMediaOptions(&instance.config.media); ffInitMemoryOptions(&instance.config.memory); ffInitMonitorOptions(&instance.config.monitor); + ffInitNetIOOptions(&instance.config.netIo); ffInitOSOptions(&instance.config.os); ffInitOpenCLOptions(&instance.config.openCL); ffInitOpenGLOptions(&instance.config.openGL); @@ -234,11 +236,11 @@ void ffStart(void) startDetectionThreads(); #endif - ffDisableLinewrap = instance.config.disableLinewrap && !instance.config.pipe; - ffHideCursor = instance.config.hideCursor && !instance.config.pipe; + ffDisableLinewrap = instance.config.disableLinewrap && !instance.config.pipe && !instance.state.resultDoc; + ffHideCursor = instance.config.hideCursor && !instance.config.pipe && !instance.state.resultDoc; #ifdef _WIN32 - if (!instance.config.noBuffer) setvbuf(stdout, NULL, _IOFBF, 4096); + setvbuf(stdout, NULL, _IOFBF, instance.config.noBuffer ? 0 : 4096); SetConsoleCtrlHandler(consoleHandler, TRUE); HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); DWORD mode = 0; @@ -254,7 +256,7 @@ void ffStart(void) #endif //reset everything to default before we start printing - if(!instance.config.pipe) + if(!instance.config.pipe && !instance.state.resultDoc) fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); if(ffHideCursor) @@ -318,6 +320,7 @@ static void destroyConfig(void) ffDestroyMediaOptions(&instance.config.media); ffDestroyMemoryOptions(&instance.config.memory); ffDestroyMonitorOptions(&instance.config.monitor); + ffDestroyNetIOOptions(&instance.config.netIo); ffDestroyOSOptions(&instance.config.os); ffDestroyOpenCLOptions(&instance.config.openCL); ffDestroyOpenGLOptions(&instance.config.openGL); @@ -375,6 +378,7 @@ static void destroyState(void) { ffPlatformDestroy(&instance.state.platform); yyjson_doc_free(instance.state.configDoc); + yyjson_mut_doc_free(instance.state.resultDoc); } void ffDestroyInstance(void) diff --git a/src/common/io/io.h b/src/common/io/io.h index 8cca6e147..78c94ad9f 100644 --- a/src/common/io/io.h +++ b/src/common/io/io.h @@ -12,6 +12,7 @@ typedef HANDLE FFNativeFD; #else #include + #include typedef int FFNativeFD; #endif @@ -84,6 +85,8 @@ typedef enum FFPathType bool ffPathExists(const char* path, FFPathType pathType); bool ffPathExpandEnv(const char* in, FFstrbuf* out); +#define FF_IO_TERM_RESP_WAIT_MS 100 // #554 + FF_C_SCANF(2, 3) const char* ffGetTerminalResponse(const char* request, const char* format, ...); @@ -130,4 +133,25 @@ static inline bool wrapFclose(FILE** pfile) } #define FF_AUTO_CLOSE_FILE __attribute__((__cleanup__(wrapFclose))) +#ifndef _WIN32 +static inline bool wrapClosedir(DIR** pdir) +{ + assert(pdir); + if (!*pdir) + return false; + closedir(*pdir); + return true; +} +#else +static inline bool wrapClosedir(HANDLE* pdir) +{ + assert(pdir); + if (!*pdir) + return false; + FindClose(*pdir); + return true; +} +#endif +#define FF_AUTO_CLOSE_DIR __attribute__((__cleanup__(wrapClosedir))) + #endif // FF_INCLUDED_common_io_io diff --git a/src/common/io/io_unix.c b/src/common/io/io_unix.c index 1941e6637..db5f3c65b 100644 --- a/src/common/io/io_unix.c +++ b/src/common/io/io_unix.c @@ -54,24 +54,20 @@ bool ffAppendFDBuffer(int fd, FFstrbuf* buffer) uint32_t free = ffStrbufGetFree(buffer); while( - (readed = read(fd, buffer->chars + buffer->length, free)) > 0 && - (uint32_t) readed == free + (readed = read(fd, buffer->chars + buffer->length, free)) > 0 ) { buffer->length += (uint32_t) readed; - ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. + if((uint32_t) readed == free) + ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. free = ffStrbufGetFree(buffer); } - // In case of failure, read returns -1. We don't want to substract the length of the buffer. - if(readed > 0) - buffer->length += (uint32_t) readed; - buffer->chars[buffer->length] = '\0'; ffStrbufTrimRight(buffer, '\n'); ffStrbufTrimRight(buffer, ' '); - return readed >= 0; + return buffer->length > 0; } ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data) @@ -150,7 +146,7 @@ const char* ffGetTerminalResponse(const char* request, const char* format, ...) fflush(stdout); //Give the terminal 35ms to respond - if(poll(&(struct pollfd) { .fd = STDIN_FILENO, .events = POLLIN }, 1, 35) <= 0) + if(poll(&(struct pollfd) { .fd = STDIN_FILENO, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0) { tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm); return "poll() timeout or failed"; diff --git a/src/common/io/io_windows.c b/src/common/io/io_windows.c index 2cd916bd8..c4c26145b 100644 --- a/src/common/io/io_windows.c +++ b/src/common/io/io_windows.c @@ -44,16 +44,14 @@ bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer) bool success; while( (success = !!ReadFile(handle, buffer->chars + buffer->length, free, &readed, NULL)) && - (uint32_t) readed == free + readed > 0 ) { buffer->length += (uint32_t) readed; - ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. + if((uint32_t) readed == free) + ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. free = ffStrbufGetFree(buffer); } - if(readed > 0) - buffer->length += (uint32_t) readed; - buffer->chars[buffer->length] = '\0'; ffStrbufTrimRight(buffer, '\n'); @@ -173,7 +171,7 @@ const char* ffGetTerminalResponse(const char* request, const char* format, ...) while (true) { - if (WaitForSingleObjectEx(hInput, 35, TRUE) != WAIT_OBJECT_0) + if (WaitForSingleObjectEx(hInput, FF_IO_TERM_RESP_WAIT_MS, TRUE) != WAIT_OBJECT_0) { SetConsoleMode(hInput, prev_mode); return "WaitForSingleObject() failed or timeout"; diff --git a/src/common/jsonconfig.c b/src/common/jsonconfig.c index 71bc3cf8f..6d8fee329 100644 --- a/src/common/jsonconfig.c +++ b/src/common/jsonconfig.c @@ -70,6 +70,20 @@ const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair p return "Invalid enum value type; must be a string or integer"; } +static inline yyjson_mut_val* genJson(FFModuleBaseInfo* baseInfo) +{ + yyjson_mut_doc* doc = instance.state.resultDoc; + if (__builtin_expect(!doc, true)) return NULL; + + yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root); + yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name); + if (baseInfo->generateJson) + baseInfo->generateJson(baseInfo, doc, module); + else + yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format"); + return module; +} + static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal) { if(!isalpha(type[0])) return false; @@ -80,7 +94,8 @@ static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal) if (ffStrEqualsIgnCase(type, baseInfo->name)) { if (jsonVal) baseInfo->parseJsonObject(baseInfo, jsonVal); - baseInfo->printModule(baseInfo); + if (!genJson(baseInfo)) + baseInfo->printModule(baseInfo); return true; } } @@ -97,6 +112,14 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) ffPrepareCPUUsage(); break; } + case 'n': case 'N': { + if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME)) + { + if (module) ffParseNetIOJsonObject(&cfg->netIo, module); + ffPrepareNetIO(&cfg->netIo); + } + break; + } case 'p': case 'P': { if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME)) { @@ -128,6 +151,8 @@ static const char* printJsonConfig(bool prepare) if (!modules) return NULL; if (!yyjson_is_arr(modules)) return "Property 'modules' must be an array of strings or objects"; + yyjson_mut_doc* resultDoc = instance.state.resultDoc; + yyjson_val* item; size_t idx, max; yyjson_arr_foreach(modules, idx, max, item) @@ -157,16 +182,25 @@ static const char* printJsonConfig(bool prepare) if(!prepare && instance.config.stat) { - char str[32]; - int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ffTimeGetTick() - ms); - if(instance.config.pipe) - puts(str); + ms = ffTimeGetTick() - ms; + if (resultDoc) + { + yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(resultDoc->root); + yyjson_mut_obj_add_uint(resultDoc, moduleJson, "stat", ms); + } else - printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load + { + char str[32]; + int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ms); + if(instance.config.pipe) + puts(str); + else + printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load + } } #if defined(_WIN32) - if (!instance.config.noBuffer) fflush(stdout); + if (!instance.config.noBuffer && !resultDoc) fflush(stdout); #endif } diff --git a/src/common/jsonconfig.h b/src/common/jsonconfig.h index e701cae86..afb0fd865 100644 --- a/src/common/jsonconfig.h +++ b/src/common/jsonconfig.h @@ -8,3 +8,16 @@ void ffPrintJsonConfig(bool prepare); const char* ffParseGeneralJsonConfig(void); const char* ffParseDisplayJsonConfig(void); const char* ffParseLibraryJsonConfig(void); + +yyjson_api_inline bool yyjson_mut_obj_add_strbuf(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + const FFstrbuf* buf) { + return yyjson_mut_obj_add_strncpy(doc, obj, _key, buf->chars, buf->length); +} + +yyjson_api_inline bool yyjson_mut_arr_add_strbuf(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const FFstrbuf* buf) { + return yyjson_mut_arr_add_strncpy(doc, obj, buf->chars, buf->length); +} diff --git a/src/common/modules.c b/src/common/modules.c index 1c4cd108a..420903c8e 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -82,6 +82,7 @@ static FFModuleBaseInfo* M[] = { }; static FFModuleBaseInfo* N[] = { + (void*) &instance.config.netIo, NULL, }; diff --git a/src/common/netif/netif.c b/src/common/netif/netif.c new file mode 100644 index 000000000..ec317454b --- /dev/null +++ b/src/common/netif/netif.c @@ -0,0 +1,30 @@ +#include "netif.h" + +#ifndef _WIN32 + #include +#else + #define IF_NAMESIZE 16 +#endif + +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex); +enum { IF_INDEX_UNINITIALIZED = (uint32_t) -1, IF_INDEX_INVALID = (uint32_t) -2 }; +static uint32_t ifIndex = IF_INDEX_UNINITIALIZED; +static char ifName[IF_NAMESIZE + 1]; + +static inline void init() +{ + if (ifIndex == (uint32_t) IF_INDEX_UNINITIALIZED && !ffNetifGetDefaultRouteImpl(ifName, &ifIndex)) + ifIndex = (uint32_t) IF_INDEX_INVALID; +} + +const char* ffNetifGetDefaultRouteIfName() +{ + init(); + return ifName; +} + +uint32_t ffNetifGetDefaultRouteIfIndex() +{ + init(); + return ifIndex; +} diff --git a/src/common/netif/netif.h b/src/common/netif/netif.h new file mode 100644 index 000000000..23ca5ef60 --- /dev/null +++ b/src/common/netif/netif.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#ifndef _WIN32 +const char* ffNetifGetDefaultRouteIfName(); +#endif + +uint32_t ffNetifGetDefaultRouteIfIndex(); diff --git a/src/common/netif/netif_bsd.c b/src/common/netif/netif_bsd.c new file mode 100644 index 000000000..f288d7d35 --- /dev/null +++ b/src/common/netif/netif_bsd.c @@ -0,0 +1,98 @@ +#include "netif.h" + +#include "common/io/io.h" + +#include +#include +#include +#include +#include + +#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) + +#if defined(__APPLE__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) +#elif defined(__NetBSD__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) +#elif defined(__FreeBSD__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) +#elif defined(__OpenBSD__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) +#else +# error unknown platform +#endif + +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 <sa_len) + (char *)sa); + } + } + return NULL; +} + +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +{ + //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) + 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)); + } + + int pid = getpid(); + + struct { + struct rt_msghdr hdr; + struct sockaddr_in dst; + uint8_t data[512]; + } rtmsg = { + .hdr = { + .rtm_type = RTM_GET, + .rtm_flags = RTF_UP | RTF_GATEWAY, + .rtm_version = RTM_VERSION, + .rtm_addrs = RTA_DST | RTA_IFP, + .rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst), + .rtm_pid = pid, + .rtm_seq = 1, + }, + .dst = { + .sin_family = AF_INET, + .sin_len = sizeof(rtmsg.dst), + }, + }; + + if (write(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen) != rtmsg.hdr.rtm_msglen) + return false; + + while (read(pfRoute, &rtmsg, sizeof(rtmsg)) > 0) + { + if (rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid) + { + struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP); + if (sdl) + { + assert(sdl->sdl_nlen <= IF_NAMESIZE); + memcpy(iface, sdl->sdl_data, sdl->sdl_nlen); + iface[sdl->sdl_nlen] = '\0'; + *ifIndex = sdl->sdl_index; + return true; + } + return false; + } + } + return false; +} diff --git a/src/common/netif/netif_linux.c b/src/common/netif/netif_linux.c new file mode 100644 index 000000000..f645b0a0b --- /dev/null +++ b/src/common/netif/netif_linux.c @@ -0,0 +1,28 @@ +#include "netif.h" +#include "common/io/io.h" + +#include +#include + +#define FF_STR_INDIR(x) #x +#define FF_STR(x) FF_STR_INDIR(x) + +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +{ + FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/route", "r"); + if (!netRoute) return false; + + // skip first line + flockfile(netRoute); + while (getc_unlocked(netRoute) != '\n'); + funlockfile(netRoute); + unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, + + while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", iface, &destination) == 2) + { + if (destination != 0) continue; + *ifIndex = if_nametoindex(iface); + return true; + } + return false; +} diff --git a/src/common/netif/netif_windows.c b/src/common/netif/netif_windows.c new file mode 100644 index 000000000..8607ed486 --- /dev/null +++ b/src/common/netif/netif_windows.c @@ -0,0 +1,27 @@ +#include "netif.h" +#include "util/mallocHelper.h" + +#include + +bool ffNetifGetDefaultRouteImpl(FF_MAYBE_UNUSED char* iface/* unsupported */, uint32_t* ifIndex) +{ + ULONG size = 0; + if (GetIpForwardTable(NULL, &size, TRUE) != ERROR_INSUFFICIENT_BUFFER) + return false; + + FF_AUTO_FREE MIB_IPFORWARDTABLE* pIpForwardTable = (MIB_IPFORWARDTABLE*) malloc(size); + if (GetIpForwardTable(pIpForwardTable, &size, TRUE) != ERROR_SUCCESS) + return false; + + for (uint32_t i = 0; i < pIpForwardTable->dwNumEntries; ++i) + { + MIB_IPFORWARDROW* ipForwardRow = &pIpForwardTable->table[i]; + if (ipForwardRow->dwForwardDest == 0 && ipForwardRow->dwForwardMask == 0) + { + *ifIndex = ipForwardRow->dwForwardIfIndex; + break; + } + } + + return true; +} diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c index 7fb6fade5..37c796276 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking_linux.c @@ -77,7 +77,8 @@ bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, con bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout) { #ifdef FF_HAVE_THREADS - ffThreadJoin(state->thread); + if (!ffThreadJoin(state->thread, timeout)) + return false; #endif if(state->sockfd == -1) return false; @@ -85,8 +86,8 @@ bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, ui if(timeout > 0) { struct timeval timev; - timev.tv_sec = 0; - timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds + timev.tv_sec = timeout / 1000; + timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); //milliseconds to microseconds setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)); } diff --git a/src/common/option.h b/src/common/option.h index 342b48083..3fef4995a 100644 --- a/src/common/option.h +++ b/src/common/option.h @@ -3,14 +3,21 @@ #include "util/FFstrbuf.h" struct yyjson_val; +struct yyjson_mut_doc; +struct yyjson_mut_val; // Must be the first field of FFModuleOptions typedef struct FFModuleBaseInfo { const char* name; + // A dirty polymorphic implementation in C. + // This is UB, because `void*` is not compatible with `FF*Options*`. + // However we can't do it better unless we move to C++, so that `option` becomes a `this` pointer + // https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type bool (*parseCommandOptions)(void* options, const char* key, const char* value); void (*parseJsonObject)(void* options, struct yyjson_val *module); void (*printModule)(void* options); + void (*generateJson)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* module); } FFModuleBaseInfo; static inline void ffOptionInitModuleBaseInfo( @@ -18,13 +25,15 @@ static inline void ffOptionInitModuleBaseInfo( const char* name, void* parseCommandOptions, // bool (*const parseCommandOptions)(void* options, const char* key, const char* value) void* parseJsonObject, // void (*const parseJsonObject)(void* options, yyjson_val *module) - void* printModule // void (*const printModule)(void* options) + void* printModule, // void (*const printModule)(void* options) + void* generateJson // void (*const generateJson)(void* options, yyjson_mut_doc* doc, yyjson_mut_val* obj) ) { baseInfo->name = name; baseInfo->parseCommandOptions = (__typeof__(baseInfo->parseCommandOptions)) parseCommandOptions; baseInfo->parseJsonObject = (__typeof__(baseInfo->parseJsonObject)) parseJsonObject; baseInfo->printModule = (__typeof__(baseInfo->printModule)) printModule; + baseInfo->generateJson = (__typeof__(baseInfo->generateJson)) generateJson; } typedef struct FFModuleArgs diff --git a/src/common/printing.c b/src/common/printing.c index 063127081..0d8635f2f 100644 --- a/src/common/printing.c +++ b/src/common/printing.c @@ -10,6 +10,10 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu if(moduleName == NULL) return; + //This is used as a magic value for hiding keys + if(moduleArgs && ffStrbufEqualS(&moduleArgs->key, " ")) + return; + if(!instance.config.pipe) { fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); @@ -112,9 +116,7 @@ void ffPrintColor(const FFstrbuf* colorValue) if(colorValue->length == 0) return; - fputs("\033[", stdout); - ffStrbufWriteTo(colorValue, stdout); - fputc('m', stdout); + printf("\e[%sm", colorValue->chars); } void ffPrintCharTimes(char c, uint32_t times) diff --git a/src/common/processing_linux.c b/src/common/processing_linux.c index 815ed1fc8..84bc79cec 100644 --- a/src/common/processing_linux.c +++ b/src/common/processing_linux.c @@ -38,6 +38,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use close(pipes[0]); close(pipes[1]); close(useStdErr ? STDOUT_FILENO : STDERR_FILENO); + setenv("LANG", "C", 1); execvp(argv[0], argv); exit(901); } diff --git a/src/common/thread.h b/src/common/thread.h index 0b8989c4e..55b4c5be7 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -10,6 +10,7 @@ #include #include #include + #include #define FF_THREAD_MUTEX_INITIALIZER SRWLOCK_INIT typedef SRWLOCK FFThreadMutex; typedef HANDLE FFThreadType; @@ -21,9 +22,23 @@ #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static __stdcall unsigned fn ## ThreadMain (void* data) { fn((paramType)data); return 0; } #define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) static __stdcall unsigned fn ## ThreadMain () { fn(); return 0; } static inline void ffThreadDetach(FFThreadType thread) { CloseHandle(thread); } - static inline void ffThreadJoin(FFThreadType thread) { WaitForSingleObject(thread, 0xffffffff /*INFINITE*/); } + static inline bool ffThreadJoin(FFThreadType thread, uint32_t timeout) + { + if (WaitForSingleObject(thread, timeout == 0 ? (DWORD) -1 : timeout) != 0 /*WAIT_OBJECT_0*/) + { + TerminateThread(thread, (DWORD) -1); + CloseHandle(thread); + return false; + } + CloseHandle(thread); + return true; + } #else #include + #include + #if __has_include() + #include + #endif #define FF_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER typedef pthread_mutex_t FFThreadMutex; typedef pthread_t FFThreadType; @@ -37,7 +52,28 @@ #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static void* fn ## ThreadMain (void* data) { fn((paramType)data); return NULL; } #define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) static void* fn ## ThreadMain () { fn(); return NULL; } static inline void ffThreadDetach(FFThreadType thread) { pthread_detach(thread); } - static inline void ffThreadJoin(FFThreadType thread) { pthread_join(thread, NULL); } + static inline bool ffThreadJoin(FFThreadType thread, FF_MAYBE_UNUSED uint32_t timeout) + { + #if (defined(__linux__) && !defined(__ANDROID__)) || __has_include() + if (timeout > 0) + { + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) + { + ts.tv_sec += ts.tv_sec / 1000; + ts.tv_nsec += (ts.tv_nsec % 1000) * 1000000; + if (pthread_timedjoin_np(thread, NULL, &ts) != 0) + { + pthread_kill(thread, SIGTERM); + return false; + } + return true; + } + } + #endif + pthread_join(thread, NULL); + return true; + } #endif #else //FF_HAVE_THREADS #define FF_THREAD_MUTEX_INITIALIZER 0 diff --git a/src/common/time.h b/src/common/time.h index 797461cbd..8da249448 100644 --- a/src/common/time.h +++ b/src/common/time.h @@ -7,6 +7,7 @@ #ifdef _WIN32 #include #include + #include #else #include #endif @@ -26,6 +27,19 @@ static inline uint64_t ffTimeGetTick() //In msec #endif } +static inline uint64_t ffTimeGetNow() +{ + #ifdef _WIN32 + uint64_t timeNow; + GetSystemTimeAsFileTime((FILETIME*) &timeNow); + return (timeNow - 116444736000000000ull) / 10000ull; + #else + struct timespec timeNow; + clock_gettime(CLOCK_REALTIME, &timeNow); + return (uint64_t)((timeNow.tv_sec * 1000) + (timeNow.tv_nsec / 1000000)); + #endif +} + static inline void ffTimeSleep(uint32_t msec) { #ifdef _WIN32 diff --git a/src/data/config_user.txt b/src/data/config_user.txt index 7bd07a49e..c8a257ec5 100644 --- a/src/data/config_user.txt +++ b/src/data/config_user.txt @@ -360,7 +360,7 @@ #--terminal-key Terminal #--terminalfont-key Terminal Font #--cpu-key CPU -#--cpu-usage-key CPU Usage +#--cpuusage-key CPU Usage #--gpu-key GPU {1} #--memory-key Memory #--swap-key Swap @@ -370,6 +370,7 @@ #--lm-key LM #--locale-key Locale #--localip-key Local IP ({1}) +#--netio-key Network IO ({1}) #--publicip-key Public IP #--wifi-key Wifi #--weather-key Weather @@ -391,6 +392,7 @@ # For information on format strings, see "fastfetch --help format". # To see the parameter they take and their default value, see "fastfetch --help *-format", e.g. "fastfetch --help os-format". # An empty format string (As they are currently below) will behave as if it was not set. +#--title-format #--os-format #--host-format #--chassis-format @@ -411,7 +413,7 @@ #--terminal-format #--terminalfont-format #--cpu-format -#--cpu-usage-format +#--cpuusage-format #--gpu-format #--memory-format #--swap-format @@ -421,6 +423,7 @@ #--lm-format #--locale-format #--localip-format +#--netio-format #--publicip-format #--weather-format #--monitor-format @@ -439,6 +442,7 @@ # Key color options: # Overrides the global `--color-keys` for one specified module +#--title-key-color #--os-key-color #--host-key-color #--chassis-key-color @@ -459,7 +463,7 @@ #--terminal-key-color #--terminalfont-key-color #--cpu-key-color -#--cpu-usage-key-color +#--cpuusage-key-color #--gpu-key-color #--memory-key-color #--swap-key-color @@ -469,6 +473,7 @@ #--lm-key-color #--locale-key-color #--localip-key-color +#--netio-key-color #--publicip-key-color #--weather-key-color #--monitor-key-color diff --git a/src/data/help.txt b/src/data/help.txt index eb975f786..94ba7f617 100644 --- a/src/data/help.txt +++ b/src/data/help.txt @@ -147,9 +147,11 @@ Module specific options: --localip-show-ipv6 : Show IPv6 addresses in local ip module. Default is false --localip-show-mac : Show mac addresses in local ip module. Default is false --localip-show-loop : Show loop back addresses (127.0.0.1) in local ip module. Default is false - --localip-name-prefix : Show IPs with given name prefix only. Default is empty - --localip-default-route-only : Show ips that are used for default routing only. Default is false + --localip-name-prefix : Show interfaces with given interface name prefix only. Default is empty + --localip-default-route-only : Show the interface that is used for default routing only. Default is false --localip-compact : Show all IPs in one line. Default is false + --netio-name-prefix : Show interfaces with given name prefix only. Default is empty + --netio-default-route-only : Show the interfac that is used for default routing only. Default is false --publicip-timeout: Time in milliseconds to wait for the public ip server to respond. Default is disabled (0) --publicip-url: The URL of public IP detection server to be used. --weather-location: Set the location to be used. It must be URI encoded (eg a whitespace must be encoded as `+`). diff --git a/src/detection/battery/battery.h b/src/detection/battery/battery.h index 5667af76c..7df622ea2 100644 --- a/src/detection/battery/battery.h +++ b/src/detection/battery/battery.h @@ -7,7 +7,7 @@ #define FF_BATTERY_TEMP_UNSET (0/0.0) -typedef struct BatteryResult +typedef struct FFBatteryResult { FFstrbuf manufacturer; FFstrbuf modelName; @@ -15,7 +15,7 @@ typedef struct BatteryResult double capacity; FFstrbuf status; double temperature; -} BatteryResult; +} FFBatteryResult; const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results); diff --git a/src/detection/battery/battery_android.c b/src/detection/battery/battery_android.c index b05cce111..16dce83d0 100644 --- a/src/detection/battery/battery_android.c +++ b/src/detection/battery/battery_android.c @@ -33,7 +33,7 @@ static const char* parseTermuxApi(FFBatteryOptions* options, FFlist* results) if (!yyjson_is_obj(root)) return "Battery info result is not a JSON object"; - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->temperature = FF_BATTERY_TEMP_UNSET; ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); @@ -68,7 +68,7 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results) return NULL; ffStrbufClear(&temp); - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->temperature = FF_BATTERY_TEMP_UNSET; ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); @@ -78,7 +78,7 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results) if (ffParsePropLines(start, "AC powered: ", &temp) && ffStrbufEqualS(&temp, "true")) ffStrbufAppendS(&battery->status, "AC powered"); ffStrbufClear(&temp); - + if (ffParsePropLines(start, "USB powered: ", &temp) && ffStrbufEqualS(&temp, "true")) { if (battery->status.length) ffStrbufAppendS(&battery->status, ", "); diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c index dc1281dda..52f179780 100644 --- a/src/detection/battery/battery_apple.c +++ b/src/detection/battery/battery_apple.c @@ -38,7 +38,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) bool boolValue; const char* error; - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->capacity = 0.0/0.0; int currentCapacity, maxCapacity; diff --git a/src/detection/battery/battery_bsd.c b/src/detection/battery/battery_bsd.c index 287f004a1..8675ab099 100644 --- a/src/detection/battery/battery_bsd.c +++ b/src/detection/battery/battery_bsd.c @@ -32,7 +32,7 @@ const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* r if(ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) < 0 || (battio.battinfo.state == ACPI_BATT_STAT_NOT_PRESENT)) continue; - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->temperature = FF_BATTERY_TEMP_UNSET; ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); diff --git a/src/detection/battery/battery_linux.c b/src/detection/battery/battery_linux.c index 41d49db62..a0bbf85f6 100644 --- a/src/detection/battery/battery_linux.c +++ b/src/detection/battery/battery_linux.c @@ -27,7 +27,7 @@ static void parseBattery(FFstrbuf* dir, FFlist* results) if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Device") == 0) return; - BatteryResult* result = ffListAdd(results); + FFBatteryResult* result = ffListAdd(results); //capacity must exist and be not empty ffStrbufAppendS(dir, "/capacity"); diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c index 66df36cf3..056bfd06a 100644 --- a/src/detection/battery/battery_windows.c +++ b/src/detection/battery/battery_windows.c @@ -74,7 +74,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) if(!(bi.Capabilities & BATTERY_SYSTEM_BATTERY)) continue; - BatteryResult* battery = (BatteryResult*)ffListAdd(results); + FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results); if(memcmp(bi.Chemistry, "PbAc", 4) == 0) ffStrbufInitS(&battery->technology, "Lead Acid"); @@ -143,7 +143,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) SYSTEM_BATTERY_STATE info; if (NT_SUCCESS(NtPowerInformation(SystemBatteryState, NULL, 0, &info, sizeof(info))) && info.BatteryPresent) { - BatteryResult* battery = (BatteryResult*)ffListAdd(results); + FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results); ffStrbufInit(&battery->modelName); ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->technology); diff --git a/src/detection/bluetooth/bluetooth.h b/src/detection/bluetooth/bluetooth.h index 4aecdfc6f..a9c07b161 100644 --- a/src/detection/bluetooth/bluetooth.h +++ b/src/detection/bluetooth/bluetooth.h @@ -5,15 +5,15 @@ #include "fastfetch.h" -typedef struct FFBluetoothDevice +typedef struct FFBluetoothResult { FFstrbuf name; FFstrbuf address; FFstrbuf type; uint8_t battery; // 0-100% bool connected; -} FFBluetoothDevice; +} FFBluetoothResult; -const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */); +const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */); #endif diff --git a/src/detection/bluetooth/bluetooth_apple.m b/src/detection/bluetooth/bluetooth_apple.m index d6f3e9878..abd31a0c2 100644 --- a/src/detection/bluetooth/bluetooth_apple.m +++ b/src/detection/bluetooth/bluetooth_apple.m @@ -2,7 +2,7 @@ #import -const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */) { NSArray* ioDevices = IOBluetoothDevice.pairedDevices; if(!ioDevices) @@ -10,7 +10,7 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) for(IOBluetoothDevice* ioDevice in ioDevices) { - FFBluetoothDevice* device = ffListAdd(devices); + FFBluetoothResult* device = ffListAdd(devices); ffStrbufInitS(&device->name, ioDevice.name.UTF8String); ffStrbufInitS(&device->address, ioDevice.addressString.UTF8String); ffStrbufInit(&device->type); diff --git a/src/detection/bluetooth/bluetooth_linux.c b/src/detection/bluetooth/bluetooth_linux.c index 79f61d5fb..1ed712c90 100644 --- a/src/detection/bluetooth/bluetooth_linux.c +++ b/src/detection/bluetooth/bluetooth_linux.c @@ -44,7 +44,7 @@ array [ //root ] */ -static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothDevice* device) +static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) { if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) return; @@ -72,7 +72,7 @@ static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBlue ffDBusGetBool(dbus, &dictIter, &device->connected); } -static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothDevice* device) +static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) { if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) return; @@ -130,7 +130,7 @@ static void detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessage DBusMessageIter arrayIter; dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter); - FFBluetoothDevice* device = ffListAdd(devices); + FFBluetoothResult* device = ffListAdd(devices); ffStrbufInit(&device->name); ffStrbufInit(&device->address); ffStrbufInit(&device->type); @@ -193,7 +193,7 @@ static const char* detectBluetooth(FFlist* devices) #endif -const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothResult */) { #ifdef FF_HAVE_DBUS return detectBluetooth(devices); diff --git a/src/detection/bluetooth/bluetooth_nosupport.c b/src/detection/bluetooth/bluetooth_nosupport.c index 4ae74ac3e..8db5a465c 100644 --- a/src/detection/bluetooth/bluetooth_nosupport.c +++ b/src/detection/bluetooth/bluetooth_nosupport.c @@ -1,6 +1,6 @@ #include "bluetooth.h" -const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothResult */) { return "Not supported on this platform"; } diff --git a/src/detection/bluetooth/bluetooth_windows.c b/src/detection/bluetooth/bluetooth_windows.c index 0066cebb3..bcd9da33e 100644 --- a/src/detection/bluetooth/bluetooth_windows.c +++ b/src/detection/bluetooth/bluetooth_windows.c @@ -7,7 +7,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpointer-sign" -const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */) { BLUETOOTH_DEVICE_SEARCH_PARAMS btsp = { .fReturnConnected = TRUE, @@ -25,7 +25,7 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) return "BluetoothFindFirstDevice() failed or no devices found"; do { - FFBluetoothDevice* device = ffListAdd(devices); + FFBluetoothResult* device = ffListAdd(devices); ffStrbufInit(&device->name); ffStrbufInit(&device->address); ffStrbufInit(&device->type); @@ -33,9 +33,14 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) device->connected = !!btdi.fConnected; ffStrbufSetWS(&device->name, btdi.szName); - for (uint32_t i = 0; i < sizeof(btdi.Address.rgBytes) / sizeof(btdi.Address.rgBytes); ++i) - ffStrbufAppendF(&device->address, "%X:", btdi.Address.rgBytes[i]); - ffStrbufTrimRight(&device->name, ':'); + + ffStrbufAppendF(&device->address, "%02x:%02x:%02x:%02x:%02x:%02x", + btdi.Address.rgBytes[0], + btdi.Address.rgBytes[1], + btdi.Address.rgBytes[2], + btdi.Address.rgBytes[3], + btdi.Address.rgBytes[4], + btdi.Address.rgBytes[5]); //https://btprodspecificationrefs.blob.core.windows.net/assigned-numbers/Assigned%20Number%20Types/Assigned%20Numbers.pdf if(BitTest(&btdi.ulClassofDevice, 13)) diff --git a/src/detection/brightness/brightness.h b/src/detection/brightness/brightness.h index d65843947..58aeed20a 100644 --- a/src/detection/brightness/brightness.h +++ b/src/detection/brightness/brightness.h @@ -9,7 +9,7 @@ typedef struct FFBrightnessResult { FFstrbuf name; - float value; + double min, max, current; } FFBrightnessResult; const char* ffDetectBrightness(FFlist* result); // list of FFBrightnessResult diff --git a/src/detection/brightness/brightness_apple.c b/src/detection/brightness/brightness_apple.c index ecbe60478..a3b1d3c8f 100644 --- a/src/detection/brightness/brightness_apple.c +++ b/src/detection/brightness/brightness_apple.c @@ -20,7 +20,9 @@ static const char* detectWithDisplayServices(const FFDisplayServerResult* displa if(DisplayServicesGetBrightness((CGDirectDisplayID) display->id, &value) == kCGErrorSuccess) { FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = value * 100; + brightness->current = value; + brightness->max = 1; + brightness->min = 0; ffStrbufInitCopy(&brightness->name, &display->name); } } @@ -90,7 +92,9 @@ FF_MAYBE_UNUSED static const char* detectWithDdcci(FFlist* result) uint32_t max = ((uint32_t) i2cOut[6] << 8u) + (uint32_t) i2cOut[7]; FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = (float) current * 100.f / max; + brightness->max = max; + brightness->min = 0; + brightness->current = current; ffStrbufInit(&brightness->name); uint8_t edid[128] = {}; diff --git a/src/detection/brightness/brightness_bsd.c b/src/detection/brightness/brightness_bsd.c index 8c6a89e2f..72f1e5476 100644 --- a/src/detection/brightness/brightness_bsd.c +++ b/src/detection/brightness/brightness_bsd.c @@ -26,13 +26,16 @@ const char* ffDetectBrightness(FFlist* result) if(ioctl(blfd, BACKLIGHTGETSTATUS, &status) < 0) continue; - FFBrightnessResult* display = (FFBrightnessResult*) ffListAdd(result); - ffStrbufInit(&display->name); - display->value = (float) status.brightness / BACKLIGHTMAXLEVELS; + FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); + ffStrbufInit(&brightness->name); + + brightness->max = BACKLIGHTMAXLEVELS; + brightness->min = 0; + brightness->current = status.brightness; struct backlight_info info; if(ioctl(blfd, BACKLIGHTGETINFO, &info) < 0) - ffStrbufAppendS(&display->name, info.name); + ffStrbufAppendS(&brightness->name, info.name); } return NULL; } @@ -44,4 +47,4 @@ const char* ffDetectBrightness(FF_MAYBE_UNUSED FFlist* result) return "Backlight is supported only on FreeBSD 13 and newer"; } -#endif \ No newline at end of file +#endif diff --git a/src/detection/brightness/brightness_linux.c b/src/detection/brightness/brightness_linux.c index 8ab4e5f86..b3e75b234 100644 --- a/src/detection/brightness/brightness_linux.c +++ b/src/detection/brightness/brightness_linux.c @@ -72,8 +72,9 @@ static const char* detectWithBacklight(FFlist* result) } else ffStrbufInitS(&brightness->name, entry->d_name); - double maxBrightness = ffStrbufToDouble(&buffer); - brightness->value = (float) (actualBrightness * 100 / maxBrightness); + brightness->max = ffStrbufToDouble(&buffer); + brightness->min = 0; + brightness->current = actualBrightness; } } ffStrbufSubstrBefore(&backlightDir, backlightDirLength); @@ -123,7 +124,9 @@ static const char* detectWithDdcci(FFlist* result) ffddca_free_any_vcp_value(vcpValue); FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = (float) current * 100.f / (float) max; + brightness->max = max; + brightness->min = 0; + brightness->current = current; ffStrbufInitS(&brightness->name, display->model_name); } ffddca_close_display(handle); diff --git a/src/detection/brightness/brightness_windows.cpp b/src/detection/brightness/brightness_windows.cpp index 28f489b96..06c169017 100644 --- a/src/detection/brightness/brightness_windows.cpp +++ b/src/detection/brightness/brightness_windows.cpp @@ -20,7 +20,9 @@ static const char* detectWithWmi(FFlist* result) if(FFWmiVariant vtValue = record.get(L"CurrentBrightness")) { FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = vtValue.get(); + brightness->max = 100; + brightness->min = 0; + brightness->current = vtValue.get(); ffStrbufInit(&brightness->name); if (FFWmiVariant vtName = record.get(L"InstanceName")) @@ -51,7 +53,9 @@ static char* detectWithDdcci(const FFDisplayServerResult* displayServer, FFlist* else ffStrbufInitWS(&brightness->name, physicalMonitor.szPhysicalMonitorDescription); - brightness->value = (float) (curr - min) * 100.f / (float) (max - min); + brightness->max = max; + brightness->min = min; + brightness->current = curr; } } } diff --git a/src/detection/chassis/chassis_linux.c b/src/detection/chassis/chassis_linux.c index deace35d9..074753eb6 100644 --- a/src/detection/chassis/chassis_linux.c +++ b/src/detection/chassis/chassis_linux.c @@ -25,7 +25,7 @@ const char* ffDetectChassis(FFChassisResult* result) if(result->type.length) { - const char* typeStr = ffChassisTypeToString(ffStrbufToUInt16(&result->type, 9999)); + const char* typeStr = ffChassisTypeToString((uint32_t) ffStrbufToUInt(&result->type, 9999)); if(typeStr) ffStrbufSetS(&result->type, typeStr); } diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c index 9dcb07edd..ded5e0d89 100644 --- a/src/detection/cpu/cpu_apple.c +++ b/src/detection/cpu/cpu_apple.c @@ -2,17 +2,6 @@ #include "common/sysctl.h" #include "detection/temps/temps_apple.h" -static double getFrequency(const char* propName) -{ - double herz = (double) ffSysctlGetInt64(propName, 0); - if(herz <= 0.0) - return herz; - - herz /= 1000.0; //to KHz - herz /= 1000.0; //to MHz - return herz / 1000.0; //to GHz -} - static double detectCpuTemp(const FFstrbuf* cpuName) { double result = 0; @@ -31,11 +20,85 @@ static double detectCpuTemp(const FFstrbuf* cpuName) return result; } +#ifdef __aarch64__ +#include "util/apple/cf_helpers.h" + +#include + +static const char* detectFrequency(FFCPUResult* cpu) +{ + // https://github.com/giampaolo/psutil/pull/2222/files + + CFMutableDictionaryRef matchDict = IOServiceMatching("AppleARMIODevice"); + if (matchDict == NULL) + return "IOServiceMatching(\"AppleARMIODevice\") failed"; + + io_iterator_t iterator; + if(IOServiceGetMatchingServices(MACH_PORT_NULL, matchDict, &iterator) != kIOReturnSuccess) + return "IOServiceGetMatchingServices() failed"; + + io_registry_entry_t registryEntry; + while((registryEntry = IOIteratorNext(iterator)) != 0) + { + CFMutableDictionaryRef properties; + if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) + { + IOObjectRelease(registryEntry); + continue; + } + + io_name_t name; + if (IORegistryEntryGetName(registryEntry, name) != KERN_SUCCESS) + continue; + if (strcmp(name, "pmgr") != 0) + continue; + + uint32_t pMin, eMin, aMax, pCoreLength; + ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), 0, 4, (uint8_t*) &pMin, &pCoreLength); // pCore + ffCfDictGetData(properties, CFSTR("voltage-states1-sram"), 0, 4, (uint8_t*) &eMin, NULL); // eCore + cpu->frequencyMin = (pMin < eMin ? pMin : eMin) / (1000.0 * 1000 * 1000); + + if (pCoreLength >= 8) + { + ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), pCoreLength - 8, 4, (uint8_t*) &aMax, NULL); + cpu->frequencyMax = aMax / (1000.0 * 1000 * 1000); + } + else + cpu->frequencyMax = 0.0; + + CFRelease(properties); + IOObjectRelease(registryEntry); + } + + IOObjectRelease(iterator); + return NULL; +} +#else +static const char* detectFrequency(FFCPUResult* cpu) +{ + cpu->frequencyMin = ffSysctlGetInt64("hw.cpufrequency_min", 0) / 1000.0 / 1000.0 / 1000.0; + cpu->frequencyMax = ffSysctlGetInt64("hw.cpufrequency_max", 0); + if(cpu->frequencyMax > 0.0) + cpu->frequencyMax /= 1000.0 * 1000.0 * 1000.0; + else + { + unsigned current = 0; + size_t size = sizeof(current); + if (sysctl((int[]){ CTL_HW, HW_CPU_FREQ }, 2, ¤t, &size, NULL, 0) == 0) + cpu->frequencyMax = (double) current / 1000.0 / 1000.0 / 1000.0; + } + return NULL; +} +#endif + const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { if (ffSysctlGetString("machdep.cpu.brand_string", &cpu->name) != NULL) return "sysctlbyname(machdep.cpu.brand_string) failed"; + ffSysctlGetString("machdep.cpu.vendor", &cpu->vendor); + if (cpu->vendor.length == 0 && ffStrbufStartsWithS(&cpu->name, "Apple ")) + ffStrbufAppendS(&cpu->vendor, "Apple"); cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu_max", 1); if(cpu->coresPhysical == 1) @@ -49,10 +112,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if(cpu->coresOnline == 1) cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1); - cpu->frequencyMin = getFrequency("hw.cpufrequency_min"); - cpu->frequencyMax = getFrequency("hw.cpufrequency_max"); - if(cpu->frequencyMax == 0.0) - cpu->frequencyMax = getFrequency("hw.cpufrequency"); + detectFrequency(cpu); cpu->temperature = options->temp ? detectCpuTemp(&cpu->name) : FF_CPU_TEMP_UNSET; diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index bdffc755f..1241ef889 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -1,5 +1,6 @@ #include "cpu.h" #include "common/io/io.h" +#include "common/processing.h" #include "common/properties.h" #include "detection/temps/temps_linux.h" #include "util/mallocHelper.h" @@ -128,7 +129,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) const char* error = parseCpuInfo(cpu, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch); if (error) return error; - cpu->coresPhysical = ffStrbufToUInt16(&physicalCoresBuffer, 1); + cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, 1); cpu->coresLogical = (uint16_t) get_nprocs_conf(); cpu->coresOnline = (uint16_t) get_nprocs(); @@ -163,5 +164,17 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) detectAndroid(cpu); #endif + #ifdef __linux__ + if (cpu->name.length == 0) + { + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (!ffProcessAppendStdOut(&buffer, (char *const[]) { "lscpu", NULL })) + { + ffParsePropLines(buffer.chars, "Model name:", &cpu->name); + if (ffStrbufEqualS(&cpu->name, "-")) ffStrbufClear(&cpu->name); + } + } + #endif + return NULL; } diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 231ea342e..59f07ab7a 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -35,7 +35,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) return "GetLogicalProcessorInformationEx(RelationAll, pProcessorInfo, &length) failed"; } - FF_HKEY_AUTO_DESTROY hKey; + FF_HKEY_AUTO_DESTROY hKey = NULL; if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL)) return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\", &hKey, NULL) failed"; diff --git a/src/detection/datetime/datetime.c b/src/detection/datetime/datetime.c deleted file mode 100644 index 60cc1f658..000000000 --- a/src/detection/datetime/datetime.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "fastfetch.h" -#include "detection/datetime/datetime.h" -#include "common/thread.h" - -#include - -const FFDateTimeResult* ffDetectDateTime(void) -{ - static FFDateTimeResult result; - static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; - static bool init = false; - - ffThreadMutexLock(&mutex); - if (init) - { - ffThreadMutexUnlock(&mutex); - return &result; - } - init = true; - - const time_t t = time(NULL); - struct tm* tm = localtime(&t); - - result.year = (uint16_t) (tm->tm_year + 1900); - result.yearShort = (uint8_t) (result.year % 100); - result.month = (uint8_t) (tm->tm_mon + 1); - - ffStrbufInitA(&result.monthPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.monthPretty.length = (uint32_t) strftime(result.monthPretty.chars, ffStrbufGetFree(&result.monthPretty), "%m", tm); - - ffStrbufInitA(&result.monthName, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.monthName.length = (uint32_t) strftime(result.monthName.chars, ffStrbufGetFree(&result.monthName), "%B", tm); - - ffStrbufInitA(&result.monthNameShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.monthNameShort.length = (uint32_t) strftime(result.monthNameShort.chars, ffStrbufGetFree(&result.monthNameShort), "%b", tm); - - result.week = (uint8_t) (tm->tm_yday / 7 + 1); - - ffStrbufInitA(&result.weekday, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.weekday.length = (uint32_t) strftime(result.weekday.chars, ffStrbufGetFree(&result.weekday), "%A", tm); - - ffStrbufInitA(&result.weekdayShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.weekdayShort.length = (uint32_t) strftime(result.weekdayShort.chars, ffStrbufGetFree(&result.weekdayShort), "%a", tm); - - result.dayInYear = (uint8_t) (tm->tm_yday + 1); - result.dayInMonth = (uint8_t) tm->tm_mday; - result.dayInWeek = tm->tm_wday == 0 ? 7 : (uint8_t) tm->tm_wday; - - result.hour = (uint8_t) tm->tm_hour; - - ffStrbufInitA(&result.hourPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.hourPretty.length = (uint32_t) strftime(result.hourPretty.chars, ffStrbufGetFree(&result.hourPretty), "%H", tm); - - result.hour12 = (uint8_t) (result.hour % 12); - - ffStrbufInitA(&result.hour12Pretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.hour12Pretty.length = (uint32_t) strftime(result.hour12Pretty.chars, ffStrbufGetFree(&result.hour12Pretty), "%I", tm); - - result.minute = (uint8_t) tm->tm_min; - - ffStrbufInitA(&result.minutePretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.minutePretty.length = (uint32_t) strftime(result.minutePretty.chars, ffStrbufGetFree(&result.minutePretty), "%M", tm); - - result.second = (uint8_t) tm->tm_sec; - - ffStrbufInitA(&result.secondPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.secondPretty.length = (uint32_t) strftime(result.secondPretty.chars, ffStrbufGetFree(&result.secondPretty), "%S", tm); - - ffThreadMutexUnlock(&mutex); - return &result; -} diff --git a/src/detection/datetime/datetime.h b/src/detection/datetime/datetime.h deleted file mode 100644 index fd47ba03b..000000000 --- a/src/detection/datetime/datetime.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#ifndef FF_INCLUDED_detection_datetime_datetime -#define FF_INCLUDED_detection_datetime_datetime - -#include "fastfetch.h" - -typedef struct FFDateTimeResult -{ - //Examples for 21.02.2022 - 15:18:37 - uint16_t year; //2022 - uint8_t yearShort; //22 - uint8_t month; //2 - FFstrbuf monthPretty; //02 - FFstrbuf monthName; //February - FFstrbuf monthNameShort; //Feb - uint8_t week; //8 - FFstrbuf weekday; //Monday - FFstrbuf weekdayShort; //Mon - uint16_t dayInYear; //52 - uint8_t dayInMonth; //21 - uint8_t dayInWeek; //1 - uint8_t hour; //15 - FFstrbuf hourPretty; //15 - uint8_t hour12; //3 - FFstrbuf hour12Pretty; //03 - uint8_t minute; //18 - FFstrbuf minutePretty; //18 - uint8_t second; //37 - FFstrbuf secondPretty; //37 -} FFDateTimeResult; - -const FFDateTimeResult* ffDetectDateTime(); - -#endif diff --git a/src/detection/disk/disk.h b/src/detection/disk/disk.h index fced34a92..06611f85c 100644 --- a/src/detection/disk/disk.h +++ b/src/detection/disk/disk.h @@ -5,12 +5,21 @@ #include "fastfetch.h" +typedef enum FFDiskPhysicalType +{ + FF_DISK_PHYSICAL_TYPE_UNKNOWN, + FF_DISK_PHYSICAL_TYPE_HDD, + FF_DISK_PHYSICAL_TYPE_SSD, +} FFDiskPhysicalType; + typedef struct FFDisk { + FFstrbuf mountFrom; FFstrbuf mountpoint; FFstrbuf filesystem; FFstrbuf name; FFDiskVolumeType type; + FFDiskPhysicalType physicalType; uint64_t bytesUsed; uint64_t bytesFree; diff --git a/src/detection/disk/disk_apple.m b/src/detection/disk/disk_apple.m deleted file mode 100644 index 81bcdeb48..000000000 --- a/src/detection/disk/disk_apple.m +++ /dev/null @@ -1,17 +0,0 @@ -#include "disk.h" - -#include -#import - -void detectFsInfo(struct statfs* fs, FFDisk* disk) -{ - // FreeBSD doesn't support these flags - if(fs->f_flags & MNT_DONTBROWSE) - disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; - else if(fs->f_flags & MNT_REMOVABLE) - disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; - else - disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; - - ffStrbufInitS(&disk->name, [NSFileManager.defaultManager displayNameAtPath:@(fs->f_mntonname)].UTF8String); -} diff --git a/src/detection/disk/disk_bsd.c b/src/detection/disk/disk_bsd.c index b6c656e8b..55fc94a2e 100644 --- a/src/detection/disk/disk_bsd.c +++ b/src/detection/disk/disk_bsd.c @@ -1,42 +1,68 @@ #include "disk.h" +#include "util/mallocHelper.h" +#include "util/stringUtils.h" #include #ifdef __FreeBSD__ -#include "util/stringUtils.h" - static void detectFsInfo(struct statfs* fs, FFDisk* disk) { if(ffStrbufEqualS(&disk->filesystem, "zfs")) { - disk->type = !ffStrStartsWith(fs->f_mntfromname, "zroot/") || ffStrStartsWith(fs->f_mntfromname, "zroot/ROOT/") + disk->type = !ffStrbufStartsWithS(&disk->mountFrom, "zroot/") || ffStrbufStartsWithS(&disk->mountFrom, "zroot/ROOT/") ? FF_DISK_VOLUME_TYPE_REGULAR_BIT : FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT; } - else if(!ffStrStartsWith(fs->f_mntfromname, "/dev/")) + else if(ffStrbufStartsWithS(&disk->mountpoint, "/boot") || ffStrbufStartsWithS(&disk->mountpoint, "/efi")) disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; else if(!(fs->f_flags & MNT_LOCAL)) disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; else disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; - - ffStrbufInit(&disk->name); } -#else -void detectFsInfo(struct statfs* fs, FFDisk* disk); +#elif __APPLE__ +#include +#include + +struct VolAttrBuf { + uint32_t length; + attrreference_t volNameRef; + char volNameSpace[MAXPATHLEN]; +} __attribute__((aligned(4), packed)); + +void detectFsInfo(struct statfs* fs, FFDisk* disk) +{ + if(fs->f_flags & MNT_DONTBROWSE) + disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; + else if(fs->f_flags & MNT_REMOVABLE || !(fs->f_flags & MNT_LOCAL)) + disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; + else + disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; + + struct VolAttrBuf attrBuf; + if (getattrlist(disk->mountpoint.chars, &(struct attrlist) { + .bitmapcount = ATTR_BIT_MAP_COUNT, + .volattr = ATTR_VOL_INFO | ATTR_VOL_NAME, + }, &attrBuf, sizeof(attrBuf), 0) == 0) + ffStrbufInitNS(&disk->name, attrBuf.volNameRef.attr_length - 1 /* excluding '\0' */, attrBuf.volNameSpace); +} #endif const char* ffDetectDisksImpl(FFlist* disks) { - struct statfs* buf; + int size = getfsstat(NULL, 0, MNT_WAIT); - int size = getmntinfo(&buf, MNT_WAIT); if(size <= 0) - return "getmntinfo(&buf, MNT_WAIT) failed"; + return "getfsstat(NULL, 0, MNT_WAIT) failed"; + + FF_AUTO_FREE struct statfs* buf = malloc(sizeof(*buf) * (unsigned) size); + if(getfsstat(buf, (int) (sizeof(*buf) * (unsigned) size), MNT_NOWAIT) <= 0) + return "getfsstat(buf, size, MNT_NOWAIT) failed"; for(struct statfs* fs = buf; fs < buf + size; ++fs) { - FFDisk* disk = ffListAdd(disks); + if(!ffStrStartsWith(fs->f_mntfromname, "/dev/") && !ffStrEquals(fs->f_fstypename, "zfs")) + continue; #ifdef __FreeBSD__ // f_bavail and f_ffree are signed on FreeBSD... @@ -44,6 +70,9 @@ const char* ffDetectDisksImpl(FFlist* disks) if(fs->f_ffree < 0) fs->f_ffree = 0; #endif + FFDisk* disk = ffListAdd(disks); + disk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + disk->bytesTotal = fs->f_blocks * fs->f_bsize; disk->bytesFree = (uint64_t)fs->f_bfree * fs->f_bsize; disk->bytesAvailable = (uint64_t)fs->f_bavail * fs->f_bsize; @@ -52,8 +81,11 @@ const char* ffDetectDisksImpl(FFlist* disks) disk->filesTotal = (uint32_t) fs->f_files; disk->filesUsed = (uint32_t) (disk->filesTotal - (uint64_t)fs->f_ffree); + ffStrbufInitS(&disk->mountFrom, fs->f_mntfromname); ffStrbufInitS(&disk->mountpoint, fs->f_mntonname); ffStrbufInitS(&disk->filesystem, fs->f_fstypename); + ffStrbufInit(&disk->name); + disk->type = 0; detectFsInfo(fs, disk); if(fs->f_flags & MNT_RDONLY) diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index fb5597baa..bc53eb783 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -1,5 +1,6 @@ #include "disk.h" +#include "common/io/io.h" #include "util/stringUtils.h" #include @@ -7,6 +8,8 @@ #include #include #include +#include +#include #ifdef __USE_LARGEFILE64 #define stat stat64 @@ -15,77 +18,60 @@ #define readdir readdir64 #endif -static bool isPhysicalDevice(FFstrbuf* device) +static bool isPhysicalDevice(const struct mntent* device) { #ifndef __ANDROID__ //On Android, `/dev` is not accessable, so that the following checks always fail - //https://askubuntu.com/a/1289123 - if(ffStrbufEqualS(device, "/cow")) + //Always show the root path + if(ffStrEquals(device->mnt_dir, "/")) return true; //DrvFs is a filesystem plugin to WSL that was designed to support interop between WSL and the Windows filesystem. - if(ffStrbufEqualS(device, "drvfs")) + if(ffStrEquals(device->mnt_fsname, "drvfs")) return true; - //ZFS root pool. The format is rpool/// - if(ffStrbufStartsWithS(device, "rpool/")) + //ZFS pool + if(ffStrEquals(device->mnt_type, "zfs")) return true; + //Pseudo filesystems don't have a device in /dev + if(!ffStrStartsWith(device->mnt_fsname, "/dev/")) + return false; + + if( + ffStrStartsWith(device->mnt_fsname + 5, "loop") || //Ignore loop devices + ffStrStartsWith(device->mnt_fsname + 5, "ram") || //Ignore ram devices + ffStrStartsWith(device->mnt_fsname + 5, "fd") //Ignore fd devices + ) return false; + struct stat deviceStat; - if(stat(device->chars, &deviceStat) != 0) + if(stat(device->mnt_fsname, &deviceStat) != 0) return false; //Ignore all devices that are not block devices if(!S_ISBLK(deviceStat.st_mode)) return false; + #else + //Pseudo filesystems don't have a device in /dev - if(!ffStrbufStartsWithS(device, "/dev/")) + if(!ffStrStartsWith(device->mnt_fsname, "/dev/")) return false; - #endif // __ANDROID__ - if( - ffStrbufStartsWithS(device, "/dev/loop") || //Ignore loop devices - ffStrbufStartsWithS(device, "/dev/ram") || //Ignore ram devices - ffStrbufStartsWithS(device, "/dev/fd") //Ignore fd devices + ffStrStartsWith(device->mnt_fsname + 5, "loop") || //Ignore loop devices + ffStrStartsWith(device->mnt_fsname + 5, "ram") || //Ignore ram devices + ffStrStartsWith(device->mnt_fsname + 5, "fd") //Ignore fd devices ) return false; + #endif // __ANDROID__ + return true; } -static void appendNextEntry(FFstrbuf* buffer, char** source) -{ - //Read the current entry into the buffer - while(**source != '\0' && !isspace(**source)) - { - //After a backslash the next 3 characters are octal ascii codes - if(**source == '\\' && strnlen(*source, 4) == 4) - { - char octal[4] = {0}; - strncpy(octal, *source + 1, 3); - - long value = strtol(octal, NULL, 8); //Returns 0 on error, so no need to check endptr - if(value > 0 && value < CHAR_MAX) - { - ffStrbufAppendC(buffer, (char) value); - *source += 4; - continue; - } - } - - ffStrbufAppendC(buffer, **source); - ++*source; - } - - //Skip whitespace - while(isspace(**source)) - ++*source; -} - static void detectNameFromPath(FFDisk* disk, const struct stat* deviceStat, FFstrbuf* basePath) { - DIR* dir = opendir(basePath->chars); + FF_AUTO_CLOSE_DIR DIR* dir = opendir(basePath->chars); if(dir == NULL) return; @@ -110,18 +96,24 @@ static void detectNameFromPath(FFDisk* disk, const struct stat* deviceStat, FFst ffStrbufAppendS(&disk->name, entry->d_name); break; } - - closedir(dir); } -static void detectName(FFDisk* disk, const FFstrbuf* device) +static void detectName(FFDisk* disk) { struct stat deviceStat; - if(stat(device->chars, &deviceStat) != 0) + if(stat(disk->mountFrom.chars, &deviceStat) != 0) return; FF_STRBUF_AUTO_DESTROY basePath = ffStrbufCreate(); + //Detect USB devices. Code is put here to reuse deviceStat + //https://stackoverflow.com/a/73302717 + ffStrbufAppendS(&basePath, "/dev/disk/by-id/"); + detectNameFromPath(disk, &deviceStat, &basePath); + if (ffStrbufStartsWithS(&disk->name, "usb-")) + disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; + ffStrbufClear(&disk->name); + //Try label first ffStrbufSetS(&basePath, "/dev/disk/by-label/"); detectNameFromPath(disk, &deviceStat, &basePath); @@ -133,6 +125,8 @@ static void detectName(FFDisk* disk, const FFstrbuf* device) detectNameFromPath(disk, &deviceStat, &basePath); } + if (disk->name.length == 0) return; + // Basic\x20data\x20partition for (uint32_t i = ffStrbufFirstIndexS(&disk->name, "\\x"); i != disk->name.length; @@ -152,7 +146,7 @@ static void detectName(FFDisk* disk, const FFstrbuf* device) #ifdef __ANDROID__ -static void detectType(FF_MAYBE_UNUSED const FFlist* devices, FFDisk* currentDisk, FF_MAYBE_UNUSED const char* options) +static void detectType(FF_MAYBE_UNUSED const FFlist* disks, FFDisk* currentDisk) { if(ffStrbufEqualS(¤tDisk->mountpoint, "/") || ffStrbufEqualS(¤tDisk->mountpoint, "/storage/emulated")) currentDisk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; @@ -164,36 +158,95 @@ static void detectType(FF_MAYBE_UNUSED const FFlist* devices, FFDisk* currentDis #else -static bool isSubvolume(const FFlist* devices) +static bool isSubvolume(const FFlist* disks, FFDisk* currentDisk) { - const FFstrbuf* current = ffListGet(devices, devices->length - 1); - - if(ffStrbufEqualS(current, "drvfs")) // WSL Windows drives + if(ffStrbufEqualS(¤tDisk->mountFrom, "drvfs")) // WSL Windows drives return false; - //Filter all disks which device was already found. This catches BTRFS subvolumes. - for(uint32_t i = 0; i < devices->length - 1; i++) + if(ffStrbufEqualS(¤tDisk->filesystem, "zfs")) { - const FFstrbuf* otherDevice = ffListGet(devices, i); + //ZFS subvolumes + uint32_t index = ffStrbufFirstIndexC(¤tDisk->mountFrom, '/'); + if (index == currentDisk->mountFrom.length) + return false; - if(ffStrbufEqual(current, otherDevice)) - return true; + FF_STRBUF_AUTO_DESTROY zpoolName = ffStrbufCreateNS(index, currentDisk->mountFrom.chars); + for(uint32_t i = 0; i < disks->length - 1; i++) + { + const FFDisk* otherDevice = ffListGet(disks, i); + if(ffStrbufEqualS(&otherDevice->filesystem, "zfs") && ffStrbufStartsWith(&otherDevice->mountFrom, &zpoolName)) + return true; + } + + return false; } + else + { + //Filter all disks which device was already found. This catches BTRFS subvolumes. + for(uint32_t i = 0; i < disks->length - 1; i++) + { + const FFDisk* otherDevice = ffListGet(disks, i); - //ZFS subvolumes: rpool///. - //Test if the third slash is present. - if(ffStrbufStartsWithS(current, "rpool/") && ffStrHasNChars(current->chars, '/', 3)) - return true; + if(ffStrbufEqual(¤tDisk->mountFrom, &otherDevice->mountFrom)) + return true; + } + } return false; } -static void detectType(const FFlist* devices, FFDisk* currentDisk, const char* options) +static bool detectPhysicalTypeAndReturnRemovable(FFDisk* currentDisk) { - if(isSubvolume(devices)) - currentDisk->type = FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT; - else if(strstr(options, "nosuid") != NULL || strstr(options, "nodev") != NULL) + // https://stackoverflow.com/a/73302025 + // Note my USB mobile hard disk isn't detected as removable, but my USB flash disk does. + // USB devices should be handled in detectName + FF_STRBUF_AUTO_DESTROY basePath = ffStrbufCreateS("/sys/block/"); + + FF_AUTO_CLOSE_DIR DIR* dir = opendir(basePath.chars); + if(dir == NULL) + return false; + + uint32_t index = ffStrbufLastIndexC(¤tDisk->mountFrom, '/'); + const char* partitionName = index == currentDisk->mountFrom.length ? NULL : currentDisk->mountFrom.chars + index + 1; + if (!partitionName || !*partitionName) return false; + + struct dirent* entry; + while((entry = readdir(dir)) != NULL) + { + if(entry->d_name[0] == '.') + continue; + + if (!ffStrStartsWith(partitionName, entry->d_name)) continue; + + ffStrbufAppendS(&basePath, entry->d_name); + index = basePath.length; + // /sys/block/sdx/queue/rotational + ffStrbufAppendS(&basePath, "/queue/rotational"); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffReadFileBuffer(basePath.chars, &buffer)) + currentDisk->physicalType = ffStrbufEqualS(&buffer, "1") ? FF_DISK_PHYSICAL_TYPE_HDD : FF_DISK_PHYSICAL_TYPE_SSD; + else + currentDisk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + ffStrbufSubstrBefore(&basePath, index); + + // /sys/block/sdx/removable + ffStrbufAppendS(&basePath, "/removable"); + + return ffReadFileBuffer(basePath.chars, &buffer) && ffStrbufEqualS(&buffer, "1"); + } + + return false; +} + +static void detectType(const FFlist* disks, FFDisk* currentDisk) +{ + bool isRemovable = detectPhysicalTypeAndReturnRemovable(currentDisk); + + if(currentDisk->type != FF_DISK_VOLUME_TYPE_NONE) return; + if(isRemovable) currentDisk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; + else if(isSubvolume(disks, currentDisk)) + currentDisk->type = FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT; else if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/boot") || ffStrbufStartsWithS(¤tDisk->mountpoint, "/efi")) currentDisk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; else @@ -222,61 +275,43 @@ static void detectStats(FFDisk* disk) const char* ffDetectDisksImpl(FFlist* disks) { - FILE* mountsFile = fopen("/proc/mounts", "r"); + FILE* mountsFile = setmntent("/proc/mounts", "r"); if(mountsFile == NULL) - return "fopen(\"/proc/mounts\", \"r\") == NULL"; + return "setmntent(\"/proc/mounts\", \"r\") == NULL"; - FF_LIST_AUTO_DESTROY devices = ffListCreate(sizeof(FFstrbuf)); + struct mntent* device; - char* line = NULL; - size_t len = 0; - - while(getline(&line, &len, mountsFile) != EOF) + while((device = getmntent(mountsFile))) { - //Format of the file: " ..." (Same as fstab) - char* currentPos = line; - - //detect device - FFstrbuf* device = ffListAdd(&devices); - ffStrbufInit(device); - appendNextEntry(device, ¤tPos); - if(!isPhysicalDevice(device)) - { - ffStrbufDestroy(device); - devices.length--; continue; - } //We have a valid device, add it to the list FFDisk* disk = ffListAdd(disks); + disk->type = FF_DISK_VOLUME_TYPE_NONE; + disk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + + //detect mountFrom + ffStrbufInitS(&disk->mountFrom, device->mnt_fsname); //detect mountpoint - ffStrbufInit(&disk->mountpoint); - appendNextEntry(&disk->mountpoint, ¤tPos); + ffStrbufInitS(&disk->mountpoint, device->mnt_dir); //detect filesystem - ffStrbufInit(&disk->filesystem); - appendNextEntry(&disk->filesystem, ¤tPos); + ffStrbufInitS(&disk->filesystem, device->mnt_type); //detect name ffStrbufInit(&disk->name); - detectName(disk, device); + detectName(disk); // Also detects external devices //detect type - detectType(&devices, disk, currentPos); + detectType(disks, disk); //Detects stats detectStats(disk); } - if(line != NULL) - free(line); - - FF_LIST_FOR_EACH(FFstrbuf, device, devices) - ffStrbufDestroy(device); - - fclose(mountsFile); + endmntent(mountsFile); return NULL; } diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c index 5b3b39232..dadb7d3ae 100644 --- a/src/detection/disk/disk_windows.c +++ b/src/detection/disk/disk_windows.c @@ -1,7 +1,9 @@ #include "disk.h" +#include "common/io/io.h" #include "util/windows/unicode.h" #include +#include #include const char* ffDetectDisksImpl(FFlist* disks) @@ -25,6 +27,41 @@ const char* ffDetectDisksImpl(FFlist* disks) FFDisk* disk = ffListAdd(disks); ffStrbufInitWS(&disk->mountpoint, mountpoint); + wchar_t volumeName[64]; + + disk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + if(mountpoint[1] == ':') + { + memcpy(volumeName, L"\\\\.\\ :", sizeof(L"\\\\.\\ :")); + volumeName[4] = mountpoint[0]; + FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(volumeName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) + { + DEVICE_SEEK_PENALTY_DESCRIPTOR dspd = {}; + if(DeviceIoControl( + handle, + IOCTL_STORAGE_QUERY_PROPERTY, + &(STORAGE_PROPERTY_QUERY) { + .PropertyId = StorageDeviceSeekPenaltyProperty, + .QueryType = PropertyStandardQuery, + }, + sizeof(STORAGE_PROPERTY_QUERY), + &dspd, + sizeof(dspd), + NULL, + NULL + )) + disk->physicalType = dspd.IncursSeekPenalty ? FF_DISK_PHYSICAL_TYPE_HDD : FF_DISK_PHYSICAL_TYPE_SSD; + } + } + + if(GetVolumeNameForVolumeMountPointW(mountpoint, volumeName, sizeof(volumeName) / sizeof(*volumeName))) + { + ffStrbufInitWS(&disk->mountFrom, volumeName); + } + else + ffStrbufInit(&disk->mountFrom); + if(!GetDiskFreeSpaceExW( mountpoint, (PULARGE_INTEGER)&disk->bytesAvailable, diff --git a/src/detection/displayserver/displayserver_android.c b/src/detection/displayserver/displayserver_android.c index af57a0628..d1e342503 100644 --- a/src/detection/displayserver/displayserver_android.c +++ b/src/detection/displayserver/displayserver_android.c @@ -80,9 +80,9 @@ static bool detectWithGetprop(FFDisplayServerResult* ds) ffStrbufContainC(&buffer, ',')) { // 1440,3200,560 => width,height,ppi - uint32_t width = ffStrbufToUInt16(&buffer, 0); + uint32_t width = (uint32_t) ffStrbufToUInt(&buffer, 0); ffStrbufSubstrAfterFirstC(&buffer, ','); - uint32_t height = ffStrbufToUInt16(&buffer, 0); + uint32_t height = (uint32_t) ffStrbufToUInt(&buffer, 0); return ffdsAppendDisplay(ds, width, height, diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index f1bcea4ee..b34c429f2 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -233,6 +233,23 @@ static void getXFCE4(FFDisplayServerResult* result) ffStrbufSetS(&result->dePrettyName, FF_DE_PRETTY_XFCE4); ffParsePropFileData("gtk-doc/html/libxfce4ui/index.html", "

Version", &result->deVersion); + #ifdef __FreeBSD__ + if(result->deVersion.length == 0) + { + FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/usr/local/share/licenses/"); + if (dirp) + { + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + if(!ffStrStartsWith(entry->d_name, "xfce-") || !isdigit(entry->d_name[5])) + continue; + ffStrbufAppendS(&result->deVersion, &entry->d_name[5]); + } + } + } + #endif + if(result->deVersion.length == 0 && instance.config.allowSlowOperations) { //This is somewhat slow diff --git a/src/detection/gamepad/gamepad_windows.c b/src/detection/gamepad/gamepad_windows.c index e115782aa..8a5799717 100644 --- a/src/detection/gamepad/gamepad_windows.c +++ b/src/detection/gamepad/gamepad_windows.c @@ -130,7 +130,7 @@ const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) if (hHidFile && HidD_GetProductString(hHidFile, displayName, sizeof(wchar_t) * 126)) ffStrbufSetWS(&device->name, displayName); else - ffStrbufSetF(&device->name, "Unknown gamepad %4X-%4X", rdi.hid.dwVendorId, rdi.hid.dwProductId); + ffStrbufSetF(&device->name, "Unknown gamepad %4X-%4X", (unsigned) rdi.hid.dwVendorId, (unsigned) rdi.hid.dwProductId); } } diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 749072f46..72a653008 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -311,6 +311,12 @@ static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) { + #ifdef FF_HAVE_DIRECTX_HEADERS + const char* ffGPUDetectByDirectX(const FFGPUOptions* options, FFlist* gpus); + if (!ffGPUDetectByDirectX(options, gpus)) + return NULL; + #endif + #ifdef FF_HAVE_LIBPCI return pciDetectGPUs(options, gpus); #else diff --git a/src/detection/gpu/gpu_wsl.cpp b/src/detection/gpu/gpu_wsl.cpp new file mode 100644 index 000000000..fa77a4be0 --- /dev/null +++ b/src/detection/gpu/gpu_wsl.cpp @@ -0,0 +1,102 @@ +#ifdef FF_HAVE_DIRECTX_HEADERS + +#define INITGUID +extern "C" { +#include "common/library.h" +#include "detection/gpu/gpu.h" +} + +#include +#include +#include +#include +#include +#include + +template +struct on_scope_exit { + on_scope_exit(Fn &&fn): _fn(std::move(fn)) {} + ~on_scope_exit() { this->_fn(); } + +private: + Fn _fn; +}; + +extern "C" +const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus) +{ + FF_LIBRARY_LOAD(libdxcore, nullptr, "dlopen libdxcore.so failed. Make sure WSLg is enabled", "/usr/lib/wsl/lib/libdxcore" FF_LIBRARY_EXTENSION, 4) + // DXCoreCreateAdapterFactory is a reloaded function, so we can't use FF_LIBRARY_LOAD_SYMBOL_MESSAGE here + typedef HRESULT (*DXCoreCreateAdapterFactory_t)(REFIID riid, void** ppvFactory); + auto ffDXCoreCreateAdapterFactory = (DXCoreCreateAdapterFactory_t) dlsym(libdxcore, "DXCoreCreateAdapterFactory"); + if(ffDXCoreCreateAdapterFactory == nullptr) return "dlsym DXCoreCreateAdapterFactory failed"; + + IDXCoreAdapterFactory *factory = nullptr; + + if (FAILED(ffDXCoreCreateAdapterFactory(IID_PPV_ARGS(&factory)))) + return "DXCoreCreateAdapterFactory(IID_PPV_ARGS(&factory)) failed"; + on_scope_exit destroyFactory([&] { factory->Release(); }); + + IDXCoreAdapterList *list = NULL; + if (FAILED(factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS, &list))) + return "factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS, &list) failed"; + on_scope_exit destroyList([&] { list->Release(); }); + + for (uint32_t i = 0, count = list->GetAdapterCount(); i < count; i++) + { + IDXCoreAdapter *adapter = nullptr; + if (FAILED(list->GetAdapter(i, &adapter))) + continue; + on_scope_exit destroyAdapter([&] { adapter->Release(); }); + + // https://learn.microsoft.com/en-us/windows/win32/api/dxcore_interface/ne-dxcore_interface-dxcoreadapterproperty + { + bool value = 0; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::IsHardware, sizeof(value), &value)) && !value) + continue; + } + + char desc[256]; + if (FAILED(adapter->GetProperty(DXCoreAdapterProperty::DriverDescription, sizeof(desc), desc))) + continue; + + FFGPUResult* gpu = (FFGPUResult*) ffListAdd(gpus); + ffStrbufInitS(&gpu->name, desc); + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->temperature = FF_GPU_TEMP_UNSET; + + ffStrbufInit(&gpu->driver); + uint64_t value = 0; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DriverVersion, sizeof(value), &value))) + { + ffStrbufSetF(&gpu->driver, "%" PRIu64 ".%" PRIu64 ".%" PRIu64 ".%" PRIu64, + (value >> 48) & 0xFFFF, + (value >> 32) & 0xFFFF, + (value >> 16) & 0xFFFF, + (value >> 0) & 0xFFFF); + } + + gpu->dedicated.used = gpu->shared.used = gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DedicatedAdapterMemory, sizeof(value), &value))) + gpu->dedicated.total = value; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::SharedSystemMemory, sizeof(value), &value))) + gpu->shared.total = value; + + gpu->type = FF_GPU_TYPE_UNKNOWN; + bool integrated; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::IsIntegrated, sizeof(integrated), &integrated))) + gpu->type = integrated ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; + + ffStrbufInit(&gpu->vendor); + DXCoreHardwareID hardwareId; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::HardwareID, sizeof(hardwareId), &hardwareId))) + { + const char* vendorStr = ffGetGPUVendorString((unsigned) hardwareId.vendorID); + ffStrbufAppendS(&gpu->vendor, vendorStr); + } + } + + return NULL; +} + +#endif diff --git a/src/detection/localip/localip.h b/src/detection/localip/localip.h index 6fea95a79..8d0fe84bf 100644 --- a/src/detection/localip/localip.h +++ b/src/detection/localip/localip.h @@ -12,10 +12,6 @@ typedef struct FFLocalIpResult FFstrbuf ipv6; FFstrbuf mac; bool defaultRoute; - - #ifdef _WIN32 - uint32_t ifIndex; - #endif } FFLocalIpResult; const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results); diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c index e70234735..1f319ad34 100644 --- a/src/detection/localip/localip_linux.c +++ b/src/detection/localip/localip_linux.c @@ -1,5 +1,7 @@ #include "localip.h" #include "common/io/io.h" +#include "common/netif/netif.h" +#include "util/stringUtils.h" #include #include @@ -11,124 +13,11 @@ #if defined(__FreeBSD__) || defined(__APPLE__) #include -#include -#include #else #include #endif -#if defined(__linux__) -static bool getDefaultRoute(char iface[IF_NAMESIZE + 1]) -{ - FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/route", "r"); - if (!netRoute) return false; - - // skip first line - flockfile(netRoute); - while (getc_unlocked(netRoute) != '\n'); - funlockfile(netRoute); - unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, - - static_assert(IF_NAMESIZE >= 16, "IF_NAMESIZE is too small"); - while (fscanf(netRoute, "%16s%llx%*[^\n]", iface, &destination) == 2) - { - if (destination == 0) - return true; - } - return false; -} -#elif defined(__FreeBSD__) || defined(__APPLE__) - -#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) - -#if defined(__APPLE__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) -#elif defined(__NetBSD__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) -#elif defined(__FreeBSD__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) -#elif defined(__OpenBSD__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) -#else -# error unknown platform -#endif - -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 <sa_len) + (char *)sa); - } - } - return NULL; -} - -static bool getDefaultRoute(char iface[IF_NAMESIZE + 1]) -{ - //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) - 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)); - } - - int pid = getpid(); - - struct { - struct rt_msghdr hdr; - struct sockaddr_in dst; - uint8_t data[512]; - } rtmsg = { - .hdr = { - .rtm_type = RTM_GET, - .rtm_flags = RTF_UP | RTF_GATEWAY, - .rtm_version = RTM_VERSION, - .rtm_addrs = RTA_DST | RTA_IFP, - .rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst), - .rtm_pid = pid, - .rtm_seq = 1, - }, - .dst = { - .sin_family = AF_INET, - .sin_len = sizeof(rtmsg.dst), - }, - }; - - if (write(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen) != rtmsg.hdr.rtm_msglen) - return false; - - while (read(pfRoute, &rtmsg, sizeof(rtmsg)) > 0) - { - if (rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid) - { - struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP); - if (sdl) - { - assert(sdl->sdl_nlen <= IF_NAMESIZE); - memcpy(iface, sdl->sdl_data, sdl->sdl_nlen); - iface[sdl->sdl_nlen] = 0; - return true; - } - return false; - } - } - return false; -} -#endif - -static void addNewIp(FFlist* list, const char* name, const char* addr, int type) +static void addNewIp(FFlist* list, const char* name, const char* addr, int type, bool defaultRoute) { FFLocalIpResult* ip = NULL; @@ -145,7 +34,7 @@ static void addNewIp(FFlist* list, const char* name, const char* addr, int type) ffStrbufInit(&ip->ipv4); ffStrbufInit(&ip->ipv6); ffStrbufInit(&ip->mac); - ip->defaultRoute = false; + ip->defaultRoute = defaultRoute; } switch (type) @@ -168,11 +57,17 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) if(getifaddrs(&ifAddrStruct) < 0) return "getifaddrs(&ifAddrStruct) failed"; + const char* defaultRouteIfName = ffNetifGetDefaultRouteIfName(); + for (struct ifaddrs* ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) { if (!ifa->ifa_addr || !(ifa->ifa_flags & IFF_RUNNING)) continue; + bool isDefaultRoute = ffStrEquals(defaultRouteIfName, ifa->ifa_name); + if (options->defaultRouteOnly && !isDefaultRoute) + continue; + if ((ifa->ifa_flags & IFF_LOOPBACK) && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) continue; @@ -187,7 +82,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) struct sockaddr_in* ipv4 = (struct sockaddr_in*) ifa->ifa_addr; char addressBuffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); - addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET); + addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET, isDefaultRoute); } else if (ifa->ifa_addr->sa_family == AF_INET6) { @@ -197,7 +92,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) struct sockaddr_in6* ipv6 = (struct sockaddr_in6 *)ifa->ifa_addr; char addressBuffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN); - addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6); + addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6, isDefaultRoute); } #if defined(__FreeBSD__) || defined(__APPLE__) else if (ifa->ifa_addr->sa_family == AF_LINK) @@ -209,7 +104,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = (uint8_t*) LLADDR((struct sockaddr_dl *)ifa->ifa_addr); snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute); } #else else if (ifa->ifa_addr->sa_family == AF_PACKET) @@ -221,18 +116,12 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = ((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr; snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute); } #endif } if (ifAddrStruct) freeifaddrs(ifAddrStruct); - char iface[16 /*IF_NAMESIZE*/ + 1]; - if (getDefaultRoute(iface)) - { - FF_LIST_FOR_EACH(FFLocalIpResult, ip, *results) - ip->defaultRoute = ffStrbufEqualS(&ip->name, iface); - } return NULL; } diff --git a/src/detection/localip/localip_windows.c b/src/detection/localip/localip_windows.c index c986120ba..bb39b4325 100644 --- a/src/detection/localip/localip_windows.c +++ b/src/detection/localip/localip_windows.c @@ -1,39 +1,12 @@ -#include #include #include +#include "common/netif/netif.h" #include "util/mallocHelper.h" #include "util/windows/unicode.h" #include "localip.h" -static void setDefaultRoute(FFlist* ips) -{ - ULONG size = 0; - if (GetIpForwardTable(NULL, &size, TRUE) != ERROR_INSUFFICIENT_BUFFER) - return; - - FF_AUTO_FREE MIB_IPFORWARDTABLE* pIpForwardTable = (MIB_IPFORWARDTABLE*) malloc(size); - if (GetIpForwardTable(pIpForwardTable, &size, TRUE) != ERROR_SUCCESS) - return; - - for (uint32_t i = 0; i < pIpForwardTable->dwNumEntries; ++i) - { - MIB_IPFORWARDROW* ipForwardRow = &pIpForwardTable->table[i]; - if (ipForwardRow->dwForwardDest == 0 && ipForwardRow->dwForwardMask == 0) - { - FF_LIST_FOR_EACH(FFLocalIpResult, ip, *ips) - { - if (ip->ifIndex == ipForwardRow->dwForwardIfIndex) - { - ip->defaultRoute = true; - break; - } - } - } - } -} - -static void addNewIp(FFlist* list, const char* name, const char* value, int type, bool newIp, uint32_t ifIndex) +static void addNewIp(FFlist* list, const char* name, const char* value, int type, bool newIp, bool defaultRoute) { FFLocalIpResult* ip = NULL; @@ -44,8 +17,7 @@ static void addNewIp(FFlist* list, const char* name, const char* value, int type ffStrbufInit(&ip->ipv4); ffStrbufInit(&ip->ipv6); ffStrbufInit(&ip->mac); - ip->defaultRoute = false; - ip->ifIndex = ifIndex; + ip->defaultRoute = defaultRoute; } else { @@ -70,14 +42,16 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) { IP_ADAPTER_ADDRESSES* FF_AUTO_FREE adapter_addresses = NULL; - // Start with a 16 KB buffer and resize if needed - - // multiple attempts in case interfaces change while + // Multiple attempts in case interfaces change while // we are in the middle of querying them. - DWORD adapter_addresses_buffer_size = 16 * 1024; - for (int attempts = 0; attempts != 3; ++attempts) + DWORD adapter_addresses_buffer_size = 0; + for (int attempts = 0;; ++attempts) { - adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size); - assert(adapter_addresses); + if (adapter_addresses_buffer_size) + { + adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size); + assert(adapter_addresses); + } DWORD error = GetAdaptersAddresses( options->showType & FF_LOCALIP_TYPE_IPV4_BIT @@ -90,15 +64,21 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) if (error == ERROR_SUCCESS) break; - else if (ERROR_BUFFER_OVERFLOW == error) + else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) continue; else return "GetAdaptersAddresses() failed"; } + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteIfIndex(); + // Iterate through all of the adapters for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) { + bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex; + if (options->defaultRouteOnly && !isDefaultRoute) + continue; + bool isLoop = adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK; if (isLoop && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) continue; @@ -116,7 +96,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = adapter->PhysicalAddress; snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, name, addressBuffer, -1, newIp, adapter->IfIndex); + addNewIp(results, name, addressBuffer, -1, newIp, isDefaultRoute); newIp = false; } @@ -127,7 +107,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr; char addressBuffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); - addNewIp(results, name, addressBuffer, AF_INET, newIp, adapter->IfIndex); + addNewIp(results, name, addressBuffer, AF_INET, newIp, isDefaultRoute); newIp = false; } else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) @@ -135,13 +115,11 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr; char addressBuffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN); - addNewIp(results, name, addressBuffer, AF_INET6, newIp, adapter->IfIndex); + addNewIp(results, name, addressBuffer, AF_INET6, newIp, isDefaultRoute); newIp = false; } } } - setDefaultRoute(results); - return NULL; } diff --git a/src/detection/netio/netio.c b/src/detection/netio/netio.c new file mode 100644 index 000000000..5a07af8e2 --- /dev/null +++ b/src/detection/netio/netio.c @@ -0,0 +1,67 @@ +#include "netio.h" + +#include "common/time.h" + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options); + +static FFlist ioCounters1; +static uint64_t time1; + +void ffPrepareNetIO(FFNetIOOptions* options) +{ + ffListInit(&ioCounters1, sizeof(FFNetIOResult)); + ffNetIOGetIoCounters(&ioCounters1, options); + time1 = ffTimeGetNow(); +} + +const char* ffDetectNetIO(FFlist* result, FFNetIOOptions* options) +{ + const char* error = NULL; + if (time1 == 0) + { + ffListInit(&ioCounters1, sizeof(FFNetIOResult)); + error = ffNetIOGetIoCounters(&ioCounters1, options); + if (error) + return error; + time1 = ffTimeGetNow(); + } + + if (ioCounters1.length == 0) + return "No network interfaces found"; + + uint64_t time2 = ffTimeGetNow(); + while (time2 - time1 < 1000) + { + ffTimeSleep((uint32_t) (1000 - (time2 - time1))); + time2 = ffTimeGetNow(); + } + + error = ffNetIOGetIoCounters(result, options); + if (error) + return error; + + if (result->length != ioCounters1.length) + return "Different number of network interfaces. Network change?"; + + for (uint32_t i = 0; i < result->length; ++i) + { + FFNetIOResult* icPrev = (FFNetIOResult*)ffListGet(&ioCounters1, i); + FFNetIOResult* icCurr = (FFNetIOResult*)ffListGet(result, i); + if (!ffStrbufEqual(&icPrev->name, &icCurr->name)) + return "Network interface name changed"; + + static_assert(sizeof(FFNetIOResult) - offsetof(FFNetIOResult, txBytes) == sizeof(uint64_t) * 8, "Unexpected struct FFNetIOResult layout"); + for (size_t off = offsetof(FFNetIOResult, txBytes); off < sizeof(FFNetIOResult); off += sizeof(uint64_t)) + { + uint64_t* prevValue = (uint64_t*) ((uint8_t*) icPrev + off); + uint64_t* currValue = (uint64_t*) ((uint8_t*) icCurr + off); + uint64_t temp = *currValue; + *currValue -= *prevValue; + *currValue /= (time2 - time1) / 1000 /* seconds */; + *prevValue = temp; + } + } + time1 = time2; + + return NULL; +} diff --git a/src/detection/netio/netio.h b/src/detection/netio/netio.h new file mode 100644 index 000000000..295c4478e --- /dev/null +++ b/src/detection/netio/netio.h @@ -0,0 +1,17 @@ +#include "fastfetch.h" + +typedef struct FFNetIOResult +{ + FFstrbuf name; + bool defaultRoute; + uint64_t txBytes; + uint64_t rxBytes; + uint64_t txPackets; + uint64_t rxPackets; + uint64_t rxErrors; + uint64_t txErrors; + uint64_t rxDrops; + uint64_t txDrops; +} FFNetIOResult; + +const char* ffDetectNetIO(FFlist* result, FFNetIOOptions* options); diff --git a/src/detection/netio/netio_bsd.c b/src/detection/netio/netio_bsd.c new file mode 100644 index 000000000..6bf9a8ce6 --- /dev/null +++ b/src/detection/netio/netio_bsd.c @@ -0,0 +1,58 @@ +#include "netio.h" + +#include "common/io/io.h" +#include "common/netif/netif.h" +#include "util/stringUtils.h" +#include "util/mallocHelper.h" + +#include +#include +#include +#include +#include +#include + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) +{ + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteIfIndex(); + + size_t bufSize = 0; + if (sysctl((int[]) { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, (options->defaultRouteOnly ? (int) defaultRouteIfIndex : 0) }, 6, NULL, &bufSize, 0, 0) < 0) + return "sysctl({ CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, ifIndex }, 6, NULL, &bufSize, 0, 0) failed"; + + FF_AUTO_FREE struct if_msghdr* buf = (struct if_msghdr*) malloc(bufSize); + if (sysctl((int[]) { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, (options->defaultRouteOnly ? (int) defaultRouteIfIndex : 0) }, 6, buf, &bufSize, 0, 0) < 0) + return "sysctl({ CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, ifIndex }, 6, buf, &bufSize, 0, 0) failed"; + + for (struct if_msghdr* ifm = buf; + ifm < (struct if_msghdr*) ((uint8_t*) buf + bufSize); + ifm = (struct if_msghdr*) ((uint8_t*) ifm + ifm->ifm_msglen)) + { + if (ifm->ifm_type != RTM_IFINFO || !(ifm->ifm_flags & IFF_RUNNING) || (ifm->ifm_flags & IFF_NOARP)) continue; + + struct sockaddr_dl* sdl = (struct sockaddr_dl*) (ifm + 1); + assert(sdl->sdl_family == AF_LINK); + if (sdl->sdl_type != IFT_ETHER && !(ifm->ifm_flags & IFF_LOOPBACK)) continue; + + sdl->sdl_data[sdl->sdl_nlen] = 0; + + if (options->namePrefix.length && strncmp(sdl->sdl_data, options->namePrefix.chars, options->namePrefix.length) != 0) + continue; + + FFNetIOResult* counters = (FFNetIOResult*) ffListAdd(result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateNS(sdl->sdl_nlen, sdl->sdl_data), + .txBytes = ifm->ifm_data.ifi_obytes, + .rxBytes = ifm->ifm_data.ifi_ibytes, + .txPackets = ifm->ifm_data.ifi_opackets, + .rxPackets = ifm->ifm_data.ifi_ipackets, + .txErrors = ifm->ifm_data.ifi_oerrors, + .rxErrors = ifm->ifm_data.ifi_ierrors, + .txDrops = 0, // unsupported + .rxDrops = ifm->ifm_data.ifi_iqdrops, + .defaultRoute = sdl->sdl_index == defaultRouteIfIndex, + }; + } + + return NULL; +} diff --git a/src/detection/netio/netio_linux.c b/src/detection/netio/netio_linux.c new file mode 100644 index 000000000..2b1f24478 --- /dev/null +++ b/src/detection/netio/netio_linux.c @@ -0,0 +1,97 @@ +#include "netio.h" + +#include "common/io/io.h" +#include "common/netif/netif.h" +#include "util/stringUtils.h" + +#include + +static void getData(FFstrbuf* buffer, const char* ifName, bool isDefaultRoute, FFstrbuf* path, FFlist* result) +{ + ffStrbufSetF(path, "/sys/class/net/%s/operstate", ifName); + if(!ffReadFileBuffer(path->chars, buffer) || !ffStrbufEqualS(buffer, "up")) + return; + + FFNetIOResult* counters = (FFNetIOResult*) ffListAdd(result); + ffStrbufInitS(&counters->name, ifName); + counters->defaultRoute = isDefaultRoute; + + ffStrbufSetF(path, "/sys/class/net/%s/statistics/", ifName); + uint32_t statLen = path->length; + + ffStrbufAppendS(path, "rx_bytes"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxBytes = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_bytes"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txBytes = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "rx_packets"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxPackets = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_packets"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txPackets = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "rx_errors"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxErrors = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_errors"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txErrors = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "rx_dropped"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxDrops = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_dropped"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txDrops = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); +} + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) +{ + FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/net"); + if (!dirp) return "opendir(\"/sys/class/net\") == NULL"; + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateA(64); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + + const char* defaultRouteIfName = ffNetifGetDefaultRouteIfName(); + + if (options->defaultRouteOnly) + { + if (options->namePrefix.length && strncmp(defaultRouteIfName, options->namePrefix.chars, options->namePrefix.length) != 0) + return NULL; + + getData(&buffer, defaultRouteIfName, true, &path, result); + } + else + { + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + const char* ifName = entry->d_name; + if(ifName[0] == '.') + continue; + + if (options->namePrefix.length && strncmp(ifName, options->namePrefix.chars, options->namePrefix.length) != 0) + continue; + + getData(&buffer, ifName, ffStrEquals(ifName, defaultRouteIfName), &path, result); + } + } + + return NULL; +} diff --git a/src/detection/netio/netio_windows.c b/src/detection/netio/netio_windows.c new file mode 100644 index 000000000..4291df3d4 --- /dev/null +++ b/src/detection/netio/netio_windows.c @@ -0,0 +1,74 @@ +#include "netio.h" + +#include "common/netif/netif.h" +#include "util/mallocHelper.h" +#include "util/windows/unicode.h" + +#include +#include + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) +{ + IP_ADAPTER_ADDRESSES* FF_AUTO_FREE adapter_addresses = NULL; + + // Multiple attempts in case interfaces change while + // we are in the middle of querying them. + DWORD adapter_addresses_buffer_size = 0; + for (int attempts = 0;; ++attempts) + { + if (adapter_addresses_buffer_size) + { + adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size); + assert(adapter_addresses); + } + + DWORD error = GetAdaptersAddresses( + AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, + NULL, + adapter_addresses, + &adapter_addresses_buffer_size); + + if (error == ERROR_SUCCESS) + break; + else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) + continue; + else + return "GetAdaptersAddresses() failed"; + } + + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteIfIndex(); + + // Iterate through all of the adapters + for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) + { + bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex; + if (options->defaultRouteOnly && !isDefaultRoute) + continue; + + char name[128]; + WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, name, sizeof(name), NULL, NULL); + if (options->namePrefix.length && strncmp(name, options->namePrefix.chars, options->namePrefix.length) != 0) + continue; + + MIB_IF_ROW2 ifRow = { .InterfaceIndex = adapter->IfIndex }; + if (GetIfEntry2(&ifRow) == NO_ERROR) + { + FFNetIOResult* counters = (FFNetIOResult*) ffListAdd(result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateS(name), + .txBytes = ifRow.OutOctets, + .rxBytes = ifRow.InOctets, + .txPackets = (ifRow.OutUcastPkts + ifRow.OutNUcastPkts), + .rxPackets = (ifRow.InUcastPkts + ifRow.InNUcastPkts), + .rxErrors = ifRow.InErrors, + .txErrors = ifRow.OutErrors, + .rxDrops = ifRow.InDiscards, + .txDrops = ifRow.OutDiscards, + .defaultRoute = isDefaultRoute, + }; + } + } + + return NULL; +} diff --git a/src/detection/opencl/opencl.c b/src/detection/opencl/opencl.c index d8cb4a66b..0526de2e2 100644 --- a/src/detection/opencl/opencl.c +++ b/src/detection/opencl/opencl.c @@ -49,14 +49,17 @@ static const char* openCLHandleData(OpenCLData* data, FFOpenCLResult* result) if(ffStrStartsWithIgnCase(version, "OpenCL ")) versionPretty = version + strlen("OpenCL "); ffStrbufSetS(&result->version, versionPretty); + ffStrbufTrim(&result->version, ' '); ffStrbufEnsureFree(&result->device, 128); data->ffclGetDeviceInfo(deviceID, CL_DEVICE_NAME, result->device.allocated, result->device.chars, NULL); ffStrbufRecalculateLength(&result->device); + ffStrbufTrim(&result->device, ' '); ffStrbufEnsureFree(&result->vendor, 32); data->ffclGetDeviceInfo(deviceID, CL_DEVICE_VENDOR, result->vendor.allocated, result->vendor.chars, NULL); ffStrbufRecalculateLength(&result->vendor); + ffStrbufTrim(&result->vendor, ' '); return NULL; } diff --git a/src/detection/opengl/opengl_linux.c b/src/detection/opengl/opengl_linux.c index 8da49361b..8ea44355d 100644 --- a/src/detection/opengl/opengl_linux.c +++ b/src/detection/opengl/opengl_linux.c @@ -363,7 +363,7 @@ const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) #else - FF_UNUSED(result); + FF_UNUSED(options, result); return "Fastfetch was built without gl support."; #endif //FF_HAVE_GL diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index 338a1fcfb..1b99c174c 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -1,6 +1,7 @@ #include "os.h" #include "common/properties.h" #include "common/parsing.h" +#include "common/io/io.h" #include "util/stringUtils.h" #include @@ -121,6 +122,15 @@ static void getUbuntuFlavour(FFOSResult* result) } } +static void getDebianVersion(FFOSResult* result) +{ + FF_STRBUF_AUTO_DESTROY debianVersion = ffStrbufCreate(); + ffAppendFileBuffer("/etc/debian_version", &debianVersion); + if (!debianVersion.length) return; + ffStrbufSet(&result->version, &debianVersion); + ffStrbufSet(&result->versionID, &debianVersion); +} + static void detectOS(FFOSResult* os) { if(instance.config.osFile.length > 0) @@ -169,6 +179,8 @@ void ffDetectOSImpl(FFOSResult* os) detectOS(os); - if(ffStrbufIgnCaseCompS(&os->id, "ubuntu") == 0) + if(ffStrbufIgnCaseEqualS(&os->id, "ubuntu")) getUbuntuFlavour(os); + else if(ffStrbufIgnCaseEqualS(&os->id, "debian")) + getDebianVersion(os); } diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index 77f8e6812..ae36b5078 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -27,6 +27,7 @@ typedef struct FFPackagesResult uint32_t rpm; uint32_t scoop; uint32_t snap; + uint32_t winget; uint32_t xbps; uint32_t all; //Make sure this goes last diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 7dd4061b4..1aa2c2433 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -197,6 +197,9 @@ static uint32_t getSnap(FFstrbuf* baseDir) { uint32_t result = getNumElements(baseDir, "/snap", DT_DIR); + if (result == 0) + result = getNumElements(baseDir, "/var/lib/snapd/snap", DT_DIR); + //Accounting for the /snap/bin folder return result > 0 ? result - 1 : 0; } @@ -262,7 +265,7 @@ static uint32_t getRpmFromLibrpm(void) static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts) { packageCounts->apk += getNumStrings(baseDir, "/lib/apk/db/installed", "C:Q"); - packageCounts->dpkg += getNumStrings(baseDir, "/var/lib/dpkg/status", "Status: "); + packageCounts->dpkg += getNumStrings(baseDir, "/var/lib/dpkg/status", "Status: install ok installed"); packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE"); packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", DT_DIR); packageCounts->flatpakSystem += getFlatpak(baseDir, "/var/lib/flatpak"); diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index 5b5343f79..bfd6a4c79 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -1,4 +1,5 @@ #include "packages.h" +#include "common/processing.h" #include "util/stringUtils.h" #include @@ -62,7 +63,7 @@ static void detectChoco(FF_MAYBE_UNUSED FFPackagesResult* result) result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, "choco"); } -static void detectPacman(FF_MAYBE_UNUSED FFPackagesResult* result) +static void detectPacman(FFPackagesResult* result) { const char* msystemPrefix = getenv("MSYSTEM_PREFIX"); if(!msystemPrefix) @@ -75,9 +76,40 @@ static void detectPacman(FF_MAYBE_UNUSED FFPackagesResult* result) result->pacman = getNumElements(pacmanPath, FILE_ATTRIBUTE_DIRECTORY, NULL); } +static void detectWinget(FFPackagesResult* result) +{ + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffProcessAppendStdOut(&buffer, (char* []) { + "winget.exe", + "list", + "--disable-interactivity", + NULL, + })) + return; + + uint32_t index = ffStrbufFirstIndexS(&buffer, "--\r\n"); // Ignore garbage and table headers + if (index == buffer.length) + return; + + uint32_t count = 0; + for ( + index += strlen("--\r\n"); + (index = ffStrbufNextIndexC(&buffer, index, '\n')) < buffer.length; + ++index + ) + ++count; + + if (buffer.chars[buffer.length - 1] != '\n') // count last line + ++count; + + result->winget = count; +} + void ffDetectPackagesImpl(FFPackagesResult* result) { detectScoop(result); detectChoco(result); detectPacman(result); + if (instance.config.allowSlowOperations) + detectWinget(result); } diff --git a/src/detection/poweradapter/poweradapter.h b/src/detection/poweradapter/poweradapter.h index 9abdd48ec..0c60d4d9f 100644 --- a/src/detection/poweradapter/poweradapter.h +++ b/src/detection/poweradapter/poweradapter.h @@ -5,14 +5,14 @@ #include "fastfetch.h" -typedef struct PowerAdapterResult +typedef struct FFPowerAdapterResult { FFstrbuf description; FFstrbuf name; FFstrbuf modelName; FFstrbuf manufacturer; int watts; -} PowerAdapterResult; +} FFPowerAdapterResult; const char* ffDetectPowerAdapterImpl(FFlist* results); diff --git a/src/detection/poweradapter/poweradapter_apple.c b/src/detection/poweradapter/poweradapter_apple.c index de3025a40..06d8a8df6 100644 --- a/src/detection/poweradapter/poweradapter_apple.c +++ b/src/detection/poweradapter/poweradapter_apple.c @@ -24,7 +24,7 @@ const char* ffDetectPowerAdapterImpl(FFlist* results) continue; } - PowerAdapterResult* adapter = ffListAdd(results); + FFPowerAdapterResult* adapter = ffListAdd(results); ffStrbufInit(&adapter->name); ffStrbufInit(&adapter->description); diff --git a/src/detection/processes/processes_windows.c b/src/detection/processes/processes_windows.c index dc784c5db..fb29cd210 100644 --- a/src/detection/processes/processes_windows.c +++ b/src/detection/processes/processes_windows.c @@ -6,18 +6,26 @@ const char* ffDetectProcesses(uint32_t* result) { + SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = NULL; + + // Multiple attempts in case processes change while + // we are in the middle of querying them. ULONG size = 0; - if(NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH) - return "NtQuerySystemInformation(SystemProcessInformation, NULL) failed"; - - size += sizeof(SystemProcessInformation) * 5; //What if new processes are created during two syscalls? - - SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = (SYSTEM_PROCESS_INFORMATION*)malloc(size); - if(!pstart) - return "malloc(size) failed"; - - if(!NT_SUCCESS(NtQuerySystemInformation(SystemProcessInformation, pstart, size, NULL))) - return "NtQuerySystemInformation(SystemProcessInformation, pstart) failed"; + for (int attempts = 0;; ++attempts) + { + if (size) + { + pstart = (SYSTEM_PROCESS_INFORMATION*)realloc(pstart, size); + assert(pstart); + } + NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, pstart, size, &size); + if(NT_SUCCESS(status)) + break; + else if(status == STATUS_INFO_LENGTH_MISMATCH && attempts < 4) + size += sizeof(SYSTEM_PROCESS_INFORMATION) * 5; + else + return "NtQuerySystemInformation(SystemProcessInformation) failed"; + } *result = 1; //Init with 1 because we test for ptr->NextEntryOffset for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ptr->NextEntryOffset; ptr = (SYSTEM_PROCESS_INFORMATION*)((uint8_t*)ptr + ptr->NextEntryOffset)) diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c new file mode 100644 index 000000000..4c1cdc42f --- /dev/null +++ b/src/detection/publicip/publicip.c @@ -0,0 +1,73 @@ +#include "publicip.h" +#include "common/networking.h" + +static FFNetworkingState state; +static int status = -1; + +void ffPreparePublicIp(FFPublicIpOptions* options) +{ + if (status != -1) + { + fputs("Error: this module can only be used once due to internal limitations\n", stderr); + exit(1); + } + + if (options->url.length == 0) + status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); + else + { + FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); + ffStrbufSubstrAfterFirstS(&host, "://"); + uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/'); + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + if(pathStartIndex != host.length) + { + ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex)); + host.length = pathStartIndex; + host.chars[pathStartIndex] = '\0'; + } + + status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); + } +} + +static inline void wrapYyjsonFree(yyjson_doc** doc) +{ + assert(doc); + if (*doc) + yyjson_doc_free(*doc); +} + +const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result) +{ + if (status == -1) + ffPreparePublicIp(options); + + if (status == 0) + return "Failed to connect to an IP detection server"; + + FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096); + bool success = ffNetworkingRecvHttpResponse(&state, &response, options->timeout); + if (success) ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n"); + + if (!success || response.length == 0) + return "Failed to receive the server response"; + + if (options->url.length == 0) + { + yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(response.chars, response.length, 0, NULL, NULL); + if (doc) + { + yyjson_val* root = yyjson_doc_get_root(doc); + ffStrbufAppendS(&result->ip, yyjson_get_str(yyjson_obj_get(root, "ip"))); + ffStrbufDestroy(&result->location); + ffStrbufInitF(&result->location, "%s, %s", yyjson_get_str(yyjson_obj_get(root, "city")), yyjson_get_str(yyjson_obj_get(root, "country"))); + return NULL; + } + } + + ffStrbufDestroy(&result->ip); + ffStrbufInitMove(&result->ip, &response); + return NULL; +} diff --git a/src/detection/publicip/publicip.h b/src/detection/publicip/publicip.h new file mode 100644 index 000000000..1313cc46a --- /dev/null +++ b/src/detection/publicip/publicip.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_publicip_publicip +#define FF_INCLUDED_detection_publicip_publicip + +#include "fastfetch.h" + +typedef struct FFPublicIpResult +{ + FFstrbuf ip; + FFstrbuf location; +} FFPublicIpResult; + +void ffPreparePublicIp(FFPublicIpOptions* options); +const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result); + +#endif diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 8fcb7db6c..1ebca55d9 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -237,7 +237,7 @@ static void detectFromWindowsTeriminal(const FFstrbuf* terminalExe, FFTerminalFo #endif //defined(_WIN32) || defined(__linux__) -FF_MAYBE_UNUSED static bool detectKitty(FFTerminalFontResult* result) +FF_MAYBE_UNUSED static bool detectKitty(const FFstrbuf* exe, FFTerminalFontResult* result) { FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate(); @@ -247,6 +247,23 @@ FF_MAYBE_UNUSED static bool detectKitty(FFTerminalFontResult* result) {"font_size ", &fontSize}, }; + if(instance.config.allowSlowOperations) + { + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); + if(!ffProcessAppendStdOut(&buf, (char* const[]){ + exe->chars, + "+kitten", + "query-terminal", + NULL, + })) + { + ffParsePropLines(buf.chars, "font_family: ", &fontName); + ffParsePropLines(buf.chars, "font_size: ", &fontSize); + ffFontInitValues(&result->font, fontName.chars, fontSize.chars); + return true; + } + } + if(!ffParsePropFileConfigValues("kitty/kitty.conf", 2, fontQuery)) return false; @@ -338,6 +355,35 @@ static bool detectTabby(FFTerminalFontResult* result) return true; } +static bool detectContour(const FFstrbuf* exe, FFTerminalFontResult* result) +{ + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); + if(ffProcessAppendStdOut(&buf, (char* const[]){ + exe->chars, + "font-locator", + NULL + })) + { + ffStrbufAppendS(&result->error, "`contour font-locator` failed"); + return false; + } + + //[error] Missing key .logging.enabled. Using default: false. + //[error] ... + //Matching fonts using : Fontconfig + //Font description : (family=Sarasa Term SC Nerd weight=Regular slant=Roman spacing=Monospace, strict_spacing=yes) + //Number of fonts found : 49 + // path /usr/share/fonts/google-noto/NotoSansMono-Regular.ttf Regular Roman + // path ... + + uint32_t index = ffStrbufFirstIndexS(&buf, "Font description : (family="); + if(index >= buf.length) return false; + index += (uint32_t) strlen("Font description : (family="); + ffStrbufSubstrBefore(&buf, ffStrbufNextIndexS(&buf, index, " weight=")); + ffFontInitCopy(&result->font, buf.chars + index); + return true; +} + void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont); static bool detectTerminalFontCommon(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) @@ -350,12 +396,14 @@ static bool detectTerminalFontCommon(const FFTerminalShellResult* terminalShell, detectWezterm(terminalFont); else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "tabby")) detectTabby(terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "contour")) + detectContour(&terminalShell->terminalExe, terminalFont); #ifndef _WIN32 else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/pts/")) ffStrbufAppendS(&terminalFont->error, "Terminal font detection is not supported on PTS"); else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "kitty")) - detectKitty(terminalFont); + detectKitty(&terminalShell->terminalExe, terminalFont); else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/tty")) detectTTY(terminalFont); #endif diff --git a/src/detection/terminalfont/terminalfont_android.c b/src/detection/terminalfont/terminalfont_android.c index ae79a4517..bced44147 100644 --- a/src/detection/terminalfont/terminalfont_android.c +++ b/src/detection/terminalfont/terminalfont_android.c @@ -55,7 +55,7 @@ exit: void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) { - if(ffStrbufCompS(&terminalShell->terminalProcessName, "Termux") != 0) + if(ffStrbufCompS(&terminalShell->terminalProcessName, "com.termux") != 0) { ffStrbufSetS(&terminalFont->error, "Unsupported terminal"); return; diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 18a9b79c8..cad8c5cbc 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -128,6 +128,13 @@ static bool getShellVersionOksh(FFstrbuf* exe, FFstrbuf* version) return true; } +static bool getShellVersionNushell(FFstrbuf* exe, FFstrbuf* version) +{ + ffStrbufSetS(version, getenv("NU_VERSION")); + if (version->length) return true; + return getExeVersionRaw(exe, version); //0.73.0 +} + #ifdef _WIN32 static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) { @@ -158,7 +165,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) if(strcasecmp(exeName, "csh") == 0 || strcasecmp(exeName, "tcsh") == 0) return getExeVersionGeneral(exe, version); //tcsh 6.24.07 (Astron) 2022-12-21 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec if(strcasecmp(exeName, "nu") == 0) - return getExeVersionRaw(exe, version); //0.73.0 + return getShellVersionNushell(exe, version); if(strcasecmp(exeName, "ksh") == 0) return getShellVersionKsh(exe, version); if(strcasecmp(exeName, "oksh") == 0) @@ -263,6 +270,20 @@ FF_MAYBE_UNUSED static bool getTerminalVersionXterm(FFstrbuf* exe, FFstrbuf* ver return version->length > 0; } +static bool getTerminalVersionContour(FFstrbuf* exe, FFstrbuf* version) +{ + const char* env = getenv("TERMINAL_VERSION_STRING"); + if (env) + { + ffStrbufAppendS(version, env); + return true; + } + if(!getExeVersionRaw(exe, version)) return false; + // Contour Terminal Emulator 0.3.12.262 + ffStrbufSubstrAfterFirstC(version, ' '); + return version->length > 0; +} + #ifdef _WIN32 static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version) @@ -296,7 +317,7 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe { #ifdef __ANDROID__ - if(ffStrbufEqualS(processName, "Termux")) + if(ffStrbufEqualS(processName, "com.termux")) return getTerminalVersionTermux(version); #endif @@ -361,6 +382,9 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe if(ffStrbufStartsWithIgnCaseS(processName, "alacritty")) return getExeVersionGeneral(exe, version); + if(ffStrbufStartsWithIgnCaseS(processName, "contour")) + return getTerminalVersionContour(exe, version); + const char* termProgramVersion = getenv("TERM_PROGRAM_VERSION"); if(termProgramVersion) { diff --git a/src/detection/terminalshell/terminalshell.h b/src/detection/terminalshell/terminalshell.h index c727b5fdf..41e31700f 100644 --- a/src/detection/terminalshell/terminalshell.h +++ b/src/detection/terminalshell/terminalshell.h @@ -20,10 +20,6 @@ typedef struct FFTerminalShellResult const char* terminalExeName; //pointer to a char in terminalExe FFstrbuf terminalVersion; uint32_t terminalPid; - - FFstrbuf userShellExe; - const char* userShellExeName; //pointer to a char in userShellExe - FFstrbuf userShellVersion; } FFTerminalShellResult; const FFTerminalShellResult* ffDetectTerminalShell(); diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 58e29f1f8..957a0b304 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -56,7 +56,7 @@ static void getProcessInformation(pid_t pid, FFstrbuf* processName, FFstrbuf* ex exe->chars, &size, NULL, 0 )) - exe->length = (uint32_t)size; + exe->length = (uint32_t)size - 1; #endif @@ -237,7 +237,7 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) getenv("TERMUX_VERSION") != NULL || getenv("TERMUX_MAIN_PACKAGE_FORMAT") != NULL || getenv("TMUX_TMPDIR") != NULL - )) term = "Termux"; + )) term = "com.termux"; #endif #ifdef __linux__ @@ -269,18 +269,12 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) static void getUserShellFromEnv(FFTerminalShellResult* result) { - ffStrbufSet(&result->userShellExe, &instance.state.platform.userShell); - if(result->userShellExe.length == 0) - return; - - setExeName(&result->userShellExe, &result->userShellExeName); - //If shell detection via processes failed - if(result->shellProcessName.length == 0 && result->userShellExe.length > 0) + if(result->shellProcessName.length == 0 && instance.state.platform.userShell.length > 0) { - ffStrbufAppendS(&result->shellProcessName, result->userShellExeName); - ffStrbufSet(&result->shellExe, &result->userShellExe); + ffStrbufSet(&result->shellExe, &instance.state.platform.userShell); setExeName(&result->shellExe, &result->shellExeName); + ffStrbufAppendS(&result->shellProcessName, result->shellExeName); } } @@ -331,6 +325,8 @@ const FFTerminalShellResult* ffDetectTerminalShell() const uint32_t exePathLen = PROC_PIDPATHINFO_MAXSIZE; #elif defined(MAXPATH) const uint32_t exePathLen = MAXPATH; + #elif defined(PATH_MAX) + const uint32_t exePathLen = PATH_MAX; #else const uint32_t exePathLen = 260; #endif @@ -346,10 +342,6 @@ const FFTerminalShellResult* ffDetectTerminalShell() result.terminalExeName = result.terminalExe.chars; result.terminalPid = 0; - ffStrbufInit(&result.userShellExe); - result.userShellExeName = result.userShellExe.chars; - ffStrbufInit(&result.userShellVersion); - getTerminalShell(&result, getppid()); getTerminalFromEnv(&result); @@ -358,11 +350,6 @@ const FFTerminalShellResult* ffDetectTerminalShell() ffStrbufClear(&result.shellVersion); getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion); - if(strcasecmp(result.shellExeName, result.userShellExeName) != 0) - getShellVersion(&result.userShellExe, result.userShellExeName, &result.userShellVersion); - else - ffStrbufSet(&result.userShellVersion, &result.shellVersion); - if(ffStrbufEqualS(&result.shellProcessName, "pwsh")) ffStrbufInitStatic(&result.shellPrettyName, "PowerShell"); else if(ffStrbufEqualS(&result.shellProcessName, "nu")) @@ -388,7 +375,13 @@ const FFTerminalShellResult* ffDetectTerminalShell() if(ffStrbufEqualS(&result.terminalProcessName, "wezterm-gui")) ffStrbufInitStatic(&result.terminalPrettyName, "WezTerm"); - #if defined(__linux__) || defined(__FreeBSD__) + + #if defined(__ANDROID__) + + else if(ffStrbufEqualS(&result.terminalProcessName, "com.termux")) + ffStrbufInitStatic(&result.terminalPrettyName, "Termux"); + + #elif defined(__linux__) || defined(__FreeBSD__) else if(ffStrbufStartsWithS(&result.terminalProcessName, "gnome-terminal-")) ffStrbufInitStatic(&result.terminalPrettyName, "gnome-terminal"); diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 0bd65e578..ebc062b33 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -379,10 +379,6 @@ const FFTerminalShellResult* ffDetectTerminalShell(void) ffStrbufInit(&result.terminalPrettyName); result.terminalPid = 0; - ffStrbufInit(&result.userShellExe); - result.userShellExeName = ""; - ffStrbufInit(&result.userShellVersion); - uint32_t ppid; if(!getProcessInfo(0, &ppid, NULL, NULL, NULL)) goto exit; diff --git a/src/detection/uptime/uptime.h b/src/detection/uptime/uptime.h index 447b13d5b..105e1bfa5 100644 --- a/src/detection/uptime/uptime.h +++ b/src/detection/uptime/uptime.h @@ -5,6 +5,12 @@ #include "fastfetch.h" -const char* ffDetectUptime(uint64_t* result); +typedef struct FFUptimeResult +{ + uint64_t bootTime; + uint64_t uptime; +} FFUptimeResult; + +const char* ffDetectUptime(FFUptimeResult* result); #endif diff --git a/src/detection/uptime/uptime_bsd.c b/src/detection/uptime/uptime_bsd.c index 4e3a707c6..47202544a 100644 --- a/src/detection/uptime/uptime_bsd.c +++ b/src/detection/uptime/uptime_bsd.c @@ -1,10 +1,10 @@ #include "uptime.h" +#include "common/time.h" -#include #include #include -const char* ffDetectUptime(uint64_t* result) +const char* ffDetectUptime(FFUptimeResult* result) { struct timeval bootTime; size_t bootTimeSize = sizeof(bootTime); @@ -15,7 +15,8 @@ const char* ffDetectUptime(uint64_t* result) ) != 0) return "sysctl({CTL_KERN, KERN_BOOTTIME}) failed"; - *result = (uint64_t) difftime(time(NULL), bootTime.tv_sec); + result->bootTime = (uint64_t) bootTime.tv_sec * 1000 + (uint64_t) bootTime.tv_usec / 1000; + result->uptime = ffTimeGetNow() - result->bootTime; return NULL; } diff --git a/src/detection/uptime/uptime_linux.c b/src/detection/uptime/uptime_linux.c index 0339bc7e6..bfa67215b 100644 --- a/src/detection/uptime/uptime_linux.c +++ b/src/detection/uptime/uptime_linux.c @@ -1,12 +1,15 @@ #include "uptime.h" +#include "common/time.h" -#include - -const char* ffDetectUptime(uint64_t* result) +const char* ffDetectUptime(FFUptimeResult* result) { - struct sysinfo info; - if(sysinfo(&info) != 0) - return "sysinfo() failed"; - *result = (uint64_t) info.uptime; + struct timespec uptime; + if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) + return "clock_gettime(CLOCK_BOOTTIME) failed"; + + result->uptime = (uint64_t) uptime.tv_sec * 1000 + (uint64_t) uptime.tv_nsec / 1000000; + + result->bootTime = ffTimeGetNow() + result->uptime; + return NULL; } diff --git a/src/detection/uptime/uptime_windows.c b/src/detection/uptime/uptime_windows.c index 55053ab24..6978a1163 100644 --- a/src/detection/uptime/uptime_windows.c +++ b/src/detection/uptime/uptime_windows.c @@ -1,9 +1,11 @@ #include "uptime.h" +#include "common/time.h" #include -const char* ffDetectUptime(uint64_t* result) +const char* ffDetectUptime(FFUptimeResult* result) { - *result = GetTickCount64() / 1000; + result->uptime = GetTickCount64(); + result->bootTime = ffTimeGetNow() - result->uptime; return NULL; } diff --git a/src/detection/users/users.h b/src/detection/users/users.h index 8842e16a2..8ddc2a0cf 100644 --- a/src/detection/users/users.h +++ b/src/detection/users/users.h @@ -5,6 +5,15 @@ #include "fastfetch.h" -void ffDetectUsers(FFlist* users /* List of FFstrbuf */, FFstrbuf* error); +typedef struct FFUserResult +{ + FFstrbuf name; + FFstrbuf hostName; + FFstrbuf clientIp; + FFstrbuf tty; + uint64_t loginTime; // ms +} FFUserResult; + +const char* ffDetectUsers(FFlist* users); #endif diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c index c17bf139c..294c69b3e 100644 --- a/src/detection/users/users_linux.c +++ b/src/detection/users/users_linux.c @@ -11,7 +11,7 @@ #define getutxent getutent #endif -void ffDetectUsers(FFlist* users, FFstrbuf* error) +const char* ffDetectUsers(FFlist* users) { struct utmpx* n = NULL; setutxent(); @@ -22,15 +22,24 @@ next: if(n->ut_type != USER_PROCESS) continue; - FF_LIST_FOR_EACH(FFstrbuf, user, *users) + FF_LIST_FOR_EACH(FFUserResult, user, *users) { - if(ffStrbufEqualS(user, n->ut_user)) + if(ffStrbufEqualS(&user->name, n->ut_user)) goto next; } - ffStrbufInitS((FFstrbuf*)ffListAdd(users), n->ut_user); + FFUserResult* user = (FFUserResult*) ffListAdd(users); + ffStrbufInitS(&user->name, n->ut_user); + ffStrbufInitS(&user->hostName, n->ut_host); + ffStrbufInitS(&user->tty, n->ut_line); + #ifdef __linux__ + if(n->ut_addr_v6[0] || n->ut_addr_v6[1] || n->ut_addr_v6[2] || n->ut_addr_v6[3]) + ffStrbufInitF(&user->clientIp, "%u.%u.%u.%u", n->ut_addr_v6[0], n->ut_addr_v6[1], n->ut_addr_v6[2], n->ut_addr_v6[3]); + else + #endif + ffStrbufInit(&user->clientIp); + user->loginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000; } - if(users->length == 0) - ffStrbufAppendS(error, "Unable to detect users"); + return NULL; } diff --git a/src/detection/users/users_windows.c b/src/detection/users/users_windows.c index 01fab40a7..a891520b1 100644 --- a/src/detection/users/users_windows.c +++ b/src/detection/users/users_windows.c @@ -4,17 +4,20 @@ #include #include -void ffDetectUsers(FFlist* users, FFstrbuf* error) +static inline uint64_t to_ms(uint64_t ret) +{ + ret -= 116444736000000000ull; + return ret / 10000ull; +} + +const char* ffDetectUsers(FFlist* users) { WTS_SESSION_INFO_1W* sessionInfo; DWORD sessionCount; DWORD level = 1; if(!WTSEnumerateSessionsExW(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) - { - ffStrbufAppendS(error, "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"); - return; - } + return "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"; for (DWORD i = 0; i < sessionCount; i++) { @@ -22,14 +25,33 @@ void ffDetectUsers(FFlist* users, FFstrbuf* error) if(session->State != WTSActive) continue; - FF_STRBUF_AUTO_DESTROY domainName = ffStrbufCreateWS(session->pDomainName); - FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreateWS(session->pUserName); + FFUserResult* user = (FFUserResult*) ffListAdd(users); + ffStrbufInitWS(&user->name, session->pUserName); + ffStrbufInitWS(&user->hostName, session->pHostName); - ffStrbufInitF((FFstrbuf*)ffListAdd(users), "%s\\%s", domainName.chars, userName.chars); + DWORD bytes = 0; + PWTS_CLIENT_ADDRESS address = NULL; + if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSClientAddress, (LPWSTR *) &address, &bytes)) + { + if (address->AddressFamily == 2 /*AF_INET*/) + ffStrbufInitF(&user->clientIp, "%u.%u.%u.%u", address->Address[2], address->Address[3], address->Address[4], address->Address[5]); + WTSFreeMemory(address); + } + else + ffStrbufInitS(&user->clientIp, "0.0.0.0"); + + bytes = 0; + PWTSINFOW wtsInfo = NULL; + if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSSessionInfo, (LPWSTR *) &wtsInfo, &bytes)) + { + user->loginTime = to_ms(*(uint64_t*) &wtsInfo->LogonTime); + WTSFreeMemory(wtsInfo); + } + else + user->loginTime = 0; } WTSFreeMemoryExW(WTSTypeSessionInfoLevel1, sessionInfo, 1); - if(users->length == 0) - ffStrbufAppendS(error, "Unable to detect users"); + return NULL; } diff --git a/src/detection/version/version.c b/src/detection/version/version.c index 4a8eb420c..202c10ca2 100644 --- a/src/detection/version/version.c +++ b/src/detection/version/version.c @@ -20,6 +20,9 @@ #define FF_ARCHITECTURE "unknown" #endif +#define FF_STR_INDIR(x) #x +#define FF_STR(x) FF_STR_INDIR(x) + void ffDetectVersion(FFVersionResult* version) { version->projectName = FASTFETCH_PROJECT_NAME; @@ -27,6 +30,16 @@ void ffDetectVersion(FFVersionResult* version) version->version = FASTFETCH_PROJECT_VERSION; version->versionTweak = FASTFETCH_PROJECT_VERSION_TWEAK; version->cmakeBuiltType = FASTFETCH_PROJECT_CMAKE_BUILD_TYPE; + version->compileTime = __DATE__ ", " __TIME__; + #ifdef __clang__ + version->compiler = "clang " FF_STR(__clang_major__) "." FF_STR(__clang_minor__) "." FF_STR(__clang_patchlevel__); + #elif defined(__GNUC__) + version->compiler = "gcc " FF_STR(__GNUC__) "." FF_STR(__GNUC_MINOR__) "." FF_STR(__GNUC_PATCHLEVEL__); + #elif defined(_MSC_VER) + version->compiler = "msvc " FF_STR(_MSC_VER); + #else + version->compiler = "unknown"; + #endif #ifndef NDEBUG version->debugMode = true; #else diff --git a/src/detection/version/version.h b/src/detection/version/version.h index bdb11aa3a..4cb7b9ac6 100644 --- a/src/detection/version/version.h +++ b/src/detection/version/version.h @@ -12,6 +12,8 @@ typedef struct FFVersionResult const char* version; const char* versionTweak; const char* cmakeBuiltType; + const char* compileTime; + const char* compiler; bool debugMode; } FFVersionResult; diff --git a/src/detection/weather/weather.c b/src/detection/weather/weather.c new file mode 100644 index 000000000..94bd6b5b6 --- /dev/null +++ b/src/detection/weather/weather.c @@ -0,0 +1,38 @@ +#include "weather.h" + +static FFNetworkingState state; +static int status = -1; + +void ffPrepareWeather(FFWeatherOptions* options) +{ + if (status != -1) + { + fputs("Error: this module can only be used once due to internal limitations\n", stderr); + exit(1); + } + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/"); + if (options->location.length) + ffStrbufAppend(&path, &options->location); + ffStrbufAppendS(&path, "?format="); + ffStrbufAppend(&path, &options->outputFormat); + status = ffNetworkingSendHttpRequest(&state, "wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n"); +} + +const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result) +{ + if(status == -1) + ffPrepareWeather(options); + + if(status == 0) + return "Failed to connect to 'wttr.in'"; + + ffStrbufEnsureFree(result, 4095); + bool success = ffNetworkingRecvHttpResponse(&state, result, options->timeout); + if (success) ffStrbufSubstrAfterFirstS(result, "\r\n\r\n"); + + if(!success || result->length == 0) + return "Failed to receive the server response"; + + return NULL; +} diff --git a/src/detection/weather/weather.h b/src/detection/weather/weather.h new file mode 100644 index 000000000..c15f1ea3a --- /dev/null +++ b/src/detection/weather/weather.h @@ -0,0 +1,11 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_weather_weather +#define FF_INCLUDED_detection_weather_weather + +#include "common/networking.h" + +void ffPrepareWeather(FFWeatherOptions* options); +const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result); + +#endif diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index c6b572012..775856eec 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -63,7 +63,7 @@ const char* ffDetectWifi(FFlist* result) ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode); break; } - item->conn.signalQuality = inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2; + item->conn.signalQuality = (double) (inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2); item->conn.txRate = inf.transmitRate; switch(inf.security) diff --git a/src/fastfetch.c b/src/fastfetch.c index 4c947d562..c956357eb 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -21,21 +21,6 @@ #include "modules/modules.h" -typedef struct FFCustomValue -{ - bool printKey; - FFstrbuf key; - FFstrbuf value; -} FFCustomValue; - -// Things only needed by fastfetch -typedef struct FFdata -{ - FFstrbuf structure; - FFlist customValues; // List of FFCustomValue - bool loadUserConfig; -} FFdata; - static void constructAndPrintCommandHelpFormat(const char* name, const char* def, uint32_t numArgs, ...) { va_list argp; @@ -64,6 +49,19 @@ static inline void printCommandHelp(const char* command) puts(FASTFETCH_DATATEXT_HELP_FORMAT); else if(ffStrEqualsIgnCase(command, "load-config") || ffStrEqualsIgnCase(command, "config")) puts(FASTFETCH_DATATEXT_HELP_CONFIG); + else if(ffStrEqualsIgnCase(command, "title-format")) + { + constructAndPrintCommandHelpFormat("title", "{6}{7}{8}", 8, + "User name", + "Host name", + "Home directory", + "Executable path of current process", + "User's default shell", + "User name (colored)", + "@ symbol (colored)", + "Host name (colored)" + ); + } else if(ffStrEqualsIgnCase(command, "os-format")) { constructAndPrintCommandHelpFormat("os", "{3} {12}", 12, @@ -144,7 +142,7 @@ static inline void printCommandHelp(const char* command) } else if(ffStrEqualsIgnCase(command, "packages-format")) { - constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (port), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis)", 23, + constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (port), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget)", 24, "Number of all packages", "Number of pacman packages", "Pacman branch on manjaro", @@ -168,18 +166,18 @@ static inline void printCommandHelp(const char* command) "Number of choco packages", "Number of pkgtool packages", "Number of paludis packages" + "Number of winget packages" ); } else if(ffStrEqualsIgnCase(command, "shell-format")) { - constructAndPrintCommandHelpFormat("shell", "{3} {4}", 7, + constructAndPrintCommandHelpFormat("shell", "{3} {4}", 6, "Shell process name", "Shell path with exe name", "Shell exe name", "Shell version", - "User shell path with exe name", - "User shell exe name", - "User shell version" + "Shell pid", + "Shell pretty name" ); } else if(ffStrEqualsIgnCase(command, "display-format")) @@ -276,13 +274,9 @@ static inline void printCommandHelp(const char* command) "Terminal process name", "Terminal path with exe name", "Terminal exe name", - "Shell process name", - "Shell path with exe name", - "Shell exe name", - "Shell version", - "User shell path with exe name", - "User shell exe name", - "User shell version" + "Terminal pid", + "Terminal pretty name", + "Terminal version" ); } else if(ffStrEqualsIgnCase(command, "terminalfont-format")) @@ -307,9 +301,9 @@ static inline void printCommandHelp(const char* command) "Temperature" ); } - else if(ffStrEqualsIgnCase(command, "cpu-usage-format")) + else if(ffStrEqualsIgnCase(command, "cpuusage-format")) { - constructAndPrintCommandHelpFormat("cpu-usage", "{0}%", 1, + constructAndPrintCommandHelpFormat("cpuusage", "{0}%", 1, "CPU usage without percent mark" ); } @@ -387,18 +381,31 @@ static inline void printCommandHelp(const char* command) "Locale code" ); } - else if(ffStrEqualsIgnCase(command, "local-ip-format")) + else if(ffStrEqualsIgnCase(command, "localip-format")) { - constructAndPrintCommandHelpFormat("local-ip", "{}", 1, - "Local IP address" + constructAndPrintCommandHelpFormat("localip", "{}", 5, + "Local IPv4 address", + "Local IPv6 address", + "Physical (MAC) address", + "Interface name", + "Is default route" ); } - else if(ffStrEqualsIgnCase(command, "public-ip-format")) + else if(ffStrEqualsIgnCase(command, "publicip-format")) { - constructAndPrintCommandHelpFormat("public-ip", "{}", 1, + constructAndPrintCommandHelpFormat("publicip", "{}", 1, "Public IP address" ); } + else if(ffStrEqualsIgnCase(command, "netio-format")) + { + constructAndPrintCommandHelpFormat("netio", "{}", 3, + "Size of data received per second (formatted)", + "Size of data sent per second (formatted)", + "Interface name", + "Is default route" + ); + } else if(ffStrEqualsIgnCase(command, "wifi-format")) { constructAndPrintCommandHelpFormat("wifi", "{4} - {6}", 3, @@ -465,7 +472,9 @@ static inline void printCommandHelp(const char* command) "Version tweak", "Build type (debug or release)", "Architecture", - "CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel)" + "CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel)", + "Date time when compiling", + "Compiler used" ); } else if(ffStrEqualsIgnCase(command, "vulkan-format")) @@ -836,25 +845,25 @@ static void parseOption(FFdata* data, const char* key, const char* value) puts(FASTFETCH_PROJECT_VERSION); exit(0); } - else if(ffStrStartsWithIgnCase(key, "--print")) + else if(ffStrStartsWithIgnCase(key, "--print-")) { - const char* subkey = key + strlen("--print"); - if(ffStrEqualsIgnCase(subkey, "-config-system")) + const char* subkey = key + strlen("--print-"); + if(ffStrEqualsIgnCase(subkey, "config-system")) { puts(FASTFETCH_DATATEXT_CONFIG_SYSTEM); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-config-user")) + else if(ffStrEqualsIgnCase(subkey, "config-user")) { puts(FASTFETCH_DATATEXT_CONFIG_USER); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-structure")) + else if(ffStrEqualsIgnCase(subkey, "structure")) { puts(FASTFETCH_DATATEXT_STRUCTURE); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-logos")) + else if(ffStrEqualsIgnCase(subkey, "logos")) { ffLogoBuiltinPrint(); exit(0); @@ -862,35 +871,35 @@ static void parseOption(FFdata* data, const char* key, const char* value) else goto error; } - else if(ffStrStartsWithIgnCase(key, "--list")) + else if(ffStrStartsWithIgnCase(key, "--list-")) { - const char* subkey = key + strlen("--list"); - if(ffStrEqualsIgnCase(subkey, "-modules")) + const char* subkey = key + strlen("--list-"); + if(ffStrEqualsIgnCase(subkey, "modules")) { listModules(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-presets")) + else if(ffStrEqualsIgnCase(subkey, "presets")) { listAvailablePresets(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-config-paths")) + else if(ffStrEqualsIgnCase(subkey, "config-paths")) { listConfigPaths(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-data-paths")) + else if(ffStrEqualsIgnCase(subkey, "data-paths")) { listDataPaths(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-features")) + else if(ffStrEqualsIgnCase(subkey, "features")) { ffListFeatures(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-logos")) + else if(ffStrEqualsIgnCase(subkey, "logos")) { puts("Builtin logos:"); ffLogoBuiltinList(); @@ -898,7 +907,7 @@ static void parseOption(FFdata* data, const char* key, const char* value) listAvailableLogos(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-logos-autocompletion")) + else if(ffStrEqualsIgnCase(subkey, "logos-autocompletion")) { ffLogoBuiltinListAutocompletion(); exit(0); @@ -906,12 +915,8 @@ static void parseOption(FFdata* data, const char* key, const char* value) else goto error; } - else if(ffStrStartsWithIgnCase(key, "--set")) + else if(ffStrEqualsIgnCase(key, "--set") || ffStrEqualsIgnCase(key, "--set-keyless")) { - const char* subkey = key + strlen("--set"); - if(*subkey != '\0' && !ffStrEqualsIgnCase(subkey, "-keyless")) - goto error; - FF_STRBUF_AUTO_DESTROY customValueStr = ffStrbufCreate(); ffOptionParseString(key, value, &customValueStr); uint32_t index = ffStrbufFirstIndexC(&customValueStr, '='); @@ -938,14 +943,14 @@ static void parseOption(FFdata* data, const char* key, const char* value) ffStrbufInitMove(&customValue->key, &customKey); ffStrbufSubstrAfter(&customValueStr, index); ffStrbufInitMove(&customValue->value, &customValueStr); - customValue->printKey = *subkey == '\0'; + customValue->printKey = key[5] == '\0'; } /////////////////// //General options// /////////////////// - else if(ffStrEqualsIgnCase(key, "--load-config") || ffStrEqualsIgnCase(key, "--config")) + else if(ffStrEqualsIgnCase(key, "-c") || ffStrEqualsIgnCase(key, "--load-config") || ffStrEqualsIgnCase(key, "--config")) optionParseConfigFile(data, key, value); else if(ffStrEqualsIgnCase(key, "--gen-config")) generateConfigFile(false, value); @@ -968,6 +973,30 @@ static void parseOption(FFdata* data, const char* key, const char* value) data->loadUserConfig = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--processing-timeout")) instance.config.processingTimeout = ffOptionParseInt32(key, value); + else if(ffStrEqualsIgnCase(key, "--format")) + { + switch (ffOptionParseEnum(key, value, (FFKeyValuePair[]) { + { "default", 0}, + { "json", 1 }, + {}, + })) + { + case 0: + if (instance.state.resultDoc) + { + yyjson_mut_doc_free(instance.state.resultDoc); + instance.state.resultDoc = NULL; + } + break; + case 1: + if (!instance.state.resultDoc) + { + instance.state.resultDoc = yyjson_mut_doc_new(NULL); + yyjson_mut_doc_set_root(instance.state.resultDoc, yyjson_mut_arr(instance.state.resultDoc)); + } + break; + } + } #if defined(__linux__) || defined(__FreeBSD__) else if(ffStrEqualsIgnCase(key, "--player-name")) @@ -1001,21 +1030,21 @@ static void parseOption(FFdata* data, const char* key, const char* value) ffOptionParseString(key, value, &data->structure); else if(ffStrEqualsIgnCase(key, "--separator")) ffOptionParseString(key, value, &instance.config.keyValueSeparator); - else if(ffStrStartsWith(key, "--color")) + else if(ffStrEqualsIgnCase(key, "--color")) { - const char* subkey = key + strlen("--color"); - if(*subkey == '\0') - { - optionCheckString(key, value, &instance.config.colorKeys); - ffOptionParseColor(value, &instance.config.colorKeys); - ffStrbufSet(&instance.config.colorTitle, &instance.config.colorKeys); - } - else if(ffStrEqualsIgnCase(subkey, "-keys")) + optionCheckString(key, value, &instance.config.colorKeys); + ffOptionParseColor(value, &instance.config.colorKeys); + ffStrbufSet(&instance.config.colorTitle, &instance.config.colorKeys); + } + else if(ffStrStartsWithIgnCase(key, "--color-")) + { + const char* subkey = key + strlen("--color-"); + if(ffStrEqualsIgnCase(subkey, "keys")) { optionCheckString(key, value, &instance.config.colorKeys); ffOptionParseColor(value, &instance.config.colorKeys); } - else if(ffStrEqualsIgnCase(subkey, "-title")) + else if(ffStrEqualsIgnCase(subkey, "title")) { optionCheckString(key, value, &instance.config.colorTitle); ffOptionParseColor(value, &instance.config.colorTitle); @@ -1071,16 +1100,16 @@ static void parseOption(FFdata* data, const char* key, const char* value) instance.config.percentNdigits = (uint8_t) ffOptionParseUInt32(key, value); else if(ffStrEqualsIgnCase(key, "--no-buffer")) instance.config.noBuffer = ffOptionParseBoolean(value); - else if(ffStrStartsWithIgnCase(key, "--bar")) + else if(ffStrStartsWithIgnCase(key, "--bar-")) { - const char* subkey = key + strlen("--bar"); - if(ffStrEqualsIgnCase(subkey, "-char-elapsed")) + const char* subkey = key + strlen("--bar-"); + if(ffStrEqualsIgnCase(subkey, "char-elapsed")) ffOptionParseString(key, value, &instance.config.barCharElapsed); - else if(ffStrEqualsIgnCase(subkey, "-char-total")) + else if(ffStrEqualsIgnCase(subkey, "char-total")) ffOptionParseString(key, value, &instance.config.barCharTotal); - else if(ffStrEqualsIgnCase(subkey, "-width")) + else if(ffStrEqualsIgnCase(subkey, "width")) instance.config.barWidth = (uint8_t) ffOptionParseUInt32(key, value); - else if(ffStrEqualsIgnCase(subkey, "-border")) + else if(ffStrEqualsIgnCase(subkey, "border")) instance.config.barBorder = ffOptionParseBoolean(value); else goto error; @@ -1090,56 +1119,56 @@ static void parseOption(FFdata* data, const char* key, const char* value) //Library options// /////////////////// - else if(ffStrStartsWithIgnCase(key, "--lib")) + else if(ffStrStartsWithIgnCase(key, "--lib-")) { - const char* subkey = key + strlen("--lib"); - if(ffStrEqualsIgnCase(subkey, "-PCI")) + const char* subkey = key + strlen("--lib-"); + if(ffStrEqualsIgnCase(subkey, "PCI")) ffOptionParseString(key, value, &instance.config.libPCI); - else if(ffStrEqualsIgnCase(subkey, "-vulkan")) + else if(ffStrEqualsIgnCase(subkey, "vulkan")) ffOptionParseString(key, value, &instance.config.libVulkan); - else if(ffStrEqualsIgnCase(subkey, "-freetype")) + else if(ffStrEqualsIgnCase(subkey, "freetype")) ffOptionParseString(key, value, &instance.config.libfreetype); - else if(ffStrEqualsIgnCase(subkey, "-wayland")) + else if(ffStrEqualsIgnCase(subkey, "wayland")) ffOptionParseString(key, value, &instance.config.libWayland); - else if(ffStrEqualsIgnCase(subkey, "-xcb-randr")) + else if(ffStrEqualsIgnCase(subkey, "xcb-randr")) ffOptionParseString(key, value, &instance.config.libXcbRandr); - else if(ffStrEqualsIgnCase(subkey, "-xcb")) + else if(ffStrEqualsIgnCase(subkey, "xcb")) ffOptionParseString(key, value, &instance.config.libXcb); - else if(ffStrEqualsIgnCase(subkey, "-Xrandr")) + else if(ffStrEqualsIgnCase(subkey, "Xrandr")) ffOptionParseString(key, value, &instance.config.libXrandr); - else if(ffStrEqualsIgnCase(subkey, "-X11")) + else if(ffStrEqualsIgnCase(subkey, "X11")) ffOptionParseString(key, value, &instance.config.libX11); - else if(ffStrEqualsIgnCase(subkey, "-gio")) + else if(ffStrEqualsIgnCase(subkey, "gio")) ffOptionParseString(key, value, &instance.config.libGIO); - else if(ffStrEqualsIgnCase(subkey, "-DConf")) + else if(ffStrEqualsIgnCase(subkey, "DConf")) ffOptionParseString(key, value, &instance.config.libDConf); - else if(ffStrEqualsIgnCase(subkey, "-dbus")) + else if(ffStrEqualsIgnCase(subkey, "dbus")) ffOptionParseString(key, value, &instance.config.libDBus); - else if(ffStrEqualsIgnCase(subkey, "-XFConf")) + else if(ffStrEqualsIgnCase(subkey, "XFConf")) ffOptionParseString(key, value, &instance.config.libXFConf); - else if(ffStrEqualsIgnCase(subkey, "-sqlite") || ffStrEqualsIgnCase(subkey, "-sqlite3")) + else if(ffStrEqualsIgnCase(subkey, "sqlite") || ffStrEqualsIgnCase(subkey, "sqlite3")) ffOptionParseString(key, value, &instance.config.libSQLite3); - else if(ffStrEqualsIgnCase(subkey, "-rpm")) + else if(ffStrEqualsIgnCase(subkey, "rpm")) ffOptionParseString(key, value, &instance.config.librpm); - else if(ffStrEqualsIgnCase(subkey, "-imagemagick")) + else if(ffStrEqualsIgnCase(subkey, "imagemagick")) ffOptionParseString(key, value, &instance.config.libImageMagick); - else if(ffStrEqualsIgnCase(subkey, "-z")) + else if(ffStrEqualsIgnCase(subkey, "z")) ffOptionParseString(key, value, &instance.config.libZ); - else if(ffStrEqualsIgnCase(subkey, "-chafa")) + else if(ffStrEqualsIgnCase(subkey, "chafa")) ffOptionParseString(key, value, &instance.config.libChafa); - else if(ffStrEqualsIgnCase(subkey, "-egl")) + else if(ffStrEqualsIgnCase(subkey, "egl")) ffOptionParseString(key, value, &instance.config.libEGL); - else if(ffStrEqualsIgnCase(subkey, "-glx")) + else if(ffStrEqualsIgnCase(subkey, "glx")) ffOptionParseString(key, value, &instance.config.libGLX); - else if(ffStrEqualsIgnCase(subkey, "-osmesa")) + else if(ffStrEqualsIgnCase(subkey, "osmesa")) ffOptionParseString(key, value, &instance.config.libOSMesa); - else if(ffStrEqualsIgnCase(subkey, "-opencl")) + else if(ffStrEqualsIgnCase(subkey, "opencl")) ffOptionParseString(key, value, &instance.config.libOpenCL); - else if(ffStrEqualsIgnCase(key, "-pulse")) + else if(ffStrEqualsIgnCase(subkey, "pulse")) ffOptionParseString(key, value, &instance.config.libPulse); - else if(ffStrEqualsIgnCase(subkey, "-nm")) + else if(ffStrEqualsIgnCase(subkey, "nm")) ffOptionParseString(key, value, &instance.config.libnm); - else if(ffStrEqualsIgnCase(subkey, "-ddcutil")) + else if(ffStrEqualsIgnCase(subkey, "ddcutil")) ffOptionParseString(key, value, &instance.config.libDdcutil); else goto error; @@ -1219,27 +1248,6 @@ static void parseArguments(FFdata* data, int argc, const char** argv) } } -static void parseStructureCommand(const char* line, FFlist* customValues) -{ - // handle `--set` and `--set-keyless` - FF_LIST_FOR_EACH(FFCustomValue, customValue, *customValues) - { - if (ffStrbufEqualS(&customValue->key, line)) - { - __attribute__((__cleanup__(ffDestroyCustomOptions))) FFCustomOptions options; - ffInitCustomOptions(&options); - if (customValue->printKey) - ffStrbufAppend(&options.moduleArgs.key, &customValue->key); - ffStrbufAppend(&options.moduleArgs.outputFormat, &customValue->value); - ffPrintCustom(&options); - return; - } - } - - if(!ffParseModuleCommand(line)) - ffPrintErrorString(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, ""); -} - int main(int argc, const char** argv) { ffInitInstance(); @@ -1273,25 +1281,9 @@ int main(int argc, const char** argv) const bool useJsonConfig = data.structure.length == 0 && instance.state.configDoc; if(useJsonConfig) - ffPrintJsonConfig(true); + ffPrintJsonConfig(true /* prepare */); else - { - //If we don't have a custom structure, use the default one - if(data.structure.length == 0) - ffStrbufAppendS(&data.structure, FASTFETCH_DATATEXT_STRUCTURE); - - if(ffStrbufContainIgnCaseS(&data.structure, FF_CPUUSAGE_MODULE_NAME)) - ffPrepareCPUUsage(); - - if(instance.config.multithreading) - { - if(ffStrbufContainIgnCaseS(&data.structure, FF_PUBLICIP_MODULE_NAME)) - ffPreparePublicIp(&instance.config.publicIP); - - if(ffStrbufContainIgnCaseS(&data.structure, FF_WEATHER_MODULE_NAME)) - ffPrepareWeather(&instance.config.weather); - } - } + ffPrepareCommandOption(&data); ffStart(); @@ -1300,40 +1292,13 @@ int main(int argc, const char** argv) #endif if (useJsonConfig) - { ffPrintJsonConfig(false); - } else + ffPrintCommandOption(&data); + + if (instance.state.resultDoc) { - //Parse the structure and call the modules - uint32_t startIndex = 0; - while (startIndex < data.structure.length) - { - uint32_t colonIndex = ffStrbufNextIndexC(&data.structure, startIndex, ':'); - data.structure.chars[colonIndex] = '\0'; - - uint64_t ms = 0; - if(__builtin_expect(instance.config.stat, false)) - ms = ffTimeGetTick(); - - parseStructureCommand(data.structure.chars + startIndex, &data.customValues); - - if(__builtin_expect(instance.config.stat, false)) - { - char str[32]; - int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ffTimeGetTick() - ms); - if(instance.config.pipe) - puts(str); - else - printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load - } - - #if defined(_WIN32) - if (!instance.config.noBuffer) fflush(stdout); - #endif - - startIndex = colonIndex + 1; - } + yyjson_mut_write_fp(stdout, instance.state.resultDoc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES, NULL, NULL); } ffFinish(); diff --git a/src/fastfetch.h b/src/fastfetch.h index a5f990912..0877c1088 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -14,6 +14,10 @@ #include "3rdparty/yyjson/yyjson.h" #endif +#ifdef _MSC_VER + #define __attribute__(x) +#endif + #include "util/FFstrbuf.h" #include "util/FFlist.h" #include "util/platform/FFPlatform.h" @@ -108,6 +112,7 @@ typedef struct FFconfig FFMediaOptions media; FFMemoryOptions memory; FFMonitorOptions monitor; + FFNetIOOptions netIo; FFOSOptions os; FFOpenCLOptions openCL; FFOpenGLOptions openGL; @@ -169,6 +174,7 @@ typedef struct FFstate FFPlatform platform; yyjson_doc* configDoc; + yyjson_mut_doc* resultDoc; } FFstate; typedef struct FFinstance diff --git a/src/logo/ascii/afterglow.txt b/src/logo/ascii/afterglow.txt new file mode 100644 index 000000000..3b86a44d4 --- /dev/null +++ b/src/logo/ascii/afterglow.txt @@ -0,0 +1,15 @@ + ${c2}. + ${c1}. ${c2}.{! + ${c1}.L! ${c2}J@||* + ${c1}gJJJJL` ${c2}g@FFS" + ${c1},@FFFJF`${c2}_g@@LLP` + ${c1}_@FFFFF`${c2}_@@@@@P` ${c4}. + ${c1}J@@@LLF ${c2}_@@@@@P` ${c4}.J! + ${c1}g@@@@@" ${c2}_@@@@@P`${c3}. ${c4}.L|||* +${c1}g@@@@M" ${c2}"VP`${c3}.L! ${c4}<@JJJJ` + ${c1}"@N" ${c3}:||||! ${c4}JFFFFS" + ${c3}.{JJ||F`${c4}_gFFFF@' + ${c3}.@FJJJF`${c4},@LFFFF` + ${c3}_@FFFFF ${c4}VLLLP` + ${c3}J@@LL@" ${c4}`" + ${c3}V@@" diff --git a/src/logo/ascii/amazon_linux.txt b/src/logo/ascii/amazon_linux.txt new file mode 100644 index 000000000..915267868 --- /dev/null +++ b/src/logo/ascii/amazon_linux.txt @@ -0,0 +1,10 @@ + , $2#_$1 + ~\_ $2####_$1 + ~~ \_$2#####\$1 + ~~ \$2###|$1 + ~~ \$2#/$1 ___ + ~~ V~' '-> + ~~~ / + ~~._. _/ + _/ _/ + _/m/' diff --git a/src/logo/ascii/aoscos.txt b/src/logo/ascii/aoscos.txt index 2886c092d..8681218b4 100644 --- a/src/logo/ascii/aoscos.txt +++ b/src/logo/ascii/aoscos.txt @@ -1,20 +1,17 @@ - .:+syhhhhys+:. - .ohNMMMMMMMMMMMMMMNho. - `+mMMMMMMMMMMmdmNMMMMMMMMm+` - +NMMMMMMMMMMMM/ `./smMMMMMN+ - .mMMMMMMMMMMMMMMo -yMMMMMm. - :NMMMMMMMMMMMMMMMs .hMMMMN: - .NMMMMhmMMMMMMMMMMm+/- oMMMMN. - dMMMMs ./ymMMMMMMMMMMNy. sMMMMd --MMMMN` oMMMMMMMMMMMN: `NMMMM- -/MMMMh NMMMMMMMMMMMMm hMMMM/ -/MMMMh NMMMMMMMMMMMMm hMMMM/ --MMMMN` :MMMMMMMMMMMMy. `NMMMM- - dMMMMs .yNMMMMMMMMMMMNy/. sMMMMd - .NMMMMo -/+sMMMMMMMMMMMmMMMMN. - :NMMMMh. .MMMMMMMMMMMMMMMN: - .mMMMMMy- NMMMMMMMMMMMMMm. - +NMMMMMms/.` mMMMMMMMMMMMN+ - `+mMMMMMMMMNmddMMMMMMMMMMm+` - .ohNMMMMMMMMMMMMMMNho. - .:+syhhhhys+:. + ${c2}__ + ${c2}gpBBBBBBBBBP + ${c2}_gBBBBBBBBBRP + ${c2}4BBBBBBBBRP ${c4},_____ + ${c2}`"" ${c4}_g@@@@@@@@@@@@@%g> + ${c4}__@@@@@@@@@@@@@@@@P" ${c1}___ + ${c4}_g@@@@@@@@@@@@@@@N"` ${c1}_gN@@@@@N^ + ${c4}_w@@@@@@@@@@@@@@@@P" ${c1}_g@@@@@@@P" + ${c4}_g@@@@@@@@@@@@@@@N"` ${c1}VMNN@NNNM^` +${c4}^MMM@@@@@@@@@@@MP" ${c3},ggppww__ + ${c4}`""""" ${c3}_wNNNNNNNNNNNNNNNNNNN + ${c3}_gBNNNNNNNNNNNNNNNNNP" + ${c3}_wNNNNNNNNNNNNNNNNNNMP` + ${c3}_gBNNNNNNNNNNNNNNNNNP" + ${c3}_wNNNNNNNNNNNNNNNNNNNM^ + ${c3}""Y^^MNNNNNNNNNNNNP` + ${c3}`""""""" diff --git a/src/logo/ascii/aoscos_old.txt b/src/logo/ascii/aoscos_old.txt new file mode 100644 index 000000000..2886c092d --- /dev/null +++ b/src/logo/ascii/aoscos_old.txt @@ -0,0 +1,20 @@ + .:+syhhhhys+:. + .ohNMMMMMMMMMMMMMMNho. + `+mMMMMMMMMMMmdmNMMMMMMMMm+` + +NMMMMMMMMMMMM/ `./smMMMMMN+ + .mMMMMMMMMMMMMMMo -yMMMMMm. + :NMMMMMMMMMMMMMMMs .hMMMMN: + .NMMMMhmMMMMMMMMMMm+/- oMMMMN. + dMMMMs ./ymMMMMMMMMMMNy. sMMMMd +-MMMMN` oMMMMMMMMMMMN: `NMMMM- +/MMMMh NMMMMMMMMMMMMm hMMMM/ +/MMMMh NMMMMMMMMMMMMm hMMMM/ +-MMMMN` :MMMMMMMMMMMMy. `NMMMM- + dMMMMs .yNMMMMMMMMMMMNy/. sMMMMd + .NMMMMo -/+sMMMMMMMMMMMmMMMMN. + :NMMMMh. .MMMMMMMMMMMMMMMN: + .mMMMMMy- NMMMMMMMMMMMMMm. + +NMMMMMms/.` mMMMMMMMMMMMN+ + `+mMMMMMMMMNmddMMMMMMMMMMm+` + .ohNMMMMMMMMMMMMMMNho. + .:+syhhhhys+:. diff --git a/src/logo/ascii/elbrus.txt b/src/logo/ascii/elbrus.txt new file mode 100644 index 000000000..b70cac89c --- /dev/null +++ b/src/logo/ascii/elbrus.txt @@ -0,0 +1,14 @@ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +██▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀██ +██ ██ +██ ███████ ███████ ██ +██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ +██ ██ ███████ ███████ +██ ██ ██ +██ ██▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄██ +██ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀██ +██ ██ +███████████████████████████ diff --git a/src/logo/ascii/evolutionos.txt b/src/logo/ascii/evolutionos.txt index 7a6622d4a..18ca20e23 100644 --- a/src/logo/ascii/evolutionos.txt +++ b/src/logo/ascii/evolutionos.txt @@ -1,18 +1,21 @@ - dddddddddddddddddddddddd -.dddd''''''''''''''''''''''dddd. -dd: dddddddddddddddddddd; dd: -dd: ldl:'''''''''''''''' dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: ddddddd; ddddd; dd: -dd: ldl: ''''''' ''''' dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: ddddddddddddddd; dd: -dddd:.''' ''''''''''''''' dddd: - dddddddddddddddddddddddddd;;' - ''''''''''''''''''''''''' + $2.':ldxxxxdo:,. + .lXMMMMMMMMMMMMMMMMNo' + dWMMMMMMMMMMMMMMMMMMMMMMWk + .OMMMMWWWWWWWWWWWWWWWWWWWMMMMM0; + kMMMMM$1XxxxxkkkkkkkkkkkkkkkK$2WMMMMMK. + .kMMMMMM$1Xddd0KKKKKKKKKKKKKKKN$2MMMMMMMN. + KMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMX. + cMMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMMo + KMMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMMN + XMMMMMMMM$1XdddX$2WO$1kkkkkkkK$2WK$1OOOX$2MMMMMMMMMW + XMMMMMMMM$1XdddX$2WO$1kkkkkkkK$2WK$1OOOX$2MMMMMMMMMW + 0MMMMMMMM$1XdddX$2MWWWWWWWWWMWWWWWMMMMMMMMMN + cMMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMMd + .kMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMM0. + .kMMMMMM$1XxxxN$2W0$1OOOOOOOOOOOOOK$2MMMMMMMK. + oMMMMM$1XxxxN$2W0$1OOOOOOOOOOOOOK$2WMMMMMk. + '0MMMWNNNWMWWWWWWWWWWWWWWWMMMMX; + cWMMMMMMMMMMMMMMMMMMMMMMWd + :KWMMMMMMMMMMMMMMWXo. + .cdO0KK00xl' + .. diff --git a/src/logo/ascii/evolutionos_old.txt b/src/logo/ascii/evolutionos_old.txt new file mode 100644 index 000000000..7a6622d4a --- /dev/null +++ b/src/logo/ascii/evolutionos_old.txt @@ -0,0 +1,18 @@ + dddddddddddddddddddddddd +.dddd''''''''''''''''''''''dddd. +dd: dddddddddddddddddddd; dd: +dd: ldl:'''''''''''''''' dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: ddddddd; ddddd; dd: +dd: ldl: ''''''' ''''' dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: ddddddddddddddd; dd: +dddd:.''' ''''''''''''''' dddd: + dddddddddddddddddddddddddd;;' + ''''''''''''''''''''''''' diff --git a/src/logo/ascii/evolutionos_small.txt b/src/logo/ascii/evolutionos_small.txt new file mode 100644 index 000000000..8692019a9 --- /dev/null +++ b/src/logo/ascii/evolutionos_small.txt @@ -0,0 +1,9 @@ + $2,coddoc' + 'cddddddddddc' + 'ddd$1OWWXXXXXXK$2ddo. +.dddd$1OMX$2ddddddddddd. +odddd$1OMX$2k$100O$2k$1OO$2ddddo +.dddd$1OMX$2kOOOxOkdddd. + .ddd$1OWW$2X$1XXXXXK$2ddd' + 'dddddddddddd' + 'cddddd, diff --git a/src/logo/ascii/lainos.txt b/src/logo/ascii/lainos.txt new file mode 100644 index 000000000..9bd6c5530 --- /dev/null +++ b/src/logo/ascii/lainos.txt @@ -0,0 +1,20 @@ +${c2} /==\\ + \\==/ +${c1} · · · · · · · + · · · · · · · · · · + · · · ${c2}.-======-.${c1}· · · · +${c2} .::.${c1} ·${c2}.-============-.${c1}· ${c2}.::. + .:==:${c1}· ${c2}.:===:'${c1}. ·· .${c2}':===:.${c1} ·${c2}:==:. + .:===:${c1} · ${c2}:===.${c1} · ${c3}.--.${c1} · ${c2}.===:${c1} · ${c2}:===:. + :===:${c1}· · ${c2}:===.${c1} · ${c3}.:====:.${c1} · ${c2}.===:${c1} · ·${c2}:===: +(===:${c1}· · ${c2}:===-${c1} · ${c3}:======:${c1} · ${c2}-===:${c1} · ·${c2}:===) + :===:${c1}· · ${c2}:===.${c1} · ${c3}':====:'${c1} · ${c2}.===:${c1} · ·${c2}:===: + ':===:${c1} · ${c2}:===.${c1} · ${c3}'--'${c1} · ${c2}.===:${c1} · ${c2}:===:' + ':==:${c1}· ${c2}':===:.${c1}' ·· '${c2}.:===:'${c1} ·${c2}:==:' + '::'${c1} · ${c2}'===-. .-==='${c1} · ${c2}'::' + ${c2}/==\\${c1} · · · ${c2}:=== ===:${c1} · · · ${c2}/==\\ + \\==/${c1} · · ·${c2}:===${c1} ·${c2}===:${c1}· · · ${c2}\\==/${c2} + .-. ${c1}· ${c2}:===${c1}· ${c2}===:${c1} ·${c2} ${c2}.-. + .===. .=== ===. .===. + .======== ========. + ''''' ''''' diff --git a/src/logo/ascii/ubuntu_old.txt b/src/logo/ascii/ubuntu_old.txt index 5a816363b..2b158dd4c 100644 --- a/src/logo/ascii/ubuntu_old.txt +++ b/src/logo/ascii/ubuntu_old.txt @@ -1,5 +1,5 @@ - .-/+oossssoo+/-. - `:+ssssssssssssssssss+:` + --+oossssssoo+-- + .:+ssssssssssssssssss+:. -+ssssssssssssssssssyyssss+- .ossssssssssssssssssd$2MMMNy$1sssso. /sssssssssss$2hdmmNNmmyNMMMMh$1ssssss/ @@ -16,5 +16,5 @@ oss$2yNMMMNyMMh$1ssssssssssssss$2hmmmh$1ssssssso /sssssssssss$2hdmNNNNmyNMMMMh$1ssssss/ .ossssssssssssssssss$2dMMMNy$1sssso. -+sssssssssssssssss$2yyy$1ssss+- - `:+ssssssssssssssssss+:` - .-/+oossssoo+/-. \ No newline at end of file + `:+ssssssssssssssssss+:´ + --+oossssssoo+-- diff --git a/src/logo/ascii/windows_11_small.txt b/src/logo/ascii/windows_11_small.txt index c04184f24..ee78eb35a 100644 --- a/src/logo/ascii/windows_11_small.txt +++ b/src/logo/ascii/windows_11_small.txt @@ -1,9 +1,7 @@ -$1lllllllll $2lllllllll -$1lllllllll $2lllllllll -$1lllllllll $2lllllllll -$1lllllllll $2lllllllll +$1lllllll $2lllllll +$1lllllll $2lllllll +$1lllllll $2lllllll -$3lllllllll $4lllllllll -$3lllllllll $4lllllllll -$3lllllllll $4lllllllll -$3lllllllll $4lllllllll +$3lllllll $4lllllll +$3lllllll $4lllllll +$3lllllll $4lllllll diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 879328062..206363571 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -22,6 +22,17 @@ static const FFlogo A[] = { FF_COLOR_FG_CYAN, }, }, + // Afterglow + { + .names = {"Afterglow"}, + .lines = FASTFETCH_DATATEXT_LOGO_AFTERGLOW, + .colors = { + FF_COLOR_FG_MAGENTA, + FF_COLOR_FG_RED, + FF_COLOR_FG_YELLOW, + FF_COLOR_FG_BLUE, + }, + }, // AIX { .names = {"aix"}, @@ -98,9 +109,16 @@ static const FFlogo A[] = { .colors = { FF_COLOR_FG_YELLOW, FF_COLOR_FG_WHITE, - }, - .colorKeys = FF_COLOR_FG_YELLOW, - .colorTitle = FF_COLOR_FG_WHITE, + } + }, + // AmazonLinux + { + .names = {"Amazon Linux", "amzn"}, + .lines = FASTFETCH_DATATEXT_LOGO_AMAZON_LINUX, + .colors = { + FF_COLOR_FG_WHITE, + FF_COLOR_FG_256 "178", + } }, // AmogOS { @@ -195,6 +213,17 @@ static const FFlogo A[] = { .lines = FASTFETCH_DATATEXT_LOGO_AOSCOS, .colors = { FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLACK, + FF_COLOR_FG_GREEN, + FF_COLOR_FG_YELLOW, + }, + }, + // AoscOS_old + { + .names = {"Aosc OS_old", "aoscos_old"}, + .lines = FASTFETCH_DATATEXT_LOGO_AOSCOS_OLD, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .colors = { FF_COLOR_FG_WHITE, }, .colorKeys = FF_COLOR_FG_BLUE, @@ -1170,6 +1199,16 @@ static const FFlogo D[] = { }; static const FFlogo E[] = { + // Elbrus + { + .names = {"elbrus"}, + .lines = FASTFETCH_DATATEXT_LOGO_ELBRUS, + .colors = { + FF_COLOR_FG_BLUE, + }, + .colorKeys = FF_COLOR_FG_BLUE, + .colorTitle = FF_COLOR_FG_BLUE, + }, // Elementary { .names = {"Elementary"}, @@ -1261,6 +1300,26 @@ static const FFlogo E[] = { { .names = {"EvolutionOS"}, .lines = FASTFETCH_DATATEXT_LOGO_EVOLUTIONOS, + .colors = { + FF_COLOR_FG_GREEN, + FF_COLOR_FG_WHITE, + }, + }, + // EvolutionOSSmall + { + .names = {"EvolutionOS_small"}, + .type = FF_LOGO_LINE_TYPE_SMALL_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_EVOLUTIONOS_SMALL, + .colors = { + FF_COLOR_FG_GREEN, + FF_COLOR_FG_WHITE, + }, + }, + // EvolutionOS_old + { + .names = {"EvolutionOS_old"}, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_EVOLUTIONOS_OLD, .colors = { FF_COLOR_FG_LIGHT_BLUE, FF_COLOR_FG_WHITE, @@ -2087,6 +2146,16 @@ static const FFlogo L[] = { .colorKeys = FF_COLOR_FG_GREEN, .colorTitle = FF_COLOR_FG_WHITE, }, + // LainOS + { + .names = {"LainOS"}, + .lines = FASTFETCH_DATATEXT_LOGO_LAINOS, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_256 "14", + FF_COLOR_FG_WHITE, + }, + }, // Lunar { .names = {"Lunar"}, @@ -2705,7 +2774,7 @@ static const FFlogo O[] = { }, // Oracle { - .names = {"oracle", "oracle linux"}, + .names = {"oracle", "oracle linux", "oracle linux server"}, .lines = FASTFETCH_DATATEXT_LOGO_ORACLE, .colors = { FF_COLOR_FG_RED, @@ -3453,7 +3522,7 @@ static const FFlogo S[] = { .lines = FASTFETCH_DATATEXT_LOGO_SLACKWARE, .colors = { FF_COLOR_FG_BLUE, - FF_COLOR_FG_BLUE, + FF_COLOR_FG_WHITE, }, .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, @@ -4014,6 +4083,9 @@ static const FFlogo W[] = { .lines = FASTFETCH_DATATEXT_LOGO_WINDOWS_11_SMALL, .colors = { FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, }, .colorKeys = FF_COLOR_FG_YELLOW, .colorTitle = FF_COLOR_FG_CYAN, diff --git a/src/logo/image/image.c b/src/logo/image/image.c index 9277ee3a5..953d12f8d 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -11,7 +11,7 @@ #include #endif -static FFstrbuf base64Encode(FFstrbuf* in) +static FFstrbuf base64Encode(const FFstrbuf* in) { const char* base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -37,55 +37,148 @@ static FFstrbuf base64Encode(FFstrbuf* in) static bool printImageIterm(void) { - if(instance.config.logo.width == 0 || instance.config.logo.height == 0) - { - fputs("Logo: `iterm` protocol only works when both `--logo-width` and `--logo-height` being specified\n", stderr); - return false; - } - + const FFLogoOptions* options = &instance.config.logo; FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); - if(!ffAppendFileBuffer(instance.config.logo.source.chars, &buf)) + if(!ffAppendFileBuffer(options->source.chars, &buf)) { fputs("Logo: Failed to load image file\n", stderr); return false; } - ffPrintCharTimes('\n', instance.config.logo.paddingTop); - ffPrintCharTimes(' ', instance.config.logo.paddingLeft); + fflush(stdout); FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&buf); - instance.state.logoWidth = instance.config.logo.width + instance.config.logo.paddingLeft + instance.config.logo.paddingRight; - instance.state.logoHeight = instance.config.logo.paddingTop + instance.config.logo.height; - printf("\033]1337;File=inline=1;width=%u;height=%u;preserveAspectRatio=%u:%s\a\033[9999999D\n\033[%uA", - (unsigned) instance.config.logo.width, - (unsigned) instance.config.logo.height, - (unsigned) instance.config.logo.preserveAspectRadio, - base64.chars, - (unsigned) instance.state.logoHeight - ); + ffStrbufClear(&buf); + + if (!options->width || !options->height) + { + if (!options->separate) + { + ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop, + (unsigned) options->paddingLeft + ); + } + else + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + } + ffStrbufAppendF(&buf, "\e]1337;File=inline=1:%s\a", + base64.chars + ); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + + if (!options->separate) + { + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", "\e[%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (iterm): fail to query cursor position: %s\n", error); + return true; // We already printed image logo, don't print ascii logo then + } + instance.state.logoWidth = X + options->paddingRight; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } + } + else + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + ffStrbufAppendF(&buf, "\e]1337;File=inline=1;width=%u;height=%u;preserveAspectRatio=%u:%s\a\n", + (unsigned) options->width, + (unsigned) options->height, + (unsigned) options->preserveAspectRadio, + base64.chars + ); + + if (!options->separate) + { + instance.state.logoWidth = options->width + options->paddingLeft + options->paddingRight; + instance.state.logoHeight = options->paddingTop + options->height; + ffStrbufAppendF(&buf, "\e[%uA", (unsigned) instance.state.logoHeight); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffStrbufAppendNC(&buf, options->paddingRight, '\n'); + } + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + } return true; } static bool printImageKittyDirect(void) { - if (!instance.config.logo.width || !instance.config.logo.height) - { - fputs("Logo: `kitty-direct` protocol only works when both `--logo-width` and `--logo-height` being specified\n", stderr); - return false; - } - ffPrintCharTimes('\n', instance.config.logo.paddingTop); - ffPrintCharTimes(' ', instance.config.logo.paddingLeft); + const FFLogoOptions* options = &instance.config.logo; + FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&options->source); - FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&instance.config.logo.source); - instance.state.logoWidth = instance.config.logo.width + instance.config.logo.paddingLeft + instance.config.logo.paddingRight; - instance.state.logoHeight = instance.config.logo.paddingTop + instance.config.logo.height; - printf("\033_Ga=T,f=100,t=f,c=%u,r=%u;%s\033\\\033[9999999D\n\033[%uA", - (unsigned) instance.config.logo.width, - (unsigned) instance.config.logo.height, - base64.chars, - (unsigned) instance.state.logoHeight - ); + if (!options->width || !options->height) + { + if (!options->separate) + { + // We must clear the entre screen to make sure that terminal buffer won't scroll up + printf("\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop, + (unsigned) options->paddingLeft + ); + } + else + { + ffPrintCharTimes('\n', options->paddingTop); + ffPrintCharTimes(' ', options->paddingLeft); + } + printf("\e_Ga=T,f=100,t=f;%s\e\\", base64.chars); + fflush(stdout); + if (!options->separate) + { + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", "\e[%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (kitty-direct): fail to query cursor position: %s\n", error); + return true; // We already printed image logo, don't print ascii logo then + } + instance.state.logoWidth = X + options->paddingRight; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } + } + else + { + ffPrintCharTimes('\n', options->paddingTop); + ffPrintCharTimes(' ', options->paddingLeft); + + printf("\e_Ga=T,f=100,t=f,c=%u,r=%u;%s\e\\\n", + (unsigned) options->width, + (unsigned) options->height, + base64.chars + ); + if (!options->separate) + { + instance.state.logoWidth = options->width + options->paddingLeft + options->paddingRight; + instance.state.logoHeight = options->paddingTop + options->height; + printf("\e[%uA", (unsigned) instance.state.logoHeight); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } + } return true; } @@ -203,31 +296,28 @@ static void writeCacheUint32(FFLogoRequestData* requestData, uint32_t value, con static void printImagePixels(FFLogoRequestData* requestData, const FFstrbuf* result, const char* cacheFileName) { + const FFLogoOptions* options = &instance.config.logo; //Calculate character dimensions - instance.state.logoWidth = requestData->logoCharacterWidth + instance.config.logo.paddingLeft + instance.config.logo.paddingRight; - - instance.state.logoHeight = requestData->logoCharacterHeight + instance.config.logo.paddingTop; - if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY) - instance.state.logoHeight -= 1; + instance.state.logoWidth = requestData->logoCharacterWidth + options->paddingLeft + options->paddingRight; + instance.state.logoHeight = requestData->logoCharacterHeight + options->paddingTop - 1; //Write cache files writeCacheStrbuf(requestData, result, cacheFileName); - if(instance.config.logo.width == 0) - writeCacheUint32(requestData, instance.state.logoWidth, FF_CACHE_FILE_WIDTH); + if(options->width == 0) + writeCacheUint32(requestData, requestData->logoCharacterWidth, FF_CACHE_FILE_WIDTH); - if(instance.config.logo.height == 0) - writeCacheUint32(requestData, instance.state.logoHeight, FF_CACHE_FILE_HEIGHT); + if(options->height == 0) + writeCacheUint32(requestData, requestData->logoCharacterHeight, FF_CACHE_FILE_HEIGHT); //Write result to stdout - ffPrintCharTimes('\n', instance.config.logo.paddingTop); - ffPrintCharTimes(' ', instance.config.logo.paddingLeft); + ffPrintCharTimes('\n', options->paddingTop); + ffPrintCharTimes(' ', options->paddingLeft); fflush(stdout); ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), result); //Go to upper left corner - fputs("\033[9999999D", stdout); - printf("\033[%uA", instance.state.logoHeight); + printf("\e[1G\e[%uA", instance.state.logoHeight); } static bool printImageSixel(FFLogoRequestData* requestData, const ImageData* imageData) @@ -533,10 +623,7 @@ static uint32_t readCachedUint32(FFLogoRequestData* requestData, const char* cac uint32_t result = 0; if(content.length != sizeof(result)) - { - ffStrbufDestroy(&content); return 0; - } memcpy(&result, content.chars, sizeof(result)); @@ -603,7 +690,7 @@ static bool printCachedPixel(FFLogoRequestData* requestData) instance.state.logoHeight = requestData->logoCharacterHeight + instance.config.logo.paddingTop; //Go to upper left corner - printf("\033[9999999D\033[%uA", instance.state.logoHeight); + printf("\e[1G\e[%uA", instance.state.logoHeight); return true; } diff --git a/src/logo/logo.c b/src/logo/logo.c index 9206a5ab1..e03ba7221 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -19,16 +19,38 @@ typedef enum FFLogoSize static void ffLogoPrintCharsRaw(const char* data, size_t length) { FFLogoOptions* options = &instance.config.logo; + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); - if (instance.config.brightColor) - fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); + if (!options->width || !options->height) + { + ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop, + (unsigned) options->paddingLeft + ); + ffStrbufAppendNS(&buf, (uint32_t) length, data); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); - ffPrintCharTimes('\n', options->paddingTop); - ffPrintCharTimes(' ', options->paddingLeft); - fwrite(data, length, 1, stdout); - instance.state.logoHeight = options->paddingTop + options->height; - instance.state.logoWidth = options->paddingLeft + options->width + options->paddingRight; - printf("\033[9999999D\n\033[%uA", instance.state.logoHeight); + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", "\e[%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (image-raw): fail to query cursor position: %s\n", error); + return; + } + instance.state.logoWidth = X + instance.config.logo.paddingRight; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + ffStrbufAppendNS(&buf, (uint32_t) length, data); + instance.state.logoHeight = options->paddingTop + options->height; + instance.state.logoWidth = options->paddingLeft + options->width + options->paddingRight; + ffStrbufAppendF(&buf, "\n\e[%uA", instance.state.logoHeight); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + } } void ffLogoPrintChars(const char* data, bool doColorReplacement) @@ -37,33 +59,35 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) uint32_t currentlineLength = 0; - if (instance.config.brightColor) - fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(2048); - ffPrintCharTimes('\n', options->paddingTop); - ffPrintCharTimes(' ', options->paddingLeft); + if (instance.config.brightColor) + ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_BOLT); + + ffStrbufAppendNC(&result, options->paddingTop, '\n'); + ffStrbufAppendNC(&result, options->paddingLeft, ' '); instance.state.logoHeight = options->paddingTop; //Use logoColor[0] as the default color if(doColorReplacement) - ffPrintColor(&options->colors[0]); + ffStrbufAppendF(&result, "\e[%sm", options->colors[0].chars); while(*data != '\0') { //We are at the end of a line. Print paddings and update max line length if(*data == '\n' || (*data == '\r' && *(data + 1) == '\n')) { - ffPrintCharTimes(' ', options->paddingRight); + ffStrbufAppendNC(&result, options->paddingRight, ' '); //We have \r\n, skip the \r if(*data == '\r') ++data; - putchar('\n'); + ffStrbufAppendC(&result, '\n'); ++data; - ffPrintCharTimes(' ', options->paddingLeft); + ffStrbufAppendNC(&result, options->paddingLeft, ' '); if(currentlineLength > instance.state.logoWidth) instance.state.logoWidth = currentlineLength; @@ -76,26 +100,26 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) //Always print tabs as 4 spaces, to have consistent spacing if(*data == '\t') { - ffPrintCharTimes(' ', 4); + ffStrbufAppendNC(&result, 4, ' '); ++data; continue; } //We have an escape sequence direclty as bytes. We print it, but don't increase the line length - if(*data == '\033' && *(data + 1) == '[') + if(*data == '\e' && *(data + 1) == '[') { const char* start = data; - fputs("\033[", stdout); + ffStrbufAppendS(&result, "\e["); data += 2; while(isdigit(*data) || *data == ';') - putchar(*data++); // number + ffStrbufAppendC(&result, *data++); // number //We have a valid control sequence, print it and continue with next char if(isascii(*data)) { - putchar(*data++); // single letter, end of control sequence + ffStrbufAppendC(&result, *data++); // single letter, end of control sequence continue; } @@ -113,7 +137,7 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) //If we have $$, or $\0, print it as single $ if(*data == '$' || *data == '\0') { - putchar('$'); + ffStrbufAppendC(&result, '$'); ++currentlineLength; ++data; continue; @@ -125,13 +149,13 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) //If the index is valid, print the color. Otherwise continue as normal if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS) { - putchar('$'); + ffStrbufAppendC(&result, '$'); ++currentlineLength; //Don't continue here, we want to print the current char as unicode } else { - ffPrintColor(&options->colors[index]); + ffStrbufAppendF(&result, "\e[%sm", options->colors[index].chars); ++data; continue; } @@ -160,34 +184,39 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) if(*data == '\0') break; - putchar(*data++); + ffStrbufAppendC(&result, *data++); } } - ffPrintCharTimes(' ', options->paddingRight); - fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); + ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_RESET); - //Happens if the last line is the longest - if(currentlineLength > instance.state.logoWidth) - instance.state.logoWidth = currentlineLength; + if(!options->separate) + { + //Happens if the last line is the longest + if(currentlineLength > instance.state.logoWidth) + instance.state.logoWidth = currentlineLength; - instance.state.logoWidth += options->paddingLeft + options->paddingRight; + instance.state.logoWidth += options->paddingLeft + options->paddingRight; - //Go to the leftmost position - fputs("\033[9999999D", stdout); + //Go to the leftmost position and go up the height + ffStrbufAppendF(&result, "\e[1G\e[%uA", instance.state.logoHeight); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffStrbufAppendNC(&result, options->paddingRight, '\n'); + } - //If the logo height is > 1, go up the height - if(instance.state.logoHeight > 0) - printf("\033[%uA", instance.state.logoHeight); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &result); } static void logoApplyColors(const FFlogo* logo) { - if(instance.config.colorKeys.length == 0) - ffStrbufAppendS(&instance.config.colorKeys, logo->colorKeys ? logo->colorKeys : logo->colors[0]); - if(instance.config.colorTitle.length == 0) - ffStrbufAppendS(&instance.config.colorTitle, logo->colorTitle ? logo->colorTitle : logo->colors[1]); + ffStrbufAppendS(&instance.config.colorTitle, logo->colorTitle ? logo->colorTitle : logo->colors[0]); + + if(instance.config.colorKeys.length == 0) + ffStrbufAppendS(&instance.config.colorKeys, logo->colorKeys ? logo->colorKeys : logo->colors[1]); } static bool logoHasName(const FFlogo* logo, const FFstrbuf* name, bool small) @@ -417,15 +446,7 @@ static bool logoTryKnownType(void) return logoPrintFileIfExists(false, false); if(options->type == FF_LOGO_TYPE_IMAGE_RAW) - { - if(options->width == 0 || options->height == 0) - { - fputs("both `--logo-width` and `--logo-height` must be specified\n", stderr); - return false; - } - return logoPrintFileIfExists(false, true); - } return logoPrintImageIfExists(options->type, true); } @@ -441,7 +462,7 @@ void ffLogoPrint(void) //In pipe mode, we don't have a logo or padding. //We also don't need to set main color, because it won't be printed anyway. //So we can return quickly here. - if(instance.config.pipe) + if(instance.config.pipe || instance.state.resultDoc) { instance.state.logoHeight = 0; instance.state.logoWidth = 0; diff --git a/src/logo/option.c b/src/logo/option.c index 59474a2d3..32114de22 100644 --- a/src/logo/option.c +++ b/src/logo/option.c @@ -17,6 +17,7 @@ void ffInitLogoOptions(FFLogoOptions* options) options->printRemaining = true; options->preserveAspectRadio = false; options->recache = false; + options->separate = false; options->chafaFgOnly = false; ffStrbufInitStatic(&options->chafaSymbols, "block+border+space-wide-inverted"); // Chafa default @@ -116,6 +117,8 @@ logoType: options->preserveAspectRadio = ffOptionParseBoolean(value); else if(strcasecmp(subKey, "recache") == 0) options->recache = ffOptionParseBoolean(value); + else if(strcasecmp(subKey, "separate") == 0) + options->separate = ffOptionParseBoolean(value); else return false; } @@ -342,6 +345,11 @@ const char* ffParseLogoJsonConfig(void) options->recache = yyjson_get_bool(val); continue; } + else if (strcasecmp(key, "separate") == 0) + { + options->separate = yyjson_get_bool(val); + continue; + } else if (strcasecmp(key, "chafa") == 0) { if (!yyjson_is_obj(val)) diff --git a/src/logo/option.h b/src/logo/option.h index 1eb41c1e9..9273e2a3f 100644 --- a/src/logo/option.h +++ b/src/logo/option.h @@ -36,6 +36,7 @@ typedef struct FFLogoOptions bool printRemaining; bool preserveAspectRadio; bool recache; + bool separate; bool chafaFgOnly; FFstrbuf chafaSymbols; diff --git a/src/modules/battery/battery.c b/src/modules/battery/battery.c index 4f239650a..85103b5c8 100644 --- a/src/modules/battery/battery.c +++ b/src/modules/battery/battery.c @@ -8,7 +8,7 @@ #define FF_BATTERY_NUM_FORMAT_ARGS 5 -static void printBattery(FFBatteryOptions* options, BatteryResult* result, uint8_t index) +static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uint8_t index) { if(options->moduleArgs.outputFormat.length == 0) { @@ -76,8 +76,7 @@ static void printBattery(FFBatteryOptions* options, BatteryResult* result, uint8 void ffPrintBattery(FFBatteryOptions* options) { - FFlist results; - ffListInitA(&results, sizeof(BatteryResult), 0); + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFBatteryResult)); const char* error = ffDetectBattery(options, &results); @@ -89,7 +88,7 @@ void ffPrintBattery(FFBatteryOptions* options) { for(uint8_t i = 0; i < (uint8_t) results.length; i++) { - BatteryResult* result = ffListGet(&results, i); + FFBatteryResult* result = ffListGet(&results, i); printBattery(options, result, i); ffStrbufDestroy(&result->manufacturer); @@ -100,13 +99,11 @@ void ffPrintBattery(FFBatteryOptions* options) if(results.length == 0) ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, "No batteries found"); } - - ffListDestroy(&results); } void ffInitBatteryOptions(FFBatteryOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BATTERY_MODULE_NAME, ffParseBatteryCommandOptions, ffParseBatteryJsonObject, ffPrintBattery); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BATTERY_MODULE_NAME, ffParseBatteryCommandOptions, ffParseBatteryJsonObject, ffPrintBattery, ffGenerateBatteryJson); ffOptionInitModuleArg(&options->moduleArgs); options->temp = false; @@ -178,3 +175,36 @@ void ffParseBatteryJsonObject(FFBatteryOptions* options, yyjson_val* module) ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBatteryJson(FFBatteryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFBatteryResult)); + + const char* error = ffDetectBattery(options, &results); + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFBatteryResult, battery, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_real(doc, obj, "capacity", battery->capacity); + yyjson_mut_obj_add_strbuf(doc, obj, "manufacturer", &battery->manufacturer); + yyjson_mut_obj_add_strbuf(doc, obj, "modelName", &battery->modelName); + yyjson_mut_obj_add_strbuf(doc, obj, "status", &battery->status); + yyjson_mut_obj_add_strbuf(doc, obj, "technology", &battery->technology); + yyjson_mut_obj_add_real(doc, obj, "temperature", battery->temperature); + } + + FF_LIST_FOR_EACH(FFBatteryResult, battery, results) + { + ffStrbufDestroy(&battery->manufacturer); + ffStrbufDestroy(&battery->modelName); + ffStrbufDestroy(&battery->technology); + ffStrbufDestroy(&battery->status); + } +} diff --git a/src/modules/battery/battery.h b/src/modules/battery/battery.h index 26312bca7..adefbdedd 100644 --- a/src/modules/battery/battery.h +++ b/src/modules/battery/battery.h @@ -10,3 +10,4 @@ void ffInitBatteryOptions(FFBatteryOptions* options); bool ffParseBatteryCommandOptions(FFBatteryOptions* options, const char* key, const char* value); void ffDestroyBatteryOptions(FFBatteryOptions* options); void ffParseBatteryJsonObject(FFBatteryOptions* options, yyjson_val* module); +void ffGenerateBatteryJson(FFBatteryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/bios/bios.c b/src/modules/bios/bios.c index ee4201795..b3f3e598c 100644 --- a/src/modules/bios/bios.c +++ b/src/modules/bios/bios.c @@ -55,7 +55,7 @@ exit: void ffInitBiosOptions(FFBiosOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BIOS_MODULE_NAME, ffParseBiosCommandOptions, ffParseBiosJsonObject, ffPrintBios); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BIOS_MODULE_NAME, ffParseBiosCommandOptions, ffParseBiosJsonObject, ffPrintBios, ffGenerateBiosJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -90,3 +90,38 @@ void ffParseBiosJsonObject(FFBiosOptions* options, yyjson_val* module) ffPrintError(FF_BIOS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBiosJson(FF_MAYBE_UNUSED FFBiosOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFBiosResult bios; + ffStrbufInit(&bios.date); + ffStrbufInit(&bios.release); + ffStrbufInit(&bios.vendor); + ffStrbufInit(&bios.version); + + const char* error = ffDetectBios(&bios); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if (bios.version.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "bios_version is not set."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "date", &bios.date); + yyjson_mut_obj_add_strbuf(doc, obj, "release", &bios.release); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &bios.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &bios.version); + +exit: + ffStrbufDestroy(&bios.date); + ffStrbufDestroy(&bios.release); + ffStrbufDestroy(&bios.vendor); + ffStrbufDestroy(&bios.version); +} diff --git a/src/modules/bios/bios.h b/src/modules/bios/bios.h index aaaf15d65..a3bb81d8d 100644 --- a/src/modules/bios/bios.h +++ b/src/modules/bios/bios.h @@ -9,3 +9,4 @@ void ffInitBiosOptions(FFBiosOptions* options); bool ffParseBiosCommandOptions(FFBiosOptions* options, const char* key, const char* value); void ffDestroyBiosOptions(FFBiosOptions* options); void ffParseBiosJsonObject(FFBiosOptions* options, yyjson_val* module); +void ffGenerateBiosJson(FFBiosOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/bluetooth/bluetooth.c b/src/modules/bluetooth/bluetooth.c index fa8b65d51..ade503bc1 100644 --- a/src/modules/bluetooth/bluetooth.c +++ b/src/modules/bluetooth/bluetooth.c @@ -6,7 +6,7 @@ #define FF_BLUETOOTH_NUM_FORMAT_ARGS 4 -static void printDevice(FFBluetoothOptions* options, const FFBluetoothDevice* device, uint8_t index) +static void printDevice(FFBluetoothOptions* options, const FFBluetoothResult* device, uint8_t index) { if(options->moduleArgs.outputFormat.length == 0) { @@ -34,7 +34,7 @@ static void printDevice(FFBluetoothOptions* options, const FFBluetoothDevice* de void ffPrintBluetooth(FFBluetoothOptions* options) { - FF_LIST_AUTO_DESTROY devices = ffListCreate(sizeof (FFBluetoothDevice)); + FF_LIST_AUTO_DESTROY devices = ffListCreate(sizeof (FFBluetoothResult)); const char* error = ffDetectBluetooth(&devices); if(error) @@ -43,14 +43,14 @@ void ffPrintBluetooth(FFBluetoothOptions* options) } else { - FF_LIST_AUTO_DESTROY filtered = ffListCreate(sizeof(FFBluetoothDevice*)); + FF_LIST_AUTO_DESTROY filtered = ffListCreate(sizeof(FFBluetoothResult*)); - FF_LIST_FOR_EACH(FFBluetoothDevice, device, devices) + FF_LIST_FOR_EACH(FFBluetoothResult, device, devices) { if(!device->connected && !options->showDisconnected) continue; - *(FFBluetoothDevice**)ffListAdd(&filtered) = device; + *(FFBluetoothResult**)ffListAdd(&filtered) = device; } if(filtered.length == 0) @@ -61,11 +61,11 @@ void ffPrintBluetooth(FFBluetoothOptions* options) for(uint32_t i = 0; i < filtered.length; i++) { uint8_t index = (uint8_t) (filtered.length == 1 ? 0 : i + 1); - printDevice(options, *(FFBluetoothDevice**)ffListGet(&filtered, i), index); + printDevice(options, *(FFBluetoothResult**)ffListGet(&filtered, i), index); } } - FF_LIST_FOR_EACH(FFBluetoothDevice, device, devices) + FF_LIST_FOR_EACH(FFBluetoothResult, device, devices) { ffStrbufDestroy(&device->name); ffStrbufDestroy(&device->type); @@ -75,7 +75,7 @@ void ffPrintBluetooth(FFBluetoothOptions* options) void ffInitBluetoothOptions(FFBluetoothOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BLUETOOTH_MODULE_NAME, ffParseBluetoothCommandOptions, ffParseBluetoothJsonObject, ffPrintBluetooth); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BLUETOOTH_MODULE_NAME, ffParseBluetoothCommandOptions, ffParseBluetoothJsonObject, ffPrintBluetooth, ffGenerateBluetoothJson); ffOptionInitModuleArg(&options->moduleArgs); options->showDisconnected = false; } @@ -123,3 +123,36 @@ void ffParseBluetoothJsonObject(FFBluetoothOptions* options, yyjson_val* module) ffPrintError(FF_BLUETOOTH_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBluetoothJson(FF_MAYBE_UNUSED FFBluetoothOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFBluetoothResult)); + + const char* error = ffDetectBluetooth(&results); + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + else + { + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFBluetoothResult, item, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "address", &item->address); + yyjson_mut_obj_add_uint(doc, obj, "battery", item->battery); + yyjson_mut_obj_add_bool(doc, obj, "connected", item->connected); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_strbuf(doc, obj, "type", &item->type); + } + } + + FF_LIST_FOR_EACH(FFBluetoothResult, device, results) + { + ffStrbufDestroy(&device->name); + ffStrbufDestroy(&device->type); + ffStrbufDestroy(&device->address); + } +} diff --git a/src/modules/bluetooth/bluetooth.h b/src/modules/bluetooth/bluetooth.h index 411bb3965..b4d1f0a89 100644 --- a/src/modules/bluetooth/bluetooth.h +++ b/src/modules/bluetooth/bluetooth.h @@ -9,3 +9,4 @@ void ffInitBluetoothOptions(FFBluetoothOptions* options); bool ffParseBluetoothCommandOptions(FFBluetoothOptions* options, const char* key, const char* value); void ffDestroyBluetoothOptions(FFBluetoothOptions* options); void ffParseBluetoothJsonObject(FFBluetoothOptions* options, yyjson_val* module); +void ffGenerateBluetoothJson(FFBluetoothOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/board/board.c b/src/modules/board/board.c index 29a676ca8..17e187ade 100644 --- a/src/modules/board/board.c +++ b/src/modules/board/board.c @@ -51,7 +51,7 @@ exit: void ffInitBoardOptions(FFBoardOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BOARD_MODULE_NAME, ffParseBoardCommandOptions, ffParseBoardJsonObject, ffPrintBoard); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BOARD_MODULE_NAME, ffParseBoardCommandOptions, ffParseBoardJsonObject, ffPrintBoard, NULL); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +86,35 @@ void ffParseBoardJsonObject(FFBoardOptions* options, yyjson_val* module) ffPrintError(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBoardJson(FF_MAYBE_UNUSED FFBoardOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFBoardResult board; + ffStrbufInit(&board.name); + ffStrbufInit(&board.vendor); + ffStrbufInit(&board.version); + + const char* error = ffDetectBoard(&board); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if (board.name.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "board_name is not set."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &board.name); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &board.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &board.version); + +exit: + ffStrbufDestroy(&board.name); + ffStrbufDestroy(&board.vendor); + ffStrbufDestroy(&board.version); +} diff --git a/src/modules/board/board.h b/src/modules/board/board.h index f5531368e..0e1dabbab 100644 --- a/src/modules/board/board.h +++ b/src/modules/board/board.h @@ -9,3 +9,4 @@ void ffInitBoardOptions(FFBoardOptions* options); bool ffParseBoardCommandOptions(FFBoardOptions* options, const char* key, const char* value); void ffDestroyBoardOptions(FFBoardOptions* options); void ffParseBoardJsonObject(FFBoardOptions* options, yyjson_val* module); +void ffGenerateBoardJson(FFBoardOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/break/break.c b/src/modules/break/break.c index 87821ee00..59c39c1d7 100644 --- a/src/modules/break/break.c +++ b/src/modules/break/break.c @@ -9,7 +9,7 @@ void ffPrintBreak(FF_MAYBE_UNUSED FFBreakOptions* options) void ffInitBreakOptions(FF_MAYBE_UNUSED FFBreakOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BREAK_MODULE_NAME, ffParseBreakCommandOptions, ffParseBreakJsonObject, ffPrintBreak); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BREAK_MODULE_NAME, ffParseBreakCommandOptions, ffParseBreakJsonObject, ffPrintBreak, NULL); } bool ffParseBreakCommandOptions(FF_MAYBE_UNUSED FFBreakOptions* options, FF_MAYBE_UNUSED const char* key, FF_MAYBE_UNUSED const char* value) diff --git a/src/modules/brightness/brightness.c b/src/modules/brightness/brightness.c index 560353749..261122bb0 100644 --- a/src/modules/brightness/brightness.c +++ b/src/modules/brightness/brightness.c @@ -5,7 +5,7 @@ #include "modules/brightness/brightness.h" #include "util/stringUtils.h" -#define FF_BRIGHTNESS_NUM_FORMAT_ARGS 2 +#define FF_BRIGHTNESS_NUM_FORMAT_ARGS 5 void ffPrintBrightness(FFBrightnessOptions* options) { @@ -43,6 +43,8 @@ void ffPrintBrightness(FFBrightnessOptions* options) }); } + const double percent = (item->current - item->min) / (item->max - item->min) * 100; + if(options->moduleArgs.outputFormat.length == 0) { FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); @@ -50,7 +52,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) if (instance.config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffAppendPercentBar(&str, item->value, 0, 100, 100); + ffAppendPercentBar(&str, percent, 0, 100, 100); } if(instance.config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) @@ -58,7 +60,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) if(str.length > 0) ffStrbufAppendC(&str, ' '); - ffAppendPercentNum(&str, item->value, 10, 10, str.length > 0); + ffAppendPercentNum(&str, percent, 10, 10, str.length > 0); } ffStrbufPutTo(&str, stdout); @@ -66,10 +68,13 @@ void ffPrintBrightness(FFBrightnessOptions* options) else { FF_STRBUF_AUTO_DESTROY valueStr = ffStrbufCreate(); - ffAppendPercentNum(&valueStr, item->value, 10, 10, false); + ffAppendPercentNum(&valueStr, percent, 10, 10, false); ffPrintFormatString(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_BRIGHTNESS_NUM_FORMAT_ARGS, (FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &valueStr}, {FF_FORMAT_ARG_TYPE_STRBUF, &item->name}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->max}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->min}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->current}, }); } @@ -81,7 +86,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) void ffInitBrightnessOptions(FFBrightnessOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BRIGHTNESS_MODULE_NAME, ffParseBrightnessCommandOptions, ffParseBrightnessJsonObject, ffPrintBrightness); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BRIGHTNESS_MODULE_NAME, ffParseBrightnessCommandOptions, ffParseBrightnessJsonObject, ffPrintBrightness, ffGenerateBrightnessJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -116,3 +121,39 @@ void ffParseBrightnessJsonObject(FFBrightnessOptions* options, yyjson_val* modul ffPrintError(FF_BRIGHTNESS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBrightnessJson(FF_MAYBE_UNUSED FFBrightnessOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFBrightnessResult)); + + const char* error = ffDetectBrightness(&result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(result.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No result is detected."); + return; + } + + yyjson_mut_val* arr = yyjson_mut_arr(doc); + yyjson_mut_obj_add_val(doc, module, "result", arr); + + FF_LIST_FOR_EACH(FFBrightnessResult, item, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_real(doc, obj, "max", item->max); + yyjson_mut_obj_add_real(doc, obj, "min", item->min); + yyjson_mut_obj_add_real(doc, obj, "current", item->current); + } + + FF_LIST_FOR_EACH(FFBrightnessResult, item, result) + { + ffStrbufDestroy(&item->name); + } +} diff --git a/src/modules/brightness/brightness.h b/src/modules/brightness/brightness.h index 1272e5d0f..6933c6500 100644 --- a/src/modules/brightness/brightness.h +++ b/src/modules/brightness/brightness.h @@ -9,3 +9,4 @@ void ffInitBrightnessOptions(FFBrightnessOptions* options); bool ffParseBrightnessCommandOptions(FFBrightnessOptions* options, const char* key, const char* value); void ffDestroyBrightnessOptions(FFBrightnessOptions* options); void ffParseBrightnessJsonObject(FFBrightnessOptions* options, yyjson_val* module); +void ffGenerateBrightnessJson(FFBrightnessOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/chassis/chassis.c b/src/modules/chassis/chassis.c index 02cd3e97d..28a0f557d 100644 --- a/src/modules/chassis/chassis.c +++ b/src/modules/chassis/chassis.c @@ -52,7 +52,7 @@ exit: void ffInitChassisOptions(FFChassisOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CHASSIS_MODULE_NAME, ffParseChassisCommandOptions, ffParseChassisJsonObject, ffPrintChassis); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CHASSIS_MODULE_NAME, ffParseChassisCommandOptions, ffParseChassisJsonObject, ffPrintChassis, ffGenerateChassisJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -87,3 +87,35 @@ void ffParseChassisJsonObject(FFChassisOptions* options, yyjson_val* module) ffPrintError(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateChassisJson(FF_MAYBE_UNUSED FFChassisOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFChassisResult result; + ffStrbufInit(&result.type); + ffStrbufInit(&result.vendor); + ffStrbufInit(&result.version); + + const char* error = ffDetectChassis(&result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if(result.type.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "chassis_type is not set by O.E.M."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "type", &result.type); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &result.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); + +exit: + ffStrbufDestroy(&result.type); + ffStrbufDestroy(&result.vendor); + ffStrbufDestroy(&result.version); +} diff --git a/src/modules/chassis/chassis.h b/src/modules/chassis/chassis.h index 5fe4bb62f..c5553f772 100644 --- a/src/modules/chassis/chassis.h +++ b/src/modules/chassis/chassis.h @@ -9,3 +9,4 @@ void ffInitChassisOptions(FFChassisOptions* options); bool ffParseChassisCommandOptions(FFChassisOptions* options, const char* key, const char* value); void ffDestroyChassisOptions(FFChassisOptions* options); void ffParseChassisJsonObject(FFChassisOptions* options, yyjson_val* module); +void ffGenerateChassisJson(FFChassisOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/colors/colors.c b/src/modules/colors/colors.c index 488413f21..4bcf9bafd 100644 --- a/src/modules/colors/colors.c +++ b/src/modules/colors/colors.c @@ -9,7 +9,7 @@ void ffPrintColors(FFColorsOptions* options) if(instance.config.pipe) return; - ffLogoPrintLine(); + ffPrintLogoAndKey(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if(options->paddingLeft > 0) ffPrintCharTimes(' ', options->paddingLeft); @@ -57,7 +57,9 @@ void ffPrintColors(FFColorsOptions* options) void ffInitColorsOptions(FFColorsOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COLORS_MODULE_NAME, ffParseColorsCommandOptions, ffParseColorsJsonObject, ffPrintColors); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COLORS_MODULE_NAME, ffParseColorsCommandOptions, ffParseColorsJsonObject, ffPrintColors, NULL); + ffOptionInitModuleArg(&options->moduleArgs); + ffStrbufSetStatic(&options->moduleArgs.key, " "); options->symbol = FF_COLORS_SYMBOL_BLOCK; options->paddingLeft = 0; } @@ -66,6 +68,8 @@ bool ffParseColorsCommandOptions(FFColorsOptions* options, const char* key, cons { const char* subKey = ffOptionTestPrefix(key, FF_COLORS_MODULE_NAME); if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; if (ffStrEqualsIgnCase(subKey, "symbol")) { @@ -92,6 +96,7 @@ bool ffParseColorsCommandOptions(FFColorsOptions* options, const char* key, cons void ffDestroyColorsOptions(FF_MAYBE_UNUSED FFColorsOptions* options) { + ffOptionDestroyModuleArg(&options->moduleArgs); } void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) @@ -104,6 +109,9 @@ void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) if(ffStrEqualsIgnCase(key, "type")) continue; + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + if (ffStrEqualsIgnCase(key, "symbol")) { int value; diff --git a/src/modules/colors/option.h b/src/modules/colors/option.h index a17029bd8..e51cea6f1 100644 --- a/src/modules/colors/option.h +++ b/src/modules/colors/option.h @@ -17,6 +17,8 @@ typedef enum FFColorsSymbol typedef struct FFColorsOptions { FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; + FFColorsSymbol symbol; uint32_t paddingLeft; } FFColorsOptions; diff --git a/src/modules/command/command.c b/src/modules/command/command.c index f19f1f27d..393aab97b 100644 --- a/src/modules/command/command.c +++ b/src/modules/command/command.c @@ -36,7 +36,7 @@ void ffPrintCommand(FFCommandOptions* options) void ffInitCommandOptions(FFCommandOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COMMAND_MODULE_NAME, ffParseCommandCommandOptions, ffParseCommandJsonObject, ffPrintCommand); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COMMAND_MODULE_NAME, ffParseCommandCommandOptions, ffParseCommandJsonObject, ffPrintCommand, ffGenerateCommandJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInitStatic(&options->shell, @@ -107,3 +107,32 @@ void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module) ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCommandJson(FF_MAYBE_UNUSED FFCommandOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + const char* error = ffProcessAppendStdOut(&result, (char* const[]){ + options->shell.chars, + #ifdef _WIN32 + "/c", + #else + "-c", + #endif + options->text.chars, + NULL + }); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(!result.length) + { + yyjson_mut_obj_add_str(doc, module, "error", "No result printed"); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &result); +} diff --git a/src/modules/command/command.h b/src/modules/command/command.h index 8ee5cc82c..ec8e2bab3 100644 --- a/src/modules/command/command.h +++ b/src/modules/command/command.h @@ -9,3 +9,4 @@ void ffInitCommandOptions(FFCommandOptions* options); bool ffParseCommandCommandOptions(FFCommandOptions* options, const char* key, const char* value); void ffDestroyCommandOptions(FFCommandOptions* options); void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module); +void ffGenerateCommandJson(FFCommandOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 00aec6ba8..3cd5a6cf7 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -79,7 +79,7 @@ void ffPrintCPU(FFCPUOptions* options) void ffInitCPUOptions(FFCPUOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPU_MODULE_NAME, ffParseCPUCommandOptions, ffParseCPUJsonObject, ffPrintCPU); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPU_MODULE_NAME, ffParseCPUCommandOptions, ffParseCPUJsonObject, ffPrintCPU, ffGenerateCPUJson); ffOptionInitModuleArg(&options->moduleArgs); options->temp = false; } @@ -127,3 +127,44 @@ void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module) ffPrintError(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCPUJson(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFCPUResult cpu; + cpu.temperature = FF_CPU_TEMP_UNSET; + cpu.coresPhysical = cpu.coresLogical = cpu.coresOnline = 0; + cpu.frequencyMax = cpu.frequencyMin = 0; + ffStrbufInit(&cpu.name); + ffStrbufInit(&cpu.vendor); + + const char* error = ffDetectCPU(options, &cpu); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else if(cpu.vendor.length == 0 && cpu.name.length == 0 && cpu.coresOnline <= 1) + { + yyjson_mut_obj_add_str(doc, module, "error", "No CPU detected"); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "cpu", &cpu.name); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &cpu.vendor); + + yyjson_mut_val* cores = yyjson_mut_obj_add_obj(doc, obj, "cores"); + yyjson_mut_obj_add_uint(doc, cores, "physical", cpu.coresPhysical); + yyjson_mut_obj_add_uint(doc, cores, "logical", cpu.coresLogical); + yyjson_mut_obj_add_uint(doc, cores, "online", cpu.coresOnline); + + yyjson_mut_val* frequency = yyjson_mut_obj_add_obj(doc, obj, "frequency"); + yyjson_mut_obj_add_real(doc, frequency, "min", cpu.frequencyMin); + yyjson_mut_obj_add_real(doc, frequency, "max", cpu.frequencyMax); + + yyjson_mut_obj_add_real(doc, obj, "temperature", cpu.temperature); + } + + ffStrbufDestroy(&cpu.name); + ffStrbufDestroy(&cpu.vendor); +} diff --git a/src/modules/cpu/cpu.h b/src/modules/cpu/cpu.h index acff0a13c..0a3e7c4c6 100644 --- a/src/modules/cpu/cpu.h +++ b/src/modules/cpu/cpu.h @@ -9,3 +9,4 @@ void ffInitCPUOptions(FFCPUOptions* options); bool ffParseCPUCommandOptions(FFCPUOptions* options, const char* key, const char* value); void ffDestroyCPUOptions(FFCPUOptions* options); void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module); +void ffGenerateCPUJson(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/cpuusage/cpuusage.c b/src/modules/cpuusage/cpuusage.c index 23b29e791..ca0909377 100644 --- a/src/modules/cpuusage/cpuusage.c +++ b/src/modules/cpuusage/cpuusage.c @@ -46,7 +46,7 @@ void ffPrintCPUUsage(FFCPUUsageOptions* options) void ffInitCPUUsageOptions(FFCPUUsageOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPUUSAGE_MODULE_NAME, ffParseCPUUsageCommandOptions, ffParseCPUUsageJsonObject, ffPrintCPUUsage); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPUUSAGE_MODULE_NAME, ffParseCPUUsageCommandOptions, ffParseCPUUsageJsonObject, ffPrintCPUUsage, ffGenerateCPUUsageJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -81,3 +81,16 @@ void ffParseCPUUsageJsonObject(FFCPUUsageOptions* options, yyjson_val* module) ffPrintError(FF_CPUUSAGE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCPUUsageJson(FF_MAYBE_UNUSED FFCPUUsageOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + double percentage = 0.0/0.0; + const char* error = ffGetCpuUsageResult(&percentage); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + yyjson_mut_obj_add_real(doc, module, "result", percentage); +} diff --git a/src/modules/cpuusage/cpuusage.h b/src/modules/cpuusage/cpuusage.h index 5a221f554..e31bc14c7 100644 --- a/src/modules/cpuusage/cpuusage.h +++ b/src/modules/cpuusage/cpuusage.h @@ -11,3 +11,4 @@ void ffInitCPUUsageOptions(FFCPUUsageOptions* options); bool ffParseCPUUsageCommandOptions(FFCPUUsageOptions* options, const char* key, const char* value); void ffDestroyCPUUsageOptions(FFCPUUsageOptions* options); void ffParseCPUUsageJsonObject(FFCPUUsageOptions* options, yyjson_val* module); +void ffGenerateCPUUsageJson(FFCPUUsageOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/cursor/cursor.c b/src/modules/cursor/cursor.c index c33e13d26..555ec0374 100644 --- a/src/modules/cursor/cursor.c +++ b/src/modules/cursor/cursor.c @@ -52,7 +52,7 @@ void ffPrintCursor(FFCursorOptions* options) void ffInitCursorOptions(FFCursorOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CURSOR_MODULE_NAME, ffParseCursorCommandOptions, ffParseCursorJsonObject, ffPrintCursor); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CURSOR_MODULE_NAME, ffParseCursorCommandOptions, ffParseCursorJsonObject, ffPrintCursor, ffGenerateCursorJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -87,3 +87,27 @@ void ffParseCursorJsonObject(FFCursorOptions* options, yyjson_val* module) ffPrintError(FF_CURSOR_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCursorJson(FF_MAYBE_UNUSED FFCursorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFCursorResult result; + ffStrbufInit(&result.error); + ffStrbufInit(&result.theme); + ffStrbufInit(&result.size); + + ffDetectCursor(&result); + + if (result.error.length) + { + yyjson_mut_obj_add_str(doc, module, "error", result.error.chars); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "theme", &result.theme); + yyjson_mut_obj_add_strbuf(doc, obj, "size", &result.size); + + ffStrbufDestroy(&result.error); + ffStrbufDestroy(&result.theme); + ffStrbufDestroy(&result.size); +} diff --git a/src/modules/cursor/cursor.h b/src/modules/cursor/cursor.h index ff849b547..02ed47e8f 100644 --- a/src/modules/cursor/cursor.h +++ b/src/modules/cursor/cursor.h @@ -9,3 +9,4 @@ void ffInitCursorOptions(FFCursorOptions* options); bool ffParseCursorCommandOptions(FFCursorOptions* options, const char* key, const char* value); void ffDestroyCursorOptions(FFCursorOptions* options); void ffParseCursorJsonObject(FFCursorOptions* options, yyjson_val* module); +void ffGenerateCursorJson(FFCursorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/custom/custom.c b/src/modules/custom/custom.c index b9406dcd3..6576cd3c5 100644 --- a/src/modules/custom/custom.c +++ b/src/modules/custom/custom.c @@ -12,15 +12,16 @@ void ffPrintCustom(FFCustomOptions* options) return; } - ffPrintLogoAndKey(options->moduleArgs.key.length == 0 ? NULL : FF_CUSTOM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_CUSTOM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&options->moduleArgs.outputFormat, stdout); puts(FASTFETCH_TEXT_MODIFIER_RESET); } void ffInitCustomOptions(FFCustomOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CUSTOM_MODULE_NAME, ffParseCustomCommandOptions, ffParseCustomJsonObject, ffPrintCustom); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CUSTOM_MODULE_NAME, ffParseCustomCommandOptions, ffParseCustomJsonObject, ffPrintCustom, NULL); ffOptionInitModuleArg(&options->moduleArgs); + ffStrbufSetStatic(&options->moduleArgs.key, " "); } bool ffParseCustomCommandOptions(FFCustomOptions* options, const char* key, const char* value) diff --git a/src/modules/datetime/datetime.c b/src/modules/datetime/datetime.c index 9d6307a13..adc7c50cf 100644 --- a/src/modules/datetime/datetime.c +++ b/src/modules/datetime/datetime.c @@ -1,57 +1,149 @@ +#include "common/time.h" #include "common/printing.h" #include "common/jsonconfig.h" -#include "detection/datetime/datetime.h" #include "modules/datetime/datetime.h" #include "util/stringUtils.h" +#include + #define FF_DATETIME_DISPLAY_NAME "Date & Time" #define FF_DATETIME_NUM_FORMAT_ARGS 20 -void ffPrintDateTimeFormat(const char* moduleName, const FFModuleArgs* moduleArgs) +typedef struct FFDateTimeResult { - const FFDateTimeResult* result = ffDetectDateTime(); - ffPrintFormat(moduleName, 0, moduleArgs, FF_DATETIME_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_UINT16, &result->year}, // 1 - {FF_FORMAT_ARG_TYPE_UINT8, &result->yearShort}, // 2 - {FF_FORMAT_ARG_TYPE_UINT8, &result->month}, // 3 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->monthPretty}, // 4 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->monthName}, // 5 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->monthNameShort}, // 6 - {FF_FORMAT_ARG_TYPE_UINT8, &result->week}, // 7 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->weekday}, // 8 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->weekdayShort}, // 9 - {FF_FORMAT_ARG_TYPE_UINT16, &result->dayInYear}, // 10 - {FF_FORMAT_ARG_TYPE_UINT8, &result->dayInMonth}, // 11 - {FF_FORMAT_ARG_TYPE_UINT8, &result->dayInWeek}, // 12 - {FF_FORMAT_ARG_TYPE_UINT8, &result->hour}, // 13 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->hourPretty}, // 14 - {FF_FORMAT_ARG_TYPE_UINT8, &result->hour12}, // 15 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->hour12Pretty}, // 16 - {FF_FORMAT_ARG_TYPE_UINT8, &result->minute}, // 17 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->minutePretty}, // 18 - {FF_FORMAT_ARG_TYPE_UINT8, &result->second}, // 19 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->secondPretty} // 20 + //Examples for 21.02.2022 - 15:18:37 + uint16_t year; //2022 + uint8_t yearShort; //22 + uint8_t month; //2 + FFstrbuf monthPretty; //02 + FFstrbuf monthName; //February + FFstrbuf monthNameShort; //Feb + uint8_t week; //8 + FFstrbuf weekday; //Monday + FFstrbuf weekdayShort; //Mon + uint16_t dayInYear; //52 + uint8_t dayInMonth; //21 + uint8_t dayInWeek; //1 + uint8_t hour; //15 + FFstrbuf hourPretty; //15 + uint8_t hour12; //3 + FFstrbuf hour12Pretty; //03 + uint8_t minute; //18 + FFstrbuf minutePretty; //18 + uint8_t second; //37 + FFstrbuf secondPretty; //37 +} FFDateTimeResult; + +void ffPrintDateTimeFormat(struct tm* tm, const FFModuleArgs* moduleArgs) +{ + FFDateTimeResult result; + + result.year = (uint16_t) (tm->tm_year + 1900); + result.yearShort = (uint8_t) (result.year % 100); + result.month = (uint8_t) (tm->tm_mon + 1); + + ffStrbufInitA(&result.monthPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.monthPretty.length = (uint32_t) strftime(result.monthPretty.chars, ffStrbufGetFree(&result.monthPretty), "%m", tm); + + ffStrbufInitA(&result.monthName, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.monthName.length = (uint32_t) strftime(result.monthName.chars, ffStrbufGetFree(&result.monthName), "%B", tm); + + ffStrbufInitA(&result.monthNameShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.monthNameShort.length = (uint32_t) strftime(result.monthNameShort.chars, ffStrbufGetFree(&result.monthNameShort), "%b", tm); + + result.week = (uint8_t) (tm->tm_yday / 7 + 1); + + ffStrbufInitA(&result.weekday, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.weekday.length = (uint32_t) strftime(result.weekday.chars, ffStrbufGetFree(&result.weekday), "%A", tm); + + ffStrbufInitA(&result.weekdayShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.weekdayShort.length = (uint32_t) strftime(result.weekdayShort.chars, ffStrbufGetFree(&result.weekdayShort), "%a", tm); + + result.dayInYear = (uint8_t) (tm->tm_yday + 1); + result.dayInMonth = (uint8_t) tm->tm_mday; + result.dayInWeek = tm->tm_wday == 0 ? 7 : (uint8_t) tm->tm_wday; + + result.hour = (uint8_t) tm->tm_hour; + + ffStrbufInitA(&result.hourPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.hourPretty.length = (uint32_t) strftime(result.hourPretty.chars, ffStrbufGetFree(&result.hourPretty), "%H", tm); + + result.hour12 = (uint8_t) (result.hour % 12); + + ffStrbufInitA(&result.hour12Pretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.hour12Pretty.length = (uint32_t) strftime(result.hour12Pretty.chars, ffStrbufGetFree(&result.hour12Pretty), "%I", tm); + + result.minute = (uint8_t) tm->tm_min; + + ffStrbufInitA(&result.minutePretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.minutePretty.length = (uint32_t) strftime(result.minutePretty.chars, ffStrbufGetFree(&result.minutePretty), "%M", tm); + + result.second = (uint8_t) tm->tm_sec; + + ffStrbufInitA(&result.secondPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.secondPretty.length = (uint32_t) strftime(result.secondPretty.chars, ffStrbufGetFree(&result.secondPretty), "%S", tm); + + ffPrintFormat(FF_DATETIME_DISPLAY_NAME, 0, moduleArgs, FF_DATETIME_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_UINT16, &result.year}, // 1 + {FF_FORMAT_ARG_TYPE_UINT8, &result.yearShort}, // 2 + {FF_FORMAT_ARG_TYPE_UINT8, &result.month}, // 3 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.monthPretty}, // 4 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.monthName}, // 5 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.monthNameShort}, // 6 + {FF_FORMAT_ARG_TYPE_UINT8, &result.week}, // 7 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.weekday}, // 8 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.weekdayShort}, // 9 + {FF_FORMAT_ARG_TYPE_UINT16, &result.dayInYear}, // 10 + {FF_FORMAT_ARG_TYPE_UINT8, &result.dayInMonth}, // 11 + {FF_FORMAT_ARG_TYPE_UINT8, &result.dayInWeek}, // 12 + {FF_FORMAT_ARG_TYPE_UINT8, &result.hour}, // 13 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.hourPretty}, // 14 + {FF_FORMAT_ARG_TYPE_UINT8, &result.hour12}, // 15 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.hour12Pretty}, // 16 + {FF_FORMAT_ARG_TYPE_UINT8, &result.minute}, // 17 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.minutePretty}, // 18 + {FF_FORMAT_ARG_TYPE_UINT8, &result.second}, // 19 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.secondPretty} // 20 }); + + ffStrbufDestroy(&result.hour12Pretty); + ffStrbufDestroy(&result.hourPretty); + ffStrbufDestroy(&result.minutePretty); + ffStrbufDestroy(&result.monthName); + ffStrbufDestroy(&result.monthNameShort); + ffStrbufDestroy(&result.monthPretty); + ffStrbufDestroy(&result.secondPretty); + ffStrbufDestroy(&result.weekday); + ffStrbufDestroy(&result.weekdayShort); } void ffPrintDateTime(FFDateTimeOptions* options) { + uint64_t msNow = ffTimeGetNow(); + time_t sNow = (time_t) (msNow / 1000); + struct tm* tm = localtime(&sNow); + if(options->moduleArgs.outputFormat.length > 0) { - ffPrintDateTimeFormat(FF_DATETIME_DISPLAY_NAME, &options->moduleArgs); + ffPrintDateTimeFormat(tm, &options->moduleArgs); + return; + } + + char buffer[32]; + if (strftime(buffer, sizeof(buffer), "%F %T", tm) == 0) //yyyy-MM-dd HH:mm:ss + { + ffPrintError(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, "strftime() failed"); return; } - const FFDateTimeResult* datetime = ffDetectDateTime(); ffPrintLogoAndKey(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - //yyyy-MM-dd HH:mm:ss - printf("%u-%s-%02u %s:%s:%s\n", datetime->year, datetime->monthPretty.chars, datetime->dayInMonth, datetime->hourPretty.chars, datetime->minutePretty.chars, datetime->secondPretty.chars); + puts(buffer); } void ffInitDateTimeOptions(FFDateTimeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DATETIME_MODULE_NAME, ffParseDateTimeCommandOptions, ffParseDateTimeJsonObject, ffPrintDateTime); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DATETIME_MODULE_NAME, ffParseDateTimeCommandOptions, ffParseDateTimeJsonObject, ffPrintDateTime, ffGenerateDateTimeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +178,8 @@ void ffParseDateTimeJsonObject(FFDateTimeOptions* options, yyjson_val* module) ffPrintError(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDateTimeJson(FF_MAYBE_UNUSED FFDateTimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + yyjson_mut_obj_add_uint(doc, module, "result", ffTimeGetNow()); +} diff --git a/src/modules/datetime/datetime.h b/src/modules/datetime/datetime.h index 6ccb30b27..8f4ceb690 100644 --- a/src/modules/datetime/datetime.h +++ b/src/modules/datetime/datetime.h @@ -9,3 +9,4 @@ void ffInitDateTimeOptions(FFDateTimeOptions* options); bool ffParseDateTimeCommandOptions(FFDateTimeOptions* options, const char* key, const char* value); void ffDestroyDateTimeOptions(FFDateTimeOptions* options); void ffParseDateTimeJsonObject(FFDateTimeOptions* options, yyjson_val* module); +void ffGenerateDateTimeJson(FFDateTimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/de/de.c b/src/modules/de/de.c index a44f437d7..a6d896d7c 100644 --- a/src/modules/de/de.c +++ b/src/modules/de/de.c @@ -42,7 +42,7 @@ void ffPrintDE(FFDEOptions* options) void ffInitDEOptions(FFDEOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DE_MODULE_NAME, ffParseDECommandOptions, ffParseDEJsonObject, ffPrintDE); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DE_MODULE_NAME, ffParseDECommandOptions, ffParseDEJsonObject, ffPrintDE, ffGenerateDEJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -77,3 +77,19 @@ void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module) ffPrintError(FF_DE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDEJson(FF_MAYBE_UNUSED FFDEOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFDisplayServerResult* result = ffConnectDisplayServer(); + + if(result->dePrettyName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No DE found"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->deProcessName); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->dePrettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->deVersion); +} diff --git a/src/modules/de/de.h b/src/modules/de/de.h index 0a4e14541..b8d122184 100644 --- a/src/modules/de/de.h +++ b/src/modules/de/de.h @@ -9,3 +9,4 @@ void ffInitDEOptions(FFDEOptions* options); bool ffParseDECommandOptions(FFDEOptions* options, const char* key, const char* value); void ffDestroyDEOptions(FFDEOptions* options); void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module); +void ffGenerateDEJson(FFDEOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/disk/disk.c b/src/modules/disk/disk.c index 202d8e1a8..e6c976f0e 100644 --- a/src/modules/disk/disk.c +++ b/src/modules/disk/disk.c @@ -6,7 +6,7 @@ #include "modules/disk/disk.h" #include "util/stringUtils.h" -#define FF_DISK_NUM_FORMAT_ARGS 10 +#define FF_DISK_NUM_FORMAT_ARGS 11 #pragma GCC diagnostic ignored "-Wsign-conversion" static void printDisk(FFDiskOptions* options, const FFDisk* disk) @@ -111,6 +111,19 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) bool isExternal = !!(disk->type & FF_DISK_VOLUME_TYPE_EXTERNAL_BIT); bool isHidden = !!(disk->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT); bool isReadOnly = !!(disk->type & FF_DISK_VOLUME_TYPE_READONLY_BIT); + const char* physicalType; + switch(disk->physicalType) + { + case FF_DISK_PHYSICAL_TYPE_HDD: + physicalType = "HDD"; + break; + case FF_DISK_PHYSICAL_TYPE_SSD: + physicalType = "SSD"; + break; + default: + physicalType = "Unknown"; + break; + } ffPrintFormatString(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_DISK_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, @@ -123,6 +136,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) {FF_FORMAT_ARG_TYPE_STRBUF, &disk->filesystem}, {FF_FORMAT_ARG_TYPE_STRBUF, &disk->name}, {FF_FORMAT_ARG_TYPE_BOOL, &isReadOnly}, + {FF_FORMAT_ARG_TYPE_STRING, physicalType} }); } } @@ -194,6 +208,7 @@ void ffPrintDisk(FFDiskOptions* options) FF_LIST_FOR_EACH(FFDisk, disk, disks) { + ffStrbufDestroy(&disk->mountFrom); ffStrbufDestroy(&disk->mountpoint); ffStrbufDestroy(&disk->filesystem); ffStrbufDestroy(&disk->name); @@ -202,7 +217,7 @@ void ffPrintDisk(FFDiskOptions* options) void ffInitDiskOptions(FFDiskOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISK_MODULE_NAME, ffParseDiskCommandOptions, ffParseDiskJsonObject, ffPrintDisk); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISK_MODULE_NAME, ffParseDiskCommandOptions, ffParseDiskJsonObject, ffPrintDisk, ffGenerateDiskJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInit(&options->folders); @@ -370,3 +385,69 @@ void ffParseDiskJsonObject(FFDiskOptions* options, yyjson_val* module) ffPrintError(FF_DISK_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDiskJson(FFDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY disks = ffListCreate(sizeof (FFDisk)); + const char* error = ffDetectDisks(options, &disks); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "result", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFDisk, item, disks) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + + yyjson_mut_val* bytes = yyjson_mut_obj_add_obj(doc, obj, "bytes"); + yyjson_mut_obj_add_uint(doc, bytes, "available", item->bytesAvailable); + yyjson_mut_obj_add_uint(doc, bytes, "free", item->bytesFree); + yyjson_mut_obj_add_uint(doc, bytes, "total", item->bytesTotal); + yyjson_mut_obj_add_uint(doc, bytes, "used", item->bytesUsed); + + yyjson_mut_val* files = yyjson_mut_obj_add_obj(doc, obj, "files"); + yyjson_mut_obj_add_uint(doc, files, "total", item->filesTotal); + yyjson_mut_obj_add_uint(doc, files, "used", item->filesUsed); + + yyjson_mut_obj_add_strbuf(doc, obj, "filesystem", &item->filesystem); + yyjson_mut_obj_add_strbuf(doc, obj, "mountpoint", &item->mountpoint); + yyjson_mut_obj_add_strbuf(doc, obj, "mountFrom", &item->mountFrom); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_val* typeArr = yyjson_mut_obj_add_arr(doc, obj, "volumeType"); + if(item->type & FF_DISK_VOLUME_TYPE_REGULAR_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Regular"); + if(item->type & FF_DISK_VOLUME_TYPE_EXTERNAL_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "External"); + if(item->type & FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Subvolume"); + if(item->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Hidden"); + if(item->type & FF_DISK_VOLUME_TYPE_READONLY_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Read-only"); + + switch(item->physicalType) + { + case FF_DISK_PHYSICAL_TYPE_HDD: + yyjson_mut_obj_add_str(doc, obj, "physicalType", "HDD"); + break; + case FF_DISK_PHYSICAL_TYPE_SSD: + yyjson_mut_obj_add_str(doc, obj, "physicalType", "SSD"); + break; + default: + yyjson_mut_obj_add_null(doc, obj, "physicalType"); + break; + } + } + + FF_LIST_FOR_EACH(FFDisk, item, disks) + { + ffStrbufDestroy(&item->mountpoint); + ffStrbufDestroy(&item->mountFrom); + ffStrbufDestroy(&item->filesystem); + ffStrbufDestroy(&item->name); + } +} diff --git a/src/modules/disk/disk.h b/src/modules/disk/disk.h index 1f71f766b..eb4520b35 100644 --- a/src/modules/disk/disk.h +++ b/src/modules/disk/disk.h @@ -9,3 +9,4 @@ void ffInitDiskOptions(FFDiskOptions* options); bool ffParseDiskCommandOptions(FFDiskOptions* options, const char* key, const char* value); void ffDestroyDiskOptions(FFDiskOptions* options); void ffParseDiskJsonObject(FFDiskOptions* options, yyjson_val* module); +void ffGenerateDiskJson(FFDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/display/display.c b/src/modules/display/display.c index 31371de42..f623bc5f4 100644 --- a/src/modules/display/display.c +++ b/src/modules/display/display.c @@ -85,6 +85,9 @@ void ffPrintDisplay(FFDisplayOptions* options) result->scaledHeight > 0 && result->scaledHeight != result->height) printf(" (as %ix%i)", result->scaledWidth, result->scaledHeight); + if(result->type != FF_DISPLAY_TYPE_UNKNOWN) + fputs(result->type == FF_DISPLAY_TYPE_BUILTIN ? " [Built-in]" : " [External]", stdout); + if(moduleIndex > 0 && result->primary) printf(" *"); @@ -109,7 +112,7 @@ void ffPrintDisplay(FFDisplayOptions* options) void ffInitDisplayOptions(FFDisplayOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISPLAY_MODULE_NAME, ffParseDisplayCommandOptions, ffParseDisplayJsonObject, ffPrintDisplay); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISPLAY_MODULE_NAME, ffParseDisplayCommandOptions, ffParseDisplayJsonObject, ffPrintDisplay, ffGenerateDisplayJson); ffOptionInitModuleArg(&options->moduleArgs); options->compactType = FF_DISPLAY_COMPACT_TYPE_NONE; options->preciseRefreshRate = false; @@ -185,3 +188,40 @@ void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module) ffPrintError(FF_DISPLAY_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDisplayJson(FF_MAYBE_UNUSED FFDisplayOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFDisplayServerResult* dsResult = ffConnectDisplayServer(); + + if(dsResult->displays.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect display"); + return; + } + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFDisplayResult, item, dsResult->displays) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_uint(doc, obj, "width", item->width); + yyjson_mut_obj_add_uint(doc, obj, "height", item->height); + yyjson_mut_obj_add_uint(doc, obj, "id", item->id); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_bool(doc, obj, "primary", item->primary); + yyjson_mut_obj_add_real(doc, obj, "refreshRate", item->refreshRate); + yyjson_mut_obj_add_uint(doc, obj, "rotation", item->rotation); + yyjson_mut_obj_add_uint(doc, obj, "scaledHeight", item->scaledHeight); + yyjson_mut_obj_add_uint(doc, obj, "scaledWidth", item->scaledWidth); + switch (item->type) + { + case FF_DISPLAY_TYPE_BUILTIN: + yyjson_mut_obj_add_str(doc, obj, "type", "Builtin"); + break; + case FF_DISPLAY_TYPE_EXTERNAL: + yyjson_mut_obj_add_str(doc, obj, "type", "External"); + break; + default: + yyjson_mut_obj_add_str(doc, obj, "type", "Unknown"); + break; + } + } +} diff --git a/src/modules/display/display.h b/src/modules/display/display.h index b2f757056..6c46dfb18 100644 --- a/src/modules/display/display.h +++ b/src/modules/display/display.h @@ -9,3 +9,4 @@ void ffInitDisplayOptions(FFDisplayOptions* options); bool ffParseDisplayCommandOptions(FFDisplayOptions* options, const char* key, const char* value); void ffDestroyDisplayOptions(FFDisplayOptions* options); void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module); +void ffGenerateDisplayJson(FFDisplayOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/font/font.c b/src/modules/font/font.c index 97c77359b..a737c615e 100644 --- a/src/modules/font/font.c +++ b/src/modules/font/font.c @@ -45,7 +45,7 @@ void ffPrintFont(FFFontOptions* options) void ffInitFontOptions(FFFontOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_FONT_MODULE_NAME, ffParseFontCommandOptions, ffParseFontJsonObject, ffPrintFont); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_FONT_MODULE_NAME, ffParseFontCommandOptions, ffParseFontJsonObject, ffPrintFont, ffGenerateFontJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -80,3 +80,29 @@ void ffParseFontJsonObject(FFFontOptions* options, yyjson_val* module) ffPrintError(FF_FONT_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateFontJson(FF_MAYBE_UNUSED FFFontOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFFontResult font; + for(uint32_t i = 0; i < FF_DETECT_FONT_NUM_FONTS; ++i) + ffStrbufInit(&font.fonts[i]); + ffStrbufInit(&font.display); + + const char* error = ffDetectFont(&font); + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "display", &font.display); + yyjson_mut_val* fontsArr = yyjson_mut_obj_add_arr(doc, obj, "fonts"); + for (uint32_t i = 0; i < FF_DETECT_FONT_NUM_FONTS; ++i) + yyjson_mut_arr_add_strbuf(doc, fontsArr, &font.fonts[i]); + } + + ffStrbufDestroy(&font.display); + for (uint32_t i = 0; i < FF_DETECT_FONT_NUM_FONTS; ++i) + ffStrbufDestroy(&font.fonts[i]); +} diff --git a/src/modules/font/font.h b/src/modules/font/font.h index a51f9d447..dedb1dfb9 100644 --- a/src/modules/font/font.h +++ b/src/modules/font/font.h @@ -9,3 +9,4 @@ void ffInitFontOptions(FFFontOptions* options); bool ffParseFontCommandOptions(FFFontOptions* options, const char* key, const char* value); void ffDestroyFontOptions(FFFontOptions* options); void ffParseFontJsonObject(FFFontOptions* options, yyjson_val* module); +void ffGenerateFontJson(FFFontOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/gamepad/gamepad.c b/src/modules/gamepad/gamepad.c index 3691b5ac8..43818aa2f 100644 --- a/src/modules/gamepad/gamepad.c +++ b/src/modules/gamepad/gamepad.c @@ -51,7 +51,7 @@ void ffPrintGamepad(FFGamepadOptions* options) void ffInitGamepadOptions(FFGamepadOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GAMEPAD_MODULE_NAME, ffParseGamepadCommandOptions, ffParseGamepadJsonObject, ffPrintGamepad); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GAMEPAD_MODULE_NAME, ffParseGamepadCommandOptions, ffParseGamepadJsonObject, ffPrintGamepad, ffGenerateGamepadJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +86,36 @@ void ffParseGamepadJsonObject(FFGamepadOptions* options, yyjson_val* module) ffPrintError(FF_GAMEPAD_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateGamepadJson(FF_MAYBE_UNUSED FFGamepadOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFGamepadDevice)); + + const char* error = ffDetectGamepad(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(!result.length) + { + yyjson_mut_obj_add_str(doc, module, "error", "No devices detected"); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFGamepadDevice, device, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "identifier", &device->identifier); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &device->name); + } + + FF_LIST_FOR_EACH(FFGamepadDevice, device, result) + { + ffStrbufDestroy(&device->identifier); + ffStrbufDestroy(&device->name); + } +} diff --git a/src/modules/gamepad/gamepad.h b/src/modules/gamepad/gamepad.h index eae8a8a17..eff98e992 100644 --- a/src/modules/gamepad/gamepad.h +++ b/src/modules/gamepad/gamepad.h @@ -9,3 +9,4 @@ void ffInitGamepadOptions(FFGamepadOptions* options); bool ffParseGamepadCommandOptions(FFGamepadOptions* options, const char* key, const char* value); void ffDestroyGamepadOptions(FFGamepadOptions* options); void ffParseGamepadJsonObject(FFGamepadOptions* options, yyjson_val* module); +void ffGenerateGamepadJson(FFGamepadOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/gpu/gpu.c b/src/modules/gpu/gpu.c index 5f71779a7..95a25df3b 100644 --- a/src/modules/gpu/gpu.c +++ b/src/modules/gpu/gpu.c @@ -121,7 +121,7 @@ void ffPrintGPU(FFGPUOptions* options) void ffInitGPUOptions(FFGPUOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GPU_MODULE_NAME, ffParseGPUCommandOptions, ffParseGPUJsonObject, ffPrintGPU); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GPU_MODULE_NAME, ffParseGPUCommandOptions, ffParseGPUJsonObject, ffPrintGPU, ffGenerateGPUJson); ffOptionInitModuleArg(&options->moduleArgs); options->forceVulkan = false; @@ -210,3 +210,70 @@ void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module) ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateGPUJson(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY gpus = ffListCreate(sizeof (FFGPUResult)); + const char* error = ffDetectGPU(options, &gpus); + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFGPUResult, gpu, gpus) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + if (gpu->coreCount != FF_GPU_CORE_COUNT_UNSET) + yyjson_mut_obj_add_int(doc, obj, "coreCount", gpu->coreCount); + else + yyjson_mut_obj_add_null(doc, obj, "coreCount"); + + yyjson_mut_val* memoryObj = yyjson_mut_obj_add_obj(doc, obj, "memory"); + + yyjson_mut_val* dedicatedObj = yyjson_mut_obj_add_obj(doc, memoryObj, "dedicated"); + if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedObj, "total", gpu->dedicated.total); + else + yyjson_mut_obj_add_null(doc, dedicatedObj, "total"); + + if (gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedObj, "used", gpu->dedicated.used); + else + yyjson_mut_obj_add_null(doc, dedicatedObj, "used"); + + yyjson_mut_obj_add_strbuf(doc, obj, "driver", &gpu->driver); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &gpu->name); + + yyjson_mut_val* sharedObj = yyjson_mut_obj_add_obj(doc, memoryObj, "shared"); + if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedObj, "total", gpu->shared.total); + else + yyjson_mut_obj_add_null(doc, sharedObj, "total"); + if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedObj, "used", gpu->shared.used); + else + yyjson_mut_obj_add_null(doc, sharedObj, "used"); + + yyjson_mut_obj_add_null(doc, obj, "temperature"); + + const char* type; + switch (gpu->type) + { + case FF_GPU_TYPE_INTEGRATED: type = "Integrated"; break; + case FF_GPU_TYPE_DISCRETE: type = "Discrete"; break; + default: type = "Unknown"; break; + } + yyjson_mut_obj_add_str(doc, obj, "type", type); + + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &gpu->vendor); + } + + FF_LIST_FOR_EACH(FFGPUResult, gpu, gpus) + { + ffStrbufDestroy(&gpu->vendor); + ffStrbufDestroy(&gpu->name); + ffStrbufDestroy(&gpu->driver); + } +} diff --git a/src/modules/gpu/gpu.h b/src/modules/gpu/gpu.h index b5334730a..73bd7fad5 100644 --- a/src/modules/gpu/gpu.h +++ b/src/modules/gpu/gpu.h @@ -9,3 +9,4 @@ void ffInitGPUOptions(FFGPUOptions* options); bool ffParseGPUCommandOptions(FFGPUOptions* options, const char* key, const char* value); void ffDestroyGPUOptions(FFGPUOptions* options); void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module); +void ffGenerateGPUJson(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/host/host.c b/src/modules/host/host.c index d281ece1b..30df24f12 100644 --- a/src/modules/host/host.c +++ b/src/modules/host/host.c @@ -67,7 +67,7 @@ exit: void ffInitHostOptions(FFHostOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_HOST_MODULE_NAME, ffParseHostCommandOptions, ffParseHostJsonObject, ffPrintHost); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_HOST_MODULE_NAME, ffParseHostCommandOptions, ffParseHostJsonObject, ffPrintHost, ffGenerateHostJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -102,3 +102,40 @@ void ffParseHostJsonObject(FFHostOptions* options, yyjson_val* module) ffPrintError(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateHostJson(FF_MAYBE_UNUSED FFHostOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFHostResult host; + ffStrbufInit(&host.productFamily); + ffStrbufInit(&host.productName); + ffStrbufInit(&host.productVersion); + ffStrbufInit(&host.productSku); + ffStrbufInit(&host.sysVendor); + const char* error = ffDetectHost(&host); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if (host.productFamily.length == 0 && host.productName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "neither product_family nor product_name is set by O.E.M."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "family", &host.productFamily); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &host.productName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &host.productVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "sku", &host.productSku); + yyjson_mut_obj_add_strbuf(doc, obj, "sysVender", &host.sysVendor); + +exit: + ffStrbufDestroy(&host.productFamily); + ffStrbufDestroy(&host.productName); + ffStrbufDestroy(&host.productVersion); + ffStrbufDestroy(&host.productSku); + ffStrbufDestroy(&host.sysVendor); +} diff --git a/src/modules/host/host.h b/src/modules/host/host.h index 5a689288a..4ed4575a1 100644 --- a/src/modules/host/host.h +++ b/src/modules/host/host.h @@ -9,3 +9,4 @@ void ffInitHostOptions(FFHostOptions* options); bool ffParseHostCommandOptions(FFHostOptions* options, const char* key, const char* value); void ffDestroyHostOptions(FFHostOptions* options); void ffParseHostJsonObject(FFHostOptions* options, yyjson_val* module); +void ffGenerateHostJson(FFHostOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/icons/icons.c b/src/modules/icons/icons.c index aa1827a5f..720432b70 100644 --- a/src/modules/icons/icons.c +++ b/src/modules/icons/icons.c @@ -32,7 +32,7 @@ void ffPrintIcons(FFIconsOptions* options) void ffInitIconsOptions(FFIconsOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_ICONS_MODULE_NAME, ffParseIconsCommandOptions, ffParseIconsJsonObject, ffPrintIcons); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_ICONS_MODULE_NAME, ffParseIconsCommandOptions, ffParseIconsJsonObject, ffPrintIcons, ffGenerateIconsJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,17 @@ void ffParseIconsJsonObject(FFIconsOptions* options, yyjson_val* module) ffPrintError(FF_ICONS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateIconsJson(FF_MAYBE_UNUSED FFIconsOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY icons = ffStrbufCreate(); + const char* error = ffDetectIcons(&icons); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &icons); +} diff --git a/src/modules/icons/icons.h b/src/modules/icons/icons.h index 97533b096..dab251e25 100644 --- a/src/modules/icons/icons.h +++ b/src/modules/icons/icons.h @@ -9,3 +9,4 @@ void ffInitIconsOptions(FFIconsOptions* options); bool ffParseIconsCommandOptions(FFIconsOptions* options, const char* key, const char* value); void ffDestroyIconsOptions(FFIconsOptions* options); void ffParseIconsJsonObject(FFIconsOptions* options, yyjson_val* module); +void ffGenerateIconsJson(FFIconsOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/kernel/kernel.c b/src/modules/kernel/kernel.c index bc1bd21a8..89aab0af1 100644 --- a/src/modules/kernel/kernel.c +++ b/src/modules/kernel/kernel.c @@ -7,32 +7,32 @@ void ffPrintKernel(FFKernelOptions* options) { + const FFPlatform* platform = &instance.state.platform; if(options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - ffStrbufWriteTo(&instance.state.platform.systemRelease, stdout); + ffStrbufWriteTo(&platform->systemRelease, stdout); - #ifdef _WIN32 - if(instance.state.platform.systemVersion.length > 0) - printf(" (%s)", instance.state.platform.systemVersion.chars); - #endif - - putchar('\n'); + if(platform->systemDisplayVersion.length > 0) + printf(" (%s)\n", platform->systemDisplayVersion.chars); + else + putchar('\n'); } else { ffPrintFormat(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_KERNEL_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemRelease}, - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemArchitecture} + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemRelease}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemArchitecture}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemDisplayVersion} }); } } void ffInitKernelOptions(FFKernelOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_KERNEL_MODULE_NAME, ffParseKernelCommandOptions, ffParseKernelJsonObject, ffPrintKernel); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_KERNEL_MODULE_NAME, ffParseKernelCommandOptions, ffParseKernelJsonObject, ffPrintKernel, ffGenerateKernelJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,13 @@ void ffParseKernelJsonObject(FFKernelOptions* options, yyjson_val* module) ffPrintError(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateKernelJson(FF_MAYBE_UNUSED FFKernelOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "architecture", &instance.state.platform.systemArchitecture); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &instance.state.platform.systemName); + yyjson_mut_obj_add_strbuf(doc, obj, "release", &instance.state.platform.systemRelease); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &instance.state.platform.systemVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "displayVersion", &instance.state.platform.systemDisplayVersion); +} diff --git a/src/modules/kernel/kernel.h b/src/modules/kernel/kernel.h index 943641d60..419f2544a 100644 --- a/src/modules/kernel/kernel.h +++ b/src/modules/kernel/kernel.h @@ -9,3 +9,4 @@ void ffInitKernelOptions(FFKernelOptions* options); bool ffParseKernelCommandOptions(FFKernelOptions* options, const char* key, const char* value); void ffDestroyKernelOptions(FFKernelOptions* options); void ffParseKernelJsonObject(FFKernelOptions* options, yyjson_val* module); +void ffGenerateKernelJson(FFKernelOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/lm/lm.c b/src/modules/lm/lm.c index afef101a0..f84a422a3 100644 --- a/src/modules/lm/lm.c +++ b/src/modules/lm/lm.c @@ -51,7 +51,7 @@ void ffPrintLM(FFLMOptions* options) void ffInitLMOptions(FFLMOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LM_MODULE_NAME, ffParseLMCommandOptions, ffParseLMJsonObject, ffPrintLM); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LM_MODULE_NAME, ffParseLMCommandOptions, ffParseLMJsonObject, ffPrintLM, NULL); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +86,34 @@ void ffParseLMJsonObject(FFLMOptions* options, yyjson_val* module) ffPrintError(FF_LM_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateLMJson(FF_MAYBE_UNUSED FFLMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFLMResult result; + ffStrbufInit(&result.service); + ffStrbufInit(&result.type); + ffStrbufInit(&result.version); + const char* error = ffDetectLM(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if(result.service.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No LM service found"); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "service", &result.service); + yyjson_mut_obj_add_strbuf(doc, obj, "type", &result.type); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); + +exit: + ffStrbufDestroy(&result.service); + ffStrbufDestroy(&result.type); + ffStrbufDestroy(&result.version); +} diff --git a/src/modules/lm/lm.h b/src/modules/lm/lm.h index 079f90e5f..72509adcb 100644 --- a/src/modules/lm/lm.h +++ b/src/modules/lm/lm.h @@ -9,3 +9,4 @@ void ffInitLMOptions(FFLMOptions* options); bool ffParseLMCommandOptions(FFLMOptions* options, const char* key, const char* value); void ffDestroyLMOptions(FFLMOptions* options); void ffParseLMJsonObject(FFLMOptions* options, yyjson_val* module); +void ffGenerateLMJson(FFLMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/locale/locale.c b/src/modules/locale/locale.c index 4fa037b15..10c53ba3d 100644 --- a/src/modules/locale/locale.c +++ b/src/modules/locale/locale.c @@ -32,7 +32,7 @@ void ffPrintLocale(FFLocaleOptions* options) void ffInitLocaleOptions(FFLocaleOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALE_MODULE_NAME, ffParseLocaleCommandOptions, ffParseLocaleJsonObject, ffPrintLocale); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALE_MODULE_NAME, ffParseLocaleCommandOptions, ffParseLocaleJsonObject, ffPrintLocale, ffGenerateLocaleJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,17 @@ void ffParseLocaleJsonObject(FFLocaleOptions* options, yyjson_val* module) ffPrintError(FF_LOCALE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateLocaleJson(FF_MAYBE_UNUSED FFLocaleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY locale = ffStrbufCreate(); + + ffDetectLocale(&locale); + if(locale.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No locale found"); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &locale); +} diff --git a/src/modules/locale/locale.h b/src/modules/locale/locale.h index 405f697c9..8570a3c8d 100644 --- a/src/modules/locale/locale.h +++ b/src/modules/locale/locale.h @@ -9,3 +9,4 @@ void ffInitLocaleOptions(FFLocaleOptions* options); bool ffParseLocaleCommandOptions(FFLocaleOptions* options, const char* key, const char* value); void ffDestroyLocaleOptions(FFLocaleOptions* options); void ffParseLocaleJsonObject(FFLocaleOptions* options, yyjson_val* module); +void ffGenerateLocaleJson(FFLocaleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index 74b61c580..36013d505 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -5,7 +5,7 @@ #include "util/stringUtils.h" #define FF_LOCALIP_DISPLAY_NAME "Local IP" -#define FF_LOCALIP_NUM_FORMAT_ARGS 2 +#define FF_LOCALIP_NUM_FORMAT_ARGS 5 #pragma GCC diagnostic ignored "-Wsign-conversion" static int sortIps(const FFLocalIpResult* left, const FFLocalIpResult* right) @@ -13,14 +13,14 @@ static int sortIps(const FFLocalIpResult* left, const FFLocalIpResult* right) return ffStrbufComp(&left->name, &right->name); } -static void formatKey(const FFLocalIpOptions* options, const FFLocalIpResult* ip, uint32_t index, FFstrbuf* key) +static void formatKey(const FFLocalIpOptions* options, FFLocalIpResult* ip, uint32_t index, FFstrbuf* key) { if(options->moduleArgs.key.length == 0) { - if(ip->name.length) - ffStrbufSetF(key, FF_LOCALIP_DISPLAY_NAME " (%s)", ip->name.chars); - else - ffStrbufSetS(key, FF_LOCALIP_DISPLAY_NAME); + if(!ip->name.length) + ffStrbufSetF(&ip->name, "unknown %u", (unsigned) index); + + ffStrbufSetF(key, FF_LOCALIP_DISPLAY_NAME " (%s)", ip->name.chars); } else { @@ -86,9 +86,6 @@ void ffPrintLocalIp(FFLocalIpOptions* options) FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) { - if (options->defaultRouteOnly && !ip->defaultRoute) - continue; - if (flag) fputs(" - ", stdout); else @@ -104,9 +101,6 @@ void ffPrintLocalIp(FFLocalIpOptions* options) FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) { - if (options->defaultRouteOnly && !ip->defaultRoute) - continue; - formatKey(options, ip, results.length == 1 ? 0 : index + 1, &key); if(options->moduleArgs.outputFormat.length == 0) { @@ -139,7 +133,7 @@ void ffPrintLocalIp(FFLocalIpOptions* options) void ffInitLocalIpOptions(FFLocalIpOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALIP_MODULE_NAME, ffParseLocalIpCommandOptions, ffParseLocalIpJsonObject, ffPrintLocalIp); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALIP_MODULE_NAME, ffParseLocalIpCommandOptions, ffParseLocalIpJsonObject, ffPrintLocalIp, ffGenerateLocalIpJson); ffOptionInitModuleArg(&options->moduleArgs); options->showType = FF_LOCALIP_TYPE_IPV4_BIT; @@ -293,3 +287,42 @@ void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module) ffPrintError(FF_LOCALIP_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateLocalIpJson(FF_MAYBE_UNUSED FFLocalIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFLocalIpResult)); + + const char* error = ffDetectLocalIps(options, &results); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if(results.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Failed to detect any IPs"); + goto exit; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_bool(doc, obj, "defaultRoute", ip->defaultRoute); + yyjson_mut_obj_add_strbuf(doc, obj, "ipv4", &ip->ipv4); + yyjson_mut_obj_add_strbuf(doc, obj, "ipv6", &ip->ipv6); + yyjson_mut_obj_add_strbuf(doc, obj, "mac", &ip->mac); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &ip->name); + } + +exit: + FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) + { + ffStrbufDestroy(&ip->name); + ffStrbufDestroy(&ip->ipv4); + ffStrbufDestroy(&ip->ipv6); + ffStrbufDestroy(&ip->mac); + } +} diff --git a/src/modules/localip/localip.h b/src/modules/localip/localip.h index 114459c6f..799860a42 100644 --- a/src/modules/localip/localip.h +++ b/src/modules/localip/localip.h @@ -9,3 +9,4 @@ void ffInitLocalIpOptions(FFLocalIpOptions* options); bool ffParseLocalIpCommandOptions(FFLocalIpOptions* options, const char* key, const char* value); void ffDestroyLocalIpOptions(FFLocalIpOptions* options); void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module); +void ffGenerateLocalIpJson(FFLocalIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/media/media.c b/src/modules/media/media.c index 87b9b7f12..7a0e01e77 100644 --- a/src/modules/media/media.c +++ b/src/modules/media/media.c @@ -107,7 +107,7 @@ void ffPrintMedia(FFMediaOptions* options) void ffInitMediaOptions(FFMediaOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEDIA_MODULE_NAME, ffParseMediaCommandOptions, ffParseMediaJsonObject, ffPrintMedia); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEDIA_MODULE_NAME, ffParseMediaCommandOptions, ffParseMediaJsonObject, ffPrintMedia, ffGenerateMediaJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -142,3 +142,20 @@ void ffParseMediaJsonObject(FFMediaOptions* options, yyjson_val* module) ffPrintError(FF_MEDIA_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateMediaJson(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFMediaResult* media = ffDetectMedia(); + + if(media->error.length > 0) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &media->error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "song", &media->song); + yyjson_mut_obj_add_strbuf(doc, obj, "artist", &media->artist); + yyjson_mut_obj_add_strbuf(doc, obj, "album", &media->album); + yyjson_mut_obj_add_strbuf(doc, obj, "status", &media->status); +} diff --git a/src/modules/media/media.h b/src/modules/media/media.h index f5267e579..853814d7e 100644 --- a/src/modules/media/media.h +++ b/src/modules/media/media.h @@ -9,3 +9,4 @@ void ffInitMediaOptions(FFMediaOptions* options); bool ffParseMediaCommandOptions(FFMediaOptions* options, const char* key, const char* value); void ffDestroyMediaOptions(FFMediaOptions* options); void ffParseMediaJsonObject(FFMediaOptions* options, yyjson_val* module); +void ffGenerateMediaJson(FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/memory/memory.c b/src/modules/memory/memory.c index 410fb97f2..1d2bba391 100644 --- a/src/modules/memory/memory.c +++ b/src/modules/memory/memory.c @@ -68,7 +68,7 @@ void ffPrintMemory(FFMemoryOptions* options) void ffInitMemoryOptions(FFMemoryOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEMORY_MODULE_NAME, ffParseMemoryCommandOptions, ffParseMemoryJsonObject, ffPrintMemory); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEMORY_MODULE_NAME, ffParseMemoryCommandOptions, ffParseMemoryJsonObject, ffPrintMemory, ffGenerateMemoryJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -103,3 +103,19 @@ void ffParseMemoryJsonObject(FFMemoryOptions* options, yyjson_val* module) ffPrintError(FF_MEMORY_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateMemoryJson(FF_MAYBE_UNUSED FFMemoryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFMemoryResult storage; + const char* error = ffDetectMemory(&storage); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "total", storage.bytesTotal); + yyjson_mut_obj_add_uint(doc, obj, "used", storage.bytesUsed); +} diff --git a/src/modules/memory/memory.h b/src/modules/memory/memory.h index aec76b21f..b4fefff5d 100644 --- a/src/modules/memory/memory.h +++ b/src/modules/memory/memory.h @@ -9,3 +9,4 @@ void ffInitMemoryOptions(FFMemoryOptions* options); bool ffParseMemoryCommandOptions(FFMemoryOptions* options, const char* key, const char* value); void ffDestroyMemoryOptions(FFMemoryOptions* options); void ffParseMemoryJsonObject(FFMemoryOptions* options, yyjson_val* module); +void ffGenerateMemoryJson(FFMemoryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/modules.h b/src/modules/modules.h index 1f7669c10..64ab6ea21 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -31,6 +31,7 @@ #include "modules/media/media.h" #include "modules/memory/memory.h" #include "modules/monitor/monitor.h" +#include "modules/netio/netio.h" #include "modules/opengl/opengl.h" #include "modules/opencl/opencl.h" #include "modules/os/os.h" diff --git a/src/modules/monitor/monitor.c b/src/modules/monitor/monitor.c index 688c62413..be0e22830 100644 --- a/src/modules/monitor/monitor.c +++ b/src/modules/monitor/monitor.c @@ -77,7 +77,7 @@ void ffPrintMonitor(FFMonitorOptions* options) void ffInitMonitorOptions(FFMonitorOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MONITOR_MODULE_NAME, ffParseMonitorCommandOptions, ffParseMonitorJsonObject, ffPrintMonitor); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MONITOR_MODULE_NAME, ffParseMonitorCommandOptions, ffParseMonitorJsonObject, ffPrintMonitor, ffGenerateMonitorJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -112,3 +112,38 @@ void ffParseMonitorJsonObject(FFMonitorOptions* options, yyjson_val* module) ffPrintError(FF_MONITOR_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateMonitorJson(FF_MAYBE_UNUSED FFMonitorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFMonitorResult)); + + const char* error = ffDetectMonitor(&results); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else if(results.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No monitors found"); + } + else + { + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFMonitorResult, item, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_bool(doc, obj, "hdrCompatible", item->hdrCompatible); + yyjson_mut_obj_add_uint(doc, obj, "width", item->width); + yyjson_mut_obj_add_uint(doc, obj, "height", item->height); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_uint(doc, obj, "physicalHeight", item->physicalHeight); + yyjson_mut_obj_add_uint(doc, obj, "physicalWidth", item->physicalWidth); + } + } + + FF_LIST_FOR_EACH(FFMonitorResult, item, results) + { + ffStrbufDestroy(&item->name); + } +} diff --git a/src/modules/monitor/monitor.h b/src/modules/monitor/monitor.h index ff03d26e9..851144204 100644 --- a/src/modules/monitor/monitor.h +++ b/src/modules/monitor/monitor.h @@ -9,3 +9,4 @@ void ffInitMonitorOptions(FFMonitorOptions* options); bool ffParseMonitorCommandOptions(FFMonitorOptions* options, const char* key, const char* value); void ffDestroyMonitorOptions(FFMonitorOptions* options); void ffParseMonitorJsonObject(FFMonitorOptions* options, yyjson_val* module); +void ffGenerateMonitorJson(FFMonitorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/netio/netio.c b/src/modules/netio/netio.c new file mode 100644 index 000000000..3efef948f --- /dev/null +++ b/src/modules/netio/netio.c @@ -0,0 +1,199 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "common/parsing.h" +#include "detection/netio/netio.h" +#include "modules/netio/netio.h" +#include "util/stringUtils.h" + +#define FF_NETIO_DISPLAY_NAME "Network IO" +#define FF_NETIO_NUM_FORMAT_ARGS 12 + +static int sortInfs(const FFNetIOResult* left, const FFNetIOResult* right) +{ + return ffStrbufComp(&left->name, &right->name); +} + +static void formatKey(const FFNetIOOptions* options, FFNetIOResult* inf, uint32_t index, FFstrbuf* key) +{ + if(options->moduleArgs.key.length == 0) + { + if(!inf->name.length) + ffStrbufSetF(&inf->name, "unknown %u", (unsigned) index); + + ffStrbufSetF(key, FF_NETIO_DISPLAY_NAME " (%s)", inf->name.chars); + } + else + { + ffStrbufClear(key); + ffParseFormatString(key, &options->moduleArgs.key, 2, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_UINT, &index}, + {FF_FORMAT_ARG_TYPE_STRBUF, &inf->name}, + }); + } +} + +void ffPrintNetIO(FFNetIOOptions* options) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFNetIOResult)); + const char* error = ffDetectNetIO(&result, options); + + if(error) + { + ffPrintError(FF_NETIO_DISPLAY_NAME, 0, &options->moduleArgs, "%s", error); + return; + } + + ffListSort(&result, (const void*) sortInfs); + + uint32_t index = 0; + FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY buffer2 = ffStrbufCreate(); + + FF_LIST_FOR_EACH(FFNetIOResult, inf, result) + { + formatKey(options, inf, result.length == 1 ? 0 : index + 1, &key); + ffStrbufClear(&buffer); + + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + ffParseSize(inf->rxBytes, &buffer); + ffStrbufAppendS(&buffer, "/s (IN) - "); + ffParseSize(inf->txBytes, &buffer); + ffStrbufAppendS(&buffer, "/s (OUT)"); + if (!options->defaultRouteOnly && inf->defaultRoute) + ffStrbufAppendS(&buffer, " *"); + ffStrbufPutTo(&buffer, stdout); + } + else + { + ffStrbufClear(&buffer2); + ffParseSize(inf->rxBytes, &buffer); + ffStrbufAppendS(&buffer, "/s"); + ffParseSize(inf->txBytes, &buffer2); + ffStrbufAppendS(&buffer2, "/s"); + + ffPrintFormatString(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_NETIO_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &buffer}, + {FF_FORMAT_ARG_TYPE_STRBUF, &buffer2}, + {FF_FORMAT_ARG_TYPE_STRBUF, &inf->name}, + {FF_FORMAT_ARG_TYPE_BOOL, &inf->defaultRoute}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txBytes}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxBytes}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txPackets}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxPackets}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxErrors}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txErrors}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxDrops}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txDrops}, + }); + } + ++index; + } + + FF_LIST_FOR_EACH(FFNetIOResult, inf, result) + { + ffStrbufDestroy(&inf->name); + } +} + +void ffInitNetIOOptions(FFNetIOOptions* options) +{ + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_NETIO_MODULE_NAME, ffParseNetIOCommandOptions, ffParseNetIOJsonObject, ffPrintNetIO, ffGenerateNetIOJson); + ffOptionInitModuleArg(&options->moduleArgs); + + ffStrbufInit(&options->namePrefix); + options->defaultRouteOnly = false; +} + +bool ffParseNetIOCommandOptions(FFNetIOOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_NETIO_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + if (ffStrEqualsIgnCase(subKey, "name-prefix")) + { + ffOptionParseString(key, value, &options->namePrefix); + return true; + } + + if (ffStrEqualsIgnCase(subKey, "default-route-only")) + { + options->defaultRouteOnly = ffOptionParseBoolean(value); + return true; + } + + return false; +} + +void ffDestroyNetIOOptions(FFNetIOOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); + ffStrbufDestroy(&options->namePrefix); +} + +void ffParseNetIOJsonObject(FFNetIOOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + if (ffStrEqualsIgnCase(key, "namePrefix")) + { + ffStrbufSetS(&options->namePrefix, yyjson_get_str(val)); + continue; + } + + if (ffStrEqualsIgnCase(key, "defaultRouteOnly")) + { + options->defaultRouteOnly = yyjson_get_bool(val); + continue; + } + + ffPrintError(FF_NETIO_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); + } +} + +void ffGenerateNetIOJson(FFNetIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFNetIOResult)); + const char* error = ffDetectNetIO(&result, options); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFNetIOResult, counter, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &counter->name); + yyjson_mut_obj_add_bool(doc, obj, "defaultRoute", counter->defaultRoute); + yyjson_mut_obj_add_uint(doc, obj, "txBytes", counter->txBytes); + yyjson_mut_obj_add_uint(doc, obj, "rxBytes", counter->rxBytes); + yyjson_mut_obj_add_uint(doc, obj, "txPackets", counter->txPackets); + yyjson_mut_obj_add_uint(doc, obj, "rxPackets", counter->rxPackets); + yyjson_mut_obj_add_uint(doc, obj, "rxErrors", counter->rxErrors); + yyjson_mut_obj_add_uint(doc, obj, "txErrors", counter->txErrors); + yyjson_mut_obj_add_uint(doc, obj, "rxDrops", counter->rxDrops); + yyjson_mut_obj_add_uint(doc, obj, "txDrops", counter->txDrops); + } + + FF_LIST_FOR_EACH(FFNetIOResult, inf, result) + { + ffStrbufDestroy(&inf->name); + } +} diff --git a/src/modules/netio/netio.h b/src/modules/netio/netio.h new file mode 100644 index 000000000..3562d0ccd --- /dev/null +++ b/src/modules/netio/netio.h @@ -0,0 +1,14 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_NETIO_MODULE_NAME "NetIO" + +void ffPrepareNetIO(FFNetIOOptions* options); + +void ffPrintNetIO(FFNetIOOptions* options); +void ffInitNetIOOptions(FFNetIOOptions* options); +bool ffParseNetIOCommandOptions(FFNetIOOptions* options, const char* key, const char* value); +void ffDestroyNetIOOptions(FFNetIOOptions* options); +void ffParseNetIOJsonObject(FFNetIOOptions* options, yyjson_val* module); +void ffGenerateNetIOJson(FFNetIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/netio/option.h b/src/modules/netio/option.h new file mode 100644 index 000000000..62b493d29 --- /dev/null +++ b/src/modules/netio/option.h @@ -0,0 +1,14 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" + +typedef struct FFNetIOOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; + + FFstrbuf namePrefix; + bool defaultRouteOnly; +} FFNetIOOptions; diff --git a/src/modules/opencl/opencl.c b/src/modules/opencl/opencl.c index 6194b7a0b..afe3efa24 100644 --- a/src/modules/opencl/opencl.c +++ b/src/modules/opencl/opencl.c @@ -41,7 +41,7 @@ void ffPrintOpenCL(FFOpenCLOptions* options) void ffInitOpenCLOptions(FFOpenCLOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENCL_MODULE_NAME, ffParseOpenCLCommandOptions, ffParseOpenCLJsonObject, ffPrintOpenCL); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENCL_MODULE_NAME, ffParseOpenCLCommandOptions, ffParseOpenCLJsonObject, ffPrintOpenCL, ffGenerateOpenCLJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -76,3 +76,29 @@ void ffParseOpenCLJsonObject(FFOpenCLOptions* options, yyjson_val* module) ffPrintError(FF_OPENCL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateOpenCLJson(FF_MAYBE_UNUSED FFOpenCLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFOpenCLResult opencl; + ffStrbufInit(&opencl.version); + ffStrbufInit(&opencl.device); + ffStrbufInit(&opencl.vendor); + + const char* error = ffDetectOpenCL(&opencl); + + if(error != NULL) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &opencl.version); + yyjson_mut_obj_add_strbuf(doc, obj, "device", &opencl.device); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &opencl.vendor); + } + + ffStrbufDestroy(&opencl.version); + ffStrbufDestroy(&opencl.device); + ffStrbufDestroy(&opencl.vendor); +} diff --git a/src/modules/opencl/opencl.h b/src/modules/opencl/opencl.h index f5e2a32c2..592f453bf 100644 --- a/src/modules/opencl/opencl.h +++ b/src/modules/opencl/opencl.h @@ -9,3 +9,4 @@ void ffInitOpenCLOptions(FFOpenCLOptions* options); bool ffParseOpenCLCommandOptions(FFOpenCLOptions* options, const char* key, const char* value); void ffDestroyOpenCLOptions(FFOpenCLOptions* options); void ffParseOpenCLJsonObject(FFOpenCLOptions* options, yyjson_val* module); +void ffGenerateOpenCLJson(FFOpenCLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/opengl/opengl.c b/src/modules/opengl/opengl.c index d94849cf5..78bbf1842 100644 --- a/src/modules/opengl/opengl.c +++ b/src/modules/opengl/opengl.c @@ -44,7 +44,7 @@ void ffPrintOpenGL(FFOpenGLOptions* options) void ffInitOpenGLOptions(FFOpenGLOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENGL_MODULE_NAME, ffParseOpenGLCommandOptions, ffParseOpenGLJsonObject, ffPrintOpenGL); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENGL_MODULE_NAME, ffParseOpenGLCommandOptions, ffParseOpenGLJsonObject, ffPrintOpenGL, ffGenerateOpenGLJson); ffOptionInitModuleArg(&options->moduleArgs); #if defined(__linux__) || defined(__FreeBSD__) @@ -115,3 +115,31 @@ void ffParseOpenGLJsonObject(FFOpenGLOptions* options, yyjson_val* module) ffPrintError(FF_OPENGL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateOpenGLJson(FF_MAYBE_UNUSED FFOpenGLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFOpenGLResult result; + ffStrbufInit(&result.version); + ffStrbufInit(&result.renderer); + ffStrbufInit(&result.vendor); + ffStrbufInit(&result.slv); + + const char* error = ffDetectOpenGL(options, &result); + if(error != NULL) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); + yyjson_mut_obj_add_strbuf(doc, obj, "renderer", &result.renderer); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &result.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "slv", &result.slv); + } + + ffStrbufDestroy(&result.version); + ffStrbufDestroy(&result.renderer); + ffStrbufDestroy(&result.vendor); + ffStrbufDestroy(&result.slv); +} diff --git a/src/modules/opengl/opengl.h b/src/modules/opengl/opengl.h index 5d0b1937b..6c35ede54 100644 --- a/src/modules/opengl/opengl.h +++ b/src/modules/opengl/opengl.h @@ -9,3 +9,4 @@ void ffInitOpenGLOptions(FFOpenGLOptions* options); bool ffParseOpenGLCommandOptions(FFOpenGLOptions* options, const char* key, const char* value); void ffDestroyOpenGLOptions(FFOpenGLOptions* options); void ffParseOpenGLJsonObject(FFOpenGLOptions* options, yyjson_val* module); +void ffGenerateOpenGLJson(FFOpenGLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/options.h b/src/modules/options.h index 12041ce5b..1be2e88e0 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -31,6 +31,7 @@ #include "modules/media/option.h" #include "modules/memory/option.h" #include "modules/monitor/option.h" +#include "modules/netio/option.h" #include "modules/opengl/option.h" #include "modules/opencl/option.h" #include "modules/os/option.h" diff --git a/src/modules/os/os.c b/src/modules/os/os.c index e5e5d58ee..120f769fc 100644 --- a/src/modules/os/os.c +++ b/src/modules/os/os.c @@ -21,21 +21,20 @@ static void buildOutputDefault(const FFOSResult* os, FFstrbuf* result) else ffStrbufAppend(result, &instance.state.platform.systemName); - #ifdef __APPLE__ - if(os->codename.length > 0) + //Append code name if it is missing + if(os->codename.length > 0 && !ffStrbufContain(result, &os->versionID)) { ffStrbufAppendC(result, ' '); ffStrbufAppend(result, &os->codename); } - #endif //Append version if it is missing - if(os->versionID.length > 0 && ffStrbufFirstIndex(result, &os->versionID) == result->length) + if(os->versionID.length > 0 && !ffStrbufContain(result, &os->versionID)) { ffStrbufAppendC(result, ' '); ffStrbufAppend(result, &os->versionID); } - else if(os->versionID.length == 0 && os->version.length > 0 && ffStrbufFirstIndex(result, &os->version) == result->length) + else if(os->versionID.length == 0 && os->version.length > 0 && !ffStrbufContain(result, &os->version)) { ffStrbufAppendC(result, ' '); ffStrbufAppend(result, &os->version); @@ -139,7 +138,7 @@ void ffPrintOS(FFOSOptions* options) void ffInitOSOptions(FFOSOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OS_MODULE_NAME, ffParseOSCommandOptions, ffParseOSJsonObject, ffPrintOS); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OS_MODULE_NAME, ffParseOSCommandOptions, ffParseOSJsonObject, ffPrintOS, ffGenerateOSJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -174,3 +173,26 @@ void ffParseOSJsonObject(FFOSOptions* options, yyjson_val* module) ffPrintError(FF_OS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateOSJson(FF_MAYBE_UNUSED FFOSOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFOSResult* os = ffDetectOS(); + + if(os->name.length == 0 && os->prettyName.length == 0 && os->id.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Could not detect OS"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "buildID", &os->buildID); + yyjson_mut_obj_add_strbuf(doc, obj, "codename", &os->codename); + yyjson_mut_obj_add_strbuf(doc, obj, "id", &os->id); + yyjson_mut_obj_add_strbuf(doc, obj, "idLike", &os->idLike); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &os->name); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &os->prettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "variant", &os->variant); + yyjson_mut_obj_add_strbuf(doc, obj, "variantID", &os->variantID); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &os->version); + yyjson_mut_obj_add_strbuf(doc, obj, "versionID", &os->versionID); +} diff --git a/src/modules/os/os.h b/src/modules/os/os.h index 07320194d..b7ed39d0d 100644 --- a/src/modules/os/os.h +++ b/src/modules/os/os.h @@ -9,3 +9,4 @@ void ffInitOSOptions(FFOSOptions* options); bool ffParseOSCommandOptions(FFOSOptions* options, const char* key, const char* value); void ffDestroyOSOptions(FFOSOptions* options); void ffParseOSJsonObject(FFOSOptions* options, yyjson_val* module); +void ffGenerateOSJson(FFOSOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 21599e07a..1d6ba534a 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -4,7 +4,7 @@ #include "modules/packages/packages.h" #include "util/stringUtils.h" -#define FF_PACKAGES_NUM_FORMAT_ARGS 22 +#define FF_PACKAGES_NUM_FORMAT_ARGS 24 void ffPrintPackages(FFPackagesOptions* options) { @@ -63,6 +63,7 @@ void ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE(choco) FF_PRINT_PACKAGE(pkgtool) FF_PRINT_PACKAGE(paludis) + FF_PRINT_PACKAGE(winget) putchar('\n'); } @@ -91,6 +92,8 @@ void ffPrintPackages(FFPackagesOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &counts.scoop}, {FF_FORMAT_ARG_TYPE_UINT, &counts.choco}, {FF_FORMAT_ARG_TYPE_UINT, &counts.pkgtool}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.paludis}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.winget}, }); } @@ -99,7 +102,7 @@ void ffPrintPackages(FFPackagesOptions* options) void ffInitPackagesOptions(FFPackagesOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PACKAGES_MODULE_NAME, ffParsePackagesCommandOptions, ffParsePackagesJsonObject, ffPrintPackages); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PACKAGES_MODULE_NAME, ffParsePackagesCommandOptions, ffParsePackagesJsonObject, ffPrintPackages, ffGeneratePackagesJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -134,3 +137,46 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) ffPrintError(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePackagesJson(FF_MAYBE_UNUSED FFPackagesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFPackagesResult counts = {}; + ffStrbufInit(&counts.pacmanBranch); + + const char* error = ffDetectPackages(&counts); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + + #define FF_APPEND_PACKAGE_COUNT(name) yyjson_mut_obj_add_uint(doc, obj, #name, counts.name); + + FF_APPEND_PACKAGE_COUNT(all) + FF_APPEND_PACKAGE_COUNT(apk) + FF_APPEND_PACKAGE_COUNT(brew) + FF_APPEND_PACKAGE_COUNT(brewCask) + FF_APPEND_PACKAGE_COUNT(choco) + FF_APPEND_PACKAGE_COUNT(dpkg) + FF_APPEND_PACKAGE_COUNT(emerge) + FF_APPEND_PACKAGE_COUNT(eopkg) + FF_APPEND_PACKAGE_COUNT(flatpakSystem) + FF_APPEND_PACKAGE_COUNT(flatpakUser) + FF_APPEND_PACKAGE_COUNT(nixDefault) + FF_APPEND_PACKAGE_COUNT(nixSystem) + FF_APPEND_PACKAGE_COUNT(nixUser) + FF_APPEND_PACKAGE_COUNT(pacman) + FF_APPEND_PACKAGE_COUNT(paludis) + FF_APPEND_PACKAGE_COUNT(pkg) + FF_APPEND_PACKAGE_COUNT(pkgtool) + FF_APPEND_PACKAGE_COUNT(port) + FF_APPEND_PACKAGE_COUNT(rpm) + FF_APPEND_PACKAGE_COUNT(scoop) + FF_APPEND_PACKAGE_COUNT(snap) + FF_APPEND_PACKAGE_COUNT(winget) + FF_APPEND_PACKAGE_COUNT(xbps) + yyjson_mut_obj_add_strbuf(doc, obj, "pacmanBranch", &counts.pacmanBranch); +} diff --git a/src/modules/packages/packages.h b/src/modules/packages/packages.h index 14fd7301d..97b4ce239 100644 --- a/src/modules/packages/packages.h +++ b/src/modules/packages/packages.h @@ -9,3 +9,4 @@ void ffInitPackagesOptions(FFPackagesOptions* options); bool ffParsePackagesCommandOptions(FFPackagesOptions* options, const char* key, const char* value); void ffDestroyPackagesOptions(FFPackagesOptions* options); void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module); +void ffGeneratePackagesJson(FFPackagesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/player/player.c b/src/modules/player/player.c index 4dca65916..5ca58ba27 100644 --- a/src/modules/player/player.c +++ b/src/modules/player/player.c @@ -75,7 +75,7 @@ void ffPrintPlayer(FFPlayerOptions* options) void ffInitPlayerOptions(FFPlayerOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PLAYER_MODULE_NAME, ffParsePlayerCommandOptions, ffParsePlayerJsonObject, ffPrintPlayer); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PLAYER_MODULE_NAME, ffParsePlayerCommandOptions, ffParsePlayerJsonObject, ffPrintPlayer, ffGeneratePlayerJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -110,3 +110,19 @@ void ffParsePlayerJsonObject(FFPlayerOptions* options, yyjson_val* module) ffPrintError(FF_PLAYER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePlayerJson(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFMediaResult* media = ffDetectMedia(); + + if(media->error.length > 0) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &media->error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "player", &media->player); + yyjson_mut_obj_add_strbuf(doc, obj, "playerId", &media->playerId); + yyjson_mut_obj_add_strbuf(doc, obj, "url", &media->url); +} diff --git a/src/modules/player/player.h b/src/modules/player/player.h index f63e12250..829b7857a 100644 --- a/src/modules/player/player.h +++ b/src/modules/player/player.h @@ -10,3 +10,4 @@ void ffInitPlayerOptions(FFPlayerOptions* options); bool ffParsePlayerCommandOptions(FFPlayerOptions* options, const char* key, const char* value); void ffDestroyPlayerOptions(FFPlayerOptions* options); void ffParsePlayerJsonObject(FFPlayerOptions* options, yyjson_val* module); +void ffGeneratePlayerJson(FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/poweradapter/poweradapter.c b/src/modules/poweradapter/poweradapter.c index 7fcd772af..a5ce64002 100644 --- a/src/modules/poweradapter/poweradapter.c +++ b/src/modules/poweradapter/poweradapter.c @@ -9,8 +9,7 @@ void ffPrintPowerAdapter(FFPowerAdapterOptions* options) { - FFlist results; - ffListInit(&results, sizeof(PowerAdapterResult)); + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFPowerAdapterResult)); const char* error = ffDetectPowerAdapterImpl(&results); @@ -26,7 +25,7 @@ void ffPrintPowerAdapter(FFPowerAdapterOptions* options) { for(uint8_t i = 0; i < (uint8_t) results.length; i++) { - PowerAdapterResult* result = ffListGet(&results, i); + FFPowerAdapterResult* result = ffListGet(&results, i); if(result->watts != FF_POWERADAPTER_UNSET) { @@ -59,13 +58,11 @@ void ffPrintPowerAdapter(FFPowerAdapterOptions* options) ffStrbufDestroy(&result->name); } } - - ffListDestroy(&results); } void ffInitPowerAdapterOptions(FFPowerAdapterOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_POWERADAPTER_MODULE_NAME, ffParsePowerAdapterCommandOptions, ffParsePowerAdapterJsonObject, ffPrintPowerAdapter); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_POWERADAPTER_MODULE_NAME, ffParsePowerAdapterCommandOptions, ffParsePowerAdapterJsonObject, ffPrintPowerAdapter, ffGeneratePowerAdapterJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -100,3 +97,32 @@ void ffParsePowerAdapterJsonObject(FFPowerAdapterOptions* options, yyjson_val* m ffPrintError(FF_POWERADAPTER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePowerAdapterJson(FF_MAYBE_UNUSED FFPowerAdapterOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFPowerAdapterResult)); + + const char* error = ffDetectPowerAdapterImpl(&results); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else if(results.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No power adapters found"); + } + else + { + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFPowerAdapterResult, item, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "description", &item->description); + yyjson_mut_obj_add_strbuf(doc, obj, "manufacturer", &item->manufacturer); + yyjson_mut_obj_add_strbuf(doc, obj, "modelName", &item->modelName); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_int(doc, obj, "watts", item->watts); + } + } +} diff --git a/src/modules/poweradapter/poweradapter.h b/src/modules/poweradapter/poweradapter.h index e9490c832..a99d6acab 100644 --- a/src/modules/poweradapter/poweradapter.h +++ b/src/modules/poweradapter/poweradapter.h @@ -9,3 +9,4 @@ void ffInitPowerAdapterOptions(FFPowerAdapterOptions* options); bool ffParsePowerAdapterCommandOptions(FFPowerAdapterOptions* options, const char* key, const char* value); void ffDestroyPowerAdapterOptions(FFPowerAdapterOptions* options); void ffParsePowerAdapterJsonObject(FFPowerAdapterOptions* options, yyjson_val* module); +void ffGeneratePowerAdapterJson(FFPowerAdapterOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/processes/processes.c b/src/modules/processes/processes.c index fa2fc89bd..02a8bb53f 100644 --- a/src/modules/processes/processes.c +++ b/src/modules/processes/processes.c @@ -33,7 +33,7 @@ void ffPrintProcesses(FFProcessesOptions* options) void ffInitProcessesOptions(FFProcessesOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PROCESSES_MODULE_NAME, ffParseProcessesCommandOptions, ffParseProcessesJsonObject, ffPrintProcesses); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PROCESSES_MODULE_NAME, ffParseProcessesCommandOptions, ffParseProcessesJsonObject, ffPrintProcesses, ffGenerateProcessesJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -68,3 +68,17 @@ void ffParseProcessesJsonObject(FFProcessesOptions* options, yyjson_val* module) ffPrintError(FF_PROCESSES_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateProcessesJson(FF_MAYBE_UNUSED FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + uint32_t result; + const char* error = ffDetectProcesses(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_uint(doc, module, "result", result); +} diff --git a/src/modules/processes/processes.h b/src/modules/processes/processes.h index 724faf7fb..8f14e00e4 100644 --- a/src/modules/processes/processes.h +++ b/src/modules/processes/processes.h @@ -9,3 +9,4 @@ void ffInitProcessesOptions(FFProcessesOptions* options); bool ffParseProcessesCommandOptions(FFProcessesOptions* options, const char* key, const char* value); void ffDestroyProcessesOptions(FFProcessesOptions* options); void ffParseProcessesJsonObject(FFProcessesOptions* options, yyjson_val* module); +void ffGenerateProcessesJson(FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/publicip/publicip.c b/src/modules/publicip/publicip.c index 6097b54a3..e7c277e21 100644 --- a/src/modules/publicip/publicip.c +++ b/src/modules/publicip/publicip.c @@ -1,103 +1,48 @@ #include "common/printing.h" #include "common/jsonconfig.h" -#include "common/networking.h" #include "modules/publicip/publicip.h" +#include "detection/publicip/publicip.h" #include "util/stringUtils.h" #define FF_PUBLICIP_DISPLAY_NAME "Public IP" #define FF_PUBLICIP_NUM_FORMAT_ARGS 1 -static FFNetworkingState state; -static int status = -1; - -static inline void wrapYyjsonFree(yyjson_doc** doc) -{ - assert(doc); - if (*doc) - yyjson_doc_free(*doc); -} - -void ffPreparePublicIp(FFPublicIpOptions* options) -{ - if (status != -1) - { - fputs("Error: " FF_PUBLICIP_DISPLAY_NAME " can only be used once due to internal limitations\n", stderr); - exit(1); - } - - if (options->url.length == 0) - status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); - else - { - FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); - ffStrbufSubstrAfterFirstS(&host, "://"); - uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/'); - - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); - if(pathStartIndex != host.length) - { - ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex)); - host.length = pathStartIndex; - host.chars[pathStartIndex] = '\0'; - } - - status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); - } -} - void ffPrintPublicIp(FFPublicIpOptions* options) { - if (status == -1) - ffPreparePublicIp(options); + FFPublicIpResult result; + ffStrbufInit(&result.ip); + ffStrbufInit(&result.location); + const char* error = ffDetectPublicIp(options, &result); - if (status == 0) + if (error) { - ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "Failed to connect to an IP detection server"); - return; - } - - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(4096); - bool success = ffNetworkingRecvHttpResponse(&state, &result, options->timeout); - if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); - - if (!success || result.length == 0) - { - ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "Failed to receive the server response"); + ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "%s", error); return; } if (options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - - if (options->url.length == 0) - { - yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(result.chars, result.length, 0, NULL, NULL); - if (doc) - { - yyjson_val* root = yyjson_doc_get_root(doc); - printf("%s (%s, %s)\n", - yyjson_get_str(yyjson_obj_get(root, "ip")), - yyjson_get_str(yyjson_obj_get(root, "city")), - yyjson_get_str(yyjson_obj_get(root, "country")) - ); - return; - } - } - - ffStrbufPutTo(&result, stdout); + if (result.location.length) + printf("%s (%s)\n", result.ip.chars, result.location.chars); + else + ffStrbufPutTo(&result.ip, stdout); } else { ffPrintFormat(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PUBLICIP_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRBUF, &result} + {FF_FORMAT_ARG_TYPE_STRBUF, &result.ip}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.location}, }); } + + ffStrbufDestroy(&result.ip); + ffStrbufDestroy(&result.location); } void ffInitPublicIpOptions(FFPublicIpOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PUBLICIP_MODULE_NAME, ffParsePublicIpCommandOptions, ffParsePublicIpJsonObject, ffPrintPublicIp); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PUBLICIP_MODULE_NAME, ffParsePublicIpCommandOptions, ffParsePublicIpJsonObject, ffPrintPublicIp, ffGeneratePublicIpJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInit(&options->url); @@ -161,3 +106,24 @@ void ffParsePublicIpJsonObject(FFPublicIpOptions* options, yyjson_val* module) ffPrintError(FF_PUBLICIP_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePublicIpJson(FFPublicIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFPublicIpResult result; + ffStrbufInit(&result.ip); + ffStrbufInit(&result.location); + const char* error = ffDetectPublicIp(options, &result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "ip", &result.ip); + yyjson_mut_obj_add_strbuf(doc, obj, "location", &result.location); + + ffStrbufDestroy(&result.ip); + ffStrbufDestroy(&result.location); +} diff --git a/src/modules/publicip/publicip.h b/src/modules/publicip/publicip.h index 6a7c739d2..870591eb7 100644 --- a/src/modules/publicip/publicip.h +++ b/src/modules/publicip/publicip.h @@ -11,3 +11,4 @@ void ffInitPublicIpOptions(FFPublicIpOptions* options); bool ffParsePublicIpCommandOptions(FFPublicIpOptions* options, const char* key, const char* value); void ffDestroyPublicIpOptions(FFPublicIpOptions* options); void ffParsePublicIpJsonObject(FFPublicIpOptions* options, yyjson_val* module); +void ffGeneratePublicIpJson(FFPublicIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/separator/separator.c b/src/modules/separator/separator.c index 0eaceff6a..555bb2a04 100644 --- a/src/modules/separator/separator.c +++ b/src/modules/separator/separator.c @@ -29,7 +29,7 @@ void ffPrintSeparator(FFSeparatorOptions* options) uint32_t titleLength = 1 // @ + getWcsWidth(&platform->userName, wstr, &state) // user name - + (fqdn ? platform->domainName.length : platform->hostName.length); // host name + + (fqdn ? platform->hostName.length : ffStrbufFirstIndexC(&platform->hostName, '.')); // host name ffLogoPrintLine(); if(options->string.length == 0) @@ -77,7 +77,7 @@ void ffPrintSeparator(FFSeparatorOptions* options) void ffInitSeparatorOptions(FFSeparatorOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SEPARATOR_MODULE_NAME, ffParseSeparatorCommandOptions, ffParseSeparatorJsonObject, ffPrintSeparator); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SEPARATOR_MODULE_NAME, ffParseSeparatorCommandOptions, ffParseSeparatorJsonObject, ffPrintSeparator, NULL); ffStrbufInit(&options->string); } diff --git a/src/modules/shell/shell.c b/src/modules/shell/shell.c index d7e2cfa6d..f7668c588 100644 --- a/src/modules/shell/shell.c +++ b/src/modules/shell/shell.c @@ -4,7 +4,7 @@ #include "modules/shell/shell.h" #include "util/stringUtils.h" -#define FF_SHELL_NUM_FORMAT_ARGS 7 +#define FF_SHELL_NUM_FORMAT_ARGS 6 void ffPrintShell(FFShellOptions* options) { @@ -36,16 +36,15 @@ void ffPrintShell(FFShellOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellExe}, {FF_FORMAT_ARG_TYPE_STRING, result->shellExeName}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->userShellExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellVersion} + {FF_FORMAT_ARG_TYPE_UINT, &result->shellPid}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellPrettyName}, }); } } void ffInitShellOptions(FFShellOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SHELL_MODULE_NAME, ffParseShellCommandOptions, ffParseShellJsonObject, ffPrintShell); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SHELL_MODULE_NAME, ffParseShellCommandOptions, ffParseShellJsonObject, ffPrintShell, ffGenerateShellJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -80,3 +79,21 @@ void ffParseShellJsonObject(FFShellOptions* options, yyjson_val* module) ffPrintError(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateShellJson(FF_MAYBE_UNUSED FFShellOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFTerminalShellResult* result = ffDetectTerminalShell(); + + if(result->shellProcessName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect shell"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->shellExe); + yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->shellExeName); + yyjson_mut_obj_add_uint(doc, obj, "pid", result->shellPid); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->shellProcessName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->shellVersion); +} diff --git a/src/modules/shell/shell.h b/src/modules/shell/shell.h index 4e4f976bf..354b51229 100644 --- a/src/modules/shell/shell.h +++ b/src/modules/shell/shell.h @@ -9,3 +9,4 @@ void ffInitShellOptions(FFShellOptions* options); bool ffParseShellCommandOptions(FFShellOptions* options, const char* key, const char* value); void ffDestroyShellOptions(FFShellOptions* options); void ffParseShellJsonObject(FFShellOptions* options, yyjson_val* module); +void ffGenerateShellJson(FFShellOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/sound/sound.c b/src/modules/sound/sound.c index 707556360..3796a7365 100644 --- a/src/modules/sound/sound.c +++ b/src/modules/sound/sound.c @@ -85,7 +85,7 @@ void ffPrintSound(FFSoundOptions* options) void ffInitSoundOptions(FFSoundOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SOUND_MODULE_NAME, ffParseSoundCommandOptions, ffParseSoundJsonObject, ffPrintSound); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SOUND_MODULE_NAME, ffParseSoundCommandOptions, ffParseSoundJsonObject, ffPrintSound, ffGenerateSoundJson); ffOptionInitModuleArg(&options->moduleArgs); options->soundType = FF_SOUND_TYPE_MAIN; @@ -149,3 +149,38 @@ void ffParseSoundJsonObject(FFSoundOptions* options, yyjson_val* module) ffPrintError(FF_SOUND_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateSoundJson(FF_MAYBE_UNUSED FFSoundOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFSoundDevice)); + const char* error = ffDetectSound(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(result.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No active sound devices found"); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFSoundDevice, item, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_bool(doc, obj, "active", item->active); + yyjson_mut_obj_add_bool(doc, obj, "main", item->main); + yyjson_mut_obj_add_uint(doc, obj, "volume", item->volume); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_strbuf(doc, obj, "identifier", &item->identifier); + } + + FF_LIST_FOR_EACH(FFSoundDevice, device, result) + { + ffStrbufDestroy(&device->identifier); + ffStrbufDestroy(&device->name); + } +} diff --git a/src/modules/sound/sound.h b/src/modules/sound/sound.h index c35c16801..03ec8fe68 100644 --- a/src/modules/sound/sound.h +++ b/src/modules/sound/sound.h @@ -9,3 +9,4 @@ void ffInitSoundOptions(FFSoundOptions* options); bool ffParseSoundCommandOptions(FFSoundOptions* options, const char* key, const char* value); void ffDestroySoundOptions(FFSoundOptions* options); void ffParseSoundJsonObject(FFSoundOptions* options, yyjson_val* module); +void ffGenerateSoundJson(FFSoundOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/swap/swap.c b/src/modules/swap/swap.c index d4b08031d..331cbf26d 100644 --- a/src/modules/swap/swap.c +++ b/src/modules/swap/swap.c @@ -75,7 +75,7 @@ void ffPrintSwap(FFSwapOptions* options) void ffInitSwapOptions(FFSwapOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SWAP_MODULE_NAME, ffParseSwapCommandOptions, ffParseSwapJsonObject, ffPrintSwap); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SWAP_MODULE_NAME, ffParseSwapCommandOptions, ffParseSwapJsonObject, ffPrintSwap, ffGenerateSwapJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -110,3 +110,19 @@ void ffParseSwapJsonObject(FFSwapOptions* options, yyjson_val* module) ffPrintError(FF_SWAP_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateSwapJson(FF_MAYBE_UNUSED FFSwapOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFSwapResult storage; + const char* error = ffDetectSwap(&storage); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "total", storage.bytesTotal); + yyjson_mut_obj_add_uint(doc, obj, "used", storage.bytesUsed); +} diff --git a/src/modules/swap/swap.h b/src/modules/swap/swap.h index 5921ad9c5..fbb125a44 100644 --- a/src/modules/swap/swap.h +++ b/src/modules/swap/swap.h @@ -9,3 +9,4 @@ void ffInitSwapOptions(FFSwapOptions* options); bool ffParseSwapCommandOptions(FFSwapOptions* options, const char* key, const char* value); void ffDestroySwapOptions(FFSwapOptions* options); void ffParseSwapJsonObject(FFSwapOptions* options, yyjson_val* module); +void ffGenerateSwapJson(FFSwapOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/terminal/terminal.c b/src/modules/terminal/terminal.c index 5afa31c19..ac68652f0 100644 --- a/src/modules/terminal/terminal.c +++ b/src/modules/terminal/terminal.c @@ -6,7 +6,7 @@ #include -#define FF_TERMINAL_NUM_FORMAT_ARGS 10 +#define FF_TERMINAL_NUM_FORMAT_ARGS 6 void ffPrintTerminal(FFTerminalOptions* options) { @@ -33,20 +33,16 @@ void ffPrintTerminal(FFTerminalOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalProcessName}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalExe}, {FF_FORMAT_ARG_TYPE_STRING, result->terminalExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellProcessName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->shellExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->userShellExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellVersion} + {FF_FORMAT_ARG_TYPE_UINT, &result->terminalPid}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalPrettyName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalVersion}, }); } } void ffInitTerminalOptions(FFTerminalOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINAL_MODULE_NAME, ffParseTerminalCommandOptions, ffParseTerminalJsonObject, ffPrintTerminal); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINAL_MODULE_NAME, ffParseTerminalCommandOptions, ffParseTerminalJsonObject, ffPrintTerminal, ffGenerateTerminalJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -81,3 +77,22 @@ void ffParseTerminalJsonObject(FFTerminalOptions* options, yyjson_val* module) ffPrintError(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateTerminalJson(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFTerminalShellResult* result = ffDetectTerminalShell(); + + if(result->terminalProcessName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect terminal"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->terminalProcessName); + yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->terminalExe); + yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->terminalExeName); + yyjson_mut_obj_add_uint(doc, obj, "pid", result->terminalPid); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->terminalPrettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->terminalVersion); +} diff --git a/src/modules/terminal/terminal.h b/src/modules/terminal/terminal.h index 09949b00d..709b02e56 100644 --- a/src/modules/terminal/terminal.h +++ b/src/modules/terminal/terminal.h @@ -9,3 +9,4 @@ void ffInitTerminalOptions(FFTerminalOptions* options); bool ffParseTerminalCommandOptions(FFTerminalOptions* options, const char* key, const char* value); void ffDestroyTerminalOptions(FFTerminalOptions* options); void ffParseTerminalJsonObject(FFTerminalOptions* options, yyjson_val* module); +void ffGenerateTerminalJson(FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/terminalfont/terminalfont.c b/src/modules/terminalfont/terminalfont.c index 755fc302d..ca28576c9 100644 --- a/src/modules/terminalfont/terminalfont.c +++ b/src/modules/terminalfont/terminalfont.c @@ -49,7 +49,7 @@ void ffPrintTerminalFont(FFTerminalFontOptions* options) void ffInitTerminalFontOptions(FFTerminalFontOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALFONT_MODULE_NAME, ffParseTerminalFontCommandOptions, ffParseTerminalFontJsonObject, ffPrintTerminalFont); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALFONT_MODULE_NAME, ffParseTerminalFontCommandOptions, ffParseTerminalFontJsonObject, ffPrintTerminalFont, ffGenerateTerminalFontJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -84,3 +84,43 @@ void ffParseTerminalFontJsonObject(FFTerminalFontOptions* options, yyjson_val* m ffPrintError(FF_TERMINALFONT_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateTerminalFontJson(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFTerminalFontResult result; + ffFontInit(&result.font); + ffFontInit(&result.fallback); + ffStrbufInit(&result.error); + + if(!ffDetectTerminalFont(&result)) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &result.error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + + yyjson_mut_val* font = yyjson_mut_obj_add_obj(doc, obj, "font"); + yyjson_mut_obj_add_strbuf(doc, font, "name", &result.font.name); + yyjson_mut_obj_add_strbuf(doc, font, "size", &result.font.size); + yyjson_mut_val* fontStyles = yyjson_mut_obj_add_arr(doc, font, "styles"); + FF_LIST_FOR_EACH(FFstrbuf, style, result.font.styles) + { + yyjson_mut_arr_add_strbuf(doc, fontStyles, style); + } + yyjson_mut_obj_add_strbuf(doc, font, "pretty", &result.font.pretty); + + yyjson_mut_val* fallback = yyjson_mut_obj_add_obj(doc, obj, "fallback"); + yyjson_mut_obj_add_strbuf(doc, fallback, "name", &result.fallback.name); + yyjson_mut_obj_add_strbuf(doc, fallback, "size", &result.fallback.size); + yyjson_mut_val* fallbackStyles = yyjson_mut_obj_add_arr(doc, fallback, "styles"); + FF_LIST_FOR_EACH(FFstrbuf, style, result.fallback.styles) + { + yyjson_mut_arr_add_strbuf(doc, fallbackStyles, style); + } + yyjson_mut_obj_add_strbuf(doc, fallback, "pretty", &result.fallback.pretty); + + ffStrbufDestroy(&result.error); + ffFontDestroy(&result.font); + ffFontDestroy(&result.fallback); +} diff --git a/src/modules/terminalfont/terminalfont.h b/src/modules/terminalfont/terminalfont.h index ac5953a36..434eb1a1f 100644 --- a/src/modules/terminalfont/terminalfont.h +++ b/src/modules/terminalfont/terminalfont.h @@ -9,3 +9,4 @@ void ffInitTerminalFontOptions(FFTerminalFontOptions* options); bool ffParseTerminalFontCommandOptions(FFTerminalFontOptions* options, const char* key, const char* value); void ffDestroyTerminalFontOptions(FFTerminalFontOptions* options); void ffParseTerminalFontJsonObject(FFTerminalFontOptions* options, yyjson_val* module); +void ffGenerateTerminalFontJson(FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/terminalsize/terminalsize.c b/src/modules/terminalsize/terminalsize.c index f1ad620bc..22ef849ef 100644 --- a/src/modules/terminalsize/terminalsize.c +++ b/src/modules/terminalsize/terminalsize.c @@ -41,7 +41,7 @@ void ffPrintTerminalSize(FFTerminalSizeOptions* options) void ffInitTerminalSizeOptions(FFTerminalSizeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALSIZE_MODULE_NAME, ffParseTerminalSizeCommandOptions, ffParseTerminalSizeJsonObject, ffPrintTerminalSize); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALSIZE_MODULE_NAME, ffParseTerminalSizeCommandOptions, ffParseTerminalSizeJsonObject, ffPrintTerminalSize, ffGenerateTerminalSizeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -76,3 +76,20 @@ void ffParseTerminalSizeJsonObject(FFTerminalSizeOptions* options, yyjson_val* m ffPrintError(FF_TERMINALSIZE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateTerminalSizeJson(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFTerminalSizeResult result; + + if(!ffDetectTerminalSize(&result)) + { + yyjson_mut_obj_add_str(doc, module, "error", "Failed to detect terminal size"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "columns", result.columns); + yyjson_mut_obj_add_uint(doc, obj, "rows", result.rows); + yyjson_mut_obj_add_uint(doc, obj, "width", result.width); + yyjson_mut_obj_add_uint(doc, obj, "height", result.height); +} diff --git a/src/modules/terminalsize/terminalsize.h b/src/modules/terminalsize/terminalsize.h index 1703c5944..33699057d 100644 --- a/src/modules/terminalsize/terminalsize.h +++ b/src/modules/terminalsize/terminalsize.h @@ -9,3 +9,4 @@ void ffInitTerminalSizeOptions(FFTerminalSizeOptions* options); bool ffParseTerminalSizeCommandOptions(FFTerminalSizeOptions* options, const char* key, const char* value); void ffDestroyTerminalSizeOptions(FFTerminalSizeOptions* options); void ffParseTerminalSizeJsonObject(FFTerminalSizeOptions* options, yyjson_val* module); +void ffGenerateTerminalSizeJson(FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/theme/theme.c b/src/modules/theme/theme.c index 025e2c78f..2fc89549b 100644 --- a/src/modules/theme/theme.c +++ b/src/modules/theme/theme.c @@ -32,7 +32,7 @@ void ffPrintTheme(FFThemeOptions* options) void ffInitThemeOptions(FFThemeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_THEME_MODULE_NAME, ffParseThemeCommandOptions, ffParseThemeJsonObject, ffPrintTheme); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_THEME_MODULE_NAME, ffParseThemeCommandOptions, ffParseThemeJsonObject, ffPrintTheme, ffGenerateThemeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,17 @@ void ffParseThemeJsonObject(FFThemeOptions* options, yyjson_val* module) ffPrintError(FF_THEME_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateThemeJson(FF_MAYBE_UNUSED FFThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY theme = ffStrbufCreate(); + const char* error = ffDetectTheme(&theme); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &theme); +} diff --git a/src/modules/theme/theme.h b/src/modules/theme/theme.h index ef8f18a79..c0cee8584 100644 --- a/src/modules/theme/theme.h +++ b/src/modules/theme/theme.h @@ -9,3 +9,4 @@ void ffInitThemeOptions(FFThemeOptions* options); bool ffParseThemeCommandOptions(FFThemeOptions* options, const char* key, const char* value); void ffDestroyThemeOptions(FFThemeOptions* options); void ffParseThemeJsonObject(FFThemeOptions* options, yyjson_val* module); +void ffGenerateThemeJson(FFThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/title/title.c b/src/modules/title/title.c index 8374cd10b..a0174322a 100644 --- a/src/modules/title/title.c +++ b/src/modules/title/title.c @@ -4,55 +4,77 @@ #include "util/textModifier.h" #include "util/stringUtils.h" -#define FF_TITLE_NUM_FORMAT_ARGS 2 +#define FF_TITLE_NUM_FORMAT_ARGS 8 -static inline void printTitlePart(const FFstrbuf* content, const FFstrbuf* color) +static void appendText(FFstrbuf* output, const FFstrbuf* text, const FFstrbuf* color) { - if(!instance.config.pipe) + if (!instance.config.pipe) { if (instance.config.brightColor) - fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); - ffPrintColor(color->length > 0 ? color : &instance.config.colorTitle); + ffStrbufAppendS(output, FASTFETCH_TEXT_MODIFIER_BOLT); + if (color->length > 0) + ffStrbufAppendF(output, "\e[%sm", color->chars); + else if (instance.config.colorTitle.length > 0) + ffStrbufAppendF(output, "\e[%sm", instance.config.colorTitle.chars); } - ffStrbufWriteTo(content, stdout); + ffStrbufAppend(output, text); if(!instance.config.pipe) - fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); + ffStrbufAppendS(output, FASTFETCH_TEXT_MODIFIER_RESET); } void ffPrintTitle(FFTitleOptions* options) { - FFstrbuf* host = options->fqdn ? - &instance.state.platform.domainName : - &instance.state.platform.hostName; + FF_STRBUF_AUTO_DESTROY userNameColored = ffStrbufCreate(); + appendText(&userNameColored, &instance.state.platform.userName, &options->colorUser); + + FF_STRBUF_AUTO_DESTROY hostName = ffStrbufCreateCopy(&instance.state.platform.hostName); + if (!options->fqdn) + ffStrbufSubstrBeforeFirstC(&hostName, '.'); + + FF_STRBUF_AUTO_DESTROY hostNameColored = ffStrbufCreate(); + appendText(&hostNameColored, &hostName, &options->colorHost); + + FF_STRBUF_AUTO_DESTROY atColored = ffStrbufCreate(); + if (!instance.config.pipe && options->colorAt.length > 0) + { + ffStrbufAppendF(&atColored, "\e[%sm", options->colorAt.chars); + ffStrbufAppendC(&atColored, '@'); + ffStrbufAppendS(&atColored, FASTFETCH_TEXT_MODIFIER_RESET); + } + else + ffStrbufAppendC(&atColored, '@'); if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(options->moduleArgs.key.length == 0 ? NULL : FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - printTitlePart(&instance.state.platform.userName, &options->colorUser); - - if (!instance.config.pipe && options->colorAt.length > 0) - ffPrintColor(&options->colorAt); - putchar('@'); - - printTitlePart(host, &options->colorHost); - putchar('\n'); + ffStrbufWriteTo(&userNameColored, stdout); + ffStrbufWriteTo(&atColored, stdout); + ffStrbufPutTo(&hostNameColored, stdout); } else { ffPrintFormat(FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_TITLE_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.userName}, - {FF_FORMAT_ARG_TYPE_STRBUF, host}, + {FF_FORMAT_ARG_TYPE_STRBUF, &hostName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.homeDir}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.exePath}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.userShell}, + {FF_FORMAT_ARG_TYPE_STRBUF, &userNameColored}, + {FF_FORMAT_ARG_TYPE_STRBUF, &atColored}, + {FF_FORMAT_ARG_TYPE_STRBUF, &hostNameColored}, }); } } void ffInitTitleOptions(FFTitleOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TITLE_MODULE_NAME, ffParseTitleCommandOptions, ffParseTitleJsonObject, ffPrintTitle); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TITLE_MODULE_NAME, ffParseTitleCommandOptions, ffParseTitleJsonObject, ffPrintTitle, ffGenerateTitleJson); ffOptionInitModuleArg(&options->moduleArgs); + ffStrbufSetStatic(&options->moduleArgs.key, " "); + options->fqdn = false; ffStrbufInit(&options->colorUser); ffStrbufInit(&options->colorAt); @@ -140,3 +162,13 @@ void ffParseTitleJsonObject(FFTitleOptions* options, yyjson_val* module) ffPrintErrorString(FF_TITLE_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", key); } } + +void ffGenerateTitleJson(FF_MAYBE_UNUSED FFTitleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "userName", &instance.state.platform.userName); + yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &instance.state.platform.hostName); + yyjson_mut_obj_add_strbuf(doc, obj, "homeDir", &instance.state.platform.homeDir); + yyjson_mut_obj_add_strbuf(doc, obj, "exePath", &instance.state.platform.exePath); + yyjson_mut_obj_add_strbuf(doc, obj, "userShell", &instance.state.platform.userShell); +} diff --git a/src/modules/title/title.h b/src/modules/title/title.h index fd7ae5e68..76bec364d 100644 --- a/src/modules/title/title.h +++ b/src/modules/title/title.h @@ -9,3 +9,4 @@ void ffInitTitleOptions(FFTitleOptions* options); bool ffParseTitleCommandOptions(FFTitleOptions* options, const char* key, const char* value); void ffDestroyTitleOptions(FFTitleOptions* options); void ffParseTitleJsonObject(FFTitleOptions* options, yyjson_val* module); +void ffGenerateTitleJson(FFTitleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/uptime/uptime.c b/src/modules/uptime/uptime.c index 1574209e0..96be15456 100644 --- a/src/modules/uptime/uptime.c +++ b/src/modules/uptime/uptime.c @@ -4,13 +4,13 @@ #include "modules/uptime/uptime.h" #include "util/stringUtils.h" -#define FF_UPTIME_NUM_FORMAT_ARGS 4 +#define FF_UPTIME_NUM_FORMAT_ARGS 7 void ffPrintUptime(FFUptimeOptions* options) { - uint64_t uptime; + FFUptimeResult result; - const char* error = ffDetectUptime(&uptime); + const char* error = ffDetectUptime(&result); if(error) { @@ -18,10 +18,17 @@ void ffPrintUptime(FFUptimeOptions* options) return; } - uint32_t days = (uint32_t) uptime / 86400; - uint32_t hours = (uint32_t) (uptime - (days * 86400)) / 3600; - uint32_t minutes = (uint32_t) (uptime - (days * 86400) - (hours * 3600)) / 60; - uint32_t seconds = (uint32_t) uptime - (days * 86400) - (hours * 3600) - (minutes * 60); + uint64_t uptime = result.uptime; + + uint32_t milliseconds = (uint32_t) (uptime % 1000); + uptime /= 1000; + uint32_t seconds = (uint32_t) (uptime % 60); + uptime /= 60; + uint32_t minutes = (uint32_t) (uptime % 60); + uptime /= 60; + uint32_t hours = (uint32_t) (uptime % 24); + uptime /= 24; + uint32_t days = (uint32_t) uptime; if(options->moduleArgs.outputFormat.length == 0) { @@ -74,14 +81,17 @@ void ffPrintUptime(FFUptimeOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &days}, {FF_FORMAT_ARG_TYPE_UINT, &hours}, {FF_FORMAT_ARG_TYPE_UINT, &minutes}, - {FF_FORMAT_ARG_TYPE_UINT, &seconds} + {FF_FORMAT_ARG_TYPE_UINT, &seconds}, + {FF_FORMAT_ARG_TYPE_UINT, &milliseconds}, + {FF_FORMAT_ARG_TYPE_UINT64, &result.uptime}, + {FF_FORMAT_ARG_TYPE_UINT64, &result.bootTime}, }); } } void ffInitUptimeOptions(FFUptimeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_UPTIME_MODULE_NAME, ffParseUptimeCommandOptions, ffParseUptimeJsonObject, ffPrintUptime); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_UPTIME_MODULE_NAME, ffParseUptimeCommandOptions, ffParseUptimeJsonObject, ffPrintUptime, ffGenerateUptimeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -116,3 +126,19 @@ void ffParseUptimeJsonObject(FFUptimeOptions* options, yyjson_val* module) ffPrintError(FF_UPTIME_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateUptimeJson(FF_MAYBE_UNUSED FFUptimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFUptimeResult result; + const char* error = ffDetectUptime(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "uptime", result.uptime); + yyjson_mut_obj_add_uint(doc, obj, "bootTime", result.bootTime); +} diff --git a/src/modules/uptime/uptime.h b/src/modules/uptime/uptime.h index 3bc1fd55f..2fa12e963 100644 --- a/src/modules/uptime/uptime.h +++ b/src/modules/uptime/uptime.h @@ -9,3 +9,4 @@ void ffInitUptimeOptions(FFUptimeOptions* options); bool ffParseUptimeCommandOptions(FFUptimeOptions* options, const char* key, const char* value); void ffDestroyUptimeOptions(FFUptimeOptions* options); void ffParseUptimeJsonObject(FFUptimeOptions* options, yyjson_val* module); +void ffGenerateUptimeJson(FFUptimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/users/option.h b/src/modules/users/option.h index 80bf87698..6883e8dd0 100644 --- a/src/modules/users/option.h +++ b/src/modules/users/option.h @@ -8,4 +8,6 @@ typedef struct FFUsersOptions { FFModuleBaseInfo moduleInfo; FFModuleArgs moduleArgs; + + bool compact; } FFUsersOptions; diff --git a/src/modules/users/users.c b/src/modules/users/users.c index 0c1263620..ab58440c2 100644 --- a/src/modules/users/users.c +++ b/src/modules/users/users.c @@ -4,49 +4,99 @@ #include "modules/users/users.h" #include "util/stringUtils.h" +#include + #define FF_USERS_NUM_FORMAT_ARGS 1 void ffPrintUsers(FFUsersOptions* options) { - FF_LIST_AUTO_DESTROY users = ffListCreate(sizeof(FFstrbuf)); + FF_LIST_AUTO_DESTROY users = ffListCreate(sizeof(FFUserResult)); - FF_STRBUF_AUTO_DESTROY error = ffStrbufCreate(); + const char* error = ffDetectUsers(&users); - ffDetectUsers(&users, &error); - - if(error.length > 0) + if(error) { - ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%*s", error.length, error.chars); + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%s", error); return; } - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); - for(uint32_t i = 0; i < users.length; ++i) + if(users.length == 0) { - if(i > 0) - ffStrbufAppendS(&result, ", "); - FFstrbuf* user = (FFstrbuf*)ffListGet(&users, i); - ffStrbufAppend(&result, user); - ffStrbufDestroy(user); + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%s", "Unable to detect any users"); + return; } if(options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - puts(result.chars); + if(options->compact) + { + ffPrintLogoAndKey(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + for(uint32_t i = 0; i < users.length; ++i) + { + if(i > 0) + ffStrbufAppendS(&result, ", "); + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + ffStrbufAppend(&result, &user->name); + } + ffStrbufPutTo(&result, stdout); + } + else + { + for(uint32_t i = 0; i < users.length; ++i) + { + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + + ffPrintLogoAndKey(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateCopy(&user->name); + if(user->hostName.length) + ffStrbufAppendF(&result, "@%s", user->hostName.chars); + + if(user->loginTime) + { + char buf[64]; + time_t time = (time_t) (user->loginTime / 1000); + strftime(buf, sizeof(buf), "%FT%T%z", localtime(&time)); + ffStrbufAppendF(&result, " - login time %s", buf); + } + + ffStrbufPutTo(&result, stdout); + } + } } else { - ffPrintFormat(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_USERS_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &result}, - }); + for(uint32_t i = 0; i < users.length; ++i) + { + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + + ffPrintFormat(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_USERS_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &user->name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->hostName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->tty}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->clientIp}, + {FF_FORMAT_ARG_TYPE_UINT64, &user->loginTime}, + }); + } + } + + FF_LIST_FOR_EACH(FFUserResult, user, users) + { + ffStrbufDestroy(&user->clientIp); + ffStrbufDestroy(&user->hostName); + ffStrbufDestroy(&user->tty); + ffStrbufDestroy(&user->name); } } void ffInitUsersOptions(FFUsersOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_USERS_MODULE_NAME, ffParseUsersCommandOptions, ffParseUsersJsonObject, ffPrintUsers); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_USERS_MODULE_NAME, ffParseUsersCommandOptions, ffParseUsersJsonObject, ffPrintUsers, ffGenerateUsersJson); ffOptionInitModuleArg(&options->moduleArgs); + + options->compact = false; } bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const char* value) @@ -56,6 +106,12 @@ bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) return true; + if(ffStrEqualsIgnCase(subKey, "compact")) + { + options->compact = ffOptionParseBoolean(value); + return true; + } + return false; } @@ -77,6 +133,45 @@ void ffParseUsersJsonObject(FFUsersOptions* options, yyjson_val* module) if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) continue; + if (ffStrEqualsIgnCase(key, "compact")) + { + options->compact = yyjson_get_bool(val); + continue; + } + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateUsersJson(FF_MAYBE_UNUSED FFUsersOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFUserResult)); + + const char* error = ffDetectUsers(&results); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFUserResult, user, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "clientIp", &user->clientIp); + yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &user->hostName); + yyjson_mut_obj_add_uint(doc, obj, "loginTime", user->loginTime); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &user->name); + yyjson_mut_obj_add_strbuf(doc, obj, "tty", &user->tty); + } + +exit: + FF_LIST_FOR_EACH(FFUserResult, user, results) + { + ffStrbufDestroy(&user->clientIp); + ffStrbufDestroy(&user->hostName); + ffStrbufDestroy(&user->tty); + ffStrbufDestroy(&user->name); + } +} diff --git a/src/modules/users/users.h b/src/modules/users/users.h index 86d5f66f4..3ef57a1cf 100644 --- a/src/modules/users/users.h +++ b/src/modules/users/users.h @@ -9,3 +9,4 @@ void ffInitUsersOptions(FFUsersOptions* options); bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const char* value); void ffDestroyUsersOptions(FFUsersOptions* options); void ffParseUsersJsonObject(FFUsersOptions* options, yyjson_val* module); +void ffGenerateUsersJson(FFUsersOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/version/version.c b/src/modules/version/version.c index 9cef899d2..54781eb62 100644 --- a/src/modules/version/version.c +++ b/src/modules/version/version.c @@ -4,11 +4,11 @@ #include "modules/version/version.h" #include "util/stringUtils.h" -#define FF_VERSION_NUM_FORMAT_ARGS 6 +#define FF_VERSION_NUM_FORMAT_ARGS 8 void ffPrintVersion(FFVersionOptions* options) { - FFVersionResult result = {}; + FFVersionResult result; ffDetectVersion(&result); if(options->moduleArgs.outputFormat.length == 0) @@ -25,13 +25,15 @@ void ffPrintVersion(FFVersionOptions* options) {FF_FORMAT_ARG_TYPE_STRING, result.debugMode ? "debug" : "release"}, {FF_FORMAT_ARG_TYPE_STRING, result.architecture}, {FF_FORMAT_ARG_TYPE_STRING, result.cmakeBuiltType}, + {FF_FORMAT_ARG_TYPE_STRING, result.compileTime}, + {FF_FORMAT_ARG_TYPE_STRING, result.compiler}, }); } } void ffInitVersionOptions(FFVersionOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VERSION_MODULE_NAME, ffParseVersionCommandOptions, ffParseVersionJsonObject, ffPrintVersion); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VERSION_MODULE_NAME, ffParseVersionCommandOptions, ffParseVersionJsonObject, ffPrintVersion, ffGenerateVersionJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -66,3 +68,19 @@ void ffParseVersionJsonObject(FFVersionOptions* options, yyjson_val* module) ffPrintError(FF_VERSION_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateVersionJson(FF_MAYBE_UNUSED FFVersionOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFVersionResult result; + ffDetectVersion(&result); + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_str(doc, obj, "projectName", result.projectName); + yyjson_mut_obj_add_str(doc, obj, "architecture", result.architecture); + yyjson_mut_obj_add_str(doc, obj, "version", result.version); + yyjson_mut_obj_add_str(doc, obj, "versionTweak", result.versionTweak); + yyjson_mut_obj_add_str(doc, obj, "cmakeBuiltType", result.cmakeBuiltType); + yyjson_mut_obj_add_str(doc, obj, "compileTime", result.compileTime); + yyjson_mut_obj_add_str(doc, obj, "compiler", result.compiler); + yyjson_mut_obj_add_bool(doc, obj, "debugMode", result.debugMode); +} diff --git a/src/modules/version/version.h b/src/modules/version/version.h index ef423b36e..bb1e4c15f 100644 --- a/src/modules/version/version.h +++ b/src/modules/version/version.h @@ -9,3 +9,4 @@ void ffInitVersionOptions(FFVersionOptions* options); bool ffParseVersionCommandOptions(FFVersionOptions* options, const char* key, const char* value); void ffDestroyVersionOptions(FFVersionOptions* options); void ffParseVersionJsonObject(FFVersionOptions* options, yyjson_val* module); +void ffGenerateVersionJson(FFVersionOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/vulkan/vulkan.c b/src/modules/vulkan/vulkan.c index bb66ae604..14c37153a 100644 --- a/src/modules/vulkan/vulkan.c +++ b/src/modules/vulkan/vulkan.c @@ -1,5 +1,6 @@ #include "common/printing.h" #include "common/jsonconfig.h" +#include "detection/gpu/gpu.h" #include "detection/vulkan/vulkan.h" #include "modules/vulkan/vulkan.h" #include "util/stringUtils.h" @@ -45,7 +46,7 @@ void ffPrintVulkan(FFVulkanOptions* options) void ffInitVulkanOptions(FFVulkanOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VULKAN_MODULE_NAME, ffParseVulkanCommandOptions, ffParseVulkanJsonObject, ffPrintVulkan); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VULKAN_MODULE_NAME, ffParseVulkanCommandOptions, ffParseVulkanJsonObject, ffPrintVulkan, ffGenerateVulkanJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -80,3 +81,58 @@ void ffParseVulkanJsonObject(FFVulkanOptions* options, yyjson_val* module) ffPrintError(FF_VULKAN_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateVulkanJson(FF_MAYBE_UNUSED FFVulkanOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFVulkanResult* result = ffDetectVulkan(); + + if(result->error) + { + yyjson_mut_obj_add_str(doc, module, "error", result->error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "apiVersion", &result->apiVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "conformanceVersion", &result->conformanceVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "driver", &result->driver); + yyjson_mut_val* gpus = yyjson_mut_obj_add_arr(doc, obj, "gpus"); + FF_LIST_FOR_EACH(FFGPUResult, vulkanGpu, result->gpus) + { + yyjson_mut_val* gpuObj = yyjson_mut_arr_add_obj(doc, gpus); + yyjson_mut_obj_add_str(doc, gpuObj, "type", vulkanGpu->type == FF_GPU_TYPE_UNKNOWN ? "Unknown" : vulkanGpu->type == FF_GPU_TYPE_INTEGRATED ? "Integrated" : "Discrete"); + yyjson_mut_obj_add_strbuf(doc, gpuObj, "vendor", &vulkanGpu->vendor); + yyjson_mut_obj_add_strbuf(doc, gpuObj, "name", &vulkanGpu->name); + yyjson_mut_obj_add_strbuf(doc, gpuObj, "driver", &vulkanGpu->driver); + + yyjson_mut_val* memoryObj = yyjson_mut_obj_add_obj(doc, gpuObj, "memory"); + + { + yyjson_mut_val* dedicatedMemory = yyjson_mut_obj_add_obj(doc, memoryObj, "dedicated"); + if (vulkanGpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedMemory, "total", vulkanGpu->dedicated.total); + else + yyjson_mut_obj_add_null(doc, dedicatedMemory, "total"); + + if (vulkanGpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedMemory, "used", vulkanGpu->dedicated.total); + else + yyjson_mut_obj_add_null(doc, dedicatedMemory, "used"); + } + + { + yyjson_mut_val* sharedMemory = yyjson_mut_obj_add_obj(doc, memoryObj, "shared"); + if (vulkanGpu->shared.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedMemory, "total", vulkanGpu->shared.total); + else + yyjson_mut_obj_add_null(doc, sharedMemory, "total"); + + if (vulkanGpu->shared.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedMemory, "used", vulkanGpu->shared.used); + else + yyjson_mut_obj_add_null(doc, sharedMemory, "used"); + } + + yyjson_mut_obj_add_uint(doc, gpuObj, "deviceId", vulkanGpu->vulkanDeviceId); + } +} diff --git a/src/modules/vulkan/vulkan.h b/src/modules/vulkan/vulkan.h index e9b7d3954..a0b378394 100644 --- a/src/modules/vulkan/vulkan.h +++ b/src/modules/vulkan/vulkan.h @@ -9,3 +9,4 @@ void ffInitVulkanOptions(FFVulkanOptions* options); bool ffParseVulkanCommandOptions(FFVulkanOptions* options, const char* key, const char* value); void ffDestroyVulkanOptions(FFVulkanOptions* options); void ffParseVulkanJsonObject(FFVulkanOptions* options, yyjson_val* module); +void ffGenerateVulkanJson(FFVulkanOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wallpaper/wallpaper.c b/src/modules/wallpaper/wallpaper.c index e51ba590a..947a1031e 100644 --- a/src/modules/wallpaper/wallpaper.c +++ b/src/modules/wallpaper/wallpaper.c @@ -44,7 +44,7 @@ void ffPrintWallpaper(FFWallpaperOptions* options) void ffInitWallpaperOptions(FFWallpaperOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WALLPAPER_MODULE_NAME, ffParseWallpaperCommandOptions, ffParseWallpaperJsonObject, ffPrintWallpaper); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WALLPAPER_MODULE_NAME, ffParseWallpaperCommandOptions, ffParseWallpaperJsonObject, ffPrintWallpaper, ffGenerateWallpaperJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -79,3 +79,15 @@ void ffParseWallpaperJsonObject(FFWallpaperOptions* options, yyjson_val* module) ffPrintError(FF_WALLPAPER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWallpaperJson(FF_MAYBE_UNUSED FFWallpaperOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY fullpath = ffStrbufCreate(); + const char* error = ffDetectWallpaper(&fullpath); + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + yyjson_mut_obj_add_strbuf(doc, module, "result", &fullpath); +} diff --git a/src/modules/wallpaper/wallpaper.h b/src/modules/wallpaper/wallpaper.h index 244684fc4..3925784b2 100644 --- a/src/modules/wallpaper/wallpaper.h +++ b/src/modules/wallpaper/wallpaper.h @@ -9,3 +9,4 @@ void ffInitWallpaperOptions(FFWallpaperOptions* options); bool ffParseWallpaperCommandOptions(FFWallpaperOptions* options, const char* key, const char* value); void ffDestroyWallpaperOptions(FFWallpaperOptions* options); void ffParseWallpaperJsonObject(FFWallpaperOptions* options, yyjson_val* module); +void ffGenerateWallpaperJson(FFWallpaperOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/weather/weather.c b/src/modules/weather/weather.c index 68d18d550..ec25374b5 100644 --- a/src/modules/weather/weather.c +++ b/src/modules/weather/weather.c @@ -1,50 +1,22 @@ #include "common/printing.h" #include "common/jsonconfig.h" -#include "common/networking.h" +#include "detection/weather/weather.h" #include "modules/weather/weather.h" #include "util/stringUtils.h" #define FF_WEATHER_NUM_FORMAT_ARGS 1 -static FFNetworkingState state; -static int status = -1; - -void ffPrepareWeather(FFWeatherOptions* options) -{ - if (status != -1) - { - fputs("Error: " FF_WEATHER_MODULE_NAME " can only be used once due to internal limitations\n", stderr); - exit(1); - } - - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/"); - if (options->location.length) - ffStrbufAppend(&path, &options->location); - ffStrbufAppendS(&path, "?format="); - ffStrbufAppend(&path, &options->outputFormat); - status = ffNetworkingSendHttpRequest(&state, "wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n"); -} - void ffPrintWeather(FFWeatherOptions* options) { - if(status == -1) - ffPrepareWeather(options); + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + const char* error = ffDetectWeather(options, &result); - if(status == 0) + if(error) { - ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "Failed to connect to 'wttr.in'"); + ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "%s", error); return; } - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(4096); - bool success = ffNetworkingRecvHttpResponse(&state, &result, options->timeout); - if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); - - if(!success || result.length == 0) - { - ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "Failed to receive the server response"); - return; - } if(options->moduleArgs.outputFormat.length == 0) { @@ -61,7 +33,7 @@ void ffPrintWeather(FFWeatherOptions* options) void ffInitWeatherOptions(FFWeatherOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WEATHER_MODULE_NAME, ffParseWeatherCommandOptions, ffParseWeatherJsonObject, ffPrintWeather); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WEATHER_MODULE_NAME, ffParseWeatherCommandOptions, ffParseWeatherJsonObject, ffPrintWeather, ffGenerateWeatherJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInit(&options->location); @@ -138,3 +110,17 @@ void ffParseWeatherJsonObject(FFWeatherOptions* options, yyjson_val* module) ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWeatherJson(FFWeatherOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + const char* error = ffDetectWeather(options, &result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &result); +} diff --git a/src/modules/weather/weather.h b/src/modules/weather/weather.h index 888606957..a76940244 100644 --- a/src/modules/weather/weather.h +++ b/src/modules/weather/weather.h @@ -11,3 +11,4 @@ void ffInitWeatherOptions(FFWeatherOptions* options); bool ffParseWeatherCommandOptions(FFWeatherOptions* options, const char* key, const char* value); void ffDestroyWeatherOptions(FFWeatherOptions* options); void ffParseWeatherJsonObject(FFWeatherOptions* options, yyjson_val* module); +void ffGenerateWeatherJson(FFWeatherOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wifi/wifi.c b/src/modules/wifi/wifi.c index afa026917..449b82e67 100644 --- a/src/modules/wifi/wifi.c +++ b/src/modules/wifi/wifi.c @@ -72,7 +72,7 @@ void ffPrintWifi(FFWifiOptions* options) void ffInitWifiOptions(FFWifiOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WIFI_MODULE_NAME, ffParseWifiCommandOptions, ffParseWifiJsonObject, ffPrintWifi); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WIFI_MODULE_NAME, ffParseWifiCommandOptions, ffParseWifiJsonObject, ffPrintWifi, ffGenerateWifiJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -107,3 +107,49 @@ void ffParseWifiJsonObject(FFWifiOptions* options, yyjson_val* module) ffPrintError(FF_WIFI_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWifiJson(FF_MAYBE_UNUSED FFWifiOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFWifiResult)); + const char* error = ffDetectWifi(&result); + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + if(!result.length) + { + yyjson_mut_obj_add_str(doc, module, "error", "No Wifi interfaces found"); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFWifiResult, wifi, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + + yyjson_mut_val* inf = yyjson_mut_obj_add_obj(doc, obj, "inf"); + yyjson_mut_obj_add_strbuf(doc, inf, "description", &wifi->inf.description); + yyjson_mut_obj_add_strbuf(doc, inf, "status", &wifi->inf.status); + + yyjson_mut_val* conn = yyjson_mut_obj_add_obj(doc, obj, "conn"); + yyjson_mut_obj_add_strbuf(doc, conn, "status", &wifi->conn.status); + yyjson_mut_obj_add_strbuf(doc, conn, "ssid", &wifi->conn.ssid); + yyjson_mut_obj_add_strbuf(doc, conn, "macAddress", &wifi->conn.macAddress); + yyjson_mut_obj_add_strbuf(doc, conn, "protocol", &wifi->conn.protocol); + yyjson_mut_obj_add_real(doc, conn, "signalQuality", wifi->conn.signalQuality); + yyjson_mut_obj_add_real(doc, conn, "rxRate", wifi->conn.rxRate); + yyjson_mut_obj_add_real(doc, conn, "txRate", wifi->conn.txRate); + } + + FF_LIST_FOR_EACH(FFWifiResult, item, result) + { + ffStrbufDestroy(&item->inf.description); + ffStrbufDestroy(&item->inf.status); + ffStrbufDestroy(&item->conn.status); + ffStrbufDestroy(&item->conn.ssid); + ffStrbufDestroy(&item->conn.macAddress); + ffStrbufDestroy(&item->conn.protocol); + ffStrbufDestroy(&item->conn.security); + } +} diff --git a/src/modules/wifi/wifi.h b/src/modules/wifi/wifi.h index 0a7b78e2d..2957f876d 100644 --- a/src/modules/wifi/wifi.h +++ b/src/modules/wifi/wifi.h @@ -9,3 +9,4 @@ void ffInitWifiOptions(FFWifiOptions* options); bool ffParseWifiCommandOptions(FFWifiOptions* options, const char* key, const char* value); void ffDestroyWifiOptions(FFWifiOptions* options); void ffParseWifiJsonObject(FFWifiOptions* options, yyjson_val* module); +void ffGenerateWifiJson(FFWifiOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wm/wm.c b/src/modules/wm/wm.c index f02efbf4e..371b48deb 100644 --- a/src/modules/wm/wm.c +++ b/src/modules/wm/wm.c @@ -43,7 +43,7 @@ void ffPrintWM(FFWMOptions* options) void ffInitWMOptions(FFWMOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WM_MODULE_NAME, ffParseWMCommandOptions, ffParseWMJsonObject, ffPrintWM); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WM_MODULE_NAME, ffParseWMCommandOptions, ffParseWMJsonObject, ffPrintWM, ffGenerateWMJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -78,3 +78,18 @@ void ffParseWMJsonObject(FFWMOptions* options, yyjson_val* module) ffPrintError(FF_WM_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWMJson(FF_MAYBE_UNUSED FFWMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFDisplayServerResult* result = ffConnectDisplayServer(); + + if(result->wmPrettyName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No WM found"); + return; + } + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->wmProcessName); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->wmPrettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "protocolName", &result->wmProtocolName); +} diff --git a/src/modules/wm/wm.h b/src/modules/wm/wm.h index 607732d72..fe960b688 100644 --- a/src/modules/wm/wm.h +++ b/src/modules/wm/wm.h @@ -9,3 +9,4 @@ void ffInitWMOptions(FFWMOptions* options); bool ffParseWMCommandOptions(FFWMOptions* options, const char* key, const char* value); void ffDestroyWMOptions(FFWMOptions* options); void ffParseWMJsonObject(FFWMOptions* options, yyjson_val* module); +void ffGenerateWMJson(FFWMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wmtheme/wmtheme.c b/src/modules/wmtheme/wmtheme.c index 438e7f832..356076922 100644 --- a/src/modules/wmtheme/wmtheme.c +++ b/src/modules/wmtheme/wmtheme.c @@ -32,7 +32,7 @@ void ffPrintWMTheme(FFWMThemeOptions* options) void ffInitWMThemeOptions(FFWMThemeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WMTHEME_MODULE_NAME, ffParseWMThemeCommandOptions, ffParseWMThemeJsonObject, ffPrintWMTheme); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WMTHEME_MODULE_NAME, ffParseWMThemeCommandOptions, ffParseWMThemeJsonObject, ffPrintWMTheme, ffGenerateWMThemeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,15 @@ void ffParseWMThemeJsonObject(FFWMThemeOptions* options, yyjson_val* module) ffPrintError(FF_WMTHEME_DISPLAY_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWMThemeJson(FF_MAYBE_UNUSED FFWMThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY themeOrError = ffStrbufCreate(); + if(!ffDetectWmTheme(&themeOrError)) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &themeOrError); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &themeOrError); +} diff --git a/src/modules/wmtheme/wmtheme.h b/src/modules/wmtheme/wmtheme.h index 216fdc723..e90f9e253 100644 --- a/src/modules/wmtheme/wmtheme.h +++ b/src/modules/wmtheme/wmtheme.h @@ -9,3 +9,4 @@ void ffInitWMThemeOptions(FFWMThemeOptions* options); bool ffParseWMThemeCommandOptions(FFWMThemeOptions* options, const char* key, const char* value); void ffDestroyWMThemeOptions(FFWMThemeOptions* options); void ffParseWMThemeJsonObject(FFWMThemeOptions* options, yyjson_val* module); +void ffGenerateWMThemeJson(FFWMThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/util/FFcheckmacros.h b/src/util/FFcheckmacros.h index 1ed61cf82..9eb45f78c 100644 --- a/src/util/FFcheckmacros.h +++ b/src/util/FFcheckmacros.h @@ -3,8 +3,14 @@ #ifndef FASTFETCH_INCLUDED_FFCHECKMACROS #define FASTFETCH_INCLUDED_FFCHECKMACROS +#ifdef _MSC_VER + #include +#endif + #if defined(__GNUC__) || defined(__clang__) #define FF_C_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) + #define FF_C_NODISCARD _Check_return_ #else #define FF_C_NODISCARD #endif diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 70a2a029d..221a98fc3 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -73,6 +73,14 @@ void ffStrbufAppendC(FFstrbuf* strbuf, char c) strbuf->chars[strbuf->length] = '\0'; } +void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c) +{ + ffStrbufEnsureFree(strbuf, num); + memset(&strbuf->chars[strbuf->length], c, num); + strbuf->length += num; + strbuf->chars[strbuf->length] = '\0'; +} + void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value) { if(value == NULL || length == 0) @@ -423,11 +431,11 @@ double ffStrbufToDouble(const FFstrbuf* strbuf) return str_end == strbuf->chars ? 0.0/0.0 : result; } -uint16_t ffStrbufToUInt16(const FFstrbuf* strbuf, uint16_t defaultValue) +uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue) { char* str_end; - unsigned long result = strtoul(strbuf->chars, &str_end, 10); - return str_end == strbuf->chars || result > UINT16_MAX ? defaultValue : (uint16_t)result; + unsigned long long result = strtoull(strbuf->chars, &str_end, 10); + return str_end == strbuf->chars ? defaultValue : (uint64_t)result; } void ffStrbufUpperCase(FFstrbuf* strbuf) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 9e9bce198..9a28bafb4 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -38,6 +38,7 @@ void ffStrbufClear(FFstrbuf* strbuf); static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value); 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 ffStrbufAppendNSExludingC(FFstrbuf* strbuf, uint32_t length, const char* value, char exclude); void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int(*transformFunc)(int)); @@ -49,7 +50,7 @@ void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value); void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value); void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value); -void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...); +FF_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufTrimLeft(FFstrbuf* strbuf, char c); void ffStrbufTrimRight(FFstrbuf* strbuf, char c); @@ -81,7 +82,7 @@ void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file); void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file); FF_C_NODISCARD double ffStrbufToDouble(const FFstrbuf* strbuf); -FF_C_NODISCARD uint16_t ffStrbufToUInt16(const FFstrbuf* strbuf, uint16_t defaultValue); +FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue); void ffStrbufUpperCase(FFstrbuf* strbuf); void ffStrbufLowerCase(FFstrbuf* strbuf); diff --git a/src/util/apple/cf_helpers.c b/src/util/apple/cf_helpers.c index 797b51b59..2ce563d5e 100644 --- a/src/util/apple/cf_helpers.c +++ b/src/util/apple/cf_helpers.c @@ -83,6 +83,27 @@ const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) return "TypeID is neither 'CFNumber' nor 'CFData'"; } +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); + + if(trueLength < offset + size) + return "Data length is less than offset + size"; + + if(length) + *length = (uint32_t) trueLength; + + CFDataGetBytes((CFDataRef)cf, CFRangeMake(offset, size), result); + return NULL; +} + const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) { CFDictionaryRef cf = (CFDictionaryRef)CFDictionaryGetValue(dict, key); diff --git a/src/util/apple/cf_helpers.h b/src/util/apple/cf_helpers.h index 52755bb8b..a8d9804a3 100644 --- a/src/util/apple/cf_helpers.h +++ b/src/util/apple/cf_helpers.h @@ -11,6 +11,7 @@ const char* ffCfStrGetString(CFStringRef str, 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* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length); const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result); static inline CFNumberRef ffCfCreateInt(int value) diff --git a/src/util/platform/FFPlatform.c b/src/util/platform/FFPlatform.c index ad0a179da..b81adc3c6 100644 --- a/src/util/platform/FFPlatform.c +++ b/src/util/platform/FFPlatform.c @@ -12,7 +12,6 @@ void ffPlatformInit(FFPlatform* platform) ffStrbufInit(&platform->userName); ffStrbufInit(&platform->hostName); - ffStrbufInit(&platform->domainName); ffStrbufInit(&platform->userShell); ffStrbufInit(&platform->systemName); @@ -22,9 +21,6 @@ void ffPlatformInit(FFPlatform* platform) ffPlatformInitImpl(platform); - if(platform->domainName.length == 0) - ffStrbufAppend(&platform->domainName, &platform->hostName); - if(platform->systemName.length == 0) { #if defined(__linux__) @@ -59,13 +55,13 @@ void ffPlatformDestroy(FFPlatform* platform) ffStrbufDestroy(&platform->userName); ffStrbufDestroy(&platform->hostName); - ffStrbufDestroy(&platform->domainName); ffStrbufDestroy(&platform->userShell); ffStrbufDestroy(&platform->systemArchitecture); ffStrbufDestroy(&platform->systemName); ffStrbufDestroy(&platform->systemRelease); ffStrbufDestroy(&platform->systemVersion); + ffStrbufDestroy(&platform->systemDisplayVersion); } void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path) diff --git a/src/util/platform/FFPlatform.h b/src/util/platform/FFPlatform.h index fa646f9de..48c72513e 100644 --- a/src/util/platform/FFPlatform.h +++ b/src/util/platform/FFPlatform.h @@ -15,13 +15,13 @@ typedef struct FFPlatform { FFstrbuf userName; FFstrbuf hostName; - FFstrbuf domainName; FFstrbuf userShell; FFstrbuf systemName; FFstrbuf systemRelease; FFstrbuf systemVersion; FFstrbuf systemArchitecture; + FFstrbuf systemDisplayVersion; } FFPlatform; void ffPlatformInit(FFPlatform* platform); diff --git a/src/util/platform/FFPlatform_unix.c b/src/util/platform/FFPlatform_unix.c index 3f7aada12..33c76bc69 100644 --- a/src/util/platform/FFPlatform_unix.c +++ b/src/util/platform/FFPlatform_unix.c @@ -141,38 +141,9 @@ static void getUserName(FFPlatform* platform, const struct passwd* pwd) static void getHostName(FFPlatform* platform, const struct utsname* uts) { - char hostname[256]; - if(gethostname(hostname, sizeof(hostname)) == 0) - ffStrbufAppendS(&platform->hostName, hostname); - - if(platform->hostName.length == 0) - ffStrbufAppendS(&platform->hostName, uts->nodename); + ffStrbufAppendS(&platform->hostName, uts->nodename); } -#ifdef __linux__ -#include -static void getDomainName(FFPlatform* platform) -{ - struct addrinfo hints = {0}; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - - struct addrinfo* info = NULL; - - if(getaddrinfo(platform->hostName.chars, "80", &hints, &info) != 0) - return; - - struct addrinfo* current = info; - while(platform->domainName.length == 0 && current != NULL) - { - ffStrbufAppendS(&platform->domainName, current->ai_canonname); - current = current->ai_next; - } - - freeaddrinfo(info); -} -#endif - static void getUserShell(FFPlatform* platform, const struct passwd* pwd) { const char* shell = getenv("SHELL"); @@ -198,15 +169,11 @@ void ffPlatformInitImpl(FFPlatform* platform) getUserName(platform, pwd); getHostName(platform, &uts); - - #ifdef __linux__ - getDomainName(platform); - #endif - getUserShell(platform, pwd); ffStrbufAppendS(&platform->systemName, uts.sysname); ffStrbufAppendS(&platform->systemRelease, uts.release); ffStrbufAppendS(&platform->systemVersion, uts.version); ffStrbufAppendS(&platform->systemArchitecture, uts.machine); + ffStrbufInit(&platform->systemDisplayVersion); } diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c index 0009dd10e..0774c81c3 100644 --- a/src/util/platform/FFPlatform_windows.c +++ b/src/util/platform/FFPlatform_windows.c @@ -2,10 +2,16 @@ #include "common/io/io.h" #include "util/stringUtils.h" #include "util/windows/unicode.h" +#include "util/windows/registry.h" +#include #include #include +NTSTATUS NTAPI RtlGetVersion( + _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation +); + static void getExePath(FFPlatform* platform) { wchar_t exePathW[MAX_PATH]; @@ -130,63 +136,51 @@ static void getHostName(FFPlatform* platform) ffStrbufSetWS(&platform->hostName, buffer); } -static void getDomainName(FFPlatform* platform) +static void getUserShell(FFPlatform* platform) { - wchar_t buffer[128]; - DWORD len = sizeof(buffer) / sizeof(*buffer); - if(GetComputerNameExW(ComputerNameDnsDomain, buffer, &len)) - ffStrbufSetWS(&platform->domainName, buffer); -} - -static void getSystemName(FFPlatform* platform) -{ - ffStrbufAppendS(&platform->systemName, getenv("OS")); + // Works in MSYS2 + ffStrbufAppendS(&platform->userShell, getenv("SHELL")); + ffStrbufReplaceAllC(&platform->userShell, '\\', '/'); } static void getSystemReleaseAndVersion(FFPlatform* platform) { - HKEY hKey; - if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + RTL_OSVERSIONINFOW osVersion = { .dwOSVersionInfoSize = sizeof(osVersion) }; + if (!NT_SUCCESS(RtlGetVersion(&osVersion))) return; - DWORD bufSize; + FF_HKEY_AUTO_DESTROY hKey = NULL; + if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL)) + return; - char currentVersion[32]; + uint32_t ubr = 0; + ffRegReadUint(hKey, L"UBR", &ubr, NULL); + ffStrbufAppendF(&platform->systemRelease, + "%u.%u.%u.%u", + (unsigned) osVersion.dwMajorVersion, + (unsigned) osVersion.dwMinorVersion, + (unsigned) osVersion.dwBuildNumber, + (unsigned) ubr); + + ffStrbufInit(&platform->systemDisplayVersion); + if(!ffRegReadStrbuf(hKey, L"DisplayVersion", &platform->systemDisplayVersion, NULL) && osVersion.szCSDVersion[0]) + ffStrbufSetWS(&platform->systemDisplayVersion, osVersion.szCSDVersion); + + ffRegReadStrbuf(hKey, L"BuildLabEx", &platform->systemVersion, NULL); + + switch (osVersion.dwPlatformId) { - DWORD currentMajorVersionNumber; - DWORD currentMinorVersionNumber; - bufSize = sizeof(currentMajorVersionNumber); - if(RegGetValueW(hKey, NULL, L"CurrentMajorVersionNumber", RRF_RT_REG_DWORD, NULL, ¤tMajorVersionNumber, &bufSize) == ERROR_SUCCESS && - RegGetValueW(hKey, NULL, L"CurrentMinorVersionNumber", RRF_RT_REG_DWORD, NULL, ¤tMinorVersionNumber, &bufSize) == ERROR_SUCCESS - ) - snprintf(currentVersion, sizeof(currentVersion), "%u.%u", (unsigned)currentMajorVersionNumber, (unsigned)currentMinorVersionNumber); - else - { - bufSize = sizeof(currentVersion); - if(RegGetValueA(hKey, NULL, "CurrentVersion", RRF_RT_REG_SZ, NULL, currentVersion, &bufSize) != ERROR_SUCCESS) - strcpy(currentVersion, "0.0"); - } + case VER_PLATFORM_WIN32s: + ffStrbufAppendS(&platform->systemName, "WIN32s"); + break; + case VER_PLATFORM_WIN32_WINDOWS: + ffStrbufAppendS(&platform->systemName, "WIN32_WINDOWS"); + break; + case VER_PLATFORM_WIN32_NT: + ffStrbufAppendS(&platform->systemName, "WIN32_NT"); + break; } - - char currentBuildNumber[32]; - bufSize = sizeof(currentBuildNumber); - if(RegGetValueA(hKey, NULL, "CurrentBuildNumber", RRF_RT_REG_SZ, NULL, currentBuildNumber, &bufSize) != ERROR_SUCCESS) - strcpy(currentBuildNumber, "0"); - - DWORD ubr; - bufSize = sizeof(ubr); - if(RegGetValueW(hKey, NULL, L"UBR", RRF_RT_REG_DWORD, NULL, &ubr, &bufSize) != ERROR_SUCCESS || bufSize != sizeof(ubr)) - ubr = 0; - - ffStrbufAppendF(&platform->systemRelease, "%s.%s.%u", currentVersion, currentBuildNumber, (unsigned)ubr); - - ffStrbufEnsureFree(&platform->systemVersion, 256); - bufSize = (DWORD) ffStrbufGetFree(&platform->systemVersion); - if(RegGetValueA(hKey, NULL, "DisplayVersion", RRF_RT_REG_SZ, NULL, platform->systemVersion.chars, &bufSize) == ERROR_SUCCESS) - platform->systemVersion.length = (uint32_t) bufSize; - - RegCloseKey(hKey); } static void getSystemArchitecture(FFPlatform* platform) @@ -233,9 +227,8 @@ void ffPlatformInitImpl(FFPlatform* platform) getUserName(platform); getHostName(platform); - getDomainName(platform); + getUserShell(platform); - getSystemName(platform); getSystemReleaseAndVersion(platform); getSystemArchitecture(platform); } diff --git a/src/util/unused.h b/src/util/unused.h index fbc0e93e3..faef70d09 100644 --- a/src/util/unused.h +++ b/src/util/unused.h @@ -5,6 +5,10 @@ static inline void ffUnused(int dummy, ...) { (void) dummy; } #define FF_UNUSED(...) ffUnused(0, __VA_ARGS__); -#define FF_MAYBE_UNUSED __attribute__ ((__unused__)) +#if defined(__GNUC__) || defined(__clang__) + #define FF_MAYBE_UNUSED __attribute__ ((__unused__)) +#else + #define FF_MAYBE_UNUSED +#endif #endif diff --git a/src/util/windows/unicode.h b/src/util/windows/unicode.h index 6892134dc..a25d4a7b4 100644 --- a/src/util/windows/unicode.h +++ b/src/util/windows/unicode.h @@ -10,6 +10,7 @@ void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) { + if (!source) return ffStrbufClear(result); return ffStrbufSetNWS(result, (uint32_t)wcslen(source), source); } @@ -17,6 +18,7 @@ void ffStrbufInitNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); static inline void ffStrbufInitWS(FFstrbuf* result, const wchar_t* source) { + if (!source) return ffStrbufInit(result); return ffStrbufInitNWS(result, (uint32_t)wcslen(source), source); } @@ -29,6 +31,7 @@ static inline FFstrbuf ffStrbufCreateNWS(uint32_t length, const wchar_t* source) static inline FFstrbuf ffStrbufCreateWS(const wchar_t* source) { + if (!source) return ffStrbufCreate(); return ffStrbufCreateNWS((uint32_t)wcslen(source), source); } diff --git a/tests/strbuf.c b/tests/strbuf.c index d35c93e2f..702f40539 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -56,6 +56,16 @@ int main(void) VERIFY(strbuf.allocated == 0); VERIFY(strbuf.length == 0); + //append(N)C + + ffStrbufAppendC(&strbuf, '1'); + VERIFY(ffStrbufEqualS(&strbuf, "1")); + VERIFY(strbuf.allocated >= 1); + ffStrbufAppendNC(&strbuf, 5, '2'); + VERIFY(ffStrbufEqualS(&strbuf, "122222")); + VERIFY(strbuf.allocated >= 6); + ffStrbufClear(&strbuf); + //appendS ffStrbufAppendS(&strbuf, "12345"); @@ -113,7 +123,7 @@ int main(void) //toNumber VERIFY(ffStrbufToDouble(&strbuf) == 123456789.0); - VERIFY(ffStrbufToUInt16(&strbuf, 999) == 999); //overflow + VERIFY(ffStrbufToUInt(&strbuf, 999) == 123456789); //countC