a3b9d13ead3721bc530e30f2fe9e940a9073fa4f
[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))
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                            buf);
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         int pid = -1;
450         
451         if (inst->exe)
452            pid = ecore_exe_pid_get(inst->exe);
453         if (((search->startup_id > 0) && 
454              (search->startup_id == inst->startup_id)) ||
455             ((inst->exe) && (search->pid > 1) && 
456              (search->pid == ecore_exe_pid_get(inst->exe))))
457           {
458              search->desktop = inst->desktop;
459              return EINA_FALSE;
460           }
461      }
462    return EINA_TRUE;
463 }
464
465 static void    
466 _e_exec_error_dialog(Efreet_Desktop *desktop, const char *exec, Ecore_Exe_Event_Del *event,
467                      Ecore_Exe_Event_Data *error, Ecore_Exe_Event_Data *read)
468 {
469    E_Config_Dialog_View *v;
470    E_Config_Dialog_Data *cfdata;
471    E_Container *con;
472
473    v = E_NEW(E_Config_Dialog_View, 1);
474    if (!v) return;
475    cfdata = E_NEW(E_Config_Dialog_Data, 1);
476    if (!cfdata)
477      {
478         E_FREE(v);
479         return;
480      }
481    cfdata->desktop = desktop;
482    if (cfdata->desktop) efreet_desktop_ref(cfdata->desktop);
483    if (exec) cfdata->exec = strdup(exec);
484    cfdata->error = error;
485    cfdata->read = read;
486    cfdata->event = *event;
487
488    v->create_cfdata = _create_data;
489    v->free_cfdata = _free_data;
490    v->basic.create_widgets = _basic_create_widgets;
491    v->advanced.create_widgets = _advanced_create_widgets;
492
493    con = e_container_current_get(e_manager_current_get());
494    /* Create The Dialog */
495    e_config_dialog_new(con, _("Application Execution Error"), 
496                        "E", "_e_exec_error_exit_dialog",
497                        NULL, 0, v, cfdata);
498 }
499
500 static void
501 _fill_data(E_Config_Dialog_Data *cfdata)
502 {
503    char buf[PATH_MAX];
504
505    if (!cfdata->label)
506      {
507         snprintf(buf, sizeof(buf), _("%s stopped running unexpectedly."), cfdata->desktop->name);
508         cfdata->label = strdup(buf);
509      }
510    if ((cfdata->event.exited) && (!cfdata->exit))
511      {
512         snprintf(buf, sizeof(buf), 
513                  _("An exit code of %i was returned from %s."), 
514                  cfdata->event.exit_code, cfdata->exec);
515         cfdata->exit = strdup(buf);
516      }
517    if ((cfdata->event.signalled) && (!cfdata->signal))
518      {
519         if (cfdata->event.exit_signal == SIGINT)
520           snprintf(buf, sizeof(buf),
521                    _("%s was interrupted by an Interrupt Signal."), 
522                    cfdata->desktop->exec);
523         else if (cfdata->event.exit_signal == SIGQUIT)
524           snprintf(buf, sizeof(buf), _("%s was interrupted by a Quit Signal."),
525                    cfdata->exec);
526         else if (cfdata->event.exit_signal == SIGABRT)
527           snprintf(buf, sizeof(buf),
528                    _("%s was interrupted by an Abort Signal."), cfdata->exec);
529         else if (cfdata->event.exit_signal == SIGFPE)
530           snprintf(buf, sizeof(buf),
531                    _("%s was interrupted by a Floating Point Error."), 
532                    cfdata->exec);
533         else if (cfdata->event.exit_signal == SIGKILL)
534           snprintf(buf, sizeof(buf),
535                    _("%s was interrupted by an Uninterruptable Kill Signal."), 
536                    cfdata->exec);
537         else if (cfdata->event.exit_signal == SIGSEGV)
538           snprintf(buf, sizeof(buf),
539                    _("%s was interrupted by a Segmentation Fault."), 
540                    cfdata->exec);
541         else if (cfdata->event.exit_signal == SIGPIPE)
542           snprintf(buf, sizeof(buf), 
543                    _("%s was interrupted by a Broken Pipe."), cfdata->exec);
544         else if (cfdata->event.exit_signal == SIGTERM)
545           snprintf(buf, sizeof(buf),
546                    _("%s was interrupted by a Termination Signal."), 
547                    cfdata->exec);
548         else if (cfdata->event.exit_signal == SIGBUS)
549           snprintf(buf, sizeof(buf), 
550                    _("%s was interrupted by a Bus Error."), cfdata->exec);
551         else
552           snprintf(buf, sizeof(buf),
553                    _("%s was interrupted by the signal number %i."),
554                    cfdata->exec, cfdata->event.exit_signal);
555         cfdata->signal = strdup(buf);
556         /* FIXME: Add  sigchld_info stuff
557          * cfdata->event.data
558          *    siginfo_t
559          *    {
560          *       int      si_signo;     Signal number
561          *       int      si_errno;     An errno value
562          *       int      si_code;      Signal code
563          *       pid_t    si_pid;       Sending process ID
564          *       uid_t    si_uid;       Real user ID of sending process
565          *       int      si_status;    Exit value or signal
566          *       clock_t  si_utime;     User time consumed
567          *       clock_t  si_stime;     System time consumed
568          *       sigval_t si_value;     Signal value
569          *       int      si_int;       POSIX.1b signal
570          *       void *   si_ptr;       POSIX.1b signal
571          *       void *   si_addr;      Memory location which caused fault
572          *       int      si_band;      Band event
573          *       int      si_fd;        File descriptor
574          *    }
575          */
576      }
577 }
578
579 static void *
580 _create_data(E_Config_Dialog *cfd)
581 {
582    E_Config_Dialog_Data *cfdata;
583
584    cfdata = cfd->data;
585    _fill_data(cfdata);
586    return cfdata;
587 }
588
589 static void
590 _free_data(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
591 {
592    if (cfdata->error) ecore_exe_event_data_free(cfdata->error);
593    if (cfdata->read) ecore_exe_event_data_free(cfdata->read);
594
595    if (cfdata->desktop) efreet_desktop_free(cfdata->desktop);
596
597    E_FREE(cfdata->exec);
598    E_FREE(cfdata->signal);
599    E_FREE(cfdata->exit);
600    E_FREE(cfdata->label);
601    E_FREE(cfdata);
602 }
603
604 static Evas_Object *
605 _dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines)
606 {
607    Evas_Object *obj, *os;
608    char *text;
609    char *trunc_note = _("***The remaining output has been truncated. Save the output to view.***\n");
610    int tlen, max_lines, i;
611
612    os = e_widget_framelist_add(evas, _(title), 0);
613
614    obj = e_widget_textblock_add(evas);
615
616    tlen = 0;
617    for (i = 0; lines[i].line; i++)
618      {
619         tlen += lines[i].size + 1;
620         /* When the program output is extraordinarily long, it can cause
621          * significant delays during text rendering. Limit to a fixed
622          * number of characters. */
623         if (tlen > MAX_OUTPUT_CHARACTERS)
624           {
625              tlen -= lines[i].size + 1;
626              tlen += strlen(trunc_note);
627              break;
628           }
629      }
630    max_lines = i;
631    text = alloca(tlen + 1);
632
633    if (text)
634      {
635         text[0] = 0;
636         for (i = 0; i < max_lines; i++)
637           {
638              strcat(text, lines[i].line);
639              strcat(text, "\n");
640           }
641
642         /* Append the warning about truncated output. */
643         if (lines[max_lines].line) strcat(text, trunc_note);
644
645         e_widget_textblock_plain_set(obj, text);
646      }
647    e_widget_size_min_set(obj, 240, 120);
648
649    e_widget_framelist_object_append(os, obj);
650
651    return os;
652 }
653
654 static Evas_Object *
655 _basic_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
656 {
657    char buf[PATH_MAX];
658    int error_length = 0;
659    Evas_Object *o, *ob, *os;
660
661    _fill_data(cfdata);
662
663    o = e_widget_list_add(evas, 0, 0);
664
665    ob = e_widget_label_add(evas, cfdata->label);
666    e_widget_list_object_append(o, ob, 1, 1, 0.5);
667
668    if (cfdata->error) error_length = cfdata->error->size;
669    if (error_length)
670      {
671         os = _dialog_scrolltext_create(evas, _("Error Logs"), 
672                                        cfdata->error->lines);
673         e_widget_list_object_append(o, os, 1, 1, 0.5);
674      }
675    else
676      {
677         ob = e_widget_label_add(evas, _("There was no error message."));
678         e_widget_list_object_append(o, ob, 1, 1, 0.5);
679      }
680
681    ob = e_widget_button_add(evas, _("Save This Message"), "system-run", 
682                             _dialog_save_cb, NULL, cfdata);
683    e_widget_list_object_append(o, ob, 0, 0, 0.5);
684
685    snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"), 
686             e_user_homedir_get(), cfdata->desktop->name);
687    ob = e_widget_label_add(evas, buf);
688    e_widget_list_object_append(o, ob, 1, 1, 0.5);
689
690    return o;
691 }
692
693 static Evas_Object *
694 _advanced_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
695 {
696    char buf[PATH_MAX];
697    int read_length = 0;
698    int error_length = 0;
699    Evas_Object *o, *of, *ob, *ot;
700
701    _fill_data(cfdata);
702
703    o = e_widget_list_add(evas, 0, 0);
704    ot = e_widget_table_add(evas, 0);
705
706    ob = e_widget_label_add(evas, cfdata->label);
707    e_widget_list_object_append(o, ob, 1, 1, 0.5);
708
709    if (cfdata->exit)
710      {
711         of = e_widget_framelist_add(evas, _("Error Information"), 0);
712         ob = e_widget_label_add(evas, _(cfdata->exit));
713         e_widget_framelist_object_append(of, ob);
714         e_widget_list_object_append(o, of, 1, 1, 0.5);
715      }
716
717    if (cfdata->signal)
718      {
719         of = e_widget_framelist_add(evas, _("Error Signal Information"), 0);
720         ob = e_widget_label_add(evas, _(cfdata->signal));
721         e_widget_framelist_object_append(of, ob);
722         e_widget_list_object_append(o, of, 1, 1, 0.5);
723      }
724
725    if (cfdata->read) read_length = cfdata->read->size;
726
727    if (read_length)
728      {
729         of = _dialog_scrolltext_create(evas, _("Output Data"), 
730                                        cfdata->read->lines);
731         /* FIXME: Add stdout "start". */
732         /* FIXME: Add stdout "end". */
733      }
734    else
735      {
736         of = e_widget_framelist_add(evas, _("Output Data"), 0);
737         ob = e_widget_label_add(evas, _("There was no output."));
738         e_widget_framelist_object_append(of, ob);
739      }
740    e_widget_table_object_append(ot, of, 0, 0, 1, 1, 1, 1, 1, 1);
741
742    if (cfdata->error) error_length = cfdata->error->size;
743    if (error_length)
744      {
745         of = _dialog_scrolltext_create(evas, _("Error Logs"), 
746                                        cfdata->error->lines);
747         /* FIXME: Add stderr "start". */
748         /* FIXME: Add stderr "end". */
749      }
750    else
751      {
752         of = e_widget_framelist_add(evas, _("Error Logs"), 0);
753         ob = e_widget_label_add(evas, _("There was no error message."));
754         e_widget_framelist_object_append(of, ob);
755      }
756    e_widget_table_object_append(ot, of, 1, 0, 1, 1, 1, 1, 1, 1);
757
758    e_widget_list_object_append(o, ot, 1, 1, 0.5);
759
760    ob = e_widget_button_add(evas, _("Save This Message"), "system-run", 
761                             _dialog_save_cb, NULL, cfdata);
762    e_widget_list_object_append(o, ob, 0, 0, 0.5);
763
764    snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"), 
765             e_user_homedir_get(), cfdata->desktop->name);
766    ob = e_widget_label_add(evas, buf);
767    e_widget_list_object_append(o, ob, 1, 1, 0.5);
768
769    return o;
770 }
771
772 static void
773 _dialog_save_cb(void *data __UNUSED__, void *data2)
774 {
775    E_Config_Dialog_Data *cfdata;
776    FILE *f;
777    char *text;
778    char buf[1024];
779    char buffer[4096];
780    int read_length = 0;
781    int i, tlen;
782
783    cfdata = data2;
784
785    snprintf(buf, sizeof(buf), "%s/%s.log", e_user_homedir_get(),
786             e_util_filename_escape(cfdata->desktop->name));
787    f = fopen(buf, "w");
788    if (!f) return;
789
790    if (cfdata->exit)
791      {
792         snprintf(buffer, sizeof(buffer), "Error Information:\n\t%s\n\n", 
793                  cfdata->exit);
794         fwrite(buffer, sizeof(char), strlen(buffer), f);
795      }
796    if (cfdata->signal)
797      {
798         snprintf(buffer, sizeof(buffer), "Error Signal Information:\n\t%s\n\n", 
799                  cfdata->signal);
800         fwrite(buffer, sizeof(char), strlen(buffer), f);
801      }
802
803    if (cfdata->read) read_length = cfdata->read->size;
804
805    if (read_length)
806      {
807         tlen = 0;
808         for (i = 0; cfdata->read->lines[i].line; i++)
809           tlen += cfdata->read->lines[i].size + 2;
810         text = alloca(tlen + 1);
811         if (text)
812           {
813              text[0] = 0;
814              for (i = 0; cfdata->read->lines[i].line; i++)
815                {
816                   strcat(text, "\t");
817                   strcat(text, cfdata->read->lines[i].line);
818                   strcat(text, "\n");
819                }
820              snprintf(buffer, sizeof(buffer), "Output Data:\n%s\n\n", text);
821              fwrite(buffer, sizeof(char), strlen(buffer), f);
822           }
823      }
824    else
825      {
826         snprintf(buffer, sizeof(buffer), "Output Data:\n\tThere was no output\n\n");
827         fwrite(buffer, sizeof(char), strlen(buffer), f);
828      }
829
830    /* Reusing this var */
831    read_length = 0;
832    if (cfdata->error) read_length = cfdata->error->size;
833
834    if (read_length)
835      {
836         tlen = 0;
837         for (i = 0; cfdata->error->lines[i].line; i++)
838           tlen += cfdata->error->lines[i].size + 1;
839         text = alloca(tlen + 1);
840         if (text)
841           {
842              text[0] = 0;
843              for (i = 0; cfdata->error->lines[i].line; i++)
844                {
845                   strcat(text, "\t");
846                   strcat(text, cfdata->error->lines[i].line);
847                   strcat(text, "\n");
848                }
849              snprintf(buffer, sizeof(buffer), "Error Logs:\n%s\n", text);
850              fwrite(buffer, sizeof(char), strlen(buffer), f);
851           }
852      }
853    else
854      {
855         snprintf(buffer, sizeof(buffer), "Error Logs:\n\tThere was no error message\n");
856         fwrite(buffer, sizeof(char), strlen(buffer), f);
857      }
858
859    fclose(f);
860 }