├── .clang-format ├── .github └── workflows │ ├── build.yml │ ├── format.yml │ └── gen-docs.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── config ├── minimal.lua └── sample.lua ├── man ├── lcfetch.1 └── lcfetch.1.md ├── src ├── include │ ├── lcfetch.h │ └── logos │ │ ├── android.h │ │ ├── arch.h │ │ ├── debian.h │ │ ├── fedora.h │ │ ├── gentoo.h │ │ ├── linux.h │ │ ├── manjaro.h │ │ ├── nixos.h │ │ └── ubuntu.h ├── lcfetch.c └── lib │ ├── cli.c │ ├── lua_api.c │ ├── memory.c │ └── utils.c ├── third-party └── log.c │ ├── LICENSE │ ├── README.md │ └── src │ ├── log.c │ └── log.h └── xmake.lua /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | IndentWidth: 4 4 | ColumnLimit: 120 5 | 6 | # vim: ft=yaml sw=2 ts=2 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "**.md" 7 | - ".gitignore" 8 | 9 | jobs: 10 | linux: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout source code 14 | uses: actions/checkout@v2 15 | with: 16 | path: lcfetch 17 | 18 | - name: Install dependencies 19 | run: | 20 | sudo apt update -q 21 | sudo apt install -y -q build-essential libreadline-dev libx11-dev libxrandr-dev 22 | 23 | - name: Install xmake 24 | uses: xmake-io/github-action-setup-xmake@v1 25 | with: 26 | xmake-version: latest 27 | 28 | - name: Build lcfetch 29 | run: | 30 | cd ./lcfetch 31 | xmake -y 32 | 33 | - name: Upload produced binary 34 | uses: actions/upload-artifact@v2 35 | with: 36 | name: lcfetch-linux-x86_64 37 | path: ./lcfetch/build/linux/x86_64/release/lcfetch 38 | if-no-files-found: error 39 | 40 | # macos: 41 | # runs-on: macos-latest 42 | # steps: 43 | # - name: Checkout source code 44 | # uses: actions/checkout@v2 45 | # with: 46 | # path: lcfetch 47 | 48 | # - name: Install xmake 49 | # uses: xmake-io/github-action-setup-xmake@v1 50 | # with: 51 | # xmake-version: latest 52 | 53 | # # Update xmake repository (in order to have the file that will be cached) 54 | # - name: Update xmake repository 55 | # run: xmake repo --update 56 | 57 | # # Cache xmake dependencies because it's slow as hell in MacOS 58 | # - uses: actions/cache@v2 59 | # with: 60 | # path: /Users/runneradmin/.xmake/packages 61 | # key: ${{ runner.os }}-${{ matrix.arch }}-${{ matrix.mode }}-${{ hashFiles('xmake.lua') }}-${{ hashFiles('/Users/runneradmin/.xmake/xmake.conf') }}-${{ hashFiles('/Users/runneradmin/.xmake/repositories/**') }} 62 | 63 | # - name: Build lcfetch 64 | # run: | 65 | # cd ./lcfetch 66 | # xmake -y 67 | 68 | # - name: Upload produced binary 69 | # uses: actions/upload-artifact@v2 70 | # with: 71 | # name: lcfetch-macosx-x86_64 72 | # path: ./lcfetch/build/macosx/x86_64/release/lcfetch 73 | # if-no-files-found: error 74 | 75 | release: 76 | name: Release to GitHub 77 | if: contains(github.event.head_commit.message, 'release:') && github.repository == 'NTBBloodbath/lcfetch' 78 | needs: [linux] 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v2 82 | with: 83 | repository: ${{ github.repository }} 84 | path: "workspace" 85 | token: ${{ secrets.GITHUB_TOKEN }} 86 | 87 | - uses: actions/download-artifact@v2 88 | with: 89 | name: lcfetch-linux-x86_64 90 | 91 | - name: Compress Linux binaries 92 | run: | 93 | chmod +x lcfetch 94 | tar caf lcfetch-linux-x86_64.tar.xz lcfetch && rm lcfetch 95 | ls -lh 96 | 97 | - name: Setup Release information 98 | run: | 99 | versionName=`sed '11q;d' ./workspace/src/include/lcfetch.h | cut -d "\"" -f2 | xargs` 100 | export VERSION_NAME=$versionName 101 | echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV 102 | 103 | - name: Extract release notes 104 | id: extract-release-notes 105 | uses: ffurrer2/extract-release-notes@v1 106 | with: 107 | changelog_file: ./workspace/CHANGELOG.md 108 | 109 | - name: Create Release 110 | uses: Xotl/cool-github-releases@v1 111 | with: 112 | mode: update 113 | isDraft: false 114 | isPrerelease: false 115 | tag_name: v${{ env.VERSION_NAME }} 116 | release_name: v${{ env.VERSION_NAME }} 117 | assets: lcfetch-x86_64.tar.xz 118 | body_mrkdwn: ${{ steps.extract-release-notes.outputs.release_notes }} 119 | github_token: ${{ secrets.GITHUB_TOKEN }} 120 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: format 2 | 3 | on: 4 | push: 5 | paths: 6 | - "**.c" 7 | - "**.h" 8 | 9 | jobs: 10 | clang-format: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout source code 14 | uses: actions/checkout@v2 15 | with: 16 | path: lcfetch 17 | 18 | - name: Install clang-format 19 | run: | 20 | sudo apt update -q 21 | sudo apt install clang-format -y -q 22 | 23 | - name: Install xmake 24 | uses: xmake-io/github-action-setup-xmake@v1 25 | with: 26 | xmake-version: latest 27 | 28 | - name: Format source code 29 | run: | 30 | cd ./lcfetch 31 | xmake -y 32 | xmake run fmt 33 | 34 | - name: Commit changes 35 | run: | 36 | cd ./lcfetch 37 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 38 | git config --local user.name "github-actions[bot]" 39 | if [[ ! -z $(git status -s) ]]; then 40 | git add . 41 | git commit -m "chore: format source code" 42 | fi 43 | 44 | - name: Push formatted files 45 | uses: ad-m/github-push-action@master 46 | with: 47 | github_token: ${{ secrets.GITHUB_TOKEN }} 48 | branch: ${{ github.ref }} 49 | directory: ./lcfetch 50 | -------------------------------------------------------------------------------- /.github/workflows/gen-docs.yml: -------------------------------------------------------------------------------- 1 | name: generate docs 2 | 3 | on: 4 | push: 5 | paths: 6 | - "**.1.md" 7 | 8 | jobs: 9 | pandoc: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout source code 13 | uses: actions/checkout@v2 14 | with: 15 | path: lcfetch 16 | 17 | - name: Install pandoc 18 | run: | 19 | sudo apt update -q 20 | sudo apt install pandoc 21 | 22 | - name: Install xmake 23 | uses: xmake-io/github-action-setup-xmake@v1 24 | with: 25 | xmake-version: latest 26 | 27 | - name: Convert man pages 28 | run: | 29 | cd ./lcfetch 30 | xmake -y run docs 31 | 32 | - name: Commit man pages 33 | run: | 34 | cd ./lcfetch 35 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 36 | git config --local user.name "github-actions[bot]" 37 | if [[ ! -z $(git status -s) ]]; then 38 | git add . 39 | git commit -m "docs: update man pages" 40 | fi 41 | 42 | - name: Push man pages 43 | uses: ad-m/github-push-action@master 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | branch: ${{ github.ref }} 47 | directory: ./lcfetch 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Produced static libraries 2 | *.a 3 | *.o 4 | 5 | # Xmake cache 6 | .xmake/ 7 | build/ 8 | 9 | # VSCodium cache 10 | .vscode/ 11 | 12 | # MacOS Cache 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.2.0] - 2021-10-15 10 | 11 | ### Added 12 | 13 | - Add `colors_icon` option, now users can use their own color palette themes! 14 | - Add `ghosts` color palette style (_requires a patched font like nerd fonts_) 15 | - Add `custom_ascii_logo` option, set your own ASCII logo! 16 | - Add support for Android devices (Termux) 17 | - Add debugging build targets in Makefile (`make dev` and `make valgrind`) 18 | - Add `USER_FLAGS` variable to Makefile to handle extra preprocessors that are optionals (e.g. `-DUSE_PWD_SHELL`) 19 | - ASCII logos: 20 | - Android 21 | - Manjaro 22 | 23 | ### Changed 24 | 25 | - Shell is now obtained in a more robust and accurate method by default instead of relying in `SHELL` environment variable 26 | - If a field cannot be obtained then print line without field information 27 | 28 | ### Fixed 29 | 30 | - Fix CPU model if `/proc/cpuinfo` does not have a `model name` field 31 | - Fix getting username in some distributions (and WSL) 32 | - Fix ninja bug where the configuration files were ignored 33 | - Logos with special characters were having a weird length 34 | - Proper conditional to get Debian logo and accent 35 | - Do not override lcfetch configuration file when installing 36 | 37 | ## [0.1.2] - 2021-09-19 38 | 39 | ### Fixed 40 | 41 | - Properly detect some logos with case-insensitive conditionals 42 | 43 | ## [0.1.1] - 2021-09-19 44 | 45 | ### Added 46 | 47 | - Add `print_colors` function to handle colors field 48 | 49 | ### Changed 50 | 51 | - Small Lua API stack changes 52 | 53 | ### Fixed 54 | 55 | - Properly detect some logos with `strstr` 56 | 57 | ## [0.1.0] - 2021-09-19 58 | 59 | ### Added 60 | 61 | - Support for WM field 62 | - Support for `separator` configuration option 63 | - Support for `accent_color` configuration option 64 | 65 | ### Changed 66 | 67 | - Increased buffer size to 256 68 | - `user@hostname` and `separator` (`----------`) are now fields that can be enabled or disabled in your configurations 69 | - Use `-Ofast` instead of `-O2` when building to improve performance 70 | - Use `` header for boolean values instead of `0` and `1` (should improve code readability) 71 | - Refactor printing logic with a faster implementation 72 | - Use `getenv` for getting the username (fixes issues on WSL) 73 | - Use `log_fatal` instead of `log_error` when we were unable to open system files 74 | - Memory field small visual tweaks 75 | - Changed Ubuntu logo detection 76 | - Changed Fedora logo detection to also detect incoming F35 changes to `/etc/os-release` file 77 | 78 | ### Fixed 79 | 80 | - Small bugs on Lua API stack 81 | - Properly exit on errors (ERROR and FATAL logs) 82 | - Proper conditional for Nix packages 83 | - Proper terminal detection on WSL (only WT at the moment) 84 | 85 | ## [0.1.0-beta6] - 2021-08-25 86 | 87 | ### Added 88 | 89 | - ASCII logos: 90 | - Ubuntu 91 | 92 | ### Fixed 93 | 94 | - Reverted commit [f7f715e](https://github.com/NTBBloodbath/lcfetch/commit/f7f715e19f7274526052632f37912f185a259327), configurations should 95 | work as expected now 96 | - Fixed Arch logo detection 97 | - Use the current distro accent when the logo is not displayed 98 | - Logo lines will not be duplicated anymore when rendering the color palette 99 | 100 | ## [0.1.0-beta5] - 2021-08-21 101 | 102 | ### Added 103 | 104 | - Support for resolution field 105 | - Support for dynamic distribution ASCII logos 106 | - Option to set a custom delimiter between the field message and the information 107 | - Option to choose if the OS architecture should be shown at the right side of the OS name or not 108 | - Support for Nix package manager 109 | - ASCII logos: 110 | - Arch 111 | - Debian 112 | - NixOS (seems to be buggy for me) 113 | 114 | ### Fixed 115 | 116 | - packages field will not try to use non installed managers anymore 117 | 118 | ## [0.1.0-beta4] - 2021-08-20 119 | 120 | ### Added 121 | 122 | - Support for packages field 123 | - Option to choose whether showing extra information when printing CPU field 124 | or keep it short 125 | 126 | ### Changed 127 | 128 | - Releases now have Lua embedded 129 | - Lua 5.3.6 will now be optionally downloaded when building 130 | - Allow using gap when using minimal mode (not displaying logo) 131 | - Updated default configurations 132 | 133 | ## [0.1.0-beta3] - 2021-08-18 134 | 135 | ### Added 136 | 137 | - Support for CPU field 138 | - Support for memory field 139 | - Option to choose whether showing memory in GiB or MiB 140 | - Better error handling when opening system files like `/proc/cpuinfo` 141 | 142 | ### Changed 143 | 144 | - Updated default configurations 145 | - Third party dependencies are now handled by our Makefile, no more submodules! 146 | 147 | ### Fixed 148 | 149 | - Fixed a segfault caused if the gap option was equal to 0 150 | - Logo and information will not wrap anymore on small terminals or resize 151 | - If the logo was completely rendered but there is missing information (still not rendered) 152 | lcfetch will also render this information, as it should be 153 | 154 | ## [0.1.0-beta2] - 2021-08-17 155 | 156 | ### Added 157 | 158 | - Man pages, you can use `man lcfetch` now to see lcfetch documentation! 159 | - Option to choose if the ASCII distro logo should be printed or not 160 | - Option for custom spacing between the ASCII distro logo and the information 161 | 162 | ### Changed 163 | 164 | - Default distro logo is now a Tux, should make more sense 165 | - Some default configurations 166 | - Copyright notice is now a lot better 167 | 168 | ### Fixed 169 | 170 | - Set `dumpstack` debugging function as unused 171 | - We had some memory leaks, and then poof, they disappeared 172 | 173 | ## [0.1.0-beta1] - 2021-08-12 174 | 175 | - Initial release 176 | 177 | [Unreleased]: https://github.com/NTBBloodbath/lcfetch/compare/v0.2.0...HEAD 178 | [0.2.0]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.2..v0.2.0 179 | [0.1.2]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.1..v0.1.2 180 | [0.1.1]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.0..v0.1.1 181 | [0.1.0]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.0-beta6..v0.1.0 182 | [0.1.0-beta6]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.0-beta5..v0.1.0-beta6 183 | [0.1.0-beta5]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.0-beta4..v0.1.0-beta5 184 | [0.1.0-beta4]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.0-beta3..v0.1.0-beta4 185 | [0.1.0-beta3]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.0-beta2..v0.1.0-beta3 186 | [0.1.0-beta2]: https://github.com/NTBBloodbath/lcfetch/compare/v0.1.0-beta1..v0.1.0-beta2 187 | [0.1.0-beta1]: https://github.com/NTBBloodbath/lcfetch/releases/tag/v0.1.0-beta1 188 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # lcfetch 4 | 5 | ![License](https://img.shields.io/github/license/NTBBloodbath/lcfetch?style=flat-square) 6 | [![build](https://github.com/NTBBloodbath/lcfetch/actions/workflows/build.yml/badge.svg)](https://github.com/NTBBloodbath/lcfetch/actions/workflows/build.yml) 7 | 8 | ![lcfetch demo](https://user-images.githubusercontent.com/36456999/133947515-0116a6fe-8e1e-4c23-a181-8d055a0071e2.png) 9 | 10 |
11 | 12 | A fast and easy to configure alternative to [neofetch](https://github.com/dylanaraps/neofetch) 13 | written in C and configured using Lua! 14 | 15 | > **IMPORTANT**: I'm a newbie using C and the Lua C API so if you see something wrong 16 | > please let me know! 17 | 18 | ## Installation 19 | 20 | You can grab the latest release from our [releases](https://github.com/NTBBloodbath/lcfetch/releases/latest) 21 | or if you want to live in the bleeding-edge with the latest features you can [build from source](#building-from-source). 22 | 23 | > **NOTE**: lcfetch was tested only with Lua `5.3+` but should work as expected with earlier 24 | > Lua versions. 25 | 26 | ### Building from source 27 | 28 | #### Installing dependencies 29 | 30 | The packages listed below are optional because lcfetch will download `Lua 5.3.6` locally 31 | by default in order to avoid installing extra stuff in your system and libraries if they 32 | aren't installed in your system. 33 | 34 | > **IMPORTANT**: xmake is not in all distributions repositories, 35 | > please refer to xmake [installation guide](https://xmake.io/#/guide/installation). 36 | 37 | ##### Ubuntu 38 | 39 | ```sh 40 | apt install lua5.3 liblua5.3-dev libx11-dev libxrandr-dev libreadline-dev 41 | ``` 42 | 43 | ##### Fedora 44 | 45 | ```sh 46 | dnf install lua lua-devel libX11-devel libXrandr-devel readline-devel 47 | ``` 48 | 49 | ##### Arch 50 | 51 | ```sh 52 | pacman -S lua53 libx11 libxrandr readline 53 | ``` 54 | 55 | ##### Termux 56 | 57 | ```sh 58 | apt install xmake lua53 liblua53 libx11 libxrandr readline xorgproto 59 | ``` 60 | 61 | > **NOTE**: isn't your distro covered here but you know the exact packages names? Please 62 | > send a Pull Request! 63 | 64 | --- 65 | 66 | Now that you have the system-wide dependencies you can proceed to build and install lcfetch! 67 | 68 | ```sh 69 | git clone --depth 1 https://github.com/NTBBloodbath/lcfetch.git \ 70 | && cd lcfetch 71 | ``` 72 | 73 | For speeding up things, you can simply use our [XMake file](./xmake.lua). 74 | 75 | The `lcfetch` target (the default one) will automatically download the required 76 | third-party dependencies for building lcfetch (`Lua 5.3.6` and system dependencies like `libxrandr` if needed). 77 | 78 | ```sh 79 | # For only building lcfetch 80 | xmake 81 | 82 | # For building and installing lcfetch, lcfetch will be installed at '~/.local/bin' 83 | xmake install 84 | ``` 85 | 86 | > **IMPORTANT**: if you don't have clang installed you will need to change the compiler 87 | > by adding `--cc=gcc` in your xmake call. 88 | 89 | #### Troubleshooting 90 | 91 | 1. If you're getting an error related to `Xatom.h` header during compilation you will 92 | need to install `xorgproto` package (don't know if the package name changes in some distros tho). 93 | 94 | ## Usage 95 | 96 | For starting lcfetch you will only need to type `lcfetch` in your terminal. 97 | 98 | lcfetch also provides a CLI with some options. 99 | 100 | ``` 101 | $ lcfetch --help 102 | Usage: lcfetch [OPTIONS] 103 | 104 | OPTIONS: 105 | -c, --config /path/to/config Specify a path to a custom config file 106 | -h, --help Print this message and exit 107 | -v, --version Show lcfetch version 108 | 109 | Report bugs to https://github.com/NTBBloodbath/lcfetch/issues 110 | ``` 111 | 112 | > See `man lcfetch` for more information about usage and configurations. 113 | 114 | ### Configuring lcfetch 115 | 116 | lcfetch uses the [Lua scripting language](https://www.lua.org/) as its configuration 117 | language. 118 | 119 | When installing lcfetch with `XMake` we will automatically copy the default configurations 120 | under the default configurations path for lcfetch (`~/.config/lcfetch/config.lua`). 121 | 122 | > **NOTE**: if you didn't installed lcfetch with `xmake install` you will need to copy the file 123 | > by yourself under `~/.config/lcfetch` directory. The default configurations file is 124 | > located under [config](./config) directory in the repository. 125 | 126 | All the configuration options are self-documented and easy to understand. 127 | 128 | ## Uninstalling 129 | 130 | For uninstalling lcfetch you can simply run `xmake uninstall` in the lcfetch directory. 131 | 132 | This command will remove the lcfetch binary from `~/.local/bin` directory and also 133 | the lcfetch man pages from `~/.local/share/man/man1`. 134 | 135 | > **NOTE**: you will need to refresh your `mandb` to completely remove lcfetch man pages. 136 | 137 | ## Acknowledgements 138 | 139 | - [dylanaraps](https://github.com/dylanaraps), creator of `neofetch`. We _will_ 140 | extract some ASCII distribution logos from `neofetch`. 141 | - [rxi](https://github.com/rxi), lcfetch is using his [log.c](https://github.com/rxi/log.c) 142 | library for providing error logs in a fancy way. 143 | - [wmctrl](http://tripie.sweb.cz/utils/wmctrl/) creator (I don't know who is), 144 | I extracted the window manager name logic from his awesome program. 145 | - All the amazing people who helped me to test stuff :heart:. 146 | 147 | ## Contribute 148 | 149 | 1. Fork it (https://github.com/NTBBloodbath/lcfetch/fork) 150 | 2. Create your feature branch (git checkout -b my-new-feature) 151 | 3. Commit your changes (git commit -am 'Add some feature') 152 | 4. Push to the branch (git push origin my-new-feature) 153 | 5. Create a new Pull Request 154 | 155 | ## Todo 156 | 157 | - [x] Support for some missing essential fields (e.g. `Packages`, `WM`) 158 | - [x] Stop hardcoding the ASCII distro logo and accent color 159 | - [x] Add more configurations 160 | - [x] Strip content on non-wide terminals if we need to (like neofetch) 161 | - [ ] Add support for MacOS (probably the last thing to be done?) 162 | - [ ] Add support for images (we can use icat, uberzug or kitty protocols) 163 | 164 | ## License 165 | 166 | lcfetch is distributed under [GPLv2 license](./LICENSE). 167 | -------------------------------------------------------------------------------- /config/minimal.lua: -------------------------------------------------------------------------------- 1 | -----[[-----------------------------------]]----- 2 | ---- ---- 3 | --- FIELDS CONFIGURATIONS --- 4 | ---- ---- 5 | -----]]-----------------------------------[[----- 6 | 7 | -- Enabled information fields, the data that will be printed 8 | -- Available (working) fields: 9 | -- * "" (empty string) -> newline 10 | -- * User (JohnDoe@myhost) 11 | -- * Separator 12 | -- * OS 13 | -- * Kernel 14 | -- * Uptime 15 | -- * Packages 16 | -- * Resolution 17 | -- * Shell 18 | -- * Terminal 19 | -- * CPU 20 | -- * Memory 21 | -- * Colors 22 | -- 23 | -- NOTE: fields are case-insensitive 24 | options.enabled_fields = { 25 | -- "User", 26 | -- "Separator", 27 | "OS", 28 | "Kernel", 29 | "Uptime", 30 | "Packages", 31 | "", 32 | "WM", 33 | "Resolution", 34 | "", 35 | "Shell", 36 | "Terminal", 37 | "", 38 | "CPU", 39 | "Memory", 40 | "", 41 | "Colors", 42 | } 43 | 44 | ----- DEFAULT FIELDS MESSAGES ------------------- 45 | ------------------------------------------------- 46 | options.os_message = " " 47 | options.kernel_message = " " 48 | options.uptime_message = " " 49 | options.packages_message = " " 50 | options.resolution_message = " " 51 | options.wm_message = " " 52 | options.shell_message = " " 53 | options.terminal_message = " " 54 | options.cpu_message = " " 55 | options.memory_message = "ﳔ " 56 | 57 | 58 | -----[[-----------------------------------]]----- 59 | ---- ---- 60 | --- DISPLAY CONFIGURATIONS --- 61 | ---- ---- 62 | -----]]-----------------------------------[[----- 63 | 64 | -- The delimiter shown between the field message and the information, e.g. 65 | -- OS: Fedora 34 (KDE Plasma) x86_64 66 | -- ^ 67 | -- delimiter 68 | -- 69 | -- NOTE: by default is ":", change it to "" for disabling it 70 | options.delimiter = "" 71 | 72 | -- The separator shown between your USERNAME@HOSTNAME message 73 | -- 74 | -- NOTE: default is "-" 75 | options.separator = "-" 76 | 77 | -- The terminal colors style 78 | -- Available styles: 79 | -- * classic 80 | -- * circles 81 | -- 82 | -- NOTE: default is "classic" 83 | options.colors_style = "circles" 84 | 85 | -- Accent color for the fields 86 | -- Available colors: 87 | -- * black 88 | -- * red 89 | -- * green 90 | -- * yellow 91 | -- * blue 92 | -- * purple 93 | -- * cyan 94 | -- * white 95 | -- 96 | -- NOTE: by default is the ASCII distro accent color. Colors are case-insensitive 97 | options.accent_color = "" 98 | 99 | -- ASCII distro logo to be printed, unused at the moment 100 | -- 101 | -- NOTE: by default the ASCII distro logo is automatically detected (WIP) 102 | options.ascii_distro = "" 103 | 104 | -- If the ASCII distro logo should be printed 105 | -- 106 | -- NOTE: default is true 107 | options.display_logo = false 108 | 109 | -- The gap between the ASCII distro logo / terminal left border and the 110 | -- information fields 111 | -- 112 | -- NOTE: by default is 3 113 | options.gap = 4 114 | 115 | -- If the OS architecture should be shown at the right side of the OS name or not, 116 | -- e.g. Fedora x86_64 117 | -- 118 | -- NOTE: by default is true 119 | options.show_arch = true 120 | 121 | -- If the screen refresh rate should be shown when displaying the 122 | -- screen resolution 123 | -- 124 | -- NOTE: by default is false 125 | options.display_refresh_rate = false 126 | 127 | -- If the CPU information should be short or include extra information, e.g. 128 | -- when true: 129 | -- Intel i5 760 (4) @ 2.8Ghz 130 | -- when false: 131 | -- Intel(R) Core(TM) i5 760 (4) @ 2.8Ghz 132 | -- 133 | -- NOTE: by default is true 134 | options.short_cpu_info = true 135 | 136 | -- If the memory should be printed as GiB instead of MiB 137 | -- 138 | -- NOTE: by default is true 139 | options.memory_in_gib = true 140 | -------------------------------------------------------------------------------- /config/sample.lua: -------------------------------------------------------------------------------- 1 | -----[[-----------------------------------]]----- 2 | ---- ---- 3 | --- FIELDS CONFIGURATIONS --- 4 | ---- ---- 5 | -----]]-----------------------------------[[----- 6 | 7 | -- Enabled information fields, the data that will be printed 8 | -- Available (working) fields: 9 | -- * "" (empty string) -> newline 10 | -- * User (JohnDoe@myhost) 11 | -- * Separator 12 | -- * OS 13 | -- * Kernel 14 | -- * Uptime 15 | -- * Packages 16 | -- * WM 17 | -- * Resolution 18 | -- * Shell 19 | -- * Terminal 20 | -- * CPU 21 | -- * Memory 22 | -- * Colors 23 | -- 24 | -- NOTE: fields are case-insensitive 25 | options.enabled_fields = { 26 | "User", 27 | "Separator", 28 | "OS", 29 | "Kernel", 30 | "Uptime", 31 | "Packages", 32 | "", 33 | "WM", 34 | "Resolution", 35 | "", 36 | "Shell", 37 | "Terminal", 38 | "", 39 | "CPU", 40 | "Memory", 41 | "", 42 | "Colors", 43 | } 44 | 45 | ----- DEFAULT FIELDS MESSAGES ------------------- 46 | ------------------------------------------------- 47 | options.os_message = "OS" 48 | options.kernel_message = "Kernel" 49 | options.uptime_message = "Uptime" 50 | options.packages_message = "Packages" 51 | options.resolution_message = "Resolution" 52 | options.wm_message = "WM" 53 | options.shell_message = "Shell" 54 | options.terminal_message = "Terminal" 55 | options.cpu_message = "CPU" 56 | options.memory_message = "Memory" 57 | 58 | 59 | -----[[-----------------------------------]]----- 60 | ---- ---- 61 | --- DISPLAY CONFIGURATIONS --- 62 | ---- ---- 63 | -----]]-----------------------------------[[----- 64 | 65 | -- The delimiter shown between the field message and the information, e.g. 66 | -- OS: Fedora 34 (KDE Plasma) x86_64 67 | -- ^ 68 | -- delimiter 69 | -- 70 | -- NOTE: by default is ":", change it to "" for disabling it 71 | options.delimiter = ":" 72 | 73 | -- The separator shown between your USERNAME@HOSTNAME message 74 | -- 75 | -- NOTE: default is "-" 76 | options.separator = "-" 77 | 78 | -- The terminal colors icon, this option overrides colors_style 79 | -- in order to use your own icon for the color palette 80 | -- 81 | -- NOTE: default is "" 82 | options.colors_icon = "" 83 | 84 | -- The terminal colors style 85 | -- Available styles: 86 | -- * classic 87 | -- * circles 88 | -- * ghosts (requires a patched font like nerd fonts) 89 | -- 90 | -- NOTE: default is "classic" 91 | options.colors_style = "classic" 92 | 93 | -- Accent color for the fields 94 | -- Available colors: 95 | -- * black 96 | -- * red 97 | -- * green 98 | -- * yellow 99 | -- * blue 100 | -- * purple 101 | -- * cyan 102 | -- * white 103 | -- 104 | -- NOTE: by default is the ASCII distro accent color. Colors are case-insensitive 105 | options.accent_color = "" 106 | 107 | -- Custom ASCII logo to be printed 108 | -- 109 | -- NOTE: by default is an empty table (array) 110 | options.custom_ascii_logo = {} 111 | 112 | -- ASCII distro logo to be printed 113 | -- Available logos: 114 | -- * tux 115 | -- * arch 116 | -- * nixos 117 | -- * fedora 118 | -- * gentoo 119 | -- * debian 120 | -- * ubuntu 121 | -- * manjaro 122 | -- 123 | -- NOTE: by default the ASCII distro logo is automatically detected 124 | options.ascii_distro = "" 125 | 126 | -- If the ASCII distro logo should be printed 127 | -- 128 | -- NOTE: default is true 129 | options.display_logo = true 130 | 131 | -- The gap between the ASCII distro logo / terminal left border and the 132 | -- information fields 133 | -- 134 | -- NOTE: by default is 3 135 | options.gap = 3 136 | 137 | -- If the OS architecture should be shown at the right side of the OS name or not, 138 | -- e.g. Fedora x86_64 139 | -- 140 | -- NOTE: by default is true 141 | options.show_arch = true 142 | 143 | -- If the screen refresh rate should be shown when displaying the 144 | -- screen resolution 145 | -- 146 | -- NOTE: by default is false 147 | options.display_refresh_rate = false 148 | 149 | -- If the CPU information should be short or include extra information, e.g. 150 | -- when true: 151 | -- Intel i5 760 (4) @ 2.8Ghz 152 | -- when false: 153 | -- Intel(R) Core(TM) i5 760 (4) @ 2.8Ghz 154 | -- 155 | -- NOTE: by default is true 156 | options.short_cpu_info = true 157 | 158 | -- If the memory should be printed as GiB instead of MiB 159 | -- 160 | -- NOTE: by default is true 161 | options.memory_in_gib = true 162 | -------------------------------------------------------------------------------- /man/lcfetch.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.5 2 | .\" 3 | .TH "lcfetch" "1" "October 15, 2021" "lcfetch 0.2.0" "lcfetch user manual" 4 | .hy 5 | .SH NAME 6 | .PP 7 | lcfetch \- A fast and easy to configure alternative to neofetch. 8 | .SH SYNOPSIS 9 | .PP 10 | \f[B]lcfetch\f[R] [\f[I]OPTIONS\f[R]] \&... 11 | .SH DESCRIPTION 12 | .PP 13 | \f[B]lcfetch\f[R] is a fast and easy to configure alternative to 14 | neofetch, written in C and configured using Lua. 15 | .SH OPTIONS 16 | .TP 17 | .B \f[B]\-h\f[R], \f[B]\[en]help\f[R] 18 | Print help message and exit. 19 | .TP 20 | .B \f[B]\-v\f[R], \f[B]\[en]version\f[R] 21 | Show lcfetch version. 22 | .TP 23 | .B \f[B]\-c\f[R], \f[B]\[en]config\f[R] 24 | Specify a path to a custom config file. 25 | .SH EXAMPLES 26 | .TP 27 | .B \f[B]lcfetch \-h | lcfetch \[en]help\f[R] 28 | Display help message and exit. 29 | .TP 30 | .B \f[B]lcfetch \-v | lcfetch \[en]version\f[R] 31 | Show lcfetch version. 32 | .TP 33 | .B \f[B]lcfetch \[en]config \[ti]/.config/lcfetch/circle\-colors.lua\f[R] 34 | Use the \f[C]\[ti]/.config/lcfetch/circle\-colors.lua\f[R] file as the 35 | configurations file. 36 | .SH EXIT VALUES 37 | .TP 38 | .B \f[B]0\f[R] 39 | Success. 40 | .TP 41 | .B \f[B]1\f[R] 42 | Invalid option. 43 | .SH CONFIGURATION 44 | .PP 45 | \f[B]lcfetch\f[R] uses the Lua scripting language as its configurations 46 | language. 47 | All the configuration options has a \f[B]options.\f[R] prefix because 48 | they\[cq]re a Lua table. 49 | .PP 50 | The \f[B]lcfetch\f[R] configurations resides under 51 | \f[C]\[ti]/.config/lcfetch\f[R] directory and a \f[C]config.lua\f[R] 52 | file by default. 53 | .TP 54 | .B \f[B]enabled_fields\f[R] 55 | Enabled information fields, the data that will be printed. 56 | .RS 57 | .PP 58 | Available (working) fields: 59 | .IP \[bu] 2 60 | \[dq]\[dq] (empty string) \-> newline 61 | .IP \[bu] 2 62 | User (JohnDoe\[at]myhost) 63 | .IP \[bu] 2 64 | Separator 65 | .IP \[bu] 2 66 | OS 67 | .IP \[bu] 2 68 | Kernel 69 | .IP \[bu] 2 70 | Uptime 71 | .IP \[bu] 2 72 | Packages 73 | .IP \[bu] 2 74 | WM 75 | .IP \[bu] 2 76 | Resolution 77 | .IP \[bu] 2 78 | Shell 79 | .IP \[bu] 2 80 | Terminal 81 | .IP \[bu] 2 82 | CPU 83 | .IP \[bu] 2 84 | Memory 85 | .IP \[bu] 2 86 | Colors 87 | .PP 88 | Type: table 89 | .RE 90 | .TP 91 | .B \f[B]os_message\f[R] 92 | The OS field message. 93 | .RS 94 | .PP 95 | Type: string 96 | .PP 97 | Default: \[lq]OS\[rq] 98 | .RE 99 | .TP 100 | .B \f[B]kernel_message\f[R] 101 | The Kernel field message. 102 | .RS 103 | .PP 104 | Type: string 105 | .PP 106 | Default: \[lq]Kernel\[rq] 107 | .RE 108 | .TP 109 | .B \f[B]uptime_message\f[R] 110 | The Uptime field message. 111 | .RS 112 | .PP 113 | Type: string 114 | .PP 115 | Default: \[lq]Uptime\[rq] 116 | .RE 117 | .TP 118 | .B \f[B]packages_message\f[R] 119 | The Packages field message. 120 | .RS 121 | .PP 122 | Type: string 123 | .PP 124 | Default: \[lq]Packages\[rq] 125 | .RE 126 | .TP 127 | .B \f[B]resolution_message\f[R] 128 | The Resolution field message. 129 | .RS 130 | .PP 131 | Type: string 132 | .PP 133 | Default: \[lq]Resolution\[rq] 134 | .RE 135 | .TP 136 | .B \f[B]wm_message\f[R] 137 | The WM field message. 138 | .RS 139 | .PP 140 | Type: string 141 | .PP 142 | Default: \[lq]WM\[rq] 143 | .RE 144 | .TP 145 | .B \f[B]shell_message\f[R] 146 | The Shell field message. 147 | .RS 148 | .PP 149 | Type: string 150 | .PP 151 | Default: \[lq]Shell\[rq] 152 | .RE 153 | .TP 154 | .B \f[B]terminal_message\f[R] 155 | The Terminal field message. 156 | .RS 157 | .PP 158 | Type: string 159 | .PP 160 | Default: \[lq]Terminal\[rq] 161 | .RE 162 | .TP 163 | .B \f[B]cpu_message\f[R] 164 | The CPU field message. 165 | .RS 166 | .PP 167 | Type: string 168 | .PP 169 | Default: \[lq]CPU\[rq] 170 | .RE 171 | .TP 172 | .B \f[B]memory_message\f[R] 173 | The Memory field message. 174 | .RS 175 | .PP 176 | Type: string 177 | .PP 178 | Default: \[lq]Memory\[rq] 179 | .RE 180 | .TP 181 | .B \f[B]delimiter\f[R] 182 | The delimiter shown between the field message and the information. 183 | .RS 184 | .PP 185 | Type: string 186 | .PP 187 | Default: \[lq]:\[rq] 188 | .RE 189 | .TP 190 | .B \f[B]separator\f[R] 191 | The separator shown between your USERNAME\[at]HOSTNAME message. 192 | .RS 193 | .PP 194 | Type: string 195 | .PP 196 | Default: \[lq]\-\[rq] 197 | .RE 198 | .TP 199 | .B \f[B]colors_icon\f[R] 200 | The terminal colors icon, this option overrides colors_style. 201 | .RS 202 | .PP 203 | Type: string 204 | .PP 205 | Default: \[dq]\[dq] 206 | .RE 207 | .TP 208 | .B \f[B]colors_style\f[R] 209 | The terminal colors style. 210 | .RS 211 | .PP 212 | Type: string 213 | .PP 214 | Available styles: 215 | .IP \[bu] 2 216 | classic 217 | .IP \[bu] 2 218 | circles 219 | .IP \[bu] 2 220 | ghosts (requires a patched font like nerd fonts) 221 | .PP 222 | Default: \[lq]classic\[rq] 223 | .RE 224 | .TP 225 | .B \f[B]accent_color\f[R] 226 | Accent color for the fields. 227 | .RS 228 | .PP 229 | Type: string 230 | .PP 231 | Available colors: 232 | .IP \[bu] 2 233 | black 234 | .IP \[bu] 2 235 | red 236 | .IP \[bu] 2 237 | green 238 | .IP \[bu] 2 239 | yellow 240 | .IP \[bu] 2 241 | blue 242 | .IP \[bu] 2 243 | purple 244 | .IP \[bu] 2 245 | cyan 246 | .IP \[bu] 2 247 | white 248 | .PP 249 | Default: \[dq]\[dq] 250 | .RE 251 | .TP 252 | .B \f[B]ascii_distro\f[R] 253 | ASCII distro logo to be printed. 254 | .RS 255 | .PP 256 | Type: string 257 | .PP 258 | Available logos: 259 | .IP \[bu] 2 260 | tux 261 | .IP \[bu] 2 262 | arch 263 | .IP \[bu] 2 264 | nixos 265 | .IP \[bu] 2 266 | fedora 267 | .IP \[bu] 2 268 | gentoo 269 | .IP \[bu] 2 270 | debian 271 | .IP \[bu] 2 272 | ubuntu 273 | .PP 274 | Default: \[dq]\[dq] 275 | .RE 276 | .TP 277 | .B \f[B]display_logo\f[R] 278 | If the ASCII distro logo should be printed. 279 | .RS 280 | .PP 281 | Type: boolean 282 | .PP 283 | Default: true 284 | .RE 285 | .TP 286 | .B \f[B]gap\f[R] 287 | The gap between the ASCII distro logo and the information fields. 288 | .RS 289 | .PP 290 | Type: number 291 | .PP 292 | Default: 3 293 | .RE 294 | .TP 295 | .B \f[B]display_refresh_rate\f[R] 296 | If the screen refresh rate should be shown when displaying the screen 297 | resolution 298 | .RS 299 | .PP 300 | Type: boolean 301 | .PP 302 | Default: false 303 | .RE 304 | .TP 305 | .B \f[B]short_cpu_info\f[R] 306 | If the CPU information should be short or include extra information. 307 | .RS 308 | .PP 309 | Type: boolean 310 | .PP 311 | Default: true 312 | .RE 313 | .TP 314 | .B \f[B]memory_in_gib\f[R] 315 | If the memory should be printed as GiB instead of MiB. 316 | .RS 317 | .PP 318 | Type: boolean 319 | .PP 320 | Default: true 321 | .RE 322 | .SH AUTHORS 323 | .PP 324 | Written by NTBBloodbath. 325 | .SH BUGS 326 | .PP 327 | Submit bug reports online at: 328 | . 329 | .SH SEE ALSO 330 | .PP 331 | Full documentation and sources at: 332 | . 333 | .SH COPYRIGHT 334 | .PP 335 | Copyright (c) 2021 NTBBloodbath. 336 | License GPLv2: GNU GPL version 2 337 | . 338 | .PP 339 | This program is free software; you can redistribute it and/or modify it 340 | under the terms of the GNU General Public License as published by the 341 | Free Software Foundation; either version 2 of the License, or (at your 342 | option) any later version. 343 | .PP 344 | This program is distributed in the hope that it will be useful, but 345 | WITHOUT ANY WARRANTY; without even the implied warranty of 346 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 347 | See the GNU General Public License for more details. 348 | -------------------------------------------------------------------------------- /man/lcfetch.1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: lcfetch 3 | section: 1 4 | header: lcfetch user manual 5 | footer: lcfetch 0.2.0 6 | date: October 15, 2021 7 | --- 8 | 9 | # NAME 10 | 11 | lcfetch - A fast and easy to configure alternative to neofetch. 12 | 13 | # SYNOPSIS 14 | 15 | **lcfetch** [*OPTIONS*] ... 16 | 17 | # DESCRIPTION 18 | 19 | **lcfetch** is a fast and easy to configure alternative to neofetch, written 20 | in C and configured using Lua. 21 | 22 | # OPTIONS 23 | 24 | **-h**, **--help** 25 | : Print help message and exit. 26 | 27 | **-v**, **--version** 28 | : Show lcfetch version. 29 | 30 | **-c**, **--config** 31 | : Specify a path to a custom config file. 32 | 33 | **-d**, **--distro_name** 34 | : Specify the distribution logo that is going to be printed. 35 | 36 | # EXAMPLES 37 | 38 | **lcfetch -h | lcfetch --help** 39 | : Display help message and exit. 40 | 41 | **lcfetch -v | lcfetch --version** 42 | : Show lcfetch version. 43 | 44 | **lcfetch --config ~/.config/lcfetch/circle-colors.lua** 45 | : Use the `~/.config/lcfetch/circle-colors.lua` file as the configurations file. 46 | 47 | # EXIT VALUES 48 | 49 | **0** 50 | : Success. 51 | 52 | **1** 53 | : Invalid option. 54 | 55 | # CONFIGURATION 56 | 57 | **lcfetch** uses the Lua scripting language as its configurations language. All the 58 | configuration options has a **options.** prefix because they're a Lua table. 59 | 60 | The **lcfetch** configurations resides under `~/.config/lcfetch` directory and a 61 | `config.lua` file by default. 62 | 63 | **enabled_fields** 64 | : Enabled information fields, the data that will be printed. 65 | 66 | Available (working) fields: 67 | 68 | - "" (empty string) -> newline 69 | - User (JohnDoe@myhost) 70 | - Separator 71 | - OS 72 | - Kernel 73 | - Uptime 74 | - Packages 75 | - WM 76 | - Resolution 77 | - Shell 78 | - Terminal 79 | - CPU 80 | - Memory 81 | - Colors 82 | 83 | Type: table 84 | 85 | **os_message** 86 | : The OS field message. 87 | 88 | Type: string 89 | 90 | Default: "OS" 91 | 92 | **kernel_message** 93 | : The Kernel field message. 94 | 95 | Type: string 96 | 97 | Default: "Kernel" 98 | 99 | **uptime_message** 100 | : The Uptime field message. 101 | 102 | Type: string 103 | 104 | Default: "Uptime" 105 | 106 | **packages_message** 107 | : The Packages field message. 108 | 109 | Type: string 110 | 111 | Default: "Packages" 112 | 113 | **resolution_message** 114 | : The Resolution field message. 115 | 116 | Type: string 117 | 118 | Default: "Resolution" 119 | 120 | **wm_message** 121 | : The WM field message. 122 | 123 | Type: string 124 | 125 | Default: "WM" 126 | 127 | **shell_message** 128 | : The Shell field message. 129 | 130 | Type: string 131 | 132 | Default: "Shell" 133 | 134 | **terminal_message** 135 | : The Terminal field message. 136 | 137 | Type: string 138 | 139 | Default: "Terminal" 140 | 141 | **cpu_message** 142 | : The CPU field message. 143 | 144 | Type: string 145 | 146 | Default: "CPU" 147 | 148 | **memory_message** 149 | : The Memory field message. 150 | 151 | Type: string 152 | 153 | Default: "Memory" 154 | 155 | **delimiter** 156 | : The delimiter shown between the field message and the information. 157 | 158 | Type: string 159 | 160 | Default: ":" 161 | 162 | **separator** 163 | : The separator shown between your USERNAME@HOSTNAME message. 164 | 165 | Type: string 166 | 167 | Default: "-" 168 | 169 | **colors_icon** 170 | : The terminal colors icon, this option overrides colors_style. 171 | 172 | Type: string 173 | 174 | Default: "" 175 | 176 | **colors_style** 177 | : The terminal colors style. 178 | 179 | Type: string 180 | 181 | Available styles: 182 | 183 | - classic 184 | - circles 185 | - ghosts (requires a patched font like nerd fonts) 186 | 187 | Default: "classic" 188 | 189 | **accent_color** 190 | : Accent color for the fields. 191 | 192 | Type: string 193 | 194 | Available colors: 195 | 196 | - black 197 | - red 198 | - green 199 | - yellow 200 | - blue 201 | - purple 202 | - cyan 203 | - white 204 | 205 | Default: "" 206 | 207 | **ascii_distro** 208 | : ASCII distro logo to be printed. 209 | 210 | Type: string 211 | 212 | Available logos: 213 | 214 | - tux 215 | - arch 216 | - nixos 217 | - fedora 218 | - gentoo 219 | - debian 220 | - ubuntu 221 | 222 | Default: "" 223 | 224 | **display_logo** 225 | : If the ASCII distro logo should be printed. 226 | 227 | Type: boolean 228 | 229 | Default: true 230 | 231 | **gap** 232 | : The gap between the ASCII distro logo and the information fields. 233 | 234 | Type: number 235 | 236 | Default: 3 237 | 238 | **display_refresh_rate** 239 | : If the screen refresh rate should be shown when displaying the screen resolution 240 | 241 | Type: boolean 242 | 243 | Default: false 244 | 245 | **short_cpu_info** 246 | : If the CPU information should be short or include extra information. 247 | 248 | Type: boolean 249 | 250 | Default: true 251 | 252 | **memory_in_gib** 253 | : If the memory should be printed as GiB instead of MiB. 254 | 255 | Type: boolean 256 | 257 | Default: true 258 | 259 | # AUTHORS 260 | 261 | Written by NTBBloodbath. 262 | 263 | # BUGS 264 | 265 | Submit bug reports online at: . 266 | 267 | # SEE ALSO 268 | 269 | Full documentation and sources at: . 270 | 271 | # COPYRIGHT 272 | 273 | Copyright (c) 2021 NTBBloodbath. License GPLv2: GNU GPL version 2 274 | . 275 | 276 | This program is free software; you can redistribute it and/or 277 | modify it under the terms of the GNU General Public License 278 | as published by the Free Software Foundation; either version 2 279 | of the License, or (at your option) any later version. 280 | 281 | This program is distributed in the hope that it will be useful, 282 | but WITHOUT ANY WARRANTY; without even the implied warranty of 283 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 284 | GNU General Public License for more details. 285 | -------------------------------------------------------------------------------- /src/include/lcfetch.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef LCFETCH_H 8 | #define LCFETCH_H 9 | 10 | /* lcfetch version */ 11 | #define VERSION "0.2.0" 12 | 13 | /* copyright notice */ 14 | #define COPYRIGHT \ 15 | "Copyright (c) 2021 NTBBloodbath. lcfetch is distributed under GPLv2 license.\n\n" \ 16 | "This program is free software; you can redistribute it and/or " \ 17 | "modify it under the terms of the GNU General Public License " \ 18 | "as published by the Free Software Foundation; either version 2 " \ 19 | "of the License, or (at your option) any later version.\n\n" \ 20 | "This program is distributed in the hope that it will be useful, " \ 21 | "but WITHOUT ANY WARRANTY; without even the implied warranty of " \ 22 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " \ 23 | "GNU General Public License for more details.\n" 24 | 25 | /* Structures and types */ 26 | typedef struct custom_logo { 27 | size_t cols; 28 | size_t rows; 29 | char **arr; 30 | } custom_ascii_logo; 31 | 32 | /* lcfetch.c */ 33 | #define BUF_SIZE 256 34 | #define COUNT(x) (int)(sizeof x / sizeof *x) 35 | #define LEN(arr) ((int)(sizeof(arr) / sizeof(arr[0]))) 36 | char *get_title(char *accent_color); 37 | char *get_separator(); 38 | char *get_os(bool pretty_name); 39 | char *get_kernel(); 40 | char *get_uptime(); 41 | char *get_wm(); 42 | char *get_resolution(); 43 | char *get_shell(); 44 | char *get_terminal(); 45 | char *get_packages(); 46 | char *get_cpu(); 47 | char *get_memory(); 48 | char *get_colors_dark(); 49 | char *get_colors_bright(); 50 | 51 | /* memory.c */ 52 | void *xmalloc(size_t size); 53 | void xfree(void *ptr); 54 | 55 | /* cli.c */ 56 | void version(void); 57 | void help(void); 58 | 59 | /* utils.c */ 60 | size_t utf8len(char *s); 61 | char *repeat_string(char *str, int times); 62 | void truncate_whitespaces(char *str); 63 | char *remove_substr(char *str, const char *sub); 64 | char *str_to_lower(char *str); 65 | char **get_distro_logo(char *distro); 66 | int get_distro_logo_rows(char *distro); 67 | char *get_distro_accent(char *distro); 68 | char *get_custom_accent(char *color); 69 | custom_ascii_logo get_custom_logo(); 70 | void print_colors(char *logo_part, char *next_logo_part, char *gap_logo, char *gap_info); 71 | void print_field(char *logo_part, char *gap, const char *delimiter, char *accent, const char *field_name); 72 | char *get_property(Display *disp, Window win, Atom xa_prop_type, char *prop_name, unsigned long *size); 73 | bool is_android_device(); 74 | 75 | /* lua_api.c */ 76 | void start_lua(const char *config_file_path); 77 | void stop_lua(void); 78 | char *get_configuration_file_path(void); 79 | int get_table_size(const char *table); 80 | bool table_contains_string(const char *table, const char *key); 81 | 82 | // Get options 83 | bool get_option_boolean(const char *opt); 84 | const char *get_option_string(const char *opt); 85 | lua_Number get_option_number(const char *opt); 86 | const char *get_subtable_string(const char *table, int index); 87 | 88 | // Set options 89 | int set_table_boolean(const char *key, bool value); 90 | int set_table_string(const char *key, const char *value); 91 | int set_table_number(const char *key, lua_Number value); 92 | int set_table_subtable(const char *key); 93 | int set_subtable_string(const char *table, const char *key); 94 | void init_options(void); 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/include/logos/android.h: -------------------------------------------------------------------------------- 1 | char *android_accent = "\e[1;32m"; 2 | 3 | char *android[] = { 4 | "\e[1;32m -o o- ", 5 | "\e[1;32m +hydNNNNdyh+ ", 6 | "\e[1;32m +mMMMMMMMMMMMMm+ ", 7 | "\e[1;32m `dMM\e[1;37mm:\e[1;32mNMMMMMMN\e[1;37m:m\e[1;32mMMd` ", 8 | "\e[1;32m hMMMMMMMMMMMMMMMMMMh ", 9 | "\e[1;32m .. yyyyyyyyyyyyyyyyyyyy .. ", 10 | "\e[1;32m.mMMm`MMMMMMMMMMMMMMMMMMMM`mMMm.", 11 | "\e[1;32m:MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 12 | "\e[1;32m:MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 13 | "\e[1;32m:MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 14 | "\e[1;32m:MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM:", 15 | "\e[1;32m-MMMM-MMMMMMMMMMMMMMMMMMMM-MMMM-", 16 | "\e[1;32m +yy+ MMMMMMMMMMMMMMMMMMMM +yy+ ", 17 | "\e[1;32m mMMMMMMMMMMMMMMMMMMm ", 18 | "\e[1;32m `/++MMMMh++hMMMM++/` ", 19 | "\e[1;32m MMMMo oMMMM ", 20 | "\e[1;32m MMMMo oMMMM ", 21 | "\e[1;32m oNMm- -mMNs ", 22 | }; 23 | -------------------------------------------------------------------------------- /src/include/logos/arch.h: -------------------------------------------------------------------------------- 1 | char *arch_accent = "\e[1;36m"; 2 | 3 | char *arch[] = { 4 | "\e[1;36m -` ", "\e[1;36m .o+` ", 5 | "\e[1;36m `ooo/ ", "\e[1;36m `+oooo: ", 6 | "\e[1;36m `+oooooo: ", "\e[1;36m -+oooooo+: ", 7 | "\e[1;36m `/:-:++oooo+: ", "\e[1;36m `/++++/+++++++: ", 8 | "\e[1;36m `/++++++++++++++: ", "\e[1;36m `/+++ooooooooooooo/` ", 9 | "\e[1;36m ./ooosssso++osssssso+` ", "\e[1;36m .oossssso-````/ossssss+` ", 10 | "\e[1;36m -osssssso. :ssssssso. ", "\e[1;36m :osssssss/ osssso+++. ", 11 | "\e[1;36m /ossssssss/ +ssssooo/- ", "\e[1;36m `/ossssso+/:- -:/+osssso+- ", 12 | "\e[1;36m `+sso+:-` `.-/+oso: ", "\e[1;36m `++:. `-/+/", 13 | "\e[1;36m .` `/", 14 | }; 15 | -------------------------------------------------------------------------------- /src/include/logos/debian.h: -------------------------------------------------------------------------------- 1 | char *debian_accent = "\e[1;31m"; 2 | 3 | char *debian[] = { 4 | "\e[1;37m _,met$$$$$gg. ", 5 | "\e[1;37m ,g$$$$$$$$$$$$$$$P. ", 6 | "\e[1;37m ,g$$P\" \"\"\"Y$$.\". ", 7 | "\e[1;37m ,$$P' `$$$. ", 8 | "\e[1;37m',$$P ,ggs. `$$b:", 9 | "\e[1;37m`d$$' ,$P\"' \e[1;31m.\e[1;37m $$$ ", 10 | "\e[1;37m $$P d$' \e[1;31m,\e[1;37m $$P ", 11 | "\e[1;37m $$: $$. \e[1;31m-\e[1;37m ,d$$' ", 12 | "\e[1;37m $$; Y$b._ _,d$P' ", 13 | "\e[1;37m Y$$. \e[1;31m`.\e[1;37m`\"Y$$$$P\"' ", 14 | "\e[1;37m `$$b \e[1;31m\"-.__ ", 15 | "\e[1;37m `Y$$ ", 16 | "\e[1;37m `Y$$. ", 17 | "\e[1;37m `$$b. ", 18 | "\e[1;37m `Y$$b. ", 19 | "\e[1;37m `\"Y$b._ ", 20 | "\e[1;37m `\"\"\" ", 21 | }; 22 | -------------------------------------------------------------------------------- /src/include/logos/fedora.h: -------------------------------------------------------------------------------- 1 | char *fedora_accent = "\e[1;34m"; 2 | 3 | char *fedora[] = { 4 | "\e[1;34m .',;::::;,'. ", 5 | " \e[1;34m.';:cccccccccccc:;,. ", 6 | " \e[1;34m.;cccccccccccccccccccccc;. ", 7 | " \e[1;34m.:cccccccccccccccccccccccccc:. ", 8 | " \e[1;34m.;ccccccccccccc;\e[1;37m.:dddl:.\e[1;34m;ccccccc;. ", 9 | " \e[1;34m.:ccccccccccccc;\e[1;37mOWMKOOXMWd\e[1;34m;ccccccc:. ", 10 | "\e[1;34m.:ccccccccccccc;\e[1;37mKMMc\e[1;34m;cc;\e[1;37mxMMc\e[1;34m;ccccccc:.", 11 | "\e[1;34m,cccccccccccccc;\e[1;37mMMM.\e[1;34m;cc;\e[1;37m;WW:\e[1;34m;cccccccc,", 12 | "\e[1;34m:cccccccccccccc;\e[1;37mMMM.\e[1;34m;cccccccccccccccc:", 13 | "\e[1;34m:ccccccc;\e[1;37moxOOOo\e[1;34m;\e[1;37mMMM0OOk.\e[1;34m;cccccccccccc:", 14 | "\e[1;34mcccccc;\e[1;37m0MMKxdd:\e[1;34m;\e[1;37mMMMkddc.\e[1;34m;cccccccccccc;", 15 | "\e[1;34mccccc;\e[1;37mXM0'\e[1;34m;cccc;\e[1;37mMMM.\e[1;34m;cccccccccccccccc'", 16 | "\e[1;34mccccc;\e[1;37mMMo\e[1;34m;ccccc;\e[1;37mMMW.\e[1;34m;ccccccccccccccc; ", 17 | "\e[1;34mccccc;\e[1;37m0MNc.\e[1;34mccc\e[1;37m.xMMd\e[1;34m;ccccccccccccccc; ", 18 | "\e[1;34mcccccc;\e[1;37mdNMWXXXWM0:\e[1;34m;cccccccccccccc:, ", 19 | "\e[1;34mcccccccc;\e[1;37m.:odl:.\e[1;34m;cccccccccccccc:,. ", 20 | "\e[1;34m:cccccccccccccccccccccccccccc:'. ", 21 | "\e[1;34m.:cccccccccccccccccccccc:;,.. ", 22 | "\e[1;34m '::cccccccccccccc::;,. \e[0m", 23 | }; 24 | -------------------------------------------------------------------------------- /src/include/logos/gentoo.h: -------------------------------------------------------------------------------- 1 | char *gentoo_accent = "\e[1;35m"; 2 | 3 | char *gentoo[] = { 4 | "\e[1;35m -/oyddmdhs+:. ", 5 | " \e[1;35m-o\e[1;37mdNMMMMMMMMNNmhy+\e[1;35m-` ", 6 | " \e[1;35m-y\e[1;37mNMMMMMMMMMMMNNNmmdhy\e[1;35m+- ", 7 | " \e[1;35m`o\e[1;37mmMMMMMMMMMMMMNmdmmmmddhhy\e[1;35m/` ", 8 | " \e[1;35mom\e[1;37mMMMMMMMMMMMN\e[1;35mhhyyyo\e[1;37mhmdddhhhd\e[1;35mo` ", 9 | "\e[1;35m.y\e[1;37mdMMMMMMMMMMd\e[1;35mhs++so/s\e[1;37mmdddhhhhdm\e[1;35m+` ", 10 | " \e[1;35moy\e[1;37mhdmNMMMMMMMN\e[1;35mdyooy\e[1;37mdmddddhhhhyhN\e[1;35md. ", 11 | " \e[1;35m:o\e[1;37myhhdNNMMMMMMMNNNmmdddhhhhhyym\e[1;35mMh ", 12 | " \e[1;35m.:\e[1;37m+sydNMMMMMNNNmmmdddhhhhhhmM\e[1;35mmy ", 13 | " \e[1;35m/m\e[1;37mMMMMMMNNNmmmdddhhhhhmMNh\e[1;35ms: ", 14 | " \e[1;35m`o\e[1;37mNMMMMMMMNNNmmmddddhhdmMNhs\e[1;35m+` ", 15 | " \e[1;35m`s\e[1;37mNMMMMMMMMNNNmmmdddddmNMmhs\e[1;35m/. ", 16 | " \e[1;35m/N\e[1;37mMMMMMMMMNNNNmmmdddmNMNdso\e[1;35m:` ", 17 | "\e[1;35m+M\e[1;37mMMMMMMNNNNNmmmmdmNMNdso\e[1;35m/- ", 18 | "\e[1;35myM\e[1;37mMNNNNNNNmmmmmNNMmhs+/\e[1;35m-` ", 19 | "\e[1;35m/h\e[1;37mMMNNNNNNNNMNdhs++/\e[1;35m-` ", 20 | "\e[1;35m`/\e[1;37mohdmmddhys+++/:\e[1;35m.` ", 21 | " \e[1;35m`-//////:--. ", 22 | }; 23 | -------------------------------------------------------------------------------- /src/include/logos/linux.h: -------------------------------------------------------------------------------- 1 | char *linux_accent = "\e[1;33m"; 2 | 3 | /* 4 | * c2 = 30 5 | * c1 = 29 6 | * c3 = 33 7 | */ 8 | char *linux_logo[] = { 9 | "\e[1;30m ##### ", 10 | "\e[1;30m #######\e[0m ", 11 | "\e[1;30m ##\e[1;37mO\e[1;30m#\e[1;37mO\e[1;30m##\e[0m ", 12 | "\e[1;30m #\e[1;33m#####\e[1;30m#\e[0m ", 13 | "\e[1;30m ##\e[1;37m##\e[1;33m###\e[1;37m##\e[1;30m##\e[0m ", 14 | "\e[1;30m #\e[1;37m##########\e[1;30m##\e[0m ", 15 | "\e[1;30m #\e[1;37m############\e[1;30m##\e[0m ", 16 | "\e[1;30m #\e[1;37m############\e[1;30m###\e[0m ", 17 | "\e[1;33m ##\e[1;30m#\e[1;37m###########\e[1;30m#\e[1;33m##\e[0m ", 18 | "\e[1;33m######\e[1;30m#\e[1;37m#######\e[1;30m#\e[1;33m######\e[0m", 19 | "\e[1;33m#######\e[1;30m#\e[1;37m#####\e[1;30m#\e[1;33m#######\e[0m", 20 | "\e[1;33m #####\e[1;30m#######\e[1;33m#####\e[0m ", 21 | }; 22 | -------------------------------------------------------------------------------- /src/include/logos/manjaro.h: -------------------------------------------------------------------------------- 1 | char *manjaro_accent = "\e[1;32m"; 2 | 3 | char *manjaro[] = { 4 | "\e[1;32m██████████████████ ████████", "\e[1;32m██████████████████ ████████", 5 | "\e[1;32m██████████████████ ████████", "\e[1;32m██████████████████ ████████", 6 | "\e[1;32m████████ ████████", "\e[1;32m████████ ████████ ████████", 7 | "\e[1;32m████████ ████████ ████████", "\e[1;32m████████ ████████ ████████", 8 | "\e[1;32m████████ ████████ ████████", "\e[1;32m████████ ████████ ████████", 9 | "\e[1;32m████████ ████████ ████████", "\e[1;32m████████ ████████ ████████", 10 | "\e[1;32m████████ ████████ ████████", "\e[1;32m████████ ████████ ████████", 11 | }; 12 | -------------------------------------------------------------------------------- /src/include/logos/nixos.h: -------------------------------------------------------------------------------- 1 | char *nixos_accent = "\e[1;36m"; 2 | 3 | char *nixos[] = { 4 | "\e[1;34m ▗▄▄▄ \e[1;36m▗▄▄▄▄ ▄▄▄▖ ", 5 | "\e[1;34m ▜███▙ \e[1;36m▜███▙ ▟███▛ ", 6 | "\e[1;34m ▜███▙ \e[1;36m▜███▙▟███▛ ", 7 | "\e[1;34m ▜███▙ \e[1;36m▜██████▛ ", 8 | "\e[1;34m ▟█████████████████▙ \e[1;36m▜████▛ \e[1;34m▟▙ ", 9 | "\e[1;34m ▟███████████████████▙ \e[1;36m▜███▙ \e[1;34m▟██▙ ", 10 | "\e[1;36m ▄▄▄▄▖ ▜███▙ \e[1;34m▟███▛ ", 11 | "\e[1;36m ▟███▛ ▜██▛ \e[1;34m▟███▛ ", 12 | "\e[1;36m ▟███▛ ▜▛ \e[1;34m▟███▛ ", 13 | "\e[1;36m▟███████████▛ \e[1;34m▟██████████▙", 14 | "\e[1;36m▜██████████▛ \e[1;34m▟███████████▛", 15 | "\e[1;36m ▟███▛ \e[1;34m▟▙ ▟███▛ ", 16 | "\e[1;36m ▟███▛ \e[1;34m▟██▙ ▟███▛ ", 17 | "\e[1;36m ▟███▛ \e[1;34m▜███▙ ▝▀▀▀▀ ", 18 | "\e[1;36m ▜██▛ \e[1;34m▜███▙ \e[1;36m▜██████████████████▛ ", 19 | "\e[1;36m ▜▛ \e[1;34m▟████▙ \e[1;36m▜████████████████▛ ", 20 | "\e[1;34m ▟██████▙ \e[1;36m▜███▙ ", 21 | "\e[1;34m ▟███▛▜███▙ \e[1;36m▜███▙ ", 22 | "\e[1;34m ▟███▛ ▜███▙ \e[1;36m▜███▙ ", 23 | "\e[1;34m ▝▀▀▀ ▀▀▀▀▘ \e[1;36m▀▀▀▘ ", 24 | }; 25 | -------------------------------------------------------------------------------- /src/include/logos/ubuntu.h: -------------------------------------------------------------------------------- 1 | char *ubuntu_accent = "\e[1;31m"; 2 | 3 | char *ubuntu[] = { 4 | "\e[1;31m .-/+oossssoo+\\-. ", 5 | "\e[1;31m ´:+ssssssssssssssssss+:` ", 6 | "\e[1;31m -+ssssssssssssssssssyyssss+- ", 7 | "\e[1;31m .ossssssssssssssssss\e[1;37mdMMMNy\e[1;31msssso. ", 8 | "\e[1;31m /sssssssssss\e[1;37mhdmmNNmmyNMMMMh\e[1;31mssssss\\ ", 9 | "\e[1;31m +sssssssss\e[1;37mhm\e[1;31myd\e[1;37mMMMMMMMNddddy\e[1;31mssssssss+ ", 10 | "\e[1;31m /ssssssss\e[1;37mhNMMM\e[1;31myh\e[1;37mhyyyyhmNMMMNh\e[1;31mssssssss\\ ", 11 | "\e[1;31m.ssssssss\e[1;37mdMMMNh\e[1;31mssssssssss\e[1;37mhNMMMd\e[1;31mssssssss.", 12 | "\e[1;31m+ssss\e[1;37mhhhyNMMNy\e[1;31mssssssssssss\e[1;37myNMMMy\e[1;31msssssss+", 13 | "\e[1;31moss\e[1;37myNMMMNyMMh\e[1;31mssssssssssssss\e[1;37mhmmmh\e[1;31mssssssso", 14 | "\e[1;31moss\e[1;37myNMMMNyMMh\e[1;31msssssssssssssshmmmh\e[1;31mssssssso", 15 | "\e[1;31m+ssss\e[1;37mhhhyNMMNy\e[1;31mssssssssssss\e[1;37myNMMMy\e[1;31msssssss+", 16 | "\e[1;31m.ssssssss\e[1;37mdMMMNh\e[1;31mssssssssss\e[1;37mhNMMMd\e[1;31mssssssss.", 17 | "\e[1;31m \\ssssssss\e[1;37mhNMMM\e[1;31myh\e[1;37mhyyyyhdNMMMNh\e[1;31mssssssss/ ", 18 | "\e[1;31m +sssssssss\e[1;37mdm\e[1;31myd\e[1;37mMMMMMMMMddddy\e[1;31mssssssss+ ", 19 | "\e[1;31m \\sssssssssss\e[1;37mhdmNNNNmyNMMMMh\e[1;31mssssss/ ", 20 | "\e[1;31m .ossssssssssssssssss\e[1;37mdMMMNy\e[1;31msssso. ", 21 | "\e[1;31m -+sssssssssssssssss\e[1;37myyy\e[1;31mssss+- ", 22 | "\e[1;31m `:+ssssssssssssssssss+:` ", 23 | "\e[1;31m .-\\+oossssoo+/-. ", 24 | }; 25 | -------------------------------------------------------------------------------- /src/lcfetch.c: -------------------------------------------------------------------------------- 1 | /* C stdlib */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #ifdef MACOS 14 | #include 15 | #else 16 | #include 17 | #endif 18 | #include 19 | #include 20 | /* Custom headers */ 21 | #include "lcfetch.h" 22 | #include 23 | 24 | #ifdef MACOS 25 | typedef struct timespec timespec; 26 | #else 27 | struct sysinfo sys_info; 28 | #endif 29 | 30 | struct utsname os_uname; 31 | struct passwd *pw; 32 | 33 | Display *display; 34 | int title_length; 35 | 36 | char *get_title(char *accent_color) { 37 | char *title = xmalloc(BUF_SIZE); 38 | 39 | // reduce the maximum size for the title components so we don't over-fill 40 | // the string 41 | char hostname[BUF_SIZE / 3]; 42 | gethostname(hostname, BUF_SIZE / 3); 43 | 44 | // NOTE: this approach doesn't seems to work well in some machines? 45 | /* char username[BUF_SIZE / 3]; 46 | getlogin_r(username, BUF_SIZE / 3); */ 47 | char *username = pw->pw_name; 48 | 49 | // Calculate the length of hostname + @ + username 50 | title_length = strlen(hostname) + strlen(username) + 1; 51 | 52 | // Get the accent color 53 | snprintf(title, BUF_SIZE, "%s%s\e[0m@%s%s\e[0m\n", accent_color, username, accent_color, hostname); 54 | 55 | return title; 56 | } 57 | 58 | char *get_separator() { 59 | const char *separator = get_option_string("separator"); 60 | return repeat_string((char *)separator, title_length); 61 | } 62 | 63 | char *get_os(bool return_pretty_name) { 64 | char *os = xmalloc(BUF_SIZE); 65 | char *name = xmalloc(BUF_SIZE); 66 | char *line = NULL; 67 | bool show_arch = get_option_boolean("show_arch"); 68 | size_t len; 69 | 70 | FILE *os_release = fopen("/etc/os-release", "r"); 71 | if (os_release == NULL) { 72 | // Android detection 73 | if (is_android_device()) { 74 | int android_version; 75 | FILE *android_version_prop = popen("getprop ro.build.version.release", "r"); 76 | fscanf(android_version_prop, "%d", &android_version); 77 | pclose(android_version_prop); 78 | if (show_arch) { 79 | snprintf(os, BUF_SIZE, "%s %d %s", "Android", android_version, os_uname.machine); 80 | } else { 81 | snprintf(os, BUF_SIZE, "%s %d", "Android", android_version); 82 | } 83 | xfree(name); 84 | 85 | return os; 86 | } 87 | 88 | log_fatal("Unable to open /etc/os-release"); 89 | exit(1); 90 | } 91 | while (getline(&line, &len, os_release) != -1) { 92 | // NOTE: the 'NAME' field will be used later for determining the 93 | // distribution ASCII logo and accent color 94 | if ((!return_pretty_name) && (sscanf(line, "NAME=%s", name) > 0)) { 95 | break; 96 | } else if ((!return_pretty_name) && (sscanf(line, "NAME=\"%[^\"]+", name) > 0)) { 97 | break; 98 | } else if ((return_pretty_name) && (sscanf(line, "PRETTY_NAME=\"%[^\"]+", name) > 0)) { 99 | break; 100 | } 101 | } 102 | xfree(line); 103 | fclose(os_release); 104 | 105 | if (return_pretty_name && show_arch) { 106 | snprintf(os, BUF_SIZE, "%s %s", name, os_uname.machine); 107 | } else { 108 | snprintf(os, BUF_SIZE, "%s", name); 109 | } 110 | xfree(name); 111 | 112 | return os; 113 | } 114 | 115 | char *get_kernel() { return os_uname.release; } 116 | 117 | #ifdef MACOS 118 | timespec get_macos_uptime() { 119 | struct timespec uptime; 120 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) != 0) { 121 | log_error(strerror(errno)); 122 | exit(errno); 123 | } 124 | 125 | return uptime; 126 | } 127 | #endif 128 | 129 | char *get_uptime() { 130 | long seconds; 131 | #ifdef MACOS 132 | struct timespec macos_uptime = get_macos_uptime(); 133 | seconds = macos_uptime.tv_sec; 134 | #else 135 | seconds = sys_info.uptime; 136 | #endif 137 | struct { 138 | char *name; 139 | int seconds; 140 | } units[] = { 141 | {"week", 60 * 60 * 24 * 7}, 142 | {"day", 60 * 60 * 24}, 143 | {"hour", 60 * 60}, 144 | {"min", 60}, 145 | }; 146 | 147 | int n, len = 0; 148 | char *uptime = xmalloc(BUF_SIZE); 149 | for (int i = 0; i < 4; i++) { 150 | if ((n = seconds / units[i].seconds) || i == 2) { 151 | len += snprintf(uptime + len, BUF_SIZE - len, "%d %s%s, ", n, units[i].name, n != 1 ? "s" : ""); 152 | } 153 | seconds %= units[i].seconds; 154 | } 155 | 156 | // null-terminate at the trailing comma 157 | uptime[len - 2] = '\0'; 158 | return uptime; 159 | } 160 | 161 | char *get_wm() { 162 | char *wm_name = NULL; 163 | 164 | if (display != NULL) { 165 | Window *top_win = NULL; 166 | 167 | top_win = 168 | (Window *)get_property(display, DefaultRootWindow(display), XA_WINDOW, "_NET_SUPPORTING_WM_CHECK", NULL); 169 | if (!top_win) { 170 | top_win = (Window *)get_property(display, DefaultRootWindow(display), XA_CARDINAL, 171 | "_WIN_SUPPORTING_WM_CHECK", NULL); 172 | if (!top_win) { 173 | log_debug("Cannot get window manager required properties." 174 | "(_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)\n", 175 | stderr); 176 | XFree(top_win); 177 | return "lcfetch was not able to recognize your window manager"; 178 | } 179 | } 180 | 181 | wm_name = get_property(display, *top_win, XInternAtom(display, "UTF8_STRING", 0), "_NET_WM_NAME", NULL); 182 | if (!wm_name) { 183 | wm_name = get_property(display, *top_win, XA_STRING, "_NET_WM_NAME", NULL); 184 | if (!wm_name) { 185 | log_debug("Cannot get name of the window manager (_NET_WM_NAME).\n"); 186 | xfree(wm_name); 187 | return "lcfetch was not able to recognize your window manager"; 188 | } 189 | } 190 | 191 | XFree(top_win); 192 | } 193 | 194 | return wm_name; 195 | } 196 | 197 | char *get_resolution() { 198 | char *res = xmalloc(BUF_SIZE); 199 | 200 | if (display != NULL) { 201 | Screen *screen = DefaultScreenOfDisplay(display); 202 | bool display_refresh_rate = get_option_boolean("display_refresh_rate"); 203 | 204 | snprintf(res, BUF_SIZE, "%dx%d", screen->width, screen->height); 205 | if (display_refresh_rate) { 206 | Window root = RootWindow(display, 0); 207 | XRRScreenConfiguration *conf = XRRGetScreenInfo(display, root); 208 | snprintf(res + strlen(res), BUF_SIZE, " @ %dHz", XRRConfigCurrentRate(conf)); 209 | xfree(conf); 210 | } 211 | } else { 212 | // If we were unable to detect the screen resolution then return NULL 213 | xfree(res); 214 | return NULL; 215 | } 216 | 217 | return res; 218 | } 219 | 220 | char *get_shell() { 221 | char *shell = xmalloc(BUF_SIZE); 222 | if (is_android_device() && access("/etc/shells", F_OK) != 0) { 223 | // Android does not have an /etc/shells file 224 | // so we need a special treatment for it 225 | char *termux_shell = xmalloc(BUF_SIZE); 226 | char *read_termux_shell = xmalloc(BUF_SIZE); 227 | snprintf(read_termux_shell, BUF_SIZE, "readlink %s/.termux/shell", getenv("HOME")); 228 | 229 | FILE *shell_link = popen(read_termux_shell, "r"); 230 | fscanf(shell_link, "%s", termux_shell); 231 | pclose(shell_link); 232 | xfree(read_termux_shell); 233 | 234 | char *shell_name = strrchr(termux_shell, '/'); 235 | xfree(termux_shell); 236 | 237 | // Copy only the last '/', e.g. /zsh → zsh 238 | strncpy(shell, shell_name + 1, BUF_SIZE); 239 | } else { 240 | char *user_shell; 241 | // If we should use pw_shell from passwd struct for a more accurate 242 | // and portable shell detection since SHELL environment variable does not 243 | // always exists 244 | if (!(user_shell = getenv("SHELL"))) { 245 | user_shell = pw->pw_shell; 246 | }; 247 | 248 | char *shell_name = strrchr(user_shell, '/'); 249 | 250 | // If the shell does not contains a separator in the path, e.g. 251 | // zsh instead of /usr/bin/zsh then write it directly 252 | if (shell_name == NULL) { 253 | return user_shell; 254 | } else { 255 | // Copy only the last '/', e.g. /zsh → zsh 256 | strncpy(shell, shell_name + 1, BUF_SIZE); 257 | } 258 | } 259 | 260 | return shell; 261 | } 262 | 263 | char *get_terminal() { 264 | unsigned char *property; 265 | char *terminal = xmalloc(BUF_SIZE); 266 | // Windows Terminal session, we will use it for WSL detection 267 | char *wt_session = getenv("WT_SESSION"); 268 | // Get the TERM environment variable, we will use it for TTY detection 269 | char *environment_term = getenv("TERM"); 270 | 271 | // Check if we are running in a TTY, a graphical X interface or WSL (on Windows Terminal) 272 | if (display != NULL) { 273 | // Get the current window 274 | unsigned long _, window = RootWindow(display, XDefaultScreen(display)); 275 | // Get the active window and the window class name 276 | Atom a, active_win = XInternAtom(display, "_NET_ACTIVE_WINDOW", 1), 277 | win_class = XInternAtom(display, "WM_CLASS", 1); 278 | 279 | XGetWindowProperty(display, window, active_win, 0, 64, 0, 0, &a, (int *)&_, &_, &_, &property); 280 | window = (property[3] << 24) + (property[2] << 16) + (property[1] << 8) + property[0]; 281 | xfree(property); 282 | 283 | XGetWindowProperty(display, window, win_class, 0, 64, 0, 0, &a, (int *)&_, &_, &_, &property); 284 | 285 | snprintf(terminal, BUF_SIZE, "%s", property); 286 | xfree(property); 287 | } else { 288 | // Check if we are running on WSL inside the Windows Terminal 289 | if (wt_session != NULL) { 290 | strncpy(terminal, "Windows Terminal", BUF_SIZE); 291 | } else { 292 | // In TTY, $TERM is simply returned as "linux" so we get the actual TTY name 293 | if (strcmp(environment_term, "linux") == 0) { 294 | strncpy(terminal, ttyname(STDIN_FILENO), BUF_SIZE); 295 | } else if (is_android_device()) { 296 | strncpy(terminal, "Termux", BUF_SIZE); 297 | } 298 | } 299 | } 300 | 301 | return terminal; 302 | } 303 | 304 | char *get_packages() { 305 | char *pkg_managers[] = {"apt", "dnf", "rpm", "nix", "emerge", "pacman", "apk", "xbps-query", "flatpak"}; 306 | char *packages = xmalloc(BUF_SIZE * 2); 307 | int apt, rpm, dnf, emerge, pacman, aur, nix, apk, xbps, flatpak = 0; 308 | 309 | // Add an initial empty string to our packages characters array to be able 310 | // to use snprintf() for append to it later 311 | snprintf(packages, BUF_SIZE, ""); 312 | 313 | // Store a count of the displayed package managers so we can dynamically add the commas on them 314 | int displayed_pkg_managers = 0; 315 | for (int i = 0; i < COUNT(pkg_managers); i++) { 316 | char *pkg_manager = pkg_managers[i]; 317 | // Set the which command to run later 318 | char *which_command = xmalloc(BUF_SIZE); 319 | snprintf(which_command, BUF_SIZE, "which %s >/dev/null 2>&1", pkg_manager); 320 | // If the package manager is installed 321 | if (system(which_command) == 0) { 322 | if (strcmp(pkg_manager, "apt") == 0) { 323 | FILE *dpkg_packages = popen("dpkg --list 2> /dev/null | grep -c ^ii", "r"); 324 | fscanf(dpkg_packages, "%d", &apt); 325 | pclose(dpkg_packages); 326 | // If there are packages installed then let's print the packages count 327 | // NOTE: this is for avoiding values like "0 (foo)" because you can install 328 | // APT and others packages managers in almost any distro. 329 | if (apt > 0) { 330 | if (displayed_pkg_managers >= 1) { 331 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", apt, "dpkg"); 332 | } else { 333 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", apt, "dpkg"); 334 | } 335 | displayed_pkg_managers++; 336 | } 337 | } else if (strcmp(pkg_manager, "dnf") == 0) { 338 | // Using DNF package cache is much faster than RPM 339 | FILE *dnf_packages = 340 | popen("sqlite3 /var/cache/dnf/packages.db 'SELECT count(pkg) FROM installed'", "r"); 341 | fscanf(dnf_packages, "%d", &dnf); 342 | pclose(dnf_packages); 343 | if (dnf > 0) { 344 | if (displayed_pkg_managers >= 1) { 345 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", dnf, pkg_manager); 346 | } else { 347 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", dnf, pkg_manager); 348 | } 349 | displayed_pkg_managers++; 350 | } 351 | } else if ((strcmp(pkg_manager, "rpm") == 0) && (dnf == 0)) { 352 | // If the current package manager is RPM and DNF packages count is zero 353 | // because if we already scanned the packages with DNF it makes no sense 354 | // to scan them again 355 | FILE *rpm_packages = popen("rpm -qa 2> /dev/null | wc -l", "r"); 356 | fscanf(rpm_packages, "%d", &rpm); 357 | pclose(rpm_packages); 358 | if (rpm > 0) { 359 | if (displayed_pkg_managers >= 1) { 360 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", rpm, pkg_manager); 361 | } else { 362 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", rpm, pkg_manager); 363 | } 364 | displayed_pkg_managers++; 365 | } 366 | } else if (strcmp(pkg_manager, "emerge") == 0) { 367 | FILE *emerge_packages = popen("ls -d /var/db/pkg/*/* | wc -l", "r"); 368 | fscanf(emerge_packages, "%d", &emerge); 369 | pclose(emerge_packages); 370 | if (emerge > 0) { 371 | if (displayed_pkg_managers >= 1) { 372 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", emerge, pkg_manager); 373 | } else { 374 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", emerge, pkg_manager); 375 | } 376 | displayed_pkg_managers++; 377 | } 378 | } else if (strcmp(pkg_manager, "pacman") == 0) { 379 | FILE *pacman_packages = popen("pacman -Qq 2> /dev/null | wc -l", "r"); 380 | fscanf(pacman_packages, "%d", &pacman); 381 | pclose(pacman_packages); 382 | FILE *aur_packages = popen("pacman -Qqm 2> /dev/null | wc -l", "r"); 383 | fscanf(aur_packages, "%d", &aur); 384 | pclose(aur_packages); 385 | if (pacman > 0) { 386 | if (displayed_pkg_managers >= 1) { 387 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", pacman, pkg_manager); 388 | } else { 389 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", pacman, pkg_manager); 390 | } 391 | displayed_pkg_managers++; 392 | } 393 | if (aur > 0) { 394 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", aur, "AUR"); 395 | displayed_pkg_managers++; 396 | } 397 | } else if (strcmp(pkg_manager, "nix") == 0) { 398 | FILE *nix_current_system = popen("nix-store -q --requisites /run/current-system/sw", "r"); 399 | fscanf(nix_current_system, "%d", &nix); 400 | pclose(nix_current_system); 401 | FILE *nix_profile = popen("nix-store -q --requisites ~/.nix-profile", "r"); 402 | fscanf(nix_profile, "%d", &nix); 403 | pclose(nix_profile); 404 | if (nix > 0) { 405 | if (displayed_pkg_managers >= 1) { 406 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", nix, pkg_manager); 407 | } else { 408 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", nix, pkg_manager); 409 | } 410 | displayed_pkg_managers++; 411 | } 412 | } else if (strcmp(pkg_manager, "apk") == 0) { 413 | FILE *apk_packages = popen("apk info 2> /dev/null | wc -l", "r"); 414 | fscanf(apk_packages, "%d", &apk); 415 | pclose(apk_packages); 416 | if (apk > 0) { 417 | if (displayed_pkg_managers >= 1) { 418 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", apk, pkg_manager); 419 | } else { 420 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", apk, pkg_manager); 421 | } 422 | displayed_pkg_managers++; 423 | } 424 | } else if (strcmp(pkg_manager, "xbps-query") == 0) { 425 | FILE *xbps_packages = popen("xbps-query -l 2> /dev/null | wc -l", "r"); 426 | fscanf(xbps_packages, "%d", &xbps); 427 | pclose(xbps_packages); 428 | if (xbps > 0) { 429 | if (displayed_pkg_managers >= 1) { 430 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", xbps, pkg_manager); 431 | } else { 432 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", xbps, pkg_manager); 433 | } 434 | displayed_pkg_managers++; 435 | } 436 | } else if (strcmp(pkg_manager, "flatpak") == 0) { 437 | // NOTE: it seems that flatpak does not like to be called from a popen so it fails in 438 | // a really stupid way sending a non-sense error, this is why we are not using 'flatpak list' 439 | // for getting the flatpak packages at the moment 440 | FILE *flatpak_packages = popen("echo \"$(( $(ls /var/lib/flatpak/app 2> /dev/null | wc -l) + $(ls " 441 | "/var/lib/flatpak/runtime 2> /dev/null | wc -l) ))\"", 442 | "r"); 443 | fscanf(flatpak_packages, "%d", &flatpak); 444 | pclose(flatpak_packages); 445 | if (flatpak > 0) { 446 | if (displayed_pkg_managers >= 1) { 447 | snprintf(packages + strlen(packages), BUF_SIZE, ", %d (%s)", flatpak, pkg_manager); 448 | } else { 449 | snprintf(packages + strlen(packages), BUF_SIZE, "%d (%s)", flatpak, pkg_manager); 450 | } 451 | displayed_pkg_managers++; 452 | } 453 | } 454 | } 455 | xfree(which_command); 456 | } 457 | 458 | // If the packages weren't calculated because the package manager is not supported then 459 | // return an error message that actually makes sense 460 | if (strcmp(packages, "") == 0) { 461 | strncat(packages, "lcfetch was not able to recognize your system package manager", BUF_SIZE); 462 | } 463 | 464 | return packages; 465 | } 466 | 467 | char *get_cpu() { 468 | char *line = NULL; 469 | char *cpu = xmalloc(BUF_SIZE); 470 | char *cpu_model = xmalloc(BUF_SIZE / 2); 471 | int num_cores = 0; 472 | int cpu_freq = 0; 473 | int prec = 3; 474 | double freq; 475 | char freq_unit[] = "GHz"; 476 | size_t len; 477 | 478 | // TODO: find a way to get cpu cores and model without duplicating code 479 | // CPU CORES 480 | FILE *cpu_cores = fopen("/proc/cpuinfo", "r"); 481 | if (cpu_cores == NULL) { 482 | log_fatal("Unable to open /proc/cpuinfo"); 483 | exit(1); 484 | } 485 | while (getline(&line, &len, cpu_cores) != -1) { 486 | num_cores += sscanf(line, "processor : %[^\n@]", cpu_model); 487 | } 488 | fclose(cpu_cores); 489 | xfree(line); 490 | 491 | // CPU MODEL 492 | line = NULL; 493 | FILE *cpu_model_f = fopen("/proc/cpuinfo", "r"); 494 | if (cpu_model_f == NULL) { 495 | log_fatal("Unable to open /proc/cpuinfo"); 496 | exit(1); 497 | } 498 | while (getline(&line, &len, cpu_model_f) != -1) { 499 | if (sscanf(line, "model name : %[^\n@]", cpu_model) > 0) { 500 | break; 501 | } 502 | } 503 | fclose(cpu_model_f); 504 | xfree(line); 505 | 506 | // Hijack processor name detection in Android devices 507 | // without permissive SELinux 508 | if ((strlen(cpu_model) < 2) && is_android_device() && (system("which getprop >/dev/null 2>&1") == 0)) { 509 | FILE *android_processor_prop = popen("getprop ro.product.board", "r"); 510 | fscanf(android_processor_prop, "%s", cpu_model); 511 | pclose(android_processor_prop); 512 | } 513 | 514 | line = NULL; 515 | FILE *cpufreq = fopen("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r"); 516 | // If /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq file exists 517 | // then read the CPU frequency from it. Otherwise, fallback to /proc/cpuinfo 518 | if (cpufreq) { 519 | // cpuinfo_max_freq has only a line so we don't need a while loop 520 | if (getline(&line, &len, cpufreq) != -1) { 521 | sscanf(line, "%d", &cpu_freq); 522 | // KHz / 1000 = MHz 523 | cpu_freq /= 1000; 524 | } 525 | } else { 526 | cpufreq = fopen("/proc/cpuinfo", "r"); 527 | if (cpufreq == NULL) { 528 | log_fatal("Unable to open /proc/cpuinfo"); 529 | exit(1); 530 | } 531 | while (getline(&line, &len, cpufreq) != -1) { 532 | if (sscanf(line, "cpu MHz: %lf", &freq) > 0) { 533 | break; 534 | } 535 | } 536 | // Convert frequency to integer, e.g. 1483 537 | cpu_freq = (int)freq; 538 | } 539 | fclose(cpufreq); 540 | xfree(line); 541 | 542 | // If the cpu frequency is lower than 1000 then it makes no sense 543 | // to render it as GHz so we will change it to MHz 544 | if (cpu_freq < 1000) { 545 | freq = (double)cpu_freq; 546 | freq_unit[0] = 'M'; 547 | prec = 0; // Show freq as integer 548 | } else { 549 | // MHz / 1000 = GHz 550 | freq = cpu_freq / 1000.0; 551 | 552 | while (cpu_freq % 10 == 0) { 553 | prec--; 554 | cpu_freq /= 10; 555 | } 556 | } 557 | 558 | // e.g. Intel i5 760 (4) @ 2.8GHz 559 | snprintf(cpu, BUF_SIZE, "%s (%d) @ %.*f%s", strlen(cpu_model) > 1 ? cpu_model : "", num_cores, prec, freq, 560 | freq_unit); 561 | xfree(cpu_model); 562 | 563 | // Remove unneeded information 564 | bool return_short_cpu_info = get_option_boolean("short_cpu_info"); 565 | if (return_short_cpu_info) { 566 | cpu = remove_substr(remove_substr(cpu, "(R)"), "Core(TM)"); 567 | } 568 | cpu = remove_substr(cpu, "CPU"); 569 | 570 | // Remove the annoying whitespaces between characters in the string 571 | truncate_whitespaces(cpu); 572 | 573 | if (num_cores == 0) { 574 | *cpu = '\0'; 575 | } 576 | 577 | return cpu; 578 | } 579 | 580 | char *get_memory() { 581 | char *line = NULL; 582 | char *memory = xmalloc(BUF_SIZE); 583 | bool display_memory_in_gib = get_option_boolean("memory_in_gib"); 584 | 585 | int total_memory, used_memory; 586 | int total, shared, memfree, buffers, cached, reclaimable; 587 | size_t len; 588 | 589 | FILE *meminfo = fopen("/proc/meminfo", "r"); 590 | if (meminfo == NULL) { 591 | log_fatal("Unable to open /proc/meminfo"); 592 | exit(1); 593 | } 594 | while (getline(&line, &len, meminfo) != -1) { 595 | /* if sscanf doesn't find a match, pointer is untouched */ 596 | sscanf(line, "MemTotal: %d", &total); 597 | sscanf(line, "Shmem: %d", &shared); 598 | sscanf(line, "MemFree: %d", &memfree); 599 | sscanf(line, "Buffers: %d", &buffers); 600 | sscanf(line, "Cached: %d", &cached); 601 | sscanf(line, "SReclaimable: %d", &reclaimable); 602 | } 603 | fclose(meminfo); 604 | xfree(line); 605 | 606 | // we're using same calculation as neofetch 607 | // KiB / 1024 = MiB 608 | used_memory = (total + shared - memfree - buffers - cached - reclaimable) / 1024; 609 | total_memory = total / 1024; 610 | if (display_memory_in_gib) { 611 | // MiB / 1024 = GiB 612 | float used_memory_gib = (double)used_memory / (double)1024; 613 | float total_memory_gib = (double)total_memory / (double)1024; 614 | snprintf(memory, BUF_SIZE, "%.2fGiB / %.2fGiB", used_memory_gib, total_memory_gib); 615 | } else { 616 | snprintf(memory, BUF_SIZE, "%dMiB / %dMiB", used_memory, total_memory); 617 | } 618 | 619 | return memory; 620 | } 621 | 622 | char *get_colors_dark() { 623 | char *dark_colors = xmalloc(BUF_SIZE); 624 | char *str = dark_colors; 625 | const char *colors_style = get_option_string("colors_style"); 626 | const char *colors_icon = get_option_string("colors_icon"); 627 | 628 | for (int i = 0; i < 8; i++) { 629 | if (strlen(colors_icon) > 0) { 630 | int color_message_len = snprintf(NULL, 0, "\e[30m%s", colors_icon); 631 | sprintf(str, "\e[3%dm%s", i, colors_icon); 632 | str += color_message_len; 633 | } else { 634 | if (strcasecmp(colors_style, "circles") == 0) { 635 | sprintf(str, "\e[3%dm⬤ ", i); 636 | str += 10; 637 | } else if (strcasecmp(colors_style, "ghosts") == 0) { 638 | sprintf(str, "\e[3%dm ", i); 639 | str += 11; 640 | } else if (strcasecmp(colors_style, "classic") == 0) { 641 | sprintf(str, "\e[4%dm ", i); 642 | str += 8; 643 | } 644 | } 645 | } 646 | snprintf(str, 5, "\e[0m"); 647 | 648 | return dark_colors; 649 | } 650 | 651 | char *get_colors_bright() { 652 | char *bright_colors = xmalloc(BUF_SIZE); 653 | char *str = bright_colors; 654 | const char *colors_style = get_option_string("colors_style"); 655 | const char *colors_icon = get_option_string("colors_icon"); 656 | 657 | for (int i = 8; i < 16; i++) { 658 | if (strlen(colors_icon) > 0) { 659 | int color_message_len = snprintf(NULL, 0, "\e[38;5;0m%s", colors_icon); 660 | sprintf(str, "\e[38;5;%dm%s", i, colors_icon); 661 | str += color_message_len + (i >= 10 ? 1 : 0); 662 | } else { 663 | if (strcasecmp(colors_style, "circles") == 0) { 664 | sprintf(str, "\e[38;5;%dm⬤ ", i); 665 | str += 14 + (i >= 10 ? 1 : 0); 666 | } else if (strcasecmp(colors_style, "ghosts") == 0) { 667 | sprintf(str, "\e[38;5;%dm ", i); 668 | str += 15 + (i >= 10 ? 1 : 0); 669 | } else if (strcasecmp(colors_style, "classic") == 0) { 670 | sprintf(str, "\e[48;5;%dm ", i); 671 | str += 12 + (i >= 10 ? 1 : 0); 672 | } 673 | } 674 | } 675 | snprintf(str, 5, "\e[0m"); 676 | 677 | return bright_colors; 678 | } 679 | 680 | void print_info(char *distro_logo) { 681 | // If the ASCII distro logo should be printed 682 | bool display_logo = get_option_boolean("display_logo"); 683 | // The delimiter shown between the field message and the information, e.g. 684 | // OS: Fedora 34 (KDE Plasma) x86_64 685 | // ^ 686 | // delimiter 687 | const char *delimiter = get_option_string("delimiter"); 688 | 689 | // Get the accent color and logo for the current distro 690 | char *current_distro = get_os(0); 691 | const char *custom_distro_logo = get_option_string("ascii_distro"); 692 | const char *custom_accent_color = get_option_string("accent_color"); 693 | // Compare the current distribution first so we can override the information later 694 | // with the custom ascii distro logo 695 | char **logo = get_distro_logo(current_distro); 696 | int logo_rows = get_distro_logo_rows(current_distro); 697 | char *accent_color = get_distro_accent(current_distro); 698 | if (strlen(custom_distro_logo) > 0) { 699 | logo = get_distro_logo((char *)custom_distro_logo); 700 | logo_rows = get_distro_logo_rows((char *)custom_distro_logo); 701 | xfree(accent_color); 702 | accent_color = get_distro_accent((char *)custom_distro_logo); 703 | } 704 | if (distro_logo != NULL) { 705 | logo = get_distro_logo(distro_logo); 706 | logo_rows = get_distro_logo_rows(distro_logo); 707 | xfree(accent_color); 708 | accent_color = get_distro_accent(distro_logo); 709 | } 710 | if (strlen(custom_accent_color) > 0) { 711 | xfree(accent_color); 712 | accent_color = get_custom_accent((char *)custom_accent_color); 713 | } 714 | xfree(current_distro); 715 | 716 | bool is_custom_logo = false; 717 | struct custom_logo custom_ascii_logo = get_custom_logo(); 718 | if (custom_ascii_logo.cols > 0 && distro_logo == NULL) { 719 | memmove(logo, custom_ascii_logo.arr, BUF_SIZE); 720 | logo_rows = custom_ascii_logo.cols; 721 | is_custom_logo = true; 722 | xfree(custom_ascii_logo.arr); 723 | } 724 | 725 | // Get the amount of enabled information fields 726 | int enabled_fields = get_table_size("enabled_fields"); 727 | if (display_logo) { 728 | // Get the logo length, substracting the ANSI escapes length 729 | int logo_length = is_custom_logo ? custom_ascii_logo.rows : (utf8len(logo[0]) - strlen("\e[1;00m")); 730 | // Get the gap that should be between the logo and the information 731 | int gap_size = get_option_number("gap"); 732 | char *gap_logo_info = repeat_string(" ", gap_size); 733 | // This gap is specially used when there's more information but the 734 | // logo is already complete 735 | char *gap_logo = repeat_string(" ", logo_length); 736 | 737 | // Store the amount of displayed information so we can determine if there is still information 738 | // that needs to be rendered after the logo finishes 739 | int displayed_info = 0; 740 | for (int i = 0; i < logo_rows; i++) { 741 | // Count two extra fields for (user@host and the separator) 742 | if (i >= enabled_fields) { 743 | // If we've run out of information to show then we will 744 | // just print the next logo line 745 | printf("%s%s%s\n", accent_color, logo[i], "\e[0m"); 746 | } else { 747 | displayed_info++; 748 | 749 | const char *field = get_subtable_string("enabled_fields", i + 1); 750 | if (strcasecmp(field, "Colors") == 0) { 751 | print_colors(logo[i], (i + 1 >= logo_rows) ? "" : logo[i + 1], gap_logo, gap_logo_info); 752 | i++; 753 | } else if (strcmp(field, "") == 0) { 754 | // If we should draw an empty line as a separator 755 | printf("%s%s\n", logo[i], "\e[0m"); 756 | } else { 757 | print_field(logo[i], gap_logo_info, delimiter, accent_color, field); 758 | } 759 | } 760 | } 761 | // If there's still information that needs to be rendered then let's render them 762 | // leaving a padding from the logo 763 | if (displayed_info < enabled_fields + 2) { 764 | for (int i = displayed_info + 1; i <= enabled_fields; i++) { 765 | const char *field = get_subtable_string("enabled_fields", i); 766 | if (strcasecmp(field, "colors") == 0) { 767 | print_colors("", "", gap_logo, gap_logo_info); 768 | } else if (strcmp(field, "") == 0) { 769 | // If we should draw an empty line as a separator 770 | printf("%s\n", gap_logo); 771 | } else { 772 | print_field(gap_logo, gap_logo_info, delimiter, accent_color, field); 773 | } 774 | } 775 | } 776 | 777 | // If the gap between the logo and the information was higher than 0 778 | // then we will need to free it 779 | if (gap_size >= 1) { 780 | xfree(gap_logo_info); 781 | } 782 | xfree(gap_logo); 783 | } else { 784 | // Get the gap that should be between the left terminal border and the information 785 | int gap_size = get_option_number("gap"); 786 | char *gap_term_info = repeat_string(" ", gap_size); 787 | 788 | // Do not add gaps if gap_size is 0 789 | if (gap_size == 0) { 790 | gap_term_info = ""; 791 | } 792 | 793 | for (int i = 1; i <= enabled_fields; i++) { 794 | const char *field = get_subtable_string("enabled_fields", i); 795 | if (strcasecmp(field, "colors") == 0) { 796 | print_colors("", "", gap_term_info, ""); 797 | } else { 798 | // If we should draw an empty line as a separator 799 | if (strcmp(field, "") == 0) { 800 | printf("\n"); 801 | } else { 802 | print_field(NULL, gap_term_info, delimiter, accent_color, field); 803 | } 804 | } 805 | } 806 | } 807 | xfree(accent_color); 808 | } 809 | 810 | int main(int argc, char *argv[]) { 811 | // Command-line arguments (CLI) 812 | int c; 813 | char *distro_logo = NULL; 814 | char *config_file_path = NULL; 815 | while (1) { 816 | static struct option long_options[] = { 817 | {"help", no_argument, NULL, 'h'}, 818 | {"version", no_argument, NULL, 'v'}, 819 | {"config", required_argument, NULL, 'c'}, 820 | {"distro_name", required_argument, NULL, 'd'}, 821 | }; 822 | 823 | int option_index = 0; 824 | c = getopt_long(argc, argv, "hvcd:", long_options, &option_index); 825 | 826 | // Detect the end of the command-line options 827 | if (c == -1) { 828 | break; 829 | } 830 | 831 | switch (c) { 832 | case 'v': 833 | version(); 834 | case 'h': 835 | help(); 836 | exit(0); 837 | case 'c': 838 | config_file_path = optarg; 839 | break; 840 | case 'd': 841 | distro_logo = optarg; 842 | break; 843 | default: 844 | help(); 845 | exit(1); 846 | } 847 | } 848 | 849 | // Start our Lua environment 850 | start_lua(config_file_path); 851 | 852 | // populate the os_uname struct 853 | uname(&os_uname); 854 | 855 | #ifndef MACOS 856 | // populate the sys_info struct 857 | sysinfo(&sys_info); 858 | #endif 859 | 860 | // Get User ID 861 | const uid_t uid = getuid(); 862 | 863 | // populate the passwd struct 864 | pw = getpwuid(uid); 865 | 866 | display = XOpenDisplay(NULL); 867 | 868 | // Disable line wrapping so we can keep the logo intact on small terminals 869 | printf("\e[?7l"); 870 | // Print all stuff (logo, information) 871 | print_info(distro_logo); 872 | // Re-enable line wrapping again 873 | printf("\e[?7h"); 874 | 875 | if (display != NULL) { 876 | XCloseDisplay(display); 877 | } 878 | 879 | // Close our Lua environment and release resources 880 | stop_lua(); 881 | return 0; 882 | } 883 | -------------------------------------------------------------------------------- /src/lib/cli.c: -------------------------------------------------------------------------------- 1 | #include "lcfetch.h" 2 | #include 3 | #include 4 | 5 | void version() { 6 | printf("lcfetch %s\n\n%s\n", VERSION, COPYRIGHT); 7 | exit(0); 8 | } 9 | 10 | void help() { 11 | const char *help_message = "Usage: lcfetch [OPTIONS]\n\n" 12 | "OPTIONS:\n" 13 | "\t-c, --config /path/to/config\tSpecify a path to a custom config " 14 | "file\n" 15 | "\t-d, --distro_name distro_name\tDistribution logo\n" 16 | "\t-h, --help\t\t\t\t\t\tPrint this message and exit\n" 17 | "\t-v, --version\t\t\t\t\tShow lcfetch version\n\n" 18 | "Report bugs to https://github.com/NTBBloodbath/lcfetch/issues\n"; 19 | printf("%s", help_message); 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/lua_api.c: -------------------------------------------------------------------------------- 1 | /* C stdlib */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | /* Lua headers */ 7 | #include 8 | #include 9 | #include 10 | /* Custom headers */ 11 | #include "lcfetch.h" 12 | 13 | // Lua interpreter state 14 | static lua_State *lua; 15 | 16 | /** 17 | * Get the lcfetch configuration file path 18 | */ 19 | char *get_configuration_file_path() { 20 | char *config_directory = getenv("XDG_CONFIG_HOME"); 21 | if (!config_directory) { 22 | config_directory = strncat(getenv("HOME"), "/.config", -1); 23 | } 24 | char *config_file_path = strncat(config_directory, "/lcfetch/config.lua", -1); 25 | 26 | return config_file_path; 27 | } 28 | 29 | /** 30 | * Print Lua API stack to stdout, for debugging purposes 31 | * NOTE: remove the '__attribute__((unused))' statement when using it 32 | */ 33 | static void __attribute__((unused)) dumpstack(lua_State *L) { 34 | int top = lua_gettop(L); 35 | for (int i = 1; i <= top; i++) { 36 | printf("%d\t%s\t", i, luaL_typename(L, i)); 37 | switch (lua_type(L, i)) { 38 | case LUA_TNUMBER: 39 | printf("%g\n", lua_tonumber(L, i)); 40 | break; 41 | case LUA_TSTRING: 42 | printf("%s\n", lua_tostring(L, i)); 43 | break; 44 | case LUA_TBOOLEAN: 45 | printf("%s\n", (lua_toboolean(L, i) ? "true" : "false")); 46 | break; 47 | case LUA_TNIL: 48 | printf("%s\n", "nil"); 49 | break; 50 | default: 51 | printf("%p\n", lua_topointer(L, i)); 52 | break; 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * Start the Lua interpreter and load the Lua interface functions 59 | */ 60 | void start_lua(const char *config_file_path) { 61 | char *config_file = (char *)config_file_path; 62 | 63 | // Get the default configuration path for lcfetch, 64 | // e.g. /home/user/.config/lcfetch/config.lua 65 | if (config_file_path == NULL) { 66 | config_file = get_configuration_file_path(); 67 | } 68 | 69 | // Create a pointer to an empty Lua environment 70 | lua = luaL_newstate(); 71 | // Load the Lua libraries to make the Lua environment usable 72 | luaL_openlibs(lua); 73 | // Set the stack top to a specific value (0) 74 | lua_settop(lua, 0); 75 | // Load the default configurations 76 | init_options(); 77 | // Load the user configurations file 78 | luaL_loadfile(lua, config_file) || lua_pcall(lua, 0, 0, 0); 79 | } 80 | 81 | /** 82 | * Stop the Lua interpreter 83 | */ 84 | void stop_lua(void) { lua_close(lua); } 85 | 86 | /** 87 | * Set default values to lcfetch configurations 88 | */ 89 | void init_options(void) { 90 | // Create a new empty table 91 | lua_newtable(lua); 92 | 93 | // Set the default basic types options (strings, numbers, booleans) 94 | set_table_string("accent_color", ""); 95 | set_table_string("ascii_distro", ""); 96 | set_table_string("colors_icon", ""); 97 | set_table_string("colors_style", "classic"); 98 | set_table_string("delimiter", ":"); 99 | set_table_string("separator", "-"); 100 | set_table_boolean("show_arch", 1); 101 | set_table_boolean("display_refresh_rate", 0); 102 | set_table_boolean("short_cpu_info", 1); 103 | set_table_boolean("memory_in_gib", 1); 104 | set_table_boolean("display_logo", 1); 105 | set_table_number("gap", 3); 106 | 107 | // Fields messages 108 | set_table_string("os_message", "OS"); 109 | set_table_string("kernel_message", "Kernel"); 110 | set_table_string("uptime_message", "Uptime"); 111 | set_table_string("packages_message", "Packages"); 112 | set_table_string("resolution_message", "Resolution"); 113 | set_table_string("wm_message", "WM"); 114 | set_table_string("shell_message", "Shell"); 115 | set_table_string("terminal_message", "Terminal"); 116 | set_table_string("cpu_message", "CPU"); 117 | set_table_string("memory_message", "Memory"); 118 | 119 | // Set the global "options" table 120 | lua_setglobal(lua, "options"); 121 | 122 | // Create a new subtable in the "options" table 123 | set_table_subtable("enabled_fields"); 124 | // Assign "options.enabled_fields" values 125 | set_subtable_string("enabled_fields", "User"); 126 | set_subtable_string("enabled_fields", "Separator"); 127 | set_subtable_string("enabled_fields", "OS"); 128 | set_subtable_string("enabled_fields", "Kernel"); 129 | set_subtable_string("enabled_fields", "Uptime"); 130 | set_subtable_string("enabled_fields", "Packages"); 131 | set_subtable_string("enabled_fields", ""); // Newline 132 | set_subtable_string("enabled_fields", "WM"); 133 | set_subtable_string("enabled_fields", "Resolution"); 134 | set_subtable_string("enabled_fields", ""); // Newline 135 | set_subtable_string("enabled_fields", "Shell"); 136 | set_subtable_string("enabled_fields", "Terminal"); 137 | set_subtable_string("enabled_fields", ""); // Newline 138 | set_subtable_string("enabled_fields", "CPU"); 139 | set_subtable_string("enabled_fields", "Memory"); 140 | set_subtable_string("enabled_fields", ""); // Newline 141 | set_subtable_string("enabled_fields", "Colors"); 142 | } 143 | 144 | /** 145 | * Get a table size 146 | */ 147 | int get_table_size(const char *table) { 148 | int table_length = 0; 149 | 150 | lua_getglobal(lua, "options"); 151 | if (luaL_getsubtable(lua, -1, table)) { 152 | // Push the table length 153 | lua_len(lua, -1); 154 | table_length = luaL_checknumber(lua, -1); 155 | // Remove the table length from the stack 156 | lua_pop(lua, 1); 157 | } 158 | 159 | return table_length; 160 | } 161 | 162 | /** 163 | * Check if a given string is in the given table 164 | */ 165 | bool table_contains_string(const char *table, const char *key) { 166 | const char *value = NULL; 167 | 168 | lua_getglobal(lua, "options"); 169 | if (luaL_getsubtable(lua, -1, table)) { 170 | int table_length = get_table_size(table); 171 | // Iterate over all the table elements 172 | for (int i = 1; i <= table_length; i++) { 173 | // Get and store the current index value in the table 174 | lua_rawgeti(lua, -1, i); 175 | value = luaL_checkstring(lua, -1); 176 | // Remove the value from the stack 177 | lua_pop(lua, 1); 178 | // If the wanted value is in the table (case-insensitive) 179 | // then let's return 1 and break the bucle 180 | if (strcasecmp(value, key) == 0) { 181 | return true; 182 | } 183 | } 184 | } 185 | lua_pop(lua, 2); 186 | 187 | return false; 188 | } 189 | 190 | /** 191 | * Get a boolean option from the configuration file 192 | */ 193 | bool get_option_boolean(const char *opt) { 194 | bool bool_opt; 195 | 196 | lua_getglobal(lua, "options"); 197 | lua_getfield(lua, -1, opt); 198 | bool_opt = lua_toboolean(lua, -1); 199 | lua_pop(lua, -1); 200 | 201 | return bool_opt; 202 | } 203 | 204 | /** 205 | * Get a string option from the configuration file 206 | */ 207 | const char *get_option_string(const char *opt) { 208 | const char *str = NULL; 209 | 210 | lua_getglobal(lua, "options"); 211 | lua_getfield(lua, -1, opt); 212 | str = lua_tostring(lua, -1); 213 | lua_pop(lua, -1); 214 | 215 | return str; 216 | } 217 | 218 | /** 219 | * Get a number option from the configuration file 220 | */ 221 | lua_Number get_option_number(const char *opt) { 222 | lua_Number number; 223 | 224 | lua_getglobal(lua, "options"); 225 | lua_getfield(lua, -1, opt); 226 | number = lua_tonumber(lua, -1); 227 | lua_pop(lua, -1); 228 | 229 | return number; 230 | } 231 | 232 | /** 233 | * Get a table value from a subtable in the configuration file 234 | */ 235 | const char *get_subtable_string(const char *table, int index) { 236 | const char *value = NULL; 237 | 238 | lua_getglobal(lua, "options"); 239 | if (luaL_getsubtable(lua, -1, table)) { 240 | int table_length = get_table_size(table); 241 | // If the wanted index is higher than the table length then return NULL 242 | if (table_length < index) { 243 | lua_pop(lua, 1); 244 | return value; 245 | } 246 | lua_rawgeti(lua, -1, index); 247 | value = luaL_checkstring(lua, -1); 248 | lua_pop(lua, 1); 249 | } 250 | lua_pop(lua, 2); 251 | 252 | return value; 253 | } 254 | 255 | /** 256 | * Set a boolean value in a table element 257 | */ 258 | int set_table_boolean(const char *key, bool value) { 259 | lua_pushstring(lua, key); 260 | lua_pushboolean(lua, value); 261 | lua_settable(lua, -3); 262 | 263 | return 0; 264 | } 265 | 266 | /** 267 | * Set a string value in a table element 268 | */ 269 | int set_table_string(const char *key, const char *value) { 270 | lua_pushstring(lua, key); 271 | lua_pushstring(lua, value); 272 | lua_settable(lua, -3); 273 | 274 | return 0; 275 | } 276 | 277 | /** 278 | * Set a number value in a table element 279 | */ 280 | int set_table_number(const char *key, lua_Number value) { 281 | lua_pushstring(lua, key); 282 | lua_pushnumber(lua, value); 283 | lua_settable(lua, -3); 284 | 285 | return 0; 286 | } 287 | 288 | /** 289 | * Set a subtable in a table 290 | */ 291 | int set_table_subtable(const char *key) { 292 | lua_getglobal(lua, "options"); 293 | lua_setfield(lua, -1, key); 294 | 295 | return 0; 296 | } 297 | 298 | /** 299 | * Set a subtable string value in a table 300 | */ 301 | int set_subtable_string(const char *table, const char *key) { 302 | lua_getglobal(lua, "options"); 303 | if (luaL_getsubtable(lua, -1, table)) { 304 | int table_length = get_table_size(table); 305 | // Push the new value 306 | lua_pushstring(lua, key); 307 | lua_rawseti(lua, -2, table_length + 1); 308 | lua_pop(lua, 2); 309 | } 310 | lua_pop(lua, 1); 311 | 312 | return 0; 313 | } 314 | -------------------------------------------------------------------------------- /src/lib/memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | /** 9 | * A malloc() wrapper that checks the results and dies in case of error 10 | */ 11 | void *xmalloc(size_t size) { 12 | void *ptr = malloc(size); 13 | if (ptr == NULL) { 14 | log_fatal("allocating memory; %s\n", strerror(errno)); 15 | exit(errno); 16 | } 17 | 18 | return ptr; 19 | } 20 | 21 | /** 22 | * A free() wrapper that dies if fed with NULL pointer 23 | */ 24 | void xfree(void *ptr) { 25 | if (ptr == NULL) { 26 | log_fatal("allocating memory; %s\n", strerror(errno)); 27 | exit(errno); 28 | } 29 | 30 | free(ptr); 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/utils.c: -------------------------------------------------------------------------------- 1 | #include "lcfetch.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | /* ASCII logos */ 11 | #include "logos/android.h" 12 | #include "logos/arch.h" 13 | #include "logos/debian.h" 14 | #include "logos/fedora.h" 15 | #include "logos/gentoo.h" 16 | #include "logos/linux.h" 17 | #include "logos/manjaro.h" 18 | #include "logos/nixos.h" 19 | #include "logos/ubuntu.h" 20 | 21 | size_t utf8len(char *s) { 22 | size_t len = 0; 23 | for (; *s; s++) { 24 | if ((*s & 0xC0) != 0x80) { 25 | len++; 26 | } 27 | } 28 | return len; 29 | } 30 | 31 | char *repeat_string(char *str, int times) { 32 | if (times < 1) { 33 | return str; 34 | } 35 | 36 | char *result = xmalloc(BUF_SIZE); 37 | char *repeated = result; 38 | 39 | for (int i = 0; i < times; i++) { 40 | *(repeated++) = *str; 41 | } 42 | *repeated = '\0'; 43 | 44 | return result; 45 | } 46 | 47 | void truncate_whitespaces(char *str) { 48 | int src = 0; 49 | int dst = 0; 50 | while (*(str + dst) == ' ') { 51 | dst++; 52 | } 53 | 54 | while (*(str + dst) != '\0') { 55 | *(str + src) = *(str + dst); 56 | 57 | if (*(str + (dst++)) == ' ') { 58 | while (*(str + dst) == ' ') { 59 | dst++; 60 | } 61 | } 62 | src++; 63 | } 64 | 65 | *(str + src) = '\0'; 66 | } 67 | 68 | char *remove_substr(char *str, const char *sub) { 69 | char *p, *q, *r; 70 | 71 | if (*sub && (q = r = strstr(str, sub)) != NULL) { 72 | size_t len = strlen(sub); 73 | while ((r = strstr(p = r + len, sub)) != NULL) { 74 | while (p < r) { 75 | *q++ = *p++; 76 | } 77 | } 78 | while ((*q++ = *p++) != '\0') { 79 | continue; 80 | } 81 | } 82 | return str; 83 | } 84 | 85 | char *replace_string(char *str, char *pattern, char *new_pattern) { 86 | char *new_str; 87 | int i = 0, count = 0; 88 | int pattern_len = strlen(pattern); 89 | int new_pattern_len = strlen(new_pattern); 90 | 91 | // Count the number of times old pattern occurs in the string 92 | for (i = 0; str[i] != '\0'; i++) { 93 | if (strstr(&str[i], pattern) == &str[i]) { 94 | count++; 95 | 96 | i += pattern_len - 1; 97 | } 98 | } 99 | new_str = xmalloc(i + count * (new_pattern_len - pattern_len) + 1); 100 | 101 | i = 0; 102 | while (*str) { 103 | // Compare the substrings with our result 104 | if (strstr(str, pattern) == str) { 105 | strcpy(&new_str[i], new_pattern); 106 | i += new_pattern_len; 107 | str += pattern_len; 108 | } else { 109 | new_str[i++] = *str++; 110 | } 111 | } 112 | 113 | new_str[i] = '\0'; 114 | return new_str; 115 | } 116 | 117 | char *str_to_lower(char *str) { 118 | for (char *c = str; *c; c++) { 119 | *c = tolower(*c); 120 | } 121 | 122 | return str; 123 | } 124 | 125 | bool is_android_device() { 126 | DIR *sys_app = opendir("/system/app"); 127 | DIR *sys_priv_app = opendir("/system/priv-app"); 128 | if (sys_app && sys_priv_app) { 129 | closedir(sys_app); 130 | closedir(sys_priv_app); 131 | 132 | return true; 133 | } 134 | 135 | return false; 136 | } 137 | 138 | char **get_distro_logo(char *distro) { 139 | char **logo; 140 | if ((strcasecmp(distro, "fedora") == 0) || (strstr(distro, "Fedora"))) { 141 | logo = fedora; 142 | } else if (strcasecmp(distro, "gentoo") == 0) { 143 | logo = gentoo; 144 | } else if ((strcasecmp(distro, "arch") == 0) || (strstr(distro, "Arch"))) { 145 | logo = arch; 146 | } else if ((strcasecmp(distro, "debian") == 0) || (strstr(distro, "Debian"))) { 147 | logo = debian; 148 | } else if ((strcasecmp(distro, "ubuntu") == 0) || (strstr(distro, "Ubuntu"))) { 149 | logo = ubuntu; 150 | } else if (strcasecmp(distro, "nixos") == 0) { 151 | logo = nixos; 152 | } else if ((strcasecmp(distro, "manjaro") == 0) || (strstr(distro, "Manjaro"))) { 153 | logo = manjaro; 154 | } else if (is_android_device()) { 155 | logo = android; 156 | } else { 157 | logo = linux_logo; 158 | } 159 | return logo; 160 | } 161 | 162 | int get_distro_logo_rows(char *distro) { 163 | int rows; 164 | if ((strcasecmp(distro, "fedora") == 0) || (strstr(distro, "Fedora"))) { 165 | rows = LEN(fedora); 166 | } else if (strcasecmp(distro, "gentoo") == 0) { 167 | rows = LEN(gentoo); 168 | } else if ((strcasecmp(distro, "arch") == 0) || (strstr(distro, "Arch"))) { 169 | rows = LEN(arch); 170 | } else if ((strcasecmp(distro, "debian") == 0) || (strstr(distro, "Debian"))) { 171 | rows = LEN(debian); 172 | } else if ((strcasecmp(distro, "ubuntu") == 0) || (strstr(distro, "Ubuntu"))) { 173 | rows = LEN(ubuntu); 174 | } else if (strcasecmp(distro, "nixos") == 0) { 175 | rows = LEN(nixos); 176 | } else if ((strcasecmp(distro, "manjaro") == 0) || (strstr(distro, "Manjaro"))) { 177 | rows = LEN(manjaro); 178 | } else if (is_android_device()) { 179 | rows = LEN(android); 180 | } else { 181 | rows = LEN(linux_logo); 182 | } 183 | return rows; 184 | } 185 | 186 | char *get_distro_accent(char *distro) { 187 | char *accent_color = xmalloc(BUF_SIZE); 188 | if ((strcasecmp(distro, "fedora") == 0) || (strstr(distro, "Fedora"))) { 189 | strncpy(accent_color, fedora_accent, BUF_SIZE); 190 | } else if (strcasecmp(distro, "gentoo") == 0) { 191 | strncpy(accent_color, gentoo_accent, BUF_SIZE); 192 | } else if ((strcasecmp(distro, "arch") == 0) || (strstr(distro, "Arch"))) { 193 | strncpy(accent_color, arch_accent, BUF_SIZE); 194 | } else if ((strcasecmp(distro, "debian") == 0) || (strstr(distro, "Debian"))) { 195 | strncpy(accent_color, debian_accent, BUF_SIZE); 196 | } else if ((strcasecmp(distro, "ubuntu") == 0) || (strstr(distro, "Ubuntu"))) { 197 | strncpy(accent_color, ubuntu_accent, BUF_SIZE); 198 | } else if (strcasecmp(distro, "nixos") == 0) { 199 | strncpy(accent_color, nixos_accent, BUF_SIZE); 200 | } else if ((strcasecmp(distro, "manjaro") == 0) || (strstr(distro, "Manjaro"))) { 201 | strncpy(accent_color, manjaro_accent, BUF_SIZE); 202 | } else if (is_android_device()) { 203 | strncpy(accent_color, android_accent, BUF_SIZE); 204 | } else { 205 | strncpy(accent_color, linux_accent, BUF_SIZE); 206 | } 207 | return accent_color; 208 | } 209 | 210 | char *get_custom_accent(char *color) { 211 | /* 212 | * This wraps human-readable colors to terminal ANSI colors, where 213 | * black = 0 214 | * red = 1 215 | * green = 2 216 | * yellow = 3 217 | * blue = 4 218 | * purple = 5 219 | * cyan = 6 220 | * white = 7 221 | * */ 222 | char *accent_color = xmalloc(BUF_SIZE); 223 | if (strcasecmp(color, "black") == 0) { 224 | strncpy(accent_color, "\e[1;30m", BUF_SIZE); 225 | } else if (strcasecmp(color, "red") == 0) { 226 | strncpy(accent_color, "\e[1;31m", BUF_SIZE); 227 | } else if (strcasecmp(color, "green") == 0) { 228 | strncpy(accent_color, "\e[1;32m", BUF_SIZE); 229 | } else if (strcasecmp(color, "yellow") == 0) { 230 | strncpy(accent_color, "\e[1;33m", BUF_SIZE); 231 | } else if (strcasecmp(color, "blue") == 0) { 232 | strncpy(accent_color, "\e[1;34m", BUF_SIZE); 233 | } else if (strcasecmp(color, "purple") == 0) { 234 | strncpy(accent_color, "\e[1;35m", BUF_SIZE); 235 | } else if (strcasecmp(color, "cyan") == 0) { 236 | strncpy(accent_color, "\e[1;36m", BUF_SIZE); 237 | } else if (strcasecmp(color, "white") == 0) { 238 | strncpy(accent_color, "\e[1;37m", BUF_SIZE); 239 | } 240 | return accent_color; 241 | } 242 | 243 | custom_ascii_logo get_custom_logo() { 244 | // NOTE: the current colors implementation is very tricky, should have a refactor later 245 | char *colors[] = {"black", "red", "green", "yellow", "blue", "purple", "cyan", "white"}; 246 | bool has_accent = false; 247 | const char *custom_accent_color = get_option_string("accent_color"); 248 | 249 | custom_ascii_logo logo; 250 | int custom_logo_size = get_table_size("custom_ascii_logo"); 251 | if (custom_logo_size > 0) { 252 | logo.cols = custom_logo_size; 253 | logo.arr = (char **)xmalloc(BUF_SIZE * logo.cols); 254 | for (int i = 1; i <= custom_logo_size; i++) { 255 | char *logo_line = (char *)get_subtable_string("custom_ascii_logo", i); 256 | if (strlen(custom_accent_color) > 0) { 257 | char *accent_color = get_custom_accent((char *)custom_accent_color); 258 | char *logo_line_fmt = xmalloc(BUF_SIZE); 259 | snprintf(logo_line_fmt, BUF_SIZE, "%s%s", accent_color, logo_line); 260 | logo.arr[i - 1] = logo_line_fmt; 261 | xfree(accent_color); 262 | has_accent = true; 263 | } else { 264 | // Iterate over all possible colors and replace them 265 | for (int j = 0; j < LEN(colors); j++) { 266 | char *accent_color = get_custom_accent(colors[j]); 267 | char *logo_line_fmt = replace_string(logo_line, colors[j], accent_color); 268 | logo.arr[i - 1] = logo_line_fmt; 269 | unsigned long logo_fmt_len = strlen(logo_line_fmt); 270 | xfree(accent_color); 271 | if (strlen(logo_line) != logo_fmt_len) { 272 | has_accent = true; 273 | break; 274 | } 275 | } 276 | } 277 | } 278 | logo.rows = utf8len(logo.arr[0]); 279 | logo.rows -= has_accent ? strlen("\e[0;00m") : 0; 280 | return logo; 281 | } 282 | 283 | logo.cols = logo.rows = 0; 284 | return logo; 285 | } 286 | 287 | void print_colors(char *logo_part, char *next_logo_part, char *gap_logo, char *gap_info) { 288 | char *dark_colors = get_colors_dark(); 289 | char *bright_colors = get_colors_bright(); 290 | // if we should add padding instead of the next distro logo line 291 | if ((strlen(logo_part) == 0) || (strlen(logo_part) - strlen("\e[1;00m") == 0)) { 292 | printf("%s%s%s\n", gap_logo, gap_info, dark_colors); 293 | } else { 294 | printf("%s%s%s\n", logo_part, gap_info, dark_colors); 295 | } 296 | if ((strlen(next_logo_part) == 0) || (strlen(next_logo_part) - strlen("\e[1;00m") == 0)) { 297 | printf("%s%s%s\n", gap_logo, gap_info, bright_colors); 298 | } else { 299 | printf("%s%s%s\n", next_logo_part, gap_info, bright_colors); 300 | } 301 | xfree(dark_colors); 302 | xfree(bright_colors); 303 | } 304 | 305 | void print_field(char *logo_part, char *gap, const char *delimiter, char *accent, const char *field_name) { 306 | // NOTE: colors field requires a special treatment so we don't use print_info on it 307 | 308 | // User information requires a special treatment 309 | bool is_user_title = false; 310 | bool is_separator = false; 311 | 312 | char *message = xmalloc(BUF_SIZE); 313 | // Set the function that will be used for getting the field value 314 | char *field_function = NULL; 315 | char *field_message = xmalloc(BUF_SIZE); 316 | 317 | // Get the fields data 318 | if (strcasecmp(field_name, "os") == 0) { 319 | field_function = get_os(1); 320 | } else if (strcasecmp(field_name, "kernel") == 0) { 321 | field_function = get_kernel(); 322 | } else if (strcasecmp(field_name, "uptime") == 0) { 323 | field_function = get_uptime(); 324 | } else if (strcasecmp(field_name, "packages") == 0) { 325 | field_function = get_packages(); 326 | } else if (strcasecmp(field_name, "wm") == 0) { 327 | field_function = get_wm(); 328 | } else if (strcasecmp(field_name, "resolution") == 0) { 329 | field_function = get_resolution(); 330 | } else if (strcasecmp(field_name, "shell") == 0) { 331 | field_function = get_shell(); 332 | } else if (strcasecmp(field_name, "terminal") == 0) { 333 | field_function = get_terminal(); 334 | } else if (strcasecmp(field_name, "cpu") == 0) { 335 | field_function = get_cpu(); 336 | } else if (strcasecmp(field_name, "memory") == 0) { 337 | field_function = get_memory(); 338 | } else if (strcasecmp(field_name, "user") == 0) { 339 | field_function = get_title(accent); 340 | is_user_title = true; 341 | } else if (strcasecmp(field_name, "separator") == 0) { 342 | field_function = get_separator(); 343 | is_separator = true; 344 | } else { 345 | log_error("Field '%s' doesn't exists", field_name); 346 | xfree(field_message); 347 | xfree(message); 348 | exit(1); 349 | } 350 | snprintf(field_message, BUF_SIZE, "%s_message", str_to_lower((char *)field_name)); 351 | snprintf(field_message, BUF_SIZE, "%s", get_option_string(field_message)); 352 | if (is_user_title || is_separator) { 353 | snprintf(message, BUF_SIZE, "%s%s", "\e[0m", field_function); 354 | } else { 355 | snprintf(message, BUF_SIZE, "%s%s%s %s", field_message, "\e[0m", delimiter, field_function); 356 | } 357 | xfree(field_message); 358 | 359 | if ((strcasecmp(field_name, "kernel") != 0) && (field_function != NULL)) { 360 | xfree(field_function); 361 | } 362 | 363 | // Print field information 364 | if (logo_part == NULL) { 365 | // When using minimal mode (without displaying logo) 366 | if (is_user_title) { 367 | printf("%s%s%s", gap, accent, message); 368 | } else { 369 | if (field_function != NULL) { 370 | printf("%s%s%s\n", gap, accent, message); 371 | } else { 372 | printf("%s%s\n", gap, accent); 373 | } 374 | } 375 | } else { 376 | if (is_user_title) { 377 | printf("%s%s%s%s", logo_part, gap, accent, message); 378 | } else { 379 | if (field_function != NULL) { 380 | printf("%s%s%s%s\n", logo_part, gap, accent, message); 381 | } else { 382 | printf("%s%s%s\n", logo_part, gap, accent); 383 | } 384 | } 385 | } 386 | xfree(message); 387 | } 388 | 389 | char *get_property(Display *disp, Window win, Atom xa_prop_type, char *prop_name, unsigned long *size) { 390 | Atom xa_prop_name; 391 | Atom xa_ret_type; 392 | int ret_format; 393 | unsigned long ret_nitems; 394 | unsigned long ret_bytes_after; 395 | unsigned long tmp_size; 396 | unsigned char *ret_prop; 397 | 398 | xa_prop_name = XInternAtom(disp, prop_name, 0); 399 | 400 | if (XGetWindowProperty(disp, win, xa_prop_name, 0, BUF_SIZE, 0, xa_prop_type, &xa_ret_type, &ret_format, 401 | &ret_nitems, &ret_bytes_after, &ret_prop) != Success) { 402 | log_warn("Cannot get %s property.\n", prop_name); 403 | return NULL; 404 | } 405 | 406 | if (xa_ret_type != xa_prop_type) { 407 | log_warn("Invalid type of %s property.\n", prop_name); 408 | XFree(ret_prop); 409 | return NULL; 410 | } 411 | 412 | /* null terminate the result to make string handling easier */ 413 | tmp_size = (ret_format / (64 / sizeof(long))) * ret_nitems; 414 | char *ret = xmalloc(tmp_size + 1); 415 | memmove(ret, ret_prop, tmp_size); 416 | ret[tmp_size] = '\0'; 417 | 418 | if (size) { 419 | *size = tmp_size; 420 | } 421 | 422 | XFree(ret_prop); 423 | return ret; 424 | } 425 | -------------------------------------------------------------------------------- /third-party/log.c/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 rxi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /third-party/log.c/README.md: -------------------------------------------------------------------------------- 1 | # log.c 2 | A simple logging library implemented in C99 3 | 4 | ![screenshot](https://cloud.githubusercontent.com/assets/3920290/23831970/a2415e96-0723-11e7-9886-f8f5d2de60fe.png) 5 | 6 | 7 | ## Usage 8 | **[log.c](src/log.c?raw=1)** and **[log.h](src/log.h?raw=1)** should be dropped 9 | into an existing project and compiled along with it. The library provides 6 10 | function-like macros for logging: 11 | 12 | ```c 13 | log_trace(const char *fmt, ...); 14 | log_debug(const char *fmt, ...); 15 | log_info(const char *fmt, ...); 16 | log_warn(const char *fmt, ...); 17 | log_error(const char *fmt, ...); 18 | log_fatal(const char *fmt, ...); 19 | ``` 20 | 21 | Each function takes a printf format string followed by additional arguments: 22 | 23 | ```c 24 | log_trace("Hello %s", "world") 25 | ``` 26 | 27 | Resulting in a line with the given format printed to stderr: 28 | 29 | ``` 30 | 20:18:26 TRACE src/main.c:11: Hello world 31 | ``` 32 | 33 | 34 | #### log_set_quiet(bool enable) 35 | Quiet-mode can be enabled by passing `true` to the `log_set_quiet()` function. 36 | While this mode is enabled the library will not output anything to `stderr`, but 37 | will continue to write to files and callbacks if any are set. 38 | 39 | 40 | #### log_set_level(int level) 41 | The current logging level can be set by using the `log_set_level()` function. 42 | All logs below the given level will not be written to `stderr`. By default the 43 | level is `LOG_TRACE`, such that nothing is ignored. 44 | 45 | 46 | #### log_add_fp(FILE *fp, int level) 47 | One or more file pointers where the log will be written can be provided to the 48 | library by using the `log_add_fp()` function. The data written to the file 49 | output is of the following format: 50 | 51 | ``` 52 | 2047-03-11 20:18:26 TRACE src/main.c:11: Hello world 53 | ``` 54 | 55 | Any messages below the given `level` are ignored. If the library failed to add a 56 | file pointer a value less-than-zero is returned. 57 | 58 | 59 | #### log_add_callback(log_LogFn fn, void *udata, int level) 60 | One or more callback functions which are called with the log data can be 61 | provided to the library by using the `log_add_callback()` function. A callback 62 | function is passed a `log_Event` structure containing the `line` number, 63 | `filename`, `fmt` string, `va` printf va\_list, `level` and the given `udata`. 64 | 65 | 66 | #### log_set_lock(log_LockFn fn, void *udata) 67 | If the log will be written to from multiple threads a lock function can be set. 68 | The function is passed the boolean `true` if the lock should be acquired or 69 | `false` if the lock should be released and the given `udata` value. 70 | 71 | 72 | #### const char* log_level_string(int level) 73 | Returns the name of the given log level as a string. 74 | 75 | 76 | #### LOG_USE_COLOR 77 | If the library is compiled with `-DLOG_USE_COLOR` ANSI color escape codes will 78 | be used when printing. 79 | 80 | 81 | ## License 82 | This library is free software; you can redistribute it and/or modify it under 83 | the terms of the MIT license. See [LICENSE](LICENSE) for details. 84 | -------------------------------------------------------------------------------- /third-party/log.c/src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "log.h" 24 | 25 | #define MAX_CALLBACKS 32 26 | 27 | typedef struct { 28 | log_LogFn fn; 29 | void *udata; 30 | int level; 31 | } Callback; 32 | 33 | static struct { 34 | void *udata; 35 | log_LockFn lock; 36 | int level; 37 | bool quiet; 38 | Callback callbacks[MAX_CALLBACKS]; 39 | } L; 40 | 41 | 42 | static const char *level_strings[] = { 43 | "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" 44 | }; 45 | 46 | #ifdef LOG_USE_COLOR 47 | static const char *level_colors[] = { 48 | "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" 49 | }; 50 | #endif 51 | 52 | 53 | static void stdout_callback(log_Event *ev) { 54 | char buf[16]; 55 | buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; 56 | #ifdef LOG_USE_COLOR 57 | fprintf( 58 | ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", 59 | buf, level_colors[ev->level], level_strings[ev->level], 60 | ev->file, ev->line); 61 | #else 62 | fprintf( 63 | ev->udata, "%s %-5s %s:%d: ", 64 | buf, level_strings[ev->level], ev->file, ev->line); 65 | #endif 66 | vfprintf(ev->udata, ev->fmt, ev->ap); 67 | fprintf(ev->udata, "\n"); 68 | fflush(ev->udata); 69 | } 70 | 71 | 72 | static void file_callback(log_Event *ev) { 73 | char buf[64]; 74 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; 75 | fprintf( 76 | ev->udata, "%s %-5s %s:%d: ", 77 | buf, level_strings[ev->level], ev->file, ev->line); 78 | vfprintf(ev->udata, ev->fmt, ev->ap); 79 | fprintf(ev->udata, "\n"); 80 | fflush(ev->udata); 81 | } 82 | 83 | 84 | static void lock(void) { 85 | if (L.lock) { L.lock(true, L.udata); } 86 | } 87 | 88 | 89 | static void unlock(void) { 90 | if (L.lock) { L.lock(false, L.udata); } 91 | } 92 | 93 | 94 | const char* log_level_string(int level) { 95 | return level_strings[level]; 96 | } 97 | 98 | 99 | void log_set_lock(log_LockFn fn, void *udata) { 100 | L.lock = fn; 101 | L.udata = udata; 102 | } 103 | 104 | 105 | void log_set_level(int level) { 106 | L.level = level; 107 | } 108 | 109 | 110 | void log_set_quiet(bool enable) { 111 | L.quiet = enable; 112 | } 113 | 114 | 115 | int log_add_callback(log_LogFn fn, void *udata, int level) { 116 | for (int i = 0; i < MAX_CALLBACKS; i++) { 117 | if (!L.callbacks[i].fn) { 118 | L.callbacks[i] = (Callback) { fn, udata, level }; 119 | return 0; 120 | } 121 | } 122 | return -1; 123 | } 124 | 125 | 126 | int log_add_fp(FILE *fp, int level) { 127 | return log_add_callback(file_callback, fp, level); 128 | } 129 | 130 | 131 | static void init_event(log_Event *ev, void *udata) { 132 | if (!ev->time) { 133 | time_t t = time(NULL); 134 | ev->time = localtime(&t); 135 | } 136 | ev->udata = udata; 137 | } 138 | 139 | 140 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 141 | log_Event ev = { 142 | .fmt = fmt, 143 | .file = file, 144 | .line = line, 145 | .level = level, 146 | }; 147 | 148 | lock(); 149 | 150 | if (!L.quiet && level >= L.level) { 151 | init_event(&ev, stderr); 152 | va_start(ev.ap, fmt); 153 | stdout_callback(&ev); 154 | va_end(ev.ap); 155 | } 156 | 157 | for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { 158 | Callback *cb = &L.callbacks[i]; 159 | if (level >= cb->level) { 160 | init_event(&ev, cb->udata); 161 | va_start(ev.ap, fmt); 162 | cb->fn(&ev); 163 | va_end(ev.ap); 164 | } 165 | } 166 | 167 | unlock(); 168 | } 169 | -------------------------------------------------------------------------------- /third-party/log.c/src/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define LOG_VERSION "0.1.0" 17 | 18 | typedef struct { 19 | va_list ap; 20 | const char *fmt; 21 | const char *file; 22 | struct tm *time; 23 | void *udata; 24 | int line; 25 | int level; 26 | } log_Event; 27 | 28 | typedef void (*log_LogFn)(log_Event *ev); 29 | typedef void (*log_LockFn)(bool lock, void *udata); 30 | 31 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 32 | 33 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 34 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 35 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 36 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 37 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 38 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 39 | 40 | const char* log_level_string(int level); 41 | void log_set_lock(log_LockFn fn, void *udata); 42 | void log_set_level(int level); 43 | void log_set_quiet(bool enable); 44 | int log_add_callback(log_LogFn fn, void *udata, int level); 45 | int log_add_fp(FILE *fp, int level); 46 | 47 | void log_log(int level, const char *file, int line, const char *fmt, ...); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | -- project 2 | set_project("lcfetch") 3 | 4 | -- version 5 | set_version("0.2.0", { build = "%Y-%m-%d_%H:%M:%S" }) 6 | 7 | -- set xmake min version 8 | set_xmakever("2.2.3") 9 | 10 | -- '-Wall,-Wextra' equivalent 11 | set_warnings("allextra") 12 | 13 | -- set C standards 14 | set_languages("c99") 15 | 16 | -- build modes 17 | add_rules("mode.debug", "mode.release", "mode.valgrind") 18 | if is_mode("release") then 19 | -- '-Ofast' equivalent 20 | set_optimize("aggressive") 21 | -- Strip unneeded debugging symbols 22 | set_strip("all") 23 | -- Hide symbols 24 | set_symbols("hidden") 25 | end 26 | 27 | -- preprocessor variables 28 | if is_plat("macosx") then 29 | add_defines("MACOS") 30 | end 31 | 32 | -- third-party dependencies 33 | add_requires("lua >= 5.3.6", "libx11", "libxrandr", "xorgproto", "log.c") 34 | 35 | -- headers directories 36 | add_includedirs("src/include") 37 | 38 | -- log.c library 39 | package("log.c") 40 | set_homepage("https://github.com/rxi/log.c") 41 | set_description("A simple logging library implemented in C99") 42 | set_sourcedir(path.join(os.scriptdir(), "third-party/log.c/src")) 43 | 44 | on_install(function(package) 45 | -- Create directories 46 | os.mkdir(package:installdir("lib"), package:installdir("include")) 47 | -- Generate static log.c library 48 | os.run("gcc -DLOG_USE_COLOR -c -o log.o log.c") 49 | os.run("ar rcs liblog.a log.o") 50 | -- Copy libraries and headers 51 | os.cp("*.h", package:installdir("include")) 52 | os.cp("*.a", package:installdir("lib")) 53 | end) 54 | on_test(function(package) 55 | assert(package:check_csnippets( 56 | { 57 | test = [[ 58 | #include 59 | void test(int argc, char** argv) { 60 | log_info("log.c working? - %s", "yes"); 61 | } 62 | ]], 63 | }, 64 | { configs = { 65 | languages = "c99", 66 | } } 67 | )) 68 | end) 69 | package_end() 70 | 71 | -- default target 72 | target("lcfetch") 73 | set_kind("binary") 74 | set_default(true) 75 | 76 | -- Source files 77 | add_files("src/*.c", "src/lib/*.c") 78 | 79 | -- Add third-party dependencies 80 | add_packages("lua", "libx11", "libxrandr", "xorgproto", "log.c") 81 | 82 | -- Add MacOS dynamic libraries that doesn't follow the 'libfoo.*' pattern 83 | if is_plat("macosx") then 84 | add_links("libXrandr.2.dylib") 85 | end 86 | 87 | -- Precompile main lcfetch header to optimize compile time 88 | -- set_pcheader("src/include/lcfetch.h") 89 | 90 | ----- HOOKS ------------------------- 91 | after_build(function(target) 92 | import("core.project.config") 93 | local targetfile = target:targetfile() 94 | os.cp(targetfile, path.join(config.buildir(), path.filename(targetfile))) 95 | cprint("${bright green}[INFO]:${clear} Produced binary location: %s", targetfile) 96 | end) 97 | after_install(function(target) 98 | cprint( 99 | "${bright green}[INFO]:${clear} please run 'mandb -pu' if your system uses mandb to update man pages database" 100 | ) 101 | end) 102 | on_install(function(target) 103 | import("core.project.config") 104 | -- Setup directories 105 | local local_dirs = { "bin", "share/man/man1" } 106 | for _, local_dir in ipairs(local_dirs) do 107 | os.run(string.format("mkdir -p %s/.local/%s", os.getenv("HOME"), local_dir)) 108 | end 109 | 110 | -- Install produced binary 111 | cprint( 112 | "${bright green}[ 30%%]:${clear} installing lcfetch under %s", 113 | os.getenv("HOME") .. "/.local/" .. local_dirs[1] .. " ..." 114 | ) 115 | local targetfile = target:targetfile() 116 | os.run( 117 | string.format( 118 | "install %s %s", 119 | path.join(config.buildir(), path.filename(targetfile)), 120 | os.getenv("HOME") .. "/.local/bin/" .. path.filename(targetfile) 121 | ) 122 | ) 123 | 124 | -- Install default configurations 125 | local skipped_config_copy = true 126 | local config_dir = string.format("%s/.config/lcfetch", os.getenv("HOME")) 127 | os.run(string.format("mkdir -p %s", config_dir)) 128 | if not os.exists(string.format("%s/config.lua", config_dir)) then 129 | cprint("${bright green}[ 50%%]:${clear} copying default configs to %s", config_dir) 130 | os.cp("config/sample.lua", string.format("%s/config.lua", config_dir)) 131 | skipped_config_copy = false 132 | end 133 | 134 | -- Install documentation 135 | cprint( 136 | "${bright green}[ %d%%]:${clear} copying man pages to %s", 137 | skipped_config_copy and 77 or 75, 138 | os.getenv("HOME") .. "/local/" .. local_dirs[2] .. " ..." 139 | ) 140 | os.cp("man/lcfetch.1", string.format("%s/.local/%s", os.getenv("HOME"), local_dirs[2])) 141 | end) 142 | on_uninstall(function(_) 143 | local bin_dir = os.getenv("HOME") .. "/.local/bin" 144 | local man_dir = os.getenv("HOME") .. "/.local/share/man/man1" 145 | cprint("${bright green}[ 77%%]:${clear} uninstalling lcfetch ...") 146 | os.rm(bin_dir .. "/lcfetch", man_dir .. "/lcfetch.1") 147 | end) 148 | 149 | -- Format source code 150 | target("fmt") 151 | set_default(false) 152 | 153 | on_run(function(_) 154 | cprint("${bright green}[ 56%%]:${clear} formatting lcfetch source code ...") 155 | -- os.run("clang-format -style=file --sort-includes -i src/**/*.c src/include/**/*.h") 156 | local inc_files = os.files("$(projectdir)/src/include/**/*.h") 157 | local src_files = table.join( 158 | os.files("$(projectdir)/src/*.c"), 159 | os.files("$(projectdir)/src/**/*.c") 160 | ) 161 | table.join2(src_files, inc_files) 162 | for _, filepath in ipairs(src_files) do 163 | os.run("clang-format -style=file --verbose --sort-includes -i " .. filepath) 164 | end 165 | cprint("${bright green}[100%%]: format ok!${clear}") 166 | end) 167 | on_uninstall(function(_) 168 | return nil 169 | end) 170 | 171 | -- Generate man pages 172 | target("docs") 173 | set_default(false) 174 | 175 | on_run(function(_) 176 | cprint("${bright green}[ 77%%]:${clear} Generating man pages ...") 177 | os.run("pandoc man/lcfetch.1.md -s -t man -o man/lcfetch.1") 178 | cprint("${bright green}[100%%]: docs ok!${clear}") 179 | end) 180 | on_uninstall(function(_) 181 | return nil 182 | end) 183 | 184 | -- 185 | -- If you want to known more usage about xmake, please see https://xmake.io 186 | -- 187 | -- ## FAQ 188 | -- 189 | -- You can enter the project directory firstly before building project. 190 | -- 191 | -- $ cd projectdir 192 | -- 193 | -- 1. How to build project? 194 | -- 195 | -- $ xmake 196 | -- 197 | -- 2. How to configure project? 198 | -- 199 | -- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release] 200 | -- 201 | -- 3. Where is the build output directory? 202 | -- 203 | -- The default output directory is `./build` and you can configure the output directory. 204 | -- 205 | -- $ xmake f -o outputdir 206 | -- $ xmake 207 | -- 208 | -- 4. How to run and debug target after building project? 209 | -- 210 | -- $ xmake run [targetname] 211 | -- $ xmake run -d [targetname] 212 | -- 213 | -- 5. How to install target to the system directory or other output directory? 214 | -- 215 | -- $ xmake install 216 | -- $ xmake install -o installdir 217 | -- 218 | -- 6. Add some frequently-used compilation flags in xmake.lua 219 | -- 220 | -- @code 221 | -- -- add debug and release modes 222 | -- add_rules("mode.debug", "mode.release") 223 | -- 224 | -- -- add macro defination 225 | -- add_defines("NDEBUG", "_GNU_SOURCE=1") 226 | -- 227 | -- -- set warning all as error 228 | -- set_warnings("all", "error") 229 | -- 230 | -- -- set language: c99, c++11 231 | -- set_languages("c99", "c++11") 232 | -- 233 | -- -- set optimization: none, faster, fastest, smallest 234 | -- set_optimize("fastest") 235 | -- 236 | -- -- add include search directories 237 | -- add_includedirs("/usr/include", "/usr/local/include") 238 | -- 239 | -- -- add link libraries and search directories 240 | -- add_links("tbox") 241 | -- add_linkdirs("/usr/local/lib", "/usr/lib") 242 | -- 243 | -- -- add system link libraries 244 | -- add_syslinks("z", "pthread") 245 | -- 246 | -- -- add compilation and link flags 247 | -- add_cxflags("-stdnolib", "-fno-strict-aliasing") 248 | -- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true}) 249 | -- 250 | -- @endcode 251 | -- 252 | --------------------------------------------------------------------------------