aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpml68 <contact@pml68.dev>2025-09-16 21:48:56 +0200
committerpml68 <contact@pml68.dev>2025-09-16 22:15:01 +0200
commitfd3a7cc487ad5dc36d99ccf46f9e3c6461687f99 (patch)
treee1627f8ea07510d06a99ea55f275ae3b6e1919a7
parentchore: bump dwm and dmenu versions (diff)
downloadsuckless-setup-fd3a7cc487ad5dc36d99ccf46f9e3c6461687f99.tar.gz
chore: sync slstatus with upstream
-rw-r--r--slstatus/LICENSE7
-rw-r--r--slstatus/README2
-rw-r--r--slstatus/components/ip.c26
-rw-r--r--slstatus/components/keymap.c4
-rw-r--r--slstatus/components/ram.c61
-rw-r--r--slstatus/components/wifi.c258
-rw-r--r--slstatus/config.def.h14
-rw-r--r--slstatus/config.h14
-rw-r--r--slstatus/config.mk2
-rw-r--r--slstatus/slstatus.c1
-rw-r--r--slstatus/slstatus.h1
-rw-r--r--slstatus/util.c17
-rw-r--r--slstatus/util.h2
13 files changed, 308 insertions, 101 deletions
diff --git a/slstatus/LICENSE b/slstatus/LICENSE
index 8bee9c8..3fcac59 100644
--- a/slstatus/LICENSE
+++ b/slstatus/LICENSE
@@ -1,6 +1,6 @@
ISC License
-Copyright 2016-2022 Aaron Marcher <me@drkhsh.at>
+Copyright 2016-2025 Aaron Marcher <me@drkhsh.at>
Copyright 2016 Roy Freytag <rfreytag@hs-mittweida.de>
Copyright 2016 Vincent Loupmon <vincentloupmon@gmail.com>
@@ -21,7 +21,7 @@ Copyright 2018 Ian Remmler <ian@remmler.org>
Copyright 2016-2019 Joerg Jung <jung@openbsd.org>
Copyright 2019 Ryan Kes <alrayyes@gmail.com>
Copyright 2019 Cem Keylan <cem@ckyln.com>
-Copyright 2019 Dimitris Papastamos <dsp@2f30.org>
+Copyright 2019 Spiros Thanasoulas <dsp@2f30.org>
Copyright 2019-2022 Ingo Feinerer <feinerer@logic.at>
Copyright 2020 Alexandre Ratchov <alex@caoua.org>
Copyright 2020 Mart Lubbers <mart@martlubbers.net>
@@ -29,6 +29,9 @@ Copyright 2020 Daniel Moch <daniel@danielmoch.com>
Copyright 2022 Nickolas Raymond Kaczynski <nrk@disroot.org>
Copyright 2022 Patrick Iacob <iacobp@oregonstate.edu>
Copyright 2021-2022 Steven Ward <planet36@gmail.com>
+Copyright 2025 Joakim Sindholt <opensource@zhasha.com>
+Copyright 2025 Al <eirann@disroot.org>
+Copyright 2025 sewn <sewn@disroot.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/slstatus/README b/slstatus/README
index 12d38bf..4d592bb 100644
--- a/slstatus/README
+++ b/slstatus/README
@@ -18,7 +18,7 @@ Features
- Available entropy
- Username/GID/UID
- Hostname
-- IP address (IPv4 and IPv6)
+- IP address (IPv4 and IPv6), interface status
- Kernel version
- Keyboard indicators
- Keymap
diff --git a/slstatus/components/ip.c b/slstatus/components/ip.c
index 9476549..2cdad46 100644
--- a/slstatus/components/ip.c
+++ b/slstatus/components/ip.c
@@ -1,6 +1,7 @@
/* See LICENSE file for copyright and license details. */
#include <ifaddrs.h>
#include <netdb.h>
+#include <net/if.h>
#include <stdio.h>
#include <string.h>
#if defined(__OpenBSD__)
@@ -59,3 +60,28 @@ ipv6(const char *interface)
{
return ip(interface, AF_INET6);
}
+
+const char *
+up(const char *interface)
+{
+ struct ifaddrs *ifaddr, *ifa;
+
+ if (getifaddrs(&ifaddr) < 0) {
+ warn("getifaddrs:");
+ return NULL;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+
+ if (!strcmp(ifa->ifa_name, interface)) {
+ freeifaddrs(ifaddr);
+ return ifa->ifa_flags & IFF_UP ? "up" : "down";
+ }
+ }
+
+ freeifaddrs(ifaddr);
+
+ return NULL;
+}
diff --git a/slstatus/components/keymap.c b/slstatus/components/keymap.c
index f8a2a47..22224f3 100644
--- a/slstatus/components/keymap.c
+++ b/slstatus/components/keymap.c
@@ -29,8 +29,8 @@ get_layout(char *syms, int grp_num)
int grp;
layout = NULL;
- tok = strtok(syms, "+:");
- for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:")) {
+ tok = strtok(syms, "+:_");
+ for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:_")) {
if (!valid_layout_or_variant(tok)) {
continue;
} else if (strlen(tok) == 1 && isdigit(tok[0])) {
diff --git a/slstatus/components/ram.c b/slstatus/components/ram.c
index 15c4b74..bf71dcf 100644
--- a/slstatus/components/ram.c
+++ b/slstatus/components/ram.c
@@ -11,36 +11,45 @@
ram_free(const char *unused)
{
uintmax_t free;
+ FILE *fp;
- if (pscanf("/proc/meminfo",
- "MemTotal: %ju kB\n"
- "MemFree: %ju kB\n"
- "MemAvailable: %ju kB\n",
- &free, &free, &free) != 3)
+ if (!(fp = fopen("/proc/meminfo", "r")))
return NULL;
+ if (lscanf(fp, "MemFree:", "%ju kB", &free) != 1) {
+ fclose(fp);
+ return NULL;
+ }
+
+ fclose(fp);
return fmt_human(free * 1024, 1024);
}
const char *
ram_perc(const char *unused)
{
- uintmax_t total, free, buffers, cached;
+ uintmax_t total, free, buffers, cached, shmem, sreclaimable;
int percent;
+ FILE *fp;
+
+ if (!(fp = fopen("/proc/meminfo", "r")))
+ return NULL;
- if (pscanf("/proc/meminfo",
- "MemTotal: %ju kB\n"
- "MemFree: %ju kB\n"
- "MemAvailable: %ju kB\n"
- "Buffers: %ju kB\n"
- "Cached: %ju kB\n",
- &total, &free, &buffers, &buffers, &cached) != 5)
+ if (lscanf(fp, "MemTotal:", "%ju kB", &total) != 1 ||
+ lscanf(fp, "MemFree:", "%ju kB", &free) != 1 ||
+ lscanf(fp, "Buffers:", "%ju kB", &buffers) != 1 ||
+ lscanf(fp, "Cached:", "%ju kB", &cached) != 1 ||
+ lscanf(fp, "Shmem:", "%ju kB", &shmem) != 1 ||
+ lscanf(fp, "SReclaimable:", "%ju kB", &sreclaimable) != 1) {
+ fclose(fp);
return NULL;
+ }
+ fclose(fp);
if (total == 0)
return NULL;
- percent = 100 * ((total - free) - (buffers + cached)) / total;
+ percent = 100 * (total - free - buffers - cached - sreclaimable + shmem) / total;
return bprintf("%d", percent);
}
@@ -59,18 +68,24 @@
const char *
ram_used(const char *unused)
{
- uintmax_t total, free, buffers, cached, used;
+ uintmax_t total, free, buffers, cached, used, shmem, sreclaimable;
+ FILE *fp;
+
+ if (!(fp = fopen("/proc/meminfo", "r")))
+ return NULL;
- if (pscanf("/proc/meminfo",
- "MemTotal: %ju kB\n"
- "MemFree: %ju kB\n"
- "MemAvailable: %ju kB\n"
- "Buffers: %ju kB\n"
- "Cached: %ju kB\n",
- &total, &free, &buffers, &buffers, &cached) != 5)
+ if (lscanf(fp, "MemTotal:", "%ju kB", &total) != 1 ||
+ lscanf(fp, "MemFree:", "%ju kB", &free) != 1 ||
+ lscanf(fp, "Buffers:", "%ju kB", &buffers) != 1 ||
+ lscanf(fp, "Cached:", "%ju kB", &cached) != 1 ||
+ lscanf(fp, "Shmem:", "%ju kB", &shmem) != 1 ||
+ lscanf(fp, "SReclaimable:", "%ju kB", &sreclaimable) != 1) {
+ fclose(fp);
return NULL;
+ }
+ fclose(fp);
- used = (total - free - buffers - cached);
+ used = total - free - buffers - cached - sreclaimable + shmem;
return fmt_human(used * 1024, 1024);
}
#elif defined(__OpenBSD__)
diff --git a/slstatus/components/wifi.c b/slstatus/components/wifi.c
index 4543d32..23af201 100644
--- a/slstatus/components/wifi.c
+++ b/slstatus/components/wifi.c
@@ -15,86 +15,232 @@
(2 * (rssi + 100)))
#if defined(__linux__)
- #include <limits.h>
- #include <linux/wireless.h>
+ #include <stdint.h>
+ #include <net/if.h>
+ #include <linux/netlink.h>
+ #include <linux/genetlink.h>
+ #include <linux/nl80211.h>
+
+ static int nlsock = -1;
+ static uint32_t seq = 1;
+ static char resp[4096];
+
+ static char *
+ findattr(int attr, const char *p, const char *e, size_t *len)
+ {
+ while (p < e) {
+ struct nlattr nla;
+ memcpy(&nla, p, sizeof(nla));
+ if (nla.nla_type == attr) {
+ *len = nla.nla_len - NLA_HDRLEN;
+ return (char *)(p + NLA_HDRLEN);
+ }
+ p += NLA_ALIGN(nla.nla_len);
+ }
+ return NULL;
+ }
- #define NET_OPERSTATE "/sys/class/net/%s/operstate"
+ static uint16_t
+ nl80211fam(void)
+ {
+ static const char family[] = "nl80211";
+ static uint16_t id;
+ ssize_t r;
+ size_t len;
+ char ctrl[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(sizeof(family))] = {0}, *p = ctrl;
+
+ if (id)
+ return id;
+
+ memcpy(p, &(struct nlmsghdr){
+ .nlmsg_len = sizeof(ctrl),
+ .nlmsg_type = GENL_ID_CTRL,
+ .nlmsg_flags = NLM_F_REQUEST,
+ .nlmsg_seq = seq++,
+ .nlmsg_pid = 0,
+ }, sizeof(struct nlmsghdr));
+ p += NLMSG_HDRLEN;
+ memcpy(p, &(struct genlmsghdr){
+ .cmd = CTRL_CMD_GETFAMILY,
+ .version = 1,
+ }, sizeof(struct genlmsghdr));
+ p += GENL_HDRLEN;
+ memcpy(p, &(struct nlattr){
+ .nla_len = NLA_HDRLEN+sizeof(family),
+ .nla_type = CTRL_ATTR_FAMILY_NAME,
+ }, sizeof(struct nlattr));
+ p += NLA_HDRLEN;
+ memcpy(p, family, sizeof(family));
+
+ if (nlsock < 0)
+ nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
+ if (nlsock < 0) {
+ warn("socket 'AF_NETLINK':");
+ return 0;
+ }
+ if (send(nlsock, ctrl, sizeof(ctrl), 0) != sizeof(ctrl)) {
+ warn("send 'AF_NETLINK':");
+ return 0;
+ }
+ r = recv(nlsock, resp, sizeof(resp), 0);
+ if (r < 0) {
+ warn("recv 'AF_NETLINK':");
+ return 0;
+ }
+ if ((size_t)r <= sizeof(ctrl))
+ return 0;
+ p = findattr(CTRL_ATTR_FAMILY_ID, resp + sizeof(ctrl), resp + r, &len);
+ if (p && len == 2)
+ memcpy(&id, p, 2);
+
+ return id;
+ }
+
+ static int
+ ifindex(const char *interface)
+ {
+ static struct ifreq ifr;
+ static int ifsock = -1;
+
+ if (ifsock < 0)
+ ifsock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (ifsock < 0) {
+ warn("socket 'AF_UNIX':");
+ return -1;
+ }
+ if (strcmp(ifr.ifr_name, interface) != 0) {
+ strcpy(ifr.ifr_name, interface);
+ if (ioctl(ifsock, SIOCGIFINDEX, &ifr) != 0) {
+ warn("ioctl 'SIOCGIFINDEX':");
+ return -1;
+ }
+ }
+ return ifr.ifr_ifindex;
+ }
const char *
- wifi_perc(const char *interface)
+ wifi_essid(const char *interface)
{
- int cur;
- size_t i;
- char *p, *datastart;
- char path[PATH_MAX];
- char status[5];
- FILE *fp;
-
- if (esnprintf(path, sizeof(path), NET_OPERSTATE, interface) < 0)
- return NULL;
- if (!(fp = fopen(path, "r"))) {
- warn("fopen '%s':", path);
+ uint16_t fam = nl80211fam();
+ ssize_t r;
+ size_t len;
+ char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req;
+ int idx = ifindex(interface);
+ if (!fam) {
+ fprintf(stderr, "nl80211 family not found\n");
return NULL;
}
- p = fgets(status, 5, fp);
- fclose(fp);
- if (!p || strcmp(status, "up\n") != 0)
+ if (idx < 0) {
+ fprintf(stderr, "interface %s not found\n", interface);
return NULL;
+ }
- if (!(fp = fopen("/proc/net/wireless", "r"))) {
- warn("fopen '/proc/net/wireless':");
+ memcpy(p, &(struct nlmsghdr){
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = fam,
+ .nlmsg_flags = NLM_F_REQUEST,
+ .nlmsg_seq = seq++,
+ .nlmsg_pid = 0,
+ }, sizeof(struct nlmsghdr));
+ p += NLMSG_HDRLEN;
+ memcpy(p, &(struct genlmsghdr){
+ .cmd = NL80211_CMD_GET_INTERFACE,
+ .version = 1,
+ }, sizeof(struct genlmsghdr));
+ p += GENL_HDRLEN;
+ memcpy(p, &(struct nlattr){
+ .nla_len = NLA_HDRLEN+4,
+ .nla_type = NL80211_ATTR_IFINDEX,
+ }, sizeof(struct nlattr));
+ p += NLA_HDRLEN;
+ memcpy(p, &(uint32_t){idx}, 4);
+
+ if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
+ warn("send 'AF_NETLINK':");
return NULL;
}
-
- for (i = 0; i < 3; i++)
- if (!(p = fgets(buf, sizeof(buf) - 1, fp)))
- break;
-
- fclose(fp);
- if (i < 2 || !p)
+ r = recv(nlsock, resp, sizeof(resp), 0);
+ if (r < 0) {
+ warn("recv 'AF_NETLINK':");
return NULL;
+ }
- if (!(datastart = strstr(buf, interface)))
+ if ((size_t)r <= NLMSG_HDRLEN + GENL_HDRLEN)
return NULL;
+ p = findattr(NL80211_ATTR_SSID, resp + NLMSG_HDRLEN + GENL_HDRLEN, resp + r, &len);
+ if (p)
+ p[len] = 0;
- datastart = (datastart+(strlen(interface)+1));
- sscanf(datastart + 1, " %*d %d %*d %*d\t\t %*d\t "
- "%*d\t\t%*d\t\t %*d\t %*d\t\t %*d", &cur);
-
- /* 70 is the max of /proc/net/wireless */
- return bprintf("%d", (int)((float)cur / 70 * 100));
+ return p;
}
const char *
- wifi_essid(const char *interface)
+ wifi_perc(const char *interface)
{
- static char id[IW_ESSID_MAX_SIZE+1];
- int sockfd;
- struct iwreq wreq;
-
- memset(&wreq, 0, sizeof(struct iwreq));
- wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
- if (esnprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s",
- interface) < 0)
- return NULL;
+ static char strength[4];
+ struct nlmsghdr hdr;
+ uint16_t fam = nl80211fam();
+ ssize_t r;
+ size_t len;
+ char req[NLMSG_HDRLEN + GENL_HDRLEN + NLA_HDRLEN + NLA_ALIGN(4)] = {0}, *p = req, *e;
+ int idx = ifindex(interface);
- if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- warn("socket 'AF_INET':");
- return NULL;
- }
- wreq.u.essid.pointer = id;
- if (ioctl(sockfd,SIOCGIWESSID, &wreq) < 0) {
- warn("ioctl 'SIOCGIWESSID':");
- close(sockfd);
+ if (idx < 0) {
+ fprintf(stderr, "interface %s not found\n", interface);
return NULL;
}
- close(sockfd);
-
- if (!strcmp(id, ""))
+ memcpy(p, &(struct nlmsghdr){
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = fam,
+ .nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP,
+ .nlmsg_seq = seq++,
+ .nlmsg_pid = 0,
+ }, sizeof(struct nlmsghdr));
+ p += NLMSG_HDRLEN;
+ memcpy(p, &(struct genlmsghdr){
+ .cmd = NL80211_CMD_GET_STATION,
+ .version = 1,
+ }, sizeof(struct genlmsghdr));
+ p += GENL_HDRLEN;
+ memcpy(p, &(struct nlattr){
+ .nla_len = NLA_HDRLEN + 4,
+ .nla_type = NL80211_ATTR_IFINDEX,
+ }, sizeof(struct nlattr));
+ p += NLA_HDRLEN;
+ memcpy(p, &idx, 4);
+
+ if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
+ warn("send 'AF_NETLINK':");
return NULL;
+ }
- return id;
+ *strength = 0;
+ while (1) {
+ r = recv(nlsock, resp, sizeof(resp), 0);
+ if (r < 0) {
+ warn("recv 'AF_NETLINK':");
+ return NULL;
+ }
+ if ((size_t)r < sizeof(hdr))
+ return NULL;
+
+ for (p = resp; p != resp + r && (size_t)(resp + r-p) >= sizeof(hdr); p = e) {
+ memcpy(&hdr, p, sizeof(hdr));
+ e = resp + r - p < hdr.nlmsg_len ? resp + r : p + hdr.nlmsg_len;
+
+ if (!*strength && hdr.nlmsg_len > NLMSG_HDRLEN+GENL_HDRLEN) {
+ p += NLMSG_HDRLEN+GENL_HDRLEN;
+ p = findattr(NL80211_ATTR_STA_INFO, p, e, &len);
+ if (p)
+ p = findattr(NL80211_STA_INFO_SIGNAL_AVG, p, e, &len);
+ if (p && len == 1)
+ snprintf(strength, sizeof(strength), "%d", RSSI_TO_PERC(*p));
+ }
+ if (hdr.nlmsg_type == NLMSG_DONE)
+ return *strength ? strength : NULL;
+ }
+ }
}
#elif defined(__OpenBSD__)
#include <net/if.h>
diff --git a/slstatus/config.def.h b/slstatus/config.def.h
index c273999..b48aa4b 100644
--- a/slstatus/config.def.h
+++ b/slstatus/config.def.h
@@ -56,6 +56,7 @@ static const char unknown_str[] = "n/a";
* thermal zone on FreeBSD
* (tz0, tz1, etc.)
* uid UID of current user NULL
+ * up interface is running interface name (eth0)
* uptime system uptime NULL
* username username of current user NULL
* vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer)
@@ -64,12 +65,9 @@ static const char unknown_str[] = "n/a";
* wifi_perc WiFi signal in percent interface name (wlan0)
*/
static const struct arg args[] = {
-
- /* function format argument interval (in ms) */
-
- {datetime, "%s;", "%G. %B. %e. %a"},
- {run_command, "%s%% ", "pulsemixer --get-volume | awk '{print $2 }'"},
- {keymap, "%s ", NULL},
- {datetime, "%s ", "%H:%M:%S"},
-
+ /* function format argument */
+ {datetime, "%s;", "%G. %B. %e. %a"},
+ {run_command, "%s%% ", "pulsemixer --get-volume | awk '{print $2 }'"},
+ {keymap, "%s ", NULL},
+ {datetime, "%s ", "%H:%M:%S"},
};
diff --git a/slstatus/config.h b/slstatus/config.h
index c273999..b48aa4b 100644
--- a/slstatus/config.h
+++ b/slstatus/config.h
@@ -56,6 +56,7 @@ static const char unknown_str[] = "n/a";
* thermal zone on FreeBSD
* (tz0, tz1, etc.)
* uid UID of current user NULL
+ * up interface is running interface name (eth0)
* uptime system uptime NULL
* username username of current user NULL
* vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer)
@@ -64,12 +65,9 @@ static const char unknown_str[] = "n/a";
* wifi_perc WiFi signal in percent interface name (wlan0)
*/
static const struct arg args[] = {
-
- /* function format argument interval (in ms) */
-
- {datetime, "%s;", "%G. %B. %e. %a"},
- {run_command, "%s%% ", "pulsemixer --get-volume | awk '{print $2 }'"},
- {keymap, "%s ", NULL},
- {datetime, "%s ", "%H:%M:%S"},
-
+ /* function format argument */
+ {datetime, "%s;", "%G. %B. %e. %a"},
+ {run_command, "%s%% ", "pulsemixer --get-volume | awk '{print $2 }'"},
+ {keymap, "%s ", NULL},
+ {datetime, "%s ", "%H:%M:%S"},
};
diff --git a/slstatus/config.mk b/slstatus/config.mk
index 07af883..a8f5c23 100644
--- a/slstatus/config.mk
+++ b/slstatus/config.mk
@@ -1,5 +1,5 @@
# slstatus version
-VERSION = 1.0
+VERSION = 1.1
# customize below to fit your system
diff --git a/slstatus/slstatus.c b/slstatus/slstatus.c
index fd31313..16d88fe 100644
--- a/slstatus/slstatus.c
+++ b/slstatus/slstatus.c
@@ -58,6 +58,7 @@ main(int argc, char *argv[])
ARGBEGIN {
case 'v':
die("slstatus-"VERSION);
+ break;
case '1':
done = 1;
/* FALLTHROUGH */
diff --git a/slstatus/slstatus.h b/slstatus/slstatus.h
index 8ef5874..394281c 100644
--- a/slstatus/slstatus.h
+++ b/slstatus/slstatus.h
@@ -30,6 +30,7 @@ const char *hostname(const char *unused);
/* ip */
const char *ipv4(const char *interface);
const char *ipv6(const char *interface);
+const char *up(const char *interface);
/* kernel_release */
const char *kernel_release(const char *unused);
diff --git a/slstatus/util.c b/slstatus/util.c
index bca9b2e..d33cf9d 100644
--- a/slstatus/util.c
+++ b/slstatus/util.c
@@ -139,3 +139,20 @@ pscanf(const char *path, const char *fmt, ...)
return (n == EOF) ? -1 : n;
}
+
+int
+lscanf(FILE *fp, const char *key, const char *fmt, void *res)
+{
+ int n;
+ char line[256];
+
+ n = -1;
+ while (fgets(line, sizeof(line), fp))
+ if (strncmp(line, key, strlen(key)) == 0) {
+ n = sscanf(line + strlen(key), fmt, res);
+ break;
+ }
+
+ rewind(fp);
+ return (n == 1) ? 1 : -1;
+}
diff --git a/slstatus/util.h b/slstatus/util.h
index cf4b027..7a960aa 100644
--- a/slstatus/util.h
+++ b/slstatus/util.h
@@ -1,5 +1,6 @@
/* See LICENSE file for copyright and license details. */
#include <stdint.h>
+#include <stdio.h>
extern char buf[1024];
@@ -14,3 +15,4 @@ int esnprintf(char *str, size_t size, const char *fmt, ...);
const char *bprintf(const char *fmt, ...);
const char *fmt_human(uintmax_t num, int base);
int pscanf(const char *path, const char *fmt, ...);
+int lscanf(FILE *fp, const char *key, const char *fmt, void *res);