IniParser.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. #include "IniParser.h"
  2. long IniParser::startLine = 0;
  3. long IniParser::line = 1;
  4. IniParser::IniParser() : data(_FL_, 256),
  5. keys(_FL_),
  6. sections(_FL_)
  7. {
  8. }
  9. IniParser::~IniParser()
  10. {
  11. }
  12. //Выделить буфер для внешнего заполнения
  13. void * IniParser::Reserved(long _size)
  14. {
  15. if(_size <= 0) return 0;
  16. data.Empty();
  17. data.AddElements(_size);
  18. return &data[0];
  19. }
  20. //Разобрать файл на токены для дальнейшего использования
  21. void IniParser::Parse()
  22. {
  23. //Удаляем текущее состояние
  24. keys.Empty();
  25. sections.Empty();
  26. //Текущая строка
  27. line = 1;
  28. //Начало текущей строки
  29. startLine = 0;
  30. //Добавляем первую пустую секцию
  31. Section & s = sections[sections.Add()];
  32. s.name.start = 0;
  33. s.name.size = 0;
  34. s.start = 0;
  35. s.size = 0;
  36. //Разбираем данные
  37. for(long i = 0; i < data; )
  38. {
  39. //Ищем начало токена на строке
  40. i = ParseFindStartToken(i);
  41. if(i >= data) break;
  42. //Определяем тип
  43. switch(data[i])
  44. {
  45. //Перевод строки
  46. case '\n':
  47. line++;
  48. startLine = i + 1;
  49. case '\r':
  50. i++;
  51. break;
  52. //Коментарий
  53. case ';':
  54. i = ParseSkipComment(i);
  55. break;
  56. //Секция
  57. case '[':
  58. i = ParseGetSection(i);
  59. break;
  60. //Ключ
  61. default:
  62. i = ParseGetKey(i);
  63. }
  64. }
  65. //Завершение последний секции
  66. sections[sections - 1].size = keys - sections[sections - 1].start;
  67. //Вывод отладочной информации
  68. if(false)
  69. {
  70. string value;
  71. AddError("--------------------------IniParser log--------------------------\nFile:\"%s\"\n", fileName.GetBuffer());
  72. for(long i = 0; i < sections; i++)
  73. {
  74. FillString(sections[i].name, temp);
  75. AddError("S: %4i [\"%s\"]", i, temp.GetBuffer());
  76. long start = sections[i].start;
  77. long size = sections[i].size;
  78. for(long j = 0; j < size; j++)
  79. {
  80. FillString(keys[start + j].name, temp);
  81. FillString(keys[start + j].value, value);
  82. AddError("K: %4i \"%s\" = \"%s\"", start + j, temp.GetBuffer(), value.GetBuffer());
  83. }
  84. }
  85. AddError("\n");
  86. }
  87. }
  88. //Получить все секции
  89. void IniParser::GetSections(array<string> & sects)
  90. {
  91. sects.DelAll();
  92. for(long i = 0; i < sections; i++)
  93. {
  94. string & s = sects[sects.Add()];
  95. FillString(sections[i].name, s);
  96. }
  97. }
  98. //Найти секцию
  99. long IniParser::FindSection(const char * section)
  100. {
  101. if(!section || !section[0])
  102. {
  103. Assert(sections[0].name.size == 0);
  104. return 0;
  105. }
  106. long len = strlen(section);
  107. for(long i = 0; i < sections; i++)
  108. {
  109. if(Equal(sections[i].name, section, len))
  110. {
  111. return i;
  112. }
  113. }
  114. return -1;
  115. }
  116. //Добавить новую секцию
  117. long IniParser::AddSection(const char * section)
  118. {
  119. long i = FindSection(section);
  120. if(i >= 0) return i;
  121. i = sections[sections.Size() - 1].endLine;
  122. if(keys > 0)
  123. {
  124. i = keys[keys - 1].endLine;
  125. }
  126. string str = "\r\n\r\n[";
  127. str += section;
  128. str += "]\r\n\r\n";
  129. ChangeData(i, 0, str);
  130. long index = sections.Add();
  131. Section & s = sections[index];
  132. s.name.start = i + 5;
  133. s.name.size = strlen(section);
  134. s.start = keys;
  135. s.size = 0;
  136. s.startLine = i + 4;
  137. s.endLine = s.startLine + s.name.size + 2;
  138. return index;
  139. }
  140. //Удалить секцию
  141. void IniParser::DelSection(long sectionIndex)
  142. {
  143. Section & s = sections[sectionIndex];
  144. //Удаляем все ключи в секции
  145. while(s.size)
  146. {
  147. DelKey(s.start);
  148. }
  149. //Удаляем данные секции
  150. ChangeData(s.startLine, s.endLine - s.startLine + 1, "");
  151. //Удаляем секцию
  152. sections.Extract(sectionIndex);
  153. }
  154. //Найти ключ в секции
  155. long IniParser::FindKey(long sectionIndex, const char * name, long nameLen, long keyIndex)
  156. {
  157. //Имя ключа
  158. if(sectionIndex < 0) return -1;
  159. Assert(name);
  160. Assert(nameLen > 0);
  161. //Ключи в секции
  162. long i = keyIndex < 0 ? sections[sectionIndex].start : keyIndex + 1;
  163. Assert(i >= sections[sectionIndex].start);
  164. long end = sections[sectionIndex].start + sections[sectionIndex].size;
  165. //Перебираем ключи
  166. for(; i < end; i++)
  167. {
  168. if(Equal(keys[i].name, name, nameLen))
  169. {
  170. return i;
  171. }
  172. }
  173. return -1;
  174. }
  175. //Установить новое значение ключу
  176. void IniParser::SetKey(long keyIndex, const char * value)
  177. {
  178. if(!value) value = "";
  179. Key & key = keys[keyIndex];
  180. ChangeData(key.value.start, key.value.size, value);
  181. key.value.size = strlen(value);
  182. if(key.endLine < key.value.start + key.value.size) key.endLine = key.value.start + key.value.size;
  183. Assert((dword)key.endLine <= data.Size());
  184. }
  185. //Получить значение ключа
  186. const char * IniParser::GetKey(long keyIndex)
  187. {
  188. Assert(keyIndex >= 0);
  189. FillString(keys[keyIndex].value, temp);
  190. return temp;
  191. }
  192. //Добавить ключ
  193. long IniParser::AddKey(long sectionIndex, const char * name, const char * value)
  194. {
  195. if(!value) value = "";
  196. //Если ключи у секции есть то дописываемся за последним, иначе за секцией
  197. long keyNameStart = -1;
  198. if(sections[sectionIndex].size > 0)
  199. {
  200. long lastKey = sections[sectionIndex].start + sections[sectionIndex].size - 1;
  201. keyNameStart = keys[lastKey].endLine;
  202. }else{
  203. keyNameStart = sections[sectionIndex].endLine;
  204. }
  205. //Добавляем запись секции в файл
  206. temp = "\r\n";
  207. temp += name;
  208. temp += " = ";
  209. long keyValueStart = keyNameStart + temp.Len();
  210. temp += value;
  211. ChangeData(keyNameStart, 0, temp);
  212. //Добавляем описание ключа
  213. long index = sections[sectionIndex].start + sections[sectionIndex].size;
  214. keys.Insert(index);
  215. Key & key = keys[index];
  216. sections[sectionIndex].size++;
  217. for(long j = sectionIndex + 1; j < sections; j++)
  218. {
  219. sections[j].start++;
  220. }
  221. key.name.start = keyNameStart + 2;
  222. key.name.size = strlen(name);
  223. key.value.start = keyValueStart;
  224. key.value.size = strlen(value);
  225. key.startLine = key.name.start;
  226. key.endLine = key.value.start + key.value.size;
  227. Assert((dword)key.endLine <= data.Size());
  228. return index;
  229. }
  230. //Удалить ключ
  231. void IniParser::DelKey(long keyIndex)
  232. {
  233. //Запомним строку, которую надо удалить
  234. long start = keys[keyIndex].startLine;
  235. long size = keys[keyIndex].endLine - keys[keyIndex].startLine + 1;
  236. //Удалим запись ключа
  237. for(long i = 0; i < sections; i++)
  238. {
  239. if(sections[i].start <= keyIndex)
  240. {
  241. //В своей секции уменьшим количество ключей
  242. long n = sections[i].start + sections[i].size;
  243. if(keyIndex < n)
  244. {
  245. sections[i].size--;
  246. }
  247. }else{
  248. //В дальней секции уменьшим начальный индекс
  249. sections[i].start--;
  250. }
  251. }
  252. keys.Extract(keyIndex);
  253. //Удалим данные ключа
  254. ChangeData(start, size, "");
  255. }
  256. //Получить буфер
  257. const void * IniParser::GetBuffer(dword & size)
  258. {
  259. size = data;
  260. return data.GetBuffer();
  261. }
  262. //Добавить сообщение об ошибке
  263. void _cdecl IniParser::AddError(const char * format, ...)
  264. {
  265. api->TraceData(format, (&format) + 1);
  266. }
  267. //Найти начало токена
  268. __forceinline long IniParser::ParseFindStartToken(long i)
  269. {
  270. for(; i < data; i++)
  271. {
  272. switch(data[i])
  273. {
  274. case ' ':
  275. case '\t':
  276. break;
  277. default:
  278. return i;
  279. }
  280. }
  281. return i;
  282. }
  283. //Пропускаем коментарий
  284. __forceinline long IniParser::ParseSkipComment(long i)
  285. {
  286. for(; i < data; i++)
  287. {
  288. switch(data[i])
  289. {
  290. case '\n':
  291. case '\r':
  292. return i;
  293. }
  294. }
  295. return i;
  296. }
  297. //Зачитываем название секции
  298. __forceinline long IniParser::ParseGetSection(long i)
  299. {
  300. for(long start = ++i, size; i < data; i++)
  301. {
  302. switch(data[i])
  303. {
  304. case ']':
  305. //Если имя 0 длинны пропускаем эту секцию
  306. if(i == start)
  307. {
  308. AddError("Ini file error (file \"%s\", line %i): Section without name.", fileName.GetBuffer(), line);
  309. return i + 1;
  310. }
  311. size = i++ - start;
  312. goto addSection;
  313. case '\n':
  314. case '\r':
  315. goto stopSection;
  316. }
  317. }
  318. stopSection:
  319. size = i - start;
  320. AddError("Ini file error (file \"%s\", line %i): Section not closed.", fileName.GetBuffer(), line);
  321. addSection:
  322. //Ищем окончание строки
  323. for(; i < data && data[i] != '\n'; i++);
  324. //Поищим имя среди добавленных секций
  325. for(long n = 0; n < sections; n++)
  326. {
  327. if(ParseEqual(sections[n].name.start, sections[n].name.size, start, size))
  328. {
  329. AddError("Ini file error (file \"%s\", line %i): Repeat name section.", fileName.GetBuffer(), line);
  330. return i;
  331. }
  332. }
  333. //Отметим размер предыдущий секции
  334. sections[sections - 1].size = keys - sections[sections - 1].start;
  335. //Добавляем секцию
  336. Section & s = sections[sections.Add()];
  337. s.name.start = start;
  338. s.name.size = size;
  339. s.start = keys;
  340. s.size = 0;
  341. s.startLine = startLine;
  342. s.endLine = i < data ? i : data - 1;
  343. return i;
  344. }
  345. //Зачитываем ключ
  346. __forceinline long IniParser::ParseGetKey(long i)
  347. {
  348. //Ищем окончание названия ключа
  349. for(long start = i; i < data; i++)
  350. {
  351. bool isBreak = false;
  352. switch(data[i])
  353. {
  354. case '=':
  355. case '\r':
  356. case '\n':
  357. isBreak = true;
  358. break;
  359. }
  360. if(isBreak) break;
  361. }
  362. if(i >= data || data[i] != '=')
  363. {
  364. AddError("Ini file error (file \"%s\", line %i): Invalidate key syntactic, not found '='", fileName.GetBuffer(), line);
  365. return ParseSkipComment(i);
  366. }
  367. //Ищем реальное окончание ключа
  368. for(long end = i - 1; data[end] == ' ' || data[end] == '\t'; end--);
  369. //Пропускаем пробелы после '='
  370. for(i++; i < data && (data[i] == ' ' || data[i] == '\t'); i++);
  371. //Ищим окончание строки
  372. for(long vstart = i; i < data; i++)
  373. {
  374. bool isBreak = false;
  375. switch(data[i])
  376. {
  377. case '\r':
  378. case '\n':
  379. isBreak = true;
  380. break;
  381. }
  382. if(isBreak) break;
  383. }
  384. //Ищим окончание значения
  385. long vend = i - 1;
  386. if(vstart < data)
  387. {
  388. for(; data[vend] == ' ' || data[vend] == '\t'; vend--);
  389. }
  390. //Добавляем запись ключа в массив
  391. Key & key = keys[keys.Add()];
  392. key.name.start = start;
  393. key.name.size = end - start + 1;
  394. key.value.start = vstart;
  395. key.value.size = vend - vstart + 1;
  396. key.startLine = startLine;
  397. //Уточняем окончание строки
  398. for(; i < data && data[i] != '\n' && data[i] != '\r'; i++);
  399. key.endLine = i < data ? i : data - 1;
  400. return i;
  401. }
  402. //Сравнить 2 строки
  403. __forceinline bool IniParser::ParseEqual(long start1, long size1, long start2, long size2)
  404. {
  405. if(size1 != size2) return false;
  406. const byte * pnt1 = &data[start1];
  407. const byte * pnt2 = &data[start2];
  408. for(long i = 0; i < size1; i++)
  409. {
  410. if(ToLower(*pnt1++) != ToLower(*pnt2++)) return false;
  411. }
  412. return true;
  413. }
  414. //Сравнить 2 строки
  415. __forceinline bool IniParser::Equal(const Token & tk, const char * str, long len)
  416. {
  417. if(tk.size != len || len <= 0) return false;
  418. const byte * pnt = &data[tk.start];
  419. for(long i = 0; i < len; i++)
  420. {
  421. if(ToLower(*pnt++) != ToLower(*str++)) return false;
  422. }
  423. return true;
  424. }
  425. //Заполнить строку
  426. __forceinline void IniParser::FillString(const Token & tk, string & str)
  427. {
  428. str.Empty();
  429. for(long c = tk.size, i = tk.start; c > 0; c--)
  430. {
  431. str += (char)data[i++];
  432. }
  433. }
  434. //Привести символ к нижнему регистру
  435. __forceinline char IniParser::ToLower(char c)
  436. {
  437. if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
  438. return c;
  439. }
  440. //Заменить часть данных на новые
  441. void IniParser::ChangeData(long start, long size, const char * str)
  442. {
  443. //Необходимо сделать систему изменения данных с поправкой токенов
  444. if(!str) str = "";
  445. long len = strlen(str);
  446. long dsize = len - size;
  447. //Поправляем все ключи
  448. for(long i = 0; i < keys; i++)
  449. {
  450. Modify(start, dsize, keys[i].name.start);
  451. Modify(start, dsize, keys[i].value.start);
  452. Modify(start, dsize, keys[i].startLine);
  453. Modify(start, dsize, keys[i].endLine);
  454. }
  455. //Поправляем все секции
  456. for(long i = 0; i < sections; i++)
  457. {
  458. Modify(start, dsize, sections[i].name.start);
  459. Modify(start, dsize, sections[i].startLine);
  460. Modify(start, dsize, sections[i].endLine);
  461. }
  462. //Модифицируем данные
  463. if(dsize > 0)
  464. {
  465. for(long i = 0; i < dsize; i++)
  466. {
  467. data.Insert(start);
  468. }
  469. }else
  470. if(dsize < 0)
  471. {
  472. dsize = -dsize;
  473. for(long i = 0; i < dsize; i++)
  474. {
  475. data.Extract(start);
  476. }
  477. }
  478. for(long i = 0; i < len; i++)
  479. {
  480. data[start + i] = str[i];
  481. }
  482. }
  483. //Поправить начало строки
  484. __forceinline void IniParser::Modify(long start, long dlt, long & current)
  485. {
  486. if(current > start)
  487. {
  488. current += dlt;
  489. }
  490. }