static void DeserializeStruct(PrefInfo *info, size_t count, void *structBase, BencDict *prefs) { char *base = (char *)structBase; BencInt *intObj; BencString *strObj; BencArray *arrObj; for (size_t i = 0; i < count; i++) { PrefInfo& meta = info[i]; switch (meta.type) { case Pref_Bool: if ((intObj = prefs->GetInt(meta.name))) *(bool *)(base + meta.offset) = intObj->Value() != 0; break; case Pref_Int: if ((intObj = prefs->GetInt(meta.name))) *(int *)(base + meta.offset) = (int)intObj->Value(); break; case Pref_Str: if ((strObj = prefs->GetString(meta.name))) str::ReplacePtr((char **)(base + meta.offset), strObj->RawValue()); break; case Pref_WStr: if ((strObj = prefs->GetString(meta.name))) { ScopedMem<WCHAR> str(strObj->Value()); if (str) str::ReplacePtr((WCHAR **)(base + meta.offset), str); } break; case Pref_DisplayMode: if ((strObj = prefs->GetString(meta.name))) { ScopedMem<WCHAR> mode(strObj->Value()); if (mode) DisplayModeConv::EnumFromName(mode, (DisplayMode *)(base + meta.offset)); } break; case Pref_Float: if ((strObj = prefs->GetString(meta.name))) { // note: this might round the value for files produced with versions // prior to 1.6 and on a system where the decimal mark isn't a '.' // (the difference should be hardly notable, though) *(float *)(base + meta.offset) = (float)atof(strObj->RawValue()); } break; case Pref_IntVec: if ((arrObj = prefs->GetArray(meta.name))) { size_t len = arrObj->Length(); Vec<int> *intVec = new Vec<int>(len); if (intVec) { for (size_t idx = 0; idx < len; idx++) { if ((intObj = arrObj->GetInt(idx))) intVec->Append((int)intObj->Value()); } delete *(Vec<int> **)(base + meta.offset); *(Vec<int> **)(base + meta.offset) = intVec; } } break; case Pref_UILang: if ((strObj = prefs->GetString(meta.name))) { // ensure language code is valid const char *langCode = trans::ValidateLangCode(strObj->RawValue()); if (langCode) *(const char **)(base + meta.offset) = langCode; } break; } } }
static void SerializeAdvancedSettings(const WCHAR *filepath, SettingInfo *info, size_t count, void *structBase) { char *base = (char *)structBase; const WCHAR *section = NULL; bool skipSection = false; ScopedMem<WCHAR> strValue; for (size_t i = 0; i < count; i++) { SettingInfo& meta = info[i]; CrashIf(meta.type != SType_Section && !section); if (meta.type != SType_Section && skipSection) continue; switch (meta.type) { case SType_Section: section = meta.name; skipSection = !meta.offset; break; case SType_Bool: strValue.Set(str::Format(L"%d", *(bool *)(base + meta.offset) ? 1 : 0)); WritePrivateProfileString(section, meta.name, strValue, filepath); break; case SType_Color: strValue.Set(str::Format(L"#%06X", (int)*(COLORREF *)(base + meta.offset))); WritePrivateProfileString(section, meta.name, strValue, filepath); break; case SType_Int: strValue.Set(str::Format(L"%d", *(int *)(base + meta.offset))); WritePrivateProfileString(section, meta.name, strValue, filepath); break; case SType_String: WritePrivateProfileString(section, meta.name, ((ScopedMem<WCHAR> *)(base + meta.offset))->Get(), filepath); break; case SType_BoolVec: { Vec<bool> *vec = (Vec<bool> *)(base + meta.offset); for (size_t n = 1; n < vec->Count(); n++) { ScopedMem<WCHAR> key(str::Format(L"%u.%s", n, meta.name)); strValue.Set(str::Format(L"%d", vec->At(n) ? 1 : 0)); WritePrivateProfileString(section, key, strValue, filepath); } } break; case SType_ColorVec: { Vec<COLORREF> *vec = (Vec<COLORREF> *)(base + meta.offset); for (size_t n = 1; n < vec->Count(); n++) { ScopedMem<WCHAR> key(str::Format(L"%u.%s", n, meta.name)); strValue.Set(str::Format(L"#%06X", (int)vec->At(n))); WritePrivateProfileString(section, key, strValue, filepath); } } break; case SType_IntVec: { Vec<int> *vec = (Vec<int> *)(base + meta.offset); for (size_t n = 1; n < vec->Count(); n++) { ScopedMem<WCHAR> key(str::Format(L"%u.%s", n, meta.name)); strValue.Set(str::Format(L"%d", vec->At(n))); WritePrivateProfileString(section, key, strValue, filepath); } } break; case SType_StringVec: { WStrVec *vec = (WStrVec *)(base + meta.offset); for (size_t n = 1; n < vec->Count(); n++) { ScopedMem<WCHAR> key(str::Format(L"%u.%s", n, meta.name)); WritePrivateProfileString(section, key, vec->At(n), filepath); } } break; } } }
static void VecTest() { Vec<int> ints; assert(ints.Count() == 0); ints.Append(1); ints.Push(2); ints.InsertAt(0, -1); assert(ints.Count() == 3); assert(ints.At(0) == -1 && ints.At(1) == 1 && ints.At(2) == 2); assert(ints.At(0) == -1 && ints.Last() == 2); int last = ints.Pop(); assert(last == 2); assert(ints.Count() == 2); ints.Push(3); ints.RemoveAt(0); assert(ints.Count() == 2); assert(ints.At(0) == 1 && ints.At(1) == 3); ints.Reset(); assert(ints.Count() == 0); for (int i = 0; i < 1000; i++) { ints.Push(i); } assert(ints.Count() == 1000 && ints.At(500) == 500); ints.Remove(500); assert(ints.Count() == 999 && ints.At(500) == 501); last = ints.Pop(); assert(last == 999); ints.Append(last); assert(ints.AtPtr(501) == &ints.At(501)); { Vec<int> ints2(ints); assert(ints2.Count() == 999); assert(ints.LendData() != ints2.LendData()); ints.Remove(600); assert(ints.Count() < ints2.Count()); ints2 = ints; assert(ints2.Count() == 998); } { char buf[2] = {'a', '\0'}; str::Str<char> v(0); for (int i = 0; i < 7; i++) { v.Append(buf, 1); buf[0] = buf[0] + 1; } char *s = v.LendData(); assert(str::Eq("abcdefg", s)); assert(7 == v.Count()); v.Set("helo"); assert(4 == v.Count()); assert(str::Eq("helo", v.LendData())); } { str::Str<char> v(128); v.Append("boo", 3); assert(str::Eq("boo", v.LendData())); assert(v.Count() == 3); v.Append("fop"); assert(str::Eq("boofop", v.LendData())); assert(v.Count() == 6); v.RemoveAt(2, 3); assert(v.Count() == 3); assert(str::Eq("bop", v.LendData())); v.Append('a'); assert(v.Count() == 4); assert(str::Eq("bopa", v.LendData())); char *s = v.StealData(); assert(str::Eq("bopa", s)); free(s); assert(v.Count() == 0); } { str::Str<char> v(0); for (int i = 0; i < 32; i++) { assert(v.Count() == i * 6); v.Append("lambd", 5); if (i % 2 == 0) v.Append('a'); else v.Push('a'); } for (int i=1; i<=16; i++) { v.RemoveAt((16 - i) * 6, 6); assert(v.Count() == (32 - i) * 6); } v.RemoveAt(0, 6 * 15); assert(v.Count() == 6); char *s = v.LendData(); assert(str::Eq(s, "lambda")); s = v.StealData(); assert(str::Eq(s, "lambda")); free(s); assert(v.Count() == 0); v.Append("lambda"); assert(str::Eq(v.LendData(), "lambda")); char c = v.Pop(); assert(c == 'a'); assert(str::Eq(v.LendData(), "lambd")); } VecTestAppendFmt(); { Vec<PointI *> v; srand((unsigned int)time(NULL)); for (int i = 0; i < 128; i++) { v.Append(new PointI(i, i)); size_t pos = rand() % v.Count(); v.InsertAt(pos, new PointI(i, i)); } assert(v.Count() == 128 * 2); size_t idx = 0; for (PointI **p = v.IterStart(); p; p = v.IterNext()) { assert(idx == v.IterIdx()); ++idx; } while (v.Count() > 64) { size_t pos = rand() % v.Count(); PointI *f = v.At(pos); v.Remove(f); delete f; } DeleteVecMembers(v); } { Vec<int> v; v.Append(2); for (int i = 0; i < 500; i++) v.Append(4); v.At(250) = 5; v.Reverse(); assert(v.Count() == 501 && v.At(0) == 4 && v.At(249) == v.At(251) && v.At(250) == 5 && v.At(500) == 2); v.Remove(4); v.Reverse(); assert(v.Count() == 500 && v.At(0) == 2 && v.At(249) == v.At(251) && v.At(250) == 5 && v.At(499) == 4); } }
int SyncTex::SourceToDoc(const WCHAR* srcfilename, UINT line, UINT col, UINT* page, Vec<RectI>& rects) { if (IsIndexDiscarded()) { if (RebuildIndex() != PDFSYNCERR_SUCCESS) return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; } AssertCrash(this->scanner); AutoFreeW srcfilepath; // convert the source file to an absolute path if (PathIsRelative(srcfilename)) srcfilepath.Set(PrependDir(srcfilename)); else srcfilepath.SetCopy(srcfilename); if (!srcfilepath) return PDFSYNCERR_OUTOFMEMORY; bool isUtf8 = true; char* mb_srcfilepath = str::conv::ToUtf8(srcfilepath).StealData(); TryAgainAnsi: if (!mb_srcfilepath) return PDFSYNCERR_OUTOFMEMORY; int ret = synctex_display_query(this->scanner, mb_srcfilepath, line, col); free(mb_srcfilepath); // recent SyncTeX versions encode in UTF-8 instead of ANSI if (isUtf8 && -1 == ret) { isUtf8 = false; mb_srcfilepath = str::conv::ToAnsi(srcfilepath).StealData(); goto TryAgainAnsi; } if (-1 == ret) return PDFSYNCERR_UNKNOWN_SOURCEFILE; if (0 == ret) return PDFSYNCERR_NOSYNCPOINT_FOR_LINERECORD; synctex_node_t node; int firstpage = -1; rects.Reset(); while ((node = synctex_next_result(this->scanner)) != nullptr) { if (firstpage == -1) { firstpage = synctex_node_page(node); if (firstpage <= 0 || firstpage > engine->PageCount()) continue; *page = (UINT)firstpage; } if (synctex_node_page(node) != firstpage) continue; RectD rc; rc.x = synctex_node_box_visible_h(node); rc.y = synctex_node_box_visible_v(node) - synctex_node_box_visible_height(node); rc.dx = synctex_node_box_visible_width(node), rc.dy = synctex_node_box_visible_height(node) + synctex_node_box_visible_depth(node); rects.Push(rc.Round()); } if (firstpage <= 0) return PDFSYNCERR_NOSYNCPOINT_FOR_LINERECORD; return PDFSYNCERR_SUCCESS; }
// Find a record corresponding to the given source file, line number and optionally column number. // (at the moment the column parameter is ignored) // // If there are several *consecutively declared* records for the same line then they are all returned. // The list of records is added to the vector 'records' // // If there is no record for that line, the record corresponding to the nearest line is selected // (within a range of EPSILON_LINE) // // The function returns PDFSYNCERR_SUCCESS if a matching record was found. UINT Pdfsync::SourceToRecord(const WCHAR* srcfilename, UINT line, UINT col, Vec<size_t>& records) { UNUSED(col); if (!srcfilename) return PDFSYNCERR_INVALID_ARGUMENT; AutoFreeW srcfilepath; // convert the source file to an absolute path if (PathIsRelative(srcfilename)) srcfilepath.Set(PrependDir(srcfilename)); else srcfilepath.SetCopy(srcfilename); if (!srcfilepath) return PDFSYNCERR_OUTOFMEMORY; // find the source file entry size_t isrc; for (isrc = 0; isrc < srcfiles.size(); isrc++) if (path::IsSame(srcfilepath, srcfiles.at(isrc))) break; if (isrc == srcfiles.size()) return PDFSYNCERR_UNKNOWN_SOURCEFILE; if (fileIndex.at(isrc).start == fileIndex.at(isrc).end) return PDFSYNCERR_NORECORD_IN_SOURCEFILE; // there is not any record declaration for that particular source file // look for sections belonging to the specified file // starting with the first section that is declared within the scope of the file. UINT min_distance = EPSILON_LINE; // distance to the closest record size_t lineIx = (size_t)-1; // closest record-line index for (size_t isec = fileIndex.at(isrc).start; isec < fileIndex.at(isrc).end; isec++) { // does this section belong to the desired file? if (lines.at(isec).file != isrc) continue; UINT d = abs((int)lines.at(isec).line - (int)line); if (d < min_distance) { min_distance = d; lineIx = isec; if (0 == d) break; // We have found a record for the requested line! } } if (lineIx == (size_t)-1) return PDFSYNCERR_NORECORD_FOR_THATLINE; // we read all the consecutive records until we reach a record belonging to another line for (size_t i = lineIx; i < lines.size() && lines.at(i).line == lines.at(lineIx).line; i++) records.Push(lines.at(i).record); return PDFSYNCERR_SUCCESS; }
// see http://itexmac.sourceforge.net/pdfsync.html for the specification int Pdfsync::RebuildIndex() { OwnedData data(file::ReadFile(syncfilepath)); if (!data.data) { return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; } // convert the file data into a list of zero-terminated strings str::TransChars(data.data, "\r\n", "\0\0"); // parse preamble (jobname and version marker) char* line = data.data; char* dataEnd = data.data + data.size; // replace star by spaces (TeX uses stars instead of spaces in filenames) str::TransChars(line, "*/", " \\"); AutoFreeW jobName(str::conv::FromAnsi(line)); jobName.Set(str::Join(jobName, L".tex")); jobName.Set(PrependDir(jobName)); line = Advance0Line(line, dataEnd); UINT versionNumber = 0; if (!line || !str::Parse(line, "version %u", &versionNumber) || versionNumber != 1) { return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; } // reset synchronizer database srcfiles.Reset(); lines.Reset(); points.Reset(); fileIndex.Reset(); sheetIndex.Reset(); Vec<size_t> filestack; UINT page = 1; sheetIndex.Append(0); // add the initial tex file to the source file stack filestack.Push(srcfiles.size()); srcfiles.Append(jobName.StealData()); PdfsyncFileIndex findex = {0}; fileIndex.Append(findex); PdfsyncLine psline; PdfsyncPoint pspoint; // parse data UINT maxPageNo = engine->PageCount(); while ((line = Advance0Line(line, dataEnd)) != nullptr) { if (!line) break; switch (*line) { case 'l': psline.file = filestack.Last(); if (str::Parse(line, "l %u %u %u", &psline.record, &psline.line, &psline.column)) lines.Append(psline); else if (str::Parse(line, "l %u %u", &psline.record, &psline.line)) { psline.column = 0; lines.Append(psline); } // else dbg("Bad 'l' line in the pdfsync file"); break; case 's': if (str::Parse(line, "s %u", &page)) sheetIndex.Append(points.size()); // else dbg("Bad 's' line in the pdfsync file"); // if (0 == page || page > maxPageNo) // dbg("'s' line with invalid page number in the pdfsync file"); break; case 'p': pspoint.page = page; if (0 == page || page > maxPageNo) /* ignore point for invalid page number */; else if (str::Parse(line, "p %u %u %u", &pspoint.record, &pspoint.x, &pspoint.y)) points.Append(pspoint); else if (str::Parse(line, "p* %u %u %u", &pspoint.record, &pspoint.x, &pspoint.y)) points.Append(pspoint); // else dbg("Bad 'p' line in the pdfsync file"); break; case '(': { AutoFreeW filename(str::conv::FromAnsi(line + 1)); // if the filename contains quotes then remove them // TODO: this should never happen!? if (filename[0] == '"' && filename[str::Len(filename) - 1] == '"') filename.Set(str::DupN(filename + 1, str::Len(filename) - 2)); // undecorate the filepath: replace * by space and / by \ (backslash) str::TransChars(filename, L"*/", L" \\"); // if the file name extension is not specified then add the suffix '.tex' if (str::IsEmpty(path::GetExt(filename))) filename.Set(str::Join(filename, L".tex")); // ensure that the path is absolute if (PathIsRelative(filename)) filename.Set(PrependDir(filename)); filestack.Push(srcfiles.size()); srcfiles.Append(filename.StealData()); findex.start = findex.end = lines.size(); fileIndex.Append(findex); } break; case ')': if (filestack.size() > 1) fileIndex.at(filestack.Pop()).end = lines.size(); // else dbg("Unbalanced ')' line in the pdfsync file"); break; default: // dbg("Ignoring invalid pdfsync line starting with '%c'", *line); break; } } fileIndex.at(0).end = lines.size(); AssertCrash(filestack.size() == 1); return Synchronizer::RebuildIndex(); }