summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/language.h13
-rw-r--r--include/modules.h6
-rw-r--r--include/services.h25
-rw-r--r--modules/commands/bs_badwords.cpp100
-rw-r--r--modules/commands/bs_botlist.cpp41
-rw-r--r--modules/commands/cs_access.cpp397
-rw-r--r--modules/commands/cs_akick.cpp295
-rw-r--r--modules/commands/cs_entrymsg.cpp33
-rw-r--r--modules/commands/cs_flags.cpp69
-rw-r--r--modules/commands/cs_info.cpp41
-rw-r--r--modules/commands/cs_list.cpp25
-rw-r--r--modules/commands/cs_log.cpp22
-rw-r--r--modules/commands/cs_mode.cpp22
-rw-r--r--modules/commands/cs_set_misc.cpp4
-rw-r--r--modules/commands/cs_xop.cpp97
-rw-r--r--modules/commands/hs_list.cpp40
-rw-r--r--modules/commands/hs_request.cpp33
-rw-r--r--modules/commands/ms_ignore.cpp17
-rw-r--r--modules/commands/ms_list.cpp92
-rw-r--r--modules/commands/ns_ajoin.cpp20
-rw-r--r--modules/commands/ns_alist.cpp62
-rw-r--r--modules/commands/ns_cert.cpp30
-rw-r--r--modules/commands/ns_group.cpp17
-rw-r--r--modules/commands/ns_info.cpp78
-rw-r--r--modules/commands/ns_list.cpp33
-rw-r--r--modules/commands/ns_set_misc.cpp4
-rw-r--r--modules/commands/os_akill.cpp198
-rw-r--r--modules/commands/os_config.cpp16
-rw-r--r--modules/commands/os_forbid.cpp21
-rw-r--r--modules/commands/os_ignore.cpp23
-rw-r--r--modules/commands/os_list.cpp56
-rw-r--r--modules/commands/os_news.cpp24
-rw-r--r--modules/commands/os_session.cpp200
-rw-r--r--modules/commands/os_sxline.cpp205
-rw-r--r--src/misc.cpp115
35 files changed, 1309 insertions, 1165 deletions
diff --git a/include/language.h b/include/language.h
index 91bc462cb..9af139ed9 100644
--- a/include/language.h
+++ b/include/language.h
@@ -30,7 +30,6 @@
#define NO_REASON _("No reason")
#define UNKNOWN _("<unknown>")
#define NO_EXPIRE _("does not expire")
-#define END_OF_ANY_LIST _("End of \002%s\002 list.")
#define LIST_INCORRECT_RANGE _("Incorrect range specified. The correct syntax is \002#\037from\037-\037to\037\002.")
#define UNKNOWN_OPTION _("Unknown option \002%s\002.\n" \
"Type %s%s HELP %s for more information.")
@@ -47,7 +46,6 @@
#define NICK_SET_UNKNOWN_OPTION _("Unknown SET option \002%s%s\002.")
#define NICK_SET_DISPLAY_CHANGED _("The new display is now \002%s\002.")
#define NICK_LIST_SYNTAX _("LIST \037pattern\037")
-#define LIST_HEADER _("List of entries matching \002%s\002:")
#define NICK_RECOVERED _("User claiming your nick has been killed.\n" \
"\002%s%s RELEASE %s\002 to get it back before %s timeout.")
#define NICK_REQUESTED _("This nick has already been requested, please check your e-mail address for the pass code")
@@ -62,16 +60,8 @@
#define CHAN_SETTING_UNSET _("%s for %s unset.")
#define CHAN_SET_MLOCK_DEPRECATED _("MLOCK is deprecated. Use \002%s%s HELP MODE\002 instead.")
#define CHAN_ACCESS_LEVEL_RANGE _("Access level must be between %d and %d inclusive.")
-#define CHAN_ACCESS_LIST_HEADER _("Access list for %s:\n" \
- " Num Lev Mask")
-#define CHAN_ACCESS_VIEW_AXS_FORMAT _(" %3d %4d %s\n" \
- " by %s on %s, last seen %s")
-#define CHAN_AKICK_VIEW_FORMAT _("%3d %s (by %s on %s)\n" \
- " %s")
#define CHAN_INFO_HEADER _("Information for channel \002%s\002:")
#define CHAN_EXCEPTED _("\002%s\002 matches an except on %s and cannot be banned until the except have been removed.")
-#define CHAN_LIST_ENTRY _("%3d %s\n" \
- " Added by %s on %s")
#define MEMO_NEW_X_MEMO_ARRIVED _("There is a new memo on channel %s.\n" \
"Type \002%s%s READ %s %d\002 to read it.")
#define MEMO_NEW_MEMO_ARRIVED _("You have a new memo from %s.\n" \
@@ -88,9 +78,6 @@
#define BOT_ASSIGN_READONLY _("Sorry, bot assignment is temporarily disabled.")
#define ENABLED _("Enabled")
#define DISABLED _("Disabled")
-#define OPER_LIST_FORMAT _(" %3d %-32s %s")
-#define OPER_VIEW_FORMAT _("%3d %s (by %s on %s; %s)\n" \
- " %s")
#define HOST_SET_ERROR _("A vhost must be in the format of a valid hostmask.")
#define HOST_SET_IDENT_ERROR _("A vhost ident must be in the format of a valid ident")
#define HOST_SET_TOOLONG _("Error! The vhost is too long, please use a host shorter than %d characters.")
diff --git a/include/modules.h b/include/modules.h
index d3a43cd8a..ca0656e06 100644
--- a/include/modules.h
+++ b/include/modules.h
@@ -640,9 +640,10 @@ class CoreExport Module : public Extensible
/** Called when a user requests info for a channel
* @param source The user requesting info
* @param ci The channel the user is requesting info for
+ * @param info Data to show the user requesting information
* @param ShowHidden true if we should show the user everything
*/
- virtual void OnChanInfo(CommandSource &source, ChannelInfo *ci, bool ShowHidden) { }
+ virtual void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool ShowHidden) { }
/** Called on cs_findchan()
* @param chname The name being looked up
@@ -758,9 +759,10 @@ class CoreExport Module : public Extensible
/** Called when a user requests info for a nick
* @param source The user requesting info
* @param na The nick the user is requesting info from
+ * @param info Data to show the user requesting information
* @param ShowHidden true if we should show the user everything
*/
- virtual void OnNickInfo(CommandSource &source, NickAlias *na, bool ShowHidden) { }
+ virtual void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool ShowHidden) { }
/** Called in findnick()
* Useful to modify the na returned by findnick()
diff --git a/include/services.h b/include/services.h
index 1da51e296..3bf8d4494 100644
--- a/include/services.h
+++ b/include/services.h
@@ -915,4 +915,29 @@ class CoreExport NumberList
virtual bool InvalidRange(const Anope::string &list);
};
+class CoreExport ListFormatter
+{
+ public:
+ typedef std::map<Anope::string, Anope::string> ListEntry;
+ private:
+ std::vector<Anope::string> columns;
+ std::vector<ListEntry> entries;
+ public:
+ ListFormatter &addColumn(const Anope::string &name);
+ void addEntry(const ListEntry &entry);
+ bool isEmpty() const;
+ void Process(std::vector<Anope::string> &);
+};
+
+class CoreExport InfoFormatter
+{
+ User *user;
+ std::vector<std::pair<Anope::string, Anope::string> > replies;
+ unsigned longest;
+ public:
+ InfoFormatter(User *u);
+ void Process(std::vector<Anope::string> &);
+ Anope::string &operator[](const Anope::string &key);
+};
+
#endif /* SERVICES_H */
diff --git a/modules/commands/bs_badwords.cpp b/modules/commands/bs_badwords.cpp
index db3843cf4..e0db84c40 100644
--- a/modules/commands/bs_badwords.cpp
+++ b/modules/commands/bs_badwords.cpp
@@ -13,42 +13,6 @@
#include "module.h"
-class BadwordsListCallback : public NumberList
-{
- CommandSource &source;
- ChannelInfo *ci;
- bool SentHeader;
- public:
- BadwordsListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &list) : NumberList(list, false), source(_source), ci(_ci), SentHeader(false)
- {
- }
-
- ~BadwordsListCallback()
- {
- if (!SentHeader)
- source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str());
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetBadWordCount())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Bad words list for %s:\n"
- " Num Word Type"), ci->name.c_str());
- }
-
- DoList(source, Number - 1, ci->GetBadWord(Number - 1));
- }
-
- static void DoList(CommandSource &source, unsigned Number, BadWord *bw)
- {
- source.Reply(_(" %3d %-30s %s"), Number + 1, bw->word.c_str(), bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : "")));
- }
-};
class BadwordsDelCallback : public NumberList
{
@@ -92,18 +56,44 @@ class CommandBSBadwords : public Command
{
bool override = !ci->AccessFor(source.u).HasPriv("BADWORDS");
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "LIST";
+ ListFormatter list;
+
+ list.addColumn("Number").addColumn("Word").addColumn("Type");
if (!ci->GetBadWordCount())
+ {
source.Reply(_("%s bad words list is empty."), ci->name.c_str());
+ return;
+ }
else if (!word.empty() && word.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- BadwordsListCallback list(source, ci, word);
- list.Process();
+ class BadwordsListCallback : public NumberList
+ {
+ ListFormatter &list;
+ ChannelInfo *ci;
+ public:
+ BadwordsListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (!Number || Number > ci->GetBadWordCount())
+ return;
+
+ BadWord *bw = ci->GetBadWord(Number - 1);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(Number);
+ entry["Word"] = bw->word;
+ entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : ""));
+ this->list.addEntry(entry);
+ }
+ }
+ nl_list(list, ci, word);
+ nl_list.Process();
}
else
{
- bool SentHeader = false;
-
for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i)
{
BadWord *bw = ci->GetBadWord(i);
@@ -111,22 +101,28 @@ class CommandBSBadwords : public Command
if (!word.empty() && !Anope::Match(bw->word, word))
continue;
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Bad words list for %s:\n"
- " Num Word Type"), ci->name.c_str());
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Word"] = bw->word;
+ entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : ""));
+ list.addEntry(entry);
+ }
+ }
- }
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on %s badword list."), ci->name.c_str());
+ else
+ {
+ std::vector<Anope::string> replies;
+ list.Process(replies);
- BadwordsListCallback::DoList(source, i, bw);
- }
+ source.Reply(_("Badword list for %s:"), ci->name.c_str());
- if (!SentHeader)
- source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str());
- }
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
- return;
+ source.Reply(_("End of badword list."));
+ }
}
void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &word)
diff --git a/modules/commands/bs_botlist.cpp b/modules/commands/bs_botlist.cpp
index 03e0c16b8..73905f0a8 100644
--- a/modules/commands/bs_botlist.cpp
+++ b/modules/commands/bs_botlist.cpp
@@ -26,50 +26,47 @@ class CommandBSBotList : public Command
{
User *u = source.u;
unsigned count = 0;
+ ListFormatter list;
+
+ list.addColumn("Nick").addColumn("Mask");
for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
{
BotInfo *bi = it->second;
- if (!bi->HasFlag(BI_PRIVATE))
+ if (u->HasCommand("botserv/botlist") || !bi->HasFlag(BI_PRIVATE))
{
- if (!count)
- source.Reply(_("Bot list:"));
++count;
- source.Reply(" %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str());
+ ListFormatter::ListEntry entry;
+ entry["Nick"] = (bi->HasFlag(BI_PRIVATE) ? "* " : "") + bi->nick;
+ entry["Mask"] = bi->GetIdent() + "@" + bi->host;
+ list.addEntry(entry);
}
}
- if (u->HasCommand("botserv/botlist") && count < BotListByNick.size())
- {
- source.Reply(_("Bots reserved to IRC operators:"));
-
- for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
- {
- BotInfo *bi = it->second;
-
- if (bi->HasFlag(BI_PRIVATE))
- {
- source.Reply(" %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str());
- ++count;
- }
- }
- }
+ std::vector<Anope::string> replies;
+ list.Process(replies);
if (!count)
source.Reply(_("There are no bots available at this time.\n"
"Ask a Services Operator to create one!"));
else
- source.Reply(_("%d bots available."), count);
+ {
+ source.Reply(_("Bot list:"));
- return;
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("%d bots available."), count);
+ }
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand)
{
this->SendSyntax(source);
source.Reply(" ");
- source.Reply(_("Lists all available bots on this network."));
+ source.Reply(_("Lists all available bots on this network. Bots prefixed"
+ "by a * are reserved for IRC operators."));
return true;
}
};
diff --git a/modules/commands/cs_access.cpp b/modules/commands/cs_access.cpp
index 68a367701..d1d3deb93 100644
--- a/modules/commands/cs_access.cpp
+++ b/modules/commands/cs_access.cpp
@@ -95,148 +95,6 @@ class AccessAccessProvider : public AccessProvider
}
};
-class AccessListCallback : public NumberList
-{
- protected:
- CommandSource &source;
- ChannelInfo *ci;
- bool SentHeader;
- public:
- AccessListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), source(_source), ci(_ci), SentHeader(false)
- {
- }
-
- ~AccessListCallback()
- {
- if (SentHeader)
- source.Reply(_("End of access list."));
- else
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
- }
-
- virtual void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetAccessCount())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
- }
-
- DoList(source, ci, Number - 1, ci->GetAccess(Number - 1));
- }
-
- static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access)
- {
- source.Reply(_(" %3d %4d %s"), Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str());
- }
-};
-
-class AccessViewCallback : public AccessListCallback
-{
- public:
- AccessViewCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : AccessListCallback(_source, _ci, numlist)
- {
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetAccessCount())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
- }
-
- DoList(source, ci, Number - 1, ci->GetAccess(Number - 1));
- }
-
- static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access)
- {
- Anope::string timebuf;
- if (ci->c)
- for (CUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
- if (access->Matches((*cit)->user, (*cit)->user->Account()))
- timebuf = "Now";
- if (timebuf.empty())
- {
- if (access->last_seen == 0)
- timebuf = "Never";
- else
- timebuf = do_strftime(access->last_seen, NULL, true);
- }
-
- source.Reply(CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str(), access->creator.c_str(), do_strftime(access->created, NULL, true).c_str(), timebuf.c_str());
- }
-};
-
-class AccessDelCallback : public NumberList
-{
- CommandSource &source;
- ChannelInfo *ci;
- Command *c;
- unsigned Deleted;
- Anope::string Nicks;
- bool Denied;
- bool override;
- public:
- AccessDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), Denied(false)
- {
- if (!ci->AccessFor(source.u).HasPriv("ACCESS_CHANGE") && source.u->HasPriv("chanserv/access/modify"))
- this->override = true;
- }
-
- ~AccessDelCallback()
- {
- if (Denied && !Deleted)
- source.Reply(ACCESS_DENIED);
- else if (!Deleted)
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
- else
- {
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, c, ci) << "for user" << (Deleted == 1 ? " " : "s ") << Nicks;
-
- if (Deleted == 1)
- source.Reply(_("Deleted 1 entry from %s access list."), ci->name.c_str());
- else
- source.Reply(_("Deleted %d entries from %s access list."), Deleted, ci->name.c_str());
- }
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetAccessCount())
- return;
-
- User *u = source.u;
-
- ChanAccess *access = ci->GetAccess(Number - 1);
-
- AccessGroup u_access = ci->AccessFor(u);
- ChanAccess *u_highest = u_access.Highest();
-
- if (u_highest ? AccessChanAccess::DetermineLevel(u_highest) : 0 <= AccessChanAccess::DetermineLevel(access) && !u->HasPriv("chanserv/access/modify"))
- {
- Denied = true;
- return;
- }
-
- ++Deleted;
- if (!Nicks.empty())
- Nicks += ", " + access->mask;
- else
- Nicks = access->mask;
-
- FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access));
-
- ci->EraseAccess(Number - 1);
- }
-};
-
class CommandCSAccess : public Command
{
void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
@@ -329,8 +187,70 @@ class CommandCSAccess : public Command
source.Reply(_("%s access list is empty."), ci->name.c_str());
else if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- AccessDelCallback list(source, ci, this, mask);
- list.Process();
+ class AccessDelCallback : public NumberList
+ {
+ CommandSource &source;
+ ChannelInfo *ci;
+ Command *c;
+ unsigned Deleted;
+ Anope::string Nicks;
+ bool Denied;
+ bool override;
+ public:
+ AccessDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), Denied(false), override(false)
+ {
+ if (!ci->AccessFor(source.u).HasPriv("ACCESS_CHANGE") && source.u->HasPriv("chanserv/access/modify"))
+ this->override = true;
+ }
+
+ ~AccessDelCallback()
+ {
+ if (Denied && !Deleted)
+ source.Reply(ACCESS_DENIED);
+ else if (!Deleted)
+ source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
+ else
+ {
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, c, ci) << "for user" << (Deleted == 1 ? " " : "s ") << Nicks;
+
+ if (Deleted == 1)
+ source.Reply(_("Deleted 1 entry from %s access list."), ci->name.c_str());
+ else
+ source.Reply(_("Deleted %d entries from %s access list."), Deleted, ci->name.c_str());
+ }
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (!Number || Number > ci->GetAccessCount())
+ return;
+
+ User *user = source.u;
+
+ ChanAccess *access = ci->GetAccess(Number - 1);
+
+ AccessGroup u_access = ci->AccessFor(user);
+ ChanAccess *u_highest = u_access.Highest();
+
+ if (u_highest ? AccessChanAccess::DetermineLevel(u_highest) : 0 <= AccessChanAccess::DetermineLevel(access) && !u_access.Founder && !user->HasPriv("chanserv/access/modify"))
+ {
+ Denied = true;
+ return;
+ }
+
+ ++Deleted;
+ if (!Nicks.empty())
+ Nicks += ", " + access->mask;
+ else
+ Nicks = access->mask;
+
+ FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, user, access));
+
+ ci->EraseAccess(Number - 1);
+ }
+ }
+ delcallback(source, ci, this, mask);
+ delcallback.Process();
}
else
{
@@ -365,7 +285,7 @@ class CommandCSAccess : public Command
return;
}
- void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
+ void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, ListFormatter &list)
{
const Anope::string &nick = params.size() > 2 ? params[2] : "";
@@ -373,13 +293,50 @@ class CommandCSAccess : public Command
source.Reply(_("%s access list is empty."), ci->name.c_str());
else if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- AccessListCallback list(source, ci, nick);
- list.Process();
+ class AccessListCallback : public NumberList
+ {
+ ListFormatter &list;
+ ChannelInfo *ci;
+
+ public:
+ AccessListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
+ {
+ }
+
+ void HandleNumber(unsigned number)
+ {
+ if (!number || number > ci->GetAccessCount())
+ return;
+
+ ChanAccess *access = ci->GetAccess(number - 1);
+
+ Anope::string timebuf;
+ if (ci->c)
+ for (CUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
+ if (access->Matches((*cit)->user, (*cit)->user->Account()))
+ timebuf = "Now";
+ if (timebuf.empty())
+ {
+ if (access->last_seen == 0)
+ timebuf = "Never";
+ else
+ timebuf = do_strftime(access->last_seen, NULL, true);
+ }
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Level"] = stringify(AccessChanAccess::DetermineLevel(access));
+ entry["Mask"] = access->mask;
+ entry["By"] = access->creator;
+ entry["Last seen"] = timebuf;
+ this->list.addEntry(entry);
+ }
+ }
+ nl_list(list, ci, nick);
+ nl_list.Process();
}
else
{
- bool SentHeader = false;
-
for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
{
ChanAccess *access = ci->GetAccess(i);
@@ -387,62 +344,71 @@ class CommandCSAccess : public Command
if (!nick.empty() && !Anope::Match(access->mask, nick))
continue;
- if (!SentHeader)
+ Anope::string timebuf;
+ if (ci->c)
+ for (CUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
+ if (access->Matches((*cit)->user, (*cit)->user->Account()))
+ timebuf = "Now";
+ if (timebuf.empty())
{
- SentHeader = true;
- source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
+ if (access->last_seen == 0)
+ timebuf = "Never";
+ else
+ timebuf = do_strftime(access->last_seen, NULL, true);
}
- AccessListCallback::DoList(source, ci, i, access);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Level"] = stringify(AccessChanAccess::DetermineLevel(access));
+ entry["Mask"] = access->mask;
+ entry["By"] = access->creator;
+ entry["Last seen"] = timebuf;
+ list.addEntry(entry);
}
+ }
- if (SentHeader)
- source.Reply(_("End of access list."));
- else
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
+ else
+ {
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Access list for %s:"), ci->name.c_str());
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of access list"));
}
return;
}
- void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
+ void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
{
- const Anope::string &nick = params.size() > 2 ? params[2] : "";
-
if (!ci->GetAccessCount())
- source.Reply(_("%s access list is empty."), ci->name.c_str());
- else if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- AccessViewCallback list(source, ci, nick);
- list.Process();
+ source.Reply(_("%s access list is empty."), ci->name.c_str());
+ return;
}
- else
- {
- bool SentHeader = false;
-
- for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
- {
- ChanAccess *access = ci->GetAccess(i);
-
- if (!nick.empty() && !Anope::Match(access->mask, nick))
- continue;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
- }
- AccessViewCallback::DoList(source, ci, i, access);
- }
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Level").addColumn("Mask");
+ this->ProcessList(source, ci, params, list);
+ }
- if (SentHeader)
- source.Reply(_("End of access list."));
- else
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
+ void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
+ {
+ if (!ci->GetAccessCount())
+ {
+ source.Reply(_("%s access list is empty."), ci->name.c_str());
+ return;
}
- return;
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Level").addColumn("Mask").addColumn("By").addColumn("Last seen");
+ this->ProcessList(source, ci, params, list);
}
void DoClear(CommandSource &source, ChannelInfo *ci)
@@ -600,8 +566,6 @@ class CommandCSAccess : public Command
class CommandCSLevels : public Command
{
- int levelinfo_maxwidth;
-
void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
{
User *u = source.u;
@@ -681,29 +645,34 @@ class CommandCSLevels : public Command
{
source.Reply(_("Access level settings for channel %s:"), ci->name.c_str());
- const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
- if (!levelinfo_maxwidth)
- for (unsigned i = 0; i < privs.size(); ++i)
- {
- const Privilege &p = privs[i];
+ ListFormatter list;
+ list.addColumn("Name").addColumn("Level");
- int len = p.name.length();
- if (len > levelinfo_maxwidth)
- levelinfo_maxwidth = len;
- }
+ const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
for (unsigned i = 0; i < privs.size(); ++i)
{
const Privilege &p = privs[i];
int16_t j = ci->GetLevel(p.name);
+ ListFormatter::ListEntry entry;
+ entry["Name"] = p.name;
+
if (j == ACCESS_INVALID)
- source.Reply(_(" %-*s (disabled)"), levelinfo_maxwidth, p.name.c_str());
+ entry["Level"] = "(disabled)";
else if (j == ACCESS_FOUNDER)
- source.Reply(_(" %-*s (founder only)"), levelinfo_maxwidth, p.name.c_str());
+ entry["Level"] = "(founder only)";
else
- source.Reply(_(" %-*s %d"), levelinfo_maxwidth, p.name.c_str(), j);
+ entry["Level"] = stringify(j);
+
+ list.addEntry(entry);
}
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
void DoReset(CommandSource &source, ChannelInfo *ci)
@@ -721,7 +690,7 @@ class CommandCSLevels : public Command
}
public:
- CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4), levelinfo_maxwidth(0)
+ CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4)
{
this->SetDesc(_("Redefine the meanings of access levels"));
this->SetSyntax(_("\037channel\037 SET \037type\037 \037level\037"));
@@ -771,21 +740,25 @@ class CommandCSLevels : public Command
if (subcommand.equals_ci("DESC"))
{
source.Reply(_("The following feature/function names are understood."));
- const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
- if (!levelinfo_maxwidth)
- for (unsigned i = 0; i < privs.size(); ++i)
- {
- const Privilege &p = privs[i];
- int len = p.name.length();
- if (len > levelinfo_maxwidth)
- levelinfo_maxwidth = len;
- }
+ ListFormatter list;
+ list.addColumn("Name").addColumn("Description");
+
+ const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
for (unsigned i = 0; i < privs.size(); ++i)
{
const Privilege &p = privs[i];
- source.Reply(_(" %-*s %s"), levelinfo_maxwidth, p.name.c_str(), translate(source.u, p.desc.c_str()));
+ ListFormatter::ListEntry entry;
+ entry["Name"] = p.name;
+ entry["Description"] = translate(source.u, p.desc.c_str());
+ list.addEntry(entry);
}
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
else
{
diff --git a/modules/commands/cs_akick.cpp b/modules/commands/cs_akick.cpp
index f4325ea3a..b87e97354 100644
--- a/modules/commands/cs_akick.cpp
+++ b/modules/commands/cs_akick.cpp
@@ -46,115 +46,6 @@ static void split_usermask(const Anope::string &mask, Anope::string &nick, Anope
}
}
-class AkickListCallback : public NumberList
-{
- protected:
- CommandSource &source;
- ChannelInfo *ci;
- bool SentHeader;
- public:
- AkickListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), source(_source), ci(_ci), SentHeader(false)
- {
- }
-
- ~AkickListCallback()
- {
- if (!SentHeader)
- source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
- }
-
- virtual void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetAkickCount())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Autokick list for %s:"), ci->name.c_str());
- }
-
- DoList(source, ci, Number - 1, ci->GetAkick(Number - 1));
- }
-
- static void DoList(CommandSource &source, ChannelInfo *ci, unsigned index, AutoKick *akick)
- {
- source.Reply(_(" %3d %s (%s)"), index + 1, akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), !akick->reason.empty() ? akick->reason.c_str() : NO_REASON);
- }
-};
-
-class AkickViewCallback : public AkickListCallback
-{
- public:
- AkickViewCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist) : AkickListCallback(_source, _ci, numlist)
- {
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetAkickCount())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Autokick list for %s:"), ci->name.c_str());
- }
-
- DoList(source, ci, Number - 1, ci->GetAkick(Number - 1));
- }
-
- static void DoList(CommandSource &source, ChannelInfo *ci, unsigned index, AutoKick *akick)
- {
- Anope::string timebuf;
-
- if (akick->addtime)
- timebuf = do_strftime(akick->addtime);
- else
- timebuf = UNKNOWN;
-
- source.Reply(CHAN_AKICK_VIEW_FORMAT, index + 1, akick->HasFlag(AK_ISNICK) ? akick->nc->display.c_str() : akick->mask.c_str(), !akick->creator.empty() ? akick->creator.c_str() : UNKNOWN, timebuf.c_str(), !akick->reason.empty() ? akick->reason.c_str() : _(NO_REASON));
-
- if (akick->last_used)
- source.Reply(_(" Last used %s"), do_strftime(akick->last_used).c_str());
- }
-};
-
-class AkickDelCallback : public NumberList
-{
- CommandSource &source;
- ChannelInfo *ci;
- Command *c;
- unsigned Deleted;
- public:
- AkickDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), Deleted(0)
- {
- }
-
- ~AkickDelCallback()
- {
- User *u = source.u;
- bool override = !ci->AccessFor(u).HasPriv("AKICK");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, c, ci) << "DEL on " << Deleted << " users";
-
- if (!Deleted)
- source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
- else if (Deleted == 1)
- source.Reply(_("Deleted 1 entry from %s autokick list."), ci->name.c_str());
- else
- source.Reply(_("Deleted %d entries from %s autokick list."), Deleted, ci->name.c_str());
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetAkickCount())
- return;
-
- ++Deleted;
- ci->EraseAkick(Number - 1);
- }
-};
-
class CommandCSAKick : public Command
{
void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
@@ -287,8 +178,41 @@ class CommandCSAKick : public Command
/* Special case: is it a number/list? Only do search if it isn't. */
if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- AkickDelCallback list(source, ci, this, mask);
- list.Process();
+ class AkickDelCallback : public NumberList
+ {
+ CommandSource &source;
+ ChannelInfo *ci;
+ Command *c;
+ unsigned Deleted;
+ public:
+ AkickDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), Deleted(0)
+ {
+ }
+
+ ~AkickDelCallback()
+ {
+ bool override = !ci->AccessFor(source.u).HasPriv("AKICK");
+ Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, c, ci) << "DEL on " << Deleted << " users";
+
+ if (!Deleted)
+ source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
+ else if (Deleted == 1)
+ source.Reply(_("Deleted 1 entry from %s autokick list."), ci->name.c_str());
+ else
+ source.Reply(_("Deleted %d entries from %s autokick list."), Deleted, ci->name.c_str());
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (!Number || Number > ci->GetAkickCount())
+ return;
+
+ ++Deleted;
+ ci->EraseAkick(Number - 1);
+ }
+ }
+ delcallback(source, ci, this, mask);
+ delcallback.Process();
}
else
{
@@ -318,30 +242,54 @@ class CommandCSAKick : public Command
}
}
- void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
+ void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, ListFormatter &list)
{
- User *u = source.u;
-
const Anope::string &mask = params.size() > 2 ? params[2] : "";
- bool override = !ci->AccessFor(u).HasPriv("AKICK");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "LIST";
-
- if (!ci->GetAkickCount())
- {
- source.Reply(_("%s autokick list is empty."), ci->name.c_str());
- return;
- }
-
if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- AkickListCallback list(source, ci, mask);
- list.Process();
+ class AkickListCallback : public NumberList
+ {
+ ListFormatter &list;
+ ChannelInfo *ci;
+
+ public:
+ AkickListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
+ {
+ }
+
+ void HandleNumber(unsigned number)
+ {
+ if (!number || number > ci->GetAkickCount())
+ return;
+
+ AutoKick *akick = ci->GetAkick(number - 1);
+
+ Anope::string timebuf, lastused;
+ if (akick->addtime)
+ timebuf = do_strftime(akick->addtime, NULL, false);
+ else
+ timebuf = UNKNOWN;
+ if (akick->last_used)
+ lastused = do_strftime(akick->last_used, NULL, false);
+ else
+ lastused = UNKNOWN;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Mask"] = akick->mask;
+ entry["Creator"] = akick->creator;
+ entry["Created"] = timebuf;
+ entry["Last used"] = lastused;
+ entry["Reason"] = akick->reason;
+ this->list.addEntry(entry);
+ }
+ }
+ nl_list(list, ci, mask);
+ nl_list.Process();
}
else
{
- bool SentHeader = false;
-
for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i)
{
AutoKick *akick = ci->GetAkick(i);
@@ -354,68 +302,67 @@ class CommandCSAKick : public Command
continue;
}
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Autokick list for %s:"), ci->name.c_str());
- }
-
- AkickListCallback::DoList(source, ci, i, akick);
+ Anope::string timebuf, lastused;
+ if (akick->addtime)
+ timebuf = do_strftime(akick->addtime);
+ else
+ timebuf = UNKNOWN;
+ if (akick->last_used)
+ lastused = do_strftime(akick->last_used);
+ else
+ lastused = UNKNOWN;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = akick->mask;
+ entry["Creator"] = akick->creator;
+ entry["Created"] = timebuf;
+ entry["Last used"] = lastused;
+ entry["Reason"] = akick->reason;
+ list.addEntry(entry);
}
-
- if (!SentHeader)
- source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
}
- }
- void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
- {
- User *u = source.u;
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
+ else
+ {
+ std::vector<Anope::string> replies;
+ list.Process(replies);
- const Anope::string &mask = params.size() > 2 ? params[2] : "";
+ source.Reply(_("Autokick list for %s:"), ci->name.c_str());
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
- bool override = !ci->AccessFor(u).HasPriv("AKICK");
- Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "VIEW";
+ source.Reply(_("End of autokick list"));
+ }
+ }
+ void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
+ {
if (!ci->GetAkickCount())
{
source.Reply(_("%s autokick list is empty."), ci->name.c_str());
return;
}
- if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask").addColumn("Reason");
+ this->ProcessList(source, ci, params, list);
+ }
+
+ void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
+ {
+ if (!ci->GetAkickCount())
{
- AkickViewCallback list(source, ci, mask);
- list.Process();
+ source.Reply(_("%s autokick list is empty."), ci->name.c_str());
+ return;
}
- else
- {
- bool SentHeader = false;
- for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i)
- {
- AutoKick *akick = ci->GetAkick(i);
-
- if (!mask.empty())
- {
- if (!akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->mask, mask))
- continue;
- if (akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->nc->display, mask))
- continue;
- }
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Autokick list for %s:"), ci->name.c_str());
- }
-
- AkickViewCallback::DoList(source, ci, i, akick);
- }
-
- if (!SentHeader)
- source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
- }
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask").addColumn("Creator").addColumn("Created").addColumn("Last used").addColumn("Reason");
+ this->ProcessList(source, ci, params, list);
}
void DoEnforce(CommandSource &source, ChannelInfo *ci)
diff --git a/modules/commands/cs_entrymsg.cpp b/modules/commands/cs_entrymsg.cpp
index a514365fe..d7e6181ef 100644
--- a/modules/commands/cs_entrymsg.cpp
+++ b/modules/commands/cs_entrymsg.cpp
@@ -77,15 +77,34 @@ class CommandEntryMessage : public Command
void DoList(CommandSource &source, ChannelInfo *ci)
{
EntryMessageList *messages = ci->GetExt<EntryMessageList *>("cs_entrymsg");
- if (messages != NULL)
+ if (messages == NULL)
{
- source.Reply(_("Entry message list for \2%s\2:"), ci->name.c_str());
- for (unsigned i = 0; i < messages->size(); ++i)
- source.Reply(CHAN_LIST_ENTRY, i + 1, (*messages)[i].message.c_str(), (*messages)[i].creator.c_str(), do_strftime((*messages)[i].when).c_str());
- source.Reply(_("End of entry message list."));
- }
- else
source.Reply(_("Entry message list for \2%s\2 is empty."), ci->name.c_str());
+ return;
+ }
+
+ source.Reply(_("Entry message list for \2%s\2:"), ci->name.c_str());
+
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Creator").addColumn("Created").addColumn("Message");
+ for (unsigned i = 0; i < messages->size(); ++i)
+ {
+ EntryMsg &msg = messages->at(i);
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Creator"] = msg.creator;
+ entry["Created"] = do_strftime(msg.when);
+ entry["Message"] = msg.message;
+ list.addEntry(entry);
+ }
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of entry message list."));
}
void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &message)
diff --git a/modules/commands/cs_flags.cpp b/modules/commands/cs_flags.cpp
index 3003855e7..a720a2a79 100644
--- a/modules/commands/cs_flags.cpp
+++ b/modules/commands/cs_flags.cpp
@@ -221,45 +221,60 @@ class CommandCSFlags : public Command
const Anope::string &arg = params.size() > 2 ? params[2] : "";
if (!ci->GetAccessCount())
+ {
source.Reply(_("%s access list is empty."), ci->name.c_str());
- else
+ return;
+ }
+
+ ListFormatter list;
+
+ list.addColumn("Number").addColumn("Mask").addColumn("Flags").addColumn("Creator").addColumn("Created");
+
+ unsigned count = 0;
+ for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
{
- unsigned total = 0;
+ ChanAccess *access = ci->GetAccess(i);
+ const Anope::string &flags = FlagsChanAccess::DetermineFlags(access);
- for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ if (!arg.empty())
{
- ChanAccess *access = ci->GetAccess(i);
- const Anope::string &flags = FlagsChanAccess::DetermineFlags(access);
-
- if (!arg.empty())
+ if (arg[0] == '+')
{
- if (arg[0] == '+')
- {
- bool pass = true;
- for (size_t j = 1; j < arg.length(); ++j)
- if (flags.find(arg[j]) == Anope::string::npos)
- pass = false;
- if (pass == false)
- continue;
- }
- else if (!Anope::Match(access->mask, arg))
+ bool pass = true;
+ for (size_t j = 1; j < arg.length(); ++j)
+ if (flags.find(arg[j]) == Anope::string::npos)
+ pass = false;
+ if (pass == false)
continue;
}
+ else if (!Anope::Match(access->mask, arg))
+ continue;
+ }
- if (++total == 1)
- {
- source.Reply(_("Flags list for %s"), ci->name.c_str());
- }
+ ListFormatter::ListEntry entry;
+ ++count;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = access->mask;
+ entry["Flags"] = FlagsChanAccess::DetermineFlags(access);
+ entry["Creator"] = access->creator;
+ entry["Created"] = do_strftime(access->created, source.u->Account(), true);
+ list.addEntry(entry);
+ }
- source.Reply(_(" %3d %-10s +%-10s [last modified on %s by %s]"), i + 1, access->mask.c_str(), FlagsChanAccess::DetermineFlags(access).c_str(), do_strftime(access->created, source.u->Account(), true).c_str(), access->creator.c_str());
- }
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
+ else
+ {
+ std::vector<Anope::string> replies;
+ list.Process(replies);
- if (total == 0)
- source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
- else if (total == ci->GetAccessCount())
+ source.Reply(_("Flags list for %s"), ci->name.c_str());
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ if (count == ci->GetAccessCount())
source.Reply(_("End of access list."));
else
- source.Reply(_("End of access list - %d/%d entries shown."), total, ci->GetAccessCount());
+ source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount());
}
}
diff --git a/modules/commands/cs_info.cpp b/modules/commands/cs_info.cpp
index b58158576..d5994dc4a 100644
--- a/modules/commands/cs_info.cpp
+++ b/modules/commands/cs_info.cpp
@@ -15,14 +15,14 @@
class CommandCSInfo : public Command
{
- void CheckOptStr(Anope::string &buf, ChannelInfoFlag opt, const Anope::string &str, ChannelInfo *ci, const NickCore *nc)
+ void CheckOptStr(Anope::string &buf, ChannelInfoFlag opt, const char *str, ChannelInfo *ci, NickCore *nc)
{
if (ci->HasFlag(opt))
{
if (!buf.empty())
buf += ", ";
- buf += str;
+ buf += translate(nc, str);
}
}
@@ -53,30 +53,33 @@ class CommandCSInfo : public Command
if (has_auspex || ci->AccessFor(u).HasPriv("INFO"))
show_all = true;
+ InfoFormatter info(u);
+
source.Reply(CHAN_INFO_HEADER, chan.c_str());
if (ci->GetFounder())
- source.Reply(_(" Founder: %s"), ci->GetFounder()->display.c_str());
+ info["Founder"] = ci->GetFounder()->display;
if (show_all && ci->successor)
- source.Reply(_(" Successor: %s"), ci->successor->display.c_str());
+ info["Successor"] = ci->successor->display;
if (!ci->desc.empty())
- source.Reply(_(" Description: %s"), ci->desc.c_str());
- source.Reply(_(" Registered: %s"), do_strftime(ci->time_registered).c_str());
- source.Reply(_(" Last used: %s"), do_strftime(ci->last_used).c_str());
+ info["Description"] = ci->desc;
+
+ info["Registered"] = do_strftime(ci->time_registered);
+ info["Last used"] = do_strftime(ci->last_used);
ModeLock *secret = ci->GetMLock(CMODE_SECRET);
if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode(CMODE_SECRET)))))
{
- source.Reply(_(" Last topic: %s"), ci->last_topic.c_str());
- source.Reply(_(" Topic set by: %s"), ci->last_topic_setter.c_str());
+ info["Last topic"] = ci->last_topic;
+ info["Topic set by"] = ci->last_topic_setter;
}
if (show_all)
{
- source.Reply(_(" Ban type: %d"), ci->bantype);
- Anope::string optbuf;
+ info["Ban type"] = stringify(ci->bantype);
+ Anope::string optbuf;
CheckOptStr(optbuf, CI_KEEPTOPIC, _("Topic Retention"), ci, u->Account());
CheckOptStr(optbuf, CI_PEACE, _("Peace"), ci, u->Account());
CheckOptStr(optbuf, CI_PRIVATE, _("Private"), ci, u->Account());
@@ -92,20 +95,26 @@ class CommandCSInfo : public Command
CheckOptStr(optbuf, CI_PERSIST, _("Persistant"), ci, u->Account());
CheckOptStr(optbuf, CI_NO_EXPIRE, _("No expire"), ci, u->Account());
- source.Reply(_(" Options: %s"), optbuf.empty() ? _("None") : optbuf.c_str());
- source.Reply(_(" Mode lock: %s"), ci->GetMLockAsString(true).c_str());
+ info["Options"] = optbuf.empty() ? _("None") : optbuf;
+ info["Mode lock"] = ci->GetMLockAsString(true);
if (!ci->HasFlag(CI_NO_EXPIRE))
- source.Reply(_(" Expires on: %s"), do_strftime(ci->last_used + Config->CSExpire).c_str());
+ info["Expires on"] = do_strftime(ci->last_used + Config->CSExpire);
}
if (ci->HasFlag(CI_SUSPENDED))
{
Anope::string *by = ci->GetExt<ExtensibleString *>("suspend_by"), *reason = ci->GetExt<ExtensibleString *>("suspend_reason");
if (by != NULL)
- source.Reply(_(" Suspended: [%s] %s"), by->c_str(), (reason && !reason->empty() ? reason->c_str() : NO_REASON));
+ info["Suspended"] = Anope::printf("[%s] %s", by->c_str(), (reason && !reason->empty() ? reason->c_str() : NO_REASON));
}
- FOREACH_MOD(I_OnChanInfo, OnChanInfo(source, ci, show_all));
+ FOREACH_MOD(I_OnChanInfo, OnChanInfo(source, ci, info, show_all));
+
+ std::vector<Anope::string> replies;
+ info.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
return;
}
diff --git a/modules/commands/cs_list.cpp b/modules/commands/cs_list.cpp
index 29a4309b2..06c15e421 100644
--- a/modules/commands/cs_list.cpp
+++ b/modules/commands/cs_list.cpp
@@ -71,7 +71,10 @@ class CommandCSList : public Command
Anope::string spattern = "#" + pattern;
- source.Reply(LIST_HEADER, pattern.c_str());
+ source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
+
+ ListFormatter list;
+ list.addColumn("Name").addColumn("Description");
for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it)
{
@@ -88,22 +91,28 @@ class CommandCSList : public Command
{
if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= Config->CSListMax)
{
- char noexpire_char = ' ';
+ bool isnoexpire = false;
if (is_servadmin && (ci->HasFlag(CI_NO_EXPIRE)))
- noexpire_char = '!';
+ isnoexpire = true;
- Anope::string buf;
+ ListFormatter::ListEntry entry;
+ entry["Name"] = (isnoexpire ? "!" : "") + ci->name;
if (ci->HasFlag(CI_SUSPENDED))
- buf = Anope::printf("%-20s [Suspended]", ci->name.c_str());
+ entry["Description"] = "[Suspended]";
else
- buf = Anope::printf("%-20s %s", ci->name.c_str(), !ci->desc.empty() ? ci->desc.c_str() : "");
-
- source.Reply(" %c%s", noexpire_char, buf.c_str());
+ entry["Description"] = ci->desc;
+ list.addEntry(entry);
}
++count;
}
}
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
source.Reply(_("End of list - %d/%d matches shown."), nchans > Config->CSListMax ? Config->CSListMax : nchans, nchans);
return;
}
diff --git a/modules/commands/cs_log.cpp b/modules/commands/cs_log.cpp
index 0045055d3..d020aa63a 100644
--- a/modules/commands/cs_log.cpp
+++ b/modules/commands/cs_log.cpp
@@ -21,7 +21,7 @@ public:
{
this->SetDesc(_("Configures channel logging settings"));
this->SetSyntax(_("\037channel\037"));
- this->SetSyntax(_("\037channel\037 \037command\037 [\037method\037]"));
+ this->SetSyntax(_("\037channel\037 \037command\037 \037method\037 [\037status\037]"));
}
void Execute(CommandSource &source, const std::vector<Anope::string> &params)
@@ -40,13 +40,29 @@ public:
source.Reply(_("There currently are no logging configurations for %s."), ci->name.c_str());
else
{
- source.Reply(_("Log list for %s:"), ci->name.c_str());
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Service").addColumn("Command").addColumn("Method").addColumn("");
+
for (unsigned i = 0; i < ci->log_settings.size(); ++i)
{
LogSetting &log = ci->log_settings[i];
- source.Reply("%d: %s %s, %s %s", i + 1, log.command_service.c_str(), log.command_name.c_str(), log.method.c_str(), log.extra.c_str());
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Service"] = log.command_service;
+ entry["Command"] = log.command_name;
+ entry["Method"] = log.method;
+ entry[""] = log.extra;
+ list.addEntry(entry);
}
+
+ source.Reply(_("Log list for %s:"), ci->name.c_str());
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
}
else if (params.size() > 2)
diff --git a/modules/commands/cs_mode.cpp b/modules/commands/cs_mode.cpp
index 7c0f09e63..3fa80ed68 100644
--- a/modules/commands/cs_mode.cpp
+++ b/modules/commands/cs_mode.cpp
@@ -156,7 +156,9 @@ class CommandCSMode : public Command
}
else
{
- source.Reply(_("Mode locks for %s:"), ci->name.c_str());
+ ListFormatter list;
+ list.addColumn("Mode").addColumn("Param").addColumn("Creator").addColumn("Created");
+
for (std::multimap<ChannelModeName, ModeLock>::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it)
{
const ModeLock &ml = it->second;
@@ -164,11 +166,21 @@ class CommandCSMode : public Command
if (!cm)
continue;
- Anope::string modeparam = ml.param;
- if (!modeparam.empty())
- modeparam = " " + modeparam;
- source.Reply(_("%c%c%s, by %s on %s"), ml.set ? '+' : '-', cm->ModeChar, modeparam.c_str(), ml.setter.c_str(), do_strftime(ml.created).c_str());
+ ListFormatter::ListEntry entry;
+ entry["Mode"] = Anope::printf("%c%c", ml.set ? '+' : '-', cm->ModeChar);
+ entry["Param"] = ml.param;
+ entry["Creator"] = ml.setter;
+ entry["Created"] = do_strftime(ml.created, source.u->Account(), false);
+ list.addEntry(entry);
}
+
+ source.Reply(_("Mode locks for %s:"), ci->name.c_str());
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
}
else
diff --git a/modules/commands/cs_set_misc.cpp b/modules/commands/cs_set_misc.cpp
index bbd936c65..b72aca423 100644
--- a/modules/commands/cs_set_misc.cpp
+++ b/modules/commands/cs_set_misc.cpp
@@ -115,7 +115,7 @@ class CSSetMisc : public Module
ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
}
- void OnChanInfo(CommandSource &source, ChannelInfo *ci, bool ShowHidden)
+ void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool ShowHidden)
{
std::deque<Anope::string> list;
ci->GetExtList(list);
@@ -127,7 +127,7 @@ class CSSetMisc : public Module
CSMiscData *data = ci->GetExt<CSMiscData *>(list[i]);
if (data != NULL)
- source.Reply(" %s: %s", list[i].substr(12).replace_all_cs("_", " ").c_str(), data->data.c_str());
+ info[list[i].substr(12).replace_all_cs("_", " ")] = data->data;
}
}
};
diff --git a/modules/commands/cs_xop.cpp b/modules/commands/cs_xop.cpp
index cbbd2a615..3ba53e28f 100644
--- a/modules/commands/cs_xop.cpp
+++ b/modules/commands/cs_xop.cpp
@@ -203,43 +203,6 @@ class XOPAccessProvider : public AccessProvider
}
};
-class XOPListCallback : public NumberList
-{
- CommandSource &source;
- ChannelInfo *ci;
- XOPType type;
- Command *c;
- bool SentHeader;
- public:
- XOPListCallback(CommandSource &_source, ChannelInfo *_ci, const Anope::string &numlist, XOPType _type, Command *_c) : NumberList(numlist, false), source(_source), ci(_ci), type(_type), c(_c), SentHeader(false)
- {
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > ci->GetAccessCount())
- return;
-
- ChanAccess *access = ci->GetAccess(Number - 1);
-
- if (this->type != XOPChanAccess::DetermineLevel(access))
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str());
- }
-
- DoList(source, access, Number - 1, this->type);
- }
-
- static void DoList(CommandSource &source, ChanAccess *access, unsigned index, XOPType type)
- {
- source.Reply(_(" %3d %s"), index, access->mask.c_str());
- }
-};
-
class XOPDelCallback : public NumberList
{
CommandSource &source;
@@ -462,15 +425,41 @@ class XOPBase : public Command
return;
}
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask");
+
if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- XOPListCallback list(source, ci, nick, level, this);
- list.Process();
+ class XOPListCallback : public NumberList
+ {
+ ListFormatter &list;
+ ChannelInfo *ci;
+ XOPType type;
+ public:
+ XOPListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist, XOPType _type) : NumberList(numlist, false), list(_list), ci(_ci), type(_type)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (!Number || Number > ci->GetAccessCount())
+ return;
+
+ ChanAccess *a = ci->GetAccess(Number - 1);
+
+ if (this->type != XOPChanAccess::DetermineLevel(a))
+ return;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(Number);
+ entry["Mask"] = a->mask;
+ this->list.addEntry(entry);
+ }
+ } nl_list(list, ci, nick, level);
+ nl_list.Process();
}
else
{
- bool SentHeader = false;
-
for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
{
ChanAccess *a = ci->GetAccess(i);
@@ -480,20 +469,24 @@ class XOPBase : public Command
else if (!nick.empty() && !Anope::Match(a->mask, nick))
continue;
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("%s list for %s:\n Num Nick"), source.command.c_str(), ci->name.c_str());
- }
-
- XOPListCallback::DoList(source, a, i + 1, level);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = a->mask;
+ list.addEntry(entry);
}
-
- if (!SentHeader)
- source.Reply(_("No matching entries on %s %s list."), ci->name.c_str(), source.command.c_str());
}
- return;
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
+ else
+ {
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("%s list for %s"), source.command.c_str(), ci->name.c_str());
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
}
void DoClear(CommandSource &source, ChannelInfo *ci, XOPType level)
diff --git a/modules/commands/hs_list.cpp b/modules/commands/hs_list.cpp
index 785db9e5c..8c5ec5f50 100644
--- a/modules/commands/hs_list.cpp
+++ b/modules/commands/hs_list.cpp
@@ -26,7 +26,6 @@ class CommandHSList : public Command
{
const Anope::string &key = !params.empty() ? params[0] : "";
int from = 0, to = 0, counter = 1;
- unsigned display_counter = 0;
/**
* Do a check for a range here, then in the next loop
@@ -56,6 +55,10 @@ class CommandHSList : public Command
}
}
+ unsigned display_counter = 0;
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Nick").addColumn("Vhost").addColumn("Creator").addColumn("Created");
+
for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it)
{
NickAlias *na = it->second;
@@ -68,10 +71,17 @@ class CommandHSList : public Command
if ((Anope::Match(na->nick, key) || Anope::Match(na->hostinfo.GetHost(), key)) && display_counter < Config->NSListMax)
{
++display_counter;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(display_counter);
+ entry["Nick"] = na->nick;
if (!na->hostinfo.GetIdent().empty())
- source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str());
+ entry["Vhost"] = na->hostinfo.GetIdent() + "@" + na->hostinfo.GetHost();
else
- source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str());
+ entry["Vhost"] = na->hostinfo.GetHost();
+ entry["Creator"] = na->hostinfo.GetCreator();
+ entry["Created"] = do_strftime(na->hostinfo.GetTime());
+ list.addEntry(entry);
}
}
else
@@ -83,14 +93,27 @@ class CommandHSList : public Command
if (((counter >= from && counter <= to) || (!from && !to)) && display_counter < Config->NSListMax)
{
++display_counter;
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(display_counter);
+ entry["Nick"] = na->nick;
if (!na->hostinfo.GetIdent().empty())
- source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str());
+ entry["Vhost"] = na->hostinfo.GetIdent() + "@" + na->hostinfo.GetHost();
else
- source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, na->nick.c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), do_strftime(na->hostinfo.GetTime()).c_str());
+ entry["Vhost"] = na->hostinfo.GetHost();
+ entry["Creator"] = na->hostinfo.GetCreator();
+ entry["Created"] = do_strftime(na->hostinfo.GetTime());
+ list.addEntry(entry);
}
}
++counter;
}
+
+ if (!display_counter)
+ {
+ source.Reply(_("No records to display."));
+ return;
+ }
+
if (!key.empty())
source.Reply(_("Displayed records matching key \002%s\002 (Count: \002%d\002)"), key.c_str(), display_counter);
else
@@ -100,7 +123,12 @@ class CommandHSList : public Command
else
source.Reply(_("Displayed all records (Count: \002%d\002)"), display_counter);
}
- return;
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand)
diff --git a/modules/commands/hs_request.cpp b/modules/commands/hs_request.cpp
index 0e9934a33..ea7d2a36e 100644
--- a/modules/commands/hs_request.cpp
+++ b/modules/commands/hs_request.cpp
@@ -271,14 +271,16 @@ class CommandHSReject : public Command
}
};
-class HSListBase : public Command
+class CommandHSWaiting : public Command
{
- protected:
void DoList(CommandSource &source)
{
int counter = 1;
int from = 0, to = 0;
unsigned display_counter = 0;
+ ListFormatter list;
+
+ list.addColumn("Number").addColumn("Nick").addColumn("Vhost").addColumn("Created");
for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it)
{
@@ -290,25 +292,30 @@ class HSListBase : public Command
if (((counter >= from && counter <= to) || (!from && !to)) && display_counter < Config->NSListMax)
{
++display_counter;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(counter);
+ entry["Nick"] = it->first;
if (!hr->ident.empty())
- source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002@\002%s\002 (%s - %s)"), counter, it->first.c_str(), hr->ident.c_str(), hr->host.c_str(), it->first.c_str(), do_strftime(hr->time).c_str());
+ entry["Vhost"] = hr->ident + "@" + hr->host;
else
- source.Reply(_("#%d Nick:\002%s\002, vhost:\002%s\002 (%s - %s)"), counter, it->first.c_str(), hr->host.c_str(), it->first.c_str(), do_strftime(hr->time).c_str());
+ entry["Vhost"] = hr->host;
+ entry["Created"] = do_strftime(hr->time);
+ list.addEntry(entry);
}
++counter;
}
source.Reply(_("Displayed all records (Count: \002%d\002)"), display_counter);
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
- public:
- HSListBase(Module *creator, const Anope::string &cmd, int min, int max) : Command(creator, cmd, min, max)
- {
- }
-};
-class CommandHSWaiting : public HSListBase
-{
public:
- CommandHSWaiting(Module *creator) : HSListBase(creator, "hostserv/waiting", 0, 0)
+ CommandHSWaiting(Module *creator) : Command(creator, "hostserv/waiting", 0, 0)
{
this->SetDesc(_("Retrieves the vhost requests"));
this->SetSyntax("");
@@ -339,7 +346,7 @@ class HSRequest : public Module
public:
HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
- request_type("HSRequest", HostRequest::unserialize), commandhsrequest(this), commandhsactive(this), commandhsreject(this), commandhswaiting(this)
+ request_type("HostRequest", HostRequest::unserialize), commandhsrequest(this), commandhsactive(this), commandhsreject(this), commandhswaiting(this)
{
this->SetAuthor("Anope");
diff --git a/modules/commands/ms_ignore.cpp b/modules/commands/ms_ignore.cpp
index cfb863ebb..5ac4efde4 100644
--- a/modules/commands/ms_ignore.cpp
+++ b/modules/commands/ms_ignore.cpp
@@ -73,9 +73,22 @@ class CommandMSIgnore : public Command
source.Reply(_("Your memo ignore list is empty."));
else
{
- source.Reply(_("Ignore list:"));
+ ListFormatter list;
+ list.addColumn("Mask");
for (unsigned i = 0; i < mi->ignores.size(); ++i)
- source.Reply(" %s", mi->ignores[i].c_str());
+ {
+ ListFormatter::ListEntry entry;
+ entry["Mask"] = mi->ignores[i];
+ list.addEntry(entry);
+ }
+
+ source.Reply(_("Ignore list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
}
else
diff --git a/modules/commands/ms_list.cpp b/modules/commands/ms_list.cpp
index 9087ad2c5..0a41e08da 100644
--- a/modules/commands/ms_list.cpp
+++ b/modules/commands/ms_list.cpp
@@ -13,39 +13,6 @@
#include "module.h"
-class MemoListCallback : public NumberList
-{
- CommandSource &source;
- ChannelInfo *ci;
- const MemoInfo *mi;
- bool SentHeader;
- public:
- MemoListCallback(CommandSource &_source, ChannelInfo *_ci, const MemoInfo *_mi, const Anope::string &list) : NumberList(list, false), source(_source), ci(_ci), mi(_mi), SentHeader(false)
- {
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > mi->memos.size())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Memos for %s:"), ci ? ci->name.c_str() : source.u->nick.c_str());
- source.Reply(_(" Num Sender Date/Time"));
- }
-
- DoList(source, mi, Number - 1);
- }
-
- static void DoList(CommandSource &source, const MemoInfo *mi, unsigned index)
- {
- Memo *m = mi->memos[index];
- source.Reply(_("%c%3d %-16s %s"), (m->HasFlag(MF_UNREAD)) ? '*' : ' ', index + 1, m->sender.c_str(), do_strftime(m->time).c_str());
- }
-};
-
class CommandMSList : public Command
{
public:
@@ -62,7 +29,6 @@ class CommandMSList : public Command
Anope::string param = !params.empty() ? params[0] : "", chan;
ChannelInfo *ci = NULL;
const MemoInfo *mi;
- int i, end;
if (!param.empty() && param[0] == '#')
{
@@ -83,6 +49,7 @@ class CommandMSList : public Command
}
else
mi = &u->Account()->memos;
+
if (!param.empty() && !isdigit(param[0]) && !param.equals_ci("NEW"))
this->OnSyntaxError(source, param);
else if (!mi->memos.size())
@@ -94,15 +61,44 @@ class CommandMSList : public Command
}
else
{
+ ListFormatter list;
+
+ list.addColumn("Number").addColumn("Sender").addColumn("Date/Time");
+
if (!param.empty() && isdigit(param[0]))
{
- MemoListCallback list(source, ci, mi, param);
- list.Process();
+ class MemoListCallback : public NumberList
+ {
+ ListFormatter &list;
+ CommandSource &source;
+ const MemoInfo *mi;
+ public:
+ MemoListCallback(ListFormatter &_list, CommandSource &_source, const MemoInfo *_mi, const Anope::string &numlist) : NumberList(numlist, false), list(_list), source(_source), mi(_mi)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (!Number || Number > mi->memos.size())
+ return;
+
+ Memo *m = mi->memos[Number];
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = (m->HasFlag(MF_UNREAD) ? "* " : " ") + stringify(Number + 1);
+ entry["Sender"] = m->sender;
+ entry["Date/Time"] = do_strftime(m->time);
+ this->list.addEntry(entry);
+ }
+ }
+ mlc(list, source, mi, param);
+ mlc.Process();
}
else
{
if (!param.empty())
{
+ unsigned i, end;
for (i = 0, end = mi->memos.size(); i < end; ++i)
if (mi->memos[i]->HasFlag(MF_UNREAD))
break;
@@ -116,23 +112,27 @@ class CommandMSList : public Command
}
}
- bool SentHeader = false;
-
- for (i = 0, end = mi->memos.size(); i < end; ++i)
+ for (unsigned i = 0, end = mi->memos.size(); i < end; ++i)
{
if (!param.empty() && !mi->memos[i]->HasFlag(MF_UNREAD))
continue;
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("New memo for %s."), ci ? ci->name.c_str() : u->nick.c_str());
- source.Reply(_(" Num Sender Date/Time"));
- }
+ Memo *m = mi->memos[i];
- MemoListCallback::DoList(source, mi, i);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = (m->HasFlag(MF_UNREAD) ? "* " : " ") + stringify(i + 1);
+ entry["Sender"] = m->sender;
+ entry["Date/Time"] = do_strftime(m->time);
+ list.addEntry(entry);
}
}
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ source.Reply(_("Memos for %s."), ci ? ci->name.c_str() : u->nick.c_str());
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
return;
}
diff --git a/modules/commands/ns_ajoin.cpp b/modules/commands/ns_ajoin.cpp
index 1f578a7cf..3c9467f69 100644
--- a/modules/commands/ns_ajoin.cpp
+++ b/modules/commands/ns_ajoin.cpp
@@ -75,10 +75,24 @@ class CommandNSAJoin : public Command
source.Reply(_("Your auto join list is empty."));
else
{
- source.Reply(_("Your auto join list:\n"
- " Num Channel Key"));
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Channel").addColumn("Key");
for (unsigned i = 0; i < channels->size(); ++i)
- source.Reply(" %3d %-12s %s", i + 1, channels->at(i).first.c_str(), channels->at(i).second.c_str());
+ {
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Channel"] = channels->at(i).first;
+ entry["Key"] = channels->at(i).second;
+ list.addEntry(entry);
+ }
+
+ source.Reply(_("Your auto join list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
}
diff --git a/modules/commands/ns_alist.cpp b/modules/commands/ns_alist.cpp
index 84af7cadd..8b8be9290 100644
--- a/modules/commands/ns_alist.cpp
+++ b/modules/commands/ns_alist.cpp
@@ -33,42 +33,50 @@ class CommandNSAList : public Command
NickAlias *na = findnick(nick);
if (!na)
- source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
- else
{
- int chan_count = 0;
+ source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
+ return;
+ }
+
+ ListFormatter list;
+ int chan_count = 0;
- source.Reply(_("Channels that \002%s\002 has access on:\n"
- " Num Channel Access"), na->nick.c_str());
+ list.addColumn("Number").addColumn("Channel").addColumn("Access");
- for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it)
- {
- ChannelInfo *ci = it->second;
+ source.Reply(_("Channels that \002%s\002 has access on:"), na->nick.c_str());
- if (ci->GetFounder() && ci->GetFounder() == na->nc)
- {
- source.Reply(_(" %3d %c%-20s Founder"), ++chan_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str());
- continue;
- }
+ for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it)
+ {
+ ChannelInfo *ci = it->second;
+ ListFormatter::ListEntry entry;
- AccessGroup access = ci->AccessFor(na->nc);
- if (access.empty())
- continue;
-
+ if (ci->GetFounder() && ci->GetFounder() == na->nc)
+ {
++chan_count;
-
- if (access.size() > 1)
- {
- source.Reply(_(" %3d You match %d access entries on %c%s, they are"), chan_count, access.size(), ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str());
- for (unsigned i = 0; i < access.size(); ++i)
- source.Reply(_(" %3d %-8s"), i + 1, access[i]->Serialize().c_str());
- }
- else
- source.Reply(_(" %3d %c%-20s %-8s"), chan_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), access[0]->Serialize().c_str());
+ entry["Number"] = stringify(chan_count);
+ entry["Channel"] = (ci->HasFlag(CI_NO_EXPIRE) ? "!" : "") + ci->name;
+ entry["Access"] = "Founder";
+ list.addEntry(entry);
+ continue;
}
- source.Reply(_("End of list - %d channels shown."), chan_count);
+ AccessGroup access = ci->AccessFor(na->nc);
+ if (access.empty())
+ continue;
+
+ ++chan_count;
+
+ entry["Number"] = stringify(chan_count);
+ entry["Channel"] = (ci->HasFlag(CI_NO_EXPIRE) ? "!" : "") + ci->name;
+ for (unsigned i = 0; i < access.size(); ++i)
+ entry["Access"] = entry["Access"] + ", " + access[i]->Serialize();
+ entry["Access"] = entry["Access"].substr(3);
}
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand)
diff --git a/modules/commands/ns_cert.cpp b/modules/commands/ns_cert.cpp
index 88e0144a3..5a1334b53 100644
--- a/modules/commands/ns_cert.cpp
+++ b/modules/commands/ns_cert.cpp
@@ -30,13 +30,24 @@ class CommandNSCert : public Command
return;
}
- source.Reply(_("Certificate list for \002%s\002:"), nc->display.c_str());
+ ListFormatter list;
+ list.addColumn("Certificate");
+
for (unsigned i = 0, end = nc->cert.size(); i < end; ++i)
{
Anope::string fingerprint = nc->GetCert(i);
- source.Reply(" %s", fingerprint.c_str());
+ ListFormatter::ListEntry entry;
+ entry["Certificate"] = fingerprint;
+ list.addEntry(entry);
}
+ source.Reply(_("Certificate list for \002%s\002:"), nc->display.c_str());
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
return;
}
@@ -111,14 +122,21 @@ class CommandNSCert : public Command
return;
}
- source.Reply(_("Cert list:"));
+ ListFormatter list;
+ list.addColumn("Certificate");
+
for (unsigned i = 0, end = nc->cert.size(); i < end; ++i)
{
- Anope::string fingerprint = nc->GetCert(i);
- source.Reply(" %s", fingerprint.c_str());
+ ListFormatter::ListEntry entry;
+ entry["Certificate"] = nc->GetCert(i);
+ list.addEntry(entry);
}
- return;
+ source.Reply(_("Certificate list:"));
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
public:
diff --git a/modules/commands/ns_group.cpp b/modules/commands/ns_group.cpp
index 6a83f4f46..b8e9087dc 100644
--- a/modules/commands/ns_group.cpp
+++ b/modules/commands/ns_group.cpp
@@ -250,16 +250,27 @@ class CommandNSGList : public Command
source.Reply(nick.empty() ? NICK_NOT_REGISTERED : _(NICK_X_NOT_REGISTERED), nick.c_str());
else
{
- source.Reply(!nick.empty() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->display.c_str());
+ ListFormatter list;
+ list.addColumn("Nick").addColumn("Expires");
for (std::list<NickAlias *>::const_iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it)
{
NickAlias *na2 = *it;
- source.Reply(na2->HasFlag(NS_NO_EXPIRE) || !Config->NSExpire ? _(" %s (does not expire)") : _(" %s (expires in %s)"), na2->nick.c_str(), do_strftime(na2->last_seen + Config->NSExpire).c_str());
+ ListFormatter::ListEntry entry;
+ entry["Nick"] = na2->nick;
+ entry["Expires"] = (na2->HasFlag(NS_NO_EXPIRE) || !Config->NSExpire) ? "Does not expire" : ("expires in " + do_strftime(na2->last_seen + Config->NSExpire));
+ list.addEntry(entry);
}
+
+ source.Reply(!nick.empty() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->display.c_str());
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
source.Reply(_("%d nicknames in the group."), nc->aliases.size());
}
- return;
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand)
diff --git a/modules/commands/ns_info.cpp b/modules/commands/ns_info.cpp
index 8940bfaa7..ac50150f5 100644
--- a/modules/commands/ns_info.cpp
+++ b/modules/commands/ns_info.cpp
@@ -16,15 +16,14 @@
class CommandNSInfo : public Command
{
private:
- // cannot be const, as it is modified
- void CheckOptStr(Anope::string &buf, NickCoreFlag opt, const Anope::string &str, NickCore *nc, bool reverse_logic = false)
+ template<typename T, unsigned END> void CheckOptStr(User *u, Anope::string &buf, T opt, const char *str, Flags<T, END> *nc, bool reverse_logic = false)
{
if (reverse_logic ? !nc->HasFlag(opt) : nc->HasFlag(opt))
{
if (!buf.empty())
buf += ", ";
- buf += str;
+ buf += translate(u, str);
}
}
public:
@@ -64,87 +63,86 @@ class CommandNSInfo : public Command
source.Reply(_("%s is %s"), na->nick.c_str(), na->last_realname.c_str());
+ if (na->nc->HasFlag(NI_UNCONFIRMED))
+ source.Reply(_("%s nickname is unconfirmed."), na->nick.c_str());
+
if (na->nc->IsServicesOper() && (show_hidden || !na->nc->HasFlag(NI_HIDE_STATUS)))
source.Reply(_("%s is a services operator of type %s."), na->nick.c_str(), na->nc->o->ot->GetName().c_str());
+ InfoFormatter info(u);
+
if (nick_online)
{
+ if (show_hidden && !na->last_realhost.empty())
+ info[_("Online from")] = na->last_realhost;
if (show_hidden || !na->nc->HasFlag(NI_HIDE_MASK))
- source.Reply(_(" Is online from: %s"), na->last_usermask.c_str());
+ info[_("Online from")] = na->last_usermask;
else
source.Reply(_("%s is currently online."), na->nick.c_str());
-
- if (has_auspex && !na->last_realhost.empty())
- source.Reply(_(" Is online from: %s"), na->last_realhost.c_str());
}
else
{
if (show_hidden || !na->nc->HasFlag(NI_HIDE_MASK))
- source.Reply(_("Last seen address: %s"), na->last_usermask.c_str());
- if (has_auspex && !na->last_realhost.empty())
- source.Reply(_("Last seen address: %s"), na->last_realhost.c_str());
+ info[_("Last seen address")] = na->last_usermask;
+ if (show_hidden && !na->last_realhost.empty())
+ info[_("Last seen address")] = na->last_realhost;
}
- source.Reply(_(" Time registered: %s"), do_strftime(na->time_registered).c_str());
+ info[_("Time registered")] = do_strftime(na->time_registered);
if (!nick_online)
- {
- source.Reply(_(" Last seen time: %s"), do_strftime(na->last_seen).c_str());
- }
+ info[_("Last seen")] = do_strftime(na->last_seen);
if (!na->last_quit.empty() && (show_hidden || !na->nc->HasFlag(NI_HIDE_QUIT)))
- source.Reply(_("Last quit message: %s"), na->last_quit.c_str());
+ info[_("Last quit message")] = na->last_quit;
if (!na->nc->email.empty() && (show_hidden || !na->nc->HasFlag(NI_HIDE_EMAIL)))
- source.Reply(_(" E-mail address: %s"), na->nc->email.c_str());
+ info[_("Email address")] = na->nc->email;
if (show_hidden)
{
if (na->hostinfo.HasVhost())
{
if (ircd->vident && !na->hostinfo.GetIdent().empty())
- source.Reply(_(" vhost: %s@%s"), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str());
+ info[_("VHost")] = na->hostinfo.GetIdent() + "@" + na->hostinfo.GetHost();
else
- source.Reply(_(" vhost: %s"), na->hostinfo.GetHost().c_str());
+ info[_("VHost")] = na->hostinfo.GetHost();
}
+
if (!na->nc->greet.empty())
- source.Reply(_(" Greet message: %s"), na->nc->greet.c_str());
+ info[_("Greet")] = na->nc->greet;
Anope::string optbuf;
- CheckOptStr(optbuf, NI_KILLPROTECT, _("Protection"), na->nc);
- CheckOptStr(optbuf, NI_SECURE, _("Security"), na->nc);
- CheckOptStr(optbuf, NI_PRIVATE, _("Private"), na->nc);
- CheckOptStr(optbuf, NI_MSG, _("Message mode"), na->nc);
- CheckOptStr(optbuf, NI_AUTOOP, _("Auto-op"), na->nc);
+ CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_KILLPROTECT, _("Protection"), na->nc);
+ CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_SECURE, _("Security"), na->nc);
+ CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_PRIVATE, _("Private"), na->nc);
+ CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_MSG, _("Message mode"), na->nc);
+ CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_AUTOOP, _("Auto-op"), na->nc);
+ CheckOptStr<NickCoreFlag, NI_END>(u, optbuf, NI_SUSPENDED, _("Suspended"), na->nc);
+ CheckOptStr<NickNameFlag, NS_END>(u, optbuf, NS_NO_EXPIRE, _("No expire"), na);
- source.Reply(_(" Options: %s"), optbuf.empty() ? _("None") : optbuf.c_str());
-
- if (na->nc->HasFlag(NI_SUSPENDED))
- {
- if (!na->last_quit.empty())
- source.Reply(_("This nickname is currently suspended, reason: %s"), na->last_quit.c_str());
- else
- source.Reply(_("This nickname is currently suspended"));
- }
+ info[_("Options")] = optbuf.empty() ? _("None") : optbuf;
if (na->nc->HasFlag(NI_UNCONFIRMED) == false)
{
if (na->HasFlag(NS_NO_EXPIRE) || !Config->NSExpire)
- source.Reply(_("This nickname will not expire."));
+ ;
else
- source.Reply(_("Expires on: %s"), do_strftime(na->last_seen + Config->NSExpire).c_str());
+ info[_("Expires")] = do_strftime(na->last_seen + Config->NSExpire);
}
else
- source.Reply(_("Expires on: %s"), do_strftime(na->time_registered + Config->NSUnconfirmedExpire).c_str());
+ info[_("Expires")] = do_strftime(na->time_registered + Config->NSUnconfirmedExpire);
}
- FOREACH_MOD(I_OnNickInfo, OnNickInfo(source, na, show_hidden));
+ FOREACH_MOD(I_OnNickInfo, OnNickInfo(source, na, info, show_hidden));
- if (na->nc->HasFlag(NI_UNCONFIRMED))
- source.Reply(_("This nickname is unconfirmed."));
+ std::vector<Anope::string> replies;
+ info.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
- return;
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand)
diff --git a/modules/commands/ns_list.cpp b/modules/commands/ns_list.cpp
index e46be55e7..a4feaa19b 100644
--- a/modules/commands/ns_list.cpp
+++ b/modules/commands/ns_list.cpp
@@ -30,7 +30,6 @@ class CommandNSList : public Command
const NickCore *mync;
unsigned nnicks;
bool is_servadmin = u->IsServicesOper();
- char noexpire_char = ' ';
int count = 0, from = 0, to = 0;
bool suspended, nsnoexpire, unconfirmed;
@@ -40,7 +39,6 @@ class CommandNSList : public Command
{
Anope::string n1 = myStrGetToken(pattern.substr(1), '-', 0), /* Read FROM out */
n2 = myStrGetToken(pattern, '-', 1);
-
try
{
from = convertTo<int>(n1);
@@ -73,8 +71,10 @@ class CommandNSList : public Command
}
mync = u->Account();
+ ListFormatter list;
+
+ list.addColumn("Nick").addColumn("Last usermask");
- source.Reply(LIST_HEADER, pattern.c_str());
for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it)
{
NickAlias *na = it->second;
@@ -83,7 +83,7 @@ class CommandNSList : public Command
if (na->nc->HasFlag(NI_PRIVATE) && !is_servadmin && na->nc != mync)
continue;
else if (nsnoexpire && !na->HasFlag(NS_NO_EXPIRE))
- continue;
+ continue;
else if (suspended && !na->nc->HasFlag(NI_SUSPENDED))
continue;
else if (unconfirmed && !na->nc->HasFlag(NI_UNCONFIRMED))
@@ -97,24 +97,33 @@ class CommandNSList : public Command
{
if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= Config->NSListMax)
{
+ bool isnoexpire = false;
if (is_servadmin && na->HasFlag(NS_NO_EXPIRE))
- noexpire_char = '!';
- else
- noexpire_char = ' ';
+ isnoexpire = true;
+
+ ListFormatter::ListEntry entry;
+ entry["Nick"] = (isnoexpire ? "!" : "") + na->nick;
if (na->nc->HasFlag(NI_HIDE_MASK) && !is_servadmin && na->nc != mync)
- buf = Anope::printf("%-20s [Hostname Hidden]", na->nick.c_str());
+ entry["Last usermask"] = "[Hostname hidden]";
else if (na->nc->HasFlag(NI_SUSPENDED))
- buf = Anope::printf("%-20s [Suspended]", na->nick.c_str());
+ entry["Last usermask"] = "[Suspended]";
else if (na->nc->HasFlag(NI_UNCONFIRMED))
- buf = Anope::printf("%-20s [Unconfirmed]", na->nick.c_str());
+ entry["Last usermask"] = "[Unconfirmed]";
else
- buf = Anope::printf("%-20s %s", na->nick.c_str(), na->last_usermask.c_str());
- source.Reply(" %c%s", noexpire_char, buf.c_str());
+ entry["Last usermask"] = na->last_usermask;
+ list.addEntry(entry);
}
++count;
}
}
+ source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
source.Reply(_("End of list - %d/%d matches shown."), nnicks > Config->NSListMax ? Config->NSListMax : nnicks, nnicks);
return;
diff --git a/modules/commands/ns_set_misc.cpp b/modules/commands/ns_set_misc.cpp
index b53b8ceaf..b6c5a7e3b 100644
--- a/modules/commands/ns_set_misc.cpp
+++ b/modules/commands/ns_set_misc.cpp
@@ -126,7 +126,7 @@ class NSSetMisc : public Module
ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
}
- void OnNickInfo(CommandSource &source, NickAlias *na, bool ShowHidden)
+ void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool ShowHidden)
{
std::deque<Anope::string> list;
na->nc->GetExtList(list);
@@ -138,7 +138,7 @@ class NSSetMisc : public Module
NSMiscData *data = na->nc->GetExt<NSMiscData *>(list[i]);
if (data)
- source.Reply(" %s: %s", list[i].substr(12).replace_all_cs("_", " ").c_str(), data->data.c_str());
+ info[list[i].substr(12).replace_all_cs("_", " ")] = data->data;
}
}
};
diff --git a/modules/commands/os_akill.cpp b/modules/commands/os_akill.cpp
index ea8b2e40c..8b205698d 100644
--- a/modules/commands/os_akill.cpp
+++ b/modules/commands/os_akill.cpp
@@ -54,82 +54,6 @@ class AkillDelCallback : public NumberList
}
};
-class AkillListCallback : public NumberList
-{
- protected:
- CommandSource &source;
- bool SentHeader;
- public:
- AkillListCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), source(_source), SentHeader(false)
- {
- }
-
- ~AkillListCallback()
- {
- if (!SentHeader)
- source.Reply(_("No matching entries on the AKILL list."));
- else
- source.Reply(END_OF_ANY_LIST, "Akill");
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number)
- return;
-
- XLine *x = akills->GetEntry(Number - 1);
-
- if (!x)
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current AKILL list:\n"
- " Num Mask Reason"));
- }
-
- DoList(source, x, Number - 1);
- }
-
- static void DoList(CommandSource &source, XLine *x, unsigned Number)
- {
- source.Reply(OPER_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str());
- }
-};
-
-class AkillViewCallback : public AkillListCallback
-{
- public:
- AkillViewCallback(CommandSource &_source, const Anope::string &numlist) : AkillListCallback(_source, numlist)
- {
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number)
- return;
-
- XLine *x = akills->GetEntry(Number - 1);
-
- if (!x)
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current AKILL list:"));
- }
-
- DoList(source, x, Number - 1);
- }
-
- static void DoList(CommandSource &source, XLine *x, unsigned Number)
- {
- source.Reply(OPER_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), do_strftime(x->Created).c_str(), expire_left(source.u->Account(), x->Expires).c_str(), x->Reason.c_str());
- }
-};
-
class CommandOSAKill : public Command
{
private:
@@ -288,52 +212,80 @@ class CommandOSAKill : public Command
return;
}
- void DoList(CommandSource &source, const std::vector<Anope::string> &params)
+ void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
{
- if (akills->GetList().empty())
- {
- source.Reply(_("AKILL list is empty."));
- return;
- }
-
const Anope::string &mask = params.size() > 1 ? params[1] : "";
if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- AkillListCallback list(source, mask);
- list.Process();
+ class ListCallback : public NumberList
+ {
+ ListFormatter &list;
+ public:
+ ListCallback(ListFormatter &_list, const Anope::string &numstr) : NumberList(numstr, false), list(_list)
+ {
+ }
+
+ void HandleNumber(unsigned number)
+ {
+ if (!number)
+ return;
+
+ XLine *x = akills->GetEntry(number - 1);
+
+ if (!x)
+ return;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(number);
+ entry["Mask"] = x->Mask;
+ entry["Creator"] = x->By;
+ entry["Created"] = do_strftime(x->Created);
+ entry["Expires"] = expire_left(NULL, x->Expires);
+ entry["Reason"] = x->Reason;
+ this->list.addEntry(entry);
+ }
+ }
+ nl_list(list, mask);
+ nl_list.Process();
}
else
{
- bool SentHeader = false;
-
for (unsigned i = 0, end = akills->GetCount(); i < end; ++i)
{
XLine *x = akills->GetEntry(i);
if (mask.empty() || mask.equals_ci(x->Mask) || mask == x->UID || Anope::Match(x->Mask, mask))
{
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current AKILL list:\n"
- " Num Mask Reason"));
- }
-
- AkillListCallback::DoList(source, x, i);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = x->Mask;
+ entry["Creator"] = x->By;
+ entry["Created"] = do_strftime(x->Created);
+ entry["Expires"] = expire_left(source.u->Account(), x->Expires);
+ entry["Reason"] = x->Reason;
+ list.addEntry(entry);
}
}
-
- if (!SentHeader)
- source.Reply(_("No matching entries on the AKILL list."));
- else
- source.Reply(END_OF_ANY_LIST, "Akill");
}
- return;
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on the AKILL list."));
+ else
+ {
+ source.Reply(_("Current akill list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of \2akill\2 list."));
+ }
}
- void DoView(CommandSource &source, const std::vector<Anope::string> &params)
+ void DoList(CommandSource &source, const std::vector<Anope::string> &params)
{
if (akills->GetList().empty())
{
@@ -341,38 +293,24 @@ class CommandOSAKill : public Command
return;
}
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask").addColumn("Reason");
- if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
+ this->ProcessList(source, params, list);
+ }
+
+ void DoView(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ if (akills->GetList().empty())
{
- AkillViewCallback list(source, mask);
- list.Process();
+ source.Reply(_("AKILL list is empty."));
+ return;
}
- else
- {
- bool SentHeader = false;
-
- for (unsigned i = 0, end = akills->GetCount(); i < end; ++i)
- {
- XLine *x = akills->GetEntry(i);
- if (mask.empty() || mask.equals_ci(x->Mask) || mask == x->UID || Anope::Match(x->Mask, mask))
- {
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current AKILL list:"));
- }
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask").addColumn("Creator").addColumn("Created").addColumn("Reason");
- AkillViewCallback::DoList(source, x, i);
- }
- }
-
- if (!SentHeader)
- source.Reply(_("No matching entries on the AKILL list."));
- }
-
- return;
+ this->ProcessList(source, params, list);
}
void DoClear(CommandSource &source)
@@ -387,8 +325,6 @@ class CommandOSAKill : public Command
}
source.Reply(_("The AKILL list has been cleared."));
-
- return;
}
public:
CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 4)
diff --git a/modules/commands/os_config.cpp b/modules/commands/os_config.cpp
index 4955aa473..ead2448ad 100644
--- a/modules/commands/os_config.cpp
+++ b/modules/commands/os_config.cpp
@@ -168,13 +168,25 @@ class CommandOSConfig : public Command
if (ok == false)
continue;
- source.Reply(_("%s settings:"), bname.c_str());
+ ListFormatter lflist;
+ lflist.addColumn("Name").addColumn("Value");
for (unsigned i = 0; i < list.size(); ++i)
{
const Anope::string &first = list[i].first, second = list[i].second;
- source.Reply(_(" Name: %-15s Value: %s"), first.c_str(), second.c_str());
+ ListFormatter::ListEntry entry;
+ entry["Name"] = first;
+ entry["Value"] = second;
+ lflist.addEntry(entry);
}
+
+ std::vector<Anope::string> replies;
+ lflist.Process(replies);
+
+ source.Reply(_("%s settings:"), bname.c_str());
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
source.Reply(_("End of configuration."));
diff --git a/modules/commands/os_forbid.cpp b/modules/commands/os_forbid.cpp
index 4c6ead48f..80ebc850a 100644
--- a/modules/commands/os_forbid.cpp
+++ b/modules/commands/os_forbid.cpp
@@ -168,8 +168,8 @@ class CommandOSForbid : public Command
source.Reply(_("Forbid list is empty."));
else
{
- source.Reply(_("Forbid list:"));
- source.Reply(_("Mask Type Reason"));
+ ListFormatter list;
+ list.addColumn("Mask").addColumn("Type").addColumn("Reason");
for (unsigned i = 0; i < forbids.size(); ++i)
{
@@ -185,10 +185,23 @@ class CommandOSForbid : public Command
else
continue;
- source.Reply("%-10s %-5s %s", d->mask.c_str(), ftype.c_str(), d->reason.c_str());
- source.Reply(_("By %s, expires on %s"), d->creator.c_str(), d->expires ? do_strftime(d->expires).c_str() : "never");
+ ListFormatter::ListEntry entry;
+ entry["Mask"] = d->mask;
+ entry["Type"] = ftype;
+ entry["Creator"] = d->creator;
+ entry["Expires"] = d->expires ? do_strftime(d->expires).c_str() : "never";
+ entry["Reason"] = d->reason;
+ list.addEntry(entry);
}
+ source.Reply(_("Forbid list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
source.Reply(_("End of forbid list."));
}
}
diff --git a/modules/commands/os_ignore.cpp b/modules/commands/os_ignore.cpp
index 52edd04e9..d73a09197 100644
--- a/modules/commands/os_ignore.cpp
+++ b/modules/commands/os_ignore.cpp
@@ -188,17 +188,28 @@ class CommandOSIgnore : public Command
source.Reply(_("Ignore list is empty."));
else
{
- source.Reply(_("Services ignore list:\n"
- " Mask Creator Reason Expires"));
+ ListFormatter list;
+ list.addColumn("Mask").addColumn("Creator").addColumn("Reason").addColumn("Expires");
for (std::list<IgnoreData>::const_iterator ign = ignores.begin(), ign_end = ignores.end(); ign != ign_end; ++ign)
{
const IgnoreData &ignore = *ign;
- source.Reply(" %-11s %-11s %-11s %s", ignore.mask.c_str(), ignore.creator.c_str(), ignore.reason.c_str(), do_strftime(ignore.time).c_str());
+ ListFormatter::ListEntry entry;
+ entry["Mask"] = ignore.mask;
+ entry["Creator"] = ignore.creator;
+ entry["Reason"] = ignore.reason;
+ entry["Expires"] = do_strftime(ignore.time);
+ list.addEntry(entry);
}
- }
- return;
+ source.Reply(_("Services ignore list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
}
void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
@@ -266,7 +277,7 @@ class CommandOSIgnore : public Command
"Valid units are: \037s\037 for seconds, \037m\037 for minutes, \n"
"\037h\037 for hours and \037d\037 for days. \n"
"Combinations of these units are not permitted.\n"
- "To make Services permanently ignore the user, type 0 as time.\n"
+ "To make Services permanently ignore the user, type 0 as time.\n"
"When adding a \037mask\037, it should be in the format user@host\n"
"or nick!user@host, everything else will be considered a nick.\n"
"Wildcards are permitted.\n"
diff --git a/modules/commands/os_list.cpp b/modules/commands/os_list.cpp
index ccca1a617..ea6e90d79 100644
--- a/modules/commands/os_list.cpp
+++ b/modules/commands/os_list.cpp
@@ -35,10 +35,12 @@ class CommandOSChanList : public Command
Modes.push_back(CMODE_PRIVATE);
}
+ ListFormatter list;
+ list.addColumn("Name").addColumn("Users").addColumn("Modes").addColumn("Topic");
+
if (!pattern.empty() && (u2 = finduser(pattern)))
{
- source.Reply(_("\002%s\002 channel list:\n"
- "Name Users Modes Topic"), u2->nick.c_str());
+ source.Reply(_("\002%s\002 channel list:"), u2->nick.c_str());
for (UChannelList::iterator uit = u2->chans.begin(), uit_end = u2->chans.end(); uit != uit_end; ++uit)
{
@@ -49,13 +51,17 @@ class CommandOSChanList : public Command
if (!cc->chan->HasMode(*it))
continue;
- source.Reply(_("%-20s %4d +%-6s %s"), cc->chan->name.c_str(), cc->chan->users.size(), cc->chan->GetModes(true, true).c_str(), !cc->chan->topic.empty() ? cc->chan->topic.c_str() : "");
+ ListFormatter::ListEntry entry;
+ entry["Name"] = cc->chan->name;
+ entry["Users"] = stringify(cc->chan->users.size());
+ entry["Modes"] = cc->chan->GetModes(true, true);
+ entry["Topic"] = cc->chan->topic;
+ list.addEntry(entry);
}
}
else
{
- source.Reply(_("Channel list:\n"
- "Name Users Modes Topic"));
+ source.Reply(_("Channel list:"));
for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit)
{
@@ -68,12 +74,22 @@ class CommandOSChanList : public Command
if (!c->HasMode(*it))
continue;
- source.Reply(_("%-20s %4d +%-6s %s"), c->name.c_str(), c->users.size(), c->GetModes(true, true).c_str(), !c->topic.empty() ? c->topic.c_str() : "");
+ ListFormatter::ListEntry entry;
+ entry["Name"] = c->name;
+ entry["Users"] = stringify(c->users.size());
+ entry["Modes"] = c->GetModes(true, true);
+ entry["Topic"] = c->topic;
+ list.addEntry(entry);
}
}
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
source.Reply(_("End of channel list."));
- return;
}
bool OnHelp(CommandSource &source, const Anope::string &subcommand)
@@ -109,10 +125,12 @@ class CommandOSUserList : public Command
if (!opt.empty() && opt.equals_ci("INVISIBLE"))
Modes.push_back(UMODE_INVIS);
+ ListFormatter list;
+ list.addColumn("Name").addColumn("Mask");
+
if (!pattern.empty() && (c = findchan(pattern)))
{
- source.Reply(_("\002%s\002 users list:\n"
- "Nick Mask"), pattern.c_str());
+ source.Reply(_("\002%s\002 users list:"), pattern.c_str());
for (CUserList::iterator cuit = c->users.begin(), cuit_end = c->users.end(); cuit != cuit_end; ++cuit)
{
@@ -123,13 +141,15 @@ class CommandOSUserList : public Command
if (!uc->user->HasMode(*it))
continue;
- source.Reply(_("%-20s %s@%s"), uc->user->nick.c_str(), uc->user->GetIdent().c_str(), uc->user->GetDisplayedHost().c_str());
+ ListFormatter::ListEntry entry;
+ entry["Name"] = uc->user->nick;
+ entry["Mask"] = uc->user->GetIdent() + "@" + uc->user->GetDisplayedHost();
+ list.addEntry(entry);
}
}
else
{
- source.Reply(_("Users list:\n"
- "Nick Mask"));
+ source.Reply(_("Users list:"));
for (Anope::insensitive_map<User *>::iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
{
@@ -145,10 +165,20 @@ class CommandOSUserList : public Command
if (!u2->HasMode(*mit))
continue;
}
- source.Reply(_("%-20s %s@%s"), u2->nick.c_str(), u2->GetIdent().c_str(), u2->GetDisplayedHost().c_str());
+
+ ListFormatter::ListEntry entry;
+ entry["Name"] = u2->nick;
+ entry["Mask"] = u2->GetIdent() + "@" + u2->GetDisplayedHost();
+ list.addEntry(entry);
}
}
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
source.Reply(_("End of users list."));
return;
}
diff --git a/modules/commands/os_news.cpp b/modules/commands/os_news.cpp
index cf127d284..ea8a44c8c 100644
--- a/modules/commands/os_news.cpp
+++ b/modules/commands/os_news.cpp
@@ -116,10 +116,28 @@ class NewsBase : public Command
source.Reply(msgs[MSG_LIST_NONE]);
else
{
- source.Reply(msgs[MSG_LIST_HEADER]);
+ ListFormatter lflist;
+ lflist.addColumn("Number").addColumn("Creator").addColumn("Created").addColumn("Text");
+
for (unsigned i = 0, end = list.size(); i < end; ++i)
- source.Reply(_("%5d (%s by %s)\n"" %s"), i + 1, do_strftime(list[i]->time).c_str(), !list[i]->who.empty() ? list[i]->who.c_str() : "<unknown>", list[i]->text.c_str());
- source.Reply(END_OF_ANY_LIST, "News");
+ {
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Creator"] = list[i]->who;
+ entry["Created"] = do_strftime(list[i]->time);
+ entry["Text"] = list[i]->text;
+ lflist.addEntry(entry);
+ }
+
+ source.Reply(msgs[MSG_LIST_HEADER]);
+
+ std::vector<Anope::string> replies;
+ lflist.Process(replies);
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+
+ source.Reply(_("End of \2news\2 list."));
}
return;
diff --git a/modules/commands/os_session.cpp b/modules/commands/os_session.cpp
index 5417e83d7..d86fbd6f4 100644
--- a/modules/commands/os_session.cpp
+++ b/modules/commands/os_session.cpp
@@ -149,72 +149,6 @@ class ExceptionDelCallback : public NumberList
}
};
-class ExceptionListCallback : public NumberList
-{
- protected:
- CommandSource &source;
- bool SentHeader;
- public:
- ExceptionListCallback(CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), source(_source), SentHeader(false)
- {
- }
-
- virtual void HandleNumber(unsigned Number)
- {
- if (!Number || Number > session_service->GetExceptions().size())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current Session Limit Exception list:"));
- source.Reply(_("Num Limit Host"));
- }
-
- DoList(source, Number - 1);
- }
-
- static void DoList(CommandSource &source, unsigned index)
- {
- if (index >= session_service->GetExceptions().size())
- return;
-
- source.Reply(_("%3d %4d %s"), index + 1, session_service->GetExceptions()[index]->limit, session_service->GetExceptions()[index]->mask.c_str());
- }
-};
-
-class ExceptionViewCallback : public ExceptionListCallback
-{
- public:
- ExceptionViewCallback(CommandSource &_source, const Anope::string &numlist) : ExceptionListCallback(_source, numlist)
- {
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number || Number > session_service->GetExceptions().size())
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current Session Limit Exception list:"));
- }
-
- DoList(source, Number - 1);
- }
-
- static void DoList(CommandSource &source, unsigned index)
- {
- if (index >= session_service->GetExceptions().size())
- return;
-
- Anope::string expirebuf = expire_left(source.u->Account(), session_service->GetExceptions()[index]->expires);
-
- source.Reply(_("%3d. %s (by %s on %s; %s)\n " " Limit: %-4d - %s"), index + 1, session_service->GetExceptions()[index]->mask.c_str(), !session_service->GetExceptions()[index]->who.empty() ? session_service->GetExceptions()[index]->who.c_str() : "<unknown>", do_strftime((session_service->GetExceptions()[index]->time ? session_service->GetExceptions()[index]->time : Anope::CurTime)).c_str(), expirebuf.c_str(), session_service->GetExceptions()[index]->limit, session_service->GetExceptions()[index]->reason.c_str());
- }
-};
-
class CommandOSSession : public Command
{
private:
@@ -233,16 +167,30 @@ class CommandOSSession : public Command
source.Reply(_("Invalid threshold value. It must be a valid integer greater than 1."));
else
{
- source.Reply(_("Hosts with at least \002%d\002 sessions:"), mincount);
- source.Reply(_("Sessions Host"));
+ ListFormatter list;
+ list.addColumn("Session").addColumn("Host");
for (SessionService::SessionMap::iterator it = session_service->GetSessions().begin(), it_end = session_service->GetSessions().end(); it != it_end; ++it)
{
Session *session = it->second;
if (session->count >= mincount)
- source.Reply(_("%6d %s"), session->count, session->host.c_str());
+ {
+ ListFormatter::ListEntry entry;
+ entry["Session"] = stringify(session->count);
+ entry["Host"] = session->host;
+ list.addEntry(entry);
+ }
}
+
+ source.Reply(_("Hosts with at least \002%d\002 sessions:"), mincount);
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
+
+
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
}
return;
@@ -485,69 +433,93 @@ class CommandOSException : public Command
return;
}
- void DoList(CommandSource &source, const std::vector<Anope::string> &params)
+ void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
{
- Anope::string mask = params.size() > 1 ? params[1] : "";
+ const Anope::string &mask = params.size() > 1 ? params[1] : "";
+
+ if (session_service->GetExceptions().empty())
+ {
+ source.Reply(_("The session exception list is empty."));
+ return;
+ }
if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- ExceptionListCallback list(source, mask);
- list.Process();
+ class ExceptionListCallback : public NumberList
+ {
+ ListFormatter &list;
+ public:
+ ExceptionListCallback(ListFormatter &_list, const Anope::string &numlist) : NumberList(numlist, false), list(_list)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (!Number || Number > session_service->GetExceptions().size())
+ return;
+
+ Exception *e = session_service->GetExceptions()[Number - 1];
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(Number);
+ entry["Mask"] = e->mask;
+ entry["By"] = e->who;
+ entry["Created"] = do_strftime(e->time);
+ entry["Limit"] = stringify(e->limit);
+ entry["Reason"] = e->reason;
+ this->list.addEntry(entry);
+ }
+ }
+ nl_list(list, mask);
+ nl_list.Process();
}
else
{
- bool SentHeader = false;
-
for (unsigned i = 0, end = session_service->GetExceptions().size(); i < end; ++i)
- if (mask.empty() || Anope::Match(session_service->GetExceptions()[i]->mask, mask))
+ {
+ Exception *e = session_service->GetExceptions()[i];
+ if (mask.empty() || Anope::Match(e->mask, mask))
{
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current Session Limit Exception list:"));
- source.Reply(_("Num Limit Host"));
- }
-
- ExceptionListCallback::DoList(source, i);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = e->mask;
+ entry["By"] = e->who;
+ entry["Created"] = do_strftime(e->time);
+ entry["Limit"] = stringify(e->limit);
+ entry["Reason"] = e->reason;
+ list.addEntry(entry);
}
-
- if (!SentHeader)
- source.Reply(_("No matching entries on session-limit exception list."));
+ }
}
- return;
- }
-
- void DoView(CommandSource &source, const std::vector<Anope::string> &params)
- {
- Anope::string mask = params.size() > 1 ? params[1] : "";
-
- if (!mask.empty() && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- ExceptionViewCallback list(source, mask);
- list.Process();
- }
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on session-limit exception list."));
else
{
- bool SentHeader = false;
+ source.Reply(_("Current Session Limit Exception list:"));
+
+ std::vector<Anope::string> replies;
+ list.Process(replies);
- for (unsigned i = 0, end = session_service->GetExceptions().size(); i < end; ++i)
- if (mask.empty() || Anope::Match(session_service->GetExceptions()[i]->mask, mask))
- {
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current Session Limit Exception list:"));
- }
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ }
- ExceptionViewCallback::DoList(source, i);
- }
+ void DoList(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Limit").addColumn("Mask");
- if (!SentHeader)
- source.Reply(_("No matching entries on session-limit exception list."));
- }
+ this->ProcessList(source, params, list);
+ }
- return;
+ void DoView(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask").addColumn("By").addColumn("Created").addColumn("Limit").addColumn("Reason");
+
+ this->ProcessList(source, params, list);
}
public:
diff --git a/modules/commands/os_sxline.cpp b/modules/commands/os_sxline.cpp
index fb5b7f244..d3a65243c 100644
--- a/modules/commands/os_sxline.cpp
+++ b/modules/commands/os_sxline.cpp
@@ -54,82 +54,6 @@ class SXLineDelCallback : public NumberList
}
};
-class SXLineListCallback : public NumberList
-{
- protected:
- XLineManager *xlm;
- Command *command;
- CommandSource &source;
- bool SentHeader;
- public:
- SXLineListCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, false), xlm(x), command(c), source(_source), SentHeader(false)
- {
- }
-
- ~SXLineListCallback()
- {
- if (!SentHeader)
- source.Reply(_("No matching entries on the %s list."), this->command->name.c_str());
- }
-
- virtual void HandleNumber(unsigned Number)
- {
- if (!Number)
- return;
-
- XLine *x = this->xlm->GetEntry(Number - 1);
-
- if (!x)
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current %s list:\n Num Mask Reason"), this->command->name.c_str());
- }
-
- DoList(source, x, Number - 1);
- }
-
- static void DoList(CommandSource &source, XLine *x, unsigned Number)
- {
- source.Reply(OPER_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str());
- }
-};
-
-class SXLineViewCallback : public SXLineListCallback
-{
- public:
- SXLineViewCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : SXLineListCallback(x, c, _source, numlist)
- {
- }
-
- void HandleNumber(unsigned Number)
- {
- if (!Number)
- return;
-
- XLine *x = this->xlm->GetEntry(Number - 1);
-
- if (!x)
- return;
-
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current %s list:"), this->command->name.c_str());
- }
-
- DoList(source, x, Number - 1);
- }
-
- static void DoList(CommandSource &source, XLine *x, unsigned Number)
- {
- Anope::string expirebuf = expire_left(source.u->Account(), x->Expires);
- source.Reply(OPER_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), do_strftime(x->Created).c_str(), expirebuf.c_str(), x->Reason.c_str());
- }
-};
-
class CommandOSSXLineBase : public Command
{
private:
@@ -143,7 +67,7 @@ class CommandOSSXLineBase : public Command
if (!this->xlm() || this->xlm()->GetList().empty())
{
- source.Reply(_("%s list is empty."), this->name.c_str());
+ source.Reply(_("%s list is empty."), source.command.c_str());
return;
}
@@ -166,14 +90,14 @@ class CommandOSSXLineBase : public Command
if (!x)
{
- source.Reply(_("\002%s\002 not found on the %s list."), mask.c_str(), this->name.c_str());
+ source.Reply(_("\002%s\002 not found on the %s list."), mask.c_str(), source.command.c_str());
return;
}
FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, this->xlm()));
SXLineDelCallback::DoDel(this->xlm(), source, x);
- source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), this->name.c_str());
+ source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), source.command.c_str());
}
if (readonly)
@@ -182,11 +106,11 @@ class CommandOSSXLineBase : public Command
return;
}
- void OnList(CommandSource &source, const std::vector<Anope::string> &params)
+ void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
{
if (!this->xlm() || this->xlm()->GetList().empty())
{
- source.Reply(_("%s list is empty."), this->name.c_str());
+ source.Reply(_("%s list is empty."), source.command.c_str());
return;
}
@@ -194,78 +118,85 @@ class CommandOSSXLineBase : public Command
if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
{
- SXLineListCallback list(this->xlm(), this, source, mask);
- list.Process();
+ class SXLineListCallback : public NumberList
+ {
+ XLineManager *xlm;
+ ListFormatter &list;
+ public:
+ SXLineListCallback(XLineManager *x, ListFormatter &_list, const Anope::string &numlist) : NumberList(numlist, false), xlm(x), list(_list)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (!Number)
+ return;
+
+ XLine *x = this->xlm->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(Number);
+ entry["Mask"] = x->Mask;
+ entry["By"] = x->By;
+ entry["Created"] = do_strftime(x->Created);
+ entry["Expires"] = expire_left(NULL, x->Expires);
+ entry["Reason"] = x->Reason;
+ list.addEntry(entry);
+ }
+ }
+ sl_list(this->xlm(), list, mask);
+ sl_list.Process();
}
else
{
- bool SentHeader = false;
-
for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i)
{
XLine *x = this->xlm()->GetEntry(i);
if (mask.empty() || mask.equals_ci(x->Mask) || mask == x->UID || Anope::Match(x->Mask, mask))
{
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current %s list:\n Num Mask Reason"), this->name.c_str());
- }
-
- SXLineListCallback::DoList(source, x, i);
+ ListFormatter::ListEntry entry;
+ entry["Number"] = stringify(i + 1);
+ entry["Mask"] = x->Mask;
+ entry["By"] = x->By;
+ entry["Created"] = do_strftime(x->Created);
+ entry["Expires"] = expire_left(source.u->Account(), x->Expires);
+ entry["Reason"] = x->Reason;
+ list.addEntry(entry);
}
}
-
- if (!SentHeader)
- source.Reply(_("No matching entries on the %s list."), this->name.c_str());
- else
- source.Reply(END_OF_ANY_LIST, this->name.c_str());
}
- return;
- }
-
- void OnView(CommandSource &source, const std::vector<Anope::string> &params)
- {
- if (!this->xlm () || this->xlm()->GetList().empty())
- {
- source.Reply(_("%s list is empty."), this->name.c_str());
- return;
- }
-
- const Anope::string &mask = params.size() > 1 ? params[1] : "";
-
- if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
- {
- SXLineViewCallback list(this->xlm(), this, source, mask);
- list.Process();
- }
+ if (list.isEmpty())
+ source.Reply(_("No matching entries on the %s list."), source.command.c_str());
else
{
- bool SentHeader = false;
+ source.Reply(_("Current %s list:"), source.command.c_str());
- for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i)
- {
- XLine *x = this->xlm()->GetEntry(i);
+ std::vector<Anope::string> replies;
+ list.Process(replies);
- if (mask.empty() || mask.equals_ci(x->Mask) || mask == x->UID || Anope::Match(x->Mask, mask))
- {
- if (!SentHeader)
- {
- SentHeader = true;
- source.Reply(_("Current %s list:"), this->name.c_str());
- }
+ for (unsigned i = 0; i < replies.size(); ++i)
+ source.Reply(replies[i]);
+ }
+ }
- SXLineViewCallback::DoList(source, x, i);
- }
- }
+ void OnList(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask").addColumn("Reason");
- if (!SentHeader)
- source.Reply(_("No matching entries on the %s list."), this->name.c_str());
- }
+ this->ProcessList(source, params, list);
+ }
- return;
+ void OnView(CommandSource &source, const std::vector<Anope::string> &params)
+ {
+ ListFormatter list;
+ list.addColumn("Number").addColumn("Mask").addColumn("By").addColumn("Created").addColumn("Expires").addColumn("Reason");
+ this->ProcessList(source, params, list);
}
void OnClear(CommandSource &source)
@@ -279,7 +210,7 @@ class CommandOSSXLineBase : public Command
this->xlm()->DelXLine(x);
}
- source.Reply(_("The %s list has been cleared."), this->name.c_str());
+ source.Reply(_("The %s list has been cleared."), source.command.c_str());
return;
}
@@ -382,7 +313,7 @@ class CommandOSSNLine : public CommandOSSXLineBase
if (mask.find_first_not_of("*?") == Anope::string::npos)
source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
else if (canAdd.first == 1)
- source.Reply(_("\002%s\002 already exists on the %s list."), canAdd.second->Mask.c_str(), this->name.c_str());
+ source.Reply(_("\002%s\002 already exists on the %s list."), canAdd.second->Mask.c_str(), source.command.c_str());
else if (canAdd.first == 2)
source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str());
else if (canAdd.first == 3)
@@ -404,7 +335,7 @@ class CommandOSSNLine : public CommandOSSXLineBase
if (percent > 95)
{
source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
- Log(LOG_ADMIN, u, this) << "tried to " << this->name << " " << percent << "% of the network (" << affected << " users)";
+ Log(LOG_ADMIN, u, this) << "tried to " << source.command << " " << percent << "% of the network (" << affected << " users)";
return;
}
@@ -443,7 +374,7 @@ class CommandOSSNLine : public CommandOSSXLineBase
}
}
- source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), this->name.c_str());
+ source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
Log(LOG_ADMIN, u, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
if (readonly)
diff --git a/src/misc.cpp b/src/misc.cpp
index 0941c9bbc..2c079da09 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -131,6 +131,121 @@ bool NumberList::InvalidRange(const Anope::string &)
return true;
}
+ListFormatter &ListFormatter::addColumn(const Anope::string &name)
+{
+ this->columns.push_back(name);
+ return *this;
+}
+
+void ListFormatter::addEntry(const ListEntry &entry)
+{
+ this->entries.push_back(entry);
+}
+
+bool ListFormatter::isEmpty() const
+{
+ return this->entries.empty();
+}
+
+void ListFormatter::Process(std::vector<Anope::string> &buffer)
+{
+ buffer.clear();
+
+ std::map<Anope::string, size_t> lenghts;
+ std::set<Anope::string> breaks;
+ for (unsigned i = 0; i < this->columns.size(); ++i)
+ lenghts[this->columns[i]] = this->columns[i].length();
+ for (unsigned i = 0; i < this->entries.size(); ++i)
+ {
+ ListEntry &e = this->entries[i];
+ for (unsigned j = 0; j < this->columns.size(); ++j)
+ if (e[this->columns[j]].length() > lenghts[this->columns[j]])
+ lenghts[this->columns[j]] = e[this->columns[j]].length();
+ }
+ unsigned length = 0;
+ for (std::map<Anope::string, size_t>::iterator it = lenghts.begin(), it_end = lenghts.end(); it != it_end; ++it)
+ {
+ /* Break lines at 80 chars */
+ if (length > 80)
+ {
+ breaks.insert(it->first);
+ length = 0;
+ }
+ else
+ length += it->second;
+ }
+
+ /* Only put a list header if more than 1 column */
+ if (this->columns.size() > 1)
+ {
+ Anope::string s;
+ for (unsigned i = 0; i < this->columns.size(); ++i)
+ {
+ if (breaks.count(this->columns[i]))
+ {
+ buffer.push_back(s);
+ s = " ";
+ }
+ else if (!s.empty())
+ s += " ";
+ s += this->columns[i];
+ if (i + 1 != this->columns.size())
+ for (unsigned j = this->columns[i].length(); j < lenghts[this->columns[i]]; ++j)
+ s += " ";
+ }
+ buffer.push_back(s);
+ }
+
+ for (unsigned i = 0; i < this->entries.size(); ++i)
+ {
+ ListEntry &e = this->entries[i];
+
+ Anope::string s;
+ for (unsigned j = 0; j < this->columns.size(); ++j)
+ {
+ if (breaks.count(this->columns[j]))
+ {
+ buffer.push_back(s);
+ s = " ";
+ }
+ else if (!s.empty())
+ s += " ";
+ s += e[this->columns[j]];
+ if (j + 1 != this->columns.size())
+ for (unsigned k = e[this->columns[j]].length(); k < lenghts[this->columns[j]]; ++k)
+ s += " ";
+ }
+ buffer.push_back(s);
+ }
+}
+
+InfoFormatter::InfoFormatter(User *u) : user(u), longest(0)
+{
+}
+
+void InfoFormatter::Process(std::vector<Anope::string> &buffer)
+{
+ buffer.clear();
+
+ for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it)
+ {
+ Anope::string s;
+ for (unsigned i = it->first.length(); i < this->longest; ++i)
+ s += " ";
+ s += Anope::string(translate(this->user, it->first.c_str())) + ": " + it->second;
+
+ buffer.push_back(s);
+ }
+}
+
+Anope::string& InfoFormatter::operator[](const Anope::string &key)
+{
+ if (key.length() > this->longest)
+ this->longest = key.length();
+ this->replies.push_back(std::make_pair(key, ""));
+ return this->replies.back().second;
+}
+
/**
* dotime: Return the number of seconds corresponding to the given time
* string. If the given string does not represent a valid time,