fix clang build
[far2l.git] / multiarc / src / arcread.cpp
blob7dc40909e08918d21adbe546430cc537c1821a87
1 #include <fcntl.h>
2 #include "MultiArc.hpp"
3 #include "marclng.hpp"
5 PluginClass::PluginClass(int ArcPluginNumber)
7 *ArcName = 0;
8 *CurDir = 0;
9 PluginClass::ArcPluginNumber = ArcPluginNumber;
10 DizPresent = FALSE;
11 bGOPIFirstCall = true;
12 *farlang = 0;
13 ZeroFill(CurArcInfo);
16 PluginClass::~PluginClass()
18 FreeArcData();
21 void PluginClass::FreeArcData()
23 ArcData.Clear();
24 ArcDataCount = 0;
27 int PluginClass::PreReadArchive(const char *Name)
29 if (sdc_stat(Name, &ArcStat) == -1) {
30 return FALSE;
33 ArrayCpyZ(ArcName, Name);
35 if (strchr(FSF.PointToName(ArcName), '.') == NULL)
36 strcat(ArcName, ".");
38 return TRUE;
41 static void SanitizeString(std::string &s)
43 while (!s.empty() && !s.back()) {
44 s.pop_back();
48 int PluginClass::ReadArchive(const char *Name, int OpMode)
50 bGOPIFirstCall = true;
51 FreeArcData();
52 DizPresent = FALSE;
54 if (sdc_stat(Name, &ArcStat) == -1)
55 return FALSE;
57 if (!ArcPlugin->OpenArchive(ArcPluginNumber, Name, &ArcPluginType, (OpMode & OPM_SILENT) != 0))
58 return FALSE;
60 ItemsInfo = ArcItemInfo{};
61 ZeroFill(CurArcInfo);
62 TotalSize = PackedSize = 0;
63 ArcDataCount = 0;
65 HANDLE hScreen = Info.SaveScreen(0, 0, -1, -1);
67 DWORD UpdateTime = GetProcessUptimeMSec() + 1000;
68 bool MessageShown = false;
69 int GetItemCode;
71 ArcItemInfo CurItemInfo;
72 PathParts CurPP;
73 while (1) {
74 CurItemInfo = ArcItemInfo();
75 GetItemCode = ArcPlugin->GetArcItem(ArcPluginNumber, &CurItemInfo);
76 if (GetItemCode != GETARC_SUCCESS)
77 break;
79 SanitizeString(CurItemInfo.PathName);
80 if (CurItemInfo.Description)
81 SanitizeString(*CurItemInfo.Description);
82 if (CurItemInfo.LinkName)
83 SanitizeString(*CurItemInfo.LinkName);
85 if ((ArcDataCount & 0x1f) == 0) {
86 if (CheckForEsc()) {
87 FreeArcData();
88 ArcPlugin->CloseArchive(ArcPluginNumber, &CurArcInfo);
89 Info.RestoreScreen(NULL);
90 Info.RestoreScreen(hScreen);
91 return FALSE;
94 const DWORD Now = GetProcessUptimeMSec();
95 if (Now >= UpdateTime) {
96 UpdateTime = Now + 100;
97 char FilesMsg[100];
98 char NameMsg[NM];
99 FSF.sprintf(FilesMsg, GetMsg(MArcReadFiles), (unsigned int)ArcDataCount);
100 const char *MsgItems[] = {GetMsg(MArcReadTitle), GetMsg(MArcReading), NameMsg, FilesMsg};
101 ArrayCpyZ(NameMsg, Name);
102 FSF.TruncPathStr(NameMsg, MAX_WIDTH_MESSAGE);
103 Info.Message(Info.ModuleNumber, MessageShown ? FMSG_KEEPBACKGROUND : 0, NULL, MsgItems,
104 ARRAYSIZE(MsgItems), 0);
105 MessageShown = true;
109 if (CurItemInfo.Description)
110 DizPresent = TRUE;
112 if (CurItemInfo.HostOS && (!ItemsInfo.HostOS || strcmp(ItemsInfo.HostOS, CurItemInfo.HostOS) != 0))
113 ItemsInfo.HostOS = (ItemsInfo.HostOS ? CurItemInfo.HostOS : GetMsg(MSeveralOS));
115 if (ItemsInfo.Codepage <= 0)
116 ItemsInfo.Codepage = CurItemInfo.Codepage;
118 ItemsInfo.Solid|= CurItemInfo.Solid;
119 ItemsInfo.Comment|= CurItemInfo.Comment;
120 ItemsInfo.Encrypted|= CurItemInfo.Encrypted;
122 if (CurItemInfo.Encrypted)
123 CurItemInfo.Flags|= F_ENCRYPTED;
125 if (CurItemInfo.DictSize > ItemsInfo.DictSize)
126 ItemsInfo.DictSize = CurItemInfo.DictSize;
128 if (CurItemInfo.UnpVer > ItemsInfo.UnpVer)
129 ItemsInfo.UnpVer = CurItemInfo.UnpVer;
131 CurItemInfo.NumberOfLinks = 1;
133 size_t PrefixSize = 0;
134 if (StrStartsFrom(CurItemInfo.PathName, "./"))
135 PrefixSize = 2;
136 else if (StrStartsFrom(CurItemInfo.PathName, "../"))
137 PrefixSize = 3;
138 while (PrefixSize < CurItemInfo.PathName.size() && CurItemInfo.PathName[PrefixSize] == '/')
139 PrefixSize++;
141 if (PrefixSize) {
142 CurItemInfo.Prefix.reset(new std::string(CurItemInfo.PathName.substr(0, PrefixSize)));
143 CurItemInfo.PathName.erase(0, PrefixSize);
146 if (!CurItemInfo.PathName.empty() && CurItemInfo.PathName.back() == '/')
147 CurItemInfo.dwFileAttributes|= FILE_ATTRIBUTE_DIRECTORY;
149 TotalSize+= CurItemInfo.nFileSize;
150 PackedSize+= CurItemInfo.nPhysicalSize;
152 CurPP.clear();
153 CurPP.Traverse(CurItemInfo.PathName);
154 ArcItemAttributes *CurAttrs = ArcData.Ensure(CurPP.begin(), CurPP.end());
155 *CurAttrs = std::move(CurItemInfo);
156 ++ArcDataCount;
159 Info.RestoreScreen(NULL);
160 Info.RestoreScreen(hScreen);
162 ArcPlugin->CloseArchive(ArcPluginNumber, &CurArcInfo);
164 if (GetItemCode != GETARC_EOF && GetItemCode != GETARC_SUCCESS) {
165 switch (GetItemCode) {
166 case GETARC_BROKEN:
167 GetItemCode = MBadArchive;
168 break;
170 case GETARC_UNEXPEOF:
171 GetItemCode = MUnexpEOF;
172 break;
174 case GETARC_READERROR:
175 GetItemCode = MReadError;
176 break;
179 char NameMsg[NM];
180 const char *MsgItems[] = {GetMsg(MError), NameMsg, GetMsg(GetItemCode), GetMsg(MOk)};
181 FSF.TruncPathStr(strncpy(NameMsg, Name, sizeof(NameMsg) - 1), MAX_WIDTH_MESSAGE);
182 Info.Message(Info.ModuleNumber, FMSG_WARNING, NULL, MsgItems, ARRAYSIZE(MsgItems), 1);
183 return FALSE; // Mantis#0001241
186 // Info.RestoreScreen(NULL);
187 // Info.RestoreScreen(hScreen);
188 return TRUE;
191 bool PluginClass::EnsureFindDataUpToDate(int OpMode)
193 if (!ArcData.empty()) {
194 struct stat NewArcStat
196 if (sdc_stat(ArcName, &NewArcStat) == -1)
197 return false;
199 if (ArcStat.st_mtime == NewArcStat.st_mtime && ArcStat.st_size == NewArcStat.st_size)
200 return true;
203 DWORD size = (DWORD)Info.AdvControl(Info.ModuleNumber, ACTL_GETPLUGINMAXREADDATA, (void *)0);
204 int fd = sdc_open(ArcName, O_RDONLY);
205 if (fd == -1)
206 return false;
208 unsigned char *Data = (unsigned char *)malloc(size);
209 ssize_t read_size = Data ? sdc_read(fd, Data, size) : -1;
210 sdc_close(fd);
212 bool ReadArcOK = false;
213 if (read_size > 0) {
214 DWORD SFXSize = 0;
216 ReadArcOK = (ArcPlugin->IsArchive(ArcPluginNumber, ArcName, Data, read_size, &SFXSize)
217 && ReadArchive(ArcName, OpMode));
219 free(Data);
221 return ReadArcOK;
224 int PluginClass::GetFindData(PluginPanelItem **pPanelItem, int *pItemsNumber, int OpMode)
226 *pPanelItem = NULL;
227 *pItemsNumber = 0;
229 PathParts CurDirPP;
230 CurDirPP.Traverse(CurDir);
232 if (!EnsureFindDataUpToDate(OpMode)) {
233 fprintf(stderr, "MA::GetFindData: can't update at '%s'\n", CurDirPP.Join().c_str());
234 return FALSE;
237 const auto *DirNode = ArcData.Find(CurDirPP.begin(), CurDirPP.end());
238 if (!DirNode) {
239 fprintf(stderr, "MA::GetFindData: no node at '%s'\n", CurDirPP.Join().c_str());
240 return FALSE;
243 if (DirNode->empty())
244 return TRUE;
246 PluginPanelItem *CurrentItem = *pPanelItem =
247 (PluginPanelItem *)calloc(DirNode->size(), sizeof(PluginPanelItem));
248 if (!CurrentItem) {
249 fprintf(stderr, "MA::GetFindData: can't alloc %lu items at '%s'\n", (unsigned long)DirNode->size(),
250 CurDirPP.Join().c_str());
251 return FALSE;
253 *pItemsNumber = (int)DirNode->size();
255 for (const auto &it : *DirNode) {
256 CurrentItem->FindData.ftCreationTime = it.second.ftCreationTime;
257 CurrentItem->FindData.ftLastAccessTime = it.second.ftLastAccessTime;
258 CurrentItem->FindData.ftLastWriteTime = it.second.ftLastWriteTime;
259 CurrentItem->FindData.nPhysicalSize = it.second.nPhysicalSize;
260 CurrentItem->FindData.nFileSize = it.second.nFileSize;
261 CurrentItem->FindData.dwFileAttributes = it.second.dwFileAttributes;
262 CurrentItem->FindData.dwUnixMode = it.second.dwUnixMode;
263 strncpy(CurrentItem->FindData.cFileName, it.first.c_str(),
264 ARRAYSIZE(CurrentItem->FindData.cFileName));
265 CurrentItem->Flags = it.second.Flags;
266 CurrentItem->NumberOfLinks = it.second.NumberOfLinks;
267 CurrentItem->CRC32 = it.second.CRC32;
268 CurrentItem->Description = it.second.Description ? (char *)it.second.Description->c_str() : nullptr;
269 CurrentItem->UserData = (DWORD_PTR)&it.second;
271 if (!it.second.empty())
272 CurrentItem->FindData.dwFileAttributes|= FILE_ATTRIBUTE_DIRECTORY;
273 if (CurrentItem->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
274 CurrentItem->FindData.nPhysicalSize = CurrentItem->FindData.nFileSize = 0;
276 ++CurrentItem;
279 return TRUE;
282 void PluginClass::FreeFindData(PluginPanelItem *PanelItem, int ItemsNumber)
284 if (PanelItem)
285 free(PanelItem);
288 int PluginClass::SetDirectory(const char *Dir, int OpMode)
290 if (*Dir == '/' && *(++Dir) == 0) {
291 *CurDir = 0;
292 return TRUE;
295 PathParts NewDirPP;
296 NewDirPP.Traverse(CurDir);
297 NewDirPP.Traverse(Dir);
298 const auto &NewDir = NewDirPP.Join();
300 auto *DirNode = ArcData.Find(NewDirPP.begin(), NewDirPP.end());
301 if (!DirNode) {
302 fprintf(stderr, "MA::SetDirectory('%s', %d): no node for '%s'\n", Dir, OpMode, NewDir.c_str());
303 return FALSE;
306 if (NewDir.size() >= ARRAYSIZE(CurDir)) {
307 fprintf(stderr, "MA::SetDirectory('%s', %d): too long path '%s'\n", Dir, OpMode, NewDir.c_str());
308 return FALSE;
311 strcpy(CurDir, NewDir.c_str());
312 return TRUE;
315 bool PluginClass::FarLangChanged()
317 const char *tmplang = getenv("FARLANG");
319 if (!tmplang)
320 tmplang = "English";
322 if (!strcmp(tmplang, farlang))
323 return false;
325 ArrayCpyZ(farlang, tmplang);
327 return true;
330 static void AppendInfoData(std::string &Str, const char *Data)
332 if (!Str.empty())
333 Str+= ' ';
334 Str+= Data;
337 void PluginClass::SetInfoLineSZ(size_t Index, int TextID, const char *Data)
339 ArrayCpyZ(InfoLines[Index].Text, GetMsg(TextID));
340 ArrayCpyZ(InfoLines[Index].Data, Data);
343 void PluginClass::SetInfoLine(size_t Index, int TextID, const std::string &Data)
345 SetInfoLineSZ(Index, TextID, Data.c_str());
348 void PluginClass::SetInfoLine(size_t Index, int TextID, int DataID)
350 SetInfoLineSZ(Index, TextID, GetMsg(DataID));
353 void PluginClass::GetOpenPluginInfo(struct OpenPluginInfo *Info)
355 Info->StructSize = sizeof(*Info);
356 Info->Flags =
357 OPIF_USEFILTER | OPIF_USESORTGROUPS | OPIF_USEHIGHLIGHTING | OPIF_ADDDOTS | OPIF_COMPAREFATTIME;
358 Info->HostFile = ArcName;
359 Info->CurDir = CurDir;
361 if (bGOPIFirstCall)
362 ArcPlugin->GetFormatName(ArcPluginNumber, ArcPluginType, FormatName, DefExt);
364 char NameTitle[NM];
365 strncpy(NameTitle, FSF.PointToName(ArcName), sizeof(NameTitle) - 1);
368 struct PanelInfo PInfo;
369 if (::Info.Control((HANDLE)this, FCTL_GETPANELSHORTINFO, &PInfo)) { // TruncStr
370 FSF.TruncPathStr(NameTitle,
371 (PInfo.PanelRect.right - PInfo.PanelRect.left + 1 - (strlen(FormatName) + 3 + 4)));
375 FSF.snprintf(Title, ARRAYSIZE(Title) - 1, " %s:%s%s%s ", FormatName, NameTitle, *CurDir ? "/" : "",
376 *CurDir ? CurDir : "");
378 Info->PanelTitle = Title;
380 if (bGOPIFirstCall || FarLangChanged()) {
381 FSF.snprintf(Format, ARRAYSIZE(Format) - 1, GetMsg(MArcFormat), FormatName);
383 ZeroFill(InfoLines);
384 FSF.snprintf(InfoLines[0].Text, ARRAYSIZE(InfoLines[0].Text), GetMsg(MInfoTitle),
385 FSF.PointToName(ArcName));
386 InfoLines[0].Separator = TRUE;
388 std::string TmpInfoData = FormatName;
389 if (ItemsInfo.UnpVer != 0)
390 TmpInfoData+= StrPrintf(" %d.%d", ItemsInfo.UnpVer / 256, ItemsInfo.UnpVer % 256);
391 if (ItemsInfo.HostOS)
392 TmpInfoData+= StrPrintf("/%s", ItemsInfo.HostOS);
393 SetInfoLine(1, MInfoArchive, TmpInfoData);
395 TmpInfoData = ItemsInfo.Solid ? GetMsg(MInfoSolid) : "";
396 if (CurArcInfo.SFXSize)
397 AppendInfoData(TmpInfoData, GetMsg(MInfoSFX));
398 if (CurArcInfo.Flags & AF_HDRENCRYPTED)
399 AppendInfoData(TmpInfoData, GetMsg(MInfoHdrEncrypted));
400 if (CurArcInfo.Volume)
401 AppendInfoData(TmpInfoData, GetMsg(MInfoVolume));
402 if (TmpInfoData.empty())
403 TmpInfoData = GetMsg(MInfoNormal);
404 SetInfoLine(2, MInfoArcType, TmpInfoData);
406 SetInfoLine(3, MInfoArcComment, CurArcInfo.Comment ? MInfoPresent : MInfoAbsent);
407 SetInfoLine(4, MInfoFileComments, ItemsInfo.Comment ? MInfoPresent : MInfoAbsent);
408 SetInfoLine(5, MInfoPasswords, ItemsInfo.Encrypted ? MInfoPresent : MInfoAbsent);
409 SetInfoLine(6, MInfoRecovery, CurArcInfo.Recovery ? MInfoPresent : MInfoAbsent);
410 SetInfoLine(7, MInfoLock, CurArcInfo.Lock ? MInfoPresent : MInfoAbsent);
411 SetInfoLine(8, MInfoAuthVer, (CurArcInfo.Flags & AF_AVPRESENT) ? MInfoPresent : MInfoAbsent);
413 if (ItemsInfo.DictSize)
414 SetInfoLine(9, MInfoDict, StrPrintf("%d %s", ItemsInfo.DictSize, GetMsg(MInfoDictKb)));
415 else
416 SetInfoLine(9, MInfoDict, MInfoAbsent);
418 if (CurArcInfo.Chapters)
419 SetInfoLine(10, MInfoChapters, std::to_string(CurArcInfo.Chapters));
420 else
421 SetInfoLine(10, MInfoChapters, MInfoAbsent);
423 SetInfoLine(11, MInfoTotalFiles, std::to_string(ArcDataCount));
424 SetInfoLine(12, MInfoTotalSize, NumberWithCommas(TotalSize));
425 SetInfoLine(13, MInfoPackedSize, NumberWithCommas(PackedSize));
426 SetInfoLine(14, MInfoRatio, StrPrintf("%d%%", MA_ToPercent(PackedSize, TotalSize)));
428 ZeroFill(KeyBar);
429 KeyBar.ShiftTitles[1 - 1] = (char *)"";
430 KeyBar.AltTitles[6 - 1] = (char *)GetMsg(MAltF6);
431 KeyBar.AltShiftTitles[9 - 1] = (char *)GetMsg(MAltShiftF9);
434 Info->Format = Format;
435 Info->KeyBar = &KeyBar;
436 Info->InfoLines = InfoLines;
437 Info->InfoLinesNumber = ARRAYSIZE(InfoLines);
439 ArrayCpyZ(DescrFilesString, Opt.DescriptionNames.c_str());
441 size_t DescrFilesNumber = 0;
442 char *NamePtr = DescrFilesString;
444 while (DescrFilesNumber < ARRAYSIZE(DescrFiles)) {
445 while (__isspace(*NamePtr))
446 NamePtr++;
447 if (*NamePtr == 0)
448 break;
449 DescrFiles[DescrFilesNumber++] = NamePtr;
450 if ((NamePtr = strchr(NamePtr, ',')) == NULL)
451 break;
452 *(NamePtr++) = 0;
455 Info->DescrFiles = DescrFiles;
457 if (!Opt.ReadDescriptions || DizPresent)
458 Info->DescrFilesNumber = 0;
459 else
460 Info->DescrFilesNumber = (int)DescrFilesNumber;
462 bGOPIFirstCall = false;