Compare commits

..

11 Commits

Author SHA1 Message Date
thad-w541
f071e75978 config.h 2025-10-28 22:23:14 -05:00
drkhsh
8723e8b8c6 more concise memory calculation on Linux
more flexible parsing for /proc/meminfo to take shared and reclaimable
memory into account. this matches the output with free(1).

additionally this could fix some corner cases, as the order of fields in
/proc/meminfo is not strictly defined.

slstatus:
percent 81% free 2.5 Gi total 23.4 Gi used 19.0 Gi

free(1):
               total        used        free      shared  buff/cache   available
Mem:            23Gi        19Gi       2.5Gi       1.3Gi       3.2Gi       3.6Gi
2025-07-24 22:41:25 +02:00
drkhsh
6eb7887853 fix name confusion in LICENSE
thanks for reporting, dsp
2025-07-16 18:21:43 +02:00
drkhsh
f5c4e634cd bump version to 1.1 2025-04-30 04:00:57 +02:00
drkhsh
3db023f13b refactor wifi on linux 2025-04-30 04:00:53 +02:00
drkhsh
a0f960c16f update LICENSE and README 2025-04-30 03:13:41 +02:00
drkhsh
57a7653632 fix compiler warning with newer gcc 2025-04-30 03:11:53 +02:00
sewn
af508f0b4c add 'up' for whether a interface is up or down 2025-04-30 03:09:55 +02:00
Al
b6267f7d0b Add underscore to separators in strtok() call in keymap.c 2025-04-30 03:08:02 +02:00
Joakim Sindholt
0b00c0319c wifi: switch to nl80211 interface 2025-04-30 02:59:42 +02:00
drkhsh
f68f49273e Release stable 1.0 version 2023-07-04 20:47:18 +02:00
13 changed files with 376 additions and 93 deletions

View File

@@ -1,6 +1,6 @@
ISC License 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 Roy Freytag <rfreytag@hs-mittweida.de>
Copyright 2016 Vincent Loupmon <vincentloupmon@gmail.com> 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 2016-2019 Joerg Jung <jung@openbsd.org>
Copyright 2019 Ryan Kes <alrayyes@gmail.com> Copyright 2019 Ryan Kes <alrayyes@gmail.com>
Copyright 2019 Cem Keylan <cem@ckyln.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 2019-2022 Ingo Feinerer <feinerer@logic.at>
Copyright 2020 Alexandre Ratchov <alex@caoua.org> Copyright 2020 Alexandre Ratchov <alex@caoua.org>
Copyright 2020 Mart Lubbers <mart@martlubbers.net> 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 Nickolas Raymond Kaczynski <nrk@disroot.org>
Copyright 2022 Patrick Iacob <iacobp@oregonstate.edu> Copyright 2022 Patrick Iacob <iacobp@oregonstate.edu>
Copyright 2021-2022 Steven Ward <planet36@gmail.com> 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 Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above

8
README
View File

@@ -18,7 +18,7 @@ Features
- Available entropy - Available entropy
- Username/GID/UID - Username/GID/UID
- Hostname - Hostname
- IP address (IPv4 and IPv6) - IP address (IPv4 and IPv6), interface status
- Kernel version - Kernel version
- Keyboard indicators - Keyboard indicators
- Keymap - Keymap
@@ -63,9 +63,3 @@ Configuration
------------- -------------
slstatus can be customized by creating a custom config.h and (re)compiling the slstatus can be customized by creating a custom config.h and (re)compiling the
source code. This keeps it fast, secure and simple. source code. This keeps it fast, secure and simple.
Upcoming
--------
A first feature-complete release with official packages for common distributions
will come soon.

View File

