4 // Exception while loading a service
8 std::string serviceName;
9 std::string excDescription;
12 service_load_exc(const std::string &serviceName, std::string &&desc) noexcept
13 : serviceName(serviceName), excDescription(std::move(desc))
18 class service_not_found : public service_load_exc
21 service_not_found(const std::string &serviceName) noexcept
22 : service_load_exc(serviceName, "Service description not found.")
27 class service_cyclic_dependency : public service_load_exc
30 service_cyclic_dependency(const std::string &serviceName) noexcept
31 : service_load_exc(serviceName, "Has cyclic dependency.")
36 class service_description_exc : public service_load_exc
39 service_description_exc(const std::string &serviceName, std::string &&extraInfo) noexcept
40 : service_load_exc(serviceName, std::move(extraInfo))
45 namespace dinit_load {
47 using string = std::string;
48 using string_iterator = std::string::iterator;
50 // exception thrown when encountering a syntax issue when reading a setting value
51 class setting_exception
56 setting_exception(const std::string &&exc_info) : info(std::move(exc_info))
60 std::string &get_info()
67 // Utility function to skip white space. Returns an iterator at the
68 // first non-white-space position (or at end).
69 inline string_iterator skipws(string_iterator i, string_iterator end)
75 if (! isspace(*i, locale::classic())) {
83 // Read a setting name.
84 inline string read_setting_name(string_iterator & i, string_iterator end)
90 const ctype<char> & facet = use_facet<ctype<char> >(locale::classic());
93 // Allow alphabetical characters, and dash (-) in setting name
94 while (i != end && (*i == '-' || *i == '.' || facet.is(ctype<char>::alpha, *i))) {
101 // Read a setting value
103 // In general a setting value is a single-line string. It may contain multiple parts
104 // separated by white space (which is normally collapsed). A hash mark - # - denotes
105 // the end of the value and the beginning of a comment (it should be preceded by
108 // Part of a value may be quoted using double quote marks, which prevents collapse
109 // of whitespace and interpretation of most special characters (the quote marks will
110 // not be considered part of the value). A backslash can precede a character (such
111 // as '#' or '"' or another backslash) to remove its special meaning. Newline
112 // characters are not allowed in values and cannot be quoted.
114 // This function expects the string to be in an ASCII-compatible, single byte
115 // encoding (the "classic" locale).
118 // service_name - the name of the service to which the setting applies
119 // i - reference to string iterator through the line
120 // end - iterator at end of line
121 // part_positions - list of <int,int> to which the position of each setting value
122 // part will be added as [start,end). May be null.
123 inline string read_setting_value(string_iterator & i, string_iterator end,
124 std::list<std::pair<unsigned,unsigned>> * part_positions = nullptr)
132 bool new_part = true;
139 part_start = rval.length();
146 if (c == '\"') break;
148 throw setting_exception("Line end inside quoted string");
150 else if (c == '\\') {
151 // A backslash escapes the following character.
156 throw setting_exception("Line end follows backslash escape character (`\\')");
167 // String wasn't terminated
168 throw setting_exception("Unterminated quoted string");
171 else if (c == '\\') {
173 part_start = rval.length();
176 // A backslash escapes the next character
182 throw setting_exception("Backslash escape (`\\') not followed by character");
185 else if (isspace(c, locale::classic())) {
186 if (! new_part && part_positions != nullptr) {
187 part_positions->emplace_back(part_start, rval.length());
192 if (*i == '#') break; // comment
193 rval += ' '; // collapse ws to a single space
197 // Possibly intended a comment; we require leading whitespace to reduce occurrence of accidental
198 // comments in setting values.
199 throw setting_exception("hashmark (`#') comment must be separated from setting value by whitespace");
203 part_start = rval.length();
212 if (part_positions != nullptr) {
213 part_positions->emplace_back(part_start, rval.length());
219 // Process an opened service file, line by line.
220 // name - the service name
221 // service_file - the service file input stream
222 // func - a function of the form:
223 // void(string &line, string &setting, string_iterator i, string_iterator end)
225 // line - the complete line
226 // setting - the setting name, from the beginning of the line
227 // i - iterator at the beginning of the setting value
228 // end - iterator marking the end of the line
230 // May throw service load exceptions or I/O exceptions if enabled on stream.
231 template <typename T>
232 void process_service_file(string name, std::istream &service_file, T func)
236 while (getline(service_file, line)) {
237 string::iterator i = line.begin();
238 string::iterator end = line.end();
243 continue; // comment line
245 string setting = read_setting_name(i, end);
247 if (i == end || (*i != '=' && *i != ':')) {
248 throw service_description_exc(name, "Badly formed line.");
250 i = skipws(++i, end);
252 func(line, setting, i, end);
257 } // namespace dinit_load
259 using dinit_load::process_service_file;