OSEntry(const TempStringA &Line) : m_Valid(false) { SplitStrByFirstOfA keyAndValue(Line, ConstStringA("= \t")); if (!keyAndValue) return; m_Path = keyAndValue.left; for (size_t context = 0;;) { TempStringA token = FastStringRoutines::GetNextToken<false>(keyAndValue.right, &context); if (token.empty()) break; if (m_Description.empty()) m_Description = token; else { if (token[0] != '/') return; SplitStrByFirstOfA keyAndVal(token.substr(1), ConstStringA("=")); if (keyAndVal) m_Properties[keyAndVal.left] = keyAndVal.right; else m_Properties[token.substr(1)] = DynamicStringA(); } } if (m_Description.empty()) return; m_OriginalString = Line; m_Valid = true; }
bool LocalizableStringManager::ParseSourceFile( const BazisLib::String &fp ) { ManagedPointer<TextANSIFileReader> pRdr = new TextANSIFileReader(new ACFile(fp, FileModes::OpenReadOnly)); if (!pRdr->Valid()) { _tprintf(_T("ERROR: cannot open %s\n"), fp.c_str()); return false; } _tprintf(_T(" %s\n"), Path::GetFileName(fp).c_str()); bool insideCCommentBlock = false; bool insideStringConstant = false; unsigned lineNum = 0; DynamicString currentDialogID; while (!pRdr->IsEOF()) { size_t LocalizationTokenStart = -1; enum { ltUnknown = 0, ltTR, ltDIALOG, ltDLGITEM, } localizationTokenType; lineNum++; DynamicStringA line = pRdr->ReadLine(); if (line[0] == '#') break; unsigned backslashCount = 0; char prevChar = 0, ch = 0; size_t lastStringStart = -1, lastStringLen = 0; for (size_t i = 0; i < line.length(); i++, prevChar = ch) { //Even amount of backslashes followed by quotation mark toggles string constant flag (outside comment block) //The "/*" and "*/" sequences outside string block change the comment flag. //The "//" sequence skips everything till the end of the string //At the end of each line inside the string block there should be a backslash ch = line[i]; bool isLastChar = (i == (line.length() - 1)); if (insideCCommentBlock) { if ((ch == '/') && (prevChar == '*')) insideCCommentBlock = false; } else //Outside comment block - detect string literals { if (ch == '\\') backslashCount++; else { if (((ch == '\"') || (ch == '\'')) && !(backslashCount % 2)) { if (!insideStringConstant) lastStringStart = i + 1, lastStringLen = 0; else lastStringLen = i - lastStringStart; insideStringConstant = !insideStringConstant; } backslashCount = 0; } if (!insideStringConstant) //Outside comment and string blocks - check for comment start { if ((ch == '*') && (prevChar == '/')) insideCCommentBlock = true; if ((ch == '/') && (prevChar == '/')) break; } else //Inside string block { if (isLastChar && (ch != '\\')) { _tprintf(_T("%s(%d) : error: Unterminated string"), GetFullPath(fp).c_str(), lineNum); return false; } } } //Here, both insideCCommentBlock and insideStringConstant reflect the real file context. So, let's find "_TR()" and LOCALIZE_DIALOG()/LOCALIZE_DLGITEM() if (!insideCCommentBlock && !insideStringConstant) { if (!IsValidCTokenChar(prevChar)) { size_t newTokenStart = -1; size_t remaining = line.length() - i; if ((remaining > 4) && (line.substr(i, 3) == "_TR") && !IsValidCTokenChar(line[i + 3])) newTokenStart = i + 3, localizationTokenType = ltTR; else if ((remaining > 16) && (line.substr(i, 15) == "LOCALIZE_DIALOG") && !IsValidCTokenChar(line[i + 15])) newTokenStart = i + 15, localizationTokenType = ltDIALOG; else if ((remaining > 17) && (line.substr(i, 16) == "LOCALIZE_DLGITEM") && !IsValidCTokenChar(line[i + 16])) newTokenStart = i + 16, localizationTokenType = ltDLGITEM; if (newTokenStart != -1) { if (LocalizationTokenStart != -1) { _tprintf(_T("%s(%d) : error: Localization token used recursively"), GetFullPath(fp).c_str(), lineNum); return false; } LocalizationTokenStart = newTokenStart; lastStringStart = -1; lastStringLen = 0; } } if (ch == '}') currentDialogID.clear(); if ((ch == ')') && (LocalizationTokenStart != -1)) { size_t tokenParamsStart = line.find_first_not_of(" \t()", LocalizationTokenStart); TempStringA tokenParams = line.substr(tokenParamsStart, i - LocalizationTokenStart); if (tokenParams.empty()) { _tprintf(_T("%s(%d) : error: Empty localization macro"), GetFullPath(fp).c_str(), lineNum); return false; } if (localizationTokenType == ltTR) { if ((lastStringStart == -1) || !lastStringLen) { _tprintf(_T("%s(%d) : error: Invalid _TR() statement - default string not found"), GetFullPath(fp).c_str(), lineNum); return false; } DynamicString lastString = ANSIStringToString(line.substr(lastStringStart, lastStringLen)); size_t idEnd = tokenParams.find_first_of(" ,"); DynamicString id = ANSIStringToString(tokenParams.substr(0, (idEnd == -1) ? 0 : idEnd)); if (id.empty()) { _tprintf(_T("%s(%d) : error: Invalid _TR() statement - cannot determine string ID"), GetFullPath(fp).c_str(), lineNum); return false; } const DynamicString &existingValue = m_Strings[id].Value; if (!existingValue.empty() && (existingValue != lastString)) _tprintf(_T("%s(%d) : warning: %s redefined with different value\n"), GetFullPath(fp).c_str(), lineNum, id.c_str()); m_Strings[id].Value = lastString; } else { _FixedSetOfCharsSplitString<2, TempStringA> params(tokenParams, ", \t"); if (params.count() < 2) { _tprintf(_T("%s(%d) : error: Invalid LOCALIZE_xxx() statement - less than 2 arguments\n"), GetFullPath(fp).c_str(), lineNum); return false; } DynamicString stringId = ANSIStringToString(params[0]); if (stringId.empty()) { _tprintf(_T("%s(%d) : error: Invalid LOCALIZE_xxx() statement - empty string ID\n"), GetFullPath(fp).c_str(), lineNum); return false; } if (localizationTokenType == ltDIALOG) { DynamicString dlgID = ANSIStringToString(params[1]); if (m_Dialogs.find(dlgID) == m_Dialogs.end()) { _tprintf(_T("%s(%d) : error: Cannot find dialog %s\n"), GetFullPath(fp).c_str(), lineNum, dlgID.c_str()); return false; } m_Dialogs[dlgID].Localized = true; m_Dialogs[dlgID].LocalizationFileAndLine.Format(_T("%s(%d)"), GetFullPath(fp).c_str(), lineNum); m_Strings[stringId].Value = m_Dialogs[dlgID].Caption; currentDialogID = dlgID; } else if (localizationTokenType == ltDLGITEM) { if (currentDialogID.empty()) { _tprintf(_T("%s(%d) : error: LOCALIZE_DLGITEM() used outside LOCALIZE_DIALOG() block\n"), GetFullPath(fp).c_str(), lineNum); return false; } DynamicString itemID = ANSIStringToString(params[1]); std::map<BazisLib::String, DialogMember>::iterator it = m_Dialogs[currentDialogID].DialogMembers.find(itemID); if ((stringId == _T("0")) || !stringId.icompare(_T("NULL"))) { it->second.Skipped = true; continue; } if (it == m_Dialogs[currentDialogID].DialogMembers.end()) { _tprintf(_T("%s(%d) : error: %s is not defined in %s\n"), GetFullPath(fp).c_str(), lineNum, itemID.c_str(), currentDialogID.c_str()); return false; } m_Strings[stringId].Value = it->second.Name; it->second.Localized = true; } } LocalizationTokenStart = -1; } } } } return true; }