Various additions

Scripted INI functions.
New function mapkeys() that generates a list of keys in the map.
Added missing registration of script function round().
Fixed initialization issues for ScriptManager, causing crashes when using commands in the hook 'load'.
Added parameter "---" to /echo for printing to status window.
master
Tomatix 5 years ago
parent f636b99e72
commit 6f149b848c
  1. 9
      ICommand/Internal/echo.cpp
  2. 2
      IdealIRC/IdealIRC.cpp
  3. 2
      Script/Builtin/Builtin.cpp
  4. 23
      Script/Builtin/Error.cpp
  5. 4
      Script/Builtin/Error.h
  6. 4
      Script/Builtin/GeneralUtils.cpp
  7. 2
      Script/Builtin/ListUtils.cpp
  8. 2
      Script/Builtin/ListUtils.h
  9. 20
      Script/Builtin/MapUtils.cpp
  10. 1
      Script/Builtin/MapUtils.h
  11. 47
      Script/Manager.cpp
  12. 1
      Script/Manager.h
  13. 15
      ScriptFunctions/RegisterScriptFunctions.cpp
  14. 323
      ScriptFunctions/ScriptGeneralUtils.cpp
  15. 9
      ScriptFunctions/ScriptGeneralUtils.h

@ -83,13 +83,16 @@ void ICommandPriv::cmd_echo(const std::string& arg1, const std::string& arg2, co
if (target.empty()) {
auto* win = status.getActiveWindow();
win->print(color, text.c_str());
win->print(color, QString::fromStdString(text));
}
else if (target == "-") {
status.print(color, QString::fromStdString(text));
}
else {
auto* win = MdiManager::instance().findWindow(&status, target.c_str());
auto* win = MdiManager::instance().findWindow(&status, QString::fromStdString(target));
if (!win)
win = status.getActiveWindow();
win->print(color, text.c_str());
win->print(color, QString::fromStdString(text));
}
}

@ -70,7 +70,6 @@ IdealIRC::IdealIRC(QWidget* parent)
IWin* statusw = mdiManager->createSubwindow(nullptr, "Status", IWin::Type::Status);
statusw->refreshWindowTitle();
ScriptManager::init(mdiManager, this);
QTimer::singleShot(10, [this]{ startup(); });
}
@ -187,6 +186,7 @@ void IdealIRC::startup()
if (conf.geometry("Maximized") == "1")
showMaximized();
ScriptManager::init(mdiManager, this);
scriptEvent("start");
}

