1 /*****************************************************************************\
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 2000-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "ini_parser.h"
16 #include "table_configurator.h"
17 #include "variable_tokenizer.h"
19 #include <basis/astring.h>
20 #include <basis/functions.h>
21 #include <structures/amorph.h>
22 #include <structures/string_array.h>
23 #include <structures/string_table.h>
24 #include <textual/parser_bits.h>
26 //#define DEBUG_INI_PARSER
27 // uncomment for noisy version.
30 #ifdef DEBUG_INI_PARSER
31 #define LOG(to_print) printf("%s\n", astring(to_print).s())
38 using namespace basis;
39 using namespace structures;
40 using namespace textual;
43 namespace configuration {
46 // gather section until next section definition or end of file.
47 // parse the section with variable_tokenizer.
48 // eat that out of the string.
51 ini_parser::ini_parser(const astring &to_parse, treatment_of_defaults behavior)
52 : table_configurator(behavior),
59 ini_parser::~ini_parser()
64 void ini_parser::chow_through_eol(astring &to_chow)
66 while (to_chow.length()) {
67 if (parser_bits::is_eol(to_chow[0])) {
68 // zap all carriage return type chars now that we found one.
69 while (to_chow.length() && parser_bits::is_eol(to_chow[0])) {
70 *_preface += to_chow[0];
73 return; // mission accomplished.
75 *_preface += to_chow[0];
81 //this is a super expensive operation...
82 // it would be better to have the parser be a bit more intelligent.
83 void strip_blank_lines(astring &to_strip)
85 bool last_was_ret = false;
86 for (int i = 0; i < to_strip.length(); i++) {
87 if (parser_bits::is_eol(to_strip[i])) {
89 // two in a row; now that's bogus.
95 to_strip[i] = '\n'; // make sure we know which type to look for.
97 if (last_was_ret && parser_bits::white_space(to_strip[i])) {
98 // well, the last was a return but this is white space. that's also
104 last_was_ret = false;
110 void ini_parser::reset(const astring &to_parse)
112 _well_formed = false;
113 table_configurator::reset(); // clean out existing contents.
114 _preface->reset(); // set the preface string back to nothing.
118 void ini_parser::add(const astring &to_parse)
120 astring parsing = to_parse;
121 // strip_blank_lines(parsing);
122 _preface->reset(); // set the preface string back to nothing.
123 while (parsing.length()) {
124 astring section_name;
125 bool found_sect = parse_section(parsing, section_name);
127 // the line is not a section name. toss it.
128 chow_through_eol(parsing);
129 continue; // try to find another section name.
131 // we got a section. yee hah.
133 for (next_sect = 0; next_sect < parsing.length(); next_sect++) {
134 // LOG(astring("[") + astring(parsing[next_sect], 1) + "]");
135 if (parser_bits::is_eol(parsing[next_sect])) {
136 // we found the requisite return; let's see if a section beginning
137 // is just after it. we know nothing else should be, since we stripped
138 // out the blank lines and blanks after CRs.
139 if (parsing[next_sect + 1] == '[') {
140 // aha, found the bracket that should be a section start.
141 break; // done seeking next section beginning.
145 // skip back one if we hit the end of the string.
146 if (next_sect >= parsing.length()) next_sect--;
147 // now grab what should be all values within a section.
148 LOG(a_sprintf("bounds are %d to %d, string len is %d.", 0, next_sect,
150 astring sect_parsing = parsing.substring(0, next_sect);
151 LOG(astring("going to parse: >>") + sect_parsing + "<<");
152 parsing.zap(0, next_sect);
153 variable_tokenizer section_reader("\n", "=");
154 section_reader.set_comment_chars(";#");
155 section_reader.parse(sect_parsing);
156 LOG(astring("read: ") + section_reader.text_form());
157 merge_section(section_name, section_reader.table());
162 void ini_parser::merge_section(const astring §ion_name,
163 const string_table &to_merge)
165 if (!section_exists(section_name)) {
166 // didn't exist yet, so just plunk it in.
167 put_section(section_name, to_merge);
171 // since the section exists, we just write the individual entries from the
172 // new section. they'll stamp out any old values.
173 for (int i = 0; i < to_merge.symbols(); i++)
174 put(section_name, to_merge.name(i), to_merge[i]);
177 bool ini_parser::parse_section(astring &to_parse, astring §ion_name)
179 section_name = ""; // reset the section.
181 // we have a simple state machine here...
183 SEEKING_OPENING_BRACKET, // looking for the first bracket.
184 EATING_SECTION_NAME // got a bracket, now getting section name.
186 states state = SEEKING_OPENING_BRACKET;
188 // zip through the string trying to find a valid section name.
189 for (int i = 0; i < to_parse.length(); i++) {
190 char curr = to_parse[i];
191 LOG(astring("<") + astring(curr, 1) + ">");
193 case SEEKING_OPENING_BRACKET:
194 // we're looking for the first bracket now...
195 if (parser_bits::white_space(curr)) continue; // ignore white space.
196 if (curr != '[') return false; // argh, bad characters before bracket.
197 state = EATING_SECTION_NAME; // found the bracket.
199 case EATING_SECTION_NAME:
200 // we're adding to the section name now...
202 // that's the end of the section name.
203 to_parse.zap(0, i); // remove what we saw.
204 //should we take out to end of line also?
205 //eventually up to eol could be kept as a comment?
208 section_name += curr; // add a character to the name.
211 //LOG("got to unknown case in section parser!");
215 // if we got to here, the section was badly formed... the whole string was
216 // parsed through but no conclusion was reached.
220 bool ini_parser::restate(astring &new_ini, bool add_spaces)
222 new_ini = *_preface; // give it the initial text back again.
225 for (int i = 0; i < sects.length(); i++) {
226 new_ini += astring("[") + sects[i] + "]" + parser_bits::platform_eol_to_chars();
228 if (!get_section(sects[i], tab)) continue; // serious error.
229 tab.add_spaces(add_spaces);
230 new_ini += tab.text_form();