OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
StdCompiler.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 #include "C4Include.h"
17 #include "lib/StdCompiler.h"
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include "lib/C4Log.h"
23 
24 // *** StdCompiler
25 
26 void StdCompiler::Warn(const char *szWarning, ...)
27 {
28  // Got warning callback?
29  if (!pWarnCB) return;
30  // Format message
31  va_list args; va_start(args, szWarning);
32  StdStrBuf Msg; Msg.FormatV(szWarning, args);
33  // do callback
34  (*pWarnCB)(pWarnData, getPosition().getData(), Msg.getData());
35 }
36 
38 {
39  switch (eSep)
40  {
41  case SEP_SEP: return ',';
42  case SEP_SEP2: return ';';
43  case SEP_SET: return '=';
44  case SEP_PART: return '.';
45  case SEP_PART2: return ':';
46  case SEP_PLUS: return '+';
47  case SEP_START: return '(';
48  case SEP_END: return ')';
49  case SEP_START2: return '[';
50  case SEP_END2: return ']';
51  case SEP_VLINE: return '|';
52  case SEP_DOLLAR: return '$';
53  default: assert(!"Unhandled Separator value");
54  }
55  return ' ';
56 }
57 
59 {
60  switch (eType)
61  {
62  case RCT_Escaped: return c == '"' || !c || c == '\n' || c == '\r';
63  case RCT_All: return !c || c == '\n' || c == '\r';
64  // '-' is needed for Layers in Scenario.txt (C4NameList) and other Material-Texture combinations
65  case RCT_Idtf: case RCT_IdtfAllowEmpty: case RCT_ID: return !isalnum((unsigned char)c) && c != '_' && c != '-';
66  }
67  // unreachable
68  return true;
69 }
70 
71 // *** StdCompilerBinWrite
72 
73 void StdCompilerBinWrite::DWord(int32_t &rInt) { WriteValue(rInt); }
74 void StdCompilerBinWrite::DWord(uint32_t &rInt) { WriteValue(rInt); }
75 void StdCompilerBinWrite::Word(int16_t &rShort) { WriteValue(rShort); }
76 void StdCompilerBinWrite::Word(uint16_t &rShort) { WriteValue(rShort); }
77 void StdCompilerBinWrite::Byte(int8_t &rByte) { WriteValue(rByte); }
78 void StdCompilerBinWrite::Byte(uint8_t &rByte) { WriteValue(rByte); }
79 void StdCompilerBinWrite::Boolean(bool &rBool) { WriteValue(rBool); }
80 void StdCompilerBinWrite::Character(char &rChar) { WriteValue(rChar); }
81 void StdCompilerBinWrite::String(char *szString, size_t iMaxLength, RawCompileType eType)
82 {
83  WriteData(szString, strlen(szString) + 1);
84 }
85 void StdCompilerBinWrite::String(char **pszString, RawCompileType eType)
86 {
87  if (*pszString)
88  WriteData(*pszString, strlen(*pszString) + 1);
89  else
90  WriteValue('\0');
91 }
92 
93 void StdCompilerBinWrite::String(std::string &str, RawCompileType type)
94 {
95  WriteData(str.c_str(), str.size() + 1);
96 }
97 
98 template <class T>
99 void StdCompilerBinWrite::WriteValue(const T &rValue)
100 {
101  // Copy data
102  if (fSecondPass)
103  *getMBufPtr<T>(Buf, iPos) = rValue;
104  iPos += sizeof(rValue);
105 }
106 
107 void StdCompilerBinWrite::WriteData(const void *pData, size_t iSize)
108 {
109  // Copy data
110  if (fSecondPass)
111  Buf.Write(pData, iSize, iPos);
112  iPos += iSize;
113 }
114 
115 void StdCompilerBinWrite::Raw(void *pData, size_t iSize, RawCompileType eType)
116 {
117  // Copy data
118  if (fSecondPass)
119  Buf.Write(pData, iSize, iPos);
120  iPos += iSize;
121 }
122 
124 {
125  fSecondPass = false; iPos = 0;
126 }
127 
129 {
130  Buf.New(iPos);
131  fSecondPass = true; iPos = 0;
132 }
133 
134 // *** StdCompilerBinRead
135 
136 void StdCompilerBinRead::DWord(int32_t &rInt) { ReadValue(rInt); }
137 void StdCompilerBinRead::DWord(uint32_t &rInt) { ReadValue(rInt); }
138 void StdCompilerBinRead::Word(int16_t &rShort) { ReadValue(rShort); }
139 void StdCompilerBinRead::Word(uint16_t &rShort) { ReadValue(rShort); }
140 void StdCompilerBinRead::Byte(int8_t &rByte) { ReadValue(rByte); }
141 void StdCompilerBinRead::Byte(uint8_t &rByte) { ReadValue(rByte); }
142 void StdCompilerBinRead::Boolean(bool &rBool) { ReadValue(rBool); }
143 void StdCompilerBinRead::Character(char &rChar) { ReadValue(rChar); }
144 void StdCompilerBinRead::String(char *szString, size_t iMaxLength, RawCompileType eType)
145 {
146  // At least one byte data needed
147  if (iPos >= Buf.getSize())
148  { excEOF(); return; }
149  // Copy until no data left
150  char *pPos = szString;
151  while ((*pPos++ = *getBufPtr<char>(Buf, iPos++)))
152  if (iPos >= Buf.getSize())
153  { excEOF(); return; }
154  else if (pPos > szString + iMaxLength)
155  { excCorrupt("string too long"); return; }
156 }
157 
158 void StdCompilerBinRead::String(char **pszString, RawCompileType eType)
159 {
160  // At least one byte data needed
161  if (iPos >= Buf.getSize())
162  { excEOF(); return; }
163  int iStart = iPos;
164  // Search string end
165  while (*getBufPtr<char>(Buf, iPos++))
166  if (iPos >= Buf.getSize())
167  { excEOF(); return; }
168  // Allocate and copy data
169  *pszString = (char *) malloc(iPos - iStart);
170  memcpy(*pszString, Buf.getPtr(iStart), iPos - iStart);
171 }
172 
173 void StdCompilerBinRead::String(std::string &str, RawCompileType type)
174 {
175  // At least one byte data needed
176  if (iPos >= Buf.getSize())
177  {
178  excEOF(); return;
179  }
180  int iStart = iPos;
181  // Search string end
182  while (*getBufPtr<char>(Buf, iPos++))
183  if (iPos >= Buf.getSize())
184  {
185  excEOF(); return;
186  }
187  // Copy data
188  str.assign(getBufPtr<char>(Buf, iStart), getBufPtr<char>(Buf, iPos));
189 }
190 
191 void StdCompilerBinRead::Raw(void *pData, size_t iSize, RawCompileType eType)
192 {
193  if (iPos + iSize > Buf.getSize())
194  { excEOF(); return; }
195  // Copy data
196  memcpy(pData, Buf.getPtr(iPos), iSize);
197  iPos += iSize;
198 }
199 
201 {
202  return FormatString("byte %ld", static_cast<unsigned long>(iPos));
203 }
204 
205 template <class T>
206 inline void StdCompilerBinRead::ReadValue(T &rValue)
207 {
208  // Pufferüberhang prüfen
209  if (iPos + sizeof(T) > Buf.getSize())
210  { excEOF(); return; }
211  // Kopieren
212  rValue = *getBufPtr<T>(Buf, iPos);
213  iPos += sizeof(T);
214 }
215 
217 {
218  iPos = 0;
219 }
220 
221 // *** StdCompilerINIWrite
222 
223 bool StdCompilerINIWrite::Name(const char *szName)
224 {
225  // Sub-Namesections exist, so it's a section. Write name if not already done so.
226  if (fPutName) PutName(true);
227  // Push struct
228  Naming *pnNaming = new Naming;
229  pnNaming->Name.Copy(szName);
230  pnNaming->Parent = pNaming;
231  pNaming = pnNaming;
232  iDepth++;
233  // Done
234  fPutName = true; fInSection = false;
235  return true;
236 }
237 
239 {
240  // Append newline
241  if (!fPutName && !fInSection)
242  Buf.Append("\r\n");
243  fPutName = false;
244  // Note this makes it impossible to distinguish an empty name section from
245  // a non-existing name section.
246 
247  // Pop
248  assert(iDepth);
249  Naming *poNaming = pNaming;
250  pNaming = poNaming->Parent;
251  delete poNaming;
252  iDepth--;
253  // We're inside a section now
254  fInSection = true;
255 }
256 
258 {
259  if (fInSection)
260  {
261  // Re-put section name
262  PutName(true);
263  }
264  else
265  {
266  PrepareForValue();
268  }
269  return true;
270 }
271 
272 void StdCompilerINIWrite::DWord(int32_t &rInt)
273 {
274  PrepareForValue();
275  Buf.AppendFormat("%d", rInt);
276 }
277 void StdCompilerINIWrite::DWord(uint32_t &rInt)
278 {
279  PrepareForValue();
280  Buf.AppendFormat("%u", rInt);
281 }
282 void StdCompilerINIWrite::Word(int16_t &rInt)
283 {
284  PrepareForValue();
285  Buf.AppendFormat("%d", rInt);
286 }
287 void StdCompilerINIWrite::Word(uint16_t &rInt)
288 {
289  PrepareForValue();
290  Buf.AppendFormat("%u", rInt);
291 }
292 void StdCompilerINIWrite::Byte(int8_t &rByte)
293 {
294  PrepareForValue();
295  Buf.AppendFormat("%d", rByte);
296 }
297 void StdCompilerINIWrite::Byte(uint8_t &rInt)
298 {
299  PrepareForValue();
300  Buf.AppendFormat("%u", rInt);
301 }
303 {
304  PrepareForValue();
305  Buf.Append(rBool ? "true" : "false");
306 }
308 {
309  PrepareForValue();
310  Buf.AppendFormat("%c", rChar);
311 }
312 
313 void StdCompilerINIWrite::String(char *szString, size_t iMaxLength, RawCompileType eType)
314 {
315  PrepareForValue();
316  switch (eType)
317  {
318  case RCT_Escaped:
319  WriteEscaped(szString, szString + strlen(szString));
320  break;
321  case RCT_All:
322  case RCT_Idtf:
323  case RCT_IdtfAllowEmpty:
324  case RCT_ID:
325  Buf.Append(szString);
326  }
327 }
328 
329 void StdCompilerINIWrite::String(char **pszString, RawCompileType eType)
330 {
331  char cNull = '\0';
332  String(*pszString ? *pszString : &cNull, 0, eType);
333 }
334 
335 void StdCompilerINIWrite::Raw(void *pData, size_t iSize, RawCompileType eType)
336 {
337  switch (eType)
338  {
339  case RCT_Escaped:
340  WriteEscaped(reinterpret_cast<char *>(pData), reinterpret_cast<char *>(pData) + iSize);
341  break;
342  case RCT_All:
343  case RCT_Idtf:
344  case RCT_IdtfAllowEmpty:
345  case RCT_ID:
346  Buf.Append(reinterpret_cast<char *>(pData), iSize);
347  }
348 }
349 
350 void StdCompilerINIWrite::String(std::string &str, RawCompileType type)
351 {
352  Raw(&str[0], str.size(), type);
353 }
354 
356 {
357  pNaming = nullptr;
358  fPutName = false;
359  iDepth = 0;
360  fInSection = false;
361  Buf.Clear();
362 }
363 
365 {
366  // Ensure all namings were closed properly
367  assert(!iDepth);
368 }
369 
371 {
372  // Put name (value-type), if not already done so
373  if (fPutName) PutName(false);
374  // No data allowed inside of sections
375  assert(!fInSection);
376  // No values allowed on top-level - must be contained in at least one section
377  assert(iDepth > 1);
378 }
379 
380 void StdCompilerINIWrite::WriteEscaped(const char *szString, const char *pEnd)
381 {
382  Buf.AppendChar('"');
383  // Try to write chunks as huge as possible of "normal" chars.
384  // Note this excludes '\0', so the standard Append() can be used.
385  const char *pStart, *pPos; pStart = pPos = szString;
386  bool fLastNumEscape = false; // catch "\1""1", which must become "\1\61"
387  for (; pPos < pEnd; pPos++)
388  if (!isprint((unsigned char)(unsigned char) *pPos) || *pPos == '\\' || *pPos == '"' || (fLastNumEscape && isdigit((unsigned char)*pPos)))
389  {
390  // Write everything up to this point
391  if (pPos - pStart) Buf.Append(pStart, pPos - pStart);
392  // Escape
393  fLastNumEscape = false;
394  switch (*pPos)
395  {
396  case '\a': Buf.Append("\\a"); break;
397  case '\b': Buf.Append("\\b"); break;
398  case '\f': Buf.Append("\\f"); break;
399  case '\n': Buf.Append("\\n"); break;
400  case '\r': Buf.Append("\\r"); break;
401  case '\t': Buf.Append("\\t"); break;
402  case '\v': Buf.Append("\\v"); break;
403  case '\"': Buf.Append("\\\""); break;
404  case '\\': Buf.Append("\\\\"); break;
405  default:
406  Buf.AppendFormat("\\%o", *reinterpret_cast<const unsigned char *>(pPos));
407  fLastNumEscape = true;
408  }
409  // Set pointer
410  pStart = pPos + 1;
411  }
412  else
413  fLastNumEscape = false;
414  // Write the rest
415  if (pEnd - pStart) Buf.Append(pStart, pEnd - pStart);
416  Buf.AppendChar('"');
417 }
418 
420 {
421  // Do not indent level 1 (level 0 values aren't allowed - see above)
422  int iIndent = iDepth - 1;
423  // Sections are indented more, even though they belong to this level
424  if (!fSection) iIndent--;
425  // Do indention
426  if (iIndent <= 0) return;
427  Buf.AppendChars(' ', iIndent * 2);
428 }
429 
430 void StdCompilerINIWrite::PutName(bool fSection)
431 {
432  if (fSection && Buf.getLength())
433  Buf.Append("\r\n");
434  WriteIndent(fSection);
435  // Put name
436  if (fSection)
437  Buf.AppendFormat("[%s]\r\n", pNaming->Name.getData());
438  else
439  Buf.AppendFormat("%s=", pNaming->Name.getData());
440  // Set flag
441  fPutName = false;
442 }
443 
444 // *** StdCompilerINIRead
445 
447  : pNameRoot(nullptr), iDepth(0), iRealDepth(0)
448 {
449 
450 }
451 
453 {
454  FreeNameTree();
455 }
456 
457 // Naming
458 bool StdCompilerINIRead::Name(const char *szName)
459 {
460  // Increase depth
461  iDepth++;
462  // Parent category virtual?
463  if (iDepth - 1 > iRealDepth)
464  return false;
465  // Name must be alphanumerical and non-empty (force it)
466  if (!isalpha((unsigned char)*szName))
467  { assert(false); return false; }
468  for (const char *p = szName + 1; *p; p++)
469  // C4Update needs Name**...
470  if (!isalnum((unsigned char)*p) && *p != ' ' && *p != '_' && *p != '*')
471  { assert(false); return false; }
472  // Search name
473  NameNode *pNode;
474  for (pNode = pName->FirstChild; pNode; pNode = pNode->NextChild)
475  if (pNode->Pos && pNode->Name == szName)
476  break;
477  // Not found?
478  if (!pNode)
479  {
480  NotFoundName = szName;
481  return false;
482  }
483  // Save tree position, indicate success
484  pName = pNode;
485  pPos = pName->Pos;
486  pReenter = nullptr;
487  iRealDepth++;
488  return true;
489 }
491 {
492  assert(iDepth > 0);
493  if (iRealDepth == iDepth)
494  {
495  // Remove childs
496  for (NameNode *pNode = pName->FirstChild, *pNext; pNode; pNode = pNext)
497  {
498  // Report unused entries
499  if (pNode->Pos && !fBreak)
500  Warn("Unexpected %s \"%s\"!", pNode->Section ? "section" : "value", pNode->Name.getData());
501  // delete node
502  pNext = pNode->NextChild;
503  delete pNode;
504  }
505  // Remove name so it won't be found again
506  NameNode *pParent = pName->Parent;
509  delete pName;
510  // Go up
511  pName = pParent;
512  iRealDepth--;
513  }
514  // Decrease depth
515  iDepth--;
516  // This is the middle of nowhere
517  pPos = nullptr; pReenter = nullptr;
518 }
519 
520 bool StdCompilerINIRead::FollowName(const char *szName)
521 {
522  // Current naming virtual?
523  if (iDepth > iRealDepth)
524  return false;
525  // Next section must be the one
526  if (!pName->NextChild || pName->NextChild->Name != szName)
527  {
528  // End current naming
529  NameEnd();
530  // Go into virtual naming
531  iDepth++;
532  return false;
533  }
534  // End current naming
535  NameEnd();
536  // Start new one
537  Name(szName);
538  // Done
539  return true;
540 }
541 
542 // Separators
544 {
545  if (iDepth > iRealDepth) return false;
546  // In section?
547  if (pName->Section)
548  {
549  // Store current name, search another section with the same name
550  StdStrBuf CurrName = pName->Name;
551  NameEnd();
552  return Name(CurrName.getData());
553  }
554  // Position saved back from separator mismatch?
555  if (pReenter) { pPos = pReenter; pReenter = nullptr; }
556  // Nothing to read?
557  if (!pPos) return false;
558  // Read (while skipping over whitespace)
559  SkipWhitespace();
560  // Separator mismatch? Let all read attempts fail until the correct separator is found or the naming ends.
561  if (*pPos != SeparatorToChar(eSep)) { pReenter = pPos; pPos = nullptr; return false; }
562  // Go over separator, success
563  pPos++;
564  return true;
565 }
566 
568 {
569  // Position saved back from separator mismatch?
570  if (pReenter) { pPos = pReenter; pReenter = nullptr; }
571 }
572 
573 int StdCompilerINIRead::NameCount(const char *szName)
574 {
575  // not in virtual naming
576  if (iDepth > iRealDepth || !pName) return 0;
577  // count within current name
578  int iCount = 0;
579  NameNode *pNode;
580  for (pNode = pName->FirstChild; pNode; pNode = pNode->NextChild)
581  // if no name is given, all valid subsections are counted
582  if (pNode->Pos && (!szName || pNode->Name == szName))
583  ++iCount;
584  return iCount;
585 }
586 
587 const char *StdCompilerINIRead::GetNameByIndex(size_t idx) const
588 {
589  // not in virtual naming
590  if (iDepth > iRealDepth || !pName) return 0;
591  // count within current name
592  NameNode *pNode;
593  for (pNode = pName->FirstChild; pNode; pNode = pNode->NextChild)
594  // all valid subsections are counted
595  if (pNode->Pos)
596  if (!idx--)
597  return pNode->Name.getData();
598  // index out of range
599  return nullptr;
600 }
601 
602 // Various data readers
603 void StdCompilerINIRead::DWord(int32_t &rInt)
604 {
605  rInt = ReadNum();
606 }
607 void StdCompilerINIRead::DWord(uint32_t &rInt)
608 {
609  rInt = ReadUNum();
610 }
611 void StdCompilerINIRead::Word(int16_t &rShort)
612 {
613  const int MIN = -(1 << 15), MAX = (1 << 15) - 1;
614  int iNum = ReadNum();
615  if (iNum < MIN || iNum > MAX)
616  Warn("number out of range (%d to %d): %d ", MIN, MAX, iNum);
617  rShort = Clamp(iNum, MIN, MAX);
618 }
619 void StdCompilerINIRead::Word(uint16_t &rShort)
620 {
621  const unsigned int MIN = 0, MAX = (1 << 16) - 1;
622  unsigned int iNum = ReadUNum();
623  if (iNum > MAX)
624  Warn("number out of range (%u to %u): %u ", MIN, MAX, iNum);
625  rShort = Clamp(iNum, MIN, MAX);
626 }
627 void StdCompilerINIRead::Byte(int8_t &rByte)
628 {
629  const int MIN = -(1 << 7), MAX = (1 << 7) - 1;
630  int iNum = ReadNum();
631  if (iNum < MIN || iNum > MAX)
632  Warn("number out of range (%d to %d): %d ", MIN, MAX, iNum);
633  rByte = Clamp(iNum, MIN, MAX);
634 }
635 void StdCompilerINIRead::Byte(uint8_t &rByte)
636 {
637  const unsigned int MIN = 0, MAX = (1 << 8) - 1;
638  unsigned int iNum = ReadUNum();
639  if (iNum > MAX)
640  Warn("number out of range (%u to %u): %u ", MIN, MAX, iNum);
641  rByte = Clamp(iNum, MIN, MAX);
642 }
644 {
645  if (!pPos) { notFound("Boolean"); return; }
646  if (*pPos == '1' && !isdigit((unsigned char)*(pPos+1)))
647  { rBool = true; pPos ++; }
648  else if (*pPos == '0' && !isdigit((unsigned char)*(pPos+1)))
649  { rBool = false; pPos ++; }
650  else if (SEqual2(pPos, "true"))
651  { rBool = true; pPos += 4; }
652  else if (SEqual2(pPos, "false"))
653  { rBool = false; pPos += 5; }
654  else
655  { notFound("Boolean"); return; }
656 }
658 {
659  if (!pPos || !isalpha((unsigned char)*pPos))
660  { notFound("Character"); return; }
661  rChar = *pPos++;
662 }
663 void StdCompilerINIRead::String(char *szString, size_t iMaxLength, RawCompileType eType)
664 {
665  // Read data
666  StdBuf Buf = ReadString(iMaxLength, eType, true);
667  // Copy
668  SCopy(getBufPtr<char>(Buf), szString, iMaxLength);
669 }
670 void StdCompilerINIRead::String(char **pszString, RawCompileType eType)
671 {
672  // Get length
673  size_t iLength = GetStringLength(eType);
674  // Read data
675  StdBuf Buf = ReadString(iLength, eType, true);
676  // Set
677  *pszString = reinterpret_cast<char *>(Buf.GrabPointer());
678 }
679 void StdCompilerINIRead::String(std::string &str, RawCompileType type)
680 {
681  // Get length
682  size_t iLength = GetStringLength(type);
683  // Read data
684  StdBuf Buf = ReadString(iLength, type, true);
685  str = getBufPtr<char>(Buf);
686 }
687 void StdCompilerINIRead::Raw(void *pData, size_t iSize, RawCompileType eType)
688 {
689  // Read data
690  StdBuf Buf = ReadString(iSize, eType, false);
691  // Correct size?
692  if (Buf.getSize() != iSize)
693  Warn("got %u bytes raw data, but %u bytes expected!", Buf.getSize(), iSize);
694  // Copy
695  MemCopy(Buf.getData(), pData, iSize);
696 }
697 
698 uint32_t StdCompilerINIRead::getLineNumberOfPos(const char *pos) const
699 {
700  // Figure out quickly whether we already know which line this is
701  auto entry = std::lower_bound(lineBreaks.begin(), lineBreaks.end(), pos);
702  if (entry != lineBreaks.end())
703  {
704  return std::distance(lineBreaks.begin(), entry) + 1;
705  }
706  // Otherwise search through the buffer until we find out, filling the
707  // cache in the process
708  const char *cursor = Buf.getData();
709  if (!lineBreaks.empty())
710  cursor = *(lineBreaks.end() - 1) + 1;
711  for (;;)
712  {
713  if (*cursor == '\0' || *cursor == '\n')
714  {
715  lineBreaks.push_back(cursor);
716 
717  // If we're at the end of the file or have found the line break
718  // past the requested position, we're done for now
719  if (*cursor == '\0' || pos < cursor)
720  {
721  break;
722  }
723  }
724  ++cursor;
725  }
726  return std::distance(lineBreaks.begin(),
727  std::lower_bound(lineBreaks.begin(), lineBreaks.end(), pos)) + 1;
728 }
729 
731 {
732  if (pPos)
733  return FormatString("line %d", getLineNumberOfPos(pPos));
734  else if (iDepth == iRealDepth)
735  return FormatString(pName->Section ? "section \"%s\", after line %d" : "value \"%s\", line %d", pName->Name.getData(), getLineNumberOfPos(pName->Pos));
736  else if (iRealDepth)
737  return FormatString("missing value/section \"%s\" inside section \"%s\" (line %d)", NotFoundName.getData(), pName->Name.getData(), getLineNumberOfPos(pName->Pos));
738  else
739  return FormatString("missing value/section \"%s\"", NotFoundName.getData());
740 }
741 
743 {
744  // Already running? This may happen if someone confuses Compile with Value.
745  assert(!iDepth && !iRealDepth && !pNameRoot);
746  // Create tree
747  CreateNameTree();
748  // Start must be inside a section
749  iDepth = iRealDepth = 0;
750  pPos = nullptr; pReenter = nullptr;
751 }
753 {
754  assert(!iDepth && !iRealDepth);
755  FreeNameTree();
756 }
757 
759 {
760  FreeNameTree();
761  // Create root node
762  pName = pNameRoot = new NameNode();
763  // No input? Stop
764  if (!Buf) return;
765  // Start scanning
766  pPos = Buf.getPtr(0);
767  while (*pPos)
768  {
769  // Go over whitespace
770  int iIndent = 0;
771  while (*pPos == ' ' || *pPos == '\t')
772  { pPos++; iIndent++; }
773  // Name/Section?
774  bool fSection = *pPos == '[' && isalpha((unsigned char)*(pPos+1));
775  if (fSection || isalpha((unsigned char)*pPos))
776  {
777  // Treat values as if they had more indention
778  // (so they become children of sections on the same level)
779  if (!fSection) iIndent++; else pPos++;
780  // Go up in tree structure if there is less indention
781  while (pName->Parent && pName->Indent >= iIndent)
782  pName = pName->Parent;
783  // Copy name
784  StdStrBuf Name;
785  while (isalnum((unsigned char)*pPos) || *pPos == ' ' || *pPos == '_')
786  Name.AppendChar(*pPos++);
787  while (*pPos == ' ' || *pPos == '\t') pPos++;
788  if ( *pPos != (fSection ? ']' : '=') )
789  // Warn, ignore
790  Warn(isprint((unsigned char)*pPos) ? "Unexpected character ('%c'): %s ignored" : "Unexpected character ('0x%02x'): %s ignored", unsigned(*pPos), fSection ? "section" : "value");
791  else
792  {
793  pPos++;
794  // Create new node
795  NameNode *pPrev = pName->LastChild;
796  pName =
797  pName->LastChild =
799  new NameNode(pName);
800  pName->PrevChild = pPrev;
801  pName->Name.Take(std::move(Name));
802  pName->Pos = pPos;
803  pName->Indent = iIndent;
804  pName->Section = fSection;
805  // Values don't have children (even if the indention looks like it)
806  if (!fSection)
807  pName = pName->Parent;
808  }
809  }
810  // Skip line
811  while (*pPos && (*pPos != '\n' && *pPos != '\r'))
812  pPos++;
813  while (*pPos == '\n' || *pPos == '\r')
814  pPos++;
815  }
816  // Set pointer back
817  pName = pNameRoot;
818 }
819 
821 {
822  // free all nodes
824  pName = pNameRoot = nullptr;
825 }
826 
828 {
829  NameNode *pNode = pDelNode;
830  while (pNode)
831  {
832  if (pNode->FirstChild)
833  pNode = pNode->FirstChild;
834  else
835  {
836  NameNode *pDelete = pNode;
837  if (pDelete == pDelNode) { delete pDelete; break; }
838  if (pNode->NextChild)
839  pNode = pNode->NextChild;
840  else
841  {
842  pNode = pNode->Parent;
843  if (pNode) pNode->FirstChild = nullptr;
844  }
845  delete pDelete;
846  }
847  }
848 }
849 
851 {
852  while (*pPos == ' ' || *pPos == '\t')
853  pPos++;
854 }
855 
857 {
858  while (*pPos == '+' || *pPos == '-' || isdigit((unsigned char)*pPos))
859  pPos++;
860 }
861 
863 {
864  if (!pPos)
865  { notFound("Number"); return 0; }
866  // Skip whitespace
867  SkipWhitespace();
868  // Read number. If this breaks, Günther is to blame!
869  const char *pnPos = pPos;
870  long iNum = strtol(pPos, const_cast<char **>(&pnPos), 10);
871  // Could not read?
872  if (!iNum && pnPos == pPos)
873  { notFound("Number"); return 0; }
874  // Get over it
875  pPos = pnPos;
876  return iNum;
877 }
878 
880 {
881  if (!pPos)
882  { notFound("Number"); return 0; }
883  // Skip whitespace
884  SkipWhitespace();
885  // Read number. If this breaks, Günther is to blame!
886  const char *pnPos = pPos;
887  unsigned long iNum = strtoul(pPos, const_cast<char **>(&pnPos), 10);
888  // Could not read?
889  if (!iNum && pnPos == pPos)
890  { notFound("Number"); return 0; }
891  // Get over it
892  pPos = pnPos;
893  return iNum;
894 }
895 
897 {
898  // Excpect valid position
899  if (!pPos)
900  { notFound("String"); return 0; }
901  // Skip whitespace
902  SkipWhitespace();
903  // Save position
904  const char *pStart = pPos;
905  // Escaped? Go over '"'
906  if (eRawType == RCT_Escaped && *pPos++ != '"')
907  { notFound("Escaped string"); return 0; }
908  // Search end of string
909  size_t iLength = 0;
910  while (!TestStringEnd(eRawType))
911  {
912  // Read a character (we're just counting atm)
913  if (eRawType == RCT_Escaped)
914  ReadEscapedChar();
915  else
916  pPos++;
917  // Count it
918  iLength++;
919  }
920  // Reset position, return the length
921  pPos = pStart;
922  return iLength;
923 }
924 
925 StdBuf StdCompilerINIRead::ReadString(size_t iLength, RawCompileType eRawType, bool fAppendNull)
926 {
927  // Excpect valid position
928  if (!pPos)
929  { notFound("String"); return StdBuf(); }
930  // Skip whitespace
931  SkipWhitespace();
932  // Escaped? Go over '"'
933  if (eRawType == RCT_Escaped && *pPos++ != '"')
934  { notFound("Escaped string"); return StdBuf(); }
935  // Create buffer
936  StdBuf OutBuf; OutBuf.New(iLength + (fAppendNull ? sizeof('\0') : 0));
937  // Read
938  char *pOut = getMBufPtr<char>(OutBuf);
939  while (iLength && !TestStringEnd(eRawType))
940  {
941  // Read a character
942  if (eRawType == RCT_Escaped)
943  *pOut++ = ReadEscapedChar();
944  else
945  *pOut++ = *pPos++;
946  // Count it
947  iLength--;
948  }
949  // Escaped: Go over '"'
950  if (eRawType == RCT_Escaped)
951  {
952  while (*pPos != '"')
953  {
954  if (!*pPos || *pPos == '\n' || *pPos == '\r')
955  {
956  Warn("string not terminated!");
957  pPos--;
958  break;
959  }
960  pPos++;
961  }
962  pPos++;
963  }
964  // Nothing read? Identifiers need to be non-empty
965  if (pOut == OutBuf.getData() && (eRawType == RCT_Idtf || eRawType == RCT_ID))
966  { notFound("String"); return StdBuf(); }
967  // Append null
968  if (fAppendNull)
969  *pOut = '\0';
970  // Shrink, if less characters were read
971  OutBuf.Shrink(iLength);
972  // Done
973  return OutBuf;
974 }
975 
977 {
978  // Catch some no-noes like \0, \n etc.
979  if (*pPos >= 0 && iscntrl((unsigned char)*pPos))
980  {
981  Warn("Nonprintable character found in string: %02x", static_cast<unsigned char>(*pPos));
982  return *pPos;
983  }
984  // Not escaped? Just return it
985  if (*pPos != '\\') return *pPos++;
986  // What type of escape?
987  switch (*++pPos)
988  {
989  case 'a': pPos++; return '\a';
990  case 'b': pPos++; return '\b';
991  case 'f': pPos++; return '\f';
992  case 'n': pPos++; return '\n';
993  case 'r': pPos++; return '\r';
994  case 't': pPos++; return '\t';
995  case 'v': pPos++; return '\v';
996  case '\'': pPos++; return '\'';
997  case '"': pPos++; return '"';
998  case '\\': pPos++; return '\\';
999  case '?': pPos++; return '?';
1000  case 'x':
1001  // Treat '\x' as 'x' - damn special cases
1002  if (!isxdigit((unsigned char)*++pPos))
1003  return 'x';
1004  else
1005  {
1006  // Read everything that looks like it might be hexadecimal - MSVC does it this way, so do not sue me.
1007  int iCode = 0;
1008  do
1009  { iCode = iCode * 16 + (isdigit((unsigned char)*pPos) ? *pPos - '0' : *pPos - 'a' + 10); pPos++; }
1010  while (isxdigit((unsigned char)*pPos));
1011  // Done. Don't bother to check the range (we aren't doing anything mission-critical here, are we?)
1012  return char(iCode);
1013  }
1014  default:
1015  // Not octal? Let it pass through.
1016  if (!isdigit((unsigned char)*pPos) || *pPos >= '8')
1017  return *pPos++;
1018  else
1019  {
1020  // Read it the octal way.
1021  int iCode = 0;
1022  do
1023  { iCode = iCode * 8 + (*pPos - '0'); pPos++;}
1024  while (isdigit((unsigned char)*pPos) && *pPos < '8');
1025  // Done. See above.
1026  return char(iCode);
1027  }
1028  }
1029  // unreachable
1030  assert (false);
1031 }
1032 
1033 void StdCompilerINIRead::notFound(const char *szWhat)
1034 {
1035  excNotFound("%s expected", szWhat);
1036 }
1037 
1038 void StdCompilerWarnCallback(void *pData, const char *szPosition, const char *szError)
1039 {
1040  const char *szName = reinterpret_cast<const char *>(pData);
1041  if (!szPosition || !*szPosition)
1042  DebugLogF("WARNING: %s (in %s)", szError, szName);
1043  else
1044  DebugLogF("WARNING: %s (in %s, %s)", szError, szPosition, szName);
1045 }
const char * getData() const
Definition: StdBuf.h:450
void WriteData(const void *pData, size_t iSize)
void WriteEscaped(const char *szString, const char *pEnd)
virtual bool Name(const char *szName) override
const void * getData() const
Definition: StdBuf.h:107
Definition: StdBuf.h:37
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:129
virtual void Boolean(bool &rBool) override
virtual void Boolean(bool &rBool) override
virtual void String(char *szString, size_t iMaxLength, RawCompileType eType=RCT_Escaped) override
virtual void Character(char &rChar) override
void excCorrupt(const char *szMessage,...)
Definition: StdCompiler.h:259
void WriteIndent(bool fSectionName)
virtual void Boolean(bool &rBool) override
Definition: StdCompiler.cpp:79
void Clear()
Definition: StdBuf.h:474
void StdCompilerWarnCallback(void *pData, const char *szPosition, const char *szError)
StdBuf ReadString(size_t iLength, RawCompileType eTyped, bool fAppendNull=true)
virtual StdStrBuf getPosition() const override
virtual void Word(int16_t &rShort) override
virtual void Word(int16_t &rShort) override
void * GrabPointer()
Definition: StdBuf.h:141
void excEOF(const char *szMessage="EOF",...)
Definition: StdCompiler.h:253
const char * pPos
Definition: StdCompiler.h:713
virtual void String(char *szString, size_t iMaxLength, RawCompileType eType=RCT_Escaped) override
Definition: StdCompiler.cpp:81
virtual StdStrBuf getPosition() const
Definition: StdCompiler.h:161
virtual void Byte(int8_t &rByte) override
Definition: StdCompiler.cpp:77
virtual void Byte(int8_t &rByte) override
void AppendChars(char cChar, size_t iCnt)
Definition: StdBuf.h:590
size_t getSize() const
Definition: StdBuf.h:109
virtual void NameEnd(bool fBreak=false) override
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:46
virtual void String(char *szString, size_t iMaxLength, RawCompileType eType=RCT_Escaped) override
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:197
virtual void Begin() override
void AppendChar(char cChar)
Definition: StdBuf.h:596
NameNode * pNameRoot
Definition: StdCompiler.h:704
virtual bool Name(const char *szName) override
static char SeparatorToChar(Sep eSep)
Definition: StdCompiler.cpp:37
virtual const char * GetNameByIndex(size_t idx) const override
void WriteValue(const T &rValue)
Definition: StdCompiler.cpp:99
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:281
virtual void Raw(void *pData, size_t iSize, RawCompileType eType=RCT_Escaped) override
virtual void End() override
virtual void DWord(int32_t &rInt) override
Definition: StdCompiler.cpp:73
virtual void Character(char &rChar) override
void FreeNameNode(NameNode *pNode)
StdCopyStrBuf NotFoundName
Definition: StdCompiler.h:719
void Take(char *pnData)
Definition: StdBuf.h:465
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
unsigned long ReadUNum()
virtual void Raw(void *pData, size_t iSize, RawCompileType eType=RCT_Escaped) override
static bool IsStringEnd(char c, RawCompileType eType)
Definition: StdCompiler.cpp:58
virtual void Boolean(bool &rBool) override
void Shrink(size_t iShrink)
Definition: StdBuf.h:188
virtual void BeginSecond() override
void excNotFound(const char *szMessage,...)
Definition: StdCompiler.h:243
void MemCopy(const void *lpMem1, void *lpMem2, size_t dwSize)
Definition: Standard.h:68
virtual void Character(char &rChar) override
virtual void Begin() override
virtual void Byte(int8_t &rByte) override
void New(size_t inSize)
Definition: StdBuf.h:154
void Write(const void *pnData, size_t inSize, size_t iAt=0)
Definition: StdBuf.h:161
void notFound(const char *szWhat)
virtual void Byte(int8_t &rByte) override
virtual void DWord(int32_t &rInt) override
virtual void Begin() override
void Warn(const char *szWarning,...)
Definition: StdCompiler.cpp:26
virtual void Word(int16_t &rShort) override
Definition: StdCompiler.cpp:75
virtual void NoSeparator() override
virtual void Begin() override
virtual bool Separator(Sep eSep) override
virtual void End() override
virtual void String(char *szString, size_t iMaxLength, RawCompileType eType=RCT_Escaped) override
void PutName(bool fSection)
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:175
void FormatV(const char *szFmt, va_list args)
Definition: StdBuf.cpp:189
virtual void Raw(void *pData, size_t iSize, RawCompileType eType=RCT_Escaped) override
const char * getPtr(size_t i) const
Definition: StdBuf.h:456
const void * getPtr(size_t i) const
Definition: StdBuf.h:112
virtual bool Separator(Sep eSep) override
size_t getLength() const
Definition: StdBuf.h:453
virtual StdStrBuf getPosition() const override
virtual void DWord(int32_t &rInt) override
bool TestStringEnd(RawCompileType eType)
Definition: StdCompiler.h:734
virtual bool FollowName(const char *szName) override
virtual void Word(int16_t &rShort) override
void Copy()
Definition: StdBuf.h:475
NameNode * pName
Definition: StdCompiler.h:704
virtual void DWord(int32_t &rInt) override
size_t GetStringLength(RawCompileType eTyped)
virtual void NameEnd(bool fBreak=false) override
virtual void Raw(void *pData, size_t iSize, RawCompileType eType=RCT_Escaped) override
virtual int NameCount(const char *szName=nullptr) override
void ReadValue(T &rValue)
int iSize
Definition: TstC4NetIO.cpp:35
const char * pReenter
Definition: StdCompiler.h:716
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
virtual void Character(char &rChar) override
Definition: StdCompiler.cpp:80