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