Files
test/source/blender/blenkernel/intern/report.cc
Brecht Van Lommel d061b00455 Refactor: Eliminate various unsafe memcpy and memset
Some of these already have warnings with clang-tidy, others are more
safe in case these structs get (copy) constructors in the future.

Pull Request: https://projects.blender.org/blender/blender/pulls/137404
2025-04-21 17:59:41 +02:00

392 lines
8.5 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <cerrno>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <mutex>
#include "MEM_guardedalloc.h"
#include "BLI_dynstr.h"
#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_string_utils.hh"
#include "BLI_utildefines.h"
#include "BLT_translation.hh"
#include "BKE_global.hh" /* G.background only */
#include "BKE_report.hh"
const char *BKE_report_type_str(eReportType type)
{
switch (type) {
case RPT_DEBUG:
return RPT_("Debug");
case RPT_INFO:
return RPT_("Info");
case RPT_OPERATOR:
return RPT_("Operator");
case RPT_PROPERTY:
return RPT_("Property");
case RPT_WARNING:
return RPT_("Warning");
case RPT_ERROR:
return RPT_("Error");
case RPT_ERROR_INVALID_INPUT:
return RPT_("Invalid Input Error");
case RPT_ERROR_INVALID_CONTEXT:
return RPT_("Invalid Context Error");
case RPT_ERROR_OUT_OF_MEMORY:
return RPT_("Out Of Memory Error");
default:
return RPT_("Undefined Type");
}
}
void BKE_reports_init(ReportList *reports, int flag)
{
if (!reports) {
return;
}
*reports = ReportList{};
reports->storelevel = RPT_INFO;
reports->printlevel = RPT_ERROR;
reports->flag = flag;
reports->lock = MEM_new<std::mutex>(__func__);
}
void BKE_reports_free(ReportList *reports)
{
if (!reports) {
return;
}
BKE_reports_clear(reports);
MEM_delete(reports->lock);
reports->lock = nullptr;
}
void BKE_reports_clear(ReportList *reports)
{
Report *report, *report_next;
if (!reports) {
return;
}
std::scoped_lock lock(*reports->lock);
report = static_cast<Report *>(reports->list.first);
while (report) {
report_next = report->next;
MEM_freeN(report->message);
MEM_freeN(report);
report = report_next;
}
BLI_listbase_clear(&reports->list);
}
void BKE_reports_lock(ReportList *reports)
{
reports->lock->lock();
}
void BKE_reports_unlock(ReportList *reports)
{
reports->lock->unlock();
}
void BKE_reports_move_to_reports(ReportList *reports_dst, ReportList *reports_src)
{
BLI_assert(reports_dst);
if (!reports_src) {
return;
}
std::scoped_lock lock(*reports_src->lock, *reports_dst->lock);
BLI_movelisttolist(&reports_dst->list, &reports_src->list);
}
void BKE_report(ReportList *reports, eReportType type, const char *_message)
{
Report *report;
int len;
const char *message = RPT_(_message);
if (BKE_reports_print_test(reports, type)) {
printf("%s: %s\n", BKE_report_type_str(type), message);
fflush(stdout); /* this ensures the message is printed before a crash */
}
if (reports && (reports->flag & RPT_STORE) && (type >= reports->storelevel)) {
std::scoped_lock lock(*reports->lock);
char *message_alloc;
report = MEM_callocN<Report>("Report");
report->type = type;
report->typestr = BKE_report_type_str(type);
len = strlen(message);
message_alloc = MEM_malloc_arrayN<char>(size_t(len) + 1, "ReportMessage");
memcpy(message_alloc, message, sizeof(char) * (len + 1));
report->message = message_alloc;
report->len = len;
BLI_addtail(&reports->list, report);
}
}
void BKE_reportf(ReportList *reports, eReportType type, const char *_format, ...)
{
Report *report;
va_list args;
const char *format = RPT_(_format);
if (BKE_reports_print_test(reports, type)) {
printf("%s: ", BKE_report_type_str(type));
va_start(args, _format);
vprintf(format, args);
va_end(args);
fprintf(stdout, "\n"); /* otherwise each report needs to include a \n */
fflush(stdout); /* this ensures the message is printed before a crash */
}
if (reports && (reports->flag & RPT_STORE) && (type >= reports->storelevel)) {
std::scoped_lock lock(*reports->lock);
report = MEM_callocN<Report>("Report");
va_start(args, _format);
report->message = BLI_vsprintfN(format, args);
va_end(args);
report->len = strlen(report->message);
report->type = type;
report->typestr = BKE_report_type_str(type);
BLI_addtail(&reports->list, report);
}
}
/**
* Shared logic behind #BKE_reports_prepend & #BKE_reports_prependf.
*/
static void reports_prepend_impl(ReportList *reports, const char *prepend)
{
/* Caller must ensure. */
BLI_assert(reports && reports->list.first);
std::scoped_lock lock(*reports->lock);
const size_t prefix_len = strlen(prepend);
LISTBASE_FOREACH (Report *, report, &reports->list) {
char *message = BLI_string_joinN(prepend, report->message);
MEM_freeN(report->message);
report->message = message;
report->len += prefix_len;
BLI_assert(report->len == strlen(message));
}
}
void BKE_reports_prepend(ReportList *reports, const char *prepend)
{
if (!reports || !reports->list.first) {
return;
}
reports_prepend_impl(reports, RPT_(prepend));
}
void BKE_reports_prependf(ReportList *reports, const char *prepend_format, ...)
{
if (!reports || !reports->list.first) {
return;
}
va_list args;
va_start(args, prepend_format);
char *prepend = BLI_vsprintfN(RPT_(prepend_format), args);
va_end(args);
reports_prepend_impl(reports, prepend);
MEM_freeN(prepend);
}
eReportType BKE_report_print_level(ReportList *reports)
{
if (!reports) {
return RPT_ERROR;
}
return eReportType(reports->printlevel);
}
void BKE_report_print_level_set(ReportList *reports, eReportType level)
{
if (!reports) {
return;
}
std::scoped_lock lock(*reports->lock);
reports->printlevel = level;
}
eReportType BKE_report_store_level(ReportList *reports)
{
if (!reports) {
return RPT_ERROR;
}
return eReportType(reports->storelevel);
}
void BKE_report_store_level_set(ReportList *reports, eReportType level)
{
if (!reports) {
return;
}
std::scoped_lock lock(*reports->lock);
reports->storelevel = level;
}
char *BKE_reports_string(ReportList *reports, eReportType level)
{
DynStr *ds;
char *cstring;
if (!reports || !reports->list.first) {
return nullptr;
}
std::scoped_lock lock(*reports->lock);
ds = BLI_dynstr_new();
LISTBASE_FOREACH (Report *, report, &reports->list) {
if (report->type >= level) {
BLI_dynstr_appendf(ds, "%s: %s\n", report->typestr, report->message);
}
}
if (BLI_dynstr_get_len(ds)) {
cstring = BLI_dynstr_get_cstring(ds);
}
else {
cstring = nullptr;
}
BLI_dynstr_free(ds);
return cstring;
}
bool BKE_reports_print_test(const ReportList *reports, eReportType type)
{
if (reports == nullptr) {
return true;
}
if (reports->flag & RPT_PRINT_HANDLED_BY_OWNER) {
return false;
}
/* In background mode always print otherwise there are cases the errors won't be displayed,
* but still add to the report list since this is used for Python exception handling. */
if (G.background) {
return true;
}
/* Common case. */
return (reports->flag & RPT_PRINT) && (type >= reports->printlevel);
}
void BKE_reports_print(ReportList *reports, eReportType level)
{
char *cstring = BKE_reports_string(reports, level);
if (cstring == nullptr) {
return;
}
/* A trailing newline is already part of `cstring`. */
fputs(cstring, stdout);
fflush(stdout);
MEM_freeN(cstring);
}
Report *BKE_reports_last_displayable(ReportList *reports)
{
std::scoped_lock lock(*reports->lock);
LISTBASE_FOREACH_BACKWARD (Report *, report, &reports->list) {
if (ELEM(report->type, RPT_ERROR, RPT_WARNING, RPT_INFO)) {
return report;
}
}
return nullptr;
}
bool BKE_reports_contain(ReportList *reports, eReportType level)
{
if (reports != nullptr) {
std::scoped_lock lock(*reports->lock);
LISTBASE_FOREACH (Report *, report, &reports->list) {
if (report->type >= level) {
return true;
}
}
}
return false;
}
bool BKE_report_write_file_fp(FILE *fp, ReportList *reports, const char *header)
{
if (header) {
fputs(header, fp);
}
std::scoped_lock lock(*reports->lock);
LISTBASE_FOREACH (Report *, report, &reports->list) {
fprintf(fp, "%s # %s\n", report->message, report->typestr);
}
return true;
}
bool BKE_report_write_file(const char *filepath, ReportList *reports, const char *header)
{
FILE *fp;
errno = 0;
fp = BLI_fopen(filepath, "wb");
if (fp == nullptr) {
fprintf(stderr,
"Unable to save '%s': %s\n",
filepath,
errno ? strerror(errno) : "Unknown error opening file");
return false;
}
BKE_report_write_file_fp(fp, reports, header);
fclose(fp);
return true;
}