static void WriteXMLChar(BufferedOutputStream &os, TCHAR ch) { switch (ch) { case '<': os.Write("<"); break; case '>': os.Write(">"); break; case '&': os.Write("&"); break; case '\'': os.Write("'"); break; case '"': os.Write("""); break; default: if (IsWhitespaceOrNull(ch)) ch = ' '; os.Write(ch); break; } }
void PortLineSplitter::DataReceived(const void *_data, size_t length) { assert(_data != NULL); assert(length > 0); const char *data = (const char *)_data, *end = data + length; do { /* append new data to buffer, as much as fits there */ auto range = buffer.Write(); if (range.IsEmpty()) { /* overflow: reset buffer to recover quickly */ buffer.Clear(); continue; } size_t nbytes = std::min(size_t(range.size), size_t(end - data)); memcpy(range.data, data, nbytes); data += nbytes; buffer.Append(nbytes); while (true) { /* read data from the buffer, to see if there's a newline character */ range = buffer.Read(); if (range.IsEmpty()) break; char *newline = (char *)memchr(range.data, '\n', range.size); if (newline == NULL) /* no newline here: wait for more data */ break; /* remove trailing whitespace, such as '\r' */ char *end = newline; while (end > range.data && IsWhitespaceOrNull(end[-1])) --end; *end = '\0'; SanitiseLine(range.data, end); const char *line = range.data; /* if there are NUL bytes in the line, skip to after the last one, to avoid conflicts with NUL terminated C strings due to binary garbage */ const void *nul; while ((nul = memchr(line, 0, end - line)) != NULL) line = (const char *)nul + 1; LineReceived(line); buffer.Consume(newline - range.data + 1); } } while (data < end); }
/** * Find next non-white space character. */ static TCHAR FindNonWhiteSpace(XML::Parser *pXML) { assert(pXML); // Iterate through characters in the string until we find a NULL or a // non-white space character TCHAR ch; while ((ch = GetNextChar(pXML)) != 0) { if (!IsWhitespaceOrNull(ch)) return ch; } return 0; }
/** * Trim the end of the text to remove white space characters. */ gcc_pure static size_t FindEndOfText(const TCHAR *token, size_t length) { assert(token != NULL); --length; while (1) { if (IsWhitespaceOrNull(token[length])) return length + 1; --length; } }
gcc_pure static bool CompareTagName(const TCHAR *cclose, const TCHAR *copen) { assert(cclose != nullptr); assert(copen != nullptr); size_t l = _tcslen(cclose); if (!StringIsEqualIgnoreCase(cclose, copen, l)) return false; const TCHAR c = copen[l]; if (IsWhitespaceOrNull(c) || (c == _T('/')) || (c == _T('<')) || (c == _T('>')) || (c == _T('='))) return true; return false; }
/** * Reads next FlarmNet.org file entry and returns the pointer to * a new FlarmNetRecord instance or NULL on error. * * The caller is responsible for deleting the object again! */ static bool LoadRecord(FlarmRecord &record, const char *line) { if (strlen(line) < 172) return false; LoadString(line, 6, record.id); LoadString(line + 12, 21, record.pilot); LoadString(line + 54, 21, record.airfield); LoadString(line + 96, 21, record.plane_type); LoadString(line + 138, 7, record.registration); LoadString(line + 152, 3, record.callsign); LoadString(line + 158, 7, record.frequency); // Terminate callsign string on first whitespace for (TCHAR *i = record.callsign.buffer(); *i != _T('\0'); ++i) if (IsWhitespaceOrNull(*i)) *i = _T('\0'); return true; }
/** * Reads next FlarmNet.org file entry and returns the pointer to * a new FlarmNetRecord instance or NULL on error. * * The caller is responsible for deleting the object again! */ static bool LoadRecord(FlarmRecord &record, const char *line) { if (strlen(line) < 172) return false; LoadString(line, 6, record.id); LoadString(line + 12, 21, record.pilot); LoadString(line + 54, 21, record.airfield); LoadString(line + 96, 21, record.plane_type); LoadString(line + 138, 7, record.registration); LoadString(line + 152, 3, record.callsign); LoadString(line + 158, 7, record.frequency); // Terminate callsign string on first whitespace int maxSize = sizeof(record.callsign) / sizeof(TCHAR); for (int i = 0; record.callsign[i] != 0 && i < maxSize; i++) if (IsWhitespaceOrNull(record.callsign[i])) record.callsign[i] = 0; return true; }
static const char * ParseLine(char *line) { char *separator = strchr(line, '='); if (separator == NULL) /* malformed line */ return NULL; char *p = separator; while (p > separator && IsWhitespaceOrNull(p[-1])) --p; if (p == line) /* empty name */ return NULL; *p = 0; char *value = const_cast<char *>(TrimLeft(separator + 1)); TrimRight(value); return value; }
/** * Is the given character whitespace? This calls the faster one of * IsWhitespaceOrNull() or IsWhitespaceNotNull(). Use this if you * want the fastest implementation, and you don't care if a null byte * matches. */ constexpr static inline bool IsWhitespaceFast(const char ch) { return IsWhitespaceOrNull(ch); }
/** * Find the next token in a string. */ static XML::NextToken XML::GetNextToken(Parser *pXML) { XML::NextToken result; const TCHAR *lpXML; TCHAR ch; TCHAR temp_ch; size_t size; unsigned n; bool found_match; bool is_text = false; // Find next non-white space character ch = FindNonWhiteSpace(pXML); if (gcc_unlikely(ch == 0)) { // If we failed to obtain a valid character return { nullptr, 0, eTokenError }; } // Cache the current string pointer lpXML = pXML->lpXML; result.pStr = &lpXML[pXML->nIndex - 1]; switch (ch) { // Check for quotes case _T('\''): case _T('\"'): // Type of token result.type = eTokenQuotedText; temp_ch = ch; n = pXML->nIndex; // Set the size size = 1; found_match = false; // Search through the string to find a matching quote while (((ch = GetNextChar(pXML))) != 0) { size++; if (ch == temp_ch) { found_match = true; break; } if (ch == _T('<')) break; } // If we failed to find a matching quote if (!found_match) { pXML->nIndex = n; is_text = true; break; } // 4.02.2002 if (FindNonWhiteSpace(pXML)) { pXML->nIndex--; } break; // Equals (used with attribute values) case _T('='): size = 1; result.type = eTokenEquals; break; // Close tag case _T('>'): size = 1; result.type = eTokenCloseTag; break; // Check for tag start and tag end case _T('<'): // Peek at the next character to see if we have an end tag '</', // or an xml declaration '<?' temp_ch = pXML->lpXML[pXML->nIndex]; // If we have a tag end... if (temp_ch == _T('/')) { // Set the type and ensure we point at the next character GetNextChar(pXML); result.type = eTokenTagEnd; size = 2; } // If we have an XML declaration tag else if (temp_ch == _T('?')) { // Set the type and ensure we point at the next character GetNextChar(pXML); result.type = eTokenDeclaration; size = 2; } // Otherwise we must have a start tag else { result.type = eTokenTagStart; size = 1; } break; // Check to see if we have a short hand type end tag ('/>'). case _T('/'): // Peek at the next character to see if we have a short end tag '/>' temp_ch = pXML->lpXML[pXML->nIndex]; // If we have a short hand end tag... if (temp_ch == _T('>')) { // Set the type and ensure we point at the next character GetNextChar(pXML); result.type = eTokenShortHandClose; size = 2; break; } // If we haven't found a short hand closing tag then drop into the // text process #if GCC_CHECK_VERSION(7,0) [[fallthrough]]; #endif // Other characters default: is_text = true; } // If this is a TEXT node if (is_text) { // Indicate we are dealing with text result.type = eTokenText; size = 1; bool nExit = false; while (!nExit && ((ch = GetNextChar(pXML)) != 0)) { if (IsWhitespaceOrNull(ch)) // Break when we find white space break; switch (ch) { // If we find a slash then this maybe text or a short hand end tag. case _T('/'): // Peek at the next character to see it we have short hand end tag temp_ch = pXML->lpXML[pXML->nIndex]; // If we found a short hand end tag then we need to exit the loop if (temp_ch == _T('>')) { pXML->nIndex--; // 03.02.2002 nExit = true; } else { size++; } break; // Break when we find a terminator and decrement the index and // column count so that we are pointing at the right character // the next time we are called. case _T('<'): case _T('>'): case _T('='): pXML->nIndex--; nExit = true; break; case 0: nExit = true; break; default: size++; } } } result.length = size; return result; }
ButtonLabel::Expanded ButtonLabel::Expand(const TCHAR *text, TCHAR *buffer, size_t size) { Expanded expanded; const TCHAR *dollar; if ((text == NULL) || (*text == _T('\0')) || (*text == _T(' '))) { expanded.visible = false; return expanded; } else if ((dollar = _tcschr(text, '$')) == NULL) { /* no macro, we can just translate the text */ expanded.visible = true; expanded.enabled = true; const TCHAR *nl; if (((nl = _tcschr(text, '\n')) != NULL) && OnlyDigitsAndPunctuation(nl+1)) { /* Quick hack for skipping the translation for second line of a two line label with only digits and punctuation in the second line, e.g. for menu labels like "Config\n2/3" */ /* copy the text up to the '\n' to a new buffer and translate it */ TCHAR translatable[256]; std::copy(text, nl, translatable); translatable[nl - text] = _T('\0'); const TCHAR *translated = StringIsEmpty(translatable) ? _T("") : gettext(translatable); /* concatenate the translated text and the part starting with '\n' */ _tcscpy(buffer, translated); _tcscat(buffer, nl); expanded.text = buffer; } else expanded.text = gettext(text); return expanded; } else { const TCHAR *macros = dollar; /* backtrack until the first non-whitespace character, because we don't want to translate whitespace between the text and the macro */ while (macros > text && IsWhitespaceOrNull(macros[-1])) --macros; TCHAR s[100]; expanded.enabled = !ExpandMacros(text, s, ARRAY_SIZE(s)); if (s[0] == _T('\0') || s[0] == _T(' ')) { expanded.visible = false; return expanded; } /* copy the text (without trailing whitespace) to a new buffer and translate it */ TCHAR translatable[256]; std::copy(text, macros, translatable); translatable[macros - text] = _T('\0'); const TCHAR *translated = StringIsEmpty(translatable) ? _T("") : gettext(translatable); /* concatenate the translated text and the macro output */ _tcscpy(buffer, translated); _tcscat(buffer, s + (macros - text)); expanded.visible = true; expanded.text = buffer; return expanded; } }