Git init
[platform/core/uifw/e17.git] / src / modules / battery / batget.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <unistd.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <errno.h>
14 #include <limits.h>
15
16 #ifdef __FreeBSD__
17 # include <sys/ioctl.h>
18 # include <sys/sysctl.h>
19 # ifdef __i386__
20 #  include <machine/apm_bios.h>
21 # endif
22 #endif
23 #ifdef HAVE_CFBASE_H
24 # include <CFBase.h>
25 # include <CFNumber.h>
26 # include <CFArray.h>
27 # include <CFDictionary.h>
28 # include <CFRunLoop.h>
29 # include <ps/IOPSKeys.h>
30 # include <ps/IOPowerSources.h>
31 #endif
32
33 #include <Ecore.h>
34 #include <Ecore_File.h>
35 #include <Ecore_Con.h>
36
37 /* define MAX for all OS, instead of plenty #include */
38 #ifndef MAX
39 # define MAX(a, b) ((a) < (b) ? (b) : (a))
40 #endif
41
42 /* supported batery system schemes - irrespective of OS */
43 #define CHECK_NONE                   0
44 #define CHECK_ACPI                   1
45 #define CHECK_APM                    2
46 #define CHECK_PMU                    3
47 #define CHECK_SYS_CLASS_POWER_SUPPLY 4
48
49 static void      init(void);
50 static Eina_Bool poll_cb(void *data);
51
52 static int poll_interval = 512;
53 static Ecore_Poller *poller = NULL;
54
55 static int mode = CHECK_NONE;
56
57 static int time_left = -2;
58 static int battery_full = -2;
59 static int have_battery = -2;
60 static int have_power = -2;
61
62 /* UTILS */
63 static int
64 int_file_get(const char *file)
65 {
66    int val = -1;
67    FILE *f = fopen(file, "r");
68    if (f)
69      {
70         char buf[256];
71         char *str = fgets(buf, sizeof(buf), f);
72         if (str) val = atoi(str);
73         fclose(f);
74      }
75    return val;
76 }
77
78 static char *
79 str_file_get(const char *file)
80 {
81    char *val = NULL;
82    FILE *f = fopen(file, "r");
83    if (f)
84      {
85         char buf[PATH_MAX];
86         char *str = fgets(buf, sizeof(buf), f);
87         if (str)
88           {
89              size_t len = strlen(str);
90              if ((len > 0) && (str[len - 1] == '\n'))
91                {
92                   len--;
93                   str[len] = 0;
94                }
95              val = malloc(len + 1);
96              if (val) memcpy(val, str, len + 1);
97           }
98         fclose(f);
99      }
100    return val;
101 }
102
103 static int
104 int_get(const char *buf)
105 {
106    const char *p = strchr(buf, ':');
107    if (!p) return 0;
108    p++;
109    while (*p == ' ') p++;
110    return atoi(p);
111 }
112
113 static char *
114 str_get(const char *buf)
115 {
116    const char *p = strchr(buf, ':');
117    const char *q;
118    char *ret;
119
120    if (!p) return NULL;
121    p++;
122    while (*p == ' ') p++;
123
124    q = p + strlen(p) - 1;
125    while ((q > p) && ((*q == ' ') || (*q == '\n'))) q--;
126
127    if (q < p) return NULL;
128    q++;
129    ret = malloc(q - p + 1);
130    if (!ret) return NULL;
131    memcpy(ret, p, q - p);
132    ret[q - p] = '\0';
133    return ret;
134 }
135
136 static char *
137 file_str_entry_get(FILE       *f,
138                    const char *entry)
139 {
140    char buf[4096];
141    char *tmp;
142
143    tmp = fgets(buf, sizeof(buf), f);
144    if (!tmp)
145      {
146         EINA_LOG_ERR("unexpected end of file, expected: '%s'", entry);
147         return NULL;
148      }
149    if (strcmp(tmp, entry) != 0)
150      {
151         EINA_LOG_ERR("unexpected file entry, expected: '%s'", entry);
152         return NULL;
153      }
154    tmp = str_get(tmp);
155    if (!tmp)
156      {
157         EINA_LOG_ERR("unexpected file entry, missing value for '%s'", entry);
158         return NULL;
159      }
160    return tmp;
161 }
162
163 #ifdef __FreeBSD__
164
165 #define BATTERY_STATE_NONE        0
166 #define BATTERY_STATE_DISCHARGING 1
167 #define BATTERY_STATE_CHARGING    2
168 #define BATTERY_STATE_REMOVED     7
169
170 /***---***/
171 static void
172 bsd_acpi_init(void)
173 {
174    /* nothing to do */
175 }
176
177 static void
178 bsd_acpi_check(void)
179 {
180    int bat_val = 0;
181    int mib_state[4];
182    int mib_life[4];
183    int mib_time[4];
184    int mib_units[4];
185    size_t len;
186    int state = 0;
187    int level = 0;
188    int time_min = 0;
189    int life = 0;
190    int batteries = 0;
191
192    time_left = -1;
193    battery_full = -1;
194    have_battery = 0;
195    have_power = 0;
196
197    /* Read some information on first run. */
198    len = 4;
199    sysctlnametomib("hw.acpi.battery.state", mib_state, &len);
200    len = sizeof(state);
201    if (sysctl(mib_state, 4, &state, &len, NULL, 0) == -1)
202      /* ERROR */
203      state = -1;
204
205    len = 4;
206    sysctlnametomib("hw.acpi.battery.life", mib_life, &len);
207    len = sizeof(life);
208    if (sysctl(mib_life, 4, &life, &len, NULL, 0) == -1)
209      /* ERROR */
210      level = -1;
211    bat_val = life;
212
213    len = 4;
214    sysctlnametomib("hw.acpi.battery.time", mib_time, &len);
215    len = sizeof(time);
216    if (sysctl(mib_time, 4, &time_min, &len, NULL, 0) == -1)
217      /* ERROR */
218      time_min = -1;
219
220    len = 4;
221    sysctlnametomib("hw.acpi.battery.units", mib_units, &len);
222    len = sizeof(batteries);
223    if (sysctl(mib_time, 4, &batteries, &len, NULL, 0) == -1)
224      /* ERROR */
225      batteries = 1;
226
227    if (time_min >= 0) time_left = time_min * 60;
228
229    if (batteries == 1) /* hw.acpi.battery.units = 1 means NO BATTS */
230      time_left = -1;
231    else if ((state == BATTERY_STATE_CHARGING) ||
232             (state == BATTERY_STATE_DISCHARGING))
233      {
234         have_battery = 1;
235         if (state == BATTERY_STATE_CHARGING) have_power = 1;
236         else if (state == BATTERY_STATE_DISCHARGING)
237           have_power = 0;
238         if (level == -1) time_left = -1;
239         else if (time_min == -1)
240           {
241              time_left = -1;
242              battery_full = bat_val;
243           }
244         else battery_full = bat_val;
245      }
246    else
247      {
248         have_battery = 1;
249         battery_full = 100;
250         time_left = -1;
251         have_power = 1;
252      }
253 }
254
255 /***---***/
256 # ifdef __i386__
257 static void
258 bsd_apm_init(void)
259 {
260    /* nothing to do */
261 }
262
263 static void
264 bsd_apm_check(void)
265 {
266    int ac_stat, bat_stat, bat_val, time_val;
267    char buf[4096];
268    int hours, minutes;
269    int apm_fd = -1;
270    struct apm_info info;
271
272    time_left = -1;
273    battery_full = -1;
274    have_battery = 0;
275    have_power = 0;
276
277    apm_fd = open("/dev/apm", O_RDONLY);
278    if ((apm_fd != -1) && (ioctl(apm_fd, APMIO_GETINFO, &info) != -1))
279      {
280         /* set values */
281          ac_stat = info.ai_acline;
282          bat_stat = info.ai_batt_stat;
283          bat_val = info.ai_batt_life;
284          time_val = info.ai_batt_time;
285      }
286    else return;
287
288    if (info.ai_batteries == 1) /* ai_batteries == 1 means NO battery,
289                                * ai_batteries == 2 means 1 battery */
290      {
291         have_power = 1;
292         return;
293      }
294
295    if (ac_stat) /* Wallpowered */
296      {
297         have_power = 1;
298         have_battery = 1;
299         switch (bat_stat) /* On FreeBSD the time_val is -1 when AC ist plugged
300                            * in. This means we don't know how long the battery
301                            * will recharge */
302           {
303            case 0:
304              battery_full = 100;
305              break;
306
307            case 1:
308              battery_full = 50;
309              break;
310
311            case 2:
312              battery_full = 25;
313              break;
314
315            case 3:
316              battery_full = 100;
317              break;
318           }
319      }
320    else /* Running on battery */
321      {
322         have_battery = 1;
323         battery_full = bat_val;
324         time_left = time_val;
325      }
326 }
327
328 # endif
329
330 #elif defined(HAVE_CFBASE_H) /* OS X */
331 /***---***/
332 static void darwin_init(void);
333 static void darwin_check(void);
334
335 static void
336 darwin_init(void)
337 {
338    /* nothing to do */
339 }
340
341 static void
342 darwin_check(void)
343 {
344    const void *values;
345    int device_num, device_count;
346    int currentval = 0, maxval = 0;
347    char buf[4096];
348    CFTypeRef blob;
349    CFArrayRef sources;
350    CFDictionaryRef device_dict;
351
352    time_left = -1;
353    battery_full = -1;
354    have_battery = 0;
355    have_power = 0;
356
357    /* Retrieve the power source data and the array of sources. */
358    blob = IOPSCopyPowerSourcesInfo();
359    sources = IOPSCopyPowerSourcesList(blob);
360    device_count = CFArrayGetCount(sources);
361    for (device_num = 0; device_num < device_count; device_num++)
362      {
363         CFTypeRef ps;
364
365         /* Retrieve a dictionary of values for this device and the count of keys in the dictionary. */
366         ps = CFArrayGetValueAtIndex(sources, device_num);
367         device_dict = IOPSGetPowerSourceDescription(blob, ps);
368         /* Retrieve the charging key and save the present charging value if one exists. */
369         if (CFDictionaryGetValueIfPresent(device_dict,
370                                           CFSTR(kIOPSIsChargingKey), &values))
371           {
372              have_battery = 1;
373              if (CFBooleanGetValue(values) > 0) have_power = 1;
374              break;
375           }
376      }
377
378    if (!have_battery)
379      {
380         CFRelease(sources);
381         CFRelease(blob);
382         have_power = 1;
383         return;
384      }
385
386    /* Retrieve the current capacity key. */
387    values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSCurrentCapacityKey));
388    CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
389    /* Retrieve the max capacity key. */
390    values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSMaxCapacityKey));
391    CFNumberGetValue(values, kCFNumberSInt32Type, &maxval);
392    /* Calculate the percentage charged. */
393    battery_full = (currentval * 100) / maxval;
394
395    /* Retrieve the remaining battery power or time until charged in minutes. */
396    if (!have_power)
397      {
398         values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToEmptyKey));
399         CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
400         time_left = currentval * 60;
401      }
402    else
403      {
404         values = CFDictionaryGetValue(device_dict, CFSTR(kIOPSTimeToFullChargeKey));
405         CFNumberGetValue(values, kCFNumberSInt32Type, &currentval);
406         time_left = currentval * 60;
407      }
408    CFRelease(sources);
409    CFRelease(blob);
410 }
411
412 #else
413
414 /***---***/
415 /* new linux power class api to get power info - brand new and this code
416  * may have bugs, but it is a good attempt to get it right */
417 #if 0
418 static Eina_Bool linux_sys_class_power_supply_cb_event_fd_active(void             *data,
419                                                                  Ecore_Fd_Handler *fd_handler);
420 static void      linux_sys_class_power_supply_check(void);
421 #endif
422 static void      linux_sys_class_power_supply_init(void);
423
424 typedef struct _Sys_Class_Power_Supply_Uevent Sys_Class_Power_Supply_Uevent;
425
426 #define BASIS_CHARGE  1
427 #define BASIS_ENERGY  2
428 #define BASIS_VOLTAGE 3
429
430 struct _Sys_Class_Power_Supply_Uevent
431 {
432    char             *name;
433    int               fd;
434    Ecore_Fd_Handler *fd_handler;
435
436    int               present;
437
438    int               basis;
439    int               basis_empty;
440    int               basis_full;
441
442    unsigned char     have_current_avg : 1;
443    unsigned char     have_current_now : 1;
444 };
445
446 static Eina_List *events = NULL;
447
448 #if 0
449 static Ecore_Timer *sys_class_delay_check = NULL;
450
451 static Eina_Bool
452 linux_sys_class_power_supply_cb_delay_check(void *data)
453 {
454    linux_sys_class_power_supply_init();
455    poll_cb(NULL);
456    sys_class_delay_check = NULL;
457    return ECORE_CALLBACK_CANCEL;
458 }
459
460 static Ecore_Timer *re_init_timer = NULL;
461
462 static Eina_Bool
463 linux_sys_class_power_supply_cb_re_init(void *data)
464 {
465    Sys_Class_Power_Supply_Uevent *sysev;
466
467    if (events)
468      {
469         EINA_LIST_FREE(events, sysev)
470           {
471 //           if (sysev->fd_handler)
472 //             ecore_main_fd_handler_del(sysev->fd_handler);
473 //           if (sysev->fd >= 0) close(sysev->fd);
474                    free(sysev->name);
475                    free(sysev);
476           }
477      }
478    linux_sys_class_power_supply_init();
479    re_init_timer = NULL;
480    return ECORE_CALLBACK_CANCEL;
481 }
482
483 static Eina_Bool
484 linux_sys_class_power_supply_cb_event_fd_active(void             *data,
485                                                 Ecore_Fd_Handler *fd_handler)
486 {
487    Sys_Class_Power_Supply_Uevent *sysev;
488
489    sysev = data;
490    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
491      {
492         int lost = 0;
493         for (;; )
494           {
495              char buf[1024];
496              int num;
497
498              if ((num = read(sysev->fd, buf, sizeof(buf))) < 1)
499                {
500                   lost = ((errno == EIO) ||
501                           (errno == EBADF) ||
502                           (errno == EPIPE) ||
503                           (errno == EINVAL) ||
504                           (errno == ENOSPC) ||
505                           (errno == ENODEV));
506                   if (num <= 0) break;
507                }
508           }
509         if (lost)
510           {
511              events = eina_list_remove(events, sysev);
512
513 //           if (sysev->fd_handler)
514 //             ecore_main_fd_handler_del(sysev->fd_handler);
515 //           if (sysev->fd >= 0) close(sysev->fd);
516              free(sysev->name);
517              free(sysev);
518
519              if (re_init_timer) ecore_timer_del(re_init_timer);
520              re_init_timer = ecore_timer_add(1.0, linux_sys_class_power_supply_cb_re_init, NULL);
521           }
522         else
523           {
524              if (sys_class_delay_check) ecore_timer_del(sys_class_delay_check);
525              sys_class_delay_check = ecore_timer_add(0.2, linux_sys_class_power_supply_cb_delay_check, NULL);
526           }
527      }
528    return ECORE_CALLBACK_CANCEL;
529 }
530
531 #endif
532 static void
533 linux_sys_class_power_supply_sysev_init(Sys_Class_Power_Supply_Uevent *sysev)
534 {
535    char buf[4096];
536
537    sysev->basis = 0;
538    sysev->have_current_avg = 0;
539    sysev->have_current_now = 0;
540
541    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/present", sysev->name);
542    sysev->present = int_file_get(buf);
543    if (!sysev->present) return;
544
545    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_avg", sysev->name);
546    if (ecore_file_exists(buf)) sysev->have_current_avg = 1;
547    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_now", sysev->name);
548    if (ecore_file_exists(buf)) sysev->have_current_now = 1;
549
550    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full", sysev->name);
551    if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE;
552    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full_design", sysev->name);
553    if (ecore_file_exists(buf)) sysev->basis = BASIS_VOLTAGE;
554
555    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full", sysev->name);
556    if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY;
557    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full_design", sysev->name);
558    if (ecore_file_exists(buf)) sysev->basis = BASIS_ENERGY;
559
560    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full", sysev->name);
561    if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE;
562    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full_design", sysev->name);
563    if (ecore_file_exists(buf)) sysev->basis = BASIS_CHARGE;
564
565    if (sysev->basis == BASIS_CHARGE)
566      {
567         snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full", sysev->name);
568         sysev->basis_full = int_file_get(buf);
569         snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_empty", sysev->name);
570         sysev->basis_empty = int_file_get(buf);
571         if (sysev->basis_full < 0)
572           {
573              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_full_design", sysev->name);
574              sysev->basis_full = int_file_get(buf);
575           }
576         if (sysev->basis_empty < 0)
577           {
578              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_empty_design", sysev->name);
579              sysev->basis_empty = int_file_get(buf);
580           }
581      }
582    else if (sysev->basis == BASIS_ENERGY)
583      {
584         snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full", sysev->name);
585         sysev->basis_full = int_file_get(buf);
586         snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_empty", sysev->name);
587         sysev->basis_empty = int_file_get(buf);
588         if (sysev->basis_full < 0)
589           {
590              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_full_design", sysev->name);
591              sysev->basis_full = int_file_get(buf);
592           }
593         if (sysev->basis_empty < 0)
594           {
595              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_empty_design", sysev->name);
596              sysev->basis_empty = int_file_get(buf);
597           }
598      }
599    else if (sysev->basis == BASIS_VOLTAGE)
600      {
601         snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full", sysev->name);
602         sysev->basis_full = int_file_get(buf);
603         snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_empty", sysev->name);
604         sysev->basis_empty = int_file_get(buf);
605         if (sysev->basis_full < 0)
606           {
607              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_full_design", sysev->name);
608              sysev->basis_full = int_file_get(buf);
609           }
610         if (sysev->basis_empty < 0)
611           {
612              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_empty_design", sysev->name);
613              sysev->basis_empty = int_file_get(buf);
614           }
615      }
616 }
617
618 static int
619 linux_sys_class_power_supply_is_battery(char *name)
620 {
621    int fd;
622    int ret = 0;
623    char buf[256];
624
625    snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/type", name);
626    fd = open(buf, O_RDONLY);
627    if (fd < 0)
628      {
629         ret = 0;
630         goto NO_OPEN;
631      }
632    else if (read(fd, buf, sizeof(buf)) < 1)
633      ret = 0;
634    else if (!strncmp(buf, "Battery", 7))
635      ret = 1;
636
637    close(fd);
638
639 NO_OPEN:
640    return ret;
641 }
642
643 static void
644 linux_sys_class_power_supply_init(void)
645 {
646    Eina_List *l;
647
648    if (events)
649      {
650         Sys_Class_Power_Supply_Uevent *sysev;
651
652         EINA_LIST_FOREACH(events, l, sysev)
653           linux_sys_class_power_supply_sysev_init(sysev);
654      }
655    else
656      {
657         Eina_List *bats;
658         char *name;
659 //      char buf[4096];
660
661         bats = ecore_file_ls("/sys/class/power_supply/");
662         if (bats)
663           {
664              events = NULL;
665
666              EINA_LIST_FREE(bats, name)
667                {
668                   Sys_Class_Power_Supply_Uevent *sysev;
669
670                   if (!(linux_sys_class_power_supply_is_battery(name)))
671                     {
672                        free(name);
673                        continue;
674                     }
675
676                   sysev = (Sys_Class_Power_Supply_Uevent *)calloc(1, sizeof(Sys_Class_Power_Supply_Uevent));
677                   sysev->name = name;
678 //                snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/uevent", name);
679 //                sysev->fd = open(buf, O_RDONLY);
680 //                if (sysev->fd >= 0)
681 //                  sysev->fd_handler = ecore_main_fd_handler_add(sysev->fd,
682 //                                                                ECORE_FD_READ,
683 //                                                                linux_sys_class_power_supply_cb_event_fd_active,
684 //                                                                sysev,
685 //                                                                NULL, NULL);
686                   events = eina_list_append(events, sysev);
687                   linux_sys_class_power_supply_sysev_init(sysev);
688                }
689           }
690      }
691 }
692
693 static void
694 linux_sys_class_power_supply_check(void)
695 {
696    Eina_List *l;
697    char *name;
698    char buf[4096];
699
700    battery_full = -1;
701    time_left = -1;
702    have_battery = 0;
703    have_power = 0;
704
705    if (events)
706      {
707         Sys_Class_Power_Supply_Uevent *sysev;
708         int total_pwr_now;
709         int total_pwr_max;
710         int nofull = 0;
711
712         total_pwr_now = 0;
713         total_pwr_max = 0;
714         time_left = 0;
715         EINA_LIST_FOREACH(events, l, sysev)
716           {
717              char *tmp;
718              int present = 0;
719              int charging = -1;
720              int capacity = -1;
721              int current = -1;
722              int time_to_full = -1;
723              int time_to_empty = -1;
724              int full = -1;
725              int pwr_now = -1;
726              int pwr_empty = -1;
727              int pwr_full = -1;
728              int pwr = 0;
729
730              name = sysev->name;
731
732              /* fetch more generic info */
733              // init
734              present = sysev->present;
735              if (!present) continue;
736
737              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/capacity", name);
738              capacity = int_file_get(buf);
739              if (sysev->have_current_avg)
740                {
741                   snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_avg", name);
742                   current = int_file_get(buf);
743                }
744              else if (sysev->have_current_now)
745                {
746                   snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/current_now", name);
747                   current = int_file_get(buf);
748                }
749
750              /* FIXME: do we get a uevent on going from charging to full?
751               * if so, move this to init */
752              snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/status", name);
753              tmp = str_file_get(buf);
754              if (tmp)
755                {
756                   full = 0;
757                   if (!strncasecmp("discharging", tmp, 11)) charging = 0;
758                   else if (!strncasecmp("unknown", tmp, 7))
759                     charging = 0;
760                   else if (!strncasecmp("not charging", tmp, 12))
761                     charging = 0;
762                   else if (!strncasecmp("charging", tmp, 8))
763                     charging = 1;
764                   else if (!strncasecmp("full", tmp, 4))
765                     {
766                        full = 1;
767                        charging = 0;
768                     }
769                   free(tmp);
770                }
771              /* some batteries can/will/want to predict how long they will
772               * last. if so - take what the battery says. too bad if it's
773               * wrong. that's a buggy battery or driver */
774              if (!full)
775                {
776                   nofull++;
777                   if (charging)
778                     {
779                        snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/time_to_full_now", name);
780                        time_to_full = int_file_get(buf);
781                     }
782                   else
783                     {
784                        snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/time_to_empty_now", name);
785                        time_to_empty = int_file_get(buf);
786                     }
787                }
788
789              /* now get charge, energy and voltage. take the one that provides
790               * the best info (charge first, then energy, then voltage */
791              if (sysev->basis == BASIS_CHARGE)
792                snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/charge_now", name);
793              else if (sysev->basis == BASIS_ENERGY)
794                snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/energy_now", name);
795              else if (sysev->basis == BASIS_VOLTAGE)
796                snprintf(buf, sizeof(buf), "/sys/class/power_supply/%s/voltage_now", name);
797              pwr_now = int_file_get(buf);
798              pwr_empty = sysev->basis_empty;
799              pwr_full = sysev->basis_full;
800
801              if (pwr_empty < 0) pwr_empty = 0;
802
803              if (full) pwr_now = pwr_full;
804              else
805                {
806                   if (pwr_now < 0)
807                     pwr_now = (((long long)capacity * ((long long)pwr_full - (long long)pwr_empty)) / 100) + pwr_empty;
808                }
809
810              if (sysev->present) have_battery = 1;
811              if (charging)
812                {
813                   pwr_now = pwr_now;
814                   have_power = 1;
815                   if (time_to_full >= 0)
816                     {
817                        if (time_to_full > time_left)
818                          time_left = time_to_full;
819                     }
820                   else
821                     {
822                        if (current == 0) time_left = 0;
823                        else if (current < 0)
824                          time_left = -1;
825                        else
826                          {
827                             pwr = (((long long)pwr_full - (long long)pwr_now) * 3600) / -current;
828                             if (pwr > time_left) time_left = pwr;
829                          }
830                     }
831                }
832              else
833                {
834                   have_power = 0;
835                   if (time_to_empty >= 0) time_left += time_to_empty;
836                   else
837                     {
838                        if (time_to_empty < 0)
839                          {
840                             if (current > 0)
841                               {
842                                  pwr = (((long long)pwr_now - (long long)pwr_empty) * 3600) / current;
843                                  time_left += pwr;
844                               }
845                          }
846                     }
847                }
848              total_pwr_now += pwr_now - pwr_empty;
849              total_pwr_max += pwr_full - pwr_empty;
850           }
851         if (total_pwr_max > 0)
852           battery_full = ((long long)total_pwr_now * 100) / total_pwr_max;
853         if (nofull == 0)
854           time_left = -1;
855      }
856 }
857
858 /***---***/
859 /* "here and now" ACPI based power checking. is there for linux and most
860  * modern laptops. as of linux 2.6.24 it is replaced with
861  * linux_sys_class_power_supply_init/check() though as this is the new
862  * power class api to poll for power stuff
863  */
864 static Eina_Bool linux_acpi_cb_acpid_add(void *data,
865                                          int   type,
866                                          void *event);
867 static Eina_Bool linux_acpi_cb_acpid_del(void *data,
868                                          int   type,
869                                          void *event);
870 static Eina_Bool linux_acpi_cb_acpid_data(void *data,
871                                           int   type,
872                                           void *event);
873 static void linux_acpi_init(void);
874 static void linux_acpi_check(void);
875
876 static int acpi_max_full = -1;
877 static int acpi_max_design = -1;
878 static Ecore_Con_Server *acpid = NULL;
879 static Ecore_Event_Handler *acpid_handler_add = NULL;
880 static Ecore_Event_Handler *acpid_handler_del = NULL;
881 static Ecore_Event_Handler *acpid_handler_data = NULL;
882 static Ecore_Timer *delay_check = NULL;
883 static int event_fd = -1;
884 static Ecore_Fd_Handler *event_fd_handler = NULL;
885
886 static Eina_Bool
887 linux_acpi_cb_delay_check(void *data __UNUSED__)
888 {
889    linux_acpi_init();
890    poll_cb(NULL);
891    delay_check = NULL;
892    return ECORE_CALLBACK_CANCEL;
893 }
894
895 static Eina_Bool
896 linux_acpi_cb_acpid_add(void *data  __UNUSED__,
897                         int type    __UNUSED__,
898                         void *event __UNUSED__)
899 {
900    return ECORE_CALLBACK_PASS_ON;
901 }
902
903 static Eina_Bool
904 linux_acpi_cb_acpid_del(void *data  __UNUSED__,
905                         int type    __UNUSED__,
906                         void *event __UNUSED__)
907 {
908    ecore_con_server_del(acpid);
909    acpid = NULL;
910    if (acpid_handler_add) ecore_event_handler_del(acpid_handler_add);
911    acpid_handler_add = NULL;
912    if (acpid_handler_del) ecore_event_handler_del(acpid_handler_del);
913    acpid_handler_del = NULL;
914    if (acpid_handler_data) ecore_event_handler_del(acpid_handler_data);
915    acpid_handler_data = NULL;
916    return ECORE_CALLBACK_PASS_ON;
917 }
918
919 static Eina_Bool
920 linux_acpi_cb_acpid_data(void *data  __UNUSED__,
921                          int type    __UNUSED__,
922                          void *event __UNUSED__)
923 {
924    if (delay_check) ecore_timer_del(delay_check);
925    delay_check = ecore_timer_add(0.2, linux_acpi_cb_delay_check, NULL);
926    return ECORE_CALLBACK_PASS_ON;
927 }
928
929 static Eina_Bool
930 linux_acpi_cb_event_fd_active(void *data        __UNUSED__,
931                               Ecore_Fd_Handler *fd_handler)
932 {
933    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
934      {
935         int lost = 0;
936         for (;; )
937           {
938              char buf[1024];
939              int num;
940
941              if ((num = read(event_fd, buf, sizeof(buf))) < 1)
942                {
943                   lost = ((errno == EIO) ||
944                           (errno == EBADF) ||
945                           (errno == EPIPE) ||
946                           (errno == EINVAL) ||
947                           (errno == ENOSPC));
948                   if (num == 0) break;
949                }
950           }
951         if (lost)
952           {
953              ecore_main_fd_handler_del(event_fd_handler);
954              event_fd_handler = NULL;
955              close(event_fd);
956              event_fd = -1;
957           }
958         else
959           {
960              if (delay_check) ecore_timer_del(delay_check);
961              delay_check = ecore_timer_add(0.2, linux_acpi_cb_delay_check, NULL);
962           }
963      }
964    return ECORE_CALLBACK_RENEW;
965 }
966
967 static void
968 linux_acpi_init(void)
969 {
970    Eina_List *powers;
971    Eina_List *bats;
972
973    bats = ecore_file_ls("/proc/acpi/battery");
974    if (bats)
975      {
976         char *name;
977
978         have_power = 0;
979         powers = ecore_file_ls("/proc/acpi/ac_adapter");
980         if (powers)
981           {
982              char *name;
983
984              EINA_LIST_FREE(powers, name)
985                {
986                   char buf[4096];
987                   FILE *f;
988
989                   snprintf(buf, sizeof(buf), "/proc/acpi/ac_adapter/%s/state", name);
990                   f = fopen(buf, "r");
991                   if (f)
992                     {
993                        char *tmp;
994
995      /* state */
996                        tmp = fgets(buf, sizeof(buf), f);
997                        if (tmp) tmp = str_get(tmp);
998                        if (tmp)
999                          {
1000                             if (!strcmp(tmp, "on-line")) have_power = 1;
1001                             free(tmp);
1002                          }
1003                     }
1004
1005                   free(name);
1006                }
1007           }
1008
1009         have_battery = 0;
1010         acpi_max_full = 0;
1011         acpi_max_design = 0;
1012         EINA_LIST_FREE(bats, name)
1013           {
1014              char buf[4096];
1015              FILE *f;
1016
1017              snprintf(buf, sizeof(buf), "/proc/acpi/battery/%s/info", name);
1018              f = fopen(buf, "r");
1019              if (f)
1020                {
1021                   char *tmp;
1022
1023      /* present */
1024                   tmp = fgets(buf, sizeof(buf), f);
1025                   if (tmp) tmp = str_get(tmp);
1026                   if (tmp)
1027                     {
1028                        if (!strcmp(tmp, "yes")) have_battery = 1;
1029                        free(tmp);
1030                     }
1031      /* design cap */
1032                   tmp = fgets(buf, sizeof(buf), f);
1033                   if (tmp) tmp = str_get(tmp);
1034                   if (tmp)
1035                     {
1036                        if (strcmp(tmp, "unknown")) acpi_max_design += atoi(tmp);
1037                        free(tmp);
1038                     }
1039      /* last full cap */
1040                   tmp = fgets(buf, sizeof(buf), f);
1041                   if (tmp) tmp = str_get(tmp);
1042                   if (tmp)
1043                     {
1044                        if (strcmp(tmp, "unknown")) acpi_max_full += atoi(tmp);
1045                        free(tmp);
1046                     }
1047                   fclose(f);
1048                }
1049
1050              free(name);
1051           }
1052      }
1053    if (!acpid)
1054      {
1055         acpid = ecore_con_server_connect(ECORE_CON_LOCAL_SYSTEM,
1056                                          "/var/run/acpid.socket", -1, NULL);
1057         if (acpid)
1058           {
1059              acpid_handler_add = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD,
1060                                                          linux_acpi_cb_acpid_add, NULL);
1061              acpid_handler_del = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL,
1062                                                          linux_acpi_cb_acpid_del, NULL);
1063              acpid_handler_data = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA,
1064                                                           linux_acpi_cb_acpid_data, NULL);
1065           }
1066         else
1067           {
1068              if (event_fd < 0)
1069                {
1070                   event_fd = open("/proc/acpi/event", O_RDONLY);
1071                   if (event_fd >= 0)
1072                     event_fd_handler = ecore_main_fd_handler_add(event_fd,
1073                                                                  ECORE_FD_READ,
1074                                                                  linux_acpi_cb_event_fd_active,
1075                                                                  NULL,
1076                                                                  NULL, NULL);
1077                }
1078           }
1079      }
1080 }
1081
1082 static void
1083 linux_acpi_check(void)
1084 {
1085    Eina_List *bats;
1086
1087    battery_full = -1;
1088    time_left = -1;
1089    have_battery = 0;
1090    have_power = 0;
1091
1092    bats = ecore_file_ls("/proc/acpi/battery");
1093    if (bats)
1094      {
1095         char *name;
1096         int rate = 0;
1097         int capacity = 0;
1098
1099         EINA_LIST_FREE(bats, name)
1100           {
1101              char buf[4096];
1102              char *tmp;
1103              FILE *f;
1104
1105              snprintf(buf, sizeof(buf), "/proc/acpi/battery/%s/state", name);
1106              free(name);
1107              f = fopen(buf, "r");
1108              if (!f) continue;
1109
1110              tmp = file_str_entry_get(f, "present:");
1111              if (!tmp) goto fclose_and_continue;
1112              if (!strcasecmp(tmp, "yes")) have_battery = 1;
1113              free(tmp);
1114
1115              tmp = file_str_entry_get(f, "capacity state:");
1116              if (!tmp) goto fclose_and_continue;
1117              free(tmp);
1118
1119              tmp = file_str_entry_get(f, "charging state:");
1120              if (!tmp) goto fclose_and_continue;
1121              if ((have_power == 0) && (!strcasecmp(tmp, "charging")))
1122                have_power = 1;
1123              free(tmp);
1124
1125              tmp = file_str_entry_get(f, "present rate:");
1126              if (!tmp) goto fclose_and_continue;
1127              if (strcasecmp(tmp, "unknown")) rate += atoi(tmp);
1128              free(tmp);
1129
1130              tmp = file_str_entry_get(f, "remaining capacity:");
1131              if (!tmp) goto fclose_and_continue;
1132              if (strcasecmp(tmp, "unknown")) capacity += atoi(tmp);
1133              free(tmp);
1134
1135 fclose_and_continue:
1136              fclose(f);
1137           }
1138
1139         if (acpi_max_full > 0)
1140           battery_full = 100 * (long long)capacity / acpi_max_full;
1141         else if (acpi_max_design > 0)
1142           battery_full = 100 * (long long)capacity / acpi_max_design;
1143         else
1144           battery_full = -1;
1145         if (rate <= 0) time_left = -1;
1146         else
1147           {
1148              if (have_power)
1149                time_left = (3600 * ((long long)acpi_max_full - (long long)capacity)) / rate;
1150              else
1151                time_left = (3600 * (long long)capacity) / rate;
1152           }
1153      }
1154 }
1155
1156 /***---***/
1157 /* old school apm support - very old laptops and some devices support this.
1158  * this is here for legacy support and i wouldn't suggest spending any
1159  * effort on it as it is complete below as best i know, but could have missed
1160  * one or 2 things, but not worth fixing */
1161 static void linux_apm_init(void);
1162 static void linux_apm_check(void);
1163
1164 static void
1165 linux_apm_init(void)
1166 {
1167    /* nothing to do */
1168 }
1169
1170 static void
1171 linux_apm_check(void)
1172 {
1173    FILE *f;
1174    char s1[32], s2[32], s3[32], *endptr;
1175    int apm_flags, ac_stat, bat_stat, bat_flags, bat_val, time_val;
1176
1177    battery_full = -1;
1178    time_left = -1;
1179    have_battery = 0;
1180    have_power = 0;
1181
1182    f = fopen("/proc/apm", "r");
1183    if (!f) return;
1184
1185    if (fscanf(f, "%*s %*s %x %x %x %x %31s %31s %31s",
1186               &apm_flags, &ac_stat, &bat_stat, &bat_flags, s1, s2, s3) != 7)
1187      {
1188         fclose(f);
1189         return;
1190      }
1191    fclose(f);
1192
1193    bat_val = strtol(s1, &endptr, 10);
1194    if (*endptr != '%')
1195      {
1196         bat_val = -1;
1197         return;
1198      }
1199
1200    if (!strcmp(s3, "sec")) time_val = atoi(s2);
1201    else if (!strcmp(s3, "min"))
1202      time_val = atoi(s2) * 60;
1203    else time_val = 0;
1204
1205    if ((bat_flags != 0xff) && (bat_flags & 0x80))
1206      {
1207         have_battery = 0;
1208         have_power = 0;
1209         battery_full = 100;
1210         time_left = 0;
1211         return;
1212      }
1213
1214    if (bat_val >= 0)
1215      {
1216         have_battery = 1;
1217         have_power = ac_stat;
1218         battery_full = bat_val;
1219         if (battery_full > 100) battery_full = 100;
1220         if (ac_stat == 1) time_left = -1;
1221         else time_left = time_val;
1222      }
1223    else
1224      {
1225         switch (bat_stat)
1226           {
1227            case 0: /* high */
1228              have_battery = 1;
1229              have_power = ac_stat;
1230              battery_full = 100;
1231              time_left = -1;
1232              break;
1233
1234            case 1: /* medium */
1235              have_battery = 1;
1236              have_power = ac_stat;
1237              battery_full = 50;
1238              time_left = -1;
1239              break;
1240
1241            case 2: /* low */
1242              have_battery = 1;
1243              have_power = ac_stat;
1244              battery_full = 25;
1245              time_left = -1;
1246              break;
1247
1248            case 3: /* charging */
1249              have_battery = 1;
1250              have_power = ac_stat;
1251              battery_full = 100;
1252              time_left = -1;
1253              break;
1254           }
1255      }
1256 }
1257
1258 /***---***/
1259 /* for older mac powerbooks. legacy as well like linux_apm_init/check. leave
1260  * it alone unless you have to touch it */
1261 static void linux_pmu_init(void);
1262 static void linux_pmu_check(void);
1263
1264 static void
1265 linux_pmu_init(void)
1266 {
1267    /* nothing to do */
1268 }
1269
1270 static void
1271 linux_pmu_check(void)
1272 {
1273    FILE *f;
1274    char buf[4096];
1275    Eina_List *bats;
1276    char *name;
1277    int ac = 0;
1278    int charge = 0;
1279    int max_charge = 0;
1280    int seconds = 0;
1281    int curcharge = 0;
1282    int curmax = 0;
1283
1284    f = fopen("/proc/pmu/info", "r");
1285    if (f)
1286      {
1287         char *tmp;
1288         /* Skip driver */
1289         tmp = fgets(buf, sizeof(buf), f);
1290         if (!tmp)
1291           {
1292              EINA_LOG_ERR("no driver info in /proc/pmu/info");
1293              goto fclose_and_continue;
1294           }
1295         /* Skip firmware */
1296         tmp = fgets(buf, sizeof(buf), f);
1297         if (!tmp)
1298           {
1299              EINA_LOG_ERR("no firmware info in /proc/pmu/info");
1300              goto fclose_and_continue;
1301           }
1302         /* Read ac */
1303         tmp = fgets(buf, sizeof(buf), f);
1304         if (!tmp)
1305           {
1306              EINA_LOG_ERR("no AC info in /proc/pmu/info");
1307              goto fclose_and_continue;
1308           }
1309         ac = int_get(buf);
1310 fclose_and_continue:
1311         fclose(f);
1312      }
1313    bats = ecore_file_ls("/proc/pmu");
1314    if (bats)
1315      {
1316         have_battery = 1;
1317         have_power = ac;
1318         EINA_LIST_FREE(bats, name)
1319           {
1320              if (strncmp(name, "battery", 7)) continue;
1321              snprintf(buf, sizeof(buf), "/proc/pmu/%s", name);
1322              f = fopen(buf, "r");
1323              if (f)
1324                {
1325                   int timeleft = 0;
1326                   int current = 0;
1327
1328                   while (fgets(buf, sizeof (buf), f))
1329                     {
1330                        char *token;
1331
1332                        if ((token = strtok(buf, ":")))
1333                          {
1334                             if (!strncmp("charge", token, 6))
1335                               charge = atoi(strtok(0, ": "));
1336                             else if (!strncmp("max_charge", token, 9))
1337                               max_charge = atoi(strtok(0, ": "));
1338                             else if (!strncmp("current", token, 7))
1339                               current = atoi(strtok(0, ": "));
1340                             else if (!strncmp("time rem", token, 8))
1341                               timeleft = atoi(strtok(0, ": "));
1342                             else
1343                               strtok(0, ": ");
1344                          }
1345                     }
1346                   curmax += max_charge;
1347                   curcharge += charge;
1348                   fclose(f);
1349                   if (!current)
1350                     {
1351      /* Neither charging nor discharging */
1352                     }
1353                   else if (!ac)
1354                     {
1355      /* When on dc, we are discharging */
1356                         seconds += timeleft;
1357                     }
1358                   else
1359                     {
1360      /* Charging - works in parallel */
1361                         seconds = MAX(timeleft, seconds);
1362                     }
1363                }
1364
1365              free(name);
1366           }
1367         if (max_charge > 0) battery_full = ((long long)charge * 100) / max_charge;
1368         else battery_full = 0;
1369         time_left = seconds;
1370      }
1371    else
1372      {
1373         have_power = ac;
1374         have_battery = 0;
1375         battery_full = -1;
1376         time_left = -1;
1377      }
1378 }
1379
1380 #endif
1381
1382 static int
1383 dir_has_contents(const char *dir)
1384 {
1385    Eina_List *bats;
1386    char *file;
1387    int count;
1388
1389    bats = ecore_file_ls(dir);
1390
1391    count = eina_list_count(bats);
1392    EINA_LIST_FREE(bats, file)
1393      free(file);
1394    if (count > 0) return 1;
1395    return 0;
1396 }
1397
1398 static void
1399 init(void)
1400 {
1401 #ifdef __FreeBSD__
1402    int acline;
1403    size_t len;
1404
1405    len = sizeof(acline);
1406    if (!sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0))
1407      {
1408         int acline_mib[3] = {-1};
1409
1410         len = 3;
1411         if (!sysctlnametomib("hw.acpi.acline", acline_mib, &len))
1412           {
1413              mode = CHECK_ACPI;
1414              bsd_acpi_init();
1415           }
1416      }
1417    else
1418      {
1419 #ifdef __i386__
1420         if (ecore_file_exists("/dev/apm"))
1421           {
1422              mode = CHECK_APM;
1423              bsd_apm_init();
1424           }
1425 #endif
1426      }
1427 #elif defined(HAVE_CFBASE_H) /* OS X */
1428    darwin_init();
1429 #else
1430    if ((ecore_file_is_dir("/sys/class/power_supply")) &&
1431        (dir_has_contents("/sys/class/power_supply")))
1432      {
1433         mode = CHECK_SYS_CLASS_POWER_SUPPLY;
1434         linux_sys_class_power_supply_init();
1435      }
1436    else if (ecore_file_is_dir("/proc/acpi")) /* <= 2.6.24 */
1437      {
1438         mode = CHECK_ACPI;
1439         linux_acpi_init();
1440      }
1441    else if (ecore_file_exists("/proc/apm"))
1442      {
1443         mode = CHECK_APM;
1444         linux_apm_init();
1445      }
1446    else if (ecore_file_is_dir("/proc/pmu"))
1447      {
1448         mode = CHECK_PMU;
1449         linux_pmu_init();
1450      }
1451 #endif
1452 }
1453
1454 static Eina_Bool
1455 poll_cb(void *data __UNUSED__)
1456 {
1457    int ptime_left;
1458    int pbattery_full;
1459    int phave_battery;
1460    int phave_power;
1461
1462    ptime_left = time_left;
1463    pbattery_full = battery_full;
1464    phave_battery = have_battery;
1465    phave_power = have_power;
1466
1467 #ifdef __FreeBSD__
1468    switch (mode)
1469      {
1470       case CHECK_ACPI:
1471         bsd_acpi_check();
1472         break;
1473
1474 #ifdef __i386__
1475       case CHECK_APM:
1476         bsd_apm_check();
1477         break;
1478
1479 #endif
1480       default:
1481         battery_full = -1;
1482         time_left = -1;
1483         have_battery = 0;
1484         have_power = 0;
1485         break;
1486      }
1487 #elif defined(HAVE_CFBASE_H) /* OS X */
1488    darwin_check();
1489    return ECORE_CALLBACK_RENEW;
1490 #else
1491    switch (mode)
1492      {
1493       case CHECK_ACPI:
1494         linux_acpi_check();
1495         break;
1496
1497       case CHECK_APM:
1498         linux_apm_check();
1499         break;
1500
1501       case CHECK_PMU:
1502         linux_pmu_check();
1503         break;
1504
1505       case CHECK_SYS_CLASS_POWER_SUPPLY:
1506         linux_sys_class_power_supply_check();
1507         break;
1508
1509       default:
1510         battery_full = -1;
1511         time_left = -1;
1512         have_battery = 0;
1513         have_power = 0;
1514         break;
1515      }
1516 #endif
1517    if ((ptime_left != time_left) ||
1518        (pbattery_full != battery_full) ||
1519        (phave_battery != have_battery) ||
1520        (phave_power != have_power))
1521      {
1522         if ((time_left < 0) &&
1523             ((have_battery) && (battery_full < 0)))
1524           printf("ERROR\n");
1525         else
1526           printf("%i %i %i %i %i\n",
1527                  battery_full, time_left, time_left, have_battery, have_power);
1528         fflush(stdout);
1529      }
1530    return ECORE_CALLBACK_RENEW;
1531 }
1532
1533 int
1534 main(int   argc,
1535      char *argv[])
1536 {
1537    if (argc != 2)
1538      {
1539         printf("ARGS INCORRECT!\n");
1540         return 0;
1541      }
1542    poll_interval = atoi(argv[1]);
1543
1544    ecore_init();
1545    ecore_file_init();
1546    ecore_con_init();
1547
1548    init();
1549    poller = ecore_poller_add(ECORE_POLLER_CORE, poll_interval, poll_cb, NULL);
1550    poll_cb(NULL);
1551
1552    ecore_main_loop_begin();
1553
1554    ecore_con_shutdown();
1555    ecore_file_shutdown();
1556    ecore_shutdown();
1557
1558    return 0;
1559 }
1560