poafloc

poafloc - Parser Of Arguments For Lines Of Commands
git clone git://git.dimitrijedobrota.com/poafloc.git
Log | Files | Refs | README | LICENSE

help.cpp (5626B)


      1 #include <cstring>
      2 #include <format>
      3 #include <sstream>
      4 
      5 #include "poafloc/poafloc.hpp"
      6 
      7 namespace poafloc {
      8 
      9 bool Parser::help_entry_t::operator<(const help_entry_t& rhs) const
     10 {
     11   if (m_group != rhs.m_group)
     12   {
     13     if ((m_group != 0) && (rhs.m_group != 0))
     14     {
     15       if (m_group < 0 && rhs.m_group < 0) return m_group < rhs.m_group;
     16       if (m_group < 0 || rhs.m_group < 0) return rhs.m_group < 0;
     17       return m_group < rhs.m_group;
     18     }
     19 
     20     return m_group == 0;
     21   }
     22 
     23   const char ch1 = !m_opt_long.empty() ? m_opt_long.front()[0]
     24       : !m_opt_short.empty()           ? m_opt_short.front()
     25                                        : '0';
     26 
     27   const char ch22 = !rhs.m_opt_long.empty() ? rhs.m_opt_long.front()[0]
     28       : !rhs.m_opt_short.empty()            ? rhs.m_opt_short.front()
     29                                             : '0';
     30 
     31   if (ch1 != ch22)
     32   {
     33     return ch1 < ch22;
     34   }
     35 
     36   if (!m_opt_long.empty() || !rhs.m_opt_long.empty())
     37   {
     38     return !m_opt_long.empty();
     39   }
     40 
     41   return std::strcmp(m_opt_long.front(), rhs.m_opt_long.front()) < 0;
     42 }
     43 
     44 void Parser::print_other_usages(FILE* stream) const
     45 {
     46   if (m_argp->doc != nullptr)
     47   {
     48     std::istringstream iss(m_argp->doc);
     49     std::string str;
     50 
     51     std::getline(iss, str, '\n');
     52     std::ignore = std::fprintf(stream, " %s", str.c_str());
     53 
     54     while (std::getline(iss, str, '\n'))
     55     {
     56       std::ignore = std::fprintf(
     57           stream, "\n   or: %s [OPTIONS...] %s", m_name.c_str(), str.c_str());
     58     }
     59   }
     60 }
     61 
     62 void Parser::help(FILE* stream) const
     63 {
     64   std::string msg1;
     65   std::string msg2;
     66 
     67   if (m_argp->message != nullptr)
     68   {
     69     std::istringstream iss(m_argp->message);
     70     std::getline(iss, msg1, '\v');
     71     std::getline(iss, msg2, '\v');
     72   }
     73 
     74   std::ignore = std::fprintf(stream, "Usage: %s [OPTIONS...]", m_name.c_str());
     75   print_other_usages(stream);
     76 
     77   if (!msg1.empty()) std::ignore = std::fprintf(stream, "\n%s", msg1.c_str());
     78   std::ignore = std::fprintf(stream, "\n\n");
     79 
     80   bool first = true;
     81   for (const auto& entry : m_help_entries)
     82   {
     83     bool prev = false;
     84 
     85     if (entry.m_opt_short.empty() && entry.m_opt_long.empty())
     86     {
     87       if (!first) std::ignore = std::putc('\n', stream);
     88       if (entry.m_message != nullptr)
     89         std::ignore = std::fprintf(stream, " %s:\n", entry.m_message);
     90 
     91       continue;
     92     }
     93 
     94     first = false;
     95 
     96     std::string message = "  ";
     97     for (const char shrt : entry.m_opt_short)
     98     {
     99       if (!prev) prev = true;
    100       else message += ", ";
    101 
    102       message += std::format("-{}", shrt);
    103 
    104       if ((entry.m_arg == nullptr) || !entry.m_opt_long.empty()) continue;
    105 
    106       if (entry.m_opt) message += std::format("[{}]", entry.m_arg);
    107       else message += std::format(" {}", entry.m_arg);
    108     }
    109 
    110     if (!prev) message += "    ";
    111 
    112     for (const auto* const lng : entry.m_opt_long)
    113     {
    114       if (!prev) prev = true;
    115       else message += ", ";
    116 
    117       message += std::format("--{}", lng);
    118 
    119       if (entry.m_arg == nullptr) continue;
    120 
    121       if (entry.m_opt) message += std::format("[={}]", entry.m_arg);
    122       else message += std::format("={}", entry.m_arg);
    123     }
    124 
    125     static const int limit = 30;
    126     if (size(message) < limit)
    127       message += std::string(limit - size(message), ' ');
    128 
    129     std::ignore = std::fprintf(stream, "%s", message.c_str());
    130 
    131     if (entry.m_message != nullptr)
    132     {
    133       std::istringstream iss(entry.m_message);
    134       std::size_t count = 0;
    135       std::string str;
    136 
    137       std::ignore = std::fprintf(stream, "   ");
    138       while (iss >> str)
    139       {
    140         count += size(str);
    141         if (count > limit)
    142         {
    143           std::ignore = std::fprintf(stream, "\n%*c", limit + 5, ' ');
    144           count       = size(str);
    145         }
    146         std::ignore = std::fprintf(stream, "%s ", str.c_str());
    147       }
    148     }
    149     std::ignore = std::putc('\n', stream);
    150   }
    151 
    152   if (!msg2.empty())
    153     std::ignore = std::fprintf(stream, "\n%s\n", msg2.c_str());
    154 }
    155 
    156 void Parser::usage(FILE* stream) const
    157 {
    158   static const std::size_t limit = 60;
    159   static std::size_t count       = 0;
    160 
    161   const auto print = [&stream](const std::string& message)
    162   {
    163     if (count + size(message) > limit)
    164       count = static_cast<std::size_t>(std::fprintf(stream, "\n      "));
    165 
    166     std::ignore = std::fprintf(stream, "%s", message.c_str());
    167     count += size(message);
    168   };
    169 
    170   std::string message = std::format("Usage: {}", m_name);
    171 
    172   message += " [-";
    173   for (const auto& entry : m_help_entries)
    174   {
    175     if (entry.m_arg != nullptr) continue;
    176 
    177     for (const char shrt : entry.m_opt_short) message += shrt;
    178   }
    179   message += "]";
    180 
    181   std::ignore = std::fprintf(stream, "%s", message.c_str());
    182   count       = size(message);
    183 
    184   for (const auto& entry : m_help_entries)
    185   {
    186     if (entry.m_arg == nullptr) continue;
    187     for (const char shrt : entry.m_opt_short)
    188     {
    189       if (entry.m_opt) print(std::format(" [-{}[{}]]", shrt, entry.m_arg));
    190       else print(std::format(" [-{} {}]", shrt, entry.m_arg));
    191     }
    192   }
    193 
    194   for (const auto& entry : m_help_entries)
    195   {
    196     for (const char* name : entry.m_opt_long)
    197     {
    198       if (entry.m_arg == nullptr)
    199       {
    200         print(std::format(" [--{}]", name));
    201         continue;
    202       }
    203 
    204       if (entry.m_opt) print(std::format(" [--{}[={}]]", name, entry.m_arg));
    205       else print(std::format(" [--{}={}]", name, entry.m_arg));
    206     }
    207   }
    208 
    209   print_other_usages(stream);
    210   std::ignore = std::putc('\n', stream);
    211 }
    212 
    213 void Parser::see(FILE* stream) const
    214 {
    215   std::ignore =
    216       std::fprintf(stream,
    217                    "Try '%s --help' or '%s --usage' for more information\n",
    218                    m_name.c_str(),
    219                    m_name.c_str());
    220 }
    221 
    222 }  // namespace poafloc