@@ -1,6 +1,7 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <ifaddrs.h> #include <ifaddrs.h>
#include <netdb.h> #include <netdb.h>
#include <net/if.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#if defined(__OpenBSD__) #if defined(__OpenBSD__)
@@ -59,3 +60,28 @@ ipv6(const char *interface)
{ {
return ip(interface, AF_INET6); 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;
}

View File

@@ -29,8 +29,8 @@ get_layout(char *syms, int grp_num)
int grp; int grp;
layout = NULL; layout = NULL;
tok = strtok(syms, "+:"); tok = strtok(syms, "+:_");
for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:")) { for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:_")) {
if (!valid_layout_or_variant(tok)) { if (!valid_layout_or_variant(tok)) {
continue; continue;
} else if (strlen(tok) == 1 && isdigit(tok[0])) { } else if (strlen(tok) == 1 && isdigit(tok[0])) {

View File

@@ -11,36 +11,45 @@
ram_free(const char *unused) ram_free(const char *unused)
{ {
uintmax_t free; uintmax_t free;
FILE *fp;
if (pscanf("/proc/meminfo", if (!(fp = fopen("/proc/meminfo", "r")))
"MemTotal: %ju kB\n"
"MemFree: %ju kB\n"
"MemAvailable: %ju kB\n",
&free, &free, &free) != 3)
return NULL; return NULL;
if (lscanf(fp, "MemFree:", "%ju kB", &free) != 1) {
fclose(fp);
return NULL;
}
fclose(fp);
return fmt_human(free * 1024, 1024); return fmt_human(free * 1024, 1024);
} }
const char * const char *
ram_perc(const char *unused) ram_perc(const char *unused)
{ {
uintmax_t total, free, buffers, cached; uintmax_t total, free, buffers, cached, shmem, sreclaimable;
int percent; int percent;
FILE *fp;
if (pscanf("/proc/meminfo", if (!(fp = fopen("/proc/meminfo", "r")))
"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)
return NULL; return NULL;
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) if (total == 0)
return NULL; return NULL;
percent = 100 * ((total - free) - (buffers + cached)) / total; percent = 100 * (total - free - buffers - cached - sreclaimable + shmem) / total;
return bprintf("%d", percent); return bprintf("%d", percent);
} }
@@ -59,18 +68,24 @@
const char * const char *
ram_used(const char *unused) ram_used(const char *unused)
{ {
uintmax_t total, free, buffers, cached, used; uintmax_t total, free, buffers, cached, used, shmem, sreclaimable;
FILE *fp;
if (pscanf("/proc/meminfo", if (!(fp = fopen("/proc/meminfo", "r")))
"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)
return NULL; return NULL;
used = (total - free - buffers - cached); 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 - sreclaimable + shmem;
return fmt_human(used * 1024, 1024); return fmt_human(used * 1024, 1024);
} }
#elif defined(__OpenBSD__) #elif defined(__OpenBSD__)

View File

@@ -15,86 +15,232 @@
(2 * (rssi + 100))) (2 * (rssi + 100)))
#if defined(__linux__) #if defined(__linux__)
#include <limits.h> #include <stdint.h>
#include <linux/wireless.h> #include <net/if.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <linux/nl80211.h>
#define NET_OPERSTATE "/sys/class/net/%s/operstate" static int nlsock = -1;
static uint32_t seq = 1;
static char resp[4096];
const char * static char *
wifi_perc(const char *interface) findattr(int attr, const char *p, const char *e, size_t *len)
{ {
int cur; while (p < e) {
size_t i; struct nlattr nla;
char *p, *datastart; memcpy(&nla, p, sizeof(nla));
char path[PATH_MAX]; if (nla.nla_type == attr) {
char status[5]; *len = nla.nla_len - NLA_HDRLEN;
FILE *fp; return (char *)(p + NLA_HDRLEN);
}
if (esnprintf(path, sizeof(path), NET_OPERSTATE, interface) < 0) p += NLA_ALIGN(nla.nla_len);
return NULL;
if (!(fp = fopen(path, "r"))) {
warn("fopen '%s':", path);
return NULL;
} }
p = fgets(status, 5, fp); return NULL;
fclose(fp); }
if (!p || strcmp(status, "up\n") != 0)
return NULL;
if (!(fp = fopen("/proc/net/wireless", "r"))) { static uint16_t
warn("fopen '/proc/net/wireless':"); nl80211fam(void)
return NULL; {
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);
for (i = 0; i < 3; i++) return id;
if (!(p = fgets(buf, sizeof(buf) - 1, fp))) }
break;
fclose(fp); static int
if (i < 2 || !p) ifindex(const char *interface)
return NULL; {
static struct ifreq ifr;
static int ifsock = -1;
if (!(datastart = strstr(buf, interface))) if (ifsock < 0)
return NULL; ifsock = socket(AF_UNIX, SOCK_DGRAM, 0);
if (ifsock < 0) {
datastart = (datastart+(strlen(interface)+1)); warn("socket 'AF_UNIX':");
sscanf(datastart + 1, " %*d %d %*d %*d\t\t %*d\t " return -1;
"%*d\t\t%*d\t\t %*d\t %*d\t\t %*d", &cur); }
if (strcmp(ifr.ifr_name, interface) != 0) {
/* 70 is the max of /proc/net/wireless */ strcpy(ifr.ifr_name, interface);
return bprintf("%d", (int)((float)cur / 70 * 100)); if (ioctl(ifsock, SIOCGIFINDEX, &ifr) != 0) {
warn("ioctl 'SIOCGIFINDEX':");
return -1;
}
}
return ifr.ifr_ifindex;
} }
const char * const char *
wifi_essid(const char *interface) wifi_essid(const char *interface)
{ {
static char id[IW_ESSID_MAX_SIZE+1]; uint16_t fam = nl80211fam();
int sockfd; ssize_t r;
struct iwreq wreq; size_t len;
char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req;
memset(&wreq, 0, sizeof(struct iwreq)); int idx = ifindex(interface);
wreq.u.essid.length = IW_ESSID_MAX_SIZE+1; if (!fam) {
if (esnprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s", fprintf(stderr, "nl80211 family not found\n");
interface) < 0)
return NULL;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
warn("socket 'AF_INET':");
return NULL; return NULL;
} }
wreq.u.essid.pointer = id; if (idx < 0) {
if (ioctl(sockfd,SIOCGIWESSID, &wreq) < 0) { fprintf(stderr, "interface %s not found\n", interface);
warn("ioctl 'SIOCGIWESSID':");
close(sockfd);
return NULL; return NULL;
} }
close(sockfd); 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 (!strcmp(id, "")) if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) {
warn("send 'AF_NETLINK':");
return NULL; return NULL;
}
r = recv(nlsock, resp, sizeof(resp), 0);
if (r < 0) {
warn("recv 'AF_NETLINK':");
return NULL;
}
return id; 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;
return p;
}
const char *
wifi_perc(const char *interface)
{
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 (idx < 0) {
fprintf(stderr, "interface %s not found\n", interface);
return NULL;
}
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;
}
*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__) #elif defined(__OpenBSD__)
#include <net/if.h> #include <net/if.h>

View File

@@ -56,6 +56,7 @@ static const char unknown_str[] = "n/a";
* thermal zone on FreeBSD * thermal zone on FreeBSD
* (tz0, tz1, etc.) * (tz0, tz1, etc.)
* uid UID of current user NULL * uid UID of current user NULL
* up interface is running interface name (eth0)
* uptime system uptime NULL * uptime system uptime NULL
* username username of current user NULL * username username of current user NULL
* vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer) * vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer)