@ -25,6 +25,7 @@ void registerBuiltinFunctions()
Script::registerUniversalFunction("cbrt", &Builtin::Mathematics::cbrt);
Script::registerUniversalFunction("floor", &Builtin::Mathematics::floor);
Script::registerUniversalFunction("ceil", &Builtin::Mathematics::ceil);
Script::registerUniversalFunction("round", &Builtin::Mathematics::round);
Script::registerUniversalFunction("abs", &Builtin::Mathematics::abs);
Script::registerUniversalFunction("clamp", &Builtin::Mathematics::clamp);
Script::registerUniversalFunction("clamp01", &Builtin::Mathematics::clamp01);
@ -75,6 +76,7 @@ void registerBuiltinFunctions()
Script::registerUniversalFunction("maplen", &Builtin::MapUtils::maplen);
Script::registerUniversalFunction("mapremove", &Builtin::MapUtils::mapremove);
Script::registerUniversalFunction("mapdump", &Builtin::MapUtils::mapdump);
Script::registerUniversalFunction("mapkeys", &Builtin::MapUtils::mapkeys);
/*
* List utilities

@ -40,6 +40,14 @@ void throwInsufficientParameters(const std::string& fname, size_t given, size_t
throw ScriptException(ss.str());
}
void throwInsufficientParameters(const std::string& fname, size_t given, size_t requiredLow, size_t requiredHigh)
{
std::stringstream ss;
ss << fname << ": Insufficient parameter count. "
<< requiredLow << " to " << requiredHigh << " parameters required, " << given << " were given";
throw ScriptException(ss.str());
}
void throwNotAString(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a string");
@ -50,6 +58,11 @@ void throwNotANumber(const std::string& fname, int paramPos)
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a number");
}
void throwNotAStringOrWholeNumber(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a string or a whole number");
}
void throwNotAWholeNumber(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a whole number");
@ -60,6 +73,11 @@ void throwNotADialogHandle(const std::string& fname, int paramPos)
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a dialog handle (a whole number)");
}
void throwNotAnIniHandle(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be an INI handle (a whole number)");
}
void throwNotAMap(const std::string& fname, int paramPos)
{
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a map");
@ -91,4 +109,9 @@ void throwNumberMustBePositive(const std::string& fname, int paramPos)
throw ScriptException(fname + ": " + ith(paramPos) + " parameter must be a positive number");
}
void throwUnrecognizedIniHandle(const std::string& fname, int handle)
{
throw ScriptException("Unrecognized INI file handle " + std::to_string(handle));
}
}

@ -5,16 +5,20 @@
namespace Builtin::Error {
[[noreturn]] void throwInsufficientParameters(const std::string& fname, size_t given, size_t required);
[[noreturn]] void throwInsufficientParameters(const std::string& fname, size_t given, size_t requiredLow, size_t requiredHigh);
[[noreturn]] void throwNotAString(const std::string& fname, int paramPos);
[[noreturn]] void throwNotANumber(const std::string& fname, int paramPos);
[[noreturn]] void throwNotAStringOrWholeNumber(const std::string& fname, int paramPos);
[[noreturn]] void throwNotAWholeNumber(const std::string& fname, int paramPos);
[[noreturn]] void throwNotADialogHandle(const std::string& fname, int paramPos);
[[noreturn]] void throwNotAnIniHandle(const std::string& fname, int paramPos);
[[noreturn]] void throwNotAMap(const std::string& fname, int paramPos);
[[noreturn]] void throwNotAList(const std::string& fname, int paramPos);
[[noreturn]] void throwIndexOutOfRange(const std::string& fname, int paramPos);
[[noreturn]] void throwNotAReference(const std::string& fname, int paramPos);
[[noreturn]] void throwNegativeInputUndefined(const std::string& fname);
[[noreturn]] void throwNumberMustBePositive(const std::string& fname, int paramPos);
[[noreturn]] void throwUnrecognizedIniHandle(const std::string& fname, int handle);
}
#endif // ERROR_H

@ -40,7 +40,7 @@ ValueHolder toint(Script&, std::vector<ValueHolder>& args)
return 0;
if (val == "true")
return 1;
return std::atoi(val.c_str());
return std::stoi(val);
}
}
}
@ -78,7 +78,7 @@ ValueHolder toreal(Script&, std::vector<ValueHolder>& args)
auto dotpos = val.find('.');
if (dotpos != val.npos)
val.replace(dotpos, 1, decimalSeparator());
return std::stod(val.c_str());
return std::stod(val);
}
}
}

@ -4,6 +4,8 @@
#include "Script/ScriptException.h"
#include <stdexcept>
const std::string LIST_END_MAGIC = "_list_end_";
using namespace Builtin::Error;
namespace Builtin::ListUtils {

@ -6,7 +6,7 @@
class Script;
const std::string LIST_END_MAGIC = "_list_end_";
extern const std::string LIST_END_MAGIC;
namespace Builtin::ListUtils {
ValueHolder list(Script&, std::vector<ValueHolder>& args);

@ -72,4 +72,24 @@ ValueHolder mapdump(Script&, std::vector<ValueHolder>& args)
return {};
}
ValueHolder mapkeys(Script&, std::vector<ValueHolder>& args)
{
if (args.size() != 1)
throwInsufficientParameters("mapkeys", args.size(), 1);
if (args[0].getType() != ValueHolder::Type::Map)
throwNotAMap("mapkeys", 1);
ValueArray& arr = ValueExtract(args[0]).toMap();
ValueArray ret;
ret.reserve(arr.size());
int c = 0;
for (const auto& [k,v] : arr) {
ret.emplace(std::to_string(c), new ValueHolder(k));
++c;
}
ret.emplace("_list_end_", new ValueHolder(c));
return ret;
}
}

@ -11,6 +11,7 @@ ValueHolder map(Script&, std::vector<ValueHolder>& args);
ValueHolder maplen(Script&, std::vector<ValueHolder>& args);
ValueHolder mapremove(Script&, std::vector<ValueHolder>& args);
ValueHolder mapdump(Script&, std::vector<ValueHolder>& args);
ValueHolder mapkeys(Script&, std::vector<ValueHolder>& args);
}
#endif // MAPUTILS_H

@ -21,27 +21,6 @@ ScriptManager::ScriptManager(MdiManager* mdiManager, QWidget *parent) :
{
ui->setupUi(this);
registerScriptFunctions();
auto& cfg = ConfigMgr::instance();
for (const QString& path : cfg.scripts()) {
bool ok = loadFromFile(path);
if (!ok) {
auto btn = QMessageBox::warning(this, tr("Load script"), tr("The script located at:\n%1\nFailed to load. Keep the entry in the manager?").arg(path),
QMessageBox::Yes | QMessageBox::No);
if (btn == QMessageBox::No) {
m_scripts.pop_back();
continue;
}
else {
m_listModel.addPath(path);
m_listModel.markWarning(path);
}
}
else {
m_listModel.addPath(path);
}
}
ui->listView->setModel(&m_listModel);
}
@ -77,6 +56,8 @@ void ScriptManager::event(const std::string& eventName)
void ScriptManager::init(MdiManager* mdiManager, QWidget* parent)
{
Instance.emplace(new ScriptManager(mdiManager, parent));
ScriptManager::setContext(mdiManager->currentStatus());
(*Instance)->loadAllScripts();
}
void ScriptManager::setContext(IWinStatus* context)
@ -103,6 +84,30 @@ ScriptManager::~ScriptManager()
delete ui;
}
void ScriptManager::loadAllScripts()
{
auto& cfg = ConfigMgr::instance();
for (const QString& path : cfg.scripts()) {
bool ok = loadFromFile(path);
if (!ok) {
auto btn = QMessageBox::warning(this, tr("Load script"), tr("The script located at:\n%1\nFailed to load. Keep the entry in the manager?").arg(path),
QMessageBox::Yes | QMessageBox::No);
if (btn == QMessageBox::No) {
m_scripts.pop_back();
continue;
}
else {
m_listModel.addPath(path);
m_listModel.markWarning(path);
}
}
else {
m_listModel.addPath(path);
}
}
}
void ScriptManager::contextMenuPopup(ScriptMenuType type, const QPoint& pos, SymbolScope& symbols)
{
QString menuName;

@ -34,6 +34,7 @@ public:
static ScriptManager* instance();
static IWinStatus* getContext();
~ScriptManager();
void loadAllScripts();
void contextMenuPopup(ScriptMenuType type, const QPoint& pos, SymbolScope& symbols);
bool runCommand(const QString& command);
bool loadFromFile(const QString& path);

@ -11,16 +11,25 @@ void registerScriptFunctions()
*/
Script::registerUniversalFunction("input", &ScriptFunctions::input);
Script::registerUniversalFunction("showmessage", &ScriptFunctions::showmessage);
Script::registerUniversalFunction("context", &ScriptFunctions::context);
Script::registerUniversalFunction("subwintype", &ScriptFunctions::subwintype);
Script::registerUniversalFunction("subwinname", &ScriptFunctions::subwinname);
Script::registerUniversalFunction("unixts", &ScriptFunctions::unixts);
Script::registerUniversalFunction("unixnow", &ScriptFunctions::unixnow);
Script::registerUniversalFunction("now", &ScriptFunctions::now);
Script::registerUniversalFunction("subwintype", &ScriptFunctions::subwintype);
Script::registerUniversalFunction("subwinname", &ScriptFunctions::subwinname);
Script::registerUniversalFunction("openini", &ScriptFunctions::openini);
Script::registerUniversalFunction("closeini", &ScriptFunctions::closeini);
Script::registerUniversalFunction("readini", &ScriptFunctions::readini);
Script::registerUniversalFunction("readiniitem", &ScriptFunctions::readiniitem);
Script::registerUniversalFunction("readinisection", &ScriptFunctions::readinisection);
Script::registerUniversalFunction("countini", &ScriptFunctions::countini);
Script::registerUniversalFunction("writeini", &ScriptFunctions::writeini);
Script::registerUniversalFunction("sreadini", &ScriptFunctions::sreadini);
Script::registerUniversalFunction("swriteini", &ScriptFunctions::swriteini);
/*
* IRC utilities
*/
Script::registerUniversalFunction("userhost", &ScriptFunctions::userhost);
Script::registerUniversalFunction("isupport", &ScriptFunctions::isupport);
Script::registerUniversalFunction("context", &ScriptFunctions::context);
}

