9 # define alloca __builtin_alloca
11 # define alloca __alloca
12 #elif defined _MSC_VER
14 # define alloca _alloca
20 void *alloca (size_t);
27 # include <winsock2.h>
31 #include <Ecore_File.h>
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;
38 #include "efreet_private.h"
42 * The different types of commands in an Exec entry
44 typedef enum Efreet_Desktop_Command_Flag
46 EFREET_DESKTOP_EXEC_FLAG_FULLPATH = 0x0001,
47 EFREET_DESKTOP_EXEC_FLAG_URI = 0x0002
48 } Efreet_Desktop_Command_Flag;
52 * Efreet_Desktop_Command
54 typedef struct Efreet_Desktop_Command Efreet_Desktop_Command;
58 * Holds information on a desktop Exec command entry
60 struct Efreet_Desktop_Command
62 Efreet_Desktop *desktop;
65 Efreet_Desktop_Command_Flag flags;
67 Efreet_Desktop_Command_Cb cb_command;
68 Efreet_Desktop_Progress_Cb cb_progress;
71 Eina_List *files; /**< list of Efreet_Desktop_Command_File */
76 * Efreet_Desktop_Command_File
78 typedef struct Efreet_Desktop_Command_File Efreet_Desktop_Command_File;
82 * Stores information on a file passed to the desktop Exec command
84 struct Efreet_Desktop_Command_File
86 Efreet_Desktop_Command *command;
95 static int efreet_desktop_command_file_id = 0;
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);
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,
109 static char *efreet_desktop_command_append_single(char *dest, int *size, int *len,
110 Efreet_Desktop_Command_File *file,
112 static char *efreet_desktop_command_append_icon(char *dest, int *size, int *len,
113 Efreet_Desktop *desktop);
115 static Efreet_Desktop_Command_File *efreet_desktop_command_file_process(
116 Efreet_Desktop_Command *command,
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);
121 static void efreet_desktop_cb_download_complete(void *data, const char *file,
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);
127 static char *efreet_desktop_command_path_absolute(const char *path);
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,
136 efreet_desktop_exec(Efreet_Desktop *desktop, Eina_List *files, void *data)
138 efreet_desktop_command_get(desktop, files, efreet_desktop_exec_cb, data);
142 efreet_desktop_command_get(Efreet_Desktop *desktop, Eina_List *files,
143 Efreet_Desktop_Command_Cb func, void *data)
145 return efreet_desktop_command_progress_get(desktop, files, func, NULL, data);
149 efreet_desktop_command_local_get(Efreet_Desktop *desktop, Eina_List *files)
151 Efreet_Desktop_Command *command;
153 Eina_List *execs, *l;
155 EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
156 EINA_SAFETY_ON_NULL_RETURN_VAL(desktop->exec, NULL);
158 command = NEW(Efreet_Desktop_Command, 1);
159 if (!command) return 0;
161 command->desktop = desktop;
163 command->flags = efreet_desktop_command_flags_get(desktop);
164 /* get the required info for each file passed in */
167 EINA_LIST_FOREACH(files, l, file)
169 Efreet_Desktop_Command_File *dcf;
171 dcf = efreet_desktop_command_file_process(command, file);
175 efreet_desktop_command_file_free(dcf);
178 command->files = eina_list_append(command->files, dcf);
182 execs = efreet_desktop_command_build(command);
183 efreet_desktop_command_free(command);
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,
194 Efreet_Desktop_Command *command;
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);
203 command = NEW(Efreet_Desktop_Command, 1);
204 if (!command) return NULL;
206 command->cb_command = cb_command;
207 command->cb_progress = cb_progress;
208 command->data = data;
209 command->desktop = desktop;
211 command->flags = efreet_desktop_command_flags_get(desktop);
212 /* get the required info for each file passed in */
215 EINA_LIST_FOREACH(files, l, file)
217 Efreet_Desktop_Command_File *dcf;
219 dcf = efreet_desktop_command_file_process(command, file);
221 command->files = eina_list_append(command->files, dcf);
222 command->num_pending += dcf->pending;
226 if (command->num_pending == 0)
230 execs = efreet_desktop_command_build(command);
233 ret = efreet_desktop_command_execs_process(command, execs);
234 eina_list_free(execs);
236 efreet_desktop_command_free(command);
243 efreet_desktop_exec_cb(void *data,
244 Efreet_Desktop *desktop __UNUSED__,
246 int remaining __UNUSED__)
248 ecore_exe_run(exec, data);
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
262 efreet_desktop_command_flags_get(Efreet_Desktop *desktop)
266 /* first, determine which fields are present in the Exec string */
267 p = strchr(desktop->exec, '%');
275 flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
279 flags |= EFREET_DESKTOP_EXEC_FLAG_URI;
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.
296 if (!flags) flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
306 * @brief Call the command callback for each exec in the list
311 efreet_desktop_command_execs_process(Efreet_Desktop_Command *command, Eina_List *execs)
318 num = eina_list_count(execs);
319 EINA_LIST_FOREACH(execs, l, exec)
321 ret = command->cb_command(command->data, command->desktop, exec, --num);
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.
332 * @param command the command to build
333 * @return a list of executable strings
336 efreet_desktop_command_build(Efreet_Desktop_Command *command)
338 Eina_List *execs = NULL;
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 */
353 Efreet_Desktop_Command_File *file = eina_list_data_get(l);
357 if (!exec) goto error;
358 p = command->desktop->exec;
369 tmp = realloc(exec, size);
370 if (!tmp) goto error;
374 /* XXX handle fields inside quotes? */
386 exec = efreet_desktop_command_append_single(exec, &size,
388 if (!exec) goto error;
399 exec = efreet_desktop_command_append_multiple(exec, &size,
401 fprintf(stderr, "EXE: '%s'\n", exec);
402 if (!exec) goto error;
407 exec = efreet_desktop_command_append_icon(exec, &size, &len,
409 if (!exec) goto error;
412 exec = efreet_desktop_command_append_quoted(exec, &size, &len,
413 command->desktop->name);
414 if (!exec) goto error;
417 exec = efreet_desktop_command_append_quoted(exec, &size, &len,
418 command->desktop->orig_path);
419 if (!exec) goto error;
423 WRN("[Efreet]: Deprecated conversion char: '%c' in file '%s'",
424 *p, command->desktop->orig_path);
431 WRN("[Efreet_desktop]: Unknown conversion character: '%c'", *p);
436 else exec[len++] = *p;
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.
446 if ((file) && (!file_added))
448 WRN("Efreet_desktop: %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);
459 tmp = realloc(exec, size);
460 if (!tmp) goto error;
464 exec = efreet_desktop_command_append_multiple(exec, &size,
466 if (!exec) goto error;
472 if ((single) || (!execs))
474 execs = eina_list_append(execs, exec);
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;
482 while ((l = eina_list_next(l)));
487 EINA_LIST_FREE(execs, exec)
493 efreet_desktop_command_free(Efreet_Desktop_Command *command)
495 Efreet_Desktop_Command_File *dcf;
497 if (!command) return;
499 while (command->files)
501 dcf = eina_list_data_get(command->files);
502 efreet_desktop_command_file_free(dcf);
503 command->files = eina_list_remove_list(command->files,
510 efreet_desktop_command_append_quoted(char *dest, int *size, int *len, char *src)
512 if (!src) return dest;
513 dest = efreet_string_append(dest, size, len, "'");
514 if (!dest) return NULL;
516 /* single quotes in src need to be escaped */
517 if (strchr(src, '\''))
525 dest = efreet_string_append(dest, size, len, "\'\\\'");
526 if (!dest) return NULL;
529 dest = efreet_string_append_char(dest, size, len, *p);
530 if (!dest) return NULL;
536 dest = efreet_string_append(dest, size, len, src);
537 if (!dest) return NULL;
540 dest = efreet_string_append(dest, size, len, "'");
541 if (!dest) return NULL;
547 efreet_desktop_command_append_multiple(char *dest, int *size, int *len,
548 Efreet_Desktop_Command *command,
551 Efreet_Desktop_Command_File *file;
555 if (!command->files) return dest;
557 EINA_LIST_FOREACH(command->files, l, file)
563 dest = efreet_string_append_char(dest, size, len, ' ');
564 if (!dest) return NULL;
567 dest = efreet_desktop_command_append_single(dest, size, len,
568 file, tolower(type));
569 if (!dest) return NULL;
576 efreet_desktop_command_append_single(char *dest, int *size, int *len,
577 Efreet_Desktop_Command_File *file,
584 str = file->fullpath;
596 ERR("Invalid type passed to efreet_desktop_command_append_single:"
601 if (!str) return dest;
603 dest = efreet_desktop_command_append_quoted(dest, size, len, str);
604 if (!dest) return NULL;
610 efreet_desktop_command_append_icon(char *dest, int *size, int *len,
611 Efreet_Desktop *desktop)
613 if (!desktop->icon || !desktop->icon[0]) return dest;
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;
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
627 static Efreet_Desktop_Command_File *
628 efreet_desktop_command_file_process(Efreet_Desktop_Command *command, const char *file)
630 Efreet_Desktop_Command_File *f;
631 const char *uri, *base;
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);
638 f = NEW(Efreet_Desktop_Command_File, 1);
641 f->command = command;
644 if (!strncmp(file, "http://", 7) || !strncmp(file, "ftp://", 6))
647 base = ecore_file_file_get(file);
651 else if (!strncmp(file, "file:", 5))
653 file = efreet_desktop_command_file_uri_process(file);
656 efreet_desktop_command_file_free(f);
663 /* process non-local uri */
664 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
668 snprintf(buf, sizeof(buf), "/tmp/%d-%d-%s", getpid(),
669 efreet_desktop_command_file_id++, base);
670 f->fullpath = strdup(buf);
673 ecore_file_download(uri, f->fullpath, efreet_desktop_cb_download_complete,
674 efreet_desktop_cb_download_progress, f, NULL);
677 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
678 f->uri = strdup(uri);
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);
688 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
692 ef_uri.protocol = "file";
693 ef_uri.hostname = "";
695 buf = efreet_uri_encode(&ef_uri);
697 f->uri = strdup(buf);
699 eina_stringshare_del(buf);
705 INF(" fullpath: %s", f->fullpath);
706 INF(" uri: %s", f->uri);
707 INF(" dir: %s", f->dir);
708 INF(" file: %s", f->file);
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
723 efreet_desktop_command_file_uri_process(const char *uri)
725 const char *path = NULL;
726 int len = strlen(uri);
728 /* uri:foo/bar => relative path foo/bar*/
729 if (len >= 4 && uri[5] != '/')
730 path = uri + strlen("file:");
732 /* uri:/foo/bar => absolute path /foo/bar */
733 else if (len >= 5 && uri[6] != '/')
734 path = uri + strlen("file:");
736 /* uri://foo/bar => absolute path /bar on machine foo */
737 else if (len >= 6 && uri[7] != '/')
740 char hostname[PATH_MAX];
743 len2 = strlen(uri + 7) + 1;
745 memcpy(tmp, uri + 7, len2);
746 p = strchr(tmp, '/');
750 if (!strcmp(tmp, "localhost"))
751 path = uri + strlen("file://localhost");
756 ret = gethostname(hostname, PATH_MAX);
757 if ((ret == 0) && !strcmp(tmp, hostname))
758 path = uri + strlen("file://") + strlen(hostname);
763 /* uri:///foo/bar => absolute path /foo/bar on local machine */
765 path = uri + strlen("file://");
771 efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file)
775 IF_FREE(file->fullpath);
785 efreet_desktop_cb_download_complete(void *data, const char *file __UNUSED__,
786 int status __UNUSED__)
788 Efreet_Desktop_Command_File *f;
792 /* XXX check status... error handling, etc */
794 f->command->num_pending--;
796 if (f->command->num_pending <= 0)
800 execs = efreet_desktop_command_build(f->command);
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);
807 efreet_desktop_command_free(f->command);
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__)
818 Efreet_Desktop_Command_File *dcf;
821 if (dcf->command->cb_progress)
822 return dcf->command->cb_progress(dcf->command->data,
823 dcf->command->desktop,
824 dcf->uri, dltotal, dlnow);
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)
835 efreet_desktop_command_path_absolute(const char *path)
844 if (!(buf = malloc(size))) return NULL;
845 if (!getcwd(buf, size))
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;
860 /* just dup an already absolute buffer */
865 * Append a string to a buffer, reallocating as necessary.
868 efreet_string_append(char *dest, int *size, int *len, const char *src)
873 l = eina_strlcpy(dest + *len, src, *size - *len);
875 while (l > *size - *len)
878 /* we successfully appended this much */
879 off += *size - *len - 1;
882 tmp = realloc(dest, *size);
889 *(dest + *len) = '\0';
891 l = eina_strlcpy(dest + *len, src + off, *size - *len);
899 efreet_string_append_char(char *dest, int *size, int *len, char c)
901 if (*len >= *size - 1)
905 tmp = realloc(dest, *size);