ConsoleRunner.cpp 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <Common.h>
  5. #include <Exceptions.h>
  6. #include <Script.h>
  7. #include <CoreMain.h>
  8. #include <Terminal.h>
  9. #include <PuttyTools.h>
  10. #include <Consts.hpp>
  11. #include "Console.h"
  12. #include "WinInterface.h"
  13. #include "ProgParams.h"
  14. #include "TextsWin.h"
  15. #include "TextsCore.h"
  16. #include "CustomWinConfiguration.h"
  17. #include "SynchronizeController.h"
  18. #include "GUITools.h"
  19. enum { RESULT_SUCCESS = 0, RESULT_ANY_ERROR = 1 };
  20. //---------------------------------------------------------------------------
  21. #define WM_INTERUPT_IDLE (WM_WINSCP_USER + 3)
  22. #define BATCH_INPUT_TIMEOUT 10000
  23. //---------------------------------------------------------------------------
  24. #pragma package(smart_init)
  25. //---------------------------------------------------------------------------
  26. class TConsole
  27. {
  28. public:
  29. virtual __fastcall ~TConsole() {};
  30. virtual void __fastcall Print(AnsiString Str, bool FromBeginning = false) = 0;
  31. virtual bool __fastcall Input(AnsiString & Str, bool Echo, unsigned int Timer) = 0;
  32. virtual int __fastcall Choice(AnsiString Options, int Cancel, int Break,
  33. int Timeouted, unsigned int Timer) = 0;
  34. virtual bool __fastcall PendingAbort() = 0;
  35. virtual void __fastcall SetTitle(AnsiString Title) = 0;
  36. virtual bool __fastcall LimitedOutput() = 0;
  37. virtual void __fastcall WaitBeforeExit() = 0;
  38. };
  39. //---------------------------------------------------------------------------
  40. class TOwnConsole : public TConsole
  41. {
  42. public:
  43. static TOwnConsole * __fastcall Instance();
  44. virtual void __fastcall Print(AnsiString Str, bool FromBeginning = false);
  45. virtual bool __fastcall Input(AnsiString & Str, bool Echo, unsigned int Timer);
  46. virtual int __fastcall Choice(AnsiString Options, int Cancel, int Break,
  47. int Timeouted, unsigned int Timer);
  48. virtual bool __fastcall PendingAbort();
  49. virtual void __fastcall SetTitle(AnsiString Title);
  50. virtual bool __fastcall LimitedOutput();
  51. virtual void __fastcall WaitBeforeExit();
  52. protected:
  53. static TOwnConsole * FInstance;
  54. __fastcall TOwnConsole();
  55. virtual __fastcall ~TOwnConsole();
  56. void __fastcall BreakInput();
  57. static BOOL WINAPI HandlerRoutine(DWORD CtrlType);
  58. static int __fastcall InputTimerThreadProc(void * Parameter);
  59. private:
  60. HANDLE FInput;
  61. HANDLE FOutput;
  62. HANDLE FInputTimerEvent;
  63. static TCriticalSection FSection;
  64. bool FPendingAbort;
  65. };
  66. //---------------------------------------------------------------------------
  67. TOwnConsole * TOwnConsole::FInstance = NULL;
  68. TCriticalSection TOwnConsole::FSection;
  69. //---------------------------------------------------------------------------
  70. __fastcall TOwnConsole::TOwnConsole()
  71. {
  72. assert(FInstance == NULL);
  73. FInstance = this;
  74. AllocConsole();
  75. SetConsoleCtrlHandler(HandlerRoutine, true);
  76. FInput = GetStdHandle(STD_INPUT_HANDLE);
  77. FOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  78. FInputTimerEvent = NULL;
  79. FPendingAbort = false;
  80. }
  81. //---------------------------------------------------------------------------
  82. __fastcall TOwnConsole::~TOwnConsole()
  83. {
  84. TGuard Guard(&FSection);
  85. // deliberatelly do not remove ConsoleCtrlHandler as it causes
  86. // failures while exiting
  87. FreeConsole();
  88. assert(FInstance == this);
  89. FInstance = NULL;
  90. }
  91. //---------------------------------------------------------------------------
  92. TOwnConsole * __fastcall TOwnConsole::Instance()
  93. {
  94. return new TOwnConsole();
  95. }
  96. //---------------------------------------------------------------------------
  97. void __fastcall TOwnConsole::BreakInput()
  98. {
  99. FlushConsoleInputBuffer(FInput);
  100. INPUT_RECORD InputRecord;
  101. memset(&InputRecord, 0, sizeof(InputRecord));
  102. InputRecord.EventType = KEY_EVENT;
  103. InputRecord.Event.KeyEvent.bKeyDown = true;
  104. InputRecord.Event.KeyEvent.wRepeatCount = 1;
  105. InputRecord.Event.KeyEvent.uChar.AsciiChar = '\r';
  106. unsigned long Written;
  107. // this assertion occasionally fails (when console is being exited)
  108. CHECK(WriteConsoleInput(FInput, &InputRecord, 1, &Written));
  109. assert(Written == 1);
  110. FPendingAbort = true;
  111. PostMessage(Application->Handle, WM_INTERUPT_IDLE, 0, 0);
  112. }
  113. //---------------------------------------------------------------------------
  114. BOOL WINAPI TOwnConsole::HandlerRoutine(DWORD CtrlType)
  115. {
  116. if ((CtrlType == CTRL_C_EVENT) || (CtrlType == CTRL_BREAK_EVENT))
  117. {
  118. {
  119. TGuard Guard(&FSection);
  120. // just to be real thread-safe
  121. if (FInstance != NULL)
  122. {
  123. FInstance->BreakInput();
  124. }
  125. }
  126. return true;
  127. }
  128. else
  129. {
  130. return false;
  131. }
  132. }
  133. //---------------------------------------------------------------------------
  134. int __fastcall TOwnConsole::InputTimerThreadProc(void * Parameter)
  135. {
  136. assert(FInstance != NULL);
  137. unsigned int Timer = reinterpret_cast<unsigned int>(Parameter);
  138. if (WaitForSingleObject(FInstance->FInputTimerEvent, Timer) == WAIT_TIMEOUT)
  139. {
  140. FInstance->BreakInput();
  141. }
  142. return 0;
  143. }
  144. //---------------------------------------------------------------------------
  145. bool __fastcall TOwnConsole::PendingAbort()
  146. {
  147. if (FPendingAbort)
  148. {
  149. FPendingAbort = false;
  150. return true;
  151. }
  152. else
  153. {
  154. return FPendingAbort;
  155. }
  156. }
  157. //---------------------------------------------------------------------------
  158. void __fastcall TOwnConsole::Print(AnsiString Str, bool FromBeginning)
  159. {
  160. if (FromBeginning)
  161. {
  162. CONSOLE_SCREEN_BUFFER_INFO BufferInfo;
  163. GetConsoleScreenBufferInfo(FOutput, &BufferInfo);
  164. BufferInfo.dwCursorPosition.X = 0;
  165. SetConsoleCursorPosition(FOutput, BufferInfo.dwCursorPosition);
  166. }
  167. unsigned long Written;
  168. AnsiToOem(Str);
  169. bool Result = WriteConsole(FOutput, Str.c_str(), Str.Length(), &Written, NULL);
  170. assert(Result);
  171. USEDPARAM(Result);
  172. assert(Str.Length() == static_cast<long>(Written));
  173. }
  174. //---------------------------------------------------------------------------
  175. bool __fastcall TOwnConsole::Input(AnsiString & Str, bool Echo, unsigned int Timer)
  176. {
  177. unsigned long PrevMode, NewMode;
  178. GetConsoleMode(FInput, &PrevMode);
  179. NewMode = PrevMode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
  180. if (Echo)
  181. {
  182. NewMode |= ENABLE_ECHO_INPUT;
  183. }
  184. else
  185. {
  186. NewMode &= ~ENABLE_ECHO_INPUT;
  187. }
  188. SetConsoleMode(FInput, NewMode);
  189. HANDLE InputTimerThread = NULL;
  190. bool Result;
  191. try
  192. {
  193. if (Timer > 0)
  194. {
  195. unsigned int ThreadId;
  196. assert(FInputTimerEvent == NULL);
  197. FInputTimerEvent = CreateEvent(NULL, false, false, NULL);
  198. InputTimerThread = (HANDLE)BeginThread(NULL, 0, InputTimerThreadProc,
  199. reinterpret_cast<void *>(Timer), 0, ThreadId);
  200. }
  201. unsigned long Read;
  202. Str.SetLength(10240);
  203. Result = ReadConsole(FInput, Str.c_str(), Str.Length(), &Read, NULL);
  204. assert(Result);
  205. Str.SetLength(Read);
  206. OemToAnsi(Str);
  207. if (FPendingAbort || !Echo)
  208. {
  209. Print("\n");
  210. }
  211. if (FPendingAbort || (Read == 0))
  212. {
  213. Result = false;
  214. FPendingAbort = false;
  215. }
  216. }
  217. __finally
  218. {
  219. if (InputTimerThread != NULL)
  220. {
  221. SetEvent(FInputTimerEvent);
  222. WaitForSingleObject(InputTimerThread, 100);
  223. CloseHandle(FInputTimerEvent);
  224. FInputTimerEvent = NULL;
  225. CloseHandle(InputTimerThread);
  226. }
  227. SetConsoleMode(FInput, PrevMode);
  228. }
  229. return Result;
  230. }
  231. //---------------------------------------------------------------------------
  232. int __fastcall TOwnConsole::Choice(AnsiString Options, int Cancel, int Break,
  233. int Timeouted, unsigned int Timer)
  234. {
  235. AnsiToOem(Options);
  236. unsigned int ATimer = Timer;
  237. int Result = 0;
  238. unsigned long PrevMode, NewMode;
  239. GetConsoleMode(FInput, &PrevMode);
  240. NewMode = (PrevMode | ENABLE_PROCESSED_INPUT) & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
  241. SetConsoleMode(FInput, NewMode);
  242. try
  243. {
  244. do
  245. {
  246. unsigned long Read;
  247. INPUT_RECORD Record;
  248. if ((PeekConsoleInput(FInput, &Record, 1, &Read) != 0) &&
  249. (Read == 1))
  250. {
  251. if ((ReadConsoleInput(FInput, &Record, 1, &Read) != 0) &&
  252. (Read == 1))
  253. {
  254. if (PendingAbort())
  255. {
  256. Result = Break;
  257. }
  258. else if ((Record.EventType == KEY_EVENT) &&
  259. Record.Event.KeyEvent.bKeyDown)
  260. {
  261. char C = AnsiUpperCase(Record.Event.KeyEvent.uChar.AsciiChar)[1];
  262. if (C == 27)
  263. {
  264. Result = Cancel;
  265. }
  266. else if ((Options.Pos(C) > 0) &&
  267. FLAGCLEAR(Record.Event.KeyEvent.dwControlKeyState,
  268. LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED |
  269. RIGHT_CTRL_PRESSED))
  270. {
  271. Result = Options.Pos(C);
  272. }
  273. }
  274. }
  275. }
  276. if (Result == 0)
  277. {
  278. unsigned int TimerSlice = 50;
  279. Sleep(TimerSlice);
  280. if (Timer > 0)
  281. {
  282. if (ATimer > TimerSlice)
  283. {
  284. ATimer -= TimerSlice;
  285. }
  286. else
  287. {
  288. Result = Timeouted;
  289. }
  290. }
  291. }
  292. }
  293. while (Result == 0);
  294. }
  295. __finally
  296. {
  297. SetConsoleMode(FInput, PrevMode);
  298. }
  299. return Result;
  300. }
  301. //---------------------------------------------------------------------------
  302. void __fastcall TOwnConsole::SetTitle(AnsiString Title)
  303. {
  304. AnsiToOem(Title);
  305. SetConsoleTitle(Title.c_str());
  306. }
  307. //---------------------------------------------------------------------------
  308. bool __fastcall TOwnConsole::LimitedOutput()
  309. {
  310. return true;
  311. }
  312. //---------------------------------------------------------------------------
  313. void __fastcall TOwnConsole::WaitBeforeExit()
  314. {
  315. unsigned long Read;
  316. INPUT_RECORD Record;
  317. while (ReadConsoleInput(FInput, &Record, 1, &Read))
  318. {
  319. if ((Read == 1) && (Record.EventType == KEY_EVENT) &&
  320. (Record.Event.KeyEvent.uChar.AsciiChar != 0) &&
  321. Record.Event.KeyEvent.bKeyDown)
  322. {
  323. break;
  324. }
  325. }
  326. }
  327. //---------------------------------------------------------------------------
  328. class TExternalConsole : public TConsole
  329. {
  330. public:
  331. __fastcall TExternalConsole(const AnsiString Instance);
  332. virtual __fastcall ~TExternalConsole();
  333. virtual void __fastcall Print(AnsiString Str, bool FromBeginning = false);
  334. virtual bool __fastcall Input(AnsiString & Str, bool Echo, unsigned int Timer);
  335. virtual int __fastcall Choice(AnsiString Options, int Cancel, int Break,
  336. int Timeouted, unsigned int Timer);
  337. virtual bool __fastcall PendingAbort();
  338. virtual void __fastcall SetTitle(AnsiString Title);
  339. virtual bool __fastcall LimitedOutput();
  340. virtual void __fastcall WaitBeforeExit();
  341. private:
  342. bool FPendingAbort;
  343. HANDLE FRequestEvent;
  344. HANDLE FResponseEvent;
  345. HANDLE FCancelEvent;
  346. HANDLE FFileMapping;
  347. bool FLimitedOutput;
  348. static const int PrintTimeout = 5000;
  349. inline TConsoleCommStruct * __fastcall GetCommStruct();
  350. inline void __fastcall FreeCommStruct(TConsoleCommStruct * CommStruct);
  351. inline void __fastcall SendEvent(int Timeout);
  352. void __fastcall Init();
  353. };
  354. //---------------------------------------------------------------------------
  355. __fastcall TExternalConsole::TExternalConsole(const AnsiString Instance)
  356. {
  357. FRequestEvent = OpenEvent(EVENT_ALL_ACCESS, false,
  358. FORMAT("%s%s", (CONSOLE_EVENT_REQUEST, (Instance))).c_str());
  359. FResponseEvent = OpenEvent(EVENT_ALL_ACCESS, false,
  360. FORMAT("%s%s", (CONSOLE_EVENT_RESPONSE, (Instance))).c_str());
  361. FCancelEvent = OpenEvent(EVENT_ALL_ACCESS, false,
  362. FORMAT("%s%s", (CONSOLE_EVENT_CANCEL, (Instance))).c_str());
  363. FFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
  364. FORMAT("%s%s", (CONSOLE_MAPPING, (Instance))).c_str());
  365. if ((FRequestEvent == NULL) || (FResponseEvent == NULL) || (FFileMapping == NULL))
  366. {
  367. throw Exception(LoadStr(EXTERNAL_CONSOLE_INIT_ERROR));
  368. }
  369. HANDLE Kernel32 = GetModuleHandle(kernel32);
  370. typedef HANDLE (* TOpenJobObject)(DWORD DesiredAccess, BOOL InheritHandles, LPCTSTR Name);
  371. typedef HANDLE (* TAssignProcessToJobObject)(HANDLE Job, HANDLE Process);
  372. TOpenJobObject OpenJobObject =
  373. (TOpenJobObject)GetProcAddress(Kernel32, "OpenJobObjectA");
  374. TAssignProcessToJobObject AssignProcessToJobObject =
  375. (TAssignProcessToJobObject)GetProcAddress(Kernel32, "AssignProcessToJobObject");
  376. if ((OpenJobObject != NULL) && (AssignProcessToJobObject != NULL))
  377. {
  378. HANDLE Job = OpenJobObject(JOB_OBJECT_ASSIGN_PROCESS, FALSE,
  379. FORMAT("%s%s", (CONSOLE_JOB, Instance)).c_str());
  380. if (Job != NULL)
  381. {
  382. AssignProcessToJobObject(Job, GetCurrentProcess());
  383. // winscp.com keeps the only reference to the job.
  384. // once it gets closed (because winscp.com if forcefully terminated),
  385. // we get terminated as well
  386. CloseHandle(Job);
  387. }
  388. }
  389. TConsoleCommStruct * CommStruct = GetCommStruct();
  390. try
  391. {
  392. if (CommStruct->Version != TConsoleCommStruct::CurrentVersion)
  393. {
  394. throw Exception(FMTLOAD(EXTERNAL_CONSOLE_INCOMPATIBLE, (CommStruct->CurrentVersion)));
  395. }
  396. CommStruct->Version = TConsoleCommStruct::CurrentVersionConfirmed;
  397. }
  398. __finally
  399. {
  400. FreeCommStruct(CommStruct);
  401. }
  402. // to break application event loop regularly during "watching for changes"
  403. // to allow user to abort it
  404. SetTimer(Application->Handle, 1, 500, NULL);
  405. Init();
  406. }
  407. //---------------------------------------------------------------------------
  408. __fastcall TExternalConsole::~TExternalConsole()
  409. {
  410. CloseHandle(FRequestEvent);
  411. CloseHandle(FResponseEvent);
  412. CloseHandle(FCancelEvent);
  413. CloseHandle(FFileMapping);
  414. KillTimer(Application->Handle, 1);
  415. }
  416. //---------------------------------------------------------------------------
  417. TConsoleCommStruct * __fastcall TExternalConsole::GetCommStruct()
  418. {
  419. TConsoleCommStruct * Result;
  420. Result = static_cast<TConsoleCommStruct*>(MapViewOfFile(FFileMapping,
  421. FILE_MAP_ALL_ACCESS, 0, 0, 0));
  422. if (Result == NULL)
  423. {
  424. throw Exception(LoadStr(CONSOLE_COMM_ERROR));
  425. }
  426. return Result;
  427. }
  428. //---------------------------------------------------------------------------
  429. void __fastcall TExternalConsole::FreeCommStruct(TConsoleCommStruct * CommStruct)
  430. {
  431. UnmapViewOfFile(CommStruct);
  432. }
  433. //---------------------------------------------------------------------------
  434. void __fastcall TExternalConsole::SendEvent(int Timeout)
  435. {
  436. SetEvent(FRequestEvent);
  437. if (WaitForSingleObject(FResponseEvent, Timeout) != WAIT_OBJECT_0)
  438. {
  439. throw Exception(LoadStr(CONSOLE_SEND_TIMEOUT));
  440. }
  441. }
  442. //---------------------------------------------------------------------------
  443. void __fastcall TExternalConsole::Print(AnsiString Str, bool FromBeginning)
  444. {
  445. TConsoleCommStruct * CommStruct = GetCommStruct();
  446. try
  447. {
  448. if (Str.Length() >= sizeof(CommStruct->PrintEvent.Message))
  449. {
  450. throw Exception(FMTLOAD(CONSOLE_PRINT_TOO_LONG, (Str.Length())));
  451. }
  452. CommStruct->Event = TConsoleCommStruct::PRINT;
  453. CharToOem(Str.c_str(), CommStruct->PrintEvent.Message);
  454. CommStruct->PrintEvent.FromBeginning = FromBeginning;
  455. }
  456. __finally
  457. {
  458. FreeCommStruct(CommStruct);
  459. }
  460. SendEvent(PrintTimeout);
  461. }
  462. //---------------------------------------------------------------------------
  463. bool __fastcall TExternalConsole::Input(AnsiString & Str, bool Echo, unsigned int Timer)
  464. {
  465. TConsoleCommStruct * CommStruct = GetCommStruct();
  466. try
  467. {
  468. CommStruct->Event = TConsoleCommStruct::INPUT;
  469. CommStruct->InputEvent.Echo = Echo;
  470. CommStruct->InputEvent.Result = false;
  471. CommStruct->InputEvent.Str[0] = '\0';
  472. CommStruct->InputEvent.Timer = Timer;
  473. }
  474. __finally
  475. {
  476. FreeCommStruct(CommStruct);
  477. }
  478. SendEvent(INFINITE);
  479. bool Result;
  480. CommStruct = GetCommStruct();
  481. try
  482. {
  483. Result = CommStruct->InputEvent.Result;
  484. Str.SetLength(strlen(CommStruct->InputEvent.Str));
  485. OemToChar(CommStruct->InputEvent.Str, Str.c_str());
  486. }
  487. __finally
  488. {
  489. FreeCommStruct(CommStruct);
  490. }
  491. return Result;
  492. }
  493. //---------------------------------------------------------------------------
  494. int __fastcall TExternalConsole::Choice(AnsiString Options, int Cancel, int Break,
  495. int Timeouted, unsigned int Timer)
  496. {
  497. TConsoleCommStruct * CommStruct = GetCommStruct();
  498. try
  499. {
  500. CommStruct->Event = TConsoleCommStruct::CHOICE;
  501. assert(Options.Length() < sizeof(CommStruct->ChoiceEvent.Options));
  502. CharToOem(Options.c_str(), CommStruct->ChoiceEvent.Options);
  503. CommStruct->ChoiceEvent.Cancel = Cancel;
  504. CommStruct->ChoiceEvent.Break = Break;
  505. CommStruct->ChoiceEvent.Result = Break;
  506. CommStruct->ChoiceEvent.Timeouted = Timeouted;
  507. CommStruct->ChoiceEvent.Timer = Timer;
  508. }
  509. __finally
  510. {
  511. FreeCommStruct(CommStruct);
  512. }
  513. SendEvent(INFINITE);
  514. int Result;
  515. CommStruct = GetCommStruct();
  516. try
  517. {
  518. Result = CommStruct->ChoiceEvent.Result;
  519. }
  520. __finally
  521. {
  522. FreeCommStruct(CommStruct);
  523. }
  524. return Result;
  525. }
  526. //---------------------------------------------------------------------------
  527. bool __fastcall TExternalConsole::PendingAbort()
  528. {
  529. return (WaitForSingleObject(FCancelEvent, 0) == WAIT_OBJECT_0);
  530. }
  531. //---------------------------------------------------------------------------
  532. void __fastcall TExternalConsole::SetTitle(AnsiString Title)
  533. {
  534. TConsoleCommStruct * CommStruct = GetCommStruct();
  535. try
  536. {
  537. if (Title.Length() >= sizeof(CommStruct->TitleEvent.Title))
  538. {
  539. throw Exception(FMTLOAD(CONSOLE_PRINT_TOO_LONG, (Title.Length())));
  540. }
  541. CommStruct->Event = TConsoleCommStruct::TITLE;
  542. CharToOem(Title.c_str(), CommStruct->TitleEvent.Title);
  543. }
  544. __finally
  545. {
  546. FreeCommStruct(CommStruct);
  547. }
  548. SendEvent(INFINITE);
  549. }
  550. //---------------------------------------------------------------------------
  551. void __fastcall TExternalConsole::Init()
  552. {
  553. TConsoleCommStruct * CommStruct = GetCommStruct();
  554. try
  555. {
  556. CommStruct->Event = TConsoleCommStruct::INIT;
  557. }
  558. __finally
  559. {
  560. FreeCommStruct(CommStruct);
  561. }
  562. SendEvent(INFINITE);
  563. CommStruct = GetCommStruct();
  564. try
  565. {
  566. FLimitedOutput = (CommStruct->InitEvent.OutputType == FILE_TYPE_CHAR);
  567. }
  568. __finally
  569. {
  570. FreeCommStruct(CommStruct);
  571. }
  572. }
  573. //---------------------------------------------------------------------------
  574. bool __fastcall TExternalConsole::LimitedOutput()
  575. {
  576. return FLimitedOutput;
  577. }
  578. //---------------------------------------------------------------------------
  579. void __fastcall TExternalConsole::WaitBeforeExit()
  580. {
  581. // noop
  582. }
  583. //---------------------------------------------------------------------------
  584. class TNullConsole : public TConsole
  585. {
  586. public:
  587. __fastcall TNullConsole();
  588. virtual void __fastcall Print(AnsiString Str, bool FromBeginning = false);
  589. virtual bool __fastcall Input(AnsiString & Str, bool Echo, unsigned int Timer);
  590. virtual int __fastcall Choice(AnsiString Options, int Cancel, int Break,
  591. int Timeouted, unsigned int Timer);
  592. virtual bool __fastcall PendingAbort();
  593. virtual void __fastcall SetTitle(AnsiString Title);
  594. virtual bool __fastcall LimitedOutput();
  595. virtual void __fastcall WaitBeforeExit();
  596. };
  597. //---------------------------------------------------------------------------
  598. __fastcall TNullConsole::TNullConsole()
  599. {
  600. }
  601. //---------------------------------------------------------------------------
  602. void __fastcall TNullConsole::Print(AnsiString /*Str*/, bool /*FromBeginning*/)
  603. {
  604. // noop
  605. }
  606. //---------------------------------------------------------------------------
  607. bool __fastcall TNullConsole::Input(AnsiString & /*Str*/, bool /*Echo*/,
  608. unsigned int /*Timer*/)
  609. {
  610. return false;
  611. }
  612. //---------------------------------------------------------------------------
  613. int __fastcall TNullConsole::Choice(AnsiString /*Options*/, int /*Cancel*/,
  614. int Break, int /*Timeouted*/, unsigned int /*Timer*/)
  615. {
  616. return Break;
  617. }
  618. //---------------------------------------------------------------------------
  619. bool __fastcall TNullConsole::PendingAbort()
  620. {
  621. return false;
  622. }
  623. //---------------------------------------------------------------------------
  624. void __fastcall TNullConsole::SetTitle(AnsiString /*Title*/)
  625. {
  626. // noop
  627. }
  628. //---------------------------------------------------------------------------
  629. bool __fastcall TNullConsole::LimitedOutput()
  630. {
  631. return false;
  632. }
  633. //---------------------------------------------------------------------------
  634. void __fastcall TNullConsole::WaitBeforeExit()
  635. {
  636. assert(false);
  637. // noop
  638. }
  639. //---------------------------------------------------------------------------
  640. class TConsoleRunner
  641. {
  642. public:
  643. TConsoleRunner(TConsole * Console);
  644. ~TConsoleRunner();
  645. int __fastcall Run(const AnsiString Session, TOptions * Options,
  646. TStrings * ScriptCommands);
  647. void __fastcall ShowException(Exception * E);
  648. protected:
  649. bool __fastcall Input(AnsiString & Str, bool Echo, unsigned int Timer);
  650. inline void __fastcall Print(const AnsiString & Str, bool FromBeginning = false);
  651. inline void __fastcall PrintLine(const AnsiString & Str);
  652. inline void __fastcall PrintMessage(const AnsiString & Str);
  653. void __fastcall UpdateTitle();
  654. inline void __fastcall NotifyAbort();
  655. inline bool __fastcall Aborted(bool AllowCompleteAbort = true);
  656. private:
  657. TManagementScript * FScript;
  658. TConsole * FConsole;
  659. TSynchronizeController FSynchronizeController;
  660. int FLastProgressLen;
  661. bool FSynchronizeAborted;
  662. bool FCommandError;
  663. bool FBatchScript;
  664. bool FAborted;
  665. TTimer * Timer;
  666. void __fastcall ScriptPrint(TScript * Script, const AnsiString Str);
  667. void __fastcall ScriptPrintProgress(TScript * Script, bool First, const AnsiString Str);
  668. void __fastcall ScriptInput(TScript * Script, const AnsiString Prompt, AnsiString & Str);
  669. void __fastcall ScriptTerminalPromptUser(TTerminal * Terminal,
  670. TPromptKind Kind, AnsiString Name, AnsiString Instructions, TStrings * Prompts,
  671. TStrings * Results, bool & Result, void * Arg);
  672. void __fastcall ScriptShowExtendedException(TTerminal * Terminal,
  673. Exception * E, void * Arg);
  674. void __fastcall ScriptTerminalQueryUser(TObject * Sender, const AnsiString Query,
  675. TStrings * MoreMessages, int Answers, const TQueryParams * Params, int & Answer,
  676. TQueryType QueryType, void * Arg);
  677. void __fastcall ScriptQueryCancel(TScript * Script, bool & Cancel);
  678. void __fastcall SynchronizeControllerAbort(TObject * Sender, bool Close);
  679. void __fastcall SynchronizeControllerLog(TSynchronizeController * Controller,
  680. TSynchronizeLogEntry Entry, const AnsiString Message);
  681. void __fastcall ScriptSynchronizeStartStop(TScript * Script,
  682. const AnsiString LocalDirectory, const AnsiString RemoteDirectory,
  683. const TCopyParamType & CopyParam, int SynchronizeParams);
  684. void __fastcall SynchronizeControllerSynchronize(TSynchronizeController * Sender,
  685. const AnsiString LocalDirectory, const AnsiString RemoteDirectory,
  686. const TCopyParamType & CopyParam, const TSynchronizeParamType & Params,
  687. TSynchronizeChecklist ** Checklist, TSynchronizeOptions * Options, bool Full);
  688. void __fastcall SynchronizeControllerSynchronizeInvalid(TSynchronizeController * Sender,
  689. const AnsiString Directory, const AnsiString ErrorStr);
  690. void __fastcall SynchronizeControllerTooManyDirectories(TSynchronizeController * Sender,
  691. int & MaxDirectories);
  692. unsigned int InputTimeout();
  693. void __fastcall TimerTimer(TObject * Sender);
  694. };
  695. //---------------------------------------------------------------------------
  696. TConsoleRunner::TConsoleRunner(TConsole * Console) :
  697. FSynchronizeController(&SynchronizeControllerSynchronize,
  698. &SynchronizeControllerSynchronizeInvalid,
  699. &SynchronizeControllerTooManyDirectories)
  700. {
  701. FConsole = Console;
  702. FLastProgressLen = 0;
  703. FScript = NULL;
  704. FAborted = false;
  705. FBatchScript = false;
  706. Timer = new TTimer(Application);
  707. Timer->OnTimer = TimerTimer;
  708. Timer->Interval = 1000;
  709. Timer->Enabled = true;
  710. }
  711. //---------------------------------------------------------------------------
  712. TConsoleRunner::~TConsoleRunner()
  713. {
  714. delete Timer;
  715. }
  716. //---------------------------------------------------------------------------
  717. void __fastcall TConsoleRunner::TimerTimer(TObject * /*Sender*/)
  718. {
  719. // sole presence of timer causes message to be dispatched,
  720. // hence breaks the loops
  721. }
  722. //---------------------------------------------------------------------------
  723. unsigned int TConsoleRunner::InputTimeout()
  724. {
  725. return (FScript->Batch != TScript::BatchOff ? BATCH_INPUT_TIMEOUT : 0);
  726. }
  727. //---------------------------------------------------------------------------
  728. void __fastcall TConsoleRunner::ScriptInput(TScript * /*Script*/,
  729. const AnsiString Prompt, AnsiString & Str)
  730. {
  731. Print(Prompt);
  732. if (!Input(Str, true, InputTimeout()))
  733. {
  734. Abort();
  735. }
  736. }
  737. //---------------------------------------------------------------------------
  738. void __fastcall TConsoleRunner::Print(const AnsiString & Str, bool FromBeginning)
  739. {
  740. if (FLastProgressLen > 0)
  741. {
  742. FConsole->Print("\n" + Str, FromBeginning);
  743. FLastProgressLen = 0;
  744. }
  745. else
  746. {
  747. FConsole->Print(Str, FromBeginning);
  748. }
  749. }
  750. //---------------------------------------------------------------------------
  751. void __fastcall TConsoleRunner::PrintLine(const AnsiString & Str)
  752. {
  753. Print(Str + "\n");
  754. }
  755. //---------------------------------------------------------------------------
  756. void __fastcall TConsoleRunner::PrintMessage(const AnsiString & Str)
  757. {
  758. PrintLine(
  759. StringReplace(StringReplace(Str.TrimRight(), "\n\n", "\n", TReplaceFlags() << rfReplaceAll),
  760. "\n \n", "\n", TReplaceFlags() << rfReplaceAll));
  761. }
  762. //---------------------------------------------------------------------------
  763. void __fastcall TConsoleRunner::NotifyAbort()
  764. {
  765. if (FBatchScript)
  766. {
  767. FAborted = true;
  768. }
  769. }
  770. //---------------------------------------------------------------------------
  771. bool __fastcall TConsoleRunner::Aborted(bool AllowCompleteAbort)
  772. {
  773. bool Result;
  774. if (FAborted)
  775. {
  776. Result = true;
  777. }
  778. else
  779. {
  780. Result = FConsole->PendingAbort();
  781. if (Result)
  782. {
  783. PrintLine(LoadStr(USER_TERMINATED));
  784. if (AllowCompleteAbort)
  785. {
  786. NotifyAbort();
  787. }
  788. }
  789. }
  790. return Result;
  791. }
  792. //---------------------------------------------------------------------------
  793. void __fastcall TConsoleRunner::ScriptPrint(TScript * /*Script*/,
  794. const AnsiString Str)
  795. {
  796. Print(Str);
  797. }
  798. //---------------------------------------------------------------------------
  799. void __fastcall TConsoleRunner::ScriptPrintProgress(TScript * /*Script*/,
  800. bool First, const AnsiString Str)
  801. {
  802. AnsiString S = Str;
  803. if (First && (FLastProgressLen > 0))
  804. {
  805. S = "\n" + S;
  806. }
  807. else if (S.Length() < FLastProgressLen)
  808. {
  809. int Padding = FLastProgressLen - S.Length();
  810. S += AnsiString::StringOfChar(' ', Padding) +
  811. AnsiString::StringOfChar('\b', Padding);
  812. }
  813. FConsole->Print(S, true);
  814. FLastProgressLen = Str.Length();
  815. }
  816. //---------------------------------------------------------------------------
  817. void __fastcall TConsoleRunner::ScriptTerminalPromptUser(TTerminal * /*Terminal*/,
  818. TPromptKind /*Kind*/, AnsiString Name, AnsiString Instructions, TStrings * Prompts,
  819. TStrings * Results, bool & Result, void * /*Arg*/)
  820. {
  821. if (!Instructions.IsEmpty())
  822. {
  823. PrintLine(Instructions);
  824. }
  825. for (int Index = 0; Index < Prompts->Count; Index++)
  826. {
  827. AnsiString Prompt = Prompts->Strings[Index];
  828. if (!Prompt.IsEmpty() && (Prompt[Prompt.Length()] != ' '))
  829. {
  830. Prompt += ' ';
  831. }
  832. int P = Prompt.Pos('&');
  833. if (P > 0)
  834. {
  835. Prompt.Delete(P, 1);
  836. }
  837. Print(Prompt);
  838. AnsiString AResult = Results->Strings[Index]; // useless
  839. Result = Input(AResult, bool(Prompts->Objects[Index]), InputTimeout());
  840. Results->Strings[Index] = AResult;
  841. }
  842. }
  843. //---------------------------------------------------------------------------
  844. void __fastcall TConsoleRunner::ScriptShowExtendedException(
  845. TTerminal * /*Terminal*/, Exception * E, void * /*Arg*/)
  846. {
  847. ShowException(E);
  848. }
  849. //---------------------------------------------------------------------------
  850. void __fastcall TConsoleRunner::ScriptTerminalQueryUser(TObject * /*Sender*/,
  851. const AnsiString Query, TStrings * MoreMessages, int Answers,
  852. const TQueryParams * Params, int & Answer, TQueryType /*QueryType*/,
  853. void * /*Arg*/)
  854. {
  855. AnsiString AQuery = Query;
  856. unsigned int Timer = 0;
  857. unsigned int Timeout = 0;
  858. int TimeoutA = 0;
  859. if (Params != NULL)
  860. {
  861. if (Params->Timeout > 0)
  862. {
  863. assert(Params->Timer == 0);
  864. Timeout = Params->Timeout;
  865. TimeoutA = Params->TimeoutAnswer;
  866. }
  867. if (Params->Timer > 0)
  868. {
  869. assert(Params->Timeout == 0);
  870. Timer = Params->Timer;
  871. if (Params->TimerAnswers > 0)
  872. {
  873. Answers = Params->TimerAnswers;
  874. }
  875. if (!Params->TimerMessage.IsEmpty())
  876. {
  877. AQuery = Params->TimerMessage;
  878. }
  879. }
  880. if (FLAGSET(Params->Params, qpFatalAbort))
  881. {
  882. AQuery = FMTLOAD(WARN_FATAL_ERROR, (AQuery));
  883. }
  884. }
  885. int AAnswers = Answers;
  886. PrintMessage(AQuery);
  887. if ((MoreMessages != NULL) && (MoreMessages->Count > 0))
  888. {
  889. PrintMessage(MoreMessages->Text);
  890. }
  891. static const int MaxButtonCount = 15;
  892. int Buttons[MaxButtonCount];
  893. AnsiString Captions[MaxButtonCount];
  894. TNotifyEvent OnClicks[MaxButtonCount];
  895. int ButtonCount = 0;
  896. #define ADD_BUTTON(TYPE, CAPTION) \
  897. if (FLAGSET(AAnswers, qa ## TYPE)) \
  898. { \
  899. assert(ButtonCount < MaxButtonCount); \
  900. Captions[ButtonCount] = CAPTION; \
  901. Buttons[ButtonCount] = qa ## TYPE; \
  902. OnClicks[ButtonCount] = NULL; \
  903. ButtonCount++; \
  904. AAnswers -= qa ## TYPE; \
  905. }
  906. #define ADD_BUTTON_RES(TYPE) ADD_BUTTON(TYPE, LoadResourceString(&_SMsgDlg ## TYPE));
  907. ADD_BUTTON_RES(Yes);
  908. ADD_BUTTON_RES(No);
  909. ADD_BUTTON_RES(OK);
  910. ADD_BUTTON_RES(Cancel);
  911. ADD_BUTTON_RES(Abort);
  912. ADD_BUTTON_RES(Retry);
  913. ADD_BUTTON_RES(Ignore);
  914. // to keep the same order as for GUI message box
  915. ADD_BUTTON(Skip, LoadStr(SKIP_BUTTON));
  916. ADD_BUTTON_RES(All);
  917. ADD_BUTTON_RES(NoToAll);
  918. ADD_BUTTON_RES(YesToAll);
  919. ADD_BUTTON_RES(Help);
  920. #undef ADD_BUTTON_RES
  921. #undef ADD_BUTTON
  922. USEDPARAM(AAnswers);
  923. assert(AAnswers == 0);
  924. assert(ButtonCount > 0);
  925. if ((Params != NULL) && (Params->Aliases != NULL))
  926. {
  927. for (int bi = 0; bi < ButtonCount; bi++)
  928. {
  929. for (unsigned int ai = 0; ai < Params->AliasesCount; ai++)
  930. {
  931. if (static_cast<int>(Params->Aliases[ai].Button) == Buttons[bi])
  932. {
  933. Captions[bi] = Params->Aliases[ai].Alias;
  934. OnClicks[bi] = Params->Aliases[ai].OnClick;
  935. break;
  936. }
  937. }
  938. }
  939. }
  940. AnsiString Accels;
  941. for (int Index = 0; Index < ButtonCount; Index++)
  942. {
  943. AnsiString & Caption = Captions[Index];
  944. int P = Caption.Pos('&');
  945. if ((P > 0) && (P < Caption.Length()))
  946. {
  947. char Accel = AnsiUpperCase(Caption)[P + 1];
  948. if (Accels.Pos(Accel) > 0)
  949. {
  950. Caption.Delete(P, 1);
  951. Accels += ' ';
  952. }
  953. else
  954. {
  955. Accels += Accel;
  956. }
  957. }
  958. else
  959. {
  960. Accels += ' ';
  961. }
  962. }
  963. assert(Accels.Length() == ButtonCount);
  964. int NumberAccel = 0;
  965. int CancelA = CancelAnswer(Answers);
  966. int CancelIndex;
  967. int AbortA = AbortAnswer(Answers);
  968. int AbortIndex;
  969. int ContinueA = ContinueAnswer(Answers);
  970. int ContinueIndex;
  971. int TimeoutIndex = 0;
  972. for (int Index = 0; Index < ButtonCount; Index++)
  973. {
  974. AnsiString & Caption = Captions[Index];
  975. if (Accels[Index + 1] == ' ')
  976. {
  977. for (int Index2 = 1; Index2 <= Caption.Length(); Index2++)
  978. {
  979. char C = AnsiUpperCase(Caption)[Index2];
  980. if ((C >= 'A') && (C <= 'Z') && (Accels.Pos(C) == 0))
  981. {
  982. Caption.Insert("&", Index2);
  983. Accels[Index + 1] = C;
  984. break;
  985. }
  986. }
  987. }
  988. if (Accels[Index + 1] == ' ')
  989. {
  990. for (int Index2 = 1; Index2 <= Caption.Length(); Index2++)
  991. {
  992. char C = AnsiUpperCase(Caption)[Index2];
  993. if ((C != ' ') && (Accels.Pos(C) == 0))
  994. {
  995. Caption.Insert("&", Index2);
  996. Accels[Index + 1] = C;
  997. break;
  998. }
  999. }
  1000. }
  1001. if (Accels[Index + 1] == ' ')
  1002. {
  1003. NumberAccel++;
  1004. assert(NumberAccel <= 9);
  1005. Caption = FORMAT("&%d%s", (NumberAccel, Caption));
  1006. Accels[Index + 1] = Caption[2];
  1007. }
  1008. if (Buttons[Index] == CancelA)
  1009. {
  1010. CancelIndex = Index + 1;
  1011. }
  1012. if (Buttons[Index] == AbortA)
  1013. {
  1014. AbortIndex = Index + 1;
  1015. }
  1016. if (Buttons[Index] == ContinueA)
  1017. {
  1018. ContinueIndex = Index + 1;
  1019. }
  1020. if (Buttons[Index] == ContinueA)
  1021. {
  1022. ContinueIndex = Index + 1;
  1023. }
  1024. if (Buttons[Index] == TimeoutA)
  1025. {
  1026. TimeoutIndex = Index + 1;
  1027. }
  1028. }
  1029. assert(Accels.Pos(' ') == 0);
  1030. bool Timeouting = (Timeout > 0);
  1031. do
  1032. {
  1033. Answer = 0;
  1034. int AnswerIndex;
  1035. bool Retry;
  1036. do
  1037. {
  1038. Retry = false;
  1039. AnsiString Output;
  1040. for (int i = 0; i < ButtonCount; i++)
  1041. {
  1042. if (i > 0)
  1043. {
  1044. Output += ", ";
  1045. }
  1046. AnsiString Caption = Captions[i];
  1047. int P = Caption.Pos('&');
  1048. assert(P >= 0);
  1049. Caption[P] = '(';
  1050. Caption.Insert(")", P + 2);
  1051. if (i + 1 == TimeoutIndex)
  1052. {
  1053. assert(Timeouting);
  1054. Caption = FMTLOAD(TIMEOUT_BUTTON, (Caption, int(Timeout / 1000)));
  1055. }
  1056. Output += Caption;
  1057. }
  1058. Output += ": ";
  1059. // note that length of string may decrease over time due to number of
  1060. // seconds length, but supposing it decreases by one character at time
  1061. // at most, we do not mind as the prompt is terminated with space
  1062. Print(Output, true);
  1063. if (!Timeouting && (FScript->Batch == TScript::BatchContinue))
  1064. {
  1065. AnswerIndex = ContinueIndex;
  1066. }
  1067. else if (!Timeouting && (FScript->Batch != TScript::BatchOff))
  1068. {
  1069. AnswerIndex = AbortIndex;
  1070. }
  1071. else if (Timeouting && (Timeout < 1000))
  1072. {
  1073. AnswerIndex = TimeoutIndex;
  1074. }
  1075. else
  1076. {
  1077. AnswerIndex = FConsole->Choice(Accels, CancelIndex, -1, -2,
  1078. (Timeouting ? 1000 : Timer));
  1079. if (AnswerIndex == -1)
  1080. {
  1081. NotifyAbort();
  1082. AnswerIndex = AbortIndex;
  1083. }
  1084. else if (AnswerIndex == -2)
  1085. {
  1086. if (Timeouting)
  1087. {
  1088. assert(Timeout >= 1000);
  1089. Timeout -= 1000;
  1090. Retry = true;
  1091. }
  1092. else
  1093. {
  1094. assert((Params != NULL) && (Params->TimerEvent != NULL));
  1095. if ((Params != NULL) && (Params->TimerEvent != NULL))
  1096. {
  1097. unsigned int AAnswer = 0;
  1098. Params->TimerEvent(AAnswer);
  1099. if (AAnswer != 0)
  1100. {
  1101. Answer = AAnswer;
  1102. }
  1103. else
  1104. {
  1105. Retry = true;
  1106. }
  1107. }
  1108. }
  1109. }
  1110. }
  1111. }
  1112. while (Retry);
  1113. if (Answer == 0)
  1114. {
  1115. assert((AnswerIndex >= 1) && (AnswerIndex <= Accels.Length()));
  1116. AnsiString AnswerCaption = Captions[AnswerIndex - 1];
  1117. int P = AnswerCaption.Pos("&");
  1118. assert(P >= 0);
  1119. AnswerCaption.Delete(P, 1);
  1120. PrintLine(AnswerCaption);
  1121. if (OnClicks[AnswerIndex - 1] != NULL)
  1122. {
  1123. OnClicks[AnswerIndex - 1](NULL);
  1124. }
  1125. else
  1126. {
  1127. Answer = Buttons[AnswerIndex - 1];
  1128. }
  1129. }
  1130. else
  1131. {
  1132. PrintLine("");
  1133. }
  1134. }
  1135. while (Answer == 0);
  1136. if (Answer == AbortA)
  1137. {
  1138. FCommandError = true;
  1139. }
  1140. }
  1141. //---------------------------------------------------------------------------
  1142. void __fastcall TConsoleRunner::ScriptQueryCancel(TScript * /*Script*/, bool & Cancel)
  1143. {
  1144. if (Aborted())
  1145. {
  1146. Cancel = true;
  1147. }
  1148. }
  1149. //---------------------------------------------------------------------------
  1150. void __fastcall TConsoleRunner::ScriptSynchronizeStartStop(TScript * /*Script*/,
  1151. const AnsiString LocalDirectory, const AnsiString RemoteDirectory,
  1152. const TCopyParamType & CopyParam, int SynchronizeParams)
  1153. {
  1154. TSynchronizeParamType Params;
  1155. Params.LocalDirectory = LocalDirectory;
  1156. Params.RemoteDirectory = RemoteDirectory;
  1157. Params.Params = SynchronizeParams;
  1158. Params.Options = soRecurse;
  1159. FSynchronizeController.StartStop(Application, true, Params,
  1160. CopyParam, NULL, SynchronizeControllerAbort, NULL,
  1161. SynchronizeControllerLog);
  1162. try
  1163. {
  1164. FSynchronizeAborted = false;
  1165. while (!FSynchronizeAborted && !Aborted(false))
  1166. {
  1167. Application->HandleMessage();
  1168. FScript->Terminal->Idle();
  1169. }
  1170. }
  1171. __finally
  1172. {
  1173. FSynchronizeController.StartStop(Application, false, Params,
  1174. CopyParam, NULL, SynchronizeControllerAbort, NULL,
  1175. SynchronizeControllerLog);
  1176. }
  1177. }
  1178. //---------------------------------------------------------------------------
  1179. void __fastcall TConsoleRunner::SynchronizeControllerLog(
  1180. TSynchronizeController * /*Controller*/, TSynchronizeLogEntry /*Entry*/,
  1181. const AnsiString Message)
  1182. {
  1183. PrintLine(Message);
  1184. }
  1185. //---------------------------------------------------------------------------
  1186. void __fastcall TConsoleRunner::SynchronizeControllerAbort(TObject * /*Sender*/,
  1187. bool /*Close*/)
  1188. {
  1189. FSynchronizeAborted = true;
  1190. }
  1191. //---------------------------------------------------------------------------
  1192. void __fastcall TConsoleRunner::SynchronizeControllerSynchronize(
  1193. TSynchronizeController * /*Sender*/, const AnsiString LocalDirectory,
  1194. const AnsiString RemoteDirectory, const TCopyParamType & CopyParam,
  1195. const TSynchronizeParamType & Params, TSynchronizeChecklist ** Checklist,
  1196. TSynchronizeOptions * /*Options*/, bool Full)
  1197. {
  1198. if (!Full)
  1199. {
  1200. FScript->Synchronize(LocalDirectory, RemoteDirectory, CopyParam,
  1201. Params.Params, Checklist);
  1202. }
  1203. }
  1204. //---------------------------------------------------------------------------
  1205. void __fastcall TConsoleRunner::SynchronizeControllerSynchronizeInvalid(
  1206. TSynchronizeController * /*Sender*/, const AnsiString Directory, const AnsiString ErrorStr)
  1207. {
  1208. if (!Directory.IsEmpty())
  1209. {
  1210. PrintLine(FMTLOAD(WATCH_ERROR_DIRECTORY, (Directory)));
  1211. }
  1212. else
  1213. {
  1214. PrintLine(LoadStr(WATCH_ERROR_GENERAL));
  1215. }
  1216. if (!ErrorStr.IsEmpty())
  1217. {
  1218. PrintLine(ErrorStr);
  1219. }
  1220. }
  1221. //---------------------------------------------------------------------------
  1222. void __fastcall TConsoleRunner::SynchronizeControllerTooManyDirectories(
  1223. TSynchronizeController * /*Sender*/, int & MaxDirectories)
  1224. {
  1225. if (Aborted())
  1226. {
  1227. Abort();
  1228. }
  1229. if (MaxDirectories < GUIConfiguration->MaxWatchDirectories)
  1230. {
  1231. MaxDirectories = GUIConfiguration->MaxWatchDirectories;
  1232. }
  1233. else
  1234. {
  1235. MaxDirectories *= 2;
  1236. }
  1237. }
  1238. //---------------------------------------------------------------------------
  1239. void __fastcall TConsoleRunner::ShowException(Exception * E)
  1240. {
  1241. if (!E->Message.IsEmpty() &&
  1242. (dynamic_cast<EAbort *>(E) == NULL))
  1243. {
  1244. FCommandError = true;
  1245. PrintMessage(TranslateExceptionMessage(E));
  1246. ExtException * EE = dynamic_cast<ExtException *>(E);
  1247. if ((EE != NULL) && (EE->MoreMessages != NULL))
  1248. {
  1249. PrintMessage(EE->MoreMessages->Text);
  1250. }
  1251. }
  1252. }
  1253. //---------------------------------------------------------------------------
  1254. bool __fastcall TConsoleRunner::Input(AnsiString & Str, bool Echo, unsigned int Timeout)
  1255. {
  1256. bool Result = FConsole->Input(Str, Echo, Timeout);
  1257. if (Result)
  1258. {
  1259. while (!Str.IsEmpty() &&
  1260. ((Str[Str.Length()] == '\n') || (Str[Str.Length()] == '\r')))
  1261. {
  1262. Str.SetLength(Str.Length() - 1);
  1263. }
  1264. }
  1265. else
  1266. {
  1267. NotifyAbort();
  1268. }
  1269. return Result;
  1270. }
  1271. //---------------------------------------------------------------------------
  1272. int __fastcall TConsoleRunner::Run(const AnsiString Session, TOptions * Options,
  1273. TStrings * ScriptCommands)
  1274. {
  1275. bool AnyError = false;
  1276. try
  1277. {
  1278. FScript = new TManagementScript(StoredSessions, FConsole->LimitedOutput());
  1279. try
  1280. {
  1281. FScript->CopyParam = GUIConfiguration->DefaultCopyParam;
  1282. FScript->SynchronizeParams = GUIConfiguration->SynchronizeParams;
  1283. FScript->OnPrint = ScriptPrint;
  1284. FScript->OnPrintProgress = ScriptPrintProgress;
  1285. FScript->OnInput = ScriptInput;
  1286. FScript->OnTerminalPromptUser = ScriptTerminalPromptUser;
  1287. FScript->OnShowExtendedException = ScriptShowExtendedException;
  1288. FScript->OnTerminalQueryUser = ScriptTerminalQueryUser;
  1289. FScript->OnQueryCancel = ScriptQueryCancel;
  1290. FScript->OnSynchronizeStartStop = ScriptSynchronizeStartStop;
  1291. UpdateTitle();
  1292. // everything until the first manually entered command is "batch"
  1293. // (including opening session from command line and script file)
  1294. FBatchScript = true;
  1295. if (!Session.IsEmpty())
  1296. {
  1297. FScript->Connect(Session, Options, false);
  1298. }
  1299. int ScriptPos = 0;
  1300. bool Result;
  1301. do
  1302. {
  1303. UpdateTitle();
  1304. AnsiString Command;
  1305. if ((ScriptCommands != NULL) && (ScriptPos < ScriptCommands->Count))
  1306. {
  1307. Result = true;
  1308. Command = ScriptCommands->Strings[ScriptPos];
  1309. ScriptPos++;
  1310. }
  1311. else
  1312. {
  1313. // no longer batch
  1314. FBatchScript = false;
  1315. Print("winscp> ");
  1316. Result = Input(Command, true, 0);
  1317. }
  1318. if (Result)
  1319. {
  1320. FCommandError = false;
  1321. FScript->Command(ExpandEnvironmentVariables(Command));
  1322. if (FCommandError)
  1323. {
  1324. AnyError = true;
  1325. if (FScript->Batch == TScript::BatchAbort)
  1326. {
  1327. Result = false;
  1328. }
  1329. }
  1330. if (FScript->Terminal != NULL)
  1331. {
  1332. FScript->Terminal->Idle();
  1333. }
  1334. }
  1335. }
  1336. while (Result && FScript->Continue && !Aborted());
  1337. }
  1338. __finally
  1339. {
  1340. delete FScript;
  1341. FScript = NULL;
  1342. }
  1343. }
  1344. catch(Exception & E)
  1345. {
  1346. ShowException(&E);
  1347. AnyError = true;
  1348. }
  1349. return AnyError ? RESULT_ANY_ERROR : RESULT_SUCCESS;
  1350. }
  1351. //---------------------------------------------------------------------------
  1352. void __fastcall TConsoleRunner::UpdateTitle()
  1353. {
  1354. AnsiString NewTitle;
  1355. if (FScript->Terminal != NULL)
  1356. {
  1357. NewTitle = FMTLOAD(APP_CAPTION, (FScript->Terminal->SessionData->SessionName, AppName));
  1358. }
  1359. else
  1360. {
  1361. NewTitle = AppName;
  1362. }
  1363. FConsole->SetTitle(NewTitle);
  1364. }
  1365. //---------------------------------------------------------------------------
  1366. void __fastcall LoadScriptFromFile(AnsiString FileName, TStrings * Lines)
  1367. {
  1368. AnsiString UTFBOM = "\xEF\xBB\xBF";
  1369. Lines->LoadFromFile(FileName);
  1370. if ((Lines->Count > 0) &&
  1371. (Lines->Strings[0].SubString(1, UTFBOM.Length()) == UTFBOM))
  1372. {
  1373. Lines->Strings[0] = Lines->Strings[0].SubString(
  1374. UTFBOM.Length() + 1, Lines->Strings[0].Length() - UTFBOM.Length());
  1375. for (int Index = 0; Index < Lines->Count; Index++)
  1376. {
  1377. Lines->Strings[Index] = DecodeUTF(Lines->Strings[Index]);
  1378. }
  1379. }
  1380. }
  1381. //---------------------------------------------------------------------------
  1382. int __fastcall Console(bool Help)
  1383. {
  1384. TProgramParams * Params = TProgramParams::Instance();
  1385. int Result = 0;
  1386. TConsole * Console = NULL;
  1387. TConsoleRunner * Runner = NULL;
  1388. TStrings * ScriptCommands = new TStringList();
  1389. try
  1390. {
  1391. AnsiString ConsoleInstance;
  1392. if (Params->FindSwitch("consoleinstance", ConsoleInstance))
  1393. {
  1394. Console = new TExternalConsole(ConsoleInstance);
  1395. }
  1396. else if (Params->FindSwitch("Console") || Help)
  1397. {
  1398. Console = TOwnConsole::Instance();
  1399. }
  1400. else
  1401. {
  1402. Console = new TNullConsole();
  1403. }
  1404. if (Help)
  1405. {
  1406. AnsiString Usage = LoadStr(USAGE4, 10240);
  1407. AnsiString ExeBaseName = ChangeFileExt(ExtractFileName(Application->ExeName), "");
  1408. Usage = StringReplace(Usage, "%APP%", ExeBaseName,
  1409. TReplaceFlags() << rfReplaceAll << rfIgnoreCase);
  1410. AnsiString Copyright = StringReplace(LoadStr(WINSCP_COPYRIGHT), "©", "(c)",
  1411. TReplaceFlags() << rfReplaceAll << rfIgnoreCase);
  1412. Usage = FORMAT(Usage, (Configuration->VersionStr, Copyright));
  1413. Console->Print(Usage);
  1414. Console->WaitBeforeExit();
  1415. }
  1416. else
  1417. {
  1418. Runner = new TConsoleRunner(Console);
  1419. try
  1420. {
  1421. AnsiString Value;
  1422. if (Params->FindSwitch("script", Value) && !Value.IsEmpty())
  1423. {
  1424. LoadScriptFromFile(Value, ScriptCommands);
  1425. }
  1426. Params->FindSwitch("command", ScriptCommands);
  1427. bool Url = false;
  1428. AnsiString Session;
  1429. if (Params->ParamCount >= 1)
  1430. {
  1431. Session = Params->Param[1];
  1432. }
  1433. bool DefaultsOnly;
  1434. delete StoredSessions->ParseUrl(Session, Params, DefaultsOnly,
  1435. puDecodeUrlChars, NULL, &Url);
  1436. if (Url || Params->FindSwitch("Unsafe"))
  1437. {
  1438. // prevent any automatic action when URL is provided on
  1439. // command-line (the check is duplicated in Execute())
  1440. if ((ScriptCommands->Count > 0) || Params->FindSwitch("Log"))
  1441. {
  1442. Console->Print(LoadStr(UNSAFE_ACTIONS_DISABLED) + "\n");
  1443. }
  1444. ScriptCommands->Clear();
  1445. }
  1446. else
  1447. {
  1448. AnsiString LogFile;
  1449. if (Params->FindSwitch("Log", LogFile))
  1450. {
  1451. Configuration->TemporaryLogging(LogFile);
  1452. }
  1453. }
  1454. Result = Runner->Run(Session, Params,
  1455. (ScriptCommands->Count > 0 ? ScriptCommands : NULL));
  1456. }
  1457. catch(Exception & E)
  1458. {
  1459. Runner->ShowException(&E);
  1460. Result = RESULT_ANY_ERROR;
  1461. }
  1462. }
  1463. }
  1464. __finally
  1465. {
  1466. delete Runner;
  1467. delete Console;
  1468. delete ScriptCommands;
  1469. }
  1470. return Result;
  1471. }