52d028b4284847957d0f74b42b0270d42be362b7
[profile/ivi/ecore.git] / src / lib / ecore_file / ecore_file_monitor_poll.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <stdio.h>
6 #include <string.h>
7
8 #include "ecore_file_private.h"
9
10 #ifdef HAVE_POLL
11
12 /*
13  * TODO:
14  * - Implement recursive as an option!
15  * - Keep whole path or just name of file? (Memory or CPU...)
16  * - Remove requests without files?
17  * - Change poll time
18  */
19
20 typedef struct _Ecore_File_Monitor_Poll Ecore_File_Monitor_Poll;
21
22 #define ECORE_FILE_MONITOR_POLL(x) ((Ecore_File_Monitor_Poll *)(x))
23
24 struct _Ecore_File_Monitor_Poll
25 {
26    Ecore_File_Monitor  monitor;
27    int                 mtime;
28    unsigned char       deleted;
29 };
30
31 #define ECORE_FILE_INTERVAL_MIN  1.0
32 #define ECORE_FILE_INTERVAL_STEP 0.5
33 #define ECORE_FILE_INTERVAL_MAX  5.0
34
35 static double         _interval = ECORE_FILE_INTERVAL_MIN;
36 static Ecore_Timer   *_timer = NULL;
37 static Ecore_File_Monitor *_monitors = NULL;
38 static int          _lock = 0;
39
40 static Eina_Bool   _ecore_file_monitor_poll_handler(void *data);
41 static void        _ecore_file_monitor_poll_check(Ecore_File_Monitor *em);
42 static int         _ecore_file_monitor_poll_checking(Ecore_File_Monitor *em, char *name);
43
44 int
45 ecore_file_monitor_poll_init(void)
46 {
47    return 1;
48 }
49
50 int
51 ecore_file_monitor_poll_shutdown(void)
52 {
53    while(_monitors)
54         ecore_file_monitor_poll_del(_monitors);
55
56    if (_timer)
57      {
58         ecore_timer_del(_timer);
59         _timer = NULL;
60      }
61    return 1;
62 }
63
64 Ecore_File_Monitor *
65 ecore_file_monitor_poll_add(const char *path,
66                             void (*func) (void *data, Ecore_File_Monitor *em,
67                                           Ecore_File_Event event,
68                                           const char *path),
69                             void *data)
70 {
71    Ecore_File_Monitor *em;
72    size_t len;
73
74    if (!path) return NULL;
75    if (!func) return NULL;
76
77    em = calloc(1, sizeof(Ecore_File_Monitor_Poll));
78    if (!em) return NULL;
79
80    if (!_timer)
81      _timer = ecore_timer_add(_interval, _ecore_file_monitor_poll_handler, NULL);
82    else
83      ecore_timer_interval_set(_timer, ECORE_FILE_INTERVAL_MIN);
84
85    em->path = strdup(path);
86    len = strlen(em->path);
87    if (em->path[len - 1] == '/' && strcmp(em->path, "/"))
88      em->path[len - 1] = 0;
89
90    em->func = func;
91    em->data = data;
92
93    ECORE_FILE_MONITOR_POLL(em)->mtime = ecore_file_mod_time(em->path);
94    _monitors = ECORE_FILE_MONITOR(eina_inlist_append(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
95
96    if (ecore_file_exists(em->path))
97      {
98         if (ecore_file_is_dir(em->path))
99           {
100              /* Check for subdirs */
101              Eina_List *files;
102              char *file;
103
104              files = ecore_file_ls(em->path);
105              EINA_LIST_FREE(files, file)
106                     {
107                        Ecore_File *f;
108                        char buf[PATH_MAX];
109
110                        f = calloc(1, sizeof(Ecore_File));
111                        if (!f)
112                     {
113                        free(file);
114                          continue;
115                     }
116
117                        snprintf(buf, sizeof(buf), "%s/%s", em->path, file);
118                        f->name = file;
119                        f->mtime = ecore_file_mod_time(buf);
120                        f->is_dir = ecore_file_is_dir(buf);
121                        em->files = (Ecore_File *) eina_inlist_append(EINA_INLIST_GET(em->files), EINA_INLIST_GET(f));
122                     }
123           }
124      }
125    else
126      {
127         ecore_file_monitor_poll_del(em);
128         return NULL;
129      }
130
131    return em;
132 }
133
134 void
135 ecore_file_monitor_poll_del(Ecore_File_Monitor *em)
136 {
137    Ecore_File *l;
138
139    if (_lock)
140      {
141         ECORE_FILE_MONITOR_POLL(em)->deleted = 1;
142         return;
143      }
144
145    /* Remove files */
146    /*It's possible there weren't any files to monitor, so check if the list is init*/
147    if (em->files)
148      {
149         for (l = em->files; l;)
150           {
151              Ecore_File *file = l;
152
153              l = (Ecore_File *) EINA_INLIST_GET(l)->next;
154              free(file->name);
155              free(file);
156           }
157      }
158
159    if (_monitors)
160      _monitors = ECORE_FILE_MONITOR(eina_inlist_remove(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
161
162    free(em->path);
163    free(em);
164
165    if (_timer)
166      {
167         if (!_monitors)
168           {
169              ecore_timer_del(_timer);
170              _timer = NULL;
171           }
172         else
173           ecore_timer_interval_set(_timer, ECORE_FILE_INTERVAL_MIN);
174      }
175 }
176
177 static Eina_Bool
178 _ecore_file_monitor_poll_handler(void *data __UNUSED__)
179 {
180    Ecore_File_Monitor *l;
181
182    _interval += ECORE_FILE_INTERVAL_STEP;
183
184    _lock = 1;
185    EINA_INLIST_FOREACH(_monitors, l)
186         _ecore_file_monitor_poll_check(l);
187    _lock = 0;
188
189    if (_interval > ECORE_FILE_INTERVAL_MAX)
190      _interval = ECORE_FILE_INTERVAL_MAX;
191    ecore_timer_interval_set(_timer, _interval);
192
193    for (l = _monitors; l;)
194      {
195         Ecore_File_Monitor *em = l;
196
197         l = ECORE_FILE_MONITOR(EINA_INLIST_GET(l)->next);
198         if (ECORE_FILE_MONITOR_POLL(em)->deleted)
199           ecore_file_monitor_del(em);
200      }
201    return ECORE_CALLBACK_RENEW;
202 }
203
204 static void
205 _ecore_file_monitor_poll_check(Ecore_File_Monitor *em)
206 {
207    int mtime;
208
209    mtime = ecore_file_mod_time(em->path);
210    if (mtime < ECORE_FILE_MONITOR_POLL(em)->mtime)
211      {
212         Ecore_File *l;
213         Ecore_File_Event event;
214
215         /* Notify all files deleted */
216         for (l = em->files; l;)
217           {
218              Ecore_File *f = l;
219              char buf[PATH_MAX];
220
221              l = (Ecore_File *) EINA_INLIST_GET(l)->next;
222
223              snprintf(buf, sizeof(buf), "%s/%s", em->path, f->name);
224              if (f->is_dir)
225                event = ECORE_FILE_EVENT_DELETED_DIRECTORY;
226              else
227                event = ECORE_FILE_EVENT_DELETED_FILE;
228              em->func(em->data, em, event, buf);
229              free(f->name);
230              free(f);
231           }
232         em->files = NULL;
233         em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
234         _interval = ECORE_FILE_INTERVAL_MIN;
235      }
236    else
237      {
238         Ecore_File *l;
239
240         /* Check for changed files */
241         for (l = em->files; l;)
242           {
243              Ecore_File *f = l;
244              char buf[PATH_MAX];
245              int mtime;
246              Ecore_File_Event event;
247
248              l = (Ecore_File *) EINA_INLIST_GET(l)->next;
249
250              snprintf(buf, sizeof(buf), "%s/%s", em->path, f->name);
251              mtime = ecore_file_mod_time(buf);
252              if (mtime < f->mtime)
253                {
254                   if (f->is_dir)
255                     event = ECORE_FILE_EVENT_DELETED_DIRECTORY;
256                   else
257                     event = ECORE_FILE_EVENT_DELETED_FILE;
258
259                   em->func(em->data, em, event, buf);
260                   em->files = (Ecore_File *) eina_inlist_remove(EINA_INLIST_GET(em->files), EINA_INLIST_GET(f));
261                   free(f->name);
262                   free(f);
263                   _interval = ECORE_FILE_INTERVAL_MIN;
264                }
265              else if ((mtime > f->mtime) && !(f->is_dir))
266                {
267                   em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, buf);
268                   _interval = ECORE_FILE_INTERVAL_MIN;
269                   f->mtime = mtime;
270                }
271              else
272                f->mtime = mtime;
273           }
274
275         /* Check for new files */
276         if (ECORE_FILE_MONITOR_POLL(em)->mtime < mtime)
277           {
278              Eina_List *files;
279              Eina_List *l;
280              char *file;
281
282              /* Files have been added or removed */
283              files = ecore_file_ls(em->path);
284              if (files)
285                {
286                   /* Are we a directory? We should check first, rather than rely on null here*/
287                   EINA_LIST_FOREACH(files, l, file)
288                     {
289                        Ecore_File *f;
290                        char buf[PATH_MAX];
291                        Ecore_File_Event event;
292
293                        if (_ecore_file_monitor_poll_checking(em, file))
294                          continue;
295
296                        snprintf(buf, sizeof(buf), "%s/%s", em->path, file);
297                        f = calloc(1, sizeof(Ecore_File));
298                        if (!f)
299                          continue;
300
301                        f->name = strdup(file);
302                        f->mtime = ecore_file_mod_time(buf);
303                        f->is_dir = ecore_file_mod_time(buf);
304                        if (f->is_dir)
305                          event = ECORE_FILE_EVENT_CREATED_DIRECTORY;
306                        else
307                          event = ECORE_FILE_EVENT_CREATED_FILE;
308                        em->func(em->data, em, event, buf);
309                        em->files = (Ecore_File *) eina_inlist_append(EINA_INLIST_GET(em->files), EINA_INLIST_GET(f));
310                     }
311                   while (files)
312                     {
313                        file = eina_list_data_get(files);
314                        free(file);
315                        files = eina_list_remove_list(files, files);
316                     }
317                }
318
319              if (!ecore_file_is_dir(em->path))
320                em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, em->path);
321              _interval = ECORE_FILE_INTERVAL_MIN;
322           }
323      }
324    ECORE_FILE_MONITOR_POLL(em)->mtime = mtime;
325 }
326
327 static int
328 _ecore_file_monitor_poll_checking(Ecore_File_Monitor *em, char *name)
329 {
330    Ecore_File *l;
331
332    EINA_INLIST_FOREACH(em->files, l)
333      {
334         if (!strcmp(l->name, name))
335           return 1;
336      }
337    return 0;
338 }
339 #endif