y_xml.inc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. /*----------------------------------------------------------------------------*\
  2. =============================
  3. y_xml - XML file functions!
  4. =============================
  5. Description:
  6. Parses XML files according to a set of defined rules. A rule is a custom
  7. function called on a tag when all the data for that tag has been collected.
  8. The data for a tag could consist of simple bla="bla" pairs, <tag>data</tag>
  9. pairs or cominations of the two, including subtags, each with their own
  10. possible custom handlers.
  11. Data for the tag is retrieved from a custom function using only:
  12. while (XML_GetKeyValue(ident, data)) {}
  13. Legal:
  14. Version: MPL 1.1
  15. The contents of this file are subject to the Mozilla Public License Version
  16. 1.1 (the "License"); you may not use this file except in compliance with
  17. the License. You may obtain a copy of the License at
  18. http://www.mozilla.org/MPL/
  19. Software distributed under the License is distributed on an "AS IS" basis,
  20. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  21. for the specific language governing rights and limitations under the
  22. License.
  23. The Original Code is the YSI XML include.
  24. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  25. Portions created by the Initial Developer are Copyright (C) 2011
  26. the Initial Developer. All Rights Reserved.
  27. Contributors:
  28. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  29. Thanks:
  30. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  31. ZeeX - Very productive conversations.
  32. koolk - IsPlayerinAreaEx code.
  33. TheAlpha - Danish translation.
  34. breadfish - German translation.
  35. Fireburn - Dutch translation.
  36. yom - French translation.
  37. 50p - Polish translation.
  38. Zamaroht - Spanish translation.
  39. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  40. for me to strive to better.
  41. Pixels^ - Running XScripters where the idea was born.
  42. Matite - Pestering me to release it and using it.
  43. Very special thanks to:
  44. Thiadmer - PAWN, whose limits continue to amaze me!
  45. Kye/Kalcor - SA:MP.
  46. SA:MP Team past, present and future - SA:MP.
  47. Version:
  48. 1.0
  49. Changelog:
  50. 06/08/10:
  51. First version
  52. \*----------------------------------------------------------------------------*/
  53. #include "internal\y_version"
  54. #include <a_samp>
  55. #include "y_debug"
  56. #include "y_utils"
  57. #include "internal\y_natives"
  58. #if !defined MAX_XML_FILES
  59. #define MAX_XML_FILES (5)
  60. #endif
  61. #define XML_MAX_XML_FILES (XML:MAX_XML_FILES)
  62. #define NO_XML_FILE (XML:-1)
  63. #define MAX_XML_FUNCTION FUNCTION_LENGTH
  64. #define MAX_XML_ENTRY_NAME (32)
  65. #define MAX_XML_ENTRY_TEXT (80)
  66. #define XML_BUFFER_SIZE (512)
  67. #define MAX_XML_HANDLERS (16)
  68. #if !defined XML_WRITE_BUFFER_SIZE
  69. #define XML_WRITE_BUFFER_SIZE (512)
  70. #endif
  71. #define XML_NO_WRITE (XMLEntry:cellmax)
  72. enum E_XML_PARA
  73. {
  74. E_XML_PARA_NAME[MAX_XML_ENTRY_NAME],
  75. E_XML_PARA_VALUE[MAX_XML_ENTRY_TEXT],
  76. E_XML_PARA_LEVEL
  77. }
  78. enum E_XML_HANDLER
  79. {
  80. E_XML_HANDLER_TRIGGER[MAX_XML_ENTRY_NAME],
  81. E_XML_HANDLER_FUNCTION[MAX_XML_FUNCTION]
  82. }
  83. enum E_XML_WRITE
  84. {
  85. E_XML_WRITE_TAG[MAX_XML_ENTRY_NAME],
  86. E_XML_WRITE_VALUE[MAX_XML_ENTRY_TEXT],
  87. E_XML_WRITE_CHILDREN,
  88. E_XML_WRITE_SIBLINGS
  89. }
  90. static stock
  91. YSI_g_sXMLWriteBuffer[XML_WRITE_BUFFER_SIZE][E_XML_WRITE],
  92. YSI_g_sXMLWritePointer,
  93. YSI_g_sParameters[XML_BUFFER_SIZE][E_XML_PARA],
  94. YSI_g_sHandlers[XML_MAX_XML_FILES][MAX_XML_HANDLERS][E_XML_HANDLER],
  95. YSI_g_sCurHandler[XML_MAX_XML_FILES] = {-1, ...},
  96. YSI_g_sCurBuffer = -1,
  97. YSI_g_sEndTag = 0;
  98. /*----------------------------------------------------------------------------*\
  99. Function:
  100. XML_IsValid
  101. Params:
  102. XML:file - File to check validity of.
  103. Return:
  104. -
  105. Notes:
  106. -
  107. \*----------------------------------------------------------------------------*/
  108. #define XML_IsValid(%1) \
  109. ((%1) >= XML:0 && (%1) < XML_MAX_XML_FILES && YSI_g_sCurHandler[(%1)] != -1)
  110. /*----------------------------------------------------------------------------*\
  111. Function:
  112. XML_IsChar
  113. Params:
  114. char - Checks if a cell is a valid identifier character.
  115. Return:
  116. -
  117. Notes:
  118. -
  119. \*----------------------------------------------------------------------------*/
  120. #define XML_IsChar(%1) \
  121. (((%1) >= 'a' && (%1) <= 'z') || ((%1) >= 'A' && (%1) <= 'Z') || ((%1) >= '0' && (%1) <= '9') || (%1 == '_'))
  122. /*----------------------------------------------------------------------------*\
  123. Function:
  124. XML_New
  125. Params:
  126. -
  127. Return:
  128. XML
  129. Notes:
  130. Creates a new set of rules for parsing XML files.
  131. \*----------------------------------------------------------------------------*/
  132. stock XML:XML_New()
  133. {
  134. P:3("XML:XML_New called");
  135. new
  136. XML:i;
  137. while (i < XML_MAX_XML_FILES && YSI_g_sCurHandler[i] != -1) i++;
  138. if (i == XML_MAX_XML_FILES) return NO_XML_FILE;
  139. YSI_g_sCurHandler[i] = 0;
  140. return i;
  141. }
  142. /*----------------------------------------------------------------------------*\
  143. Function:
  144. XML_Destroy
  145. Params:
  146. XML:rule - Removes a set of rules from the system
  147. Return:
  148. -
  149. Notes:
  150. -
  151. \*----------------------------------------------------------------------------*/
  152. stock XML_Destroy(XML:rule)
  153. {
  154. P:3("XML_Destroy called: %i", _:rule);
  155. if (!XML_IsValid(rule)) return 0;
  156. YSI_g_sCurBuffer = -1;
  157. YSI_g_sCurHandler[rule] = -1;
  158. return 1;
  159. }
  160. /*----------------------------------------------------------------------------*\
  161. Function:
  162. XML_Parse
  163. Params:
  164. XML:rule - Set of XML rules to parse against.
  165. filename[] - XML file to parse.
  166. Return:
  167. -
  168. Notes:
  169. Now supports self-closing tags and XML comments.
  170. \*----------------------------------------------------------------------------*/
  171. stock XML_Parse(XML:rule, filename[])
  172. {
  173. P:3("XML_Parse called: %i, \"%s\"", _:rule, filename);
  174. if (!XML_IsValid(rule)) return 0;
  175. YSI_g_sCurBuffer = 0;
  176. new
  177. File:xFile = fopen(filename);
  178. if (xFile)
  179. {
  180. new
  181. line[YSI_MAX_STRING],
  182. tagCount,
  183. gotLastValue,
  184. inClose,
  185. inOpen,
  186. inComment,
  187. value[MAX_XML_ENTRY_TEXT],
  188. name[MAX_XML_ENTRY_NAME],
  189. inPar;
  190. while (fread(xFile, line))
  191. {
  192. P:5("XML_Parse() line: %s", line);
  193. new
  194. pos,
  195. ch;
  196. if (inComment)
  197. {
  198. pos = strfind(line, "-->", false, 0);
  199. if (pos == -1)
  200. {
  201. // Skip this whole line.
  202. continue;
  203. }
  204. else
  205. {
  206. pos += 3;
  207. inComment = 0;
  208. }
  209. }
  210. while ((ch = line[pos]) && ch <= ' ') pos++;
  211. while (ch)
  212. {
  213. if (ch <= ' ') pos++;
  214. else if (ch == '<')
  215. {
  216. pos++;
  217. if (line[pos] == '/')
  218. {
  219. pos++;
  220. tagCount--;
  221. if (gotLastValue)
  222. {
  223. XML_Push(XML_GetName(line, pos), value, tagCount);
  224. }
  225. else
  226. {
  227. name = XML_GetName(line, pos);
  228. value = XML_ParseTag(rule, name, tagCount);
  229. if (value[0] && tagCount > 1)
  230. {
  231. XML_Push(name, value, tagCount);
  232. }
  233. }
  234. inClose = 1;
  235. }
  236. else if (line[pos] == '!' && line[pos + 1] == '-' && line[pos + 2] == '-')
  237. {
  238. // XML comments.
  239. pos = strfind(line, "-->", false, pos + 3);
  240. if (pos == -1)
  241. {
  242. inComment = 1;
  243. break;
  244. }
  245. else
  246. {
  247. pos += 3;
  248. }
  249. }
  250. else
  251. {
  252. inOpen = 1;
  253. tagCount++;
  254. while ((ch = line[pos]) && XML_IsChar(ch)) pos++;
  255. //name = XML_GetName(line, pos);
  256. }
  257. gotLastValue = 0;
  258. inPar = 0;
  259. }
  260. else if (ch == '>')
  261. {
  262. inPar = inClose ? 0 : 1;
  263. inOpen = 0;
  264. inClose = 0;
  265. pos++;
  266. }
  267. else if (ch == '/')
  268. {
  269. // Self-closing tags (FINALLY)!
  270. pos++;
  271. if (inOpen)
  272. {
  273. tagCount--;
  274. inOpen = strfind(line, "<") + 1;
  275. if (inOpen)
  276. {
  277. name = XML_GetName(line, inOpen);
  278. value = XML_ParseTag(rule, name, tagCount);
  279. if (value[0] && tagCount > 1)
  280. {
  281. XML_Push(name, value, tagCount);
  282. }
  283. inOpen = 0;
  284. }
  285. inClose = 1; // Well, we sort of are...
  286. }
  287. }
  288. else if (inPar)
  289. {
  290. value = XML_GetValue(line, pos);
  291. gotLastValue = 1;
  292. }
  293. else if (inOpen)
  294. {
  295. name = XML_GetName(line, pos);
  296. value = XML_GetParameter(line, pos);
  297. XML_Push(name, value, tagCount);
  298. }
  299. else
  300. {
  301. pos++;
  302. }
  303. ch = line[pos];
  304. }
  305. }
  306. fclose(xFile);
  307. return 1;
  308. }
  309. return 0;
  310. }
  311. /*----------------------------------------------------------------------------*\
  312. Function:
  313. XML_Push
  314. Params:
  315. name[] - Identifer of data.
  316. text[] - Data.
  317. depth - Current XML tree depth.
  318. Return:
  319. -
  320. Notes:
  321. Pushes an identifier and it's value (either explicitaly stated or returned
  322. from another function) to the stack with basic parent info.
  323. \*----------------------------------------------------------------------------*/
  324. stock XML_Push(name[], text[], depth)
  325. {
  326. P:3("XML_Push called: \"%s\", \"%s\", %i, %i", name, text, depth, YSI_g_sCurBuffer);
  327. if (YSI_g_sCurBuffer < XML_BUFFER_SIZE && YSI_g_sCurBuffer >= 0)
  328. {
  329. P:7("XML_Push: first");
  330. strcpy(YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_NAME], name, MAX_XML_ENTRY_NAME);
  331. P:7("XML_Push: second");
  332. strcpy(YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_VALUE], text, MAX_XML_ENTRY_TEXT);
  333. P:7("XML_Push: third");
  334. YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL] = depth;
  335. P:7("XML_Push: fourth");
  336. ++YSI_g_sCurBuffer;
  337. P:7("XML_Push: fifth");
  338. }
  339. P:5("XML_Push: YSI_g_sCurBuffer = %d", YSI_g_sCurBuffer);
  340. }
  341. /*----------------------------------------------------------------------------*\
  342. Function:
  343. XML_GetParameter
  344. Params:
  345. line[] - Data to extract from.
  346. &pos - Start/end point of the text.
  347. Return:
  348. -
  349. Notes:
  350. Gets the data from inside ""s in an identifier. Now supports
  351. \ for escape characters.
  352. \*----------------------------------------------------------------------------*/
  353. stock XML_GetParameter(line[], &pos)
  354. {
  355. P:3("XML_GetParameter called: \"%s\", %i", line, pos);
  356. new
  357. ch,
  358. ret[MAX_XML_ENTRY_TEXT],
  359. i;
  360. while ((ch = line[pos++]) && ch != '"') {}
  361. if (ch)
  362. {
  363. while ((ch = line[pos++]) && i < (sizeof (ret) - 1))
  364. {
  365. if (ch == '\\')
  366. {
  367. switch (line[pos++])
  368. {
  369. case '"':
  370. {
  371. ch = '"';
  372. }
  373. case 'n':
  374. {
  375. ch = '\n';
  376. }
  377. case 'r':
  378. {
  379. ch = '\r';
  380. }
  381. case '\\': {}
  382. default:
  383. {
  384. pos--;
  385. continue;
  386. }
  387. }
  388. }
  389. else if (ch == '"')
  390. {
  391. break;
  392. }
  393. ret[i++] = ch;
  394. }
  395. }
  396. if (i == (sizeof (ret) - 1))
  397. {
  398. while ((ch = line[pos++]))
  399. {
  400. if (ch == '\\')
  401. {
  402. switch (line[pos++])
  403. {
  404. case '\\', '"', 'n', 'r': {}
  405. default:
  406. {
  407. pos--;
  408. }
  409. }
  410. }
  411. else if (ch == '"') break;
  412. }
  413. }
  414. ret[i] = '\0';
  415. return ret;
  416. }
  417. /*----------------------------------------------------------------------------*\
  418. Function:
  419. XML_GetValue
  420. Params:
  421. line[] - Line to get data from.
  422. &pos - Start and end position of the data.
  423. Return:
  424. -
  425. Notes:
  426. Gets the text between tags.
  427. \*----------------------------------------------------------------------------*/
  428. stock XML_GetValue(line[], &pos)
  429. {
  430. P:3("XML_GetValue called: \"%s\", %i", line, pos);
  431. new
  432. ch,
  433. ret[MAX_XML_ENTRY_TEXT],
  434. i;
  435. while (((ch = line[pos++]) >= ' ' || ch == '\t') && (ch != '<') && i < (sizeof (ret) - 1)) ret[i++] = ch;
  436. pos--;
  437. if (i == (sizeof (ret) - 1))
  438. {
  439. while (((ch = line[pos]) >= ' ' || ch == '\t') && (ch != '<')) pos++;
  440. }
  441. ret[i] = '\0';
  442. return ret;
  443. }
  444. /*----------------------------------------------------------------------------*\
  445. Function:
  446. XML_GetName
  447. Params:
  448. line[] - Line to get data from.
  449. &pos - Start and end position of text.
  450. Return:
  451. -
  452. Notes:
  453. Gets the identifier of a piece of data.
  454. \*----------------------------------------------------------------------------*/
  455. stock XML_GetName(line[], &pos)
  456. {
  457. P:3("XML_GetName called: \"%s\", %i", line, pos);
  458. new
  459. ch,
  460. ret[MAX_XML_ENTRY_NAME],
  461. i;
  462. while ((ch = line[pos++]) && XML_IsChar(ch) && i < (sizeof (ret) - 1)) ret[i++] = ch;
  463. pos--;
  464. if (i == (sizeof (ret) - 1))
  465. {
  466. while ((ch = line[pos]) >= ' ' && XML_IsChar(ch)) pos++;
  467. }
  468. ret[i] = '\0';
  469. return ret;
  470. }
  471. /*----------------------------------------------------------------------------*\
  472. Function:
  473. XML_ParseTag
  474. Params:
  475. XML:rule - Rule set to parse according to.
  476. name[] - Name if identifier.
  477. tagCount - New tree depth.
  478. Return:
  479. -
  480. Notes:
  481. -
  482. \*----------------------------------------------------------------------------*/
  483. stock XML_ParseTag(XML:rule, name[], tagCount)
  484. {
  485. P:3("XML_ParseTag called: %i, \"%s\", %i", _:rule, name, tagCount);
  486. new
  487. i,
  488. j = YSI_g_sCurHandler[rule],
  489. ret[MAX_XML_ENTRY_TEXT] = "\1";
  490. YSI_g_sEndTag = tagCount;
  491. while (i < j)
  492. {
  493. if (!strcmp(YSI_g_sHandlers[rule][i][E_XML_HANDLER_TRIGGER], name, true))
  494. {
  495. break;
  496. }
  497. i++;
  498. }
  499. P:5("XML_ParseTag: Check %d != %d", i, j);
  500. if (i != j)
  501. {
  502. //format(ret, sizeof (ret), "%d", CallLocalFunction(YSI_g_sHandlers[rule][i][E_XML_HANDLER_FUNCTION], ""));
  503. valstr(ret, CallLocalFunction(YSI_g_sHandlers[rule][i][E_XML_HANDLER_FUNCTION], ""));
  504. }
  505. while (YSI_g_sCurBuffer-- && YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL] > tagCount)
  506. {
  507. P:3("XML_ParseTag item: %d %s %d", YSI_g_sCurBuffer, YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_NAME], YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL]);
  508. //--YSI_g_sCurBuffer;
  509. }
  510. ++YSI_g_sCurBuffer;
  511. /*while (YSI_g_sCurBuffer)
  512. {
  513. if (YSI_g_sParameters[--YSI_g_sCurBuffer][E_XML_PARA_LEVEL] <= tagCount)
  514. {
  515. ++YSI_g_sCurBuffer;
  516. break;
  517. }
  518. }*/
  519. return ret;
  520. }
  521. /*----------------------------------------------------------------------------*\
  522. Function:
  523. XML_GetKeyValue
  524. Params:
  525. key[] - Variable to return identifier in.
  526. value[] - Variable to return value in.
  527. Return:
  528. Data found.
  529. Notes:
  530. Pops items off the stack for use in custom functions.
  531. \*----------------------------------------------------------------------------*/
  532. stock XML_GetKeyValue(key[], value[])
  533. {
  534. P:3("XML_GetKeyValue called: \"%s\", \"%s\"", key, value);
  535. key[0] = 1;
  536. key[1] = 0;
  537. value[0] = 1;
  538. value[1] = 0;
  539. P:5("XML_GetKeyValue: YSI_g_sCurBuffer: %d", YSI_g_sCurBuffer);
  540. if (YSI_g_sCurBuffer)
  541. {
  542. --YSI_g_sCurBuffer;
  543. if (YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL] <= YSI_g_sEndTag)
  544. {
  545. ++YSI_g_sCurBuffer;
  546. return 0;
  547. }
  548. P:5("XML_GetKeyValue: first");
  549. strcpy(key, YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_NAME], MAX_XML_ENTRY_NAME);
  550. P:5("XML_GetKeyValue: second");
  551. strcpy(value, YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_VALUE], MAX_XML_ENTRY_TEXT);
  552. P:3("XML_GetKeyValue ending: \"%s\", \"%s\"", key, value);
  553. return 1;
  554. }
  555. return 0;
  556. }
  557. /*----------------------------------------------------------------------------*\
  558. Function:
  559. XML_GetParentValue
  560. Params:
  561. key[] - Name of the parameter to get.
  562. value[] - Variable to return value in.
  563. Return:
  564. Data found.
  565. Notes:
  566. Does no poping, just searches for a value with the right name at the right
  567. depth so children can use the data if they HAVE to.
  568. \*----------------------------------------------------------------------------*/
  569. stock XML_GetParentValue(const key[], value[])
  570. {
  571. P:3("XML_GetParentValue called: \"%s\"", key);
  572. value[0] = 1;
  573. value[1] = 0;
  574. P:7("XML_GetParentValue: first");
  575. //P:C(for (new i = 0; i != YSI_g_sCurBuffer; ++i) P:0("XML_GetParentValue: buffer[%d] = %s", i, YSI_g_sParameters[i][E_XML_PARA_NAME]););
  576. for (new i = 0; i != YSI_g_sCurBuffer; ++i)
  577. {
  578. P:7("XML_GetParentValue: second %d %d %d", i, YSI_g_sParameters[i][E_XML_PARA_LEVEL], YSI_g_sEndTag);
  579. if (YSI_g_sParameters[i][E_XML_PARA_LEVEL] == YSI_g_sEndTag)
  580. {
  581. P:7("XML_GetParentValue: 2b: %s %s", key, YSI_g_sParameters[i][E_XML_PARA_NAME]);
  582. if (!strcmp(key, YSI_g_sParameters[i][E_XML_PARA_NAME]))
  583. {
  584. P:7("XML_GetParentValue: third");
  585. strcpy(value, YSI_g_sParameters[i][E_XML_PARA_VALUE], MAX_XML_ENTRY_TEXT);
  586. return 1;
  587. }
  588. }
  589. else if (YSI_g_sParameters[i][E_XML_PARA_LEVEL] > YSI_g_sEndTag)
  590. {
  591. P:7("XML_GetParentValue: fourth");
  592. // In to child data.
  593. return 0;
  594. }
  595. }
  596. P:7("XML_GetParentValue: fifth");
  597. return 0;
  598. }
  599. /*----------------------------------------------------------------------------*\
  600. Function:
  601. XML_AddHandler
  602. Params:
  603. XML:ruls - Rule set to add data to.
  604. trigger[] - Identifier which calls it.
  605. function[] - Function to parse identifier in.
  606. Return:
  607. -
  608. Notes:
  609. -
  610. \*----------------------------------------------------------------------------*/
  611. stock XML_AddHandler(XML:rule, trigger[], function[])
  612. {
  613. P:3("XML_AddHandler called: %i, \"%s\", \"%s\"", _:rule, trigger, function);
  614. new
  615. handle;
  616. if (!XML_IsValid(rule) || (handle = YSI_g_sCurHandler[rule]) >= MAX_XML_HANDLERS && handle >= 0) return 0;
  617. strcpy(YSI_g_sHandlers[rule][handle][E_XML_HANDLER_TRIGGER], trigger, MAX_XML_ENTRY_NAME);
  618. strcpy(YSI_g_sHandlers[rule][handle][E_XML_HANDLER_FUNCTION], function, MAX_XML_FUNCTION);
  619. YSI_g_sCurHandler[rule]++;
  620. return 1;
  621. }
  622. /*----------------------------------------------------------------------------*\
  623. Function:
  624. XML_RemoveHandler
  625. Params:
  626. XML:rule - Set to remove handler from.
  627. trigger[] - Handler name to remove.
  628. Return:
  629. -
  630. Notes:
  631. -
  632. \*----------------------------------------------------------------------------*/
  633. stock XML_RemoveHandler(XML:rule, trigger[])
  634. {
  635. P:3("XML_RemoveHandler called: %i, \"%s\"", _:rule, trigger);
  636. if (XML_IsValid(rule))
  637. {
  638. for (new i = 0, j = YSI_g_sCurHandler[rule]; i < j; i++)
  639. {
  640. if (!strcmp(YSI_g_sHandlers[rule][i][E_XML_HANDLER_TRIGGER], trigger, true))
  641. {
  642. new
  643. last = --YSI_g_sCurHandler[rule];
  644. if (last)
  645. {
  646. strcpy(YSI_g_sHandlers[rule][i][E_XML_HANDLER_TRIGGER], YSI_g_sHandlers[rule][last][E_XML_HANDLER_TRIGGER], MAX_XML_ENTRY_NAME);
  647. strcpy(YSI_g_sHandlers[rule][i][E_XML_HANDLER_FUNCTION], YSI_g_sHandlers[rule][last][E_XML_HANDLER_FUNCTION], MAX_XML_FUNCTION);
  648. }
  649. return 1;
  650. }
  651. }
  652. }
  653. return 0;
  654. }
  655. /*----------------------------------------------------------------------------*\
  656. Function:
  657. XML_AddParameter
  658. Params:
  659. parent - Tag this is a parameter of.
  660. tag[] - Name of this data.
  661. value[] - Value of this data, if this is blank there is sub parameters.
  662. Return:
  663. -
  664. Notes:
  665. -
  666. \*----------------------------------------------------------------------------*/
  667. //#define XML_AddSubEntry XML_AddParameter
  668. stock XMLEntry:XML_AddParameter(XMLEntry:parent, tag[], value[] = "")
  669. {
  670. P:3("XMLEntry:XML_AddParameter called: %i, \"%s\", \"%s\"", _:parent, tag, value);
  671. if (_:parent < YSI_g_sXMLWritePointer < XML_WRITE_BUFFER_SIZE)
  672. {
  673. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_TAG], tag, MAX_XML_ENTRY_NAME);
  674. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_VALUE], value, MAX_XML_ENTRY_TEXT);
  675. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_CHILDREN] = -1;
  676. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_SIBLINGS] = YSI_g_sXMLWriteBuffer[_:parent][E_XML_WRITE_CHILDREN];
  677. YSI_g_sXMLWriteBuffer[_:parent][E_XML_WRITE_CHILDREN] = YSI_g_sXMLWritePointer;
  678. return XMLEntry:YSI_g_sXMLWritePointer++;
  679. }
  680. return XMLEntry:cellmax;
  681. }
  682. /*----------------------------------------------------------------------------*\
  683. Function:
  684. XML_AddItem
  685. Params:
  686. tag[] - Type of data being added.
  687. name[] - The optional name parameter for identifying tags.
  688. Return:
  689. -
  690. Notes:
  691. Starts the creation of a new tag to be written to a file, the structure
  692. has to be manually created then written. There is no buffering of multiple
  693. tags before writing as a single tag can have quite a bit of data.
  694. \*----------------------------------------------------------------------------*/
  695. #define XML_CreateEntry XML_AddItem
  696. #define XML_AddSubEntry XML_AddItem
  697. stock XMLEntry:XML_AddItem(tag[], name[] = "", XMLEntry:parent = XMLEntry:cellmax)
  698. {
  699. P:3("XMLEntry:XML_AddItem called: \"%s\", \"%s\", %i", tag, name, _:parent);
  700. if (_:parent != cellmax)
  701. {
  702. return XML_AddParameter(parent, tag, name);
  703. }
  704. if (YSI_g_sXMLWritePointer < XML_WRITE_BUFFER_SIZE)
  705. {
  706. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_TAG], tag, MAX_XML_ENTRY_NAME);
  707. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_VALUE], name, MAX_XML_ENTRY_TEXT);
  708. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_CHILDREN] = -1;
  709. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_SIBLINGS] = -1;
  710. return XMLEntry:YSI_g_sXMLWritePointer++;
  711. }
  712. return XMLEntry:cellmax;
  713. }
  714. /*----------------------------------------------------------------------------*\
  715. Function:
  716. XML_WriteItem
  717. Params:
  718. filename[] - File to write to.
  719. item - Handle to the tag to write.
  720. bool:bIncludeXML - Write the default <XML> tags or not?
  721. bool:bFavourShort - Write the shortest tags possible.
  722. Return:
  723. -
  724. Notes:
  725. Writea the data for a tag to a file.
  726. \*----------------------------------------------------------------------------*/
  727. #define XML_WriteEntry XML_WriteItem
  728. stock XML_WriteItem(filename[], XMLEntry:item, bool:bIncludeXML = true, bool:bFavourShort = false)
  729. {
  730. P:3("XML_WriteItem called: \"%s\", %i", filename, _:item);
  731. if (_:item < YSI_g_sXMLWritePointer)
  732. {
  733. new
  734. data;
  735. if (fexist(filename))
  736. {
  737. new
  738. File:fHnd = fopen(filename, io_read),
  739. File:__ftemp = ftemp();//fopen("_temp_ysi_user_file_.ysi", io_write);
  740. if (fHnd && __ftemp)
  741. {
  742. new
  743. str[YSI_MAX_STRING];
  744. while (fread(fHnd, str))
  745. {
  746. fwrite(__ftemp, str);
  747. if (!data)
  748. {
  749. new
  750. i,
  751. ch;
  752. while ((ch = str[i++]) && ch <= ' ') {}
  753. if (ch == '<')
  754. {
  755. if (bFavourShort) XML_WriteItemDataShort(_:item, __ftemp, 2);
  756. else XML_WriteItemData(_:item, __ftemp, 2);
  757. data = 1;
  758. }
  759. }
  760. }
  761. fclose(fHnd);
  762. //fclose(__ftemp);
  763. fremove(filename);
  764. if (data)
  765. {
  766. fHnd = fopen(filename, io_write);
  767. fseek(__ftemp);
  768. //__ftemp = fopen("_temp_ysi_user_file_.ysi", io_read);
  769. if (fHnd)// && __ftemp)
  770. {
  771. while (fread(__ftemp, str)) fwrite(fHnd, str);
  772. fclose(fHnd);
  773. fclose(__ftemp);
  774. //fremove("_temp_ysi_user_file_.ysi");
  775. YSI_g_sXMLWritePointer = _:item;
  776. return 1;
  777. }
  778. }
  779. }
  780. if (fHnd)
  781. {
  782. fclose(fHnd);
  783. }
  784. if (__ftemp)
  785. {
  786. fclose(__ftemp);
  787. //fremove("_temp_ysi_user_file_.ysi");
  788. }
  789. }
  790. if (!data)
  791. {
  792. new
  793. File:fHnd = fopen(filename, io_write);
  794. if (fHnd)
  795. {
  796. if (bIncludeXML) fwrite(fHnd, "<XML>\r\n");
  797. if (bFavourShort) XML_WriteItemDataShort(_:item, fHnd, _:bIncludeXML * 2);
  798. else XML_WriteItemData(_:item, fHnd, _:bIncludeXML * 2);
  799. if (bIncludeXML) fwrite(fHnd, "</XML>");
  800. fclose(fHnd);
  801. YSI_g_sXMLWritePointer = _:item;
  802. return 1;
  803. }
  804. }
  805. }
  806. return 0;
  807. }
  808. /*----------------------------------------------------------------------------*\
  809. Function:
  810. XML_WriteItemData
  811. Params:
  812. item - Item to write data for.
  813. File:fHnd - File to write to.
  814. depth - Current indentation.
  815. Return:
  816. -
  817. Notes:
  818. Recursive function to write a tag and it's children to a file.
  819. \*----------------------------------------------------------------------------*/
  820. static stock XML_WriteItemData(item, File:fHnd, depth)
  821. {
  822. P:4("XML_WriteItemData called: %i, %i, %i", item, _:fHnd, depth);
  823. new
  824. str[YSI_MAX_STRING],
  825. i = YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_CHILDREN];
  826. if (i == -1)
  827. {
  828. format(str, sizeof (str), "%*s<%s>%s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  829. fwrite(fHnd, str);
  830. P:7("XML_WriteItemData: writing %s", str);
  831. }
  832. else
  833. {
  834. if (YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE][0]) format(str, sizeof (str), "%*s<%s name=\"%s\">\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE]);
  835. else format(str, sizeof (str), "%*s<%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  836. P:7("XML_WriteItemData: writing %s", str);
  837. fwrite(fHnd, str);
  838. depth += 2;
  839. while (i != -1)
  840. {
  841. XML_WriteItemData(i, fHnd, depth);
  842. i = YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_SIBLINGS];
  843. }
  844. depth -= 2;
  845. format(str, sizeof (str), "%*s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  846. P:7("XML_WriteItemData: writing %s", str);
  847. fwrite(fHnd, str);
  848. }
  849. }
  850. /*----------------------------------------------------------------------------*\
  851. Function:
  852. XML_WriteItemDataShort
  853. Params:
  854. item - Item to write data for.
  855. File:fHnd - File to write to.
  856. depth - Current indentation.
  857. Return:
  858. -
  859. Notes:
  860. Recursive function to write a tag and it's children to a file. Writes tags
  861. in the shortest manner possible. This does make it slower however (not much
  862. though given the use of linked lists).
  863. \*----------------------------------------------------------------------------*/
  864. static stock XML_WriteItemDataShort(item, File:fHnd, depth)
  865. {
  866. P:4("XML_WriteItemDataShort called: %i, %i, %i", item, _:fHnd, depth);
  867. new
  868. str[YSI_MAX_STRING],
  869. i = YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_CHILDREN];
  870. if (i == -1)
  871. {
  872. format(str, sizeof (str), "%*s<%s>%s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  873. fwrite(fHnd, str);
  874. P:7("XML_WriteItemDataShort: writing 0 %s", str);
  875. }
  876. else
  877. {
  878. // Loop through all children to find those with no children.
  879. format(str, sizeof (str), "%*s<%s", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  880. P:7("XML_WriteItemDataShort: writing 1 %s", str);
  881. fwrite(fHnd, str);
  882. new
  883. notDone = 0;
  884. do
  885. {
  886. if (YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_CHILDREN] == -1)
  887. {
  888. format(str, sizeof (str), " %s=\"%s\"", YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_VALUE]);
  889. P:7("XML_WriteItemDataShort: writing 2 %s", str);
  890. fwrite(fHnd, str);
  891. }
  892. else
  893. {
  894. ++notDone;
  895. }
  896. i = YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_SIBLINGS];
  897. }
  898. while (i != -1);
  899. if (notDone)
  900. {
  901. P:7("XML_WriteItemDataShort: writing 3 >\n");
  902. fwrite(fHnd, ">\n");
  903. depth += 2;
  904. i = YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_CHILDREN];
  905. do
  906. {
  907. if (YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_CHILDREN] != -1)
  908. {
  909. XML_WriteItemDataShort(i, fHnd, depth);
  910. }
  911. i = YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_SIBLINGS];
  912. }
  913. while (i != -1);
  914. depth -= 2;
  915. format(str, sizeof (str), "%*s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  916. fwrite(fHnd, str);
  917. P:7("XML_WriteItemDataShort: writing 4 %s", str);
  918. }
  919. else
  920. {
  921. P:7("XML_WriteItemData: writing 5 />\n");
  922. fwrite(fHnd, " />\n");
  923. }
  924. }
  925. }
  926. /*----------------------------------------------------------------------------*\
  927. Function:
  928. XML_RemoveItem
  929. Params:
  930. file[] - File to remove the tag from.
  931. tag[] - Type of tag to remove.
  932. name[] - Name of the tag to remove.
  933. Return:
  934. -
  935. Notes:
  936. Does a replace on data in a file with no new data.
  937. \*----------------------------------------------------------------------------*/
  938. stock XML_RemoveItem(file[], tag[], name[])
  939. {
  940. P:3("XML_RemoveItem called: \"%s\", \"%s\", \"%s\", ", file, tag, name);
  941. return XML_ReplaceItem(file, tag, name, -1);
  942. }
  943. /*----------------------------------------------------------------------------*\
  944. Function:
  945. XML_ReplaceItem
  946. Params:
  947. file[] - File to replace an item in.
  948. tag[] - Tag type of data to replace.
  949. name[] - Name of data to replace.
  950. replacement - Handle to the replacement data.
  951. Return:
  952. -
  953. Notes:
  954. Replaces a tag's data with new data, basically changes a tag's value.
  955. \*----------------------------------------------------------------------------*/
  956. stock XML_ReplaceItem(file[], tag[], name[], replacement)
  957. {
  958. P:3("XML_ReplaceItem called: \"%s\", \"%s\", \"%s\", %i", file, tag, name, replacement);
  959. if (fexist(file))
  960. {
  961. new
  962. File:fHnd = fopen(file, io_read),
  963. File:__ftemp = ftemp();//fopen("_temp_ysi_user_file_.ysi", io_write);
  964. if (fHnd && __ftemp)
  965. {
  966. new
  967. tagCount,
  968. line[YSI_MAX_STRING],
  969. inTag,
  970. atStart;
  971. while (fread(fHnd, line))
  972. {
  973. new
  974. pos,
  975. ch;
  976. while ((ch = line[pos]) && ch <= ' ') pos++;
  977. while (ch)
  978. {
  979. if (ch <= ' ') pos++;
  980. else if (ch == '<')
  981. {
  982. if (line[++pos] == '/')
  983. {
  984. pos++;
  985. tagCount--;
  986. if (inTag && tagCount <= atStart) inTag = 3;
  987. }
  988. else
  989. {
  990. tagCount++;
  991. if (!inTag)
  992. {
  993. if (!strcmp(XML_GetName(line, pos), tag)) inTag = 1;
  994. }
  995. else while ((ch = line[pos]) && XML_IsChar(ch)) pos++;
  996. }
  997. }
  998. else if (ch == '>')
  999. {
  1000. if (inTag == 1) inTag = 0;
  1001. pos++;
  1002. }
  1003. else if (inTag == 1)
  1004. {
  1005. if (!strcmp(XML_GetName(line, pos), "name"))
  1006. {
  1007. if (!(strcmp(XML_GetParameter(line, pos), name)))
  1008. {
  1009. inTag = 2;
  1010. atStart = tagCount - 1;
  1011. if (replacement != -1)
  1012. {
  1013. if (replacement >= YSI_g_sXMLWritePointer) replacement = -1;
  1014. else XML_WriteItemData(replacement, __ftemp, (tagCount - 1) * 2);
  1015. }
  1016. }
  1017. }
  1018. else XML_GetParameter(line, pos);
  1019. }
  1020. else pos++;
  1021. ch = line[pos];
  1022. }
  1023. if (!inTag) fwrite(__ftemp, line);
  1024. if (inTag == 3) inTag = 0;
  1025. }
  1026. fclose(fHnd);
  1027. //fclose(__ftemp);
  1028. fremove(file);
  1029. fHnd = fopen(file, io_write);
  1030. //__ftemp = fopen("_temp_ysi_user_file_.ysi", io_read);
  1031. fseek(__ftemp);
  1032. if (fHnd && __ftemp)
  1033. {
  1034. while (fread(__ftemp, line)) fwrite(fHnd, line);
  1035. fclose(fHnd);
  1036. fclose(__ftemp);
  1037. //fremove("_temp_ysi_user_file_.ysi");
  1038. if (replacement != -1) YSI_g_sXMLWritePointer = replacement;
  1039. return 1;
  1040. }
  1041. }
  1042. if (fHnd)
  1043. {
  1044. fclose(fHnd);
  1045. }
  1046. if (__ftemp)
  1047. {
  1048. fclose(__ftemp);
  1049. //fremove("_temp_ysi_user_file_.ysi");
  1050. }
  1051. return 0;
  1052. }
  1053. return 1;
  1054. }