1/* See LICENSE for license details. */23#include <assert.h>4#include <err.h>5#include <stdarg.h>6#include <stdio.h>7#include <stdlib.h>8#include <string.h>9#include <time.h>10#include <limits.h>11#include <unistd.h>1213#include <sys/types.h>14#include <tinyalsa/asoundlib.h>1516static size_t alsavol(char*, size_t);17static size_t batcap(char*, size_t);18static size_t loadavg(char*, size_t);19static size_t curtime(char*, size_t);20static size_t separator(char*, size_t);2122#include "config.h"2324enum {25 STATUSSZ = 128,26};2728static char ststr[STATUSSZ];2930static int31xsnprintf(char *restrict s, size_t n, const char *restrict fmt, ...)32{33 int ret;34 va_list ap;3536 va_start(ap, fmt);37 ret = vsnprintf(s, n, fmt, ap);38 va_end(ap);3940 if (ret < 0) {41 err(EXIT_FAILURE, "snprintf failed");42 } else if ((size_t)ret >= n) {43 warnx("snprintf: insufficient buffer size");44 ret = n;45 }4647 return ret;48}4950static double51readnum(char *bfp, char *fn)52{53 int ret;54 FILE *file;55 size_t rclen;56 char buf[16], fp[PATH_MAX + 1], *rc;5758 rc = NULL;59 if ((ret = snprintf(fp, sizeof(fp), "%s/%s", bfp, fn)) < 0)60 errx(EXIT_FAILURE, "snprintf failed");61 else if ((size_t)ret >= sizeof(fp))62 errx(EXIT_FAILURE, "buffer 'fp' is too short");6364 if (!(file = fopen(fp, "r")))65 err(EXIT_FAILURE, "couldn't open '%s'", fp);6667 if (!(rc = fgets(buf, 16, file))) {68 fclose(file);69 errx(EXIT_FAILURE, "'%s' seems to be empty", fp);70 }7172 rclen = strlen(buf);73 if (rc[rclen - 1] == '\n')74 rc[rclen - 1] = '\0';7576 fclose(file);77 return atof(rc);78}7980static size_t81actlstr(char *buf, size_t n, char *ch, struct mixer *mx) {82 size_t ret;83 char *status;84 struct mixer_ctl *ctl;8586 if (!(ctl = mixer_get_ctl_by_name(mx, ch))) {87 mixer_close(mx);88 errx(EXIT_FAILURE, "couldn't find mixer ctl '%s'\n", ch);89 }9091 switch (mixer_ctl_get_type(ctl)) {92 case MIXER_CTL_TYPE_INT:93 return xsnprintf(buf, n, "%d%%",94 mixer_ctl_get_percent(ctl, 0));95 case MIXER_CTL_TYPE_BOOL:96 status = mixer_ctl_get_value(ctl, 0) ? "On" : "Off";97 ret = stpncpy(buf, status, n) - buf;98 break;99 default:100 mixer_close(mx);101 errx(EXIT_FAILURE, "unsupported ctl type '%s'\n",102 mixer_ctl_get_type_string(ctl));103 };104105 return ret;106}107108static size_t109batcap(char *dest, size_t n)110{111 int cap;112113 cap = readnum((char*)sysbat, "capacity");114 return xsnprintf(dest, n, "%d%%", cap);115}116117static size_t118alsavol(char *dest, size_t n)119{120 size_t ret;121 struct mixer *mx;122123 if (!(mx = mixer_open(sndcrd)))124 errx(EXIT_FAILURE, "couldn't open mixer for card %d\n", sndcrd);125126 ret = actlstr(dest, n, (char*)swtchname, mx);127 if (strcmp(dest, "Off"))128 ret = actlstr(dest, n, (char*)volumname, mx);129130 mixer_close(mx);131 return ret;132}133134static size_t135loadavg(char* dest, size_t n)136{137 double avgs[3];138139 if (!getloadavg(avgs, 3))140 err(EXIT_FAILURE, "getloadavg failed");141142 return xsnprintf(dest, n, "%.2f %.2f %.2f",143 avgs[0], avgs[1], avgs[2]);144}145146static size_t147curtime(char *dest, size_t n)148{149 time_t tim;150 struct tm *timtm;151152 if ((tim = time(NULL)) == (time_t)-1)153 err(EXIT_FAILURE, "time failed");154155 if (!(timtm = localtime(&tim)))156 err(EXIT_FAILURE, "localtime failed");157158 return strftime(dest, n, timefmt, timtm);159}160161static size_t162separator(char *dest, size_t n)163{164 return stpncpy(dest, statsep, n) - dest;165}166167int168main(void)169{170 size_t i, x, fns, max;171172 max = STATUSSZ - 1;173 fns = sizeof(sfuncs) / sizeof(sfuncs[0]);174175 for (;;) {176 for (i = 0, x = 0; i < fns && x < max; i++) {177 assert(max >= x);178 x += (*sfuncs[i])(&(ststr[x]), max - x);179 }180181 assert(x < STATUSSZ);182 ststr[x] = '\0';183184 printf("%s\n", ststr);185 fflush(stdout);186 sleep(delay);187 }188189 return EXIT_SUCCESS;190}