the repository of RSA merge with private repository.
[platform/core/uifw/e17.git] / src / bin / e_exec.c
1 #include "e.h"
2
3 #define MAX_OUTPUT_CHARACTERS 5000
4
5 /* TODO:
6  * - Clear e_exec_instances on shutdown
7  * - Clear e_exec_start_pending on shutdown
8  * - Create border add handler
9  * - Launch .desktop in terminal if .desktop requires it
10  */
11
12 typedef struct _E_Exec_Launch E_Exec_Launch;
13 typedef struct _E_Exec_Search E_Exec_Search;
14 typedef struct _E_Exec_Watch E_Exec_Watch;
15
16 struct _E_Exec_Launch
17 {
18    E_Zone     *zone;
19    const char *launch_method;
20 };
21
22 struct _E_Exec_Search
23 {
24    E_Exec_Instance *inst;
25    Efreet_Desktop  *desktop;
26    int              startup_id;
27    pid_t            pid;
28 };
29
30 struct _E_Exec_Watch
31 {
32    void (*func) (void *data, E_Exec_Instance *inst, E_Exec_Watch_Type type);
33    const void *data;
34    Eina_Bool delete_me : 1;
35 };
36
37 struct _E_Config_Dialog_Data
38 {
39    Efreet_Desktop       *desktop;
40    char                 *exec;
41
42    Ecore_Exe_Event_Del   event;
43    Ecore_Exe_Event_Data *error;
44    Ecore_Exe_Event_Data *read;
45
46    char                 *label, *exit, *signal;
47 };
48
49 /* local subsystem functions */
50 static E_Exec_Instance *_e_exec_cb_exec(void *data, Efreet_Desktop *desktop, char *exec, int remaining);
51 static Eina_Bool        _e_exec_cb_expire_timer(void *data);
52 static Eina_Bool        _e_exec_cb_exit(void *data, int type, void *event);
53
54 static Eina_Bool        _e_exec_startup_id_pid_find(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *value, void *data);
55
56 static void             _e_exec_error_dialog(Efreet_Desktop *desktop, const char *exec, Ecore_Exe_Event_Del *event, Ecore_Exe_Event_Data *error, Ecore_Exe_Event_Data *read);
57 static void             _fill_data(E_Config_Dialog_Data *cfdata);
58 static void            *_create_data(E_Config_Dialog *cfd);
59 static void             _free_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
60 static Evas_Object     *_basic_create_widgets(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
61 static Evas_Object     *_advanced_create_widgets(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
62 static Evas_Object     *_dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines);
63 static void             _dialog_save_cb(void *data, void *data2);
64 static void             _e_exec_instance_free(E_Exec_Instance *inst);
65
66 /* local subsystem globals */
67 static Eina_List *e_exec_start_pending = NULL;
68 static Eina_Hash *e_exec_instances = NULL;
69 static int startup_id = 0;
70
71 static Ecore_Event_Handler *_e_exec_exit_handler = NULL;
72 static Ecore_Event_Handler *_e_exec_border_add_handler = NULL;
73
74 static E_Exec_Instance *(*_e_exec_executor_func) (void *data, E_Zone *zone, Efreet_Desktop *desktop, const char *exec, Eina_List *files, const char *launch_method) = NULL;
75 static void *_e_exec_executor_data = NULL;
76
77 /* externally accessible functions */
78 EINTERN int
79 e_exec_init(void)
80 {
81    e_exec_instances = eina_hash_string_superfast_new(NULL);
82
83    _e_exec_exit_handler =
84      ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_exec_cb_exit, NULL);
85 #if 0
86    _e_exec_border_add_handler =
87      ecore_event_handler_add(E_EVENT_BORDER_ADD, _e_exec_cb_event_border_add, NULL);
88 #endif
89    return 1;
90 }
91
92 EINTERN int
93 e_exec_shutdown(void)
94 {
95    char buf[256];
96
97    snprintf(buf, sizeof(buf), "%i", startup_id);
98    e_util_env_set("E_STARTUP_ID", buf);
99
100    if (_e_exec_exit_handler) ecore_event_handler_del(_e_exec_exit_handler);
101    if (_e_exec_border_add_handler)
102      ecore_event_handler_del(_e_exec_border_add_handler);
103    eina_hash_free(e_exec_instances);
104    eina_list_free(e_exec_start_pending);
105    return 1;
106 }
107
108 EAPI void
109 e_exec_executor_set(E_Exec_Instance *(*func) (void *data, E_Zone *zone, Efreet_Desktop *desktop, const char *exec, Eina_List *files, const char *launch_method), const void *data)
110 {
111    _e_exec_executor_func = func;
112    _e_exec_executor_data = (void *)data;
113 }
114
115 EAPI E_Exec_Instance *
116 e_exec(E_Zone *zone, Efreet_Desktop *desktop, const char *exec,
117        Eina_List *files, const char *launch_method)
118 {
119    E_Exec_Launch *launch;
120    E_Exec_Instance *inst = NULL;
121
122    if ((!desktop) && (!exec)) return NULL;
123    
124    if (_e_exec_executor_func)
125      return _e_exec_executor_func(_e_exec_executor_data, zone,
126                                   desktop, exec, files, launch_method);
127
128    if (desktop)
129      {
130         const char *single;
131         
132         single = eina_hash_find(desktop->x, "X-Enlightenment-Single-Instance");
133         if ((single) ||
134             (e_config->exe_always_single_instance))
135           {
136              Eina_Bool dosingle = EINA_FALSE;
137              
138              // first take system config for always single instance if set
139              if (e_config->exe_always_single_instance) dosingle = EINA_TRUE;
140              
141              // and now let desktop file override it
142              if (single)
143                {
144                   if ((!strcasecmp(single, "true")) ||
145                       (!strcasecmp(single, "yes"))||
146                       (!strcasecmp(single, "1")))
147                     dosingle = EINA_TRUE;
148                   else if ((!strcasecmp(single, "false")) ||
149                            (!strcasecmp(single, "no"))||
150                            (!strcasecmp(single, "0")))
151                     dosingle = EINA_FALSE;
152                }
153              
154              if (dosingle)
155                {
156                   Eina_List *l;
157                   E_Border *bd;
158                   
159                   EINA_LIST_FOREACH(e_border_client_list(), l, bd)
160                     {
161                        if ((bd) && (bd->desktop == desktop))
162                          {
163                             if (!bd->focused)
164                               e_border_activate(bd, EINA_TRUE);
165                             else
166                               e_border_raise(bd);
167                             return NULL;
168                          }
169                     }
170                }
171           }
172      }
173    
174    launch = E_NEW(E_Exec_Launch, 1);
175    if (!launch) return NULL;
176    if (zone)
177      {
178         launch->zone = zone;
179         e_object_ref(E_OBJECT(launch->zone));
180      }
181    if (launch_method)
182      launch->launch_method = eina_stringshare_add(launch_method);
183
184    if (desktop)
185      {
186         if (exec)
187           inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
188         else
189           inst = efreet_desktop_command_get
190           (desktop, files, (Efreet_Desktop_Command_Cb)_e_exec_cb_exec, launch);
191      }
192    else
193      inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
194    if ((zone) && (inst))
195      {
196         inst->screen = zone->num;
197         inst->desk_x = zone->desk_x_current;
198         inst->desk_y = zone->desk_y_current;
199      }
200    return inst;
201 }
202
203 EAPI E_Exec_Instance *
204 e_exec_startup_id_pid_instance_find(int id, pid_t pid)
205 {
206    E_Exec_Search search;
207
208    search.inst = NULL;
209    search.desktop = NULL;
210    search.startup_id = id;
211    search.pid = pid;
212    eina_hash_foreach(e_exec_instances, _e_exec_startup_id_pid_find, &search);
213    return search.inst;
214 }
215
216 EAPI Efreet_Desktop *
217 e_exec_startup_id_pid_find(int id, pid_t pid)
218 {
219    E_Exec_Instance *inst;
220
221    inst = e_exec_startup_id_pid_instance_find(id, pid);
222    if (!inst) return NULL;
223    return inst->desktop;
224 }
225
226 EAPI E_Exec_Instance *
227 e_exec_startup_desktop_instance_find(Efreet_Desktop *desktop)
228 {
229    E_Exec_Search search;
230    
231    search.inst = NULL;
232    search.desktop = desktop;
233    search.startup_id = 0;
234    search.pid = 0;
235    eina_hash_foreach(e_exec_instances, _e_exec_startup_id_pid_find, &search);
236    return search.inst;
237 }
238
239 static void
240 _e_exe_instance_watchers_call(E_Exec_Instance *inst, E_Exec_Watch_Type type)
241 {
242    E_Exec_Watch *iw;
243    Eina_List *l, *ln;
244    
245    inst->walking++;
246    EINA_LIST_FOREACH(inst->watchers, l, iw)
247      {
248         if (iw->func) iw->func((void *)(iw->data), inst, type);
249      }
250    inst->walking--;
251    if (inst->walking == 0)
252      {
253         EINA_LIST_FOREACH_SAFE(inst->watchers, l, ln, iw)
254           {
255              if (iw->delete_me)
256                {
257                   inst->watchers = eina_list_remove_list(inst->watchers, l);
258                   free(iw);
259                }
260           }
261      }
262 }
263
264 EAPI void
265 e_exec_instance_found(E_Exec_Instance *inst)
266 {
267    _e_exe_instance_watchers_call(inst, E_EXEC_WATCH_STARTED);
268 }
269
270 EAPI void
271 e_exec_instance_watcher_add(E_Exec_Instance *inst, void (*func) (void *data, E_Exec_Instance *inst, E_Exec_Watch_Type type), const void *data)
272 {
273    E_Exec_Watch *iw;
274    
275    iw = E_NEW(E_Exec_Watch, 1);
276    if (!iw) return;
277    iw->func = func;
278    iw->data = data;
279    inst->watchers = eina_list_append(inst->watchers, iw);
280 }
281
282 EAPI void
283 e_exec_instance_watcher_del(E_Exec_Instance *inst, void (*func) (void *data, E_Exec_Instance *inst, E_Exec_Watch_Type type), const void *data)
284 {
285    E_Exec_Watch *iw;
286    Eina_List *l, *ln;
287    
288    EINA_LIST_FOREACH_SAFE(inst->watchers, l, ln, iw)
289      {
290         if ((iw->func == func) && (iw->data == data))
291           {
292              if (inst->walking == 0)
293                {
294                   inst->watchers = eina_list_remove_list(inst->watchers, l);
295                   free(iw);
296                   return;
297                }
298              else
299                {
300                   iw->delete_me = EINA_TRUE;
301                   return;
302                }
303           }
304      }
305 }
306
307 /* local subsystem functions */
308 static E_Exec_Instance *
309 _e_exec_cb_exec(void *data, Efreet_Desktop *desktop, char *exec, int remaining)
310 {
311    E_Exec_Instance *inst = NULL;
312    E_Exec_Launch *launch;
313    Eina_List *l, *lnew;
314    Ecore_Exe *exe = NULL;
315    char *penv_display;
316    char buf[4096];
317
318    launch = data;
319    inst = E_NEW(E_Exec_Instance, 1);
320    if (!inst) return NULL;
321
322    if (startup_id == 0)
323      {
324         const char *p;
325
326         p = getenv("E_STARTUP_ID");
327         if (p) startup_id = atoi(p);
328         e_util_env_set("E_STARTUP_ID", NULL);
329      }
330    if (++startup_id < 1) startup_id = 1;
331    /* save previous env vars we need to save */
332    penv_display = getenv("DISPLAY");
333    if (penv_display) penv_display = strdup(penv_display);
334    if ((penv_display) && (launch->zone))
335      {
336         const char *p1, *p2;
337         char buf2[32];
338         char *buf3 = NULL;
339         int head;
340         int head_length;
341         int penv_display_length;
342
343         head = launch->zone->container->manager->num;
344
345         penv_display_length = strlen(penv_display);
346         /* Check for insane length for DISPLAY env */
347         if (penv_display_length + 32 > 4096)
348           {
349              E_FREE(inst);
350              return NULL;
351           }
352
353         /* buf2 = '.%i' */
354         *buf2 = '.';
355         head_length = eina_convert_itoa(head, buf2 + 1) + 2;
356
357         /* set env vars */
358         p1 = strrchr(penv_display, ':');
359         p2 = strrchr(penv_display, '.');
360         if ((p1) && (p2) && (p2 > p1)) /* "blah:x.y" */
361           {
362              buf3 = alloca((p2 - penv_display) + head_length + 1);
363
364              memcpy(buf3, penv_display, p2 - penv_display);
365              memcpy(buf3 + (p2 - penv_display), buf2, head_length);
366           }
367         else if (p1) /* "blah:x */
368           {
369              buf3 = alloca(penv_display_length + head_length);
370
371              memcpy(buf3, penv_display, penv_display_length);
372              memcpy(buf3 + penv_display_length, buf2, head_length);
373           }
374         else
375           {
376              buf3 = alloca(penv_display_length + 1);
377              memcpy(buf3, penv_display, penv_display_length + 1);
378           }
379
380         e_util_env_set("DISPLAY", buf3);
381      }
382    snprintf(buf, sizeof(buf), "E_START|%i", startup_id);
383    e_util_env_set("DESKTOP_STARTUP_ID", buf);
384
385    // dont set vsync for clients - maybe inherited from compositore. fixme:
386    // need a way to still inherit from parent env of wm.
387    e_util_env_set("__GL_SYNC_TO_VBLANK", NULL);
388
389 //// FIXME: seem to be some issues with the pipe and filling up ram - need to
390 //// check. for now disable.
391 //   exe = ecore_exe_pipe_run(exec,
392 //                          ECORE_EXE_PIPE_AUTO | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR |
393 //                          ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED,
394 //                          inst);
395    if ((desktop) && (desktop->path) && (desktop->path[0]))
396      {
397         if (!getcwd(buf, sizeof(buf)))
398           {
399              E_FREE(inst);
400              e_util_dialog_show
401                (_("Run Error"),
402                    _("Enlightenment was unable to get current directory"));
403              return NULL;
404           }
405         if (chdir(desktop->path))
406           {
407              E_FREE(inst);
408              e_util_dialog_show
409                (_("Run Error"),
410                    _("Enlightenment was unable to change to directory:<br>"
411                      "<br>"
412                      "%s"),
413                    desktop->path);
414              return NULL;
415           }
416         e_util_library_path_strip();
417         exe = ecore_exe_run(exec, inst);
418         e_util_library_path_restore();
419         if (chdir(buf))
420           {
421              e_util_dialog_show
422                (_("Run Error"),
423                    _("Enlightenment was unable to restore to directory:<br>"
424                      "<br>"
425                      "%s"),
426                    buf);
427              E_FREE(inst);
428              return NULL;
429           }
430      }
431    else
432      {
433         e_util_library_path_strip();
434         if ((desktop) && (desktop->terminal))
435           {
436              Efreet_Desktop *tdesktop;
437              
438              tdesktop = e_util_terminal_desktop_get();
439              if (tdesktop)
440                {
441                   if (tdesktop->exec)
442                     {
443                        Eina_Strbuf *sb;
444
445                        sb = eina_strbuf_new();
446                        if (sb)
447                          {
448                             eina_strbuf_append(sb, tdesktop->exec);
449                             eina_strbuf_append(sb, " -e ");
450                             eina_strbuf_append_escaped(sb, exec);
451                             exe = ecore_exe_run(eina_strbuf_string_get(sb),
452                                                 inst);
453                             eina_strbuf_free(sb);
454                          }
455                     }
456                   else
457                     exe = ecore_exe_run(exec, inst);
458                   efreet_desktop_free(tdesktop);
459                }
460              else
461                exe = ecore_exe_run(exec, inst);
462           }
463         else
464           exe = ecore_exe_run(exec, inst);
465         e_util_library_path_restore();
466      }
467
468    if (penv_display)
469      {
470         e_util_env_set("DISPLAY", penv_display);
471         free(penv_display);
472      }
473    if (!exe)
474      {
475         E_FREE(inst);
476         e_util_dialog_show(_("Run Error"),
477                            _("Enlightenment was unable to fork a child process:<br>"
478                              "<br>"
479                              "%s"),
480                            exec);
481         return NULL;
482      }
483    /* reset env vars */
484    if ((launch->launch_method) && (!desktop))
485      e_exehist_add(launch->launch_method, exec);
486    /* 20 lines at start and end, 20x100 limit on bytes at each end. */
487 //// FIXME: seem to be some issues with the pipe and filling up ram - need to
488 //// check. for now disable.
489 //   ecore_exe_auto_limits_set(exe, 2000, 2000, 20, 20);
490    ecore_exe_tag_set(exe, "E/exec");
491
492    if (desktop)
493      {
494         efreet_desktop_ref(desktop);
495         inst->desktop = desktop;
496         inst->key = eina_stringshare_add(desktop->orig_path);
497      }
498    else
499      inst->key = eina_stringshare_add(exec);
500    free(exec);
501    inst->exe = exe;
502    inst->startup_id = startup_id;
503    inst->launch_time = ecore_time_get();
504    inst->expire_timer = ecore_timer_add(e_config->exec.expire_timeout,
505                                         _e_exec_cb_expire_timer, inst);
506    l = eina_hash_find(e_exec_instances, inst->key);
507    lnew = eina_list_append(l, inst);
508    if (l) eina_hash_modify(e_exec_instances, inst->key, lnew);
509    else eina_hash_add(e_exec_instances, inst->key, lnew);
510    if (inst->desktop)
511      {
512         e_exec_start_pending = eina_list_append(e_exec_start_pending,
513                                                 inst->desktop);
514         e_exehist_add(launch->launch_method, inst->desktop->exec);
515      }
516
517    if (!remaining)
518      {
519         if (launch->launch_method) eina_stringshare_del(launch->launch_method);
520         if (launch->zone) e_object_unref(E_OBJECT(launch->zone));
521         free(launch);
522      }
523    return inst;
524 }
525
526 static Eina_Bool
527 _e_exec_cb_expire_timer(void *data)
528 {
529    E_Exec_Instance *inst;
530
531    inst = data;
532    if (inst->desktop)
533      e_exec_start_pending = eina_list_remove(e_exec_start_pending,
534                                              inst->desktop);
535    inst->expire_timer = NULL;
536    _e_exe_instance_watchers_call(inst, E_EXEC_WATCH_TIMEOUT);
537    return ECORE_CALLBACK_CANCEL;
538 }
539
540 static void
541 _e_exec_instance_free(E_Exec_Instance *inst)
542 {
543    Eina_List *instances;
544    E_Exec_Watch *iw;
545    
546    _e_exe_instance_watchers_call(inst, E_EXEC_WATCH_STOPPED);
547    EINA_LIST_FREE(inst->watchers, iw) free(iw);
548    
549    if (inst->key)
550      {
551         instances = eina_hash_find(e_exec_instances, inst->key);
552         if (instances)
553           {
554              instances = eina_list_remove(instances, inst);
555              if (instances)
556                eina_hash_modify(e_exec_instances, inst->key, instances);
557              else
558                eina_hash_del(e_exec_instances, inst->key, NULL);
559           }
560         eina_stringshare_del(inst->key);
561      }
562    if (inst->desktop)
563      e_exec_start_pending = eina_list_remove(e_exec_start_pending,
564                                              inst->desktop);
565    if (inst->expire_timer) ecore_timer_del(inst->expire_timer);
566    if (inst->desktop) efreet_desktop_free(inst->desktop);
567    free(inst);
568 }
569 /*
570 static Eina_Bool
571 _e_exec_cb_instance_finish(void *data)
572 {
573    _e_exec_instance_free(data);
574    return ECORE_CALLBACK_CANCEL;
575 }
576 */
577
578 static Eina_Bool
579 _e_exec_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event)
580 {
581    Ecore_Exe_Event_Del *ev;
582    E_Exec_Instance *inst;
583
584    ev = event;
585    if (!ev->exe) return ECORE_CALLBACK_PASS_ON;
586 //   if (ecore_exe_tag_get(ev->exe)) printf("  tag %s\n", ecore_exe_tag_get(ev->exe));
587    if (!(ecore_exe_tag_get(ev->exe) &&
588          (!strcmp(ecore_exe_tag_get(ev->exe), "E/exec"))))
589      return ECORE_CALLBACK_PASS_ON;
590    inst = ecore_exe_data_get(ev->exe);
591    if (!inst) return ECORE_CALLBACK_PASS_ON;
592
593    /* /bin/sh uses this if cmd not found */
594    if ((ev->exited) &&
595        ((ev->exit_code == 127) || (ev->exit_code == 255)))
596      {
597         if (e_config->exec.show_run_dialog)
598           {
599              E_Dialog *dia;
600
601              dia = e_dialog_new(e_container_current_get(e_manager_current_get()),
602                                 "E", "_e_exec_run_error_dialog");
603              if (dia)
604                {
605                   char buf[4096];
606
607                   e_dialog_title_set(dia, _("Application run error"));
608                   snprintf(buf, sizeof(buf),
609                            _("Enlightenment was unable to run the application:<br>"
610                              "<br>"
611                              "%s<br>"
612                              "<br>"
613                              "The application failed to start."),
614                            ecore_exe_cmd_get(ev->exe));
615                   e_dialog_text_set(dia, buf);
616                   e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL);
617                   e_dialog_button_focus_num(dia, 1);
618                   e_win_centered_set(dia->win, 1);
619                   e_dialog_show(dia);
620                }
621           }
622      }
623    /* Let's hope that everything returns this properly. */
624    else if (!((ev->exited) && (ev->exit_code == EXIT_SUCCESS)))
625      {
626         if (e_config->exec.show_exit_dialog)
627           {
628              /* filter out common exits via signals - int/term/quit. not really
629               * worth popping up a dialog for */
630              if (!((ev->signalled) &&
631                    ((ev->exit_signal == SIGINT) ||
632                     (ev->exit_signal == SIGQUIT) ||
633                     (ev->exit_signal == SIGTERM)))
634                  )
635                {
636                   /* Show the error dialog with details from the exe. */
637                   _e_exec_error_dialog(inst->desktop, ecore_exe_cmd_get(ev->exe), ev,
638                                        ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_ERROR),
639                                        ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_READ));
640                }
641           }
642      }
643
644 /* scripts that fork off children with & break child tracking... but this hack
645  * also breaks apps that handle single-instance themselves */
646 /*   
647    if ((ecore_time_get() - inst->launch_time) < 2.0)
648      {
649         inst->exe = NULL;
650         if (inst->expire_timer) ecore_timer_del(inst->expire_timer);
651         inst->expire_timer = ecore_timer_add(e_config->exec.expire_timeout, _e_exec_cb_instance_finish, inst);
652      }
653    else
654  */
655      _e_exec_instance_free(inst);
656
657    return ECORE_CALLBACK_PASS_ON;
658 }
659
660 static Eina_Bool
661 _e_exec_startup_id_pid_find(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *value, void *data)
662 {
663    E_Exec_Search *search;
664    E_Exec_Instance *inst;
665    Eina_List *l;
666
667    search = data;
668    EINA_LIST_FOREACH(value, l, inst)
669      {
670         if (((search->desktop) &&
671              (search->desktop == inst->desktop)) ||
672             
673             ((search->startup_id > 0) &&
674              (search->startup_id == inst->startup_id)) ||
675             
676             ((inst->exe) && (search->pid > 1) &&
677              (search->pid == ecore_exe_pid_get(inst->exe))))
678           {
679              search->inst = inst;
680              return EINA_FALSE;
681           }
682      }
683    return EINA_TRUE;
684 }
685
686 static void
687 _e_exec_error_dialog(Efreet_Desktop *desktop, const char *exec, Ecore_Exe_Event_Del *exe_event,
688                      Ecore_Exe_Event_Data *exe_error, Ecore_Exe_Event_Data *exe_read)
689 {
690    E_Config_Dialog_View *v;
691    E_Config_Dialog_Data *cfdata;
692    E_Container *con;
693
694    v = E_NEW(E_Config_Dialog_View, 1);
695    if (!v) return;
696    cfdata = E_NEW(E_Config_Dialog_Data, 1);
697    if (!cfdata)
698      {
699         E_FREE(v);
700         return;
701      }
702    cfdata->desktop = desktop;
703    if (cfdata->desktop) efreet_desktop_ref(cfdata->desktop);
704    if (exec) cfdata->exec = strdup(exec);
705    cfdata->error = exe_error;
706    cfdata->read = exe_read;
707    cfdata->event = *exe_event;
708
709    v->create_cfdata = _create_data;
710    v->free_cfdata = _free_data;
711    v->basic.create_widgets = _basic_create_widgets;
712    v->advanced.create_widgets = _advanced_create_widgets;
713
714    con = e_container_current_get(e_manager_current_get());
715    /* Create The Dialog */
716    e_config_dialog_new(con, _("Application Execution Error"),
717                        "E", "_e_exec_error_exit_dialog",
718                        NULL, 0, v, cfdata);
719 }
720
721 static void
722 _fill_data(E_Config_Dialog_Data *cfdata)
723 {
724    char buf[4096];
725
726    if (!cfdata->label)
727      {
728         if (cfdata->desktop)
729           snprintf(buf, sizeof(buf), _("%s stopped running unexpectedly."), cfdata->desktop->name);
730         else
731           snprintf(buf, sizeof(buf), _("%s stopped running unexpectedly."), cfdata->exec);
732         cfdata->label = strdup(buf);
733      }
734    if ((cfdata->event.exited) && (!cfdata->exit))
735      {
736         snprintf(buf, sizeof(buf),
737                  _("An exit code of %i was returned from %s."),
738                  cfdata->event.exit_code, cfdata->exec);
739         cfdata->exit = strdup(buf);
740      }
741    if ((cfdata->event.signalled) && (!cfdata->signal))
742      {
743         if (cfdata->event.exit_signal == SIGINT)
744           snprintf(buf, sizeof(buf),
745                    _("%s was interrupted by an Interrupt Signal."),
746                    cfdata->exec);
747         else if (cfdata->event.exit_signal == SIGQUIT)
748           snprintf(buf, sizeof(buf), _("%s was interrupted by a Quit Signal."),
749                    cfdata->exec);
750         else if (cfdata->event.exit_signal == SIGABRT)
751           snprintf(buf, sizeof(buf),
752                    _("%s was interrupted by an Abort Signal."), cfdata->exec);
753         else if (cfdata->event.exit_signal == SIGFPE)
754           snprintf(buf, sizeof(buf),
755                    _("%s was interrupted by a Floating Point Error."),
756                    cfdata->exec);
757         else if (cfdata->event.exit_signal == SIGKILL)
758           snprintf(buf, sizeof(buf),
759                    _("%s was interrupted by an Uninterruptable Kill Signal."),
760                    cfdata->exec);
761         else if (cfdata->event.exit_signal == SIGSEGV)
762           snprintf(buf, sizeof(buf),
763                    _("%s was interrupted by a Segmentation Fault."),
764                    cfdata->exec);
765         else if (cfdata->event.exit_signal == SIGPIPE)
766           snprintf(buf, sizeof(buf),
767                    _("%s was interrupted by a Broken Pipe."), cfdata->exec);
768         else if (cfdata->event.exit_signal == SIGTERM)
769           snprintf(buf, sizeof(buf),
770                    _("%s was interrupted by a Termination Signal."),
771                    cfdata->exec);
772         else if (cfdata->event.exit_signal == SIGBUS)
773           snprintf(buf, sizeof(buf),
774                    _("%s was interrupted by a Bus Error."), cfdata->exec);
775         else
776           snprintf(buf, sizeof(buf),
777                    _("%s was interrupted by the signal number %i."),
778                    cfdata->exec, cfdata->event.exit_signal);
779         cfdata->signal = strdup(buf);
780         /* FIXME: Add  sigchld_info stuff
781          * cfdata->event.data
782          *    siginfo_t
783          *    {
784          *       int      si_signo;     Signal number
785          *       int      si_errno;     An errno value
786          *       int      si_code;      Signal code
787          *       pid_t    si_pid;       Sending process ID
788          *       uid_t    si_uid;       Real user ID of sending process
789          *       int      si_status;    Exit value or signal
790          *       clock_t  si_utime;     User time consumed
791          *       clock_t  si_stime;     System time consumed
792          *       sigval_t si_value;     Signal value
793          *       int      si_int;       POSIX.1b signal
794          *       void *   si_ptr;       POSIX.1b signal
795          *       void *   si_addr;      Memory location which caused fault
796          *       int      si_band;      Band event
797          *       int      si_fd;        File descriptor
798          *    }
799          */
800      }
801 }
802
803 static void *
804 _create_data(E_Config_Dialog *cfd)
805 {
806    E_Config_Dialog_Data *cfdata;
807
808    cfdata = cfd->data;
809    _fill_data(cfdata);
810    return cfdata;
811 }
812
813 static void
814 _free_data(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
815 {
816    if (cfdata->error) ecore_exe_event_data_free(cfdata->error);
817    if (cfdata->read) ecore_exe_event_data_free(cfdata->read);
818
819    if (cfdata->desktop) efreet_desktop_free(cfdata->desktop);
820
821    E_FREE(cfdata->exec);
822    E_FREE(cfdata->signal);
823    E_FREE(cfdata->exit);
824    E_FREE(cfdata->label);
825    E_FREE(cfdata);
826 }
827
828 static Evas_Object *
829 _dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines)
830 {
831    Evas_Object *obj, *os;
832    char *text;
833    char *trunc_note = _("***The remaining output has been truncated. Save the output to view.***\n");
834    int tlen, max_lines, i;
835
836    os = e_widget_framelist_add(evas, _(title), 0);
837
838    obj = e_widget_textblock_add(evas);
839
840    tlen = 0;
841    for (i = 0; lines[i].line; i++)
842      {
843         tlen += lines[i].size + 1;
844         /* When the program output is extraordinarily long, it can cause
845          * significant delays during text rendering. Limit to a fixed
846          * number of characters. */
847         if (tlen > MAX_OUTPUT_CHARACTERS)
848           {
849              tlen -= lines[i].size + 1;
850              tlen += strlen(trunc_note);
851              break;
852           }
853      }
854    max_lines = i;
855    text = alloca(tlen + 1);
856
857    text[0] = 0;
858    for (i = 0; i < max_lines; i++)
859      {
860         strcat(text, lines[i].line);
861         strcat(text, "\n");
862      }
863
864    /* Append the warning about truncated output. */
865    if (lines[max_lines].line) strcat(text, trunc_note);
866
867    e_widget_textblock_plain_set(obj, text);
868    e_widget_size_min_set(obj, 240, 120);
869
870    e_widget_framelist_object_append(os, obj);
871
872    return os;
873 }
874
875 static Evas_Object *
876 _basic_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
877 {
878    char buf[4096];
879    int error_length = 0;
880    Evas_Object *o, *ob, *os;
881
882    _fill_data(cfdata);
883
884    o = e_widget_list_add(evas, 0, 0);
885
886    ob = e_widget_label_add(evas, cfdata->label);
887    e_widget_list_object_append(o, ob, 1, 1, 0.5);
888
889    if (cfdata->error) error_length = cfdata->error->size;
890    if (error_length)
891      {
892         os = _dialog_scrolltext_create(evas, _("Error Logs"),
893                                        cfdata->error->lines);
894         e_widget_list_object_append(o, os, 1, 1, 0.5);
895      }
896    else
897      {
898         ob = e_widget_label_add(evas, _("There was no error message."));
899         e_widget_list_object_append(o, ob, 1, 1, 0.5);
900      }
901
902    ob = e_widget_button_add(evas, _("Save This Message"), "system-run",
903                             _dialog_save_cb, NULL, cfdata);
904    e_widget_list_object_append(o, ob, 0, 0, 0.5);
905
906    if (cfdata->desktop)
907      snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
908               e_user_homedir_get(), cfdata->desktop->name);
909    else
910      snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
911               e_user_homedir_get(), "Error");
912    ob = e_widget_label_add(evas, buf);
913    e_widget_list_object_append(o, ob, 1, 1, 0.5);
914
915    return o;
916 }
917
918 static Evas_Object *
919 _advanced_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
920 {
921    char buf[4096];
922    int read_length = 0;
923    int error_length = 0;
924    Evas_Object *o, *of, *ob, *ot;
925
926    _fill_data(cfdata);
927
928    o = e_widget_list_add(evas, 0, 0);
929    ot = e_widget_table_add(evas, 0);
930
931    ob = e_widget_label_add(evas, cfdata->label);
932    e_widget_list_object_append(o, ob, 1, 1, 0.5);
933
934    if (cfdata->exit)
935      {
936         of = e_widget_framelist_add(evas, _("Error Information"), 0);
937         ob = e_widget_label_add(evas, _(cfdata->exit));
938         e_widget_framelist_object_append(of, ob);
939         e_widget_list_object_append(o, of, 1, 1, 0.5);
940      }
941
942    if (cfdata->signal)
943      {
944         of = e_widget_framelist_add(evas, _("Error Signal Information"), 0);
945         ob = e_widget_label_add(evas, _(cfdata->signal));
946         e_widget_framelist_object_append(of, ob);
947         e_widget_list_object_append(o, of, 1, 1, 0.5);
948      }
949
950    if (cfdata->read) read_length = cfdata->read->size;
951
952    if (read_length)
953      {
954         of = _dialog_scrolltext_create(evas, _("Output Data"),
955                                        cfdata->read->lines);
956         /* FIXME: Add stdout "start". */
957         /* FIXME: Add stdout "end". */
958      }
959    else
960      {
961         of = e_widget_framelist_add(evas, _("Output Data"), 0);
962         ob = e_widget_label_add(evas, _("There was no output."));
963         e_widget_framelist_object_append(of, ob);
964      }
965    e_widget_table_object_append(ot, of, 0, 0, 1, 1, 1, 1, 1, 1);
966
967    if (cfdata->error) error_length = cfdata->error->size;
968    if (error_length)
969      {
970         of = _dialog_scrolltext_create(evas, _("Error Logs"),
971                                        cfdata->error->lines);
972         /* FIXME: Add stderr "start". */
973         /* FIXME: Add stderr "end". */
974      }
975    else
976      {
977         of = e_widget_framelist_add(evas, _("Error Logs"), 0);
978         ob = e_widget_label_add(evas, _("There was no error message."));
979         e_widget_framelist_object_append(of, ob);
980      }
981    e_widget_table_object_append(ot, of, 1, 0, 1, 1, 1, 1, 1, 1);
982
983    e_widget_list_object_append(o, ot, 1, 1, 0.5);
984
985    ob = e_widget_button_add(evas, _("Save This Message"), "system-run",
986                             _dialog_save_cb, NULL, cfdata);
987    e_widget_list_object_append(o, ob, 0, 0, 0.5);
988
989    if (cfdata->desktop)
990      snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
991               e_user_homedir_get(), cfdata->desktop->name);
992    else
993      snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
994               e_user_homedir_get(), "Error");
995    ob = e_widget_label_add(evas, buf);
996    e_widget_list_object_append(o, ob, 1, 1, 0.5);
997
998    return o;
999 }
1000
1001 static void
1002 _dialog_save_cb(void *data __UNUSED__, void *data2)
1003 {
1004    E_Config_Dialog_Data *cfdata;
1005    FILE *f;
1006    char *text;
1007    char buf[1024];
1008    char buffer[4096];
1009    int read_length = 0;
1010    int i, tlen;
1011
1012    cfdata = data2;
1013
1014    if (cfdata->desktop)
1015      snprintf(buf, sizeof(buf), "%s/%s.log", e_user_homedir_get(),
1016               e_util_filename_escape(cfdata->desktop->name));
1017    else
1018      snprintf(buf, sizeof(buf), "%s/%s.log", e_user_homedir_get(),
1019               "Error");
1020    f = fopen(buf, "w");
1021    if (!f) return;
1022
1023    if (cfdata->exit)
1024      {
1025         snprintf(buffer, sizeof(buffer), "Error Information:\n\t%s\n\n",
1026                  cfdata->exit);
1027         fwrite(buffer, sizeof(char), strlen(buffer), f);
1028      }
1029    if (cfdata->signal)
1030      {
1031         snprintf(buffer, sizeof(buffer), "Error Signal Information:\n\t%s\n\n",
1032                  cfdata->signal);
1033         fwrite(buffer, sizeof(char), strlen(buffer), f);
1034      }
1035
1036    if (cfdata->read) read_length = cfdata->read->size;
1037
1038    if (read_length)
1039      {
1040         tlen = 0;
1041         for (i = 0; cfdata->read->lines[i].line; i++)
1042           tlen += cfdata->read->lines[i].size + 2;
1043         text = alloca(tlen + 1);
1044         text[0] = 0;
1045         for (i = 0; cfdata->read->lines[i].line; i++)
1046           {
1047              strcat(text, "\t");
1048              strcat(text, cfdata->read->lines[i].line);
1049              strcat(text, "\n");
1050           }
1051         snprintf(buffer, sizeof(buffer), "Output Data:\n%s\n\n", text);
1052         fwrite(buffer, sizeof(char), strlen(buffer), f);
1053      }
1054    else
1055      {
1056         snprintf(buffer, sizeof(buffer), "Output Data:\n\tThere was no output\n\n");
1057         fwrite(buffer, sizeof(char), strlen(buffer), f);
1058      }
1059
1060    /* Reusing this var */
1061    read_length = 0;
1062    if (cfdata->error) read_length = cfdata->error->size;
1063
1064    if (read_length)
1065      {
1066         tlen = 0;
1067         for (i = 0; cfdata->error->lines[i].line; i++)
1068           tlen += cfdata->error->lines[i].size + 1;
1069         text = alloca(tlen + 1);
1070         text[0] = 0;
1071         for (i = 0; cfdata->error->lines[i].line; i++)
1072           {
1073              strcat(text, "\t");
1074              strcat(text, cfdata->error->lines[i].line);
1075              strcat(text, "\n");
1076           }
1077         snprintf(buffer, sizeof(buffer), "Error Logs:\n%s\n", text);
1078         fwrite(buffer, sizeof(char), strlen(buffer), f);
1079      }
1080    else
1081      {
1082         snprintf(buffer, sizeof(buffer), "Error Logs:\n\tThere was no error message\n");
1083         fwrite(buffer, sizeof(char), strlen(buffer), f);
1084      }
1085
1086    fclose(f);
1087 }