poafloc

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

help.cpp (5470B)


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