77
config.h Normal file
View File

@@ -0,0 +1,77 @@
/* See LICENSE file for copyright and license details. */
/* interval between updates (in ms) */
const unsigned int interval = 1000;
/* text to show if no value can be retrieved */
static const char unknown_str[] = "n/a";
/* maximum output string length */
#define MAXLEN 2048
/*
* function description argument (example)
*
* battery_perc battery percentage battery name (BAT0)
* NULL on OpenBSD/FreeBSD
* battery_remaining battery remaining HH:MM battery name (BAT0)
* NULL on OpenBSD/FreeBSD
* battery_state battery charging state battery name (BAT0)
* NULL on OpenBSD/FreeBSD
* cat read arbitrary file path
* cpu_freq cpu frequency in MHz NULL
* cpu_perc cpu usage in percent NULL
* datetime date and time format string (%F %T)
* disk_free free disk space in GB mountpoint path (/)
* disk_perc disk usage in percent mountpoint path (/)
* disk_total total disk space in GB mountpoint path (/)
* disk_used used disk space in GB mountpoint path (/)
* entropy available entropy NULL
* gid GID of current user NULL
* hostname hostname NULL
* ipv4 IPv4 address interface name (eth0)
* ipv6 IPv6 address interface name (eth0)
* kernel_release `uname -r` NULL
* keyboard_indicators caps/num lock indicators format string (c?n?)
* see keyboard_indicators.c
* keymap layout (variant) of current NULL
* keymap
* load_avg load average NULL
* netspeed_rx receive network speed interface name (wlan0)
* netspeed_tx transfer network speed interface name (wlan0)
* num_files number of files in a directory path
* (/home/foo/Inbox/cur)
* ram_free free memory in GB NULL
* ram_perc memory usage in percent NULL
* ram_total total memory size in GB NULL
* ram_used used memory in GB NULL
* run_command custom shell command command (echo foo)
* swap_free free swap in GB NULL
* swap_perc swap usage in percent NULL
* swap_total total swap size in GB NULL
* swap_used used swap in GB NULL
* temp temperature in degree celsius sensor file
* (/sys/class/thermal/...)
* NULL on OpenBSD
* 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)
* NULL on OpenBSD/FreeBSD
* wifi_essid WiFi ESSID interface name (wlan0)
* wifi_perc WiFi signal in percent interface name (wlan0)
*/
static const struct arg args[] = {
/* function format argument */
{ datetime, "%s", "%F %T" },
{ cpu_perc, " {%2s%%", NULL},
{ cpu_freq, " @%8sHz}", NULL},
{ wifi_essid, " << %s ", "wlp3s0" },
{ wifi_perc, "@%3s%% >>", "wlp3s0" },
{ battery_perc, " [[%3s%% ", "BAT0" },
{ battery_remaining, "%7s ]]", "BAT0" },
};

