EFL 1.7 svn doobies
[profile/ivi/efreet.git] / src / lib / efreet_desktop_command.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #undef alloca
6 #ifdef HAVE_ALLOCA_H
7 # include <alloca.h>
8 #elif defined __GNUC__
9 # define alloca __builtin_alloca
10 #elif defined _AIX
11 # define alloca __alloca
12 #elif defined _MSC_VER
13 # include <malloc.h>
14 # define alloca _alloca
15 #else
16 # include <stddef.h>
17 # ifdef  __cplusplus
18 extern "C"
19 # endif
20 void *alloca (size_t);
21 #endif
22
23 #include <unistd.h>
24 #include <ctype.h>
25
26 #ifdef _WIN32
27 # include <winsock2.h>
28 #endif
29
30 #include <Ecore.h>
31 #include <Ecore_File.h>
32
33 /* define macros and variable for using the eina logging system  */
34 #define EFREET_MODULE_LOG_DOM _efreet_desktop_log_dom
35 extern int _efreet_desktop_log_dom;
36
37 #include "Efreet.h"
38 #include "efreet_private.h"
39
40 /**
41  * @internal
42  * The different types of commands in an Exec entry
43  */
44 typedef enum Efreet_Desktop_Command_Flag
45 {
46     EFREET_DESKTOP_EXEC_FLAG_FULLPATH = 0x0001,
47     EFREET_DESKTOP_EXEC_FLAG_URI      = 0x0002
48 } Efreet_Desktop_Command_Flag;
49
50 /**
51  * @internal
52  * Efreet_Desktop_Command
53  */
54 typedef struct Efreet_Desktop_Command Efreet_Desktop_Command;
55
56 /**
57  * @internal
58  * Holds information on a desktop Exec command entry
59  */
60 struct Efreet_Desktop_Command
61 {
62   Efreet_Desktop *desktop;
63   int num_pending;
64
65   Efreet_Desktop_Command_Flag flags;
66
67   Efreet_Desktop_Command_Cb cb_command;
68   Efreet_Desktop_Progress_Cb cb_progress;
69   void *data;
70
71   Eina_List *files; /**< list of Efreet_Desktop_Command_File */
72 };
73
74 /**
75  * @internal
76  * Efreet_Desktop_Command_File
77  */
78 typedef struct Efreet_Desktop_Command_File Efreet_Desktop_Command_File;
79
80 /**
81  * @internal
82  * Stores information on a file passed to the desktop Exec command
83  */
84 struct Efreet_Desktop_Command_File
85 {
86   Efreet_Desktop_Command *command;
87   char *dir;
88   char *file;
89   char *fullpath;
90   char *uri;
91
92   int pending;
93 };
94
95 static int efreet_desktop_command_file_id = 0;
96
97 static void *efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop,
98                                             char *exec, int remaining);
99 static int efreet_desktop_command_flags_get(Efreet_Desktop *desktop);
100 static void *efreet_desktop_command_execs_process(Efreet_Desktop_Command *command, Eina_List *execs);
101
102 static Eina_List *efreet_desktop_command_build(Efreet_Desktop_Command *command);
103 static void efreet_desktop_command_free(Efreet_Desktop_Command *command);
104 static char *efreet_desktop_command_append_quoted(char *dest, int *size,
105                                                     int *len, char *src);
106 static char *efreet_desktop_command_append_multiple(char *dest, int *size, int *len,
107                                                     Efreet_Desktop_Command *command,
108                                                     char type);
109 static char *efreet_desktop_command_append_single(char *dest, int *size, int *len,
110                                                 Efreet_Desktop_Command_File *file,
111                                                 char type);
112 static char *efreet_desktop_command_append_icon(char *dest, int *size, int *len,
113                                                 Efreet_Desktop *desktop);
114
115 static Efreet_Desktop_Command_File *efreet_desktop_command_file_process(
116                                                     Efreet_Desktop_Command *command,
117                                                     const char *file);
118 static const char *efreet_desktop_command_file_uri_process(const char *uri);
119 static void efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file);
120
121 static void efreet_desktop_cb_download_complete(void *data, const char *file,
122                                                                 int status);
123 static int efreet_desktop_cb_download_progress(void *data, const char *file,
124                                            long int dltotal, long int dlnow,
125                                            long int ultotal, long int ulnow);
126
127 static char *efreet_desktop_command_path_absolute(const char *path);
128
129 static char *efreet_string_append(char *dest, int *size,
130                                     int *len, const char *src);
131 static char *efreet_string_append_char(char *dest, int *size,
132                                         int *len, char c);
133
134
135 EAPI void
136 efreet_desktop_exec(Efreet_Desktop *desktop, Eina_List *files, void *data)
137 {
138     efreet_desktop_command_get(desktop, files, efreet_desktop_exec_cb, data);
139 }
140
141 EAPI void *
142 efreet_desktop_command_get(Efreet_Desktop *desktop, Eina_List *files,
143                             Efreet_Desktop_Command_Cb func, void *data)
144 {
145     return efreet_desktop_command_progress_get(desktop, files, func, NULL, data);
146 }
147
148 EAPI Eina_List *
149 efreet_desktop_command_local_get(Efreet_Desktop *desktop, Eina_List *files)
150 {
151     Efreet_Desktop_Command *command;
152     char *file;
153     Eina_List *execs, *l;
154
155     EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
156     EINA_SAFETY_ON_NULL_RETURN_VAL(desktop->exec, NULL);
157
158     command = NEW(Efreet_Desktop_Command, 1);
159     if (!command) return 0;
160
161     command->desktop = desktop;
162
163     command->flags = efreet_desktop_command_flags_get(desktop);
164     /* get the required info for each file passed in */
165     if (files)
166     {
167         EINA_LIST_FOREACH(files, l, file)
168         {
169             Efreet_Desktop_Command_File *dcf;
170
171             dcf = efreet_desktop_command_file_process(command, file);
172             if (!dcf) continue;
173             if (dcf->pending)
174             {
175                 efreet_desktop_command_file_free(dcf);
176                 continue;
177             }
178             command->files = eina_list_append(command->files, dcf);
179         }
180     }
181
182     execs = efreet_desktop_command_build(command);
183     efreet_desktop_command_free(command);
184
185     return execs;
186 }
187
188 EAPI void *
189 efreet_desktop_command_progress_get(Efreet_Desktop *desktop, Eina_List *files,
190                                     Efreet_Desktop_Command_Cb cb_command,
191                                     Efreet_Desktop_Progress_Cb cb_progress,
192                                     void *data)
193 {
194     Efreet_Desktop_Command *command;
195     Eina_List *l;
196     char *file;
197     void *ret = NULL;
198
199     EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
200     EINA_SAFETY_ON_NULL_RETURN_VAL(desktop->exec, NULL);
201     EINA_SAFETY_ON_NULL_RETURN_VAL(cb_command, NULL);
202
203     command = NEW(Efreet_Desktop_Command, 1);
204     if (!command) return NULL;
205
206     command->cb_command = cb_command;
207     command->cb_progress = cb_progress;
208     command->data = data;
209     command->desktop = desktop;
210
211     command->flags = efreet_desktop_command_flags_get(desktop);
212     /* get the required info for each file passed in */
213     if (files)
214     {
215         EINA_LIST_FOREACH(files, l, file)
216         {
217             Efreet_Desktop_Command_File *dcf;
218
219             dcf = efreet_desktop_command_file_process(command, file);
220             if (!dcf) continue;
221             command->files = eina_list_append(command->files, dcf);
222             command->num_pending += dcf->pending;
223         }
224     }
225
226     if (command->num_pending == 0)
227     {
228         Eina_List *execs;
229
230         execs = efreet_desktop_command_build(command);
231         if (execs)
232         {
233             ret = efreet_desktop_command_execs_process(command, execs);
234             eina_list_free(execs);
235         }
236         efreet_desktop_command_free(command);
237     }
238
239     return ret;
240 }
241
242 static void *
243 efreet_desktop_exec_cb(void *data,
244                        Efreet_Desktop *desktop __UNUSED__,
245                        char *exec,
246                        int remaining __UNUSED__)
247 {
248     ecore_exe_run(exec, data);
249     free(exec);
250
251     return NULL;
252 }
253
254 /**
255  * @internal
256  *
257  * @brief Determine which file related field codes are present in the Exec string of a .desktop
258  * @params desktop and Efreet Desktop
259  * @return a bitmask of file field codes present in exec string
260  */
261 static int
262 efreet_desktop_command_flags_get(Efreet_Desktop *desktop)
263 {
264     int flags = 0;
265     const char *p;
266     /* first, determine which fields are present in the Exec string */
267     p = strchr(desktop->exec, '%');
268     while (p)
269     {
270         p++;
271         switch(*p)
272         {
273             case 'f':
274             case 'F':
275                 flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
276                 break;
277             case 'u':
278             case 'U':
279                 flags |= EFREET_DESKTOP_EXEC_FLAG_URI;
280                 break;
281             case '%':
282                 p++;
283                 break;
284             default:
285                 break;
286         }
287
288         p = strchr(p, '%');
289     }
290 #ifdef SLOPPY_SPEC
291     /* NON-SPEC!!! this is to work around LOTS of 'broken' .desktop files that
292      * do not specify %U/%u, %F/F etc. etc. at all. just a command. this is
293      * unlikely to be fixed in distributions etc. in the long run as gnome/kde
294      * seem to have workarounds too so no one notices.
295      */
296     if (!flags) flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
297 #endif
298
299     return flags;
300 }
301
302
303 /**
304  * @internal
305  *
306  * @brief Call the command callback for each exec in the list
307  * @param command
308  * @param execs
309  */
310 static void *
311 efreet_desktop_command_execs_process(Efreet_Desktop_Command *command, Eina_List *execs)
312 {
313     Eina_List *l;
314     char *exec;
315     int num;
316     void *ret = NULL;
317
318     num = eina_list_count(execs);
319     EINA_LIST_FOREACH(execs, l, exec)
320     {
321         ret = command->cb_command(command->data, command->desktop, exec, --num);
322     }
323     return ret;
324 }
325
326
327 /**
328  * @brief Builds the actual exec string from the raw string and a list of
329  * processed filename information. The callback passed in to
330  * efreet_desktop_command_get is called for each exec string created.
331  *
332  * @param command the command to build
333  * @return a list of executable strings
334  */
335 static Eina_List *
336 efreet_desktop_command_build(Efreet_Desktop_Command *command)
337 {
338     Eina_List *execs = NULL;
339     const Eina_List *l;
340     char *exec;
341
342     /* if the Exec field appends multiple, that will run the list to the end,
343      * causing this loop to only run once. otherwise, this loop will generate a
344      * command for each file in the list. if the list is empty, this
345      * will run once, removing any file field codes */
346     l = command->files;
347     do
348     {
349         const char *p;
350         int len = 0;
351         int size = PATH_MAX;
352         int file_added = 0;
353         Efreet_Desktop_Command_File *file = eina_list_data_get(l);
354        int single;
355
356         exec = malloc(size);
357         if (!exec) goto error;
358         p = command->desktop->exec;
359         len = 0;
360
361         single = 0;
362         while (*p)
363         {
364             if (len >= size - 1)
365             {
366                 char *tmp;
367
368                 size = len + 1024;
369                 tmp = realloc(exec, size);
370                 if (!tmp) goto error;
371                 exec = tmp;
372             }
373
374             /* XXX handle fields inside quotes? */
375             if (*p == '%')
376             {
377                 p++;
378                 switch (*p)
379                 {
380                     case 'f':
381                     case 'u':
382                     case 'd':
383                     case 'n':
384                         if (file)
385                         {
386                             exec = efreet_desktop_command_append_single(exec, &size,
387                                     &len, file, *p);
388                             if (!exec) goto error;
389                             file_added = 1;
390                             single = 1;
391                         }
392                         break;
393                     case 'F':
394                     case 'U':
395                     case 'D':
396                     case 'N':
397                         if (file)
398                         {
399                             exec = efreet_desktop_command_append_multiple(exec, &size,
400                                     &len, command, *p);
401                            fprintf(stderr, "EXE: '%s'\n", exec);
402                             if (!exec) goto error;
403                             file_added = 1;
404                         }
405                         break;
406                     case 'i':
407                         exec = efreet_desktop_command_append_icon(exec, &size, &len,
408                                 command->desktop);
409                         if (!exec) goto error;
410                         break;
411                     case 'c':
412                         exec = efreet_desktop_command_append_quoted(exec, &size, &len,
413                                 command->desktop->name);
414                         if (!exec) goto error;
415                         break;
416                     case 'k':
417                         exec = efreet_desktop_command_append_quoted(exec, &size, &len,
418                                 command->desktop->orig_path);
419                         if (!exec) goto error;
420                         break;
421                     case 'v':
422                     case 'm':
423                         WRN("[Efreet]: Deprecated conversion char: '%c' in file '%s'",
424                                 *p, command->desktop->orig_path);
425                         break;
426                     case '%':
427                         exec[len++] = *p;
428                         break;
429                     default:
430 #ifdef STRICT_SPEC
431                         WRN("[Efreet_desktop]: Unknown conversion character: '%c'", *p);
432 #endif
433                         break;
434                 }
435             }
436             else exec[len++] = *p;
437             p++;
438         }
439
440 #ifdef SLOPPY_SPEC
441         /* NON-SPEC!!! this is to work around LOTS of 'broken' .desktop files that
442          * do not specify %U/%u, %F/F etc. etc. at all. just a command. this is
443          * unlikely to be fixed in distributions etc. in the long run as gnome/kde
444          * seem to have workarounds too so no one notices.
445          */
446         if ((file) && (!file_added))
447         {
448             WRN("Efreet_desktop: %s\n"
449                 "  command: %s\n"
450                 "  has no file path/uri spec info for executing this app WITH a\n"
451                 "  file/uri as a parameter. This is unlikely to be the intent.\n"
452                 "  please check the .desktop file and fix it by adding a %%U or %%F\n"
453                 "  or something appropriate.",
454                 command->desktop->orig_path, command->desktop->exec);
455             if (len >= size - 1)
456             {
457                 char *tmp;
458                 size = len + 1024;
459                 tmp = realloc(exec, size);
460                 if (!tmp) goto error;
461                 exec = tmp;
462             }
463             exec[len++] = ' ';
464             exec = efreet_desktop_command_append_multiple(exec, &size,
465                     &len, command, 'F');
466             if (!exec) goto error;
467             file_added = 1;
468         }
469 #endif
470         exec[len++] = '\0';
471
472        if ((single) || (!execs))
473          {
474             execs = eina_list_append(execs, exec);
475             exec = NULL;
476          }
477
478         /* If no file was added, then the Exec field doesn't contain any file
479          * fields (fFuUdDnN). We only want to run the app once in this case. */
480         if (!file_added) break;
481     }
482     while ((l = eina_list_next(l)));
483
484     return execs;
485 error:
486     IF_FREE(exec);
487     EINA_LIST_FREE(execs, exec)
488         free(exec);
489     return NULL;
490 }
491
492 static void
493 efreet_desktop_command_free(Efreet_Desktop_Command *command)
494 {
495     Efreet_Desktop_Command_File *dcf;
496
497     if (!command) return;
498
499     while (command->files)
500     {
501         dcf = eina_list_data_get(command->files);
502         efreet_desktop_command_file_free(dcf);
503         command->files = eina_list_remove_list(command->files,
504                                                command->files);
505     }
506     FREE(command);
507 }
508
509 static char *
510 efreet_desktop_command_append_quoted(char *dest, int *size, int *len, char *src)
511 {
512     if (!src) return dest;
513     dest = efreet_string_append(dest, size, len, "'");
514     if (!dest) return NULL;
515
516     /* single quotes in src need to be escaped */
517     if (strchr(src, '\''))
518     {
519         char *p;
520         p = src;
521         while (*p)
522         {
523             if (*p == '\'')
524             {
525                 dest = efreet_string_append(dest, size, len, "\'\\\'");
526                 if (!dest) return NULL;
527             }
528
529             dest = efreet_string_append_char(dest, size, len, *p);
530             if (!dest) return NULL;
531             p++;
532         }
533     }
534     else
535     {
536         dest = efreet_string_append(dest, size, len, src);
537         if (!dest) return NULL;
538     }
539
540     dest = efreet_string_append(dest, size, len, "'");
541     if (!dest) return NULL;
542
543     return dest;
544 }
545
546 static char *
547 efreet_desktop_command_append_multiple(char *dest, int *size, int *len,
548                                         Efreet_Desktop_Command *command,
549                                         char type)
550 {
551     Efreet_Desktop_Command_File *file;
552     Eina_List *l;
553     int first = 1;
554
555     if (!command->files) return dest;
556
557     EINA_LIST_FOREACH(command->files, l, file)
558     {
559         if (first)
560             first = 0;
561         else
562         {
563             dest = efreet_string_append_char(dest, size, len, ' ');
564             if (!dest) return NULL;
565         }
566
567         dest = efreet_desktop_command_append_single(dest, size, len,
568                                                     file, tolower(type));
569         if (!dest) return NULL;
570     }
571
572     return dest;
573 }
574
575 static char *
576 efreet_desktop_command_append_single(char *dest, int *size, int *len,
577                                         Efreet_Desktop_Command_File *file,
578                                         char type)
579 {
580     char *str;
581     switch(type)
582     {
583         case 'f':
584             str = file->fullpath;
585             break;
586         case 'u':
587             str = file->uri;
588             break;
589         case 'd':
590             str = file->dir;
591             break;
592         case 'n':
593             str = file->file;
594             break;
595         default:
596             ERR("Invalid type passed to efreet_desktop_command_append_single:"
597                                                                 " '%c'", type);
598             return dest;
599     }
600
601     if (!str) return dest;
602
603     dest = efreet_desktop_command_append_quoted(dest, size, len, str);
604     if (!dest) return NULL;
605
606     return dest;
607 }
608
609 static char *
610 efreet_desktop_command_append_icon(char *dest, int *size, int *len,
611                                             Efreet_Desktop *desktop)
612 {
613     if (!desktop->icon || !desktop->icon[0]) return dest;
614
615     dest = efreet_string_append(dest, size, len, "--icon ");
616     if (!dest) return NULL;
617     dest = efreet_desktop_command_append_quoted(dest, size, len, desktop->icon);
618     if (!dest) return NULL;
619
620     return dest;
621 }
622
623 /**
624  * @param command the Efreet_Desktop_Comand that this file is for
625  * @param file the filname as either an absolute path, relative path, or URI
626  */
627 static Efreet_Desktop_Command_File *
628 efreet_desktop_command_file_process(Efreet_Desktop_Command *command, const char *file)
629 {
630     Efreet_Desktop_Command_File *f;
631     const char *uri, *base;
632     int nonlocal = 0;
633 /*
634     DBG("FLAGS: %d, %d, %d, %d\n",
635         command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH ? 1 : 0,
636         command->flags & EFREET_DESKTOP_EXEC_FLAG_URI ? 1 : 0);
637 */
638     f = NEW(Efreet_Desktop_Command_File, 1);
639     if (!f) return NULL;
640
641     f->command = command;
642
643     /* handle uris */
644     if (!strncmp(file, "http://", 7) || !strncmp(file, "ftp://", 6))
645     {
646         uri = file;
647         base = ecore_file_file_get(file);
648
649         nonlocal = 1;
650     }
651     else if (!strncmp(file, "file:", 5))
652     {
653         file = efreet_desktop_command_file_uri_process(file);
654         if (!file)
655         {
656             efreet_desktop_command_file_free(f);
657             return NULL;
658         }
659     }
660
661     if (nonlocal)
662     {
663         /* process non-local uri */
664         if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
665         {
666             char buf[PATH_MAX];
667
668             snprintf(buf, sizeof(buf), "/tmp/%d-%d-%s", getpid(),
669                             efreet_desktop_command_file_id++, base);
670             f->fullpath = strdup(buf);
671             f->pending = 1;
672
673             ecore_file_download(uri, f->fullpath, efreet_desktop_cb_download_complete,
674                                             efreet_desktop_cb_download_progress, f, NULL);
675         }
676
677         if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
678             f->uri = strdup(uri);
679     }
680     else
681     {
682         char *absol = efreet_desktop_command_path_absolute(file);
683         if (!absol) goto error;
684         /* process local uri/path */
685         if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
686             f->fullpath = strdup(absol);
687
688         if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
689         {
690             const char *buf;
691             Efreet_Uri ef_uri;
692             ef_uri.protocol = "file";
693             ef_uri.hostname = "";
694             ef_uri.path = absol;
695             buf = efreet_uri_encode(&ef_uri);
696
697             f->uri = strdup(buf);
698
699             eina_stringshare_del(buf);
700         }
701
702         free(absol);
703     }
704 #if 0
705     INF("  fullpath: %s", f->fullpath);
706     INF("  uri: %s", f->uri);
707     INF("  dir: %s", f->dir);
708     INF("  file: %s", f->file);
709 #endif
710     return f;
711 error:
712     IF_FREE(f);
713     return NULL;
714 }
715
716 /**
717  * @brief Find the local path portion of a file uri.
718  * @param uri a uri beginning with "file"
719  * @return the location of the path portion of the uri,
720  * or NULL if the file is not on this machine
721  */
722 static const char *
723 efreet_desktop_command_file_uri_process(const char *uri)
724 {
725     const char *path = NULL;
726     int len = strlen(uri);
727
728     /* uri:foo/bar => relative path foo/bar*/
729     if (len >= 4 && uri[5] != '/')
730         path = uri + strlen("file:");
731
732     /* uri:/foo/bar => absolute path /foo/bar */
733     else if (len >= 5 && uri[6] != '/')
734         path = uri + strlen("file:");
735
736     /* uri://foo/bar => absolute path /bar on machine foo */
737     else if (len >= 6 && uri[7] != '/')
738     {
739         char *tmp, *p;
740         char hostname[PATH_MAX];
741         size_t len2;
742
743         len2 = strlen(uri + 7) + 1;
744         tmp = alloca(len2);
745         memcpy(tmp, uri + 7, len2);
746         p = strchr(tmp, '/');
747         if (p)
748         {
749             *p = '\0';
750             if (!strcmp(tmp, "localhost"))
751                 path = uri + strlen("file://localhost");
752             else
753             {
754                 int ret;
755
756                 ret = gethostname(hostname, PATH_MAX);
757                 if ((ret == 0) && !strcmp(tmp, hostname))
758                     path = uri + strlen("file://") + strlen(hostname);
759             }
760         }
761     }
762
763     /* uri:///foo/bar => absolute path /foo/bar on local machine */
764     else if (len >= 7)
765         path = uri + strlen("file://");
766
767     return path;
768 }
769
770 static void
771 efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file)
772 {
773     if (!file) return;
774
775     IF_FREE(file->fullpath);
776     IF_FREE(file->uri);
777     IF_FREE(file->dir);
778     IF_FREE(file->file);
779
780     FREE(file);
781 }
782
783
784 static void
785 efreet_desktop_cb_download_complete(void *data, const char *file __UNUSED__,
786                                                         int status __UNUSED__)
787 {
788     Efreet_Desktop_Command_File *f;
789
790     f = data;
791
792     /* XXX check status... error handling, etc */
793     f->pending = 0;
794     f->command->num_pending--;
795
796     if (f->command->num_pending <= 0)
797     {
798         Eina_List *execs;
799
800         execs = efreet_desktop_command_build(f->command);
801         if (execs)
802         {
803             /* TODO: Need to handle the return value from efreet_desktop_command_execs_process */
804             efreet_desktop_command_execs_process(f->command, execs);
805             eina_list_free(execs);
806         }
807         efreet_desktop_command_free(f->command);
808     }
809 }
810
811 static int
812 efreet_desktop_cb_download_progress(void *data,
813                                     const char *file __UNUSED__,
814                                     long int dltotal, long int dlnow,
815                                     long int ultotal __UNUSED__,
816                                     long int ulnow __UNUSED__)
817 {
818     Efreet_Desktop_Command_File *dcf;
819
820     dcf = data;
821     if (dcf->command->cb_progress)
822         return dcf->command->cb_progress(dcf->command->data,
823                                         dcf->command->desktop,
824                                         dcf->uri, dltotal, dlnow);
825
826     return 0;
827 }
828
829 /**
830  * @brief Build an absolute path from an absolute or relative one.
831  * @param path an absolute or relative path
832  * @return an allocated absolute path (must be freed)
833  */
834 static char *
835 efreet_desktop_command_path_absolute(const char *path)
836 {
837     char *buf;
838     int size = PATH_MAX;
839     int len = 0;
840
841     /* relative url */
842     if (path[0] != '/')
843     {
844         if (!(buf = malloc(size))) return NULL;
845         if (!getcwd(buf, size))
846         {
847             FREE(buf);
848             return NULL;
849         }
850         len = strlen(buf);
851
852         if (buf[len-1] != '/') buf = efreet_string_append(buf, &size, &len, "/");
853         if (!buf) return NULL;
854         buf = efreet_string_append(buf, &size, &len, path);
855         if (!buf) return NULL;
856
857         return buf;
858     }
859
860     /* just dup an already absolute buffer */
861     return strdup(path);
862 }
863
864 /**
865  * Append a string to a buffer, reallocating as necessary.
866  */
867 static char *
868 efreet_string_append(char *dest, int *size, int *len, const char *src)
869 {
870     int l;
871     int off = 0;
872
873     l = eina_strlcpy(dest + *len, src, *size - *len);
874
875     while (l > *size - *len)
876     {
877         char *tmp;
878         /* we successfully appended this much */
879         off += *size - *len - 1;
880         *len = *size - 1;
881         *size += 1024;
882         tmp = realloc(dest, *size);
883         if (!tmp)
884         {
885             free(dest);
886             return NULL;
887         }
888         dest = tmp;
889         *(dest + *len) = '\0';
890
891         l = eina_strlcpy(dest + *len, src + off, *size - *len);
892     }
893     *len += l;
894
895     return dest;
896 }
897
898 static char *
899 efreet_string_append_char(char *dest, int *size, int *len, char c)
900 {
901     if (*len >= *size - 1)
902     {
903         char *tmp;
904         *size += 1024;
905         tmp = realloc(dest, *size);
906         if (!tmp)
907         {
908             free(dest);
909             return NULL;
910         }
911         dest = tmp;
912     }
913
914     dest[(*len)++] = c;
915     dest[*len] = '\0';
916
917     return dest;
918 }
919