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