7 Copyright (c) 1996 Eugene Roshal
8 Copyright (c) 2000 Far Group
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the authors may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "headers.hpp"
36 #include "dizlist.hpp"
38 #include "savescr.hpp"
39 #include "TPreRedrawFunc.hpp"
41 #include "keyboard.hpp"
42 #include "message.hpp"
44 #include "pathmix.hpp"
46 #include "filestr.hpp"
47 #include "codepage.hpp"
50 static DizRecord
*SearchDizData
;
51 static int _cdecl
SortDizIndex(const void *el1
, const void *el2
);
52 static int _cdecl
SortDizSearch(const void *key
, const void *elem
);
67 OrigCodePage(CP_AUTODETECT
),
81 for (int I
= 0; I
< DizCount
; I
++)
82 free(DizData
[I
].DizText
);
96 OrigCodePage
= CP_AUTODETECT
;
99 void DizList::PR_ReadingMsg()
101 Message(0, 0, L
"", Msg::ReadingDiz
);
104 void DizList::Read(const wchar_t *Path
, const wchar_t *DizName
)
107 TPreRedrawFuncGuard
preRedrawFuncGuard(DizList::PR_ReadingMsg
);
108 const wchar_t *NamePtr
= Opt
.Diz
.strListNames
;
112 strDizFileName
= DizName
;
114 strDizFileName
= Path
;
116 if (!PathCanHoldRegularFile(strDizFileName
))
119 FARString strArgName
;
121 if (!(NamePtr
= GetCommaWord(NamePtr
, strArgName
)))
124 AddEndSlash(strDizFileName
);
125 strDizFileName
+= strArgName
;
129 FAR_FIND_DATA_EX FindData
;
130 if (apiGetFindDataEx(strDizFileName
, FindData
, FIND_FILE_FLAG_CASE_INSENSITIVE
) &&
131 DizFile
.Open(FindData
.strFileName
, GENERIC_READ
, FILE_SHARE_READ
, nullptr, OPEN_EXISTING
)) {
132 strDizFileName
=FindData
.strFileName
;
133 GetFileString
GetStr(DizFile
);
136 clock_t StartTime
= GetProcessUptimeMSec();
137 UINT CodePage
= CP_AUTODETECT
;
138 bool bSigFound
= false;
140 if (!GetFileFormat(DizFile
, CodePage
, &bSigFound
, false) || !bSigFound
)
141 CodePage
= Opt
.Diz
.AnsiByDefault
? CP_ACP
: CP_OEMCP
;
143 while (GetStr
.GetString(&DizText
, CodePage
, DizLength
) > 0) {
144 if (!(DizCount
& 127) && GetProcessUptimeMSec() - StartTime
> 1000) {
145 SetCursorType(FALSE
, 0);
152 RemoveTrailingSpaces(DizText
);
158 OrigCodePage
= CodePage
;
169 strDizFileName
.Clear();
172 bool DizList::AddRecord(const wchar_t *DizText
)
174 DizRecord
*NewDizData
= DizData
;
176 if (!(DizCount
& 15))
177 NewDizData
= (DizRecord
*)realloc(DizData
, (DizCount
+ 16 + 1) * sizeof(*DizData
));
182 DizData
= NewDizData
;
183 DizData
[DizCount
].DizLength
= StrLength(DizText
);
184 DizData
[DizCount
].DizText
= (wchar_t *)malloc((DizData
[DizCount
].DizLength
+ 1) * sizeof(wchar_t));
186 if (!DizData
[DizCount
].DizText
)
189 wcscpy(DizData
[DizCount
].DizText
, DizText
);
190 DizData
[DizCount
].NameStart
= 0;
191 DizData
[DizCount
].NameLength
= 0;
193 if (*DizText
== L
'\"') {
195 DizData
[DizCount
].NameStart
++;
197 while (*DizText
&& *DizText
!= L
'\"') {
199 DizData
[DizCount
].NameLength
++;
202 while (!IsSpaceOrEos(*DizText
)) {
204 DizData
[DizCount
].NameLength
++;
208 DizData
[DizCount
].Deleted
= false;
215 const wchar_t *DizList::GetDizTextAddr(const wchar_t *Name
, const int64_t FileSize
)
217 const wchar_t *DizText
= nullptr;
219 int DizPos
= GetDizPosEx(Name
, &TextPos
);
222 DizText
= DizData
[DizPos
].DizText
+ TextPos
;
224 while (*DizText
&& IsSpace(*DizText
))
227 if (iswdigit(*DizText
)) {
228 wchar_t SizeText
[30];
229 const wchar_t *DizPtr
= DizText
;
230 bool SkipSize
= true;
231 swprintf(SizeText
, ARRAYSIZE(SizeText
), L
"%llu", FileSize
);
233 for (int I
= 0; SizeText
[I
]; DizPtr
++) {
234 if (*DizPtr
!= L
',' && *DizPtr
!= L
'.') {
235 if (SizeText
[I
++] != *DizPtr
) {
242 if (SkipSize
&& IsSpace(*DizPtr
)) {
245 while (*DizText
&& IsSpace(*DizText
))
254 int DizList::GetDizPosEx(const wchar_t *Name
, int *TextPos
)
256 int DizPos
= GetDizPos(Name
, TextPos
);
258 // если файл описаний был в OEM/ANSI то имена файлов могут не совпадать с юникодными
259 if (DizPos
== -1 && !IsUnicodeOrUtfCodePage(OrigCodePage
) && OrigCodePage
!= CP_AUTODETECT
) {
260 int len
= StrLength(Name
);
261 char *tmp
= (char *)malloc(len
+ 1);
268 WINPORT(WideCharToMultiByte
)(OrigCodePage
, 0, Name
, len
, AnsiBuf
, len
, nullptr, nullptr);
270 FARString
strRecoded(AnsiBuf
, OrigCodePage
);
272 if (strRecoded
== Name
)
275 return GetDizPos(strRecoded
, TextPos
);
281 int DizList::GetDizPos(const wchar_t *Name
, int *TextPos
)
283 if (!DizData
|| !*Name
)
289 SearchDizData
= DizData
;
290 DizSearchKey Key
= {Name
, StrLength(Name
)};
291 int *DestIndex
= (int *)bsearch(&Key
, IndexData
, IndexCount
, sizeof(*IndexData
), SortDizSearch
);
295 *TextPos
= DizData
[*DestIndex
].NameStart
+ DizData
[*DestIndex
].NameLength
;
297 if (DizData
[*DestIndex
].NameStart
&& DizData
[*DestIndex
].DizText
[*TextPos
] == L
'\"')
307 void DizList::BuildIndex()
309 if (!IndexData
|| IndexCount
!= DizCount
) {
313 if (!(IndexData
= (int *)malloc(DizCount
* sizeof(int)))) {
318 IndexCount
= DizCount
;
321 for (int I
= 0; I
< IndexCount
; I
++)
324 SearchDizData
= DizData
;
325 far_qsort((void *)IndexData
, IndexCount
, sizeof(*IndexData
), SortDizIndex
);
329 int _cdecl
SortDizIndex(const void *el1
, const void *el2
)
331 const wchar_t *Diz1
= SearchDizData
[*(int *)el1
].DizText
+ SearchDizData
[*(int *)el1
].NameStart
;
332 const wchar_t *Diz2
= SearchDizData
[*(int *)el2
].DizText
+ SearchDizData
[*(int *)el2
].NameStart
;
333 int Len1
= SearchDizData
[*(int *)el1
].NameLength
;
334 int Len2
= SearchDizData
[*(int *)el2
].NameLength
;
335 int CmpCode
= StrCmpNI(Diz1
, Diz2
, Min(Len1
, Len2
));
344 // for equal names, deleted is bigger
345 bool Del1
= SearchDizData
[*(int *)el1
].Deleted
;
346 bool Del2
= SearchDizData
[*(int *)el2
].Deleted
;
358 int _cdecl
SortDizSearch(const void *key
, const void *elem
)
360 const wchar_t *SearchName
= ((DizSearchKey
*)key
)->Str
;
361 wchar_t *DizName
= SearchDizData
[*(int *)elem
].DizText
+ SearchDizData
[*(int *)elem
].NameStart
;
362 int DizNameLength
= SearchDizData
[*(int *)elem
].NameLength
;
363 int NameLength
= ((DizSearchKey
*)key
)->Len
;
364 int CmpCode
= StrCmpNI(SearchName
, DizName
, Min(DizNameLength
, NameLength
));
367 if (NameLength
> DizNameLength
)
370 if (NameLength
+ 1 < DizNameLength
)
373 // filename == filename.
374 if (NameLength
+ 1 == DizNameLength
375 && !(DizName
[NameLength
] == L
'.' && wcschr(DizName
, L
'.') == &DizName
[NameLength
]))
378 // for equal names, deleted is bigger so deleted items are never matched
379 if (SearchDizData
[*(int *)elem
].Deleted
)
386 bool DizList::DeleteDiz(const wchar_t *Name
)
388 int DizPos
= GetDizPosEx(Name
, nullptr);
393 DizData
[DizPos
++].Deleted
= true;
395 while (DizPos
< DizCount
) {
396 if (*DizData
[DizPos
].DizText
&& !IsSpace(DizData
[DizPos
].DizText
[0]))
399 DizData
[DizPos
].Deleted
= true;
408 bool DizList::Flush(const wchar_t *Path
, const wchar_t *DizName
)
414 strDizFileName
= DizName
;
415 } else if (strDizFileName
.IsEmpty()) {
416 if (!DizData
|| !Path
)
419 strDizFileName
= Path
;
420 AddEndSlash(strDizFileName
);
421 FARString strArgName
;
422 GetCommaWord(Opt
.Diz
.strListNames
, strArgName
);
423 strDizFileName
+= strArgName
;
426 DWORD FileAttr
= apiGetFileAttributes(strDizFileName
);
428 if (FileAttr
!= INVALID_FILE_ATTRIBUTES
) {
429 if (FileAttr
& FILE_ATTRIBUTE_READONLY
) {
430 if (Opt
.Diz
.ROUpdate
) {
431 if (apiSetFileAttributes(strDizFileName
, FileAttr
)) {
432 FileAttr
^= FILE_ATTRIBUTE_READONLY
;
437 if (!(FileAttr
& FILE_ATTRIBUTE_READONLY
)) {
438 apiSetFileAttributes(strDizFileName
, FILE_ATTRIBUTE_ARCHIVE
);
440 Message(MSG_WARNING
, 1, Msg::Error
, Msg::CannotUpdateDiz
, Msg::CannotUpdateRODiz
, Msg::Ok
);
447 bool AnyError
= false;
449 bool EmptyDiz
= true;
450 // Don't use CreationDisposition=CREATE_ALWAYS here - it's kills alternate streams
452 && DizFile
.Open(strDizFileName
, GENERIC_WRITE
, FILE_SHARE_READ
, nullptr,
453 FileAttr
== INVALID_FILE_ATTRIBUTES
? CREATE_NEW
: TRUNCATE_EXISTING
)) {
454 UINT CodePage
= Opt
.Diz
.SaveInUTF
? CP_UTF8
: (Opt
.Diz
.AnsiByDefault
? CP_ACP
: CP_OEMCP
);
456 CachedWrite
Cache(DizFile
);
458 if (CodePage
== CP_UTF8
) {
459 DWORD dwSignature
= SIGN_UTF8
;
460 if (!Cache
.Write(&dwSignature
, 3)) {
466 for (int I
= 0; I
< DizCount
; I
++) {
467 if (!DizData
[I
].Deleted
) {
468 DWORD Size
= (DizData
[I
].DizLength
+ 1) * (CodePage
== CP_UTF8
? 3 : 1); // UTF-8, up to 3 bytes per char support
469 char *lpDizText
= new (std::nothrow
) char[Size
];
471 int BytesCount
= WINPORT(WideCharToMultiByte
)(CodePage
, 0, DizData
[I
].DizText
,
472 DizData
[I
].DizLength
+ 1, lpDizText
, Size
, nullptr, nullptr);
473 if (BytesCount
&& BytesCount
- 1) {
474 if (Cache
.Write(lpDizText
, BytesCount
- 1)) {
480 if (!Cache
.Write("\r\n", 2)) {
492 if (!Cache
.Flush()) {
500 if (!EmptyDiz
&& !AnyError
) {
501 if (FileAttr
== INVALID_FILE_ATTRIBUTES
) {
502 FileAttr
= FILE_ATTRIBUTE_ARCHIVE
| (Opt
.Diz
.SetHidden
? FILE_ATTRIBUTE_HIDDEN
: 0);
504 apiSetFileAttributes(strDizFileName
, FileAttr
);
506 apiDeleteFile(strDizFileName
);
508 Message(MSG_WARNING
| MSG_ERRORTYPE
, 1, Msg::Error
, Msg::CannotUpdateDiz
, Msg::Ok
);
517 bool DizList::AddDizText(const wchar_t *Name
, const wchar_t *DizText
)
520 FARString strQuotedName
= Name
;
521 QuoteSpaceOnly(strQuotedName
);
522 FormatString FString
;
523 FString
<< fmt::LeftAlign() << fmt::Expand(Opt
.Diz
.StartPos
> 1 ? Opt
.Diz
.StartPos
- 2 : 0)
524 << strQuotedName
<< L
" " << DizText
;
525 return AddRecord(FString
);
528 bool DizList::CopyDiz(const wchar_t *Name
, const wchar_t *DestName
, DizList
*DestDiz
)
531 int DizPos
= GetDizPosEx(Name
, &TextPos
);
536 while (IsSpace(DizData
[DizPos
].DizText
[TextPos
]))
539 DestDiz
->AddDizText(DestName
, &DizData
[DizPos
].DizText
[TextPos
]);
541 while (++DizPos
< DizCount
) {
542 if (*DizData
[DizPos
].DizText
&& !IsSpace(DizData
[DizPos
].DizText
[0]))
545 DestDiz
->AddRecord(DizData
[DizPos
].DizText
);
551 void DizList::GetDizName(FARString
&strDizName
)
553 strDizName
= strDizFileName
;