//============================================================================= // Scanner::GetNextString // // Return the next string of characters pointed at by position, where each // character comforms to the classification determined by includeMask // and does not conform to excludeMask and is not equal to the delimiter. // Return the next character in the input stream in nextChar. The // caller can reference this to see if the delimiter was reached. // If a string is returned then position is advanced accordingly. //============================================================================= String Scanner::GetNextStringDelimited(ScannerPosition& position, CharTypeFacet::Mask includeMask, CharTypeFacet::Mask excludeMask, CharType delimiter, Character& nextChar, size_t maxSize) { String strRet; size_t count = 0; while (true) { nextChar = PeekNextCharacter(position); if ( (CharTypeFacet::IsCharType(nextChar, includeMask) || includeMask == CharTypeFacet::Any) && !CharTypeFacet::IsCharType(nextChar, excludeMask) && nextChar != delimiter && !nextChar.isEOF()) { GetNextCharacter(position).appendToString(strRet); if(maxSize && count++ > maxSize) break; } else break; } return strRet; }
///--------------------------------------------------------------------------------- /// ///--------------------------------------------------------------------------------- void GetNextString( char** out_string, char** line ) { char *pos = *line; char *out = *out_string; // nothing to do, doesn't start with a quote; if (*pos != QUOTE) { return; } ++pos; while ((*pos != NULL) && (*pos != QUOTE)) { char nextChar = GetNextCharacter( &pos ); *out = nextChar; ++out; ++pos; } if ((*pos) == QUOTE) { ++pos; } *out_string = out; *line = pos; }
static void GetNextString(char **out_stream, char **stream) { char *s = *stream; char *out = *out_stream; // nothing to do, doesn't start with a quote; if (*s != QUOTE) { return; } ++s; while ((*s != NULL) && (*s != QUOTE)) { char c = GetNextCharacter(&s); *out = c; ++out; ++s; } if ((*s) == QUOTE) { ++s; } *out_stream = out; *stream = s; }
//============================================================================== // Scanner::Skip // // Skip n UCS4 Characters //============================================================================== void Scanner::Skip(ScannerPosition& position, size_t n) { for(size_t i=0; i<n; i++) { GetNextCharacter(position); } }
void C4MenuItem::DoTextProgress(int32_t &riByVal) { // any progress to be done? if (TextDisplayProgress<0) return; // if this is an option or empty text, show it immediately if (IsSelectable || !*Caption) { TextDisplayProgress=-1; return; } // normal text: move forward in unbroken message, ignoring markup StdStrBuf sText(Caption); C4Markup MarkupChecker(false); const char *szPos = sText.getPtr(std::min<int>(TextDisplayProgress, sText.getLength())); while (riByVal && *szPos) { MarkupChecker.SkipTags(&szPos); if (!*szPos) break; --riByVal; // Advance one UTF-8 character uint32_t c = GetNextCharacter(&szPos); // Treat embedded images {{XXX}} as one entity if(c == '{' && *szPos == '{') { int32_t end = SCharPos('}', szPos); if(end > 0 && szPos[end+1] == '}') szPos += end + 2; } } if (!*szPos) TextDisplayProgress=-1; else TextDisplayProgress = szPos - Caption; }
//============================================================================== // Scanner::SkipToDelimiter // //============================================================================== CharType Scanner::SkipToDelimiter(ScannerPosition& position, CharType delim, CharTypeFacet::Mask includeMask) { Character x; while( (x = PeekNextCharacter(position)) != delim && CharTypeFacet::IsCharType(x, includeMask) && !x.isEOF()) { GetNextCharacter(position); } return x.first(); }
//============================================================================= // Scanner::skipWhiteSpace // //============================================================================= size_t Scanner::SkipWhiteSpace(ScannerPosition& position) { size_t skipCount(0); while (CharTypeFacet::IsWhiteSpace(PeekNextCharacter(position))) { GetNextCharacter(position); skipCount++; } return skipCount; }
//============================================================================= // Scanner::SkipNextCharConstant // // Called by the parser to test the input stream against a given character. // If there is a match, then the input position is incremented and we // return true, otherwise position remains where it is and we return false. //============================================================================= bool Scanner::SkipNextCharConstant(ScannerPosition& position, CharType x) { if(PeekNextCharacter(position) == x) { GetNextCharacter(position); return true; } else { return false; } }
//============================================================================= // Scanner::SkipNextStringConstant // // Called by the parser to test the input stream against a given string. // If there is a match, then the input position is incremented and we // return true, otherwise position remains where it is and we return false. // // Note: Zero length string is fine! //============================================================================= bool Scanner::SkipNextStringConstant(ScannerPosition& position, const String& str) { ScannerPosition myPosition = position; for(size_t i=0; i<str.size(); i++) { if(GetNextCharacter(myPosition) != str[i]) return false; } // all the string's characters matched the input stream, so advance position // and return true. position = myPosition; return true; }
//============================================================================= // Scanner::PeekNextStringConstant // // Simply test if the next n characters in the input stream match exactly // the characters of the string str. //============================================================================= bool Scanner::PeekNextStringConstant(ScannerPosition& position, const String& str) { QC_DBG_ASSERT(!str.empty()); ScannerPosition myPosition = position; for(size_t i=0; i<str.size(); i++) { if(GetNextCharacter(myPosition) != str[i]) return false; } // all the string's characters matched the input stream, so // return true. return true; }
//============================================================================= // Scanner::SkipNextStringConstant // // Called by the parser to test the input stream against a given string. // If there is a match, then the input position is incremented and we // return true, otherwise position remains where it is and we return false. // // Note: Overridden for const char* //============================================================================= bool Scanner::SkipNextStringConstant(ScannerPosition& position, const char* pStr) { QC_DBG_ASSERT(pStr!=0); ScannerPosition myPosition = position; size_t stringLen = strlen(pStr); for(size_t i=0; i<stringLen; i++) { if(GetNextCharacter(myPosition) != pStr[i]) return false; } // all the string's characters matched the input stream, so advance position // and return true. position = myPosition; return true; }
//============================================================================= // Scanner::SkipToDelimiters // // Skip the input position up to (but not including) any one of the passed // delimiter strings or EOF - whatever comes first. // // Return the index number of the string found, or -1 // // Note the delims parameter is an array of char *s. We do not pass CharType // for the simple reason that all delimiters will appear in the ascii range. //============================================================================= int Scanner::SkipToDelimiters(ScannerPosition& position, size_t numDelimiters, const char* delims[]) { Character nextChar; while( (nextChar=PeekNextCharacter(position)).isEOF() != true) { for(size_t i=0; i<numDelimiters; i++) { if(PeekNextStringConstant(position, delims[i])) { return (int)i; } } GetNextCharacter(position); } return -1; }
//============================================================================== // Scanner::GetNextContigHexString // //============================================================================== String Scanner::GetNextContigHexString(ScannerPosition& position, Character& nextChar) { String strRet; while (true) { nextChar = PeekNextCharacter(position); if(nextChar.isHexDigit()) { GetNextCharacter(position).appendToString(strRet); } else { break; } } return strRet; }
//============================================================================= // Scanner::SkipToDelimiter // // Skip the input position up to (but not including) delimiter or EOF, // whatever comes first. // // Return the next character in the iunput stream. //============================================================================= CharType Scanner::SkipToDelimiter(ScannerPosition& position, const String& delim, CharTypeFacet::Mask includeMask) { QC_DBG_ASSERT(!delim.empty()); const CharType firstDelimChar = delim[0]; CharType nextChar; while((nextChar = SkipToDelimiter(position, firstDelimChar, includeMask)) == firstDelimChar) { if(PeekNextStringConstant(position, delim)) { break; } else { GetNextCharacter(position); } } return nextChar; }
//============================================================================= // Scanner::GetNextStringDelimited // // Return the next string of characters pointed at by position, where each // character comforms to the classification determined by includeMask // and does not conform to excludeMask and is not equal to the delimiter. // // Return the nexr char in the input stream in nextChar. // If a string is returned then position is advanced accordingly. // // Note: For convenience this function is implemented using // the character version of GetNextStringDelimited. This could // pose a performance problem if it was used extensively. //============================================================================= String Scanner::GetNextStringDelimited(ScannerPosition& position, CharTypeFacet::Mask includeMask, CharTypeFacet::Mask excludeMask, const String& delimiter, Character& nextChar) { String strRet; QC_DBG_ASSERT(!delimiter.empty()); const CharType cDelim = delimiter.at(0); while (true) { // // obtain string up to first character of the delimiter string // strRet += GetNextStringDelimited(position, includeMask, excludeMask, cDelim, nextChar); if(nextChar == cDelim) { if(PeekNextStringConstant(position, delimiter)) { break; } else { GetNextCharacter(position).appendToString(strRet); } } else { // either at EOF or an illegal character! break; } } return strRet; }
//============================================================================== // Scanner::GetNextContigDecimalString // //============================================================================== String Scanner::GetNextContigDecimalString(ScannerPosition& position, bool /*bAllowDecimalPoint*/, Character& nextChar) { String strRet; while (true) { nextChar = PeekNextCharacter(position); if(nextChar.isDigit()) { GetNextCharacter(position).appendToString(strRet); } else { break; } } return strRet; }
///--------------------------------------------------------------------------------- /// ///--------------------------------------------------------------------------------- char* GetNextToken( char** line ) { char *pos = *line; char *token = nullptr; // get up the first real character while (IsWhitespace( *pos )) { ++pos; } if (*pos != NULL) { token = pos; char *out = pos; if (*pos == QUOTE) { GetNextString( &out, &pos ); } else { while (!IsWhitespace( *pos ) && (*pos != NULL)) { char nextChar = GetNextCharacter( &pos ); *out = nextChar; ++out; ++pos; } } // move past the possible "last position" we left on // since we're NULLing it out for our token if (*pos != NULL) { ++pos; } *out = NULL; } *line = pos; return token; }
static char* GetNextToken(char **stream) { char *s = *stream; char *token = nullptr; // get up the first real character while (IsWhitespace(*s)) { ++s; } if (*s != NULL) { token = s; char *out = s; if (*s == QUOTE) { GetNextString(&out, &s); } else { while (!IsWhitespace(*s) && (*s != NULL)) { char c = GetNextCharacter(&s); *out = c; ++out; ++s; } } // move past the possible "last position" we left on // since we're NULLing it out for our token if (*s != NULL) { ++s; } *out = NULL; } *stream = s; return token; }
BOOLEAN DoTextEntry (PTEXTENTRY_STATE pTES) { wchar_t ch; UNICODE *pStr; UNICODE *CacheInsPt; int CacheCursorPos; int len; BOOLEAN changed = FALSE; if (GLOBAL (CurrentActivity) & CHECK_ABORT) return (FALSE); if (!pTES->Initialized) { // init basic vars int lwlen; pTES->InputFunc = DoTextEntry; pTES->Success = FALSE; pTES->Initialized = TRUE; pTES->JoystickMode = FALSE; pTES->UpperRegister = TRUE; // init insertion point if ((size_t)pTES->CursorPos > utf8StringCount (pTES->BaseStr)) pTES->CursorPos = utf8StringCount (pTES->BaseStr); pTES->InsPt = skipUTF8Chars (pTES->BaseStr, pTES->CursorPos); // load joystick alphabet pTES->JoyAlphaString = CaptureStringTable ( LoadStringTable (JOYSTICK_ALPHA_STRTAB)); pTES->JoyAlpha = LoadJoystickAlpha ( SetAbsStringTableIndex (pTES->JoyAlphaString, 0), &pTES->JoyAlphaLength); pTES->JoyUpper = LoadJoystickAlpha ( SetAbsStringTableIndex (pTES->JoyAlphaString, 1), &pTES->JoyRegLength); pTES->JoyLower = LoadJoystickAlpha ( SetAbsStringTableIndex (pTES->JoyAlphaString, 2), &lwlen); if (lwlen != pTES->JoyRegLength) { if (lwlen < pTES->JoyRegLength) pTES->JoyRegLength = lwlen; log_add (log_Warning, "Warning: Joystick upper-lower registers" " size mismatch; using the smallest subset (%d)", pTES->JoyRegLength); } pTES->CacheStr = HMalloc (pTES->MaxSize * sizeof (*pTES->CacheStr)); DoInput (pTES, TRUE); if (pTES->CacheStr) HFree (pTES->CacheStr); if (pTES->JoyLower) HFree (pTES->JoyLower); if (pTES->JoyUpper) HFree (pTES->JoyUpper); if (pTES->JoyAlpha) HFree (pTES->JoyAlpha); DestroyStringTable ( ReleaseStringTable (pTES->JoyAlphaString)); return pTES->Success; } pStr = pTES->InsPt; len = strlen (pStr); // save a copy of string CacheInsPt = pTES->InsPt; CacheCursorPos = pTES->CursorPos; memcpy (pTES->CacheStr, pTES->BaseStr, pTES->MaxSize); // process the pending character buffer ch = GetNextCharacter (); if (!ch && PulsedInputState.menu[KEY_MENU_ANY]) { // keyboard repeat, but only when buffer empty ch = GetLastCharacter (); } while (ch) { UNICODE chbuf[8]; int chsize; pTES->JoystickMode = FALSE; chsize = getStringFromChar (chbuf, sizeof (chbuf), ch); if (isWidePrintChar (ch) && chsize > 0) { if (pStr + len - pTES->BaseStr + chsize < pTES->MaxSize) { // insert character, when fits memmove (pStr + chsize, pStr, len + 1); memcpy (pStr, chbuf, chsize); pStr += chsize; ++pTES->CursorPos; changed = TRUE; } else { // does not fit PlayMenuSound (MENU_SOUND_FAILURE); } } ch = GetNextCharacter (); } if (PulsedInputState.menu[KEY_MENU_DELETE]) { if (len) { joy_char_t ch; ReadOneChar (&ch, pStr); memmove (pStr, pStr + ch.len, len - ch.len + 1); len -= ch.len; changed = TRUE; } } else if (PulsedInputState.menu[KEY_MENU_BACKSPACE]) { if (pStr > pTES->BaseStr) { UNICODE *prev = skipUTF8Chars (pTES->BaseStr, pTES->CursorPos - 1); memmove (prev, pStr, len + 1); pStr = prev; --pTES->CursorPos; changed = TRUE; } } else if (PulsedInputState.menu[KEY_MENU_LEFT]) { if (pStr > pTES->BaseStr) { UNICODE *prev = skipUTF8Chars (pTES->BaseStr, pTES->CursorPos - 1); pStr = prev; len += (prev - pStr); --pTES->CursorPos; changed = TRUE; } } else if (PulsedInputState.menu[KEY_MENU_RIGHT]) { if (len > 0) { joy_char_t ch; ReadOneChar (&ch, pStr); pStr += ch.len; len -= ch.len; ++pTES->CursorPos; changed = TRUE; } } else if (PulsedInputState.menu[KEY_MENU_HOME]) { if (pStr > pTES->BaseStr) { pStr = pTES->BaseStr; len = strlen (pStr); pTES->CursorPos = 0; changed = TRUE; } } else if (PulsedInputState.menu[KEY_MENU_END]) { if (len > 0) { pTES->CursorPos += utf8StringCount (pStr); pStr += len; len = 0; changed = TRUE; } } if (pTES->JoyAlpha && ( PulsedInputState.menu[KEY_MENU_UP] || PulsedInputState.menu[KEY_MENU_DOWN] || PulsedInputState.menu[KEY_MENU_PAGE_UP] || PulsedInputState.menu[KEY_MENU_PAGE_DOWN]) ) { // do joystick text joy_char_t ch; joy_char_t newch; joy_char_t cmpch; int i; pTES->JoystickMode = TRUE; if (len) ReadOneChar (&ch, pStr); else ch = pTES->JoyAlpha[0]; newch = ch; JoyCharToUpper (&cmpch, &ch, pTES); // find current char in the alphabet i = JoyCharFindIn (&cmpch, pTES->JoyAlpha, pTES->JoyAlphaLength); if (PulsedInputState.menu[KEY_MENU_UP]) { --i; if (i < 0) i = pTES->JoyAlphaLength - 1; newch = pTES->JoyAlpha[i]; } else if (PulsedInputState.menu[KEY_MENU_DOWN]) { ++i; if (i >= pTES->JoyAlphaLength) i = 0; newch = pTES->JoyAlpha[i]; } if (PulsedInputState.menu[KEY_MENU_PAGE_UP] || PulsedInputState.menu[KEY_MENU_PAGE_DOWN]) { if (len) { // single char change if (JoyCharIsLower (&newch, pTES)) JoyCharToUpper (&newch, &newch, pTES); else JoyCharToLower (&newch, &newch, pTES); } else { // register change pTES->UpperRegister = !pTES->UpperRegister; } } else { // check register if (pTES->UpperRegister) JoyCharToUpper (&newch, &newch, pTES); else JoyCharToLower (&newch, &newch, pTES); } if (strcmp (newch.enc, ch.enc) != 0) { // new char is different, put it in if (len) { // change current -- this is messy with utf8 int l = len - ch.len; if (pStr + l - pTES->BaseStr + newch.len < pTES->MaxSize) { // adjust other chars if necessary if (newch.len != ch.len) memmove (pStr + newch.len, pStr + ch.len, l + 1); memcpy (pStr, newch.enc, newch.len); len = l + newch.len; changed = TRUE; } } else { // append if (pStr + len - pTES->BaseStr + newch.len < pTES->MaxSize) { memcpy (pStr, newch.enc, newch.len); pStr[newch.len] = '\0'; len += newch.len; changed = TRUE; } else { // does not fit PlayMenuSound (MENU_SOUND_FAILURE); } } } } if (PulsedInputState.menu[KEY_MENU_SELECT]) { // done entering pTES->Success = TRUE; return FALSE; } else if (PulsedInputState.menu[KEY_MENU_EDIT_CANCEL]) { // canceled entering pTES->Success = FALSE; return FALSE; } pTES->InsPt = pStr; if (changed && pTES->ChangeCallback) { if (!pTES->ChangeCallback (pTES)) { // changes not accepted - revert memcpy (pTES->BaseStr, pTES->CacheStr, pTES->MaxSize); pTES->InsPt = CacheInsPt; pTES->CursorPos = CacheCursorPos; PlayMenuSound (MENU_SOUND_FAILURE); } } if (pTES->FrameCallback) return pTES->FrameCallback (pTES); return TRUE; }
//============================================================================= // Scanner::GetNextCharacterImpl // // Return the next character pointed at by position and advance position. // // Note: Notice the similarity to PeekNextChar - basically doing the same // job without advancing the position. Would like to use common // routine - but without the expense of run-time performance. //============================================================================= Character Scanner::GetNextCharacterImpl(ScannerPosition& position) { Character ret = Character::EndOfFileCharacter; // // Because position.m_rpBuffer is dereferenced several times, it aids // performance to take a local copy. // Buffer* pBuffer = position.m_rpBuffer.get(); // // If we have reached the end of the available portion of the buffer // then try and read some more. // // The result of this is that there should be at least one character // more to read or the buffer is at the end of the Entity, or the // burrfer is full and we need to move to the next one. // if (position.m_bufferOffset == pBuffer->m_used) { pBuffer->read(); } // // If there is unread data in the buffer, then that's easy // take the Char sequence and increment position by its length // const size_t charAvailable = (pBuffer->m_used - position.m_bufferOffset); if(charAvailable) { ret = Character(pBuffer->m_pData+position.m_bufferOffset, charAvailable); position.m_bufferOffset+=ret.length(); } else if(!pBuffer->m_bEOF) { // Otherwise, if the current buffer is not the last in line (eof) // we want to Get the next buffer. It is likely that this involves // creating (and chaining) a new buffer - but it might not if we have // been here before courtesy of a different streamPosition object. // Whatever, the result will be that we can chain forward using the // rcpNext member. Buffer* pNextBuffer = pBuffer->m_rpNext.get(); // Advance position to the Next buffer position.m_rpBuffer = pNextBuffer; position.m_bufferOffset = 0; if(pNextBuffer->m_used) { // Take the available Char sequence and increment position by its length ret = Character(pNextBuffer->m_pData, pNextBuffer->m_used); position.m_bufferOffset+=ret.length(); } } // // If the ScannerPosition allows us to process the next position in the // chain then we will do so. // if(ret.isEOF() && position.m_pNext) { switch (position.m_eofAction) { case ScannerPosition::space: position.m_eofAction = ScannerPosition::skip; return chSpace; case ScannerPosition::skip: position = ScannerPosition(*position.m_pNext); return GetNextCharacter(position); case ScannerPosition::softEOF: break; default: QC_DBG_ASSERT(false); } } else { // // Update the StreamPosition to reflect the latest gotten Char // Note that the space generated above does not increment the StreamPosition if(!ret.isEOF()) { position.m_streamPosition.incrementByChar(ret); } } return ret; }