+/** Maps from characters to statistics descriptors.
+ * Statistics descriptors with no single-character alias are not included.
+ */
+static struct StatDesc *statsmap[256];
+/** Number of statistics descriptors. */
+static int statscount;
+
+/** Compare two StatDesc structures by long name (StatDesc::sd_name).
+ * @param[in] a_ Pointer to a StatDesc.
+ * @param[in] b_ Pointer to a StatDesc.
+ * @return Less than, equal to, or greater than zero if \a a_ is
+ * lexicographically less than, equal to, or greater than \a b_.
+ */
+static int
+stats_cmp(const void *a_, const void *b_)
+{
+ const struct StatDesc *a = a_;
+ const struct StatDesc *b = b_;
+ return ircd_strcmp(a->sd_name, b->sd_name);
+}
+
+/** Compare a StatDesc's name against a string.
+ * @param[in] key Pointer to a null-terminated string.
+ * @param[in] sd_ Pointer to a StatDesc.
+ * @return Less than, equal to, or greater than zero if \a key is
+ * lexicographically less than, equal to, or greater than \a
+ * sd_->sd_name.
+ */
+static int
+stats_search(const void *key, const void *sd_)
+{
+ const struct StatDesc *sd = sd_;
+ return ircd_strcmp(key, sd->sd_name);
+}
+
+/** Look up a stats handler. If name_or_char is just one character
+ * long, use that as a character index; otherwise, look it up by name
+ * in #statsinfo.
+ * @param[in] name_or_char Null-terminated string to look up.
+ * @return The statistics descriptor for \a name_or_char (NULL if none).
+ */
+const struct StatDesc *
+stats_find(const char *name_or_char)
+{
+ if (!name_or_char[1])
+ return statsmap[name_or_char[0] - CHAR_MIN];
+ else
+ return bsearch(name_or_char, statsinfo, statscount, sizeof(statsinfo[0]), stats_search);
+}