3 #define MAX_OUTPUT_CHARACTERS 5000
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
12 typedef struct _E_Exec_Launch E_Exec_Launch;
13 typedef struct _E_Exec_Search E_Exec_Search;
18 const char *launch_method;
23 Efreet_Desktop *desktop;
28 struct _E_Config_Dialog_Data
30 Efreet_Desktop *desktop;
33 Ecore_Exe_Event_Del event;
34 Ecore_Exe_Event_Data *error;
35 Ecore_Exe_Event_Data *read;
37 char *label, *exit, *signal;
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);
45 static Eina_Bool _e_exec_startup_id_pid_find(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *value, void *data);
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);
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;
62 static Ecore_Event_Handler *_e_exec_exit_handler = NULL;
63 static Ecore_Event_Handler *_e_exec_border_add_handler = NULL;
65 /* externally accessible functions */
69 e_exec_instances = eina_hash_string_superfast_new(NULL);
71 _e_exec_exit_handler =
72 ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_exec_cb_exit, NULL);
74 _e_exec_border_add_handler =
75 ecore_event_handler_add(E_EVENT_BORDER_ADD, _e_exec_cb_event_border_add, NULL);
85 snprintf(buf, sizeof(buf), "%i", startup_id);
86 e_util_env_set("E_STARTUP_ID", buf);
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);
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)
100 E_Exec_Launch *launch;
101 E_Exec_Instance *inst = NULL;
103 if ((!desktop) && (!exec)) return NULL;
104 launch = E_NEW(E_Exec_Launch, 1);
105 if (!launch) return NULL;
109 e_object_ref(E_OBJECT(launch->zone));
112 launch->launch_method = eina_stringshare_add(launch_method);
117 inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
119 inst = efreet_desktop_command_get(desktop, files,
120 (Efreet_Desktop_Command_Cb) _e_exec_cb_exec, launch);
123 inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
127 EAPI Efreet_Desktop *
128 e_exec_startup_id_pid_find(int startup_id, pid_t pid)
130 E_Exec_Search search;
132 search.desktop = NULL;
133 search.startup_id = startup_id;
135 eina_hash_foreach(e_exec_instances, _e_exec_startup_id_pid_find, &search);
136 return search.desktop;
139 /* local subsystem functions */
140 static E_Exec_Instance *
141 _e_exec_cb_exec(void *data, Efreet_Desktop *desktop, char *exec, int remaining)
143 E_Exec_Instance *inst = NULL;
144 E_Exec_Launch *launch;
152 inst = E_NEW(E_Exec_Instance, 1);
153 if (!inst) return NULL;
160 p = getenv("E_STARTUP_ID");
161 if (p) startup_id = atoi(p);
162 e_util_env_set("E_STARTUP_ID", NULL);
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))
174 head = launch->zone->container->manager->num;
177 p1 = strrchr(penv_display, ':');
178 p2 = strrchr(penv_display, '.');
179 if ((p1) && (p2) && (p2 > p1)) /* "blah:x.y" */
181 /* yes it could overflow... but who will overflow DISPLAY eh? why? to
182 * "exploit" your own applications running as you?
184 strcpy(buf, penv_display);
185 buf[p2 - penv_display + 1] = 0;
186 snprintf(buf2, sizeof(buf2), "%i", head);
189 else if (p1) /* "blah:x */
191 strcpy(buf, penv_display);
192 snprintf(buf2, sizeof(buf2), ".%i", head);
196 strcpy(buf, penv_display);
197 e_util_env_set("DISPLAY", buf);
199 snprintf(buf, sizeof(buf), "E_START|%i", startup_id);
200 e_util_env_set("DESKTOP_STARTUP_ID", buf);
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);
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,
212 if ((desktop) && (desktop->path))
214 if (!getcwd(buf, sizeof(buf)))
217 e_util_dialog_show(_("Run Error"),
218 _("Enlightenment was unable to get current directory"));
221 if (chdir(desktop->path))
224 e_util_dialog_show(_("Run Error"),
225 _("Enlightenment was unable to change to directory:<br>"
231 e_util_library_path_strip();
232 exe = ecore_exe_run(exec, inst);
233 e_util_library_path_restore();
236 e_util_dialog_show(_("Run Error"),
237 _("Enlightenment was unable to restore to directory:<br>"
247 e_util_library_path_strip();
248 exe = ecore_exe_run(exec, inst);
249 e_util_library_path_restore();
254 e_util_env_set("DISPLAY", penv_display);
260 e_util_dialog_show(_("Run Error"),
261 _("Enlightenment was unable to fork a child process:<br>"
268 if (launch->launch_method && !desktop)
269 e_exehist_add(launch->launch_method, 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");
281 efreet_desktop_ref(desktop);
282 inst->desktop = desktop;
283 inst->key = eina_stringshare_add(desktop->orig_path);
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);
292 eina_hash_modify(e_exec_instances, desktop->orig_path, lnew);
294 eina_hash_add(e_exec_instances, desktop->orig_path, lnew);
295 e_exec_start_pending = eina_list_append(e_exec_start_pending, desktop);
297 e_exehist_add(launch->launch_method, desktop->exec);
308 if (launch->launch_method) eina_stringshare_del(launch->launch_method);
309 if (launch->zone) e_object_unref(E_OBJECT(launch->zone));
316 _e_exec_cb_expire_timer(void *data)
318 E_Exec_Instance *inst;
321 e_exec_start_pending = eina_list_remove(e_exec_start_pending, inst->desktop);
322 inst->expire_timer = NULL;
323 return ECORE_CALLBACK_CANCEL;
327 _e_exec_instance_free(E_Exec_Instance *inst)
329 Eina_List *instances;
333 instances = eina_hash_find(e_exec_instances, inst->key);
336 instances = eina_list_remove(instances, inst);
338 eina_hash_modify(e_exec_instances, inst->key, instances);
340 eina_hash_del(e_exec_instances, inst->key, NULL);
342 eina_stringshare_del(inst->key);
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);
353 _e_exec_cb_instance_finish(void *data)
355 _e_exec_instance_free(data);
356 return ECORE_CALLBACK_CANCEL;
361 _e_exec_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event)
363 Ecore_Exe_Event_Del *ev;
364 E_Exec_Instance *inst;
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;
375 /* /bin/sh uses this if cmd not found */
377 ((ev->exit_code == 127) || (ev->exit_code == 255)))
379 if (e_config->exec.show_run_dialog)
383 dia = e_dialog_new(e_container_current_get(e_manager_current_get()),
384 "E", "_e_exec_run_error_dialog");
389 e_dialog_title_set(dia, _("Application run error"));
390 snprintf(buf, sizeof(buf),
391 _("Enlightenment was unable to run the application:<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);
405 /* Let's hope that everything returns this properly. */
406 else if (!((ev->exited) && (ev->exit_code == EXIT_SUCCESS)))
408 if (e_config->exec.show_exit_dialog)
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)))
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));
426 /* maybe better 1 minute? it might be openoffice */
427 if (ecore_time_get() - inst->launch_time < 2.0)
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);
434 _e_exec_instance_free(inst);
436 return ECORE_CALLBACK_PASS_ON;
440 _e_exec_startup_id_pid_find(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *value, void *data)
442 E_Exec_Search *search;
443 E_Exec_Instance *inst;
447 EINA_LIST_FOREACH(value, l, inst)
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))))
458 search->desktop = inst->desktop;
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)
469 E_Config_Dialog_View *v;
470 E_Config_Dialog_Data *cfdata;
473 v = E_NEW(E_Config_Dialog_View, 1);
475 cfdata = E_NEW(E_Config_Dialog_Data, 1);
481 cfdata->desktop = desktop;
482 if (cfdata->desktop) efreet_desktop_ref(cfdata->desktop);
483 if (exec) cfdata->exec = strdup(exec);
484 cfdata->error = error;
486 cfdata->event = *event;
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;
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",
501 _fill_data(E_Config_Dialog_Data *cfdata)
507 snprintf(buf, sizeof(buf), _("%s stopped running unexpectedly."), cfdata->desktop->name);
508 cfdata->label = strdup(buf);
510 if ((cfdata->event.exited) && (!cfdata->exit))
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);
517 if ((cfdata->event.signalled) && (!cfdata->signal))
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."),
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."),
533 else if (cfdata->event.exit_signal == SIGKILL)
534 snprintf(buf, sizeof(buf),
535 _("%s was interrupted by an Uninterruptable Kill Signal."),
537 else if (cfdata->event.exit_signal == SIGSEGV)
538 snprintf(buf, sizeof(buf),
539 _("%s was interrupted by a Segmentation Fault."),
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."),
548 else if (cfdata->event.exit_signal == SIGBUS)
549 snprintf(buf, sizeof(buf),
550 _("%s was interrupted by a Bus Error."), cfdata->exec);
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
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
580 _create_data(E_Config_Dialog *cfd)
582 E_Config_Dialog_Data *cfdata;
590 _free_data(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfdata)
592 if (cfdata->error) ecore_exe_event_data_free(cfdata->error);
593 if (cfdata->read) ecore_exe_event_data_free(cfdata->read);
595 if (cfdata->desktop) efreet_desktop_free(cfdata->desktop);
597 E_FREE(cfdata->exec);
598 E_FREE(cfdata->signal);
599 E_FREE(cfdata->exit);
600 E_FREE(cfdata->label);
605 _dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines)
607 Evas_Object *obj, *os;
609 char *trunc_note = _("***The remaining output has been truncated. Save the output to view.***\n");
610 int tlen, max_lines, i;
612 os = e_widget_framelist_add(evas, _(title), 0);
614 obj = e_widget_textblock_add(evas);
617 for (i = 0; lines[i].line; i++)
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)
625 tlen -= lines[i].size + 1;
626 tlen += strlen(trunc_note);
631 text = alloca(tlen + 1);
636 for (i = 0; i < max_lines; i++)
638 strcat(text, lines[i].line);
642 /* Append the warning about truncated output. */
643 if (lines[max_lines].line) strcat(text, trunc_note);
645 e_widget_textblock_plain_set(obj, text);
647 e_widget_size_min_set(obj, 240, 120);
649 e_widget_framelist_object_append(os, obj);
655 _basic_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
658 int error_length = 0;
659 Evas_Object *o, *ob, *os;
663 o = e_widget_list_add(evas, 0, 0);
665 ob = e_widget_label_add(evas, cfdata->label);
666 e_widget_list_object_append(o, ob, 1, 1, 0.5);
668 if (cfdata->error) error_length = cfdata->error->size;
671 os = _dialog_scrolltext_create(evas, _("Error Logs"),
672 cfdata->error->lines);
673 e_widget_list_object_append(o, os, 1, 1, 0.5);
677 ob = e_widget_label_add(evas, _("There was no error message."));
678 e_widget_list_object_append(o, ob, 1, 1, 0.5);
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);
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);
694 _advanced_create_widgets(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data *cfdata)
698 int error_length = 0;
699 Evas_Object *o, *of, *ob, *ot;
703 o = e_widget_list_add(evas, 0, 0);
704 ot = e_widget_table_add(evas, 0);
706 ob = e_widget_label_add(evas, cfdata->label);
707 e_widget_list_object_append(o, ob, 1, 1, 0.5);
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);
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);
725 if (cfdata->read) read_length = cfdata->read->size;
729 of = _dialog_scrolltext_create(evas, _("Output Data"),
730 cfdata->read->lines);
731 /* FIXME: Add stdout "start". */
732 /* FIXME: Add stdout "end". */
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);
740 e_widget_table_object_append(ot, of, 0, 0, 1, 1, 1, 1, 1, 1);
742 if (cfdata->error) error_length = cfdata->error->size;
745 of = _dialog_scrolltext_create(evas, _("Error Logs"),
746 cfdata->error->lines);
747 /* FIXME: Add stderr "start". */
748 /* FIXME: Add stderr "end". */
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);
756 e_widget_table_object_append(ot, of, 1, 0, 1, 1, 1, 1, 1, 1);
758 e_widget_list_object_append(o, ot, 1, 1, 0.5);
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);
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);
773 _dialog_save_cb(void *data __UNUSED__, void *data2)
775 E_Config_Dialog_Data *cfdata;
785 snprintf(buf, sizeof(buf), "%s/%s.log", e_user_homedir_get(),
786 e_util_filename_escape(cfdata->desktop->name));
792 snprintf(buffer, sizeof(buffer), "Error Information:\n\t%s\n\n",
794 fwrite(buffer, sizeof(char), strlen(buffer), f);
798 snprintf(buffer, sizeof(buffer), "Error Signal Information:\n\t%s\n\n",
800 fwrite(buffer, sizeof(char), strlen(buffer), f);
803 if (cfdata->read) read_length = cfdata->read->size;
808 for (i = 0; cfdata->read->lines[i].line; i++)
809 tlen += cfdata->read->lines[i].size + 2;
810 text = alloca(tlen + 1);
814 for (i = 0; cfdata->read->lines[i].line; i++)
817 strcat(text, cfdata->read->lines[i].line);
820 snprintf(buffer, sizeof(buffer), "Output Data:\n%s\n\n", text);
821 fwrite(buffer, sizeof(char), strlen(buffer), f);
826 snprintf(buffer, sizeof(buffer), "Output Data:\n\tThere was no output\n\n");
827 fwrite(buffer, sizeof(char), strlen(buffer), f);
830 /* Reusing this var */
832 if (cfdata->error) read_length = cfdata->error->size;
837 for (i = 0; cfdata->error->lines[i].line; i++)
838 tlen += cfdata->error->lines[i].size + 1;
839 text = alloca(tlen + 1);
843 for (i = 0; cfdata->error->lines[i].line; i++)
846 strcat(text, cfdata->error->lines[i].line);
849 snprintf(buffer, sizeof(buffer), "Error Logs:\n%s\n", text);
850 fwrite(buffer, sizeof(char), strlen(buffer), f);
855 snprintf(buffer, sizeof(buffer), "Error Logs:\n\tThere was no error message\n");
856 fwrite(buffer, sizeof(char), strlen(buffer), f);