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