Coder
/* $Id$ */
							/* Copyright (c) 2015-2020 Pierre Pronchery <khorben@defora.org> */
							/* Redistribution and use in source and binary forms, with or without
							 * modification, are permitted provided that the following conditions
							 * are met:
							 * 1. Redistributions of source code must retain the above copyright
							 *    notice, this list of conditions and the following disclaimer.
							 * 2. Redistributions in binary form must reproduce the above copyright
							 *    notice, this list of conditions and the following disclaimer in the
							 *    documentation and/or other materials provided with the distribution.
							 * 3. Neither the name of the authors nor the names of the contributors
							 *    may be used to endorse or promote products derived from this software
							 *    without specific prior written permission.
							 * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND
							 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
							 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
							 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
							 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
							 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
							 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
							 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
							 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
							 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
							 * SUCH DAMAGE. */
							#include <stdarg.h>
							#include <string.h>
							#include <libintl.h>
							#include <gtk/gtk.h>
							#include <System.h>
							#include <Desktop.h>
							#include <Devel/Asm.h>
							#include "../backend.h"
							#include "../debugger.h"
							#include "../../config.h"
							#define _(string) gettext(string)
							#ifndef PREFIX
							# define PREFIX	"/usr/local"
							#endif
							#ifndef LIBDIR
							# define LIBDIR	PREFIX "/lib"
							#endif
							/* asm */
							/* private */
							typedef struct _DebuggerBackend AsmBackend;
							struct _DebuggerBackend
							{
								DebuggerBackendHelper const * helper;
								Asm * a;
								AsmCode * code;
								guint source;
							};
							/* prototypes */
							/* plug-in */
							static AsmBackend * _asm_init(DebuggerBackendHelper const * helper);
							static void _asm_destroy(AsmBackend * backend);
							static int _asm_open(AsmBackend * backend, char const * arch,
									char const * format, char const * filename);
							static char * _asm_open_dialog(AsmBackend * backend, GtkWidget * window,
									char const * arch, char const * format);
							static int _asm_close(AsmBackend * backend);
							static char const * _asm_arch_get_name(AsmBackend * backend);
							static char const * _asm_format_get_name(AsmBackend * backend);
							/* constants */
							DebuggerBackendDefinition backend =
							{
								"asm",
								NULL,
								LICENSE_BSD3_FLAGS,
								_asm_init,
								_asm_destroy,
								_asm_open,
								_asm_open_dialog,
								_asm_close,
								_asm_arch_get_name,
								_asm_format_get_name
							};
							/* protected */
							/* functions */
							/* plug-in */
							/* asm_init */
							static AsmBackend * _asm_init(DebuggerBackendHelper const * helper)
							{
								AsmBackend * backend;
								if((backend = object_new(sizeof(*backend))) == NULL)
									return NULL;
								backend->helper = helper;
								backend->a = NULL;
								backend->code = NULL;
								backend->source = 0;
								return backend;
							}
							/* asm_destroy */
							static void _asm_destroy(AsmBackend * backend)
							{
								_asm_close(backend);
								object_delete(backend);
							}
							/* asm_open */
							/* callbacks */
							static gboolean _open_on_idle(gpointer data);
							static int _asm_open(AsmBackend * backend, char const * arch,
									char const * format, char const * filename)
							{
								if(_asm_close(backend) != 0)
									return -1;
								if((backend->a = asm_new(arch, format)) == NULL)
									return -1;
								if((backend->code = asm_open_deassemble(backend->a, filename, TRUE))
										== NULL)
								{
									asm_delete(backend->a);
									backend->a = NULL;
									return -1;
								}
								else
									backend->source = g_idle_add(_open_on_idle, backend);
								return 0;
							}
							static gboolean _open_on_idle(gpointer data)
							{
								AsmBackend * backend = data;
								AsmArchRegister const * registers;
								size_t cnt = 0;
								backend->source = 0;
								if((registers = asmcode_get_arch_registers(backend->code)) != NULL)
									for(cnt = 0; registers[cnt].name != NULL; cnt++);
								backend->helper->set_registers(backend->helper->debugger, registers,
										cnt);
								return FALSE;
							}
							/* asm_open_dialog */
							static void _open_dialog_type(GtkWidget * combobox, char const * type,
									char const * value);
							static char * _asm_open_dialog(AsmBackend * backend, GtkWidget * window,
									char const * arch, char const * format)
							{
								GtkWidget * dialog;
								GtkWidget * vbox;
								GtkWidget * hbox;
								GtkWidget * awidget;
								GtkWidget * fwidget;
								GtkWidget * widget;
								GtkFileFilter * filter;
								char * a = NULL;
								char * f = NULL;
								char * filename = NULL;
								(void) backend;
								dialog = gtk_file_chooser_dialog_new(_("Open file..."),
										(window != NULL) ? GTK_WINDOW(window) : NULL,
										GTK_FILE_CHOOSER_ACTION_OPEN,
										GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
										GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
							#if GTK_CHECK_VERSION(2, 14, 0)
								vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
							#else
								vbox = GTK_DIALOG(dialog)->vbox;
							#endif
								/* arch */
								hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
							#if GTK_CHECK_VERSION(2, 24, 0)
								awidget = gtk_combo_box_text_new();
								gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(awidget),
										_("Auto-detect"));
							#else
								awidget = gtk_combo_box_new_text();
								gtk_combo_box_append_text(GTK_COMBO_BOX(awidget), _("Auto-detect"));
							#endif
								_open_dialog_type(awidget, "arch", arch);
								gtk_box_pack_end(GTK_BOX(hbox), awidget, FALSE, TRUE, 0);
								widget = gtk_label_new(_("Architecture:"));
								gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
								gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
								/* format */
								hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
							#if GTK_CHECK_VERSION(2, 24, 0)
								fwidget = gtk_combo_box_text_new();
								gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(fwidget),
										_("Auto-detect"));
							#else
								fwidget = gtk_combo_box_new_text();
								gtk_combo_box_append_text(GTK_COMBO_BOX(fwidget), _("Auto-detect"));
							#endif
								_open_dialog_type(fwidget, "format", format);
								gtk_box_pack_end(GTK_BOX(hbox), fwidget, FALSE, TRUE, 0);
								widget = gtk_label_new(_("File format:"));
								gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
								gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
								gtk_widget_show_all(vbox);
								/* core files */
								filter = gtk_file_filter_new();
								gtk_file_filter_set_name(filter, _("Core files"));
								gtk_file_filter_add_mime_type(filter, "application/x-core");
								gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
								/* executable files */
								filter = gtk_file_filter_new();
								gtk_file_filter_set_name(filter, _("Executable files"));
								gtk_file_filter_add_mime_type(filter, "application/x-executable");
								gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
								gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
								/* java classes */
								filter = gtk_file_filter_new();
								gtk_file_filter_set_name(filter, _("Java classes"));
								gtk_file_filter_add_mime_type(filter, "application/x-java");
								gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
								/* objects */
								filter = gtk_file_filter_new();
								gtk_file_filter_set_name(filter, _("Objects"));
								gtk_file_filter_add_mime_type(filter, "application/x-object");
								gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
								/* shared objects */
								filter = gtk_file_filter_new();
								gtk_file_filter_set_name(filter, _("Shared objects"));
								gtk_file_filter_add_mime_type(filter, "application/x-sharedlib");
								gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
								/* all files */
								filter = gtk_file_filter_new();
								gtk_file_filter_set_name(filter, _("All files"));
								gtk_file_filter_add_pattern(filter, "*");
								gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
								if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
								{
									if(gtk_combo_box_get_active(GTK_COMBO_BOX(awidget)) == 0)
										a = NULL;
									else
							#if GTK_CHECK_VERSION(2, 24, 0)
										a = gtk_combo_box_text_get_active_text(
												GTK_COMBO_BOX_TEXT(awidget));
							#else
										a = gtk_combo_box_get_active_text(GTK_COMBO_BOX(
													awidget));
							#endif
									if(gtk_combo_box_get_active(GTK_COMBO_BOX(fwidget)) == 0)
										f = NULL;
									else
							#if GTK_CHECK_VERSION(2, 24, 0)
										f = gtk_combo_box_text_get_active_text(
												GTK_COMBO_BOX_TEXT(fwidget));
							#else
										f = gtk_combo_box_get_active_text(GTK_COMBO_BOX(
													fwidget));
							#endif
									filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
												dialog));
								}
								gtk_widget_destroy(dialog);
								g_free(a);
								g_free(f);
								return filename;
							}
							static void _open_dialog_type(GtkWidget * combobox, char const * type,
									char const * value)
							{
								char * path;
								DIR * dir;
								struct dirent * de;
							#if defined(__APPLE__)
								char const ext[] = ".dylib";
							#elif defined(__WIN32__)
								char const ext[] = ".dll";
							#else
								char const ext[] = ".so";
							#endif
								size_t len;
								int i;
								int active = 0;
								if((path = g_build_filename(LIBDIR, "Asm", type, NULL)) == NULL)
									return;
							#ifdef DEBUG
								fprintf(stderr, "DEBUG: %s(%s) \"%s\"\n", __func__, type, path);
							#endif
								dir = opendir(path);
								g_free(path);
								if(dir == NULL)
									return;
								for(i = 0; (de = readdir(dir)) != NULL; i++)
								{
									if(strcmp(de->d_name, ".") == 0
											|| strcmp(de->d_name, "..") == 0)
										continue;
									if((len = strlen(de->d_name)) < sizeof(ext))
										continue;
									if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0)
										continue;
									de->d_name[len - sizeof(ext) + 1] = '\0';
							#if GTK_CHECK_VERSION(2, 24, 0)
									gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combobox),
											de->d_name);
							#else
									gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), de->d_name);
							#endif
									if(value != NULL && strcmp(de->d_name, value) == 0)
										active = i;
								}
								closedir(dir);
								gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), active);
							}
							/* asm_close */
							static int _asm_close(AsmBackend * backend)
							{
								if(backend->source != 0)
									g_source_remove(backend->source);
								backend->source = 0;
								if(backend->a != NULL)
									asm_delete(backend->a);
								backend->a = NULL;
								return 0;
							}
							/* asm_arch_get_name */
							static char const * _asm_arch_get_name(AsmBackend * backend)
							{
								return asmcode_get_arch(backend->code);
							}
							/* asm_format_get_name */
							static char const * _asm_format_get_name(AsmBackend * backend)
							{
								return asmcode_get_format(backend->code);
							}
							