mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-31 20:20:47 +01:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d697197a8b | |||
| d6c15e1cec | |||
| d4dd15763f | |||
| 7d5174a489 | |||
| 1ee988a698 | |||
| 2eea01aac3 | |||
| 44b32e9f84 | |||
| 06957877a8 | |||
| 2f68ce7f74 | |||
| 408646df1c | |||
| ad62e3d1f5 |
@@ -157,7 +157,7 @@ jobs:
|
||||
ls -l fastfetch
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-android-${{ inputs.abi }}
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: download benchmark data
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: benchmark-*
|
||||
path: benchmarks
|
||||
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-freebsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-haiku-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-linux-armv7l
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
||||
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||
with:
|
||||
languages: c
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
||||
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
@@ -123,13 +123,13 @@ jobs:
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
- name: upload benchmark data
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: benchmark-linux-${{ inputs.arch }}
|
||||
path: benchmark.json
|
||||
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
'
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-linux-loong64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -74,13 +74,13 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-macos-universal
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
- name: upload benchmark data
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: benchmark-macos-universal
|
||||
path: benchmark.json
|
||||
|
||||
@@ -11,7 +11,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-musl-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-omnios-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
- name: download artifacts
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
|
||||
- name: create release
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-solaris-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
id: upload-unsigned-artifact
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-windows-${{ inputs.arch }}
|
||||
path: |
|
||||
@@ -134,14 +134,14 @@ jobs:
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ inputs.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: fastfetch-windows-${{ inputs.arch }}
|
||||
path: ./fastfetch-windows-${{ inputs.arch }}.*
|
||||
overwrite: true
|
||||
|
||||
- name: upload benchmark data
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: benchmark-windows-${{ inputs.arch }}
|
||||
path: benchmark.json
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
# Unreleased
|
||||
|
||||
Features:
|
||||
* Added display detection through the `org.gnome.Mutter.DisplayConfig` D-Bus interface on GNOME and Cinnamon Wayland sessions, which reports the primary monitor and the raw EDID directly instead of matching the `wl_output` name against the DRM connectors. A D-Bus enabled build is required. (Display, Linux)
|
||||
|
||||
Bugfixes:
|
||||
* Rejected an empty `separator.string` in the JSON config with an error message. (#2689, Separator)
|
||||
* Fixed fastfetch blocking forever when the terminal answered a terminal query only partially, for example with just one of the two OSC 10/11 color replies. (Logo / Terminal / TerminalTheme, Unix / Windows)
|
||||
* Fixed a `$` ending a logo line or the logo itself being parsed as the start of a color code instead of a plain dollar sign. (#2666, Logo)
|
||||
* Fixed AMD GPU name detection in amdgpu.ids when PCI device ID or revision ID has leading zeros. (GPU, Linux)
|
||||
* Improved uptime detection on SunOS via kstat. (#2674, Uptime, SunOS)
|
||||
* Fixed substring formatting returning the remaining string when a negative end index is before the start of the string. (General)
|
||||
* Fixed a skipped `{?}` / `{/}` conditional in a format string ending at the `{?}` / `{/}` of a conditional nested in it. (General)
|
||||
* Fixed numbers in format strings that don't fit in 32 bits wrapping around to a valid argument index, constant index, substring index or length, e.g. `{4294967297}` being read as `{1}`, and a signed integer overflow for `{1:-2147483648}`. (General)
|
||||
* Fixed substring formatting printing the placeholder as is, instead of nothing, when the start index is outside of the string and an end index is given. (General)
|
||||
|
||||
Logos:
|
||||
* Added Tiny Core Linux (#2655)
|
||||
|
||||
# 2.70.0
|
||||
|
||||
|
||||
@@ -4142,7 +4142,8 @@
|
||||
"string": {
|
||||
"description": "String to print for the separator line",
|
||||
"type": "string",
|
||||
"default": "-"
|
||||
"default": "-",
|
||||
"minLength": 1
|
||||
},
|
||||
"outputColor": {
|
||||
"description": "Color of the separator line",
|
||||
|
||||
+76
-17
@@ -63,6 +63,13 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) {
|
||||
}
|
||||
}
|
||||
|
||||
// strtol() to an int32_t. Values that don't fit are clamped instead of wrapping around,
|
||||
// so that e.g. 4294967297 can't turn into 1.
|
||||
static int32_t strToInt32(const char* str, char** pEnd) {
|
||||
long long value = strtoll(str, pEnd, 10);
|
||||
return value > INT32_MAX ? INT32_MAX : value < INT32_MIN ? INT32_MIN : (int32_t) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief parses a string to a uint32_t
|
||||
*
|
||||
@@ -79,14 +86,14 @@ static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs,
|
||||
|
||||
if (firstChar >= '0' && firstChar <= '9') {
|
||||
char* pEnd = nullptr;
|
||||
uint32_t result = (uint32_t) strtoul(placeholderValue, &pEnd, 10);
|
||||
unsigned long result = strtoul(placeholderValue, &pEnd, 10);
|
||||
if (result > numArgs) {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
if (*pEnd != '\0') {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
return result;
|
||||
return (uint32_t) result;
|
||||
} else if (ffCharIsEnglishAlphabet(firstChar)) {
|
||||
for (uint32_t i = 0; i < numArgs; ++i) {
|
||||
const FFformatarg* arg = &arguments[i];
|
||||
@@ -343,6 +350,51 @@ static bool skipAnsiEscape(FFstrbuf* in, FFstrbuf* out, FFstrbuf* trailingEscape
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the index of the `}` of the `{<marker>}` that closes the conditional we are in, or the
|
||||
// length of the format string if there is none. `i` is the index of the `}` that ends the opening
|
||||
// placeholder. The part to skip may hold conditionals of its own, which have to be passed over.
|
||||
// Like in `parseFormatString`, only a conditional with a valid argument opens a block.
|
||||
static uint32_t findConditionalEnd(const FFstrbuf* formatstr, uint32_t i, char marker, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate();
|
||||
uint32_t depth = 0;
|
||||
|
||||
for (++i; i < formatstr->length; ++i) {
|
||||
if (formatstr->chars[i] != '{') {
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
if (i >= formatstr->length) {
|
||||
break;
|
||||
}
|
||||
|
||||
// double {{ evaluates to a single { and doesn't start a placeholder
|
||||
if (formatstr->chars[i] == '{') {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t iEnd = ffStrbufNextIndexC(formatstr, i, '}');
|
||||
if (formatstr->chars[i] == marker) {
|
||||
if (iEnd - i == 1) {
|
||||
if (depth == 0) {
|
||||
return iEnd;
|
||||
}
|
||||
--depth;
|
||||
} else {
|
||||
ffStrbufClear(&placeholderValue);
|
||||
ffStrbufAppendNS(&placeholderValue, iEnd - i - 1, &formatstr->chars[i + 1]);
|
||||
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||
if (index >= 1 && index <= numArgs) {
|
||||
++depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
i = iEnd;
|
||||
}
|
||||
|
||||
return formatstr->length;
|
||||
}
|
||||
|
||||
static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
uint32_t argCounter = 0;
|
||||
|
||||
@@ -440,7 +492,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
||||
}
|
||||
|
||||
// fastforward to the end of the if without printing the in between
|
||||
i = ffStrbufNextIndexS(formatstr, i, "{?}") + 2; // 2 is the length of "{?}" - 1 because the loop will increment it again directly after continue
|
||||
i = findConditionalEnd(formatstr, i, '?', numArgs, arguments);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -463,7 +515,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
||||
}
|
||||
|
||||
// fastforward to the end of the if without printing the in between
|
||||
i = ffStrbufNextIndexS(formatstr, i, "{/}") + 2; // 2 is the length of "{/}" - 1 because the loop will increment it again directly after continue
|
||||
i = findConditionalEnd(formatstr, i, '/', numArgs, arguments);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -480,7 +532,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
||||
// test for constant or env var, if so evaluate it
|
||||
if (firstChar == '$') {
|
||||
char* pend = nullptr;
|
||||
int32_t indexSigned = (int32_t) strtol(placeholderValue.chars + 1, &pend, 10);
|
||||
int32_t indexSigned = strToInt32(placeholderValue.chars + 1, &pend);
|
||||
if (pend == placeholderValue.chars + 1) {
|
||||
// treat placeholder as an environment variable
|
||||
char* envValue = getenv(placeholderValue.chars + 1);
|
||||
@@ -539,20 +591,27 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
||||
skipAnsiEscape(&tempString, buffer, &trailingEscape);
|
||||
|
||||
char* pEnd = nullptr;
|
||||
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
||||
if (start < 0) {
|
||||
start = (int32_t) tempString.length + start;
|
||||
int32_t start = strToInt32(pSep + 1, &pEnd);
|
||||
bool hasEnd = *pEnd == ',';
|
||||
int32_t end = 0;
|
||||
if (hasEnd) {
|
||||
end = strToInt32(pEnd + 1, &pEnd);
|
||||
}
|
||||
if (start >= 0 && (uint32_t) start < tempString.length) {
|
||||
if (*pEnd == '\0') {
|
||||
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
||||
} else if (*pEnd == ',') {
|
||||
int32_t end = (int32_t) strtol(pEnd + 1, &pEnd, 10);
|
||||
if (!*pEnd) {
|
||||
|
||||
// Check the whole range before using it, so that a valid range that lies
|
||||
// outside of the string gives an empty result instead of an invalid placeholder
|
||||
if (!*pEnd) {
|
||||
if (start < 0) {
|
||||
start = (int32_t) tempString.length + start;
|
||||
}
|
||||
if (start >= 0 && (uint32_t) start < tempString.length) {
|
||||
if (!hasEnd) {
|
||||
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
||||
} else {
|
||||
if (end < 0) {
|
||||
end = (int32_t) tempString.length + end;
|
||||
}
|
||||
if ((uint32_t) end > tempString.length) {
|
||||
if (end >= 0 && (uint32_t) end > tempString.length) {
|
||||
end = (int32_t) tempString.length;
|
||||
}
|
||||
if (end > start) {
|
||||
@@ -573,7 +632,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
||||
}
|
||||
} else {
|
||||
char* pEnd = nullptr;
|
||||
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
||||
int32_t truncLength = strToInt32(pSep + 1, &pEnd);
|
||||
if (*pEnd != '\0') {
|
||||
*pSep = cSep;
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
@@ -583,7 +642,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
|
||||
bool ellipsis = false;
|
||||
if (truncLength < 0) {
|
||||
ellipsis = true;
|
||||
truncLength = -truncLength;
|
||||
truncLength = truncLength == INT32_MIN ? INT32_MAX : -truncLength;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
||||
|
||||
+36
-21
@@ -149,6 +149,32 @@ void restoreTerm(void) {
|
||||
tcsetattr(ftty, TCSAFLUSH, &oldTerm);
|
||||
}
|
||||
|
||||
static const char* waitForTerminalResponse(void) {
|
||||
#ifndef __APPLE__
|
||||
const int pollResult = poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS);
|
||||
if (pollResult <= 0) {
|
||||
FF_DEBUG("poll(/dev/tty) returned %d after %d ms: %s",
|
||||
pollResult, FF_IO_TERM_RESP_WAIT_MS, pollResult == 0 ? "timed out" : strerror(errno));
|
||||
return "poll(/dev/tty) timeout or failed";
|
||||
}
|
||||
#else
|
||||
{
|
||||
// On macOS, poll(/dev/tty) always returns immediately
|
||||
// See also https://nathancraddock.com/blog/macos-dev-tty-polling/
|
||||
fd_set rd;
|
||||
FD_ZERO(&rd);
|
||||
FD_SET(ftty, &rd);
|
||||
const int selectResult = select(ftty + 1, &rd, nullptr, nullptr, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 });
|
||||
if (selectResult <= 0) {
|
||||
FF_DEBUG("select(/dev/tty) returned %d after %d ms: %s",
|
||||
selectResult, FF_IO_TERM_RESP_WAIT_MS, selectResult == 0 ? "timed out" : strerror(errno));
|
||||
return "select(/dev/tty) timeout or failed";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
||||
if (ftty < 0) {
|
||||
ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||
@@ -174,28 +200,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
ffWriteFDData(ftty, strlen(request), request);
|
||||
|
||||
// Give the terminal some time to respond
|
||||
#ifndef __APPLE__
|
||||
const int pollResult = poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS);
|
||||
if (pollResult <= 0) {
|
||||
FF_DEBUG("poll(/dev/tty) returned %d after %d ms: %s",
|
||||
pollResult, FF_IO_TERM_RESP_WAIT_MS, pollResult == 0 ? "timed out" : strerror(errno));
|
||||
return "poll(/dev/tty) timeout or failed";
|
||||
const char* error = waitForTerminalResponse();
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
#else
|
||||
{
|
||||
// On macOS, poll(/dev/tty) always returns immediately
|
||||
// See also https://nathancraddock.com/blog/macos-dev-tty-polling/
|
||||
fd_set rd;
|
||||
FD_ZERO(&rd);
|
||||
FD_SET(ftty, &rd);
|
||||
const int selectResult = select(ftty + 1, &rd, nullptr, nullptr, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 });
|
||||
if (selectResult <= 0) {
|
||||
FF_DEBUG("select(/dev/tty) returned %d after %d ms: %s",
|
||||
selectResult, FF_IO_TERM_RESP_WAIT_MS, selectResult == 0 ? "timed out" : strerror(errno));
|
||||
return "select(/dev/tty) timeout or failed";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
char buffer[1024];
|
||||
size_t bytesRead = 0;
|
||||
@@ -204,6 +212,13 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
va_start(args, format);
|
||||
|
||||
while (true) {
|
||||
// The response may arrive in several chunks; a terminal that sends an incomplete
|
||||
// response would otherwise block `read` forever
|
||||
if (bytesRead > 0 && (error = waitForTerminalResponse())) {
|
||||
va_end(args);
|
||||
return error;
|
||||
}
|
||||
|
||||
ssize_t nRead = read(ftty, buffer + bytesRead, sizeof(buffer) - bytesRead - 1);
|
||||
|
||||
if (nRead <= 0) {
|
||||
|
||||
@@ -414,6 +414,35 @@ void ffListFilesRecursively(const char* path, bool pretty) {
|
||||
listFilesRecursively(folder.length, &folder, 0, nullptr, pretty);
|
||||
}
|
||||
|
||||
static const char* waitForTerminalResponse(HANDLE hInput) {
|
||||
while (true) {
|
||||
NTSTATUS status = NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 });
|
||||
if (status != STATUS_WAIT_0) {
|
||||
FF_DEBUG("NtWaitForSingleObject() failed or timeout: %s", ffDebugNtStatus(status));
|
||||
return "NtWaitForSingleObject() failed or timeout";
|
||||
}
|
||||
|
||||
// Ignore all unexpected input events
|
||||
INPUT_RECORD record;
|
||||
DWORD len = 0;
|
||||
if (!PeekConsoleInputW(hInput, &record, 1, &len)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Key-up events carry no input for `ReadFile`; leaving one in the buffer would make the
|
||||
// wait return while the next `ReadFile` still blocks
|
||||
if (
|
||||
record.EventType == KEY_EVENT &&
|
||||
record.Event.KeyEvent.bKeyDown &&
|
||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\r' &&
|
||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\n') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ReadConsoleInputW(hInput, &record, 1, &len);
|
||||
}
|
||||
}
|
||||
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
||||
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
FF_AUTO_CLOSE_FD HANDLE hConin = INVALID_HANDLE_VALUE;
|
||||
@@ -440,29 +469,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
WriteFile(hOutput, request, (DWORD) strlen(request), &bytes, nullptr);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
NTSTATUS status = NtWaitForSingleObject(hInput, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) FF_IO_TERM_RESP_WAIT_MS * -10000 });
|
||||
if (status != STATUS_WAIT_0) {
|
||||
FF_DEBUG("NtWaitForSingleObject() failed or timeout: %s", ffDebugNtStatus(status));
|
||||
SetConsoleMode(hInput, inputMode);
|
||||
return "NtWaitForSingleObject() failed or timeout";
|
||||
}
|
||||
|
||||
// Ignore all unexpected input events
|
||||
INPUT_RECORD record;
|
||||
DWORD len = 0;
|
||||
if (!PeekConsoleInputW(hInput, &record, 1, &len)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
record.EventType == KEY_EVENT &&
|
||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\r' &&
|
||||
record.Event.KeyEvent.uChar.UnicodeChar != L'\n') {
|
||||
break;
|
||||
} else {
|
||||
ReadConsoleInputW(hInput, &record, 1, &len);
|
||||
}
|
||||
const char* error = waitForTerminalResponse(hInput);
|
||||
if (error) {
|
||||
SetConsoleMode(hInput, inputMode);
|
||||
return error;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
@@ -472,6 +482,16 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
uint32_t bytesRead = 0;
|
||||
|
||||
while (true) {
|
||||
// The response may arrive in several chunks; a terminal that sends an incomplete
|
||||
// response would otherwise block `ReadFile` forever
|
||||
if (bytesRead > 0 && (error = waitForTerminalResponse(hInput))) {
|
||||
va_end(args);
|
||||
if (hasInputMode) {
|
||||
SetConsoleMode(hInput, inputMode);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
DWORD bytes = 0;
|
||||
BOOL readOk = ReadFile(hInput, buffer + bytesRead, (DWORD) (sizeof(buffer) - 1 - bytesRead), &bytes, nullptr);
|
||||
if (!readOk || bytes == 0) {
|
||||
|
||||
@@ -272,7 +272,7 @@ static bool loadAmdGpuIdsInc(uint16_t deviceId, uint8_t revision, FFGPUResult* g
|
||||
|
||||
static void parseAmdGpuIdsFile(uint16_t deviceId, uint8_t revision, FFGPUResult* gpu) {
|
||||
char query[32];
|
||||
snprintf(query, ARRAY_SIZE(query), "%X,\t%X,", (unsigned) deviceId, (unsigned) revision);
|
||||
snprintf(query, ARRAY_SIZE(query), "%04X,\t%02X,", (unsigned) deviceId, (unsigned) revision);
|
||||
#ifdef FF_CUSTOM_AMDGPU_IDS_PATH
|
||||
FF_DEBUG("Searching AMD GPU IDs at custom path '%s' for device=0x%04x revision=0x%02x", FF_STR(FF_CUSTOM_AMDGPU_IDS_PATH), deviceId, revision);
|
||||
ffParsePropFile(FF_STR(FF_CUSTOM_AMDGPU_IDS_PATH), query, &gpu->name);
|
||||
|
||||
@@ -52,15 +52,15 @@ const char* ffDetectWifi(FFlist* result) {
|
||||
|
||||
if (inf.ssid) { // https://developer.apple.com/forums/thread/732431
|
||||
ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String);
|
||||
} else if (networkProfile.ssid) {
|
||||
ffStrbufSetStatic(&item->conn.ssid, inf.configuration.networkProfiles.firstObject.ssid.UTF8String);
|
||||
} else if (networkProfile.ssid) { // Dead in macOS 27.2
|
||||
ffStrbufSetStatic(&item->conn.ssid, networkProfile.ssid.UTF8String);
|
||||
} else {
|
||||
ffStrbufSetStatic(&item->conn.ssid, "<redacted>"); // https://developer.apple.com/forums/thread/732431
|
||||
ffStrbufSetStatic(&item->conn.ssid, "<redacted>");
|
||||
}
|
||||
|
||||
if (inf.bssid) {
|
||||
ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String);
|
||||
} else if (networkProfile.bssidList) {
|
||||
} else if (networkProfile.bssidList) { // Dead in macOS 27.2
|
||||
ffStrbufSetStatic(&item->conn.bssid, [networkProfile.bssidList.firstObject[@"BSSID"] UTF8String]);
|
||||
} else {
|
||||
ffStrbufSetStatic(&item->conn.bssid, "<redacted>");
|
||||
|
||||
@@ -80,6 +80,14 @@ void ffParseSeparatorJsonObject(FFSeparatorOptions* options, yyjson_val* module)
|
||||
}
|
||||
|
||||
if (unsafe_yyjson_equals_str(key, "string")) {
|
||||
if (!yyjson_is_str(val)) {
|
||||
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Property 'string' must be a string");
|
||||
continue;
|
||||
}
|
||||
if (unsafe_yyjson_get_len(val) == 0) {
|
||||
ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Property 'string' must not be empty");
|
||||
continue;
|
||||
}
|
||||
ffStrbufSetJsonVal(&options->string, val);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,19 @@ static void verify(const char* format, const char* arg, const char* expected, in
|
||||
|
||||
#define VERIFY(format, argument, expected) verify((format), (argument), (expected), __LINE__)
|
||||
|
||||
static void verify2(const char* format, const char* arg1, const char* arg2, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY formatter = ffStrbufCreateStatic(format);
|
||||
const FFformatarg arguments[] = { { .type = FF_ARG_TYPE_STRING, arg1 }, { .type = FF_ARG_TYPE_STRING, arg2 } };
|
||||
ffParseFormatString(&result, &formatter, 2, arguments);
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s: expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, format, expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY2(format, argument1, argument2, expected) verify2((format), (argument1), (argument2), (expected), __LINE__)
|
||||
|
||||
int main(void) {
|
||||
instance.config.display.pipe = true;
|
||||
|
||||
@@ -96,9 +109,14 @@ int main(void) {
|
||||
VERIFY("output({1~10,10})", "12345 67890", "output()");
|
||||
VERIFY("output({1~10,5})", "12345 67890", "output()");
|
||||
VERIFY("output({1~3,-3})", "12345 67890", "output(45 67)");
|
||||
VERIFY("output({1~0,-12})", "12345 67890", "output()");
|
||||
VERIFY("output({1~-5,-3})", "12345 67890", "output(67)");
|
||||
VERIFY("output({1~-0,10})", "12345 67890", "output(12345 6789)");
|
||||
VERIFY("output({1~-3,-5})", "12345 67890", "output()");
|
||||
VERIFY("output({1~11,12})", "12345 67890", "output()"); // Same as {1~11}
|
||||
VERIFY("output({1~20,30})", "12345 67890", "output()"); // Same as {1~20}
|
||||
VERIFY("output({1~-50,3})", "12345 67890", "output()"); // Same as {1~-50}
|
||||
VERIFY("output({1~-50,-1})", "12345 67890", "output()"); // Same as {1~-50}
|
||||
VERIFY("output({1~})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
|
||||
VERIFY("output({1~-0})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
|
||||
VERIFY("output({1~,-1})", "12345 67890", "output(12345 6789)"); // Same as {1~0,-1}
|
||||
@@ -122,6 +140,28 @@ int main(void) {
|
||||
VERIFY("output({?1}OK{?}{/1}NOT OK{/})", "", "output(NOT OK)");
|
||||
}
|
||||
|
||||
{
|
||||
// a skipped conditional may hold conditionals of its own
|
||||
VERIFY2("{?1}A{?2}B{?}C{?}D", "x", "x", "ABCD");
|
||||
VERIFY2("{?1}A{?2}B{?}C{?}D", "x", "", "ACD");
|
||||
VERIFY2("{?1}A{?2}B{?}C{?}D", "", "x", "D");
|
||||
VERIFY2("{?1}A{?2}B{?}C{?}D", "", "", "D");
|
||||
VERIFY2("{/1}A{/2}B{/}C{/}D", "", "", "ABCD");
|
||||
VERIFY2("{/1}A{/2}B{/}C{/}D", "", "x", "ACD");
|
||||
VERIFY2("{/1}A{/2}B{/}C{/}D", "x", "", "D");
|
||||
VERIFY2("{/1}A{/2}B{/}C{/}D", "x", "x", "D");
|
||||
VERIFY2("{?1}A{?1}B{?1}C{?}D{?}E{?}F", "", "", "F");
|
||||
VERIFY2("{?1}{/2}A{/}B{?}C", "", "", "C");
|
||||
|
||||
// a conditional with an invalid argument does not open a block
|
||||
VERIFY2("{?1}A{?3}B{?}C", "", "x", "C");
|
||||
VERIFY2("{?1}A{?3}B{?}C{?}D", "", "x", "C{?}D");
|
||||
|
||||
// an escaped brace does not start a placeholder
|
||||
VERIFY2("{?1}{{?}}A{?}B", "", "", "B");
|
||||
VERIFY2("{?1}{{?2}A{?}B", "", "x", "B");
|
||||
}
|
||||
|
||||
{
|
||||
VERIFY("output({1:3})", "\e[1m12345", "output(\e[1m123)");
|
||||
VERIFY("output({1:-3})", "\e[1m12345", "output(\e[1m123…)");
|
||||
@@ -164,6 +204,22 @@ int main(void) {
|
||||
VERIFY("output({1~4})", "\e[?25l12345", "output(\e[?25l5)");
|
||||
}
|
||||
|
||||
{
|
||||
// numbers that don't fit in 32 bits must not wrap around to a valid value
|
||||
VERIFY("output({4294967296})", "x", "output({4294967296})");
|
||||
VERIFY("output({4294967297})", "x", "output({4294967297})");
|
||||
VERIFY("output({99999999999999999999})", "x", "output({99999999999999999999})");
|
||||
VERIFY("output({1~4294967297})", "12345 67890", "output()");
|
||||
VERIFY("output({1~-4294967297})", "12345 67890", "output()");
|
||||
VERIFY("output({1~0,4294967296})", "12345 67890", "output(12345 67890)");
|
||||
VERIFY("output({1:4294967297})", "12345 67890", "output(12345 67890)");
|
||||
VERIFY("output({1:-4294967296})", "12345 67890", "output(12345 67890)");
|
||||
|
||||
// the length is negated for the ellipsis, which overflows for the smallest int32
|
||||
VERIFY("output({1:-2147483648})", "12345 67890", "output(12345 67890)");
|
||||
VERIFY("output({1:-2147483647})", "12345 67890", "output(12345 67890)");
|
||||
}
|
||||
|
||||
#ifndef _WIN32 // Windows doesn't have setenv
|
||||
{
|
||||
ffListInit(&instance.config.display.constants);
|
||||
@@ -180,6 +236,10 @@ int main(void) {
|
||||
VERIFY("output({$FF_INVAL})", "", "output({$FF_INVAL})");
|
||||
VERIFY("output({$9}{$0}${-9})", "", "output({$9}{$0}${-9})");
|
||||
VERIFY("output({$1NO})", "", "output({$1NO})");
|
||||
|
||||
// numbers that don't fit in 32 bits must not wrap around to a valid index
|
||||
VERIFY("output({$4294967297})", "", "output({$4294967297})");
|
||||
VERIFY("output({$-4294967297})", "", "output({$-4294967297})");
|
||||
ffListDestroy(&instance.config.display.constants);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user