Files
klammertext/mac/util.h
T
ack 830b19394d Klammertext runs natively on Windows; a Windows source-install guide; the bundled sh for @eval :shell
Assembled from klammertext-dev f344a37d772c. The commands, the library,
the Standard Klammer Set, the reflection klammerset and the Python
binding build and run natively on Windows (MSYS2 UCRT64 gcc, python.org
Python), and both test tiers pass there. doc/install/windows_source_install.md
is the fifth install guide: a source install parallel to the Linux and
macOS ones, verified by following it on a pristine Windows 11.

Behaviour on every system: paths Klammertext prints use forward slashes;
output is LF; the command line is UTF-8 on Windows; @eval :shell is a
POSIX shell command everywhere -- on Windows it runs the sh Klammertext
ships, BusyBox for Windows (win/busybox/, GPLv2, with its licence and the
source pointer). KLAMMERTEXT_FONTS and KLAMMERTEXT_KLAMMERSETS are lists in
the system's PATH convention (';' on Windows). On Linux and macOS nothing
observable changes.
2026-09-25 00:33:26 +02:00

155 lines
6.8 KiB
C++

#pragma once
#include <vector>
#include <string>
#include <regex>
#include <mutex>
#include <map>
#include <memory>
#include <functional>
#include <tuple>
class Locator; // locator.h; a full include would cycle through util.h
#include <utility>
#include <algorithm>
inline std::mutex env_mutex;
class Katom;
using strings_t = std::vector<std::string>;
using string_pairs = std::vector<std::pair<std::string, std::string>>;
using string_map = std::map<std::string, std::string>;
using katom_ptr = std::shared_ptr<Katom>;
using katom_list = std::vector<Katom>;
using katom_lists = std::vector<katom_list>;
using katom_list_map = std::map<std::string, katom_list>;
using katom_iter = katom_list::iterator;
using spans_t = std::vector<std::pair<Katom, Katom>>;
using argument_value_map = std::map<std::string, std::string>;
std::string trim_left(const std::string& s);
std::string trim_right(const std::string& s);
std::string trim(std::string s);
std::string trim_char_left(std::string s, char remove);
std::string trim_char_right(std::string s, char remove);
std::string trim_char(std::string s, char remove);
std::string string_replace(const std::string& source, const std::string& old_str, const std::string& new_str);
std::string regex_escape(const std::string& s);
bool contains(const std::string& str, const std::string& substr);
bool contains(const std::vector<std::string>& strings, const std::string& element);
std::vector<std::string> regex_split(const std::string& s, const std::regex& re, bool trim_parts=true);
std::vector<std::string> word_split(const std::string& s);
// Every run of whitespace becomes one space, and the ends are trimmed. Needed
// wherever a phrase typed on one line is matched against text that was written
// across several: a klammer's description sits on its own lines in a ".k" file
// and may wrap, so "displayed verbatim" would otherwise match the klammer
// whose description happens to fit one line and miss the identically worded
// one that does not.
std::string collapse_whitespace(const std::string& s);
// Klammertext source shown as PLAIN text: "^" before an ASCII punctuation
// character is removed ("^@date" -> "@date"), nothing else changes. For an
// error message or a log line that quotes a literal description; a display
// that can render should hand the source to a Machine instead.
std::string unquote_punctuation(const std::string& s);
// The string with its first character in upper case ("table" -> "Table").
// ASCII only: a non-ASCII first byte is left as it is, since a std::string
// is bytes and the standard library has no Unicode case mapping. Unlike
// Python's str.capitalize(), the rest of the string is untouched.
std::string capitalize(std::string s);
// Case-insensitive substring test, ASCII folding only. The project takes no
// ICU or locale dependency (the same rule as the language tables and
// ":decimal"), so a non-ASCII query matches only exactly.
bool contains_fold(const std::string& haystack, const std::string& needle);
bool is_in(const std::string& s, const std::vector<std::string>& v);
bool is_not_in(const std::string& s, const std::vector<std::string>& v);
std::vector<std::string> find_all(const std::string& str, const std::regex& pattern, int match_group=0);
std::vector<std::string> find_all(const std::string& str, const std::string& pattern, int match_group=0);
std::string add_margin(const std::string& s, unsigned int margin_size);
std::string justify(const std::string& input_text, unsigned int text_width=80, unsigned int margin_width=0);
std::string join(const std::vector<std::string>& ss, const std::string& separator = " ");
std::string join(int argc, char* array[], const std::string& separator = " ");
std::string argv_to_string(int argc, char* argv[]);
std::string plural(const std::string& word, int count);
std::string plural(const std::string& word, const std::vector<std::string>& things);
std::string to_be(int count, bool present = true);
int max_length(const std::vector<std::string>& ss);
std::vector<std::pair<std::string, std::string>> environment_variables(bool allow_empty_definitions=true);
std::string replace_environment_variables(const std::string& str);
std::string abbrev(const std::string& s, unsigned int max_length=65, bool remove_newlines=true);
void remove_element(std::vector<std::string>& ss, const std::string& removed);
void remove_duplicates(std::vector<std::string>& ss);
std::string display_string(const std::string& s, unsigned int width=40, bool replace_newlines=true);
std::pair<std::string,std::string> extract_parameter_type(const std::string& parameter_name);
std::tuple<std::string, std::string, bool> regex_split_prefix(const std::regex& pattern, const std::string& text);
std::vector<std::string> dlist_split(const std::string& s);
std::string get_env_var(const std::string& var);
// A POSIX sh command as popen()/system() must receive it. On Linux and
// macOS line() is the text itself. On Windows popen() runs cmd.exe, which
// cannot carry a quoted string with an embedded newline, so the command is
// written to a script file and line() runs the sh Klammertext ships
// (win/busybox/) on that file; the file lives as long as this object. exec()
// uses it, so every command Klammertext composes runs under sh on every
// system; code that calls popen()/system() itself (eval.cpp,
// tst/klammer_test) uses it too.
class Shell_command
{
public:
Shell_command(const std::string& sh_command, const Locator& loc);
~Shell_command();
Shell_command(const Shell_command&) = delete;
Shell_command& operator=(const Shell_command&) = delete;
const std::string& line() const { return m_line; }
private:
std::string m_line {};
std::string m_script {}; // the script file, Windows only
};
std::string freplace(const std::string& src, const std::regex& pattern,
const std::function<std::string(std::smatch)>& func);
std::string exec(const char* cmd);
inline std::string q_(const std::string& s)
{
return "\"" + s + "\"";
}
inline std::string qq_(const std::string& s)
{
if (s.find('\n') == std::string::npos) {
return "\"" + s + "\"";
} else {
return "\"\"\"" + s + "\"\"\"";
}
}
template <typename T, typename Pred>
std::vector<T> collect_if(const std::vector<T>& xs, Pred pred) {
std::vector<T> out;
out.reserve(xs.size());
for (const auto& v : xs) {
if (pred(v)) out.push_back(v);
}
out.shrink_to_fit();
return out;
}
template <typename T>
bool all_equal(const std::vector<T>& v) {
if (v.size() < 2) return true;
return std::adjacent_find(v.begin(), v.end(), std::not_equal_to<T>{}) == v.end();
}
template <typename T>
int max_key_length(const std::map<std::string, T>& map)
{
size_t result = 0;
for_each(map.begin(), map.end(),
[&result](const auto& item) { result = std::max(result, item.first.size()); });
return result;
}