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