aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-17 12:10:22 +0200
committerDanilo M. <danix@danix.xyz>2026-09-17 12:10:22 +0200
commit518b2969108ed3d728e562614c05e40400129240 (patch)
tree502a61a38db9d9ee35eb0e3f770e28596847ace3 /lib
parent3690d0ee9ceb5c0287cfe72eb4dcfd8f43324f0e (diff)
downloadconky-theme-udt-518b2969108ed3d728e562614c05e40400129240.tar.gz
conky-theme-udt-518b2969108ed3d728e562614c05e40400129240.zip
feat: parse df and du output for the disk and cache cards
Diffstat (limited to 'lib')
-rw-r--r--lib/data.lua76
1 files changed, 76 insertions, 0 deletions
diff --git a/lib/data.lua b/lib/data.lua
index 9bd5433..acf7a0f 100644
--- a/lib/data.lua
+++ b/lib/data.lua
@@ -152,4 +152,80 @@ function M.sensor_raw(chip, file)
return tonumber(s:match('^%s*(-?%d+)'))
end
+-- Parse `df -P -B1` output into a list of filesystems.
+--
+-- The mountpoint is taken as the LAST field, not the sixth. df -P guarantees
+-- one line per filesystem with the mountpoint last, and an NFS device reads
+-- 'server:/export', so counting fields from the left is fragile in a way
+-- counting from the right is not.
+--
+-- Sizes are bytes because the sampler passes -B1. Never parse `df -h` output
+-- here: the user's shell aliases df to df -h, so a sampler calling bare df
+-- would hand this function '1.6G' where it expects an integer.
+function M.df_parse(text)
+ local out = {}
+ if type(text) ~= 'string' then return out end
+ for line in text:gmatch('[^\n]+') do
+ -- A data row ends in 'NN% /some/path'. The header ends in 'Mounted on',
+ -- which fails the percent match, so it is skipped without a special case.
+ local size, used, pct, mount = line:match('(%d+)%s+(%d+)%s+%d+%s+(%d+)%%%s+(%S+)%s*$')
+ if size then
+ -- The device is kept so a caller can tell an NFS mount from a local one
+ -- and recover its server, which is how the disks card labels the two
+ -- shares without hardcoding an address in the repo.
+ local dev = line:match('^(%S+)')
+ out[#out + 1] = {
+ mount = mount,
+ dev = dev,
+ host = dev and dev:match('^([^/:]+):') or nil,
+ size = tonumber(size),
+ used = tonumber(used),
+ pct = tonumber(pct),
+ }
+ end
+ end
+ return out
+end
+
+-- Human-readable size to bytes: '1.1G' -> 1181116006.
+--
+-- Needed because du -sh is what the sampler runs (its output is also what the
+-- card displays), and 'biggest' has to be decided numerically: sorted as
+-- strings, '245M' beats '1.1G'.
+local SUFFIX = { K = 1024, M = 1024^2, G = 1024^3, T = 1024^4, P = 1024^5 }
+
+function M.human_bytes(s)
+ if type(s) ~= 'string' then return nil end
+ local n, suf = s:match('^%s*([%d%.]+)%s*([KMGTP]?)')
+ n = tonumber(n)
+ if not n then return nil end
+ return n * (SUFFIX[suf] or 1)
+end
+
+-- Parse `du -sh` output: the first line is the total, the rest are children
+-- already sorted largest-first by the sampler. Returns nil when there is
+-- nothing usable, so the widget can draw its "no data" state.
+function M.du_parse(text)
+ if type(text) ~= 'string' or text == '' then return nil end
+ local lines = {}
+ for line in text:gmatch('[^\n]+') do lines[#lines + 1] = line end
+ if #lines == 0 then return nil end
+
+ local function entry(line)
+ local label, path = line:match('^(%S+)%s+(.+)$')
+ if not label then return nil end
+ return { label = label, bytes = M.human_bytes(label) or 0,
+ name = path:match('([^/]+)/?$') or path, path = path }
+ end
+
+ local total = entry(lines[1])
+ if not total then return nil end
+ local items = {}
+ for i = 2, #lines do
+ local e = entry(lines[i])
+ if e then items[#items + 1] = e end
+ end
+ return { total = total, items = items }
+end
+
return M