1/* See LICENSE for license details. */
2
3#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>
12
13#include <sys/types.h>
14#include <tinyalsa/asoundlib.h>
15
16static 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);
21
22#include "config.h"
23
24enum {
25 STATUSSZ = 128,
26};
27
28static char ststr[STATUSSZ];
29
30static int
31xsnprintf(char *restrict s, size_t n, const char *restrict fmt, ...)
32{
33 int ret;
34 va_list ap;
35
36 va_start(ap, fmt);
37 ret = vsnprintf(s, n, fmt, ap);
38 va_end(ap);
39
40 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 }
46
47 return ret;
48}
49
50static double
51readnum(char *bfp, char *fn)
52{
53 int ret;
54 FILE *file;
55 size_t rclen;
56 char buf[16], fp[PATH_MAX + 1], *rc;
57
58 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");
63
64 if (!(file = fopen(fp, "r")))
65 err(EXIT_FAILURE, "couldn't open '%s'", fp);
66
67 if (!(rc = fgets(buf, 16, file))) {
68 fclose(file);
69 errx(EXIT_FAILURE, "'%s' seems to be empty", fp);
70 }
71
72 rclen = strlen(buf);
73 if (rc[rclen - 1] == '\n')
74 rc[rclen - 1] = '\0';
75
76 fclose(file);
77 return atof(rc);
78}
79
80static size_t
81actlstr(char *buf, size_t n, char *ch, struct mixer *mx) {
82 size_t ret;
83 char *status;
84 struct mixer_ctl *ctl;
85
86 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 }
90
91 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 };
104
105 return ret;
106}
107
108static size_t
109batcap(char *dest, size_t n)
110{
111 int cap;
112
113 cap = readnum((char*)sysbat, "capacity");
114 return xsnprintf(dest, n, "%d%%", cap);
115}
116
117static size_t
118alsavol(char *dest, size_t n)
119{
120 size_t ret;
121 struct mixer *mx;
122
123 if (!(mx = mixer_open(sndcrd)))
124 errx(EXIT_FAILURE, "couldn't open mixer for card %d\n", sndcrd);
125
126 ret = actlstr(dest, n, (char*)swtchname, mx);
127 if (strcmp(dest, "Off"))
128 ret = actlstr(dest, n, (char*)volumname, mx);
129
130 mixer_close(mx);
131 return ret;
132}
133
134static size_t
135loadavg(char* dest, size_t n)
136{
137 double avgs[3];
138
139 if (!getloadavg(avgs, 3))
140 err(EXIT_FAILURE, "getloadavg failed");
141
142 return xsnprintf(dest, n, "%.2f %.2f %.2f",
143 avgs[0], avgs[1], avgs[2]);
144}
145
146static size_t
147curtime(char *dest, size_t n)
148{
149 time_t tim;
150 struct tm *timtm;
151
152 if ((tim = time(NULL)) == (time_t)-1)
153 err(EXIT_FAILURE, "time failed");
154
155 if (!(timtm = localtime(&tim)))
156 err(EXIT_FAILURE, "localtime failed");
157
158 return strftime(dest, n, timefmt, timtm);
159}
160
161static size_t
162separator(char *dest, size_t n)
163{
164 return stpncpy(dest, statsep, n) - dest;
165}
166
167int
168main(void)
169{
170 size_t i, x, fns, max;
171
172 max = STATUSSZ - 1;
173 fns = sizeof(sfuncs) / sizeof(sfuncs[0]);
174
175 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 }
180
181 assert(x < STATUSSZ);
182 ststr[x] = '\0';
183
184 printf("%s\n", ststr);
185 fflush(stdout);
186 sleep(delay);
187 }
188
189 return EXIT_SUCCESS;
190}