diff options
author | Adam <Adam@anope.org> | 2011-12-19 15:37:15 -0500 |
---|---|---|
committer | Adam <Adam@anope.org> | 2011-12-19 15:37:15 -0500 |
commit | 45fc3ce1c41b06af6e03712988870ead95b72435 (patch) | |
tree | 7b574d077f77707edb3916fb43dfa30dec8b0f54 | |
parent | d320c73f23ff7a9b848b86b59c6bf91c1254e410 (diff) |
Fixed formatting of many lists and INFO outputs
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> ¶ms) @@ -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> ¶ms) + void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, 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> ¶ms) + void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) { - 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> ¶ms) + { + 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> ¶ms) { 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> ¶ms) @@ -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> ¶ms) + void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, 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> ¶ms) - { - 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> ¶ms) + { 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> ¶ms) + { + 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> ¶ms) @@ -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> ¶ms) + void ProcessList(CommandSource &source, const std::vector<Anope::string> ¶ms, 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> ¶ms) + void DoList(CommandSource &source, const std::vector<Anope::string> ¶ms) { 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> ¶ms) + { + 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> ¶ms) @@ -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> ¶ms) + void ProcessList(CommandSource &source, const std::vector<Anope::string> ¶ms, 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> ¶ms) - { - 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + void ProcessList(CommandSource &source, const std::vector<Anope::string> ¶ms, 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> ¶ms) - { - 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> ¶ms) + { + 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> ¶ms) + { + 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, |