File indexing completed on 2024-05-12 05:45:31

0001 /*
0002     Copyright Hannah von Reth <vonreth@kde.org>
0003 
0004     Redistribution and use in source and binary forms, with or without
0005     modification, are permitted provided that the following conditions
0006     are met:
0007     1. Redistributions of source code must retain the above copyright
0008        notice, this list of conditions and the following disclaimer.
0009     2. Redistributions in binary form must reproduce the above copyright
0010        notice, this list of conditions and the following disclaimer in the
0011        documentation and/or other materials provided with the distribution.
0012 
0013     THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
0014     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0015     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0016     ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
0017     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0018     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0019     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0020     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0021     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0022     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0023     SUCH DAMAGE.
0024 */
0025 
0026 #include "kshim.h"
0027 #include "kshimdata.h"
0028 
0029 #include <algorithm>
0030 #include <iostream>
0031 #include <fstream>
0032 #include <sstream>
0033 #include <cstring>
0034 
0035 #ifdef _WIN32
0036 #include <windows.h>
0037 #endif
0038 
0039 bool KLog::s_loggingEnabled = !KShimLib::getenv(KSTRING("KSHIM_LOG")).empty();
0040 bool KLog::s_stdLoggingEnabled = !KShimLib::getenv(KSTRING("KSHIM_LOG_STD")).empty();
0041 
0042 KLog::KLog(KLog::Type t) : m_type(t), m_stream(new KShimLib::stringstream) {}
0043 
0044 KLog::KLog(const KLog &other) : m_type(other.m_type), m_stream(other.m_stream) {}
0045 
0046 KLog::~KLog()
0047 {
0048     if (m_stream.use_count() == 1) {
0049         *this << "\n";
0050         const auto line = m_stream->str();
0051         if (s_stdLoggingEnabled) {
0052 #ifdef _WIN32
0053             std::wcerr << line;
0054 #else
0055             std::cerr << line;
0056 #endif
0057         } else {
0058             switch (m_type) {
0059             case KLog::Type::Error:
0060             case KLog::Type::Fatal:
0061 #ifdef _WIN32
0062                 std::wcerr << line << std::endl;;
0063 #else
0064                 std::cerr << line << std::endl;
0065 #endif
0066                 [[fallthrough]];
0067             case KLog::Type::Debug: {
0068                 if (doLog()) {
0069                     static auto _log = [] {
0070                         auto home = KShimLib::getenv(KSTRING("HOME"));
0071                         if (home.empty()) {
0072                             home = KShimLib::getenv(KSTRING("USERPROFILE"));
0073                         }
0074                         const auto logPath = std::filesystem::path(home) / KSTRING(".kshim.log");
0075 #ifdef _WIN32
0076 #if !defined(__MINGW32__)
0077                         const auto &_name = logPath;
0078 #else
0079                         const auto _name = logPath.string();
0080 #endif
0081                         auto out = std::wofstream(_name, std::ios::app);
0082 #else
0083                         auto out = std::ofstream(logPath, std::ios::app);
0084 #endif
0085                         if (!out.is_open()) {
0086                             std::cerr << "KShim: Failed to open log \"" << logPath.string() << "\" "
0087                                       << strerror(errno) << std::endl;
0088                         }
0089                         out << "----------------------------\n";
0090                         return out;
0091                     }();
0092 #ifdef _WIN32
0093                     OutputDebugStringW(line.data());
0094 #endif
0095                     _log << line;
0096                     _log.flush();
0097                 }
0098             }
0099             }
0100         }
0101     }
0102     if (m_type == Type::Fatal) {
0103         exit(-1);
0104     }
0105 }
0106 
0107 KLog &KLog::log()
0108 {
0109     *this << "KShimgen " << KShimLib::version << ": ";
0110     return *this;
0111 }
0112 
0113 KLog::Type KLog::type() const
0114 {
0115     return m_type;
0116 }
0117 
0118 bool KLog::doLog() const
0119 {
0120     return loggingEnabled() || m_type != KLog::Type::Debug;
0121 }
0122 
0123 bool KLog::getStdLoggingEnabled()
0124 {
0125     return s_stdLoggingEnabled;
0126 }
0127 
0128 void KLog::setStdLoggingEnabled(bool value)
0129 {
0130     s_stdLoggingEnabled = value;
0131 }
0132 
0133 bool KLog::loggingEnabled()
0134 {
0135     return s_loggingEnabled;
0136 }
0137 
0138 void KLog::setLoggingEnabled(bool loggingEnabled)
0139 {
0140     s_loggingEnabled = loggingEnabled;
0141 }
0142 
0143 KLog &operator<<(KLog &log, const std::filesystem::path &t)
0144 {
0145     return log << t.native();
0146 }
0147 
0148 KLog &operator<<(KLog &log, const std::string &t)
0149 {
0150     log << t.data();
0151     return log;
0152 }
0153 
0154 KShimLib::string KShimLib::quoteArgs(const std::vector<KShimLib::string_view> &args)
0155 {
0156     KShimLib::stringstream command;
0157     for (const auto &arg : args) {
0158         command << " " << quote(arg);
0159     }
0160     return command.str();
0161 }
0162 
0163 KShimLib::string KShimLib::quote(const KShimLib::string_view &arg)
0164 {
0165     // based on https://github.com/python/cpython/blob/master/Lib/subprocess.py#L493
0166     if (arg.empty()) {
0167         return KSTRING("\"\"");
0168     }
0169     bool needsQuote = false;
0170     for (const auto c : arg) {
0171         needsQuote = c == ' ' || c == '\t';
0172         if (needsQuote) {
0173             break;
0174         }
0175     }
0176     KShimLib::stringstream out;
0177     KShimLib::stringstream backslash;
0178     if (needsQuote) {
0179         out << '"';
0180     }
0181     for (const auto c : arg) {
0182         if (c == '\\') {
0183             backslash << c;
0184         } else if (c == '"') {
0185             const auto bs = backslash.str();
0186             out << bs << bs << "\\\"";
0187             backslash.str(KShimLib::string());
0188         } else {
0189             const auto bs = backslash.str();
0190             if (!bs.empty()) {
0191                 out << bs;
0192                 backslash.str(KShimLib::string());
0193             }
0194             out << c;
0195         }
0196     }
0197     const auto bs = backslash.str();
0198     if (!bs.empty()) {
0199         out << bs;
0200     }
0201     if (needsQuote) {
0202         out << bs;
0203         out << '"';
0204     }
0205     return out.str();
0206 }