]> git.sur5r.net Git - i3/i3status/blob - src/print_battery_info.c
Merge pull request #3 from chrko/master
[i3/i3status] / src / print_battery_info.c
1 // vim:ts=8:expandtab
2 #include <ctype.h>
3 #include <time.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <yajl/yajl_gen.h>
8 #include <yajl/yajl_version.h>
9
10 #include "i3status.h"
11
12 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
13 #include <sys/types.h>
14 #include <sys/sysctl.h>
15 #endif
16
17 #if defined(__OpenBSD__)
18 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <sys/fcntl.h>
21 #include <machine/apmvar.h>
22 #endif
23
24 #if defined(__NetBSD__)
25 #include <fcntl.h>
26 #include <prop/proplib.h>
27 #include <sys/envsys.h>
28 #endif
29
30 /*
31  * Get battery information from /sys. Note that it uses the design capacity to
32  * calculate the percentage, not the last full capacity, so you can see how
33  * worn off your battery is.
34  *
35  */
36 void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, const char *status_chr, const char *status_bat, const char *status_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds) {
37         time_t empty_time;
38         struct tm *empty_tm;
39         char buf[1024];
40         char statusbuf[16];
41         char percentagebuf[16];
42         char remainingbuf[256];
43         char emptytimebuf[256];
44         char consumptionbuf[256];
45         const char *walk, *last;
46         char *outwalk = buffer;
47         bool watt_as_unit = false;
48         bool colorful_output = false;
49         int full_design = -1,
50             remaining = -1,
51             present_rate = -1,
52             voltage = -1;
53         charging_status_t status = CS_DISCHARGING;
54
55         memset(statusbuf, '\0', sizeof(statusbuf));
56         memset(percentagebuf, '\0', sizeof(percentagebuf));
57         memset(remainingbuf, '\0', sizeof(remainingbuf));
58         memset(emptytimebuf, '\0', sizeof(emptytimebuf));
59         memset(consumptionbuf, '\0', sizeof(consumptionbuf));
60
61         static char batpath[512];
62         sprintf(batpath, path, number);
63         INSTANCE(batpath);
64
65 #define BATT_STATUS_NAME(status) \
66     (status == CS_CHARGING ? status_chr : \
67         (status == CS_DISCHARGING ? status_bat : status_full))
68
69 #if defined(LINUX)
70         if (!slurp(batpath, buf, sizeof(buf))) {
71                 OUTPUT_FULL_TEXT(format_down);
72                 return;
73         }
74
75         for (walk = buf, last = buf; (walk-buf) < 1024; walk++) {
76                 if (*walk == '\n') {
77                         last = walk+1;
78                         continue;
79                 }
80
81                 if (*walk != '=')
82                         continue;
83
84                 if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_NOW")) {
85                         watt_as_unit = true;
86                         remaining = atoi(walk+1);
87                 }
88                 else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_NOW")) {
89                         watt_as_unit = false;
90                         remaining = atoi(walk+1);
91                 }
92                 else if (BEGINS_WITH(last, "POWER_SUPPLY_CURRENT_NOW"))
93                         present_rate = abs(atoi(walk+1));
94                 else if (BEGINS_WITH(last, "POWER_SUPPLY_VOLTAGE_NOW"))
95                         voltage = abs(atoi(walk+1));
96                 /* on some systems POWER_SUPPLY_POWER_NOW does not exist, but actually
97                  * it is the same as POWER_SUPPLY_CURRENT_NOW but with μWh as
98                  * unit instead of μAh. We will calculate it as we need it
99                  * later. */
100                 else if (BEGINS_WITH(last, "POWER_SUPPLY_POWER_NOW"))
101                         present_rate = abs(atoi(walk+1));
102                 else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS=Charging"))
103                         status = CS_CHARGING;
104                 else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS=Full"))
105                         status = CS_FULL;
106                 else {
107                         /* The only thing left is the full capacity */
108                         if (last_full_capacity) {
109                                 if (!BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL") &&
110                                     !BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL"))
111                                         continue;
112                         } else {
113                                 if (!BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL_DESIGN") &&
114                                     !BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL_DESIGN"))
115                                         continue;
116                         }
117
118                         full_design = atoi(walk+1);
119                 }
120         }
121
122         /* the difference between POWER_SUPPLY_ENERGY_NOW and
123          * POWER_SUPPLY_CHARGE_NOW is the unit of measurement. The energy is
124          * given in mWh, the charge in mAh. So calculate every value given in
125          * ampere to watt */
126         if (!watt_as_unit) {
127             present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0));
128
129             if (voltage != -1) {
130                 remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
131                 full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
132             }
133         }
134
135         if ((full_design == -1) || (remaining == -1)) {
136                 OUTPUT_FULL_TEXT(format_down);
137                 return;
138         }
139
140         (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status));
141
142         float percentage_remaining = (((float)remaining / (float)full_design) * 100);
143         if (integer_battery_capacity) {
144                 (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.00f%%", percentage_remaining);
145         } else {
146                 (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.02f%%", percentage_remaining);
147         }
148
149         if (present_rate > 0) {
150                 float remaining_time;
151                 int seconds, hours, minutes, seconds_remaining;
152                 if (status == CS_CHARGING)
153                         remaining_time = ((float)full_design - (float)remaining) / (float)present_rate;
154                 else if (status == CS_DISCHARGING)
155                         remaining_time = ((float)remaining / (float)present_rate);
156                 else remaining_time = 0;
157
158                 seconds_remaining = (int)(remaining_time * 3600.0);
159
160                 hours = seconds_remaining / 3600;
161                 seconds = seconds_remaining - (hours * 3600);
162                 minutes = seconds / 60;
163                 seconds -= (minutes * 60);
164
165                 if (status == CS_DISCHARGING && low_threshold > 0) {
166                         if (strcasecmp(threshold_type, "percentage") == 0
167                                 && percentage_remaining < low_threshold) {
168                                 START_COLOR("color_bad");
169                                 colorful_output = true;
170                         } else if (strcasecmp(threshold_type, "time") == 0
171                                 && seconds_remaining < 60 * low_threshold) {
172                                 START_COLOR("color_bad");
173                                 colorful_output = true;
174                         } else {
175                             colorful_output = false;
176                         }
177                 }
178
179                 if (hide_seconds)
180                         (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d",
181                                 max(hours, 0), max(minutes, 0));
182                 else
183                         (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d",
184                                 max(hours, 0), max(minutes, 0), max(seconds, 0));
185
186                 empty_time = time(NULL);
187                 empty_time += seconds_remaining;
188                 empty_tm = localtime(&empty_time);
189
190                 if (hide_seconds)
191                         (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d",
192                                 max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0));
193                 else
194                         (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d",
195                                 max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0));
196
197                 (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW",
198                         ((float)present_rate / 1000.0 / 1000.0));
199         } else {
200                 /* On some systems, present_rate may not exist. Still, make sure
201                  * we colorize the output if threshold_type is set to percentage
202                  * (since we don't have any information on remaining time). */
203                 if (status == CS_DISCHARGING && low_threshold > 0) {
204                         if (strcasecmp(threshold_type, "percentage") == 0
205                                 && percentage_remaining < low_threshold) {
206                                 START_COLOR("color_bad");
207                                 colorful_output = true;
208                         }
209                 }
210         }
211 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
212         int state;
213         int sysctl_rslt;
214         size_t sysctl_size = sizeof(sysctl_rslt);
215
216         if (sysctlbyname(BATT_LIFE, &sysctl_rslt, &sysctl_size, NULL, 0) != 0) {
217                 OUTPUT_FULL_TEXT(format_down);
218                 return;
219         }
220
221         present_rate = sysctl_rslt;
222         if (sysctlbyname(BATT_TIME, &sysctl_rslt, &sysctl_size, NULL, 0) != 0) {
223                 OUTPUT_FULL_TEXT(format_down);
224                 return;
225         }
226
227         remaining = sysctl_rslt;
228         if (sysctlbyname(BATT_STATE, &sysctl_rslt, &sysctl_size, NULL,0) != 0) {
229                 OUTPUT_FULL_TEXT(format_down);
230                 return;
231         }
232
233         state = sysctl_rslt;
234         if (state == 0 && present_rate == 100)
235                 status = CS_FULL;
236         else if (state == 0 && present_rate < 100)
237                 status = CS_CHARGING;
238         else
239                 status = CS_DISCHARGING;
240
241         full_design = sysctl_rslt;
242
243         (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status));
244
245         (void)snprintf(percentagebuf, sizeof(percentagebuf), "%02d%%",
246                        present_rate);
247
248         if (state == 1) {
249                 int hours, minutes;
250                 minutes = remaining;
251                 hours = minutes / 60;
252                 minutes -= (hours * 60);
253                 (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02dh%02d",
254                                max(hours, 0), max(minutes, 0));
255                 if (strcasecmp(threshold_type, "percentage") == 0
256                     && present_rate < low_threshold) {
257                         START_COLOR("color_bad");
258                         colorful_output = true;
259                 } else if (strcasecmp(threshold_type, "time") == 0
260                            && remaining < (u_int) low_threshold) {
261                         START_COLOR("color_bad");
262                         colorful_output = true;
263                 }
264         }
265 #elif defined(__OpenBSD__)
266         /*
267          * We're using apm(4) here, which is the interface to acpi(4) on amd64/i386 and
268          * the generic interface on macppc/sparc64/zaurus, instead of using sysctl(3) and
269          * probing acpi(4) devices.
270          */
271         struct apm_power_info apm_info;
272         int apm_fd;
273
274         apm_fd = open("/dev/apm", O_RDONLY);
275         if (apm_fd < 0) {
276                 OUTPUT_FULL_TEXT("can't open /dev/apm");
277                 return;
278         }
279         if (ioctl(apm_fd, APM_IOC_GETPOWER, &apm_info) < 0)
280                 OUTPUT_FULL_TEXT("can't read power info");
281
282         close(apm_fd);
283
284         /* Don't bother to go further if there's no battery present. */
285         if ((apm_info.battery_state == APM_BATTERY_ABSENT) ||
286             (apm_info.battery_state == APM_BATT_UNKNOWN)) {
287                 OUTPUT_FULL_TEXT(format_down);
288                 return;
289         }
290
291         switch(apm_info.ac_state) {
292         case APM_AC_OFF:
293                 status = CS_DISCHARGING;
294                 break;
295         case APM_AC_ON:
296                 status = CS_CHARGING;
297                 break;
298         default:
299                 /* If we don't know what's going on, just assume we're discharging. */
300                 status = CS_DISCHARGING;
301                 break;
302         }
303
304         (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status));
305         /* integer_battery_capacity is implied as battery_life is already in whole numbers. */
306         (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.00d%%", apm_info.battery_life);
307
308         if (status == CS_DISCHARGING && low_threshold > 0) {
309                 if (strcasecmp(threshold_type, "percentage") == 0
310                     && apm_info.battery_life < low_threshold) {
311                         START_COLOR("color_bad");
312                         colorful_output = true;
313                 } else if (strcasecmp(threshold_type, "time") == 0
314                            && apm_info.minutes_left < (u_int) low_threshold) {
315                         START_COLOR("color_bad");
316                         colorful_output = true;
317                 }
318         }
319
320         /* Can't give a meaningful value for remaining minutes if we're charging. */
321         if (status != CS_CHARGING) {
322                 (void)snprintf(remainingbuf, sizeof(remainingbuf), "%d", apm_info.minutes_left);
323         } else {
324                 (void)snprintf(remainingbuf, sizeof(remainingbuf), "%s", "(CHR)");
325         }
326
327         if (colorful_output)
328                 END_COLOR;
329 #elif defined(__NetBSD__)
330         /*
331          * Using envsys(4) via sysmon(4).
332          */
333         int fd, rval, last_full_cap;
334         bool is_found = false;
335         char *sensor_desc;
336         bool is_full = false;
337
338         prop_dictionary_t dict;
339         prop_array_t array;
340         prop_object_iterator_t iter;
341         prop_object_iterator_t iter2;
342         prop_object_t obj, obj2, obj3, obj4, obj5;
343
344         asprintf(&sensor_desc, "acpibat%d", number);
345
346         fd = open("/dev/sysmon", O_RDONLY);
347         if (fd < 0) {
348                 OUTPUT_FULL_TEXT("can't open /dev/sysmon");
349                 return;
350         }
351
352         rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict);
353         if (rval == -1) {
354                 close(fd);
355                 return;
356         }
357
358         if (prop_dictionary_count(dict) == 0) {
359                 prop_object_release(dict);
360                 close(fd);
361                 return;
362         }
363
364         iter = prop_dictionary_iterator(dict);
365         if (iter == NULL) {
366                 prop_object_release(dict);
367                 close(fd);
368         }
369
370         /* iterate over the dictionary returned by the kernel */
371         while ((obj = prop_object_iterator_next(iter)) != NULL) {
372                 /* skip this dict if it's not what we're looking for */
373                 if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) == strlen(sensor_desc)) &&
374                     (strncmp(sensor_desc,
375                             prop_dictionary_keysym_cstring_nocopy(obj),
376                             strlen(sensor_desc)) != 0))
377                         continue;
378
379                 is_found = true;
380
381                 array = prop_dictionary_get_keysym(dict, obj);
382                 if (prop_object_type(array) != PROP_TYPE_ARRAY) {
383                         prop_object_iterator_release(iter);
384                         prop_object_release(dict);
385                         close(fd);
386                         return;
387                 }
388
389                 iter2 = prop_array_iterator(array);
390                 if (!iter2) {
391                         prop_object_iterator_release(iter);
392                         prop_object_release(dict);
393                         close(fd);
394                         return;
395                 }
396
397                 /* iterate over array of dicts specific to target battery */
398                 while ((obj2 = prop_object_iterator_next(iter2)) != NULL) {
399                         obj3 = prop_dictionary_get(obj2, "description");
400
401                         if (obj3 &&
402                             strlen(prop_string_cstring_nocopy(obj3)) == 8 &&
403                             strncmp("charging",
404                                     prop_string_cstring_nocopy(obj3),
405                                     8) == 0)
406                         {
407                                 obj3 = prop_dictionary_get(obj2, "cur-value");
408
409                                 if (prop_number_integer_value(obj3))
410                                         status = CS_CHARGING;
411                                 else
412                                         status = CS_DISCHARGING;
413
414                                 continue;
415                         }
416
417                         if (obj3 &&
418                             strlen(prop_string_cstring_nocopy(obj3)) == 6 &&
419                             strncmp("charge",
420                                     prop_string_cstring_nocopy(obj3),
421                                     6) == 0)
422                         {
423                                 obj3 = prop_dictionary_get(obj2, "cur-value");
424                                 obj4 = prop_dictionary_get(obj2, "max-value");
425                                 obj5 = prop_dictionary_get(obj2, "type");
426
427                                 remaining = prop_number_integer_value(obj3);
428                                 full_design = prop_number_integer_value(obj4);
429
430                                 if (remaining == full_design)
431                                         is_full = true;
432
433                                 if (strncmp("Ampere hour",
434                                             prop_string_cstring_nocopy(obj5),
435                                             11) == 0)
436                                         watt_as_unit = false;
437                                 else
438                                         watt_as_unit = true;
439
440                                 continue;
441                         }
442
443                         if (obj3 &&
444                             strlen(prop_string_cstring_nocopy(obj3)) == 14 &&
445                             strncmp("discharge rate",
446                                     prop_string_cstring_nocopy(obj3),
447                                     14) == 0)
448                         {
449                                 obj3 = prop_dictionary_get(obj2, "cur-value");
450                                 present_rate = prop_number_integer_value(obj3);
451                                 continue;
452                         }
453
454                         if (obj3 &&
455                             strlen(prop_string_cstring_nocopy(obj3)) == 13 &&
456                             strncmp("last full cap",
457                                     prop_string_cstring_nocopy(obj3),
458                                     13) == 0)
459                         {
460                                 obj3 = prop_dictionary_get(obj2, "cur-value");
461                                 last_full_cap = prop_number_integer_value(obj3);
462                                 continue;
463                         }
464
465                         if (obj3 &&
466                             strlen(prop_string_cstring_nocopy(obj3)) == 7 &&
467                             strncmp("voltage",
468                                     prop_string_cstring_nocopy(obj3),
469                                     7) == 0)
470                         {
471                                 obj3 = prop_dictionary_get(obj2, "cur-value");
472                                 voltage = prop_number_integer_value(obj3);
473                                 continue;
474                         }
475                 }
476                 prop_object_iterator_release(iter2);
477         }
478
479         prop_object_iterator_release(iter);
480         prop_object_release(dict);
481         close(fd);
482
483         if (! is_found) {
484                 OUTPUT_FULL_TEXT(format_down);
485                 return;
486         }
487
488         if (last_full_capacity)
489                 full_design = last_full_cap;
490
491         if (! watt_as_unit) {
492                 present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0));
493                 remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
494                 full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
495         }
496
497         float percentage_remaining =
498                 (((float)remaining / (float)full_design) * 100);
499
500         if (integer_battery_capacity)
501                 (void)snprintf(percentagebuf,
502                            sizeof(percentagebuf),
503                            "%d%%",
504                            (int) percentage_remaining);
505         else
506                 (void)snprintf(percentagebuf,
507                            sizeof(percentagebuf),
508                            "%.02f%%",
509                            percentage_remaining);
510
511         /*
512          * Handle percentage low_threshold here, and time low_threshold when
513          * we have it.
514          */
515         if (status == CS_DISCHARGING && low_threshold > 0) {
516                 if (strcasecmp(threshold_type, "percentage") == 0
517                     && (((float)remaining / (float)full_design) * 100) < low_threshold) {
518                         START_COLOR("color_bad");
519                         colorful_output = true;
520                 }
521         }
522
523         if (is_full)
524                 (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(CS_FULL));
525         else
526                 (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status));
527
528         /*
529          * The envsys(4) ACPI routines do not appear to provide a 'time
530          * remaining' figure, so we must deduce it.
531          */
532         float remaining_time;
533         int seconds, hours, minutes, seconds_remaining;
534
535         if (status == CS_CHARGING)
536                 remaining_time = ((float)full_design - (float)remaining)
537                         / (float)present_rate;
538         else if (status == CS_DISCHARGING)
539                 remaining_time = ((float)remaining / (float)present_rate);
540         else remaining_time = 0;
541
542         seconds_remaining = (int)(remaining_time * 3600.0);
543
544         hours = seconds_remaining / 3600;
545         seconds = seconds_remaining - (hours * 3600);
546         minutes = seconds / 60;
547         seconds -= (minutes * 60);
548
549         if (status != CS_CHARGING) {
550                 if (hide_seconds)
551                         (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d",
552                                 max(hours, 0), max(minutes, 0));
553                 else
554                         (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d",
555                                 max(hours, 0), max(minutes, 0), max(seconds, 0));
556
557                 if (low_threshold > 0) {
558                         if (strcasecmp(threshold_type, "time") == 0
559                             && ((float) seconds_remaining / 60.0) < (u_int) low_threshold) {
560                                 START_COLOR("color_bad");
561                                 colorful_output = true;
562                         }
563                 }
564         } else {
565                 if (hide_seconds)
566                         (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d until full)",
567                                 max(hours, 0), max(minutes, 0));
568                 else
569                         (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d:%02d until full)",
570                                 max(hours, 0), max(minutes, 0), max(seconds, 0));
571         }
572
573         empty_time = time(NULL);
574         empty_time += seconds_remaining;
575         empty_tm = localtime(&empty_time);
576
577         /* No need to show empty time if battery is charging */
578         if (status != CS_CHARGING) {
579                 if (hide_seconds)
580                         (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d",
581                                 max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0));
582                 else
583                         (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d",
584                                 max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0));
585         }
586
587         (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW",
588                 ((float)present_rate / 1000.0 / 1000.0));
589 #endif
590
591 #define EAT_SPACE_FROM_OUTPUT_IF_EMPTY(_buf) \
592         do { \
593                 if (strlen(_buf) == 0) { \
594                         if (outwalk > buffer && isspace(outwalk[-1])) \
595                                 outwalk--; \
596                         else if (isspace(*(walk+1))) \
597                                 walk++; \
598                 } \
599         } while (0)
600
601         for (walk = format; *walk != '\0'; walk++) {
602                 if (*walk != '%') {
603                         *(outwalk++) = *walk;
604                         continue;
605                 }
606
607                 if (BEGINS_WITH(walk+1, "status")) {
608                         outwalk += sprintf(outwalk, "%s", statusbuf);
609                         walk += strlen("status");
610                 } else if (BEGINS_WITH(walk+1, "percentage")) {
611                         outwalk += sprintf(outwalk, "%s", percentagebuf);
612                         walk += strlen("percentage");
613                 } else if (BEGINS_WITH(walk+1, "remaining")) {
614                         outwalk += sprintf(outwalk, "%s", remainingbuf);
615                         walk += strlen("remaining");
616                         EAT_SPACE_FROM_OUTPUT_IF_EMPTY(remainingbuf);
617                 } else if (BEGINS_WITH(walk+1, "emptytime")) {
618                         outwalk += sprintf(outwalk, "%s", emptytimebuf);
619                         walk += strlen("emptytime");
620                         EAT_SPACE_FROM_OUTPUT_IF_EMPTY(emptytimebuf);
621                 } else if (BEGINS_WITH(walk+1, "consumption")) {
622                         outwalk += sprintf(outwalk, "%s", consumptionbuf);
623                         walk += strlen("consumption");
624                         EAT_SPACE_FROM_OUTPUT_IF_EMPTY(consumptionbuf);
625                 }
626         }
627
628         if (colorful_output)
629                 END_COLOR;
630
631         OUTPUT_FULL_TEXT(buffer);
632 }