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