View File

@@ -1,5 +1,5 @@
# slstatus version # slstatus version
VERSION = 0 VERSION = 1.1
# customize below to fit your system # customize below to fit your system

View File

@@ -58,6 +58,7 @@ main(int argc, char *argv[])
ARGBEGIN { ARGBEGIN {
case 'v': case 'v':
die("slstatus-"VERSION); die("slstatus-"VERSION);
break;
case '1': case '1':
done = 1; done = 1;
/* FALLTHROUGH */ /* FALLTHROUGH */

View File

@@ -30,6 +30,7 @@ const char *hostname(const char *unused);
/* ip */ /* ip */
const char *ipv4(const char *interface); const char *ipv4(const char *interface);
const char *ipv6(const char *interface); const char *ipv6(const char *interface);
const char *up(const char *interface);
/* kernel_release */ /* kernel_release */
const char *kernel_release(const char *unused); const char *kernel_release(const char *unused);

17
util.c
View File

@@ -139,3 +139,20 @@ pscanf(const char *path, const char *fmt, ...)
return (n == EOF) ? -1 : n; 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;
}

2
util.h
View File

@@ -1,5 +1,6 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
extern char buf[1024]; 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 *bprintf(const char *fmt, ...);
const char *fmt_human(uintmax_t num, int base); const char *fmt_human(uintmax_t num, int base);
int pscanf(const char *path, const char *fmt, ...); int pscanf(const char *path, const char *fmt, ...);
int lscanf(FILE *fp, const char *key, const char *fmt, void *res);