Compare commits

..

24 Commits

Author SHA1 Message Date
Carter Li ff8b69019f Release: v2.66.1 2026-09-01 10:44:16 +08:00
Carter Li 28c367c6a4 Shell: improves fish version detection for newer fish version 2026-09-01 10:41:25 +08:00
李通洲 48929622f8 DisplayServer (Linux): don't assume the monitor has only one preferred mode
Fixes #2481
2026-09-01 10:41:25 +08:00
李通洲 1754aaef5f Common: forces optionBuf to be 8-byte aligned 2026-09-01 10:41:25 +08:00
aldahiiir da401a7c3e OS (Linux): detects Ubuntu Studio installed with core mode
The minimal (core) installation ships the `ubuntustudio-desktop-core`
metapackage instead of `ubuntustudio-desktop`, so the existing check
missed it and the system was reported as Kubuntu.

Fixes #2485
2026-09-01 10:36:42 +08:00
李通洲 412fda612f Version (macOS): siliences a compiler warning 2026-09-01 10:36:42 +08:00
Carter Li 2299439873 DisplayServer (Linux): don't try to inline functions with variable arguments 2026-09-01 10:36:42 +08:00
h4sht 62fe9e1d11 Weather: fixes memory leak and replace aggressive exit(1) with proper error handling (#2474)
- Add missing ffStrbufDestroy(&options->location) in ffDestroyWeatherOptions
  to prevent memory leak when location is set via config
- Replace exit(1) in ffPrepareWeather with setting status to an error string,
  allowing the error to be properly propagated through the existing error mechanism

Co-authored-by: tru3 <tru3@tru3.com>
2026-09-01 10:36:42 +08:00
h4sht e9258cc5a8 Display: adds missing ffStrbufDestroy calls in ffOptionsDestroyDisplay (#2476)
13 FFstrbuf fields were initialized but never destroyed, potentially
leaking memory when configured via JSON or command-line:
- barBorderLeft, barBorderRight
- barBorderLeftElapsed, barBorderRightElapsed
- barColorElapsed, barColorTotal, barColorBorder
- tempColorGreen, tempColorYellow, tempColorRed
- percentColorGreen, percentColorYellow, percentColorRed

Co-authored-by: tru3 <tru3@tru3.com>
2026-09-01 10:36:42 +08:00
tru3 d8ae93f85d Separator: fixes memory leakes
- Add missing ffStrbufDestroy(&options->outputColor) in ffDestroySeparatorOptions
  to prevent memory leak when outputColor is set via JSON config
- Replace two exit(1) calls in ffPreparePublicIp with proper error status
  propagation, matching the pattern used in the weather module
2026-09-01 10:36:42 +08:00
李通洲 ff1ecb4166 Common (Sysctl): handle varying data lengths in integer retrieval functions 2026-09-01 10:36:42 +08:00
Carter Li d8ef56c3b3 Shell: fixes the fast path of ash version detection 2026-09-01 10:27:09 +08:00
Carter Li 68a0f5e3c6 DiskIO / NetIO: fixes I/O rate calculation precision, prevent div-by-zero 2026-09-01 10:27:09 +08:00
Carter Li a0e913a14c Display: fixes bright colours incorrectly active in light theme
Fixes #2509
2026-09-01 10:27:09 +08:00
Carter Li 9a940d73ba Codec (macOS): fixes a symbol not found error when running on 10.15 2026-09-01 10:27:09 +08:00
Carter Li 19ae272052 FFstrbuf: prefers ffStrbufEnsureFree 2026-09-01 10:27:09 +08:00
Carter Li d9286050b6 FFstrbuf: adds integer overflow checks to EnsureFreeNoCheck and EnsureFixedLengthFree 2026-09-01 10:27:09 +08:00
Carter Li 14ca210655 Networking: adds Content-Length size limit to prevent excessive memory allocation and potential attacks 2026-09-01 10:27:09 +08:00
Carter Li 300ad93022 PublicIP: fixes invalid URL parser 2026-09-01 10:27:09 +08:00
Carter Li 63ff315914 Networking: relaxes accepted response format 2026-09-01 10:27:09 +08:00
Carter Li 7663cb4399 Codec (macOS): fixes detection for VP9 2026-09-01 10:27:09 +08:00
Carter Li 944c63e8ec Common (Base64): unifies the code of endian testing 2026-08-31 16:35:27 +08:00
Carter Li 12f0088d72 Codec (macOS): fixes a memory leak 2026-08-31 16:34:59 +08:00
Carter Li 4826986cd8 Camera: adds missing ffStrbufDestroy 2026-08-31 16:34:23 +08:00
710 changed files with 6514 additions and 15410 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
[codespell]
check-filenames =
builtin = clear,rare,usage,informal
skip = */.git,*/cmake-build-*,*/.idea,*/completions,*/presets,*/screenshots,*/tests,*/3rdparty,*/logo/ascii,./src/detection/gpu/asahi_drm.h,./src/detection/cpu/cpu_x86.c,./src/detection/gpu/gpu_bsddrm.c,./src/detection/displayserver/linux/wayland/*-protocol.*,./build,./build-*,./build_*,./src/common/impl/genconfig.c
ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,slave,sur,doas,conexant,ags,bu,renderD,SME,Carmel,kinda,symbole,connexion,comando,processus,lokale,adres,titel,som,environnement,Sistem,Lokal,Lokal,Sistem,Paket,Hava
skip = */.git,*/cmake-build-*,*/.idea,*/completions,*/presets,*/screenshots,*/tests,*/3rdparty,*/logo/ascii,./src/detection/gpu/asahi_drm.h,./src/detection/cpu/cpu_x86.c,./src/detection/gpu/gpu_bsddrm.c,./src/detection/displayserver/linux/wayland/*-protocol.*,./build,./build-*,./build_*
ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,slave,sur,doas,conexant,ags,bu,renderD,SME,Carmel,kinda
+7 -32
View File
@@ -6,31 +6,16 @@ body:
- type: markdown
attributes:
value: |
> [!WARNING]
> **NB:** If your logo request does not meet the basic requirements outlined in this template, the issue may be closed at any time without any further explanation.
**Tip:** You can display a logo in fastfetch without adding it to fastfetch's official repo.
Tip: You can display a logo in fastfetch without adding it to fastfetch's official repo.
For highly customized, personal logos, we recommend keeping them locally.
You can test your logo locally using:
`fastfetch -l /path/to/logo.txt --logo-color-1 white --logo-color-2 red`
Please refer to the [Migration Guide](https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch) for details on color placeholders (`$1`, `$2`, etc.).
Please refer to https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch
- type: textarea
attributes:
label: OS
description: Paste the contents of `/etc/os-release` (specifically the `ID` and `PRETTY_NAME` fields). If neither file exists, describe how to identify the distro.
description: Paste the contents of `/etc/os-release` and `/etc/lsb-release` here. If neither file exists, describe how to identify the distro.
placeholder: cat /etc/os-release
validations:
required: true
- type: input
attributes:
label: Public repository URL
description: A public repository is required to verify the request. Please provide a link to the repository where the distro is developed.
placeholder: https://github.com/example/distro
validations:
required: true
- type: input
attributes:
label: Distro Website
@@ -38,35 +23,25 @@ body:
placeholder: https://example.com
validations:
required: true
- type: textarea
attributes:
label: ASCII Art
description: |
The ASCII logo should not take up too much space (smaller than 50x20 characters, W x H).
**Important:** Fastfetch uses `$1`, `$2`, `$3` etc. for color placeholders. Do NOT use hardcoded ANSI escape codes (like `\033[38;2...m`) or Neofetch's `${c1}` syntax.
Take <https://github.com/fastfetch-cli/fastfetch/tree/dev/src/logo/ascii> as examples.
placeholder: Paste ASCII art with $1, $2 placeholders here
description: The ASCII logo should not take up too much space (smaller than 50x20 characters, W x H). Please also include the color codes if they are not available in `os-release`.
placeholder: Paste ASCII art here
validations:
required: true
- type: input
attributes:
label: Original Image URL
description: If the ASCII art is based on an image, please provide a link to the original image file.
placeholder: Image URL from the distro website mentioned above
- type: checkboxes
attributes:
label: Checklist
options:
- label: My distro has reached a stable state (out of beta, alpha, RC).
required: true
- label: The ASCII art is smaller than 50x20 characters (W x H).
required: true
- label: The ASCII art uses `$N` color placeholders (or colors are natively available in `os-release`).
- label: The ASCII art includes color codes, or the color codes are available in `os-release`.
required: true
- label: The ASCII art has no unnecessary trailing spaces at the end of lines.
required: true
- label: I promise that I will send a removal request if the distro is discontinued.
- label: The ASCII art has no internal padding (spaces at the start and/or end of lines).
required: true
-405
View File
@@ -1,405 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fastfetch · Benchmark</title>
<style>
:root {
color-scheme: light;
--page: #f5f7fb;
--card: #fff;
--text: #172033;
--muted: #64748b;
--border: #dbe3ef;
--accent: #2563eb;
--accent-soft: #dbeafe;
--chart-text: #334155;
--chart-grid: #cbd5e1;
}
:root[data-theme="dark"] {
color-scheme: dark;
--page: #0f172a; --card: #172033; --text: #e5edf8; --muted: #9aabc1; --border: #334155; --accent-soft: #1e3a8a;
--chart-text: #f1f5f9;
--chart-grid: #475569;
}
* { box-sizing: border-box; }
body { max-width: 1440px; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; background: var(--page); color: var(--text); font: 16px/1.5 system-ui, -apple-system, sans-serif; }
header { margin-bottom: 1.75rem; }
.header-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
h1 { margin: 0; font-size: clamp(2rem, 4vw, 3rem); letter-spacing: -.04em; }
h2 { margin: 0; font-size: 1.25rem; }
p { color: var(--muted); }
.intro { margin: .6rem 0 1rem; }
.links a { color: var(--accent); font-weight: 600; text-decoration: none; }
.links a:hover { text-decoration: underline; }
.theme-toggle, .fullscreen { border: 1px solid var(--border); border-radius: .55rem; padding: .5rem .8rem; background: var(--card); color: var(--text); cursor: pointer; font: inherit; font-weight: 600; }
.theme-toggle:hover, .fullscreen:hover { border-color: var(--accent); color: var(--accent); }
.tabs { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.5rem 0; padding: .35rem; border: 1px solid var(--border); border-radius: .8rem; background: var(--card); }
.tab { border: 0; border-radius: .55rem; padding: .65rem 1.15rem; background: transparent; color: var(--muted); cursor: pointer; font: inherit; font-weight: 700; }
.tab:hover, .tab[aria-selected="true"] { background: var(--accent); color: #fff; }
.tab-panel[hidden] { display: none; }
.platform-title { display: flex; align-items: center; gap: .65rem; margin: 1.5rem 0 1rem; }
.platform-title::before { content: ''; width: .35rem; height: 1.4rem; border-radius: 1rem; background: var(--accent); }
.charts { display: flex; flex-direction: column; gap: 1.25rem; }
.chart { min-width: 0; padding: 1.25rem; border: 1px solid var(--border); border-radius: 1rem; background: var(--card); box-shadow: 0 8px 28px #0f172a0c; }
.chart:fullscreen { overflow: auto; padding: 2rem; background: var(--page); }
.chart:fullscreen .canvas-wrap { height: calc(100vh - 13rem); }
.chart-header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .75rem; }
.chart-header span { color: var(--muted); font-size: .85rem; }
.canvas-wrap { position: relative; height: 360px; }
.chart-actions { display: flex; align-items: center; gap: .75rem; }
.legend-actions { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1rem; }
.legend-action { border: 1px solid var(--border); border-radius: .45rem; padding: .35rem .6rem; background: var(--card); color: var(--muted); cursor: pointer; font: inherit; font-size: .8rem; }
.legend-action:hover { border-color: var(--accent); color: var(--accent); }
.legend { display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-top: 1rem; }
.legend-item { display: inline-flex; align-items: center; gap: .4rem; border: 0; padding: .15rem 0; background: transparent; color: var(--text); cursor: pointer; font: inherit; font-size: .85rem; }
.legend-item.is-hidden { color: var(--muted); }
.legend-swatch { width: .75rem; height: .75rem; border-radius: 50%; }
.legend-item.is-hidden .legend-swatch { opacity: .35; }
.chart-tooltip { position: fixed; z-index: 1000; pointer-events: none; width: min(90vw, 900px); max-height: calc(100vh - 24px); overflow: hidden; padding: .7rem .85rem; border: 1px solid var(--border); border-radius: .6rem; background: var(--card); color: var(--text); box-shadow: 0 10px 30px #0f172a33; font-size: .8rem; opacity: 0; transition: opacity .1s ease; }
.chart-tooltip-title { margin-bottom: .35rem; font-weight: 700; white-space: normal; overflow-wrap: anywhere; }
.chart-tooltip-body { max-height: calc(100vh - 76px); overflow: auto; columns: 220px auto; column-gap: 1.5rem; }
.chart-tooltip-row { display: flex; align-items: center; gap: .45rem; break-inside: avoid; white-space: nowrap; }
.chart-tooltip-swatch { width: .55rem; height: .55rem; border-radius: 50%; flex: 0 0 auto; }
.empty { padding: 2rem 0; color: var(--muted); }
</style>
<script src="https://cdn.jsdelivr.net/npm/chart.js/dist/chart.umd.min.js"></script>
<script src="data.js"></script>
</head>
<body>
<header>
<div class="header-row">
<h1>Fastfetch Benchmark</h1>
<button id="theme-toggle" class="theme-toggle" type="button"></button>
</div>
<p class="intro">Performance history grouped by CPU family. Each chart contains all modules and total process time for one operating system and architecture. Module curves are available from the legend and hidden by default.</p>
<p class="links"><a href="data.js">Download raw benchmark data</a></p>
</header>
<nav id="tabs" class="tabs" aria-label="CPU architecture"></nav>
<main id="panels"></main>
<script>
(() => {
const benchmarkData = window.BENCHMARK_DATA || {};
const entries = Object.values(benchmarkData.entries || {}).flat();
const groups = new Map();
function architectureFamily(arch) {
const normalized = arch.toLowerCase();
if (/^(amd64|x86_64|x86|i[3-6]86)$/.test(normalized)) return 'Intel';
if (/^(aarch64|arm64|armv[5-8].*|arm)$/.test(normalized)) return 'ARM';
return 'Other';
}
function parseName(name) {
let match = name.match(/^fastfetch (linux|macos|windows) ([^ ]+) (module .+|total process time)$/i);
if (match) {
return { platform: match[1].toLowerCase(), arch: match[2], metric: match[3] };
}
// Keep older macOS records visible while new records use the platform field.
match = name.match(/^fastfetch ([^ ]+) (module .+|total process time)$/i);
return match ? { platform: 'macos', arch: match[1], metric: match[2] } : null;
}
function entryMetadata(entry) {
const raw = entry.commit && entry.commit.timestamp || entry.date;
const date = new Date(raw);
return {
date: Number.isNaN(date.valueOf()) ? String(entry.date || '') : date.toISOString(),
sha: entry.commit && entry.commit.id ? entry.commit.id : 'unknown'
};
}
for (const entry of entries) {
const metricsByGroup = new Map();
for (const bench of entry.benches || []) {
const parsed = parseName(bench.name);
if (!parsed) continue;
const key = `${parsed.platform} ${parsed.arch}`;
if (!metricsByGroup.has(key)) metricsByGroup.set(key, new Map());
metricsByGroup.get(key).set(parsed.metric, bench.value);
}
for (const [key, metrics] of metricsByGroup) {
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push({ ...entryMetadata(entry), metrics });
}
}
const colors = [
'#2563eb', '#dc2626', '#16a34a', '#9333ea', '#ea580c', '#0891b2',
'#db2777', '#65a30d', '#4f46e5', '#ca8a04', '#0f766e', '#be123c'
];
const tabs = document.getElementById('tabs');
const panels = document.getElementById('panels');
const root = document.documentElement;
const themeToggle = document.getElementById('theme-toggle');
const savedTheme = localStorage.getItem('fastfetch-benchmark-theme');
let theme = savedTheme || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
const chartsByInstance = [];
function chartColors() {
const styles = getComputedStyle(root);
return {
text: styles.getPropertyValue('--chart-text').trim(),
grid: styles.getPropertyValue('--chart-grid').trim()
};
}
function externalTooltipHandler(context) {
const { chart, tooltip } = context;
let tooltipElement = chart.canvas.parentNode.querySelector('.chart-tooltip');
if (!tooltipElement) {
tooltipElement = document.createElement('div');
tooltipElement.className = 'chart-tooltip';
chart.canvas.parentNode.appendChild(tooltipElement);
}
if (tooltip.opacity === 0) {
tooltipElement.style.opacity = '0';
return;
}
tooltipElement.replaceChildren();
const title = document.createElement('div');
title.className = 'chart-tooltip-title';
const row = chart._benchmarkRows[tooltip.dataPoints[0].dataIndex];
title.textContent = `${new Date(row.date).toLocaleString()} · ${row.sha}`;
tooltipElement.appendChild(title);
const bodyElement = document.createElement('div');
bodyElement.className = 'chart-tooltip-body';
for (const body of [...tooltip.dataPoints].sort((a, b) => b.parsed.y - a.parsed.y)) {
const item = document.createElement('div');
item.className = 'chart-tooltip-row';
const swatch = document.createElement('span');
swatch.className = 'chart-tooltip-swatch';
swatch.style.background = body.dataset.borderColor;
const value = document.createElement('span');
value.textContent = `${body.dataset.label}: ${body.formattedValue} ms`;
item.append(swatch, value);
bodyElement.appendChild(item);
}
tooltipElement.appendChild(bodyElement);
const canvasRect = chart.canvas.getBoundingClientRect();
const margin = 12;
let left = canvasRect.left + tooltip.caretX + margin;
let top = canvasRect.top + tooltip.caretY + margin;
tooltipElement.style.left = `${left}px`;
tooltipElement.style.top = `${top}px`;
tooltipElement.style.opacity = '0';
const tooltipRect = tooltipElement.getBoundingClientRect();
if (left + tooltipRect.width > window.innerWidth - margin) left = canvasRect.left + tooltip.caretX - tooltipRect.width - margin;
if (top + tooltipRect.height > window.innerHeight - margin) top = canvasRect.top + tooltip.caretY - tooltipRect.height - margin;
tooltipElement.style.left = `${Math.max(margin, left)}px`;
tooltipElement.style.top = `${Math.max(margin, top)}px`;
tooltipElement.style.opacity = '1';
}
function updateTheme() {
root.dataset.theme = theme;
themeToggle.textContent = theme === 'dark' ? '☀ Light mode' : '☾ Dark mode';
themeToggle.setAttribute('aria-label', `Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`);
const colors = chartColors();
Chart.defaults.color = colors.text;
Chart.defaults.borderColor = colors.grid;
for (const chart of chartsByInstance) {
chart.options.scales.x.title.color = colors.text;
chart.options.scales.x.ticks.color = colors.text;
chart.options.scales.x.grid.color = colors.grid;
chart.options.scales.y.title.color = colors.text;
chart.options.scales.y.ticks.color = colors.text;
chart.options.scales.y.grid.color = colors.grid;
chart.update();
}
}
themeToggle.addEventListener('click', () => {
theme = theme === 'dark' ? 'light' : 'dark';
localStorage.setItem('fastfetch-benchmark-theme', theme);
updateTheme();
});
updateTheme();
const families = new Map();
for (const [key, rows] of groups) {
const arch = key.slice(key.lastIndexOf(' ') + 1);
const family = architectureFamily(arch);
if (!families.has(family)) families.set(family, []);
families.get(family).push({ key, rows });
}
const familyOrder = ['Intel', 'ARM', 'Other'];
const availableFamilies = familyOrder.filter(family => families.has(family));
for (const [index, family] of availableFamilies.entries()) {
const tab = document.createElement('button');
const panelId = `panel-${family.toLowerCase()}`;
tab.className = 'tab';
tab.type = 'button';
tab.id = `tab-${family.toLowerCase()}`;
tab.setAttribute('role', 'tab');
tab.textContent = family;
tab.setAttribute('aria-controls', panelId);
tab.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
tabs.appendChild(tab);
const panel = document.createElement('section');
panel.className = 'tab-panel';
panel.id = panelId;
panel.setAttribute('role', 'tabpanel');
panel.hidden = index !== 0;
panel.setAttribute('aria-labelledby', tab.id);
const title = document.createElement('h2');
title.className = 'platform-title';
title.textContent = `${family} processors`;
panel.appendChild(title);
const chartGrid = document.createElement('div');
chartGrid.className = 'charts';
panel.appendChild(chartGrid);
panels.appendChild(panel);
tab.addEventListener('click', () => {
for (const otherTab of tabs.children) otherTab.setAttribute('aria-selected', otherTab === tab ? 'true' : 'false');
for (const otherPanel of panels.children) otherPanel.hidden = otherPanel !== panel;
});
for (const { key, rows } of families.get(family).sort((a, b) => a.key.localeCompare(b.key))) {
rows.sort((a, b) => a.date.localeCompare(b.date));
const metrics = [...new Set(rows.flatMap(row => [...row.metrics.keys()]))].sort((a, b) => {
if (a === 'total process time') return -1;
if (b === 'total process time') return 1;
return a.localeCompare(b);
});
const section = document.createElement('article');
section.className = 'chart';
const chartHeader = document.createElement('div');
chartHeader.className = 'chart-header';
const chartTitle = document.createElement('h2');
chartTitle.textContent = key;
const chartActions = document.createElement('div');
chartActions.className = 'chart-actions';
const chartHint = document.createElement('span');
chartHint.textContent = `${rows.length} run${rows.length === 1 ? '' : 's'}`;
const fullscreen = document.createElement('button');
fullscreen.className = 'fullscreen';
fullscreen.type = 'button';
fullscreen.textContent = '⛶ Fullscreen';
fullscreen.addEventListener('click', () => {
if (document.fullscreenElement) document.exitFullscreen();
else section.requestFullscreen();
});
chartActions.append(chartHint, fullscreen);
chartHeader.append(chartTitle, chartActions);
const canvasWrap = document.createElement('div');
canvasWrap.className = 'canvas-wrap';
const canvas = document.createElement('canvas');
canvasWrap.appendChild(canvas);
section.append(chartHeader, canvasWrap);
chartGrid.appendChild(section);
const colorsForChart = chartColors();
const chart = new Chart(canvas, {
type: 'line',
data: {
labels: rows.map(row => row.sha.slice(0, 7)),
datasets: metrics.map((metric, index) => ({
label: metric,
data: rows.map(row => row.metrics.has(metric) ? row.metrics.get(metric) : null),
borderColor: colors[index % colors.length],
backgroundColor: colors[index % colors.length],
pointRadius: 3,
tension: 0.2,
spanGaps: true,
hidden: metric !== 'total process time'
}))
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { display: false },
tooltip: {
enabled: false,
external: externalTooltipHandler,
callbacks: {
title: tooltipItems => {
const row = rows[tooltipItems[0].dataIndex];
return `${row.date} · ${row.sha}`;
}
}
}
},
scales: {
x: { title: { display: true, text: 'Date', color: colorsForChart.text }, ticks: { color: colorsForChart.text }, grid: { color: colorsForChart.grid } },
y: { beginAtZero: true, title: { display: true, text: 'Milliseconds', color: colorsForChart.text }, ticks: { color: colorsForChart.text }, grid: { color: colorsForChart.grid } }
}
}
});
chart._benchmarkRows = rows;
chartsByInstance.push(chart);
const legendActions = document.createElement('div');
legendActions.className = 'legend-actions';
const legend = document.createElement('div');
legend.className = 'legend';
const legendItems = [];
function refreshLegend() {
legendItems.forEach((item, metricIndex) => {
item.classList.toggle('is-hidden', !chart.isDatasetVisible(metricIndex));
});
}
function setAllLegends(visible) {
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, visible));
refreshLegend();
chart.update();
}
function addLegendAction(label, action) {
const button = document.createElement('button');
button.className = 'legend-action';
button.type = 'button';
button.textContent = label;
button.addEventListener('click', action);
legendActions.appendChild(button);
}
addLegendAction('Show all', () => setAllLegends(true));
addLegendAction('Toggle all', () => {
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, !chart.isDatasetVisible(metricIndex)));
refreshLegend();
chart.update();
});
addLegendAction('Reset', () => {
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, metric === 'total process time'));
refreshLegend();
chart.update();
});
metrics.forEach((metric, metricIndex) => {
const item = document.createElement('button');
item.className = `legend-item${chart.isDatasetVisible(metricIndex) ? '' : ' is-hidden'}`;
item.type = 'button';
item.innerHTML = `<span class="legend-swatch" style="background:${colors[metricIndex % colors.length]}"></span>${metric}`;
item.addEventListener('click', () => {
chart.setDatasetVisibility(metricIndex, !chart.isDatasetVisible(metricIndex));
item.classList.toggle('is-hidden', !chart.isDatasetVisible(metricIndex));
chart.update();
});
legendItems.push(item);
legend.appendChild(item);
});
section.append(legendActions, legend);
}
}
if (!groups.size) {
tabs.hidden = true;
panels.innerHTML = '<p class="empty">No benchmark data available.</p>';
}
})();
</script>
</body>
</html>
-18
View File
@@ -1,18 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "sunday"
cooldown:
default-days: 7
open-pull-requests-limit: 10
commit-message:
prefix: "CI"
labels:
- "ci"
groups:
github-actions:
patterns:
- "*"
-55
View File
@@ -1,55 +0,0 @@
name: Reusable Benchmark
on:
workflow_call:
permissions:
contents: write
jobs:
benchmark:
runs-on: ubuntu-latest
concurrency:
group: benchmark
cancel-in-progress: false
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: download benchmark data
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: benchmark-*
path: benchmarks
- name: merge benchmark data
run: jq -s 'add' benchmarks/*/benchmark.json > benchmark.json
- name: save benchmark dashboard
run: cp .github/benchmark-index.html "$RUNNER_TEMP/benchmark-index.html"
- name: store benchmark result
uses: benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba # v1.22.1
with:
name: Fastfetch
tool: customSmallerIsBetter
output-file-path: benchmark.json
github-token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.sha }}
auto-push: true
summary-always: true
benchmark-data-dir-path: dev/bench
- name: publish benchmark dashboard
run: |
git fetch origin gh-pages
git switch --force-create gh-pages FETCH_HEAD
mkdir -p dev/bench
cp "$RUNNER_TEMP/benchmark-index.html" dev/bench/index.html
git config user.name github-action-benchmark
git config user.email github@users.noreply.github.com
git add dev/bench/index.html
if ! git diff --cached --quiet; then
git commit -m "Update benchmark dashboard"
git push origin gh-pages
fi
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable DragonFly amd64
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
@@ -15,10 +12,10 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@master
with:
operating_system: dragonflybsd
version: '6.4.2'
@@ -60,7 +57,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-dragonfly-amd64
path: ./fastfetch-*.*
+4 -7
View File
@@ -3,9 +3,6 @@ name: Reusable FreeBSD amd64
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
@@ -15,10 +12,10 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@master
with:
operating_system: freebsd
version: '15.1'
@@ -34,7 +31,7 @@ jobs:
- name: Update and install dependencies
run: |
sudo pkg update
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3 egl opencl ocl-icd v4l_compat chafa lua54 libva libvdpau
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa lua54 libva libvdpau
- name: CMake configuration
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} .
@@ -61,7 +58,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-freebsd-amd64
path: ./fastfetch-*.*
+4 -7
View File
@@ -3,9 +3,6 @@ name: Reusable Haiku amd64
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
@@ -15,13 +12,13 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@master
with:
operating_system: haiku
version: 'r1beta6'
version: 'r1beta5'
shell: bash
cpu_count: 4
@@ -54,7 +51,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-haiku-amd64
path: ./fastfetch-*.*
+40
View File
@@ -0,0 +1,40 @@
name: Reusable Linux armv6l
on:
workflow_call:
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: checkout repository
uses: actions/checkout@v7
- name: run VM
uses: uraimo/run-on-arch-action@v3
id: runcmd
with:
arch: armv6
distro: bookworm
githubToken: ${{ github.token }}
run: |
uname -a
apt-get update && apt-get install -y wget
apt-get install -y cmake make gcc libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libefl-all-dev liblua5.4-dev libvdpau-dev libva-dev rpm
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
cmake --build . --target package --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-armv6l
path: ./fastfetch-*.*
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable Linux armv7l
on:
workflow_call:
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -14,10 +11,10 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: run VM
uses: uraimo/run-on-arch-action@460cb8e6d9f726a588fc9b5e681c8a6cab09ae41 # v3.2.0
uses: uraimo/run-on-arch-action@v3
id: runcmd
with:
arch: armv7
@@ -41,7 +38,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-armv7l
path: ./fastfetch-*.*
+6 -38
View File
@@ -14,10 +14,6 @@ on:
description: fastfetch version from linux host build
value: ${{ jobs.build.outputs.ffversion }}
permissions:
security-events: write
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -28,7 +24,7 @@ jobs:
ffversion: ${{ steps.ffversion.outputs.ffversion }}
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: uname -a
run: uname -a
@@ -43,7 +39,7 @@ jobs:
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- name: install required packages
run: sudo apt-get update && sudo apt-get install -y gcc-13 jq libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev liblua5.4-dev libvdpau-dev libva-dev rpm ninja-build
run: sudo apt-get update && sudo apt-get install -y gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev liblua5.4-dev libvdpau-dev libva-dev rpm ninja-build
- name: install linuxbrew packages
run: |
@@ -52,7 +48,7 @@ jobs:
- name: Initialize CodeQL
if: inputs.arch == 'amd64'
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/init@v4
with:
languages: c
@@ -64,7 +60,7 @@ jobs:
- name: perform CodeQL analysis
if: inputs.arch == 'amd64'
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
uses: github/codeql-action/analyze@v4
- name: list features
run: ./fastfetch --list-features
@@ -73,29 +69,7 @@ jobs:
run: time ./fastfetch -c presets/ci.jsonc --stat false
- name: run fastfetch --format json
run: |
{ time -p ./fastfetch -c presets/ci.jsonc --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time
sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json
cat fastfetch.json
cat fastfetch.error >&2
cat fastfetch.time >&2
- name: prepare benchmark data
run: |
real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time)
jq -e 'type == "array"' fastfetch.json > /dev/null
jq --arg platform "linux" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" '
map(select(.stat | type == "number") | {
name: ("fastfetch " + $platform + " " + $arch + " module " + .type),
unit: "ms",
value: .stat
}) + [{
name: ("fastfetch " + $platform + " " + $arch + " total process time"),
unit: "ms",
value: ($real_seconds * 1000)
}]
' fastfetch.json > benchmark.json
cat benchmark.json
run: time ./fastfetch -c presets/ci.jsonc --format json
- name: run flashfetch
run: time ./flashfetch
@@ -123,13 +97,7 @@ jobs:
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-${{ inputs.arch }}
path: ./fastfetch-*.*
- name: upload benchmark data
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: benchmark-linux-${{ inputs.arch }}
path: benchmark.json
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable Linux i686
on:
workflow_call:
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -14,7 +11,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: uname -a
run: uname -a
@@ -40,7 +37,7 @@ jobs:
run: cmake --version
- name: configure project
run: CC=gcc-13 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_VERSION=5.10.0 -DCMAKE_SYSTEM_PROCESSOR=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
run: CC=gcc-13 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
- name: build project
run: cmake --build . --target package --verbose -j4
@@ -67,7 +64,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-i686
path: ./fastfetch-*.*
-60
View File
@@ -1,60 +0,0 @@
name: Reusable Linux loong64
on:
workflow_call:
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: set up QEMU for loong64
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: build in loong64 container
run: |
docker run --rm --platform linux/loong64 \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
lcr.loongnix.cn/debian:14 \
bash -c '
set -e
uname -a
cat /etc/os-release || true
apt-get update
apt-get install -y --no-install-recommends \
cmake make gcc g++ \
libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev \
libdconf-dev libdbus-1-dev libmagickcore-dev \
libsqlite3-dev librpm-dev \
libegl-dev libglx-dev ocl-icd-opencl-dev \
libpulse-dev libdrm-dev \
libelf-dev libefl-all-dev \
liblua5.4-dev \
libvdpau-dev libva-dev \
rpm
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
cmake --build . --target package --verbose -j$(nproc)
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
'
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: fastfetch-linux-loong64
path: ./fastfetch-*.*
+3 -6
View File
@@ -7,9 +7,6 @@ on:
required: true
type: string
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -18,10 +15,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: run VM
uses: uraimo/run-on-arch-action@460cb8e6d9f726a588fc9b5e681c8a6cab09ae41 # v3.2.0
uses: uraimo/run-on-arch-action@v3
id: runcmd
with:
arch: ${{ inputs.arch }}
@@ -42,7 +39,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-${{ inputs.arch }}
path: ./fastfetch-*.*
+7 -55
View File
@@ -10,9 +10,6 @@ on:
required: true
type: string
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -21,35 +18,18 @@ jobs:
runs-on: ${{ inputs.runs-on }}
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: uname -a
run: uname -a
- name: install required packages with MacPorts # Homebrew dropped Intel support
if: inputs.arch == 'amd64'
run: |
curl -fsSL https://github.com/macports/macports-base/releases/download/v2.12.6/MacPorts-2.12.6-15-Sequoia.pkg -o "$RUNNER_TEMP/MacPorts.pkg"
sudo installer -pkg "$RUNNER_TEMP/MacPorts.pkg" -target /
echo /opt/local/bin >> "$GITHUB_PATH"
echo /opt/local/sbin >> "$GITHUB_PATH"
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
sudo port selfupdate
sudo port install vulkan-loader vulkan-headers MoltenVK ImageMagick7 chafa lua quickjs-ng jq
- name: install required packages with Homebrew
if: inputs.arch == 'aarch64'
- name: install required packages
run: |
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa lua quickjs-ng jq
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa lua quickjs-ng
- name: configure project
run: |
if [ "${{ inputs.arch }}" = "amd64" ]; then
export CMAKE_PREFIX_PATH=/opt/local
export PKG_CONFIG_PATH=/opt/local/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}
fi
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
- name: build project
run: cmake --build . --target package --verbose -j4
@@ -58,32 +38,10 @@ jobs:
run: ./fastfetch --list-features
- name: run fastfetch
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan:bluetooth:bluetoothradio --stat false
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --stat false
- name: run fastfetch --format json
run: |
{ time -p ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan:bluetooth:bluetoothradio --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time
sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json
cat fastfetch.json
cat fastfetch.error >&2
cat fastfetch.time >&2
- name: prepare benchmark data
run: |
real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time)
jq -e 'type == "array"' fastfetch.json > /dev/null
jq --arg platform "macos" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" '
map(select(.stat | type == "number") | {
name: ("fastfetch " + $platform + " " + $arch + " module " + .type),
unit: "ms",
value: .stat
}) + [{
name: ("fastfetch " + $platform + " " + $arch + " total process time"),
unit: "ms",
value: ($real_seconds * 1000)
}]
' fastfetch.json > benchmark.json
cat benchmark.json
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --format json
- name: run flashfetch
run: time ./flashfetch
@@ -95,13 +53,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-macos-${{ inputs.arch }}
path: ./fastfetch-*.*
- name: upload benchmark data
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: benchmark-macos-${{ inputs.arch }}
path: benchmark.json
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable Musl amd64
on:
workflow_call:
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -13,10 +10,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v7
- name: setup alpine linux
uses: jirutka/setup-alpine@ae3b3ddba35054804fc4a3507b519fa7e8152050 # v1.4.1
uses: jirutka/setup-alpine@master
with:
branch: edge
@@ -44,7 +41,7 @@ jobs:
shell: alpine.sh {0}
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-musl-amd64
path: ./fastfetch-*.*
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable NetBSD amd64
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
@@ -15,10 +12,10 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@master
with:
operating_system: netbsd
version: '10.1'
@@ -59,7 +56,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-netbsd-amd64
path: ./fastfetch-*.*
+1 -4
View File
@@ -3,9 +3,6 @@ name: Reusable No Features Test
on:
workflow_call:
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -15,7 +12,7 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: uname -a
run: uname -a
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable OmniOS amd64
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
@@ -15,10 +12,10 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@master
with:
operating_system: omnios
version: 'r151058'
@@ -64,7 +61,7 @@ jobs:
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-omnios-amd64
path: ./fastfetch-*.*
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable OpenBSD amd64
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
@@ -15,10 +12,10 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@master
with:
operating_system: openbsd
version: '7.9'
@@ -59,7 +56,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-openbsd-amd64
path: ./fastfetch-*.*
+4 -7
View File
@@ -7,26 +7,23 @@ on:
required: true
type: string
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: get latest release version
id: get_version_release
uses: pozetroninc/github-action-get-latest-release@2a61c339ea7ef0a336d1daa35ef0cb1418e7676c # v0.8.0
uses: pozetroninc/github-action-get-latest-release@master
with:
repository: ${{ github.repository }}
- name: download artifacts
if: inputs.ffversion != steps.get_version_release.outputs.release
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@v8
- name: create release
if: inputs.ffversion != steps.get_version_release.outputs.release
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
uses: ncipollo/release-action@v1
with:
tag: ${{ inputs.ffversion }}
commit: ${{ github.sha }}
@@ -53,7 +50,7 @@ jobs:
- name: update release body
if: inputs.ffversion != steps.get_version_release.outputs.release
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
uses: ncipollo/release-action@v1
with:
tag: ${{ inputs.ffversion }}
commit: ${{ github.sha }}
+3 -6
View File
@@ -3,9 +3,6 @@ name: Reusable Solaris amd64
on:
workflow_call:
permissions:
contents: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -14,10 +11,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: run VM
uses: vmactions/solaris-vm@315163f088b66e55bbcc45928bd224d4973b2312 # v1.3.8
uses: vmactions/solaris-vm@v1
with:
usesh: true
envs: 'CMAKE_BUILD_TYPE'
@@ -40,7 +37,7 @@ jobs:
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-solaris-amd64
path: ./fastfetch-*.*
+1 -4
View File
@@ -3,16 +3,13 @@ name: Reusable Spellcheck
on:
workflow_call:
permissions:
contents: read
jobs:
spellcheck:
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Install codespell
shell: bash
+7 -47
View File
@@ -18,18 +18,6 @@ on:
msys-arch:
required: true
type: string
secrets:
SIGNPATH_API_TOKEN:
description: SignPath REST API token used to submit the signing request
# Optional so that runs without the secret (pull requests, forks)
# still work; the signing step is skipped there anyway.
required: false
permissions:
contents: read
# The SignPath action reads job details and downloads the unsigned
# artifact with the workflow token.
actions: read
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
@@ -42,14 +30,14 @@ jobs:
shell: msys2 {0}
steps:
- name: checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: setup-msys2
uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0
uses: msys2/setup-msys2@v2
with:
msystem: ${{ inputs.msystem }}
update: true
install: git mingw-w64-clang-${{ inputs.msys-arch }}-7zip mingw-w64-clang-${{ inputs.msys-arch }}-cmake mingw-w64-clang-${{ inputs.msys-arch }}-clang mingw-w64-clang-${{ inputs.msys-arch }}-jq mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-loader mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-headers mingw-w64-clang-${{ inputs.msys-arch }}-opencl-icd mingw-w64-clang-${{ inputs.msys-arch }}-opencl-headers mingw-w64-clang-${{ inputs.msys-arch }}-cppwinrt mingw-w64-clang-${{ inputs.msys-arch }}-imagemagick mingw-w64-clang-${{ inputs.msys-arch }}-chafa mingw-w64-clang-${{ inputs.msys-arch }}-lua mingw-w64-clang-${{ inputs.msys-arch }}-quickjs-ng mingw-w64-clang-${{ inputs.msys-arch }}-directx-headers
install: git mingw-w64-clang-${{ inputs.msys-arch }}-7zip mingw-w64-clang-${{ inputs.msys-arch }}-cmake mingw-w64-clang-${{ inputs.msys-arch }}-clang mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-loader mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-headers mingw-w64-clang-${{ inputs.msys-arch }}-opencl-icd mingw-w64-clang-${{ inputs.msys-arch }}-opencl-headers mingw-w64-clang-${{ inputs.msys-arch }}-cppwinrt mingw-w64-clang-${{ inputs.msys-arch }}-imagemagick mingw-w64-clang-${{ inputs.msys-arch }}-chafa mingw-w64-clang-${{ inputs.msys-arch }}-lua mingw-w64-clang-${{ inputs.msys-arch }}-quickjs-ng mingw-w64-clang-${{ inputs.msys-arch }}-directx-headers
- name: print msys version
run: uname -a
@@ -70,29 +58,7 @@ jobs:
run: time ./fastfetch -c presets/ci.jsonc --stat false
- name: run fastfetch --format json
run: |
{ time -p ./fastfetch -c presets/ci.jsonc --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time
sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json
cat fastfetch.json
cat fastfetch.error >&2
cat fastfetch.time >&2
- name: prepare benchmark data
run: |
real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time)
jq -e 'type == "array"' fastfetch.json > /dev/null
jq --arg platform "windows" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" '
map(select(.stat | type == "number") | {
name: ("fastfetch " + $platform + " " + $arch + " module " + .type),
unit: "ms",
value: .stat
}) + [{
name: ("fastfetch " + $platform + " " + $arch + " total process time"),
unit: "ms",
value: ($real_seconds * 1000)
}]
' fastfetch.json > benchmark.json
cat benchmark.json
run: time ./fastfetch -c presets/ci.jsonc --format json
- name: run flashfetch
run: time ./flashfetch
@@ -106,7 +72,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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-windows-${{ inputs.arch }}
path: |
@@ -116,7 +82,7 @@ jobs:
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
name: submit signing request
uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2.3
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '${{ vars.SIGNPATH_ORG_ID }}'
@@ -133,14 +99,8 @@ 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v7
with:
name: fastfetch-windows-${{ inputs.arch }}
path: ./fastfetch-windows-${{ inputs.arch }}.*
overwrite: true
- name: upload benchmark data
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: benchmark-windows-${{ inputs.arch }}
path: benchmark.json
+33 -28
View File
@@ -6,16 +6,16 @@ on:
jobs:
spellcheck:
permissions:
contents: read
uses: ./.github/workflows/build-spellcheck.yml
no-features-test:
needs: spellcheck
name: No-features-test
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-no-features-test.yml
secrets: inherit
linux-hosts:
needs: no-features-test
@@ -34,32 +34,40 @@ jobs:
with:
arch: ${{ matrix.arch }}
runs-on: ${{ matrix.runs-on }}
secrets: inherit
linux-i686:
needs: no-features-test
name: Linux-i686
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-linux-i686.yml
secrets: inherit
linux-armv7l:
needs: no-features-test
name: Linux-armv7l
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-linux-armv7l.yml
secrets: inherit
linux-loong64:
linux-armv6l:
needs: no-features-test
name: Linux-loong64
name: Linux-armv6l
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-linux-loong64.yml
uses: ./.github/workflows/build-linux-armv6l.yml
secrets: inherit
linux-vms:
needs: no-features-test
name: Linux-${{ matrix.arch }}
permissions:
security-events: write
contents: read
strategy:
matrix:
@@ -70,18 +78,19 @@ jobs:
uses: ./.github/workflows/build-linux-vms.yml
with:
arch: ${{ matrix.arch }}
secrets: inherit
musl-amd64:
needs: no-features-test
name: Musl-amd64
permissions:
contents: read
uses: ./.github/workflows/build-musl-amd64.yml
secrets: inherit
macos-hosts:
needs: no-features-test
name: macOS-${{ matrix.arch }}
permissions:
security-events: write
contents: read
strategy:
matrix:
@@ -94,64 +103,71 @@ jobs:
with:
arch: ${{ matrix.arch }}
runs-on: ${{ matrix.runs-on }}
secrets: inherit
omnios-amd64:
needs: no-features-test
name: OmniOS-amd64
permissions:
contents: read
uses: ./.github/workflows/build-omnios-amd64.yml
secrets: inherit
solaris-amd64:
needs: no-features-test
name: Solaris-amd64
permissions:
contents: read
uses: ./.github/workflows/build-solaris-amd64.yml
secrets: inherit
freebsd-amd64:
needs: no-features-test
name: FreeBSD-amd64
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-freebsd-amd64.yml
secrets: inherit
openbsd-amd64:
needs: no-features-test
name: OpenBSD-amd64
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-openbsd-amd64.yml
secrets: inherit
netbsd-amd64:
needs: no-features-test
name: NetBSD-amd64
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-netbsd-amd64.yml
secrets: inherit
dragonfly-amd64:
needs: no-features-test
name: DragonFly-amd64
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-dragonfly-amd64.yml
secrets: inherit
haiku-amd64:
needs: no-features-test
name: Haiku-amd64
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-haiku-amd64.yml
secrets: inherit
windows-hosts:
needs: no-features-test
name: Windows-${{ matrix.arch }}
permissions:
security-events: write
contents: read
# The SignPath action reads job details and downloads the unsigned
# artifact with the workflow token.
actions: read
strategy:
matrix:
include:
@@ -172,19 +188,7 @@ jobs:
msystem: ${{ matrix.msystem }}
msystem-lower: ${{ matrix.msystem-lower }}
msys-arch: ${{ matrix.msys-arch }}
secrets:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
benchmark:
if: github.event_name == 'push' && github.ref == 'refs/heads/dev' && github.repository == 'fastfetch-cli/fastfetch'
name: Benchmark
needs:
- linux-hosts
- macos-hosts
- windows-hosts
permissions:
contents: write
uses: ./.github/workflows/build-benchmark.yml
secrets: inherit
release:
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'fastfetch-cli/fastfetch'
@@ -193,7 +197,7 @@ jobs:
- linux-hosts
- linux-i686
- linux-armv7l
- linux-loong64
- linux-armv6l
- linux-vms
- musl-amd64
- macos-hosts
@@ -210,3 +214,4 @@ jobs:
uses: ./.github/workflows/build-release.yml
with:
ffversion: ${{ needs.linux-hosts.outputs.ffversion }}
secrets: inherit
-3
View File
@@ -12,6 +12,3 @@ fastfetch.kdev4
*.user
*.user.*
*.swp
*.log
/*.zh.md
/.*-ai/
+19 -110
View File
@@ -1,110 +1,19 @@
# Unreleased
Features:
* Added CPU name and frequency detection support on SPARC. (CPU, Linux)
# 2.68.1
Changes:
* The DE / WM / LM modules now reports the full name "Desktop Environment" / "Window Manager" / "Login Manager" instead of the abbreviations.
Features:
* Improved Login Manager (LM) module
* Added support on macOS, Windows and POSIX systems without systemd (by process list enumeration).
* Reports pretty name on Linux.
* Added Top module to print processes with the highest CPU, memory or disk I/O usage. (Top)
* Added module key localization support. (General)
* Added a new option `--key-language <?lang>` (`display.key.language: "<?lang>"` in JSON config). When left empty, it defaults to the system locale.
* See `fastfetch -h key-language` for all supported languages.
* Exposed in custom key format as `{module-name}`.
* Improved Wi-Fi module
* Added Wifi channel width detection, exposed via `{channel-width}` in custom format (reports 0 when not supported).
* Improved Wifi channel frequency accuracy and fortified detection on Windows, macOS.
* Added Wifi detection support on SunOS, Haiku.
* Added Battery detection support on SunOS. (Battery, SunOS)
* Added terminal font size detection support on Termux. (TerminalFont, Android)
* Improved GTK theme/icons/font detection on standalone WMs such as Hyprland, sway, niri and i3. (Theme, Linux)
* Improved fish completion scripts to print enum descriptions. (Completion)
* Enabled Wayland support on Android. (DisplayServer, Android)
* Added DE detection support for ASUS Zenfone. (DE, Android)
* Added new ARM SoCs (CPU, Linux / Android)
# 2.66.1
Bugfixes:
* Fixed I/O rate calculation precision in DiskIO and NetIO, and prevented division by zero. (DiskIO / NetIO)
* Fixed the fast path of ash version detection. (Shell)
* Fixed memory usage detection support on x86-32 FreeBSD (Memory, FreeBSD)
* Note: Although 32-bit systems are still supported, they are deprecated and barely tested. Users are encouraged to upgrade to 64-bit systems.
* Some internal cleanups and optimizations.
* Attempted to fix potential issues on big-endian systems
* Not tested due to lack of access to big-endian hardware.
Logos:
* Added macOS_old
* Added Omarchy, Omarchy2
# 2.67.1
Bugfixes:
* Fixed a `Symbol not found` error when running on macOS 10.15. (#2499, Codec, macOS)
* Fixed Alacritty font size never being parsed from the TOML config. (#2501, TerminalFont)
* Fixed bright colors incorrectly being active when the terminal uses a light theme. (#2509, Display)
* Fixed Ghostty terminal font detection (TerminalFont)
* Now tries to load the config with the `.ghostty` extension. (#2500)
* Fixed `font-family` fallback order. (#2510)
* Some internal cleanups and optimizations.
Features:
* Added a C23 compiler check so the build process gracefully exits during CMake configuration if the compiler is unsupported. (CMake)
* Added compatibility workarounds for Apple Clang 16 to fix building on macOS 14. (CMake, macOS)
* Reported real Android version codenames (e.g., "Baklava", "Vanilla Ice Cream") instead of "REL". (OS, Android)
* Added Loadavg detection support on Haiku R1B6. (Loadavg, Haiku)
* Enabled image logo support in the Zellij terminal multiplexer. (#2507, Logo)
* Improved Locale detection on Windows (Locale, Windows)
* Now respects `LC_ALL` and `LANG` environment variables if set for better compatibility with MSYS2/cygwin.
* Appends the active system Code Page (e.g., `.UTF-8` or `.Windows-1252`) to the default system locale fallback.
# 2.67.0
Changes:
* The minimum supported compiler version has been raised to GCC 13 (released on 2023-04-26) and Clang 16 (released on 2023-03-17) to accommodate the new C23 syntax used throughout the codebase. (General)
* As a result, building from source in Debian 12 (which only ships GCC 12 by default) is no longer supported. Users must upgrade their compiler or use the prebuilt binaries from the GitHub releases page.
* The `general.preRun` option has been removed due to security concerns. (General)
* Users who relied on this option to run a command before printing output will need to move that logic to an external wrapper.
* The `{status}` placeholder in Battery custom format is now an array type instead of a single comma-separated string. (Battery)
* This is a breaking change for users who print `{status}` in Lua scripts. They can use `table.concat(...)` to join the array into a single string if needed.
* The `--gen-config-*` flags (`--gen-config-full`, `--gen-config-force`, `--gen-config-full-force`) have been removed and their functions have been merged into `--gen-config`. (Global)
* See features below.
Features:
* `--gen-config` now opens an interactive configuration TUI to make generating config files easier.
* It falls back to non-interactive generation when `$NO_COLOR` is set or when stdin/stdout is not a TTY. This behavior can also be used to disable the interactive mode.
* Added an experimental [WebUI](https://fastfetch-cli.github.io/fastfetch-config/) ([separate project](https://github.com/fastfetch-cli/fastfetch-config)) for generating and editing configs.
* Added `-w`/`--watch` as a seconds-based alias for `--dynamic-interval`, defaulting to 1 second when no value is provided. (#2478, Watch)
* Added InitSystem detection on Windows, reporting the first userland process (`smss.exe`). (InitSystem, Windows)
* Added CPU and GPU temperature detection support for Apple M5 series chips. (CPU / GPU, macOS)
* Reported `Basic` as the Windows theme when DWM is disabled. (Theme, Windows)
* Improved Ghostty terminal font detection performance. (#2122, TerminalFont)
* It now parses Ghostty directly instead of running `ghostty +show-config`.
* Added support for a wider range of TOML config syntaxes used by Alacritty. (#2456, TerminalFont)
Bugfixes:
* Fixed a potential fish version detection error. (Shell, Linux)
* Fixed incorrect resolution reporting when a monitor advertises multiple preferred modes. (#2481, Display, Linux)
* Fixed connected monitors being missed when Wayland output events arrive late. (#2451, Display, Linux)
* Fixed VP9 codec detection on macOS. (Codec, macOS)
* Corrected Base64 encoding on big-endian hosts. (#2470)
* Fixed invalid URL parsing in the PublicIP module. (PublicIP)
* Correctly reported virtual GPUs on Windows. (#2461, GPU, Windows)
* Added a size limit for network responses to prevent excessive memory usage and mitigate potential attacks. (PublicIP / Weather)
* Fixed I/O rate calculation precision and prevented division by zero. (DiskIO / NetIO)
* Fixed ash and fish version detection, including a potential out-of-bounds read. (Shell)
* Fixed integer handling for varying sysctl data lengths. (Sysctl)
* Fixed several memory leaks in Camera, Codec, Display, Separator and Weather.
* Fixed Base64 encoding on big-endian hosts. (Base64)
* Fixed VP9 codec detection and macOS 10.15 Codec compatibility. (Codec, macOS)
* Relaxed accepted HTTP response formats and fixed invalid PublicIP URL parsing. (Networking / PublicIP)
* Added a response size limit to prevent excessive network memory allocation. (Networking)
* Added integer overflow checks to string buffer capacity handling. (FFstrbuf)
* Fixed Ubuntu Studio Core detection. (OS, Linux)
* Relaxed the HTTP response check so that both `HTTP/1.0` and `HTTP/1.1` responses are accepted when fetching data over the network. (PublicIP / Weather)
* Various internal cleanups and optimizations:
* Fixed multiple memory leaks (Separator, Camera, Codec, Display).
* Added integer overflow checks to the string buffer implementation.
* Various code cleanups and compiler warning fixes.
Logos:
* Removed Hypros, MagpieOS, Furreto, EmperorOS and Magix
* Fixed preferred monitor mode detection for KDE Plasma. (Display, Linux)
* Fixed bright colors being enabled incorrectly in light themes. (Display)
* Fixed a macOS compiler warning. (Version, macOS)
# 2.66.0
@@ -114,9 +23,9 @@ Changes:
Bugfixes:
* Fixed compatibility with WSL 2.9.3 (#2433, WM, Linux)
* Fixed AMD GPU names not being queried accurately in `driverSpecific` mode (GPU, Linux)
* Fixed accurate AMD GPU names are not queried in `driverSpecific` mode (GPU, Linux)
* Fixed GPU core counting for Apple Silicon GPU on Asahi Linux (GPU, Linux)
* Some internal cleanups and optimizations.
* Some internal cleanups and optimizations
Features:
* Added Astra Linux version detection (OS, Linux)
@@ -849,7 +758,7 @@ Features:
* Added support for interface speed detection on SunOS (LocalIP, SunOS)
* Added detection support for Xlibre (#1888, WM, Linux)
* Improved the accuracy of color detection (Cursor, macOS)
* Improved the performance of `Nix` package manager detection on macOS by porting optimizations from the Linux port (#1893, Packages, macOS)
* Improved the proformance of `Nix` package manager detection on macOS by porting optimizations form Linux port (#1893, Packages, macOS)
Bugfixes:
* Fixed custom object inheriting a key from the previous custom object if the key is blank (#1477)
@@ -1360,7 +1269,7 @@ Features:
* Change package manager name of NetBSD from `pkg` to `pkgsrc` (#1515, Packages, NetBSD)
* Detect SOC name on RISCV (#1519, CPU, Linux)
* Report marketing name of new QS8Es (CPU, Android)
* Acquire more OS info from lsb-release if missing from os-release (#1521)
* Acquire acquire more os info from lsb-release if missing from os-release (#1521)
* CMake: add option `-DCUSTOM_LSB_RELEASE_PATH` to specify the path of `lsb-release` file
* `-DCUSTOM_OS_RELEASE_PATH` has been supported since `v2.11.4`
* Report more SOC names on Android (CPU, Android)
@@ -1828,7 +1737,7 @@ Logos:
Bugfixes:
* Fix bad Intel Arc GPU name detection, which was supposed to be fixed in the last version but the change was reverted accidentally (#1177, GPU, Linux)
* Fix arm32 CPU name detection no longer working. Regression of 2.21.2 (CPU, Linux)
* Fix arm32 CPU name detection no longer work. Regression of 2.21.2 (CPU, Linux)
# 2.21.2
@@ -3323,7 +3232,7 @@ Features:
* Enhance `--percent-type` to allow hiding other texts (#387)
* Add Wifi module support for Linux
* Detect scaled resolutions (Windows, macOS)
* Optimize font module printing (Windows)
* Optimise font module printing (Windows)
* Detect pacman package count inside MSYS2 environment (Windows)
* Add Wifi / Battery module support for Android
* Disk name support for Linux
+115 -118
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.21.0) # C_STANDARD C17 and C23
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
VERSION 2.68.1
VERSION 2.66.1
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -9,6 +9,9 @@ project(fastfetch
set(PROJECT_LICENSE "MIT license")
if(DEFINED CMAKE_SYSTEM_PROCESSOR_OVERRIDE) # Used by github actions for i686 build
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR_OVERRIDE} CACHE INTERNAL "")
endif()
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" CMAKE_SYSTEM_PROCESSOR)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(CMAKE_SYSTEM_PROCESSOR "amd64")
@@ -59,7 +62,6 @@ if(NOT PKG_CONFIG_FOUND)
endif()
include(CheckIncludeFile)
include(CheckFunctionExists)
if(LINUX)
CHECK_INCLUDE_FILE("linux/version.h" HAVE_LINUX_VERSION_H)
@@ -75,7 +77,7 @@ endif()
include(CMakeDependentOption)
cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "ANDROID OR LINUX OR FreeBSD OR OpenBSD OR NetBSD" OFF)
cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD" OFF)
cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
@@ -125,23 +127,12 @@ if(APPLE)
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
endif()
# A module is a directory `src/modules/<module>/` that contains `<module>.c`.
# Globbing the sources rather than the directories skips empty directories and
# other leftovers, and makes `CONFIGURE_DEPENDS` react to added or removed sources.
file(GLOB FF_MODULE_SRCS CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/*.c")
set(FF_MODULE_DIRS "")
foreach(FF_MODULE_SRC ${FF_MODULE_SRCS})
get_filename_component(FF_MODULE_DIR "${FF_MODULE_SRC}" DIRECTORY)
get_filename_component(FF_MODULE_NAME "${FF_MODULE_SRC}" NAME_WE)
if("${FF_MODULE_DIR}" STREQUAL "${FF_MODULE_NAME}")
list(APPEND FF_MODULE_DIRS "${FF_MODULE_DIR}")
endif()
endforeach()
unset(FF_MODULE_SRCS)
file(GLOB FF_MODULE_DIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/")
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/${FF_MODULE_DIR}")
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
endif()
endforeach()
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
@@ -191,30 +182,7 @@ endif()
set(WARNING_FLAGS "-Wall -Wextra -Wconversion -Werror=uninitialized -Werror=return-type -Werror=vla")
if(LINUX)
if(NOT IS_MUSL) # `NOT DEFINED IS_MUSL` doesn't work
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.so
OUTPUT_VARIABLE LIBC_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(LIBC_PATH MATCHES "-musl")
set(IS_MUSL ON CACHE BOOL "Build with musl libc" FORCE)
endif()
endif()
if(IS_MUSL)
message(STATUS "MUSL libc detected")
# Silence ioctl related warnings
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-sign-conversion")
endif()
endif()
# Minimal compiler version supported: GCC 13 (2023-04-26), Clang 16 (2023-03-17)
# Most C23 features are allowed with noticeable exceptions of `constexpr` and `#embed`
# See https://cppreference.com/c/compiler_support/23 for detail
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion")
if(WIN32 OR Haiku)
@@ -443,7 +411,6 @@ set(LIBFASTFETCH_SRC
src/common/impl/font.c
src/common/impl/format.c
src/common/impl/frequency.c
src/common/impl/genconfig.c
src/common/impl/init.c
src/common/impl/jsonconfig.c
src/common/impl/library.c
@@ -492,7 +459,6 @@ set(LIBFASTFETCH_SRC
src/detection/physicalmemory/physicalmemory.c
src/detection/publicip/publicip.c
src/detection/terminaltheme/terminaltheme.c
src/detection/top/top.c
src/detection/terminalfont/terminalfont.c
src/detection/terminalshell/terminalshell.c
src/detection/version/version.c
@@ -504,18 +470,87 @@ set(LIBFASTFETCH_SRC
src/logo/image/im7.c
src/logo/image/image.c
src/logo/logo.c
src/modules/battery/battery.c
src/modules/bios/bios.c
src/modules/bluetooth/bluetooth.c
src/modules/bluetoothradio/bluetoothradio.c
src/modules/board/board.c
src/modules/bootmgr/bootmgr.c
src/modules/brightness/brightness.c
src/modules/break/break.c
src/modules/btrfs/btrfs.c
src/modules/camera/camera.c
src/modules/chassis/chassis.c
src/modules/codec/codec.c
src/modules/colors/colors.c
src/modules/cpu/cpu.c
src/modules/cpucache/cpucache.c
src/modules/cpuusage/cpuusage.c
src/modules/cursor/cursor.c
src/modules/custom/custom.c
src/modules/command/command.c
src/modules/datetime/datetime.c
src/modules/de/de.c
src/modules/disk/disk.c
src/modules/diskio/diskio.c
src/modules/dns/dns.c
src/modules/editor/editor.c
src/modules/font/font.c
src/modules/gpu/gpu.c
src/modules/host/host.c
src/modules/icons/icons.c
src/modules/initsystem/initsystem.c
src/modules/gamepad/gamepad.c
src/modules/kernel/kernel.c
src/modules/keyboard/keyboard.c
src/modules/lm/lm.c
src/modules/loadavg/loadavg.c
src/modules/locale/locale.c
src/modules/localip/localip.c
src/modules/logo/logo.c
src/modules/memory/memory.c
src/modules/monitor/monitor.c
src/modules/netio/netio.c
src/modules/opencl/opencl.c
src/modules/opengl/opengl.c
src/modules/os/os.c
src/modules/packages/packages.c
src/modules/physicaldisk/physicaldisk.c
src/modules/physicalmemory/physicalmemory.c
src/modules/processes/processes.c
src/modules/player/player.c
src/modules/poweradapter/poweradapter.c
src/modules/publicip/publicip.c
src/modules/display/display.c
src/modules/separator/separator.c
src/modules/shell/shell.c
src/modules/sound/sound.c
src/modules/swap/swap.c
src/modules/media/media.c
src/modules/mouse/mouse.c
src/modules/terminal/terminal.c
src/modules/terminaltheme/terminaltheme.c
src/modules/terminalfont/terminalfont.c
src/modules/terminalsize/terminalsize.c
src/modules/theme/theme.c
src/modules/title/title.c
src/modules/tpm/tpm.c
src/modules/uptime/uptime.c
src/modules/users/users.c
src/modules/version/version.c
src/modules/vulkan/vulkan.c
src/modules/wallpaper/wallpaper.c
src/modules/weather/weather.c
src/modules/wifi/wifi.c
src/modules/wm/wm.c
src/modules/wmtheme/wmtheme.c
src/modules/zpool/zpool.c
src/modules/modules.c
src/options/display.c
src/options/logo.c
src/options/general.c
)
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
list(APPEND LIBFASTFETCH_SRC
src/modules/${FF_MODULE_DIR}/${FF_MODULE_DIR}.c
)
endforeach()
if(LINUX)
list(APPEND LIBFASTFETCH_SRC
src/common/impl/dbus.c
@@ -591,7 +626,6 @@ if(LINUX)
src/detection/terminalshell/terminalshell_linux.c
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_linux.c
src/detection/top/top_linux.c
src/detection/tpm/tpm_linux.c
src/detection/uptime/uptime_linux.c
src/detection/users/users_linux.c
@@ -632,13 +666,6 @@ elseif(ANDROID)
src/detection/physicalmemory/physicalmemory_nosupport.c
src/detection/diskio/diskio_linux.c
src/detection/displayserver/displayserver_android.c
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wayland/kde-output.c
src/detection/displayserver/linux/wayland/wl-output-protocol.c
src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c
src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c
src/detection/displayserver/linux/wayland/xdg-output-unstable-v1-protocol.c
src/detection/displayserver/linux/common.c
src/detection/displayserver/linux/wmde.c
src/detection/displayserver/linux/xcb.c
@@ -674,7 +701,6 @@ elseif(ANDROID)
src/detection/terminalshell/terminalshell_linux.c
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_linux.c
src/detection/top/top_linux.c
src/detection/tpm/tpm_nosupport.c
src/detection/uptime/uptime_linux.c
src/detection/users/users_linux.c
@@ -751,6 +777,7 @@ elseif(FreeBSD)
src/detection/os/os_linux.c
src/detection/packages/packages_bsd.c
src/detection/poweradapter/poweradapter_nosupport.c
src/detection/processes/processes_bsd.c
src/detection/gtk_qt/qt.c
src/detection/sound/sound_bsd.c
src/detection/swap/swap_bsd.c
@@ -770,15 +797,11 @@ elseif(FreeBSD)
if(DragonFly)
list(APPEND LIBFASTFETCH_SRC
src/detection/bluetooth/bluetooth_nosupport.c
src/detection/processes/processes_dbsd.c
src/detection/top/top_dbsd.c
src/detection/wifi/wifi_nosupport.c
)
else()
list(APPEND LIBFASTFETCH_SRC
src/detection/bluetooth/bluetooth_bsd.c
src/detection/processes/processes_bsd.c
src/detection/top/top_bsd.c
src/detection/wifi/wifi_bsd.c
)
endif()
@@ -856,7 +879,6 @@ elseif(NetBSD)
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_linux.c
src/detection/tpm/tpm_nosupport.c
src/detection/top/top_nbsd.c
src/detection/uptime/uptime_bsd.c
src/detection/users/users_linux.c
src/detection/wallpaper/wallpaper_linux.c
@@ -917,7 +939,7 @@ elseif(OpenBSD)
src/detection/gpu/gpu_bsddrm.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_obsd.c
src/detection/lm/lm_linux.c
src/detection/lm/lm_nosupport.c
src/detection/icons/icons_linux.c
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_nosupport.c
@@ -943,7 +965,6 @@ elseif(OpenBSD)
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_linux.c
src/detection/tpm/tpm_nosupport.c
src/detection/top/top_obsd.c
src/detection/uptime/uptime_bsd.c
src/detection/users/users_obsd.c
src/detection/wallpaper/wallpaper_linux.c
@@ -977,7 +998,7 @@ elseif(APPLE)
src/detection/cpu/cpu_apple.c
src/detection/cpucache/cpucache_apple.c
src/detection/cpuusage/cpuusage_apple.c
src/detection/cursor/cursor_apple.c
src/detection/cursor/cursor_apple.m
src/detection/disk/disk_bsd.c
src/detection/dns/dns_apple.c
src/detection/physicaldisk/physicaldisk_apple.c
@@ -989,7 +1010,7 @@ elseif(APPLE)
src/detection/icons/icons_nosupport.c
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_apple.c
src/detection/lm/lm_apple.m
src/detection/lm/lm_nosupport.c
src/detection/loadavg/loadavg_bsd.c
src/detection/libc/libc_apple.c
src/detection/locale/locale_linux.c
@@ -1004,14 +1025,13 @@ elseif(APPLE)
src/detection/packages/packages_apple.c
src/detection/packages/packages_nix.c
src/detection/poweradapter/poweradapter_apple.c
src/detection/processes/processes_apple.c
src/detection/processes/processes_bsd.c
src/detection/sound/sound_apple.c
src/detection/swap/swap_apple.c
src/detection/terminalfont/terminalfont_apple.m
src/detection/terminalshell/terminalshell_linux.c
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_apple.c
src/detection/top/top_apple.c
src/detection/tpm/tpm_apple.c
src/detection/uptime/uptime_bsd.c
src/detection/users/users_linux.c
@@ -1019,7 +1039,7 @@ elseif(APPLE)
src/detection/wifi/wifi_apple.m
src/detection/wm/wm_apple.m
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_apple.c
src/detection/wmtheme/wmtheme_apple.m
src/detection/camera/camera_apple.m
)
# CMAKE_SYSTEM_PROCESSOR has been normalized before
@@ -1080,10 +1100,10 @@ elseif(WIN32)
src/detection/gpu/gpu_windows.cpp
src/detection/host/host_windows.c
src/detection/icons/icons_windows.c
src/detection/initsystem/initsystem_windows.c
src/detection/initsystem/initsystem_nosupport.c
src/detection/keyboard/keyboard_windows.c
src/detection/libc/libc_windows.cpp
src/detection/lm/lm_windows.c
src/detection/lm/lm_nosupport.c
src/detection/loadavg/loadavg_nosupport.c
src/detection/locale/locale_windows.c
src/detection/localip/localip_windows.c
@@ -1104,7 +1124,6 @@ elseif(WIN32)
src/detection/terminalshell/terminalshell_windows.c
src/detection/terminalsize/terminalsize_windows.c
src/detection/theme/theme_windows.c
src/detection/top/top_windows.c
src/detection/tpm/tpm_windows.c
src/detection/uptime/uptime_windows.c
src/detection/users/users_windows.c
@@ -1125,7 +1144,7 @@ elseif(SunOS)
src/common/impl/FFPlatform_unix.c
src/common/impl/binary_linux.c
src/common/impl/kmod_sunos.c
src/detection/battery/battery_sunos.c
src/detection/battery/battery_nosupport.c
src/detection/bios/bios_windows.c
src/detection/board/board_windows.c
src/detection/bootmgr/bootmgr_nosupport.c
@@ -1166,7 +1185,7 @@ elseif(SunOS)
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_nosupport.c
src/detection/libc/libc_nosupport.c
src/detection/lm/lm_linux.c
src/detection/lm/lm_nosupport.c
src/detection/loadavg/loadavg_sunos.c
src/detection/locale/locale_linux.c
src/detection/localip/localip_linux.c
@@ -1179,7 +1198,7 @@ elseif(SunOS)
src/detection/os/os_sunos.c
src/detection/packages/packages_sunos.c
src/detection/poweradapter/poweradapter_nosupport.c
src/detection/processes/processes_sunos.c
src/detection/processes/processes_linux.c
src/detection/gtk_qt/qt.c
src/detection/sound/sound_sunos.c
src/detection/swap/swap_sunos.c
@@ -1187,12 +1206,11 @@ elseif(SunOS)
src/detection/terminalshell/terminalshell_linux.c
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_linux.c
src/detection/top/top_sunos.c
src/detection/tpm/tpm_nosupport.c
src/detection/uptime/uptime_sunos.c
src/detection/users/users_linux.c
src/detection/wallpaper/wallpaper_linux.c
src/detection/wifi/wifi_sunos.c
src/detection/wifi/wifi_nosupport.c
src/detection/wm/wm_nosupport.c
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
@@ -1239,6 +1257,7 @@ elseif(Haiku)
src/detection/keyboard/keyboard_haiku.cpp
src/detection/libc/libc_nosupport.c
src/detection/lm/lm_nosupport.c
src/detection/loadavg/loadavg_nosupport.c
src/detection/locale/locale_linux.c
src/detection/localip/localip_linux.c
src/detection/gamepad/gamepad_haiku.cpp
@@ -1258,24 +1277,16 @@ elseif(Haiku)
src/detection/terminalshell/terminalshell_linux.c
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_nosupport.c
src/detection/top/top_haiku.c
src/detection/tpm/tpm_nosupport.c
src/detection/uptime/uptime_haiku.c
src/detection/users/users_linux.c
src/detection/wallpaper/wallpaper_haiku.cpp
src/detection/wifi/wifi_haiku.cpp
src/detection/wifi/wifi_nosupport.c
src/detection/wm/wm_nosupport.c
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_haiku.cpp
src/detection/camera/camera_nosupport.c
)
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} bsd)
check_function_exists(getloadavg HAVE_GETLOADAVG)
if(HAVE_GETLOADAVG)
list(APPEND LIBFASTFETCH_SRC src/detection/loadavg/loadavg_sunos.c)
else()
list(APPEND LIBFASTFETCH_SRC src/detection/loadavg/loadavg_nosupport.c)
endif()
elseif(GNU)
list(APPEND LIBFASTFETCH_SRC
src/common/impl/dbus.c
@@ -1335,7 +1346,7 @@ elseif(GNU)
src/detection/packages/packages_linux.c
src/detection/packages/packages_nix.c
src/detection/poweradapter/poweradapter_nosupport.c
src/detection/processes/processes_gnu.c
src/detection/processes/processes_linux.c
src/detection/gtk_qt/qt.c
src/detection/sound/sound_linux.c
src/detection/swap/swap_linux.c
@@ -1343,7 +1354,6 @@ elseif(GNU)
src/detection/terminalshell/terminalshell_linux.c
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_linux.c
src/detection/top/top_gnu.c
src/detection/tpm/tpm_nosupport.c
src/detection/uptime/uptime_linux.c
src/detection/users/users_linux.c
@@ -1394,6 +1404,7 @@ if(WIN32)
endif()
endif()
endif()
include(CheckFunctionExists)
if(NOT WIN32)
check_function_exists(pipe2 HAVE_PIPE2)
endif()
@@ -1423,33 +1434,21 @@ add_library(libfastfetch OBJECT
)
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
if(MODULE_DISABLE_${FF_MODULE_UPPER})
target_compile_definitions(libfastfetch PUBLIC FF_MODULE_DISABLE_${FF_MODULE_UPPER}=1)
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/${FF_MODULE_DIR}")
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
if(MODULE_DISABLE_${FF_MODULE_UPPER})
target_compile_definitions(libfastfetch PUBLIC FF_MODULE_DISABLE_${FF_MODULE_UPPER}=1)
endif()
endif()
endforeach()
include(CheckCSourceCompiles)
check_c_source_compiles("int main(void){int arr[1];return _Countof(arr);}" COMPILER_SUPPORTS_COUNT_OF)
if(COMPILER_SUPPORTS_COUNT_OF)
message(STATUS "C2Y _Countof is supported by the compiler")
message(STATUS "_Countof is supported by the compiler")
target_compile_definitions(libfastfetch PUBLIC FF_SUPPORTS_COUNT_OF=1)
else()
message(STATUS "C2Y _Countof is NOT supported by the compiler")
endif()
# Test C23 compiler support
check_c_source_compiles("enum my_enum: long{MY_ENUM_ZERO}; void test(void*p){} int main(int,const char**){[[gnu::cleanup(test)]] void* x=nullptr; return (int)MY_ENUM_ZERO;}" COMPILER_SUPPORTS_C23)
if(NOT COMPILER_SUPPORTS_C23)
message(FATAL_ERROR "Compiler does not support C23. Please use a newer compiler.")
endif()
# A hack for AppleClang 17, which supports most C23 features we use except for the `auto` keyword.
# See https://github.com/Homebrew/homebrew-core/pull/297403
check_c_source_compiles("int main(void){auto v = 12.34; return (int)v;}" COMPILER_SUPPORTS_AUTO)
if(NOT COMPILER_SUPPORTS_AUTO)
message(WARNING "Compiler does not support C23 'auto' keyword. A workaround will be applied.")
target_compile_definitions(libfastfetch PUBLIC $<$<COMPILE_LANGUAGE:C>:auto=__auto_type>)
message(STATUS "_Countof is NOT supported by the compiler")
endif()
if(yyjson_FOUND)
@@ -1458,11 +1457,10 @@ if(yyjson_FOUND)
# `target_link_libraries(yyjson::yyjson)` sets rpath implicitly
endif()
# Used for dlopen finding dylibs installed by package managers
# `/opt/homebrew/lib`, `/usr/local/lib` and `/opt/local/lib` are not on the
# dlopen search path by default
# Used for dlopen finding dylibs installed by homebrew
# `/opt/homebrew/lib` is not on in dlopen search path by default
if(APPLE AND BINARY_LINK_TYPE STREQUAL "dlopen")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib -Wl,-rpath,/opt/local/lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
endif()
if(ANDROID)
@@ -1533,7 +1531,7 @@ elseif(Haiku)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE)
elseif(GNU)
# On Hurd PATH_MAX is not defined. Set an arbitrary limit as workaround.
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE PATH_MAX=4096 O_PATH=O_NORW)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE PATH_MAX=4096 O_PATH=0)
endif()
if(APPLE)
@@ -1931,6 +1929,7 @@ elseif(FreeBSD)
elseif(OpenBSD)
target_link_libraries(libfastfetch
PRIVATE "m"
PRIVATE "kvm"
PRIVATE "sndio"
PRIVATE "util"
)
@@ -1952,7 +1951,6 @@ elseif(SunOS)
elseif(GNU)
target_link_libraries(libfastfetch
PRIVATE "m"
PRIVATE "ps"
)
elseif(ANDROID)
target_link_libraries(libfastfetch
@@ -1984,7 +1982,6 @@ elseif(Haiku)
PRIVATE "GL"
PRIVATE "be"
PRIVATE "gnu"
PRIVATE "bsd"
)
endif()
-797
View File
@@ -1,797 +0,0 @@
# Contributing to fastfetch
<details>
<summary>
Disclaimer
</summary>
This document is generated by AI and reviewed by humans. It is primarily intended for AI-assisted development, while remaining useful to human developers.
</details>
Thank you for your interest in fastfetch. This document covers building, architecture, how to add a module or a logo, code style, commit conventions, and the pull request workflow.
fastfetch is a system information tool written in C23, supporting Linux, macOS, Windows, the BSDs, Solaris, Haiku and Android. The project places strong emphasis on **startup time** and on keeping **dependencies optional**; many design decisions only make sense under that constraint, and this document returns to it repeatedly.
---
## Table of contents
- [Contributor quick reference](#contributor-quick-reference)
- [Getting started](#getting-started)
- [Code map](#code-map)
- [Core architecture: modules vs. detection](#core-architecture-modules-vs-detection)
- [Module registration](#module-registration)
- [Walkthrough: adding a module](#walkthrough-adding-a-module)
- [Walkthrough: adding a logo](#walkthrough-adding-a-logo)
- [Platform conventions](#platform-conventions)
- [Code style](#code-style)
- [Commit messages](#commit-messages)
- [Changelog](#changelog)
- [Tests](#tests)
- [Pull requests](#pull-requests)
- [Pitfalls](#pitfalls)
---
## Contributor quick reference
| Task | Start here | Verify with |
|---|---|---|
| Build and run fastfetch | `run.sh`, `CMakeLists.txt` | `./run.sh` |
| Add a module | `src/modules/<name>/`, `src/detection/<name>/`, `src/modules/modules.c`, `CMakeLists.txt` | `cmake -B build && cmake --build build -j` |
| Add a platform implementation | `src/detection/<name>/<name>_<platform>.c`, the matching platform block in `CMakeLists.txt` | Build on the target platform or CI |
| Add an ASCII logo | `src/logo/ascii/<letter>/<name>.txt`, matching `<letter>.inc` | `./build/fastfetch --logo <name>` |
| Change formatting or JSON output | `src/modules/<name>/<name>.c` | `./build/fastfetch -s <name> --format json` |
| Change shared formatting or containers | `src/common/format.h`, `src/common/color.h`, `src/common/FFstrbuf.h` | Build and run the matching test |
| Run the test suite | `tests/`, `build/` | `cd build && ctest --output-on-failure` |
For a typical code change, the shortest useful iteration is: configure, build the `fastfetch` target, run the affected module, then run the tests. If you add a directory or a generated input, re-run `cmake -B build` so that CMake refreshes its file globs.
---
## Getting started
### Building
The quickest way is `run.sh` in the repository root: it creates `build/`, configures, compiles and runs the binary.
```sh
./run.sh # build and run
./run.sh --format json # arguments are forwarded to fastfetch
```
Manual build:
```sh
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build --target fastfetch -j$(nproc)
./build/fastfetch
```
The default build type is `RelWithDebInfo`. LTO is enabled whenever `ENABLE_LTO=ON` **and** `CMAKE_BUILD_TYPE != Debug`, so the default configuration enables it and only `Debug` builds omit it. This is significant because LTO here is not merely an optimization: it is what removes the code of disabled modules. See [Pitfalls](#pitfalls).
### Common build options
| Option | Default | Notes |
|---|---|---|
| `CMAKE_BUILD_TYPE` | `RelWithDebInfo` | LTO is on for anything but `Debug` |
| `BUILD_TESTS` | `OFF` | Builds the unit tests in `tests/` |
| `BUILD_FLASHFETCH` | `ON` | Also builds the stripped-down `flashfetch` |
| `BINARY_LINK_TYPE` | `dlopen` | `dlopen` / `dynamic` / `static` |
| `ENABLE_ASAN` | `OFF` | Address Sanitizer |
| `ENABLE_LTO` | `ON` | Link-time optimization |
| `MODULE_DISABLE_<NAME>` | `OFF` | Disable a module, e.g. `MODULE_DISABLE_GPU=ON` |
| `SET_TWEAK` | `ON` | Appends a tweak to the dev version; turned off for releases |
`BINARY_LINK_TYPE=dlopen` (the default) is a deliberate design choice: optional dependencies (Vulkan, Wayland, DBus, ImageMagick, chafa, …) are loaded at runtime, so a missing library never prevents startup. When adding a third-party dependency, use the dlopen helpers in `common/library.h` — do not link it directly.
For a minimal-dependency build, see `.github/workflows/build-no-features-test.yml`:
```sh
cmake -DBUILD_TESTS=On -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_X11=OFF \
-DENABLE_DBUS=OFF -DENABLE_ZLIB=OFF ... .
```
### Dependencies
Required: CMake ≥ 3.21 and a C23 compiler (GCC, Clang or MSVC). Everything else is optional.
Optional dependencies are auto-detected: libpci, libdrm, vulkan, wayland, xcb, xrandr, dbus, sqlite3, rpm, imagemagick{6,7}, chafa, zlib, egl, glx, opencl, freetype, pulse, ddcutil, elf, libzfs, and more.
---
## Code map
```
src/
├── fastfetch.c entry point: CLI parsing, module dispatch, main loop (36 KB)
├── flashfetch.c entry point for the stripped-down build
├── options/ global (non-module) config: general / display / logo
├── modules/ module layer — module formatting and output
├── detection/ detection layer — platform-specific data retrieval
├── common/ infrastructure: FFstrbuf, FFlist, formatting, printing, networking
├── logo/ 530 ASCII logos plus the image logo backends
└── 3rdparty/ yyjson, widecharwidth, display-library
```
Flow of control:
```
fastfetch.c → options/ (global configuration)
→ modules/ (76 modules, formatting and output)
→ detection/ (one platform implementation each, raw data)
→ common/ (infrastructure)
```
The number of module directories does not match the number of detection directories, which is expected. The following relationships are structural rather than a fixed inventory:
- **13 modules have no detection directory** — they are either pure layout (`break`, `separator`, `colors`, `title`, `logo`) or reuse another module's detection result (`display`, `monitor`, `kernel`, `shell`, `terminal`, `player`, `custom`, `datetime`).
- **4 detection directories serve modules with different names** — `displayserver` (→ `display`, `monitor`), `gtk_qt` (→ `theme`, `icons`, `font`, `cursor`), `terminalshell` (→ `terminal`, `shell`) and `libc`.
Therefore, do not assume that `modules/<x>/` always has a matching `detection/<x>/`.
### Layer boundaries
| Layer | Owns | Must not |
|---|---|---|
| `options/` | **Global** config (colors, logo, threading, timeouts) | hold per-module options |
| `modules/` | formatting, printing, JSON serialization | read `/proc`, call Win32 APIs, parse sysfs |
| `detection/` | cross-platform data retrieval, clean result structs | print anything, read display config |
| `common/` | general-purpose utilities, no business logic | depend on a specific module |
A simple check: **no file under `detection/` should contain `printf` or read `instance.config`.** Symmetrically, no file under `modules/` should contain `#ifdef __linux__`.
---
## Core architecture: modules vs. detection
This is the most important convention in the codebase. Every feature consists of a fixed set of files; using CPU as the example:
```
modules/cpu/option.h module option struct
modules/cpu/cpu.c formatting and output
detection/cpu/cpu.h platform-independent interface
detection/cpu/cpu_linux.c ┐
detection/cpu/cpu_apple.c │
detection/cpu/cpu_windows.c ├─ platform implementations, one linked per build
detection/cpu/cpu_bsd.c │
detection/cpu/cpu_nosupport.c ┘
```
The interface is deliberately minimal, typically consisting of only two declarations:
```c
typedef struct FFCPUResult { ... } FFCPUResult; // result struct
const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu);
```
The `const char*` return value is an **error string**; `nullptr` means success. This pattern is used throughout `detection/` — please keep it consistent.
Helper functions are only exposed when they are genuinely shared between platform implementations (as in `cpu.h`: `ffCPUAppleCodeToName`, `ffCPUDetectByCpuid`).
**The benefit of this separation:** adding a platform never touches `modules/`; fixing output formatting never touches `detection/`.
---
## Module registration
### The descriptor
Every module exports one `FFModuleBaseInfo` (defined in `common/option.h:46`) — essentially a hand-written vtable:
```c
typedef struct FFModuleBaseInfo {
const char* name; // lookup key, e.g. "cpu"
const char* description; // help text
FFModuleDisplayName displayName; // module name in 20 languages
void (*initOptions)(void* options);
void (*destroyOptions)(void* options);
void (*parseJsonObject)(void* options, struct yyjson_val* module);
bool (*printModule)(void* options);
bool (*generateJsonResult)(void* options, yyjson_mut_doc*, yyjson_mut_val*);
void (*generateJsonConfig)(void* options, yyjson_mut_doc*, yyjson_mut_val*);
FFModuleFormatArgList formatArgs; // format placeholder table
const uint8_t defaultOrder; // sort weight
} FFModuleBaseInfo;
```
The source comment acknowledges that this is undefined behavior, since `void*` is not compatible with `FF*Options*`. It is a pragmatic compromise to obtain polymorphism in C; do not attempt to "fix" it.
### The registry: a first-letter hash bucket
`src/modules/modules.c` defines 26 `static FFModuleBaseInfo*` arrays (`A[]` through `Z[]`), each terminated by `nullptr`, collected into `ffModuleInfos[26]`:
```c
FFModuleBaseInfo** modules = ffModuleInfos[toupper(name[0]) - 'A']; // O(1) bucket lookup
for (; *modules; ++modules) { // linear scan in the bucket
if (ffStrEqualsIgnCase(name, (*modules)->name)) { ... }
}
```
Because the registry is small, a single arithmetic bucket lookup followed by a few string comparisons is preferable to a general-purpose hash table. When adding a module, place its descriptor in the bucket matching the first letter of `.name`, and keep the existing `nullptr` terminator at the end.
### The calling convention: zero heap allocation
Every dispatch site (`jsonconfig.c:98`, `commandoption.c:189`) follows the same pattern:
```c
alignas(uint64_t) uint8_t optionBuf[FF_OPTION_MAX_SIZE]; // 256 bytes, on the stack
baseInfo->initOptions(optionBuf);
baseInfo->parseJsonObject(optionBuf, jsonVal); // JSONC path only
baseInfo->printModule(optionBuf); // or generateJsonResult
baseInfo->destroyOptions(optionBuf);
```
`FF_OPTION_MAX_SIZE = 1 << 8` (256 bytes). Every module's `option.h` must end with:
```c
static_assert(sizeof(FFCPUOptions) <= FF_OPTION_MAX_SIZE, "FFCPUOptions size exceeds maximum allowed size");
```
**In other words, no module option struct may exceed 256 bytes.** This is a hard constraint, enforced at compile time.
The three dispatch entry points:
| Entry point | Location | Used for |
|---|---|---|
| `parseModuleJsonObject` | `common/impl/jsonconfig.c:90` | the `modules[]` array in a JSONC config |
| `parseStructureCommand` | `common/impl/commandoption.c:181` | the colon-separated structure string |
### Build-time module discovery
`CMakeLists.txt:134` globs `src/modules/*/*.c`, keeps only the entries whose file name matches their directory name, and generates a `MODULE_DISABLE_<UPPER>` option for each:
```cmake
file(GLOB FF_MODULE_SRCS CONFIGURE_DEPENDS RELATIVE "..." ".../src/modules/*/*.c")
set(FF_MODULE_DIRS "")
foreach(FF_MODULE_SRC ${FF_MODULE_SRCS})
get_filename_component(FF_MODULE_DIR "${FF_MODULE_SRC}" DIRECTORY)
get_filename_component(FF_MODULE_NAME "${FF_MODULE_SRC}" NAME_WE)
if("${FF_MODULE_DIR}" STREQUAL "${FF_MODULE_NAME}")
list(APPEND FF_MODULE_DIRS "${FF_MODULE_DIR}")
endif()
endforeach()
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
endforeach()
```
Because the glob matches sources rather than directories, a directory counts as a module only when `src/modules/<name>/<name>.c` exists. Empty directories — which git does not track, so they are easy to leave behind — and stray files are ignored instead of breaking the configure step with `Cannot find source file`.
### Module sources are discovered automatically
`FF_MODULE_DIRS` also drives the source list (`CMakeLists.txt:516`):
```cmake
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
list(APPEND LIBFASTFETCH_SRC
src/modules/${FF_MODULE_DIR}/${FF_MODULE_DIR}.c
)
endforeach()
```
Consequences:
- `src/modules/<name>/<name>.c` is compiled as soon as the file exists — **there is no source list to edit for the module layer**.
- The file name must match the directory name. A directory whose `.c` file is named differently, or has none at all, is silently not a module: a typo therefore shows up as a module missing from `fastfetch --list-modules`, not as a build error.
- The glob uses `CONFIGURE_DEPENDS`, so with the Makefile and Ninja generators the build re-evaluates it and re-runs CMake when a module source is added or removed. With other generators, or to be safe, re-run `cmake -B build` explicitly.
Only the module layer is automated. Sources under `src/detection/` and `src/common/impl/` are **not** globbed and must still be listed by hand — see [step 5](#5-add-the-platform-sources-to-cmakeliststxt).
---
## Walkthrough: adding a module
Adding a `Foo` module, step by step.
### 1. Define the option struct
`src/modules/foo/option.h`:
```c
#pragma once
#include "common/option.h"
typedef struct FFFooOptions {
FFModuleArgs moduleArgs; // must be the first field
bool showBar;
} FFFooOptions;
static_assert(sizeof(FFFooOptions) <= FF_OPTION_MAX_SIZE, "FFFooOptions size exceeds maximum allowed size");
```
`FFModuleArgs` must come first. It provides `key`, `format`, `outputColor`, `keyColor`, `keyIcon` and `keyWidth`; because it is the first field, `ffJsonConfigParseModuleArgs()` handles these generically, **so no parsing code is required**.
### 2. Implement detection
`src/detection/foo/foo.h`:
```c
#pragma once
#include "fastfetch.h"
typedef struct FFFooResult {
FFstrbuf name;
uint32_t count;
} FFFooResult;
const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result);
```
`src/detection/foo/foo_linux.c`:
```c
#include "foo.h"
const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result) {
// read /sys, /proc, sysfs, dbus, ...
return nullptr; // success; return an error string on failure
}
```
**Write one file per target platform, plus a `foo_nosupport.c` fallback for the remaining platforms.** The repository currently contains 45 `*_nosupport.c` files serving this purpose.
### 3. Implement the module
`src/modules/foo/foo.c` — implement the six functions, then define the descriptor:
```c
FFModuleBaseInfo ffFooModuleInfo = {
.name = "Foo",
.description = "Print foo information",
.displayName = {
.en = "Foo", .ar = "Foo", .cs = "Foo", .de = "Foo", .es = "Foo",
.fr = "Foo", .gl = "Foo", .he = "Foo", .id = "Foo", .it = "Foo",
.ja = "Foo", .ko = "Foo", .pl = "Foo", .pt = "Foo", .ru = "Foo",
.tr = "Foo", .uk = "Foo", .vi = "Foo", .zh_CN = "Foo", .zh_TW = "Foo",
},
.initOptions = (void*) ffInitFooOptions,
.destroyOptions = (void*) ffDestroyFooOptions,
.parseJsonObject = (void*) ffParseFooJsonObject,
.printModule = (void*) ffPrintFoo,
.generateJsonResult = (void*) ffGenerateFooJsonResult,
.generateJsonConfig = (void*) ffGenerateFooJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Name", "name" },
{ "Count", "count" },
})),
.defaultOrder = 73,
};
```
Points to note:
- **`displayName`** is a literal block of all 20 language fields; there is no shortcut macro, so copy the layout from an existing module: **`en`, `ar`, `cs`, `de`, `es`, `fr`, `gl`, `he`, `id`, `it`, `ja`, `ko`, `pl`, `pt`, `ru`, `tr`, `uk`, `vi`, `zh_CN`, `zh_TW`**.
**Fill in all 20.** The struct is read by byte offset (see [Pitfalls](#6-localization-uses-byte-offsets-not-enums)) and there is **no fallback** — a missing field means the key prints empty in that language.
- **`formatArgs`** must be exhaustive. It drives the placeholder list printed by `fastfetch -h foo-format`; **any placeholder omitted here is neither listed nor usable by the user.**
- The `moduleFormat` section in `doc/json_schema.json` is generated by `fastfetch -h format-json`. Its metadata comes from `FFModuleBaseInfo::formatArgs`. To keep the schema and module descriptors consistent, do not edit the `moduleFormat` section in `doc/json_schema.json` by hand; update the module metadata and regenerate it instead.
- **`defaultOrder`**: use the current maximum plus 1. Search the existing descriptors for `.defaultOrder =` before choosing a value; do not copy a hard-coded value from this document. Leaving it out, or setting it to `0`, excludes the module from the interactive `--gen-config` picker. Only `logo`, `command` and `custom` rely on this behavior, because they require user arguments or are invoked directly by the display layer.
### 4. Register it
- Add `#include "modules/foo/foo.h"` to `src/modules/modules.h`
- Insert into the `F[]` array in `src/modules/modules.c`:
```c
#if !FF_MODULE_DISABLE_FOO
&ffFooModuleInfo,
#endif
```
`FF_MODULE_DISABLE_FOO` is generated by CMake — you do not define it yourself.
### 5. Add the platform sources to `CMakeLists.txt`
The module layer is discovered by glob (see [Build-time module discovery](#module-sources-are-discovered-automatically)), **but `src/detection/**` and `src/common/impl/**` are not**. `CMakeLists.txt` lists them explicitly, inside one mutually exclusive chain of platform blocks:
| Block | Line | Covers |
|---|---|---|
| `if(LINUX)` | `CMakeLists.txt:522` | Linux |
| `elseif(ANDROID)` | `CMakeLists.txt:608` | Android (Termux) |
| `elseif(FreeBSD)` | `CMakeLists.txt:691` | FreeBSD, MidnightBSD, DragonFly |
| `elseif(NetBSD)` | `CMakeLists.txt:788` | NetBSD |
| `elseif(OpenBSD)` | `CMakeLists.txt:872` | OpenBSD |
| `elseif(APPLE)` | `CMakeLists.txt:959` | macOS / iOS |
| `elseif(WIN32)` | `CMakeLists.txt:1046` | Windows |
| `elseif(SunOS)` | `CMakeLists.txt:1121` | Solaris / illumos |
| `elseif(Haiku)` | `CMakeLists.txt:1204` | Haiku |
| `elseif(GNU)` | `CMakeLists.txt:1282` | GNU/Hurd |
Add the platform implementation to every block whose platform it supports, and `foo_nosupport.c` to every remaining block, so that all ten platforms still link:
```cmake
elseif(FreeBSD)
list(APPEND LIBFASTFETCH_SRC
...
src/detection/foo/foo_bsd.c
)
```
Points to note:
- Exactly one block is compiled per build, so a file omitted from a block does not exist for that platform, and the link fails with an undefined reference to `ffDetectFoo()` — **on that platform only**. A missing entry therefore builds fine locally and fails in CI; this is why every block must be covered.
- A platform may reuse another platform's implementation instead of a stub. `src/common/impl/networking_linux.c`, for example, is listed in nine of the ten blocks (all but `WIN32`). Check where the closest sibling module points before adding a new file.
- `DragonFly` is handled by the inner `if(DragonFly)` sub-block inside the FreeBSD block (`CMakeLists.txt:773`); add the variant there, as `processes`, `top` and `wifi` do.
- New helpers under `src/common/impl/` follow the same rule: they are not globbed, and each block that needs one must list it.
- A few files are appended outside the platform chain because they depend on an option or on a specific feature — for example the proprietary GPU backends (`CMakeLists.txt:1379`) and `src/common/impl/wcwidth.c` (`CMakeLists.txt:1408`). Those are written by hand as well.
### 6. Modules that need warm-up
If your module needs a sampling interval (CPU usage) or a network round-trip (public IP, weather), also implement `ffPrepareFoo()` and register it in the switch inside `ffPrepareCommandOption()` in `common/impl/commandoption.c`, under the matching first-letter `case`. Six modules currently do this: CPUUsage, DiskIO, NetIO, PublicIP, Top and Weather.
### 7. Verify
```sh
cmake -B build && cmake --build build -j
./build/fastfetch -s foo --format json # JSON output
./build/fastfetch -h foo-format # list formatArgs placeholders
./build/fastfetch --gen-config # confirm it appears in the picker
```
Note the `-format` suffix on the help flag: `fastfetch -h foo` is not supported; only `fastfetch -h foo-format` works.
If you added detection sources, re-check [step 5](#5-add-the-platform-sources-to-cmakeliststxt) before pushing: a platform block you missed compiles fine locally and fails only when that platform is built.
---
## Walkthrough: adding a logo
A new logo **must** have a corresponding "Logo Request" issue, linked from the PR with `Closes #1234`. Logo PRs without a linked issue are not accepted.
### 1. Add the ASCII file
```
src/logo/ascii/<first-letter>/<distro>.txt
```
For example `src/logo/ascii/d/distro.txt`. Directories are already split by first letter (`a/` … `z/`, plus `_/`).
The file is plain ASCII art with `$1` … `$9` as color placeholders:
```
$3 ,-^-___
$3 /\\\///
$2refined.$1 /\\\\//
```
- `$1` … `$9` map to palette slots 19; at most 9 are supported (`FASTFETCH_LOGO_MAX_COLORS = 9`)
- `$$` is a literal `$`
- Characters without a placeholder inherit the current color. Of the 530 existing logos, 239 use no placeholders at all (monochrome) and 291 do; **new logos should use placeholders**, as monochrome is a legacy style
- Tabs are expanded to 4 spaces
- Colors can be overridden with `--logo-color-1` … `--logo-color-9`
### 2. Register it in the `.inc`
CMake turns each `.txt` file into a `FASTFETCH_DATATEXT_LOGO_<UPPERCASE>` macro (`CMakeLists.txt:418`), but **the registry itself is maintained by hand**. Edit `src/logo/ascii/<first-letter>.inc`:
```c
// src/logo/ascii/d.inc
#ifdef FASTFETCH_DATATEXT_LOGO_DISTRO
// Distro
{
.names = { "Distro" }, // ID (preferred) or NAME from /etc/os-release, do NOT add both
.lines = FASTFETCH_DATATEXT_LOGO_DISTRO,
.colors = {
FF_COLOR_FG_PRIMARY, // recommended for using as WHITE replacement, light-theme terminal friendly
FF_COLOR_FG_BLUE, // preferred
FF_COLOR_FG_256 "34",
FF_COLOR_FG_RGB "0;255;0", // not recommended because of bad compatibility with raw TTY
},
},
#endif
```
- `names` is matched case-insensitively. Do not add names that differ only by case, because they can never be distinguished. Every name is also shown by `--list-logos`, so use user-friendly spelling such as an initial capital where appropriate.
- For a new logo, use exactly one name unless there is a specific compatibility reason to add more. That name should first be the `ID` from `/etc/os-release`. If the ID cannot distinguish this logo from another logo for the same OS, use the `NAME` value instead. `logoGetBuiltinDetected` in `src/logo/logo.c` documents the detection order: `ID`, then `NAME`, then tokens from `ID_LIKE`, then the platform name fallback. Manual `logo-source` selection uses the name directly.
- `colors` is positional — entry 0 is `$1`, entry 1 is `$2`, and so on
- `colors[0]` also becomes the title color and `colors[1]` the key color, unless the user overrides them
If the same OS has multiple logo variants, mark the variant explicitly with `.type`:
```c
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
```
Use `FF_LOGO_LINE_TYPE_ALTER_BIT` for an alternate logo, `FF_LOGO_LINE_TYPE_SMALL_BIT` for a small logo, or combine the flags when both apply. This is also a lookup optimization: a logo marked `FF_LOGO_LINE_TYPE_SMALL_BIT` is considered only for `type = small`, while a logo marked `FF_LOGO_LINE_TYPE_ALTER_BIT` is never selected by automatic detection. Alternate logos are available only when explicitly requested through `-l <source>`.
### 3. Verify
```sh
./build/fastfetch -l distro
./build/fastfetch --list-logos | grep -i distro
```
---
## Platform conventions
The `detection/` layer picks its implementation by **filename suffix**; the choice is made at build time from `CMAKE_SYSTEM_NAME`:
| Suffix | Platform |
|---|---|
| `_linux.c` | Linux |
| `_apple.c` / `_apple.m` | macOS / iOS (`.m` for Objective-C) |
| `_windows.c` / `_windows.cpp` | Windows (`.cpp` for WinRT / WMI) |
| `_bsd.c` | FreeBSD |
| `_nbsd.c` | NetBSD |
| `_obsd.c` | OpenBSD |
| `_sunos.c` | Solaris / illumos |
| `_haiku.c` / `.cpp` | Haiku |
| `_android.c` | Android (Termux) |
| `_gnu.c` | GNU/Hurd |
| `_nosupport.c` | Empty fallback |
Current distribution of platform files in `detection/`:
| Suffix | Files | Suffix | Files |
|---|---|---|---|
| `_linux` | 58 | `_obsd` | 16 |
| `_windows` | 47 | `_haiku` | 13 |
| `_apple` | 46 | `_android` | 12 |
| `_nosupport` | 45 | `_gnu` | 3 |
| `_bsd` | 29 | | |
| `_sunos` | 19 | | |
| `_nbsd` | 17 | | |
(These are repository inventory figures, counting `.c`, `.m`, `.cpp`, and `.h`; they may change as platforms and modules are added.)
**Do not accumulate `#ifdef __linux__` blocks in a single file.** When adding platform support, copy the closest existing implementation and change the suffix.
`common/` follows the same convention: `common/impl/` contains `io_unix.c` / `io_windows.c`, `netif_linux.c` / `netif_apple.c` / `netif_bsd.c` and similar files, with `common/apple/`, `common/windows/` and `common/haiku/` holding platform-specific helpers.
When several platform suffixes could apply, use the most specific implementation supported by the build system (for example, `_nbsd.c` instead of the generic `_bsd.c` on NetBSD). Keep the generic file as the fallback for platforms that share its conventions.
---
## Code style
### Formatting
The project uses clang-format with `BasedOnStyle: LLVM` plus these key overrides:
```yaml
IndentWidth: 4
UseTab: Never
ColumnLimit: 0 # never wrap
InsertBraces: true # braces even on single-statement ifs
PointerAlignment: Left # char* p, not char *p
SortIncludes: Never # include order is managed by hand
AlignAfterOpenBracket: DontAlign
BinPackParameters: false # all params on one line, or one per line
```
Before committing:
```sh
clang-format -i src/modules/foo/*.c src/modules/foo/*.h
```
`src/3rdparty/**`, `build/**` and `src/logo/builtin.c` are listed in `.clang-format-ignore` — **do not reformat them.**
`.editorconfig` specifies LF line endings, a 4-space indent, a final newline, and trailing whitespace trimmed (except in Markdown).
### Naming
| Kind | Convention | Example |
|---|---|---|
| Functions | `ff` + PascalCase | `ffDetectCPU`, `ffPrintCPU` |
| Types | `FF` + PascalCase | `FFCPUResult`, `FFModuleBaseInfo` |
| Variables / fields | camelCase | `coresPhysical`, `keyWidth` |
| Macros | SCREAMING_SNAKE_CASE | `FF_OPTION_MAX_SIZE` |
| Module options | `FF<Name>Options` | `FFCPUOptions` |
| Detection results | `FF<Name>Result` | `FFCPUResult` |
### Spelling
CI runs codespell (`.codespellrc`). Known false positives are listed in `ignore-words-list` (`iterm`, `compiletime`, and various non-English distro words). Add new words there rather than changing the code.
### Compiler warnings
The build enables `-Wall -Wextra -Wconversion` plus several `-Werror`s:
```
-Werror=uninitialized -Werror=return-type -Werror=vla
-Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion
```
**`-Wconversion` is strict**: every implicit narrowing conversion needs an explicit cast.
---
## Commit messages
Format:
```
<Scope>[ (Platform)]: <third-person singular verb> <object>
```
Scope is the module or subsystem name; Platform is optional. Use the third-person singular present tense, capitalize the first letter, no trailing period.
Real examples from recent history:
```
Processes (Haiku): honors `options->countKprocs`
WM (macOS): improves reliability of WM plugin detection
Memory (Windows): prefers `NQSI`
Top (Linux): improves performance of `stat` parsing
Logo (Builtin): adds omarchy
Global: introduces global macro `FF_PATH_PKG_BASE` to replace `_PATH_LOCALBASE`
LM (OpenBSD): adds support
CI: disables fail on alert
Doc: updates README
Presets: moves `top` to the bottom of running modules [ci skip]
```
Common verbs: `adds`, `removes`, `fixes`, `improves`, `updates`, `corrects`, `prefers`, `honors`, `disables`, `enables`, `introduces`, `detects`, `reports`, `skips`, `uses`.
Scopes in use: `Top`, `Processes`, `Memory`, `Logo (Builtin)`, `CI`, `Doc`, `Presets`, `Global`, plus individual module names.
Documentation-only changes use a `[ci skip]` suffix.
---
## Changelog
User-visible changes go into `CHANGELOG.md`, under the topmost version heading:
```markdown
# Unreleased
Changes:
* The DE / WM / LM modules now reports the full name ...
Features:
* Added Top module to print processes with the highest CPU, memory or disk I/O usage. (Top)
* Improved Wi-Fi module
* Added Wifi channel width detection, exposed via `{channel-width}` in custom format.
* Improved Wifi channel frequency accuracy on Windows, macOS.
* Added Battery detection support on SunOS. (Battery, SunOS)
Bugfixes:
* Fixed I/O rate calculation precision in DiskIO and NetIO. (DiskIO / NetIO)
```
Rules:
- Three sections: `Changes:` (behavior changes), `Features:`, `Bugfixes:`
- Past tense: `Added` / `Fixed` / `Improved`
- End each entry with its scope: `(Module, Platform)` or `(ModuleA / ModuleB)`
- Sub-bullets are indented 4 spaces
---
## Tests
Tests are standalone executables in `tests/` using a `VERIFY` macro that exits non-zero on failure:
```c
#define VERIFY(expression) \
if (!(expression)) testFailed(&strbuf, #expression, __LINE__)
```
Current tests: `strbuf.c`, `list.c`, `format.c`, `color.c`, `duration.c`, `strutil.c`.
```sh
cmake -B build -DBUILD_TESTS=On
cmake --build build
cd build && ctest --output-on-failure
```
Coverage focuses on the core data structures and the formatting engine in `common/`. The `detection/` layer has no automated tests, because it depends on the state of a running system; it is instead covered by the CI matrix — 20 workflows under `.github/workflows/` spanning Linux (including musl, loong64, armv7l, i686), macOS, Windows, FreeBSD, NetBSD, OpenBSD, DragonFly, Solaris, OmniOS and Haiku, plus spellcheck and benchmark jobs.
**If you modify `common/FFstrbuf.h`, `common/format.h` or `common/color.h`, extend the corresponding test.**
---
## Pull requests
1. **Open an issue first** (feature request / bug report / logo request) to confirm that the change is wanted before investing effort. Templates are in `.github/ISSUE_TEMPLATE/`.
2. Branch off `dev` — **`dev` is the main development branch**, not `master`.
3. Follow the [commit message convention](#commit-messages).
4. Update `CHANGELOG.md` for user-visible changes.
5. Open the PR against `dev` and fill in `.github/pull_request_template.md`:
- Summary
- Related issue (**required for new logos**; otherwise the PR is not accepted)
- Changes
- Screenshots (required for visual changes)
- Checklist: confirm you tested locally
### Pre-submit checklist
```sh
clang-format -i <changed files> # format
codespell # spelling
cmake -B build -DBUILD_TESTS=On && cmake --build build -j
cd build && ctest --output-on-failure # tests
./build/fastfetch --format json # verify JSON output is well-formed
./build/fastfetch -c presets/all.jsonc --stat false # smoke-test every module
```
---
## Pitfalls
The following points are easy to misinterpret when reading this codebase. Most of them follow from the startup-time objective described above.
### 1. Option structs must not exceed 256 bytes
`FF_OPTION_MAX_SIZE = 1 << 8`. Exceeding it is caught at compile time by `static_assert`. Do not attempt to increase the value: it determines the stack cost of every module invocation.
### 2. `FF_MODULE_DISABLE_*` controls registration, not compilation
```c
// verbatim from the top of modules/modules.c:
// FF_MODULE_DISABLE_<module> only controls if the module is registered,
// the module code itself is still compiled.
// We rely `LTO` to remove the unused code (only enabled in Release mode)
```
**Disabling modules in a Debug build does not shrink the binary.** LTO is enabled whenever `CMAKE_BUILD_TYPE != Debug`, and the default `RelWithDebInfo` already satisfies that — so measure size with `RelWithDebInfo` or `Release`, never with `Debug`. (The "Release mode" wording in the comment above is imprecise.)
### 3. Only two things are discovered by glob
The module sources (`CMakeLists.txt:134`) and the logo `.txt` files (`CMakeLists.txt:429`). Both use `CONFIGURE_DEPENDS`, so with the Makefile and Ninja generators the build re-checks the glob and re-runs CMake when the result changes; other generators (Visual Studio and Xcode in particular) do not track it as reliably. Re-run `cmake -B build` after adding a module source or a logo file rather than relying on that behavior.
Everything else — `src/detection/**` and `src/common/impl/**`, and any source outside the platform chain — must be listed in `CMakeLists.txt` by hand; see [step 5](#5-add-the-platform-sources-to-cmakeliststxt).
### 4. CLI module options are removed
Per-module command-line flags such as `--cpu-temp` are **no longer supported**. The only job of `ffParseModuleOptions` now is to translate the flag into a JSON key, then `exit(477)` with a pointer to the config file:
```
Error: Unsupported module option: --cpu-temp
Support of module options has been removed. Please add the flag to the JSON config instead.
Example (demonstration only): `{ "modules": [ { "type": "cpu", "temp": true } ] }`
```
**JSONC is the first-class configuration interface; the command line is not.** When adding module options, implement `parseJsonObject` only — do not add a CLI branch.
### 5. `defaultOrder = 0` hides a module from `--gen-config`
`collectModuleInfos` (`genconfig.c:186`) skips any module whose `defaultOrder` is `0`. C zero-initializes the field, so **omitting it is the same as setting it to 0**. Only `logo`, `command` and `custom` rely on this intentionally.
`defaultOrder` affects only the ordering in the interactive `--gen-config` picker; it has no effect on the runtime output order.
### 6. Localization uses byte offsets, not enums
`instance.config.display.keyLanguage` does not hold a language enum — it holds a **byte offset** such as `offsetof(FFModuleDisplayName, zh_CN)`. The macro `FF_MODULE_GET_DISPLAY_NAME` (`common/option.h:123`) does plain pointer arithmetic with it:
```c
#define FF_MODULE_GET_DISPLAY_NAME(moduleName) \
(*(const char**) ((uint8_t*) &ff ## moduleName ## ModuleInfo.displayName + instance.config.display.keyLanguage))
```
Consequence: **the field order of `FFModuleDisplayName` must never change** — reordering the fields silently corrupts the output for every language.
### 7. The `multithreading` option has limited effect
The global `multithreading` option currently takes effect in exactly one place: `common/impl/networking_linux.c:339`. Modules are still printed sequentially. Modules that need concurrency go through the `ffPrepare*` warm-up hooks instead, which start sampling or fire off requests before the print loop begins.
This may change in the future. The main blocking issue is that there are dependencies between different modules.
### 8. Do not print or read configuration inside `detection/`
The `detection/` layer must stay pure: read system state, fill a struct, return an error string. Any `printf` or any read of `instance.config` there is a design error.
Use `FF_DEBUG` (`src/common/debug.h`) for logging.
### 9. `src/logo/builtin.c` and `3rdparty/` are formatting-exempt
The former is a large generated and hand-maintained data table; the latter is upstream code. Both are excluded via `.clang-format-ignore`; do not reformat either.
---
## Reference
- Configuration guide: <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration>
- JSON schema: `doc/json_schema.json`
- Example presets: `presets/` (`all.jsonc`, `neofetch.jsonc`, `ci.jsonc`, …)
- Man page source: `doc/fastfetch.1.in`
- Code generation scripts: `scripts/` (`gen-pciids.py`, `gen-amdgpuids.py`, `gen-man.py`)
+6 -8
View File
@@ -1,6 +1,5 @@
# Fastfetch
[![Benchmark](https://img.shields.io/badge/GitHub%20Pages-live-blue?logo=github)](https://fastfetch-cli.github.io/fastfetch/dev/bench)
[![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/fastfetch-cli/fastfetch/ci.yml)](https://github.com/fastfetch-cli/fastfetch/actions)
[![GitHub license](https://img.shields.io/github/license/fastfetch-cli/fastfetch)](https://github.com/fastfetch-cli/fastfetch/blob/dev/LICENSE)
[![GitHub contributors](https://img.shields.io/github/contributors/fastfetch-cli/fastfetch)](https://github.com/fastfetch-cli/fastfetch/graphs/contributors)
@@ -100,7 +99,7 @@ See the Wiki: https://github.com/fastfetch-cli/fastfetch/wiki/Building
* View all data that fastfetch detects: `fastfetch -s <module1>[:<module2>][:<module3>] --format json`
* Display help messages: `fastfetch --help`
* Generate a minimal config file: `fastfetch [-s <module1>[:<module2>]] --gen-config [</path/to/config.jsonc>]`
* Use [The online configuration generator](https://fastfetch-cli.github.io/fastfetch-config/) to generate a full config file with all optional options
* Use: `--gen-config-full` to generate a full config file with all optional options
## Customization
@@ -200,8 +199,7 @@ See [#1096](https://github.com/fastfetch-cli/fastfetch/issues/1096).
### Q: Fastfetch shows fewer dpkg packages than neofetch. Is it a bug?
1. Neofetch incorrectly counts `rc` packages for apt (packages that have been removed but still have configuration files remaining). See bug: https://github.com/dylanaraps/neofetch/issues/2278
2. Neofetch incorrectly counts `gpg-pubkey` as packages for rpm. You may check the results of `dnf list --installed | wc -l` and `rpm -qa | wc -l` to see the difference.
Neofetch incorrectly counts `rc` packages (packages that have been removed but still have configuration files remaining). See bug: https://github.com/dylanaraps/neofetch/issues/2278
### Q: I use Debian / Ubuntu / Debian-derived distro. My GPU is detected as `XXXX Device XXXX (VGA compatible)`. Is this a bug?
@@ -336,10 +334,10 @@ If you find Fastfetch useful, please consider donating.
Give us a star to show your support!
<a href="https://star-history.dera.page/#fastfetch-cli/fastfetch&Date">
<a href="https://star-history.com/#fastfetch-cli/fastfetch&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://star-history.dera.page/svg?repos=fastfetch-cli/fastfetch&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://star-history.dera.page/svg?repos=fastfetch-cli/fastfetch&type=Date" />
<img alt="Star History Chart" src="https://star-history.dera.page/svg?repos=fastfetch-cli/fastfetch&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=fastfetch-cli/fastfetch&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=fastfetch-cli/fastfetch&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=fastfetch-cli/fastfetch&type=Date" />
</picture>
</a>
+24 -56
View File
@@ -63,78 +63,46 @@ echo '
import json, subprocess, sys
def escape_fish_dq(s: str) -> str:
"""Escape special characters for Fish double quotes to prevent unexpected expansions. One slash in output needs four slashes in script"""
return str(s).replace("\\\\", "\\\\\\\\").replace("\\"", "\\\\\\"").replace("$", "\\\\$")
def main():
data: dict[str, list[dict]] = json.loads(subprocess.check_output(["fastfetch", "--help-raw"]))
enum_functions = []
complete_commands = []
for key in data:
for flag in data[key]:
long_name = flag.get("long", "")
# Handle pseudo flag logo-color-[1-9]
if long_name == "logo-color-[1-9]":
safe_desc_global = escape_fish_dq(flag.get("desc", ""))
if flag["long"] == "logo-color-[1-9]":
for i in range(1, 10):
complete_commands.append(f"complete -c fastfetch -d \"{safe_desc_global}\" -l \"logo-color-{i}\" -x -a \"(__fastfetch_complete_color)\"")
print(f"""complete -c fastfetch -d "{flag["desc"]}" -l "logo-color-{i}" -x -a "(__fastfetch_complete_color)" """)
continue
if flag.get("pseudo", False):
continue
safe_desc_global = escape_fish_dq(flag.get("desc", ""))
command_prefix = f"complete -c fastfetch -d \"{safe_desc_global}\" -l \"{long_name}\""
command_prefix = f"""complete -c fastfetch -d "{flag["desc"]}" -l "{flag["long"]}\""""
if "short" in flag:
short_name = flag["short"]
command_prefix += f" -o {short_name}"
command_prefix += f""" -o {flag["short"]}"""
if "arg" in flag:
arg_type: str = flag["arg"]["type"]
if arg_type == "bool":
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_bool)\"")
elif arg_type == "color":
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_color)\"")
elif arg_type == "command":
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_command)\"")
elif arg_type == "config":
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_config)\"")
elif arg_type == "enum":
# Sanitize flag name to create a valid Fish function name
func_name = long_name.replace("-", "_").replace("[", "_").replace("]", "_")
func_name = f"__fastfetch_complete_enum_{func_name}"
# Generate the enum function
func_lines = [f"function {func_name}"]
for enum_val, enum_desc in flag["arg"]["enum"].items():
safe_val = escape_fish_dq(enum_val)
safe_desc = escape_fish_dq(enum_desc)
func_lines.append(f" echo -e \"{safe_val}\\t{safe_desc}\"")
func_lines.append("end\n")
enum_functions.append("\n".join(func_lines))
# Append complete command pointing to the new function
complete_commands.append(f"{command_prefix} -x -a \"({func_name})\"")
elif arg_type == "logo":
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_logo)\"")
elif arg_type == "structure":
complete_commands.append(f"{command_prefix} -x -a \"(__fish_complete_list : __fastfetch_complete_structure)\"")
elif arg_type == "path":
complete_commands.append(f"{command_prefix} -r -F")
type: str = flag["arg"]["type"]
if type == "bool":
print(f"{command_prefix} -x -a \"(__fastfetch_complete_bool)\"")
elif type == "color":
print(f"{command_prefix} -x -a \"(__fastfetch_complete_color)\"")
elif type == "command":
print(f"{command_prefix} -x -a \"(__fastfetch_complete_command)\"")
elif type == "config":
print(f"{command_prefix} -x -a \"(__fastfetch_complete_config)\"")
elif type == "enum":
temp: str = " ".join(flag["arg"]["enum"])
print(f"{command_prefix} -x -a \"{temp}\"")
elif type == "logo":
print(f"{command_prefix} -x -a \"(__fastfetch_complete_logo)\"")
elif type == "structure":
print(f"{command_prefix} -x -a \"(__fish_complete_list : __fastfetch_complete_structure)\"")
elif type == "path":
print(f"{command_prefix} -r -F")
else:
complete_commands.append(f"{command_prefix} -x")
print(f"{command_prefix} -x")
else:
complete_commands.append(f"{command_prefix} -f")
# Print generated enum functions first, then complete rules
print("\n".join(enum_functions))
print("\n".join(complete_commands))
print(f"{command_prefix} -f")
if __name__ == "__main__":
-24
View File
@@ -1,27 +1,3 @@
fastfetch (2.68.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.68.1
-- Carter Li <zhangsongcui@live.cn> Wed, 02 Sep 2026 10:47:51 +0800
fastfetch (2.68.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.68.0
-- Carter Li <zhangsongcui@live.cn> Tue, 01 Sep 2026 16:34:36 +0800
fastfetch (2.67.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.67.0
-- Carter Li <zhangsongcui@live.cn> Thu, 06 Aug 2026 16:22:22 +0800
fastfetch (2.66.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.66.0
-- Carter Li <zhangsongcui@live.cn> Fri, 10 Jul 2026 15:13:37 +0800
fastfetch (2.65.2~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.65.2
+20 -44
View File
@@ -83,16 +83,6 @@
"type": "num",
"default": 0
}
},
{
"long": "watch",
"short": "w",
"desc": "Alias of dynamic-interval, but the value is in seconds instead of milliseconds. If no value is provided, it defaults to 1 second",
"arg": {
"type": "num",
"optional": true,
"default": 1
}
}
],
"Config": [
@@ -107,8 +97,26 @@
},
{
"long": "gen-config",
"desc": "Interactively generate a config file at the specified path",
"remark": "Opens an interactive configuration UI when run in a terminal. Falls back to non-interactive generation when env-var `$NO_COLOR` is set or stdin/stdout is not a TTY. Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"desc": "Generate a minimal config file at the specified path",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"arg": {
"type": "path",
"optional": true
}
},
{
"long": "gen-config-full",
"desc": "Generate a full config file with all optional settings at the specified path",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"arg": {
"type": "path",
"optional": true
}
},
{
"long": "gen-config-force",
"desc": "Generate a config file at the specified path, overwriting any existing file",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\"",
"arg": {
"type": "path",
"optional": true
@@ -585,38 +593,6 @@
"default": "string"
}
},
{
"long": "key-language",
"desc": "Specify the language used for module keys",
"remark": "Leave empty for auto-detection based on the system locale. Unsupported locales fall back to English",
"arg": {
"type": "enum",
"default": "en",
"optional": true,
"enum": {
"en": "English",
"ar": "Arabic",
"cs": "Czech",
"de": "German",
"es": "Spanish",
"fr": "French",
"gl": "Galician",
"he": "Hebrew",
"id": "Indonesian",
"it": "Italian",
"ja": "Japanese",
"ko": "Korean",
"pl": "Polish",
"pt": "Portuguese",
"ru": "Russian",
"tr": "Turkish",
"uk": "Ukrainian",
"vi": "Vietnamese",
"zh_CN": "Simplified Chinese",
"zh_TW": "Traditional Chinese"
}
}
},
{
"long": "bright-color",
"desc": "Specify whether keys, title, and ASCII logo should be printed in bright color",
+14 -227
View File
@@ -243,7 +243,7 @@
"oneOf": [
{
"type": "boolean",
"description": "True to show the module only if the last run succeeded; false to show it only if the last run failed"
"description": "True to show the module only if it succeeded; false to show it only if it failed"
},
{
"type": "null",
@@ -398,7 +398,7 @@
"type": "string"
},
"lmFormat": {
"description": "Output format for the `LM` module. See Wiki for formatting syntax\n 1. {service}: LM service / process name\n 2. {pretty-name}: LM pretty name\n 3. {version}: LM version",
"description": "Output format for the `LM` module. See Wiki for formatting syntax\n 1. {service}: LM service\n 2. {type}: LM type\n 3. {version}: LM version",
"type": "string"
},
"loadavgFormat": {
@@ -446,7 +446,7 @@
"type": "string"
},
"packagesFormat": {
"description": "Output format for the `Packages` module. See Wiki for formatting syntax\n 1. {am-system}: Number of am-system packages\n 2. {am-user}: Number of am-user (aka appman) packages\n 3. {appimage}: Number of appimage packages\n 4. {apk}: Number of apk packages\n 5. {brew}: Number of brew packages\n 6. {brew-cask}: Number of brew-cask packages\n 7. {cards}: Number of cards packages\n 8. {choco}: Number of choco packages\n 9. {crux}: Number of crux packages\n 10. {dpkg}: Number of dpkg packages\n 11. {emerge}: Number of emerge packages\n 12. {eopkg}: Number of eopkg packages\n 13. {flatpak-system}: Number of flatpak-system app packages\n 14. {flatpak-user}: Number of flatpak-user app packages\n 15. {guix-home}: Number of guix-home packages\n 16. {guix-system}: Number of guix-system packages\n 17. {guix-user}: Number of guix-user packages\n 18. {hpkg-system}: Number of hpkg-system packages\n 19. {hpkg-user}: Number of hpkg-user packages\n 20. {install-release}: Number of install-release packages\n 21. {kiss}: Number of kiss packages\n 22. {linglong}: Number of linglong packages\n 23. {lpkg}: Number of lpkg packages\n 24. {lpkgbuild}: Number of lpkgbuild packages\n 25. {macports}: Number of macports packages\n 26. {mport}: Number of mport packages\n 27. {moss}: Number of moss packages\n 28. {nix-default}: Number of nix-default packages\n 29. {nix-system}: Number of nix-system packages\n 30. {nix-user}: Number of nix-user packages\n 31. {opkg}: Number of opkg packages\n 32. {pacman}: Number of pacman packages\n 33. {pacman-branch}: Pacman branch on manjaro\n 34. {pacstall}: Number of pacstall packages\n 35. {paludis}: Number of paludis packages\n 36. {pisi}: Number of pisi packages\n 37. {pkg}: Number of pkg packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {pkgtool}: Number of pkgtool packages\n 40. {porg}: Number of porg packages\n 41. {rpm}: Number of rpm packages\n 42. {scoop-global}: Number of scoop-global packages\n 43. {scoop-user}: Number of scoop-user packages\n 44. {snap}: Number of snap packages\n 45. {soar}: Number of soar packages\n 46. {sorcery}: Number of sorcery packages\n 47. {winget}: Number of winget packages\n 48. {xbps}: Number of xbps packages\n 49. {brew-all}: Total number of all brew packages\n 50. {flatpak-all}: Total number of all flatpak app packages\n 51. {guix-all}: Total number of all guix packages\n 52. {hpkg-all}: Total number of all hpkg packages\n 53. {nix-all}: Total number of all nix packages\n 54. {all}: Number of all packages",
"description": "Output format for the `Packages` module. See Wiki for formatting syntax\n 1. {am-system}: Number of am-system packages\n 2. {am-user}: Number of am-user (aka appman) packages\n 3. {appimage}: Number of appimage packages\n 4. {apk}: Number of apk packages\n 5. {brew}: Number of brew packages\n 6. {brew-cask}: Number of brew-cask packages\n 7. {cards}: Number of cards packages\n 8. {choco}: Number of choco packages\n 9. {dpkg}: Number of dpkg packages\n 10. {emerge}: Number of emerge packages\n 11. {eopkg}: Number of eopkg packages\n 12. {flatpak-system}: Number of flatpak-system app packages\n 13. {flatpak-user}: Number of flatpak-user app packages\n 14. {guix-home}: Number of guix-home packages\n 15. {guix-system}: Number of guix-system packages\n 16. {guix-user}: Number of guix-user packages\n 17. {hpkg-system}: Number of hpkg-system packages\n 18. {hpkg-user}: Number of hpkg-user packages\n 19. {install-release}: Number of install-release packages\n 20. {kiss}: Number of kiss packages\n 21. {linglong}: Number of linglong packages\n 22. {lpkg}: Number of lpkg packages\n 23. {lpkgbuild}: Number of lpkgbuild packages\n 24. {macports}: Number of macports packages\n 25. {mport}: Number of mport packages\n 26. {moss}: Number of moss packages\n 27. {nix-default}: Number of nix-default packages\n 28. {nix-system}: Number of nix-system packages\n 29. {nix-user}: Number of nix-user packages\n 30. {opkg}: Number of opkg packages\n 31. {pacman}: Number of pacman packages\n 32. {pacman-branch}: Pacman branch on manjaro\n 33. {pacstall}: Number of pacstall packages\n 34. {paludis}: Number of paludis packages\n 35. {pisi}: Number of pisi packages\n 36. {pkg}: Number of pkg packages\n 37. {pkgsrc}: Number of pkgsrc packages\n 38. {pkgtool}: Number of pkgtool packages\n 39. {porg}: Number of porg packages\n 40. {rpm}: Number of rpm packages\n 41. {scoop-global}: Number of scoop-global packages\n 42. {scoop-user}: Number of scoop-user packages\n 43. {snap}: Number of snap packages\n 44. {soar}: Number of soar packages\n 45. {sorcery}: Number of sorcery packages\n 46. {winget}: Number of winget packages\n 47. {xbps}: Number of xbps packages\n 48. {brew-all}: Total number of all brew packages\n 49. {flatpak-all}: Total number of all flatpak app packages\n 50. {guix-all}: Total number of all guix packages\n 51. {hpkg-all}: Total number of all hpkg packages\n 52. {nix-all}: Total number of all nix packages\n 53. {all}: Number of all packages",
"type": "string"
},
"physicaldiskFormat": {
@@ -505,10 +505,6 @@
"description": "Output format for the `Title` module. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name\n 10. {user-id}: UID (*nix) / SID (Windows)\n 11. {pid}: PID of current process\n 12. {cwd}: CWD with home dir replaced by `~`",
"type": "string"
},
"topFormat": {
"description": "Output format for the `Top` module. See Wiki for formatting syntax\n 1. {name}: Process name\n 2. {pid}: Process ID\n 3. {cpu}: CPU usage\n 4. {mem}: Memory usage (RSS) in bytes\n 5. {disk-read}: Disk read bytes per second (0 if unsupported)\n 6. {disk-write}: Disk write bytes per second (0 if unsupported)\n 7. {threads}: Number of threads\n 8. {cpu-percentage}: CPU usage percentage\n 9. {mem-formatted}: Memory usage (RSS) formatted\n 10. {disk-read-formatted}: Disk read formatted\n 11. {disk-write-formatted}: Disk write formatted",
"type": "string"
},
"themeFormat": {
"description": "Output format for the `Theme` module. See Wiki for formatting syntax\n 1. {theme1}: Theme part 1\n 2. {theme2}: Theme part 2",
"type": "string"
@@ -546,7 +542,7 @@
"type": "string"
},
"wifiFormat": {
"description": "Output format for the `Wifi` module. See Wiki for formatting syntax\n 1. {inf-desc}: Interface description\n 2. {inf-status}: Interface status\n 3. {status}: Connection status\n 4. {ssid}: Connection SSID\n 5. {bssid}: Connection BSSID\n 6. {protocol}: Connection protocol\n 7. {signal-quality}: Connection signal quality (percentage num)\n 8. {rx-rate}: Connection RX rate\n 9. {tx-rate}: Connection TX rate\n 10. {security}: Connection Security algorithm\n 11. {signal-quality-bar}: Connection signal quality (percentage bar)\n 12. {channel}: Connection channel number\n 13. {channel-width}: Connection channel width in MHz\n 14. {band}: Connection channel band in GHz",
"description": "Output format for the `Wifi` module. See Wiki for formatting syntax\n 1. {inf-desc}: Interface description\n 2. {inf-status}: Interface status\n 3. {status}: Connection status\n 4. {ssid}: Connection SSID\n 5. {bssid}: Connection BSSID\n 6. {protocol}: Connection protocol\n 7. {signal-quality}: Connection signal quality (percentage num)\n 8. {rx-rate}: Connection RX rate\n 9. {tx-rate}: Connection TX rate\n 10. {security}: Connection Security algorithm\n 11. {signal-quality-bar}: Connection signal quality (percentage bar)\n 12. {channel}: Connection channel number\n 13. {band}: Connection channel band in GHz",
"type": "string"
},
"wmthemeFormat": {
@@ -916,6 +912,11 @@
"description": "Set the timeout (ms) when waiting for child processes, `-1` for no timeout",
"default": 5000
},
"preRun": {
"type": "string",
"description": "Command to run before printing logos",
"default": ""
},
"detectVersion": {
"type": "boolean",
"description": "Whether to detect and display component versions. Mainly for benchmarking",
@@ -1061,116 +1062,6 @@
"description": "Left padding for keys",
"minimum": 0,
"default": 0
},
"language": {
"description": "Language used for module keys. `null` for auto detection based on system locale",
"oneOf": [
{
"type": "null",
"description": "Auto detection based on system locale"
},
{
"type": "string",
"const": "en",
"description": "English"
},
{
"type": "string",
"const": "ar",
"description": "Arabic"
},
{
"type": "string",
"const": "cs",
"description": "Czech"
},
{
"type": "string",
"const": "de",
"description": "German"
},
{
"type": "string",
"const": "es",
"description": "Spanish"
},
{
"type": "string",
"const": "fr",
"description": "French"
},
{
"type": "string",
"const": "gl",
"description": "Galician"
},
{
"type": "string",
"const": "he",
"description": "Hebrew"
},
{
"type": "string",
"const": "id",
"description": "Indonesian"
},
{
"type": "string",
"const": "it",
"description": "Italian"
},
{
"type": "string",
"const": "ja",
"description": "Japanese"
},
{
"type": "string",
"const": "ko",
"description": "Korean"
},
{
"type": "string",
"const": "pl",
"description": "Polish"
},
{
"type": "string",
"const": "pt",
"description": "Portuguese"
},
{
"type": "string",
"const": "ru",
"description": "Russian"
},
{
"type": "string",
"const": "tr",
"description": "Turkish"
},
{
"type": "string",
"const": "uk",
"description": "Ukrainian"
},
{
"type": "string",
"const": "vi",
"description": "Vietnamese"
},
{
"type": "string",
"const": "zh_CN",
"description": "Simplified Chinese"
},
{
"type": "string",
"const": "zh_TW",
"description": "Traditional Chinese"
}
],
"default": "en"
}
}
},
@@ -1596,7 +1487,6 @@
"poweradapter",
"processes",
"publicip",
"top",
"separator",
"shell",
"sound",
@@ -2700,8 +2590,8 @@
},
"waitTime": {
"type": "integer",
"description": "Wait time (in ms) used to measure the I/O rate (calculated in bytes/sec).\nAvoid setting this too low, as the kernel requires time to accurately update counters.\nIgnored if 'detectTotal' is true.",
"default": 500,
"description": "Wait time (in ms). Disk I/O = (totalBytesEnd - totalBytesStart) / waitTime",
"default": 200,
"minimum": 1
},
"key": {
@@ -3601,8 +3491,8 @@
},
"waitTime": {
"type": "integer",
"description": "Wait time (in ms) used to measure the I/O rate (calculated in bytes/sec).\nAvoid setting this too low, as the kernel requires time to accurately update counters.\nIgnored if 'detectTotal' is true.",
"default": 500,
"description": "Wait time (in ms). Net I/O = (totalBytesEnd - totalBytesStart) / waitTime",
"default": 200,
"minimum": 1
},
"key": {
@@ -3763,7 +3653,6 @@
"apk",
"brew",
"choco",
"crux",
"dpkg",
"emerge",
"eopkg",
@@ -3985,12 +3874,7 @@
"properties": {
"type": {
"const": "processes",
"description": "Print the number of running processes and threads"
},
"countKprocs": {
"description": "Whether to take kernel processes and threads into account",
"type": "boolean",
"default": false
"description": "Print the number of running processes"
},
"key": {
"$ref": "#/$defs/key"
@@ -4435,103 +4319,6 @@
}
}
},
{
"title": "Top",
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"const": "top",
"description": "Print processes with the highest CPU, memory, disk I/O usage, latest start time or thread count"
},
"sort": {
"type": "string",
"description": "Sort processes by CPU, memory, disk IO usage, start time or thread count",
"enum": [
"cpu",
"memory",
"disk-read",
"disk-write",
"start-time",
"threads"
],
"default": "cpu"
},
"showTypes": {
"description": "Specify which resource usages are detected and displayed.\nIf memory or threads is enabled is enabled alone, only a single process snapshot is taken and `waitTime` sampling wait is skipped",
"default": [
"cpu",
"memory",
"disk"
],
"oneOf": [
{
"type": "string",
"enum": [
"cpu",
"memory",
"disk",
"threads"
]
},
{
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"enum": [
"cpu",
"memory",
"disk",
"threads"
]
}
}
]
},
"processes": {
"type": "integer",
"description": "Number of top processes to print",
"minimum": 1,
"default": 5
},
"waitTime": {
"type": "integer",
"description": "Wait time between process samples, in milliseconds. Ignored when showTypes contains \"memory\" only",
"minimum": 1,
"default": 500
},
"compact": {
"type": "boolean",
"description": "Print all process on a single line",
"default": false
},
"percent": {
"$ref": "#/$defs/percent"
},
"key": {
"$ref": "#/$defs/key"
},
"keyColor": {
"$ref": "#/$defs/keyColor"
},
"keyIcon": {
"$ref": "#/$defs/keyIcon"
},
"keyWidth": {
"$ref": "#/$defs/keyWidth"
},
"outputColor": {
"$ref": "#/$defs/outputColor"
},
"format": {
"$ref": "#/$defs/topFormat"
},
"condition": {
"$ref": "#/$defs/conditions"
}
}
},
{
"title": "TPM",
"type": "object",
+4 -5
View File
@@ -99,17 +99,16 @@
"gamepad",
"mouse",
"keyboard",
"top",
{
"type": "weather",
"timeout": 1000
},
"netio",
"diskio",
{
"type": "physicaldisk",
"temp": true
},
{
"type": "weather",
"timeout": 1000
},
"tpm",
"version",
"break",
+6 -7
View File
@@ -94,24 +94,23 @@
"opengl",
"opencl",
"users",
"bluetooth",
"bluetoothradio",
// "bluetooth", // doesn't work on macOS because it requires bluetooth permissions
// "bluetoothradio",
"sound",
"camera",
"gamepad",
"mouse",
"keyboard",
"top",
{
"type": "weather",
"timeout": 1000
},
"netio",
"diskio",
{
"type": "physicaldisk",
"temp": true
},
{
"type": "weather",
"timeout": 1000
},
"tpm",
"version",
"logo",
+1 -1
View File
@@ -12,7 +12,7 @@
},
"modules": [
{
// draw borders first to make colors of left and right border consistent
// draw borders first to make colors of left and right border consistant
"key": " user",
"type": "title",
"format": "{user-name}",
-7
View File
@@ -16,13 +16,6 @@
"separator",
"os",
"host",
{
"type": "board",
"key": "Host",
"condition": {
"succeeded": false
}
},
{
"type": "kernel",
"format": "{release}"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"home": "https://github.com/ibireme/yyjson",
"license": "MIT ( embed in source )",
"version": "0.13.0",
"version": "0.12.0",
"author": "ibireme"
}
+285 -700
View File
File diff suppressed because it is too large Load Diff
+674 -911
View File
File diff suppressed because it is too large Load Diff
-4
View File
@@ -31,10 +31,6 @@ typedef struct FFPlatform {
FFstrbuf userShell;
FFPlatformSysinfo sysinfo;
#if _WIN32
uint32_t initCP; // The code page used by the console when the program started
#endif
} FFPlatform;
void ffPlatformInit(FFPlatform* platform);
+13 -31
View File
@@ -1,10 +1,11 @@
#pragma once
#include "common/attributes.h"
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define FF_LIST_DEFAULT_ALLOC 16
@@ -22,33 +23,33 @@ bool ffListPop(FFlist* list, uint32_t elementSize, void* __restrict result);
static inline void ffListInit(FFlist* list) {
list->capacity = 0;
list->length = 0;
list->data = nullptr;
list->data = NULL;
}
static inline void ffListInitA(FFlist* list, uint32_t elementSize, uint32_t capacity) {
ffListInit(list);
list->capacity = capacity;
list->data = __builtin_expect(capacity == 0, 0) ? nullptr : (uint8_t*) malloc((size_t) capacity * elementSize);
list->data = __builtin_expect(capacity == 0, 0) ? NULL : (uint8_t*) malloc((size_t) capacity * elementSize);
}
[[nodiscard]] static inline FFlist ffListCreate() {
FF_A_NODISCARD static inline FFlist ffListCreate() {
FFlist result;
ffListInit(&result);
return result;
}
[[nodiscard]] static inline FFlist ffListCreateA(uint32_t elementSize, uint32_t capacity) {
FF_A_NODISCARD static inline FFlist ffListCreateA(uint32_t elementSize, uint32_t capacity) {
FFlist result;
ffListInitA(&result, elementSize, capacity);
return result;
}
[[nodiscard]] static inline void* ffListGet(const FFlist* list, uint32_t elementSize, uint32_t index) {
FF_A_NODISCARD static inline void* ffListGet(const FFlist* list, uint32_t elementSize, uint32_t index) {
assert(list->capacity > index);
return list->data + (index * elementSize);
}
[[nodiscard]] static inline uint32_t ffListFirstIndexComp(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
FF_A_NODISCARD static inline uint32_t ffListFirstIndexComp(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
for (uint32_t i = 0; i < list->length; i++) {
if (compFunc(ffListGet(list, elementSize, i), compElement)) {
return i;
@@ -58,7 +59,7 @@ static inline void ffListInitA(FFlist* list, uint32_t elementSize, uint32_t capa
return list->length;
}
[[nodiscard]] static inline bool ffListContains(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
FF_A_NODISCARD static inline bool ffListContains(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
return ffListFirstIndexComp(list, elementSize, compElement, compFunc) != list->length;
}
@@ -86,7 +87,7 @@ static inline void ffListDestroy(FFlist* list) {
// Avoid free-after-use. These 3 assignments are cheap so don't remove them
list->capacity = list->length = 0;
free(list->data);
list->data = nullptr;
list->data = NULL;
}
static inline void ffListClear(FFlist* list) {
@@ -111,25 +112,12 @@ static inline void* ffListAdd(FFlist* list, uint32_t elementSize) {
return ffListGet(list, elementSize, list->length - 1);
}
static inline void ffListRemoveAt(FFlist* list, uint32_t elementSize, uint32_t index) {
assert(list->length > index);
memmove(list->data + (index * elementSize), list->data + ((index + 1) * elementSize), (size_t) (list->length - index - 1) * elementSize);
--list->length;
}
static inline void ffListInsertAt(FFlist* list, uint32_t elementSize, uint32_t index, const void* element) {
assert(list->length >= index);
ffListAdd(list, elementSize);
memmove(list->data + ((index + 1) * elementSize), list->data + (index * elementSize), (size_t) (list->length - index - 1) * elementSize);
memcpy(list->data + (index * elementSize), element, elementSize);
}
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
for (itemType* itemVarName = (itemType*) (listVar).data; \
itemVarName - (itemType*) (listVar).data < (intptr_t) (listVar).length; \
++itemVarName)
#define FF_LIST_AUTO_DESTROY [[gnu::cleanup(ffListDestroy)]] FFlist
#define FF_LIST_AUTO_DESTROY FFlist FF_A_CLEANUP(ffListDestroy)
#define FF_LIST_GET(itemType, listVar, index) \
({ \
@@ -139,12 +127,6 @@ static inline void ffListInsertAt(FFlist* list, uint32_t elementSize, uint32_t i
#define FF_LIST_ADD(itemType, listVar) (itemType*) ffListAdd(&(listVar), (uint32_t) sizeof(itemType))
#define FF_LIST_REMOVE_AT(itemType, listVar, index) \
ffListRemoveAt(&(listVar), (uint32_t) sizeof(itemType), (index))
#define FF_LIST_INSERT_AT(itemType, listVar, index, pElement) \
ffListInsertAt(&(listVar), (uint32_t) sizeof(itemType), (index), (pElement))
#define FF_LIST_FIRST(itemType, listVar) FF_LIST_GET(itemType, listVar, 0)
#define FF_LIST_LAST(itemType, listVar) \
({ \
@@ -154,9 +136,9 @@ static inline void ffListInsertAt(FFlist* list, uint32_t elementSize, uint32_t i
#define FF_LIST_CONTAINS(listVar, pCompElement, compFunc) \
({ \
typedef typeof(*(pCompElement)) compElementType; \
typedef __typeof__(*(pCompElement)) compElementType; \
typedef bool compFuncType(const compElementType*, const compElementType*); \
static_assert(__builtin_types_compatible_p(typeof(compFunc), compFuncType), "Incompatible callback function"); \
static_assert(__builtin_types_compatible_p(__typeof__(compFunc), compFuncType), "Incompatible callback function"); \
ffListContains(&(listVar), (uint32_t) sizeof(*(pCompElement)), (pCompElement), (bool (*)(const void*, const void*)) compFunc); \
})
+70 -70
View File
@@ -1,5 +1,7 @@
#pragma once
#include "common/attributes.h"
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -8,7 +10,6 @@
#include <stdlib.h>
#include <assert.h>
#include "common/memrchr.h"
#include "common/arrutil.h"
#ifdef FF_USE_SYSTEM_YYJSON
#include <yyjson.h>
@@ -36,15 +37,15 @@ static inline void ffStrbufInit(FFstrbuf* strbuf);
void ffStrbufInitA(FFstrbuf* strbuf, uint32_t allocate);
void ffStrbufInitVF(FFstrbuf* strbuf, const char* format, va_list arguments);
void ffStrbufInitMoveNS(FFstrbuf* strbuf, uint32_t length, char* heapStr);
[[gnu::format(printf, 2, 3)]] void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...);
[[gnu::format(printf, 1, 2)]] [[nodiscard]] FFstrbuf ffStrbufCreateF(const char* format, ...);
FF_A_PRINTF(2, 3) void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...);
FF_A_PRINTF(1, 2) FF_A_NODISCARD FFstrbuf ffStrbufCreateF(const char* format, ...);
void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free);
void ffStrbufEnsureFreeNoCheck(FFstrbuf* strbuf, uint32_t free);
static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value);
void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int (*transformFunc)(int));
[[gnu::format(printf, 2, 3)]] void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...);
FF_A_PRINTF(2, 3) void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...);
void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments);
const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until);
@@ -57,7 +58,7 @@ void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c);
// NOTE: Unlike ffStrbufAppend*, ffStrbufSet* functions may NOT reserve extra space
void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value);
void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value);
[[gnu::format(printf, 2, 3)]] void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
FF_A_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
void ffStrbufTrimLeft(FFstrbuf* strbuf, char c);
void ffStrbufTrimRight(FFstrbuf* strbuf, char c);
@@ -78,7 +79,7 @@ bool ffStrbufSubstrAfterFirstS(FFstrbuf* strbuf, const char* str);
bool ffStrbufSubstrAfterLastC(FFstrbuf* strbuf, char c);
bool ffStrbufSubstr(FFstrbuf* strbuf, uint32_t start, uint32_t end);
[[nodiscard]] uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
FF_A_NODISCARD uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
bool ffStrbufRemoveIgnCaseEndS(FFstrbuf* strbuf, const char* end);
@@ -89,7 +90,7 @@ void ffStrbufLowerCase(FFstrbuf* strbuf);
// Function alters the buffer to extract lines or delimited segments (replaces the delimiter with '\0')
// so that buffer MUST be heap allocated (NOT a static string)
// `lineptr` must be `nullptr` and `n` MUST be `0` for the first call
// `lineptr` must be `NULL` and `n` MUST be `0` for the first call
// Caller MUST NOT free `*lineptr`
bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
@@ -102,7 +103,7 @@ void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line
(points to buffer's internal memory address to avoid memory allocation and copy).
MUST NOT be freed by the caller, unlike `getline(3)`.
* MUST be nullptr for the first call.
* MUST be NULL for the first call.
* @param[in,out] n The pointer to the size of the buffer of lineptr.
MUST be 0 for the first call.
* @param[in] buffer The buffer to read from.
@@ -134,7 +135,7 @@ void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value);
// if `precision < 0`, let yyjson decide the precision
void ffStrbufAppendDouble(FFstrbuf* strbuf, double value, int8_t precision, bool trailingZeros);
[[nodiscard]] static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) {
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) {
FFstrbuf strbuf;
ffStrbufInitA(&strbuf, allocate);
return strbuf;
@@ -149,7 +150,7 @@ static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf*
}
}
[[nodiscard]] static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src) {
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src) {
FFstrbuf strbuf;
ffStrbufInitCopy(&strbuf, src);
return strbuf;
@@ -165,7 +166,7 @@ static inline void ffStrbufInitMove(FFstrbuf* strbuf, FFstrbuf* src) {
}
}
[[nodiscard]] static inline FFstrbuf ffStrbufCreateMove(FFstrbuf* src) {
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateMove(FFstrbuf* src) {
FFstrbuf strbuf;
ffStrbufInitMove(&strbuf, src);
return strbuf;
@@ -184,8 +185,8 @@ static inline void ffStrbufDestroy(FFstrbuf* strbuf) {
ffStrbufInit(strbuf);
}
[[nodiscard]] static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) {
assert(strbuf != nullptr);
FF_A_NODISCARD static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) {
assert(strbuf != NULL);
if (strbuf->allocated == 0) {
return 0;
}
@@ -209,7 +210,7 @@ static inline void ffStrbufEnsureFree(FFstrbuf* strbuf, uint32_t free) {
static inline void ffStrbufClear(FFstrbuf* strbuf) {
assert(strbuf != nullptr);
assert(strbuf != NULL);
extern char* CHAR_NULL_PTR;
if (strbuf->allocated == 0) {
@@ -239,7 +240,7 @@ static inline void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c) {
}
static inline void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value) {
if (__builtin_expect(value == nullptr || length == 0, false)) {
if (__builtin_expect(value == NULL || length == 0, false)) {
return;
}
ffStrbufEnsureFree(strbuf, length);
@@ -251,7 +252,7 @@ static inline void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const cha
static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value) {
assert(value != strbuf);
if (value == nullptr) {
if (value == NULL) {
return;
}
ffStrbufAppendNS(strbuf, value->length, value->chars);
@@ -262,9 +263,9 @@ static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf) {
}
static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) {
assert(strbuf != nullptr);
assert(strbuf != NULL);
if (value == nullptr) {
if (value == NULL) {
ffStrbufClear(strbuf);
} else {
ffStrbufSetNS(strbuf, (uint32_t) strlen(value), value);
@@ -272,7 +273,7 @@ static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) {
}
static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
assert(strbuf != nullptr);
assert(strbuf != NULL);
if (yyjson_is_str(jsonVal)) {
ffStrbufSetNS(strbuf, (uint32_t) unsafe_yyjson_get_len(jsonVal), unsafe_yyjson_get_str(jsonVal));
@@ -284,7 +285,7 @@ static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
}
static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value) {
if (value == nullptr) {
if (value == NULL) {
return;
}
ffStrbufAppendNS(strbuf, (uint32_t) strlen(value), value);
@@ -304,7 +305,7 @@ static inline void ffStrbufInit(FFstrbuf* strbuf) {
strbuf->chars = CHAR_NULL_PTR;
}
[[nodiscard]] static inline FFstrbuf ffStrbufCreate(void) {
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreate(void) {
FFstrbuf strbuf;
ffStrbufInit(&strbuf);
return strbuf;
@@ -321,7 +322,7 @@ static inline void ffStrbufInitStatic(FFstrbuf* strbuf, const char* str) {
strbuf->chars = (char*) str;
}
[[nodiscard]] static inline FFstrbuf ffStrbufCreateStatic(const char* str) {
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateStatic(const char* str) {
FFstrbuf strbuf;
ffStrbufInitStatic(&strbuf, str);
return strbuf;
@@ -332,7 +333,7 @@ static inline void ffStrbufSetStatic(FFstrbuf* strbuf, const char* value) {
free(strbuf->chars);
}
if (value != nullptr) {
if (value != NULL) {
ffStrbufInitStatic(strbuf, value);
} else {
ffStrbufInit(strbuf);
@@ -344,7 +345,7 @@ static inline void ffStrbufInitNS(FFstrbuf* strbuf, uint32_t length, const char*
ffStrbufAppendNS(strbuf, length, str);
}
[[nodiscard]] static inline FFstrbuf ffStrbufCreateNS(uint32_t length, const char* str) {
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateNS(uint32_t length, const char* str) {
FFstrbuf strbuf;
ffStrbufInitNS(&strbuf, length, str);
return strbuf;
@@ -360,113 +361,113 @@ static inline void ffStrbufInitS(FFstrbuf* strbuf, const char* str) {
ffStrbufAppendS(strbuf, str);
}
[[nodiscard]] static inline FFstrbuf ffStrbufCreateS(const char* str) {
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateS(const char* str) {
FFstrbuf strbuf;
ffStrbufInitS(&strbuf, str);
return strbuf;
}
static inline void ffStrbufPrepend(FFstrbuf* strbuf, FFstrbuf* value) {
if (value == nullptr) {
if (value == NULL) {
return;
}
ffStrbufPrependNS(strbuf, value->length, value->chars);
}
static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value) {
if (value == nullptr) {
if (value == NULL) {
return;
}
ffStrbufPrependNS(strbuf, (uint32_t) strlen(value), value);
}
[[nodiscard]] static inline int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
static inline FF_A_NODISCARD int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
uint32_t length = strbuf->length > comp->length ? comp->length : strbuf->length;
return memcmp(strbuf->chars, comp->chars, length + 1);
}
[[nodiscard]] static inline bool ffStrbufEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
static inline FF_A_NODISCARD bool ffStrbufEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
return ffStrbufComp(strbuf, comp) == 0;
}
[[nodiscard]] static inline int ffStrbufCompS(const FFstrbuf* strbuf, const char* comp) {
static inline FF_A_NODISCARD int ffStrbufCompS(const FFstrbuf* strbuf, const char* comp) {
return strcmp(strbuf->chars, comp);
}
[[nodiscard]] static inline bool ffStrbufEqualS(const FFstrbuf* strbuf, const char* comp) {
static inline FF_A_NODISCARD bool ffStrbufEqualS(const FFstrbuf* strbuf, const char* comp) {
return ffStrbufCompS(strbuf, comp) == 0;
}
[[nodiscard]] static inline int ffStrbufIgnCaseCompS(const FFstrbuf* strbuf, const char* comp) {
static inline FF_A_NODISCARD int ffStrbufIgnCaseCompS(const FFstrbuf* strbuf, const char* comp) {
return strcasecmp(strbuf->chars, comp);
}
[[nodiscard]] static inline bool ffStrbufIgnCaseEqualS(const FFstrbuf* strbuf, const char* comp) {
static inline FF_A_NODISCARD bool ffStrbufIgnCaseEqualS(const FFstrbuf* strbuf, const char* comp) {
return ffStrbufIgnCaseCompS(strbuf, comp) == 0;
}
[[nodiscard]] static inline int ffStrbufIgnCaseComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
static inline FF_A_NODISCARD int ffStrbufIgnCaseComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
return ffStrbufIgnCaseCompS(strbuf, comp->chars);
}
[[nodiscard]] static inline bool ffStrbufIgnCaseEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
static inline FF_A_NODISCARD bool ffStrbufIgnCaseEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
return ffStrbufIgnCaseComp(strbuf, comp) == 0;
}
[[nodiscard]] static inline bool ffStrbufContainC(const FFstrbuf* strbuf, char c) {
return memchr(strbuf->chars, c, strbuf->length) != nullptr;
static inline FF_A_NODISCARD bool ffStrbufContainC(const FFstrbuf* strbuf, char c) {
return memchr(strbuf->chars, c, strbuf->length) != NULL;
}
[[nodiscard]] static inline bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str) {
return strstr(strbuf->chars, str) != nullptr;
static inline FF_A_NODISCARD bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str) {
return strstr(strbuf->chars, str) != NULL;
}
[[nodiscard]] static inline bool ffStrbufContain(const FFstrbuf* strbuf, const FFstrbuf* str) {
static inline FF_A_NODISCARD bool ffStrbufContain(const FFstrbuf* strbuf, const FFstrbuf* str) {
return ffStrbufContainS(strbuf, str->chars);
}
[[nodiscard]] static inline bool ffStrbufContainIgnCaseS(const FFstrbuf* strbuf, const char* str) {
return strcasestr(strbuf->chars, str) != nullptr;
static inline FF_A_NODISCARD bool ffStrbufContainIgnCaseS(const FFstrbuf* strbuf, const char* str) {
return strcasestr(strbuf->chars, str) != NULL;
}
[[nodiscard]] static inline bool ffStrbufContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* str) {
static inline FF_A_NODISCARD bool ffStrbufContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* str) {
return ffStrbufContainIgnCaseS(strbuf, str->chars);
}
[[nodiscard]] static inline uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
FF_A_NODISCARD static inline uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
assert(start <= strbuf->length);
const char* ptr = (const char*) memchr(strbuf->chars + start, c, strbuf->length - start);
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
}
[[nodiscard]] static inline uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str) {
FF_A_NODISCARD static inline uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str) {
assert(start <= strbuf->length);
const char* ptr = strstr(strbuf->chars + start, str);
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
}
[[nodiscard]] static inline uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
FF_A_NODISCARD static inline uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
assert(start <= strbuf->length);
const char* ptr = (const char*) memrchr(strbuf->chars, c, start + 1);
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
}
[[nodiscard]] static inline uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, char c) {
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, char c) {
return ffStrbufNextIndexC(strbuf, 0, c);
}
[[nodiscard]] static inline uint32_t ffStrbufFirstIndex(const FFstrbuf* strbuf, const FFstrbuf* searched) {
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndex(const FFstrbuf* strbuf, const FFstrbuf* searched) {
return ffStrbufNextIndexS(strbuf, 0, searched->chars);
}
[[nodiscard]] static inline uint32_t ffStrbufFirstIndexS(const FFstrbuf* strbuf, const char* str) {
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndexS(const FFstrbuf* strbuf, const char* str) {
return ffStrbufNextIndexS(strbuf, 0, str);
}
[[nodiscard]] static inline uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, char c) {
static inline FF_A_NODISCARD uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, char c) {
if (strbuf->length == 0) {
return 0;
}
@@ -482,11 +483,11 @@ static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c) {
return ffStrbufSubstrBefore(strbuf, ffStrbufLastIndexC(strbuf, c));
}
[[nodiscard]] static inline bool ffStrbufStartsWithC(const FFstrbuf* strbuf, char c) {
static inline FF_A_NODISCARD bool ffStrbufStartsWithC(const FFstrbuf* strbuf, char c) {
return strbuf->chars[0] == c;
}
[[nodiscard]] static inline bool ffStrbufStartsWithSN(const FFstrbuf* strbuf, const char* start, uint32_t length) {
static inline FF_A_NODISCARD bool ffStrbufStartsWithSN(const FFstrbuf* strbuf, const char* start, uint32_t length) {
if (length > strbuf->length) {
return false;
}
@@ -494,34 +495,34 @@ static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c) {
return memcmp(strbuf->chars, start, length) == 0;
}
[[nodiscard]] static inline bool ffStrbufStartsWithS(const FFstrbuf* strbuf, const char* start) {
static inline FF_A_NODISCARD bool ffStrbufStartsWithS(const FFstrbuf* strbuf, const char* start) {
return ffStrbufStartsWithSN(strbuf, start, (uint32_t) strlen(start));
}
[[nodiscard]] static inline bool ffStrbufStartsWith(const FFstrbuf* strbuf, const FFstrbuf* start) {
static inline FF_A_NODISCARD bool ffStrbufStartsWith(const FFstrbuf* strbuf, const FFstrbuf* start) {
return ffStrbufStartsWithSN(strbuf, start->chars, start->length);
}
[[nodiscard]] static inline bool ffStrbufStartsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t length, const char* start) {
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t length, const char* start) {
if (length > strbuf->length) {
return false;
}
return strncasecmp(strbuf->chars, start, length) == 0;
}
[[nodiscard]] static inline bool ffStrbufStartsWithIgnCaseS(const FFstrbuf* strbuf, const char* start) {
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCaseS(const FFstrbuf* strbuf, const char* start) {
return ffStrbufStartsWithIgnCaseNS(strbuf, (uint32_t) strlen(start), start);
}
[[nodiscard]] static inline bool ffStrbufStartsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* start) {
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* start) {
return ffStrbufStartsWithIgnCaseNS(strbuf, start->length, start->chars);
}
[[nodiscard]] static inline bool ffStrbufEndsWithC(const FFstrbuf* strbuf, char c) {
static inline FF_A_NODISCARD bool ffStrbufEndsWithC(const FFstrbuf* strbuf, char c) {
return strbuf->length == 0 ? false : strbuf->chars[strbuf->length - 1] == c;
}
[[nodiscard]] static inline bool ffStrbufEndsWithNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
static inline FF_A_NODISCARD bool ffStrbufEndsWithNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
if (endLength > strbuf->length) {
return false;
}
@@ -529,30 +530,30 @@ static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c) {
return memcmp(strbuf->chars + strbuf->length - endLength, end, endLength) == 0;
}
[[nodiscard]] static inline bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end) {
static inline FF_A_NODISCARD bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end) {
return ffStrbufEndsWithNS(strbuf, (uint32_t) strlen(end), end);
}
[[nodiscard]] static inline bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*const fn)(int)) {
static inline FF_A_NODISCARD bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*const fn)(int)) {
return strbuf->length == 0 ? false : fn(strbuf->chars[strbuf->length - 1]);
}
[[nodiscard]] static inline bool ffStrbufEndsWith(const FFstrbuf* strbuf, const FFstrbuf* end) {
static inline FF_A_NODISCARD bool ffStrbufEndsWith(const FFstrbuf* strbuf, const FFstrbuf* end) {
return ffStrbufEndsWithNS(strbuf, end->length, end->chars);
}
[[nodiscard]] static inline bool ffStrbufEndsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
if (endLength > strbuf->length) {
return false;
}
return strcasecmp(strbuf->chars + strbuf->length - endLength, end) == 0;
}
[[nodiscard]] static inline bool ffStrbufEndsWithIgnCaseS(const FFstrbuf* strbuf, const char* end) {
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCaseS(const FFstrbuf* strbuf, const char* end) {
return ffStrbufEndsWithIgnCaseNS(strbuf, (uint32_t) strlen(end), end);
}
[[nodiscard]] static inline bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* end) {
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* end) {
return ffStrbufEndsWithIgnCaseNS(strbuf, end->length, end->chars);
}
@@ -607,19 +608,19 @@ static inline void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file) {
fputc('\n', file);
}
[[nodiscard]] static inline double ffStrbufToDouble(const FFstrbuf* strbuf, double defaultValue) {
FF_A_NODISCARD static inline double ffStrbufToDouble(const FFstrbuf* strbuf, double defaultValue) {
char* str_end;
double result = strtod(strbuf->chars, &str_end);
return str_end == strbuf->chars ? defaultValue : result;
}
[[nodiscard]] static inline uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue) {
FF_A_NODISCARD static inline uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue) {
char* str_end;
unsigned long long result = strtoull(strbuf->chars, &str_end, 10);
return str_end == strbuf->chars ? defaultValue : (uint64_t) result;
}
[[nodiscard]] static inline int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue) {
FF_A_NODISCARD static inline int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue) {
char* str_end;
long long result = strtoll(strbuf->chars, &str_end, 10);
return str_end == strbuf->chars ? defaultValue : (int64_t) result;
@@ -628,5 +629,4 @@ static inline void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file) {
// Returns true if the strbuf is modified
bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf);
#define FF_STRBUF_AUTO_DESTROY [[gnu::cleanup(ffStrbufDestroy)]] FFstrbuf
#define FF_STRBUF_STATIC(str) { .allocated = 0, .length = (uint32_t) sizeof(str) - 1, .chars = str }
#define FF_STRBUF_AUTO_DESTROY FFstrbuf FF_A_CLEANUP(ffStrbufDestroy)
+25 -25
View File
@@ -5,13 +5,13 @@ const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result) {
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt64Type, result)) {
return "Number type is not SInt64";
}
return nullptr;
return NULL;
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
if (CFDataGetLength((CFDataRef) cf) != sizeof(int64_t)) {
return "Data length is not sizeof(int64_t)";
}
CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(int64_t)), (uint8_t*) result);
return nullptr;
return NULL;
}
return "TypeID is neither 'CFNumber' nor 'CFData'";
@@ -22,13 +22,13 @@ const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result) {
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt32Type, result)) {
return "Number type is not SInt32";
}
return nullptr;
return NULL;
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
if (CFDataGetLength((CFDataRef) cf) != sizeof(*result)) {
return "Data length is not sizeof(int32_t)";
}
CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(*result)), (uint8_t*) result);
return nullptr;
return NULL;
}
return "TypeID is neither 'CFNumber' nor 'CFData'";
@@ -40,7 +40,7 @@ const char* ffCfNumGetDouble(CFTypeRef cf, double* result) {
!CFNumberGetValue((CFNumberRef) cf, kCFNumberFloatType, result)) {
return "Number type is not Double or Float";
}
return nullptr;
return NULL;
}
return "TypeID is neither 'CFNumber'";
@@ -54,13 +54,13 @@ const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result) {
CFAbsoluteTime absTime = CFDateGetAbsoluteTime((CFDateRef) cf);
// Convert from seconds to milliseconds and add the difference between 1970 and 2001 in milliseconds
*result = (uint64_t) ((absTime + 978307200 /*kCFAbsoluteTimeIntervalSince1970*/) * 1000);
return nullptr;
return NULL;
}
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
ffStrbufClear(result);
if (!cf) {
return nullptr;
return NULL;
}
if (CFGetTypeID(cf) == CFStringGetTypeID()) {
@@ -72,7 +72,7 @@ const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
} else {
uint32_t length = (uint32_t) CFStringGetLength(cfStr);
if (length == 0) {
return nullptr;
return NULL;
}
ffStrbufEnsureFixedLengthFree(result, (uint32_t) CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8));
if (!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) {
@@ -86,7 +86,7 @@ const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
CFDataRef cfData = (CFDataRef) cf;
uint32_t length = (uint32_t) CFDataGetLength(cfData);
if (length == 0) {
return nullptr;
return NULL;
}
ffStrbufEnsureFixedLengthFree(result, length + 1);
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
@@ -96,20 +96,20 @@ const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
return "TypeID is neither 'CFString' nor 'CFData'";
}
return nullptr;
return NULL;
}
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result) {
ffStrbufClear(result);
if (!cf) {
return nullptr;
return NULL;
}
if (CFGetTypeID(cf) == CFDataGetTypeID()) {
CFDataRef cfData = (CFDataRef) cf;
uint32_t length = (uint32_t) CFDataGetLength(cfData);
if (length == 0) {
return nullptr;
return NULL;
}
ffStrbufEnsureFixedLengthFree(result, length + 1);
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
@@ -119,12 +119,12 @@ const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result) {
return "TypeID is not 'CFData'";
}
return nullptr;
return NULL;
}
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
@@ -133,7 +133,7 @@ const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* r
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
@@ -142,7 +142,7 @@ const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstr
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) {
CFBooleanRef cf = (CFBooleanRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
@@ -151,12 +151,12 @@ const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result)
}
*result = CFBooleanGetValue(cf);
return nullptr;
return NULL;
}
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) {
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
@@ -165,7 +165,7 @@ const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) {
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result) {
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
@@ -174,7 +174,7 @@ const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* res
const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result) {
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
@@ -183,7 +183,7 @@ const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* res
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) {
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
@@ -202,22 +202,22 @@ const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offs
}
CFDataGetBytes((CFDataRef) cf, CFRangeMake(offset, size), result);
return nullptr;
return NULL;
}
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) {
CFDictionaryRef cf = (CFDictionaryRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr || CFGetTypeID(cf) != CFDictionaryGetTypeID()) {
if (cf == NULL || CFGetTypeID(cf) != CFDictionaryGetTypeID()) {
return "TypeID is not 'CFDictionary'";
}
*result = cf;
return nullptr;
return NULL;
}
const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result) {
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
if (cf == nullptr) {
if (cf == NULL) {
return "CFDictionaryGetValue() failed";
}
+3 -3
View File
@@ -4,7 +4,7 @@
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
// Return error info if failed, nullptr otherwise
// Return error info if failed, NULL otherwise
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result);
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result);
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result);
@@ -32,7 +32,7 @@ static inline void cfReleaseWrapper(void* type) {
}
}
#define FF_CFTYPE_AUTO_RELEASE [[gnu::cleanup(cfReleaseWrapper)]]
#define FF_CFTYPE_AUTO_RELEASE FF_A_CLEANUP(cfReleaseWrapper)
static inline void wrapIoObjectRelease(io_object_t* service) {
assert(service);
@@ -40,4 +40,4 @@ static inline void wrapIoObjectRelease(io_object_t* service) {
IOObjectRelease(*service);
}
}
#define FF_IOOBJECT_AUTO_RELEASE [[gnu::cleanup(wrapIoObjectRelease)]]
#define FF_IOOBJECT_AUTO_RELEASE FF_A_CLEANUP(wrapIoObjectRelease)
+10 -45
View File
@@ -86,7 +86,7 @@ static const char* smcCall(io_connect_t conn, uint32_t selector, SmcKeyData_t* i
if (IOConnectCallStructMethod(conn, selector, inputStructure, size, outputStructure, &size) != kIOReturnSuccess) {
return "IOConnectCallStructMethod(conn) failed";
}
return nullptr;
return NULL;
}
// Provides key info, using a cache to dramatically improve the energy impact of smcFanControl
@@ -103,7 +103,7 @@ static const char* smcGetKeyInfo(io_connect_t conn, const uint32_t key, SmcKeyDa
}
*key_info = outputStructure.keyInfo;
return nullptr;
return NULL;
}
static const char* smcReadSmcVal(io_connect_t conn, const UInt32Char_t key, SmcVal_t* val) {
@@ -130,7 +130,7 @@ static const char* smcReadSmcVal(io_connect_t conn, const UInt32Char_t key, SmcV
memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
return nullptr;
return NULL;
}
static const char* smcOpen(io_connect_t* conn) {
@@ -143,13 +143,13 @@ static const char* smcOpen(io_connect_t* conn) {
return "IOServiceOpen() failed";
}
return nullptr;
return NULL;
}
static const char* smcReadValue(io_connect_t conn, const UInt32Char_t key, double* value) {
SmcVal_t val = { 0 };
const char* error = smcReadSmcVal(conn, key, &val);
if (error != nullptr) {
if (error != NULL) {
return error;
}
if (val.dataSize == 0) {
@@ -270,7 +270,7 @@ static const char* smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
default:
return "Unsupported SMC data type";
}
return nullptr;
return NULL;
}
static bool detectTemp(io_connect_t conn, const char* sensor, double* sum) {
@@ -291,7 +291,7 @@ static io_connect_t conn;
const char* ffDetectSmcSpecificTemp(const char* sensor, double* result) {
if (!conn) {
if (smcOpen(&conn) != nullptr) {
if (smcOpen(&conn) != NULL) {
conn = (io_connect_t) -1;
}
}
@@ -303,12 +303,12 @@ const char* ffDetectSmcSpecificTemp(const char* sensor, double* result) {
return "Could not read SMC temperature";
}
return nullptr;
return NULL;
}
const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
if (!conn) {
if (smcOpen(&conn) != nullptr) {
if (smcOpen(&conn) != NULL) {
conn = (io_connect_t) -1;
}
}
@@ -392,27 +392,6 @@ const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
count += detectTemp(conn, "Tp0e", result); // CPU performance core 8
break;
case FF_TEMP_CPU_M5X:
count += detectTemp(conn, "Tp00", result); // CPU super core 1
count += detectTemp(conn, "Tp04", result); // CPU super core 2
count += detectTemp(conn, "Tp08", result); // CPU super core 3
count += detectTemp(conn, "Tp0C", result); // CPU super core 4
count += detectTemp(conn, "Tp0G", result); // CPU super core 5
count += detectTemp(conn, "Tp0K", result); // CPU super core 6
count += detectTemp(conn, "Tp0O", result); // CPU performance core 1
count += detectTemp(conn, "Tp0R", result); // CPU performance core 2
count += detectTemp(conn, "Tp0U", result); // CPU performance core 3
count += detectTemp(conn, "Tp0X", result); // CPU performance core 4
count += detectTemp(conn, "Tp0a", result); // CPU performance core 5
count += detectTemp(conn, "Tp0d", result); // CPU performance core 6
count += detectTemp(conn, "Tp0g", result); // CPU performance core 7
count += detectTemp(conn, "Tp0j", result); // CPU performance core 8
count += detectTemp(conn, "Tp0m", result); // CPU performance core 9
count += detectTemp(conn, "Tp0p", result); // CPU performance core 10
count += detectTemp(conn, "Tp0u", result); // CPU performance core 11
count += detectTemp(conn, "Tp0y", result); // CPU performance core 12
break;
case FF_TEMP_GPU_INTEL:
count += detectTemp(conn, "TCGC", result); // GPU Intel Graphics
goto gpu_unknown;
@@ -463,17 +442,6 @@ const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
count += detectTemp(conn, "Tg0k", result); // GPU 8
break;
case FF_TEMP_GPU_M5X:
count += detectTemp(conn, "Tg0U", result); // GPU 1
count += detectTemp(conn, "Tg0X", result); // GPU 2
count += detectTemp(conn, "Tg0d", result); // GPU 3
count += detectTemp(conn, "Tg0g", result); // GPU 4
count += detectTemp(conn, "Tg0j", result); // GPU 5
count += detectTemp(conn, "Tg1Y", result); // GPU 6
count += detectTemp(conn, "Tg1c", result); // GPU 7
count += detectTemp(conn, "Tg1g", result); // GPU 8
break;
case FF_TEMP_BATTERY:
count += detectTemp(conn, "TB1T", result); // Battery
count += detectTemp(conn, "TB2T", result); // Battery
@@ -484,9 +452,6 @@ const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
count += detectTemp(conn, "Tm06", result); // Memory 2
count += detectTemp(conn, "Tm08", result); // Memory 3
count += detectTemp(conn, "Tm09", result); // Memory 4
count += detectTemp(conn, "Tm0p", result); // Memory Proximity 1 (M4)
count += detectTemp(conn, "Tm1p", result); // Memory Proximity 2 (M4)
count += detectTemp(conn, "Tm2p", result); // Memory Proximity 3 (M4)
break;
}
@@ -496,5 +461,5 @@ const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
*result /= count;
return nullptr;
return NULL;
}
+1 -3
View File
@@ -8,13 +8,12 @@ typedef struct FFTempValue {
double value;
} FFTempValue;
enum FFTempType: uint8_t {
enum FFTempType {
FF_TEMP_CPU_X64,
FF_TEMP_CPU_M1X,
FF_TEMP_CPU_M2X,
FF_TEMP_CPU_M3X,
FF_TEMP_CPU_M4X,
FF_TEMP_CPU_M5X,
FF_TEMP_GPU_INTEL,
FF_TEMP_GPU_AMD,
@@ -23,7 +22,6 @@ enum FFTempType: uint8_t {
FF_TEMP_GPU_M2X,
FF_TEMP_GPU_M3X,
FF_TEMP_GPU_M4X,
FF_TEMP_GPU_M5X,
FF_TEMP_BATTERY,
+1 -1
View File
@@ -10,7 +10,7 @@ typedef struct FFArgBuffer {
uint32_t length;
} FFArgBuffer;
typedef enum FFArgType: uint8_t {
typedef enum FF_A_PACKED FFArgType {
FF_ARG_TYPE_NULL = 0,
FF_ARG_TYPE_UINT,
FF_ARG_TYPE_UINT64,
+2 -2
View File
@@ -6,9 +6,9 @@
#if !__cplusplus && FF_SUPPORTS_COUNT_OF
#define ARRAY_SIZE(x) _Countof(x)
#elif __has_builtin(__is_array)
#define ARRAY_SIZE(x) ({ static_assert(__is_array(typeof(x)), "Must be an array"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
#define ARRAY_SIZE(x) ({ static_assert(__is_array(__typeof__(x)), "Must be an array"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
#elif __has_builtin(__builtin_types_compatible_p)
#define ARRAY_SIZE(x) ({ static_assert(!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), "Must not be a pointer"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
#define ARRAY_SIZE(x) ({ static_assert(!__builtin_types_compatible_p(__typeof__(x), __typeof__(&*(x))), "Must not be a pointer"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
#endif
#endif
#ifndef ARRAY_SIZE
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#ifndef __has_attribute
#error Unsupported compiler
#define __attribute__(x)
#endif
#define FF_A_FALLTHROUGH __attribute__((__fallthrough__))
#define FF_A_DEPRECATED __attribute__((__deprecated__))
#define FF_A_CLEANUP(func) __attribute__((__cleanup__(func)))
#define FF_A_NODISCARD __attribute__((__warn_unused_result__))
#define FF_A_PRINTF(formatStrIndex, argsStartIndex) __attribute__((__format__(printf, formatStrIndex, argsStartIndex)))
#define FF_A_SCANF(formatStrIndex, argsStartIndex) __attribute__((__format__(scanf, formatStrIndex, argsStartIndex)))
#define FF_A_NONNULL(argIndex, ...) __attribute__((__nonnull__(argIndex, ##__VA_ARGS__)))
#define FF_A_RETURNS_NONNULL __attribute__((__returns_nonnull__))
#define FF_A_UNUSED __attribute__((__unused__))
#define FF_A_PACKED __attribute__((__packed__))
#define FF_A_WEAK_IMPORT __attribute__((__weak_import__))
#define FF_A_ALWAYS_INLINE __attribute__((__always_inline__))
#define FF_A_COLD __attribute__((__cold__))
+1 -1
View File
@@ -11,7 +11,7 @@
* @param userdata User-provided data passed to the callback function
* @param minLength Minimum length of strings to extract
*
* @return nullptr on success, error message on failure.
* @return NULL on success, error message on failure.
* @note This function won't return an error if no strings are found.
* Always check if strings are correctly extracted after this function all.
*/
-10
View File
@@ -10,16 +10,6 @@
#define FF_COLOR_MODE_HIDDEN "8;"
#define FF_COLOR_MODE_STRIKETHROUGH "9;"
#define FF_COLOR_MODE_RESET_ "0"
#define FF_COLOR_MODE_BOLD_ "1"
#define FF_COLOR_MODE_DIM_ "2"
#define FF_COLOR_MODE_ITALIC_ "3"
#define FF_COLOR_MODE_UNDERLINE_ "4"
#define FF_COLOR_MODE_BLINK_ "5"
#define FF_COLOR_MODE_INVERSE_ "7"
#define FF_COLOR_MODE_HIDDEN_ "8"
#define FF_COLOR_MODE_STRIKETHROUGH_ "9"
#define FF_COLOR_FG_BLACK "30"
#define FF_COLOR_FG_RED "31"
#define FF_COLOR_FG_GREEN "32"
+3 -3
View File
@@ -26,7 +26,7 @@ typedef struct FFDBusData {
DBusConnection* connection;
} FFDBusData;
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data); // Returns an error message or nullptr on success
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data); // Returns an error message or NULL on success
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result);
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result);
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint64_t* result);
@@ -38,9 +38,9 @@ bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* ob
void ffDBusDestroyData(FFDBusData* data);
static inline DBusMessage* ffDBusGetAllProperties(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface) {
return ffDBusGetMethodReply(dbus, busName, objectPath, "org.freedesktop.DBus.Properties", "GetAll", interface, nullptr);
return ffDBusGetMethodReply(dbus, busName, objectPath, "org.freedesktop.DBus.Properties", "GetAll", interface, NULL);
}
#define FF_DBUS_AUTO_DESTROY_DATA [[gnu::cleanup(ffDBusDestroyData)]]
#define FF_DBUS_AUTO_DESTROY_DATA FF_A_CLEANUP(ffDBusDestroyData)
#endif // FF_HAVE_DBUS
+2 -2
View File
@@ -6,11 +6,11 @@
static inline const char* ffFindFileName(const char* file) {
const char* lastSlash = __builtin_strrchr(file, '/');
#ifdef _WIN32
if (lastSlash == nullptr) {
if (lastSlash == NULL) {
lastSlash = __builtin_strrchr(file, '\\');
}
#endif
if (lastSlash != nullptr) {
if (lastSlash != NULL) {
return lastSlash + 1;
}
return file;
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include <stdint.h>
#if __BIG_ENDIAN__
#define FF_READ_LE(x) _Generic((x), \
uint16_t: __builtin_bswap16(x), \
uint32_t: __builtin_bswap32(x), \
uint64_t: __builtin_bswap64(x))
#define FF_READ_BE(x) (x)
#else
#define FF_READ_LE(x) (x)
#define FF_READ_BE(x) _Generic((x), \
uint16_t: __builtin_bswap16(x), \
uint32_t: __builtin_bswap32(x), \
uint64_t: __builtin_bswap64(x))
#endif
+1 -2
View File
@@ -2,7 +2,7 @@
#include "common/FFstrbuf.h"
typedef enum FFDataResultDocType: uint8_t {
typedef enum FF_A_PACKED FFDataResultDocType {
FF_RESULT_DOC_TYPE_DEFAULT = 0,
FF_RESULT_DOC_TYPE_JSON,
FF_RESULT_DOC_TYPE_CONFIG,
@@ -19,5 +19,4 @@ typedef struct FFdata {
FFstrbuf genConfigPath; // Path to generate configuration file
FFDataResultDocType docType; // Type of result document
bool configLoaded;
bool genConfigInteractive; // `--gen-config` entered the interactive CUI
} FFdata;
+1 -1
View File
@@ -21,5 +21,5 @@ void ffFontInitWithSpace(FFfont* font, const char* rawName);
void ffFontDestroy(FFfont* font);
static inline void ffFontInitCopy(FFfont* font, const char* name) {
ffFontInitValues(font, name, nullptr);
ffFontInitValues(font, name, NULL);
}
-11
View File
@@ -1,11 +0,0 @@
#pragma once
#include "common/ffdata.h"
// Runs the interactive config generation CUI.
//
// On success (the user saved the config), it sets `data->structure` to the user chosen
// module structure and `instance.config.logo.type` to the user chosen logo type, creates
// the result document and returns true. The caller is expected to call `writeConfigFile`.
// Returns false if the user quits without saving.
bool ffGenConfigInteractive(FFdata* data);
-1
View File
@@ -18,7 +18,6 @@ void ffPlatformInit(FFPlatform* platform) {
#ifdef _WIN32
ffStrbufInit(&platform->sid);
platform->initCP = 0;
#endif
FFPlatformSysinfo* info = &platform->sysinfo;
+33 -46
View File
@@ -3,8 +3,7 @@
#include "common/arrutil.h"
#include "common/strutil.h"
#include "common/io.h"
#include "common/path.h"
#include "common/mallocHelper.h"
#include "fastfetch_config.h"
#include <unistd.h>
#include <pwd.h>
@@ -20,6 +19,7 @@
#elif defined(__OpenBSD__)
#include <sys/sysctl.h>
#include <sys/stat.h>
#include <kvm.h>
#include "common/path.h"
#elif defined(__HAIKU__)
#include <image.h>
@@ -57,7 +57,7 @@ static void getExePath(FFPlatform* platform) {
4,
exePath,
&exePathLen,
nullptr,
NULL,
0) < 0)
exePathLen = 0;
else {
@@ -68,17 +68,16 @@ static void getExePath(FFPlatform* platform) {
// Current implementation uses argv[0], which can be easily spoofed.
// See #2195
size_t exePathLen = 0;
{
char argvBuf[ARG_MAX];
size_t argvSize = sizeof(argvBuf);
int argvMib[] = { CTL_KERN, KERN_PROC_ARGS, (pid_t) platform->pid, KERN_PROC_ARGV };
if (sysctl(argvMib, ARRAY_SIZE(argvMib), argvBuf, &argvSize, nullptr, 0) == 0) {
// The buffer is filled with an array of char pointers followed by the strings themselves
char** argv = (char**) argvBuf;
if (argv[0] && (char*) argv[0] >= argvBuf && (char*) argv[0] < argvBuf + argvSize) {
kvm_t* kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
if (kd) {
int kpCount;
struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
if (kp && kpCount == 1) {
char** argv = kvm_getargv(kd, kp, 0);
if (argv && argv[0]) {
char* arg0 = argv[0];
if (arg0[0]) {
if (strchr(arg0, '/') != nullptr) // likely a path (absolute or relative)
if (strchr(arg0, '/') != NULL) // likely a path (absolute or relative)
{
exePathLen = strlen(arg0);
if (exePathLen < ARRAY_SIZE(exePath)) {
@@ -89,7 +88,7 @@ static void getExePath(FFPlatform* platform) {
}
} else {
FF_STRBUF_AUTO_DESTROY tmpPath = ffStrbufCreate();
if (ffFindExecutableInPath(arg0, &tmpPath) == nullptr && tmpPath.length < ARRAY_SIZE(exePath)) {
if (ffFindExecutableInPath(arg0, &tmpPath) == NULL && tmpPath.length < ARRAY_SIZE(exePath)) {
memcpy(exePath, tmpPath.chars, tmpPath.length + 1);
exePathLen = tmpPath.length;
}
@@ -98,38 +97,25 @@ static void getExePath(FFPlatform* platform) {
if (exePathLen > 0) {
struct stat st;
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode)) {
// Replicate kvm_getfiles()'s live path: {CTL_KERN, KERN_FILE, KERN_FILE_BYPID, pid, esize, count}
int fileMib[6] = { CTL_KERN, KERN_FILE, KERN_FILE_BYPID, (pid_t) platform->pid, (int) sizeof(struct kinfo_file), 0 };
size_t fileSize = 0;
if (sysctl(fileMib, ARRAY_SIZE(fileMib), nullptr, &fileSize, nullptr, 0) == 0) {
fileSize += fileSize / 8; // add ~10%
FF_AUTO_FREE struct kinfo_file* kf = (struct kinfo_file*) malloc(fileSize);
if (kf) {
int rv;
do {
fileMib[5] = (int) (fileSize / sizeof(struct kinfo_file));
rv = sysctl(fileMib, ARRAY_SIZE(fileMib), kf, &fileSize, nullptr, 0);
} while (rv == -1 && errno == ENOMEM);
if (rv == 0) {
int cntp = (int) (fileSize / sizeof(struct kinfo_file));
int i;
for (i = 0; i < cntp; i++) {
if (kf[i].fd_fd == KERN_FILE_TEXT) {
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
i = -1;
}
break;
}
}
if (i < 0) {
exePathLen = 0;
int cntp;
struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, (pid_t) platform->pid, sizeof(*kf), &cntp);
if (kf) {
int i;
for (i = 0; i < cntp; i++) {
if (kf[i].fd_fd == KERN_FILE_TEXT) {
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
i = -1;
}
break;
}
// If we can't get the list of open files, we can't verify that the file is actually the executable
// Assume it is
}
if (i < 0) {
exePathLen = 0;
}
} else {
// If we can't get the list of open files, we can't verify that the file is actually the executable
// Assume it is
}
} else {
exePathLen = 0;
@@ -138,6 +124,7 @@ static void getExePath(FFPlatform* platform) {
}
}
}
kvm_close(kd);
}
#elif defined(__sun)
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
@@ -251,8 +238,8 @@ static void getDataDirs(FFPlatform* platform) {
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS");
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
ffPlatformPathAddAbsolute(&platform->dataDirs, FF_PATH_PKG_BASE "/share/");
#ifdef _PATH_LOCALBASE
ffPlatformPathAddAbsolute(&platform->dataDirs, _PATH_LOCALBASE "/share/");
#endif
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/local/share/");
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/share/");
@@ -295,7 +282,7 @@ static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) {
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
size_t length = sizeof(info->pageSize);
sysctl((int[]) { CTL_HW, HW_PAGESIZE }, 2, &info->pageSize, &length, nullptr, 0);
sysctl((int[]) { CTL_HW, HW_PAGESIZE }, 2, &info->pageSize, &length, NULL, 0);
#else
info->pageSize = (uint32_t) sysconf(_SC_PAGESIZE);
#endif
@@ -303,7 +290,7 @@ static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) {
static void getCwd(FFPlatform* platform) {
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != nullptr) {
if (getcwd(cwd, sizeof(cwd)) != NULL) {
ffStrbufSetS(&platform->cwd, cwd);
ffStrbufEnsureEndsWithC(&platform->cwd, '/');
}
+18 -18
View File
@@ -21,10 +21,10 @@ static void getExePath(FFPlatform* platform) {
ffGetPeb()->ProcessParameters->ImagePathName.Buffer,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
nullptr);
NULL);
if (hPath != INVALID_HANDLE_VALUE) {
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_NORMALIZED);
if (len > 0 && len < MAX_PATH) {
@@ -44,8 +44,8 @@ static void getExePath(FFPlatform* platform) {
}
static void getHomeDir(FFPlatform* platform) {
PWSTR pPath = nullptr;
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, nullptr, &pPath))) {
PWSTR pPath = NULL;
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pPath))) {
ffStrbufSetWS(&platform->homeDir, pPath);
ffStrbufReplaceAllC(&platform->homeDir, '\\', '/');
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
@@ -58,8 +58,8 @@ static void getHomeDir(FFPlatform* platform) {
}
static void getCacheDir(FFPlatform* platform) {
PWSTR pPath = nullptr;
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &pPath))) {
PWSTR pPath = NULL;
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &pPath))) {
ffStrbufSetWS(&platform->cacheDir, pPath);
ffStrbufReplaceAllC(&platform->cacheDir, '\\', '/');
ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
@@ -71,8 +71,8 @@ static void getCacheDir(FFPlatform* platform) {
}
static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId) {
PWSTR pPath = nullptr;
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, nullptr, &pPath))) {
PWSTR pPath = NULL;
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath))) {
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath);
CoTaskMemFree(pPath);
ffStrbufReplaceAllC(&buffer, '\\', '/');
@@ -107,7 +107,7 @@ static void getConfigDirs(FFPlatform* platform) {
if (getenv("MSYSTEM")) {
// We are in MSYS2 / Git Bash
platformPathAddEnvSuffix(&platform->configDirs, "HOME", ".config/");
platformPathAddEnvSuffix(&platform->configDirs, "HOME", nullptr);
platformPathAddEnvSuffix(&platform->configDirs, "HOME", NULL);
platformPathAddEnvSuffix(&platform->configDirs, "MINGW_PREFIX", "etc");
}
@@ -122,7 +122,7 @@ static void getDataDirs(FFPlatform* platform) {
if (getenv("MSYSTEM") && getenv("HOME")) {
// We are in MSYS2 / Git Bash
platformPathAddEnvSuffix(&platform->dataDirs, "HOME", ".local/share/");
platformPathAddEnvSuffix(&platform->dataDirs, "HOME", nullptr);
platformPathAddEnvSuffix(&platform->dataDirs, "HOME", NULL);
platformPathAddEnvSuffix(&platform->dataDirs, "MINGW_PREFIX", "share");
}
ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/");
@@ -142,8 +142,8 @@ static void getUserName(FFPlatform* platform) {
NTSYSAPI NTSTATUS NTAPI LsaGetUserName(
_Outptr_ PLSA_UNICODE_STRING * UserName,
_Outptr_opt_ PLSA_UNICODE_STRING * DomainName);
PLSA_UNICODE_STRING userName = nullptr;
if (NT_SUCCESS(LsaGetUserName(&userName, nullptr))) {
PLSA_UNICODE_STRING userName = NULL;
if (NT_SUCCESS(LsaGetUserName(&userName, NULL))) {
ffStrbufSetNWS(&platform->userName, userName->Length / sizeof(wchar_t), userName->Buffer);
RtlFreeUnicodeString(userName); // Required. userName.Buffer is allocated separately
LsaFreeMemory(userName);
@@ -187,18 +187,18 @@ static const char* detectWine(void) {
const char* __cdecl wine_get_version(void);
void* hntdll = ffLibraryGetModule(L"ntdll.dll");
if (!hntdll) {
return nullptr;
return NULL;
}
FF_LIBRARY_LOAD_SYMBOL_LAZY(hntdll, wine_get_version);
if (!ffwine_get_version) {
return nullptr;
return NULL;
}
return ffwine_get_version();
}
static void getSystemReleaseAndVersion(FFPlatformSysinfo* info) {
FF_AUTO_CLOSE_FD HANDLE hKey = nullptr;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, nullptr)) {
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL)) {
return;
}
@@ -207,7 +207,7 @@ static void getSystemReleaseAndVersion(FFPlatformSysinfo* info) {
FF_ARG(ubr, L"UBR"),
FF_ARG(info->version, L"BuildLabEx"),
},
nullptr);
NULL);
PPEB_FULL peb = ffGetPeb();
@@ -228,7 +228,7 @@ static void getSystemReleaseAndVersion(FFPlatformSysinfo* info) {
static void getSystemPageSize(FFPlatformSysinfo* info) {
SYSTEM_BASIC_INFORMATION sbi;
if (NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), nullptr))) {
if (NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL))) {
info->pageSize = sbi.PhysicalPageSize;
} else {
info->pageSize = 4096;
+20 -19
View File
@@ -21,9 +21,9 @@ void ffStrbufInitA(FFstrbuf* strbuf, uint32_t allocate) {
}
void ffStrbufInitVF(FFstrbuf* strbuf, const char* format, va_list arguments) {
assert(format != nullptr);
assert(format != NULL);
char* buffer = nullptr;
char* buffer = NULL;
int len = vasprintf(&buffer, format, arguments);
assert(len >= 0);
@@ -33,7 +33,7 @@ void ffStrbufInitVF(FFstrbuf* strbuf, const char* format, va_list arguments) {
// Takes ownership of `heapStr`. The caller must not free `heapStr` after calling this
// function; the memory will be managed and freed via the associated FFstrbuf.
void ffStrbufInitMoveNS(FFstrbuf* strbuf, uint32_t length, char* heapStr) {
assert(heapStr != nullptr);
assert(heapStr != NULL);
strbuf->length = length;
size_t allocSize = ffMallocUsableSize(heapStr);
@@ -83,7 +83,6 @@ void ffStrbufEnsureFreeNoCheck(FFstrbuf* strbuf, uint32_t free) {
// Round up to the next power of 2.
// If the value is already a power of 2, it will be rounded up to the next power of 2.
// allocate = __builtin_stdc_bit_ceil(allocate + 1); // Currently generates worse asm code
allocate = 1U << (32 - __builtin_clz(allocate));
}
@@ -139,7 +138,7 @@ void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free) {
}
void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int (*transformFunc)(int)) {
if (value == nullptr) {
if (value == NULL) {
return;
}
@@ -157,7 +156,7 @@ void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int (*transfo
}
void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments) {
assert(format != nullptr);
assert(format != NULL);
va_list copy;
va_copy(copy, arguments);
@@ -178,12 +177,12 @@ void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments) {
}
const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until) {
if (value == nullptr) {
return nullptr;
if (value == NULL) {
return NULL;
}
const char* end = strchr(value, until);
if (end == nullptr) {
if (end == NULL) {
ffStrbufAppendS(strbuf, value);
} else {
ffStrbufAppendNS(strbuf, (uint32_t) (end - value), value);
@@ -192,7 +191,7 @@ const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char unti
}
void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...) {
assert(format != nullptr);
assert(format != NULL);
va_list arguments;
va_start(arguments, format);
@@ -209,7 +208,7 @@ void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...) {
}
void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...) {
assert(format != nullptr);
assert(format != NULL);
va_list arguments;
va_start(arguments, format);
@@ -218,7 +217,7 @@ void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...) {
}
void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value) {
if (value == nullptr || length == 0) {
if (value == NULL || length == 0) {
return;
}
@@ -236,14 +235,14 @@ void ffStrbufPrependC(FFstrbuf* strbuf, char c) {
}
void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value) {
assert(strbuf != nullptr);
assert(strbuf != NULL);
if (length == 0) {
ffStrbufClear(strbuf);
return;
}
assert(value != nullptr);
assert(value != NULL);
if (strbuf->allocated <= length) {
char* newBuf = malloc(sizeof(char) * (length + 1));
@@ -433,7 +432,9 @@ bool ffStrbufSubstrBefore(FFstrbuf* strbuf, uint32_t index) {
if (strbuf->allocated == 0) {
// static string
ffStrbufInitNS(strbuf, index, strbuf->chars);
if (index < strbuf->length) {
ffStrbufInitNS(strbuf, index, strbuf->chars);
}
return true;
}
@@ -554,7 +555,7 @@ void ffStrbufAppendSInt(FFstrbuf* strbuf, int64_t value) {
unsafe_yyjson_set_sint(&val, value);
char* end = yyjson_write_number(&val, start);
assert(end != nullptr);
assert(end != NULL);
strbuf->length += (uint32_t) (end - start);
}
@@ -567,7 +568,7 @@ void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value) {
unsafe_yyjson_set_uint(&val, value);
char* end = yyjson_write_number(&val, start);
assert(end != nullptr);
assert(end != NULL);
strbuf->length += (uint32_t) (end - start);
}
@@ -734,7 +735,7 @@ bool ffStrbufMatchSeparatedNS(const FFstrbuf* strbuf, uint32_t compLength, const
for (const char* p = comp; p < comp + compLength;) {
const char* colon = memchr(p, separator, (size_t) (comp + compLength - p));
if (colon == nullptr) {
if (colon == NULL) {
uint32_t remainingLen = (uint32_t) (comp + compLength - p);
return strbuf->length == remainingLen && memcmp(strbuf->chars, p, remainingLen) == 0;
}
@@ -762,7 +763,7 @@ bool ffStrbufMatchSeparatedIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength
for (const char* p = comp; p < comp + compLength;) {
const char* colon = memchr(p, separator, (size_t) (comp + compLength - p));
if (colon == nullptr) {
if (colon == NULL) {
uint32_t remainingLen = (uint32_t) (comp + compLength - p);
return strbuf->length == remainingLen && strncasecmp(strbuf->chars, p, remainingLen) == 0;
}
+6 -3
View File
@@ -1,5 +1,4 @@
#include "common/base64.h"
#include "common/endian.h"
// https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145
void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
@@ -8,8 +7,12 @@ void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char*
const char* ends = str + (size - size % 3);
while (str != ends) {
uint32_t n = *(uint32_t*) str;
// The 3 input bytes must be laid out big-endian (str[0] in the most significant position).
n = FF_READ_BE(n);
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
// The 3 input bytes must be laid out big-endian (str[0] in the most
// significant position). On little-endian hosts swap; on big-endian
// hosts the word is already in the right order.
n = __builtin_bswap32(n);
#endif
*out++ = chars[(n >> 26) & 63];
*out++ = chars[(n >> 20) & 63];
*out++ = chars[(n >> 14) & 63];
+11 -11
View File
@@ -20,7 +20,7 @@ typedef struct {
static inline void wrapMunmap(FFMemoryMapping* mapping) {
assert(mapping);
if (mapping->data == nullptr || mapping->data == MAP_FAILED) {
if (mapping->data == NULL || mapping->data == MAP_FAILED) {
return;
}
munmap((void*) mapping->data, mapping->length);
@@ -31,12 +31,12 @@ static inline void wrapMunmap(FFMemoryMapping* mapping) {
*/
static inline const void* readData(const FFMemoryMapping* mapping, size_t size, off_t offset) {
if (offset < 0) {
return nullptr;
return NULL;
}
size_t start = (size_t) offset;
if (start > mapping->length || size > mapping->length - start) {
return nullptr;
return NULL;
}
return mapping->data + start;
@@ -99,7 +99,7 @@ static bool handleMachSection(const FFMemoryMapping* mapping, const char* name,
* @param userdata User data for the callback
* @param minLength Minimum string length to extract
*
* @return nullptr on success, error message on failure
* @return NULL on success, error message on failure
*/
static const char* dumpMachHeader(const FFMemoryMapping* mapping, off_t offset, bool is_64, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
uint32_t ncmds;
@@ -124,7 +124,7 @@ static const char* dumpMachHeader(const FFMemoryMapping* mapping, off_t offset,
}
off_t commandOffset = loadCommandsOffset;
const struct load_command* cmd = nullptr;
const struct load_command* cmd = NULL;
for (uint32_t i = 0U; i < ncmds; i++, commandOffset += cmd->cmdsize) {
cmd = readData(mapping, sizeof(*cmd), commandOffset);
if (!cmd) {
@@ -153,7 +153,7 @@ static const char* dumpMachHeader(const FFMemoryMapping* mapping, off_t offset,
}
if (!handleMachSection(mapping, section->sectname, (off_t) section->offset, (size_t) section->size, cb, userdata, minLength)) {
return nullptr;
return NULL;
}
}
} else if (cmd->cmd == LC_SEGMENT) {
@@ -174,13 +174,13 @@ static const char* dumpMachHeader(const FFMemoryMapping* mapping, off_t offset,
}
if (!handleMachSection(mapping, section->sectname, (off_t) section->offset, (size_t) section->size, cb, userdata, minLength)) {
return nullptr;
return NULL;
}
}
}
}
return nullptr;
return NULL;
}
/**
@@ -195,7 +195,7 @@ static const char* dumpMachHeader(const FFMemoryMapping* mapping, off_t offset,
* @param userdata User data for the callback
* @param minLength Minimum string length to extract
*
* @return nullptr on success, error message on failure
* @return NULL on success, error message on failure
*/
static const char* dumpFatHeader(const FFMemoryMapping* mapping, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
const struct fat_header* headerRaw = readData(mapping, sizeof(struct fat_header), 0);
@@ -273,8 +273,8 @@ const char* ffBinaryExtractStrings(const char* machoFile, bool (*cb)(const char*
return "Failed to stat file";
}
[[gnu::cleanup(wrapMunmap)]] FFMemoryMapping mapping = {
.data = mmap(nullptr, (size_t) st.st_size, PROT_READ, MAP_PRIVATE, fd, 0),
FF_A_CLEANUP(wrapMunmap) FFMemoryMapping mapping = {
.data = mmap(NULL, (size_t) st.st_size, PROT_READ, MAP_PRIVATE, fd, 0),
.length = (size_t) st.st_size,
};
if (mapping.data == MAP_FAILED) {
+13 -13
View File
@@ -56,10 +56,10 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_strptr)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_end)
libelf = nullptr;
libelf = NULL;
}
if (elfData.ffelf_end == nullptr) {
if (elfData.ffelf_end == NULL) {
return "load libelf failed";
}
@@ -69,8 +69,8 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
return "open() failed";
}
Elf* elf = elfData.ffelf_begin(fd, ELF_C_READ, nullptr);
if (elf == nullptr) {
Elf* elf = elfData.ffelf_begin(fd, ELF_C_READ, NULL);
if (elf == NULL) {
return "elf_begin() failed";
}
@@ -82,27 +82,27 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
}
// Iterate through all sections, looking for .rodata which contains string literals
Elf_Scn* scn = nullptr;
while ((scn = elfData.ffelf_nextscn(elf, scn)) != nullptr) {
Elf_Scn* scn = NULL;
while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL) {
// Try 64-bit section header first, then 32-bit if that fails
Elf64_Shdr* shdr64 = elfData.ffelf64_getshdr(scn);
Elf32_Shdr* shdr32 = nullptr;
if (shdr64 == nullptr) {
Elf32_Shdr* shdr32 = NULL;
if (shdr64 == NULL) {
shdr32 = elfData.ffelf32_getshdr(scn);
if (shdr32 == nullptr) {
if (shdr32 == NULL) {
continue;
}
}
// Get the section name and check if it's .rodata
const char* name = elfData.ffelf_strptr(elf, shstrndx, shdr64 ? shdr64->sh_name : shdr32->sh_name);
if (name == nullptr || !ffStrEquals(name, ".rodata")) {
if (name == NULL || !ffStrEquals(name, ".rodata")) {
continue;
}
// Get the section data
Elf_Data* data = elfData.ffelf_getdata(scn, nullptr);
if (data == nullptr) {
Elf_Data* data = elfData.ffelf_getdata(scn, NULL);
if (data == NULL) {
continue;
}
@@ -131,7 +131,7 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
}
elfData.ffelf_end(elf);
return nullptr;
return NULL;
}
#else
+6 -6
View File
@@ -15,19 +15,19 @@
* Each string found is passed to the callback function for processing.
*/
const char* ffBinaryExtractStrings(const char* peFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileA(peFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileA(peFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return "CreateFileA() failed";
}
FF_AUTO_CLOSE_FD HANDLE hSection = nullptr;
if (!NT_SUCCESS(NtCreateSection(&hSection, SECTION_MAP_READ, nullptr, nullptr, PAGE_READONLY, SEC_COMMIT, hFile))) {
FF_AUTO_CLOSE_FD HANDLE hSection = NULL;
if (!NT_SUCCESS(NtCreateSection(&hSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, SEC_COMMIT, hFile))) {
return "NtCreateSection() failed";
}
PVOID base = nullptr;
PVOID base = NULL;
SIZE_T viewSize = 0;
if (!NT_SUCCESS(NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, nullptr, &viewSize, ViewUnmap, 0, PAGE_READONLY))) {
if (!NT_SUCCESS(NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READONLY))) {
return "NtMapViewOfSection() failed";
}
@@ -67,5 +67,5 @@ const char* ffBinaryExtractStrings(const char* peFile, bool (*cb)(const char* st
}
NtUnmapViewOfSection(NtCurrentProcess(), base);
return nullptr;
return NULL;
}
+34 -73
View File
@@ -9,18 +9,19 @@
#include <ctype.h>
#include <inttypes.h>
#include <stdalign.h>
bool ffParseModuleOptions(const char* key, const char* value) {
if (!ffStrStartsWith(key, "--") || !ffCharIsEnglishAlphabet(key[2])) {
return false;
}
if (value && !*value) {
value = nullptr;
value = NULL;
}
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(key[2]) - 'A']; *modules; ++modules) {
FFModuleBaseInfo* baseInfo = *modules;
const char* subKey = ffOptionTestPrefix(key, baseInfo->name);
if (subKey != nullptr) {
if (subKey != NULL) {
if (subKey[0] == '\0' || subKey[0] == '-') // Key is exactly the module name or has a leading '-'
{
fprintf(stderr, "Error: unknown module key %s\n", key);
@@ -64,74 +65,63 @@ bool ffParseModuleOptions(const char* key, const char* value) {
}
void ffPrepareCommandOption(FFdata* data) {
char* moduleType = nullptr;
char* moduleType = NULL;
size_t moduleLen = 0;
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (moduleLen == strlen(moduleNameConstant) && ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
switch (moduleType[0]) {
#if !FF_MODULE_DISABLE_CPUUSAGE
#if !FF_MODULE_DISABLE_CPUUSAGE
case 'C':
case 'c':
FF_IF_MODULE_MATCH(ffCPUUsageModuleInfo.name)
FF_IF_MODULE_MATCH(FF_CPUUSAGE_MODULE_NAME)
ffPrepareCPUUsage();
break;
#endif
#endif
#if !FF_MODULE_DISABLE_DISKIO
#if !FF_MODULE_DISABLE_DISKIO
case 'D':
case 'd':
FF_IF_MODULE_MATCH(ffDiskIOModuleInfo.name) {
[[gnu::cleanup(ffDestroyDiskIOOptions)]] FFDiskIOOptions options;
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyDiskIOOptions) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
ffPrepareDiskIO(&options);
}
break;
#endif
#endif
#if !FF_MODULE_DISABLE_NETIO
#if !FF_MODULE_DISABLE_NETIO
case 'N':
case 'n':
FF_IF_MODULE_MATCH(ffNetIOModuleInfo.name) {
[[gnu::cleanup(ffDestroyNetIOOptions)]] FFNetIOOptions options;
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyNetIOOptions) FFNetIOOptions options;
ffInitNetIOOptions(&options);
ffPrepareNetIO(&options);
}
break;
#endif
#endif
#if !FF_MODULE_DISABLE_PUBLICIP
#if !FF_MODULE_DISABLE_PUBLICIP
case 'P':
case 'p':
FF_IF_MODULE_MATCH(ffPublicIPModuleInfo.name) {
[[gnu::cleanup(ffDestroyPublicIpOptions)]] FFPublicIPOptions options;
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyPublicIpOptions) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
ffPreparePublicIp(&options);
}
break;
#endif
#endif
#if !FF_MODULE_DISABLE_TOP
case 'T':
case 't':
FF_IF_MODULE_MATCH(ffTopModuleInfo.name) {
[[gnu::cleanup(ffDestroyTopOptions)]] FFTopOptions options;
ffInitTopOptions(&options);
ffPrepareTopProcesses(options.showTypes);
}
break;
#endif
#if !FF_MODULE_DISABLE_WEATHER
#if !FF_MODULE_DISABLE_WEATHER
case 'W':
case 'w':
FF_IF_MODULE_MATCH(ffWeatherModuleInfo.name) {
[[gnu::cleanup(ffDestroyWeatherOptions)]] FFWeatherOptions options;
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME) {
FF_A_CLEANUP(ffDestroyWeatherOptions) FFWeatherOptions options;
ffInitWeatherOptions(&options);
ffPrepareWeather(&options);
}
break;
#endif
#endif
}
#undef FF_IF_MODULE_MATCH
@@ -178,34 +168,6 @@ static void genJsonResult(FFdata* data, FFModuleBaseInfo* baseInfo, void* option
}
}
static yyjson_val* findStructureModuleConfig(FFdata* data, const char* moduleType) {
if (data->configDoc == nullptr) {
return nullptr;
}
yyjson_val* root = yyjson_doc_get_root(data->configDoc);
if (root == nullptr) {
return nullptr;
}
yyjson_val* modules = yyjson_obj_get(root, "modules");
if (!yyjson_is_arr(modules)) {
return nullptr;
}
yyjson_val* item;
size_t idx, max;
yyjson_arr_foreach (modules, idx, max, item) {
if (yyjson_is_obj(item)) {
const char* type = yyjson_get_str(yyjson_obj_get(item, "type"));
if (type != nullptr && ffStrEqualsIgnCase(type, moduleType)) {
return item;
}
}
}
return nullptr;
}
static bool parseStructureCommand(
FFdata* data,
const char* line,
@@ -216,16 +178,10 @@ static bool parseStructureCommand(
if (ffStrEqualsIgnCase(line, baseInfo->name)) {
alignas(uint64_t) uint8_t optionBuf[FF_OPTION_MAX_SIZE];
baseInfo->initOptions(optionBuf);
if (data->resultDoc != nullptr) {
if (data->resultDoc != NULL) {
fn(data, baseInfo, optionBuf);
} else {
yyjson_val* configModule = findStructureModuleConfig(data, baseInfo->name);
if (configModule != nullptr) {
baseInfo->parseJsonObject(optionBuf, configModule);
baseInfo->printModule(optionBuf);
} else {
baseInfo->printModule(optionBuf);
}
baseInfo->printModule(optionBuf);
}
baseInfo->destroyOptions(optionBuf);
return true;
@@ -239,7 +195,7 @@ static bool parseStructureCommand(
yyjson_mut_obj_add_str(doc, module, "type", line);
yyjson_mut_obj_add_str(doc, module, "error", "Unknown module type");
} else {
ffPrintError(line, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
ffPrintError(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
}
return false;
}
@@ -248,7 +204,7 @@ void ffPrintCommandOption(FFdata* data) {
// Parse the structure and call the modules
int32_t thres = instance.config.display.stat;
char* moduleType = nullptr;
char* moduleType = NULL;
size_t moduleLen = 0;
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':')) {
@@ -289,7 +245,12 @@ void ffPrintCommandOption(FFdata* data) {
}
void ffMigrateCommandOptionToJsonc(FFdata* data) {
char* moduleType = nullptr;
// If we don't have a custom structure, use the default one
if (data->structure.length == 0) {
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
}
char* moduleType = NULL;
size_t moduleLen = 0;
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':')) {
+17 -17
View File
@@ -19,7 +19,7 @@ static bool loadLibSymbols(FFDBusLibrary* lib) {
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_unref, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_send_with_reply_and_block, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_unref, false)
dbus = nullptr; // don't auto dlclose
dbus = NULL; // don't auto dlclose
return true;
}
@@ -33,27 +33,27 @@ static const FFDBusLibrary* loadLib(void) {
loadSuccess = loadLibSymbols(&lib);
}
return loadSuccess ? &lib : nullptr;
return loadSuccess ? &lib : NULL;
}
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data) {
data->lib = loadLib();
if (data->lib == nullptr) {
if (data->lib == NULL) {
return "Failed to load DBus library";
}
data->connection = data->lib->ffdbus_bus_get(busType, nullptr);
if (data->connection == nullptr) {
data->connection = data->lib->ffdbus_bus_get(busType, NULL);
if (data->connection == NULL) {
return "Failed to connect to DBus";
}
return nullptr;
return NULL;
}
void ffDBusDestroyData(FFDBusData* data) {
if (data->connection != nullptr) {
if (data->connection != NULL) {
data->lib->ffdbus_connection_unref(data->connection);
data->connection = nullptr;
data->connection = NULL;
}
}
@@ -61,7 +61,7 @@ bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result)
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
if (argType == DBUS_TYPE_STRING || argType == DBUS_TYPE_OBJECT_PATH) {
const char* value = nullptr;
const char* value = NULL;
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
if (!ffStrSet(value)) {
@@ -254,8 +254,8 @@ bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int64_t* result) {
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg1, const char* arg2) {
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, interface, method);
if (message == nullptr) {
return nullptr;
if (message == NULL) {
return NULL;
}
if (arg1) {
@@ -266,7 +266,7 @@ DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const c
}
}
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, nullptr);
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL);
dbus->lib->ffdbus_message_unref(message);
@@ -275,8 +275,8 @@ DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const c
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property) {
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, "org.freedesktop.DBus.Properties", "Get");
if (message == nullptr) {
return nullptr;
if (message == NULL) {
return NULL;
}
dbus->lib->ffdbus_message_append_args(message,
@@ -286,7 +286,7 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char
&property,
DBUS_TYPE_INVALID);
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, nullptr);
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL);
dbus->lib->ffdbus_message_unref(message);
@@ -295,7 +295,7 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result) {
DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property);
if (reply == nullptr) {
if (reply == NULL) {
return false;
}
@@ -314,7 +314,7 @@ bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char*
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint64_t* result) {
DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property);
if (reply == nullptr) {
if (reply == NULL) {
return false;
}
+2 -2
View File
@@ -10,12 +10,12 @@ const char* ffDebugWin32Error(DWORD errorCode) {
wchar_t bufferW[256];
ULONG len = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
NULL,
(DWORD) errorCode,
0,
bufferW,
ARRAY_SIZE(bufferW),
nullptr);
NULL);
if (len == 0) {
snprintf(buffer, sizeof(buffer), "Unknown error code (%lu)", errorCode);
+7 -7
View File
@@ -16,7 +16,7 @@ void ffFontInit(FFfont* font) {
}
static void strbufAppendNSExcludingC(FFstrbuf* strbuf, uint32_t length, const char* value, char exclude) {
if (value == nullptr || length == 0) {
if (value == NULL || length == 0) {
return;
}
@@ -151,7 +151,7 @@ static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* altern
ffStrStartsWithIgnCase(wordStart, "Black") ||
ffStrStartsWithIgnCase(wordStart, "Condensed") ||
ffStrStartsWithIgnCase(wordStart, "Expanded")) {
if (alternativeBuffer == nullptr) {
if (alternativeBuffer == NULL) {
alternativeBuffer = FF_LIST_ADD(FFstrbuf, font->styles);
ffStrbufInit(alternativeBuffer);
}
@@ -169,7 +169,7 @@ static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* altern
return;
}
if (alternativeBuffer != nullptr) {
if (alternativeBuffer != NULL) {
strbufAppendNSExcludingC(alternativeBuffer, wordLength, wordStart, '-');
return;
}
@@ -184,7 +184,7 @@ void ffFontInitPango(FFfont* font, const char* data) {
ffFontInit(font);
while (*data != '\0' && *data != '`' && *data != '\\') {
fontPangoParseWord(&data, font, nullptr);
fontPangoParseWord(&data, font, NULL);
}
fontInitPretty(font);
@@ -329,8 +329,8 @@ void ffFontInitXft(FFfont* font, const char* xft) {
}
// Try parse trailing "-<number>" as size, otherwise entire head is name
const char* dashPos = nullptr;
const char* sizeStart = nullptr;
const char* dashPos = NULL;
const char* sizeStart = NULL;
for (const char* q = headEnd; q > headStart;) {
--q;
@@ -488,7 +488,7 @@ void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf
void ffFontInitWithSpace(FFfont* font, const char* rawName) {
const char* pspace = strrchr(rawName, ' ');
if (pspace == nullptr) {
if (pspace == NULL) {
ffFontInitCopy(font, rawName);
return;
}
+19 -33
View File
@@ -78,7 +78,7 @@ static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs,
}
if (firstChar >= '0' && firstChar <= '9') {
char* pEnd = nullptr;
char* pEnd = NULL;
uint32_t result = (uint32_t) strtoul(placeholderValue, &pEnd, 10);
if (result > numArgs) {
return UINT32_MAX;
@@ -109,24 +109,10 @@ static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start,
}
static inline bool formatArgSet(const FFformatarg* arg) {
// clang-format off
return arg->value != nullptr && (
(arg->type == FF_ARG_TYPE_DOUBLE && *(double*) arg->value > 0.0) ||
(arg->type == FF_ARG_TYPE_FLOAT && *(float*) arg->value > 0.0) ||
(arg->type == FF_ARG_TYPE_INT && *(int32_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*) arg->value)->length > 0) ||
(arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*) arg->value)) ||
(arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT && *(uint32_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT64 && *(uint64_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_BOOL && *(bool*) arg->value) ||
(arg->type == FF_ARG_TYPE_LIST && ((FFlist*) arg->value)->length > 0)
);
// clang-format on
return arg->value != NULL && ((arg->type == FF_ARG_TYPE_DOUBLE && *(double*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_FLOAT && *(float*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_INT && *(int32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*) arg->value)->length > 0) || (arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*) arg->value)) || (arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT && *(uint32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT64 && *(uint64_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_BOOL && *(bool*) arg->value) || (arg->type == FF_ARG_TYPE_LIST && ((FFlist*) arg->value)->length > 0));
}
[[maybe_unused]] static inline void normalizeArgName(FFstrbuf* dst, const char* src) {
FF_A_UNUSED static inline void normalizeArgName(FFstrbuf* dst, const char* src) {
ffStrbufClear(dst);
bool flag = false;
for (const char* p = src; *p; ++p) {
@@ -145,7 +131,7 @@ static inline bool formatArgSet(const FFformatarg* arg) {
#include "common/lua.h"
static void appendLuaError(FFstrbuf* buffer, const char* prefix, lua_State* L) {
const char* err = lua_tolstring(L, -1, nullptr);
const char* err = lua_tolstring(L, -1, NULL);
if (err) {
const char* tmp = strchr(err, ':');
if (tmp) {
@@ -242,12 +228,12 @@ static bool parseLuaString(FFstrbuf* buffer, const char* script, uint32_t script
ffStrbufAppendS(buffer, "Lua result error: no result");
} else {
// Convert first result to string
const char* res = lua_tolstring(L, 1, nullptr);
const char* res = lua_tolstring(L, 1, NULL);
if (res) {
ffStrbufAppendS(buffer, res);
} else {
luaL_tolstring(L, 1, nullptr);
const char* sval = lua_tolstring(L, -1, nullptr);
luaL_tolstring(L, 1, NULL);
const char* sval = lua_tolstring(L, -1, NULL);
if (sval) {
ffStrbufAppendS(buffer, sval);
}
@@ -287,10 +273,10 @@ struct FFQuickJSData {
static const char* loadQuickJSState(void) {
if (qjsData.inited) {
if (qjsData.ctx == nullptr) {
if (qjsData.ctx == NULL) {
return "QuickJS is not available";
}
return nullptr;
return NULL;
}
qjsData.inited = true;
@@ -315,20 +301,20 @@ static const char* loadQuickJSState(void) {
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libqjs, qjsData, JS_FreeValue)
qjsData.rt = qjsData.ffJS_NewRuntime();
if (qjsData.rt == nullptr) {
if (qjsData.rt == NULL) {
return "JS_NewRuntime() failed";
}
qjsData.ctx = qjsData.ffJS_NewContext(qjsData.rt);
if (qjsData.ctx == nullptr) {
if (qjsData.ctx == NULL) {
qjsData.ffJS_FreeRuntime(qjsData.rt);
qjsData.rt = nullptr;
qjsData.rt = NULL;
return "JS_NewContext() failed";
}
libqjs = nullptr; // don't close quickjs
libqjs = NULL; // don't close quickjs
return nullptr;
return NULL;
}
static bool parseQuickJSString(FFstrbuf* buffer, const char* script, uint32_t scriptLen, uint32_t numArgs, const FFformatarg* arguments) {
@@ -415,7 +401,7 @@ static bool parseQuickJSString(FFstrbuf* buffer, const char* script, uint32_t sc
bool ret = false;
if (JS_IsException(result)) {
JSValue exc = qjsData.ffJS_GetException(ctx);
const char* message = qjsData.ffJS_ToCStringLen2(ctx, nullptr, exc, false);
const char* message = qjsData.ffJS_ToCStringLen2(ctx, NULL, exc, false);
qjsData.ffJS_FreeValue(ctx, exc);
ffStrbufAppendF(buffer, "Qjs runtime error: %s", message ?: "unknown");
if (message) {
@@ -601,7 +587,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;
char* pend = NULL;
int32_t indexSigned = (int32_t) strtol(placeholderValue.chars + 1, &pend, 10);
if (pend == placeholderValue.chars + 1) {
// treat placeholder as an environment variable
@@ -634,7 +620,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
cSep = *pSep;
*pSep = '\0';
} else {
pSep = nullptr;
pSep = NULL;
}
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
@@ -660,7 +646,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
FF_STRBUF_AUTO_DESTROY trailingEscape = ffStrbufCreate();
skipAnsiEscape(&tempString, buffer, &trailingEscape);
char* pEnd = nullptr;
char* pEnd = NULL;
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
if (start < 0) {
start = (int32_t) tempString.length + start;
@@ -694,7 +680,7 @@ static bool parseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint3
continue;
}
} else {
char* pEnd = nullptr;
char* pEnd = NULL;
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
if (*pEnd != '\0') {
*pSep = cSep;
File diff suppressed because it is too large Load Diff
+10 -14
View File
@@ -39,7 +39,7 @@ static void defaultConfig(void) {
}
#ifdef _WIN32
static UINT oldCp = CP_UTF8;
static volatile UINT oldCp = CP_UTF8;
void resetConsoleCP(void) {
if (oldCp != CP_UTF8) {
SetConsoleOutputCP(oldCp);
@@ -69,10 +69,6 @@ void ffInitInstance(void) {
defaultConfig();
initState(&instance.state);
#ifdef _WIN32
instance.state.platform.initCP = oldCp;
#endif
}
static volatile bool ffDisableLinewrap = false;
@@ -97,12 +93,12 @@ static void resetConsole(void) {
}
#ifdef _WIN32
BOOL WINAPI consoleHandler([[maybe_unused]] DWORD signal) {
BOOL WINAPI consoleHandler(FF_A_UNUSED DWORD signal) {
resetConsole();
exit(0);
}
#else
static void exitSignalHandler([[maybe_unused]] int signal) {
static void exitSignalHandler(FF_A_UNUSED int signal) {
resetConsole();
exit(0);
}
@@ -115,26 +111,26 @@ void ffStart(void) {
#ifdef _WIN32
SetErrorMode(SEM_FAILCRITICALERRORS);
if (instance.config.display.noBuffer) {
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
} else {
setvbuf(stdout, nullptr, _IOFBF, 4096);
setvbuf(stdout, NULL, _IOFBF, 4096);
}
SetConsoleCtrlHandler(consoleHandler, TRUE);
#else
if (instance.config.display.noBuffer) {
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
}
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = exitSignalHandler;
sigaction(SIGINT, &action, nullptr);
sigaction(SIGTERM, &action, nullptr);
sigaction(SIGQUIT, &action, nullptr);
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
sigset_t newmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &newmask, nullptr);
sigprocmask(SIG_BLOCK, &newmask, NULL);
#endif
// reset everything to default before we start printing
+10 -10
View File
@@ -22,8 +22,8 @@
static void createSubfolders(const char* fileName) {
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* token = nullptr;
while ((token = strchr(fileName, '/')) != nullptr) {
const char* token = NULL;
while ((token = strchr(fileName, '/')) != NULL) {
ffStrbufAppendNS(&path, (uint32_t) (token - fileName + 1), fileName);
mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH);
fileName = token + 1;
@@ -34,7 +34,7 @@ bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data) {
int openFlagsModes = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
mode_t openFlagsRights = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
FF_AUTO_CLOSE_FD int fd = open(fileName, openFlagsModes, openFlagsRights);
int FF_AUTO_CLOSE_FD fd = open(fileName, openFlagsModes, openFlagsRights);
if (fd == -1) {
if (errno == ENOENT) {
createSubfolders(fileName);
@@ -119,7 +119,7 @@ bool ffPathExpandEnv(const char* in, FFstrbuf* out) {
| GLOB_BRACE
#endif
,
nullptr,
NULL,
&gb) != 0)
return false;
@@ -174,7 +174,7 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
fd_set rd;
FD_ZERO(&rd);
FD_SET(ftty, &rd);
if (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 }) <= 0) {
if (select(ftty + 1, &rd, NULL, NULL, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 }) <= 0) {
return "select(/dev/tty) timeout or failed";
}
}
@@ -213,7 +213,7 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
va_end(args);
return nullptr;
return NULL;
}
bool ffSuppressIO(bool suppress) {
@@ -258,14 +258,14 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
}
FF_AUTO_CLOSE_DIR DIR* dir = fdopendir(dfd);
if (dir == nullptr) {
if (dir == NULL) {
close(dfd);
return; // Should not happen
}
uint32_t folderLength = folder->length;
if (pretty && folderName != nullptr) {
if (pretty && folderName != NULL) {
for (uint8_t i = 0; i < indentation - 1; i++) {
fputs(" | ", stdout);
}
@@ -274,7 +274,7 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') { // skip hidden files
continue;
}
@@ -316,7 +316,7 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
void ffListFilesRecursively(const char* path, bool pretty) {
FF_STRBUF_AUTO_DESTROY folder = ffStrbufCreateS(path);
ffStrbufEnsureEndsWithC(&folder, '/');
listFilesRecursively(folder.length, &folder, 0, nullptr, pretty);
listFilesRecursively(folder.length, &folder, 0, NULL, pretty);
}
FFNativeFD ffGetNullFD(void) {
+25 -25
View File
@@ -20,10 +20,10 @@ static bool createSubfolders(wchar_t* fileName) {
fileName,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
NULL,
OPEN_EXISTING,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
nullptr);
NULL);
ptr[3] = saved;
if (hRoot == INVALID_HANDLE_VALUE) {
@@ -36,12 +36,12 @@ static bool createSubfolders(wchar_t* fileName) {
// UNC path: \\server\share\...
else if (ptr[0] == L'\\' && ptr[1] == L'\\') {
wchar_t* serverEnd = wcschr(ptr + 2, L'\\');
if (serverEnd == nullptr) {
if (serverEnd == NULL) {
return false;
}
wchar_t* shareEnd = wcschr(serverEnd + 1, L'\\');
if (shareEnd == nullptr) {
if (shareEnd == NULL) {
return true; // no parent subfolder exists before file name
}
@@ -52,10 +52,10 @@ static bool createSubfolders(wchar_t* fileName) {
fileName,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
NULL,
OPEN_EXISTING,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
nullptr);
NULL);
*shareEnd = saved;
if (hRoot == INVALID_HANDLE_VALUE) {
@@ -73,10 +73,10 @@ static bool createSubfolders(wchar_t* fileName) {
driveRoot,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
NULL,
OPEN_EXISTING,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_FLAG_BACKUP_SEMANTICS,
nullptr);
NULL);
if (hRoot == INVALID_HANDLE_VALUE) {
return false;
}
@@ -86,7 +86,7 @@ static bool createSubfolders(wchar_t* fileName) {
while (true) {
wchar_t* token = wcschr(ptr, L'\\');
if (token == nullptr) {
if (token == NULL) {
break;
}
@@ -113,12 +113,12 @@ static bool createSubfolders(wchar_t* fileName) {
.Attributes = OBJ_CASE_INSENSITIVE,
},
&iosb,
nullptr,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
nullptr,
NULL,
0);
if (!NT_SUCCESS(status)) {
@@ -157,13 +157,13 @@ bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data) {
}
}
FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
HANDLE FF_AUTO_CLOSE_FD handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_PATH_NOT_FOUND) {
if (!createSubfolders(fileNameW)) {
return false;
}
handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
handle = CreateFileW(fileNameW, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE) {
return false;
}
@@ -173,7 +173,7 @@ bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data) {
}
DWORD written;
return !!WriteFile(handle, data, (DWORD) dataSize, &written, nullptr);
return !!WriteFile(handle, data, (DWORD) dataSize, &written, NULL);
}
static inline void readWithLength(HANDLE handle, FFstrbuf* buffer, uint32_t length) {
@@ -181,7 +181,7 @@ static inline void readWithLength(HANDLE handle, FFstrbuf* buffer, uint32_t leng
DWORD bytesRead = 0;
while (
length > 0 &&
ReadFile(handle, buffer->chars + buffer->length, length, &bytesRead, nullptr) != FALSE &&
ReadFile(handle, buffer->chars + buffer->length, length, &bytesRead, NULL) != FALSE &&
bytesRead > 0) {
buffer->length += (uint32_t) bytesRead;
length -= (uint32_t) bytesRead;
@@ -193,7 +193,7 @@ static inline void readUntilEOF(HANDLE handle, FFstrbuf* buffer) {
uint32_t available = ffStrbufGetFree(buffer);
DWORD bytesRead = 0;
while (
ReadFile(handle, buffer->chars + buffer->length, available, &bytesRead, nullptr) != FALSE &&
ReadFile(handle, buffer->chars + buffer->length, available, &bytesRead, NULL) != FALSE &&
bytesRead > 0) {
buffer->length += (uint32_t) bytesRead;
if ((uint32_t) bytesRead == available) {
@@ -282,7 +282,7 @@ bool ffPathExpandEnv(const char* in, FFstrbuf* out) {
len /= sizeof(wchar_t); // convert from bytes to characters
SIZE_T outLen; // in characters, including null terminator
if (!NT_SUCCESS(RtlExpandEnvironmentStrings(nullptr, pathInW, len, pathOutW, ARRAY_SIZE(pathOutW), &outLen))) {
if (!NT_SUCCESS(RtlExpandEnvironmentStrings(NULL, pathInW, len, pathOutW, ARRAY_SIZE(pathOutW), &outLen))) {
return false;
}
@@ -336,7 +336,7 @@ bool ffSuppressIO(bool suppress) {
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty) {
uint32_t folderLength = folder->length;
if (pretty && folderName != nullptr) {
if (pretty && folderName != NULL) {
for (uint8_t i = 0; i < indentation - 1; i++) {
fputs(" | ", stdout);
}
@@ -381,7 +381,7 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
void ffListFilesRecursively(const char* path, bool pretty) {
FF_STRBUF_AUTO_DESTROY folder = ffStrbufCreateS(path);
ffStrbufEnsureEndsWithC(&folder, '/');
listFilesRecursively(folder.length, &folder, 0, nullptr, pretty);
listFilesRecursively(folder.length, &folder, 0, NULL, pretty);
}
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
@@ -390,7 +390,7 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
DWORD inputMode = 0;
bool hasInputMode = !!GetConsoleMode(hInput, &inputMode);
if (!hasInputMode) {
hConin = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, nullptr);
hConin = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, NULL);
hInput = hConin;
hasInputMode = !!GetConsoleMode(hInput, &inputMode);
}
@@ -404,10 +404,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
FF_AUTO_CLOSE_FD HANDLE hConout = INVALID_HANDLE_VALUE;
DWORD outputMode;
if (!GetConsoleMode(hOutput, &outputMode)) {
hConout = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, nullptr);
hConout = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, NULL);
hOutput = hConout;
}
WriteFile(hOutput, request, (DWORD) strlen(request), &bytes, nullptr);
WriteFile(hOutput, request, (DWORD) strlen(request), &bytes, NULL);
}
while (true) {
@@ -441,7 +441,7 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
while (true) {
DWORD bytes = 0;
if (!ReadFile(hInput, buffer + bytesRead, (DWORD) (sizeof(buffer) - 1 - bytesRead), &bytes, nullptr) || bytes == 0) {
if (!ReadFile(hInput, buffer + bytesRead, (DWORD) (sizeof(buffer) - 1 - bytesRead), &bytes, NULL) || bytes == 0) {
va_end(args);
return "ReadFile() failed";
}
@@ -473,7 +473,7 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
va_end(args);
return nullptr;
return NULL;
}
FFNativeFD ffGetNullFD(void) {
@@ -490,7 +490,7 @@ FFNativeFD ffGetNullFD(void) {
0,
&(SECURITY_ATTRIBUTES) {
.nLength = sizeof(SECURITY_ATTRIBUTES),
.lpSecurityDescriptor = nullptr,
.lpSecurityDescriptor = NULL,
.bInheritHandle = TRUE,
});
return hNullFile;
+19 -33
View File
@@ -11,6 +11,7 @@
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdalign.h>
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs) {
if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition")) {
@@ -67,7 +68,7 @@ const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair p
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
if (intVal == pPair->value) {
*result = pPair->value;
return nullptr;
return NULL;
}
}
@@ -77,7 +78,7 @@ const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair p
for (const FFKeyValuePair* pPair = pairs; pPair->key; ++pPair) {
if (ffStrEqualsIgnCase(strVal, pPair->key)) {
*result = pPair->value;
return nullptr;
return NULL;
}
}
@@ -136,10 +137,10 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
#if !FF_MODULE_DISABLE_CPUUSAGE
case 'c':
case 'C': {
if (ffStrEqualsIgnCase(type, ffCPUUsageModuleInfo.name)) {
if (ffStrEqualsIgnCase(type, FF_CPUUSAGE_MODULE_NAME)) {
ffPrepareCPUUsage();
} else if (ffStrEqualsIgnCase(type, ffCommandModuleInfo.name)) {
[[gnu::cleanup(ffDestroyCommandOptions)]] FFCommandOptions options;
} else if (ffStrEqualsIgnCase(type, FF_COMMAND_MODULE_NAME)) {
FF_A_CLEANUP(ffDestroyCommandOptions) FFCommandOptions options;
ffInitCommandOptions(&options);
if (module) {
ffCommandModuleInfo.parseJsonObject(&options, module);
@@ -153,8 +154,8 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
#if !FF_MODULE_DISABLE_DISKIO
case 'd':
case 'D': {
if (ffStrEqualsIgnCase(type, ffDiskIOModuleInfo.name)) {
[[gnu::cleanup(ffDestroyDiskIOOptions)]] FFDiskIOOptions options;
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME)) {
FF_A_CLEANUP(ffDestroyDiskIOOptions) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
if (module) {
ffDiskIOModuleInfo.parseJsonObject(&options, module);
@@ -168,8 +169,8 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
#if !FF_MODULE_DISABLE_NETIO
case 'n':
case 'N': {
if (ffStrEqualsIgnCase(type, ffNetIOModuleInfo.name)) {
[[gnu::cleanup(ffDestroyNetIOOptions)]] FFNetIOOptions options;
if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME)) {
FF_A_CLEANUP(ffDestroyNetIOOptions) FFNetIOOptions options;
ffInitNetIOOptions(&options);
if (module) {
ffNetIOModuleInfo.parseJsonObject(&options, module);
@@ -183,8 +184,8 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
#if !FF_MODULE_DISABLE_PUBLICIP
case 'p':
case 'P': {
if (ffStrEqualsIgnCase(type, ffPublicIPModuleInfo.name)) {
[[gnu::cleanup(ffDestroyPublicIpOptions)]] FFPublicIPOptions options;
if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME)) {
FF_A_CLEANUP(ffDestroyPublicIpOptions) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
if (module) {
ffPublicIPModuleInfo.parseJsonObject(&options, module);
@@ -195,26 +196,11 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) {
}
#endif
#if !FF_MODULE_DISABLE_TOP
case 't':
case 'T': {
if (ffStrEqualsIgnCase(type, ffTopModuleInfo.name)) {
[[gnu::cleanup(ffDestroyTopOptions)]] FFTopOptions options;
ffInitTopOptions(&options);
if (module) {
ffTopModuleInfo.parseJsonObject(&options, module);
}
ffPrepareTopProcesses(options.showTypes);
}
break;
}
#endif
#if !FF_MODULE_DISABLE_WEATHER
case 'w':
case 'W': {
if (ffStrEqualsIgnCase(type, ffWeatherModuleInfo.name)) {
[[gnu::cleanup(ffDestroyWeatherOptions)]] FFWeatherOptions options;
if (ffStrEqualsIgnCase(type, FF_WEATHER_MODULE_NAME)) {
FF_A_CLEANUP(ffDestroyWeatherOptions) FFWeatherOptions options;
ffInitWeatherOptions(&options);
if (module) {
ffWeatherModuleInfo.parseJsonObject(&options, module);
@@ -259,7 +245,7 @@ static const char* printJsonConfig(FFdata* data, bool prepare) {
yyjson_val* modules = yyjson_obj_get(root, "modules");
if (!modules) {
return nullptr;
return NULL;
}
if (!yyjson_is_arr(modules)) {
return "Property 'modules' must be an array of strings or objects";
@@ -278,7 +264,7 @@ static const char* printJsonConfig(FFdata* data, bool prepare) {
yyjson_val* module = item;
const char* type = yyjson_get_str(module);
if (type) {
module = nullptr;
module = NULL;
} else if (yyjson_is_obj(module)) {
yyjson_val* conditions = yyjson_obj_get(module, "condition");
if (conditions) {
@@ -322,7 +308,7 @@ static const char* printJsonConfig(FFdata* data, bool prepare) {
return "module object must contain a \"type\" key ( case sensitive )";
}
if (yyjson_obj_size(module) == 1) { // contains only Property type
module = nullptr;
module = NULL;
}
} else {
return "modules must be an array of strings or objects";
@@ -362,7 +348,7 @@ static const char* printJsonConfig(FFdata* data, bool prepare) {
#endif
}
return nullptr;
return NULL;
}
void ffPrintJsonConfig(FFdata* data, bool prepare) {
@@ -374,7 +360,7 @@ void ffPrintJsonConfig(FFdata* data, bool prepare) {
yyjson_mut_obj_add_str(jsonDoc, obj, "error", error);
yyjson_mut_doc_set_root(jsonDoc, obj);
} else {
ffPrintError("JsonConfig", 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "%s", error);
ffPrintError("JsonConfig", 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "%s", error);
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
bool ffKmodLoaded(const char* modName) {
FF_CFTYPE_AUTO_RELEASE CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, modName, kCFStringEncodingUTF8);
FF_CFTYPE_AUTO_RELEASE CFArrayRef identifiers = CFArrayCreate(kCFAllocatorDefault, (const void**) &name, 1, &kCFTypeArrayCallBacks);
FF_CFTYPE_AUTO_RELEASE CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, nullptr, 0, nullptr);
FF_CFTYPE_AUTO_RELEASE CFArrayRef keys = CFArrayCreate(kCFAllocatorDefault, NULL, 0, NULL);
FF_CFTYPE_AUTO_RELEASE CFDictionaryRef kextInfo = KextManagerCopyLoadedKextInfo(identifiers, keys);
return CFDictionaryContainsKey(kextInfo, name);
}
+2 -2
View File
@@ -3,7 +3,7 @@
bool ffKmodLoaded(const char* modName) {
static FFstrbuf modules;
if (modules.chars == nullptr) {
if (modules.chars == NULL) {
ffStrbufInitS(&modules, "\n");
ffAppendFileBuffer("/proc/modules", &modules);
}
@@ -21,5 +21,5 @@ bool ffKmodLoaded(const char* modName) {
temp[0] = '\n';
memcpy(temp + 1, modName, len);
temp[1 + len] = ' ';
return memmem(modules.chars, modules.length, temp, len + 2) != nullptr;
return memmem(modules.chars, modules.length, temp, len + 2) != NULL;
}
+3 -3
View File
@@ -4,15 +4,15 @@
#include <sys/module.h>
#include <sys/param.h>
typedef struct [[gnu::packed]] FFNbsdModList {
typedef struct FF_A_PACKED FFNbsdModList {
int len;
modstat_t mods[];
} FFNbsdModList;
bool ffKmodLoaded(const char* modName) {
static FFNbsdModList* list = nullptr;
static FFNbsdModList* list = NULL;
if (list == nullptr) {
if (list == NULL) {
struct iovec iov = {};
for (size_t len = 8192;; len = iov.iov_len) {
+1 -1
View File
@@ -1,5 +1,5 @@
#include "common/kmod.h"
bool ffKmodLoaded([[maybe_unused]] const char* modName) {
bool ffKmodLoaded(FF_A_UNUSED const char* modName) {
return true; // Don't generate kernel module related errors
}
+1 -1
View File
@@ -5,7 +5,7 @@
bool ffKmodLoaded(const char* modName) {
ULONG bufferSize = 0;
NtQuerySystemInformation(SystemModuleInformation, nullptr, 0, &bufferSize);
NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &bufferSize);
if (bufferSize == 0) {
return true; // ignore errors
}
+15 -95
View File
@@ -35,7 +35,7 @@ void* ffLibraryLoadSingle(const char* path, int maxVersion) {
// libX.dll.1 never exists on Windows, while libX-1.dll may exist
FF_UNUSED(maxVersion)
if (result != nullptr) {
if (result != NULL) {
return result;
}
@@ -50,11 +50,11 @@ void* ffLibraryLoadSingle(const char* path, int maxVersion) {
#else
if (result == nullptr) {
if (result == NULL) {
FF_DEBUG("dlopen(\"%s\"): %s", path, dlerror());
}
if (result != nullptr || maxVersion < 0) {
if (result != NULL || maxVersion < 0) {
return result;
}
@@ -67,7 +67,7 @@ void* ffLibraryLoadSingle(const char* path, int maxVersion) {
ffStrbufAppendSInt(&pathbuf, i);
result = dlopen(pathbuf.chars, FF_DLOPEN_FLAGS);
if (result != nullptr) {
if (result != NULL) {
break;
} else {
FF_DEBUG("dlopen(\"%s\"): %s", pathbuf.chars, dlerror());
@@ -90,7 +90,7 @@ void* ffLibraryLoadMulti(const char* path, int maxVersion, ...) {
do {
const char* pathRest = va_arg(defaultNames, const char*);
if (pathRest == nullptr) {
if (pathRest == NULL) {
break;
}
@@ -108,18 +108,18 @@ void* ffLibraryLoadMulti(const char* path, int maxVersion, ...) {
#if _WIN32
void* dlopen(const char* path, [[maybe_unused]] int mode) {
void* dlopen(const char* path, FF_A_UNUSED int mode) {
wchar_t pathW[MAX_PATH + 1];
ULONG pathWBytes = 0;
NTSTATUS status = RtlUTF8ToUnicodeN(pathW, sizeof(pathW), &pathWBytes, path, (uint32_t) strlen(path) + 1);
if (!NT_SUCCESS(status)) {
FF_DEBUG("RtlUTF8ToUnicodeN failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
return nullptr;
return NULL;
}
PVOID module = nullptr;
status = LdrLoadDll(nullptr, nullptr, &(UNICODE_STRING) {
PVOID module = NULL;
status = LdrLoadDll(NULL, NULL, &(UNICODE_STRING) {
.Length = (USHORT) (pathWBytes - sizeof(wchar_t)), // Exclude null terminator
.MaximumLength = (USHORT) pathWBytes,
.Buffer = pathW,
@@ -128,7 +128,7 @@ void* dlopen(const char* path, [[maybe_unused]] int mode) {
if (!NT_SUCCESS(status)) {
FF_DEBUG("LdrLoadDll failed for path %s with status 0x%08lX: %s", path, status, ffDebugNtStatus(status));
return nullptr;
return NULL;
}
return module;
@@ -155,17 +155,17 @@ void* dlsym(void* handle, const char* symbol) {
&address);
if (!NT_SUCCESS(status)) {
FF_DEBUG("LdrGetProcedureAddress failed for symbol %s with status 0x%08lX: %s", symbol, status, ffDebugNtStatus(status));
return nullptr;
return NULL;
}
return address;
}
void* ffLibraryGetModule(const wchar_t* libraryFileName) {
assert(libraryFileName != nullptr && "Use \"ffGetPeb()->ImageBaseAddress\" instead");
assert(libraryFileName != NULL && "Use \"ffGetPeb()->ImageBaseAddress\" instead");
void* module = nullptr;
void* module = NULL;
USHORT libraryFileNameBytes = (USHORT) (wcslen(libraryFileName) * sizeof(wchar_t) + sizeof(wchar_t));
NTSTATUS status = LdrGetDllHandle(nullptr, nullptr, &(UNICODE_STRING) {
NTSTATUS status = LdrGetDllHandle(NULL, NULL, &(UNICODE_STRING) {
.Length = libraryFileNameBytes - sizeof(wchar_t),
.MaximumLength = libraryFileNameBytes,
.Buffer = (wchar_t*) libraryFileName,
@@ -173,88 +173,8 @@ void* ffLibraryGetModule(const wchar_t* libraryFileName) {
&module);
if (!NT_SUCCESS(status)) {
FF_DEBUG("LdrGetDllHandle failed for library %ls with status 0x%08lX: %s", libraryFileName, status, ffDebugNtStatus(status));
return nullptr;
return NULL;
}
return module;
}
#endif
struct LibraryIterateDynamicLibsBundle {
FFLibraryIterateCallback callback;
void* userData;
};
#if _WIN32
static void ffLibraryIterateDynamicLibsCallback(PLDR_DATA_TABLE_ENTRY DataTableEntry, PVOID Context, BOOLEAN* StopEnumeration) {
if (DataTableEntry->FullDllName.Buffer == nullptr || DataTableEntry->FullDllName.Buffer[0] == L'\0') {
return;
}
char path[PATH_MAX * 3];
ULONG outBytes;
if (NT_SUCCESS(RtlUnicodeToUTF8N(path, sizeof(path), &outBytes, DataTableEntry->FullDllName.Buffer, DataTableEntry->FullDllName.Length))) {
path[outBytes] = '\0';
struct LibraryIterateDynamicLibsBundle* bundle = (struct LibraryIterateDynamicLibsBundle*) Context;
*StopEnumeration = !bundle->callback(path, bundle->userData);
}
}
bool ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) {
struct LibraryIterateDynamicLibsBundle bundle = {
.callback = callback,
.userData = userData,
};
return NT_SUCCESS(LdrEnumerateLoadedModules(FALSE, ffLibraryIterateDynamicLibsCallback, &bundle));
}
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
bool ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) {
uint32_t imageCount = _dyld_image_count();
for (uint32_t i = 0; i < imageCount; ++i) {
const char* name = _dyld_get_image_name(i);
if (name == nullptr || name[0] == '\0') {
continue;
}
if (!callback(name, userData)) {
break;
}
}
return true;
}
#elif __has_include(<link.h>)
#include <link.h>
static int ffLibraryIterateDynamicLibsCallback(struct dl_phdr_info* info, size_t, void* data) {
if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') {
return 0;
}
struct LibraryIterateDynamicLibsBundle* bundle = (struct LibraryIterateDynamicLibsBundle*) data;
return !bundle->callback(info->dlpi_name, bundle->userData);
}
bool ffLibraryIterateDynamicLibs(bool (*callback)(const char* name, void* userData), void* userData) {
dl_iterate_phdr(
ffLibraryIterateDynamicLibsCallback,
&(struct LibraryIterateDynamicLibsBundle) {
.callback = callback,
.userData = userData,
});
return true;
}
#else
bool ffLibraryIterateDynamicLibs(FFLibraryIterateCallback, void*) {
// Not implemented for this platform
return false;
}
#endif
+8 -8
View File
@@ -109,7 +109,7 @@ static int yyjsonEncode(lua_State* L) {
pretty = (int) lua_toboolean(L, 2);
}
yyjson_mut_doc* doc = yyjson_mut_doc_new(nullptr);
yyjson_mut_doc* doc = yyjson_mut_doc_new(NULL);
if (__builtin_expect(!doc, false)) {
lua_pushlstring(L, "yyjson: yyjson_mut_doc_new() failed", strlen("yyjson: yyjson_mut_doc_new() failed"));
return lua_error(L);
@@ -120,9 +120,9 @@ static int yyjsonEncode(lua_State* L) {
size_t jsonLen;
yyjson_write_err err = {};
FF_AUTO_FREE const char* jsonStr = yyjson_mut_write_opts(doc, YYJSON_WRITE_ALLOW_INF_AND_NAN | (pretty ? YYJSON_WRITE_PRETTY_TWO_SPACES : 0), nullptr, &jsonLen, &err);
FF_AUTO_FREE const char* jsonStr = yyjson_mut_write_opts(doc, YYJSON_WRITE_ALLOW_INF_AND_NAN | (pretty ? YYJSON_WRITE_PRETTY_TWO_SPACES : 0), NULL, &jsonLen, &err);
if (__builtin_expect(jsonStr != nullptr, true)) {
if (__builtin_expect(jsonStr != NULL, true)) {
lua_pushlstring(L, jsonStr, jsonLen);
yyjson_mut_doc_free(doc);
return 1;
@@ -140,10 +140,10 @@ static int yyjsonEncode(lua_State* L) {
const char* ffLuaLoadState(void) {
if (luaData.inited) {
if (luaData.L == nullptr) {
if (luaData.L == NULL) {
return "Lua library is not available";
}
return nullptr;
return NULL;
}
luaData.inited = true;
@@ -211,7 +211,7 @@ const char* ffLuaLoadState(void) {
#endif
lua_State* L = ffluaL_newstate();
if (L == nullptr) {
if (L == NULL) {
return "luaL_newstate() failed";
}
#if LUA_VERSION_NUM >= 505
@@ -226,8 +226,8 @@ const char* ffLuaLoadState(void) {
lua_pushcfunction(L, yyjsonEncode);
lua_setglobal(L, "json_encode");
luaData.L = L;
liblua = nullptr; // don't close lua
return nullptr;
liblua = NULL; // don't close lua
return NULL;
}
#endif
+2 -2
View File
@@ -4,7 +4,7 @@
void* memrchr(const void* s, int c, size_t n) {
if (n == 0) {
return nullptr;
return NULL;
}
const uint8_t uc = (uint8_t) c;
@@ -17,5 +17,5 @@ void* memrchr(const void* s, int c, size_t n) {
}
}
return nullptr;
return NULL;
}
+1 -1
View File
@@ -52,7 +52,7 @@ get_rt_address(struct rt_msghdr* rtm, int desired) {
sa = (struct sockaddr*) (ROUNDUP(salen) + (char*) sa);
}
}
return nullptr;
return NULL;
}
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
+5 -5
View File
@@ -39,20 +39,20 @@ get_rt_address(struct rt_msghdr* rtm, int desired) {
sa = (struct sockaddr*) (ROUNDUP(sa->sa_len) + (char*) sa);
}
}
return nullptr;
return NULL;
}
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY };
size_t needed;
if (sysctl(mib, 6, nullptr, &needed, nullptr, 0) < 0 || needed == 0) {
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0) {
return false;
}
FF_AUTO_FREE char* buf = malloc(needed);
if (sysctl(mib, 6, buf, &needed, nullptr, 0) < 0) {
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
return false;
}
@@ -89,13 +89,13 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_FLAGS, RTF_GATEWAY };
size_t needed;
if (sysctl(mib, 6, nullptr, &needed, nullptr, 0) < 0 || needed == 0) {
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0) {
return false;
}
FF_AUTO_FREE char* buf = malloc(needed);
if (sysctl(mib, 6, buf, &needed, nullptr, 0) < 0) {
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
return false;
}
+1 -1
View File
@@ -6,7 +6,7 @@
#include <stdio.h>
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
FF_AUTO_CLOSE_FILE FILE* netRoute = fopen("/proc/route", "r");
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/route", "r");
if (!netRoute) {
return false;
+8 -8
View File
@@ -29,7 +29,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
}
FF_AUTO_FREE void* buffer = malloc((size_t) size);
if (buffer == nullptr) {
if (buffer == NULL) {
return false;
}
@@ -54,13 +54,13 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
}
size_t addressSize = 0;
if (interface->ifr_route.destination != nullptr) {
if (interface->ifr_route.destination != NULL) {
addressSize += interface->ifr_route.destination->sa_len;
}
if (interface->ifr_route.mask != nullptr) {
if (interface->ifr_route.mask != NULL) {
addressSize += interface->ifr_route.mask->sa_len;
}
if (interface->ifr_route.gateway != nullptr) {
if (interface->ifr_route.gateway != NULL) {
addressSize += interface->ifr_route.gateway->sa_len;
}
@@ -88,7 +88,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
}
FF_AUTO_FREE void* buffer = malloc((size_t) size);
if (buffer == nullptr) {
if (buffer == NULL) {
return false;
}
@@ -109,13 +109,13 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
}
size_t addressSize = 0;
if (interface->ifr_route.destination != nullptr) {
if (interface->ifr_route.destination != NULL) {
addressSize += interface->ifr_route.destination->sa_len;
}
if (interface->ifr_route.mask != nullptr) {
if (interface->ifr_route.mask != NULL) {
addressSize += interface->ifr_route.mask->sa_len;
}
if (interface->ifr_route.gateway != nullptr) {
if (interface->ifr_route.gateway != NULL) {
addressSize += interface->ifr_route.gateway->sa_len;
}
+8 -8
View File
@@ -44,7 +44,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
uint32_t pid = ffNetifGetNetlinkPortId(sock_fd);
struct [[gnu::packed]] {
struct FF_A_PACKED {
struct nlmsghdr nlh;
struct rtmsg rtm;
struct rtattr rta;
@@ -97,7 +97,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
uint32_t minMetric = UINT32_MAX;
[[maybe_unused]] int routeCount = 0;
FF_A_UNUSED int routeCount = 0;
while (true) {
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0, (struct sockaddr*) &src_addr, &src_addr_len);
@@ -163,7 +163,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
}
FF_DEBUG("Processing IPv4 default route candidate #%d", routeCount);
entry = (typeof(entry)) {}; // Default to zero metric (no RTA_PRIORITY found)
entry = (__typeof__(entry)) {}; // Default to zero metric (no RTA_PRIORITY found)
// Parse route attributes
size_t rtm_len = RTM_PAYLOAD(nlh);
@@ -254,7 +254,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
uint32_t pid = ffNetifGetNetlinkPortId(sock_fd);
struct [[gnu::packed]] {
struct FF_A_PACKED {
struct nlmsghdr nlh;
struct rtmsg rtm;
struct rtattr rta;
@@ -307,7 +307,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
uint32_t minMetric = UINT32_MAX;
[[maybe_unused]] int routeCount = 0;
FF_A_UNUSED int routeCount = 0;
while (true) {
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0, (struct sockaddr*) &src_addr, &src_addr_len);
@@ -372,7 +372,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
}
FF_DEBUG("Processing IPv6 default route candidate #%d", routeCount);
entry = (typeof(entry)) {}; // Default to zero metric (no RTA_PRIORITY found)
entry = (__typeof__(entry)) {}; // Default to zero metric (no RTA_PRIORITY found)
// Parse route attributes
size_t rtm_len = RTM_PAYLOAD(nlh);
@@ -382,7 +382,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
switch (rta->rta_type) {
case RTA_DST:
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
[[maybe_unused]] char str[INET6_ADDRSTRLEN];
FF_A_UNUSED char str[INET6_ADDRSTRLEN];
FF_DEBUG("Unexpected RTA_DST: %s", inet_ntop(AF_INET6, RTA_DATA(rta), str, sizeof(str)));
goto next;
}
@@ -399,7 +399,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
if (IN6_IS_ADDR_UNSPECIFIED(gw)) {
goto next;
}
[[maybe_unused]] char str[INET6_ADDRSTRLEN];
FF_A_UNUSED char str[INET6_ADDRSTRLEN];
FF_DEBUG("Found gateway: %s", inet_ntop(AF_INET6, gw, str, sizeof(str)));
}
break;
+2 -2
View File
@@ -4,7 +4,7 @@
#include <iphlpapi.h>
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
PMIB_IPFORWARD_TABLE2 pIpForwardTable = nullptr;
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_INET, &pIpForwardTable))) {
return false;
@@ -48,7 +48,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
}
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) {
PMIB_IPFORWARD_TABLE2 pIpForwardTable = nullptr;
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_INET6, &pIpForwardTable))) {
return false;
+5 -5
View File
@@ -29,9 +29,9 @@ const char* ffNetworkingLoadZlibLibrary(void) {
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateInit2_)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflate)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateEnd)
zlib = nullptr; // don't auto dlclose
zlib = NULL; // don't auto dlclose
}
return zlibData.ffinflateEnd == nullptr ? "Failed to load libz" : nullptr;
return zlibData.ffinflateEnd == NULL ? "Failed to load libz" : NULL;
}
// Try to pre-read gzip header to determine uncompressed size
@@ -64,14 +64,14 @@ static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize) {
// Decompress gzip content
bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd) {
assert(headerEnd != nullptr && *headerEnd == '\r');
assert(headerEnd != NULL && *headerEnd == '\r');
// Calculate header size
uint32_t headerSize = (uint32_t) (headerEnd - buffer->chars);
*headerEnd = '\0'; // Replace delimiter with null character for easier processing
// Ensure Content-Encoding is in response headers, not in response body
bool hasGzip = strcasestr(buffer->chars, "\nContent-Encoding: gzip") != nullptr;
bool hasGzip = strcasestr(buffer->chars, "\nContent-Encoding: gzip") != NULL;
*headerEnd = '\r'; // Restore delimiter
if (!hasGzip) {
@@ -165,7 +165,7 @@ bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd) {
// Use decompressedBuffer.length (total) not decompressedSize (last chunk only)
FF_STRBUF_AUTO_DESTROY newBuffer = ffStrbufCreateA(headerSize + decompressedBuffer.length + 64);
char* line = nullptr;
char* line = NULL;
size_t len = 0;
while (ffStrbufGetline(&line, &len, buffer)) {
if (ffStrStartsWithIgnCase(line, "Content-Encoding:")) {
+19 -19
View File
@@ -97,7 +97,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state) {
},
1,
&sent,
nullptr) != 0) {
NULL) != 0) {
sent = 0;
}
if (fcntl(state->sockfd, F_SETFL, 0) == -1) {
@@ -123,9 +123,9 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state) {
sent,
strerror(errno));
freeaddrinfo(state->addr);
state->addr = nullptr;
state->addr = NULL;
ffStrbufDestroy(&state->command);
return nullptr;
return NULL;
}
FF_DEBUG(
@@ -149,7 +149,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state) {
// Traditional connect and send function
static const char* connectAndSend(FFNetworkingState* state) {
const char* ret = nullptr;
const char* ret = NULL;
FF_DEBUG("Using traditional connection method to connect");
FF_DEBUG("Attempting connect() to server...");
@@ -178,7 +178,7 @@ error:
exit:
FF_DEBUG("Releasing address info and other resources");
freeaddrinfo(state->addr);
state->addr = nullptr;
state->addr = NULL;
ffStrbufDestroy(&state->command);
return ret;
@@ -212,7 +212,7 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
FF_DEBUG("Thread ID initialized to 0");
#endif
const char* ret = nullptr;
const char* ret = NULL;
struct addrinfo hints = {
.ai_family = state->ipv6 ? AF_INET6 : AF_INET,
@@ -261,7 +261,7 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
if (state->timeout > 0) {
FF_DEBUG("Setting connection timeout: %u ms", state->timeout);
[[maybe_unused]] uint32_t sec = state->timeout / 1000;
FF_A_UNUSED uint32_t sec = state->timeout / 1000;
if (sec == 0) {
sec = 1;
}
@@ -280,14 +280,14 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos
#endif
}
return nullptr;
return NULL;
error:
FF_DEBUG("Error occurred during initialization");
if (state->addr != nullptr) {
if (state->addr != NULL) {
FF_DEBUG("Releasing address information");
freeaddrinfo(state->addr);
state->addr = nullptr;
state->addr = NULL;
}
if (state->sockfd > 0) {
@@ -307,7 +307,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
#ifdef FF_HAVE_ZLIB
const char* zlibError = ffNetworkingLoadZlibLibrary();
// Only enable compression if zlib library is successfully loaded
if (zlibError == nullptr) {
if (zlibError == NULL) {
FF_DEBUG("Successfully loaded zlib library, compression enabled");
} else {
FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError);
@@ -322,16 +322,16 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
}
const char* initResult = initNetworkingState(state, host, path, headers);
if (initResult != nullptr) {
if (initResult != NULL) {
FF_DEBUG("Initialization failed: %s", initResult);
return initResult;
}
FF_DEBUG("Network state initialization successful");
const char* tfoResult = tryNonThreadingFastPath(state);
if (tfoResult == nullptr) {
if (tfoResult == NULL) {
FF_DEBUG("TryNonThreadingFastPath() succeeded or in progress");
return nullptr;
return NULL;
}
FF_DEBUG("TryNonThreadingFastPath() failed: %s, trying traditional connection", tfoResult);
@@ -341,7 +341,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
state->thread = ffThreadCreate(connectAndSendThreadMain, state);
if (state->thread) {
FF_DEBUG("Thread creation successful: thread=%p", (void*) (uintptr_t) state->thread);
return nullptr;
return NULL;
}
FF_DEBUG("Thread creation failed");
} else {
@@ -406,7 +406,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
FF_DEBUG("Setting receive timeout: %u ms", timeout);
struct timeval timev;
timev.tv_sec = timeout / 1000;
timev.tv_usec = (typeof(timev.tv_usec)) ((timeout % 1000) * 1000); // milliseconds to microseconds
timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); // milliseconds to microseconds
setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
}
#endif
@@ -417,7 +417,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
}
FF_DEBUG("Starting data reception");
[[maybe_unused]] int recvCount = 0;
FF_A_UNUSED int recvCount = 0;
uint32_t contentLength = 0;
uint32_t headerEnd = 0;
@@ -455,7 +455,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
// Check for Content-Length header to pre-allocate enough memory
const char* clHeader = strcasestr(buffer->chars, "Content-Length:");
if (clHeader) {
contentLength = (uint32_t) strtoul(clHeader + 15, nullptr, 10);
contentLength = (uint32_t) strtoul(clHeader + 15, NULL, 10);
if (contentLength > 0) {
if (contentLength > 1024 * 1024) { // 1MB limit to prevent excessive memory allocation and potential attacks
FF_DEBUG("Content-Length is too large: %u bytes, aborting", contentLength);
@@ -512,5 +512,5 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
}
#endif
return nullptr;
return NULL;
}
+51 -39
View File
@@ -23,7 +23,7 @@ static const char* initWsaData(WSADATA* wsaData) {
}
// Dummy socket needed for WSAIoctl
SOCKET sockfd = WSASocketW(AF_INET, SOCK_STREAM, 0, nullptr, 0, 0);
SOCKET sockfd = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
if (sockfd == INVALID_SOCKET) {
FF_DEBUG("WSASocketW(AF_INET, SOCK_STREAM) failed");
WSACleanup();
@@ -32,7 +32,7 @@ static const char* initWsaData(WSADATA* wsaData) {
DWORD dwBytes;
GUID guid = WSAID_CONNECTEX;
if (WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx), &dwBytes, nullptr, nullptr) != 0) {
if (WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx), &dwBytes, NULL, NULL) != 0) {
FF_DEBUG("WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER) failed");
closesocket(sockfd);
WSACleanup();
@@ -42,7 +42,7 @@ static const char* initWsaData(WSADATA* wsaData) {
closesocket(sockfd);
FF_DEBUG("WinSock initialized successfully");
return nullptr;
return NULL;
}
const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) {
@@ -52,7 +52,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
#ifdef FF_HAVE_ZLIB
const char* zlibError = ffNetworkingLoadZlibLibrary();
// Only enable compression if zlib library is successfully loaded
if (zlibError == nullptr) {
if (zlibError == NULL) {
FF_DEBUG("Successfully loaded zlib library, compression enabled");
} else {
FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError);
@@ -69,7 +69,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
static WSADATA wsaData;
if (wsaData.wVersion == 0) {
const char* error = initWsaData(&wsaData);
if (error != nullptr) {
if (error != NULL) {
wsaData.wVersion = (WORD) -1;
FF_DEBUG("WinSock initialization failed: %s", error);
return error;
@@ -87,7 +87,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
};
wchar_t hostW[256];
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(hostW, (ULONG) sizeof(hostW), nullptr, host, (ULONG) strlen(host) + 1))) {
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(hostW, (ULONG) sizeof(hostW), NULL, host, (ULONG) strlen(host) + 1))) {
FF_DEBUG("Failed to convert host to wide string: %s", host);
return "Failed to convert host to wide string";
}
@@ -98,7 +98,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
return "GetAddrInfoW() failed";
}
state->sockfd = WSASocketW(addr->ai_family, addr->ai_socktype, addr->ai_protocol, nullptr, 0, 0);
state->sockfd = WSASocketW(addr->ai_family, addr->ai_socktype, addr->ai_protocol, NULL, 0, 0);
if (state->sockfd == INVALID_SOCKET) {
FF_DEBUG("WSASocketW() failed");
FreeAddrInfoW(addr);
@@ -141,14 +141,16 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
}
// Initialize overlapped structure with WSA event for asynchronous I/O
state->overlapped = (OVERLAPPED) {};
state->overlapped = (OVERLAPPED) {
.hEvent = WSACreateEvent()
};
if (!NT_SUCCESS(NtCreateEvent(&state->overlapped.hEvent, EVENT_ALL_ACCESS, nullptr, NotificationEvent, FALSE))) {
FF_DEBUG("NtCreateEvent() failed");
if (state->overlapped.hEvent == WSA_INVALID_EVENT) {
FF_DEBUG("WSACreateEvent() failed");
closesocket(state->sockfd);
FreeAddrInfoW(addr);
state->sockfd = INVALID_SOCKET;
return "NtCreateEvent() failed";
return "WSACreateEvent() failed";
}
// Build HTTP command
@@ -187,12 +189,12 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int) addr->ai_addrlen, state->command.chars, state->command.length, &sent, &state->overlapped);
FreeAddrInfoW(addr);
addr = nullptr;
addr = NULL;
if (!result) {
if (WSAGetLastError() != WSA_IO_PENDING) {
FF_DEBUG("ConnectEx() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
NtClose(state->overlapped.hEvent);
WSACloseEvent(state->overlapped.hEvent);
closesocket(state->sockfd);
state->sockfd = INVALID_SOCKET;
ffStrbufDestroy(&state->command);
@@ -205,7 +207,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
}
// No need to cleanup state fields here since we need them in the receive function
return nullptr;
return NULL;
}
const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer) {
@@ -217,30 +219,40 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
return "ffNetworkingSendHttpRequest() failed";
}
DWORD transfer;
uint32_t timeout = state->timeout;
if (!GetOverlappedResultEx((HANDLE) state->sockfd, &state->overlapped, &transfer, timeout > 0 ? timeout : INFINITE, FALSE)) {
DWORD error = GetLastError();
if (error == WAIT_TIMEOUT) {
FF_DEBUG("GetOverlappedResultEx timed out");
} else {
FF_DEBUG("GetOverlappedResultEx failed: %s", ffDebugWin32Error(error));
if (timeout > 0) {
FF_DEBUG("WSAWaitForMultipleEvents with timeout: %u ms", timeout);
DWORD result = WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, timeout, FALSE);
if (result != WSA_WAIT_EVENT_0) {
if (result == WSA_WAIT_TIMEOUT) {
FF_DEBUG("WSAWaitForMultipleEvents timed out");
} else {
FF_DEBUG("WSAWaitForMultipleEvents failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
}
if (CancelIoEx((HANDLE) state->sockfd, &state->overlapped)) {
WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, 10, TRUE);
}
WSACloseEvent(state->overlapped.hEvent);
closesocket(state->sockfd);
ffStrbufDestroy(&state->command);
return "WSAWaitForMultipleEvents() failed or timeout";
}
IO_STATUS_BLOCK cancelIosb = {};
if (NT_SUCCESS(NtCancelIoFileEx((HANDLE) state->sockfd, (PIO_STATUS_BLOCK) &state->overlapped, &cancelIosb))) {
NtWaitForSingleObject(state->overlapped.hEvent, TRUE, &(LARGE_INTEGER) { .QuadPart = (int64_t) 10 * -10000 });
}
NtClose(state->overlapped.hEvent);
closesocket(state->sockfd);
ffStrbufDestroy(&state->command);
return "GetOverlappedResultEx() failed or timeout";
}
FF_DEBUG("GetOverlappedResultEx succeeded, %u bytes sent", (unsigned) transfer);
ffStrbufDestroy(&state->command);
NtClose(state->overlapped.hEvent);
state->overlapped.hEvent = nullptr;
if (setsockopt(state->sockfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nullptr, 0) != 0) {
DWORD transfer, flags;
if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags)) {
FF_DEBUG("WSAGetOverlappedResult failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
closesocket(state->sockfd);
WSACloseEvent(state->overlapped.hEvent);
ffStrbufDestroy(&state->command);
return "WSAGetOverlappedResult() failed";
}
FF_DEBUG("WSAGetOverlappedResult succeeded, %u bytes sent", (unsigned) transfer);
ffStrbufDestroy(&state->command);
WSACloseEvent(state->overlapped.hEvent);
state->overlapped.hEvent = NULL;
if (setsockopt(state->sockfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) {
FF_DEBUG("Failed to update connect context: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
// Not a critical error, continue anyway
}
@@ -263,7 +275,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
}
FF_DEBUG("Starting data reception");
[[maybe_unused]] int recvCount = 0;
FF_A_UNUSED int recvCount = 0;
uint32_t contentLength = 0;
uint32_t headerEnd = 0;
@@ -281,8 +293,8 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
1,
&received,
&recvFlags,
nullptr,
nullptr);
NULL,
NULL);
if (recvResult == SOCKET_ERROR || received == 0) {
if (recvResult == 0 && received == 0) {
@@ -308,7 +320,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
// Check for Content-Length header to pre-allocate enough memory
const char* clHeader = strcasestr(buffer->chars, "Content-Length:");
if (clHeader) {
contentLength = (uint32_t) strtoul(clHeader + 15, nullptr, 10);
contentLength = (uint32_t) strtoul(clHeader + 15, NULL, 10);
if (contentLength > 0) {
if (contentLength > 1024 * 1024) { // 1MB limit to prevent excessive memory allocation and potential attacks
FF_DEBUG("Content-Length is too large: %u bytes, aborting", contentLength);
@@ -367,5 +379,5 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
}
#endif
return nullptr;
return NULL;
}
+10 -10
View File
@@ -5,19 +5,19 @@
#include <limits.h>
// Return start position of the inner key if the argument key belongs to the module specified, nullptr otherwise
// Return start position of the inner key if the argument key belongs to the module specified, NULL otherwise
const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName) {
assert(argumentKey && moduleName);
const char* subKey = argumentKey;
if (!(subKey[0] == '-' && subKey[1] == '-')) {
return nullptr;
return NULL;
}
subKey += 2;
uint32_t moduleNameLen = (uint32_t) strlen(moduleName);
if (strncasecmp(subKey, moduleName, moduleNameLen) != 0) {
return nullptr;
return NULL;
}
subKey += moduleNameLen;
@@ -27,7 +27,7 @@ const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName)
}
if (subKey[0] != '-') {
return nullptr;
return NULL;
}
subKey += 1;
@@ -36,7 +36,7 @@ const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName)
}
void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer) {
if (value == nullptr) {
if (value == NULL) {
fprintf(stderr, "Error: usage: %s <str>\n", argumentKey);
exit(477);
}
@@ -45,7 +45,7 @@ void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* b
}
uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value) {
if (value == nullptr) {
if (value == NULL) {
fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
exit(480);
}
@@ -61,7 +61,7 @@ uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value) {
}
int32_t ffOptionParseInt32(const char* argumentKey, const char* value) {
if (value == nullptr) {
if (value == NULL) {
fprintf(stderr, "Error: usage: %s <num>\n", argumentKey);
exit(480);
}
@@ -77,7 +77,7 @@ int32_t ffOptionParseInt32(const char* argumentKey, const char* value) {
}
int ffOptionParseEnum(const char* argumentKey, const char* requestedKey, FFKeyValuePair pairs[]) {
if (requestedKey == nullptr) {
if (requestedKey == NULL) {
fprintf(stderr, "Error: usage: %s <value>\n", argumentKey);
exit(476);
}
@@ -139,7 +139,7 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) {
} else if (value[0] == '@') {
// Xterm 256 color
++value;
char* pend = nullptr;
char* pend = NULL;
uint32_t color = (uint32_t) strtoul(value, &pend, 10);
if (pend == value || color > 255) {
fprintf(stderr, "Error: invalid 256 color code found: %s\n", value);
@@ -153,7 +153,7 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) {
} else if (value[0] == '#') {
// RGB color
++value;
char* pend = nullptr;
char* pend = NULL;
uint32_t rgb = (uint32_t) strtoul(value, &pend, 16);
if (pend == value) {
fprintf(stderr, "Error: invalid RGB color code found: %s\n", value);
+18 -18
View File
@@ -47,7 +47,7 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) {
}
#endif
return nullptr;
return NULL;
}
ffStrbufClear(result);
return "Executable not found";
@@ -60,13 +60,13 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) {
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) {
char buffer[MAX_PATH + 1];
DWORD length = SearchPathA(nullptr, name, ".exe", sizeof(buffer), buffer, nullptr);
DWORD length = SearchPathA(NULL, name, ".exe", sizeof(buffer), buffer, NULL);
if (length == 0) {
ffStrbufClear(result);
return "Executable not found";
}
ffStrbufSetS(result, buffer);
return nullptr;
return NULL;
}
static inline int winerr2Errno(DWORD err) {
@@ -93,7 +93,7 @@ static inline int winerr2Errno(DWORD err) {
char* frealpath(HANDLE hFile, char* resolved_name) {
if (__builtin_expect(hFile == INVALID_HANDLE_VALUE || !hFile, false)) {
errno = EINVAL;
return nullptr;
return NULL;
}
wchar_t resolvedNameW[MAX_PATH + 4]; /* +4 for "\\\\?\\" prefix */
@@ -101,11 +101,11 @@ char* frealpath(HANDLE hFile, char* resolved_name) {
if (lenW == 0) {
errno = winerr2Errno(GetLastError());
return nullptr;
return NULL;
}
if (lenW >= ARRAY_SIZE(resolvedNameW)) {
errno = E2BIG;
return nullptr;
return NULL;
}
lenW++; // Include null terminator
@@ -126,7 +126,7 @@ char* frealpath(HANDLE hFile, char* resolved_name) {
ULONG outBytes = 0;
if (!NT_SUCCESS(RtlUnicodeToUTF8N(resolved_name, MAX_PATH, &outBytes, srcW, (ULONG) (srcLenW * sizeof(wchar_t))))) {
errno = E2BIG;
return nullptr;
return NULL;
}
} else {
/* UTF-8 worst-case: up to 4 bytes per UTF-16 code unit */
@@ -135,13 +135,13 @@ char* frealpath(HANDLE hFile, char* resolved_name) {
if (!NT_SUCCESS(RtlUnicodeToUTF8N(tmp, (ULONG) sizeof(tmp), &outBytes, srcW, (ULONG) (srcLenW * sizeof(wchar_t))))) {
errno = E2BIG;
return nullptr;
return NULL;
}
resolved_name = (char*) malloc(outBytes);
if (!resolved_name) {
errno = ENOMEM;
return nullptr;
return NULL;
}
memcpy(resolved_name, tmp, outBytes);
@@ -153,7 +153,7 @@ char* frealpath(HANDLE hFile, char* resolved_name) {
char* realpath(const char* __restrict file_name, char* __restrict resolved_name) {
if (!file_name) {
errno = EINVAL;
return nullptr;
return NULL;
}
wchar_t fileNameW[MAX_PATH];
@@ -161,21 +161,21 @@ char* realpath(const char* __restrict file_name, char* __restrict resolved_name)
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(fileNameW, (ULONG) sizeof(fileNameW), &lenBytes, file_name, (ULONG) strlen(file_name) + 1))) {
errno = EINVAL;
return nullptr;
return NULL;
}
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW(
fileNameW,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
nullptr);
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
errno = winerr2Errno(GetLastError());
return nullptr;
return NULL;
}
return frealpath(hFile, resolved_name);
@@ -189,13 +189,13 @@ ssize_t freadlink(HANDLE hFile, char* buf, size_t bufsiz) {
alignas(REPARSE_DATA_BUFFER) BYTE reparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
DWORD bytesReturned = 0;
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, nullptr)) {
if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, reparseBuf, (DWORD) sizeof(reparseBuf), &bytesReturned, NULL)) {
errno = winerr2Errno(GetLastError());
return -1;
}
REPARSE_DATA_BUFFER* rp = (REPARSE_DATA_BUFFER*) reparseBuf;
const wchar_t* targetW = nullptr;
const wchar_t* targetW = NULL;
USHORT targetBytes = 0;
if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
@@ -262,10 +262,10 @@ ssize_t readlink(const char* path, char* buf, size_t bufsiz) {
pathW,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
nullptr);
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
errno = winerr2Errno(GetLastError());
+3 -3
View File
@@ -17,7 +17,7 @@ static void appendOutputColor(FFstrbuf* buffer, const FFModuleArgs* module) {
const char* ffPercentParseTypeJsonConfig(yyjson_val* jsonVal, FFPercentageTypeFlags* result) {
if (yyjson_is_uint(jsonVal)) {
*result = (FFPercentageTypeFlags) yyjson_get_uint(jsonVal);
return nullptr;
return NULL;
}
if (yyjson_is_arr(jsonVal)) {
FFPercentageTypeFlags flags = 0;
@@ -45,7 +45,7 @@ const char* ffPercentParseTypeJsonConfig(yyjson_val* jsonVal, FFPercentageTypeFl
}
*result = flags;
return nullptr;
return NULL;
}
return "Error: usage: percent.type must be a number or an array of strings";
@@ -90,7 +90,7 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConf
bool monochrome = (percentType & FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT) || !autoColorElapsed;
if (!options->pipe && options->barColorElapsed.length > 0 && monochrome) {
const char* color = nullptr;
const char* color = NULL;
if (!autoColorElapsed) {
color = options->barColorElapsed.chars;
} else if (green <= yellow) {
+3 -4
View File
@@ -7,7 +7,7 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
ffLogoPrintLine();
// This is used by --set-keyless, in this case we want neither the module name nor the separator
if (moduleName == nullptr) {
if (moduleName == NULL) {
return;
}
@@ -35,7 +35,7 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_STRING) {
ffPrintCharTimes(' ', instance.config.display.keyType >> FF_MODULE_KEY_TYPE_SPACE_SHIFT);
// nullptr check is required for modules with custom keys, e.g. disk with the folder path
// NULL check is required for modules with custom keys, e.g. disk with the folder path
if ((printType & FF_PRINT_TYPE_NO_CUSTOM_KEY) || !moduleArgs || moduleArgs->key.length == 0) {
fputs(moduleName, stdout);
@@ -47,7 +47,6 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
FF_PARSE_FORMAT_STRING_CHECKED(&key, &moduleArgs->key, ((FFformatarg[]) {
FF_ARG(moduleIndex, "index"),
FF_ARG(moduleArgs->keyIcon, "icon"),
FF_ARG(moduleName, "module-name"),
}));
ffStrbufWriteTo(&key, stdout);
}
@@ -85,7 +84,7 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
bool ffPrintFormat(const char* moduleName, uint8_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments) {
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
bool success;
if (__builtin_expect(moduleArgs != nullptr, 1)) {
if (__builtin_expect(moduleArgs != NULL, 1)) {
success = ffParseFormatString(&buffer, &moduleArgs->outputFormat, numArgs, arguments);
} else {
ffStrbufSetStatic(&buffer, "undefined format");
+38 -35
View File
@@ -28,6 +28,7 @@
#elif defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#elif defined(__NetBSD__)
#include <sys/types.h>
#include <sys/sysctl.h>
@@ -78,7 +79,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
posix_spawn_file_actions_adddup2(&file_actions, pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO);
posix_spawn_file_actions_adddup2(&file_actions, nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO);
static char* oldLang = nullptr;
static char* oldLang = NULL;
static int langIndex = -1;
if (langIndex >= 0) {
@@ -94,7 +95,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
}
}
if (langIndex < 0) {
for (int i = 0; environ[i] != nullptr; i++) {
for (int i = 0; environ[i] != NULL; i++) {
if (ffStrStartsWith(environ[i], "LANG=")) {
langIndex = i;
const char* langValue = environ[i] + 5; // Skip "LANG="
@@ -111,7 +112,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
}
}
int ret = posix_spawnp(&childPid, argv[0], &file_actions, nullptr, argv, environ);
int ret = posix_spawnp(&childPid, argv[0], &file_actions, NULL, argv, environ);
if (oldLang) {
environ[langIndex] = oldLang;
@@ -152,7 +153,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
close(pipes[1]);
outHandle->pid = childPid;
outHandle->pipeRead = pipes[0];
return nullptr;
return NULL;
}
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
@@ -172,11 +173,11 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
int pollret = poll(&pollfd, 1, timeout);
if (pollret == 0) {
kill(childPid, SIGTERM);
waitpid(childPid, nullptr, 0);
waitpid(childPid, NULL, 0);
return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)";
} else if (pollret < 0 || (pollfd.revents & POLLERR)) {
kill(childPid, SIGTERM);
waitpid(childPid, nullptr, 0);
waitpid(childPid, NULL, 0);
return pollret < 0
? "poll(&pollfd, 1, timeout) error: pollret < 0"
: "poll(&pollfd, 1, timeout) error: pollfd.revents & POLLERR";
@@ -196,9 +197,9 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
return "command not found";
}
// We only handle 127 as an error. See `getTerminalVersionUrxvt` in `terminalshell.c`
return nullptr;
return NULL;
}
return nullptr;
return NULL;
} else if (nRead < 0) {
break;
}
@@ -277,11 +278,11 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
size_t len = 0;
int mibs[] = { CTL_KERN, KERN_PROCARGS2, pid };
if (sysctl(mibs, ARRAY_SIZE(mibs), nullptr, &len, nullptr, 0) == 0) { // try get arg0
if (sysctl(mibs, ARRAY_SIZE(mibs), NULL, &len, NULL, 0) == 0) { // try get arg0
// don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version.
len++;
FF_AUTO_FREE char* const procArgs2 = malloc(len);
if (sysctl(mibs, ARRAY_SIZE(mibs), procArgs2, &len, nullptr, 0) == 0) {
if (sysctl(mibs, ARRAY_SIZE(mibs), procArgs2, &len, NULL, 0) == 0) {
// https://gist.github.com/nonowarn/770696#file-getargv-c-L46
uint32_t argc = *(uint32_t*) procArgs2;
const char* realExePath = procArgs2 + sizeof(argc);
@@ -353,7 +354,7 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
4,
args,
&size,
nullptr,
NULL,
0) == 0)
ffStrbufSetNS(exePath, (uint32_t) (size - 1), args);
@@ -373,7 +374,7 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
4,
args,
&size,
nullptr,
NULL,
0) == 0) {
char* arg0 = args;
size_t arg0Len = strlen(args);
@@ -422,13 +423,12 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
#elif defined(__OpenBSD__)
char argvBuf[ARG_MAX];
size_t argvSize = sizeof(argvBuf);
int argvMib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV };
if (sysctl(argvMib, ARRAY_SIZE(argvMib), argvBuf, &argvSize, nullptr, 0) == 0) {
// The buffer is filled with an array of char pointers followed by the strings themselves
char** argv = (char**) argvBuf;
if (argv[0] && (char*) argv[0] >= argvBuf && (char*) argv[0] < argvBuf + argvSize) {
kvm_t* kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &count);
if (proc) {
char** argv = kvm_getargv(kd, proc, 0);
if (argv) {
const char* arg0 = argv[0];
if (arg0[0] == '-') {
arg0++;
@@ -436,6 +436,7 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
ffStrbufSetS(exe, arg0);
}
}
kvm_close(kd);
#elif defined(__HAIKU__)
@@ -487,7 +488,7 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
}
buf[nRead] = '\0'; // pid (comm) state ppid pgrp session tty
const char* pState = nullptr;
const char* pState = NULL;
{
// comm in `/proc/pid/stat` is not encoded, and may contain ' ', ')' or even `\n`
@@ -541,7 +542,7 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
struct kinfo_proc proc;
size_t size = sizeof(proc);
if (sysctl(
(int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, nullptr, 0)) {
(int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, NULL, 0)) {
return "sysctl(KERN_PROC_PID) failed";
}
@@ -567,7 +568,7 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
struct kinfo_proc proc;
size_t size = sizeof(proc);
if (sysctl(
(int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, nullptr, 0)) {
(int[]) { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }, 4, &proc, &size, NULL, 0)) {
return "sysctl(KERN_PROC_PID) failed";
}
@@ -579,7 +580,7 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
if (proc.ki_tdev != NODEV && proc.ki_flag & P_CONTROLT) {
const char* ttyName = devname(proc.ki_tdev, S_IFCHR);
if (ffStrStartsWith(ttyName, "pts/")) {
*tty = (int32_t) strtol(ttyName + strlen("pts/"), nullptr, 10);
*tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10);
} else {
*tty = -1;
}
@@ -593,7 +594,7 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
struct kinfo_proc2 proc;
size_t size = sizeof(proc);
if (sysctl(
(int[]) { CTL_KERN, KERN_PROC2, KERN_PROC_PID, pid, sizeof(proc), 1 }, 6, &proc, &size, nullptr, 0) != 0) {
(int[]) { CTL_KERN, KERN_PROC2, KERN_PROC_PID, pid, sizeof(proc), 1 }, 6, &proc, &size, NULL, 0) != 0) {
return "sysctl(KERN_PROC_PID) failed";
}
@@ -605,7 +606,7 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
if (proc.p_flag & P_CONTROLT) {
const char* ttyName = devname(proc.p_tdev, S_IFCHR);
if (ffStrStartsWith(ttyName, "pts/")) {
*tty = (int32_t) strtol(ttyName + strlen("pts/"), nullptr, 10);
*tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10);
} else {
*tty = -1;
}
@@ -632,19 +633,21 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
#elif defined(__OpenBSD__)
struct kinfo_proc proc;
size_t size = sizeof(proc);
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, (int) sizeof(struct kinfo_proc), 1 };
if (sysctl(mib, ARRAY_SIZE(mib), &proc, &size, nullptr, 0) == 0) {
ffStrbufSetS(name, proc.p_comm);
kvm_t* kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &count);
if (proc) {
ffStrbufSetS(name, proc->p_comm);
if (ppid) {
*ppid = proc.p_ppid;
*ppid = proc->p_ppid;
}
if (tty) {
*tty = (int) proc.p_tdev;
*tty = (int) proc->p_tdev;
}
} else {
return "sysctl(KERN_PROC_PID) failed";
}
kvm_close(kd);
if (!proc) {
return "kvm_getprocs() failed";
}
#elif defined(__HAIKU__)
@@ -665,5 +668,5 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
#endif
return nullptr;
return NULL;
}
+25 -61
View File
@@ -15,7 +15,7 @@ static void argvToCmdline(char* const argv[], FFstrbuf* result) {
// From https://gist.github.com/jin-x/cdd641d98887524b091fb1f82a68717d
FF_STRBUF_AUTO_DESTROY temp = ffStrbufCreate();
for (int i = 0; argv[i] != nullptr; i++) {
for (int i = 0; argv[i] != NULL; i++) {
ffStrbufSetS(&temp, argv[i]);
// Add slash (\) before double quotes (") and duplicate slashes before it
for (
@@ -48,41 +48,6 @@ static void argvToCmdline(char* const argv[], FFstrbuf* result) {
}
}
static wchar_t* createChildEnvironment(void) {
auto pparams = ffGetPeb()->ProcessParameters;
const wchar_t* readStart = pparams->Environment;
if (!readStart) {
return nullptr;
}
RtlAcquirePebLock();
wchar_t* result = malloc(pparams->EnvironmentSize + sizeof(L"LANG=C.UTF-8"));
const wchar_t* entry = readStart;
wchar_t* writeStart = result;
// Copy all environment variables except LANG
while (*entry) {
assert((const uint8_t*) entry <= (const uint8_t*) pparams->Environment + pparams->EnvironmentSize);
size_t entryLength = wcslen(entry);
if (entryLength >= strlen("LANG=") && memcmp(entry, L"LANG=", strlen("LANG=") * sizeof(wchar_t)) == 0) {
memcpy(writeStart, readStart, (size_t) (entry - readStart) * sizeof(wchar_t));
writeStart += (size_t) (entry - readStart);
readStart = entry + entryLength + 1;
}
entry += entryLength + 1;
}
// Copy the remaining environment variables
memcpy(writeStart, readStart, (size_t) (entry - readStart) * sizeof(wchar_t));
writeStart += (size_t) (entry - readStart);
// Add LANG=C.UTF-8 and double null terminator
memcpy(writeStart, L"LANG=C.UTF-8\0", sizeof(L"LANG=C.UTF-8\0"));
RtlReleasePebLock();
return result;
}
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle) {
const int32_t timeout = instance.config.general.processingTimeout;
@@ -98,7 +63,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
FF_PIPE_BUFSIZ,
FF_PIPE_BUFSIZ,
0,
nullptr);
NULL);
if (hChildPipeRead == INVALID_HANDLE_VALUE) {
return "CreateNamedPipeW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
}
@@ -109,12 +74,12 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
0,
&(SECURITY_ATTRIBUTES) {
.nLength = sizeof(SECURITY_ATTRIBUTES),
.lpSecurityDescriptor = nullptr,
.lpSecurityDescriptor = NULL,
.bInheritHandle = TRUE,
},
OPEN_EXISTING,
0,
nullptr);
NULL);
if (hChildPipeWrite == INVALID_HANDLE_VALUE) {
return "CreateFileW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
}
@@ -132,29 +97,28 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
siStartInfo.hStdError = ffGetNullFD();
}
FF_AUTO_FREE wchar_t* cmdline = nullptr;
FF_AUTO_FREE wchar_t* cmdline = NULL;
{
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
argvToCmdline(argv, &buf);
uint32_t cmdlineBytes = (buf.length + 1) * sizeof(wchar_t);
cmdline = malloc(cmdlineBytes);
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(cmdline, cmdlineBytes, nullptr, buf.chars, buf.length + 1))) {
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(cmdline, cmdlineBytes, NULL, buf.chars, buf.length + 1))) {
return "RtlUTF8ToUnicodeN() failed";
}
}
FF_AUTO_FREE wchar_t* environment = createChildEnvironment();
BOOL success = CreateProcessW(
nullptr, // application name
cmdline, // command line
nullptr, // process security attributes
nullptr, // primary thread security attributes
TRUE, // handles are inherited
CREATE_UNICODE_ENVIRONMENT, // creation flags
environment, // child environment
nullptr, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo // receives PROCESS_INFORMATION
NULL, // application name
cmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo // receives PROCESS_INFORMATION
);
NtClose(hChildPipeWrite);
@@ -170,7 +134,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
outHandle->pipeRead = hChildPipeRead;
hChildPipeRead = INVALID_HANDLE_VALUE; // ownership transferred, don't close it
return nullptr;
return NULL;
}
static void terminateChildProcess(HANDLE hProcess, HANDLE hChildPipeRead, HANDLE hReadEvent, IO_STATUS_BLOCK* piosb) {
@@ -190,11 +154,11 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
int32_t timeout = instance.config.general.processingTimeout;
FF_AUTO_CLOSE_FD HANDLE hProcess = handle->pid;
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = handle->pipeRead;
FF_AUTO_CLOSE_FD HANDLE hReadEvent = nullptr;
FF_AUTO_CLOSE_FD HANDLE hReadEvent = NULL;
handle->pid = INVALID_HANDLE_VALUE;
handle->pipeRead = INVALID_HANDLE_VALUE;
if (timeout >= 0 && !NT_SUCCESS(NtCreateEvent(&hReadEvent, EVENT_ALL_ACCESS, nullptr, SynchronizationEvent, FALSE))) {
if (timeout >= 0 && !NT_SUCCESS(NtCreateEvent(&hReadEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) {
return "NtCreateEvent() failed";
}
@@ -205,13 +169,13 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
NTSTATUS status = NtReadFile(
hChildPipeRead,
hReadEvent,
nullptr,
nullptr,
NULL,
NULL,
&iosb,
str,
(ULONG) sizeof(str),
nullptr,
nullptr);
NULL,
NULL);
if (status == STATUS_PENDING) {
switch (NtWaitForSingleObject(hReadEvent, FALSE, &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 })) {
case STATUS_WAIT_0:
@@ -234,7 +198,7 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer) {
}
if (!NT_SUCCESS(status)) {
terminateChildProcess(hProcess, hChildPipeRead, nullptr, &iosb);
terminateChildProcess(hProcess, hChildPipeRead, NULL, &iosb);
return "NtReadFile(hChildPipeRead) failed";
}
@@ -255,7 +219,7 @@ exit: {
}
}
return nullptr;
return NULL;
}
bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui) {

Some files were not shown because too many files have changed in this diff Show More