diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 372e1fe550..ecd7ec7a12 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -13,6 +13,7 @@ assignees: '' * What happened: * What should happen: +* Fastfetch version used: * Did it work in an older version: * Where did you get the binary: * Does this issue still occurs in [the latest dev build](https://github.com/fastfetch-cli/fastfetch/actions/)? @@ -41,18 +42,19 @@ Output of `fastfetch --list-features`: //paste here ``` -## If fastfatch crashed +## If fastfatch crashed or freezed Paste the stacktrace here. You may get it with: -``` -$ gdb -q -ex 'set confirm off' -ex run -ex 'bt full' -ex quit --args /path/to/fastfetch +```shell +# You may need Ctrl+C to stop the process if it freezes +gdb -q -ex 'set confirm off' -ex run -ex 'bt full' -ex quit --args /path/to/fastfetch ``` If you are able to identify which module crashed, the strace can be helpful too -``` -$ strace /path/to/fastfetch --multithreading false --structure {MODULE} --pipe +```shell +strace /path/to/fastfetch --multithreading false -s {MODULE} --pipe ``` If you cannot do the instructions above, please upload the core dump file: diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index febdba8360..2a1749ce77 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,10 +7,28 @@ assignees: '' --- - +## Before requesting a new feature -# Current state: +* A lot of features are not enabled by default. Please try `fastfetch --list-modules` and `fastfetch -c all.jsonc` to see if it has been supported +* Fastfetch supports `Command` module, which can be used to grab output from a custom shell script. Please check if it fits your needs -# Wanted state: +```jsonc +// ~/.config/fastfetch/fastfetch.jsonc +{ + "modules": [ + { + "type": "command", + "text": "/path/to/your/script", + "key": "Feature Title" + } + ] +} +``` -# Why the change is sensible: +## Wanted features: + + + +## Motivation: + + diff --git a/.github/ISSUE_TEMPLATE/logo_request.md b/.github/ISSUE_TEMPLATE/logo_request.md index 223b86f0fe..697223798c 100644 --- a/.github/ISSUE_TEMPLATE/logo_request.md +++ b/.github/ISSUE_TEMPLATE/logo_request.md @@ -7,6 +7,8 @@ assignees: '' --- +Tip: A logo can be displayed by fastfetch without getting into fastfetch's official repo. For highly customized logo for personal use, it's recommended to keep it locally. Please refer to https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch + # OS ``` Paste content of /etc/os-release here. If this file doesn't exist, describe a way to identify the distro. diff --git a/CHANGELOG.md b/CHANGELOG.md index 753feb5fd2..1c5026de20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# 2.9.2 + +Changes: +* To make use of the newly introduced `yyjson` flag `YYJSON_WRITE_NEWLINE_AT_END`, fastfetch now requires `yyjson` 0.9.0 or later + +Features: +* Always add a final new-line when generating JSON output +* Detect partition create time, which can be used as OS installation time (Disk) +* Print time string when generating JSON result instead of UNIX epoch time number, which is more human-readable + +Bugfixes: +* Fix a memory leak +* Better portable mode detection of Windows Terminal (TerminalFont, Windows) +* Fix parsing of option `--packages-disabled` (Packages) +* Don't use command `time` as a shell (Shell) + +Logos: +* Add openSUSE MicroOS +* Fix color of AOSC OS + # 2.9.1 Features: diff --git a/CMakeLists.txt b/CMakeLists.txt index 316048646a..9b315f5a38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url project(fastfetch - VERSION 2.9.1 + VERSION 2.9.2 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -699,6 +699,9 @@ check_function_exists(wcwidth HAVE_WCWIDTH) if(NOT HAVE_WCWIDTH) list(APPEND LIBFASTFETCH_SRC src/3rdparty/mk_wcwidch/wcwidth.c) endif() +if(LINUX) + check_function_exists(statx HAVE_STATX) +endif() if(ENABLE_SYSTEM_YYJSON) find_package(yyjson) @@ -762,6 +765,10 @@ elseif(APPLE) target_compile_definitions(libfastfetch PUBLIC _DARWIN_C_SOURCE) endif() +if(HAVE_STATX) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_STATX) +endif() + if(HAVE_WCWIDTH) target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WCWIDTH) endif() diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..20bcc4f647 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/doc/json_schema.json b/doc/json_schema.json index 363497e301..387fda5ed8 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -730,7 +730,6 @@ }, { "type": "object", - "additionalProperties": false, "description": "Run module with custom configurations", "required": [ "type" @@ -1577,9 +1576,35 @@ "description": "List installed package managers and count of installed packages" }, "disabled": { - "description": "A colon separated list of package managers to be disabled when detecting", - "type": "string", - "default": "winget" + "description": "List of package managers to be disabled when detecting", + "type": "array", + "items": { + "type": "string", + "enum": [ + "am", + "apk", + "brew", + "choco", + "dpkg", + "emerge", + "eopkg", + "flatpak", + "macports", + "nix", + "opkg", + "pacman", + "paludis", + "pkg", + "pkgtool", + "rpm", + "scoop", + "snap", + "winget", + "xbps" + ], + "uniqueItems": true + }, + "default": ["winget"] }, "key": { "$ref": "#/$defs/key" diff --git a/presets/examples/10.jsonc b/presets/examples/10.jsonc index 96ec3bb062..40af25fefc 100644 --- a/presets/examples/10.jsonc +++ b/presets/examples/10.jsonc @@ -84,17 +84,17 @@ }, { "type": "host", - "key": " PC", + "key": "󰌢 PC", "keyColor": "green" }, { "type": "cpu", - "key": "│ ├", + "key": "│ ├󰻠", "keyColor": "green" }, { "type": "gpu", - "key": "│ ├﬙", + "key": "│ ├󰍛", "keyColor": "green" }, { diff --git a/presets/examples/12.jsonc b/presets/examples/12.jsonc index eca55ab7f4..86a66d1aa1 100644 --- a/presets/examples/12.jsonc +++ b/presets/examples/12.jsonc @@ -23,7 +23,7 @@ "type": "os" }, { - "key": "  Machine ", + "key": " 󰌢 Machine ", "keyColor": "green", "type": "host" }, @@ -64,12 +64,12 @@ "type": "terminal" }, { - "key": "  CPU ", + "key": " 󰻠 CPU ", "keyColor": "yellow", "type": "cpu" }, { - "key": " ﬙ GPU ", + "key": " 󰍛 GPU ", "keyColor": "blue", "type": "gpu" }, diff --git a/presets/examples/2.jsonc b/presets/examples/2.jsonc index 4b89cff761..8c1c4bfcf7 100644 --- a/presets/examples/2.jsonc +++ b/presets/examples/2.jsonc @@ -19,15 +19,15 @@ }, { "type": "host", - "key": " " + "key": " 󰌢" }, { "type": "cpu", - "key": " " + "key": " 󰻠" }, { "type": "gpu", - "key": " ﬙" + "key": " 󰍛" }, { "type": "disk", diff --git a/presets/examples/5.jsonc b/presets/examples/5.jsonc index 888c763d7c..d8545dd775 100644 --- a/presets/examples/5.jsonc +++ b/presets/examples/5.jsonc @@ -14,8 +14,7 @@ }, { "type": "icons", - "key": "I", - "format": "{5}" + "key": "I" }, { "type": "font", diff --git a/presets/examples/6.jsonc b/presets/examples/6.jsonc index 4a9c4c7ebe..0e9e626fa5 100644 --- a/presets/examples/6.jsonc +++ b/presets/examples/6.jsonc @@ -15,17 +15,17 @@ "modules": [ { "type": "host", - "key": "╭─", + "key": "╭─󰌢", "keyColor": "green" }, { "type": "cpu", - "key": "├─", + "key": "├─󰻠", "keyColor": "green" }, { "type": "gpu", - "key": "├─﬙", + "key": "├─󰍛", "keyColor": "green" }, { diff --git a/presets/examples/7.jsonc b/presets/examples/7.jsonc index 59048f7d20..941703ce72 100644 --- a/presets/examples/7.jsonc +++ b/presets/examples/7.jsonc @@ -77,17 +77,17 @@ "break", { "type": "host", - "key": " PC", + "key": "󰌢 PC", "keyColor": "green" }, { "type": "cpu", - "key": "├", + "key": "├󰻠", "keyColor": "green" }, { "type": "gpu", - "key": "├﬙", + "key": "├󰍛", "keyColor": "green" }, { diff --git a/presets/examples/8.jsonc b/presets/examples/8.jsonc index cc7b47407b..2553bd8942 100644 --- a/presets/examples/8.jsonc +++ b/presets/examples/8.jsonc @@ -33,7 +33,7 @@ }, { "type": "memory", - "key": "" + "key": "󰻠" }, { "type": "packages", diff --git a/presets/paleofetch.jsonc b/presets/paleofetch.jsonc index 7fe9ea0aaf..9271f19693 100644 --- a/presets/paleofetch.jsonc +++ b/presets/paleofetch.jsonc @@ -12,7 +12,7 @@ "uptime", { "type": "battery", - "format": "{/4}{-}{/}{4}%{?5} [{5}]{?}" + "format": "{/4}{-}{/}{4}{?5} [{5}]{?}" }, "break", "packages", diff --git a/src/3rdparty/yyjson/repo.json b/src/3rdparty/yyjson/repo.json index 4e8e1106d0..1d62a5a24d 100644 --- a/src/3rdparty/yyjson/repo.json +++ b/src/3rdparty/yyjson/repo.json @@ -1,6 +1,6 @@ { "home": "https://github.com/ibireme/yyjson", "license": "MIT ( embed in source )", - "version": "0.8.0", + "version": "0.9.0", "author": "ibireme" } diff --git a/src/3rdparty/yyjson/yyjson.c b/src/3rdparty/yyjson/yyjson.c index 3e74166c19..4202d8a031 100644 --- a/src/3rdparty/yyjson/yyjson.c +++ b/src/3rdparty/yyjson/yyjson.c @@ -1407,6 +1407,7 @@ bool yyjson_mut_doc_set_val_pool_size(yyjson_mut_doc *doc, size_t count) { void yyjson_mut_doc_free(yyjson_mut_doc *doc) { if (doc) { yyjson_alc alc = doc->alc; + memset(&doc->alc, 0, sizeof(alc)); unsafe_yyjson_str_pool_release(&doc->str_pool, &alc); unsafe_yyjson_val_pool_release(&doc->val_pool, &alc); alc.free(alc.ctx, doc); @@ -8309,13 +8310,15 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, bool cpy = (enc_table == enc_table_cpy); bool esc = has_write_flag(ESCAPE_UNICODE) != 0; bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0; + bool newline = has_write_flag(NEWLINE_AT_END) != 0; + const usize end_len = 2; /* '\n' and '\0' */ switch (unsafe_yyjson_get_type(val)) { case YYJSON_TYPE_RAW: str_len = unsafe_yyjson_get_len(val); str_ptr = (const u8 *)unsafe_yyjson_get_str(val); check_str_len(str_len); - incr_len(str_len + 1); + incr_len(str_len + end_len); cur = write_raw(cur, str_ptr, str_len); break; @@ -8323,7 +8326,7 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, str_len = unsafe_yyjson_get_len(val); str_ptr = (const u8 *)unsafe_yyjson_get_str(val); check_str_len(str_len); - incr_len(str_len * 6 + 4); + incr_len(str_len * 6 + 2 + end_len); if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { cur = write_string_noesc(cur, str_ptr, str_len); } else { @@ -8333,7 +8336,7 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, break; case YYJSON_TYPE_NUM: - incr_len(32); + incr_len(32 + end_len); cur = write_number(cur, val, flg); if (unlikely(!cur)) goto fail_num; break; @@ -8349,13 +8352,13 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, break; case YYJSON_TYPE_ARR: - incr_len(4); + incr_len(2 + end_len); byte_copy_2(cur, "[]"); cur += 2; break; case YYJSON_TYPE_OBJ: - incr_len(4); + incr_len(2 + end_len); byte_copy_2(cur, "{}"); cur += 2; break; @@ -8364,6 +8367,7 @@ static_inline u8 *yyjson_write_single(yyjson_val *val, goto fail_type; } + if (newline) *cur++ = '\n'; *cur = '\0'; *dat_len = (usize)(cur - hdr); memset(err, 0, sizeof(yyjson_write_err)); @@ -8436,6 +8440,7 @@ static_inline u8 *yyjson_write_minify(const yyjson_val *root, bool cpy = (enc_table == enc_table_cpy); bool esc = has_write_flag(ESCAPE_UNICODE) != 0; bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0; + bool newline = has_write_flag(NEWLINE_AT_END) != 0; alc_len = root->uni.ofs / sizeof(yyjson_val); alc_len = alc_len * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; @@ -8542,6 +8547,11 @@ static_inline u8 *yyjson_write_minify(const yyjson_val *root, } doc_end: + if (newline) { + incr_len(2); + *(cur - 1) = '\n'; + cur++; + } *--cur = '\0'; *dat_len = (usize)(cur - hdr); memset(err, 0, sizeof(yyjson_write_err)); @@ -8615,6 +8625,7 @@ static_inline u8 *yyjson_write_pretty(const yyjson_val *root, bool esc = has_write_flag(ESCAPE_UNICODE) != 0; bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0; usize spaces = has_write_flag(PRETTY_TWO_SPACES) ? 2 : 4; + bool newline = has_write_flag(NEWLINE_AT_END) != 0; alc_len = root->uni.ofs / sizeof(yyjson_val); alc_len = alc_len * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; @@ -8745,6 +8756,10 @@ static_inline u8 *yyjson_write_pretty(const yyjson_val *root, } doc_end: + if (newline) { + incr_len(2); + *cur++ = '\n'; + } *cur = '\0'; *dat_len = (usize)(cur - hdr); memset(err, 0, sizeof(yyjson_write_err)); @@ -8977,6 +8992,7 @@ static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root, bool cpy = (enc_table == enc_table_cpy); bool esc = has_write_flag(ESCAPE_UNICODE) != 0; bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0; + bool newline = has_write_flag(NEWLINE_AT_END) != 0; alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); @@ -9087,6 +9103,11 @@ static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root, } doc_end: + if (newline) { + incr_len(2); + *(cur - 1) = '\n'; + cur++; + } *--cur = '\0'; *dat_len = (usize)(cur - hdr); err->code = YYJSON_WRITE_SUCCESS; @@ -9162,6 +9183,7 @@ static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root, bool esc = has_write_flag(ESCAPE_UNICODE) != 0; bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0; usize spaces = has_write_flag(PRETTY_TWO_SPACES) ? 2 : 4; + bool newline = has_write_flag(NEWLINE_AT_END) != 0; alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); @@ -9296,6 +9318,10 @@ static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root, } doc_end: + if (newline) { + incr_len(2); + *cur++ = '\n'; + } *cur = '\0'; *dat_len = (usize)(cur - hdr); err->code = YYJSON_WRITE_SUCCESS; diff --git a/src/3rdparty/yyjson/yyjson.h b/src/3rdparty/yyjson/yyjson.h index 97b86fb96b..64c44742fe 100644 --- a/src/3rdparty/yyjson/yyjson.h +++ b/src/3rdparty/yyjson/yyjson.h @@ -527,16 +527,16 @@ extern "C" { #define YYJSON_VERSION_MAJOR 0 /** The minor version of yyjson. */ -#define YYJSON_VERSION_MINOR 8 +#define YYJSON_VERSION_MINOR 9 /** The patch version of yyjson. */ #define YYJSON_VERSION_PATCH 0 /** The version of yyjson in hex: `(major << 16) | (minor << 8) | (patch)`. */ -#define YYJSON_VERSION_HEX 0x000800 +#define YYJSON_VERSION_HEX 0x000900 /** The version string of yyjson. */ -#define YYJSON_VERSION_STRING "0.8.0" +#define YYJSON_VERSION_STRING "0.9.0" /** The version of yyjson in hex, same as `YYJSON_VERSION_HEX`. */ yyjson_api uint32_t yyjson_version(void); @@ -738,7 +738,7 @@ typedef uint32_t yyjson_read_flag; - Report error if double number is infinity. - Report error if string contains invalid UTF-8 character or BOM. - Report error on trailing commas, comments, inf and nan literals. */ -static const yyjson_read_flag YYJSON_READ_NOFLAG = 0 << 0; +static const yyjson_read_flag YYJSON_READ_NOFLAG = 0; /** Read the input data in-situ. This option allows the reader to modify and use input data to store string @@ -1067,7 +1067,7 @@ typedef uint32_t yyjson_write_flag; - Report error on inf or nan number. - Report error on invalid UTF-8 string. - Do not escape unicode or slash. */ -static const yyjson_write_flag YYJSON_WRITE_NOFLAG = 0 << 0; +static const yyjson_write_flag YYJSON_WRITE_NOFLAG = 0; /** Write JSON pretty with 4 space indent. */ static const yyjson_write_flag YYJSON_WRITE_PRETTY = 1 << 0; @@ -1096,6 +1096,10 @@ static const yyjson_write_flag YYJSON_WRITE_ALLOW_INVALID_UNICODE = 1 << 5; This flag will override `YYJSON_WRITE_PRETTY` flag. */ static const yyjson_write_flag YYJSON_WRITE_PRETTY_TWO_SPACES = 1 << 6; +/** Adds a newline character `\n` at the end of the JSON. + This can be helpful for text editors or NDJSON. */ +static const yyjson_write_flag YYJSON_WRITE_NEWLINE_AT_END = 1 << 7; + /** Result code for JSON writer */ @@ -3695,7 +3699,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, The `len` should be the length of the `val`, in bytes. This function allows duplicated key in one object. - @warning The key/value strings are not copied, you should keep these strings + @warning The key strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -4829,6 +4833,7 @@ yyjson_api_inline size_t yyjson_doc_get_val_count(yyjson_doc *doc) { yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc) { if (doc) { yyjson_alc alc = doc->alc; + memset(&doc->alc, 0, sizeof(alc)); if (doc->str_pool) alc.free(alc.ctx, doc->str_pool); alc.free(alc.ctx, doc); } @@ -5668,6 +5673,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_bool(yyjson_mut_doc *doc, if (yyjson_likely(doc)) { yyjson_mut_val *val = unsafe_yyjson_mut_val(doc, 1); if (yyjson_likely(val)) { + _val = !!_val; val->tag = YYJSON_TYPE_BOOL | (uint8_t)((uint8_t)_val << 3); return val; } @@ -5920,7 +5926,8 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr(yyjson_mut_doc *doc) { yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_bool( yyjson_mut_doc *doc, const bool *vals, size_t count) { yyjson_mut_arr_with_func({ - val->tag = YYJSON_TYPE_BOOL | (uint8_t)((uint8_t)vals[i] << 3); + bool _val = !!vals[i]; + val->tag = YYJSON_TYPE_BOOL | (uint8_t)((uint8_t)_val << 3); }); } @@ -6872,6 +6879,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, const char *_key, bool _val) { yyjson_mut_obj_add_func({ + _val = !!_val; val->tag = YYJSON_TYPE_BOOL | (uint8_t)((uint8_t)(_val) << 3); }); } @@ -7730,33 +7738,39 @@ yyjson_api_inline bool yyjson_ptr_get_bool( } /** - Set provided `value` if the JSON Pointer (RFC 6901) exists and is type uint. - Returns true if value at `ptr` exists and is the correct type, otherwise false. + Set provided `value` if the JSON Pointer (RFC 6901) exists and is an integer + that fits in `uint64_t`. Returns true if successful, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_uint( yyjson_val *root, const char *ptr, uint64_t *value) { yyjson_val *val = yyjson_ptr_get(root, ptr); - if (value && yyjson_is_uint(val)) { - *value = unsafe_yyjson_get_uint(val); - return true; - } else { - return false; + if (value && val) { + uint64_t ret = val->uni.u64; + if (unsafe_yyjson_is_uint(val) || + (unsafe_yyjson_is_sint(val) && !(ret >> 63))) { + *value = ret; + return true; + } } + return false; } /** - Set provided `value` if the JSON Pointer (RFC 6901) exists and is type sint. - Returns true if value at `ptr` exists and is the correct type, otherwise false. + Set provided `value` if the JSON Pointer (RFC 6901) exists and is an integer + that fits in `int64_t`. Returns true if successful, otherwise false. */ yyjson_api_inline bool yyjson_ptr_get_sint( yyjson_val *root, const char *ptr, int64_t *value) { yyjson_val *val = yyjson_ptr_get(root, ptr); - if (value && yyjson_is_sint(val)) { - *value = unsafe_yyjson_get_sint(val); - return true; - } else { - return false; + if (value && val) { + int64_t ret = val->uni.i64; + if (unsafe_yyjson_is_sint(val) || + (unsafe_yyjson_is_uint(val) && ret >= 0)) { + *value = ret; + return true; + } } + return false; } /** diff --git a/src/common/time.h b/src/common/time.h index ddcd3f1c13..0becc7e1d3 100644 --- a/src/common/time.h +++ b/src/common/time.h @@ -1,12 +1,12 @@ #pragma once #include +#include +#include #ifdef _WIN32 #include #include #include -#else - #include #endif static inline uint64_t ffTimeGetTick() //In msec @@ -45,3 +45,37 @@ static inline void ffTimeSleep(uint32_t msec) nanosleep(&(struct timespec){ msec / 1000, (long) (msec % 1000) * 1000000 }, NULL); #endif } + +#ifdef _WIN32 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat" +#endif + +// Not thread-safe +static inline const char* ffTimeToFullStr(uint64_t msec) +{ + if (msec == 0) return ""; + time_t tsec = (time_t) (msec / 1000); + const struct tm* tm = localtime(&tsec); + + static char buf[32]; + strftime(buf, __builtin_strlen("0000-00-00T00:00:00") + 1, "%FT%T", tm); + snprintf(buf + __builtin_strlen("0000-00-00T00:00:00"), __builtin_strlen(".000") + 1, ".%03u", (unsigned) (msec % 1000)); + strftime(buf + __builtin_strlen("0000-00-00T00:00:00.000"), __builtin_strlen("+0000") + 1, "%z", tm); + return buf; +} + +// Not thread-safe +static inline const char* ffTimeToShortStr(uint64_t msec) +{ + if (msec == 0) return ""; + time_t tsec = (time_t) (msec / 1000); + + static char buf[32]; + strftime(buf, sizeof(buf), "%F %T", localtime(&tsec)); + return buf; +} + +#ifdef _WIN32 + #pragma GCC diagnostic pop +#endif diff --git a/src/detection/disk/disk.h b/src/detection/disk/disk.h index d288c83650..dc70bb88a4 100644 --- a/src/detection/disk/disk.h +++ b/src/detection/disk/disk.h @@ -17,6 +17,8 @@ typedef struct FFDisk uint32_t filesUsed; uint32_t filesTotal; + + uint64_t createTime; } FFDisk; /** diff --git a/src/detection/disk/disk_bsd.c b/src/detection/disk/disk_bsd.c index 8584530e9c..c6f9a1c82a 100644 --- a/src/detection/disk/disk_bsd.c +++ b/src/detection/disk/disk_bsd.c @@ -3,6 +3,7 @@ #include "util/stringUtils.h" #include +#include #ifdef __FreeBSD__ #include @@ -130,6 +131,10 @@ const char* ffDetectDisksImpl(FFlist* disks) if(fs->f_flags & MNT_RDONLY) disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT; + + struct stat st; + if(stat(fs->f_mntonname, &st) == 0) + disk->createTime = (uint64_t)((st.st_birthtimespec.tv_sec * 1000) + (st.st_birthtimespec.tv_nsec / 1000000)); } return NULL; diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index b7150bb474..89194414f0 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -6,9 +6,9 @@ #include #include #include +#include #include #include -#include #include #ifdef __USE_LARGEFILE64 @@ -241,6 +241,13 @@ static void detectStats(FFDisk* disk) if(fs.f_flag & ST_RDONLY) disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT; + + disk->createTime = 0; + #ifdef FF_HAVE_STATX + struct statx stx; + if (statx(0, disk->mountpoint.chars, 0, STATX_BTIME, &stx) == 0 && (stx.stx_mask & STATX_BTIME)) + disk->createTime = (uint64_t)((stx.stx_btime.tv_sec * 1000) + (stx.stx_btime.tv_nsec / 1000000)); + #endif } const char* ffDetectDisksImpl(FFlist* disks) diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c index cf951f2945..ef5329e50f 100644 --- a/src/detection/disk/disk_windows.c +++ b/src/detection/disk/disk_windows.c @@ -71,6 +71,12 @@ const char* ffDetectDisksImpl(FFlist* disks) disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT; } + WIN32_FILE_ATTRIBUTE_DATA data; + if(GetFileAttributesExW(mountpoint, GetFileExInfoStandard, &data) && data.ftCreationTime.dwHighDateTime > 0) + disk->createTime = (*(uint64_t*) &data.ftCreationTime - 116444736000000000ull) / 10000ull; + else + disk->createTime = 0; + ffStrbufInitWS(&disk->mountpoint, mountpoint); if (mountpoint[2] == L'\\' && mountpoint[3] == L'\0') { diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index f48c37caff..78609ef9c3 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -160,7 +160,7 @@ static const char* detectFromWTImpl(FFstrbuf* content, FFstrbuf* name, double* s #include #endif -static void detectFromWindowsTeriminal(const FFstrbuf* terminalExe, FFTerminalFontResult* terminalFont) +static void detectFromWindowsTerminal(const FFstrbuf* terminalExe, FFTerminalFontResult* terminalFont) { //https://learn.microsoft.com/en-us/windows/terminal/install#settings-json-file FF_STRBUF_AUTO_DESTROY json = ffStrbufCreate(); @@ -174,7 +174,7 @@ static void detectFromWindowsTeriminal(const FFstrbuf* terminalExe, FFTerminalFo char* pathEnd = jsonPath + strlen(jsonPath); strncpy(pathEnd, ".portable", sizeof(jsonPath) - (size_t) (pathEnd - jsonPath) - 1); - if(ffPathExists(jsonPath, FF_PATHTYPE_FILE)) + if(ffPathExists(jsonPath, FF_PATHTYPE_ANY)) { strncpy(pathEnd, "settings\\settings.json", sizeof(jsonPath) - (size_t) (pathEnd - jsonPath) - 1); if(!ffAppendFileBuffer(jsonPath, &json)) @@ -432,7 +432,7 @@ static bool detectTerminalFontCommon(const FFTerminalResult* terminal, FFTermina //Used by both Linux (WSL) and Windows else if(ffStrbufIgnCaseEqualS(&terminal->processName, "Windows Terminal") || ffStrbufIgnCaseEqualS(&terminal->processName, "WindowsTerminal.exe")) - detectFromWindowsTeriminal(&terminal->exe, terminalFont); + detectFromWindowsTerminal(&terminal->exe, terminalFont); #endif else diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index d62d7275ea..6d46a552a7 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -231,6 +231,7 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) ffStrEquals(name, "ltrace") || ffStrEquals(name, "perf") || ffStrEquals(name, "guake-wrapped") || + ffStrEquals(name, "time") || ffStrContainsIgnCase(name, "debug") || ffStrContainsIgnCase(name, "not-found") || ffStrEndsWith(name, ".sh") diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index bd92f08293..a0d5335840 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -98,16 +98,14 @@ static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) if( ffStrbufIgnCaseEqualS(&result->prettyName, "sudo") || ffStrbufIgnCaseEqualS(&result->prettyName, "su") || - ffStrbufIgnCaseEqualS(&result->prettyName, "doas") || - ffStrbufIgnCaseEqualS(&result->prettyName, "strace") || ffStrbufIgnCaseEqualS(&result->prettyName, "sshd") || ffStrbufIgnCaseEqualS(&result->prettyName, "gdb") || ffStrbufIgnCaseEqualS(&result->prettyName, "lldb") || - ffStrbufIgnCaseEqualS(&result->prettyName, "guake-wrapped") || ffStrbufIgnCaseEqualS(&result->prettyName, "python") || // python on windows generates shim executables ffStrbufIgnCaseEqualS(&result->prettyName, "fastfetch") || // scoop warps the real binaries with a "shim" exe ffStrbufIgnCaseEqualS(&result->prettyName, "flashfetch") || ffStrbufContainIgnCaseS(&result->prettyName, "debug") || + ffStrbufContainIgnCaseS(&result->prettyName, "time") || ffStrbufStartsWithIgnCaseS(&result->prettyName, "ConEmu") // https://github.com/fastfetch-cli/fastfetch/issues/488#issuecomment-1619982014 ) { ffStrbufClear(&result->processName); @@ -260,6 +258,7 @@ static bool detectDefaultTerminal(FFTerminalResult* result) if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE)) { result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1; + ffStrbufSet(&result->exePath, &result->exe); } else { @@ -294,7 +293,7 @@ static uint32_t getTerminalInfo(FFTerminalResult* result, uint32_t pid) while (pid != 0 && getProcessInfo(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, &hasGui)) { - if(!hasGui) + if(!hasGui || ffStrbufIgnCaseEqualS(&result->processName, "far.exe")) // Far includes GUI objects... { //We are in nested shell ffStrbufClear(&result->processName); diff --git a/src/fastfetch.c b/src/fastfetch.c index 0109c2717f..3959d7f8aa 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -819,10 +819,7 @@ static void run(FFdata* data) ffPrintCommandOption(data, instance.state.resultDoc); if (instance.state.resultDoc) - { - yyjson_mut_write_fp(stdout, instance.state.resultDoc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES, NULL, NULL); - putchar('\n'); - } + yyjson_mut_write_fp(stdout, instance.state.resultDoc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES | YYJSON_WRITE_NEWLINE_AT_END, NULL, NULL); else ffFinish(); } @@ -841,11 +838,11 @@ static void writeConfigFile(FFdata* data, const FFstrbuf* filename) ffMigrateCommandOptionToJsonc(data, doc); if (ffStrbufEqualS(filename, "-")) - yyjson_mut_write_fp(stdout, doc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES, NULL, NULL); + yyjson_mut_write_fp(stdout, doc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES | YYJSON_WRITE_NEWLINE_AT_END, NULL, NULL); else { size_t len; - FF_AUTO_FREE const char* str = yyjson_mut_write(doc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES, &len); + FF_AUTO_FREE const char* str = yyjson_mut_write(doc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES | YYJSON_WRITE_NEWLINE_AT_END, &len); if (!str) { printf("Error: failed to generate config file\n"); diff --git a/src/logo/ascii/opensuse_microos.txt b/src/logo/ascii/opensuse_microos.txt new file mode 100644 index 0000000000..5f6b4c075f --- /dev/null +++ b/src/logo/ascii/opensuse_microos.txt @@ -0,0 +1,10 @@ + ⣀⣠⣴⣶⣶⣿⣿⣿⣿⣶⣶⣦⣄⣀ + ⢀⣴⣾⣿⠿⠛⠉⠉ ⠉⠉⠛⠿⣿⣷⣦⡀ + ⣴⣿⡿⠋ ⠙⢿⣿⣦ + ⣾⣿⡟ ⣠⣴⣶⣿⣿⣶⣦⣄ ⢻⣿⣷ +⣠⣤⣤⣤⣤⣤⣤⣼⣿⣿ ⣼⣿⡟⠉ ⠉⢻⣿⣧ ⣿⣿⣧⣤⣤⣤⣤⣤⣤⣄ +⠙⠛⠛⠛⠛⠛⠛⢻⣿⣿ ⢻⣿⣧⡀ ⢀⣼⣿⡟ ⣿⣿⡟⠛⠛⠛⠛⠛⠛⠋ + ⢿⣿⣇ ⠙⠿⣿⣿⣿⣿⠿⠋ ⣸⣿⡿ + ⠈⢻⣿⣧⣀ ⣀⣾⣿⡟⠁ + ⠙⠻⣿⣷⣦⣄⣀ ⣀⣠⣴⣾⣿⠟⠋ + ⠉⠛⠿⢿⣿⣿⣿⣿⣿⣿⡿⠿⠛⠉ diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 2b06782c55..df4c95c6d5 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -224,7 +224,7 @@ static const FFlogo A[] = { .colors = { FF_COLOR_FG_BLUE, FF_COLOR_FG_BLACK, - FF_COLOR_FG_GREEN, + FF_COLOR_FG_RED, FF_COLOR_FG_YELLOW, }, }, @@ -2932,6 +2932,14 @@ static const FFlogo O[] = { .colorKeys = FF_COLOR_FG_GREEN, .colorTitle = FF_COLOR_FG_GREEN, }, + // openSuseMicroOS + { + .names = {"opensuse-microos", "opensuse_microos"}, + .lines = FASTFETCH_DATATEXT_LOGO_OPENSUSE_MICROOS, + .colors = { + FF_COLOR_FG_GREEN, + }, + }, // OpenSuseLeap { .names = {"opensuse_leap", "open_suse_leap", "opensuse-leap", "open-suse-leap", "suse_leap", "suse-leap", "opensuseleap"}, diff --git a/src/modules/disk/disk.c b/src/modules/disk/disk.c index 20c53f980c..364d5bf2d3 100644 --- a/src/modules/disk/disk.c +++ b/src/modules/disk/disk.c @@ -2,11 +2,12 @@ #include "common/jsonconfig.h" #include "common/parsing.h" #include "common/percent.h" +#include "common/time.h" #include "detection/disk/disk.h" #include "modules/disk/disk.h" #include "util/stringUtils.h" -#define FF_DISK_NUM_FORMAT_ARGS 11 +#define FF_DISK_NUM_FORMAT_ARGS 12 #pragma GCC diagnostic ignored "-Wsign-conversion" static void printDisk(FFDiskOptions* options, const FFDisk* disk) @@ -112,6 +113,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) bool isExternal = !!(disk->type & FF_DISK_VOLUME_TYPE_EXTERNAL_BIT); bool isHidden = !!(disk->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT); bool isReadOnly = !!(disk->type & FF_DISK_VOLUME_TYPE_READONLY_BIT); + FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_DISK_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, @@ -124,6 +126,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) {FF_FORMAT_ARG_TYPE_STRBUF, &disk->filesystem}, {FF_FORMAT_ARG_TYPE_STRBUF, &disk->name}, {FF_FORMAT_ARG_TYPE_BOOL, &isReadOnly}, + {FF_FORMAT_ARG_TYPE_STRING, ffTimeToShortStr(disk->createTime)}, })); } } @@ -448,6 +451,12 @@ void ffGenerateDiskJsonResult(FFDiskOptions* options, yyjson_mut_doc* doc, yyjso yyjson_mut_arr_add_str(doc, typeArr, "Hidden"); if(item->type & FF_DISK_VOLUME_TYPE_READONLY_BIT) yyjson_mut_arr_add_str(doc, typeArr, "Read-only"); + + const char* pstr = ffTimeToFullStr(item->createTime); + if (*pstr) + yyjson_mut_obj_add_strcpy(doc, obj, "createTime", pstr); + else + yyjson_mut_obj_add_null(doc, obj, "createTime"); } FF_LIST_FOR_EACH(FFDisk, item, disks) @@ -473,6 +482,7 @@ void ffPrintDiskHelpFormat(void) "Filesystem", "Label / name", "True if read-only", + "Create time in local timezone", })); } diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 9107624de3..843f620c6b 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -123,33 +123,63 @@ bool ffParsePackagesCommandOptions(FFPackagesOptions* options, const char* key, *end = '\0'; #define FF_TEST_PACKAGE_NAME(name) else if (ffStrEqualsIgnCase(start, #name)) { options->disabled |= FF_PACKAGES_FLAG_ ## name ## _BIT; } - if (false); - FF_TEST_PACKAGE_NAME(APK) - FF_TEST_PACKAGE_NAME(BREW) - FF_TEST_PACKAGE_NAME(CHOCO) - FF_TEST_PACKAGE_NAME(DPKG) - FF_TEST_PACKAGE_NAME(EMERGE) - FF_TEST_PACKAGE_NAME(EOPKG) - FF_TEST_PACKAGE_NAME(FLATPAK) - FF_TEST_PACKAGE_NAME(NIX) - FF_TEST_PACKAGE_NAME(OPKG) - FF_TEST_PACKAGE_NAME(PACMAN) - FF_TEST_PACKAGE_NAME(PALUDIS) - FF_TEST_PACKAGE_NAME(PKG) - FF_TEST_PACKAGE_NAME(PKGTOOL) - FF_TEST_PACKAGE_NAME(MACPORTS) - FF_TEST_PACKAGE_NAME(RPM) - FF_TEST_PACKAGE_NAME(SCOOP) - FF_TEST_PACKAGE_NAME(SNAP) - FF_TEST_PACKAGE_NAME(WINGET) - FF_TEST_PACKAGE_NAME(XBPS) - FF_TEST_PACKAGE_NAME(AM) + switch (toupper(start[0])) + { + case 'A': if (false); + FF_TEST_PACKAGE_NAME(APK) + FF_TEST_PACKAGE_NAME(AM) + break; + case 'B': if (false); + FF_TEST_PACKAGE_NAME(BREW) + break; + case 'C': if (false); + FF_TEST_PACKAGE_NAME(CHOCO) + break; + case 'D': if (false); + FF_TEST_PACKAGE_NAME(DPKG) + break; + case 'E': if (false); + FF_TEST_PACKAGE_NAME(EMERGE) + FF_TEST_PACKAGE_NAME(EOPKG) + break; + case 'F': if (false); + FF_TEST_PACKAGE_NAME(FLATPAK) + break; + case 'M': if (false); + FF_TEST_PACKAGE_NAME(MACPORTS) + break; + case 'N': if (false); + FF_TEST_PACKAGE_NAME(NIX) + break; + case 'O': if (false); + FF_TEST_PACKAGE_NAME(OPKG) + break; + case 'P': if (false); + FF_TEST_PACKAGE_NAME(PACMAN) + FF_TEST_PACKAGE_NAME(PKG) + FF_TEST_PACKAGE_NAME(PKGTOOL) + FF_TEST_PACKAGE_NAME(PALUDIS) + break; + case 'R': if (false); + FF_TEST_PACKAGE_NAME(RPM) + break; + case 'S': if (false); + FF_TEST_PACKAGE_NAME(SCOOP) + FF_TEST_PACKAGE_NAME(SNAP) + break; + case 'W': if (false); + FF_TEST_PACKAGE_NAME(WINGET) + break; + case 'X': if (false); + FF_TEST_PACKAGE_NAME(XBPS) + break; + } #undef FF_TEST_PACKAGE_NAME if (end) { start = end + 1; - end = strchr(end, ':'); + end = strchr(start, ':'); } else break; @@ -193,29 +223,60 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) const char* flag = yyjson_get_str(flagObj); #define FF_TEST_PACKAGE_NAME(name) else if (ffStrEqualsIgnCase(flag, #name)) { options->disabled |= FF_PACKAGES_FLAG_ ## name ## _BIT; } - if (false); - FF_TEST_PACKAGE_NAME(APK) - FF_TEST_PACKAGE_NAME(BREW) - FF_TEST_PACKAGE_NAME(CHOCO) - FF_TEST_PACKAGE_NAME(DPKG) - FF_TEST_PACKAGE_NAME(EMERGE) - FF_TEST_PACKAGE_NAME(EOPKG) - FF_TEST_PACKAGE_NAME(FLATPAK) - FF_TEST_PACKAGE_NAME(NIX) - FF_TEST_PACKAGE_NAME(OPKG) - FF_TEST_PACKAGE_NAME(PACMAN) - FF_TEST_PACKAGE_NAME(PALUDIS) - FF_TEST_PACKAGE_NAME(PKG) - FF_TEST_PACKAGE_NAME(PKGTOOL) - FF_TEST_PACKAGE_NAME(MACPORTS) - FF_TEST_PACKAGE_NAME(RPM) - FF_TEST_PACKAGE_NAME(SCOOP) - FF_TEST_PACKAGE_NAME(SNAP) - FF_TEST_PACKAGE_NAME(WINGET) - FF_TEST_PACKAGE_NAME(XBPS) - FF_TEST_PACKAGE_NAME(AM) + switch (toupper(flag[0])) + { + case 'A': if (false); + FF_TEST_PACKAGE_NAME(APK) + FF_TEST_PACKAGE_NAME(AM) + break; + case 'B': if (false); + FF_TEST_PACKAGE_NAME(BREW) + break; + case 'C': if (false); + FF_TEST_PACKAGE_NAME(CHOCO) + break; + case 'D': if (false); + FF_TEST_PACKAGE_NAME(DPKG) + break; + case 'E': if (false); + FF_TEST_PACKAGE_NAME(EMERGE) + FF_TEST_PACKAGE_NAME(EOPKG) + break; + case 'F': if (false); + FF_TEST_PACKAGE_NAME(FLATPAK) + break; + case 'M': if (false); + FF_TEST_PACKAGE_NAME(MACPORTS) + break; + case 'N': if (false); + FF_TEST_PACKAGE_NAME(NIX) + break; + case 'O': if (false); + FF_TEST_PACKAGE_NAME(OPKG) + break; + case 'P': if (false); + FF_TEST_PACKAGE_NAME(PACMAN) + FF_TEST_PACKAGE_NAME(PKG) + FF_TEST_PACKAGE_NAME(PKGTOOL) + FF_TEST_PACKAGE_NAME(PALUDIS) + break; + case 'R': if (false); + FF_TEST_PACKAGE_NAME(RPM) + break; + case 'S': if (false); + FF_TEST_PACKAGE_NAME(SCOOP) + FF_TEST_PACKAGE_NAME(SNAP) + break; + case 'W': if (false); + FF_TEST_PACKAGE_NAME(WINGET) + break; + case 'X': if (false); + FF_TEST_PACKAGE_NAME(XBPS) + break; + } #undef FF_TEST_PACKAGE_NAME } + continue; } } diff --git a/src/modules/uptime/uptime.c b/src/modules/uptime/uptime.c index 791e7b8daa..1a513152a6 100644 --- a/src/modules/uptime/uptime.c +++ b/src/modules/uptime/uptime.c @@ -1,10 +1,11 @@ #include "common/printing.h" #include "common/jsonconfig.h" +#include "common/time.h" #include "detection/uptime/uptime.h" #include "modules/uptime/uptime.h" #include "util/stringUtils.h" -#define FF_UPTIME_NUM_FORMAT_ARGS 7 +#define FF_UPTIME_NUM_FORMAT_ARGS 6 void ffPrintUptime(FFUptimeOptions* options) { @@ -83,8 +84,7 @@ void ffPrintUptime(FFUptimeOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &minutes}, {FF_FORMAT_ARG_TYPE_UINT, &seconds}, {FF_FORMAT_ARG_TYPE_UINT, &milliseconds}, - {FF_FORMAT_ARG_TYPE_UINT64, &result.uptime}, - {FF_FORMAT_ARG_TYPE_UINT64, &result.bootTime}, + {FF_FORMAT_ARG_TYPE_STRING, ffTimeToShortStr(result.uptime)}, })); } } @@ -137,7 +137,7 @@ void ffGenerateUptimeJsonResult(FF_MAYBE_UNUSED FFUptimeOptions* options, yyjson yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); yyjson_mut_obj_add_uint(doc, obj, "uptime", result.uptime); - yyjson_mut_obj_add_uint(doc, obj, "bootTime", result.bootTime); + yyjson_mut_obj_add_strcpy(doc, obj, "bootTime", ffTimeToFullStr(result.bootTime)); } void ffPrintUptimeHelpFormat(void) @@ -148,8 +148,7 @@ void ffPrintUptimeHelpFormat(void) "Minutes", "Seconds", "Milliseconds", - "Uptime in unix timestamp (ms)", - "Boot time in unix timestamp (ms)", + "Boot time in local timezone", })); } diff --git a/src/modules/users/users.c b/src/modules/users/users.c index d77b3b7f59..f2aa3e527d 100644 --- a/src/modules/users/users.c +++ b/src/modules/users/users.c @@ -1,11 +1,10 @@ #include "common/printing.h" #include "common/jsonconfig.h" +#include "common/time.h" #include "detection/users/users.h" #include "modules/users/users.h" #include "util/stringUtils.h" -#include - #pragma GCC diagnostic ignored "-Wformat" // warning: unknown conversion type character 'F' in format #define FF_USERS_NUM_FORMAT_ARGS 5 @@ -57,12 +56,7 @@ void ffPrintUsers(FFUsersOptions* options) ffStrbufAppendF(&result, "@%s", user->hostName.chars); if(user->loginTime) - { - char buf[64]; - time_t time = (time_t) (user->loginTime / 1000); - strftime(buf, sizeof(buf), "%FT%T%z", localtime(&time)); - ffStrbufAppendF(&result, " - login time %s", buf); - } + ffStrbufAppendF(&result, " - login time %s", ffTimeToShortStr(user->loginTime)); ffStrbufPutTo(&result, stdout); } @@ -79,7 +73,7 @@ void ffPrintUsers(FFUsersOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &user->hostName}, {FF_FORMAT_ARG_TYPE_STRBUF, &user->sessionName}, {FF_FORMAT_ARG_TYPE_STRBUF, &user->clientIp}, - {FF_FORMAT_ARG_TYPE_UINT64, &user->loginTime}, + {FF_FORMAT_ARG_TYPE_STRING, ffTimeToShortStr(user->loginTime)}, })); } } @@ -163,7 +157,11 @@ void ffGenerateUsersJsonResult(FF_MAYBE_UNUSED FFUsersOptions* options, yyjson_m yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &user->hostName); yyjson_mut_obj_add_strbuf(doc, obj, "sessionName", &user->sessionName); yyjson_mut_obj_add_strbuf(doc, obj, "clientIp", &user->clientIp); - yyjson_mut_obj_add_uint(doc, obj, "loginTime", user->loginTime); + const char* pstr = ffTimeToFullStr(user->loginTime); + if (*pstr) + yyjson_mut_obj_add_strcpy(doc, obj, "loginTime", pstr); + else + yyjson_mut_obj_add_null(doc, obj, "loginTime"); } exit: @@ -183,7 +181,7 @@ void ffPrintUsersHelpFormat(void) "Host name", "Session name", "Client IP", - "Login Time" + "Login Time in local timezone" })); } diff --git a/src/util/platform/FFPlatform.c b/src/util/platform/FFPlatform.c index b81adc3c6a..8ce979044e 100644 --- a/src/util/platform/FFPlatform.c +++ b/src/util/platform/FFPlatform.c @@ -52,6 +52,7 @@ void ffPlatformDestroy(FFPlatform* platform) FF_LIST_FOR_EACH(FFstrbuf, dir, platform->dataDirs) ffStrbufDestroy(dir); ffListDestroy(&platform->dataDirs); + ffStrbufDestroy(&platform->exePath); ffStrbufDestroy(&platform->userName); ffStrbufDestroy(&platform->hostName);