/* Chrysalide - Outil d'analyse de fichiers binaires * gcodebuffer.h - prototypes pour l'affichage d'un fragment de code d'assemblage * * Copyright (C) 2010-2014 Cyrille Bagard * * This file is part of Chrysalide. * * OpenIDA is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * OpenIDA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "gcodebuffer.h" #include #include #include #include #include "chrysamarshal.h" #include "delayed-int.h" #include "../common/extstr.h" /* -------------------------- PARCOURS DU CODE D'UN TAMPON -------------------------- */ #define G_TYPE_BUFFER_SCAN g_buffer_scan_get_type() #define G_BUFFER_SCAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_buffer_scan_get_type(), GDelayedExport)) #define G_IS_BUFFER_SCAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_buffer_scan_get_type())) #define G_BUFFER_SCAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BUFFER_SCAN, GDelayedExportClass)) #define G_IS_BUFFER_SCAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BUFFER_SCAN)) #define G_BUFFER_SCAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BUFFER_SCAN, GDelayedExportClass)) /* Ensembles binaires à désassembler (instance) */ typedef struct _GBufferScan { GDelayedWork parent; /* A laisser en premier */ GCodeBuffer *buffer; /* Tampon à manipuler */ vmpa2t start; /* Début du parcours */ bool has_start; /* Validité des données #1 */ vmpa2t end; /* Fin du parcours */ bool has_end; /* Validité des données #2 */ char *message; /* Message de progression */ process_line_fc process; /* Fonction de traitement réel */ void *user_data; /* Données à faire suivre */ } GBufferScan; /* Ensembles binaires à désassembler (classe) */ typedef struct _GBufferScanClass { GDelayedWorkClass parent; /* A laisser en premier */ } GBufferScanClass; /* Indique le type défini pour les tâches d'exportation différée. */ static GType g_buffer_scan_get_type(void); /* Initialise la classe des tâches d'exportation différée. */ static void g_buffer_scan_class_init(GBufferScanClass *); /* Initialise une tâche d'exportation différée. */ static void g_buffer_scan_init(GBufferScan *); /* Supprime toutes les références externes. */ static void g_buffer_scan_dispose(GBufferScan *); /* Procède à la libération totale de la mémoire. */ static void g_buffer_scan_finalize(GBufferScan *); /* Crée une tâche d'exportation différée. */ static GBufferScan *g_buffer_scan_new(GCodeBuffer *, const vmpa2t *, const vmpa2t *, const char *, process_line_fc, void *); /* Assure l'exportation en différé. */ static void g_buffer_scan_process(GBufferScan *, GtkExtStatusBar *); /* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */ /* Tampon pour code désassemblé (instance) */ struct _GCodeBuffer { GObject parent; /* A laisser en premier */ BufferLineColumn main_column; /* Colonne principale */ GBufferLine **lines; /* Liste des lignes intégrées */ size_t count; /* Quantité en cache */ size_t used; /* Quantité utilisée */ size_t indent; /* Indentation des lignes */ }; /* Tampon pour code désassemblé (classe) */ struct _GCodeBufferClass { GObjectClass parent; /* A laisser en premier */ /* Signaux */ void (* line_changed) (GCodeBuffer *, GBufferLine *, GBufferSegment *); }; /* Taille des allocations de masse */ #define LINE_ALLOC_BULK 20 /* Procède à l'initialisation d'une classe de tampon de code. */ static void g_code_buffer_class_init(GCodeBufferClass *); /* Procède à l'initialisation d'un tampon pour code désassemblé. */ static void g_code_buffer_init(GCodeBuffer *); /* Convertit une adresse en indice de ligne. */ static size_t g_code_buffer_get_index_from_address(const GCodeBuffer *, const vmpa2t *, bool); /* Actualise les largeurs maximales par groupes de lignes. */ static void g_code_buffer_update_line_max_widths(const GCodeBuffer *, size_t, size_t); /* Réagit à un changement de contenu d'une ligne donnée. */ static void on_line_content_change(GBufferLine *, GBufferSegment *, GCodeBuffer *); /* Réagit à un changement de propriété rattachée à une ligne. */ static void on_line_flag_flip(GBufferLine *, BufferLineFlags, BufferLineFlags, GCodeBuffer *); /* Ajoute de nouvelles lignes à une position donnée. */ static void g_code_buffer_insert_lines_at(GCodeBuffer *, GBufferLine **, size_t, size_t); /* ------------------------- CONFORTS POUR LES COMMENTAIRES ------------------------- */ /* Affiche un commentaire sur une ligne de tampon donnée. */ static bool _g_code_buffer_write_inlined_comment(GCodeBuffer *, GBufferLine *, const char *); /* Affiche un commentaire sur une ligne de tampon dédiée. */ static bool _g_code_buffer_write_comment_area(GCodeBuffer *, GBufferLine *, const char *, bool); /* Retrouve la première ligne d'une zone de commentaire. */ static size_t g_code_buffer_find_first_line_comment(const GCodeBuffer *, size_t); /* Retrouve la dernière ligne d'une zone de commentaire. */ static size_t g_code_buffer_find_last_line_comment(const GCodeBuffer *, size_t); /* Supprime un commentaire existant. */ static bool _g_code_buffer_delete_lines_comment(GCodeBuffer *, GBufferLine *); /* ---------------------- VUE PARTICULIERE D'UN TAMPON DE CODE ---------------------- */ /* Vue d'un tampon pour code désassemblé (instance) */ struct _GBufferView { GObject parent; /* A laisser en premier */ GCodeBuffer *buffer; /* Tampon de code visualisé */ vmpa2t *start; /* Première ligne intégrée */ vmpa2t *end; /* Dernière ligne intégrée */ size_t first_index; /* Indice de la première ligne */ /* FIXME : utiliser partout ? */ size_t last_index; /* Indice de la dernière ligne */ /* FIXME : idem */ gint line_height; /* Hauteur maximale des lignes */ gint left_margin; /* Marge gauche + espace */ gint left_text; /* Début d'impression du code */ gint max_widths[BLC_COUNT]; /* Taille cachée des colonnes */ gint merged_width; /* Plus grande taille de fusion*/ segcnt_list *highlighted; /* Segments mis en évidence */ bool external; /* Note l'origine de la liste */ }; /* Vue d'un tampon pour code désassemblé (classe) */ struct _GBufferViewClass { GObjectClass parent; /* A laisser en premier */ /* Signaux */ void (* need_redraw) (GBufferView *); }; #define HEIGHT_CACHED(view) ((view)->line_height != -1) #define WIDTHS_CACHED(view) ((view)->max_widths[0] != -1) /* Procède à l'initialisation d'une classe de vue de tampon. */ static void g_buffer_view_class_init(GBufferViewClass *); /* Procède à l'initialisation d'une vue d'un tampon pour code. */ static void g_buffer_view_init(GBufferView *); /* Supprime toutes les références externes. */ static void g_buffer_view_dispose(GBufferView *); /* Procède à la libération totale de la mémoire. */ static void g_buffer_view_finalize(GBufferView *); /* Réagit à un changement de contenu d'une ligne donnée. */ static void on_buffer_line_changed(GCodeBuffer *, GBufferLine *, GBufferSegment *, GBufferView *); /* Réinitialise le cache de la hauteur des lignes. */ static void g_buffer_view_reset_required_height(GBufferView *); /* Réinitialise le cache des largeurs de colonne calculées. */ static void g_buffer_view_reset_required_widths(GBufferView *); /* Calcule les largeurs requises par une visualisation. */ static void g_buffer_view_compute_required_widths(GBufferView *, const bool *); /* ---------------------------------------------------------------------------------- */ /* PARCOURS DU CODE D'UN TAMPON */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour les tâches d'exportation différée. */ G_DEFINE_TYPE(GBufferScan, g_buffer_scan, G_TYPE_DELAYED_WORK); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des tâches d'exportation différée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_scan_class_init(GBufferScanClass *klass) { GObjectClass *object; /* Autre version de la classe */ GDelayedWorkClass *work; /* Version en classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_scan_dispose; object->finalize = (GObjectFinalizeFunc)g_buffer_scan_finalize; work = G_DELAYED_WORK_CLASS(klass); work->run = (run_task_fc)g_buffer_scan_process; } /****************************************************************************** * * * Paramètres : scan = instance à initialiser. * * * * Description : Initialise une tâche d'exportation différée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_scan_init(GBufferScan *scan) { } /****************************************************************************** * * * Paramètres : scan = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_scan_dispose(GBufferScan *scan) { g_object_unref(G_OBJECT(scan->buffer)); G_OBJECT_CLASS(g_buffer_scan_parent_class)->dispose(G_OBJECT(scan)); } /****************************************************************************** * * * Paramètres : scan = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_scan_finalize(GBufferScan *scan) { free(scan->message); G_OBJECT_CLASS(g_buffer_scan_parent_class)->finalize(G_OBJECT(scan)); } /****************************************************************************** * * * Paramètres : buffer = tampon à manipuler. * * start = première adresse visée ou NULL. * * end = dernière adresse visée ou NULL. * * message = message à afficher lors de la progression. * * process = fonction assurant le traitement effectif. * * data = données utilisateur à faire suivre. * * * * Description : Crée une tâche d'exportation différée. * * * * Retour : Tâche créée. * * * * Remarques : - * * * ******************************************************************************/ static GBufferScan *g_buffer_scan_new(GCodeBuffer *buffer, const vmpa2t *start, const vmpa2t *end, const char *message, process_line_fc process, void *data) { GBufferScan *result; /* Tâche à retourner */ result = g_object_new(G_TYPE_BUFFER_SCAN, NULL); result->buffer = buffer; g_object_ref(G_OBJECT(buffer)); result->has_start = (start != NULL); if (result->has_start) copy_vmpa(&result->start, start); result->has_end = (end != NULL); if (result->has_end) copy_vmpa(&result->end, end); result->message = strdup(message); result->process = process; result->user_data = data; return result; } /****************************************************************************** * * * Paramètres : scan = parcours à mener. * * statusbar = barre de statut à tenir informée. * * * * Description : Assure l'exportation en différé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_scan_process(GBufferScan *scan, GtkExtStatusBar *statusbar) { size_t first; /* Première ligne visée */ size_t last; /* Dernière ligne visée + 1 */ GBufferLine **lines; /* Liste des lignes à traiter */ //bstatus_id_t id; /* Identifiant de statut */ size_t i; /* Boucle de parcours */ /* TODO : lock scan->buffer->lines */ if (scan->has_start) first = g_code_buffer_get_index_from_address(scan->buffer, &scan->start, true); else first = 0; if (scan->has_end) last = g_code_buffer_get_index_from_address(scan->buffer, &scan->end, false); else last = scan->buffer->used - 1; lines = scan->buffer->lines; //id = gtk_extended_status_bar_push(statusbar, scan->message, true); if (scan->buffer->used > 0) for (i = first; i <= last; i++) { if (!scan->process(scan->buffer, lines[i], scan->user_data)) break; /* gtk_extended_status_bar_update_activity(statusbar, id, (i - first) * 1.0 / (last - first)); */ } /* TODO : unlock scan->buffer->lines */ //gtk_extended_status_bar_remove(statusbar, id); } /* ---------------------------------------------------------------------------------- */ /* CONFORTS POUR LES COMMENTAIRES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * line = ligne à l'intérieur d'un commentaire. * * comment = nouveau commentaire à inscrire à la ligne donnée. * * * * Description : Affiche un commentaire sur une ligne de tampon donnée. * * * * Retour : Bilan de l'opération : ajout ou non ? * * * * Remarques : - * * * ******************************************************************************/ static bool _g_code_buffer_write_inlined_comment(GCodeBuffer *buffer, GBufferLine *line, const char *comment) { bool result; /* Bilan à retourner */ const mrange_t *range; /* Emplace de ligne à utiliser */ char *wcopy; /* Copie de travail */ GBufferLine **extra; /* Lignes supplémentaires */ size_t extra_count; /* Quantité de ces lignes */ char *saveptr; /* Sauvegarde pour la sécurité */ char *token; /* Fragment à insérer */ size_t len; /* Taille dudit fragment */ GBufferLine *new; /* Nouvelle ligne créée */ size_t i; /* Boucle de parcours */ assert(!g_buffer_line_has_comment(line)); result = false; range = g_buffer_line_get_range(line); wcopy = strdup(comment); extra = NULL; extra_count = 0; for (token = strtok_r(wcopy, COMMENT_LINE_SEP, &saveptr); token != NULL; token = strtok_r(NULL, COMMENT_LINE_SEP, &saveptr)) { len = strlen(token); if (!result) g_buffer_line_insert_text(line, BLC_COMMENTS, token, len, RTT_COMMENT); else { new = g_code_buffer_prepare_new_line(buffer, range); g_buffer_line_insert_text(new, BLC_COMMENTS, token, len, RTT_COMMENT); extra = (GBufferLine **)realloc(extra, ++extra_count * sizeof(GBufferLine *)); extra[extra_count - 1] = new; } result = true; } free(wcopy); if (extra_count > 0) { result &= g_code_buffer_insert_lines(buffer, extra, extra_count, line, false); if (!result) for (i = 0; i < extra_count; i++) g_object_unref(G_OBJECT(extra[i])); free(extra); } return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * line = ligne à l'intérieur d'un commentaire. * * comment = nouveau commentaire à inscrire à la ligne donnée. * * * * Description : Affiche un commentaire sur une ligne de tampon donnée. * * * * Retour : Bilan de l'opération : ajout ou non ? * * * * Remarques : - * * * ******************************************************************************/ bool g_code_buffer_update_inlined_comment(GCodeBuffer *buffer, GBufferLine *line, const char *comment) { bool result; /* Bilan à retourner */ if (g_buffer_line_has_comment(line)) result = _g_code_buffer_delete_lines_comment(buffer, line); else result = true; if (result) result = _g_code_buffer_write_inlined_comment(buffer, line, comment); /* TODO : emit() */ return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * line = ligne à l'intérieur d'un commentaire. * * comment = nouveau commentaire à inscrire à la ligne donnée. * * before = précise la position du commentaire. * * * * Description : Affiche un commentaire sur une ligne de tampon dédiée. * * * * Retour : Bilan de l'opération : ajout ou non ? * * * * Remarques : - * * * ******************************************************************************/ static bool _g_code_buffer_write_comment_area(GCodeBuffer *buffer, GBufferLine *line, const char *comment, bool before) { bool result; /* Bilan à retourner */ const mrange_t *range; /* Emplace de ligne à utiliser */ char *wcopy; /* Copie de travail */ GBufferLine **extra; /* Lignes supplémentaires */ size_t extra_count; /* Quantité de ces lignes */ char *saveptr; /* Sauvegarde pour la sécurité */ char *token; /* Fragment à insérer */ size_t len; /* Taille dudit fragment */ GBufferLine *new; /* Nouvelle ligne créée */ size_t i; /* Boucle de parcours */ assert(!g_buffer_line_has_comment(line)); result = false; range = g_buffer_line_get_range(line); wcopy = strdup(comment); extra = NULL; extra_count = 0; for (token = strtok_r(wcopy, COMMENT_LINE_SEP, &saveptr); token != NULL; token = strtok_r(NULL, COMMENT_LINE_SEP, &saveptr)) { len = strlen(token); new = g_code_buffer_prepare_new_line(buffer, range); g_buffer_line_start_merge_at(new, BLC_DISPLAY); g_buffer_line_insert_text(new, BLC_DISPLAY, token, len, RTT_COMMENT); extra = (GBufferLine **)realloc(extra, ++extra_count * sizeof(GBufferLine *)); extra[extra_count - 1] = new; result = true; } free(wcopy); if (extra_count > 0) { result &= g_code_buffer_insert_lines(buffer, extra, extra_count, line, before); if (!result) for (i = 0; i < extra_count; i++) g_object_unref(G_OBJECT(extra[i])); free(extra); } return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * line = ligne à l'intérieur d'un commentaire. * * comment = nouveau commentaire à inscrire à la ligne donnée. * * before = précise la position du commentaire. * * * * Description : Affiche un commentaire sur une ligne de tampon dédiée. * * * * Retour : Bilan de l'opération : ajout ou non ? * * * * Remarques : - * * * ******************************************************************************/ bool g_code_buffer_update_comment_area(GCodeBuffer *buffer, GBufferLine *line, const char *comment, bool before) { bool result; /* Bilan à retourner */ if (g_buffer_line_has_comment(line)) result = _g_code_buffer_delete_lines_comment(buffer, line); else result = true; if (result) result = _g_code_buffer_write_comment_area(buffer, line, comment, before); /* TODO : emit() */ return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * index = indice de ligne à l'intérieur d'un commentaire. * * * * Description : Retrouve la première ligne d'une zone de commentaire. * * * * Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. * * * * Remarques : - * * * ******************************************************************************/ static size_t g_code_buffer_find_first_line_comment(const GCodeBuffer *buffer, size_t index) { size_t result; /* Indice trouvé à retourner */ GBufferLine *prev; /* Ligne précédente */ assert(index < buffer->used); bool is_first_line_of_comment(const GBufferLine *ln, const GBufferLine *pv) { bool first; /* Statut à renvoyer */ BufferLineColumn merge_col; /* Colonne de fusion #1 */ BufferLineColumn prev_merge_col; /* Colonne de fusion #2 */ merge_col = g_buffer_line_get_merge_start(ln); /** * La ligne consultée contient toujours un commentaire. * * Deux cas de figures sont possibles ici : * * - soit du texte est présent dans la colonne "commentaires". * Si du texte est présent avant, alors il s'agit forcément de * la première (et unique ?) ligne de commentaire. * * - soit la ligne effectue une fusion des colonnes depuis BLC_DISPLAY. * Si la ligne qui précède fait de même, il s'agit alors d'une étiquette * ou de l'amont du commentaire. * */ if (g_buffer_line_has_text(ln, BLC_COMMENTS, BLC_COUNT)) { first = g_buffer_line_has_text(ln, BLC_DISPLAY, BLC_COMMENTS); if (!first) { /* La ligne d'avant doit avoir un commentaire ! */ first = !g_buffer_line_has_text(pv, BLC_COMMENTS, BLC_COUNT); } } else { /** * Le prologue "merge_col == BLC_FIRST" n'étant pas éditable, * la seule fusion possible ici est la suivante. */ assert(merge_col == BLC_DISPLAY); /** * La première ligne d'un tampon est toujours un prologue. */ assert(pv != NULL); prev_merge_col = g_buffer_line_get_merge_start(pv); first = (prev_merge_col != BLC_DISPLAY); if (!first) first = (g_buffer_line_get_flags(pv) & BLF_IS_LABEL); } return first; } for (result = index; result > 0; result--) { prev = (result > 0 ? buffer->lines[result - 1] : NULL); if (is_first_line_of_comment(buffer->lines[result], prev)) break; } if (result == 0) { if (!is_first_line_of_comment(buffer->lines[0], NULL)) result = buffer->used; } return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * index = indice de ligne à l'intérieur d'un commentaire. * * * * Description : Retrouve la dernière ligne d'une zone de commentaire. * * * * Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. * * * * Remarques : - * * * ******************************************************************************/ static size_t g_code_buffer_find_last_line_comment(const GCodeBuffer *buffer, size_t index) { size_t result; /* Indice trouvé à retourner */ GBufferLine *next; /* Ligne suivante */ assert(index < buffer->used); bool is_last_line_of_comment(const GBufferLine *ln, const GBufferLine *nx) { bool last; /* Statut à renvoyer */ BufferLineColumn merge_col; /* Colonne de fusion #1 */ BufferLineColumn next_merge_col; /* Colonne de fusion #2 */ merge_col = g_buffer_line_get_merge_start(ln); /** * La ligne consultée contient toujours un commentaire. * * Deux cas de figures sont possibles ici : * * - soit du texte est présent dans la colonne "commentaires". * Si la ligne suivante est similaire et si du texte est présent avant, * alors il s'agit forcément de d'un nouveau commentaire. S'il n'y a * aucun texte, il s'agit de la suite du commentaire. * * - soit la ligne effectue une fusion des colonnes depuis BLC_DISPLAY. * Si la ligne qui suit fait de même, il s'agit alors d'une étiquette * ou de l'aval du commentaire. * */ if (g_buffer_line_has_text(ln, BLC_COMMENTS, BLC_COUNT)) { last = !g_buffer_line_has_text(nx, BLC_COMMENTS, BLC_COUNT); if (!last) last = g_buffer_line_has_text(nx, BLC_DISPLAY, BLC_COMMENTS); } else { /** * Le prologue "merge_col == BLC_FIRST" n'étant pas éditable, * la seule fusion possible ici est la suivante. */ assert(merge_col == BLC_DISPLAY); if (nx == NULL) last = true; else { next_merge_col = g_buffer_line_get_merge_start(nx); last = (next_merge_col != BLC_DISPLAY); if (!last) last = (g_buffer_line_get_flags(nx) & BLF_IS_LABEL); } } return last; } for (result = index; result < buffer->used; result++) { next = ((result + 1) < buffer->used ? buffer->lines[result + 1] : NULL); if (is_last_line_of_comment(buffer->lines[result], next)) break; } return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * line = ligne à l'intérieur d'un commentaire. * * * * Description : Récupère le contenu d'un commentaire existant. * * * * Retour : Commentaire retrouver à libérer ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ char *g_code_buffer_get_lines_comment(const GCodeBuffer *buffer, const GBufferLine *line) { char *result; /* Contenu à retourner */ size_t index; /* Indice de la ligne fournie */ size_t start; /* Ligne de départ */ size_t end; /* Ligne d'arrivée */ BufferLineColumn merge_col; /* Colonne de fusion */ size_t i; /* Boucle de parcours */ char *extra; /* Commentaire supplémentaire */ /* Pas de prologue ici ! */ assert(g_buffer_line_has_comment(line)); result = NULL; index = g_code_buffer_find_index_by_line(buffer, line); if (index == buffer->used) goto gcbglc_exit; start = g_code_buffer_find_first_line_comment(buffer, index); if (start == buffer->used) goto gcbglc_exit; end = g_code_buffer_find_last_line_comment(buffer, index); if (end == buffer->used) goto gcbglc_exit; merge_col = g_buffer_line_get_merge_start(line); for (i = start; i <= end; i++) { if (merge_col == BLC_DISPLAY) extra = g_buffer_line_get_text(buffer->lines[i], BLC_DISPLAY, BLC_COUNT, false); else extra = g_buffer_line_get_text(buffer->lines[i], BLC_COMMENTS, BLC_COUNT, false); assert(extra != NULL); if (result == NULL) result = extra; else { result = stradd(result, extra); free(extra); } } gcbglc_exit: return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à modifier. * * line = ligne à l'intérieur d'un commentaire. * * * * Description : Supprime un commentaire existant. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool _g_code_buffer_delete_lines_comment(GCodeBuffer *buffer, GBufferLine *line) { bool result; /* Bilan à retourner */ size_t index; /* Indice de la ligne fournie */ size_t start; /* Ligne de départ */ size_t end; /* Ligne d'arrivée */ BufferLineColumn merge_col; /* Colonne de fusion */ /* Pas de prologue ici ! */ assert(g_buffer_line_has_comment(line)); result = false; index = g_code_buffer_find_index_by_line(buffer, line); if (index == buffer->used) goto gcbdlc_exit; start = g_code_buffer_find_first_line_comment(buffer, index); if (start == buffer->used) goto gcbdlc_exit; end = g_code_buffer_find_last_line_comment(buffer, index); if (end == buffer->used) goto gcbdlc_exit; result = true; merge_col = g_buffer_line_get_merge_start(line); if (merge_col == BLC_DISPLAY) g_code_buffer_delete_lines(buffer, start, end); else { g_buffer_line_delete_text(buffer->lines[start], BLC_COMMENTS, BLC_COUNT); if (end > start) g_code_buffer_delete_lines(buffer, start + 1, end); } gcbdlc_exit: return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à modifier. * * line = ligne à l'intérieur d'un commentaire. * * * * Description : Supprime un commentaire existant. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_code_buffer_delete_lines_comment(GCodeBuffer *buffer, GBufferLine *line) { bool result; /* Bilan à retourner */ result = _g_code_buffer_delete_lines_comment(buffer, line); /* TODO : emit() */ return result; } /* ---------------------------------------------------------------------------------- */ /* TAMPON POUR CODE DESASSEMBLE */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type du composant de tampon pour code désassemblé. */ G_DEFINE_TYPE(GCodeBuffer, g_code_buffer, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : class = classe de composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'une classe de tampon de code. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_code_buffer_class_init(GCodeBufferClass *class) { g_signal_new("line-changed", G_TYPE_CODE_BUFFER, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GCodeBufferClass, line_changed), NULL, NULL, g_cclosure_user_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); } /****************************************************************************** * * * Paramètres : buffer = composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'un tampon pour code désassemblé.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_code_buffer_init(GCodeBuffer *buffer) { } /****************************************************************************** * * * Paramètres : main = colonne à référencer comme étant la principale. * * * * Description : Crée un nouveau composant de tampon pour code désassemblé. * * * * Retour : Composant GTK créé. * * * * Remarques : - * * * ******************************************************************************/ GCodeBuffer *g_code_buffer_new(BufferLineColumn main) { GCodeBuffer *result; /* Composant à retourner */ result = g_object_new(G_TYPE_CODE_BUFFER, NULL); result->main_column = main; return result; } /****************************************************************************** * * * Paramètres : buffer = composant GTK à mettre à jour. * * addr = adresse où va se situer la ligne. * * first = indique si on l'arrête à la première ou la dernière.* * * * Description : Convertit une adresse en indice de ligne. * * * * Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. * * * * Remarques : - * * * ******************************************************************************/ static size_t g_code_buffer_get_index_from_address(const GCodeBuffer *buffer, const vmpa2t *addr, bool first) { size_t result; /* Indice à retourner */ GBufferLine **found; /* Renvoi vers une trouvaille */ const mrange_t *range; /* Couverture d'une ligne */ /** * Si aucune adresse (ie. aucune limite ?) n'est précisée, on se base sur * la direction pour trouver le bon indice. */ if (addr == NULL) result = (first ? 0 : buffer->used - 1); /** * Sinon on parcourt méthodiquement toutes les lignes ! */ else { /* Recherche dichotomique grossière */ int cmp_addr_and_line(const vmpa2t *addr, const GBufferLine **line) { int status; /* Bilan d'une comparaison */ const mrange_t *lrange; /* Couverture d'une ligne */ lrange = g_buffer_line_get_range(*line); status = cmp_mrange_with_vmpa(lrange, addr); return status; } found = bsearch(addr, buffer->lines, buffer->used, sizeof(GBufferLine *), (__compar_fn_t)cmp_addr_and_line); /* Dernier raffinage pour approcher la cible réelle */ if (found == NULL) result = buffer->used; else { result = found - buffer->lines; if (first) for (; result > 0; result--) { range = g_buffer_line_get_range(buffer->lines[result - 1]); if (!mrange_contains_addr(range, addr)) break; } else for (; (result + 1) < buffer->used; result++) { range = g_buffer_line_get_range(buffer->lines[result + 1]); if (!mrange_contains_addr(range, addr)) break; } } } return result; } /****************************************************************************** * * * Paramètres : buffer = tampon contentenant un ensemble de lignes. * * first = première ligne modifiée à considérer. * * last = dernière ligne modifiée à considérer. * * * * Description : Actualise les largeurs maximales par groupes de lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_code_buffer_update_line_max_widths(const GCodeBuffer *buffer, size_t first, size_t last) { GBufferLine **lines; /* Liste des lignes à traiter */ size_t start; /* Début de groupe de largeurs */ size_t end; /* Fin de groupe de largeurs */ GBufferLine *manager; /* Ligne de gestion de largeurs*/ size_t i; /* Boucle de parcours */ assert(buffer->used > 0); lines = buffer->lines; /* Recherche des bornes du groupe de largeurs courant */ for (start = first; start > 0; start--) if (g_buffer_line_get_flags(lines[start]) & BLF_WIDTH_MANAGER) break; for (end = last; end < (buffer->used - 1); end++) if (g_buffer_line_get_flags(lines[end + 1]) & BLF_WIDTH_MANAGER) break; /* Réinitialisation ciblée des largeurs */ assert(g_buffer_line_get_flags(lines[start]) & BLF_WIDTH_MANAGER); manager = NULL; for (i = start; i <= end; i++) { if (g_buffer_line_get_flags(lines[i]) & BLF_WIDTH_MANAGER) manager = lines[i]; g_buffer_line_update_max_widths(lines[i], manager); } } /****************************************************************************** * * * Paramètres : buffer = composant GLib à consulter. * * range = emplacement où va se situer la ligne. * * * * Description : Initie une nouvelle ligne devant être insérée dans le tampon.* * * * Retour : Nouvelle ligne vierge à écrire. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_code_buffer_prepare_new_line(GCodeBuffer *buffer, const mrange_t *range) { GBufferLine *result; /* Instance à retourner */ size_t i; /* Boucle de parcours */ result = g_buffer_line_new(range, buffer->main_column); for (i = 0; i < buffer->indent; i++) g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, " ", 4, RTT_RAW); return result; } /****************************************************************************** * * * Paramètres : line = ligne dont la définition vient d'évoluer. * * segment = éventuel segment qui vient d'évoluer ou NULL. * * buffer = tampon de lignes cohérentes à manipuler. * * * * Description : Réagit à un changement de contenu d'une ligne donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_line_content_change(GBufferLine *line, GBufferSegment *segment, GCodeBuffer *buffer) { g_signal_emit_by_name(buffer, "line-changed", line, segment); } /****************************************************************************** * * * Paramètres : line = ligne dont la définition vient d'évoluer. * * old = ancien groupe de propriétés associées. * * old = nouveau groupe de propriétés associées. * * buffer = tampon de lignes cohérentes à manipuler. * * * * Description : Réagit à un changement de propriété rattachée à une ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_line_flag_flip(GBufferLine *line, BufferLineFlags old, BufferLineFlags new, GCodeBuffer *buffer) { g_signal_emit_by_name(buffer, "line-changed", line, NULL); } /****************************************************************************** * * * Paramètres : buffer = composant GLib à mettre à jour. * * lines = liste de lignes à insérer. * * count = taille de cette liste. * * index = point d'insertion de la première ligne. * * * * Description : Ajoute de nouvelles lignes à une position donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_code_buffer_insert_lines_at(GCodeBuffer *buffer, GBufferLine **lines, size_t count, size_t index) { size_t i; /* Boucle de parcours */ /* Elaboration d'un espace suffisant */ if ((buffer->used + count) > buffer->count) { if (count > LINE_ALLOC_BULK) buffer->count += count; else buffer->count += LINE_ALLOC_BULK; buffer->lines = (GBufferLine **)realloc(buffer->lines, buffer->count * sizeof(GBufferLine *)); } /* Insertion des lignes */ if (index < buffer->used) { memmove(&buffer->lines[index + count], &buffer->lines[index], (buffer->used - index) * sizeof(GBufferLine *)); } buffer->used += count; for (i = 0; i < count; i++) { assert((index + i) < buffer->used); buffer->lines[index + i] = lines[i]; g_signal_connect(lines[i], "content-changed", G_CALLBACK(on_line_content_change), buffer); g_signal_connect(lines[i], "flip-flag", G_CALLBACK(on_line_flag_flip), buffer); } /* Recueil initial des largeurs */ /** * Comme des optimisations reposant sur des cas particuliers peuvent être * réalisées ici, c'est à l'appelant de prendre cette charge de calculs à * son compte ! */ } /****************************************************************************** * * * Paramètres : buffer = composant GLib à mettre à jour. * * line = lign à insérer à la fin du tampon. * * * * Description : Ajoute une nouvelle ligne en fin de tampon. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_code_buffer_append_new_line(GCodeBuffer *buffer, GBufferLine *line) { GBufferLine *manager; /* Ligne de gestion de largeurs*/ g_code_buffer_insert_lines_at(buffer, (GBufferLine *[]) { line }, 1, buffer->used); /* Recueil initial des largeurs */ if (g_buffer_line_get_flags(line) & BLF_WIDTH_MANAGER) manager = line; else { assert(buffer->used > 1); manager = g_buffer_line_get_width_manager(buffer->lines[buffer->used - 2]); } g_buffer_line_update_max_widths(line, manager); } /****************************************************************************** * * * Paramètres : buffer = composant GLib à mettre à jour. * * lines = liste de lignes à insérer. * * count = taille de cette liste. * * point = point d'insertion du bloc de ligne. * * before = emplacement de l'insertion par rapport au point. * * * * Description : Ajoute de nouvelles lignes par rapport à une ligne donnée. * * * * Retour : Bilan : insertion réussie ou non ? * * * * Remarques : - * * * ******************************************************************************/ bool g_code_buffer_insert_lines(GCodeBuffer *buffer, GBufferLine **lines, size_t count, const GBufferLine *point, bool before) { bool result; /* Bilan à retourner */ size_t index; /* Indice d'insertion final */ result = false; index = g_code_buffer_find_index_by_line(buffer, point); if (index == buffer->used) goto gcbil_exit; if (!before) index++; g_code_buffer_insert_lines_at(buffer, lines, count, index); g_code_buffer_update_line_max_widths(buffer, index, index + count); result = true; gcbil_exit: return result; } /****************************************************************************** * * * Paramètres : buffer = composant GLib à mettre à jour. * * start = première ligne devant être supprimée. * * end = dernière ligne devant être supprimée. * * * * Description : Supprime une ou plusieurs lignes du tampon indiqué. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_code_buffer_delete_lines(GCodeBuffer *buffer, size_t start, size_t end) { size_t i; /* Boucle de parcours */ GBufferLine *line; /* Ligne en cours de traitement*/ assert(start < buffer->used); assert(end < buffer->used); for (i = start; i <= end; i++) { line = buffer->lines[i]; g_signal_handlers_disconnect_by_func(line, G_CALLBACK(on_line_content_change), buffer); g_signal_handlers_disconnect_by_func(line, G_CALLBACK(on_line_flag_flip), buffer); g_object_unref(G_OBJECT(line)); } memmove(&buffer->lines[start], &buffer->lines[end + 1], (buffer->used - end - 1) * sizeof(GBufferLine *)); } /****************************************************************************** * * * Paramètres : buffer = composant GTK à mettre à jour. * * addr = adresse où retrouver la ligne recherchée. * * flags = propriétés à vérifier en tout ou partie. * * idx = indice de la ligne trouvée ou NULL. [OUT] * * * * Description : Retrouve une ligne au sein d'un tampon avec une adresse. * * * * Retour : Line retrouvée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_code_buffer_find_line_by_addr(const GCodeBuffer *buffer, const vmpa2t *addr, BufferLineFlags flags, size_t *idx) { GBufferLine *result; /* Instance à retourner */ size_t index; /* Indice de la ligne visée */ index = g_code_buffer_get_index_from_address(buffer, addr, true); if (index == buffer->used) result = NULL; else { if (idx != NULL) *idx = index; result = buffer->lines[index]; if (flags != BLF_NONE) while ((g_buffer_line_get_flags(result) & flags) == 0) { if ((index + 1) == buffer->used) break; /* FIXME : vérifier que l'adresse est toujours celle recherchée ! */ if (idx != NULL) (*idx)++; result = buffer->lines[++index]; } g_object_ref(G_OBJECT(result)); } return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * index = indice de la ligne recherchée. * * * * Description : Retrouve une ligne au sein d'un tampon avec un indice. * * * * Retour : Line retrouvée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_code_buffer_find_line_by_index(const GCodeBuffer *buffer, size_t index) { GBufferLine *result; /* Ligne trouvée à retourner */ if (index < buffer->used) result = buffer->lines[index]; else result = NULL; return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes à consulter. * * line = ligne dont l'indice est à retrouver. * * * * Description : Retrouve l'indice associé à une ligne au sein d'un tampon. * * * * Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. * * * * Remarques : - * * * ******************************************************************************/ size_t g_code_buffer_find_index_by_line(const GCodeBuffer *buffer, const GBufferLine *line) { size_t result; /* Indice trouvé à retourner */ const mrange_t *range; /* Emplacement de la ligne */ const mrange_t *next; /* Emplacement suivant */ range = g_buffer_line_get_range(line); result = g_code_buffer_get_index_from_address(buffer, get_mrange_addr(range), true); /** * Comme plusieurs lignes consécutives peuvent avoir la même adresse, * on parcourt les lignes suivantes pour retrouver la ligne recherchée. */ if (result < buffer->used) { while (buffer->lines[result] != line) { if (++result == buffer->used) break; next = g_buffer_line_get_range(buffer->lines[result]); if (cmp_vmpa(get_mrange_addr(range), get_mrange_addr(next)) != 0) { result = buffer->used; break; } } } return result; } /****************************************************************************** * * * Paramètres : buffer = composant GTK à mettre à jour. * * * * Description : Augmente l'indentation des prochaines lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_code_buffer_inc_indentation(GCodeBuffer *buffer) { buffer->indent++; } /****************************************************************************** * * * Paramètres : buffer = composant GTK à mettre à jour. * * * * Description : Diminue l'indentation des prochaines lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_code_buffer_dec_indentation(GCodeBuffer *buffer) { /* BUG_ON(buffer->indent == 0) */ buffer->indent--; } /****************************************************************************** * * * Paramètres : buffer = tampon de données à utiliser. * * start = première adresse visée ou 0. * * end = dernière adresse visée ou VMPA_MAX. * * message = message à afficher lors de la progression. * * process = fonction assurant le traitement effectif. * * data = données utilisateur à faire suivre. * * * * Description : Lance un parcours des différentes lignes du tampon de code. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ GDelayedWork *g_buffer_code_scan(GCodeBuffer *buffer, const vmpa2t *start, const vmpa2t *end, const char *message, process_line_fc process, void *data) { GBufferScan *result; /* Procédure à créer / renvoyer*/ GWorkQueue *queue; /* Gestionnaire de différés */ result = g_buffer_scan_new(buffer, start, end, message, process, data); g_object_ref(G_OBJECT(result)); queue = get_work_queue(); g_work_queue_schedule_work(queue, G_DELAYED_WORK(result), DEFAULT_WORK_GROUP); return G_DELAYED_WORK(result); } /* ---------------------------------------------------------------------------------- */ /* VUE PARTICULIERE D'UN TAMPON DE CODE */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type de la vue d'un tampon pour code désassemblé. */ G_DEFINE_TYPE(GBufferView, g_buffer_view, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : class = classe de composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'une classe de vue de tampon. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_class_init(GBufferViewClass *class) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_view_dispose; object->finalize = (GObjectFinalizeFunc)g_buffer_view_finalize; g_signal_new("need-redraw", G_TYPE_BUFFER_VIEW, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GBufferViewClass, need_redraw), NULL, NULL, g_cclosure_user_marshal_VOID__OBJECT, G_TYPE_NONE, 0); } /****************************************************************************** * * * Paramètres : buffer = composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'une vue d'un tampon pour code. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_init(GBufferView *buffer) { buffer->first_index = 0; g_buffer_view_reset_required_height(buffer); g_buffer_view_reset_required_widths(buffer); } /****************************************************************************** * * * Paramètres : view = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_dispose(GBufferView *view) { g_object_unref(G_OBJECT(view->buffer)); G_OBJECT_CLASS(g_buffer_view_parent_class)->dispose(G_OBJECT(view)); } /****************************************************************************** * * * Paramètres : view = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_finalize(GBufferView *view) { if (!view->external) exit_segment_content_list(view->highlighted); G_OBJECT_CLASS(g_buffer_view_parent_class)->finalize(G_OBJECT(view)); } /****************************************************************************** * * * Paramètres : buffer = tampon à représenter à l'écran. * * widget = composant GTK de destination pour le rendu. * * * * Description : Crée une nouvelle vue d'un tampon pour code désassemblé. * * * * Retour : Composant GTK créé. * * * * Remarques : - * * * ******************************************************************************/ GBufferView *g_buffer_view_new(GCodeBuffer *buffer, segcnt_list *highlighted) { GBufferView *result; /* Composant à retourner */ result = g_object_new(G_TYPE_BUFFER_VIEW, NULL); g_object_ref(G_OBJECT(buffer)); result->buffer = buffer; g_buffer_view_restrict(result, NULL, NULL); g_signal_connect(buffer, "line-changed", G_CALLBACK(on_buffer_line_changed), result); if (highlighted != NULL) result->highlighted = highlighted; else result->highlighted = init_segment_content_list(); result->external = (highlighted != NULL); return result; } /****************************************************************************** * * * Paramètres : buffer = tampon de lignes cohérentes à manipuler. * * line = ligne dont la définition vient d'évoluer. * * segment = éventuel segment qui vient d'évoluer ou NULL. * * view = vue active du tampon de lignes concerné. * * * * Description : Réagit à un changement de contenu d'une ligne donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_buffer_line_changed(GCodeBuffer *buffer, GBufferLine *line, GBufferSegment *segment, GBufferView *view) { /* TODO : regarder si la vue et concernée et cibler d'avantage l'actualisation */ g_signal_emit_by_name(view, "need-redraw"); } /****************************************************************************** * * * Paramètres : view = visualisateur à mettre à jour. * * first = première ligne à imprimer. * * last = première ligne hors cadre. * * * * Description : Restreint le champ d'application de l'affichage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_view_restrict(GBufferView *view, const vmpa2t *start, const vmpa2t *end) { view->start = (start != NULL ? dup_vmpa(start) : NULL); view->end = (end != NULL ? dup_vmpa(end) : NULL); view->first_index = g_code_buffer_get_index_from_address(view->buffer, start, true); view->last_index = g_code_buffer_get_index_from_address(view->buffer, end, false); } /****************************************************************************** * * * Paramètres : view = visualisateur à mettre à jour. * * first = première ligne à imprimer ou NULL. [OUT] * * last = première ligne hors cadre ou NULL. [OUT] * * * * Description : Indique le champ d'application de l'affichage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_view_get_restrictions(GBufferView *view, vmpa2t *start, vmpa2t *end) { /* FIXME view->xxx == NULL -> plantage */ if (start != NULL) copy_vmpa(start, view->start); if (end != NULL) copy_vmpa(end, view->end); } /****************************************************************************** * * * Paramètres : view = visualisateur à consulter. * * * * Description : Fournit le tampon de code lié à un visualisateur donné. * * * * Retour : Tampon de code associé au gestionnaire d'affichage. * * * * Remarques : - * * * ******************************************************************************/ GCodeBuffer *g_buffer_view_get_buffer(const GBufferView *view) { return view->buffer; } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * * * Description : Réinitialise le cache de la hauteur des lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_reset_required_height(GBufferView *view) { view->line_height = -1; } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * * * Description : Réinitialise le cache des largeurs de colonne calculées. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_reset_required_widths(GBufferView *view) { unsigned int i; /* Boucle de parcours */ for (i = 0; i < BLC_COUNT; i++) view->max_widths[i] = -1; view->merged_width = 0; } /****************************************************************************** * * * Paramètres : view = visualisation à mettre à jour. * * * * Description : Calcule la hauteur requise par une visualisation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_compute_required_height(GBufferView *view) { view->line_height = 17; } /****************************************************************************** * * * Paramètres : view = visualisation à mettre à jour. * * display = règles d'affichage des colonnes modulables. * * * * Description : Calcule les largeurs requises par une visualisation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_view_compute_required_widths(GBufferView *view, const bool *display) { GBufferLine **lines; /* Liste des lignes à traiter */ size_t first; /* Première ligne intégrée */ size_t last; /* Dernière ligne intégrée */ size_t i; /* Boucle de parcours */ if (!HEIGHT_CACHED(view)) g_buffer_view_compute_required_height(view); lines = view->buffer->lines; first = g_code_buffer_get_index_from_address(view->buffer, view->start, true); last = g_code_buffer_get_index_from_address(view->buffer, view->end, false); view->left_margin = 2 * view->line_height; view->left_text = 2.5 * view->line_height; if (view->buffer->used > 0) { //g_code_buffer_update_line_max_widths(view->buffer, first, last); for (i = first; i <= last; i++) g_buffer_line_apply_max_widths(lines[i], view->max_widths, &view->merged_width); } } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * * * Description : Fournit la hauteur d'impression d'une ligne visualisée. * * * * Retour : Hauteur de ligne en pixel. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_view_get_line_height(GBufferView *view) { if (!HEIGHT_CACHED(view)) g_buffer_view_compute_required_height(view); return view->line_height; } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * display = règles d'affichage des colonnes modulables. * * * * Description : Fournit la largeur requise par une visualisation. * * * * Retour : Dimension calculée. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_view_get_width(GBufferView *view, const bool *display) { gint result; /* Taille à retourner */ gint col_width; /* Calcul selon les colonnes */ gint full_width; /* Calcul selon les fusions */ BufferLineColumn i; /* Boucle de parcours */ if (!WIDTHS_CACHED(view)) g_buffer_view_compute_required_widths(view, display); result = view->left_text; col_width = 0; full_width = 0; /* Première méthode */ for (i = 0; i < BLC_COUNT; i++) { if (i < BLC_DISPLAY && !display[i]) continue; col_width += view->max_widths[i]; if ((i + 1) < BLC_COUNT) col_width += COL_MARGIN; } /* Seconde méthode */ for (i = 0; i < BLC_DISPLAY; i++) { if (!display[i]) continue; full_width += view->max_widths[i] + COL_MARGIN; } full_width += view->merged_width; /* Mise en concurrence et poursuite... */ result += + MAX(col_width, full_width); return result; } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * display = règles d'affichage des colonnes modulables. * * * * Description : Fournit la largeur requise pour dépasser les marges gauches. * * * * Retour : Dimension calculée. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_view_get_margin(GBufferView *view, const bool *display) { gint result; /* Taille à retourner */ BufferLineColumn i; /* Boucle de parcours */ if (!WIDTHS_CACHED(view)) g_buffer_view_compute_required_widths(view, display); result = view->left_text; for (i = 0; i < BLC_DISPLAY; i++) { if (!display[i]) continue; result += view->max_widths[i] + COL_MARGIN; } return result; } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * * * Description : Fournit la hauteur requise par une visualisation. * * * * Retour : Dimension calculée. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_view_get_height(const GBufferView *view) { gint result; /* Taille à retourner */ size_t first; /* Première ligne intégrée */ size_t last; /* Dernière ligne intégrée */ result = view->line_height; first = g_code_buffer_get_index_from_address(view->buffer, view->start, true); last = g_code_buffer_get_index_from_address(view->buffer, view->end, false); result *= (last - first + 1); return result; } /****************************************************************************** * * * Paramètres : view = vue de tampon à mettre à jour. * * x = abscisse de la zone principale à traiter. * * y = ordonnée de la zone principale à traiter. * * display = règles d'affichage des colonnes modulables. * * caret = position du curseur à construire. [OUT] * * * * Description : Calcule la position idéale de curseur pour un point donné. * * * * Retour : Adresse si une a pu être déterminée, NULL sinon. * * * * Remarques : - * * * ******************************************************************************/ const vmpa2t *g_buffer_view_compute_caret(GBufferView *view, gint x, gint y, const bool *display, GdkRectangle *caret) { gint remaining; /* Copie de travail modifiable */ size_t index; /* Indice de ligne de tampon */ GBufferLine *line; /* Ligne à la position courante*/ GBufferSegment *segment; /* Segment présent sur la place*/ remaining = x; line = g_buffer_view_find_line_and_segment_at(view, &remaining, y, &index, display, &segment); if (line == NULL) return NULL; if (segment == NULL) printf(" -- no segment\n"); if (segment == NULL) return NULL; printf("\n[BASE] orig = %d tronc = %d reste = %d dernier = %d largeur = %d\n", x, x - remaining, remaining, g_buffer_segment_get_caret_position(segment, remaining), g_buffer_segment_get_width(segment)); printf(" '%s'\n", g_buffer_segment_get_text(segment, false)); caret->x = /*view->left_text +*/ (x - remaining) + g_buffer_segment_get_caret_position(segment, remaining); caret->y = (index - view->first_index) * view->line_height; caret->width = 2; caret->height = view->line_height; return get_mrange_addr(g_buffer_line_get_range(line)); } /****************************************************************************** * * * Paramètres : view = vue de tampon à mettre à jour. * * line = ligne correspondant à la position. * * index = indice de cette même ligne dans le tampon. * * x = abscisse de la zone principale à traiter. * * display = règles d'affichage des colonnes modulables. * * caret = position du curseur à construire. [OUT] * * * * Description : Calcule la position idéale de curseur pour un point donné. * * * * Retour : Adresse si une a pu être déterminée, NULL sinon. * * * * Remarques : - * * * ******************************************************************************/ const vmpa2t *g_buffer_view_compute_caret_full(GBufferView *view, GBufferLine *line, size_t index, gint x, const bool *display, GdkRectangle *caret) { gint offset; /* Point de travail modifiable */ gint base; /* Position absolue de segment */ GBufferSegment *segment; /* Segment visé par le pointeur*/ offset = x; offset -= view->left_text; if (offset < 0) return NULL; segment = g_buffer_line_get_segment_at(line, view->max_widths, display, &base, &offset, GDK_SCROLL_LEFT, true); if (segment == NULL) return NULL; caret->x = view->left_text + base + offset; printf("caret Y : %zu -> %zu\n", view->first_index, index); caret->y = (index - view->first_index) * view->line_height; caret->width = 2; caret->height = view->line_height; return get_mrange_addr(g_buffer_line_get_range(line)); } /****************************************************************************** * * * Paramètres : line = ligne à venir consulter. * * caret = position du curseur à faire évoluer. * * ctrl = indique la demande d'un parcours rapide. * * dir = direction du parcours. * * display = règles d'affichage des colonnes modulables. * * * * Description : Déplace le curseur au sein d'une vue de tampon. * * * * Retour : true si un déplacement a été effectué, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool _g_buffer_view_move_caret(GBufferView *view, const GBufferLine *line, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, const bool *display) { bool result; /* Bilan à retourner */ gint offset; /* Point de travail modifiable */ gint base; /* Position absolue de segment */ GBufferSegment *segment; /* Segment visé par le pointeur*/ offset = caret->x; offset -= view->left_text; if (offset < 0) return false; segment = g_buffer_line_get_segment_at(line, view->max_widths, display, &base, &offset, dir, false); if (segment == NULL) printf(" ===== NO SEG...\n"); if (segment == NULL) return false; printf(" ====== FIRST SEG :: %p ('%s')\n", segment, g_buffer_segment_get_text(segment, false)); //if (dir == GDK_SCROLL_LEFT || dir == GDK_SCROLL_RIGHT) result = g_buffer_segment_move_caret(segment, &offset, ctrl, dir); //else //result = true; printf(" ====== MOVE 1 ? %d\n", result); /////////////////// if (!result) { base = 0; segment = g_buffer_line_find_near_segment(line, segment, view->max_widths, display, dir, &offset); printf(" ====== NEAR SEG :: %p ('%s')\n", segment, segment ? g_buffer_segment_get_text(segment, false) : NULL); if (segment != NULL) { result = true; //result = g_buffer_segment_move_caret(segment, &offset, ctrl, dir); /* if (result) caret->x -= COL_MARGIN; */ printf(" ====== MOVE 2 ? %d (offset=%d)\n", result, offset); } } if (result) printf(" ====== NEW CARET: %d -> %d\n", caret->x, view->left_text + base + offset); else printf(" ====== NO NEW CARET!\n"); if (result) caret->x = view->left_text + base + offset; return result; } /****************************************************************************** * * * Paramètres : view = vue de tampon à mettre à jour. * * caret = position du curseur à faire évoluer. * * ctrl = indique la demande d'un parcours rapide. * * dir = direction du parcours. * * display = règles d'affichage des colonnes modulables. * * * * Description : Déplace le curseur au sein d'une vue de tampon. * * * * Retour : Adresse si une a pu être déterminée, VMPA_INVALID sinon. * * * * Remarques : - * * * ******************************************************************************/ const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, const bool *display) { const vmpa2t *result; /* Actualisation à renvoyer */ size_t index; /* Indice de ligne de tampon */ GBufferLine *line; /* Ligne sous le pointeur */ bool computed; /* Récursivité pris en compte */ gint lheight; /* Hauteur d'une ligne */ gint left_pos; /* Retour à la ligne */ gint right_pos; /* Position d'extrème droite */ BufferLineColumn i; /* Boucle de parcours */ size_t first; /* Première ligne intégrée */ size_t last; /* Dernière ligne intégrée */ bool moved; /* Mémorisation d'une évolut° */ result = NULL; computed = false; line = g_buffer_view_find_line_at(view, caret->y, &index); if (line == NULL) return NULL; lheight = g_buffer_view_get_line_height(view); switch (dir) { case GDK_SCROLL_UP: case GDK_SCROLL_DOWN: lheight = g_buffer_view_get_line_height(view); break; case GDK_SCROLL_LEFT: case GDK_SCROLL_RIGHT: left_pos = view->left_text; if (display[BLC_PHYSICAL]) left_pos += view->max_widths[BLC_PHYSICAL] + COL_MARGIN; if (display[BLC_VIRTUAL]) left_pos += view->max_widths[BLC_VIRTUAL] + COL_MARGIN; if (display[BLC_BINARY]) left_pos += view->max_widths[BLC_BINARY] + COL_MARGIN; right_pos = left_pos; for (i = BLC_ASSEMBLY_HEAD; i < BLC_COUNT; i++) right_pos += view->max_widths[i] + COL_MARGIN; /* gint g_buffer_line_compute_max_width(const GBufferLine *line, BufferLineColumn index, const gint *max_widths) BufferLineColumn g_buffer_line_get_merge_start(const GBufferLine *line) */ left_pos = view->left_text; break; default: /* GDK_SCROLL_SMOOTH */ break; } first = view->first_index; last = view->last_index; switch (dir) { case GDK_SCROLL_UP: if (index > first) { line = view->buffer->lines[index - 1]; result = g_buffer_view_compute_caret_full(view, line, index - 1, caret->x, display, caret); } break; case GDK_SCROLL_DOWN: if (index < last) { line = view->buffer->lines[index + 1]; result = g_buffer_view_compute_caret_full(view, line, index + 1, caret->x, display, caret); } break; case GDK_SCROLL_LEFT: /* line = g_buffer_view_find_line_at(view, caret->y, &index); if (line == NULL) break; */ moved = _g_buffer_view_move_caret(view, line, caret, ctrl, GDK_SCROLL_LEFT, display); if (moved) result = get_mrange_addr(g_buffer_line_get_range(line)); else if (index > first) { line = view->buffer->lines[index - 1]; result = g_buffer_view_compute_caret_full(view, line, index - 1, INT_MAX, display, caret); } break; case GDK_SCROLL_RIGHT: /* line = g_buffer_view_find_line_at(view, caret->y, &index); if (line == NULL) break; */ moved = _g_buffer_view_move_caret(view, line, caret, ctrl, GDK_SCROLL_RIGHT, display); if (moved) result = get_mrange_addr(g_buffer_line_get_range(line)); else if (index < last) { line = view->buffer->lines[index + 1]; result = g_buffer_view_compute_caret_full(view, line, index + 1, left_pos, display, caret); } break; default: /* GDK_SCROLL_SMOOTH */ break; } /* printf(" --- CARET --- moved = %d index = %d result = %p\n", moved, index, result); */ /* if (result && !computed) result = g_buffer_view_compute_caret_full(view, caret->x, caret->y, caret, display, NULL); */ return result; } /****************************************************************************** * * * Paramètres : view = vue de tampon à mettre à jour. * * * * Description : Supprime toute mise en évidence de segments. * * * * Retour : true si un besoin d'actualisation d'affichage se fait sentir.* * * * Remarques : - * * * ******************************************************************************/ bool g_buffer_view_unhighlight_segments(GBufferView *view) { bool result; /* Bilan d'action à renvoyer */ result = reset_segment_content_list(view->highlighted); return result; } /****************************************************************************** * * * Paramètres : view = vue de tampon à mettre à jour. * * x = abscisse de la zone principale à traiter. * * y = ordonnée de la zone principale à traiter. * * display = règles d'affichage des colonnes modulables. * * * * Description : Surligne tous les segments similaires à celui sous la souris.* * * * Retour : true si un besoin d'actualisation d'affichage se fait sentir.* * * * Remarques : - * * * ******************************************************************************/ bool g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y, const bool *display) { bool need_redraw; /* Besoin d'actualisation ? */ GBufferSegment *segment; /* Segment sélectionnable */ if (view->highlighted != NULL) need_redraw = g_buffer_view_unhighlight_segments(view); else need_redraw = false; g_buffer_view_find_line_and_segment_at(view, &x, y, NULL, display, &segment); if (segment) need_redraw |= add_segment_content_to_selection_list(view->highlighted, segment); if (need_redraw) g_signal_emit_by_name(view, "need-redraw"); return true; } /****************************************************************************** * * * Paramètres : view = visualisation à représenter. * * cr = contexte graphique dédié à la procédure. * * fake_x = abscisse réelle du point 0 à l'écran. * * fake_y = ordonnée réelle du point 0 à l'écran. * * area = position et surface à traiter. * * display = règles d'affichage des colonnes modulables. * * selected = ordonnée d'une ligne sélectionnée ou NULL. * * * * Description : Imprime la visualisation du tampon de code désassemblé. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint fake_x, gint fake_y, const cairo_rectangle_int_t *area, const bool *display, const gint *selected) { gint real_x; /* Abscisse réelle pour tampon */ gint real_y; /* Ordonnée réelle pour tampon */ size_t first; /* Première ligne visée */ size_t last; /* Dernière ligne visée + 1 */ gint y; /* Point de départ + décallage */ GBufferLine **lines; /* Liste des lignes à traiter */ bool wait_selection; /* Sélection déjà passée ? */ gint rel_selected; /* Position relative de sélect°*/ size_t i; /* Boucle de parcours */ real_x = fake_x + view->left_text; real_y = fake_y + area->y; first = view->first_index; first += (real_y / view->line_height); last = first + (area->height / view->line_height); if (area->height % view->line_height > 0) last++; last = MIN(last, view->last_index); y = area->y - (real_y % view->line_height); lines = view->buffer->lines; wait_selection = true; if (selected != NULL) rel_selected = *selected - fake_y; if (view->buffer->used > 0) for (i = first; i <= last; i++) { /* Si sélection, on sousligne la ligne concernée */ if (wait_selection && selected != NULL && rel_selected == y) { cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.05); cairo_rectangle(cr, area->x, y, area->width, view->line_height); cairo_fill(cr); wait_selection = false; } g_buffer_line_draw(lines[i], cr, view->max_widths, real_x, y, display, view->highlighted); y += view->line_height; } } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * addr = adresse où retrouver la ligne recherchée. * * flags = propriétés à vérifier en tout ou partie. * * idx = indice de la ligne trouvée ou NULL. [OUT] * * * * Description : Retrouve une ligne au sein d'un tampon avec une adresse. * * * * Retour : Line retrouvée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_buffer_view_find_line_by_addr(const GBufferView *view, const vmpa2t *addr, BufferLineFlags flags, size_t *idx) { GBufferLine *result; /* Ligne trouvée à retourner */ phys_t length; /* Taille de la vue */ mrange_t vrange; /* Couverture de la vue */ bool allowed; /* Rechercher validée ? */ /* Vérification des bornes */ if (view->start != NULL/* && view->end != NULL*/) { length = compute_vmpa_diff(view->start, view->end); init_mrange(&vrange, view->start, length); allowed = mrange_contains_addr_inclusive(&vrange, addr); } else allowed = true; /* Lancement des recherches ? */ if (allowed) result = g_code_buffer_find_line_by_addr(view->buffer, addr, flags, idx); else result = NULL; return result; } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * index = indice de la ligne recherchée. * * * * Description : Retrouve une ligne au sein d'un tampon avec un indice. * * * * Retour : Line retrouvée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_buffer_view_find_line_by_index(const GBufferView *view, size_t index) { GBufferLine *result; /* Ligne trouvée à retourner */ bool allowed; /* Rechercher validée ? */ /* Vérification des bornes */ allowed = (view->first_index <= index && index < view->last_index); /* Lancement des recherches ? */ if (allowed) result = view->buffer->lines[index]; else result = NULL; return result; } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * y = ordonnée comprise dans la ligne recherchée. * * idx = indice de la ligne trouvée ou NULL. [OUT] * * * * Description : Fournit la ligne présente à une ordonnée donnée. * * * * Retour : Ligne retrouvée ou NULL si aucune. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_buffer_view_find_line_at(GBufferView *view, gint y, size_t *idx) { gint lheight; /* Hauteur d'une ligne */ size_t index; /* Indice attendu */ lheight = g_buffer_view_get_line_height(view); index = y / lheight; index += view->first_index; if (idx != NULL) *idx = index; return (index < view->buffer->used ? view->buffer->lines[index] : NULL); } /****************************************************************************** * * * Paramètres : view = visualisation à consulter. * * x = abscisse comprise dans le segment recherché. [OUT] * * y = ordonnée comprise dans la ligne recherchée. * * idx = indice de la ligne trouvée ou NULL. [OUT] * * display = règles d'affichage des colonnes modulables. * * segment = portion de texte recherchée ou NULL. [OUT] * * * * Description : Fournit la ligne et son segment présents à une position. * * * * Retour : Ligne retrouvée ou NULL si aucune. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_buffer_view_find_line_and_segment_at(GBufferView *view, gint *x, gint y, size_t *idx, const bool *display, GBufferSegment **segment) { GBufferLine *result; /* Ligne trouvée à retourner */ /* Recherche d'une ligne correspondante */ result = g_buffer_view_find_line_at(view, y, idx); /* Recherche du segment visé éventuel */ if (result != NULL && segment != NULL) { if (*x < view->left_text) *segment = NULL; else { *x -= view->left_text; *segment = g_buffer_line_get_segment_at(result, view->max_widths, display, (gint []) { 0 }, x, GDK_SCROLL_LEFT, true); } } return result; } /****************************************************************************** * * * Paramètres : view = composant GTK à consulter. * * addr = adresse à présenter à l'écran. * * x = position horizontale au sein du composant. [OUT] * * y = position verticale au sein du composant. [OUT] * * code = s'arrête si possible à une ligne avec code. * * * * Description : Indique la position d'affichage d'une adresse donnée. * * * * Retour : true si l'adresse fait partie du composant, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_buffer_view_get_address_coordinates(GBufferView *view, const vmpa2t *addr, gint *x, gint *y, bool code) { bool result; /* Bilan à retourner */ gint lheight; /* Hauteur d'une ligne */ size_t first; /* Première ligne intégrée */ size_t last; /* Dernière ligne intégrée */ size_t i; /* Boucle de parcours */ const mrange_t *range; /* Emplacement parcouru */ result = false; *x = 0; *y = 0; lheight = g_buffer_view_get_line_height(view); first = g_code_buffer_get_index_from_address(view->buffer, view->start, true); last = g_code_buffer_get_index_from_address(view->buffer, view->end, false); for (i = first; i <= last; i++) { /** * Si l'adresse recherchée est plus petite que l'adresse de départ, * on va effectuer un parcours complet pour rien. * * On considère cependant que le seul cas où celà peut arriver * est lorsque que des découpages en blocs sont impliqués. * * Les découpages conduisent alors à la formation de petites zones, * rapides à parcourir. */ range = g_buffer_line_get_range(view->buffer->lines[i]); result = mrange_contains_addr(range, addr); if (result) break; *y += lheight; } if (result && code) for (; i <= last; i++) { if (g_buffer_line_get_flags(view->buffer->lines[i]) & BLF_HAS_CODE) break; if (i == last) break; range = g_buffer_line_get_range(view->buffer->lines[i + 1]); if (!mrange_contains_addr(range, addr)) break; *y += lheight; } return result; }