Merge pull request #2436 from m32/master
[far2l.git] / multiarc / src / arcread.cpp
blobd7cb2a69d469afcd5a08d422c483270feecaa901
1 #include <fcntl.h>
2 #include "MultiArc.hpp"
3 #include "marclng.hpp"
5 PluginClass::PluginClass(int ArcPluginNumber)
7 *CurDir = 0;
8 PluginClass::ArcPluginNumber = ArcPluginNumber;
9 DizPresent = FALSE;
10 bGOPIFirstCall = true;
11 ZeroFill(CurArcInfo);
14 PluginClass::~PluginClass()
16 FreeArcData();
19 void PluginClass::FreeArcData()
21 ArcData.Clear();
22 ArcDataCount = 0;
25 int PluginClass::PreReadArchive(const char *Name)
27 if (sdc_stat(Name, &ArcStat) == -1) {
28 return FALSE;
31 ArcName = Name;
33 if (FindExt(ArcName) == std::string::npos) {
34 ArcName+= '.';
37 return TRUE;
40 static void SanitizeString(std::string &s)
42 while (!s.empty() && !s.back()) {
43 s.pop_back();
47 int PluginClass::ReadArchive(const char *Name, int OpMode)
49 bGOPIFirstCall = true;
50 FreeArcData();
51 DizPresent = FALSE;
53 if (sdc_stat(Name, &ArcStat) == -1)
54 return FALSE;
56 if (!ArcPlugin->OpenArchive(ArcPluginNumber, Name, &ArcPluginType, (OpMode & OPM_SILENT) != 0))
57 return FALSE;
59 ItemsInfo = ArcItemInfo{};
60 ZeroFill(CurArcInfo);
61 TotalSize = PackedSize = 0;
62 ArcDataCount = 0;
64 HANDLE hScreen = Info.SaveScreen(0, 0, -1, -1);
66 DWORD UpdateTime = GetProcessUptimeMSec() + 1000;
67 bool MessageShown = false;
68 int GetItemCode;
70 ArcItemInfo CurItemInfo;
71 PathParts CurPP;
72 while (1) {
73 CurItemInfo = ArcItemInfo();
74 GetItemCode = ArcPlugin->GetArcItem(ArcPluginNumber, &CurItemInfo);
75 if (GetItemCode != GETARC_SUCCESS)
76 break;
78 SanitizeString(CurItemInfo.PathName);
79 if (CurItemInfo.Description)
80 SanitizeString(*CurItemInfo.Description);
81 if (CurItemInfo.LinkName)
82 SanitizeString(*CurItemInfo.LinkName);
84 if ((ArcDataCount & 0x1f) == 0) {
85 if (CheckForEsc()) {
86 FreeArcData();
87 ArcPlugin->CloseArchive(ArcPluginNumber, &CurArcInfo);
88 Info.RestoreScreen(NULL);
89 Info.RestoreScreen(hScreen);
90 return FALSE;
93 const DWORD Now = GetProcessUptimeMSec();
94 if (Now >= UpdateTime) {
95 UpdateTime = Now + 100;
96 const auto &NameMsg = FormatMessagePath(Name, false);
97 const auto &FilesMsg = StrPrintf(GetMsg(MArcReadFiles), (unsigned int)ArcDataCount);
98 const char *MsgItems[] = {GetMsg(MArcReadTitle), GetMsg(MArcReading), NameMsg.c_str(), FilesMsg.c_str()};
99 Info.Message(Info.ModuleNumber, MessageShown ? FMSG_KEEPBACKGROUND : 0, NULL, MsgItems,
100 ARRAYSIZE(MsgItems), 0);
101 MessageShown = true;
105 if (CurItemInfo.Description)
106 DizPresent = TRUE;
108 if (CurItemInfo.HostOS && (!ItemsInfo.HostOS || strcmp(ItemsInfo.HostOS, CurItemInfo.HostOS) != 0))
109 ItemsInfo.HostOS = (ItemsInfo.HostOS ? CurItemInfo.HostOS : GetMsg(MSeveralOS));
111 if (ItemsInfo.Codepage <= 0)
112 ItemsInfo.Codepage = CurItemInfo.Codepage;
114 ItemsInfo.Solid|= CurItemInfo.Solid;
115 ItemsInfo.Comment|= CurItemInfo.Comment;
116 ItemsInfo.Encrypted|= CurItemInfo.Encrypted;
118 if (CurItemInfo.Encrypted)
119 CurItemInfo.Flags|= F_ENCRYPTED;
121 if (CurItemInfo.DictSize > ItemsInfo.DictSize)
122 ItemsInfo.DictSize = CurItemInfo.DictSize;
124 if (CurItemInfo.UnpVer > ItemsInfo.UnpVer)
125 ItemsInfo.UnpVer = CurItemInfo.UnpVer;
127 CurItemInfo.NumberOfLinks = 1;
129 size_t PrefixSize = 0;
130 if (StrStartsFrom(CurItemInfo.PathName, "./"))
131 PrefixSize = 2;
132 else if (StrStartsFrom(CurItemInfo.PathName, "../"))
133 PrefixSize = 3;
134 while (PrefixSize < CurItemInfo.PathName.size() && CurItemInfo.PathName[PrefixSize] == '/')
135 PrefixSize++;
137 if (PrefixSize) {
138 CurItemInfo.Prefix.reset(new std::string(CurItemInfo.PathName.substr(0, PrefixSize)));
139 CurItemInfo.PathName.erase(0, PrefixSize);
142 if (!CurItemInfo.PathName.empty() && CurItemInfo.PathName.back() == '/')
143 CurItemInfo.dwFileAttributes|= FILE_ATTRIBUTE_DIRECTORY;
145 TotalSize+= CurItemInfo.nFileSize;
146 PackedSize+= CurItemInfo.nPhysicalSize;
148 CurPP.clear();
149 CurPP.Traverse(CurItemInfo.PathName);
150 ArcItemAttributes *CurAttrs = ArcData.Ensure(CurPP.begin(), CurPP.end());
151 *CurAttrs = std::move(CurItemInfo);
152 ++ArcDataCount;
155 Info.RestoreScreen(NULL);
156 Info.RestoreScreen(hScreen);
158 ArcPlugin->CloseArchive(ArcPluginNumber, &CurArcInfo);
160 if (GetItemCode != GETARC_EOF && GetItemCode != GETARC_SUCCESS) {
161 switch (GetItemCode) {
162 case GETARC_BROKEN:
163 GetItemCode = MBadArchive;
164 break;
166 case GETARC_UNEXPEOF:
167 GetItemCode = MUnexpEOF;
168 break;
170 case GETARC_READERROR:
171 GetItemCode = MReadError;
172 break;
175 const auto &NameMsg = FormatMessagePath(Name, true);
176 const char *MsgItems[] = {GetMsg(MError), NameMsg.c_str(), GetMsg(GetItemCode), GetMsg(MOk)};
177 Info.Message(Info.ModuleNumber, FMSG_WARNING, NULL, MsgItems, ARRAYSIZE(MsgItems), 1);
178 return FALSE; // Mantis#0001241
181 // Info.RestoreScreen(NULL);
182 // Info.RestoreScreen(hScreen);
183 return TRUE;
186 bool PluginClass::EnsureFindDataUpToDate(int OpMode)
188 if (!ArcData.empty()) {
189 struct stat NewArcStat{};
190 if (sdc_stat(ArcName.c_str(), &NewArcStat) == -1)
191 return false;
193 if (ArcStat.st_mtime == NewArcStat.st_mtime && ArcStat.st_size == NewArcStat.st_size)
194 return true;
197 DWORD size = (DWORD)Info.AdvControl(Info.ModuleNumber, ACTL_GETPLUGINMAXREADDATA, (void *)0);
198 int fd = sdc_open(ArcName.c_str(), O_RDONLY);
199 if (fd == -1)
200 return false;
202 unsigned char *Data = (unsigned char *)malloc(size);
203 ssize_t read_size = Data ? sdc_read(fd, Data, size) : -1;
204 sdc_close(fd);
206 bool ReadArcOK = false;
207 if (read_size > 0) {
208 DWORD SFXSize = 0;
210 ReadArcOK = (ArcPlugin->IsArchive(ArcPluginNumber, ArcName.c_str(), Data, read_size, &SFXSize)
211 && ReadArchive(ArcName.c_str(), OpMode));
213 free(Data);
215 return ReadArcOK;
218 int PluginClass::GetFindData(PluginPanelItem **pPanelItem, int *pItemsNumber, int OpMode)
220 *pPanelItem = NULL;
221 *pItemsNumber = 0;
223 PathParts CurDirPP;
224 CurDirPP.Traverse(CurDir);
226 if (!EnsureFindDataUpToDate(OpMode)) {
227 fprintf(stderr, "MA::GetFindData: can't update at '%s'\n", CurDirPP.Join().c_str());
228 return FALSE;
231 const auto *DirNode = ArcData.Find(CurDirPP.begin(), CurDirPP.end());
232 if (!DirNode) {
233 fprintf(stderr, "MA::GetFindData: no node at '%s'\n", CurDirPP.Join().c_str());
234 return FALSE;
237 if (DirNode->empty())
238 return TRUE;
240 PluginPanelItem *CurrentItem = *pPanelItem =
241 (PluginPanelItem *)calloc(DirNode->size(), sizeof(PluginPanelItem));
242 if (!CurrentItem) {
243 fprintf(stderr, "MA::GetFindData: can't alloc %lu items at '%s'\n", (unsigned long)DirNode->size(),
244 CurDirPP.Join().c_str());
245 return FALSE;
247 *pItemsNumber = (int)DirNode->size();
249 for (const auto &it : *DirNode) {
250 CurrentItem->FindData.ftCreationTime = it.second.ftCreationTime;
251 CurrentItem->FindData.ftLastAccessTime = it.second.ftLastAccessTime;
252 CurrentItem->FindData.ftLastWriteTime = it.second.ftLastWriteTime;
253 CurrentItem->FindData.nPhysicalSize = it.second.nPhysicalSize;
254 CurrentItem->FindData.nFileSize = it.second.nFileSize;
255 CurrentItem->FindData.dwFileAttributes = it.second.dwFileAttributes;
256 CurrentItem->FindData.dwUnixMode = it.second.dwUnixMode;
257 strncpy(CurrentItem->FindData.cFileName, it.first.c_str(),
258 ARRAYSIZE(CurrentItem->FindData.cFileName));
259 CurrentItem->Flags = it.second.Flags;
260 CurrentItem->NumberOfLinks = it.second.NumberOfLinks;
261 CurrentItem->CRC32 = it.second.CRC32;
262 CurrentItem->Description = it.second.Description ? (char *)it.second.Description->c_str() : nullptr;
263 CurrentItem->UserData = (DWORD_PTR)&it.second;
265 if (!it.second.empty())
266 CurrentItem->FindData.dwFileAttributes|= FILE_ATTRIBUTE_DIRECTORY;
267 if (CurrentItem->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
268 CurrentItem->FindData.nPhysicalSize = CurrentItem->FindData.nFileSize = 0;
270 ++CurrentItem;
273 return TRUE;
276 void PluginClass::FreeFindData(PluginPanelItem *PanelItem, int ItemsNumber)
278 if (PanelItem)
279 free(PanelItem);
282 int PluginClass::SetDirectory(const char *Dir, int OpMode)
284 if (*Dir == '/' && *(++Dir) == 0) {
285 *CurDir = 0;
286 return TRUE;
289 PathParts NewDirPP;
290 NewDirPP.Traverse(CurDir);
291 NewDirPP.Traverse(Dir);
292 const auto &NewDir = NewDirPP.Join();
294 auto *DirNode = ArcData.Find(NewDirPP.begin(), NewDirPP.end());
295 if (!DirNode) {
296 fprintf(stderr, "MA::SetDirectory('%s', %d): no node for '%s'\n", Dir, OpMode, NewDir.c_str());
297 return FALSE;
300 if (NewDir.size() >= ARRAYSIZE(CurDir)) {
301 fprintf(stderr, "MA::SetDirectory('%s', %d): too long path '%s'\n", Dir, OpMode, NewDir.c_str());
302 return FALSE;
305 CharArrayCpyZ(CurDir, NewDir.c_str());
306 return TRUE;
309 bool PluginClass::FarLangChanged()
311 const char *tmplang = getenv("FARLANG");
313 if (!tmplang)
314 tmplang = "English";
316 if (farlang == tmplang)
317 return false;
319 farlang = tmplang;
320 return true;
323 static void AppendInfoData(std::string &Str, const char *Data)
325 if (!Str.empty())
326 Str+= ' ';
327 Str+= Data;
330 void PluginClass::SetInfoLineSZ(size_t Index, int TextID, const char *Data)
332 CharArrayCpyZ(InfoLines[Index].Text, GetMsg(TextID));
333 CharArrayCpyZ(InfoLines[Index].Data, Data);
336 void PluginClass::SetInfoLine(size_t Index, int TextID, const std::string &Data)
338 SetInfoLineSZ(Index, TextID, Data.c_str());
341 void PluginClass::SetInfoLine(size_t Index, int TextID, int DataID)
343 SetInfoLineSZ(Index, TextID, GetMsg(DataID));
346 void PluginClass::GetOpenPluginInfo(struct OpenPluginInfo *Info)
348 Info->StructSize = sizeof(*Info);
349 Info->Flags = OPIF_USEFILTER | OPIF_USESORTGROUPS | OPIF_USEHIGHLIGHTING | OPIF_ADDDOTS | OPIF_COMPAREFATTIME;
350 Info->HostFile = ArcName.c_str();
351 Info->CurDir = CurDir;
353 if (bGOPIFirstCall)
354 ArcPlugin->GetFormatName(ArcPluginNumber, ArcPluginType, FormatName, DefExt);
356 std::string NameTitle;
357 struct PanelInfo PInfo;
358 if (::Info.Control((HANDLE)this, FCTL_GETPANELSHORTINFO, &PInfo)) { // TruncStr
359 NameTitle = FormatMessagePath(ArcName.c_str(), true,
360 (PInfo.PanelRect.right - PInfo.PanelRect.left + 1 - (FormatName.size() + 3 + 4)));
361 } else {
362 NameTitle = FormatMessagePath(ArcName.c_str(), true, -1);
365 PanelTitle = StrPrintf(" %s:%s%s%s ", FormatName.c_str(), NameTitle.c_str(), *CurDir ? "/" : "", *CurDir ? CurDir : "");
367 Info->PanelTitle = PanelTitle.c_str();
369 if (bGOPIFirstCall || FarLangChanged()) {
370 Format = StrPrintf(GetMsg(MArcFormat), FormatName.c_str());
372 ZeroFill(InfoLines);
373 FSF.snprintf(InfoLines[0].Text, ARRAYSIZE(InfoLines[0].Text), GetMsg(MInfoTitle), FSF.PointToName((char *)ArcName.c_str()));
374 InfoLines[0].Separator = TRUE;
376 std::string TmpInfoData = FormatName;
377 if (ItemsInfo.UnpVer != 0)
378 TmpInfoData+= StrPrintf(" %d.%d", ItemsInfo.UnpVer / 256, ItemsInfo.UnpVer % 256);
379 if (ItemsInfo.HostOS)
380 TmpInfoData+= StrPrintf("/%s", ItemsInfo.HostOS);
381 SetInfoLine(1, MInfoArchive, TmpInfoData);
383 TmpInfoData = ItemsInfo.Solid ? GetMsg(MInfoSolid) : "";
384 if (CurArcInfo.SFXSize)
385 AppendInfoData(TmpInfoData, GetMsg(MInfoSFX));
386 if (CurArcInfo.Flags & AF_HDRENCRYPTED)
387 AppendInfoData(TmpInfoData, GetMsg(MInfoHdrEncrypted));
388 if (CurArcInfo.Volume)
389 AppendInfoData(TmpInfoData, GetMsg(MInfoVolume));
390 if (TmpInfoData.empty())
391 TmpInfoData = GetMsg(MInfoNormal);
392 SetInfoLine(2, MInfoArcType, TmpInfoData);
394 SetInfoLine(3, MInfoArcComment, CurArcInfo.Comment ? MInfoPresent : MInfoAbsent);
395 SetInfoLine(4, MInfoFileComments, ItemsInfo.Comment ? MInfoPresent : MInfoAbsent);
396 SetInfoLine(5, MInfoPasswords, ItemsInfo.Encrypted ? MInfoPresent : MInfoAbsent);
397 SetInfoLine(6, MInfoRecovery, CurArcInfo.Recovery ? MInfoPresent : MInfoAbsent);
398 SetInfoLine(7, MInfoLock, CurArcInfo.Lock ? MInfoPresent : MInfoAbsent);
399 SetInfoLine(8, MInfoAuthVer, (CurArcInfo.Flags & AF_AVPRESENT) ? MInfoPresent : MInfoAbsent);
401 if (ItemsInfo.DictSize)
402 SetInfoLine(9, MInfoDict, StrPrintf("%d %s", ItemsInfo.DictSize, GetMsg(MInfoDictKb)));
403 else
404 SetInfoLine(9, MInfoDict, MInfoAbsent);
406 if (CurArcInfo.Chapters)
407 SetInfoLine(10, MInfoChapters, std::to_string(CurArcInfo.Chapters));
408 else
409 SetInfoLine(10, MInfoChapters, MInfoAbsent);
411 SetInfoLine(11, MInfoTotalFiles, std::to_string(ArcDataCount));
412 SetInfoLine(12, MInfoTotalSize, NumberWithCommas(TotalSize));
413 SetInfoLine(13, MInfoPackedSize, NumberWithCommas(PackedSize));
414 SetInfoLine(14, MInfoRatio, StrPrintf("%d%%", MA_ToPercent(PackedSize, TotalSize)));
416 ZeroFill(KeyBar);
417 KeyBar.ShiftTitles[1 - 1] = (char *)"";
418 KeyBar.AltTitles[6 - 1] = (char *)GetMsg(MAltF6);
419 KeyBar.AltShiftTitles[9 - 1] = (char *)GetMsg(MAltShiftF9);
422 Info->Format = Format.c_str();
423 Info->KeyBar = &KeyBar;
424 Info->InfoLines = InfoLines;
425 Info->InfoLinesNumber = ARRAYSIZE(InfoLines);
427 CharArrayCpyZ(DescrFilesString, Opt.DescriptionNames.c_str());
429 size_t DescrFilesNumber = 0;
430 char *NamePtr = DescrFilesString;
432 while (DescrFilesNumber < ARRAYSIZE(DescrFiles)) {
433 while (__isspace(*NamePtr))
434 NamePtr++;
435 if (*NamePtr == 0)
436 break;
437 DescrFiles[DescrFilesNumber++] = NamePtr;
438 if ((NamePtr = strchr(NamePtr, ',')) == NULL)
439 break;
440 *(NamePtr++) = 0;
443 Info->DescrFiles = DescrFiles;
445 if (!Opt.ReadDescriptions || DizPresent)
446 Info->DescrFilesNumber = 0;
447 else
448 Info->DescrFilesNumber = (int)DescrFilesNumber;
450 bGOPIFirstCall = false;