2 #include "MultiArc.hpp"
8 ArcCommand::ArcCommand(struct PluginPanelItem
*PanelItem
, int ItemsNumber
, const std::string
&FormatString
,
9 const std::string
&ArcName
, const std::string
&ArcDir
, const std::string
&Password
, const std::string
&AllFilesMask
,
10 int IgnoreErrors
, int CommandType
, int ASilent
, const char *RealArcDir
, int DefaultCodepage
)
16 ArcCommand::DefaultCodepage
= DefaultCodepage
;
18 // fprintf(stderr, "ArcCommand::ArcCommand = %d, FormatString=%s\n", ArcCommand::DefaultCodepage, FormatString);
19 if (FormatString
.empty())
23 (CommandType
!= CMD_EXTRACT
&& CommandType
!= CMD_EXTRACTWITHOUTPATH
&& CommandType
!= CMD_TEST
);
26 if (!ArcName
.empty()) {
27 const auto &ArcPath
= ExtractFilePath(ArcName
);
28 if ((sudo_client_is_required_for(ArcName
.c_str(), true) == 1) || // no write perms to archive itself?
29 (sudo_client_is_required_for(ArcPath
.c_str(), true)==1)) { // no write perms to archive dir?
33 if(!NeedSudo
&& (CommandType
==CMD_ADD
|| CommandType
==CMD_ADDRECURSE
)) { // adding files to the archive,
34 for(int i
=0; i
<ItemsNumber
; ++i
) { // check if we have read access to all of them
35 if(sudo_client_is_required_for(PanelItem
[i
].FindData
.cFileName
,false)==1) {
43 if((sudo_client_is_required_for(ArcName
.c_str(), false) == 1) // do we have read access to the archive?
44 || ((CommandType
== CMD_EXTRACT
|| CommandType
== CMD_EXTRACTWITHOUTPATH
) // extraction from the archive,
45 && (sudo_client_is_required_for(".", true) == 1))) { // check if we have write access to dest dir
50 // char QPassword[NM+5],QTempPath[NM+5];
51 ArcCommand::PanelItem
= PanelItem
;
52 ArcCommand::ItemsNumber
= ItemsNumber
;
53 ArcCommand::ArcName
= ArcName
;
54 ArcCommand::ArcDir
= ArcDir
;
55 ArcCommand::RealArcDir
= RealArcDir
? RealArcDir
: "";
56 ArcCommand::Password
= Password
;
57 QuoteCmdArgIfNeed(ArcCommand::Password
);
58 ArcCommand::AllFilesMask
= AllFilesMask
;
59 // WINPORT(GetTempPath)(ARRAYSIZE(TempPath),TempPath);
60 ArcCommand::TempPath
= InMyTemp();
64 PrevFileNameNumber
= -1;
65 if (!ProcessCommand(FormatString
, CommandType
, IgnoreErrors
, ListFileName
.c_str()))
67 if (!ListFileName
.empty()) {
69 sdc_remove(ListFileName
.c_str());
72 } while (NameNumber
!= -1 && NameNumber
< ItemsNumber
);
75 bool ArcCommand::ProcessCommand(std::string FormatString
, int CommandType
, int IgnoreErrors
, const char *pcListFileName
)
77 MaxAllowedExitCode
= 0;
78 DeleteBraces(FormatString
);
80 // for (char *CurPtr=Command; *CurPtr;)
81 std::string tmp
, NonVar
, Command
;
82 while (!FormatString
.empty()) {
84 int r
= ReplaceVar(tmp
);
85 // fprintf(stderr, "ReplaceVar: %d '%s' -> '%s'\n", r, FormatString.c_str(), tmp.c_str());
90 NonVar
+= FormatString
[0];
92 Command
+= ExpandEnv(NonVar
);
97 FormatString
.erase(0, r
);
99 Command
+= ExpandEnv(NonVar
);
100 // fprintf(stderr, "Command='%s'\n", Command.c_str());
102 if (Command
.empty()) {
104 const char *MsgItems
[] = {GetMsg(MError
), GetMsg(MArcCommandNotFound
), GetMsg(MOk
)};
105 Info
.Message(Info
.ModuleNumber
, FMSG_WARNING
, NULL
, MsgItems
, ARRAYSIZE(MsgItems
), 1);
110 int Hide
= Opt
.HideOutput
;
111 if ((Hide
== 1 && CommandType
== 0) || CommandType
== 2)
114 ExecCode
= Execute(this, Command
, Hide
, Silent
, NeedSudo
, Password
.empty(), ListFileName
.c_str());
115 fprintf(stderr
, "ArcCommand::ProcessCommand: ExecCode=%d for '%s'\n", ExecCode
, Command
.c_str());
117 // Unzip in MacOS definitely doesn't have -I and -O options, so dont even try encoding workarounds
119 if (ExecCode
== 11 && strncmp(Command
.c_str(), "unzip ", 6) == 0) {
121 std::string CommandRetry
= Command
;
122 CommandRetry
.insert(6, "-I utf8 -O utf8 ");
123 ExecCode
= Execute(this, CommandRetry
, Hide
, Silent
, NeedSudo
, Password
.empty(), ListFileName
.c_str());
124 if (ExecCode
== 11) {
125 // "11" means file was not found in archive. retrying as oem
126 CommandRetry
= Command
;
127 unsigned int retry_cp
= (DefaultCodepage
> 0) ? DefaultCodepage
: WINPORT(GetOEMCP
)();
128 CommandRetry
.insert(6, StrPrintf("-I CP%u -O CP%u ", retry_cp
, retry_cp
));
129 ExecCode
= Execute(this, CommandRetry
, Hide
, Silent
, NeedSudo
, Password
.empty(), ListFileName
.c_str());
132 // "1" exit code for unzip is warning only, no need to bother user
135 } else if (ExecCode
== 12 && strncmp(Command
.c_str(), "zip -d", 6) == 0) {
137 for (size_t i_entries
= 6; i_entries
+ 1 < Command
.size(); ++i_entries
) {
138 if (Command
[i_entries
] == ' ' && Command
[i_entries
+ 1] != ' ' && Command
[i_entries
+ 1] != '-') {
139 i_entries
= Command
.find(' ', i_entries
+ 1);
140 if (i_entries
!= std::string::npos
) {
141 std::wstring wstr
= StrMB2Wide(Command
.substr(i_entries
));
142 std::vector
<char> oemstr(wstr
.size() * 6 + 2);
143 char def_char
= '\x01';
144 BOOL def_char_used
= FALSE
;
145 unsigned int retry_cp
= (DefaultCodepage
> 0) ? DefaultCodepage
: WINPORT(GetOEMCP
)();
146 WINPORT(WideCharToMultiByte
)
147 (retry_cp
, 0, wstr
.c_str(), wstr
.size() + 1, &oemstr
[0], oemstr
.size() - 1, &def_char
,
149 if (!def_char_used
) {
150 std::string CommandRetry
= Command
.substr(0, i_entries
);
151 CommandRetry
.append(&oemstr
[0]);
152 ExecCode
= Execute(this, CommandRetry
.c_str(),
153 Hide
, Silent
, NeedSudo
, Password
.empty(), ListFileName
.c_str());
154 fprintf(stderr
, "ArcCommand::ProcessCommand: retry ExecCode=%d for '%s'\n", ExecCode
,
155 CommandRetry
.c_str());
157 fprintf(stderr
, "ArcCommand::ProcessCommand: can't translate to CP%u: '%s'\n",
158 retry_cp
, Command
.c_str());
166 if (ExecCode
== RETEXEC_ARCNOTFOUND
)
169 if (ExecCode
<= MaxAllowedExitCode
)
172 if (!IgnoreErrors
&& ExecCode
!= 0) {
174 const auto &ErrMsg
= StrPrintf(GetMsg(MArcNonZero
), ExecCode
);
175 const auto &NameMsg
= FormatMessagePath(ArcName
.c_str(), false);
176 const char *MsgItems
[] = {GetMsg(MError
), NameMsg
.c_str(), ErrMsg
.c_str(), GetMsg(MOk
)};
177 Info
.Message(Info
.ModuleNumber
, FMSG_WARNING
, NULL
, MsgItems
, ARRAYSIZE(MsgItems
), 1);
185 void ArcCommand::DeleteBraces(std::string
&Command
)
187 std::string CheckStr
;
188 for (size_t left
= std::string::npos
;;) {
189 size_t right
= Command
.rfind('}', left
);
190 if (right
== std::string::npos
|| right
== 0)
193 left
= Command
.rfind('{', right
- 1);
194 if (left
== std::string::npos
)
197 bool NonEmptyVar
= false;
198 for (size_t i
= left
+ 1; i
+ 2 < right
; ++i
) {
199 if (Command
[i
] == '%' && Command
[i
+ 1] == '%' && strchr("FfLl", Command
[i
+ 2]) != NULL
) {
200 NonEmptyVar
= (ItemsNumber
> 0);
203 CheckStr
.assign(Command
.c_str() + i
, std::min((size_t)4, right
- i
));
204 if (ReplaceVar(CheckStr
) && CheckStr
.size() > 0) {
212 Command
[right
] = ' ';
214 Command
.erase(left
, right
- left
+ 1);
219 static void CutToPathOrSpace(std::string
&Path
)
221 size_t slash
= Path
.rfind(GOOD_SLASH
);
222 if (slash
!= std::string::npos
)
228 int ArcCommand::ReplaceVar(std::string
&Command
)
230 int MaxNamesLength
= 0x10000;
231 std::string LocalAllFilesMask
= AllFilesMask
;
232 bool UseSlash
= false;
233 bool FolderMask
= false;
234 bool FolderName
= false;
235 bool NameOnly
= false;
236 bool PathOnly
= false;
239 if (Command
.size() < 3)
242 char Chr
= Command
[2] & (~0x20);
243 if (Command
[0] != '%' || Command
[1] != '%' || Chr
< 'A' || Chr
> 'Z')
246 size_t VarLength
= 3;
248 while (VarLength
< Command
.size()) {
249 bool BreakScan
= false;
250 Chr
= Command
[VarLength
];
251 if (Command
[2] == 'F' && Chr
>= '0' && Chr
<= '9') {
252 MaxNamesLength
= FSF
.atoi(Command
.c_str() + VarLength
);
253 while (Chr
>= '0' && Chr
<= '9' && VarLength
< Command
.size())
254 Chr
= Command
[++VarLength
];
257 if (Command
[2] == 'E' && Chr
>= '0' && Chr
<= '9') {
258 MaxAllowedExitCode
= FSF
.atoi(Command
.c_str() + VarLength
);
259 while (Chr
>= '0' && Chr
<= '9' && VarLength
< Command
.size())
260 Chr
= Command
[++VarLength
];
263 switch (Command
[VarLength
]) {
265 break; /* deprecated AnsiCode = true; */
288 LocalAllFilesMask
= "*";
298 if ((MaxNamesLength
-= (int)Command
.size()) <= 0)
301 if (!FolderMask
&& !FolderName
)
304 /////////////////////////////////
305 switch (Command
[2]) {
306 case 'T': /* charset, if known */
308 for (int N
= 0; N
< ItemsNumber
; ++N
) {
309 const ArcItemAttributes
*Attrs
= (const ArcItemAttributes
*)PanelItem
[N
].UserData
;
310 if (Attrs
&& Attrs
->Codepage
> 0) {
311 Command
= StrPrintf("CP%u", Attrs
->Codepage
);
316 if (Command.empty() && DefaultCodepage > 0)
317 Command = StrPrintf("CP%u", DefaultCodepage);
323 case 'a': /* deprecated: short name - works same as normal name */
326 CutToPathOrSpace(Command
);
327 QuoteCmdArgIfNeed(Command
);
337 if (!MakeListFile(QuoteName
, UseSlash
, FolderName
, NameOnly
, PathOnly
, FolderMask
, LocalAllFilesMask
.c_str())) {
340 Command
= ListFileName
;
341 QuoteCmdArgIfNeed(Command
);
349 if (!CommentFileName
.empty()) {// второй раз сюда не лезем
352 if (FSF
.MkTemp(Buf
, "FAR")) {
353 CharArrayAssignToStr(CommentFileName
, Buf
);
354 if (Info
.InputBox(GetMsg(MComment
), GetMsg(MInputComment
), NULL
, "", Buf
, sizeof(Buf
), NULL
, 0)) {
355 //??тут можно и заполнить строку комментарием, но надо знать, файловый
356 //?? он или архивный. да и имя файла в архиве тоже надо знать...
357 if (WriteWholeFile(CommentFileName
.c_str(), Buf
, strnlen(Buf
, ARRAYSIZE(Buf
)))) {
358 Command
= CommentFileName
;
361 WINPORT(FlushConsoleInputBuffer
)(NULL
); // GetStdHandle(STD_INPUT_HANDLE));
367 Command
= RealArcDir
;
368 if (!Command
.empty())
372 Command
= RealArcDir
;
375 QuoteCmdArgIfNeed(Command
);
384 if (PanelItem
!= NULL
) {
385 std::string CurArcDir
= ArcDir
;
386 if (!CurArcDir
.empty() && CurArcDir
[CurArcDir
.size() - 1] != GOOD_SLASH
)
387 CurArcDir
+= GOOD_SLASH
;
389 std::string Names
, Name
;
391 if (NameNumber
== -1)
394 while (NameNumber
< ItemsNumber
|| Command
[2] == 'f') {
395 int IncreaseNumber
= 0;
397 if (!NextFileName
.empty()) {
398 Name
= PrefixFileName
;
401 NextFileName
.clear();
405 if (Command
[2] == 'f' && PrevFileNameNumber
!= -1)
406 N
= PrevFileNameNumber
;
411 if (N
>= ItemsNumber
)
414 PrefixFileName
.clear();
415 const char *cFileName
= PanelItem
[N
].FindData
.cFileName
;
416 const ArcItemAttributes
*Attrs
= (const ArcItemAttributes
*)PanelItem
[N
].UserData
;
419 PrefixFileName
= *Attrs
->Prefix
;
421 cFileName
= Attrs
->LinkName
->c_str();
424 Name
= PrefixFileName
;
425 if (*cFileName
!= GOOD_SLASH
) {
429 Name
+= cFileName
+ 1;
431 FileAttr
= PanelItem
[N
].FindData
.dwFileAttributes
;
432 PrevFileNameNumber
= N
;
435 size_t slash
= Name
.rfind(GOOD_SLASH
);
436 if (slash
!= std::string::npos
)
437 Name
.erase(0, slash
+ 1);
440 CutToPathOrSpace(Name
);
442 || (int(Names
.size() + Name
.size()) < MaxNamesLength
&& Command
[2] != 'f')) {
443 NameNumber
+= IncreaseNumber
;
444 if (FileAttr
& FILE_ATTRIBUTE_DIRECTORY
) {
445 std::string FolderMaskName
= Name
;
447 FolderMaskName
+= GOOD_SLASH
;
448 FolderMaskName
+= LocalAllFilesMask
;
450 CutToPathOrSpace(FolderMaskName
);
453 NextFileName
.swap(FolderMaskName
);
455 Name
.swap(FolderMaskName
);
460 QuoteCmdArgIfNeed(Name
);
461 else if (QuoteName
== 2)
481 int ArcCommand::MakeListFile(int QuoteName
, int UseSlash
, int FolderName
, int NameOnly
,
482 int PathOnly
, int FolderMask
, const char *LocalAllFilesMask
)
485 HANDLE ListFile
= INVALID_HANDLE_VALUE
;
487 /*SECURITY_ATTRIBUTES sa;
489 sa.nLength=sizeof(sa);
490 sa.lpSecurityDescriptor=NULL;
491 sa.bInheritHandle=TRUE; //WTF???
493 char TmpListFileName
[MAX_PATH
+ 1] = {0};
494 if (FSF
.MkTemp(TmpListFileName
, "FAR") == NULL
495 || (ListFile
= WINPORT(CreateFile
)(MB2Wide(TmpListFileName
).c_str(), GENERIC_WRITE
,
496 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, //&sa
497 FILE_FLAG_SEQUENTIAL_SCAN
, NULL
)) == INVALID_HANDLE_VALUE
)
500 const auto &MsgListFileName
= FormatMessagePath(TmpListFileName
, false);
501 const char *MsgItems
[] = {GetMsg(MError
), GetMsg(MCannotCreateListFile
), MsgListFileName
.c_str(), GetMsg(MOk
)};
502 Info
.Message(Info
.ModuleNumber
, FMSG_WARNING
, NULL
, MsgItems
, ARRAYSIZE(MsgItems
), 1);
505 if(ListFile != INVALID_HANDLE_VALUE)
506 WINPORT(CloseHandle)(ListFile);
510 std::string CurArcDir
, FileName
, OutName
;
516 if (!CurArcDir
.empty() && CurArcDir
[CurArcDir
.size() - 1] != GOOD_SLASH
)
517 CurArcDir
+= GOOD_SLASH
;
521 for (int I
= 0; I
< ItemsNumber
; I
++) {
523 FileName
= FSF
.PointToName(PanelItem
[I
].FindData
.cFileName
);
525 FileName
.assign(PanelItem
[I
].FindData
.cFileName
,
526 FSF
.PointToName(PanelItem
[I
].FindData
.cFileName
) - PanelItem
[I
].FindData
.cFileName
);
528 FileName
= PanelItem
[I
].FindData
.cFileName
;
530 int FileAttr
= PanelItem
[I
].FindData
.dwFileAttributes
;
532 PrefixFileName
.clear();
533 const ArcItemAttributes
*Attrs
= (const ArcItemAttributes
*)PanelItem
[I
].UserData
;
536 PrefixFileName
= *Attrs
->Prefix
;
538 FileName
= *Attrs
->LinkName
;
542 if (((FileAttr
& FILE_ATTRIBUTE_DIRECTORY
) == 0 || FolderName
)) {
544 OutName
= PrefixFileName
;
545 if (*FileName
.c_str() != '/') {
549 OutName
+= FileName
.c_str() + 1;
551 NormalizePath(OutName
);
554 QuoteCmdArgIfNeed(OutName
);
555 else if (QuoteName
== 2)
556 QuoteCmdArg(OutName
);
558 OutName
+= NATIVE_EOL
;
560 Error
= WINPORT(WriteFile
)(ListFile
, OutName
.c_str(), OutName
.size(), &WriteSize
, NULL
) == FALSE
;
561 // Error=fwrite(Buf,1,strlen(Buf),ListFile) != strlen(Buf);
563 if (!Error
&& (FileAttr
& FILE_ATTRIBUTE_DIRECTORY
) && FolderMask
) {
564 OutName
= PrefixFileName
;
568 OutName
+= LocalAllFilesMask
;
570 QuoteCmdArgIfNeed(OutName
);
571 else if (QuoteName
== 2)
572 QuoteCmdArg(OutName
);
573 OutName
+= NATIVE_EOL
;
574 Error
= WINPORT(WriteFile
)(ListFile
, OutName
.c_str(), OutName
.size(), &WriteSize
, NULL
) == FALSE
;
575 // Error=fwrite(Buf,1,strlen(Buf),ListFile) != strlen(Buf);
578 WINPORT(CloseHandle
)(ListFile
);
579 sdc_remove(TmpListFileName
);
581 const char *MsgItems
[] = {GetMsg(MError
), GetMsg(MCannotCreateListFile
), GetMsg(MOk
)};
582 Info
.Message(Info
.ModuleNumber
, FMSG_WARNING
, NULL
, MsgItems
, ARRAYSIZE(MsgItems
), 1);
588 WINPORT(CloseHandle
)(ListFile
);
589 CharArrayAssignToStr(ListFileName
, TmpListFileName
);
592 if (!WINPORT(CloseHandle)(ListFile))
594 // clearerr(ListFile);
595 WINPORT(CloseHandle)(ListFile);
596 DeleteFile(ListFileName);
599 char *MsgItems[]={GetMsg(MError),GetMsg(MCannotCreateListFile),GetMsg(MOk)};
600 Info.Message(Info.ModuleNumber,FMSG_WARNING,NULL,MsgItems,ARRAYSIZE(MsgItems),1);
608 ArcCommand::~ArcCommand() //$ AA 25.11.2001
610 /* if(CommentFile!=INVALID_HANDLE_VALUE)
611 WINPORT(CloseHandle)(CommentFile);*/
612 if (!CommentFileName
.empty())
613 sdc_remove(CommentFileName
.c_str());