1 #pragma once 2 3 #include <set> 4 5 #include "common.hpp" 6 #include "components/config.hpp" 7 #include "components/logger.hpp" 8 #include "errors.hpp" 9 10 POLYBAR_NS 11 12 DEFINE_ERROR(parser_error); 13 14 /** 15 * \brief Exception object for syntax errors 16 * 17 * Contains filepath and line number where syntax error was found 18 */ 19 class syntax_error : public parser_error { 20 public: 21 /** 22 * Default values are used when the thrower doesn't know the position. 23 * parse_line has to catch, set the proper values and rethrow 24 */ syntax_error(string msg,const string & file="",int line_no=-1)25 explicit syntax_error(string msg, const string& file = "", int line_no = -1) 26 : parser_error(file + ":" + to_string(line_no) + ": " + msg), msg(move(msg)) {} 27 get_msg()28 const string& get_msg() { 29 return msg; 30 }; 31 32 private: 33 string msg; 34 }; 35 36 class invalid_name_error : public syntax_error { 37 public: 38 /** 39 * type is either Header or Key 40 */ invalid_name_error(const string & type,const string & name)41 invalid_name_error(const string& type, const string& name) 42 : syntax_error(type + " name '" + name + "' is empty or contains forbidden characters.") {} 43 }; 44 45 /** 46 * \brief All different types a line in a config can be 47 */ 48 enum class line_type { KEY, HEADER, COMMENT, EMPTY, UNKNOWN }; 49 50 /** 51 * \brief Storage for a single config line 52 * 53 * More sanitized than the actual string of the comment line, with information 54 * about line type and structure 55 */ 56 struct line_t { 57 /** 58 * Whether or not this struct represents a "useful" line, a line that has 59 * any semantic significance (key-value or header line) 60 * If false all other fields are not set. 61 * Set this to false, if you want to return a line that has no effect 62 * (for example when you parse a comment line) 63 */ 64 bool useful; 65 66 /** 67 * Index of the config_parser::files vector where this line is from 68 */ 69 int file_index; 70 int line_no; 71 72 /** 73 * We access header, if is_header == true otherwise we access key, value 74 */ 75 bool is_header; 76 77 /** 78 * Only set for header lines 79 */ 80 string header; 81 82 /** 83 * Only set for key-value lines 84 */ 85 string key, value; 86 }; 87 88 class config_parser { 89 public: 90 config_parser(const logger& logger, string&& file, string&& bar); 91 92 /** 93 * \brief Performs the parsing of the main config file m_file 94 * 95 * \returns config class instance populated with the parsed config 96 * 97 * \throws syntax_error If there was any kind of syntax error 98 * \throws parser_error If aynthing else went wrong 99 */ 100 config::make_type parse(); 101 102 protected: 103 /** 104 * \brief Converts the `lines` vector to a proper sectionmap 105 */ 106 sectionmap_t create_sectionmap(); 107 108 /** 109 * \brief Parses the given file, extracts key-value pairs and section 110 * headers and adds them onto the `lines` vector 111 * 112 * This method directly resolves `include-file` directives and checks for 113 * cyclic dependencies 114 * 115 * `file` is expected to be an already resolved absolute path 116 */ 117 void parse_file(const string& file, file_list path); 118 119 /** 120 * \brief Parses the given line string to create a line_t struct 121 * 122 * We use the INI file syntax (https://en.wikipedia.org/wiki/INI_file) 123 * Whitespaces (tested with isspace()) at the beginning and end of a line are ignored 124 * Keys and section names can contain any character except for the following: 125 * - spaces 126 * - equal sign (=) 127 * - semicolon (;) 128 * - pound sign (#) 129 * - Any kind of parentheses ([](){}) 130 * - colon (:) 131 * - period (.) 132 * - dollar sign ($) 133 * - backslash (\) 134 * - percent sign (%) 135 * - single and double quotes ('") 136 * So basically any character that has any kind of special meaning is prohibited. 137 * 138 * Comment lines have to start with a semicolon (;) or a pound sign (#), 139 * you cannot put a comment after another type of line. 140 * 141 * key and section names are case-sensitive. 142 * 143 * Keys are specified as `key = value`, spaces around the equal sign, as 144 * well as double quotes around the value are ignored 145 * 146 * sections are defined as [section], everything inside the square brackets is part of the name 147 * 148 * \throws syntax_error if the line isn't well formed. The syntax error 149 * does not contain the filename or line numbers because parse_line 150 * doesn't know about those. Whoever calls parse_line needs to 151 * catch those exceptions and set the file path and line number 152 */ 153 line_t parse_line(const string& line); 154 155 /** 156 * \brief Determines the type of a line read from a config file 157 * 158 * Expects that line is trimmed 159 * This mainly looks at the first character and doesn't check if the line is 160 * actually syntactically correct. 161 * HEADER ('['), COMMENT (';' or '#') and EMPTY (None) are uniquely 162 * identified by their first character (or lack thereof). Any line that 163 * is none of the above and contains an equal sign, is treated as KEY. 164 * All others are UNKNOWN 165 */ 166 static line_type get_line_type(const string& line); 167 168 /** 169 * \brief Parse a line containing a section header and returns the header name 170 * 171 * Only assumes that the line starts with '[' and is trimmed 172 * 173 * \throws syntax_error if the line doesn't end with ']' or the header name 174 * contains forbidden characters 175 */ 176 string parse_header(const string& line); 177 178 /** 179 * \brief Parses a line containing a key-value pair and returns the key name 180 * and the value string inside an std::pair 181 * 182 * Only assumes that the line contains '=' at least once and is trimmed 183 * 184 * \throws syntax_error if the key contains forbidden characters 185 */ 186 std::pair<string, string> parse_key(const string& line); 187 188 /** 189 * \brief Name of all the files the config includes values from 190 * 191 * The line_t struct uses indices to this vector to map lines to their 192 * original files. This allows us to point the user to the exact location 193 * of errors 194 */ 195 file_list m_files; 196 197 private: 198 /** 199 * \brief Checks if the given name doesn't contain any spaces or characters 200 * in config_parser::m_forbidden_chars 201 */ 202 bool is_valid_name(const string& name); 203 204 /** 205 * \brief Whether or not an xresource manager should be used 206 * 207 * Is set to true if any ${xrdb...} references are found 208 */ 209 bool use_xrm{false}; 210 211 const logger& m_log; 212 213 /** 214 * \brief Absolute path to the main config file 215 */ 216 string m_config; 217 218 /** 219 * Is used to resolve ${root...} references 220 */ 221 string m_barname; 222 223 /** 224 * \brief List of all the lines in the config (with included files) 225 * 226 * The order here matters, as we have not yet associated key-value pairs 227 * with sections 228 */ 229 vector<line_t> m_lines; 230 231 /** 232 * \brief None of these characters can be used in the key and section names 233 */ 234 const string m_forbidden_chars{"\"'=;#[](){}:.$\\%"}; 235 236 /** 237 * \brief List of names that cannot be used as section names 238 * 239 * These strings have a special meaning inside references and so the 240 * section [self] could never be referenced. 241 * 242 * Note: BAR is deprecated 243 */ 244 const std::set<string> m_reserved_section_names = {"self", "BAR", "root"}; 245 }; 246 247 POLYBAR_NS_END 248