├── .clang-format ├── .clang-tidy ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── flakehub.yml │ └── update-flake.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── MANUAL.md ├── Makefile ├── README.md ├── albafetch.conf ├── debian └── makedeb.sh ├── default.nix ├── example_logo.txt ├── flake.lock ├── flake.nix ├── images ├── albafetch.png ├── albafetch_demo.png ├── albafetch_demo_default.png ├── time_albafetch.png ├── time_neofetch.png └── v2.0.png ├── meson.build ├── nix └── package.nix └── src ├── config ├── config.c ├── config.h ├── parsing.c └── parsing.h ├── debug.c ├── info ├── battery.c ├── bios.c ├── colors.c ├── cpu.c ├── cursor_theme.c ├── date.c ├── desktop.c ├── gpu.c ├── gtk_theme.c ├── host.c ├── hostname.c ├── icon_theme.c ├── info.h ├── kernel.c ├── light_colors.c ├── local_ip.c ├── login_shell.c ├── memory.c ├── os.c ├── packages.c ├── public_ip.c ├── pwd.c ├── shell.c ├── swap.c ├── term.c ├── uptime.c └── user.c ├── macos ├── bsdwrap.c ├── bsdwrap.h ├── macos_gpu_string.m ├── macos_infos.c └── macos_infos.h ├── main.c ├── optdeps ├── glib.c ├── libpci.c └── optdeps.h └── utils ├── logos.h ├── queue.c ├── queue.h ├── return.h ├── utils.c ├── utils.h ├── wrappers.c └── wrappers.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | # Based on Hyprland's 4 | Language: Cpp 5 | BasedOnStyle: LLVM 6 | 7 | AccessModifierOffset: -2 8 | AlignAfterOpenBracket: Align 9 | AlignConsecutiveAssignments: None 10 | AlignConsecutiveDeclarations: None 11 | AlignConsecutiveMacros: true 12 | AlignEscapedNewlines: Right 13 | AlignOperands: false 14 | AlignTrailingComments: true 15 | AllowAllArgumentsOnNextLine: true 16 | AllowAllConstructorInitializersOnNextLine: true 17 | AllowAllParametersOfDeclarationOnNextLine: true 18 | AllowShortBlocksOnASingleLine: true 19 | AllowShortCaseLabelsOnASingleLine: true 20 | AllowShortFunctionsOnASingleLine: Empty 21 | AllowShortIfStatementsOnASingleLine: Never 22 | AllowShortLambdasOnASingleLine: All 23 | AllowShortLoopsOnASingleLine: false 24 | AlwaysBreakAfterDefinitionReturnType: None 25 | AlwaysBreakAfterReturnType: None 26 | AlwaysBreakBeforeMultilineStrings: false 27 | AlwaysBreakTemplateDeclarations: Yes 28 | BreakBeforeBraces: Attach 29 | BreakBeforeTernaryOperators: false 30 | BreakConstructorInitializers: AfterColon 31 | ColumnLimit: 180 32 | CompactNamespaces: false 33 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 34 | ExperimentalAutoDetectBinPacking: false 35 | FixNamespaceComments: false 36 | IncludeBlocks: Preserve 37 | IndentCaseLabels: true 38 | IndentWidth: 4 39 | InsertNewlineAtEOF: true 40 | PointerAlignment: Right 41 | ReflowComments: false 42 | SortIncludes: false 43 | SortUsingDeclarations: false 44 | SpaceAfterCStyleCast: false 45 | SpaceAfterLogicalNot: false 46 | SpaceAfterTemplateKeyword: true 47 | SpaceBeforeCtorInitializerColon: true 48 | SpaceBeforeInheritanceColon: true 49 | SpaceBeforeParens: false 50 | SpaceBeforeRangeBasedForLoopColon: true 51 | SpaceInEmptyParentheses: false 52 | SpacesBeforeTrailingComments: 1 53 | SpacesInAngles: false 54 | SpacesInContainerLiterals: false 55 | SpacesInCStyleCastParentheses: false 56 | SpacesInParentheses: false 57 | SpacesInSquareBrackets: false 58 | Standard: Auto 59 | TabWidth: 4 60 | UseTab: Never 61 | 62 | AllowShortEnumsOnASingleLine: false 63 | 64 | BraceWrapping: 65 | AfterEnum: false 66 | 67 | NamespaceIndentation: All 68 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | WarningsAsErrors: '*' 2 | HeaderFilterRegex: '.*\.hpp' 3 | FormatStyle: file 4 | Checks: > 5 | -*, 6 | bugprone-*, 7 | -bugprone-easily-swappable-parameters, 8 | -bugprone-forward-declararion-namespace, 9 | -bugprone-forward-declararion-namespace, 10 | -bugprone-macro-parentheses, 11 | -bugprone-narrowing-conversions, 12 | -bugprone-branch-clone, 13 | -bugprone-assignment-in-if-condition, 14 | concurrency-*, 15 | -concurrency-mt-unsafe, 16 | cppcoreguidelines-*, 17 | -cppcoreguidelines-owning-memory, 18 | -cppcoreguidelines-avoid-magic-numbers, 19 | -cppcoreguidelines-pro-bounds-constant-array-index, 20 | -cppcoreguidelines-avoid-const-or-ref-data-members, 21 | -cppcoreguidelines-non-private-member-variables-in-classes, 22 | -cppcoreguidelines-avoid-goto, 23 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 24 | -cppcoreguidelines-avoid-do-while, 25 | -cppcoreguidelines-avoid-non-const-global-variables, 26 | -cppcoreguidelines-special-member-functions, 27 | -cppcoreguidelines-explicit-virtual-functions, 28 | -cppcoreguidelines-avoid-c-arrays, 29 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 30 | -cppcoreguidelines-narrowing-conversions, 31 | -cppcoreguidelines-pro-type-union-access, 32 | -cppcoreguidelines-pro-type-member-init, 33 | -cppcoreguidelines-macro-usage, 34 | -cppcoreguidelines-macro-to-enum, 35 | -cppcoreguidelines-init-variables, 36 | -cppcoreguidelines-pro-type-cstyle-cast, 37 | -cppcoreguidelines-pro-type-vararg, 38 | -cppcoreguidelines-pro-type-reinterpret-cast, 39 | google-global-names-in-headers, 40 | -google-readability-casting, 41 | google-runtime-operator, 42 | misc-*, 43 | -misc-unused-parameters, 44 | -misc-no-recursion, 45 | -misc-non-private-member-variables-in-classes, 46 | -misc-include-cleaner, 47 | -misc-use-anonymous-namespace, 48 | -misc-const-correctness, 49 | modernize-*, 50 | -modernize-return-braced-init-list, 51 | -modernize-use-trailing-return-type, 52 | -modernize-use-using, 53 | -modernize-use-override, 54 | -modernize-avoid-c-arrays, 55 | -modernize-macro-to-enum, 56 | -modernize-loop-convert, 57 | -modernize-use-nodiscard, 58 | -modernize-pass-by-value, 59 | -modernize-use-auto, 60 | performance-*, 61 | -performance-avoid-endl, 62 | -performance-unnecessary-value-param, 63 | portability-std-allocator-const, 64 | readability-*, 65 | -readability-function-cognitive-complexity, 66 | -readability-function-size, 67 | -readability-identifier-length, 68 | -readability-magic-numbers, 69 | -readability-uppercase-literal-suffix, 70 | -readability-braces-around-statements, 71 | -readability-redundant-access-specifiers, 72 | -readability-else-after-return, 73 | -readability-container-data-pointer, 74 | -readability-implicit-bool-conversion, 75 | -readability-avoid-nested-conditional-operator, 76 | -readability-redundant-member-init, 77 | -readability-redundant-string-init, 78 | -readability-avoid-const-params-in-decls, 79 | -readability-named-parameter, 80 | -readability-convert-member-functions-to-static, 81 | -readability-qualified-auto, 82 | -readability-make-member-function-const, 83 | -readability-isolate-declaration, 84 | -readability-inconsistent-declaration-parameter-name, 85 | -clang-diagnostic-error, 86 | 87 | CheckOptions: 88 | performance-for-range-copy.WarnOnAllAutoCopies: true 89 | performance-inefficient-string-concatenation.StrictMode: true 90 | readability-braces-around-statements.ShortStatementLines: 0 91 | readability-identifier-naming.ClassCase: CamelCase 92 | readability-identifier-naming.ClassIgnoredRegexp: I.* 93 | readability-identifier-naming.ClassPrefix: C 94 | readability-identifier-naming.EnumCase: CamelCase 95 | readability-identifier-naming.EnumPrefix: e 96 | readability-identifier-naming.EnumConstantCase: UPPER_CASE 97 | readability-identifier-naming.FunctionCase: camelBack 98 | readability-identifier-naming.NamespaceCase: CamelCase 99 | readability-identifier-naming.NamespacePrefix: N 100 | readability-identifier-naming.StructPrefix: S 101 | readability-identifier-naming.StructCase: CamelCase 102 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: alba4k -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help me improve 4 | title: "[Bug Report]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Screenshots** 17 | If applicable, add screenshots to help explain your problem. 18 | 19 | **Desktop (please complete the following information):** 20 | - OS: [e.g. Arch Linux] 21 | - Terminal [e.g. Kitty, Alacritty] 22 | 23 | **Possibly relevant config lines:** 24 | - Post a screenshot of / copy paste part the relevant config file 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature Request]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated that [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen or to be added. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | commit-message: 13 | prefix: "actions" 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | push: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest] 13 | include: 14 | - os: ubuntu-latest 15 | platform: linux 16 | - os: macos-latest 17 | platform: darwin 18 | 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - uses: cachix/install-nix-action@v31 24 | 25 | - uses: cachix/cachix-action@v16 26 | with: 27 | name: albafetch 28 | authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" 29 | 30 | - name: build (linux/x64) 31 | if: runner.os == 'Linux' 32 | run: nix build -L --accept-flake-config .#albafetch-static 33 | 34 | - name: build (darwin/x64) 35 | if: runner.os == 'macOS' 36 | run: nix build -L --accept-flake-config 37 | 38 | - name: test 39 | run: ./result/bin/albafetch 40 | 41 | - uses: actions/upload-artifact@v4 42 | if: runner.os == 'Linux' 43 | with: 44 | name: albafetch-linux-x64-static 45 | path: ${{ github.workspace }}/result/bin/albafetch 46 | 47 | - uses: actions/upload-artifact@v4 48 | if: runner.os == 'macOS' 49 | with: 50 | name: albafetch-darwin-x64 51 | path: ${{ github.workspace }}/result/bin/albafetch 52 | 53 | - name: build (linux/aarch64-multiplatform) 54 | if: runner.os == 'Linux' 55 | run: nix build -L --accept-flake-config .#albafetch-arm 56 | 57 | - uses: actions/upload-artifact@v4 58 | if: runner.os == 'Linux' 59 | with: 60 | name: albafetch-linux-aarch64-static 61 | path: ${{ github.workspace }}/result/bin/albafetch 62 | -------------------------------------------------------------------------------- /.github/workflows/flakehub.yml: -------------------------------------------------------------------------------- 1 | name: Publish tags to FlakeHub 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: DeterminateSystems/nix-installer-action@main 19 | 20 | - uses: DeterminateSystems/flakehub-push@main 21 | with: 22 | visibility: "public" 23 | rolling: true 24 | -------------------------------------------------------------------------------- /.github/workflows/update-flake.yml: -------------------------------------------------------------------------------- 1 | name: Update Nix Flake lock 2 | 3 | on: 4 | schedule: 5 | # run every saturday 6 | - cron: "0 0 * * 6" 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | jobs: 14 | update-flake: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - uses: cachix/install-nix-action@v31 21 | with: 22 | github_access_token: ${{ github.token }} 23 | 24 | - uses: DeterminateSystems/update-flake-lock@v25 25 | id: update 26 | with: 27 | commit-msg: "flake: update inputs" 28 | pr-title: "flake: update inputs" 29 | token: ${{ github.token }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | build 4 | debian/albafetch* 5 | 6 | # nix stuff 7 | .direnv/ 8 | result* 9 | repl-result-out* 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes since v4.2.1 2 | 3 | ## New Features 4 | 5 | ### Modules 6 | * Added a `swap` module that prints the used and total swap on linux 7 | * The `cpu` modules now prints 2 decimal digits is the frequency is below 1 GHz 8 | 9 | ### Config syntax 10 | * Custom ascii arts will now print in the specified color by default 11 | * Added `swap_label`, the label for the new swap module 12 | * Added `swap_perc`, whether the used swap percentage should be printed 13 | 14 | ### Command line arguments 15 | * Added `--version` (`-v` for short), prints version and build commit 16 | 17 | ### Other 18 | * Added a logo for [Rocky Linux](https://rockylinux.org) 19 | 20 | ## Changes 21 | * Updated the [Garuda Linux](https://garudalinux.org) logo 22 | * Changed return values to be more clear than 0 or 1 23 | * User errors are now handled gracefully 24 | * term will not print `Apple Terminal` instead of `Apple_Terminal` 25 | 26 | ## Bug fixes 27 | 28 | ### Noticeable fixes 29 | * Packages should be counted faster for dpkg and especially snap 30 | * Alacritty will now be properly recognized even with `general.ipc_socket = false` 31 | * `gpu` should now print all gpus even when libpci doesn't work 32 | * Fixed `pwd_path` not working 33 | * Should now exit on OOM, not segfault 34 | 35 | ### Technical fixes 36 | * Reduced the size of default logos 37 | * Moved some functions from `src/utils/utils.c` to `src/config/config.c` and `src/config/config.c`. 38 | Planning on rewriting config parsing and reducing `src/utils/utils.c` even more. 39 | * Minor memory safety improvements 40 | 41 | ## Dependencies 42 | * sqlite3 is now needed to build the project 43 | * meson, ninja and pkg-config are now used in the Makefile 44 | * glib is now an optional dependency. If not installed at compile time, custom implementations will be used 45 | * libpci is not an optional dependency at compile time too 46 | 47 | --- 48 | 49 | ##### © Aaron Blasko 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2022 Aaron Blasko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /MANUAL.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 1. [Configuration](#configuration) 3 | * [file location](#which-file-is-used) 4 | * [config syntax](#syntax) 5 | 2. [Command-line Arguments](#command-line-arguments) 6 | 3. [Return Codes](#return-codes-and-errors) 7 | * [0](#return-code-0) 8 | * [1](#error-1) 9 | * [-1](#error--1-255) 10 | 11 | # Configuration 12 | 13 | A complete configuration file with comments can be found as [`albafetch.conf`](albafetch.conf) in this repository. 14 | 15 | ## Which file is used? 16 | Albafetch supports a series of configuration options that can be placed inside of a configuration file located in `$XDG_CONFIG_HOME/albafetch.conf` (`~/.config/albafetch.conf` is parsed if `XDG_CONFIG_HOME` is not set). 17 | 18 | Alternatively, if this file is not found, albafetch will look for `XDG_CONFIG_HOME/albafetch/albafetch.conf` (or `~/.config/albafetch/albafetch.conf`). This can be useful if you want to keep multiple configurations in the same folder. 19 | 20 | A custom file might be specified using the `--config` argument. 21 | 22 | ## Syntax 23 | The config should contain `entry = "value",` pairs (using quotation marks is mandatory). During parsing, albafetch will look for the first string matching the entry (which you can find in [the default config](albafetch.conf)), then locate the next quotation mark, check whether there is another quotation mark following it and if so take the value between the two. 24 | 25 | There are three different types of data that will be parsed: 26 | * Strings: No more than N bytes between the quotation marks will be read, but every single one of those bytes will be used. 27 | These variables will be marked in the example config with a `; str [N]` following the option. 28 | * Integers: An integer smaller or equal to N will be parsed using the C standard `atoi()` function (use `man 3 atoi` for specific information). 29 | The provided value will always be treaded as unsigned (using something like -1 will therefore be treated as 2³²-1, 4294967295) 30 | These variables will be marked in the example config with a `; int [N]` following the option. 31 | * Booleans: The program will recognize anything different from "false" as "true". 32 | These variables will be marked in the example config with a `; bool` following the option. 33 | 34 | All of this means that `AB"CboldDEF"whatever lol"wo"w` will be parsed the exact same way as `bold = "true",` or `bold""`, but I would not recommend this as it makes everything less readable. I might also stop supporting this at any moment, so the specified syntax is the only one that's guaranteed to work. 35 | I might also make the parser stricter in the future and configs written this way might stop working entirely. 36 | 37 | Also, any `~` that you may want to use will not get expanded to `/home/username` and will instead be parsed as it is. If you want to reference your home directory inside of this config file (e.g. to specify the path to a custom ascii art) you will have to do so manually. 38 | 39 | A specific option that's worth spending some extra time talking about is `ascii_art`. This option expects the path to a file that contains a custom logo. Its syntax is really straight-forward: You can specify up to 40 lines you want to use as logo (which can not be longer than 256 bytes long, including the closing null character. Note that Unicode characters will be bigger than 1B), and eventually a color on the first line. Anything that's not recognized as a color ("colors" are defined as black, red, green, yellow, blue, purple, cyan, gray or white) Will be considered the first line of the logo. 40 | 41 | This is what a logo file could look like: 42 | ``` 43 | blue 44 | first_line 45 | second_line 46 | third_line 47 | fourth_line 48 | ``` 49 | 50 | This file should **not** end with an empty line, and every logo line should be equally long (you can achieve this simply by adding spaces at the end of the shorter lines), or albafetch will have alignment problems. There is no way to add comments in this file, everything will be used as it is written (except some escape sequences, more about this further down). 51 | 52 | You can find an example logo file in this repository, more specifically [example_logo.txt](example_logo.txt). 53 | 54 | The config can also contain an ordered array of the modules that you want albafetch to print. The array has a vastly different syntax in the config, as shown here: 55 | ``` 56 | modules = { 57 | "module1", 58 | "module2", 59 | "module3", 60 | } 61 | ``` 62 | You can found a list of the accepted modules inside of [the default config](albafetch.conf). Any other value will be considered as plain text (and will be printed as-is, with no label nor dash). 63 | 64 | To parse this section, albafetch first locates a string matching `modules` in the config file, takes the part between the following curly braces, and reads the text between the following pairs of quotation marks. 65 | As for normal options, this allows some weird formats, like `modules{"module1""module2""module3"}`, but I also invite anyone to consider the parsing of similar strings undefined behavior. 66 | 67 | Anything that doesn't match what the parser is looking for will be ignored, but the usage of explicit comments is encouraged: whatever stands between a `;` or an `#` and the end of the line will not be read as part of the config. You can, however, still freely use `#` and `;` in your config, as they will **not** be considered when enclosed in a string (between a pair of `"`). 68 | There is currently no way to use multi-line comments. 69 | 70 | When albafetch parses a file (config or custom ascii art), it will also automatically unescape some escape sequences, like the following table shows: 71 | | File content | How it will be parsed | 72 | | --- | --- | 73 | | "\\e" | '\\033' (ANSI escape) | 74 | | "\\033" | '\\033' (ANSI escape) | 75 | | "\\n" | '\\n' (new line) | 76 | | "\\X" | 'X' (everything else) | 77 | 78 | Since it might be useful, here are some of the most useful ANSI escape sequences (you can find a more complete list [here](https://stackoverflow.com/a/33206814)) 79 | | Function | Escape | 80 | | --- | --- | 81 | | Reset | `\e[0m` | 82 | | Bold | `\e[1m` | 83 | | Black | `\e[30m` | 84 | | Red | `\e[31m` | 85 | | Green | `\e[32m` | 86 | | Yellow | `\e[33m` | 87 | | Blue | `\e[34m` | 88 | | Purple | `\e[35m` | 89 | | Cyan | `\e[36m` | 90 | | White | `\e[97m` | 91 | 92 | Please note that these colors will be displayed as defined in the configuration of your terminal. 93 | You can check how a certain string will look using something like `echo -e "\e[1mHello, \e[31mWorld\e[0m"`. 94 | These escape sequences may be placed at any point inside of your config or custom ascii art and they *should* get parsed correctly (as long as you stay inside of the size limits defined for each field). You can even use multiple inside a single line. 95 | 96 | # Command-line arguments 97 | albafetch accepts a few command line arguments, which can be used to override certain values set in the config file. 98 | 99 | A short explanation of what every argument does, including every accepted and default value, can be obtained by running `albafetch --help` 100 | 101 | Here they are: 102 | * `--help` (or `-h`): Prints a small guide on the program usage and return RET_OK. 103 | * `--color` (or `-c`): Followed by the color you want to set, this option will override the config `default_color` entry and set a custom color for the whole output. 104 | * `--bold` (or `-b`): Followed by a boolean "on" or "off", this option overrides the config `bold` entry and enables or disables the usage of bold in the output. 105 | * `--logo` (or `-l`): This option, followed by the logo you want to print, overrides the config `logo` entry and makes albafetch print a custom logo instead of the default one. 106 | * `--align` (or `-a`): Followed by a boolean "on" or "off", this option overrides the config `align_infos` entry and enables of disables the alignment of the infos in the printed output. 107 | * `--ascii`: Followed by a valid file path, it lets you pick a file containing a custom ascii art that you'd like to use. When this option is used together with `--logo`, the latter has priority. 108 | * `--config`: Followed by a valid file path, this changes the config file that will be parsed to look for a valid configuration. 109 | * `--no-logo`: Using this will make albafetch not print a logo or ascii art (while still using it to get the color that should be printed). 110 | * `--no-config`: Using this will prevent any config file (provided using `--config` or the default one) from being used. 111 | 112 | # Return codes and errors 113 | | Return Code | Meaning | 114 | | --- | --- | 115 | | 0 | Correct execution | 116 | | 1 | Bad argument usage | 117 | | -1 | Bad environment | 118 | 119 | ## Return code 0 120 | As you probably already know, this is not an error, but it simply indicates that everything executed correctly. No need to worry. 121 | 122 | ## Error 1 123 | This is probably the result of a wrong usage of some command-line arguments. One or more error messages will be printed to stderr telling you exactly which argument(s) was used wrong. 124 | 125 | ## Error -1 (255) 126 | This error should only pop up if the program was not able to get the `HOME` environment variable, which should never happen. If you get this return code, you are probably experiencing worse than this dumb program not working. 127 | 128 | --- 129 | 130 | ###### © Aaron Blasko 131 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all compile clean run debug deb install uninstall 2 | 3 | OS := $(shell uname -o 2> /dev/null) 4 | KERNEL := $(shell uname -s 2> /dev/null) 5 | 6 | INSTALLPATH := /usr/local/bin 7 | CONFIGPATH := /etc/xdg 8 | 9 | INSTALLFLAGS := -Dm755 10 | CONFIGFLAGS := -Dm644 11 | 12 | ifeq ($(OS),Android) 13 | INSTALLPATH := $(PREFIX)/bin 14 | CONFIGPATH := $(PREFIX)/etc/xdg 15 | endif 16 | 17 | ifeq ($(KERNEL),Darwin) 18 | INSTALLFLAGS := -m755 19 | CONFIGFLAGS := -m644 20 | INSTALLPATH := /usr/local/bin 21 | CONFIGPATH := ~/.config/ 22 | endif 23 | 24 | all: compile 25 | 26 | build: 27 | meson setup build 28 | 29 | clean: 30 | rm -rf build 31 | 32 | compile: build 33 | meson compile -C build albafetch 34 | 35 | run: compile 36 | build/albafetch 37 | 38 | debug: build 39 | meson compile -C build debug 40 | build/debug --no-pip 41 | 42 | deb: compile 43 | cd debian; \ 44 | ./makedeb.sh 45 | 46 | install: compile 47 | mkdir -p $(INSTALLPATH) $(CONFIGPATH) 48 | 49 | install $(INSTALLFLAGS) build/albafetch $(INSTALLPATH)/albafetch 50 | install $(CONFIGFLAGS) albafetch.conf $(CONFIGPATH)/albafetch.conf 51 | 52 | uninstall: 53 | rm $(INSTALLPATH)/albafetch 54 | rm $(CONFIGPATH)/albafetch.conf 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # albafetch ~by alba4k 2 | 3 | #### Note: to prevent merge conflicts, please open your pull requests to the "development" branch, and check that one out before doing anything. Master is more stagnant and only updated when I know what I'm currently working on works as expected (most of the time). 4 | 5 | ![intro](images/albafetch.png) 6 | 7 | albafetch is a simple and fast program to display a lot of system information in a neofetch-like layout in way less than a second. I decided to make this as a challenge for myself and since I found neofetch too slow (which is understandable given that we're talking about a 10k+ lines shell script). 8 | 9 |
10 | 11 | Preview 12 | This is what albafetch will likely look like by default: 13 | 14 | ![default](images/albafetch_demo_default.png) 15 | 16 | And this is what [my configuration](https://github.com/alba4k/.dotfiles/blob/master/.config/albafetch/albafetch.conf) looks like 17 | ![custom](images/albafetch_demo.png) 18 | 19 |
20 | 21 | Here is a time comparison (exact execution times change between machines and runs): 22 |
23 | 24 | Time comparison 25 | 26 | ![neofetch](images/time_neofetch.png) 27 | ![albafetch](images/time_albafetch.png) 28 | 29 |
30 | 31 | You will find a lot of useful usage and configuration related info inside of the [user manual](MANUAL.md) and a small list of the things I changed since the last release in the [changelog](CHANGELOG.md). 32 | 33 | It currently supports a lot of GNU/Linux distributions, macOS (both x64 and arm64 macs) and even Android (only tested in Termux). 34 | Feel free to test any other platform :) 35 | 36 | ## Table of contents 37 | 1. [Dependencies](#dependencies) 38 | * [For building](#build-dependencies) 39 | * [At runtime](#runtime-dependencies) 40 | 2. [Compilation](#compilation) 41 | * [make](#using-the-makefile) 42 | * [meson](#using-meson) 43 | * [nix](#using-nix) 44 | 3. [Installation](#installation) 45 | * [Arch BTW](#for-arch-linux) 46 | * [NixOS](#for-nixos) 47 | * [MacPorts](#for-older-macos-versions) 48 | * [Manually](#manual-installation) 49 | 4. [Configuration](#configuration) 50 | > [example config](albafetch.conf) 51 | 5. [Contributing](#contributing) 52 | 53 | # Dependencies 54 | 55 | ## Build dependencies 56 | These will usually also install the relative [runtime dependencies](#runtime-dependencies) 57 | Dependencies marked with an asterisk are optional. This means that if not installed at compile-time, albafetch will be compiles with custom implementations of the functions used. 58 | 59 | * [libpci](https://github.com/pciutils/pciutils)\*: 60 | Used to get gpu information 61 | - On Arch Linux, [pciutils](https://archlinux.org/packages/core/x86_64/pciutils) 62 | - On Debian, [libpci-dev](https://packages.debian.org/bookworm/libpci-dev) 63 | - On Fedora, [pciutils-devel](https://packages.fedoraproject.org/pkgs/pciutils/pciutils-devel) 64 | - On Alpine Linux, [pciutils-dev](https://pkgs.alpinelinux.org/package/edge/main/x86_64/pciutils-dev) 65 | * [sqlite3](https://www.sqlite.org): 66 | Used to gather the amount of installed rpm packages 67 | - On Arch Linux, [sqlite](https://archlinux.org/packages/core/x86_64/sqlite) 68 | - On Debian, [libsqlite3-dev](https://packages.debian.org/bookworm/libsqlite3-dev) 69 | - On Fedora, [sqlite-devel](https://packages.fedoraproject.org/pkgs/sqlite/sqlite-devel) 70 | - On Alpine Linux, [sqlite-dev](https://pkgs.alpinelinux.org/package/edge/main/x86_64/sqlite-dev) 71 | * [libc](https://www.gnu.org/software/libc) (likely already installed): 72 | - On Alpine Linux, [musl-dev](https://pkgs.alpinelinux.org/package/edge/main/x86_64/musl-dev) 73 | * [glib](https://docs.gtk.org/glib/)\*: 74 | Provides some useful abstractions 75 | - On Arch Linux, [glib2]((https://archlinux.org/packages/core/x86_64/glib2)) 76 | - On Debian, [libglib2.0-dev](https://packages.debian.org/bookworm/libglib2.0) 77 | - On Fedora, [glib2-devel](https://packages.fedoraproject.org/pkgs/glib2/glib2-devel) 78 | - On Alpine Linux, [glib-dev](https://pkgs.alpinelinux.org/package/edge/main/x86/glib) 79 | * [git](https://git-scm.com)\*: 80 | When building from this repo, used to add the commit hash in the `--version` output 81 | * A build system: 82 | To build the project 83 | - A [Makefile](https://www.gnu.org/software/make) exists (which uses [meson](https://mesonbuild.com/Getting-meson.html), [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages) and [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config)/[pkgconf](http://pkgconf.org)), more details are found [here](#compilation). 84 | 85 | ## Runtime dependencies 86 | I would like to eventually remove those, by checking at runtime if they are installed and not use them if not so. 87 | Dependencies marked with an asterisk are optional. This means that if not installed at compile-time, albafetch will be compiles with custom implementations of the functions used. If the binary was compiled with the dependency installed, however, it will be needed at runtime too. 88 | 89 | * [libpci](https://github.com/pciutils/pciutils)\*: 90 | - On Arch Linux, [pciutils](https://archlinux.org/packages/core/x86_64/pciutils) 91 | - On Debian, [libpci3](https://packages.debian.org/buster/libpci3) 92 | - On Fedora, [pciutils-libs](https://packages.fedoraproject.org/pkgs/pciutils/pciutils-libs) 93 | - On Alpine Linux, [pciutils-dev](https://pkgs.alpinelinux.org/package/edge/main/x86_64/pciutils) 94 | * [sqlite3](https://www.sqlite.org): 95 | - On Arch Linux, [sqlite](https://archlinux.org/packages/core/x86_64/sqlite/) 96 | - On Debian, [sqlite3](https://packages.debian.org/bookworm/sqlite3) 97 | - On Fedora, [sqlite](https://packages.fedoraproject.org/pkgs/sqlite/sqlite) 98 | - On Alpine Linux, [sqlite](https://pkgs.alpinelinux.org/package/edge/main/x86_64/sqlite) 99 | * [glib](https://docs.gtk.org/glib/)\*: 100 | - On Arch Linux, [glib2]((https://archlinux.org/packages/core/x86_64/glib2)) 101 | - On Debian, [libglib2.0-dev](https://packages.debian.org/bookworm/libglib2.0) 102 | - On Fedora, [glib2-devel](https://packages.fedoraproject.org/pkgs/glib2/glib2) 103 | - On Alpine Linux, [glib-dev](https://pkgs.alpinelinux.org/package/edge/main/x86/glib) 104 | * there must be a `sh` binary in your PATH. This should already be satisfied on any UNIX-like system 105 | 106 | # Compilation 107 | 108 | ## Using the Makefile 109 | 110 | This will need you to have a C compiler installed and will still use meson/ninja under the hood. 111 | 112 | ```shell 113 | $ git clone https://github.com/alba4k/albafetch 114 | $ cd albafetch 115 | $ make 116 | ``` 117 | 118 | ## Using meson 119 | 120 | If you prefer to build with meson/ninja directly, you can use these commands: 121 | 122 | ```sh 123 | $ git clone https://github.com/alba4k/albafetch 124 | $ cd albafetch 125 | $ meson setup build 126 | $ meson compile -C build 127 | ``` 128 | 129 | In both cases, an executable file should appear as `build/albafetch` if the compilation succeeds. 130 | 131 | ### Debug builds 132 | It is possible to build a debug binary (`build/debug`) that will test every single function and make sure it runs correctly. This can be done by running 133 | 134 | ```sh 135 | $ make debug 136 | ``` 137 | 138 | ## Using meson 139 | 140 | If you prefer to build with meson/ninja directly, you can use these commands: 141 | 142 | ```sh 143 | $ meson setup build 144 | $ meson compile -C build 145 | $ build/debug 146 | ``` 147 | 148 | ## Using nix 149 | 150 | Building with nix can make compiling in some ways much easier, such as when compiling statically 151 | or cross compiling. A few convenience outputs are included: 152 | 153 | ```sh 154 | nix build .#albafetch # regular, dynamically linked build 155 | nix build .#albafetch-static # statically linked build (only available on linux) 156 | nix build .#albafetch-arm # cross compiling from x86_64 to arm (only available on x86_64) 157 | ``` 158 | 159 | # Installation 160 | 161 | ## For Arch Linux 162 | An AUR package is available, [albafetch-git](https://aur.archlinux.org/packages/albafetch-git). 163 | There are three packages on the AUR that provide albafetch: 164 | * [albafetch](https://aur.archlinux.org/packages/albafetch-) will compile the source code of the latest release 165 | * [albafetch-bin](https://aur.archlinux.org/packages/albafetch-bin) will install a pre-compiled binary from the latest release 166 | * [albafetch-git](https://aur.archlinux.org/packages/albafetch-git) will compile the source of the latest commit in master 167 | 168 | You can find more information on how to install packages from the AUR in the [Arch Wiki](https://wiki.archlinux.org/title/Arch_User_Repository#Installing_and_upgrading_packages) 169 | 170 | ## For Debian 171 | You can create a DEB package from the repo itself 172 | 173 | This can be done via a guided procedure 174 | 175 | ``` 176 | $ git clone https://github.com/alba4k/albafetch 177 | $ cd albafetch 178 | 179 | $ make deb 180 | ``` 181 | 182 | This will create a deb package for you and ask if you want to install it. 183 | 184 | ## For NixOS 185 | 186 | `nix profile`: 187 | 188 | ```sh 189 | $ nix profile install .#albafetch 190 | ``` 191 | 192 | `nix-env`: 193 | 194 | ```sh 195 | $ nix-env -iA packages..albafetch # platform examples: x86_64-linux, aarch64-linux, aarch64-darwin 196 | ``` 197 | 198 | Using the overlay (Flake): 199 | 200 | ```nix 201 | { 202 | inputs = { 203 | nixpkgs.url = "nixpkgs/nixos-unstable"; 204 | albafetch = { 205 | url = "github:alba4k/albafetch"; 206 | inputs.nixpkgs.follows = "nixpkgs"; 207 | }; 208 | }; 209 | 210 | outputs = {nixpkgs, albafetch, ...}: { 211 | nixosConfigurations.host = nixpkgs.lib.nixosSystem { 212 | system = "x86_64-linux"; 213 | modules = [ 214 | ./configuration.nix 215 | { 216 | nixpkgs.overlays = [albafetch.overlays.default]; 217 | environment.systemPackages = [pkgs.albafetch]; 218 | } 219 | ]; 220 | }; 221 | }; 222 | } 223 | ``` 224 | 225 | Using the overlay (`builtins.fetchTarball`): 226 | 227 | ```nix 228 | {pkgs, ...}: { 229 | nixpkgs.overlays = [(import (builtins.fetchTarball "https://github.com/alba4k/albafetch/master.tar.gz")).overlays.default]; 230 | environment.systemPackages = with pkgs; [ 231 | albafetch 232 | ]; 233 | } 234 | ``` 235 | 236 | ## For older macOS versions 237 | 238 | On older macOS (~11 and lower) versions, albafetch likely won't build natively. 239 | 240 | You can, however, install albafetch on those using the [package](https://ports.macports.org/port/albafetch) on MacPorts 241 | 242 | This can be easily done with the following 243 | ``` 244 | # port install albafetch 245 | ``` 246 | 247 | ## Manual installation 248 | 249 | What if your OS is not included in the ones mentioned? 250 | In this case, you can either compile the source code yourself and install albafetch manually, or you can grab an executable from the [latest release](https://github.com/alba4k/albafetch/releases/latest). 251 | 252 | Please note that albafetch currently won't run on Windows (despite `albafetch --logo windows` being an option), but I'm planning to eventually add support (sooner or later). Feel free to help :) 253 | 254 | ``` 255 | $ git clone https://github.com/alba4k/albafetch 256 | $ cd albafetch 257 | 258 | $ make 259 | # make install 260 | ``` 261 | 262 | `make install` needs elevated privileges on Linux (e.g. `sudo` or a root shell) to write to `/usr/bin`, while `/usr/local/bin` can be accessed as a normal user *on macOS*. 263 | 264 | Alternatively, you may prefer meson to perform the installation: 265 | 266 | ``` 267 | $ git clone https://github.com/alba4k/albafetch 268 | $ cd albafetch 269 | $ meson setup build 270 | $ meson compile -C build 271 | $ meson install -C build 272 | ``` 273 | 274 | Meson will install the executable to `/usr/local/bin`, which you may or may not want (executables in this directory are ran instead of ones in `/usr/bin`). 275 | 276 | # Configuration 277 | 278 | albafetch can be customized using a config file, usually `~/.config/albafetch.conf` for your user or `/etc/xdg/albafetch.conf`. 279 | 280 | You can find an example configuration file (which only provides the default values of every option) [here](albafetch.conf). 281 | Although this file includes some short comments on how the various options work, I highly recommend checking out the [user manual](MANUAL.md) for a deeper understanding of the way this config file works. 282 | 283 | # Contributing 284 | 285 | Almost everything included in this program is written in C. 286 | 287 | If you want to, you can directly modify the source code contained in this repository and recompile the program afterwards to get some features you might want or need. 288 | 289 | New logos can be added in [`src/logos.h`](src/logos.h) (be careful to follow the format), new infos in `src/info` and [`src/info/info.h`](src/info/info.h). Config options are mainly parsed in [`src/utils.c`](src/utils.c). You will also need to edit [`src/main.c`](src/main.c) afterwards to fully enable the new features. 290 | 291 | Don't mind opening a pull request if you think some of the changes you made should be in the public version. Please run `clang-tidy --fix src/**/*{.c,.h}` to check for any issues and `clang-format -i src/**/*{c,h}` to make sure the code style is consistent with the rest of the project before committing. 292 | 293 | Any contribution, even just a fix of a typo, is highly appreciated. 294 | 295 | --- 296 | 297 | ###### © Aaron Blasko 298 | 299 | ###### Initially started in March 2022 300 | -------------------------------------------------------------------------------- /albafetch.conf: -------------------------------------------------------------------------------- 1 | # Check https://github.com/alba4k/albafetch/blob/master/MANUAL.md for additional info. 2 | 3 | # My (the authors) personal configuration can be found here: 4 | # https://github.com/alba4k/.dotfiles/blob/master/.config/albafetch/albafetch.conf 5 | 6 | # CONFIGURATION OPTIONS: 7 | 8 | # align all the infos 9 | align_infos = "false" ; bool 10 | 11 | # should bold be used? 12 | bold = "true" ; bool 13 | 14 | # you can provide a file containing a logo to use 15 | # the normal logo is used when not set 16 | ; ascii_art = "/path/to/example_logo.txt" ; str[96] 17 | 18 | # which logo should always be printed? 19 | # OS default is used when not set 20 | ; logo = "linux" ; str [24] 21 | 22 | # what color should be used? 23 | # the color of the logo is used when not set 24 | ; default_color = "green" ; str [8] 25 | 26 | # the little separator used between each module label and content 27 | dash = ": " ; str [16] 28 | 29 | # lenght of the spacing between the logo and the modules 30 | spacing = "5" ; int [64] 31 | 32 | 33 | # LAYOUT 34 | 35 | # this contains an ordered array of the modules that should be printed. 36 | # every unrecognized value will be printed as-is 37 | modules = { 38 | # here is the default order: 39 | "title", # title in the format user@hostname 40 | "separator", # separator between two lines 41 | "uptime", # current uptime 42 | "separator", # -------------- 43 | "os", # operating system 44 | "kernel", # kernel version 45 | "desktop", # desktop environnment 46 | "shell", # shell (parent process, actually) 47 | "term", # terminal 48 | "packages", # number of installed packages 49 | "separator", # ---------------------------- 50 | "host", # OEM device / motherboard model name 51 | "cpu", # CPU 52 | "gpu", # GPU 53 | "memory", # used and total RAM 54 | "space", # empty line 55 | "colors", # terminal colors 56 | "light_colors", # terminal colors (light versions) 57 | # there are also some other modules, disabled by default 58 | ; "user", # username 59 | ; "hostname", # hostname 60 | ; "gtk_theme" # gtk_theme 61 | ; "icon_theme" # icon_theme 62 | ; "cursor_theme" # cursor_theme 63 | ; "login_shell", # login shell 64 | ; "bios", # BIOS version (Linux only) 65 | ; "public_ip", # public IP adress 66 | ; "local_ip", # local IP adress 67 | ; "pwd", # current working directory 68 | ; "date", # date and time 69 | ; "battery", # battery percentage and status (Linux only) 70 | ; "swap", # swap usage and percentage (Linux only) 71 | } 72 | 73 | # MODULE-SPECIFIC OPTIONS: 74 | 75 | # Separators 76 | # the prefix printed before a separator 77 | separator_prefix = "" ; str [64] 78 | # the character used in separators 79 | separator_character = "-" ; str [8] 80 | 81 | # Spacings 82 | # the prefix printed before a spacing 83 | spacing_prefix = "" ; str [64] 84 | 85 | # Title (user@host) 86 | # the prefix printed before the title 87 | title_prefix = "" ; str [64] 88 | # defines whether the title should be colored 89 | colored_title = "true" ; bool 90 | 91 | # User 92 | # the prefix printed before the user 93 | user_prefix = "User" ; str [64] 94 | 95 | # Hostname 96 | # the prefix printed before the hostnmae 97 | hostname_prefix = "Hostname" ; str [64] 98 | 99 | # Current Uptime 100 | # the prefix printed before the uptime 101 | uptime_prefix = "Uptime" ; str [64] 102 | 103 | # Operating System 104 | # the prefix printed before the OS 105 | os_prefix = "OS" ; str [64] 106 | # whether the architecture should be printed 107 | os_arch = "true" ; bool 108 | 109 | # Kernel Version 110 | # the prefix printed before the kernel 111 | kernel_prefix = "Kernel" ; str [64] 112 | # whether the kernel version should be printed in a shorter form 113 | kernel_short = "false" ; bool 114 | # prints the kernel type (useful when using kernel_short) 115 | kernel_type = "false" 116 | 117 | # Desktop Environnment 118 | # the prefix printed before the desktop 119 | desktop_prefix = "Desktop" ; str [64] 120 | # whether the desktop type should be printed (X11 / Wayland) 121 | desktop_type = "true" ; bool 122 | 123 | # GTK Theme 124 | # the prefix printed before the theme name 125 | gtk_theme_prefix = "Theme" ; str [64] 126 | 127 | # Icon Theme 128 | # the prefix printed before the theme name 129 | icon_theme_prefix = "Icons" ; str [64] 130 | 131 | # Cursor Theme 132 | # the prefix printed before the theme name 133 | cursor_theme_prefix = "Cursor" ; str [64] 134 | 135 | # Shells 136 | # the prefix printed before the shell 137 | shell_prefix = "Shell" ; str [64] 138 | # the prefix printed before the login shell 139 | login_shell_prefix = "Login" ; str [64] 140 | # whether the full shell path should be printed 141 | shell_path = "false" ; bool 142 | 143 | # Terminal 144 | # the prefix printed before the terminal 145 | term_prefix = "Terminal" ; str [64] 146 | # whether the current session is running via ssh 147 | term_ssh = "true" ; bool 148 | 149 | # Installed Packages 150 | # the prefix printed before the numebr of packages 151 | pkg_prefix = "Packages" ; str [64] 152 | # whether the source of the individual packages should be printed 153 | pkg_mgr = "true" ; bool 154 | # whether the amount of packages installed 155 | # from a specific source should be printed 156 | pkg_pacman = "true" ; bool 157 | pkg_dpkg = "true" ; bool 158 | pkg_flatpak = "true" ; bool 159 | pkg_snap = "true" ; bool 160 | pkg_brew = "true" ; bool 161 | pkg_pip = "false" ; bool 162 | 163 | # Host System 164 | # the prefix printed before the host 165 | host_prefix = "Host" ; str [64] 166 | 167 | # BIOS Version 168 | # the prefix printed before the BIOS 169 | bios_prefix = "BIOS" ; str [64] 170 | 171 | # Processor 172 | # the prefix printed before the cpu 173 | cpu_prefix = "CPU" ; str [64] 174 | # whether the manufacturer should be printed 175 | cpu_brand = "true" ; bool 176 | # whether the frequency should be printed 177 | cpu_freq = "true" ; bool 178 | # whether the amount of threads should be printed 179 | cpu_count = "true" ; bool 180 | 181 | # Graphics card 182 | # the prefix printed before the gpu 183 | gpu_prefix = "GPU" ; str [64] 184 | # whether the manufacturer should be printed 185 | gpu_brand = "true" ; bool 186 | # the specific GPU that should be printed (0 for all, otherwise the specific number 1-3) 187 | gpu_index = "0" ; int [3] 188 | 189 | # Memory 190 | # the prefix printed before the ram 191 | mem_prefix = "Memory" ; str [64] 192 | # whether the percentage of used memory should be printed 193 | mem_perc = "true" ; bool 194 | 195 | # Swap 196 | # the prefix printed before the swap 197 | swap_prefix = "Swap" ; str [64] 198 | # whether the percentage of used swap should be printed 199 | swap_perc = "true" ; bool 200 | 201 | # IPs 202 | # the prefix printed before the public IP 203 | pub_prefix = "Public IP" ; str [64] 204 | # the prefix printed before the local IPs 205 | loc_prefix = "Local IP" ; str [64] 206 | # whether the localhost should be shown as local IP 207 | loc_localhost = "false" ; bool 208 | # whether docker should be shown as local IP 209 | loc_docker = "false" ; bool 210 | 211 | # Current working directory 212 | # the prefix printed before the path 213 | pwd_prefix = "Directory" ; str [64] 214 | # whether the full path should be printed 215 | pwd_path = "true" ; bool 216 | 217 | # Current date 218 | # the prefix printed before the date 219 | date_prefix = "Date" ; str [64] 220 | # the format the date is printed in (don't change the %02d parts) 221 | date_format = "%02d/%02d/%d %02d:%02d:%02d" ; str [32] 222 | 223 | # Battery 224 | # the prefix printed before the battery info 225 | bat_prefix = "Battery" ; str [64] 226 | # whether the battery status should be printed 227 | bat_status = "true" ; bool 228 | 229 | 230 | # Terminal Colors 231 | # the prefix printed before the colors 232 | colors_prefix = "" ; str [64] 233 | # the prefix printed before the light colors 234 | colors_light_prefix = "" ; str [64] 235 | # the string that will be colored 236 | col_block_str = " " ; str [24] 237 | # should the foreground get colored, instead of the background? 238 | col_background = "true" ; bool 239 | -------------------------------------------------------------------------------- /debian/makedeb.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | version=$(../build/albafetch --version | cut -d " " -f 1) 4 | arch=$(uname -m) 5 | 6 | if [ "$arch" = "x86_64" ]; then 7 | arch="amd64" 8 | elif [ "$arch" = "aarch64" ]; then 9 | arch="arm64" 10 | else 11 | arch="any" 12 | fi 13 | 14 | echo -e "\n\e[1m[\e[33mINFO\e[0m\e[1m]\e[0m Detected version $version" 15 | echo -e "\e[1m[\e[33mINFO\e[0m\e[1m]\e[0m Detected architecture $arch\n" 16 | 17 | dirname="albafetch_"$version"_"$arch 18 | 19 | mkdir -p "$dirname/DEBIAN" 20 | 21 | install -Dm 755 "../build/albafetch" "$dirname/usr/bin/albafetch" 22 | 23 | install -Dm 644 "../albafetch.conf" "$dirname/etc/xdg/albafetch.conf" 24 | install -Dm 644 "../LICENSE" "$dirname/usr/share/licences/albafetch/LICENSE" 25 | install -Dm 644 "../README.md" "$dirname/usr/share/doc/albafetch/README.md" 26 | install -Dm 644 "../MANUAL.md" "$dirname/usr/share/doc/albafetch/MANUAL.md" 27 | 28 | echo "Package: albafetch 29 | Version: $version 30 | Architecture: $arch 31 | Essential: no 32 | Priority: optional 33 | Depends: libpci3 34 | Maintainer: Aaron Blasko 35 | Description: Faster neofetch alternative, written in C." > "$dirname"/DEBIAN/control 36 | 37 | dpkg-deb --build "$dirname" 38 | 39 | echo -e -n "\n\e[1m\e[32mInstall Now\e[0m\e[1m [Y/n] ?\e[0m > " 40 | read -r -n 1 install 41 | 42 | if [ "$install" = "N" ] || [ "$install" = "n" ]; then 43 | echo -e "\n\e[1m[\e[32mDONE\e[0m\e[1m]\e[0m File created as $PWD/$dirname.deb" 44 | else 45 | sudo dpkg -i "$dirname.deb" 46 | fi -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import { 3 | inherit system; 4 | config = { }; 5 | overlays = [ ]; 6 | }, 7 | system ? builtins.currentSystem, 8 | }: 9 | 10 | { 11 | albafetch = pkgs.callPackage ./nix/package.nix { }; 12 | } 13 | -------------------------------------------------------------------------------- /example_logo.txt: -------------------------------------------------------------------------------- 1 | blue 2 | ███ █ ███ 3 | █ █ █ █ █ 4 | ███ █ ███ 5 | █ █ █ █ █ 6 | █ █ ███ ███ 7 | 8 | ███ ███ ███ 9 | █ █ █ █ 10 | ███ ██ ██ 11 | █ █ █ █ 12 | █ █ █ ███ 13 | 14 | ███ ███ █ █ 15 | █ █ █ █ 16 | █ █ ███ 17 | █ █ █ █ 18 | █ ███ █ █ 19 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1748460289, 24 | "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "id": "nixpkgs", 32 | "ref": "nixos-unstable", 33 | "type": "indirect" 34 | } 35 | }, 36 | "root": { 37 | "inputs": { 38 | "flake-utils": "flake-utils", 39 | "nixpkgs": "nixpkgs" 40 | } 41 | }, 42 | "systems": { 43 | "locked": { 44 | "lastModified": 1681028828, 45 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 46 | "owner": "nix-systems", 47 | "repo": "default", 48 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 49 | "type": "github" 50 | }, 51 | "original": { 52 | "owner": "nix-systems", 53 | "repo": "default", 54 | "type": "github" 55 | } 56 | } 57 | }, 58 | "root": "root", 59 | "version": 7 60 | } 61 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Faster neofetch alternative, written in C"; 3 | 4 | nixConfig = { 5 | extra-substituters = [ "https://albafetch.cachix.org" ]; 6 | extra-trusted-public-keys = [ 7 | "albafetch.cachix.org-1:wTFl2hoUXiYlzUp/XA/bfZTP5KqZTToyq0+sfIVseUU=" 8 | ]; 9 | }; 10 | 11 | inputs = { 12 | nixpkgs.url = "nixpkgs/nixos-unstable"; 13 | flake-utils.url = "github:numtide/flake-utils"; 14 | }; 15 | 16 | outputs = 17 | { 18 | self, 19 | nixpkgs, 20 | flake-utils, 21 | }: 22 | 23 | flake-utils.lib.eachDefaultSystem ( 24 | system: 25 | 26 | let 27 | inherit (nixpkgs) lib; 28 | pkgs = nixpkgs.legacyPackages.${system}; 29 | 30 | callWith = pkgs: (lib.makeScope pkgs.newScope (lib.flip self.overlays.default pkgs)).albafetch; 31 | 32 | armPkgs = pkgs.pkgsCross.aarch64-multiplatform; 33 | albafetch-static = callWith pkgs.pkgsStatic; 34 | in 35 | 36 | { 37 | devShells.default = pkgs.mkShell { 38 | inputsFrom = [ self.packages.${system}.albafetch ]; 39 | }; 40 | 41 | formatter = pkgs.alejandra; 42 | 43 | packages = { 44 | albafetch = callWith pkgs; 45 | default = self.packages.${system}.albafetch; 46 | }; 47 | 48 | legacyPackages = 49 | { 50 | x86_64-linux = { 51 | albafetch-arm = callWith armPkgs.pkgsStatic; 52 | inherit albafetch-static; 53 | }; 54 | 55 | aarch64-linux = { inherit albafetch-static; }; 56 | 57 | x86_64-darwin = { 58 | albafetch-arm = callWith armPkgs; 59 | }; 60 | } 61 | .${system} or { }; 62 | } 63 | ) 64 | // { 65 | overlays.default = final: _: { 66 | albafetch = final.callPackage ./nix/package.nix { inherit self; }; 67 | }; 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /images/albafetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alba4k/albafetch/575fcee1ae5cb8d30f06e0af9275fcdc8d1e5ba6/images/albafetch.png -------------------------------------------------------------------------------- /images/albafetch_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alba4k/albafetch/575fcee1ae5cb8d30f06e0af9275fcdc8d1e5ba6/images/albafetch_demo.png -------------------------------------------------------------------------------- /images/albafetch_demo_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alba4k/albafetch/575fcee1ae5cb8d30f06e0af9275fcdc8d1e5ba6/images/albafetch_demo_default.png -------------------------------------------------------------------------------- /images/time_albafetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alba4k/albafetch/575fcee1ae5cb8d30f06e0af9275fcdc8d1e5ba6/images/time_albafetch.png -------------------------------------------------------------------------------- /images/time_neofetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alba4k/albafetch/575fcee1ae5cb8d30f06e0af9275fcdc8d1e5ba6/images/time_neofetch.png -------------------------------------------------------------------------------- /images/v2.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alba4k/albafetch/575fcee1ae5cb8d30f06e0af9275fcdc8d1e5ba6/images/v2.0.png -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'albafetch', 3 | 'c', 4 | version : '4.2.1', 5 | default_options : ['warning_level=3'], 6 | license : 'MIT', 7 | meson_version: '>=1.3.0' 8 | ) 9 | 10 | src = [ 11 | 'src/config/config.c', 12 | 'src/config/parsing.c', 13 | 'src/info/battery.c', 14 | 'src/info/bios.c', 15 | 'src/info/colors.c', 16 | 'src/info/cpu.c', 17 | 'src/info/cursor_theme.c', 18 | 'src/info/date.c', 19 | 'src/info/desktop.c', 20 | 'src/info/gpu.c', 21 | 'src/info/gtk_theme.c', 22 | 'src/info/host.c', 23 | 'src/info/hostname.c', 24 | 'src/info/icon_theme.c', 25 | 'src/info/info.h', 26 | 'src/info/kernel.c', 27 | 'src/info/light_colors.c', 28 | 'src/info/local_ip.c', 29 | 'src/info/login_shell.c', 30 | 'src/info/memory.c', 31 | 'src/info/os.c', 32 | 'src/info/packages.c', 33 | 'src/info/public_ip.c', 34 | 'src/info/pwd.c', 35 | 'src/info/shell.c', 36 | 'src/info/swap.c', 37 | 'src/info/term.c', 38 | 'src/info/uptime.c', 39 | 'src/info/user.c', 40 | 'src/optdeps/glib.c', 41 | 'src/optdeps/libpci.c', 42 | 'src/utils/queue.c', 43 | 'src/utils/utils.c', 44 | 'src/utils/wrappers.c', 45 | ] 46 | 47 | build_args = [ 48 | '-Wall', 49 | '-Wextra', 50 | '-O3', 51 | '-ffast-math', 52 | '-std=gnu99', 53 | ] 54 | 55 | # DEPENDENCY HANDLING 56 | 57 | project_dependencies = [] 58 | 59 | glib = dependency('glib-2.0', required: false) 60 | if glib.found() 61 | build_args += '-DGLIB_EXISTS' 62 | project_dependencies += glib 63 | endif 64 | 65 | if host_machine.system() == 'linux' 66 | project_dependencies += dependency('sqlite3', required: true) 67 | 68 | libpci = dependency('libpci', required: false) 69 | if libpci.found() 70 | build_args += '-DLIBPCI_EXISTS' 71 | project_dependencies += libpci 72 | endif 73 | elif host_machine.system() == 'darwin' 74 | project_dependencies += dependency('appleframeworks', modules : ['Foundation', 'IOKit'], required: true) 75 | add_languages('objc') 76 | src += [ 77 | 'src/macos/macos_infos.c', 78 | 'src/macos/bsdwrap.c', 79 | 'src/macos/macos_gpu_string.m', 80 | ] 81 | endif 82 | 83 | # MAKE THE VERSION PRINTED BY albafetch -h DYNAMIC 84 | git = find_program('git', required : false) 85 | commit = '{unknown}' 86 | if git.found() 87 | commit_cmd = run_command(git, 'rev-parse', '--short', 'HEAD', check: false) 88 | if commit_cmd.returncode() == 0 89 | commit = commit_cmd.stdout().strip() 90 | endif 91 | endif 92 | 93 | config = configuration_data() 94 | config.set('VERSION', '"'+meson.project_version()+'"') 95 | config.set('COMMIT', '"'+commit+'"') 96 | configure_file(configuration: config, output: 'version.h') 97 | build_args += '-DHAVE_VERSION_H' 98 | 99 | # BUILD 100 | 101 | src_debug = src + ['src/debug.c'] 102 | src += 'src/main.c' 103 | 104 | debug_args = build_args + ['-g'] 105 | 106 | executable( 107 | meson.project_name(), 108 | src, 109 | dependencies : project_dependencies, 110 | install : true, 111 | c_args : build_args, 112 | include_directories: include_directories('.') 113 | ) 114 | 115 | executable( 116 | 'debug', 117 | src_debug, 118 | dependencies : project_dependencies, 119 | install : false, 120 | c_args : debug_args, 121 | include_directories: include_directories('.') 122 | ) 123 | -------------------------------------------------------------------------------- /nix/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | apple-sdk_12, 5 | meson, 6 | ninja, 7 | pciutils, 8 | pkg-config, 9 | sqlite, 10 | 11 | self ? { }, 12 | }: 13 | 14 | let 15 | latestTag = "4.2.1"; 16 | 17 | fullDate = lib.substring 0 8 self.lastModifiedDate; 18 | 19 | formattedDate = 20 | if (self ? "lastModifiedDate") then 21 | lib.concatStringsSep "-" [ 22 | # YYYY 23 | (lib.substring 0 4 fullDate) 24 | ## MM 25 | (lib.substring 4 2 fullDate) 26 | # DD 27 | (lib.substring 6 2 fullDate) 28 | ] 29 | else 30 | "unknown"; 31 | in 32 | 33 | stdenv.mkDerivation { 34 | name = "albafetch"; 35 | version = "${latestTag}-unstable-${formattedDate}"; 36 | 37 | src = self; 38 | 39 | buildInputs = 40 | [ 41 | sqlite 42 | ] 43 | ++ lib.optional stdenv.hostPlatform.isDarwin apple-sdk_12 44 | ++ lib.optional stdenv.hostPlatform.isLinux pciutils; 45 | 46 | nativeBuildInputs = [ 47 | meson 48 | ninja 49 | pkg-config 50 | ]; 51 | 52 | OBJC = lib.optionalString stdenv.hostPlatform.isDarwin "clang"; 53 | 54 | postFixup = lib.optionalString stdenv.hostPlatform.isStatic '' 55 | rm -r $out/nix-support 56 | ''; 57 | 58 | meta = { 59 | description = "Faster neofetch alternative, written in C."; 60 | homepage = "https://github.com/alba4k/albafetch"; 61 | license = lib.licenses.mit; 62 | maintainers = with lib.maintainers; [ getchoo ]; 63 | platforms = lib.platforms.unix; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/config/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "parsing.h" 3 | #include "../utils/logos.h" 4 | #include "../utils/return.h" 5 | #include "../utils/utils.h" 6 | 7 | #define GNU_SOURCE 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // a return code of 0 means that the option was parsed successfully 16 | int parseConfigStr(const char *source, const char *field, char *dest, const size_t maxlen) { 17 | char *ptr, *end; 18 | 19 | // looks for the keyword 20 | ptr = strstr(source, field); 21 | if(ptr == NULL) 22 | return ERR_PARSING; 23 | 24 | // looks for the opening " 25 | ptr = strchr(ptr, '"'); 26 | if(ptr == NULL) 27 | return ERR_PARSING; 28 | 29 | // checks whether the string continues after 30 | ++ptr; 31 | if(*ptr == 0) 32 | return ERR_PARSING; 33 | 34 | // looks for the closing " 35 | end = strchr(ptr, '"'); 36 | if(end == NULL) 37 | return ERR_PARSING; 38 | 39 | // copies the option 40 | *end = 0; 41 | 42 | size_t len = strlen(ptr); 43 | if(len + 1 > maxlen) { 44 | memcpy(dest, ptr, maxlen); 45 | dest[maxlen - 1] = 0; 46 | } else 47 | memcpy(dest, ptr, len + 1); 48 | 49 | *end = '"'; 50 | 51 | return RET_OK; 52 | } 53 | 54 | // a return code of 0 means that the option was parsed successfully 55 | int parseConfigInt(const char *source, const char *field, int *dest, const unsigned max) { 56 | char *ptr, *end; 57 | 58 | // looks for the keyword 59 | ptr = strstr(source, field); 60 | if(ptr == NULL) 61 | return ERR_PARSING; 62 | 63 | // looks for the opening " 64 | ptr = strchr(ptr, '"'); 65 | if(ptr == NULL) 66 | return ERR_PARSING; 67 | 68 | // checks whether the string continues after 69 | ++ptr; 70 | if(*ptr == 0) 71 | return ERR_PARSING; 72 | 73 | // looks for the closing " 74 | end = strchr(ptr, '"'); 75 | if(end == NULL) 76 | return ERR_PARSING; 77 | 78 | // copies the option 79 | *end = 0; 80 | int num = atoi(ptr); 81 | *end = '"'; 82 | 83 | if((unsigned)num > max) 84 | return ERR_PARSING; 85 | 86 | *dest = num; 87 | 88 | return RET_OK; 89 | } 90 | 91 | // a return code of 0 means that the option was parsed successfully 92 | int parseConfigBool(const char *source, const char *field, bool *dest) { 93 | char *ptr, *end; 94 | 95 | // looks for the keyword 96 | ptr = strstr(source, field); 97 | if(ptr == NULL) 98 | return ERR_PARSING; 99 | 100 | // looks for the opening " 101 | ptr = strchr(ptr, '"'); 102 | if(ptr == NULL) 103 | return ERR_PARSING; 104 | 105 | // checks whether the string continues after 106 | ++ptr; 107 | if(*ptr == 0) 108 | return ERR_PARSING; 109 | 110 | // looks for the closing " 111 | end = strchr(ptr, '"'); 112 | if(end == NULL) 113 | return ERR_PARSING; 114 | 115 | // copies the option 116 | *end = 0; 117 | *dest = strcmp(ptr, "false"); 118 | *end = '"'; 119 | 120 | return RET_OK; 121 | } 122 | 123 | // parse the provided config file 124 | void parseConfig(bool error, const char *file, struct SModule *modules, void **ascii_ptr, bool *default_bold, char *default_color, char *default_logo) { 125 | FILE *fp = fopen(file, "r"); 126 | 127 | if(fp == NULL) { 128 | if(error) 129 | perror(file); 130 | return; 131 | } 132 | 133 | fseek(fp, 0, SEEK_END); 134 | size_t len = (size_t)ftell(fp); 135 | fseek(fp, 0, SEEK_SET); 136 | 137 | char *conf = malloc(len); 138 | if(conf == NULL) { 139 | perror("malloc"); 140 | return; 141 | } 142 | conf[fread(conf, 1, len, fp) - 1] = 0; 143 | fclose(fp); 144 | 145 | // used later 146 | char *ptr, *ptr2; 147 | 148 | // remove comments 149 | uncomment(conf, '#'); 150 | uncomment(conf, ';'); 151 | 152 | // handle escape sequences 153 | unescape(conf); 154 | 155 | // GENERAL OPTIONS 156 | 157 | // ascii art 158 | char path[96] = ""; 159 | parseConfigStr(conf, "ascii_art", path, sizeof(path)); 160 | if(path[0]) 161 | *ascii_ptr = fileToLogo(path); 162 | 163 | // logo 164 | char logo[32] = ""; 165 | parseConfigStr(conf, "logo", logo, sizeof(logo)); 166 | if(logo[0]) { 167 | for(size_t i = 0; i < sizeof(logos) / sizeof(logos[0]); ++i) 168 | if(strcmp(logos[i][0], logo) == 0) { 169 | config.logo = logos[i]; 170 | strcpy(default_logo, logos[i][0]); 171 | strcpy(config.color, logos[i][1]); 172 | } 173 | } 174 | 175 | // color 176 | char color[16] = ""; 177 | parseConfigStr(conf, "default_color", color, sizeof(color)); 178 | if(color[0]) { 179 | const char *colors[][2] = { 180 | {"black", "\033[30m"}, {"red", "\033[31m"}, {"green", "\033[32m"}, {"yellow", "\033[33m"}, {"blue", "\033[34m"}, 181 | {"purple", "\033[35m"}, {"cyan", "\033[36m"}, {"gray", "\033[90m"}, {"white", "\033[37m"}, 182 | }; 183 | 184 | for(int i = 0; i < 9; ++i) 185 | if(strcmp(color, *colors[i]) == 0) { 186 | strcpy(config.color, colors[i][1]); 187 | strcpy(default_color, colors[i][1]); 188 | } 189 | } 190 | 191 | // dash 192 | parseConfigStr(conf, "dash", config.dash, sizeof(config.dash)); 193 | 194 | // spacing 195 | parseConfigInt(conf, "spacing", &config.spacing, 64); 196 | 197 | // separator 198 | parseConfigStr(conf, "separator_character", config.separator, sizeof(config.separator)); 199 | 200 | // BOOLEAN OPTIONS (check utils/utils.h) 201 | 202 | const char *booleanOptions[] = {"align_infos", "bold", "colored_title", "os_arch", "kernel_short", "desktop_type", "shell_path", 203 | "term_ssh", "pkg_mgr", "pkg_pacman", "pkg_dpkg", "pkg_rpm", "pkg_flatpak", "pkg_snap", 204 | "pkg_brew", "pkg_pip", "cpu_brand", "cpu_freq", "cpu_count", "gpu_brand", "mem_perc", 205 | "loc_localhost", "loc_docker", "pwd_path", "kernel_type", "col_background", "bat_status", "swap_perc"}; 206 | 207 | bool buffer; 208 | for(size_t i = 0; i < sizeof(booleanOptions) / sizeof(booleanOptions[0]); ++i) { 209 | if(parseConfigBool(conf, booleanOptions[i], &buffer) == 0) { 210 | if(buffer) 211 | config.boolean_options |= ((uint64_t)1 << i); 212 | else 213 | config.boolean_options &= ~((uint64_t)1 << i); 214 | } 215 | } 216 | *default_bold = _bold; 217 | 218 | // OTHER MODULE-RELATED OPTIONS 219 | 220 | parseConfigInt(conf, "gpu_index", &config.gpu_index, 3); 221 | 222 | parseConfigStr(conf, "date_format", config.date_format, sizeof(config.date_format)); 223 | 224 | parseConfigStr(conf, "col_block_str", config.col_block_str, sizeof(config.col_block_str)); 225 | 226 | // LABELS 227 | 228 | struct SPrefix { 229 | char *option; 230 | const char *config_name; 231 | }; 232 | 233 | struct SPrefix prefixes[] = { 234 | // {config.module_prefix, "module_prefix"} 235 | {config.separator_prefix, "separator_prefix"}, 236 | {config.spacing_prefix, "spacing_prefix"}, 237 | {config.title_prefix, "title_prefix"}, 238 | {config.user_prefix, "user_prefix"}, 239 | {config.hostname_prefix, "hostname_prefix"}, 240 | {config.uptime_prefix, "uptime_prefix"}, 241 | {config.os_prefix, "os_prefix"}, 242 | {config.kernel_prefix, "kernel_prefix"}, 243 | {config.desktop_prefix, "desktop_prefix"}, 244 | {config.gtk_theme_prefix, "gtk_theme_prefix"}, 245 | {config.icon_theme_prefix, "icon_theme_prefix"}, 246 | {config.cursor_theme_prefix, "cursor_theme_prefix"}, 247 | {config.shell_prefix, "shell_prefix"}, 248 | {config.login_shell_prefix, "login_shell_prefix"}, 249 | {config.term_prefix, "term_prefix"}, 250 | {config.pkg_prefix, "pkg_prefix"}, 251 | {config.host_prefix, "host_prefix"}, 252 | {config.bios_prefix, "bios_prefix"}, 253 | {config.cpu_prefix, "cpu_prefix"}, 254 | {config.gpu_prefix, "gpu_prefix"}, 255 | {config.mem_prefix, "mem_prefix"}, 256 | {config.swap_prefix, "swap_prefix"}, 257 | {config.pub_prefix, "pub_prefix"}, 258 | {config.loc_prefix, "loc_prefix"}, 259 | {config.pwd_prefix, "pwd_prefix"}, 260 | {config.date_prefix, "date_prefix"}, 261 | {config.bat_prefix, "bat_prefix"}, 262 | {config.colors_prefix, "colors_prefix"}, 263 | {config.light_colors_prefix, "colors_light_prefix"}, 264 | }; 265 | 266 | for(size_t i = 0; i < sizeof(prefixes) / sizeof(prefixes[0]); ++i) 267 | parseConfigStr(conf, prefixes[i].config_name, prefixes[i].option, 64); 268 | 269 | // MODULES 270 | 271 | ptr = strstr(conf, "modules"); 272 | if(ptr == NULL) { 273 | free(conf); 274 | return; 275 | } 276 | 277 | ptr2 = strchr(ptr, '{'); 278 | if(ptr2 == NULL) { 279 | free(conf); 280 | return; 281 | } 282 | 283 | char *end = strchr(ptr2, '}'); 284 | if(end == NULL) { 285 | free(conf); 286 | return; 287 | } 288 | *end = 0; 289 | 290 | while((ptr = strchr(ptr2, '"'))) { 291 | ++ptr; 292 | 293 | ptr2 = strchr(ptr, '"'); 294 | if(ptr2 == NULL) { 295 | free(conf); 296 | return; 297 | } 298 | *ptr2 = 0; 299 | 300 | addModule(modules, ptr); 301 | 302 | // *ptr2 = '"'; 303 | ++ptr2; 304 | } 305 | 306 | // *end = '}'; 307 | 308 | free(conf); 309 | } 310 | -------------------------------------------------------------------------------- /src/config/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utils/utils.h" 4 | 5 | #include 6 | #include 7 | 8 | struct SConfig { 9 | /* Starting from the least significant byte, see the #define statements later 10 | * 0. _align 11 | * 1. _bold 12 | * 2. _title_color 13 | * 3. _os_arch 14 | * 4. _kernel_short 15 | * 5. _de_type 16 | * 6. _shell_path 17 | * 7. _term_ssh 18 | * 8. _pkg_mgr 19 | * 9. _pkg_pacman 20 | * 10. _pkg_dpkg 21 | * 11. _pkg_rpm 22 | * 12. _pkg_flatpak 23 | * 13. _pkg_snap 24 | * 14. _pkg_brew 25 | * 15. _pkg_pip 26 | * 16. _cpu_brand 27 | * 17. _cpu_freq 28 | * 18. _cpu_count 29 | * 19. _gpu_brand 30 | * 20. _mem_perc 31 | * 21. _loc_localhost 32 | * 22. _loc_docker 33 | * 23. _pwd_path 34 | * 24. _col_background 35 | * 25. _bat_status 36 | * 26. _swap_perc 37 | * [...] 38 | */ 39 | uint64_t boolean_options; 40 | 41 | char **logo; 42 | char color[8]; 43 | char dash[16]; 44 | char separator[8]; 45 | int spacing; 46 | 47 | int gpu_index; 48 | char date_format[32]; 49 | char col_block_str[24]; 50 | 51 | char separator_prefix[64]; 52 | char spacing_prefix[64]; 53 | char title_prefix[64]; 54 | char user_prefix[64]; 55 | char hostname_prefix[64]; 56 | char uptime_prefix[64]; 57 | char os_prefix[64]; 58 | char kernel_prefix[64]; 59 | char desktop_prefix[64]; 60 | char gtk_theme_prefix[64]; 61 | char icon_theme_prefix[64]; 62 | char cursor_theme_prefix[64]; 63 | char shell_prefix[64]; 64 | char login_shell_prefix[64]; 65 | char term_prefix[64]; 66 | char pkg_prefix[64]; 67 | char host_prefix[64]; 68 | char bios_prefix[64]; 69 | char cpu_prefix[64]; 70 | char gpu_prefix[64]; 71 | char mem_prefix[64]; 72 | char swap_prefix[64]; 73 | char pub_prefix[64]; 74 | char loc_prefix[64]; 75 | char pwd_prefix[64]; 76 | char date_prefix[64]; 77 | char bat_prefix[64]; 78 | char colors_prefix[64]; 79 | char light_colors_prefix[64]; 80 | }; 81 | extern struct SConfig config; 82 | 83 | #define _align config.boolean_options & 0x1 84 | #define _bold config.boolean_options & 0x2 85 | #define _title_color config.boolean_options & 0x4 86 | #define _os_arch config.boolean_options & 0x8 87 | #define _kernel_short config.boolean_options & 0x10 88 | #define _de_type config.boolean_options & 0x20 89 | #define _shell_path config.boolean_options & 0x40 90 | #define _term_ssh config.boolean_options & 0x80 91 | #define _pkg_mgr config.boolean_options & 0x100 92 | #define _pkg_pacman config.boolean_options & 0x200 93 | #define _pkg_dpkg config.boolean_options & 0x400 94 | #define _pkg_rpm config.boolean_options & 0x800 95 | #define _pkg_flatpak config.boolean_options & 0x1000 96 | #define _pkg_snap config.boolean_options & 0x2000 97 | #define _pkg_brew config.boolean_options & 0x4000 98 | #define _pkg_pip config.boolean_options & 0x8000 99 | #define _cpu_brand config.boolean_options & 0x10000 100 | #define _cpu_freq config.boolean_options & 0x20000 101 | #define _cpu_count config.boolean_options & 0x40000 102 | #define _gpu_brand config.boolean_options & 0x80000 103 | #define _mem_perc config.boolean_options & 0x100000 104 | #define _loc_localhost config.boolean_options & 0x200000 105 | #define _loc_docker config.boolean_options & 0x400000 106 | #define _pwd_path config.boolean_options & 0x800000 107 | #define _kernel_type config.boolean_options & 0x1000000 108 | #define _col_background config.boolean_options & 0x2000000 109 | #define _bat_status config.boolean_options & 0x4000000 110 | #define _swap_perc config.boolean_options & 0x8000000 111 | 112 | void parseConfig(bool error, const char *file, struct SModule *modules, void **ascii_ptr, bool *default_bold, char *default_color, char *default_logo); 113 | -------------------------------------------------------------------------------- /src/config/parsing.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../utils/return.h" 8 | 9 | // TODO: rewrite config parsing from scratch 10 | 11 | /* 12 | 13 | // checks if the given character is between two " " 14 | bool is_in_string(const char *str, const char *place) { 15 | int count = 0; 16 | 17 | char *ptr = (char*)str; 18 | while((ptr = strchr(ptr, '"')) && ptr < (char*)place) { 19 | ++count; 20 | ptr += 1; 21 | } 22 | 23 | if(count % 2 == 1) 24 | return true; 25 | 26 | return false; 27 | } 28 | 29 | // skip every whitespace, return the first full character 30 | char *skip_whites(char *ptr) { 31 | if(ptr == NULL) 32 | return NULL; 33 | if(ptr[0] == 0) 34 | return NULL; 35 | 36 | while(*ptr) { 37 | if(*ptr != ' ' && *ptr != '\n' && *ptr != '\t') 38 | return ptr; 39 | ++ptr; 40 | } 41 | 42 | return NULL; 43 | } 44 | 45 | // skip every full character, return the first whitespace 46 | char *skip_full(char *ptr) { 47 | if(ptr == NULL) 48 | return NULL; 49 | 50 | while(*ptr) { 51 | if(*ptr == ' ' || *ptr == '\n' || *ptr == '\t') 52 | return ptr; 53 | ++ptr; 54 | } 55 | 56 | return NULL; 57 | } 58 | 59 | */ 60 | 61 | // remove comments, aka from start to the end of the line 62 | void uncomment(char *str, const char start) { 63 | char *ptr = str, *ptr2; 64 | while((ptr = strchr(ptr, start))) { 65 | // when it is between two " (aka the number of " before it is odd) 66 | int counter = 0; 67 | ptr2 = str; 68 | while((ptr2 = strchr(ptr2, '"')) && ptr2 < ptr) { 69 | ++ptr2; 70 | ++counter; 71 | } 72 | if(counter & 1) { 73 | ++ptr; 74 | continue; 75 | } 76 | 77 | ptr2 = strchr(ptr, '\n'); 78 | 79 | if(ptr2 == NULL) { 80 | *ptr = 0; 81 | break; 82 | } 83 | 84 | memmove(ptr, ptr2 + 1, strlen(ptr2)); 85 | } 86 | } 87 | 88 | // check every '\' in str and unescape "\\" "\n" "\e" "\033" 89 | void unescape(char *str) { 90 | while((str = strchr(str, '\\'))) { 91 | switch(str[1]) { 92 | case 'e': 93 | memmove(str, str + 1, strlen(str)); 94 | *str = '\033'; 95 | break; 96 | case '0': 97 | if(str[2] == '3' && str[3] == '3') { 98 | memmove(str, str + 3, strlen(str + 2)); 99 | *str = '\033'; 100 | } 101 | break; 102 | case 'n': 103 | memmove(str, str + 1, strlen(str)); 104 | *str = '\n'; 105 | break; 106 | default: // takes care of "\\" and any other sort of "\X" 107 | memmove(str, str + 1, strlen(str)); 108 | ++str; 109 | break; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/config/parsing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // bool is_in_string(const char *str, const char *place); 6 | 7 | // char *skip_whites(char *ptr); 8 | 9 | // char *skip_full(char *ptr); 10 | 11 | void uncomment(char *str, const char start); 12 | 13 | void unescape(char *str); 14 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is not part of albafetch. 3 | * All it does is check whether every single module is working, and what might not be working. 4 | * Useful for checking what info I'm able to get on multiple systems. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "config/config.h" 12 | #include "info/info.h" 13 | #include "utils/return.h" 14 | #include "utils/utils.h" 15 | #ifdef HAVE_VERSION_H 16 | #include "version.h" // generated by meson - defines COMMIT and VERSION 17 | #else 18 | #define VERSION "[UNKNOWN]" 19 | #define COMMIT "[UNKNOWN]" 20 | #endif // HAVE_VERSION_H 21 | 22 | // Most of those aren't even needed 23 | struct SConfig config; 24 | 25 | int main(int argc, char **argv) { 26 | puts("version: \033[1m\033[34m" VERSION "\033[0m, commit: \033[1m\033[34m" COMMIT "\033[0m\n"); 27 | 28 | struct SModule { 29 | int (*func)(char *); 30 | char *name; 31 | }; 32 | struct SModule arr[] = {{user, "user"}, 33 | {hostname, "hostname"}, 34 | {uptime, "uptime"}, 35 | {os, "os"}, 36 | {kernel, "kernel"}, 37 | {desktop, "desktop"}, 38 | {gtkTheme, "gtk_theme"}, 39 | {iconTheme, "icon_theme"}, 40 | {cursorTheme, "cursor_theme"}, 41 | {shell, "shell"}, 42 | {loginShell, "login_shell"}, 43 | {term, "term"}, 44 | {packages, "packages"}, 45 | {host, "host"}, 46 | {bios, "bios"}, 47 | {cpu, "cpu"}, 48 | {gpu, "gpu"}, 49 | {memory, "memory"}, 50 | {swap, "swap"}, 51 | {publicIp, "public_ip"}, 52 | {localIp, "local_ip"}, 53 | {pwd, "pwd"}, 54 | {date, "date"}, 55 | {battery, "battery"}, 56 | {colors, "colors"}, 57 | {lightColors, "light_colors"}}; 58 | 59 | unsigned errors = 0; 60 | int return_value; 61 | char mem[DEST_SIZE]; 62 | 63 | struct timeval start, end, start_all; 64 | double time; 65 | 66 | // just setting every option to 1 (except maybe _pkg_pip cause pip is slow af) 67 | if(argc > 1) 68 | config.boolean_options = strcmp(argv[1], "--no-pip") ? 0xffffffffffffffff : 0xffffffffffff7fff; 69 | else 70 | config.boolean_options = 0xffffffffffffffff; 71 | // these are just defaults 72 | strcpy(config.col_block_str, " "); 73 | strcpy(config.date_format, "%02d/%02d/%d %02d:%02d:%02d"); 74 | 75 | gettimeofday(&start_all, NULL); 76 | 77 | for(unsigned long i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i) { 78 | gettimeofday(&start, NULL); 79 | 80 | return_value = arr[i].func(mem); 81 | 82 | gettimeofday(&end, NULL); 83 | 84 | time = ((end.tv_sec - start.tv_sec) * 1e6 + end.tv_usec - start.tv_usec) / 1e3; 85 | 86 | if(return_value == 0) { 87 | printf("\033[1m\033[32m%-12s\033[0m %-40s [\033[1m\033[36m\033[1m%.3f ms\033[0m]\n", arr[i].name, mem, time); 88 | } else { 89 | printf("\033[1m\033[31m%-13s\033[0m 0x%-38x" 90 | "[\033[1m\033[36m\033[1m%.3f ms\033[0m]\n", 91 | arr[i].name, return_value, time); 92 | ++errors; 93 | } 94 | } 95 | 96 | gettimeofday(&end, NULL); 97 | time = ((end.tv_sec - start_all.tv_sec) * 1e6 + end.tv_usec - start_all.tv_usec) / 1e3; 98 | 99 | printf("\n\033[1mDebug run finished with a total of %u errors.\033[0m [\033[1m\033[36m\033[1m%.3f ms\033[0m]\n", errors, time); 100 | 101 | return RET_OK; 102 | } 103 | -------------------------------------------------------------------------------- /src/info/battery.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifdef __ANDROID__ 6 | #include 7 | #endif // __ANDROID__ 8 | 9 | #include "info.h" 10 | #include "../config/config.h" 11 | #include "../utils/wrappers.h" 12 | 13 | // get the battery percentage and status (Linux only!) 14 | int battery(char *dest) { 15 | char capacity[5] = ""; 16 | char status[20] = ""; 17 | 18 | #ifdef __ANDROID__ // relies on termux api 19 | char buf[DEST_SIZE]; 20 | char *args[] = {"termux-battery-status", NULL}; 21 | execCmd(buf, DEST_SIZE, args); 22 | 23 | char *ptr = strstr(buf, "percentage"); 24 | char *ptr2 = strstr(buf, "status"); 25 | char *end; 26 | 27 | if(ptr != NULL) { 28 | ptr += 13; 29 | end = strchr(ptr, ','); 30 | if(end != NULL) { 31 | *end = 0; 32 | safeStrncpy(capacity, ptr, 5); 33 | } 34 | } 35 | 36 | if(ptr2 != NULL) { 37 | ptr2 += 10; 38 | end = strstr(ptr2, "\","); 39 | ptr = ptr2 + 1; 40 | while(*ptr) { 41 | *ptr = tolower(*ptr); 42 | 43 | ++ptr; 44 | } 45 | 46 | if(end != NULL) { 47 | *end = 0; 48 | safeStrncpy(status, ptr2, 20); 49 | } 50 | } 51 | #else 52 | FILE *fp = NULL; 53 | 54 | if((fp = fopen("/sys/class/power_supply/BAT0/capacity", "r"))) { 55 | capacity[fread(capacity, 1, 5, fp) - 1] = 0; 56 | 57 | fclose(fp); 58 | } 59 | if((fp = fopen("/sys/class/power_supply/BAT0/status", "r"))) { 60 | status[fread(status, 1, 20, fp) - 1] = 0; 61 | 62 | fclose(fp); 63 | } 64 | #endif // __ANDROID__ 65 | 66 | if(capacity[0] != 0 && status[0] != 0) { 67 | if((_bat_status)) 68 | snprintf(dest, DEST_SIZE, "%s%% (%s)", capacity, status); 69 | else 70 | snprintf(dest, DEST_SIZE, "%s%%", capacity); 71 | } else if(capacity[0] != 0) 72 | snprintf(dest, DEST_SIZE, "%s%%", capacity); 73 | else if(status[0] != 0 && (_bat_status)) 74 | safeStrncpy(dest, status, DEST_SIZE); 75 | else 76 | return ERR_NO_INFO; 77 | 78 | return RET_OK; 79 | } 80 | -------------------------------------------------------------------------------- /src/info/bios.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "info.h" 7 | #include "../utils/wrappers.h" 8 | 9 | // get the current BIOS vendor and version (Linux only!) 10 | int bios(char *dest) { 11 | char *vendor = NULL, *version = NULL; 12 | FILE *fp = NULL; 13 | size_t len; 14 | 15 | if((fp = fopen("/sys/devices/virtual/dmi/id/bios_vendor", "r"))) { 16 | fseek(fp, 0, SEEK_END); 17 | len = ftell(fp); 18 | fseek(fp, 0, SEEK_SET); 19 | 20 | vendor = malloc(len); 21 | if(vendor == NULL) 22 | return ERR_OOM; 23 | vendor[fread(vendor, 1, len, fp) - 1] = 0; 24 | 25 | fclose(fp); 26 | } 27 | 28 | if((fp = fopen("/sys/devices/virtual/dmi/id/bios_version", "r"))) { 29 | fseek(fp, 0, SEEK_END); 30 | len = ftell(fp); 31 | fseek(fp, 0, SEEK_SET); 32 | 33 | version = malloc(len); 34 | if(version == NULL) { 35 | free(vendor); 36 | return ERR_OOM; 37 | } 38 | version[fread(version, 1, len, fp) - 1] = 0; 39 | 40 | fclose(fp); 41 | } 42 | 43 | if(vendor != NULL && version != NULL) 44 | snprintf(dest, DEST_SIZE, "%s %s", vendor, version); 45 | else if(vendor != NULL) 46 | safeStrncpy(dest, vendor, DEST_SIZE); 47 | else if(version != NULL) 48 | safeStrncpy(dest, version, DEST_SIZE); 49 | else 50 | return ERR_NO_INFO; 51 | 52 | free(vendor); 53 | free(version); 54 | 55 | return RET_OK; 56 | } 57 | -------------------------------------------------------------------------------- /src/info/colors.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../config/config.h" 3 | 4 | #include 5 | 6 | #include 7 | 8 | // show the terminal color configuration 9 | int colors(char *dest) { 10 | memset(dest, 0, DEST_SIZE); 11 | 12 | for(int i = 0; i < 8; ++i) 13 | sprintf(dest + strlen(dest), "\033[%s%dm%s", _col_background ? "4" : "3", i, config.col_block_str); 14 | 15 | strcat(dest, "\033[0m"); 16 | 17 | return RET_OK; 18 | } 19 | -------------------------------------------------------------------------------- /src/info/cpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifdef __APPLE__ 6 | #include 7 | #endif // __APPLE__ 8 | 9 | #include "info.h" 10 | #include "../config/config.h" 11 | #include "../utils/wrappers.h" 12 | 13 | // get the cpu name and frequency 14 | int cpu(char *dest) { 15 | char *cpu_info; 16 | char *end; 17 | int count = 0; 18 | char freq[24] = ""; 19 | 20 | #ifdef __APPLE__ 21 | size_t BUF_SIZE = DEST_SIZE; 22 | char buf[BUF_SIZE]; 23 | buf[0] = 0; 24 | sysctlbyname("machdep.cpu.brand_string", buf, &BUF_SIZE, NULL, 0); 25 | 26 | if(buf[0] == 0) 27 | return ERR_NO_INFO; 28 | 29 | if((_cpu_freq) == 0) { 30 | if((end = strstr(buf, " @"))) 31 | *end = 0; 32 | else if((end = strchr(buf, '@'))) 33 | *end = 0; 34 | } 35 | 36 | cpu_info = buf; 37 | #else 38 | FILE *fp = fopen("/proc/cpuinfo", "r"); 39 | if(fp == NULL) 40 | return ERR_NO_FILE; 41 | 42 | char *buf = malloc(0x10000); 43 | if(buf == NULL) 44 | return ERR_OOM; 45 | buf[fread(buf, 1, 0x10000, fp)] = 0; 46 | fclose(fp); 47 | 48 | cpu_info = buf; 49 | if(_cpu_count) { 50 | end = cpu_info; 51 | while((end = strstr(end, "processor"))) { 52 | ++count; 53 | ++end; 54 | } 55 | } 56 | 57 | cpu_info = strstr(cpu_info, "model name"); 58 | if(cpu_info == NULL) { 59 | free(buf); 60 | return ERR_PARSING; 61 | } 62 | 63 | cpu_info += 13; 64 | 65 | end = strstr(cpu_info, " @"); 66 | if(end) 67 | *end = 0; 68 | else { 69 | end = strchr(cpu_info, '\n'); 70 | if(end == NULL) { 71 | free(buf); 72 | return ERR_PARSING + 0x10; 73 | } 74 | 75 | *end = 0; 76 | } 77 | 78 | /* I might eventually add an option to get the "default" clock speed 79 | * by parsing one or more of the following files: 80 | * - /sys/devices/system/cpu/cpu0/cpufreq/cpupower_max_freq 81 | * - /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq 82 | * - /sys/devices/system/cpu/cpu0/cpufreq/bios_limit 83 | * - /sys/devices/system/cpu/cpu0/cpufreq/base_frequency 84 | */ 85 | // Printing the clock frequency the first thread is currently running at 86 | ++end; 87 | char *frequency = strstr(end, "cpu MHz"); 88 | if(frequency && _cpu_freq) { 89 | frequency = strchr(frequency, ':'); 90 | if(frequency) { 91 | frequency += 2; 92 | 93 | end = strchr(frequency, '\n'); 94 | if(end) { 95 | *end = 0; 96 | 97 | snprintf(freq, 24, " @ %.2g GHz", atof(frequency) / 1e3); 98 | } 99 | } 100 | } 101 | #endif 102 | 103 | // cleaning the string from various garbage 104 | if((end = strstr(cpu_info, "(R)"))) 105 | memmove(end, end + 3, strlen(end + 3) + 1); 106 | if((end = strstr(cpu_info, "(TM)"))) 107 | memmove(end, end + 4, strlen(end + 4) + 1); 108 | if((end = strstr(cpu_info, " CPU"))) 109 | memmove(end, end + 4, strlen(end + 4) + 1); 110 | if((end = strstr(cpu_info, "th Gen "))) 111 | memmove(end - 2, end + 7, strlen(end + 7) + 1); 112 | if((end = strstr(cpu_info, " with Radeon Graphics"))) 113 | *end = 0; 114 | if((end = strstr(cpu_info, "-Core Processor"))) { 115 | if(end >= cpu_info + 5) { 116 | end -= 5; 117 | end = strchr(end, ' '); 118 | if(end != NULL) 119 | *end = 0; 120 | } 121 | } 122 | 123 | if((_cpu_brand) == 0) { 124 | if((end = strstr(cpu_info, "Intel Core "))) 125 | memmove(end, end + 11, strlen(end + 1)); 126 | else if((end = strstr(cpu_info, "Apple "))) 127 | memmove(end, end + 6, strlen(end + 6) + 1); 128 | else if((end = strstr(cpu_info, "AMD "))) 129 | memmove(end, end + 4, strlen(end + 1)); 130 | } 131 | 132 | safeStrncpy(dest, cpu_info, DEST_SIZE); 133 | #ifdef __linux__ 134 | free(buf); 135 | #endif 136 | 137 | if(freq[0]) 138 | strncat(dest, freq, DEST_SIZE - 1 - strlen(dest)); 139 | 140 | if(count && _cpu_count) { 141 | char core_count[16]; 142 | snprintf(core_count, 16, " (%d) ", count); 143 | strncat(dest, core_count, DEST_SIZE - 1 - strlen(dest)); 144 | } 145 | // final cleanup ("Intel Core i5 650" lol) 146 | while((end = strstr(dest, " "))) 147 | memmove(end, end + 1, strlen(end)); 148 | 149 | return RET_OK; 150 | } 151 | -------------------------------------------------------------------------------- /src/info/cursor_theme.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "info.h" 7 | #include "../optdeps/optdeps.h" 8 | #include "../utils/wrappers.h" 9 | 10 | // get the current Cursor Theme 11 | int cursorTheme(char *dest) { 12 | // try using gsettings 13 | // reading ~/.config/gtk-3.0/settings.ini could also be an option 14 | if(binaryInPath("gsettings")) { 15 | char buf[DEST_SIZE] = ""; 16 | char *args[] = {"gsettings", "get", "org.gnome.desktop.interface", "cursor-theme", NULL}; 17 | execCmd(buf, DEST_SIZE, args); 18 | 19 | // cleanup 20 | if(buf[0] != 0) { 21 | if(buf[0] == '\'') { 22 | memmove(buf, buf + 1, strlen(buf)); 23 | 24 | char *ptr = strchr(buf, '\''); 25 | if(ptr) 26 | *ptr = 0; 27 | } 28 | 29 | safeStrncpy(dest, buf, DEST_SIZE); 30 | return RET_OK; 31 | } 32 | } 33 | 34 | return ERR_UNSUPPORTED; 35 | } 36 | -------------------------------------------------------------------------------- /src/info/date.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../config/config.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // get the current date and time 10 | int date(char *dest) { 11 | time_t t = time(NULL); 12 | struct tm tm = *localtime(&t); 13 | snprintf(dest, DEST_SIZE, config.date_format, tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); 14 | return RET_OK; 15 | } 16 | -------------------------------------------------------------------------------- /src/info/desktop.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../config/config.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // get the current desktop environment 11 | int desktop(char *dest) { 12 | #ifdef __APPLE__ 13 | strcpy(dest, "Aqua"); 14 | #else 15 | char *desktop = getenv("SWAYSOCK") ? "Sway" : 16 | (desktop = getenv("XDG_CURRENT_DESKTOP")) ? desktop : 17 | (desktop = getenv("DESKTOP_SESSION")) ? desktop : 18 | getenv("KDE_SESSION_VERSION") ? "KDE" : 19 | getenv("GNOME_DESKTOP_SESSION_ID") ? "GNOME" : 20 | getenv("MATE_DESKTOP_SESSION_ID") ? "MATE" : 21 | getenv("TDE_FULL_SESSION") ? "Trinity" : 22 | // strcmp("linux", getenv("TERM") == 0 ? "none" : // running in tty 23 | NULL; 24 | if(desktop == NULL) 25 | return ERR_NO_INFO; 26 | 27 | strcpy(dest, desktop); 28 | 29 | if(_de_type) { 30 | if(getenv("WAYLAND_DISPLAY")) 31 | strncat(dest, " (Wayland)", DEST_SIZE - strlen(dest)); 32 | else if((desktop = getenv("XDG_SESSION_TYPE"))) { 33 | if(desktop[0] == 0) 34 | return RET_OK; 35 | desktop[0] = toupper(desktop[0]); 36 | 37 | char buf[32]; 38 | snprintf(buf, 32, " (%s) ", desktop); 39 | strncat(dest, buf, DEST_SIZE - 1 - strlen(dest)); 40 | } 41 | } 42 | #endif 43 | 44 | return RET_OK; 45 | } 46 | -------------------------------------------------------------------------------- /src/info/gpu.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../config/config.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __APPLE__ 11 | #include 12 | #include "../macos/macos_infos.h" 13 | #include "../utils/wrappers.h" 14 | #else 15 | #ifndef __ANDROID__ 16 | #include "../optdeps/optdeps.h" 17 | #endif // __ANDROID__ 18 | #endif // __APPLE__ 19 | 20 | // get the gpu name(s) 21 | int gpu(char *dest) { 22 | char *gpus[] = {NULL, NULL, NULL}; 23 | 24 | #ifdef __APPLE__ 25 | struct utsname name; 26 | uname(&name); 27 | 28 | if(strcmp(name.machine, "x86_64") == 0) 29 | gpus[0] = getGpuString(); // only works on x64 30 | if(gpus[0] == 0 || strcmp(name.machine, "x86_64")) { // fallback 31 | char buf[1024]; 32 | char *args[] = {"/usr/sbin/system_profiler", "SPDisplaysDataType", NULL}; 33 | execCmd(buf, 1024, args); 34 | 35 | gpus[0] = strstr(buf, "Chipset Model: "); 36 | if(gpus[0] == 0) 37 | return ERR_NO_INFO; 38 | gpus[0] += 15; 39 | char *end = strchr(gpus[0], '\n'); 40 | if(end == NULL) 41 | return ERR_PARSING; 42 | *end = 0; 43 | } 44 | #else 45 | #ifdef __ANDROID__ 46 | return ERR_UNSUPPORTED; 47 | #else 48 | getGpus(gpus); 49 | #endif // __ANDROID__ 50 | #endif // __APPLE__ 51 | 52 | if(gpus[0] == NULL) 53 | return ERR_NO_INFO; 54 | 55 | // this next part is just random cleanup 56 | // also, I'm using end as a random char* - BaD pRaCtIcE aNd CoNfUsInG - lol stfu 57 | // yk it's decent and it works 58 | dest[0] = 0; 59 | for(unsigned i = 0; i < sizeof(gpus) / sizeof(gpus[0]) && gpus[i] != NULL; ++i) { 60 | if((_gpu_brand) == 0) { 61 | if(strstr(gpus[i], "Intel ") || strstr(gpus[i], "Apple ")) 62 | gpus[i] += 6; 63 | else if(strstr(gpus[i], "AMD ")) 64 | gpus[i] += 4; 65 | } 66 | 67 | char *end = strchr(gpus[i], '['); 68 | if(end) { // sometimes the gpu is "Architecture [GPU Name]" 69 | char *ptr = strchr(end, ']'); 70 | if(ptr) { 71 | gpus[i] = end + 1; 72 | *ptr = 0; 73 | } 74 | } 75 | 76 | if((end = strstr(gpus[i], " Integrated Graphics Controller"))) 77 | *end = 0; 78 | if((end = strstr(gpus[i], " Rev. "))) 79 | *end = 0; 80 | 81 | // (finally) writing the GPU(s) into dest 82 | if(i > 0) 83 | strncat(dest, ", ", DEST_SIZE - strlen(dest)); 84 | strncat(dest, gpus[i], DEST_SIZE - 1 - strlen(dest)); 85 | } 86 | 87 | return RET_OK; 88 | } 89 | -------------------------------------------------------------------------------- /src/info/gtk_theme.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "info.h" 7 | #include "../optdeps/optdeps.h" 8 | #include "../utils/wrappers.h" 9 | 10 | // get the current GTK Theme 11 | int gtkTheme(char *dest) { 12 | char *theme = getenv("GTK_THEME"); 13 | 14 | // try using GTK_THEME (faster) 15 | if(theme) { 16 | safeStrncpy(dest, theme, DEST_SIZE); 17 | 18 | return RET_OK; 19 | } 20 | 21 | // try using gsettings (fallback) 22 | // reading ~/.config/gtk-3.0/settings.ini could also be an option 23 | if(binaryInPath("gsettings")) { 24 | char buf[DEST_SIZE] = ""; 25 | char *args[] = {"gsettings", "get", "org.gnome.desktop.interface", "gtk-theme", NULL}; 26 | execCmd(buf, DEST_SIZE, args); 27 | 28 | // cleanup 29 | if(buf[0] != 0) { 30 | if(buf[0] == '\'') { 31 | memmove(buf, buf + 1, strlen(buf)); 32 | 33 | char *ptr = strchr(buf, '\''); 34 | if(ptr) 35 | *ptr = 0; 36 | } 37 | 38 | safeStrncpy(dest, buf, DEST_SIZE); 39 | return RET_OK; 40 | } 41 | } 42 | 43 | return ERR_UNSUPPORTED; 44 | } 45 | -------------------------------------------------------------------------------- /src/info/host.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __APPLE__ 8 | #include 9 | #else 10 | #ifdef __ANDROID__ 11 | #include 12 | #include 13 | 14 | #include "../utils/wrappers.h" 15 | #else 16 | #include "../utils/wrappers.h" 17 | #endif // __ANDROID__ 18 | #endif // __APPLE__ 19 | 20 | #include "info.h" 21 | 22 | // get the machine name and eventually model version 23 | int host(char *dest) { 24 | #ifdef __APPLE__ 25 | size_t BUF_SIZE = DEST_SIZE; 26 | sysctlbyname("hw.model", dest, &BUF_SIZE, NULL, 0); 27 | #else 28 | #ifdef __ANDROID__ 29 | char brand[64], model[64]; 30 | char *brand_args[] = {"getprop", "ro.product.brand", NULL}; 31 | char *model_args[] = {"getprop", "ro.product.model", NULL}; 32 | 33 | execCmd(brand, 64, brand_args); 34 | execCmd(model, 64, model_args); 35 | 36 | if((brand[0] || model[0]) == 0) 37 | return ERR_NO_INFO; 38 | 39 | snprintf(dest, DEST_SIZE, "%s%s%s", brand, brand[0] ? " " : "", model); 40 | #else 41 | char *name = NULL, *version = NULL; 42 | FILE *fp = NULL; 43 | size_t len; 44 | 45 | if((fp = fopen("/sys/devices/virtual/dmi/id/product_name", "r"))) { 46 | fseek(fp, 0, SEEK_END); 47 | len = ftell(fp); 48 | fseek(fp, 0, SEEK_SET); 49 | 50 | name = malloc(len); 51 | if(name == NULL) 52 | return ERR_OOM; 53 | name[fread(name, 1, len, fp) - 1] = 0; 54 | 55 | fclose(fp); 56 | } 57 | 58 | if((fp = fopen("/sys/devices/virtual/dmi/id/product_version", "r"))) { 59 | fseek(fp, 0, SEEK_END); 60 | len = ftell(fp); 61 | fseek(fp, 0, SEEK_SET); 62 | 63 | version = malloc(len); 64 | if(version == NULL) 65 | return ERR_OOM; 66 | version[fread(version, 1, len, fp) - 1] = 0; 67 | 68 | fclose(fp); 69 | } 70 | 71 | // filtering out some shitty defaults because the file can't just be empty" 72 | const char *errors[] = {"System Product Name", "System Version", "To Be Filled By O.E.M.", "None", ""}; 73 | bool name_defined = true; 74 | bool version_defined = true; 75 | 76 | for(unsigned long i = 0; i < sizeof(errors) / sizeof(errors[0]); ++i) { 77 | if(name) 78 | if(strcmp(name, errors[i]) == 0) 79 | name_defined = false; 80 | if(version) 81 | if(strcmp(version, errors[i]) == 0) 82 | version_defined = false; 83 | } 84 | 85 | if(name && version && name_defined && version_defined) 86 | snprintf(dest, DEST_SIZE, "%s %s", name, version); 87 | else if(name && name_defined) 88 | safeStrncpy(dest, name, DEST_SIZE); 89 | else if(version && version_defined) 90 | safeStrncpy(dest, version, DEST_SIZE); 91 | else { 92 | free(name); 93 | free(version); 94 | 95 | return ERR_NO_INFO; 96 | } 97 | 98 | free(name); 99 | free(version); 100 | #endif // __ANDROID__ 101 | #endif // __APPLE__ 102 | 103 | return RET_OK; 104 | } 105 | -------------------------------------------------------------------------------- /src/info/hostname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "info.h" 6 | #include "../utils/wrappers.h" 7 | 8 | // idk why but this is sometimes not defined 9 | #ifndef HOST_NAME_MAX 10 | #ifdef _POSIX_HOST_NAME_MAX 11 | #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX 12 | #else 13 | #define HOST_NAME_MAX 255 14 | #endif 15 | #endif 16 | 17 | // print the machine hostname 18 | int hostname(char *dest) { 19 | char hostname[HOST_NAME_MAX + 1]; 20 | gethostname(hostname, HOST_NAME_MAX + 1); 21 | 22 | char *ptr = strstr(hostname, ".local"); 23 | if(ptr) 24 | *ptr = 0; 25 | 26 | safeStrncpy(dest, hostname, DEST_SIZE); 27 | 28 | return RET_OK; 29 | } 30 | -------------------------------------------------------------------------------- /src/info/icon_theme.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "info.h" 7 | #include "../optdeps/optdeps.h" 8 | #include "../utils/wrappers.h" 9 | 10 | // get the current Icon Theme 11 | int iconTheme(char *dest) { 12 | // try using gsettings 13 | // reading ~/.config/gtk-3.0/settings.ini could also be an option 14 | if(binaryInPath("gsettings")) { 15 | char buf[DEST_SIZE] = ""; 16 | char *args[] = {"gsettings", "get", "org.gnome.desktop.interface", "icon-theme", NULL}; 17 | execCmd(buf, DEST_SIZE, args); 18 | 19 | // cleanup 20 | if(buf[0] != 0) { 21 | if(buf[0] == '\'') { 22 | memmove(buf, buf + 1, strlen(buf)); 23 | 24 | char *ptr = strchr(buf, '\''); 25 | if(ptr) 26 | *ptr = 0; 27 | } 28 | 29 | safeStrncpy(dest, buf, DEST_SIZE); 30 | return RET_OK; 31 | } 32 | } 33 | 34 | return ERR_UNSUPPORTED; 35 | } 36 | -------------------------------------------------------------------------------- /src/info/info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utils/return.h" 4 | 5 | #define DEST_SIZE 256 6 | 7 | int user(char *dest); 8 | 9 | int hostname(char *dest); 10 | 11 | int uptime(char *dest); 12 | 13 | int os(char *dest); 14 | 15 | int kernel(char *dest); 16 | 17 | int desktop(char *dest); 18 | 19 | int gtkTheme(char *dest); 20 | 21 | int iconTheme(char *dest); 22 | 23 | int cursorTheme(char *dest); 24 | 25 | int shell(char *dest); 26 | 27 | int loginShell(char *dest); 28 | 29 | int term(char *dest); 30 | 31 | int packages(char *dest); 32 | 33 | int host(char *dest); 34 | 35 | int bios(char *dest); 36 | 37 | int cpu(char *dest); 38 | 39 | int gpu(char *dest); 40 | 41 | int memory(char *dest); 42 | 43 | int swap(char *dest); 44 | 45 | int publicIp(char *dest); 46 | 47 | int localIp(char *dest); 48 | 49 | int pwd(char *dest); 50 | 51 | int date(char *dest); 52 | 53 | int battery(char *dest); 54 | 55 | int colors(char *dest); 56 | 57 | int lightColors(char *dest); 58 | -------------------------------------------------------------------------------- /src/info/kernel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "info.h" 6 | #include "../config/config.h" 7 | #include "../utils/wrappers.h" 8 | 9 | // print the running kernel version (uname -r) 10 | int kernel(char *dest) { 11 | struct utsname name; 12 | uname(&name); 13 | char *ptr = name.release, *type = NULL; 14 | 15 | if(_kernel_type) { 16 | while((ptr = strchr(ptr, '-'))) 17 | type = ++ptr; 18 | } 19 | 20 | if(_kernel_short) { 21 | if((ptr = strchr(name.release, '-'))) 22 | *ptr = 0; 23 | } 24 | 25 | if(_kernel_type && type) 26 | snprintf(dest, DEST_SIZE, "%s (%s)", name.release, type); 27 | else 28 | safeStrncpy(dest, name.release, DEST_SIZE); 29 | 30 | return RET_OK; 31 | } 32 | -------------------------------------------------------------------------------- /src/info/light_colors.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../config/config.h" 3 | 4 | #include 5 | 6 | #include 7 | 8 | // show the terminal color configuration 9 | int lightColors(char *dest) { 10 | memset(dest, 0, DEST_SIZE); 11 | 12 | for(int i = 0; i < 8; ++i) 13 | sprintf(dest + strlen(dest), "\033[%s%dm%s", _col_background ? "10" : "9", i, config.col_block_str); 14 | 15 | strcat(dest, "\033[0m"); 16 | 17 | return RET_OK; 18 | } 19 | -------------------------------------------------------------------------------- /src/info/local_ip.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../config/config.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // get all local ips 11 | int localIp(char *dest) { 12 | struct ifaddrs *addrs = NULL; 13 | bool done = false; 14 | int buf_size = DEST_SIZE; 15 | 16 | getifaddrs(&addrs); 17 | 18 | struct ifaddrs *first = addrs; 19 | 20 | while(addrs) { 21 | // checking if the ip is valid 22 | if(addrs->ifa_addr && addrs->ifa_addr->sa_family == AF_INET) { 23 | // filtering out docker or localhost ips 24 | if((strcmp(addrs->ifa_name, "lo") != 0 || _loc_localhost) && (strcmp(addrs->ifa_name, "docker0") != 0 || _loc_docker)) { 25 | struct sockaddr_in *pAddr = (struct sockaddr_in *)addrs->ifa_addr; 26 | 27 | // saving it to the list of interfaces 28 | snprintf(dest, buf_size, "%s%s (%s)", done ? ", " : "", inet_ntoa(pAddr->sin_addr), addrs->ifa_name); 29 | dest += strlen(dest); 30 | buf_size -= strlen(dest); 31 | done = true; 32 | } 33 | } 34 | 35 | addrs = addrs->ifa_next; 36 | } 37 | 38 | freeifaddrs(first); 39 | 40 | if(done) 41 | return RET_OK; 42 | else 43 | return ERR_NO_INFO; 44 | } 45 | -------------------------------------------------------------------------------- /src/info/login_shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "info.h" 5 | #include "../config/config.h" 6 | #include "../utils/wrappers.h" 7 | 8 | // get the current login shell 9 | int loginShell(char *dest) { 10 | char *buf = getenv("SHELL"); 11 | 12 | if(buf != NULL) { 13 | safeStrncpy(dest, _shell_path ? buf : basename(buf), DEST_SIZE); 14 | return RET_OK; 15 | } 16 | 17 | return ERR_NO_INFO; 18 | } 19 | -------------------------------------------------------------------------------- /src/info/memory.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../utils/queue.h" 3 | #include "../config/config.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #ifdef __APPLE__ 11 | #include "../macos/macos_infos.h" 12 | #else 13 | #include 14 | #endif // __APPLE__ 15 | 16 | // get used and total memory 17 | int memory(char *dest) { 18 | #ifdef __APPLE__ 19 | unsigned long usedram = (unsigned long)usedMemSize(); 20 | unsigned long totalram = (unsigned long)systemMemSize(); 21 | 22 | if(usedram == 0 || totalram == 0) { 23 | return ERR_NO_INFO; 24 | } 25 | 26 | snprintf(dest, DEST_SIZE, "%lu MiB / %lu MiB", usedram / 1048576, totalram / 1048576); 27 | 28 | #else 29 | struct sysinfo info; 30 | if(sysinfo(&info)) 31 | return ERR_NO_INFO; 32 | 33 | unsigned long totalram = info.totalram / 1024; 34 | unsigned long freeram = info.freeram / 1024; 35 | // unsigned long sharedram = info.sharedram / 1024; 36 | 37 | FILE *fp = fopen("/proc/meminfo", "r"); 38 | 39 | if(fp == NULL) 40 | return ERR_NO_FILE; 41 | 42 | char buf[DEST_SIZE]; 43 | char *cachedram = buf; 44 | 45 | readAfterSequence(fp, "Cached:", buf, DEST_SIZE); 46 | fclose(fp); 47 | 48 | if(buf[0] == 0) 49 | return ERR_PARSING; 50 | cachedram += 2; 51 | 52 | char *end = strstr(cachedram, " kB"); 53 | 54 | if(end == NULL) 55 | return ERR_PARSING + 0x10; 56 | 57 | *end = 0; 58 | 59 | unsigned long usedram = totalram - freeram - atol(cachedram); 60 | // usedram -= sharedram; 61 | 62 | snprintf(dest, DEST_SIZE, "%lu MiB / %lu MiB", usedram / 1024, totalram / 1024); 63 | #endif 64 | 65 | if(_mem_perc) { 66 | const size_t len = DEST_SIZE - strlen(dest); 67 | char perc[len]; 68 | 69 | snprintf(perc, len, " (%lu%%)", (usedram * 100) / totalram); 70 | strcat(dest, perc); 71 | } 72 | 73 | return RET_OK; 74 | } 75 | -------------------------------------------------------------------------------- /src/info/os.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "info.h" 6 | #include "../config/config.h" 7 | #include "../utils/queue.h" 8 | #include "../utils/wrappers.h" 9 | 10 | // print the operating system name and architecture (uname -m) 11 | int os(char *dest) { 12 | struct utsname name; 13 | uname(&name); 14 | 15 | #ifdef __APPLE__ 16 | if(_os_arch) 17 | snprintf(dest, DEST_SIZE, "macOS (%s)", name.machine); 18 | else 19 | safeStrncpy(dest, "macOS", DEST_SIZE); 20 | #else 21 | #ifdef __ANDROID__ 22 | char version[16]; 23 | char *args[] = {"getprop", "ro.build.version.release", NULL}; 24 | execCmd(version, 16, args); 25 | 26 | if(_os_arch) 27 | snprintf(dest, DEST_SIZE, "Android %s%s(%s)", version, version[0] ? " " : "", name.machine); 28 | else 29 | snprintf(dest, DEST_SIZE, "Android %s", version); 30 | #else 31 | FILE *fp = fopen("/etc/os-release", "r"); 32 | if(fp == NULL) { 33 | fp = fopen("/usr/lib/os-release", "r"); 34 | if(fp == NULL) 35 | return ERR_NO_FILE; 36 | } 37 | 38 | char buf[64]; 39 | char *os_name = buf; 40 | char *end; 41 | 42 | readAfterSequence(fp, "PRETTY_NAME", buf, 64); 43 | fclose(fp); 44 | 45 | if(buf[0] == 0) 46 | return ERR_PARSING; 47 | 48 | if((end = strchr(os_name, '\n')) == 0) 49 | return ERR_PARSING + 0x10; 50 | *end = 0; 51 | 52 | // sometimes we have something like `ID="distro"`. yeah its stupid 53 | if(os_name[0] == '"' || os_name[0] == '\'') 54 | ++os_name; 55 | 56 | if((end = strchr(os_name, '"')) == 0) 57 | end = strchr(os_name, '\''); 58 | if(end) 59 | *end = 0; 60 | 61 | if(_os_arch) 62 | snprintf(dest, DEST_SIZE, "%s (%s)", os_name, name.machine); 63 | else 64 | safeStrncpy(dest, os_name, DEST_SIZE); 65 | #endif // __ANDROID__ 66 | #endif // __APPLE__ 67 | 68 | return RET_OK; 69 | } 70 | -------------------------------------------------------------------------------- /src/info/packages.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // just here to stop vscode from complaining about DT_DIR 7 | #ifndef __USE_MISC 8 | #define __USE_MISC 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "info.h" 16 | #include "../config/config.h" 17 | #include "../optdeps/optdeps.h" 18 | #include "../utils/wrappers.h" 19 | 20 | /* 21 | * I know that I could probably use specific libraries (like ALPM 22 | * or some RPM stuff) and add them as optional dependencies, however: 23 | * - I'm too lazy to do that 24 | * - that would be way slower than just counting directories 25 | */ 26 | 27 | // get the number of installed packages 28 | int packages(char *dest) { 29 | dest[0] = 0; 30 | char buf[DEST_SIZE] = "", str[128] = ""; 31 | DIR *dir; 32 | struct dirent *entry; 33 | unsigned count = 0; 34 | bool done = false; 35 | 36 | #ifdef __linux__ // package managers that won't run on macOS 37 | FILE *fp; 38 | char *prefix = getenv("PREFIX"); 39 | char path[256]; 40 | 41 | path[0] = 0; 42 | if(prefix) 43 | safeStrncpy(path, prefix, 256); 44 | strncat(path, "/var/lib/pacman/local", 255 - strlen(path)); 45 | if(_pkg_pacman && (dir = opendir(path)) != NULL) { 46 | while((entry = readdir(dir)) != NULL) 47 | if(entry->d_type == DT_DIR && entry->d_name[0] != '.') 48 | ++count; 49 | closedir(dir); 50 | 51 | if(count) { 52 | snprintf(dest, DEST_SIZE, "%u%s", count, _pkg_mgr ? " (pacman)" : ""); 53 | done = true; 54 | } 55 | } 56 | 57 | path[0] = 0; 58 | if(prefix) 59 | safeStrncpy(path, prefix, 256); 60 | strncat(path, "/var/lib/dpkg/status", 255 - strlen(path)); 61 | if(_pkg_dpkg && (fp = fopen(path, "r")) != NULL) { 62 | char line[512]; 63 | int count = 0; 64 | 65 | while(fgets(line, sizeof(line), fp)) { 66 | // check if the line starts with "Package:" 67 | if(strncmp(line, "Package:", 8) == 0) { 68 | ++count; 69 | } 70 | } 71 | fclose(fp); 72 | 73 | if(count) { 74 | snprintf(buf, DEST_SIZE, "%u%s", count, _pkg_mgr ? " (dpkg)" : ""); 75 | done = true; 76 | strncat(dest, buf, DEST_SIZE - strlen(dest)); 77 | } 78 | } 79 | 80 | path[0] = 0; 81 | if(prefix) 82 | safeStrncpy(path, prefix, 256); 83 | strncat(path, "/var/lib/rpm/rpmdb.sqlite", 255 - strlen(path)); 84 | if(_pkg_rpm && access(path, F_OK) == 0) { 85 | sqlite3 *db; 86 | sqlite3_stmt *stmt; 87 | int count = 0; 88 | 89 | if(sqlite3_open(path, &db) != SQLITE_OK) 90 | goto skip; 91 | if(sqlite3_prepare_v2(db, "SELECT count(*) FROM Packages", -1, &stmt, NULL) != SQLITE_OK) { 92 | sqlite3_close(db); 93 | goto skip; 94 | } 95 | 96 | if(sqlite3_step(stmt) == SQLITE_ROW) { 97 | count = sqlite3_column_int(stmt, 0); 98 | 99 | if(count > 0) { 100 | snprintf(buf, DEST_SIZE - strlen(buf), "%s%s%s", done ? ", " : "", str, _pkg_mgr ? " (rpm)" : ""); 101 | done = true; 102 | strncat(dest, buf, DEST_SIZE - strlen(dest)); 103 | } 104 | } 105 | 106 | sqlite3_finalize(stmt); 107 | sqlite3_close(db); 108 | 109 | skip:; 110 | } 111 | 112 | path[0] = 0; 113 | if(prefix) 114 | safeStrncpy(path, prefix, 256); 115 | strncat(path, "/var/lib/flatpak/runtime", 255 - strlen(path)); 116 | if(_pkg_flatpak && (dir = opendir(path)) != NULL) { 117 | count = 0; 118 | while((entry = readdir(dir)) != NULL) 119 | if(entry->d_type == DT_DIR && entry->d_name[0] != '.') 120 | ++count; 121 | closedir(dir); 122 | 123 | if(count) { 124 | snprintf(buf, DEST_SIZE - strlen(buf), "%s%u%s", done ? ", " : "", count, _pkg_mgr ? " (flatpak)" : ""); 125 | done = true; 126 | strncat(dest, buf, DEST_SIZE - strlen(dest)); 127 | } 128 | } 129 | 130 | path[0] = 0; 131 | if(prefix) 132 | safeStrncpy(path, prefix, 256); 133 | strncat(path, "/var/snap", 255 - strlen(path)); 134 | if(_pkg_snap && (dir = opendir(path)) != NULL) { 135 | count = 0; 136 | while((entry = readdir(dir)) != NULL) 137 | if(entry->d_type == DT_DIR && entry->d_name[0] != '.') 138 | ++count; 139 | closedir(dir); 140 | 141 | if(count) { 142 | snprintf(buf, DEST_SIZE - strlen(buf), "%s%u%s", done ? ", " : "", count, _pkg_mgr ? " (snap)" : ""); 143 | done = true; 144 | strncat(dest, buf, DEST_SIZE - strlen(dest)); 145 | } 146 | } 147 | #endif 148 | 149 | if(_pkg_brew && (binaryInPath("brew"))) { 150 | char *args[] = {"brew", "--cellar", NULL}; 151 | execCmd(str, 16, args); 152 | 153 | if(str[0]) { 154 | if((dir = opendir(str)) != NULL) { 155 | count = 0; 156 | 157 | while((entry = readdir(dir)) != NULL) 158 | if(entry->d_type == DT_DIR && strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) 159 | ++count; 160 | closedir(dir); 161 | 162 | if(count) { 163 | snprintf(buf, DEST_SIZE, "%s%u%s", done ? ", " : "", count, _pkg_mgr ? " (brew)" : ""); 164 | done = true; 165 | strncat(dest, buf, DEST_SIZE - strlen(dest)); 166 | } 167 | } 168 | } 169 | } 170 | 171 | if(_pkg_pip && binaryInPath("pip")) { 172 | char *args[] = {"sh", "-c", "pip list 2>/dev/null | wc -l", NULL}; 173 | execCmd(str, 16, args); 174 | 175 | if(str[0] != '0' && str[0]) { 176 | snprintf(buf, DEST_SIZE - strlen(buf), "%s%d%s", done ? ", " : "", atoi(str) - 2, _pkg_mgr ? " (pip)" : ""); 177 | done = true; 178 | strncat(dest, buf, DEST_SIZE - strlen(dest)); 179 | } 180 | } 181 | 182 | if(done) 183 | return RET_OK; 184 | else 185 | return ERR_NO_INFO; 186 | } 187 | -------------------------------------------------------------------------------- /src/info/public_ip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "info.h" 9 | #include "../utils/wrappers.h" 10 | 11 | // Also, this doesn't seem to be working with the Wifi of a Hotel I'm currently staying in 12 | // I wonder why. There seems to be no IP adress in buf, but using curl works just fine. 13 | // I should probably reimplement curl as optdep for fallback, idk 14 | int puts(const char *); 15 | // get the current public ip 16 | int publicIp(char *dest) { 17 | // https://stackoverflow.com/a/65362666 - thanks dbush 18 | 19 | struct addrinfo hints = {0}, *addrs; 20 | hints.ai_family = AF_INET; 21 | hints.ai_socktype = SOCK_STREAM; 22 | hints.ai_protocol = 0; 23 | 24 | // using this as it is faster than ident.me 25 | if(getaddrinfo("whatismyip.akamai.com", "80", &hints, &addrs)) { 26 | return ERR_NO_INFO; 27 | } 28 | 29 | int socket_fd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol); 30 | if(socket_fd == -1) { 31 | freeaddrinfo(addrs); 32 | return ERR_NO_INFO + 0x10; 33 | } 34 | 35 | if(connect(socket_fd, addrs->ai_addr, addrs->ai_addrlen) == -1) { 36 | close(socket_fd); 37 | freeaddrinfo(addrs); 38 | return ERR_NO_INFO + 0x20; 39 | } 40 | 41 | char cmd[] = "GET / HTTP/1.1\nHost: whatismyip.akamai.com\n\n"; 42 | if(send(socket_fd, cmd, strlen(cmd), 0) == -1) { 43 | close(socket_fd); 44 | freeaddrinfo(addrs); 45 | return ERR_NO_INFO + 0x30; 46 | } 47 | 48 | char buf[1024] = ""; 49 | if(recv(socket_fd, buf, sizeof(buf), 0) == -1) { 50 | close(socket_fd); 51 | freeaddrinfo(addrs); 52 | return ERR_NO_INFO + 0x40; 53 | } 54 | 55 | close(socket_fd); 56 | freeaddrinfo(addrs); 57 | 58 | /* buf should now look like this: 59 | * """ 60 | * HTTP/1.1 200 OK 61 | * [...] 62 | * [...] 63 | * 64 | * 123.123.123.123 65 | * """ 66 | */ 67 | char *start = buf, *end; 68 | end = strchr(start, '\n'); 69 | if(strncmp(start, "HTTP/1.1 200 OK", end - start - 1) != 0) 70 | return ERR_NO_INFO + 0x50; 71 | 72 | start = strstr(start, "\n\r\n"); 73 | if(start == NULL) 74 | return ERR_PARSING; 75 | 76 | start += 3; 77 | 78 | safeStrncpy(dest, start, DEST_SIZE); 79 | 80 | return RET_OK; 81 | } 82 | -------------------------------------------------------------------------------- /src/info/pwd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "info.h" 6 | #include "../config/config.h" 7 | #include "../utils/wrappers.h" 8 | 9 | // get the current working directory 10 | int pwd(char *dest) { 11 | #ifdef __APPLE__ 12 | char *pwd = getcwd(NULL, DEST_SIZE); 13 | #else 14 | char *pwd = getcwd(NULL, 0); 15 | #endif // __APPLE__ 16 | 17 | if(pwd == NULL) 18 | return ERR_NO_INFO; 19 | 20 | safeStrncpy(dest, _pwd_path ? pwd : basename(pwd), DEST_SIZE); 21 | free(pwd); 22 | 23 | return RET_OK; 24 | } 25 | -------------------------------------------------------------------------------- /src/info/shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef __linux__ 5 | #include 6 | #include 7 | #endif // __linux__ 8 | 9 | #include "info.h" 10 | #include "../config/config.h" 11 | #include "../utils/wrappers.h" 12 | 13 | // get the parent process name (usually the shell) 14 | int shell(char *dest) { 15 | #ifdef __linux__ 16 | char path[32]; 17 | 18 | sprintf(path, "/proc/%d/cmdline", getppid()); 19 | 20 | FILE *fp = fopen(path, "r"); 21 | if(fp) { 22 | char shell[DEST_SIZE]; 23 | shell[fread(shell, 1, DEST_SIZE - 1, fp)] = 0; 24 | fclose(fp); 25 | 26 | if(shell[0] == '-') { // cmdline is "-bash" when login shell 27 | safeStrncpy(dest, _shell_path ? shell + 1 : basename(shell + 1), DEST_SIZE); 28 | return RET_OK; 29 | } 30 | 31 | safeStrncpy(dest, _shell_path ? shell : basename(shell), DEST_SIZE); 32 | return RET_OK; 33 | } 34 | #endif 35 | 36 | char *shell = getenv("SHELL"); 37 | if(shell == NULL) 38 | return ERR_NO_INFO; 39 | if(shell[0] == 0) 40 | return ERR_NO_INFO + 0x10; 41 | 42 | safeStrncpy(dest, _shell_path ? shell : basename(shell), DEST_SIZE); 43 | 44 | return RET_OK; 45 | } 46 | -------------------------------------------------------------------------------- /src/info/swap.c: -------------------------------------------------------------------------------- 1 | #include "info.h" 2 | #include "../utils/queue.h" 3 | #include "../config/config.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #ifndef __APPLE__ 11 | #include 12 | #endif // __APPLE__ 13 | 14 | // get used and total swap 15 | int swap(char *dest) { 16 | #ifdef __APPLE__ 17 | // don't really know how to do it and am too lazy to research it, so... 18 | (void)dest; 19 | return ERR_UNSUPPORTED; 20 | #else 21 | struct sysinfo info; 22 | if(sysinfo(&info)) 23 | return ERR_NO_INFO; 24 | 25 | unsigned long totalswap = info.totalswap / 1024; 26 | unsigned long freeswap = info.freeswap / 1024; 27 | 28 | if(totalswap * freeswap == 0) // one or the other 29 | return ERR_NO_INFO; 30 | 31 | unsigned long usedswap = totalswap - freeswap; 32 | 33 | snprintf(dest, DEST_SIZE, "%lu MiB / %lu MiB", usedswap / 1024, totalswap / 1024); 34 | 35 | if(_swap_perc) { 36 | const size_t len = DEST_SIZE - strlen(dest); 37 | char perc[len]; 38 | 39 | snprintf(perc, len, " (%lu%%)", (usedswap * 100) / totalswap); 40 | strcat(dest, perc); 41 | } 42 | #endif 43 | 44 | return RET_OK; 45 | } 46 | -------------------------------------------------------------------------------- /src/info/term.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "info.h" 6 | #include "../config/config.h" 7 | #include "../utils/wrappers.h" 8 | 9 | // get the current terminal 10 | int term(char *dest) { 11 | // TODO: print terminal version (using env variables, parsing --version outputs, ...) 12 | const char *terminal = NULL; 13 | 14 | const char *terminals[][2] = {// {"ENVIRONMENT_VARIABLE", "terminal"}, 15 | {"ALACRITTY_WINDOW_ID", "Alacritty"}, {"KITTY_PID", "Kitty"}, {"VSCODE_INJECTION", "VS Code"}, 16 | {"TERMUX_VERSION", "Termux"}, {"KONSOLE_VERSION", "Konsole"}, {"GNOME_TERMINAL_SCREEN", "GNOME Terminal"}, 17 | {"WT_SESSION", "Windows Terminal"}, {"TERMINATOR_UUID", "Terminator"}}; 18 | 19 | for(size_t i = 0; i < sizeof(terminals) / sizeof(terminals[0]); ++i) 20 | if(getenv(terminals[i][0])) 21 | terminal = terminals[i][1]; 22 | 23 | if(terminal == NULL) { 24 | terminal = getenv("TERM_PROGRAM"); 25 | if(terminal == NULL) 26 | terminal = getenv("TERM"); 27 | if(terminal == NULL) 28 | return ERR_NO_INFO; 29 | 30 | if(strcmp(terminal, "xterm-kitty") == 0) 31 | terminal = "Kitty"; 32 | else if(strcmp(terminal, "alacritty") == 0) 33 | terminal = "Alacritty"; 34 | else if(strcmp(terminal, "Apple_Terminal") == 0) 35 | terminal = "Apple Terminal"; 36 | } 37 | 38 | if(_term_ssh && getenv("SSH_CONNECTION")) 39 | snprintf(dest, DEST_SIZE, "%s (SSH)", terminal); 40 | else 41 | safeStrncpy(dest, terminal, DEST_SIZE); 42 | 43 | return RET_OK; 44 | } 45 | -------------------------------------------------------------------------------- /src/info/uptime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef __APPLE__ 5 | #include "../macos/bsdwrap.h" 6 | #else 7 | #include 8 | #endif // __APPLE__ 9 | 10 | #include "info.h" 11 | #include "../utils/wrappers.h" 12 | 13 | // print the current uptime 14 | int uptime(char *dest) { 15 | #ifdef __APPLE__ 16 | struct timeval boottime; 17 | int error; 18 | long uptime; 19 | error = sysctlWrap(&boottime, sizeof(boottime), CTL_KERN, KERN_BOOTTIME); 20 | 21 | if(error < 0) 22 | return ERR_NO_INFO; 23 | 24 | time_t boot_seconds = boottime.tv_sec; 25 | time_t current_seconds = time(NULL); 26 | 27 | uptime = (long)difftime(current_seconds, boot_seconds); 28 | #else 29 | struct sysinfo info; 30 | if(sysinfo(&info)) 31 | return ERR_NO_INFO; 32 | 33 | const long uptime = info.uptime; 34 | #endif // __APPLE__ 35 | 36 | long days = uptime / 86400; 37 | long hours = (uptime / 3600) - (days * 24); 38 | long mins = (uptime / 60) - (days * 1440) - (hours * 60); 39 | 40 | char result[DEST_SIZE] = ""; 41 | char str[24] = ""; 42 | 43 | if(days) { 44 | snprintf(str, 24, "%ldd%c", days, hours || mins ? ' ' : 0); // print the number of days passed if more than 0 45 | strcat(result, str); 46 | } 47 | if(hours) { 48 | snprintf(str, 24, "%ldh%c", hours, mins ? ' ' : 0); // print the number of days passed if more than 0 49 | strcat(result, str); 50 | } 51 | if(mins) { 52 | snprintf(str, 24, "%ldm", mins); // print the number of minutes passed if more than 0 53 | strcat(result, str); 54 | } else if(uptime < 60) { 55 | snprintf(str, 24, "%lds", uptime); // print the number of seconds passed if less than 60 56 | strcat(result, str); 57 | } 58 | 59 | safeStrncpy(dest, result, DEST_SIZE); 60 | 61 | return RET_OK; 62 | } 63 | -------------------------------------------------------------------------------- /src/info/user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "info.h" 5 | #include "../utils/wrappers.h" 6 | 7 | // print the current user 8 | int user(char *dest) { 9 | struct passwd *pw; 10 | 11 | unsigned uid = geteuid(); 12 | if((int)uid == -1) { 13 | // couldn't get UID 14 | return ERR_NO_INFO; 15 | } 16 | 17 | pw = getpwuid(uid); 18 | 19 | safeStrncpy(dest, pw->pw_name, DEST_SIZE); 20 | 21 | return RET_OK; 22 | } 23 | -------------------------------------------------------------------------------- /src/macos/bsdwrap.c: -------------------------------------------------------------------------------- 1 | #include "bsdwrap.h" 2 | #include "../utils/return.h" 3 | 4 | #ifdef __APPLE__ 5 | 6 | int sysctlWrap(void *out, size_t outsize, int domain, int field) { 7 | int mib[] = {domain, field}; 8 | int error = sysctl(mib, 2, out, &outsize, NULL, 0); 9 | 10 | if(error < 0) 11 | return ERR_NO_INFO; 12 | 13 | return RET_OK; 14 | } 15 | 16 | #endif // __APPLE__ 17 | -------------------------------------------------------------------------------- /src/macos/bsdwrap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __APPLE__ 4 | 5 | #include 6 | #include 7 | 8 | /* 9 | * Header for BSD standard system-querying 10 | * functions (also applicable to macos). 11 | */ 12 | 13 | /* 14 | * This is a wrapper function over the fairly 15 | * cryptic BSD `sysctl` function. 16 | * 17 | * Note that `out` is meant to be casted by the 18 | * user as it can return various types depending on the query. 19 | * If the type is _slightly_ incorrect, the function will fail. 20 | * 21 | * The function returns 0 if the query was successful, 22 | * or -1 if the query failed. 23 | * 24 | * To find the expected values for `domain` and `field`, 25 | * refer to the `sysctl` man pages. 26 | * 27 | * ex. For querying total memory size. 28 | * 29 | * ```c 30 | * uint64_t size; 31 | * size_t len = sizeof(uin64_t); 32 | * sysctlWrap(&size, &len, 33 | * CTL_HW, // Query the hardware domain. 34 | * HW_MEMSIZE); // Get memory size from the hardware domain. 35 | * ``` 36 | */ 37 | 38 | int sysctlWrap(void *out, size_t outsize, int domain, int field); 39 | 40 | #endif // __APPLE__ 41 | -------------------------------------------------------------------------------- /src/macos/macos_gpu_string.m: -------------------------------------------------------------------------------- 1 | #include "macos_infos.h" 2 | 3 | #import // this lets us know when to use deprecated apis 4 | #import 5 | #import 6 | 7 | char *getGpuString(void) { 8 | CFMutableDictionaryRef dict = IOServiceMatching("IOPCIDevice"); 9 | io_iterator_t iter; 10 | int success; 11 | const char *result = NULL; 12 | 13 | #if __OSX_AVAILABLE_STARTING(__MAC_12_0,__IPHONE_NA) 14 | 15 | mach_port_t port = kIOMainPortDefault; 16 | 17 | #else 18 | #pragma clang diagnostic push 19 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 20 | 21 | mach_port_t port = kIOMasterPortDefault; // kIOMasterPortDefault has been deprecated since macOS Monterey 22 | 23 | #pragma clang diagnostic pop 24 | 25 | #endif 26 | 27 | success = IOServiceGetMatchingServices(port, dict, &iter); 28 | if(success != kIOReturnSuccess) 29 | return NULL; 30 | 31 | io_registry_entry_t entry; 32 | 33 | while((entry = IOIteratorNext(iter))) { 34 | CFMutableDictionaryRef services; 35 | success = IORegistryEntryCreateCFProperties(entry, &services, kCFAllocatorDefault, kNilOptions); 36 | if(success != kIOReturnSuccess) { 37 | IOObjectRelease(entry); 38 | continue; 39 | } 40 | 41 | const void *gpu_model = CFDictionaryGetValue(services, @"model"); 42 | if(gpu_model != nil) { 43 | if(CFGetTypeID(gpu_model) == CFDataGetTypeID()) { 44 | NSString *modelName = [[NSString alloc] initWithData: 45 | (NSData *)gpu_model encoding:NSASCIIStringEncoding]; 46 | 47 | result = [modelName cStringUsingEncoding:NSUTF8StringEncoding]; 48 | } 49 | } 50 | 51 | CFRelease(services); 52 | IOObjectRelease(entry); 53 | } 54 | 55 | IOObjectRelease(iter); 56 | 57 | return (char*)result; 58 | } 59 | -------------------------------------------------------------------------------- /src/macos/macos_infos.c: -------------------------------------------------------------------------------- 1 | #include "macos_infos.h" 2 | #include "../utils/return.h" 3 | 4 | /* STATIC HELPERS */ 5 | 6 | #ifdef __APPLE__ 7 | 8 | const static vm_size_t FALLBACK_PAGE_SIZE = 4096; 9 | 10 | static vm_size_t page_size(mach_port_t host) { 11 | vm_size_t page_size; 12 | int error; 13 | 14 | error = host_page_size(host, &page_size); 15 | 16 | return (error < 0) ? page_size : FALLBACK_PAGE_SIZE; 17 | } 18 | 19 | /* 20 | * Original source: 21 | * https://opensource.apple.com/source/system_cmds/system_cmds-496/vm_stat.tproj/vm_stat.c.auto.html 22 | */ 23 | #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 24 | static int get_stats(struct vm_statistics *stat, mach_port_t host) { 25 | int error; 26 | 27 | unsigned count = HOST_VM_INFO_COUNT; 28 | error = host_statistics(host, HOST_VM_INFO, (host_info_t)stat, &count); 29 | 30 | if(error != KERN_SUCCESS) 31 | return error; 32 | 33 | return RET_OK; 34 | } 35 | #else 36 | static int get_stats(struct vm_statistics64 *stat, mach_port_t host) { 37 | int error; 38 | 39 | unsigned count = HOST_VM_INFO64_COUNT; 40 | error = host_statistics64(host, HOST_VM_INFO64, (host_info64_t)stat, &count); 41 | 42 | if(error != KERN_SUCCESS) 43 | return error; 44 | 45 | return RET_OK; 46 | } 47 | #endif 48 | 49 | /* EXPORTS */ 50 | 51 | bytes_t systemMemSize(void) { 52 | uint64_t size; 53 | int error; 54 | 55 | error = sysctlWrap(&size, sizeof(uint64_t), CTL_HW, HW_MEMSIZE); 56 | 57 | // Since no computer should have 0 bytes of memory, 58 | // 0 indicates failure. 59 | if(error < 0) 60 | return RET_OK; 61 | 62 | return size; 63 | } 64 | 65 | bytes_t usedMemSize(void) { 66 | #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 67 | pages_t active, wired, inactive; 68 | mach_port_t host = mach_host_self(); 69 | 70 | struct vm_statistics vm_stat; 71 | if(get_stats(&vm_stat, host) < 0) 72 | return RET_OK; 73 | 74 | active = vm_stat.active_count; 75 | wired = vm_stat.wire_count; 76 | inactive = vm_stat.inactive_count; 77 | 78 | return (active + wired + inactive) * page_size(host); 79 | #else 80 | pages_t internal, wired, compressed; 81 | mach_port_t host = mach_host_self(); 82 | 83 | struct vm_statistics64 vm_stat; 84 | if(get_stats(&vm_stat, host) < 0) 85 | return RET_OK; 86 | 87 | internal = vm_stat.internal_page_count - vm_stat.purgeable_count; 88 | wired = vm_stat.wire_count; 89 | compressed = vm_stat.compressor_page_count; 90 | 91 | return (internal + wired + compressed) * page_size(host); 92 | #endif 93 | } 94 | 95 | #endif // __APPLE__ 96 | -------------------------------------------------------------------------------- /src/macos/macos_infos.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __APPLE__ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "bsdwrap.h" 14 | 15 | typedef uint64_t bytes_t; 16 | typedef uint64_t pages_t; 17 | 18 | char *getGpuString(void); 19 | 20 | /* 21 | * Gets the used memory int 22 | */ 23 | bytes_t usedMemSize(void); 24 | 25 | bytes_t systemMemSize(void); 26 | 27 | #endif // __APPLE__ 28 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "config/config.h" 10 | #include "info/info.h" 11 | #include "utils/logos.h" 12 | #include "utils/queue.h" 13 | #include "utils/return.h" 14 | #include "utils/utils.h" 15 | #include "utils/wrappers.h" 16 | 17 | #ifdef HAVE_VERSION_H 18 | #include "version.h" // generated by meson - defines COMMIT and VERSION 19 | #else 20 | #define VERSION "[UNKNOWN]" 21 | #define COMMIT "[UNKNOWN]" 22 | #endif // HAVE_VERSION_H 23 | 24 | // idk hy but this is sometimes not defined 25 | #ifndef HOST_NAME_MAX 26 | #ifdef _POSIX_HOST_NAME_MAX 27 | #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX 28 | #else 29 | #define HOST_NAME_MAX 255 30 | #endif 31 | #endif 32 | 33 | // Not sure if this makes sense 34 | #ifndef LOGIN_NAME_MAX 35 | #define LOGIN_NAME_MAX HOST_NAME_MAX 36 | #endif 37 | 38 | // TODO: 39 | /* 40 | * print de, shell and terminal versions 41 | * Windows support? *BSD support? 42 | * make ascii dynamically use the available space (aka line by line) 43 | * start using gtk for theme and icons 44 | * display resolution 45 | * storage 46 | * steam installed "packages" (off by default) 47 | * cpu temp (off by default) 48 | * rewrite config parsing and make it decent (check rewrite-config-parsing branch) 49 | * write manpage 50 | * fix writing in the previous block when fread returns 0 51 | */ 52 | 53 | // This contains the default config values 54 | struct SConfig config = { 55 | // Default values for boolean options (least to most significant bit) 56 | // 0111 0101 1111 1110 1111 1001 0110 ... 57 | .boolean_options = 0x69f7fae, 58 | 59 | .logo = NULL, 60 | .color = "", 61 | .dash = ": ", 62 | .separator = "-", 63 | .spacing = 5, 64 | 65 | .gpu_index = 0, 66 | .date_format = "%02d/%02d/%d %02d:%02d:%02d", 67 | .col_block_str = " ", 68 | 69 | .separator_prefix = "", 70 | .spacing_prefix = "", 71 | .title_prefix = "", 72 | .user_prefix = "User", 73 | .hostname_prefix = "Hostname", 74 | .uptime_prefix = "Uptime", 75 | .os_prefix = "OS", 76 | .kernel_prefix = "Kernel", 77 | .desktop_prefix = "Desktop", 78 | .gtk_theme_prefix = "Theme", 79 | .icon_theme_prefix = "Icons", 80 | .cursor_theme_prefix = "Cursor", 81 | .shell_prefix = "Shell", 82 | .login_shell_prefix = "Login", 83 | .term_prefix = "Terminal", 84 | .pkg_prefix = "Packages", 85 | .host_prefix = "Host", 86 | .bios_prefix = "BIOS", 87 | .cpu_prefix = "CPU", 88 | .gpu_prefix = "GPU", 89 | .mem_prefix = "Memory", 90 | .swap_prefix = "Swap", 91 | .pub_prefix = "Public IP", 92 | .loc_prefix = "Local IP", 93 | .pwd_prefix = "Directory", 94 | .date_prefix = "Date", 95 | .bat_prefix = "Battery", 96 | .colors_prefix = "", 97 | .light_colors_prefix = "", 98 | }; 99 | 100 | int main(int argc, char **argv) { 101 | // are the following command line args used? 102 | bool asking_help = false; 103 | bool use_config = true; 104 | bool print_logo = true; 105 | int asking_color = 0; 106 | int asking_bold = 0; 107 | int asking_logo = 0; 108 | int asking_align = 0; 109 | 110 | // these store either the default values or the ones defined in the config 111 | // they are needed to know what is used if no arguments are given (for --help) 112 | bool default_bold = _bold; 113 | char default_color[8] = ""; 114 | char default_logo[16] = ""; 115 | 116 | // the default config file is ~/.config/albafetch.conf 117 | char config_file[LOGIN_NAME_MAX + 64] = ""; 118 | // path of a custom ascii art 119 | char *ascii_file = NULL; 120 | void *ascii_ptr = NULL; 121 | 122 | // parsing the command args 123 | for(int i = 1; i < argc; ++i) { 124 | if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) 125 | asking_help = true; 126 | if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) { 127 | puts(VERSION " (built from commit " COMMIT ")"); 128 | return RET_OK; 129 | } else if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--color") == 0) 130 | asking_color = i + 1; 131 | else if(strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--bold") == 0) 132 | asking_bold = i + 1; 133 | else if(strcmp(argv[i], "--ascii") == 0) { 134 | if(i + 1 >= argc) { // is there such an arg? 135 | fputs("\033[31m\033[1mERROR\033[0m: --ascii requires an extra argument!\n", stderr); 136 | continue; 137 | } 138 | ascii_file = argv[i + 1]; 139 | continue; 140 | } else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--logo") == 0) 141 | asking_logo = i + 1; 142 | else if(strcmp(argv[i], "--align") == 0 || strcmp(argv[i], "-a") == 0) 143 | asking_align = i + 1; 144 | else if(strcmp(argv[i], "--config") == 0 && use_config) { 145 | if(i + 1 >= argc) { // is there such an arg? 146 | fputs("\033[31m\033[1mERROR\033[0m: --config requires an extra argument!\n", stderr); 147 | continue; 148 | } 149 | safeStrncpy(config_file, argv[i + 1], sizeof(config_file)); 150 | continue; 151 | } else if(strcmp(argv[i], "--no-logo") == 0) 152 | print_logo = false; 153 | else if(strcmp(argv[i], "--no-config") == 0) 154 | use_config = false; 155 | } 156 | 157 | char data[DEST_SIZE] = ""; // output of each module 158 | char printed[DEST_SIZE * 4] = ""; // line-by-line output of albafetch 159 | 160 | struct SModule *modules = malloc(sizeof(struct SModule)); 161 | if(modules == NULL) 162 | return ERR_OOM; 163 | modules->id = NULL; 164 | modules->next = NULL; 165 | 166 | // albafetch will first parse ~/.config/albafetch.conf 167 | // ~/.config/albafetch/albafetch.conf if the former is not found 168 | if(use_config) { // --no-config was not used 169 | bool error = true; 170 | 171 | if(config_file[0] == 0) { // --config was not used, using the default path 172 | char *home = getenv("HOME"); 173 | char *config_home = getenv("XDG_CONFIG_HOME"); 174 | error = false; 175 | 176 | if(config_home) { // is XDG_CONFIG_HOME set? 177 | snprintf(config_file, sizeof(config_file), "%s/albafetch.conf", config_home); 178 | if(access(config_file, F_OK)) 179 | snprintf(config_file, sizeof(config_file), "%s/albafetch/albafetch.conf", config_home); 180 | } 181 | if(home && access(config_file, F_OK)) { // is HOME set? 182 | snprintf(config_file, sizeof(config_file), "%s/.config/albafetch.conf", home); 183 | if(access(config_file, F_OK)) 184 | snprintf(config_file, sizeof(config_file), "%s/.config/albafetch/albafetch.conf", home); 185 | } 186 | if(access(config_file, F_OK)) { 187 | strcpy(config_file, "/etc/xdg/albafetch.conf"); 188 | } 189 | } 190 | 191 | parseConfig(error, config_file, modules, &ascii_ptr, &default_bold, default_color, default_logo); 192 | } 193 | 194 | if(ascii_file) { 195 | free(ascii_ptr); 196 | ascii_ptr = fileToLogo(ascii_file); 197 | } 198 | 199 | if(asking_logo) { // --logo was used 200 | if(asking_logo < argc) { 201 | bool found = false; 202 | 203 | // find the matching logo 204 | for(size_t i = 0; i < sizeof(logos) / sizeof(logos[0]); ++i) 205 | if(strcmp(logos[i][0], argv[asking_logo]) == 0) { 206 | config.logo = logos[i]; 207 | found = true; 208 | } 209 | 210 | if(found == false) 211 | fprintf(stderr, "\033[31m\033[1mERROR\033[0m: invalid logo \"%s\"!\n", argv[asking_logo]); 212 | else 213 | strcpy(config.color, config.logo[1]); 214 | } else 215 | fputs("\033[31m\033[1mERROR\033[0m: --logo requires an extra argument!\n", stderr); 216 | } 217 | if(config.logo == NULL) { // get a logo based on the OS (--logo was not used and no logo was set by the config) 218 | #ifdef __APPLE__ 219 | config.logo = logos[1]; 220 | #else 221 | #ifdef __ANDROID__ 222 | config.logo = logos[2]; 223 | #else 224 | config.logo = logos[0]; 225 | FILE *fp = fopen("/etc/os-release", "r"); 226 | 227 | if(fp == NULL) 228 | fp = fopen("/usr/lib/os-release", "r"); 229 | 230 | if(fp != NULL) { 231 | char os_id[48]; 232 | // check with a newline first 233 | readAfterSequence(fp, "\nID", os_id, 48); 234 | if(os_id[0] == 0) { 235 | fseek(fp, 0, SEEK_SET); 236 | readAfterSequence(fp, "ID", os_id, 48); 237 | } 238 | fclose(fp); 239 | 240 | char *end = strchr(os_id, '\n'); 241 | if(end != NULL) 242 | *end = 0; 243 | 244 | // because Arch Linux ARM has to be special for some reason 245 | // edit: fedora asahi too, yay 246 | if(strcmp(os_id, "archarm") == 0) 247 | os_id[5] = 0; 248 | else if(strcmp(os_id, "fedora-asahi-remix") == 0) 249 | os_id[6] = 0; 250 | 251 | // clean up because of some distros randomly using " or ' when they shouldnt be 252 | if(os_id[0] == '\'' || os_id[0] == '"') { 253 | memmove(os_id, os_id + 1, strlen(os_id)); 254 | 255 | end = strchr(os_id, '\''); 256 | if(end == NULL) 257 | end = strchr(os_id, '"'); 258 | 259 | if(end != NULL) 260 | *end = 0; 261 | } 262 | 263 | // find the matching logo 264 | for(size_t i = 0; i < sizeof(logos) / sizeof(*logos); ++i) 265 | if(strcmp(logos[i][0], os_id) == 0) { 266 | config.logo = logos[i]; 267 | break; 268 | } 269 | } 270 | #endif // __ANDROID__ 271 | #endif // __APPLE__ 272 | 273 | strcpy(default_logo, config.logo[0]); 274 | strcpy(config.color, config.logo[1]); 275 | } 276 | 277 | if(asking_color) { 278 | if(asking_color < argc) { 279 | const char *colors[][2] = { 280 | {"black", "\033[30m"}, {"red", "\033[31m"}, {"green", "\033[32m"}, {"yellow", "\033[33m"}, {"blue", "\033[34m"}, 281 | {"purple", "\033[35m"}, {"cyan", "\033[36m"}, {"gray", "\033[90m"}, {"white", "\033[37m"}, 282 | }; 283 | 284 | for(int j = 0; j < 9; ++j) 285 | if(strcmp(argv[asking_color], *colors[j]) == 0) { 286 | strcpy(config.color, colors[j][1]); 287 | goto color_done; 288 | } 289 | 290 | fprintf(stderr, "\033[31m\033[1mERROR\033[0m: invalid color \"%s\"!\n", argv[asking_color]); 291 | } else 292 | fputs("\033[31m\033[1mERROR\033[0m: --color requires an extra argument!\n", stderr); 293 | 294 | color_done:; 295 | } 296 | 297 | if(asking_bold) { 298 | if(asking_bold < argc) { 299 | // modifying the 2nd least significant bit of boolean_options 300 | if(strcmp(argv[asking_bold], "on") == 0) { 301 | config.boolean_options |= ((uint64_t)1 << 1); 302 | goto bold_done; 303 | } else if(strcmp(argv[asking_bold], "off") == 0) { 304 | config.boolean_options &= ~((uint64_t)1 << 1); 305 | goto bold_done; 306 | } 307 | 308 | fputs("\033[31m\033[1mERROR\033[0m: --bold should be followed by either \"on\" or \"off\"!\n", stderr); 309 | } else 310 | fputs("\033[31m\033[1mERROR\033[0m: --bold requires an extra argument!\n", stderr); 311 | 312 | bold_done:; 313 | } 314 | 315 | if(asking_help) { 316 | // it won't be used anyway lol 317 | destroyArray(modules); 318 | 319 | printf("%s%salbafetch\033[0m - a system fetch utility\n", config.color, _bold ? "\033[1m" : ""); 320 | 321 | printf("\n%s%sFLAGS\033[0m:\n", config.color, _bold ? "\033[1m" : ""); 322 | 323 | printf("\t%s%s-h\033[0m,%s%s --help\033[0m:\t Print this help menu and exit\n", config.color, _bold ? "\033[1m" : "", config.color, _bold ? "\033[1m" : ""); 324 | 325 | printf("\t%s%s-v\033[0m,%s%s --version\033[0m:\t Print the version and exit\n", config.color, _bold ? "\033[1m" : "", config.color, _bold ? "\033[1m" : ""); 326 | 327 | printf("\t%s%s-c\033[0m,%s%s --color\033[0m:\t Change the output color (%s%s\033[0m)\n" 328 | "\t\t\t [\033[30mblack\033[0m, \033[31mred\033[0m, \033[32mgreen\033[0m, \033[33myellow\033[0m," 329 | " \033[34mblue\033[0m, \033[35mpurple\033[0m, \033[36mcyan\033[0m, \033[90mgray\033[0m," 330 | " \033[37mwhite\033[0m]\n", 331 | config.color, _bold ? "\033[1m" : "", config.color, _bold ? "\033[1m" : "", default_color[0] ? default_color : config.logo[1], 332 | default_color[0] ? "default" : "logo default"); 333 | 334 | printf("\t%s%s-b\033[0m,%s%s --bold\033[0m:\t Specifies if bold should be used in colored parts (default: %s\033[0m)\n" 335 | "\t\t\t [\033[1mon\033[0m, off]\n", 336 | config.color, _bold ? "\033[1m" : "", config.color, _bold ? "\033[1m" : "", default_bold ? "\033[1mon" : "off"); 337 | 338 | printf("\t%s%s-l\033[0m,%s%s --logo\033[0m:\t Changes the logo that will be displayed (default: %s)\n" 339 | "\t\t\t [alpine, android, apple, arch, arch_small, debian, endeavouros, fedora, gentoo]\n" 340 | "\t\t\t [linux, linuxmint, mageia, manjaro, neon, nixos, none, parrot, pop, ubuntu, windows]\n", 341 | config.color, _bold ? "\033[1m" : "", config.color, _bold ? "\033[1m" : "", default_logo[0] ? default_logo : "OS Default"); 342 | 343 | printf("\t%s%s--ascii\033[0m:\t Specifies a file containing a custom ascii art to use as logo (default: none)\n" 344 | "\t\t\t [path]\n", 345 | config.color, _bold ? "\033[1m" : ""); 346 | 347 | printf("\t%s%s-a\033[0m, %s%s--align\033[0m:\t Aligns the infos if set (default: %s)\n" 348 | "\t\t\t [on, off]\n", 349 | config.color, _bold ? "\033[1m" : "", config.color, _bold ? "\033[1m" : "", _align ? "on" : "off"); 350 | 351 | printf("\t%s%s--config\033[0m:\t Specifies a custom config (default: ~/.config/albafetch.conf)\n" 352 | "\t\t\t [path]\n", 353 | config.color, _bold ? "\033[1m" : ""); 354 | 355 | printf("\t%s%s--no-logo\033[0m:\t Prints the infos without a logo or ascii art\n", config.color, _bold ? "\033[1m" : ""); 356 | 357 | printf("\t%s%s--no-config\033[0m:\t Ignores any provided or existing config file\n", config.color, _bold ? "\033[1m" : ""); 358 | 359 | printf("\nReport a bug: %s%s\033[4mhttps://github.com/alba4k/albafetch/issues\033[0m\n", config.color, _bold ? "\033[1m" : ""); 360 | 361 | return RET_OK; 362 | } 363 | 364 | if(asking_align) { 365 | if(asking_align < argc) { 366 | if(strcmp(argv[asking_align], "on") == 0) { 367 | config.boolean_options |= (uint64_t)1; 368 | goto align_done; 369 | } else if(strcmp(argv[asking_align], "off") == 0) { 370 | config.boolean_options &= ~((uint64_t)1); 371 | goto align_done; 372 | } 373 | 374 | fputs("\033[31m\033[1mERROR\033[0m: --align should be followed by either \"on\" or \"off\"!\n", stderr); 375 | } else 376 | fputs("\033[31m\033[1mERROR\033[0m: --align requires an extra argument!\n", stderr); 377 | 378 | align_done:; 379 | } 380 | 381 | // I am deeply sorry for the code you're about to see - I hope you like spaghetti 382 | unsigned line = 1; 383 | char format[32] = "%s\033[0m%s"; 384 | 385 | /* getting the terminal width 386 | * I start by using stdout 387 | * if it is not a terminal (e.g. if the user did albafetch | lolcat) I use stderr 388 | * if stderr doesn't work either, I use stdin (albafetch 2>/dev/null | lolcat) 389 | * now, let's assume the user is a complete moron and redirects everything. Then I just use an "infinite" width 390 | */ 391 | struct winsize win; 392 | win.ws_col = 0; 393 | if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &win)) 394 | if(ioctl(STDERR_FILENO, TIOCGWINSZ, &win)) 395 | ioctl(STDIN_FILENO, TIOCGWINSZ, &win); 396 | if(win.ws_col == 0) 397 | win.ws_col = -1; 398 | 399 | struct SInfo { 400 | char *id; // module identifier 401 | char *label; // module label 402 | int (*func)(char *); // function to run 403 | }; 404 | struct SInfo module_table[] = { 405 | // {"identifier", label, func}, 406 | {"separator", config.separator_prefix, NULL}, 407 | {"space", config.spacing_prefix, NULL}, 408 | {"title", config.title_prefix, NULL}, 409 | {"user", config.user_prefix, user}, 410 | {"hostname", config.hostname_prefix, hostname}, 411 | {"uptime", config.uptime_prefix, uptime}, 412 | {"os", config.os_prefix, os}, 413 | {"kernel", config.kernel_prefix, kernel}, 414 | {"desktop", config.desktop_prefix, desktop}, 415 | {"gtk_theme", config.gtk_theme_prefix, gtkTheme}, 416 | {"icon_theme", config.icon_theme_prefix, iconTheme}, 417 | {"cursor_theme", config.cursor_theme_prefix, cursorTheme}, 418 | {"shell", config.shell_prefix, shell}, 419 | {"login_shell", config.login_shell_prefix, loginShell}, 420 | {"term", config.term_prefix, term}, 421 | {"packages", config.pkg_prefix, packages}, 422 | {"host", config.host_prefix, host}, 423 | {"bios", config.bios_prefix, bios}, 424 | {"cpu", config.cpu_prefix, cpu}, 425 | {"gpu", config.gpu_prefix, gpu}, 426 | {"memory", config.mem_prefix, memory}, 427 | {"swap", config.swap_prefix, swap}, 428 | {"public_ip", config.pub_prefix, publicIp}, 429 | {"local_ip", config.loc_prefix, localIp}, 430 | {"pwd", config.pwd_prefix, pwd}, 431 | {"date", config.date_prefix, date}, 432 | {"battery", config.bat_prefix, battery}, 433 | {"colors", config.colors_prefix, colors}, 434 | {"light_colors", config.light_colors_prefix, lightColors}, 435 | }; 436 | 437 | // this sets the default module order in case it was not set in a config file 438 | if(modules->next == NULL) { 439 | char *default_order[] = { 440 | "title", "separator", "uptime", "separator", "os", "kernel", "desktop", "shell", "term", 441 | "packages", "separator", "host", "cpu", "gpu", "memory", "space", "colors", "light_colors", 442 | }; 443 | 444 | for(size_t i = 0; i < sizeof(default_order) / sizeof(default_order[0]); ++i) 445 | addModule(modules, default_order[i]); 446 | } 447 | 448 | // filling in the linked list (with function pointers and labels) based on the ids 449 | // I prefer doing it here than in addModule as this part only runs when it's needed 450 | for(struct SModule *current = modules->next; current; current = current->next) 451 | for(size_t i = 0; i < sizeof(module_table) / sizeof(module_table[0]); ++i) 452 | if(strcmp(module_table[i].id, current->id) == 0) { 453 | current->label = module_table[i].label; 454 | current->func = module_table[i].func; 455 | } 456 | 457 | if(_align) { 458 | size_t current_len; 459 | asking_align = 0; 460 | 461 | // determining how far the text should be aligned 462 | for(struct SModule *current = modules->next; current; current = current->next) { 463 | current_len = realStrlen(current->label); 464 | 465 | if(current_len > (size_t)asking_align) 466 | asking_align = (int)current_len; 467 | } 468 | 469 | asking_align += (int)realStrlen(config.dash); 470 | 471 | snprintf(format, 32, "%%-%ds\033[0m%%s", asking_align); 472 | } 473 | 474 | // printing every module 475 | for(struct SModule *current = modules->next; current; current = current->next) { 476 | if(strcmp(current->id, "separator") == 0) { // separators are handled differently 477 | if(printed[0] == 0) // first thing being printed 478 | continue; 479 | 480 | // this is the length of the last printed text 481 | const size_t len = realStrlen(printed) - realStrlen(config.separator_prefix) - (print_logo ? realStrlen(config.logo[2]) + config.spacing : 0); 482 | 483 | printed[0] = 0; 484 | 485 | if(print_logo) { 486 | getLogoLine(printed, &line); 487 | 488 | for(int i = 0; i < config.spacing; ++i) 489 | strcat(printed, " "); 490 | } 491 | 492 | strcat(printed, config.color); 493 | strcat(printed, current->label); 494 | 495 | const size_t separator_len = strlen(config.separator); 496 | for(size_t i = 0; i < len && strlen(printed) < 1023 - separator_len * i; ++i) 497 | strcat(printed, config.separator); 498 | } else if(strcmp(current->id, "space") == 0) { // spacings are handled differently (they don't do shit) 499 | printed[0] = 0; 500 | 501 | if(print_logo) { 502 | getLogoLine(printed, &line); 503 | 504 | for(int i = 0; i < config.spacing; ++i) 505 | strcat(printed, " "); 506 | } 507 | 508 | strcat(printed, config.color); 509 | strcat(printed, current->label); 510 | } else if(strcmp(current->id, "title") == 0) { // titles are handled differently 511 | char name[DEST_SIZE]; 512 | char host[DEST_SIZE]; 513 | 514 | if(user(name) || hostname(host)) 515 | continue; 516 | 517 | printed[0] = 0; 518 | 519 | if(print_logo) { 520 | getLogoLine(printed, &line); 521 | 522 | for(int i = 0; i < config.spacing; ++i) 523 | strcat(printed, " "); 524 | } 525 | 526 | strcat(printed, config.color); 527 | strcat(printed, current->label); 528 | 529 | if(_title_color) 530 | snprintf(printed + strlen(printed), 1024 - strlen(printed), "%s%s%s%s@%s%s%s", config.color, _bold ? "\033[1m" : "", name, "\033[0m", _bold ? "\033[1m" : "", 531 | config.color, host); 532 | else 533 | snprintf(printed + strlen(printed), 1024 - strlen(printed), "%s%s@%s", "\033[0m", name, host); 534 | } else if(current->func == NULL) { // printing a custom text 535 | printed[0] = 0; 536 | 537 | if(print_logo) { 538 | getLogoLine(printed, &line); 539 | 540 | for(int i = 0; i < config.spacing; ++i) 541 | strcat(printed, " "); 542 | } 543 | 544 | strcat(printed, config.color); 545 | strncat(printed, current->id, 1023 - strlen(printed)); 546 | } else { 547 | int ret = current->func(data); 548 | if(ret != RET_OK) { 549 | if(ret == ERR_OOM) 550 | return ERR_OOM; 551 | continue; 552 | } 553 | 554 | char label[80]; 555 | printed[0] = 0; 556 | 557 | if(print_logo) { 558 | getLogoLine(printed, &line); 559 | 560 | for(int i = 0; i < config.spacing; ++i) 561 | strcat(printed, " "); 562 | } 563 | 564 | strcat(printed, config.color); 565 | strcpy(label, current->label); 566 | if(current->label[0] && current->func != colors && current->func != lightColors) 567 | strcat(label, config.dash); 568 | 569 | snprintf(printed + strlen(printed), 1024 - strlen(printed), format, label, data); 570 | } 571 | 572 | printLine(printed, win.ws_col); 573 | } 574 | 575 | // remaining lines 576 | while(config.logo[line + 1] && print_logo) { 577 | printed[0] = 0; 578 | 579 | getLogoLine(printed, &line); 580 | 581 | printLine(printed, win.ws_col); 582 | } 583 | 584 | // memory clean up 585 | free(ascii_ptr); 586 | destroyArray(modules); 587 | 588 | return RET_OK; 589 | } 590 | -------------------------------------------------------------------------------- /src/optdeps/glib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "optdeps.h" 5 | 6 | #ifdef GLIB_EXISTS 7 | #include 8 | #else 9 | #include 10 | #include 11 | #include 12 | #endif // GLIB_EXISTS 13 | 14 | // use g_find_program_in_path() if glib can be used 15 | bool binaryInPath(const char *binary) { 16 | #ifdef GLIB_EXISTS 17 | // valgrind complains about this, don't really know why 18 | gchar *program = g_find_program_in_path(binary); 19 | 20 | g_free(program); 21 | 22 | if(program == NULL) 23 | return false; 24 | 25 | return true; 26 | #else 27 | char *path_env = getenv("PATH"); 28 | if(path_env == NULL) { 29 | return false; 30 | } 31 | 32 | char *path = strdup(path_env); 33 | if(path == NULL) { 34 | return false; 35 | } 36 | 37 | char *dir = strtok(path, ":"); 38 | while(dir) { 39 | size_t len = strlen(dir) + strlen(binary) + 2; 40 | char full_path[len]; 41 | snprintf(full_path, sizeof(full_path), "%s/%s", dir, binary); 42 | 43 | if(access(full_path, X_OK) == 0) { 44 | free(path); 45 | return true; 46 | } 47 | 48 | dir = strtok(NULL, ":"); 49 | } 50 | 51 | free(path); 52 | return false; 53 | #endif // GLIB_EXISTS 54 | } 55 | -------------------------------------------------------------------------------- /src/optdeps/libpci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "optdeps.h" 5 | #include "../config/config.h" 6 | #include "../info/info.h" 7 | 8 | #ifdef LIBPCI_EXISTS 9 | #include 10 | #else 11 | #include "../utils/utils.h" 12 | #include "../utils/wrappers.h" 13 | #endif // LIBPCI_EXISTS 14 | 15 | #define CLASS0 "VGA compatible controller" 16 | #define CLASS1 "3D controller" 17 | 18 | // use libpci directly if possible, else try with lspci 19 | void getGpus(char **gpus) { 20 | #ifdef LIBPCI_EXISTS 21 | // based on https://github.com/pciutils/pciutils/blob/master/example.c 22 | char device_class[DEST_SIZE]; 23 | static char namebuf[DEST_SIZE * 3]; 24 | struct pci_dev *dev; 25 | struct pci_access *pacc = pci_alloc(); // get the pci_access structure; 26 | pci_init(pacc); // initialize the PCI library 27 | pci_scan_bus(pacc); // we want to get the list of devices 28 | ptrdiff_t i = 0; 29 | 30 | for(dev = pacc->devices; dev; dev = dev->next) { // iterates over all devices 31 | pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); // fill in header info 32 | pci_lookup_name(pacc, device_class, DEST_SIZE, PCI_LOOKUP_CLASS, dev->device_class); 33 | if(strcmp(device_class, CLASS0) == 0 || strcmp(device_class, CLASS1) == 0) { 34 | // look up the full name of the device 35 | if(config.gpu_index == 0) { 36 | gpus[i] = pci_lookup_name(pacc, namebuf + (i * DEST_SIZE), DEST_SIZE, PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id); 37 | 38 | if(i < 2) 39 | ++i; 40 | else 41 | break; 42 | } else { 43 | if(i == config.gpu_index - 1) { 44 | gpus[0] = pci_lookup_name(pacc, namebuf, DEST_SIZE, PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id); 45 | break; 46 | } 47 | if(i < 2) 48 | ++i; 49 | else 50 | break; 51 | } 52 | } 53 | } 54 | 55 | pci_cleanup(pacc); // close everything 56 | #else 57 | char *temp_gpus[] = {NULL, NULL, NULL}; 58 | 59 | char *lspci = malloc(0x2000); 60 | if(lspci == NULL) 61 | return; 62 | char *args[] = {"lspci", "-mm", NULL}; 63 | execCmd(lspci, 0x2000, args); 64 | char *current = lspci; 65 | char *ptr; 66 | 67 | for(int i = 0; i < 3; ++i) { 68 | ptr = strstr(current, CLASS0); 69 | if(ptr == NULL) { 70 | ptr = strstr(current, CLASS1); 71 | if(ptr == NULL) { 72 | break; 73 | } 74 | } 75 | char *current = ptr; 76 | 77 | for(int j = 0; j < 4; ++j) { 78 | current = strchr(current, '"'); 79 | if(current == NULL) 80 | break; 81 | ++current; 82 | 83 | /* class" "manufacturer" "name" 84 | * "manufacturer" "name" 85 | * manufacturer" "name" 86 | * "name" 87 | * name" 88 | */ 89 | } 90 | 91 | ptr = strchr(current, '"'); // name 92 | if(ptr == NULL) 93 | break; 94 | *ptr = 0; 95 | 96 | temp_gpus[i] = current; 97 | current = ptr + 1; 98 | } 99 | 100 | if(config.gpu_index == 0) { 101 | static char gpus_buf[768]; 102 | for(ptrdiff_t i = 0; i < 3 && temp_gpus[i] != NULL; ++i) { 103 | safeStrncpy(gpus_buf + (i * DEST_SIZE), temp_gpus[0], DEST_SIZE); 104 | 105 | gpus[0] = gpus_buf + (i * DEST_SIZE); 106 | } 107 | } else if(temp_gpus[config.gpu_index - 1] != NULL) { 108 | static char gpu_buf[DEST_SIZE]; 109 | safeStrncpy(gpu_buf, temp_gpus[config.gpu_index - 1], DEST_SIZE); 110 | 111 | gpus[0] = gpu_buf; 112 | } 113 | 114 | free(lspci); 115 | #endif // LIBPCI_EXISTS 116 | } 117 | -------------------------------------------------------------------------------- /src/optdeps/optdeps.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This whole idea might be really stupid. 3 | * It provides custom implementations of functions if 4 | * the related library (that I would prefer using) is 5 | * not found at compile time. It probably makes no sense 6 | * to do this (as I could probably just always use my own 7 | * implementation), but who cares 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | // can use libc or alternatives 15 | bool binaryInPath(const char *str); 16 | 17 | void getGpus(char **gpus); 18 | -------------------------------------------------------------------------------- /src/utils/logos.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // A lot of these logos come from the following projects: 6 | // - neofetch: https://github.com/dylanaraps/neofetch 7 | // - fastfetch: https://github.com/fastfetch-cli/fastfetch 8 | 9 | static char *logos[][32] = { 10 | // please leave logos[0] to Linux and logos[1] to macOS 11 | { 12 | // Linux - default logo 13 | "linux", // this first line contains the distro ID, taken from /etc/os-release 14 | "\033[90m", // default color for the printed text 15 | " ##### ", // just the logo 16 | " ####### ", " ##\033[37mO\033[90m#\033[37mO\033[90m## ", " #\033[33m#####\033[90m# ", 17 | " ##\033[37m##\033[33m###\033[37m##\033[90m## ", " #\033[37m##########\033[90m## ", " #\033[37m############\033[90m## ", 18 | " #\033[37m############\033[90m### ", " ##\033[90m#\033[37m###########\033[90m##\033[33m# ", "######\033[90m#\033[37m#######\033[90m#\033[33m######", 19 | "#######\033[90m#\033[37m#####\033[90m#\033[33m#######", " #####\033[90m#######\033[33m##### ", 20 | NULL // the logo is terminated by NULL 21 | }, 22 | {// macOS 23 | "apple", 24 | "\033[35m", 25 | "\033[32m 'c. ", 26 | "\033[32m ,xNMM. ", 27 | "\033[32m .OMMMMo ", 28 | "\033[32m OMMM0, ", 29 | "\033[32m .;loddo:' loolloddol;. ", 30 | "\033[32m cKMMMMMMMMMMNWMMMMMMMMMM0: ", 31 | "\033[33m .KMMMMMMMMMMMMMMMMMMMMMMMWd. ", 32 | "\033[33m XMMMMMMMMMMMMMMMMMMMMMMMX. ", 33 | "\033[31m;MMMMMMMMMMMMMMMMMMMMMMMM: ", 34 | "\033[31m:MMMMMMMMMMMMMMMMMMMMMMMM: ", 35 | "\033[31m.MMMMMMMMMMMMMMMMMMMMMMMMX. ", 36 | "\033[31m kMMMMMMMMMMMMMMMMMMMMMMMMWd. ", 37 | " .XMMMMMMMMMMMMMMMMMMMMMMMMMMk", 38 | " .XMMMMMMMMMMMMMMMMMMMMMMMMK.", 39 | "\033[34m kMMMMMMMMMMMMMMMMMMMMMMd ", 40 | "\033[34m ;KMMMMMMMWXXWMMMMMMMk. ", 41 | "\033[34m .cooc,. .,coo:. ", 42 | NULL}, 43 | {// Android 44 | "android", 45 | "\033[32m", 46 | " -o o- ", 47 | " +hydNNNNdyh+ ", 48 | " +mMMMMMMMMMMMMm+ ", 49 | " `dMM\033[37mm:\033[32mNMMMMMMN\033[37m:m\033[32mMMd` ", 50 | " hMMMMMMMMMMMMMMMMMMh ", 51 | " .. yyyyyyyyyyyyyyyyyyyy .. ", 52 | ".mMMm`MMMMMMMMMMMMMMMMMMMM`mMMm.", 53 | ":MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 54 | ":MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 55 | ":MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 56 | ":MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 57 | "-MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM-", 58 | " +yy+ MMMMMMMMMMMMMMMMMMMM +yy+ ", 59 | " mMMMMMMMMMMMMMMMMMMm ", 60 | " `/++MMMMh++hMMMM++/` ", 61 | " MMMMo oMMMM ", 62 | " MMMMo oMMMM ", 63 | " oNMm- -mMNs ", 64 | NULL}, 65 | {// Arch Linux 66 | "arch", 67 | "\033[36m", 68 | " -` ", 69 | " .o+` ", 70 | " `ooo/ ", 71 | " `+oooo: ", 72 | " `+oooooo: ", 73 | " -+oooooo+: ", 74 | " `/:-:++oooo+: ", 75 | " `/++++/+++++++: ", 76 | " `/++++++++++++++: ", 77 | " `/+++ooooooooooooo/` ", 78 | " ./ooosssso++osssssso+` ", 79 | " .oossssso-````/ossssss+` ", 80 | " -osssssso. :ssssssso. ", 81 | " :osssssss/ osssso+++. ", 82 | " /ossssssss/ +ssssooo/- ", 83 | " `/ossssso+/:- -:/+osssso+- ", 84 | " `+sso+:-` `.-/+oso: ", 85 | "`++:. `-/+/", 86 | ".` `/", 87 | NULL}, 88 | {// Arch Linux (small version) 89 | "arch_small", "\033[36m", " /\\ ", " / \\ ", " /\\ \\ ", " / \\ ", " / ,, \\ ", " / | | -\\ ", "/_-'' ''-_\\", NULL}, 90 | {// Debian 91 | "debian", 92 | "\033[31m", 93 | " _,met$$$$$gg. ", 94 | " ,g$$$$$$$$$$$$$$$P. ", 95 | " ,g$$P\" \"\"\"Y$$.\". ", 96 | " ,$$P' `$$$. ", 97 | "',$$P ,ggs. `$$b: ", 98 | "`d$$' ,$P\"' \033[37m.\033[31m $$$ ", 99 | " $$P d$' \033[37m,\033[31m $$P ", 100 | " $$: $$. \033[37m-\033[31m ,d$$' ", 101 | " $$; Y$b._ _,d$P' ", 102 | " Y$$. \033[37m`.\033[31m`\"Y$$$$P\"' ", 103 | " `$$b \033[37m\"-.__\033[31m ", 104 | " `Y$$ ", 105 | " `Y$$. ", 106 | " `$$b. ", 107 | " `Y$$b. ", 108 | " `\"Y$b._ ", 109 | " `\"\"\" ", 110 | NULL}, 111 | {// Linux Mint 112 | "linuxmint", 113 | "\033[32m", 114 | "\033[37m ...-:::::-... ", 115 | "\033[37m .-MMMMMMMMMMMMMMM-. ", 116 | "\033[37m .-MMMM\033[32m`..-:::::::-..`\033[37mMMMM-. ", 117 | "\033[37m .:MMMM\033[32m.:MMMMMMMMMMMMMMM:.\033[37mMMMM:. ", 118 | "\033[37m -MMM\033[32m-M---MMMMMMMMMMMMMMMMMMM.\033[37mMMM- ", 119 | "\033[37m `:MMM\033[32m:MM` :MMMM:....::-...-MMMM:\033[37mMMM:` ", 120 | "\033[37m :MMM\033[32m:MMM` :MM:` `` `` `:MMM:\033[37mMMM: ", 121 | "\033[37m.MMM\033[32m.MMMM` :MM. -MM. .MM- `MMMM.\033[37mMMM.", 122 | "\033[37m:MMM\033[32m:MMMM` :MM. -MM- .MM: `MMMM-\033[37mMMM:", 123 | "\033[37m:MMM\033[32m:MMMM` :MM. -MM- .MM: `MMMM:\033[37mMMM:", 124 | "\033[37m:MMM\033[32m:MMMM` :MM. -MM- .MM: `MMMM-\033[37mMMM:", 125 | "\033[37m.MMM\033[32m.MMMM` :MM:--:MM:--:MM: `MMMM.\033[37mMMM.", 126 | "\033[37m :MMM\033[32m:MMM- `-MMMMMMMMMMMM-` -MMM-\033[37mMMM: ", 127 | "\033[37m :MMM\033[32m:MMM:` `:MMM:\033[37mMMM: ", 128 | "\033[37m .MMM\033[32m.MMMM:--------------:MMMM.\033[37mMMM. ", 129 | "\033[37m '-MMMM\033[32m.-MMMMMMMMMMMMMMM-.\033[37mMMMM-' ", 130 | "\033[37m '.-MMMM\033[32m``--:::::--``\033[37mMMMM-.' ", 131 | "\033[37m '-MMMMMMMMMMMMM-' ", 132 | "\033[37m ``-:::::-`` ", 133 | NULL}, 134 | {// Endeavour OS 135 | "endeavouros", "\033[35m", "\033[31m ./\033[35mo\033[34m. ", "\033[31m ./\033[35msssso\033[34m- ", 136 | "\033[31m `:\033[35mosssssss+\033[34m- ", "\033[31m `:+\033[35msssssssssso\033[34m/. ", 137 | "\033[31m `-/o\033[35mssssssssssssso\033[34m/. ", "\033[31m `-/+\033[35msssssssssssssssso\033[34m+:` ", 138 | "\033[31m `-:/+\033[35msssssssssssssssssso«\033[34m+/. ", "\033[31m `.://\033[35mosssssssssssssssssssso\033[34m++- ", 139 | "\033[31m .://+\033[35mssssssssssssssssssssssso\033[34m++: ", "\033[31m .:///\033[35mossssssssssssssssssssssssso\033[34m++: ", 140 | "\033[31m `:////\033[35mssssssssssssssssssssssssssso\033[34m+++.", "\033[31m`-////+\033[35mssssssssssssssssssssssssssso\033[34m++++-", 141 | "\033[31m `..-+\033[35moosssssssssssssssssssssssso\033[34m+++++/`", "\033[34m ./++++++++++++++++++++++++++++++/:. ", "\033[34m `:::::::::::::::::::::::::------`` ", 142 | NULL}, 143 | {// Ubuntu 144 | "ubuntu", 145 | "\033[31m", 146 | " .-/+oossssoo+/-. ", 147 | " `:+ssssssssssssssssss+:` ", 148 | " -+ssssssssssssssssssyyssss+- ", 149 | " .ossssssssssssssssss\033[37mdMMMNy\033[31msssso. ", 150 | " /sssssssssss\033[37mhdmmNNmmyNMMMMh\033[31mssssss/ ", 151 | " +sssssssss\033[37mhm\033[31myd\033[37mMMMMMMMNddddy\033[31mssssssss+ ", 152 | " /ssssssss\033[37mhNMMM\033[31myh\033[37mhyyyyhmNMMMNh\033[31mssssssss/ ", 153 | ".ssssssss\033[37mdMMMNh\033[31mssssssssss\033[37mhNMMMd\033[31mssssssss.", 154 | "+ssss\033[37mhhhyNMMNy\033[31mssssssssssss\033[37myNMMMy\033[31msssssss+", 155 | "oss\033[37myNMMMNyMMh\033[31mssssssssssssss\033[37mhmmmh\033[31mssssssso", 156 | "oss\033[37myNMMMNyMMh\033[31msssssssssssssshmmmhssssssso", 157 | "+ssss\033[37mhhhyNMMNy\033[31mssssssssssss\033[37myNMMMy\033[31msssssss+", 158 | ".ssssssss\033[37mdMMMNh\033[31mssssssssss\033[37mhNMMMd\033[31mssssssss.", 159 | " /ssssssss\033[37mhNMMM\033[31myh\033[37mhyyyyhdNMMMNh\033[31mssssssss/ ", 160 | " +sssssssss\033[37mdm\033[31myd\033[37mMMMMMMMMddddy\033[31mssssssss+ ", 161 | " /sssssssssss\033[37mhdmNNNNmyNMMMMh\033[31mssssss/ ", 162 | " .ossssssssssssssssss\033[37mdMMMNy\033[31msssso. ", 163 | " -+sssssssssssssssss\033[37myyy\033[31mssss+- ", 164 | " `:+ssssssssssssssssss+:` ", 165 | " .-/+oossssoo+/-. ", 166 | NULL}, 167 | {// Parrot OS 168 | "parrot", 169 | "\033[36m", 170 | " `:oho/-` ", 171 | "`mMMMMMMMMMMMNmmdhy- ", 172 | " dMMMMMMMMMMMMMMMMMMs` ", 173 | " +MMsohNMMMMMMMMMMMMMm/ ", 174 | " .My .+dMMMMMMMMMMMMMh. ", 175 | " + :NMMMMMMMMMMMMNo ", 176 | " `yMMMMMMMMMMMMMm: ", 177 | " /NMMMMMMMMMMMMMy` ", 178 | " .hMMMMMMMMMMMMMN+ ", 179 | " ``-NMMMMMMMMMd- ", 180 | " /MMMMMMMMMMMs` ", 181 | " mMMMMMMMsyNMN/ ", 182 | " +MMMMMMMo :sNh. ", 183 | " `NMMMMMMm -o/", 184 | " oMMMMMMM. ", 185 | " `NMMMMMM+ ", 186 | " +MMd/NMh ", 187 | " mMm -mN` ", 188 | " /MM `h: ", 189 | " dM` . ", 190 | " :M- ", 191 | " d: ", 192 | " -+ ", 193 | " - ", 194 | NULL}, 195 | {// Mageia 196 | "mageia", 197 | "\033[36m", 198 | " .°°. ", 199 | " °° .°°. ", 200 | " .°°°. °° ", 201 | " . . ", 202 | " °°° .°°°. ", 203 | " .°°°. '___' ", 204 | " .'___' . ", 205 | "\033[37m :dkxc;'. ..,cxkd; ", 206 | "\033[37m .dkk. kkkkkkkkkk .kkd. ", 207 | "\033[37m.dkk. ';cloolc;. .kkd ", 208 | "\033[37mckk. .kk;", 209 | "\033[37mxO: cOd", 210 | "\033[37mxO: lOd", 211 | "\033[37mlOO. .OO:", 212 | "\033[37m.k00. .00x ", 213 | "\033[37m .k00; ;00O. ", 214 | "\033[37m .lO0Kc;,,,,,,;c0KOc. ", 215 | "\033[37m ;d00KKKKKK00d; ", 216 | "\033[37m .,KKKK,. ", 217 | NULL}, 218 | {// Manjaro Linux 219 | "manjaro", "\033[32m", "██████████████████ ████████", "██████████████████ ████████", "██████████████████ ████████", "██████████████████ ████████", 220 | "████████ ████████", "████████ ████████ ████████", "████████ ████████ ████████", "████████ ████████ ████████", "████████ ████████ ████████", 221 | "████████ ████████ ████████", "████████ ████████ ████████", "████████ ████████ ████████", "████████ ████████ ████████", "████████ ████████ ████████", NULL}, 222 | {// Fedora 223 | "fedora", 224 | "\033[34m", 225 | " /:-------------:\\ ", 226 | " :-------------------:: ", 227 | " :-----------\033[39m/shhOHbmp\033[34m---:\\ ", 228 | " /-----------\033[39momMMMNNNMMD\033[34m] ---: ", 229 | " :-----------\033[39msMMMMNMNMP\033[34m. ---: ", 230 | " :-----------\033[39m:MMMdP\033[34m------- ---\\", 231 | ",------------\033[39m:MMMd\033[34m-------- ---:", 232 | ":------------\033[39m:MMMd\033[34m------- .---:", 233 | ":---- \033[39moNMMMMMMMMMNho\033[34m .----:", 234 | ":-- .\033[39m+shhhMMMmhhy++\033[34m .------/", 235 | ":- -------\033[39m:MMMd\033[34m--------------: ", 236 | ":- --------\033[39m/MMMd\033[34m-------------; ", 237 | ":- ------\033[39m/hMMMy\033[34m------------: ", 238 | ":-- \033[39m:dMNdhhdNMMNo\033[34m------------; ", 239 | ":---\033[39m:sdNMMMMNds:\033[34m------------: ", 240 | ":------\033[39m:://:\033[34m-------------:: ", 241 | ":---------------------:// ", 242 | NULL}, 243 | {// Rocky Linux 244 | "rocky", 245 | "\033[32m", 246 | " __wgliliiligw_, ", 247 | " _williiiiiiliilililw, ", 248 | " _%iiiiiilililiiiiiiiiiii_ ", 249 | " .Qliiiililiiiiiiililililiilm. ", 250 | " _iiiiiliiiiiililiiiiiiiiiiliil, ", 251 | " .lililiiilililiiiilililililiiiii, ", 252 | "_liiiiiiliiiiiiiliiiiiF{iiiiiilili,", 253 | "jliililiiilililiiili@` ~ililiiiiiL", 254 | "iiiliiiiliiiiiiili>` ~liililii", 255 | "liliiiliiilililii` -9liiiil", 256 | "iiiiiliiliiiiii~ \"4lili", 257 | "4ililiiiiilil~| -w, )4lf", 258 | "-liiiiililiF' _liig, )'", 259 | " )iiiliii@` _QIililig, ", 260 | " )iiii>` .Qliliiiililw ", 261 | " )<>~ .mliiiiiliiiiiil, ", 262 | " _gllilililiililii~ ", 263 | " giliiiiiiiiiiiiT` ", 264 | " -^~$ililili@~~' ", 265 | NULL}, 266 | {// KDE Neon 267 | "neon", 268 | "\033[32m", 269 | " `..---+/---..` ", 270 | " `---.`` `` `.---.` ", 271 | " .--.` `` `-:-. ", 272 | " `:/: `.----//----.` :/- ", 273 | " .:. `---` `--.` .:` ", 274 | " .:` `--` .:- `:. ", 275 | " `/ `:. `.-::-.` -:` `/` ", 276 | " /. /. `:++++++++:` .: .: ", 277 | "`/ .: `+++++++++++/ /` `+`", 278 | "/+` -- .++++++++++++` :. .+:", 279 | "`/ .: `+++++++++++/ /` `+`", 280 | " /` /. `:++++++++:` .: .: ", 281 | " ./ `:. `.:::-.` -:` `/` ", 282 | " .:` `--` .:- `:. ", 283 | " .:. `---` `--.` .:` ", 284 | " `:/: `.----//----.` :/- ", 285 | " .-:.` `` `-:-. ", 286 | " `---.`` `` `.---.` ", 287 | " `..---+/---..` ", 288 | NULL}, 289 | {// NixOS 290 | "nixos", 291 | "\033[36m", 292 | "\033[34m ::::. \033[36m '::::: ::::' ", 293 | "\033[34m '::::: \033[36m':::::. ::::' ", 294 | "\033[34m ::::: \033[36m'::::.::::: ", 295 | "\033[34m .......:::::..... \033[36m:::::::: ", 296 | "\033[34m ::::::::::::::::::. \033[36m:::::: \033[34m::::. ", 297 | "\033[34m ::::::::::::::::::::: \033[36m:::::. \033[34m.::::' ", 298 | " ..... \033[36m::::' \033[34m:::::' ", 299 | " ::::: \033[36m'::' \033[34m:::::' ", 300 | " ........::::: \033[36m' \033[34m:::::::::::.", 301 | "::::::::::::: \033[34m:::::::::::::", 302 | " ::::::::::: \033[34m.. \033[34m::::: ", 303 | " .::::: \033[34m.::: \033[34m::::: ", 304 | " .::::: \033[34m::::: \033[34m''''' \033[36m..... ", 305 | " ::::: \033[34m':::::. \033[36m......:::::::::::::' ", 306 | " ::: \033[34m::::::. \033[36m':::::::::::::::::' ", 307 | "\033[34m .:::::::: \033[36m':::::::::: ", 308 | "\033[34m .::::''::::. \033[36m'::::. ", 309 | "\033[34m .::::' ::::. \033[36m'::::. ", 310 | "\033[34m .:::: :::: \033[36m'::::. ", 311 | NULL}, 312 | {// Pop!_OS 313 | "pop", 314 | "\033[36m", 315 | " ///////////// ", 316 | " ///////////////////// ", 317 | " ///////\033[39m*767\033[36m//////////////// ", 318 | " //////\033[39m7676767676*\033[36m////////////// ", 319 | " /////\033[39m76767\033[36m//\033[39m7676767\033[36m////////////// ", 320 | " /////\033[39m767676\033[36m///\033[39m*76767\033[36m/////////////// ", 321 | " ///////\033[39m767676\033[36m///\033[39m76767\033[36m].///\033[39m7676*\033[36m///////", 322 | "/////////\033[39m767676\033[36m//\033[39m76767\033[36m///\033[39m767676\033[36m////////", 323 | "//////////\033[39m76767676767\033[36m////\033[39m76767\033[36m/////////", 324 | "///////////\033[39m76767676\033[36m//////\033[39m7676\033[36m//////////", 325 | "////////////,\033[39m7676\033[36m,///////\033[39m767\033[36m///////////", 326 | "/////////////*\033[39m7676\033[36m///////\033[39m76\033[36m////////////", 327 | "///////////////\033[39m7676\033[36m////////////////////", 328 | " ///////////////\033[39m7676\033[36m///\033[39m767\033[36m//////////// ", 329 | " //////////////////////\033[39m'\033[36m//////////// ", 330 | " //////\033[39m.7676767676767676767,\033[36m////// ", 331 | " /////\033[39m767676767676767676767\033[36m///// ", 332 | " /////////////////////////// ", 333 | " ///////////////////// ", 334 | " ///////////// ", 335 | NULL}, 336 | {// Gentoo 337 | "gentoo", 338 | "\033[35m", 339 | " -/oyddmdhs+:. ", 340 | " -o\033[97mdNMMMMMMMMNNmhy+\033[35m-` ", 341 | " -y\033[97mNMMMMMMMMMMMNNNmmdhy\033[35m+- ", 342 | " `o\033[97mmMMMMMMMMMMMMNmdmmmmddhhy\033[35m/` ", 343 | " om\033[97mMMMMMMMMMMMN\033[35mhhyyyo\033[97mhmdddhhhd\033[35mo` ", 344 | ".y\033[97mdMMMMMMMMMMd\033[35mhs++so/s\033[97mmdddhhhhdm\033[35m+` ", 345 | " oy\033[97mhdmNMMMMMMMN\033[35mdyooy\033[97mdmddddhhhhyhN\033[35md.", 346 | " :o\033[97myhhdNNMMMMMMMNNNmmdddhhhhhyym\033[35mMh", 347 | " .:\033[97m+sydNMMMMMNNNmmmdddhhhhhhmM\033[35mmy", 348 | " /m\033[97mMMMMMMNNNmmmdddhhhhhmMNh\033[35ms:", 349 | " `o\033[97mNMMMMMMMNNNmmmddddhhdmMNhs\033[35m+` ", 350 | " `s\033[97mNMMMMMMMMNNNmmmdddddmNMmhs\033[35m/. ", 351 | " /N\033[97mMMMMMMMMNNNNmmmdddmNMNdso\033[35m:` ", 352 | "+M\033[97mMMMMMMNNNNNmmmmdmNMNdso\033[35m/- ", 353 | "yM\033[97mMNNNNNNNmmmmmNNMmhs+/\033[35m-` ", 354 | "/h\033[97mMMNNNNNNNNMNdhs++/\033[35m-` ", 355 | " `/\033[97mohdmmddhys+++/:\033[35m.` ", 356 | " `-//////:--. ", 357 | NULL}, 358 | {// Windows 359 | "windows", "\033[34m", "\033[31m ,.=:!!t3Z3z., ", "\033[31m :tt:::tt333EE3 ", 360 | "\033[31m Et:::ztt33EEEL \033[32m@Ee., .., ", "\033[31m ;tt:::tt333EE7 \033[32m;EEEEEEttttt33# ", "\033[31m :Et:::zt333EEQ. \033[32m$EEEEEttttt33QL ", 361 | "\033[31m it::::tt333EEF \033[32m@EEEEEEttttt33F ", "\033[31m ;3=*^```\"*4EEV \033[32m:EEEEEEttttt33@. ", " ,.=::::!t=., \033[31m` \033[32m@EEEEEEtttz33QF ", 362 | " ;::::::::zt33) \033[32m\"4EEEtttji3P* ", " :t::::::::tt33.\033[33m:Z3z.. \033[32m`` \033[33m,..g. ", " i::::::::zt33F \033[33mAEEEtttt::::ztF ", 363 | " ;:::::::::t33V \033[33m;EEEttttt::::t3 ", " E::::::::zt33L \033[33m@EEEtttt::::z3F ", "{3=*^```\"*4E3) \033[33m;EEEtttt:::::tZ` ", 364 | " `\033[33m \033[33m:EEEEtttt::::z7 ", "\033[33m \"VEzjt:;;z>*` ", NULL}, 365 | {// ElementaryOS 366 | "elementary", 367 | "\033[34m", 368 | "\033[90m eeeeeeeeeeeeeeeee ", 369 | "\033[97m eeeeeeeeeeeeeeeeeeeeeee ", 370 | "\033[97m eeeee eeeeeeeeeeee eeeee ", 371 | "\033[97m eeee eeeee eee eeee ", 372 | "\033[97m eeee eeee eee eeee ", 373 | "\033[97meee eee eee eee ", 374 | "\033[97meee eee eee eee ", 375 | "\033[97mee eee eeee eeee ", 376 | "\033[97mee eee eeeee eeeeee ", 377 | "\033[97mee eee eeeee eeeee ee ", 378 | "\033[97meee eeee eeeeee eeeee eee ", 379 | "\033[97meee eeeeeeeeee eeeeee eee ", 380 | "\033[97m eeeeeeeeeeeeeeeeeeeeeeee eeeee ", 381 | "\033[97m eeeeeeee eeeeeeeeeeee eeee ", 382 | "\033[97m eeeee eeeee ", 383 | "\033[97m eeeeeee eeeeeee ", 384 | "\033[97m eeeeeeeeeeeeeeeee ", 385 | NULL}, 386 | {// Garuda Linux 387 | "garuda", 388 | "\033[31m", 389 | " .%;888:8898898: ", 390 | " x;XxXB%89b8:b8%b88: ", 391 | " .8Xxd 8X:. ", 392 | " .8Xx; 8x:. ", 393 | " .tt8x .d x88; ", 394 | " .@8x8; .db: xx@; ", 395 | " ,tSXX° .bbbbbbbbbbbbbbbbbbbB8x@;", 396 | " .SXxx bBBBBBBBBBBBBBBBBBBBbSBX8;", 397 | " ,888S pd!", 398 | "8X88/ q ", 399 | "8X88/ ", 400 | "GBB. ", 401 | " x%88 d888@8@X@X@X88X@@XX@@X@8@X. ", 402 | " dxXd dB8b8b8B8B08bB88b998888b88x. ", 403 | " dxx8o .@@;. ", 404 | " dx88 .t@x. ", 405 | " d:SS@8ba89aa67a853Sxxad. ", 406 | " .d988999889889899dd. ", 407 | NULL}, 408 | {// Alpine Linux 409 | "alpine", 410 | "\033[34m", 411 | " .hddddddddddddddddddddddh. ", 412 | " :dddddddddddddddddddddddddd: ", 413 | " /dddddddddddddddddddddddddddd/ ", 414 | " +dddddddddddddddddddddddddddddd+ ", 415 | " `sdddddddddddddddddddddddddddddddds` ", 416 | " `ydddddddddddd++hdddddddddddddddddddy` ", 417 | " `ydddddddddddd++hdddddddddddddddddddy` ", 418 | "hdddddddddd+` `+y: .sddddddddddh", 419 | "ddddddddh+` `//` `.` -sddddddddd", 420 | "ddddddh+` `/hddh/` `:s- -sddddddd", 421 | "ddddh+` `/+/dddddh/` `+s- -sddddd", 422 | "ddd+` `/o` :dddddddh/` `oy- .yddd", 423 | "hdddyo+ohddyosdddddddddho+oydddy++ohdddh", 424 | ".hddddddddddddddddddddddddddddddddddddh.", 425 | " `yddddddddddddddddddddddddddddddddddy` ", 426 | " `sdddddddddddddddddddddddddddddddds` ", 427 | " +dddddddddddddddddddddddddddddd+ ", 428 | " /dddddddddddddddddddddddddddd/ ", 429 | " :dddddddddddddddddddddddddd: ", 430 | " .hddddddddddddddddddddddh. ", 431 | NULL}, 432 | {// Empty Logo 433 | "none", "", "", NULL}, 434 | }; 435 | -------------------------------------------------------------------------------- /src/utils/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "queue.h" 6 | #include "../utils/return.h" 7 | 8 | Queue *queueWithSIze(size_t size) { 9 | Queue *q = malloc(sizeof(Queue)); 10 | if(q == NULL) 11 | return NULL; 12 | 13 | size_t byte_size = sizeof(char) * size; 14 | q->data = malloc(byte_size); 15 | if(q->data == NULL) 16 | return NULL; 17 | memset(q->data, 0, byte_size); 18 | 19 | q->alloc_size = size; 20 | q->size = 0; 21 | q->offset = 0; 22 | 23 | return q; 24 | } 25 | 26 | int requeue(Queue *q) { 27 | // If there is no available space, 28 | // requeueing won't do anything. 29 | if(q->size == q->alloc_size) 30 | return ERR_GENERIC; 31 | 32 | size_t used_byte_size = sizeof(char) * q->size; 33 | char buf[q->size]; 34 | 35 | char *relevant_data_start = q->data + q->offset; 36 | memcpy(buf, relevant_data_start, used_byte_size); 37 | 38 | memcpy(q->data, buf, used_byte_size); 39 | q->offset = 0; 40 | 41 | return RET_OK; 42 | } 43 | 44 | int enqueue(Queue *q, char val) { 45 | if(q->size >= q->alloc_size) 46 | return QUEUE_FULL; 47 | 48 | size_t write_index = q->size + q->offset; 49 | 50 | if(write_index >= q->alloc_size) { 51 | // We know that a requeue is possible because we check that the queue is not full. 52 | requeue(q); 53 | write_index = q->size; 54 | } 55 | 56 | q->data[write_index] = val; 57 | q->size++; 58 | 59 | return QUEUE_OK; 60 | } 61 | 62 | int dequeue(Queue *q, char *out) { 63 | // If there is no elements in the array, returns the null character. 64 | if(q->size < 1) 65 | return QUEUE_EMPTY; 66 | 67 | // If the offset is greater than data, 68 | // it points to nothing. 69 | if(q->offset >= q->alloc_size) 70 | return QUEUE_EMPTY; 71 | 72 | char front = q->data[q->offset]; 73 | 74 | q->offset++; // Increment the front. 75 | q->size--; // Decrease the size since we removed a character. 76 | 77 | *out = front; 78 | 79 | return QUEUE_OK; 80 | } 81 | 82 | void destroyQueue(Queue *q) { 83 | free(q->data); 84 | free(q); 85 | } 86 | 87 | void readAfterSequence(FILE *fp, const char *seq, char *buffer, size_t buffer_size) { 88 | size_t seq_size = strlen(seq); 89 | Queue *q = queueWithSIze(3 * seq_size); 90 | if(q == NULL) 91 | return; 92 | int ch; 93 | int error; 94 | bool found = false; 95 | char elem; 96 | 97 | while((ch = fgetc(fp)) != EOF) { 98 | if(q->size < seq_size) { 99 | enqueue(q, ch); 100 | continue; 101 | } 102 | 103 | assert(q->size == seq_size); // Window is of correct width 104 | 105 | if(strncmp(q->data + q->offset, seq, seq_size) == 0) { 106 | found = true; 107 | break; 108 | } 109 | 110 | error = dequeue(q, &elem); 111 | assert(error != QUEUE_EMPTY); // Queue should always have at least `seq_size` items 112 | 113 | error = enqueue(q, ch); 114 | assert(error != QUEUE_FULL); // Queue should maintain same size, as 1 item is added and another is removed 115 | } 116 | 117 | destroyQueue(q); 118 | 119 | if(found == false) { 120 | buffer[0] = 0; // make buffer an empty string if the sequence is not found 121 | } 122 | 123 | // Actually read for the rest of the file, or the buffer. 124 | for(size_t i = 0; i < buffer_size; ++i) { 125 | if((ch = fgetc(fp)) == EOF) 126 | break; 127 | 128 | buffer[i] = ch; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/utils/queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define QUEUE_OK 0 8 | #define QUEUE_EMPTY (-1) 9 | #define QUEUE_FULL (-2) 10 | 11 | typedef struct { 12 | size_t offset; 13 | size_t size; 14 | size_t alloc_size; 15 | char *data; 16 | } Queue; 17 | 18 | Queue *queueWithSIze(size_t size); 19 | 20 | int requeue(Queue *q); 21 | 22 | int enqueue(Queue *q, char val); 23 | 24 | int dequeue(Queue *q, char *out); 25 | 26 | void destroyQueue(Queue *q); 27 | 28 | void readAfterSequence(FILE *fp, const char *seq, char *buffer, size_t buffer_size); 29 | -------------------------------------------------------------------------------- /src/utils/return.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RET_OK 0x00 4 | 5 | #define ERR_USER 0x10 6 | #define ERR_GENERIC 0x11 7 | 8 | #define ERR_UNSUPPORTED 0x20 9 | #define ERR_NO_INFO 0x21 10 | #define ERR_PARSING 0x22 11 | #define ERR_NO_FILE 0x23 12 | #define ERR_OOM 0x24 13 | -------------------------------------------------------------------------------- /src/utils/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "utils.h" 7 | #include "return.h" 8 | #include "wrappers.h" 9 | #include "../config/config.h" 10 | #include "../config/parsing.h" 11 | 12 | // copy an ascii art from file to mem 13 | void *fileToLogo(char *file) { 14 | FILE *fp = fopen(file, "r"); 15 | if(fp == NULL) { 16 | perror(file); 17 | return NULL; 18 | } 19 | 20 | /* 21 | * mem is assumed to be a 10 KiB buffer, aka 10240 B. 22 | * this will be filled in with up to 64 lines, 23 | * each of which can be up to 160 bytes long. 24 | * (LINE_LEN * LINE_COUNT) should equal this size. 25 | * Check out main.c, line 132 26 | */ 27 | #define LINE_LEN 256 28 | #define LINE_COUNT 40 29 | 30 | // where the final logo is saved 31 | static char *logo[LINE_COUNT + 1]; 32 | char *mem = NULL; 33 | 34 | char *buffer = NULL; 35 | size_t len = 0; 36 | size_t line_len; 37 | ptrdiff_t i = 0; 38 | 39 | // setting the correct color (or eventually the first line) 40 | 41 | line_len = getline(&buffer, &len, fp); // save the first line to buffer 42 | if(line_len == (size_t)-1) { // getline returns -1 in case of error 43 | free(buffer); 44 | return NULL; 45 | } 46 | 47 | if(buffer[line_len - 1] == '\n') 48 | buffer[line_len - 1] = 0; 49 | 50 | config.color[0] = 0; 51 | 52 | const char *colors[][2] = { 53 | {"black", "\033[30m"}, {"red", "\033[31m"}, {"green", "\033[32m"}, {"yellow", "\033[33m"}, {"blue", "\033[34m"}, 54 | {"purple", "\033[35m"}, {"cyan", "\033[36m"}, {"gray", "\033[90m"}, {"white", "\033[37m"}, 55 | }; 56 | 57 | for(int j = 0; j < 9; ++j) 58 | if(strcmp(buffer, *colors[j]) == 0) 59 | strcpy(config.color, colors[j][1]); 60 | 61 | if(config.color[0] == 0) { 62 | unescape(buffer); 63 | 64 | mem = malloc(LINE_LEN); 65 | if(mem == NULL) { 66 | perror("malloc"); 67 | return NULL; 68 | } 69 | 70 | safeStrncpy(mem, buffer, LINE_LEN); 71 | 72 | ++i; 73 | } 74 | 75 | // for every remaining line of the logo... 76 | while((line_len = getline(&buffer, &len, fp)) != (size_t)-1 && i < LINE_COUNT) { 77 | if(buffer[line_len - 1] == '\n') 78 | buffer[line_len - 1] = 0; 79 | 80 | unescape(buffer); 81 | 82 | void *newmem = realloc(mem, (i + 1) * LINE_LEN); 83 | if(newmem == NULL) { 84 | free(mem); 85 | perror("realloc"); 86 | return NULL; 87 | } 88 | mem = newmem; 89 | 90 | safeStrncpy(mem + (i * LINE_LEN), buffer, LINE_LEN); 91 | 92 | ++i; 93 | } 94 | // cleaning up 95 | free(buffer); 96 | fclose(fp); 97 | 98 | // set up the logo metadata; 99 | logo[0] = "custom"; // logo ID 100 | 101 | for(ptrdiff_t j = 0; j < i; ++j) 102 | logo[j + 2] = mem + (j * LINE_LEN); 103 | 104 | // the array of lines is NULL-terminated; 105 | logo[i + 2] = NULL; 106 | 107 | // finally, the logo can be saved 108 | config.logo = logo; 109 | 110 | return mem; 111 | } 112 | 113 | // add a module containing id to array 114 | void addModule(struct SModule *array, char *id) { 115 | struct SModule *new = malloc(sizeof(struct SModule)); 116 | if(new == NULL) 117 | return; 118 | struct SModule *last = array; 119 | 120 | for(struct SModule *current = array; current->next; current = current->next) 121 | last = current->next; 122 | 123 | last->next = new; 124 | 125 | new->id = malloc(strlen(id) + 1); 126 | if(new->id == NULL) 127 | return; 128 | strcpy(new->id, id); 129 | 130 | new->label = NULL; 131 | new->func = NULL; 132 | 133 | new->next = NULL; 134 | } 135 | 136 | // free every module in array 137 | void destroyArray(struct SModule *array) { 138 | struct SModule *current = array; 139 | struct SModule *next; 140 | 141 | while(current) { 142 | next = current->next; 143 | free(current->id); 144 | free(current); 145 | current = next; 146 | } 147 | } 148 | 149 | // print a certain line of the logo 150 | void getLogoLine(char *dest, unsigned *line) { 151 | if(config.logo == NULL || dest == NULL || *line < 1) 152 | return; 153 | 154 | if(config.logo[(*line) + 1]) { 155 | ++(*line); 156 | strcat(dest, config.logo[*line]); 157 | } else { 158 | for(size_t i = 0; i < realStrlen(config.logo[2]); ++i) 159 | strcat(dest, " "); 160 | } 161 | } 162 | 163 | // print no more than maxlen visible characters of line 164 | void printLine(char *line, const size_t maxlen) { 165 | if(_bold) 166 | fputs("\033[1m", stdout); 167 | fputs(config.color, stdout); 168 | 169 | bool escaping = false; 170 | int unicode = 0; 171 | 172 | for(size_t i = 0, len = 0; len < maxlen && i < strlen(line); ++i) { 173 | putc(line[i], stdout); 174 | 175 | if(line[i] == '\n') 176 | break; 177 | else if(line[i] == '\033') 178 | escaping = true; 179 | else if(line[i] & 0x80) { // is the 1st bit 1? 180 | if(line[i] & 0x40) { // is the 2nd bit 1? 181 | if(line[i] & 0x20) { // is the 3rd bit 1? 182 | if(line[i] & 0x10) // is the 4th bit 1? 183 | unicode = 3; // first of 4 unicode bytes (0b11110xxx) 184 | else 185 | unicode = 2; // first of 3 unicode bytes (0b1110xxxx) 186 | } else 187 | unicode = 1; // first of 2 unicode bytes (0b110xxxxx) 188 | } else if(unicode-- == 1) // unicode continuation byte (0b10xxxxxx) 189 | ++len; 190 | } else { 191 | // look mom, I just wanted to try to write some branchless code 192 | 193 | // this line is a bit weird 194 | // ++len <=> escaping == 0 195 | len += (size_t)1 - escaping; 196 | 197 | /* m is found and escaping => escaping = 0 198 | * m is found and not escaping => escaping = 0 199 | * m is not found and escaping => escaping = 1 200 | * m is not found and not escaping => escaping = 0 201 | */ 202 | escaping = (line[i] != 'm') && escaping; 203 | } 204 | } 205 | 206 | fputs("\033[0m\n", stdout); 207 | } 208 | -------------------------------------------------------------------------------- /src/utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // element of a module linked list 8 | struct SModule { 9 | char *id; // module identifier 10 | char *label; // module label 11 | int (*func)(char *); // function to run 12 | struct SModule *next; // next module 13 | }; 14 | 15 | void *fileToLogo(char *file); 16 | 17 | void addModule(struct SModule *array, char *id); 18 | 19 | void destroyArray(struct SModule *array); 20 | 21 | void getLogoLine(char *dest, unsigned *line); 22 | 23 | void printLine(char *line, const size_t maxlen); 24 | 25 | void unescape(char *str); 26 | -------------------------------------------------------------------------------- /src/utils/wrappers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "wrappers.h" 7 | #include "return.h" 8 | 9 | // Run a command using execvp and copy the output into buf 10 | int execCmd(char *buf, size_t len, char *const *argv) { 11 | int stderr_pipes[2]; 12 | int stdout_pipes[2]; 13 | 14 | if(pipe(stdout_pipes) != 0 || pipe(stderr_pipes) != 0) 15 | return ERR_GENERIC; 16 | 17 | if(fork() == 0) { 18 | close(stdout_pipes[0]); 19 | close(stderr_pipes[0]); 20 | dup2(stdout_pipes[1], STDOUT_FILENO); 21 | dup2(stderr_pipes[1], STDERR_FILENO); 22 | 23 | execvp(argv[0], argv); 24 | } 25 | 26 | wait(0); 27 | 28 | close(stderr_pipes[0]); 29 | close(stderr_pipes[1]); 30 | 31 | close(stdout_pipes[1]); 32 | buf[read(stdout_pipes[0], buf, len) - 1] = 0; 33 | close(stdout_pipes[0]); 34 | 35 | return RET_OK; 36 | } 37 | 38 | // get the printed length of a string (not how big it is in memory) 39 | __attribute__((pure)) size_t realStrlen(const char *str) { 40 | if(str == NULL) 41 | return RET_OK; 42 | 43 | size_t len = 0; 44 | 45 | bool escaping = false; 46 | 47 | // determine how long the printed string is (same logic as in printLine, utils.c) 48 | while(*str) { 49 | if(*str == '\n') 50 | break; 51 | 52 | // unicode continuation byte 0x10xxxxxx 53 | if(*str & 0x80 && (*str & 0x40) == 0) { 54 | ++str; 55 | continue; 56 | } 57 | 58 | if(*str != '\033') { 59 | len += (size_t)1 - escaping; 60 | 61 | escaping = (*str != 'm') && escaping; 62 | } else 63 | escaping = true; 64 | 65 | ++str; 66 | } 67 | 68 | return len; 69 | } 70 | 71 | // Copy no more than N characters of SRC to DEST, but always guarantee null-termination 72 | void safeStrncpy(char *dest, const char *src, size_t n) { 73 | strncpy(dest, src, n - 1); 74 | dest[n - 1] = 0; 75 | } 76 | -------------------------------------------------------------------------------- /src/utils/wrappers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int execCmd(char *buf, size_t len, char *const *argv); 6 | 7 | size_t realStrlen(const char *str); 8 | 9 | void safeStrncpy(char *dest, const char *src, size_t n); 10 | --------------------------------------------------------------------------------