/*
* Copyright (C) 2012-2025 Robin Haberkorn
*
* This program 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.
*
* This program 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 this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include "sciteco.h"
#include "string-utils.h"
#include "interface.h"
#include "list.h"
#include "error.h"
guint teco_error_return_args = 0;
/*
* FIXME: Does this have to be stored in teco_machine_main_t?
* Probably becomes clear once we implement error handling by macros.
*/
guint teco_error_pos = 0, teco_error_line = 0, teco_error_column = 0;
typedef enum {
TECO_FRAME_QREG,
TECO_FRAME_FILE,
TECO_FRAME_EDHOOK,
TECO_FRAME_TOPLEVEL
} teco_frame_type_t;
typedef struct teco_frame_t {
teco_stailq_entry_t entry;
teco_frame_type_t type;
guint pos, line, column;
/*
* NOTE: This is currently sufficient to describe all
* frame types. Otherwise, add an union.
*/
gchar name[];
} teco_frame_t;
/**
* List of teco_frame_t describing the stack frames.
*
* Stack frames are collected deliberately unformatted
* since there are future applications where displaying
* a stack frame will not be necessary (e.g. error handled
* by SciTECO macro).
* Preformatting all stack frames would be very costly.
*/
static teco_stailq_head_t teco_frames = TECO_STAILQ_HEAD_INITIALIZER(&teco_frames);
void
teco_error_display_short(const GError *error)
{
teco_interface_msg(TECO_MSG_ERROR, "%s (at %d)",
error->message, teco_error_pos);
}
void
teco_error_display_full(const GError *error)
{
teco_interface_msg_literal(TECO_MSG_ERROR, error->message, strlen(error->message));
guint nr = 0;
for (teco_stailq_entry_t *cur = teco_frames.first; cur != NULL; cur = cur->next) {
teco_frame_t *frame = (teco_frame_t *)cur;
switch (frame->type) {
case TECO_FRAME_QREG:
teco_interface_msg(TECO_MSG_INFO,
"#%d in Q-Register \"%s\" at %d (%d:%d)",
nr, frame->name, frame->pos, frame->line, frame->column);
break;
case TECO_FRAME_FILE:
teco_interface_msg(TECO_MSG_INFO,
"#%d in file \"%s\" at %d (%d:%d)",
nr, frame->name, frame->pos, frame->line, frame->column);
break;
case TECO_FRAME_EDHOOK:
teco_interface_msg(TECO_MSG_INFO,
"#%d in \"%s\" hook execution",
nr, frame->name);
break;
case TECO_FRAME_TOPLEVEL:
teco_interface_msg(TECO_MSG_INFO,
"#%d in toplevel macro at %d (%d:%d)",
nr, frame->pos, frame->line, frame->column);
break;
}
nr++;
}
}
static teco_frame_t *
teco_error_add_frame(teco_frame_type_t type, gsize size)
{
teco_frame_t *frame = g_malloc(sizeof(teco_frame_t) + size);
frame->type = type;
frame->pos = teco_error_pos;
frame->line = teco_error_line;
frame->column = teco_error_column;
teco_stailq_insert_tail(&teco_frames, &frame->entry);
return frame;
}
void
teco_error_add_frame_qreg(const gchar *name, gsize len)
{
g_autofree gchar *name_printable = teco_string_echo(name, len);
teco_frame_t *frame = teco_error_add_frame(TECO_FRAME_QREG, strlen(name_printable) + 1);
strcpy(frame->name, name_printable);
}
void
teco_error_add_frame_file(const gchar *name)
{
teco_frame_t *frame = teco_error_add_frame(TECO_FRAME_FILE, strlen(name) + 1);
strcpy(frame->name, name);
}
void
teco_error_add_frame_edhook(const gchar *type)
{
teco_frame_t *frame = teco_error_add_frame(TECO_FRAME_EDHOOK, strlen(type) + 1);
strcpy(frame->name, type);
}
void
teco_error_add_frame_toplevel(void)
{
teco_error_add_frame(TECO_FRAME_TOPLEVEL, 0);
}
void TECO_DEBUG_CLEANUP
teco_error_clear_frames(void)
{
teco_stailq_entry_t *entry;
while ((entry = teco_stailq_remove_head(&teco_frames)))
g_free(entry);
}