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