/* except.cpp --

   This file is part of the UPX executable compressor.

   Copyright (C) 1996-2024 Markus Franz Xaver Johannes Oberhumer
   Copyright (C) 1996-2024 Laszlo Molnar
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 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; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer              Laszlo Molnar
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
 */

#include "conf.h"

/*************************************************************************
//
**************************************************************************/

/*static*/ Throwable::Stats Throwable::stats;

Throwable::Throwable(const char *m, int e, bool w) noexcept : super(),
                                                              msg(nullptr),
                                                              err(e),
                                                              is_warning(w) {
    if (m != nullptr) {
        msg = strdup(m);
        assert_noexcept(msg != nullptr);
    }
    NO_fprintf(stderr, "construct exception: %zu %zu %s\n", size_t(stats.counter_current),
               size_t(stats.counter_total), (const char *) msg);
    stats.counter_current += 1;
    stats.counter_total += 1;
}

Throwable::Throwable(const Throwable &other) noexcept : super(other),
                                                        msg(nullptr),
                                                        err(other.err),
                                                        is_warning(other.is_warning) {
    if (other.msg != nullptr) {
        msg = strdup(other.msg);
        assert_noexcept(msg != nullptr);
    }
    NO_fprintf(stderr, "copy construct exception: %zu %zu %s\n", size_t(stats.counter_current),
               size_t(stats.counter_total), (const char *) msg);
    stats.counter_current += 1;
    stats.counter_total += 1;
}

Throwable::~Throwable() noexcept {
    stats.counter_current -= 1;
    NO_fprintf(stderr, "destruct exception: %zu %zu %s\n", size_t(stats.counter_current),
               size_t(stats.counter_total), (const char *) msg);
    upx::owner_free(msg);
}

/*************************************************************************
// compression
**************************************************************************/

void throwCantPack(const char *msg) {
    // UGLY, but makes things easier
    if (opt->cmd == CMD_NONE)
        throw CantPackException(msg);
    else if (opt->cmd == CMD_COMPRESS)
        throw CantPackException(msg);
    else if (opt->cmd == CMD_FILEINFO)
        throw CantPackException(msg);
    else
        throw CantUnpackException(msg);
}

void throwCantPackExact() { throwCantPack("option '--exact' does not work with this file"); }

void throwFilterException() { throwCantPack("filter problem"); }

void throwUnknownExecutableFormat(const char *msg, bool warn) {
    throw UnknownExecutableFormatException(msg, warn);
}

void throwNotCompressible(const char *msg) { throw NotCompressibleException(msg); }

void throwAlreadyPacked(const char *msg) { throw AlreadyPackedException(msg); }

void throwAlreadyPackedByUPX(const char *msg) {
    if (msg == nullptr)
        msg = "already packed by UPX";
    throwAlreadyPacked(msg);
}

/*************************************************************************
// decompression
**************************************************************************/

void throwCantUnpack(const char *msg) {
    // UGLY, but makes things easier
    throwCantPack(msg);
}

void throwNotPacked(const char *msg) {
    if (msg == nullptr)
        msg = "not packed by UPX";
    throw NotPackedException(msg);
}

void throwChecksumError() { throw Exception("checksum error"); }

void throwCompressedDataViolation() { throw Exception("compressed data violation"); }

/*************************************************************************
// other
**************************************************************************/

void throwInternalError(const char *msg) { throw InternalError(msg); }

void throwBadLoader() { throwInternalError("bad loader"); }

void throwOutOfMemoryException(const char *msg) {
    if (msg == nullptr)
        msg = "out of memory";
    throw OutOfMemoryException(msg);
}

void throwIOException(const char *msg, int e) { throw IOException(msg, e); }

void throwEOFException(const char *msg, int e) {
    if (msg == nullptr && e == 0)
        msg = "premature end of file";
    throw EOFException(msg, e);
}

/*************************************************************************
// varargs overloads
**************************************************************************/

template <>
void throwCantPack(const char *format, ...) {
    char msg[1024];
    va_list ap;
    va_start(ap, format);
    (void) upx_safe_vsnprintf_noexcept(msg, sizeof(msg), format, ap);
    va_end(ap);
    throwCantPack(msg);
}

template <>
void throwCantUnpack(const char *format, ...) {
    char msg[1024];
    va_list ap;
    va_start(ap, format);
    (void) upx_safe_vsnprintf_noexcept(msg, sizeof(msg), format, ap);
    va_end(ap);
    throwCantUnpack(msg);
}

template <>
void throwInternalError(const char *format, ...) {
    char msg[1024];
    va_list ap;
    va_start(ap, format);
    (void) upx_safe_vsnprintf_noexcept(msg, sizeof(msg), format, ap);
    va_end(ap);
    throwCantUnpack(msg);
}

/*************************************************************************
// util
**************************************************************************/

void assertFailed(const char *expr, const char *file, int line, const char *func) noexcept {
    fflush(stdout);
    fprintf(stderr, "Assertion failed: %s (%s: %s: %d)\n", expr, file, func, line);
    std::terminate();
}

void throwAssertFailed(const char *expr, const char *file, int line, const char *func) {
    if (opt->debug.debug_level >= 1) {
        throwCantPack("corrupted file; details: %s (%s: %s: %d)", expr, file, func, line);
    } else {
        throwCantPack("corrupted file; try '--debug' for more details");
    }
}

const char *prettyExceptionName(const char *n) noexcept {
    if (n == nullptr)
        return "(null)";
    while (*n) {
        if (*n >= '0' && *n <= '9') // Linux ABI
            n++;
        else if (*n == ' ')
            n++;
        else if (strncmp(n, "class ", 6) == 0) // Visual C++ (MSVC)
            n += 6;
        else
            break;
    }
    return n;
}

/* vim:set ts=4 sw=4 et: */
