]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/s_stats.c
2 * IRC - Internet Relay Chat, ircd/s_stats.c
3 * Copyright (C) 2000 Joseph Bongaarts
5 * See file AUTHORS in IRC package for additional names of
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "ircd_chattr.h"
30 #include "ircd_events.h"
31 #include "ircd_features.h"
32 #include "ircd_crypt.h"
34 #include "ircd_reply.h"
35 #include "ircd_string.h"
44 #include "querycmds.h"
64 * @brief Report configuration lines and other statistics from this
66 * @version $Id: s_stats.c,v 1.44.2.4 2006/03/14 14:56:50 entrope Exp $
68 * Note: The info is reported in the order the server uses
69 * it--not reversed as in ircd.conf!
72 /* The statsinfo array should only be used in this file, but just TRY
73 * telling the compiler that you want to forward declare a static
74 * array without specifying a length, and see how it responds. So we
75 * forward declare it "extern".
77 extern struct StatDesc statsinfo
[];
79 /** Report items from #GlobalConfList.
80 * Uses sd->sd_funcdata as a filter for ConfItem::status.
81 * @param[in] sptr Client requesting statistics.
82 * @param[in] sd Stats descriptor for request.
83 * @param[in] param Extra parameter from user (ignored).
86 stats_configured_links(struct Client
*sptr
, const struct StatDesc
* sd
,
89 static char null
[] = "<NULL>";
91 unsigned short int port
;
93 char *host
, *pass
, *name
, *username
, *hub_limit
;
95 for (tmp
= GlobalConfList
; tmp
; tmp
= tmp
->next
)
97 if ((tmp
->status
& sd
->sd_funcdata
))
99 host
= BadPtr(tmp
->host
) ? null
: tmp
->host
;
100 pass
= BadPtr(tmp
->passwd
) ? null
: tmp
->passwd
;
101 name
= BadPtr(tmp
->name
) ? null
: tmp
->name
;
102 username
= BadPtr(tmp
->username
) ? null
: tmp
->username
;
103 hub_limit
= BadPtr(tmp
->hub_limit
) ? null
: tmp
->hub_limit
;
104 maximum
= tmp
->maximum
;
105 port
= tmp
->address
.port
;
107 if (tmp
->status
& CONF_UWORLD
)
108 send_reply(sptr
, RPL_STATSULINE
, host
);
109 else if (tmp
->status
& CONF_SERVER
)
110 send_reply(sptr
, RPL_STATSCLINE
, name
, port
, maximum
, hub_limit
, get_conf_class(tmp
));
111 else if (tmp
->status
& CONF_CLIENT
)
112 send_reply(sptr
, RPL_STATSILINE
,
113 (tmp
->username
? tmp
->username
: ""), (tmp
->username
? "@" : ""),
114 (tmp
->host
? tmp
->host
: "*"), maximum
,
115 (name
[0] == ':' ? "0" : ""), (tmp
->name
? tmp
->name
: "*"),
116 port
, get_conf_class(tmp
));
117 else if (tmp
->status
& CONF_OPERATOR
)
118 send_reply(sptr
, RPL_STATSOLINE
,
119 ((FlagHas(&tmp
->privs_dirty
, PRIV_PROPAGATE
)
120 && FlagHas(&tmp
->privs
, PRIV_PROPAGATE
))
121 || (FlagHas(&tmp
->conn_class
->privs_dirty
, PRIV_PROPAGATE
)
122 && FlagHas(&tmp
->conn_class
->privs
, PRIV_PROPAGATE
)))
123 ? 'O' : 'o', username
, host
, name
, get_conf_class(tmp
));
128 /** Report connection rules from conf_get_crule_list().
129 * Uses sd->sd_funcdata as a filter for CRuleConf::type.
130 * @param[in] to Client requesting statistics.
131 * @param[in] sd Stats descriptor for request.
132 * @param[in] param Extra parameter from user (ignored).
135 stats_crule_list(struct Client
* to
, const struct StatDesc
*sd
,
138 const struct CRuleConf
* p
= conf_get_crule_list();
140 for ( ; p
; p
= p
->next
)
142 if (p
->type
& sd
->sd_funcdata
)
143 send_reply(to
, RPL_STATSDLINE
, (p
->type
& CRULE_ALL
? 'D' : 'd'), p
->hostmask
, p
->rule
);
147 /** Report active event engine name.
148 * @param[in] to Client requesting statistics.
149 * @param[in] sd Stats descriptor for request (ignored).
150 * @param[in] param Extra parameter from user (ignored).
153 stats_engine(struct Client
*to
, const struct StatDesc
*sd
, char *param
)
155 send_reply(to
, RPL_STATSENGINE
, engine_name());
158 /** Report client access lists.
159 * @param[in] to Client requesting statistics.
160 * @param[in] sd Stats descriptor for request.
161 * @param[in] param Filter for hostname or IP (NULL to show all).
164 stats_access(struct Client
*to
, const struct StatDesc
*sd
, char *param
)
166 struct ConfItem
*aconf
;
172 stats_configured_links(to
, sd
, param
);
176 wilds
= string_has_wildcards(param
);
178 for (aconf
= GlobalConfList
; aconf
; aconf
= aconf
->next
)
180 if (aconf
->status
!= CONF_CLIENT
)
182 if (wilds
? ((aconf
->host
&& !mmatch(aconf
->host
, param
))
183 || (aconf
->name
&& !mmatch(aconf
->name
, param
)))
184 : ((aconf
->host
&& !match(param
, aconf
->host
))
185 || (aconf
->name
&& !match(param
, aconf
->name
))))
187 send_reply(to
, RPL_STATSILINE
,
188 (aconf
->username
? aconf
->username
: ""), (aconf
->username
? "@" : ""),
189 (aconf
->host
? aconf
->host
: "*"), aconf
->maximum
,
190 (aconf
->name
&& aconf
->name
[0] == ':' ? "0":""),
191 aconf
->name
? aconf
->name
: "*",
192 aconf
->address
.port
, get_conf_class(aconf
));
200 /** Report DenyConf entries.
201 * @param[in] to Client requesting list.
204 report_deny_list(struct Client
* to
)
206 const struct DenyConf
* p
= conf_get_deny_list();
207 for ( ; p
; p
= p
->next
)
208 send_reply(to
, RPL_STATSKLINE
, p
->bits
> 0 ? 'k' : 'K',
209 p
->usermask
? p
->usermask
: "*",
210 p
->hostmask
? p
->hostmask
: "*",
211 p
->message
? p
->message
: "(none)",
212 p
->realmask
? p
->realmask
: "*");
215 /** Report K/k-lines to a user.
216 * @param[in] sptr Client requesting statistics.
217 * @param[in] sd Stats descriptor for request (ignored).
218 * @param[in] mask Filter for hostmasks to show.
221 stats_klines(struct Client
*sptr
, const struct StatDesc
*sd
, char *mask
)
228 const struct DenyConf
* conf
;
236 need_more_params(sptr
, "STATS K");
238 report_deny_list(sptr
);
244 wilds
= string_has_wildcards(mask
);
247 if ((host
= strchr(mask
, '@')))
255 for (conf
= conf_get_deny_list(); conf
; conf
= conf
->next
)
257 /* Skip this block if the user is searching for a user-matching
258 * mask but the current Kill doesn't have a usermask, or if user
259 * is searching for a host-matching mask but the Kill has no
260 * hostmask, or if the user mask is specified and doesn't match,
261 * or if the host mask is specified and doesn't match.
263 if ((user
&& !conf
->usermask
)
264 || (host
&& !conf
->hostmask
)
265 || (user
&& conf
->usermask
267 ? mmatch(user
, conf
->usermask
)
268 : match(conf
->usermask
, user
)))
269 || (host
&& conf
->hostmask
271 ? mmatch(host
, conf
->hostmask
)
272 : match(conf
->hostmask
, host
))))
274 send_reply(sptr
, RPL_STATSKLINE
, conf
->bits
> 0 ? 'k' : 'K',
275 conf
->usermask
? conf
->usermask
: "*",
276 conf
->hostmask
? conf
->hostmask
: "*",
277 conf
->message
? conf
->message
: "(none)",
278 conf
->realmask
? conf
->realmask
: "*");
284 /** Report on servers and/or clients connected to the network.
285 * @param[in] sptr Client requesting statistics.
286 * @param[in] sd Stats descriptor for request (ignored).
287 * @param[in] name Filter for client names to show.
290 stats_links(struct Client
* sptr
, const struct StatDesc
* sd
, char* name
)
292 struct Client
*acptr
;
297 wilds
= string_has_wildcards(name
);
300 * Send info about connections which match, or all if the
301 * mask matches me.name. Only restrictions are on those who
302 * are invisible not being visible to 'foreigners' who use
303 * a wild card based search to list it.
305 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSLINKINFO
, "Connection SendQ "
306 "SendM SendKBytes RcveM RcveKBytes :Open since");
307 for (i
= 0; i
<= HighestFd
; i
++)
309 if (!(acptr
= LocalClientArray
[i
]))
311 /* Don't return clients when this is a request for `all' */
312 if (!name
&& IsUser(acptr
))
314 /* Don't show invisible people to non opers unless they know the nick */
315 if (IsInvisible(acptr
) && (!name
|| wilds
) && !IsAnOper(acptr
) &&
318 /* Only show the ones that match the given mask - if any */
319 if (name
&& wilds
&& match(name
, cli_name(acptr
)))
321 /* Skip all that do not match the specific query */
322 if (!(!name
|| wilds
) && 0 != ircd_strcmp(name
, cli_name(acptr
)))
324 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSLINKINFO
,
325 "%s %u %u %Lu %u %Lu :%Tu",
326 (*(cli_name(acptr
))) ? cli_name(acptr
) : "<unregistered>",
327 (int)MsgQLength(&(cli_sendQ(acptr
))), (int)cli_sendM(acptr
),
328 (cli_sendB(acptr
) >> 10), (int)cli_receiveM(acptr
),
329 (cli_receiveB(acptr
) >> 10), CurrentTime
- cli_firsttime(acptr
));
333 /** Report on loaded modules.
334 * @param[in] to Client requesting statistics.
335 * @param[in] sd Stats descriptor for request (ignored).
336 * @param[in] param Extra parameter from user (ignored).
339 stats_modules(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
341 crypt_mechs_t
* mechs
;
343 send_reply(to
, SND_EXPLICIT
| RPL_STATSLLINE
,
344 "Module Description Entry Point");
346 /* atm the only "modules" we have are the crypto mechanisms,
347 eventualy they'll be part of a global dl module list, for now
348 i'll just output data about them -- hikari */
350 if(crypt_mechs_root
== NULL
)
353 mechs
= crypt_mechs_root
->next
;
360 send_reply(to
, SND_EXPLICIT
| RPL_STATSLLINE
,
362 mechs
->mech
->shortname
, mechs
->mech
->description
,
363 mechs
->mech
->crypt_function
);
370 /** Report how many times each command has been used.
371 * @param[in] to Client requesting statistics.
372 * @param[in] sd Stats descriptor for request (ignored).
373 * @param[in] param Extra parameter from user (ignored).
376 stats_commands(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
378 struct Message
*mptr
;
380 for (mptr
= msgtab
; mptr
->cmd
; mptr
++)
382 send_reply(to
, RPL_STATSCOMMANDS
, mptr
->cmd
, mptr
->count
, mptr
->bytes
);
385 /** List channel quarantines.
386 * @param[in] to Client requesting statistics.
387 * @param[in] sd Stats descriptor for request (ignored).
388 * @param[in] param Filter for quarantined channel names.
391 stats_quarantine(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
395 for (qline
= GlobalQuarantineList
; qline
; qline
= qline
->next
)
397 if (param
&& match(param
, qline
->chname
)) /* narrow search */
399 send_reply(to
, RPL_STATSQLINE
, qline
->chname
, qline
->reason
);
403 /** List service pseudo-command mappings.
404 * @param[in] to Client requesting statistics.
405 * @param[in] sd Stats descriptor for request (ignored).
406 * @param[in] param Extra parameter from user (ignored).
409 stats_mapping(struct Client
*to
, const struct StatDesc
* sd
, char* param
)
413 send_reply(to
, RPL_STATSRLINE
, "Command", "Name", "Prepend", "Target");
414 for (map
= GlobalServiceMapList
; map
; map
= map
->next
) {
415 struct nick_host
*nh
;
416 for (nh
= map
->services
; nh
; nh
= nh
->next
) {
417 send_reply(to
, RPL_STATSRLINE
, map
->command
, map
->name
,
418 (map
->prepend
? map
->prepend
: "*"), nh
->nick
);
423 /** Report server uptime and maximum connection/client counts.
424 * @param[in] to Client requesting statistics.
425 * @param[in] sd Stats descriptor for request (ignored).
426 * @param[in] param Extra parameter from user (ignored).
429 stats_uptime(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
433 nowr
= CurrentTime
- cli_since(&me
);
434 send_reply(to
, RPL_STATSUPTIME
, nowr
/ 86400, (nowr
/ 3600) % 24,
435 (nowr
/ 60) % 60, nowr
% 60);
436 send_reply(to
, RPL_STATSCONN
, max_connection_count
, max_client_count
);
439 /** Verbosely report on servers connected to the network.
440 * If sd->sd_funcdata != 0, then display in a more human-friendly format.
441 * @param[in] sptr Client requesting statistics.
442 * @param[in] sd Stats descriptor for request.
443 * @param[in] param Filter for server names to display.
446 stats_servers_verbose(struct Client
* sptr
, const struct StatDesc
* sd
,
449 struct Client
*acptr
;
453 * lowercase 'v' is for human-readable,
454 * uppercase 'V' is for machine-readable
456 if (sd
->sd_funcdata
) {
457 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSVERBOSE
,
458 "%-20s %-20s Flags Hops Numeric Lag RTT Up Down "
459 "Clients/Max Proto %-10s :Info", "Servername", "Uplink",
461 fmt
= "%-20s %-20s %c%c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i P%-2i %Tu :%s";
463 fmt
= "%s %s %c%c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s";
466 for (acptr
= GlobalClientList
; acptr
; acptr
= cli_next(acptr
))
468 if (!IsServer(acptr
) && !IsMe(acptr
))
471 if (param
&& match(param
, cli_name(acptr
)))
473 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSVERBOSE
, fmt
,
475 cli_name(cli_serv(acptr
)->up
),
476 IsBurst(acptr
) ? 'B' : '-',
477 IsBurstAck(acptr
) ? 'A' : '-',
478 IsHub(acptr
) ? 'H' : '-',
479 IsService(acptr
) ? 'S' : '-',
480 IsIPv6(acptr
) ? '6' : '-',
483 base64toint(cli_yxx(acptr
)),
484 cli_serv(acptr
)->lag
,
485 cli_serv(acptr
)->asll_rtt
,
486 cli_serv(acptr
)->asll_to
,
487 cli_serv(acptr
)->asll_from
,
488 (acptr
== &me
? UserStats
.local_clients
: cli_serv(acptr
)->clients
),
489 cli_serv(acptr
)->nn_mask
,
490 cli_serv(acptr
)->prot
,
491 cli_serv(acptr
)->timestamp
,
496 /** Display objects allocated (and total memory used by them) for
497 * several types of structures.
498 * @param[in] to Client requesting statistics.
499 * @param[in] sd Stats descriptor for request (ignored).
500 * @param[in] param Extra parameter from user (ignored).
503 stats_meminfo(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
505 extern void bans_send_meminfo(struct Client
*cptr
);
507 class_send_meminfo(to
);
508 bans_send_meminfo(to
);
509 send_listinfo(to
, 0);
512 /** Send a list of available statistics.
513 * @param[in] to Client requesting statistics.
514 * @param[in] sd Stats descriptor for request.
515 * @param[in] param Extra parameter from user (ignored).
518 stats_help(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
520 struct StatDesc
*asd
;
522 /* only if it's my user */
524 for (asd
= statsinfo
; asd
->sd_name
; asd
++)
525 if (asd
!= sd
) /* don't send the help for us */
526 sendcmdto_one(&me
, CMD_NOTICE
, to
, "%C :%c (%s) - %s", to
, asd
->sd_c
,
527 asd
->sd_name
, asd
->sd_desc
);
530 /** Contains information about all statistics. */
531 struct StatDesc statsinfo
[] = {
532 { 'a', "nameservers", STAT_FLAG_OPERFEAT
|STAT_FLAG_LOCONLY
, FEAT_HIS_STATS_a
,
533 report_dns_servers
, 0,
535 { 'c', "connect", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_c
,
536 stats_configured_links
, CONF_SERVER
,
537 "Remote server connection lines." },
538 { 'd', "maskrules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_d
,
539 stats_crule_list
, CRULE_MASK
,
540 "Dynamic routing configuration." },
541 { 'D', "crules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_d
,
542 stats_crule_list
, CRULE_ALL
,
543 "Dynamic routing configuration." },
544 { 'e', "engine", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_e
,
546 "Report server event loop engine." },
547 { 'f', "features", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_f
,
549 "Feature settings." },
550 { 'g', "glines", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_g
,
552 "Global bans (G-lines)." },
553 { 'i', "access", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_i
,
554 stats_access
, CONF_CLIENT
,
555 "Connection authorization lines." },
556 { 'j', "histogram", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_j
,
558 "Message length histogram." },
559 { 'J', "jupes", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_J
,
562 { 'k', "klines", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_k
,
564 "Local bans (K-Lines)." },
565 { 'l', "links", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
),
568 "Current connections information." },
569 { 'L', "modules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
),
572 "Dynamically loaded modules." },
573 { 'm', "commands", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_m
,
575 "Message usage information." },
576 { 'o', "operators", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_o
,
577 stats_configured_links
, CONF_OPERATOR
,
578 "Operator information." },
579 { 'p', "ports", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_p
,
581 "Listening ports." },
582 { 'q', "quarantines", (STAT_FLAG_OPERONLY
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_q
,
584 "Quarantined channels list." },
585 { 'R', "mappings", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_R
,
587 "Service mappings." },
589 { 'r', "usage", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_r
,
591 "System resource usage (Debug only)." },
593 { 'T', "motds", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_T
,
595 "Configured Message Of The Day files." },
596 { 't', "locals", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_t
,
598 "Local connection statistics (Total SND/RCV, etc)." },
599 { 'U', "uworld", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_U
,
600 stats_configured_links
, CONF_UWORLD
,
601 "Service server information." },
602 { 'u', "uptime", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_u
,
604 "Current uptime & highest connection count." },
605 { 'v', "vservers", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_v
,
606 stats_servers_verbose
, 1,
607 "Verbose server information." },
608 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_v
,
609 stats_servers_verbose
, 0,
610 "Verbose server information." },
611 { 'w', "userload", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_w
,
613 "Userload statistics." },
614 { 'x', "memusage", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_x
,
616 "List usage information." },
617 { 'y', "classes", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_y
,
619 "Connection classes." },
620 { 'z', "memory", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_z
,
622 "Memory/Structure allocation information." },
623 { ' ', "iauth", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_IAUTH
,
624 report_iauth_stats
, 0,
625 "IAuth statistics." },
626 { ' ', "iauthconf", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_IAUTH
,
627 report_iauth_conf
, 0,
628 "IAuth configuration." },
629 { '*', "help", STAT_FLAG_CASESENS
, FEAT_LAST_F
,
631 "Send help for stats." },
632 { '\0', 0, FEAT_LAST_F
, 0, 0, 0 }
635 /** Maps from characters to statistics descriptors.
636 * Statistics descriptors with no single-character alias are not included.
638 static struct StatDesc
*statsmap
[256];
639 /** Number of statistics descriptors. */
640 static int statscount
;
642 /** Compare two StatDesc structures by long name (StatDesc::sd_name).
643 * @param[in] a_ Pointer to a StatDesc.
644 * @param[in] b_ Pointer to a StatDesc.
645 * @return Less than, equal to, or greater than zero if \a a_ is
646 * lexicographically less than, equal to, or greater than \a b_.
649 stats_cmp(const void *a_
, const void *b_
)
651 const struct StatDesc
*a
= a_
;
652 const struct StatDesc
*b
= b_
;
653 return ircd_strcmp(a
->sd_name
, b
->sd_name
);
656 /** Compare a StatDesc's name against a string.
657 * @param[in] key Pointer to a null-terminated string.
658 * @param[in] sd_ Pointer to a StatDesc.
659 * @return Less than, equal to, or greater than zero if \a key is
660 * lexicographically less than, equal to, or greater than \a
664 stats_search(const void *key
, const void *sd_
)
666 const struct StatDesc
*sd
= sd_
;
667 return ircd_strcmp(key
, sd
->sd_name
);
670 /** Look up a stats handler. If name_or_char is just one character
671 * long, use that as a character index; otherwise, look it up by name
673 * @param[in] name_or_char Null-terminated string to look up.
674 * @return The statistics descriptor for \a name_or_char (NULL if none).
676 const struct StatDesc
*
677 stats_find(const char *name_or_char
)
679 if (!name_or_char
[1])
680 return statsmap
[name_or_char
[0] - CHAR_MIN
];
682 return bsearch(name_or_char
, statsinfo
, statscount
, sizeof(statsinfo
[0]), stats_search
);
685 /** Build statsmap from the statsinfo array. */
691 /* Count number of stats entries and sort them. */
692 for (statscount
= 0, sd
= statsinfo
; sd
->sd_name
; sd
++, statscount
++) {}
693 qsort(statsinfo
, statscount
, sizeof(statsinfo
[0]), stats_cmp
);
695 /* Build the mapping */
696 for (sd
= statsinfo
; sd
->sd_name
; sd
++)
700 else if (sd
->sd_flags
& STAT_FLAG_CASESENS
)
701 /* case sensitive character... */
702 statsmap
[sd
->sd_c
- CHAR_MIN
] = sd
;
705 /* case insensitive--make sure to put in two entries */
706 statsmap
[ToLower(sd
->sd_c
) - CHAR_MIN
] = sd
;
707 statsmap
[ToUpper(sd
->sd_c
) - CHAR_MIN
] = sd
;