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