The complete source code of IdealIRC http://www.idealirc.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
idealirc/ScriptFunctions/ScriptGeneralUtils.cpp

537 lines
15 KiB

#include "ScriptGeneralUtils.h"
#include "Script/Builtin/Error.h"
#include "Script/ValueExtract.h"
#include "Script/ScriptException.h"
#include "Script/Manager.h"
#include "IRC.h"
#include "IWin/IWinStatus.h"
#include "MdiManager.h"
#include "IniFile.h"
#include "config.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QDateTime>
#include <random>
#include <unordered_map>
#include <fstream>
#include <fmt/format.h>
namespace {
std::unordered_map<int,IniFile> iniFiles;
int iniFileHandleCount{ 1 };
IniFile& getIniHandle(const std::string& callingFunctionName, int handle)
{
try {
return iniFiles.at(handle);
}
catch (const std::out_of_range&) {
Builtin::Error::throwUnrecognizedIniHandle(callingFunctionName, handle);
}
}
bool createFileIfNotExists(const std::string& path)
{
/*
* Opening a file in ostream will create it if it doesn't exist.
* Tested with GNU stdlib (Linux) and Microsoft's stdlib (Windows).
*/
std::ofstream fs(path, std::fstream::app);
auto ok = fs.is_open();
fs.close();
return ok;
}
std::string setRelativePath(const std::string& path)
{
if (path.empty())
return std::string{};
#if defined(Q_OS_LINUX)
if (path[0] != '/') {
#elif defined(Q_OS_WIN32) | defined(Q_OS_WIN64)
if (path[1] != ':') {
#else
#error Update ScriptGeneralUtils.cpp setRelativePath to work with the target.
#endif
return fmt::format("{}/{}", LOCAL_PATH.toStdString(), path);
}
else {
return path;
}
}
}
using namespace Builtin::Error;
namespace ScriptFunctions {
ValueHolder input(Script&, std::vector<ValueHolder>& args)
{
if (args.size() < 2 || args.size() > 3)
throwInsufficientParameters("input", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("input", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("input", 2);
QString defaultText;
if (args.size() == 3) {
if (args[2].getType() != ValueHolder::Type::String)
throwNotAString("input", 2);
defaultText = ValueExtract(args[2]).toString().c_str();
}
QString title(ValueExtract(args[0]).toString().c_str());
QString message(ValueExtract(args[1]).toString().c_str());
bool ok = false;
QString input = QInputDialog::getText(nullptr, title, message, QLineEdit::Normal, defaultText, &ok);
if (!ok)
return ValueHolder();
return input.toStdString();
}
ValueHolder showmessage(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 4)
throwInsufficientParameters("showmessage", args.size(), 4);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("showmessage", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("showmessage", 2);
if (args[2].getType() != ValueHolder::Type::String)
throwNotAString("showmessage", 3);
if (args[3].getType() != ValueHolder::Type::String)
throwNotAString("showmessage", 4);
QString title(ValueExtract(args[0]).toString().c_str());
QString message(ValueExtract(args[1]).toString().c_str());
QString type(ValueExtract(args[2]).toString().c_str());
QString buttons(ValueExtract(args[3]).toString().c_str());
QMessageBox::StandardButtons dlgbtns = QMessageBox::NoButton;
for (QChar c : buttons) {
switch (c.toLatin1()) {
case 'y':
dlgbtns |= QMessageBox::Yes;
break;
case 'n':
dlgbtns |= QMessageBox::No;
break;
case 'o':
dlgbtns |= QMessageBox::Ok;
break;
case 'c':
dlgbtns |= QMessageBox::Cancel;
break;
case 'a':
dlgbtns |= QMessageBox::Abort;
break;
case 'r':
dlgbtns |= QMessageBox::Retry;
break;
case 'i':
dlgbtns |= QMessageBox::Ignore;
break;
default:
{
std::string cs;
cs += c.toLatin1();
throw ScriptException("showmessage: Unknown button type: " + cs);
}
}
}
QMessageBox::StandardButtons btnPressed;
if (type == "question")
btnPressed = QMessageBox::question(nullptr, title, message, dlgbtns);
else if (type == "info")
btnPressed = QMessageBox::information(nullptr, title, message, dlgbtns);
else if (type == "warn")
btnPressed = QMessageBox::warning(nullptr, title, message, dlgbtns);
else if (type == "crit")
btnPressed = QMessageBox::critical(nullptr, title, message, dlgbtns);
else
throw ScriptException("showmessage: Unknown message box type: " + type.toStdString());
switch (btnPressed) {
case QMessageBox::Yes:
return std::string("y");
case QMessageBox::No:
return std::string("n");
case QMessageBox::Abort:
return std::string("a");
case QMessageBox::Retry:
return std::string("r");
case QMessageBox::Ignore:
return std::string("i");
case QMessageBox::Ok:
return std::string("o");
case QMessageBox::Cancel: [[fallthrough]];
default:
return std::string("c");
}
}
ValueHolder context(Script&, std::vector<ValueHolder>& args)
{
if (!args.empty())
throwInsufficientParameters("context", args.size(), 0);
const auto& connection = ScriptManager::getContext()->getConnection();
ValueArray ret;
ret.emplace("online", new ValueHolder{ connection.isOnline() });
ret.emplace("ssl", new ValueHolder{ connection.isSSL() });
ret.emplace("host", new ValueHolder{ connection.getHostname() });
ret.emplace("port", new ValueHolder{ connection.getPort() });
ret.emplace("nickname", new ValueHolder{ connection.getNickname() });
return ValueHolder(ret);
}
ValueHolder unixts(Script&, std::vector<ValueHolder>& args)
{
if (args.empty())
throwInsufficientParameters("unixts", args.size(), 1);
qint64 ts = ValueExtract(args[0]).toInt();
auto datetime = QDateTime::fromSecsSinceEpoch(ts);
if (args.size() > 1) {
QString format = ValueExtract(args[1]).toString().c_str();
return datetime.toString(format).toStdString();
}
else
return datetime.toString(Qt::ISODate).toStdString();
}
ValueHolder unixnow(Script&, std::vector<ValueHolder>& /*args*/)
{
return static_cast<int>( QDateTime::currentSecsSinceEpoch() );
}
ValueHolder now(Script&, std::vector<ValueHolder>& args)
{
if (args.empty())
return QDateTime::currentDateTime().toString(Qt::ISODate).toStdString();
else {
const auto format = QString::fromStdString( ValueExtract(args[0]).toString() );
const auto dt = QDateTime::currentDateTime();
const auto dtStr = dt.toString(format);
return dtStr.toStdString();
}
}
ValueHolder subwintype(Script&, std::vector<ValueHolder>& args)
{
if (!args.empty())
throwInsufficientParameters("subwintype", args.size(), 0);
const auto& mdiman = MdiManager::instance();
const auto* cwin = mdiman.currentWindow();
IWin::Type wintype = IWin::Type::Undefined;
if (cwin)
wintype = cwin->getType();
return IWin::TypeString.at(wintype).toStdString();
}
ValueHolder subwinname(Script&, std::vector<ValueHolder>& args)
{
if (!args.empty())
throwInsufficientParameters("subwinname", args.size(), 0);
const auto& mdiman = MdiManager::instance();
const auto* cwin = mdiman.currentWindow();
if (!cwin)
return ValueHolder{}; // void
return cwin->getButtonText().toStdString();
}
ValueHolder openini(Script&, std::vector<ValueHolder>& args)
{
if (args.empty())
throwInsufficientParameters("openini", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("openini", 1);
const std::string fname = setRelativePath( ValueExtract(args[0]).toString() );
if (!createFileIfNotExists(fname)) {
throw ScriptException("openini: Unable to open INI file: " + fname);
}
++iniFileHandleCount;
iniFiles.emplace(iniFileHandleCount, IniFile(fname));
const auto& ini = iniFiles.at(iniFileHandleCount);
if (ini.error() != IniFile::Error::NoError) {
iniFiles.erase(iniFileHandleCount);
throw ScriptException("openini: Unable to open INI file: " + fname);
}
else
return iniFileHandleCount;
}
ValueHolder closeini(Script&, std::vector<ValueHolder>& args)
{
if (args.empty())
throwInsufficientParameters("closeini", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotAnIniHandle("closeini", 1);
int hndl = ValueExtract(args[0]).toInt();
auto& ini = getIniHandle("readiniitem", ValueExtract(args[0]).toInt());
ini.flush();
iniFiles.erase(hndl);
return {};
}
ValueHolder readini(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 3 && args.size() != 4)
throwInsufficientParameters("readini", args.size(), 3, 4);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotAnIniHandle("readini", 1);
int sectN = -1;
int itemN = -1;
std::string sectS;
std::string itemS;
std::string defaultValue;
/* Section parameter */
if (args[1].getType() == ValueHolder::Type::String)
sectS = ValueExtract(args[1]).toString();
else if (args[1].getType() == ValueHolder::Type::Integer)
sectN = ValueExtract(args[1]).toInt();
else
throwNotAStringOrWholeNumber("readini", 2);
/* Item parameter */
if (args[2].getType() == ValueHolder::Type::String)
itemS = ValueExtract(args[2]).toString();
else if (args[2].getType() == ValueHolder::Type::Integer)
itemN = ValueExtract(args[2]).toInt();
else
throwNotAStringOrWholeNumber("readini", 2);
/* Default value parameter */
if (args.size() == 4)
defaultValue = args[3].toStdString();
auto& ini = getIniHandle("readini", ValueExtract(args[0]).toInt());
/* Section and Item by key */
if (sectN == -1 && itemN == -1) {
return ini.read(sectS, itemS, defaultValue);
}
/* Section by key, item by position */
else if (sectN == -1 && itemN > -1) {
return ini.read(sectS, itemN, defaultValue);
}
/* Section by position, item by key */
else if (sectN > -1 && itemN == -1) {
return ini.read(sectN, itemS, defaultValue);
}
/* Section and item by position */
else {
return ini.read(sectN, itemN, defaultValue);
}
}
ValueHolder readiniitem(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 3)
throwInsufficientParameters("readiniitem", args.size(), 3);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotAnIniHandle("readiniitem", 1);
int sectN = -1;
int itemN = -1;
std::string sectS;
/* Section parameter */
if (args[1].getType() == ValueHolder::Type::String)
sectS = ValueExtract(args[1]).toString();
else if (args[1].getType() == ValueHolder::Type::Integer)
sectN = ValueExtract(args[1]).toInt();
else
throwNotAStringOrWholeNumber("readiniitem", 2);
/* Item parameter */
if (args[2].getType() == ValueHolder::Type::Integer)
itemN = ValueExtract(args[2]).toInt();
else
throwNotAWholeNumber("readiniitem", 2);
auto& ini = getIniHandle("readiniitem", ValueExtract(args[0]).toInt());
/* Section by key, item by position */
if (sectN == -1 && itemN > -1) {
return ini.item(sectS, itemN);
}
/* Section by position, item by position */
else {
return ini.item(sectN, itemN);
}
}
ValueHolder readinisection(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 2)
throwInsufficientParameters("readinisection", args.size(), 2);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotAnIniHandle("readinisection", 1);
int sectN = -1;
/* Section parameter */
if (args[1].getType() == ValueHolder::Type::Integer)
sectN = ValueExtract(args[1]).toInt();
else
throwNotAWholeNumber("readinisection", 2);
auto& ini = getIniHandle("readinisection", ValueExtract(args[0]).toInt());
return ini.section(sectN);
}
ValueHolder countini(Script&, std::vector<ValueHolder>& args)
{
/* Return amount of sections */
if (args.size() == 1) {
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotAnIniHandle("countini", 1);
auto& ini = getIniHandle("countini", ValueExtract(args[0]).toInt());
return static_cast<int>( ini.count() );
}
/* Return amount of items in section */
else if (args.size() == 2) {
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotAnIniHandle("countini", 1);
auto& ini = getIniHandle("countini", ValueExtract(args[0]).toInt());
const auto& section = ValueExtract(args[1]).toString();
return static_cast<int>( ini.count(section) );
}
else {
throwInsufficientParameters("countini", args.size(), 0, 1);
}
}
ValueHolder writeini(Script&, std::vector<ValueHolder>& args)
{
if (args.size() < 4)
throwInsufficientParameters("writeini", args.size(), 4);
if (args[0].getType() != ValueHolder::Type::Integer)
throwNotAnIniHandle("writeini", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("writeini", 2);
if (args[2].getType() != ValueHolder::Type::String)
throwNotAString("writeini", 3);
const auto section = ValueExtract(args[1]).toString();
const auto item = ValueExtract(args[2]).toString();
const auto value = args[3].toStdString();
auto& ini = getIniHandle("writeini", ValueExtract(args[0]).toInt());
ini.write(section, item, value);
return {};
}
ValueHolder sreadini(Script&, std::vector<ValueHolder>& args)
{
if (args.size() < 3)
throwInsufficientParameters("sreadini", args.size(), 0);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("sreadini", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("sreadini", 2);
if (args[2].getType() != ValueHolder::Type::String)
throwNotAString("sreadini", 3);
const auto filename = setRelativePath( ValueExtract(args[0]).toString() );
const auto section = ValueExtract(args[1]).toString();
const auto item = ValueExtract(args[2]).toString();
IniFile f(filename);
if (f.error() != IniFile::Error::NoError) {
// Could be that the file didn't exist.
// For "simple readini" we will return an empty value as if it isn't an error.
return std::string{};
}
return f.read(section, item);
}
ValueHolder swriteini(Script&, std::vector<ValueHolder>& args)
{
if (args.size() < 4)
throwInsufficientParameters("swriteini", args.size(), 0);
if (args[0].getType() != ValueHolder::Type::String)
throwNotAString("swriteini", 1);
if (args[1].getType() != ValueHolder::Type::String)
throwNotAString("swriteini", 2);
if (args[2].getType() != ValueHolder::Type::String)
throwNotAString("swriteini", 3);
const auto filename = setRelativePath( ValueExtract(args[0]).toString() );
const auto section = ValueExtract(args[1]).toString();
const auto item = ValueExtract(args[2]).toString();
const auto value = args[3].toStdString();
if (!createFileIfNotExists(filename)) {
throw ScriptException("swriteini: Unable to open INI file: " + filename);
}
IniFile f(filename);
if (f.error() != IniFile::Error::NoError) {
throw ScriptException("swriteini: Unable to open INI file: " + filename);
}
f.write(section, item, value);
f.flush();
return {};
}
}