@ -6,11 +6,59 @@
#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;
@ -211,4 +259,279 @@ ValueHolder subwinname(Script&, std::vector<ValueHolder>& args)
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 {};
}
}

@ -15,6 +15,15 @@ ValueHolder unixnow(Script& , std::vector<ValueHolder>& args);
ValueHolder now(Script& , std::vector<ValueHolder>& args);
ValueHolder subwintype(Script&, std::vector<ValueHolder>& args);
ValueHolder subwinname(Script&, std::vector<ValueHolder>& args);
ValueHolder openini(Script&, std::vector<ValueHolder>& args);
ValueHolder closeini(Script&, std::vector<ValueHolder>& args);
ValueHolder readini(Script&, std::vector<ValueHolder>& args);
ValueHolder readiniitem(Script&, std::vector<ValueHolder>& args);
ValueHolder readinisection(Script&, std::vector<ValueHolder>& args);
ValueHolder countini(Script&, std::vector<ValueHolder>& args);
ValueHolder writeini(Script&, std::vector<ValueHolder>& args);
ValueHolder sreadini(Script&, std::vector<ValueHolder>& args);
ValueHolder swriteini(Script&, std::vector<ValueHolder>& args);
}
#endif // SCRIPTGENERALUTILS_H

Loading…
Cancel
Save