update for beta release
[framework/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
15 struct _E_Exec_Launch
16 {
17    E_Zone *zone;
18    const char *launch_method;
19 };
20
21 struct _E_Exec_Search
22 {
23    Efreet_Desktop *desktop;
24    int startup_id;
25    pid_t pid;
26 };
27
28 struct _E_Config_Dialog_Data
29 {
30    Efreet_Desktop *desktop;
31    char *exec;
32
33    Ecore_Exe_Event_Del event;
34    Ecore_Exe_Event_Data *error;
35    Ecore_Exe_Event_Data *read;
36
37    char *label, *exit, *signal;
38 };
39
40 /* local subsystem functions */
41 static E_Exec_Instance *_e_exec_cb_exec(void *data, Efreet_Desktop *desktop, char *exec, int remaining);
42 static Eina_Bool _e_exec_cb_expire_timer(void *data);
43 static Eina_Bool _e_exec_cb_exit(void *data, int type, void *event);
44
45 static Eina_Bool _e_exec_startup_id_pid_find(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *value, void *data);
46
47 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);
48 static void _fill_data(E_Config_Dialog_Data *cfdata);
49 static void *_create_data(E_Config_Dialog *cfd);
50 static void _free_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
51 static Evas_Object *_basic_create_widgets(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
52 static Evas_Object *_advanced_create_widgets(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
53 static Evas_Object *_dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines);
54 static void _dialog_save_cb(void *data, void *data2);
55 static void _e_exec_instance_free(E_Exec_Instance *inst);
56
57 /* local subsystem globals */
58 static Eina_List *e_exec_start_pending = NULL;
59 static Eina_Hash *e_exec_instances = NULL;
60 static int startup_id = 0;
61
62 static Ecore_Event_Handler *_e_exec_exit_handler = NULL;
63 static Ecore_Event_Handler *_e_exec_border_add_handler = NULL;
64
65 /* externally accessible functions */
66 EINTERN int
67 e_exec_init(void)
68 {
69    e_exec_instances = eina_hash_string_superfast_new(NULL);
70
71    _e_exec_exit_handler =
72       ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_exec_cb_exit, NULL);
73 #if 0
74    _e_exec_border_add_handler =
75       ecore_event_handler_add(E_EVENT_BORDER_ADD, _e_exec_cb_event_border_add, NULL);
76 #endif
77    return 1;
78 }
79
80 EINTERN int
81 e_exec_shutdown(void)
82 {
83    char buf[256];
84
85    snprintf(buf, sizeof(buf), "%i", startup_id);
86    e_util_env_set("E_STARTUP_ID", buf);
87
88    if (_e_exec_exit_handler) ecore_event_handler_del(_e_exec_exit_handler);
89    if (_e_exec_border_add_handler) 
90      ecore_event_handler_del(_e_exec_border_add_handler);
91    eina_hash_free(e_exec_instances);
92    eina_list_free(e_exec_start_pending);
93    return 1;
94 }
95
96 EAPI E_Exec_Instance *
97 e_exec(E_Zone *zone, Efreet_Desktop *desktop, const char *exec,
98        Eina_List *files, const char *launch_method)
99 {
100    E_Exec_Launch *launch;
101    E_Exec_Instance *inst = NULL;
102
103    if ((!desktop) && (!exec)) return NULL;
104    launch = E_NEW(E_Exec_Launch, 1);
105    if (!launch) return NULL;
106    if (zone)
107      {
108         launch->zone = zone;
109         e_object_ref(E_OBJECT(launch->zone));
110      }
111    if (launch_method) 
112      launch->launch_method = eina_stringshare_add(launch_method);
113    
114    if (desktop)
115      {
116         if (exec)
117           inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
118         else 
119           inst = efreet_desktop_command_get(desktop, files, 
120                                             (Efreet_Desktop_Command_Cb) _e_exec_cb_exec, launch);
121      }
122    else
123      inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
124    return inst;
125 }
126
127 EAPI Efreet_Desktop *
128 e_exec_startup_id_pid_find(int startup_id, pid_t pid)
129 {
130    E_Exec_Search search;
131
132    search.desktop = NULL;
133    search.startup_id = startup_id;
134    search.pid = pid;
135    eina_hash_foreach(e_exec_instances, _e_exec_startup_id_pid_find, &search);
136    return search.desktop;
137 }
138
139 /* local subsystem functions */
140 static E_Exec_Instance *
141 _e_exec_cb_exec(void *data, Efreet_Desktop *desktop, char *exec, int remaining)
142 {
143    E_Exec_Instance *inst = NULL;
144    E_Exec_Launch *launch;
145    Ecore_Exe *exe;
146    char *penv_display;
147    char buf[PATH_MAX];
148
149    launch = data;
150    if (desktop)
151      {
152         inst = E_NEW(E_Exec_Instance, 1);
153         if (!inst) return NULL;
154      }
155
156    if (startup_id == 0)
157      {
158         const char *p;
159
160         p = getenv("E_STARTUP_ID");
161         if (p) startup_id = atoi(p);
162         e_util_env_set("E_STARTUP_ID", NULL);
163      }
164    if (++startup_id < 1) startup_id = 1;
165    /* save previous env vars we need to save */
166    penv_display = getenv("DISPLAY");
167    if (penv_display) penv_display = strdup(penv_display);
168    if ((penv_display) && (launch->zone))
169      {
170         const char *p1, *p2;
171         char buf2[32];
172         int head;
173
174         head = launch->zone->container->manager->num;
175
176         /* set env vars */
177         p1 = strrchr(penv_display, ':');
178         p2 = strrchr(penv_display, '.');
179         if ((p1) && (p2) && (p2 > p1)) /* "blah:x.y" */
180           {
181              /* yes it could overflow... but who will overflow DISPLAY eh? why? to
182               * "exploit" your own applications running as you?
183               */
184              strcpy(buf, penv_display);
185              buf[p2 - penv_display + 1] = 0;
186              snprintf(buf2, sizeof(buf2), "%i", head);
187              strcat(buf, buf2);
188           }
189         else if (p1) /* "blah:x */
190           {
191              strcpy(buf, penv_display);
192              snprintf(buf2, sizeof(buf2), ".%i", head);
193              strcat(buf, buf2);
194           }
195         else
196           strcpy(buf, penv_display);
197         e_util_env_set("DISPLAY", buf);
198      }
199    snprintf(buf, sizeof(buf), "E_START|%i", startup_id);
200    e_util_env_set("DESKTOP_STARTUP_ID", buf);
201
202    // dont set vsync for clients - maybe inherited from compositore. fixme:
203    // need a way to still inherit from parent env of wm.
204    e_util_env_set("__GL_SYNC_TO_VBLANK", NULL);
205    
206 //// FIXME: seem to be some issues with the pipe and filling up ram - need to
207 //// check. for now disable.   
208 //   exe = ecore_exe_pipe_run(exec,
209 //                          ECORE_EXE_PIPE_AUTO | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR |
210 //                          ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED,
211 //                          inst);
212    if ((desktop) && (desktop->path) && (desktop->path[0]))
213      {
214         if (!getcwd(buf, sizeof(buf)))
215           {
216              E_FREE(inst);
217              e_util_dialog_show(_("Run Error"),
218                                 _("Enlightenment was unable to get current directory"));
219              return NULL;
220           }
221         if (chdir(desktop->path))
222           {
223              E_FREE(inst);
224              e_util_dialog_show(_("Run Error"),
225                                 _("Enlightenment was unable to change to directory:<br>"
226                                   "<br>"
227                                   "%s"),
228                            desktop->path);
229              return NULL;
230           }
231         e_util_library_path_strip();
232         exe = ecore_exe_run(exec, inst);
233         e_util_library_path_restore();
234         if (chdir(buf))
235           {
236              e_util_dialog_show(_("Run Error"),
237                                 _("Enlightenment was unable to restore to directory:<br>"
238                                   "<br>"
239                                   "%s"),
240                            buf);
241              E_FREE(inst);
242              return NULL;
243           }
244      }
245    else
246      {
247         e_util_library_path_strip();
248         exe = ecore_exe_run(exec, inst);
249         e_util_library_path_restore();
250      }
251
252    if (penv_display)
253      {
254         e_util_env_set("DISPLAY", penv_display);
255         free(penv_display);
256      }
257    if (!exe)
258      {
259         E_FREE(inst);
260         e_util_dialog_show(_("Run Error"),
261                            _("Enlightenment was unable to fork a child process:<br>"
262                              "<br>"
263                              "%s"),
264                            exec);
265         return NULL;
266      }
267    /* reset env vars */
268    if (launch->launch_method && !desktop) 
269      e_exehist_add(launch->launch_method, exec);
270    free(exec);
271    /* 20 lines at start and end, 20x100 limit on bytes at each end. */
272 //// FIXME: seem to be some issues with the pipe and filling up ram - need to
273 //// check. for now disable.   
274 //   ecore_exe_auto_limits_set(exe, 2000, 2000, 20, 20);
275    ecore_exe_tag_set(exe, "E/exec");
276
277    if (desktop)
278      {
279         Eina_List *l, *lnew;
280
281         efreet_desktop_ref(desktop);
282         inst->desktop = desktop;
283         inst->key = eina_stringshare_add(desktop->orig_path);
284         inst->exe = exe;
285         inst->startup_id = startup_id;
286         inst->launch_time = ecore_time_get();
287         inst->expire_timer = ecore_timer_add(e_config->exec.expire_timeout, 
288                                              _e_exec_cb_expire_timer, inst);
289         l = eina_hash_find(e_exec_instances, desktop->orig_path);
290         lnew = eina_list_append(l, inst);
291         if (l)
292           eina_hash_modify(e_exec_instances, desktop->orig_path, lnew);
293         else
294           eina_hash_add(e_exec_instances, desktop->orig_path, lnew);
295         e_exec_start_pending = eina_list_append(e_exec_start_pending, desktop);
296
297         e_exehist_add(launch->launch_method, desktop->exec);
298      }
299    else
300      {
301         E_FREE(inst);
302         inst = NULL;
303         ecore_exe_free(exe);
304      }
305
306    if (!remaining)
307      {
308         if (launch->launch_method) eina_stringshare_del(launch->launch_method);
309         if (launch->zone) e_object_unref(E_OBJECT(launch->zone));
310         free(launch);
311      }
312    return inst;
313 }
314
315 static Eina_Bool
316 _e_exec_cb_expire_timer(void *data)
317 {
318    E_Exec_Instance *inst;
319
320    inst = data;
321    e_exec_start_pending = eina_list_remove(e_exec_start_pending, inst->desktop);
322    inst->expire_timer = NULL;
323    return ECORE_CALLBACK_CANCEL;
324 }
325
326 static void
327 _e_exec_instance_free(E_Exec_Instance *inst)
328 {
329    Eina_List *instances;
330    
331    if (inst->key)
332      {
333         instances = eina_hash_find(e_exec_instances, inst->key);
334         if (instances)
335           {
336              instances = eina_list_remove(instances, inst);
337              if (instances)
338                eina_hash_modify(e_exec_instances, inst->key, instances);
339              else
340                eina_hash_del(e_exec_instances, inst->key, NULL);
341           }
342         eina_stringshare_del(inst->key);
343      }
344    e_exec_start_pending = eina_list_remove(e_exec_start_pending, inst->desktop);
345    if (inst->expire_timer) ecore_timer_del(inst->expire_timer);
346    if (inst->desktop) efreet_desktop_free(inst->desktop);
347    free(inst); 
348 }
349
350
351
352 static Eina_Bool
353 _e_exec_cb_instance_finish(void *data)
354 {
355    _e_exec_instance_free(data);
356    return ECORE_CALLBACK_CANCEL;
357 }
358
359
360 static Eina_Bool
361 _e_exec_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event)
362 {
363    Ecore_Exe_Event_Del *ev;
364    E_Exec_Instance *inst;
365
366    ev = event;
367    if (!ev->exe) return ECORE_CALLBACK_PASS_ON;
368 //   if (ecore_exe_tag_get(ev->exe)) printf("  tag %s\n", ecore_exe_tag_get(ev->exe));
369    if (!(ecore_exe_tag_get(ev->exe) &&
370          (!strcmp(ecore_exe_tag_get(ev->exe), "E/exec"))))
371      return ECORE_CALLBACK_PASS_ON;
372    inst = ecore_exe_data_get(ev->exe);
373    if (!inst) return ECORE_CALLBACK_PASS_ON;
374
375    /* /bin/sh uses this if cmd not found */
376    if ((ev->exited) &&
377        ((ev->exit_code == 127) || (ev->exit_code == 255)))
378      {
379         if (e_config->exec.show_run_dialog)
380           {
381              E_Dialog *dia;
382              
383              dia = e_dialog_new(e_container_current_get(e_manager_current_get()),
384                                 "E", "_e_exec_run_error_dialog");
385              if (dia)
386                {
387                   char buf[PATH_MAX];
388
389                   e_dialog_title_set(dia, _("Application run error"));
390                   snprintf(buf, sizeof(buf),
391                            _("Enlightenment was unable to run the application:<br>"
392                              "<br>"
393                              "%s<br>"
394                              "<br>"
395                              "The application failed to start."),
396                            ecore_exe_cmd_get(ev->exe));
397                   e_dialog_text_set(dia, buf);
398                   e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL);
399                   e_dialog_button_focus_num(dia, 1);
400                   e_win_centered_set(dia->win, 1);
401                   e_dialog_show(dia);
402                }
403           }
404      }
405    /* Let's hope that everything returns this properly. */
406    else if (!((ev->exited) && (ev->exit_code == EXIT_SUCCESS))) 
407      {
408         if (e_config->exec.show_exit_dialog)
409           {
410              /* filter out common exits via signals - int/term/quit. not really
411               * worth popping up a dialog for */
412              if (!((ev->signalled) &&
413                    ((ev->exit_signal == SIGINT) ||
414                     (ev->exit_signal == SIGQUIT) ||
415                     (ev->exit_signal == SIGTERM)))
416                  )
417                {
418                   /* Show the error dialog with details from the exe. */
419                   _e_exec_error_dialog(inst->desktop, ecore_exe_cmd_get(ev->exe), ev,
420                                        ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_ERROR),
421                                        ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_READ));
422                }
423           }
424      }
425
426    /* maybe better 1 minute? it might be openoffice */
427    if (ecore_time_get() - inst->launch_time < 2.0)
428      {
429         inst->exe = NULL;
430         if (inst->expire_timer) ecore_timer_del(inst->expire_timer);
431         inst->expire_timer = ecore_timer_add(e_config->exec.expire_timeout, _e_exec_cb_instance_finish, inst);
432      }
433    else
434      _e_exec_instance_free(inst);
435
436    return ECORE_CALLBACK_PASS_ON;
437 }
438
439 static Eina_Bool
440 _e_exec_startup_id_pid_find(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *value, void *data)
441 {
442    E_Exec_Search *search;
443    E_Exec_Instance *inst;
444    Eina_List *l;
445
446    search = data;
447    EINA_LIST_FOREACH(value, l, inst)
448      {
449         if (((search->startup_id > 0) && 
450              (search->startup_id == inst->startup_id)) ||
451             ((inst->exe) && (search->pid > 1) && 
452              (search->pid == ecore_exe_pid_get(inst->exe))))
453           {
454              search->desktop = inst->desktop;
455              return EINA_FALSE;
456           }
457      }
458    return EINA_TRUE;
459 }
460
461 static void    
462 _e_exec_error_dialog(Efreet_Desktop *desktop, const char *exec, Ecore_Exe_Event_Del *event,
463                      Ecore_Exe_Event_Data *error, Ecore_Exe_Event_Data *read)
464 {
465    E_Config_Dialog_View *v;
466    E_Config_Dialog_Data *cfdata;
467    E_Container *con;
468
469    v = E_NEW(E_Config_Dialog_View, 1);
470    if (!v) return;
471    cfdata = E_NEW(E_Config_Dialog_Data, 1);
472    if (!cfdata)
473      {
474         E_FREE(v);
475         return;
476      }
477    cfdata->desktop = desktop;
478    if (cfdata->desktop) efreet_desktop_ref(cfdata->desktop);
479    if (exec) cfdata->exec = strdup(exec);
480    cfdata->error = error;
481    cfdata->read = read;
482    cfdata->event = *event;
483
484    v->create_cfdata = _create_data;
485    v->free_cfdata = _free_data;
486    v->basic.create_widgets = _basic_create_widgets;
487    v->advanced.create_widgets = _advanced_create_widgets;
488
489    con = e_container_current_get(e_manager_current_get());
490    /* Create The Dialog */
491    e_config_dialog_new(con, _("Application Execution Error"), 
492                        "E", "_e_exec_error_exit_dialog",
493                        NULL, 0, v, cfdata);
494 }
495
496 static void
497 _fill_data(E_Config_Dialog_Data *cfdata)
498 {
499    char buf[PATH_MAX];
500
501    if (!cfdata->label)
502      {
503         snprintf(buf, sizeof(buf), _("%s stopped running unexpectedly."), cfdata->desktop->name);
504         cfdata->label = strdup(buf);
505      }
506    if ((cfdata->event.exited) && (!cfdata->exit))
507      {
508         snprintf(buf, sizeof(buf), 
509                  _("An exit code of %i was returned from %s."), 
510                  cfdata->event.exit_code, cfdata->exec);
511         cfdata->exit = strdup(buf);
512      }
513    if ((cfdata->event.signalled) && (!cfdata->signal))
514      {
515         if (cfdata->event.exit_signal == SIGINT)
516           snprintf(buf, sizeof(buf),
517                    _("%s was interrupted by an Interrupt Signal."), 
518                    cfdata->desktop->exec);
519         else if (cfdata->event.exit_signal == SIGQUIT)
520           snprintf(buf, sizeof(buf), _("%s was interrupted by a Quit Signal."),
521                    cfdata->exec);
522         else if (cfdata->event.exit_signal == SIGABRT)
523           snprintf(buf, sizeof(buf),
524                    _("%s was interrupted by an Abort Signal."), cfdata->exec);
525         else if (cfdata->event.exit_signal == SIGFPE)
526           snprintf(buf, sizeof(buf),
527                    _("%s was interrupted by a Floating Point Error."), 
528                    cfdata->exec);
529         else if (cfdata->event.exit_signal == SIGKILL)
530           snprintf(buf, sizeof(buf),
531                    _("%s was interrupted by an Uninterruptable Kill Signal."), 
532                    cfdata->exec);
533         else if (cfdata->event.exit_signal == SIGSEGV)
534           snprintf(buf, sizeof(buf),
535                    _("%s was interrupted by a Segmentation Fault."), 
536                    cfdata->exec);
537         else if (cfdata->event.exit_signal == SIGPIPE)
538           snprintf(buf, sizeof(buf), 
539                    _("%s was interrupted by a Broken Pipe."), cfdata->exec);
540         else if (cfdata->event.exit_signal == SIGTERM)
541           snprintf(buf, sizeof(buf),
542                    _("%s was interrupted by a Termination Signal."), 
543                    cfdata->exec);
544         else if (cfdata->event.exit_signal == SIGBUS)
545           snprintf(buf, sizeof(buf), 
546                    _("%s was interrupted by a Bus Error."), cfdata->exec);
547         else
548           snprintf(buf, sizeof(buf),
549                    _("%s was interrupted by the signal number %i."),
550                    cfdata->exec, cfdata->event.exit_signal);
551         cfdata->signal = strdup(buf);
552         /* FIXME: Add  sigchld_info stuff
553          * cfdata->event.data
554          *    siginfo_t
555          *    {
556          *       int      si_signo;     Signal number
557          *       int      si_errno;     An errno value
558          *       int      si_code;      Signal code
559          *       pid_t    si_pid;       Sending process ID
560          *       uid_t    si_uid;       Real user ID of sending process
561          *       int      si_status;    Exit value or signal
562          *       clock_t  si_utime;     User time consumed
563          *       clock_t  si_stime;     System time consumed
564          *       sigval_t si_value;     Signal value
565          *       int      si_int;       POSIX.1b signal
566          *       void *   si_ptr;       POSIX.1b signal
567          *       void *   si_addr;      Memory location which caused fault
568          *       int      si_band;      Band event
569          *       int      si_fd;        File descriptor
570          *    }
571          */
572      }
573 }
574
575 static void *
576 _create_data(E_Config_Dialog *cfd)
577 {
578    E_Config_Dialog_Data *cfdata;
579
580    cfdata = cfd->data;
581    _fill_data(cfdata);
582    return cfdata;
583 }
584
585 static void
586 _free_data(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
587 {
588    if (cfdata->error) ecore_exe_event_data_free(cfdata->error);
589    if (cfdata->read) ecore_exe_event_data_free(cfdata->read);
590
591    if (cfdata->desktop) efreet_desktop_free(cfdata->desktop);
592
593    E_FREE(cfdata->exec);
594    E_FREE(cfdata->signal);
595    E_FREE(cfdata->exit);
596    E_FREE(cfdata->label);
597    E_FREE(cfdata);
598 }
599
600 static Evas_Object *
601 _dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines)
602 {
603    Evas_Object *obj, *os;
604    char *text;
605    char *trunc_note = _("***The remaining output has been truncated. Save the output to view.***\n");
606    int tlen, max_lines, i;
607
608    os = e_widget_framelist_add(evas, _(title), 0);
609
610    obj = e_widget_textblock_add(evas);
611
612    tlen = 0;
613    for (i = 0; lines[i].line; i++)
614      {
615         tlen += lines[i].size + 1;
616         /* When the program output is extraordinarily long, it can cause
617          * significant delays during text rendering. Limit to a fixed
618          * number of characters. */
619         if (tlen > MAX_OUTPUT_CHARACTERS)
620           {
621              tlen -= lines[i].size + 1;
622              tlen += strlen(trunc_note);
623              break;
624           }
625      }
626    max_lines = i;
627    text = alloca(tlen + 1);
628
629    if (text)
630      {
631         text[0] = 0;
632         for (i = 0; i < max_lines; i++)
633           {
634              strcat(text, lines[i].line);
635              strcat(text, "\n");
636           }
637
638         /* Append the warning about truncated output. */
639         if (lines[max_lines].line) strcat(text, trunc_note);
640
641         e_widget_textblock_plain_set(obj, text);
642      }
643    e_widget_size_min_set(obj, 240, 120);
644
645    e_widget_framelist_object_append(os, obj);
646
647    return os;
648 }
649
650 static Evas_Object *
651 _basic_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
652 {
653    char buf[PATH_MAX];
654    int error_length = 0;
655    Evas_Object *o, *ob, *os;
656
657    _fill_data(cfdata);
658
659    o = e_widget_list_add(evas, 0, 0);
660
661    ob = e_widget_label_add(evas, cfdata->label);
662    e_widget_list_object_append(o, ob, 1, 1, 0.5);
663
664    if (cfdata->error) error_length = cfdata->error->size;
665    if (error_length)
666      {
667         os = _dialog_scrolltext_create(evas, _("Error Logs"), 
668                                        cfdata->error->lines);
669         e_widget_list_object_append(o, os, 1, 1, 0.5);
670      }
671    else
672      {
673         ob = e_widget_label_add(evas, _("There was no error message."));
674         e_widget_list_object_append(o, ob, 1, 1, 0.5);
675      }
676
677    ob = e_widget_button_add(evas, _("Save This Message"), "system-run", 
678                             _dialog_save_cb, NULL, cfdata);
679    e_widget_list_object_append(o, ob, 0, 0, 0.5);
680
681    snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"), 
682             e_user_homedir_get(), cfdata->desktop->name);
683    ob = e_widget_label_add(evas, buf);
684    e_widget_list_object_append(o, ob, 1, 1, 0.5);
685
686    return o;
687 }
688
689 static Evas_Object *
690 _advanced_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
691 {
692    char buf[PATH_MAX];
693    int read_length = 0;
694    int error_length = 0;
695    Evas_Object *o, *of, *ob, *ot;
696
697    _fill_data(cfdata);
698
699    o = e_widget_list_add(evas, 0, 0);
700    ot = e_widget_table_add(evas, 0);
701
702    ob = e_widget_label_add(evas, cfdata->label);
703    e_widget_list_object_append(o, ob, 1, 1, 0.5);
704
705    if (cfdata->exit)
706      {
707         of = e_widget_framelist_add(evas, _("Error Information"), 0);
708         ob = e_widget_label_add(evas, _(cfdata->exit));
709         e_widget_framelist_object_append(of, ob);
710         e_widget_list_object_append(o, of, 1, 1, 0.5);
711      }
712
713    if (cfdata->signal)
714      {
715         of = e_widget_framelist_add(evas, _("Error Signal Information"), 0);
716         ob = e_widget_label_add(evas, _(cfdata->signal));
717         e_widget_framelist_object_append(of, ob);
718         e_widget_list_object_append(o, of, 1, 1, 0.5);
719      }
720
721    if (cfdata->read) read_length = cfdata->read->size;
722
723    if (read_length)
724      {
725         of = _dialog_scrolltext_create(evas, _("Output Data"), 
726                                        cfdata->read->lines);
727         /* FIXME: Add stdout "start". */
728         /* FIXME: Add stdout "end". */
729      }
730    else
731      {
732         of = e_widget_framelist_add(evas, _("Output Data"), 0);
733         ob = e_widget_label_add(evas, _("There was no output."));
734         e_widget_framelist_object_append(of, ob);
735      }
736    e_widget_table_object_append(ot, of, 0, 0, 1, 1, 1, 1, 1, 1);
737
738    if (cfdata->error) error_length = cfdata->error->size;
739    if (error_length)
740      {
741         of = _dialog_scrolltext_create(evas, _("Error Logs"), 
742                                        cfdata->error->lines);
743         /* FIXME: Add stderr "start". */
744         /* FIXME: Add stderr "end". */
745      }
746    else
747      {
748         of = e_widget_framelist_add(evas, _("Error Logs"), 0);
749         ob = e_widget_label_add(evas, _("There was no error message."));
750         e_widget_framelist_object_append(of, ob);
751      }
752    e_widget_table_object_append(ot, of, 1, 0, 1, 1, 1, 1, 1, 1);
753
754    e_widget_list_object_append(o, ot, 1, 1, 0.5);
755
756    ob = e_widget_button_add(evas, _("Save This Message"), "system-run", 
757                             _dialog_save_cb, NULL, cfdata);
758    e_widget_list_object_append(o, ob, 0, 0, 0.5);
759
760    snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"), 
761             e_user_homedir_get(), cfdata->desktop->name);
762    ob = e_widget_label_add(evas, buf);
763    e_widget_list_object_append(o, ob, 1, 1, 0.5);
764
765    return o;
766 }
767
768 static void
769 _dialog_save_cb(void *data __UNUSED__, void *data2)
770 {
771    E_Config_Dialog_Data *cfdata;
772    FILE *f;
773    char *text;
774    char buf[1024];
775    char buffer[4096];
776    int read_length = 0;
777    int i, tlen;
778
779    cfdata = data2;
780
781    snprintf(buf, sizeof(buf), "%s/%s.log", e_user_homedir_get(),
782             e_util_filename_escape(cfdata->desktop->name));
783    f = fopen(buf, "w");
784    if (!f) return;
785
786    if (cfdata->exit)
787      {
788         snprintf(buffer, sizeof(buffer), "Error Information:\n\t%s\n\n", 
789                  cfdata->exit);
790         fwrite(buffer, sizeof(char), strlen(buffer), f);
791      }
792    if (cfdata->signal)
793      {
794         snprintf(buffer, sizeof(buffer), "Error Signal Information:\n\t%s\n\n", 
795                  cfdata->signal);
796         fwrite(buffer, sizeof(char), strlen(buffer), f);
797      }
798
799    if (cfdata->read) read_length = cfdata->read->size;
800
801    if (read_length)
802      {
803         tlen = 0;
804         for (i = 0; cfdata->read->lines[i].line; i++)
805           tlen += cfdata->read->lines[i].size + 2;
806         text = alloca(tlen + 1);
807         if (text)
808           {
809              text[0] = 0;
810              for (i = 0; cfdata->read->lines[i].line; i++)
811                {
812                   strcat(text, "\t");
813                   strcat(text, cfdata->read->lines[i].line);
814                   strcat(text, "\n");
815                }
816              snprintf(buffer, sizeof(buffer), "Output Data:\n%s\n\n", text);
817              fwrite(buffer, sizeof(char), strlen(buffer), f);
818           }
819      }
820    else
821      {
822         snprintf(buffer, sizeof(buffer), "Output Data:\n\tThere was no output\n\n");
823         fwrite(buffer, sizeof(char), strlen(buffer), f);
824      }
825
826    /* Reusing this var */
827    read_length = 0;
828    if (cfdata->error) read_length = cfdata->error->size;
829
830    if (read_length)
831      {
832         tlen = 0;
833         for (i = 0; cfdata->error->lines[i].line; i++)
834           tlen += cfdata->error->lines[i].size + 1;
835         text = alloca(tlen + 1);
836         if (text)
837           {
838              text[0] = 0;
839              for (i = 0; cfdata->error->lines[i].line; i++)
840                {
841                   strcat(text, "\t");
842                   strcat(text, cfdata->error->lines[i].line);
843                   strcat(text, "\n");
844                }
845              snprintf(buffer, sizeof(buffer), "Error Logs:\n%s\n", text);
846              fwrite(buffer, sizeof(char), strlen(buffer), f);
847           }
848      }
849    else
850      {
851         snprintf(buffer, sizeof(buffer), "Error Logs:\n\tThere was no error message\n");
852         fwrite(buffer, sizeof(char), strlen(buffer), f);
853      }
854
855    fclose(f);
856 }