ConsoleRunner.cpp 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076
  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 <Queue.h>
  11. #include <Consts.hpp>
  12. #include <StrUtils.hpp>
  13. #include "Console.h"
  14. #include "WinInterface.h"
  15. #include "ProgParams.h"
  16. #include "TextsWin.h"
  17. #include "TextsCore.h"
  18. #include "WinConfiguration.h"
  19. #include "SynchronizeController.h"
  20. #include "GUITools.h"
  21. enum { RESULT_SUCCESS = 0, RESULT_ANY_ERROR = 1 };
  22. //---------------------------------------------------------------------------
  23. #define WM_INTERUPT_IDLE (WM_WINSCP_USER + 3)
  24. #define BATCH_INPUT_TIMEOUT 10000
  25. //---------------------------------------------------------------------------
  26. #pragma package(smart_init)
  27. //---------------------------------------------------------------------------
  28. class TConsole
  29. {
  30. public:
  31. virtual __fastcall ~TConsole() {};
  32. virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false) = 0;
  33. virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer) = 0;
  34. virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
  35. int Timeouted, bool Timeouting, unsigned int Timer) = 0;
  36. virtual bool __fastcall PendingAbort() = 0;
  37. virtual void __fastcall SetTitle(UnicodeString Title) = 0;
  38. virtual bool __fastcall LimitedOutput() = 0;
  39. virtual bool __fastcall LiveOutput() = 0;
  40. virtual bool __fastcall NoInteractiveInput() = 0;
  41. virtual void __fastcall WaitBeforeExit() = 0;
  42. virtual bool __fastcall CommandLineOnly() = 0;
  43. virtual bool __fastcall WantsProgress() = 0;
  44. virtual void __fastcall Progress(const TScriptProgress & Progress) = 0;
  45. };
  46. //---------------------------------------------------------------------------
  47. class TOwnConsole : public TConsole
  48. {
  49. public:
  50. static TOwnConsole * __fastcall Instance();
  51. virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false);
  52. virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer);
  53. virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
  54. int Timeouted, bool Timeouting, unsigned int Timer);
  55. virtual bool __fastcall PendingAbort();
  56. virtual void __fastcall SetTitle(UnicodeString Title);
  57. virtual bool __fastcall LimitedOutput();
  58. virtual bool __fastcall LiveOutput();
  59. virtual bool __fastcall NoInteractiveInput();
  60. virtual void __fastcall WaitBeforeExit();
  61. virtual bool __fastcall CommandLineOnly();
  62. virtual bool __fastcall WantsProgress();
  63. virtual void __fastcall Progress(const TScriptProgress & Progress);
  64. protected:
  65. static TOwnConsole * FInstance;
  66. friend class TConsoleInputThread;
  67. __fastcall TOwnConsole();
  68. virtual __fastcall ~TOwnConsole();
  69. void __fastcall BreakInput();
  70. void __fastcall CancelInput();
  71. static BOOL WINAPI HandlerRoutine(DWORD CtrlType);
  72. void __fastcall WindowStateTimer(TObject * Sender);
  73. void __fastcall ProcessMessages();
  74. void __fastcall TrayIconClick(TObject * Sender);
  75. private:
  76. HANDLE FInput;
  77. HANDLE FOutput;
  78. HWND FConsoleWindow;
  79. TTimer * FWindowStateTimer;
  80. bool FMinimized;
  81. ::TTrayIcon * FTrayIcon;
  82. static std::unique_ptr<TCriticalSection> FSection;
  83. bool FPendingAbort;
  84. };
  85. //---------------------------------------------------------------------------
  86. TOwnConsole * TOwnConsole::FInstance = NULL;
  87. std::unique_ptr<TCriticalSection> TOwnConsole::FSection(new TCriticalSection());
  88. //---------------------------------------------------------------------------
  89. __fastcall TOwnConsole::TOwnConsole()
  90. {
  91. assert(FInstance == NULL);
  92. FInstance = this;
  93. AllocConsole();
  94. SetConsoleCtrlHandler(HandlerRoutine, true);
  95. FInput = GetStdHandle(STD_INPUT_HANDLE);
  96. FOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  97. FPendingAbort = false;
  98. FConsoleWindow = NULL;
  99. FWindowStateTimer = NULL;
  100. FMinimized = false;
  101. FTrayIcon = new ::TTrayIcon(0);
  102. FTrayIcon->OnClick = TrayIconClick;
  103. if (WinConfiguration->MinimizeToTray)
  104. {
  105. FConsoleWindow = GetConsoleWindow();
  106. if (ALWAYS_TRUE(FConsoleWindow != NULL))
  107. {
  108. FWindowStateTimer = new TTimer(Application);
  109. FWindowStateTimer->OnTimer = WindowStateTimer;
  110. FWindowStateTimer->Interval = 250;
  111. FWindowStateTimer->Enabled = true;
  112. }
  113. }
  114. }
  115. //---------------------------------------------------------------------------
  116. __fastcall TOwnConsole::~TOwnConsole()
  117. {
  118. TGuard Guard(FSection.get());
  119. delete FTrayIcon;
  120. delete FWindowStateTimer;
  121. // deliberatelly do not remove ConsoleCtrlHandler as it causes
  122. // failures while exiting
  123. FreeConsole();
  124. assert(FInstance == this);
  125. FInstance = NULL;
  126. }
  127. //---------------------------------------------------------------------------
  128. TOwnConsole * __fastcall TOwnConsole::Instance()
  129. {
  130. return new TOwnConsole();
  131. }
  132. //---------------------------------------------------------------------------
  133. void __fastcall TOwnConsole::WindowStateTimer(TObject * /*Sender*/)
  134. {
  135. assert(FConsoleWindow != NULL);
  136. WINDOWPLACEMENT Placement;
  137. memset(&Placement, 0, sizeof(Placement));
  138. Placement.length = sizeof(Placement);
  139. if (GetWindowPlacement(FConsoleWindow, &Placement))
  140. {
  141. bool Minimized = (Placement.showCmd == SW_SHOWMINIMIZED);
  142. if (FMinimized != Minimized)
  143. {
  144. FMinimized = Minimized;
  145. if (FMinimized && WinConfiguration->MinimizeToTray)
  146. {
  147. FTrayIcon->Visible = true;
  148. ShowWindow(FConsoleWindow, SW_HIDE);
  149. }
  150. else
  151. {
  152. FTrayIcon->Visible = false;
  153. ShowWindow(FConsoleWindow, SW_SHOW);
  154. }
  155. }
  156. }
  157. else
  158. {
  159. assert(false);
  160. }
  161. }
  162. //---------------------------------------------------------------------------
  163. void __fastcall TOwnConsole::ProcessMessages()
  164. {
  165. // as of now, there's no point doing this unless we have icon tray
  166. // (i.e. we need to monitor window state and eventually process tray icon messages)
  167. if (FWindowStateTimer != NULL)
  168. {
  169. assert(WinConfiguration->MinimizeToTray);
  170. Application->ProcessMessages();
  171. }
  172. }
  173. //---------------------------------------------------------------------------
  174. void __fastcall TOwnConsole::TrayIconClick(TObject * /*Sender*/)
  175. {
  176. assert(FConsoleWindow != NULL);
  177. SetForegroundWindow(FConsoleWindow);
  178. ShowWindow(FConsoleWindow, SW_RESTORE);
  179. }
  180. //---------------------------------------------------------------------------
  181. void __fastcall TOwnConsole::BreakInput()
  182. {
  183. FlushConsoleInputBuffer(FInput);
  184. INPUT_RECORD InputRecord;
  185. memset(&InputRecord, 0, sizeof(InputRecord));
  186. InputRecord.EventType = KEY_EVENT;
  187. InputRecord.Event.KeyEvent.bKeyDown = true;
  188. InputRecord.Event.KeyEvent.wRepeatCount = 1;
  189. InputRecord.Event.KeyEvent.uChar.UnicodeChar = L'\r';
  190. unsigned long Written;
  191. // this assertion occasionally fails (when console is being exited)
  192. CHECK(WriteConsoleInput(FInput, &InputRecord, 1, &Written));
  193. assert(Written == 1);
  194. CancelInput();
  195. }
  196. //---------------------------------------------------------------------------
  197. void __fastcall TOwnConsole::CancelInput()
  198. {
  199. FPendingAbort = true;
  200. PostMessage(Application->Handle, WM_INTERUPT_IDLE, 0, 0);
  201. }
  202. //---------------------------------------------------------------------------
  203. BOOL WINAPI TOwnConsole::HandlerRoutine(DWORD CtrlType)
  204. {
  205. if ((CtrlType == CTRL_C_EVENT) || (CtrlType == CTRL_BREAK_EVENT))
  206. {
  207. {
  208. TGuard Guard(FSection.get());
  209. // just to be real thread-safe
  210. if (FInstance != NULL)
  211. {
  212. FInstance->CancelInput();
  213. }
  214. }
  215. return true;
  216. }
  217. else
  218. {
  219. return false;
  220. }
  221. }
  222. //---------------------------------------------------------------------------
  223. bool __fastcall TOwnConsole::PendingAbort()
  224. {
  225. if (FPendingAbort)
  226. {
  227. FPendingAbort = false;
  228. return true;
  229. }
  230. else
  231. {
  232. return FPendingAbort;
  233. }
  234. }
  235. //---------------------------------------------------------------------------
  236. void __fastcall TOwnConsole::Print(UnicodeString Str, bool FromBeginning)
  237. {
  238. if (FromBeginning)
  239. {
  240. CONSOLE_SCREEN_BUFFER_INFO BufferInfo;
  241. GetConsoleScreenBufferInfo(FOutput, &BufferInfo);
  242. BufferInfo.dwCursorPosition.X = 0;
  243. SetConsoleCursorPosition(FOutput, BufferInfo.dwCursorPosition);
  244. }
  245. unsigned long Written;
  246. bool Result = WriteConsole(FOutput, Str.c_str(), Str.Length(), &Written, NULL);
  247. assert(Result);
  248. USEDPARAM(Result);
  249. assert(Str.Length() == static_cast<long>(Written));
  250. ProcessMessages();
  251. }
  252. //---------------------------------------------------------------------------
  253. class TConsoleInputThread : public TSimpleThread
  254. {
  255. public:
  256. __fastcall TConsoleInputThread(HANDLE Input, UnicodeString & Str, bool & Result) :
  257. FInput(Input),
  258. FStr(Str),
  259. FResult(Result)
  260. {
  261. }
  262. virtual __fastcall ~TConsoleInputThread()
  263. {
  264. Close();
  265. }
  266. protected:
  267. virtual void __fastcall Execute()
  268. {
  269. unsigned long Read;
  270. FStr.SetLength(10240);
  271. FResult = ReadConsole(FInput, FStr.c_str(), FStr.Length(), &Read, NULL);
  272. assert(FResult);
  273. FStr.SetLength(Read);
  274. }
  275. virtual void __fastcall Terminate()
  276. {
  277. TOwnConsole::FInstance->BreakInput();
  278. }
  279. private:
  280. HANDLE FInput;
  281. UnicodeString & FStr;
  282. bool & FResult;
  283. };
  284. //---------------------------------------------------------------------------
  285. bool __fastcall TOwnConsole::Input(UnicodeString & Str, bool Echo, unsigned int Timer)
  286. {
  287. unsigned long PrevMode, NewMode;
  288. GetConsoleMode(FInput, &PrevMode);
  289. NewMode = PrevMode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
  290. if (Echo)
  291. {
  292. NewMode |= ENABLE_ECHO_INPUT;
  293. }
  294. else
  295. {
  296. NewMode &= ~ENABLE_ECHO_INPUT;
  297. }
  298. SetConsoleMode(FInput, NewMode);
  299. bool Result = false;
  300. try
  301. {
  302. {
  303. TConsoleInputThread InputThread(FInput, Str, Result);
  304. InputThread.Start();
  305. double Start = Now();
  306. double End = Start + double(Timer)/MSecsPerDay;
  307. while (!InputThread.IsFinished() &&
  308. ((Timer == 0) || (double(Now()) < End)))
  309. {
  310. ProcessMessages();
  311. InputThread.WaitFor(50);
  312. }
  313. }
  314. if (FPendingAbort || !Echo)
  315. {
  316. Print(L"\n");
  317. }
  318. if (FPendingAbort || (Str.Length() == 0))
  319. {
  320. Result = false;
  321. FPendingAbort = false;
  322. }
  323. }
  324. __finally
  325. {
  326. SetConsoleMode(FInput, PrevMode);
  327. }
  328. return Result;
  329. }
  330. //---------------------------------------------------------------------------
  331. int __fastcall TOwnConsole::Choice(UnicodeString Options, int Cancel, int Break,
  332. int Timeouted, bool /*Timeouting*/, unsigned int Timer)
  333. {
  334. unsigned int ATimer = Timer;
  335. int Result = 0;
  336. unsigned long PrevMode, NewMode;
  337. GetConsoleMode(FInput, &PrevMode);
  338. NewMode = (PrevMode | ENABLE_PROCESSED_INPUT) & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
  339. SetConsoleMode(FInput, NewMode);
  340. try
  341. {
  342. do
  343. {
  344. unsigned long Read;
  345. INPUT_RECORD Record;
  346. if ((PeekConsoleInput(FInput, &Record, 1, &Read) != 0) &&
  347. (Read == 1))
  348. {
  349. if ((ReadConsoleInput(FInput, &Record, 1, &Read) != 0) &&
  350. (Read == 1))
  351. {
  352. if (PendingAbort())
  353. {
  354. Result = Break;
  355. }
  356. else if ((Record.EventType == KEY_EVENT) &&
  357. Record.Event.KeyEvent.bKeyDown)
  358. {
  359. wchar_t C = AnsiUpperCase(Record.Event.KeyEvent.uChar.UnicodeChar)[1];
  360. if (C == 27)
  361. {
  362. Result = Cancel;
  363. }
  364. else if ((Options.Pos(C) > 0) &&
  365. FLAGCLEAR(Record.Event.KeyEvent.dwControlKeyState,
  366. LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED |
  367. RIGHT_ALT_PRESSED))
  368. {
  369. Result = Options.Pos(C);
  370. }
  371. }
  372. }
  373. }
  374. if (Result == 0)
  375. {
  376. unsigned int TimerSlice = 50;
  377. Sleep(TimerSlice);
  378. if (Timer > 0)
  379. {
  380. if (ATimer > TimerSlice)
  381. {
  382. ATimer -= TimerSlice;
  383. }
  384. else
  385. {
  386. Result = Timeouted;
  387. }
  388. }
  389. }
  390. ProcessMessages();
  391. }
  392. while (Result == 0);
  393. }
  394. __finally
  395. {
  396. SetConsoleMode(FInput, PrevMode);
  397. }
  398. return Result;
  399. }
  400. //---------------------------------------------------------------------------
  401. void __fastcall TOwnConsole::SetTitle(UnicodeString Title)
  402. {
  403. FTrayIcon->Hint = Title;
  404. SetConsoleTitle(Title.c_str());
  405. }
  406. //---------------------------------------------------------------------------
  407. bool __fastcall TOwnConsole::LimitedOutput()
  408. {
  409. return true;
  410. }
  411. //---------------------------------------------------------------------------
  412. bool __fastcall TOwnConsole::LiveOutput()
  413. {
  414. return true;
  415. }
  416. //---------------------------------------------------------------------------
  417. bool __fastcall TOwnConsole::NoInteractiveInput()
  418. {
  419. return false;
  420. }
  421. //---------------------------------------------------------------------------
  422. void __fastcall TOwnConsole::WaitBeforeExit()
  423. {
  424. unsigned long Read;
  425. INPUT_RECORD Record;
  426. while (true)
  427. {
  428. if (PeekConsoleInput(FInput, &Record, 1, &Read) && (Read == 1) &&
  429. ReadConsoleInput(FInput, &Record, 1, &Read) &&
  430. (Read == 1) && (Record.EventType == KEY_EVENT) &&
  431. (Record.Event.KeyEvent.uChar.UnicodeChar != 0) &&
  432. Record.Event.KeyEvent.bKeyDown)
  433. {
  434. break;
  435. }
  436. Sleep(50);
  437. ProcessMessages();
  438. }
  439. }
  440. //---------------------------------------------------------------------------
  441. bool __fastcall TOwnConsole::CommandLineOnly()
  442. {
  443. return false;
  444. }
  445. //---------------------------------------------------------------------------
  446. bool __fastcall TOwnConsole::WantsProgress()
  447. {
  448. return false;
  449. }
  450. //---------------------------------------------------------------------------
  451. void __fastcall TOwnConsole::Progress(const TScriptProgress & /*Progress*/)
  452. {
  453. FAIL;
  454. }
  455. //---------------------------------------------------------------------------
  456. class TExternalConsole : public TConsole
  457. {
  458. public:
  459. __fastcall TExternalConsole(const UnicodeString Instance, bool NoInteractiveInput);
  460. virtual __fastcall ~TExternalConsole();
  461. virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false);
  462. virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer);
  463. virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
  464. int Timeouted, bool Timeouting, unsigned int Timer);
  465. virtual bool __fastcall PendingAbort();
  466. virtual void __fastcall SetTitle(UnicodeString Title);
  467. virtual bool __fastcall LimitedOutput();
  468. virtual bool __fastcall LiveOutput();
  469. virtual bool __fastcall NoInteractiveInput();
  470. virtual void __fastcall WaitBeforeExit();
  471. virtual bool __fastcall CommandLineOnly();
  472. virtual bool __fastcall WantsProgress();
  473. virtual void __fastcall Progress(const TScriptProgress & Progress);
  474. private:
  475. bool FPendingAbort;
  476. HANDLE FRequestEvent;
  477. HANDLE FResponseEvent;
  478. HANDLE FCancelEvent;
  479. HANDLE FFileMapping;
  480. bool FLimitedOutput;
  481. bool FLiveOutput;
  482. bool FPipeOutput;
  483. bool FNoInteractiveInput;
  484. bool FWantsProgress;
  485. static const int PrintTimeout = 30000;
  486. inline TConsoleCommStruct * __fastcall GetCommStruct();
  487. inline void __fastcall FreeCommStruct(TConsoleCommStruct * CommStruct);
  488. inline void __fastcall SendEvent(int Timeout);
  489. void __fastcall Init();
  490. };
  491. //---------------------------------------------------------------------------
  492. __fastcall TExternalConsole::TExternalConsole(
  493. const UnicodeString Instance, bool NoInteractiveInput)
  494. {
  495. FRequestEvent = OpenEvent(EVENT_ALL_ACCESS, false,
  496. FORMAT(L"%s%s", (CONSOLE_EVENT_REQUEST, (Instance))).c_str());
  497. FResponseEvent = OpenEvent(EVENT_ALL_ACCESS, false,
  498. FORMAT(L"%s%s", (CONSOLE_EVENT_RESPONSE, (Instance))).c_str());
  499. FCancelEvent = OpenEvent(EVENT_ALL_ACCESS, false,
  500. FORMAT(L"%s%s", (CONSOLE_EVENT_CANCEL, (Instance))).c_str());
  501. FFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
  502. FORMAT(L"%s%s", (CONSOLE_MAPPING, (Instance))).c_str());
  503. if ((FRequestEvent == NULL) || (FResponseEvent == NULL) || (FFileMapping == NULL))
  504. {
  505. throw Exception(LoadStr(EXTERNAL_CONSOLE_INIT_ERROR));
  506. }
  507. HANDLE Job = OpenJobObject(JOB_OBJECT_ASSIGN_PROCESS, FALSE,
  508. FORMAT(L"%s%s", (CONSOLE_JOB, Instance)).c_str());
  509. if (ALWAYS_TRUE(Job != NULL))
  510. {
  511. AssignProcessToJobObject(Job, GetCurrentProcess());
  512. // winscp.com/winscp.dll keeps the only reference to the job.
  513. // once it gets closed (because winscp.com if forcefully terminated),
  514. // we get terminated as well
  515. CloseHandle(Job);
  516. }
  517. TConsoleCommStruct * CommStruct = GetCommStruct();
  518. try
  519. {
  520. if (CommStruct->Version != TConsoleCommStruct::CurrentVersion)
  521. {
  522. throw Exception(FMTLOAD(EXTERNAL_CONSOLE_INCOMPATIBLE, (CommStruct->Version)));
  523. }
  524. CommStruct->Version = TConsoleCommStruct::CurrentVersionConfirmed;
  525. }
  526. __finally
  527. {
  528. FreeCommStruct(CommStruct);
  529. }
  530. // to break application event loop regularly during "watching for changes"
  531. // to allow user to abort it
  532. SetTimer(Application->Handle, 1, 500, NULL);
  533. FNoInteractiveInput = NoInteractiveInput;
  534. Init();
  535. }
  536. //---------------------------------------------------------------------------
  537. __fastcall TExternalConsole::~TExternalConsole()
  538. {
  539. CloseHandle(FRequestEvent);
  540. CloseHandle(FResponseEvent);
  541. CloseHandle(FCancelEvent);
  542. CloseHandle(FFileMapping);
  543. KillTimer(Application->Handle, 1);
  544. }
  545. //---------------------------------------------------------------------------
  546. TConsoleCommStruct * __fastcall TExternalConsole::GetCommStruct()
  547. {
  548. TConsoleCommStruct * Result;
  549. Result = static_cast<TConsoleCommStruct*>(MapViewOfFile(FFileMapping,
  550. FILE_MAP_ALL_ACCESS, 0, 0, 0));
  551. if (Result == NULL)
  552. {
  553. throw Exception(LoadStr(CONSOLE_COMM_ERROR));
  554. }
  555. return Result;
  556. }
  557. //---------------------------------------------------------------------------
  558. void __fastcall TExternalConsole::FreeCommStruct(TConsoleCommStruct * CommStruct)
  559. {
  560. UnmapViewOfFile(CommStruct);
  561. }
  562. //---------------------------------------------------------------------------
  563. void __fastcall TExternalConsole::SendEvent(int Timeout)
  564. {
  565. SetEvent(FRequestEvent);
  566. unsigned int Result = WaitForSingleObject(FResponseEvent, Timeout);
  567. if (Result != WAIT_OBJECT_0)
  568. {
  569. UnicodeString Message = LoadStr(CONSOLE_SEND_TIMEOUT);
  570. if (FPipeOutput)
  571. {
  572. Message = FORMAT("%s %s", (Message, LoadStr(CONSOLE_SEND_PIPE)));
  573. }
  574. throw Exception(Message);
  575. }
  576. }
  577. //---------------------------------------------------------------------------
  578. void __fastcall TExternalConsole::Print(UnicodeString Str, bool FromBeginning)
  579. {
  580. TConsoleCommStruct * CommStruct = GetCommStruct();
  581. try
  582. {
  583. if (Str.Length() >= static_cast<int>(LENOF(CommStruct->PrintEvent.Message)))
  584. {
  585. throw Exception(FMTLOAD(CONSOLE_PRINT_TOO_LONG, (Str.Length())));
  586. }
  587. CommStruct->Event = TConsoleCommStruct::PRINT;
  588. wcscpy(CommStruct->PrintEvent.Message, Str.c_str());
  589. CommStruct->PrintEvent.FromBeginning = FromBeginning;
  590. }
  591. __finally
  592. {
  593. FreeCommStruct(CommStruct);
  594. }
  595. SendEvent(PrintTimeout);
  596. }
  597. //---------------------------------------------------------------------------
  598. bool __fastcall TExternalConsole::Input(UnicodeString & Str, bool Echo, unsigned int Timer)
  599. {
  600. TConsoleCommStruct * CommStruct = GetCommStruct();
  601. try
  602. {
  603. CommStruct->Event = TConsoleCommStruct::INPUT;
  604. CommStruct->InputEvent.Echo = Echo;
  605. CommStruct->InputEvent.Result = false;
  606. CommStruct->InputEvent.Str[0] = L'\0';
  607. CommStruct->InputEvent.Timer = Timer;
  608. }
  609. __finally
  610. {
  611. FreeCommStruct(CommStruct);
  612. }
  613. SendEvent(INFINITE);
  614. bool Result;
  615. CommStruct = GetCommStruct();
  616. try
  617. {
  618. Result = CommStruct->InputEvent.Result;
  619. Str = CommStruct->InputEvent.Str;
  620. }
  621. __finally
  622. {
  623. FreeCommStruct(CommStruct);
  624. }
  625. return Result;
  626. }
  627. //---------------------------------------------------------------------------
  628. int __fastcall TExternalConsole::Choice(UnicodeString Options, int Cancel, int Break,
  629. int Timeouted, bool Timeouting, unsigned int Timer)
  630. {
  631. TConsoleCommStruct * CommStruct = GetCommStruct();
  632. try
  633. {
  634. CommStruct->Event = TConsoleCommStruct::CHOICE;
  635. assert(Options.Length() < static_cast<int>(LENOF(CommStruct->ChoiceEvent.Options)));
  636. wcscpy(CommStruct->ChoiceEvent.Options, Options.c_str());
  637. CommStruct->ChoiceEvent.Cancel = Cancel;
  638. CommStruct->ChoiceEvent.Break = Break;
  639. CommStruct->ChoiceEvent.Result = Break;
  640. CommStruct->ChoiceEvent.Timeouted = Timeouted;
  641. CommStruct->ChoiceEvent.Timer = Timer;
  642. CommStruct->ChoiceEvent.Timeouting = Timeouting;
  643. }
  644. __finally
  645. {
  646. FreeCommStruct(CommStruct);
  647. }
  648. SendEvent(INFINITE);
  649. int Result;
  650. CommStruct = GetCommStruct();
  651. try
  652. {
  653. Result = CommStruct->ChoiceEvent.Result;
  654. }
  655. __finally
  656. {
  657. FreeCommStruct(CommStruct);
  658. }
  659. return Result;
  660. }
  661. //---------------------------------------------------------------------------
  662. bool __fastcall TExternalConsole::PendingAbort()
  663. {
  664. return (WaitForSingleObject(FCancelEvent, 0) == WAIT_OBJECT_0);
  665. }
  666. //---------------------------------------------------------------------------
  667. void __fastcall TExternalConsole::SetTitle(UnicodeString Title)
  668. {
  669. TConsoleCommStruct * CommStruct = GetCommStruct();
  670. try
  671. {
  672. if (Title.Length() >= static_cast<int>(LENOF(CommStruct->TitleEvent.Title)))
  673. {
  674. throw Exception(FMTLOAD(CONSOLE_PRINT_TOO_LONG, (Title.Length())));
  675. }
  676. CommStruct->Event = TConsoleCommStruct::TITLE;
  677. wcscpy(CommStruct->TitleEvent.Title, Title.c_str());
  678. }
  679. __finally
  680. {
  681. FreeCommStruct(CommStruct);
  682. }
  683. SendEvent(INFINITE);
  684. }
  685. //---------------------------------------------------------------------------
  686. void __fastcall TExternalConsole::Init()
  687. {
  688. TConsoleCommStruct * CommStruct = GetCommStruct();
  689. try
  690. {
  691. CommStruct->Event = TConsoleCommStruct::INIT;
  692. CommStruct->InitEvent.WantsProgress = false;
  693. }
  694. __finally
  695. {
  696. FreeCommStruct(CommStruct);
  697. }
  698. SendEvent(INFINITE);
  699. CommStruct = GetCommStruct();
  700. try
  701. {
  702. FLimitedOutput = (CommStruct->InitEvent.OutputType == FILE_TYPE_CHAR);
  703. FLiveOutput =
  704. (CommStruct->InitEvent.OutputType != FILE_TYPE_DISK) &&
  705. (CommStruct->InitEvent.OutputType != FILE_TYPE_PIPE);
  706. FPipeOutput = (CommStruct->InitEvent.OutputType != FILE_TYPE_PIPE);
  707. FWantsProgress = CommStruct->InitEvent.WantsProgress;
  708. }
  709. __finally
  710. {
  711. FreeCommStruct(CommStruct);
  712. }
  713. }
  714. //---------------------------------------------------------------------------
  715. bool __fastcall TExternalConsole::LimitedOutput()
  716. {
  717. return FLimitedOutput;
  718. }
  719. //---------------------------------------------------------------------------
  720. bool __fastcall TExternalConsole::LiveOutput()
  721. {
  722. return FLiveOutput;
  723. }
  724. //---------------------------------------------------------------------------
  725. bool __fastcall TExternalConsole::NoInteractiveInput()
  726. {
  727. return FNoInteractiveInput;
  728. }
  729. //---------------------------------------------------------------------------
  730. void __fastcall TExternalConsole::WaitBeforeExit()
  731. {
  732. // noop
  733. }
  734. //---------------------------------------------------------------------------
  735. bool __fastcall TExternalConsole::CommandLineOnly()
  736. {
  737. return true;
  738. }
  739. //---------------------------------------------------------------------------
  740. bool __fastcall TExternalConsole::WantsProgress()
  741. {
  742. return FWantsProgress;
  743. }
  744. //---------------------------------------------------------------------------
  745. void __fastcall TExternalConsole::Progress(const TScriptProgress & Progress)
  746. {
  747. TConsoleCommStruct * CommStruct = GetCommStruct();
  748. try
  749. {
  750. CommStruct->Event = TConsoleCommStruct::PROGRESS;
  751. typedef TConsoleCommStruct::TProgressEvent TProgressEvent;
  752. TProgressEvent & ProgressEvent = CommStruct->ProgressEvent;
  753. switch (Progress.Operation)
  754. {
  755. case foCopy:
  756. case foMove:
  757. ProgressEvent.Operation = TProgressEvent::COPY;
  758. break;
  759. default:
  760. FAIL;
  761. }
  762. switch (Progress.Side)
  763. {
  764. case osLocal:
  765. ProgressEvent.Side = TProgressEvent::LOCAL;
  766. break;
  767. case osRemote:
  768. ProgressEvent.Side = TProgressEvent::REMOTE;
  769. break;
  770. default:
  771. FAIL;
  772. }
  773. wcsncpy(ProgressEvent.FileName, Progress.FileName.c_str(), LENOF(ProgressEvent.FileName));
  774. NULL_TERMINATE(ProgressEvent.FileName);
  775. wcsncpy(ProgressEvent.Directory, Progress.Directory.c_str(), LENOF(ProgressEvent.Directory));
  776. NULL_TERMINATE(ProgressEvent.Directory);
  777. ProgressEvent.OverallProgress = Progress.OverallProgress;
  778. ProgressEvent.FileProgress = Progress.FileProgress;
  779. ProgressEvent.CPS = Progress.CPS;
  780. }
  781. __finally
  782. {
  783. FreeCommStruct(CommStruct);
  784. }
  785. SendEvent(INFINITE);
  786. // nothing to read from response, just wait for it
  787. FreeCommStruct(GetCommStruct());
  788. }
  789. //---------------------------------------------------------------------------
  790. class TNullConsole : public TConsole
  791. {
  792. public:
  793. __fastcall TNullConsole();
  794. virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false);
  795. virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer);
  796. virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
  797. int Timeouted, bool Timeouting, unsigned int Timer);
  798. virtual bool __fastcall PendingAbort();
  799. virtual void __fastcall SetTitle(UnicodeString Title);
  800. virtual bool __fastcall LimitedOutput();
  801. virtual bool __fastcall LiveOutput();
  802. virtual bool __fastcall NoInteractiveInput();
  803. virtual void __fastcall WaitBeforeExit();
  804. virtual bool __fastcall CommandLineOnly();
  805. virtual bool __fastcall WantsProgress();
  806. virtual void __fastcall Progress(const TScriptProgress & Progress);
  807. };
  808. //---------------------------------------------------------------------------
  809. __fastcall TNullConsole::TNullConsole()
  810. {
  811. }
  812. //---------------------------------------------------------------------------
  813. void __fastcall TNullConsole::Print(UnicodeString /*Str*/, bool /*FromBeginning*/)
  814. {
  815. // noop
  816. }
  817. //---------------------------------------------------------------------------
  818. bool __fastcall TNullConsole::Input(UnicodeString & /*Str*/, bool /*Echo*/,
  819. unsigned int /*Timer*/)
  820. {
  821. return false;
  822. }
  823. //---------------------------------------------------------------------------
  824. int __fastcall TNullConsole::Choice(UnicodeString /*Options*/, int /*Cancel*/,
  825. int Break, int Timeouted, bool Timeouting, unsigned int Timer)
  826. {
  827. int Result;
  828. if (Timeouting)
  829. {
  830. Sleep(Timer);
  831. Result = Timeouted;
  832. }
  833. else
  834. {
  835. Result = Break;
  836. }
  837. return Result;
  838. }
  839. //---------------------------------------------------------------------------
  840. bool __fastcall TNullConsole::PendingAbort()
  841. {
  842. return false;
  843. }
  844. //---------------------------------------------------------------------------
  845. void __fastcall TNullConsole::SetTitle(UnicodeString /*Title*/)
  846. {
  847. // noop
  848. }
  849. //---------------------------------------------------------------------------
  850. bool __fastcall TNullConsole::LimitedOutput()
  851. {
  852. return false;
  853. }
  854. //---------------------------------------------------------------------------
  855. bool __fastcall TNullConsole::LiveOutput()
  856. {
  857. return false;
  858. }
  859. //---------------------------------------------------------------------------
  860. bool __fastcall TNullConsole::NoInteractiveInput()
  861. {
  862. // do not matter, even if we return false,
  863. // it fails immediatelly afterwards in TNullConsole::Input
  864. return true;
  865. }
  866. //---------------------------------------------------------------------------
  867. void __fastcall TNullConsole::WaitBeforeExit()
  868. {
  869. assert(false);
  870. // noop
  871. }
  872. //---------------------------------------------------------------------------
  873. bool __fastcall TNullConsole::CommandLineOnly()
  874. {
  875. assert(false);
  876. return false;
  877. }
  878. //---------------------------------------------------------------------------
  879. bool __fastcall TNullConsole::WantsProgress()
  880. {
  881. return false;
  882. }
  883. //---------------------------------------------------------------------------
  884. void __fastcall TNullConsole::Progress(const TScriptProgress & /*Progress*/)
  885. {
  886. FAIL;
  887. }
  888. //---------------------------------------------------------------------------
  889. class TConsoleRunner
  890. {
  891. public:
  892. TConsoleRunner(TConsole * Console);
  893. ~TConsoleRunner();
  894. int __fastcall Run(const UnicodeString Session, TOptions * Options,
  895. TStrings * ScriptCommands, TStrings * ScriptParameters);
  896. void __fastcall ShowException(Exception * E);
  897. protected:
  898. bool __fastcall DoInput(UnicodeString & Str, bool Echo, unsigned int Timer,
  899. bool Interactive);
  900. void __fastcall Input(const UnicodeString Prompt, UnicodeString & Str,
  901. bool Echo, bool Interactive);
  902. inline void __fastcall Print(const UnicodeString & Str, bool FromBeginning = false);
  903. inline void __fastcall PrintLine(const UnicodeString & Str);
  904. inline void __fastcall PrintMessage(const UnicodeString & Str);
  905. void __fastcall UpdateTitle();
  906. inline void __fastcall NotifyAbort();
  907. inline bool __fastcall Aborted(bool AllowCompleteAbort = true);
  908. void __fastcall MasterPasswordPrompt();
  909. void __fastcall DoShowException(TTerminal * Terminal, Exception * E);
  910. private:
  911. TManagementScript * FScript;
  912. TConsole * FConsole;
  913. TSynchronizeController FSynchronizeController;
  914. int FLastProgressLen;
  915. bool FSynchronizeAborted;
  916. bool FCommandError;
  917. bool FBatchScript;
  918. bool FAborted;
  919. TTimer * Timer;
  920. void __fastcall ScriptPrint(TScript * Script, const UnicodeString Str);
  921. void __fastcall ScriptPrintProgress(TScript * Script, bool First, const UnicodeString Str);
  922. void __fastcall ScriptInput(TScript * Script, const UnicodeString Prompt, UnicodeString & Str);
  923. void __fastcall ScriptTerminalPromptUser(TTerminal * Terminal,
  924. TPromptKind Kind, UnicodeString Name, UnicodeString Instructions, TStrings * Prompts,
  925. TStrings * Results, bool & Result, void * Arg);
  926. void __fastcall ScriptShowExtendedException(TTerminal * Terminal,
  927. Exception * E, void * Arg);
  928. void __fastcall ScriptTerminalQueryUser(TObject * Sender, const UnicodeString Query,
  929. TStrings * MoreMessages, unsigned int Answers, const TQueryParams * Params, unsigned int & Answer,
  930. TQueryType QueryType, void * Arg);
  931. void __fastcall ScriptQueryCancel(TScript * Script, bool & Cancel);
  932. void __fastcall SynchronizeControllerAbort(TObject * Sender, bool Close);
  933. void __fastcall SynchronizeControllerLog(TSynchronizeController * Controller,
  934. TSynchronizeLogEntry Entry, const UnicodeString Message);
  935. void __fastcall ScriptSynchronizeStartStop(TScript * Script,
  936. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  937. const TCopyParamType & CopyParam, int SynchronizeParams);
  938. void __fastcall SynchronizeControllerSynchronize(TSynchronizeController * Sender,
  939. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  940. const TCopyParamType & CopyParam, const TSynchronizeParamType & Params,
  941. TSynchronizeChecklist ** Checklist, TSynchronizeOptions * Options, bool Full);
  942. void __fastcall SynchronizeControllerSynchronizeInvalid(TSynchronizeController * Sender,
  943. const UnicodeString Directory, const UnicodeString ErrorStr);
  944. void __fastcall SynchronizeControllerTooManyDirectories(TSynchronizeController * Sender,
  945. int & MaxDirectories);
  946. unsigned int InputTimeout();
  947. void __fastcall TimerTimer(TObject * Sender);
  948. UnicodeString ExpandCommand(UnicodeString Command, TStrings * ScriptParameters);
  949. void __fastcall Failed(bool & AnyError);
  950. void __fastcall ScriptProgress(TScript * Script, const TScriptProgress & Progress);
  951. void __fastcall ConfigurationChange(TObject * Sender);
  952. };
  953. //---------------------------------------------------------------------------
  954. TConsoleRunner::TConsoleRunner(TConsole * Console) :
  955. FSynchronizeController(&SynchronizeControllerSynchronize,
  956. &SynchronizeControllerSynchronizeInvalid,
  957. &SynchronizeControllerTooManyDirectories)
  958. {
  959. FConsole = Console;
  960. FLastProgressLen = 0;
  961. FScript = NULL;
  962. FAborted = false;
  963. FBatchScript = false;
  964. Timer = new TTimer(Application);
  965. Timer->OnTimer = TimerTimer;
  966. Timer->Interval = MSecsPerSec;
  967. Timer->Enabled = true;
  968. assert(WinConfiguration->OnMasterPasswordPrompt == NULL);
  969. WinConfiguration->OnMasterPasswordPrompt = MasterPasswordPrompt;
  970. assert(Configuration->OnChange == NULL);
  971. Configuration->OnChange = ConfigurationChange;
  972. }
  973. //---------------------------------------------------------------------------
  974. TConsoleRunner::~TConsoleRunner()
  975. {
  976. assert(WinConfiguration->OnMasterPasswordPrompt == MasterPasswordPrompt);
  977. WinConfiguration->OnMasterPasswordPrompt = NULL;
  978. assert(Configuration->OnChange == ConfigurationChange);
  979. Configuration->OnChange = NULL;
  980. delete Timer;
  981. }
  982. //---------------------------------------------------------------------------
  983. void __fastcall TConsoleRunner::TimerTimer(TObject * /*Sender*/)
  984. {
  985. // sole presence of timer causes message to be dispatched,
  986. // hence breaks the loops
  987. }
  988. //---------------------------------------------------------------------------
  989. unsigned int TConsoleRunner::InputTimeout()
  990. {
  991. return ((FScript != NULL) && (FScript->Batch != TScript::BatchOff) ? BATCH_INPUT_TIMEOUT : 0);
  992. }
  993. //---------------------------------------------------------------------------
  994. void __fastcall TConsoleRunner::Input(
  995. const UnicodeString Prompt, UnicodeString & Str, bool Echo, bool Interactive)
  996. {
  997. Print(Prompt);
  998. if (!DoInput(Str, Echo, InputTimeout(), Interactive))
  999. {
  1000. Abort();
  1001. }
  1002. }
  1003. //---------------------------------------------------------------------------
  1004. void __fastcall TConsoleRunner::ScriptInput(TScript * /*Script*/,
  1005. const UnicodeString Prompt, UnicodeString & Str)
  1006. {
  1007. Input(Prompt, Str, true, true);
  1008. }
  1009. //---------------------------------------------------------------------------
  1010. void __fastcall TConsoleRunner::Print(const UnicodeString & Str, bool FromBeginning)
  1011. {
  1012. if (FLastProgressLen > 0)
  1013. {
  1014. FConsole->Print(L"\n" + Str, FromBeginning);
  1015. FLastProgressLen = 0;
  1016. }
  1017. else
  1018. {
  1019. FConsole->Print(Str, FromBeginning);
  1020. }
  1021. }
  1022. //---------------------------------------------------------------------------
  1023. void __fastcall TConsoleRunner::PrintLine(const UnicodeString & Str)
  1024. {
  1025. Print(Str + L"\n");
  1026. }
  1027. //---------------------------------------------------------------------------
  1028. void __fastcall TConsoleRunner::PrintMessage(const UnicodeString & Str)
  1029. {
  1030. UnicodeString Line =
  1031. ReplaceStr(ReplaceStr(Str.TrimRight(), L"\n\n", L"\n"),
  1032. L"\n \n", L"\n");
  1033. if (FScript != NULL)
  1034. {
  1035. // this also logs the message
  1036. FScript->PrintLine(Line);
  1037. }
  1038. else
  1039. {
  1040. PrintLine(Line);
  1041. }
  1042. }
  1043. //---------------------------------------------------------------------------
  1044. void __fastcall TConsoleRunner::NotifyAbort()
  1045. {
  1046. if (FBatchScript)
  1047. {
  1048. FAborted = true;
  1049. }
  1050. }
  1051. //---------------------------------------------------------------------------
  1052. bool __fastcall TConsoleRunner::Aborted(bool AllowCompleteAbort)
  1053. {
  1054. bool Result;
  1055. if (FAborted)
  1056. {
  1057. Result = true;
  1058. }
  1059. else
  1060. {
  1061. Result = FConsole->PendingAbort();
  1062. if (Result)
  1063. {
  1064. PrintMessage(LoadStr(USER_TERMINATED));
  1065. if (AllowCompleteAbort)
  1066. {
  1067. NotifyAbort();
  1068. }
  1069. }
  1070. }
  1071. return Result;
  1072. }
  1073. //---------------------------------------------------------------------------
  1074. void __fastcall TConsoleRunner::ScriptPrint(TScript * /*Script*/,
  1075. const UnicodeString Str)
  1076. {
  1077. Print(Str);
  1078. }
  1079. //---------------------------------------------------------------------------
  1080. void __fastcall TConsoleRunner::ScriptPrintProgress(TScript * /*Script*/,
  1081. bool First, const UnicodeString Str)
  1082. {
  1083. UnicodeString S = Str;
  1084. if (First && (FLastProgressLen > 0))
  1085. {
  1086. S = L"\n" + S;
  1087. }
  1088. else if (S.Length() < FLastProgressLen)
  1089. {
  1090. int Padding = FLastProgressLen - S.Length();
  1091. S += UnicodeString::StringOfChar(L' ', Padding) +
  1092. UnicodeString::StringOfChar(L'\b', Padding);
  1093. }
  1094. FConsole->Print(S, true);
  1095. FLastProgressLen = Str.Length();
  1096. }
  1097. //---------------------------------------------------------------------------
  1098. void __fastcall TConsoleRunner::ScriptTerminalPromptUser(TTerminal * /*Terminal*/,
  1099. TPromptKind /*Kind*/, UnicodeString Name, UnicodeString Instructions, TStrings * Prompts,
  1100. TStrings * Results, bool & Result, void * /*Arg*/)
  1101. {
  1102. if (!Instructions.IsEmpty())
  1103. {
  1104. PrintMessage(Instructions);
  1105. }
  1106. // if there are no prompts, success is default
  1107. Result = true;
  1108. for (int Index = 0; Index < Prompts->Count; Index++)
  1109. {
  1110. UnicodeString Prompt = Prompts->Strings[Index];
  1111. if (!Prompt.IsEmpty() && (Prompt[Prompt.Length()] != L' '))
  1112. {
  1113. Prompt += L' ';
  1114. }
  1115. int P = Prompt.Pos(L'&');
  1116. if (P > 0)
  1117. {
  1118. Prompt.Delete(P, 1);
  1119. }
  1120. Print(Prompt);
  1121. UnicodeString AResult = Results->Strings[Index]; // useless
  1122. bool Echo = FLAGSET(int(Prompts->Objects[Index]), pupEcho);
  1123. Result = DoInput(AResult, Echo, InputTimeout(), true);
  1124. Results->Strings[Index] = AResult;
  1125. }
  1126. }
  1127. //---------------------------------------------------------------------------
  1128. void __fastcall TConsoleRunner::ScriptShowExtendedException(
  1129. TTerminal * Terminal, Exception * E, void * /*Arg*/)
  1130. {
  1131. DoShowException(Terminal, E);
  1132. }
  1133. //---------------------------------------------------------------------------
  1134. void __fastcall TConsoleRunner::ScriptTerminalQueryUser(TObject * /*Sender*/,
  1135. const UnicodeString Query, TStrings * MoreMessages, unsigned int Answers,
  1136. const TQueryParams * Params, unsigned int & Answer, TQueryType /*QueryType*/,
  1137. void * /*Arg*/)
  1138. {
  1139. UnicodeString AQuery = Query;
  1140. unsigned int Timer = 0;
  1141. unsigned int Timeout = 0;
  1142. unsigned int TimeoutA = 0;
  1143. unsigned int NoBatchA = 0;
  1144. if (Params != NULL)
  1145. {
  1146. if (Params->Timeout > 0)
  1147. {
  1148. Timeout = Params->Timeout;
  1149. TimeoutA = Params->TimeoutAnswer;
  1150. }
  1151. if (Params->Timer > 0)
  1152. {
  1153. Timer = Params->Timer;
  1154. if (Params->TimerAnswers > 0)
  1155. {
  1156. Answers = Params->TimerAnswers;
  1157. }
  1158. // not considering TimerQueryType as we do not use QueryType anyway
  1159. if (!Params->TimerMessage.IsEmpty())
  1160. {
  1161. AQuery = Params->TimerMessage;
  1162. }
  1163. }
  1164. if (FLAGSET(Params->Params, qpFatalAbort))
  1165. {
  1166. AQuery = FMTLOAD(WARN_FATAL_ERROR, (AQuery));
  1167. }
  1168. NoBatchA = Params->NoBatchAnswers;
  1169. }
  1170. AQuery = UnformatMessage(AQuery);
  1171. ApplyTabs(AQuery, L' ', NULL, NULL);
  1172. unsigned int AAnswers = Answers;
  1173. PrintMessage(AQuery);
  1174. if ((MoreMessages != NULL) && (MoreMessages->Count > 0))
  1175. {
  1176. PrintMessage(MoreMessages->Text);
  1177. }
  1178. std::vector<unsigned int> Buttons;
  1179. std::vector<UnicodeString> Captions;
  1180. std::vector<TNotifyEvent> OnClicks;
  1181. for (unsigned int Answer = qaFirst; Answer <= qaLast; Answer = Answer << 1)
  1182. {
  1183. if (FLAGSET(Answers, Answer))
  1184. {
  1185. UnicodeString Name; // unused
  1186. UnicodeString Caption;
  1187. AnswerNameAndCaption(Answer, Name, Caption);
  1188. Captions.push_back(Caption);
  1189. Buttons.push_back(Answer);
  1190. OnClicks.push_back(NULL);
  1191. AAnswers -= Answer;
  1192. }
  1193. }
  1194. USEDPARAM(AAnswers);
  1195. assert(AAnswers == 0);
  1196. assert(!Buttons.empty());
  1197. if ((Params != NULL) && (Params->Aliases != NULL))
  1198. {
  1199. for (unsigned int bi = 0; bi < Buttons.size(); bi++)
  1200. {
  1201. for (unsigned int ai = 0; ai < Params->AliasesCount; ai++)
  1202. {
  1203. if (Params->Aliases[ai].Button == Buttons[bi])
  1204. {
  1205. if (!Params->Aliases[ai].Alias.IsEmpty())
  1206. {
  1207. Captions[bi] = Params->Aliases[ai].Alias;
  1208. }
  1209. OnClicks[bi] = Params->Aliases[ai].OnClick;
  1210. break;
  1211. }
  1212. }
  1213. }
  1214. }
  1215. UnicodeString Accels;
  1216. for (unsigned int Index = 0; Index < Buttons.size(); Index++)
  1217. {
  1218. UnicodeString & Caption = Captions[Index];
  1219. int P = Caption.Pos(L'&');
  1220. if ((P > 0) && (P < Caption.Length()))
  1221. {
  1222. wchar_t Accel = AnsiUpperCase(Caption)[P + 1];
  1223. if (Accels.Pos(Accel) > 0)
  1224. {
  1225. Caption.Delete(P, 1);
  1226. Accels += L' ';
  1227. }
  1228. else
  1229. {
  1230. Accels += Accel;
  1231. }
  1232. }
  1233. else
  1234. {
  1235. Accels += L' ';
  1236. }
  1237. }
  1238. assert(Accels.Length() == static_cast<int>(Buttons.size()));
  1239. int NumberAccel = 0;
  1240. unsigned int CancelA = CancelAnswer(Answers);
  1241. int CancelIndex;
  1242. unsigned int AbortA = AbortAnswer(Answers & ~NoBatchA);
  1243. int AbortIndex;
  1244. unsigned int ContinueA = ContinueAnswer(Answers & ~NoBatchA);
  1245. int ContinueIndex;
  1246. int TimeoutIndex = 0;
  1247. for (unsigned int Index = 0; Index < Buttons.size(); Index++)
  1248. {
  1249. UnicodeString & Caption = Captions[Index];
  1250. if (Accels[Index + 1] == L' ')
  1251. {
  1252. for (int Index2 = 1; Index2 <= Caption.Length(); Index2++)
  1253. {
  1254. wchar_t C = AnsiUpperCase(Caption)[Index2];
  1255. if (IsLetter(C) && (Accels.Pos(C) == 0))
  1256. {
  1257. Caption.Insert(L"&", Index2);
  1258. Accels[Index + 1] = C;
  1259. break;
  1260. }
  1261. }
  1262. }
  1263. if (Accels[Index + 1] == L' ')
  1264. {
  1265. for (int Index2 = 1; Index2 <= Caption.Length(); Index2++)
  1266. {
  1267. wchar_t C = AnsiUpperCase(Caption)[Index2];
  1268. if ((C != L' ') && (Accels.Pos(C) == 0))
  1269. {
  1270. Caption.Insert(L"&", Index2);
  1271. Accels[Index + 1] = C;
  1272. break;
  1273. }
  1274. }
  1275. }
  1276. if (Accels[Index + 1] == L' ')
  1277. {
  1278. NumberAccel++;
  1279. assert(NumberAccel <= 9);
  1280. Caption = FORMAT(L"&%d%s", (NumberAccel, Caption));
  1281. Accels[Index + 1] = Caption[2];
  1282. }
  1283. if (Buttons[Index] == CancelA)
  1284. {
  1285. CancelIndex = Index + 1;
  1286. }
  1287. if (Buttons[Index] == AbortA)
  1288. {
  1289. AbortIndex = Index + 1;
  1290. }
  1291. if (Buttons[Index] == ContinueA)
  1292. {
  1293. ContinueIndex = Index + 1;
  1294. }
  1295. if (Buttons[Index] == TimeoutA)
  1296. {
  1297. TimeoutIndex = Index + 1;
  1298. }
  1299. }
  1300. assert(Accels.Pos(L' ') == 0);
  1301. bool Timeouting = (Timeout > 0);
  1302. bool FirstOutput = true;
  1303. do
  1304. {
  1305. Answer = 0;
  1306. int AnswerIndex;
  1307. bool Retry;
  1308. do
  1309. {
  1310. Retry = false;
  1311. if (FirstOutput || FConsole->LiveOutput())
  1312. {
  1313. UnicodeString Output;
  1314. for (unsigned int i = 0; i < Buttons.size(); i++)
  1315. {
  1316. if (i > 0)
  1317. {
  1318. Output += L", ";
  1319. }
  1320. UnicodeString Caption = Captions[i];
  1321. int P = Caption.Pos(L'&');
  1322. if (ALWAYS_TRUE(P >= 0))
  1323. {
  1324. Caption[P] = L'(';
  1325. Caption.Insert(L")", P + 2);
  1326. }
  1327. if (i + 1 == static_cast<unsigned int>(TimeoutIndex))
  1328. {
  1329. assert(Timeouting);
  1330. Caption = FMTLOAD(TIMEOUT_BUTTON, (Caption, int(Timeout / MSecsPerSec)));
  1331. }
  1332. Output += Caption;
  1333. }
  1334. Output += L": ";
  1335. // note that length of string may decrease over time due to number of
  1336. // seconds length, but supposing it decreases by one character at time
  1337. // at most, we do not mind as the prompt is terminated with space
  1338. // If output is not live (file or pipe), do not use 'from beginning'
  1339. // as it means that the output is not actually stored until new line
  1340. // is sent (and we will not [because we cannot] rewrite the output anyway)
  1341. Print(Output, !FirstOutput);
  1342. FirstOutput = false;
  1343. }
  1344. if (!Timeouting && (FScript->Batch == TScript::BatchContinue))
  1345. {
  1346. AnswerIndex = ContinueIndex;
  1347. }
  1348. else if (!Timeouting && (FScript->Batch != TScript::BatchOff))
  1349. {
  1350. AnswerIndex = AbortIndex;
  1351. }
  1352. else if (Timeouting && (Timeout < MSecsPerSec))
  1353. {
  1354. AnswerIndex = TimeoutIndex;
  1355. }
  1356. else
  1357. {
  1358. unsigned int ActualTimer =
  1359. (Timeouting && ((Timer == 0) || (Timer > MSecsPerSec)) ? MSecsPerSec : Timer);
  1360. AnswerIndex = FConsole->Choice(Accels, CancelIndex, -1, -2,
  1361. Timeouting, ActualTimer);
  1362. if (AnswerIndex == -1)
  1363. {
  1364. NotifyAbort();
  1365. AnswerIndex = AbortIndex;
  1366. }
  1367. else if (AnswerIndex == -2)
  1368. {
  1369. if (Timeouting)
  1370. {
  1371. assert(Timeout >= MSecsPerSec);
  1372. Timeout -= ActualTimer;
  1373. Retry = true;
  1374. }
  1375. // this does not take Timer into account,
  1376. // but as of now Timer is used for TSecureShell timeout prompt only,
  1377. // where Timer is less than MSecsPerSec
  1378. if (Timer > 0)
  1379. {
  1380. assert((Params != NULL) && (Params->TimerEvent != NULL));
  1381. if ((Params != NULL) && (Params->TimerEvent != NULL))
  1382. {
  1383. unsigned int AAnswer = 0;
  1384. Params->TimerEvent(AAnswer);
  1385. if (AAnswer != 0)
  1386. {
  1387. Answer = AAnswer;
  1388. Retry = false;
  1389. }
  1390. else
  1391. {
  1392. Retry = true;
  1393. }
  1394. }
  1395. }
  1396. }
  1397. }
  1398. }
  1399. while (Retry);
  1400. if (Answer == 0)
  1401. {
  1402. assert((AnswerIndex >= 1) && (AnswerIndex <= Accels.Length()));
  1403. UnicodeString AnswerCaption = Captions[AnswerIndex - 1];
  1404. int P = AnswerCaption.Pos(L"&");
  1405. assert(P >= 0);
  1406. AnswerCaption.Delete(P, 1);
  1407. PrintLine(AnswerCaption);
  1408. FirstOutput = true;
  1409. if (OnClicks[AnswerIndex - 1] != NULL)
  1410. {
  1411. OnClicks[AnswerIndex - 1](NULL);
  1412. }
  1413. else
  1414. {
  1415. Answer = Buttons[AnswerIndex - 1];
  1416. }
  1417. }
  1418. else
  1419. {
  1420. PrintLine(L"");
  1421. }
  1422. }
  1423. while (Answer == 0);
  1424. if ((Answer == AbortA) && FLAGCLEAR(Params->Params, qpIgnoreAbort))
  1425. {
  1426. if (FScript->Terminal != NULL)
  1427. {
  1428. TStrings * Messages = new TStringList();
  1429. try
  1430. {
  1431. Messages->Add(Query);
  1432. if (MoreMessages != NULL)
  1433. {
  1434. Messages->AddStrings(MoreMessages);
  1435. }
  1436. FScript->Terminal->ActionLog->AddFailure(Messages);
  1437. }
  1438. __finally
  1439. {
  1440. delete Messages;
  1441. }
  1442. }
  1443. FCommandError = true;
  1444. }
  1445. }
  1446. //---------------------------------------------------------------------------
  1447. void __fastcall TConsoleRunner::ScriptQueryCancel(TScript * /*Script*/, bool & Cancel)
  1448. {
  1449. if (Aborted())
  1450. {
  1451. Cancel = true;
  1452. }
  1453. }
  1454. //---------------------------------------------------------------------------
  1455. void __fastcall TConsoleRunner::ScriptSynchronizeStartStop(TScript * /*Script*/,
  1456. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  1457. const TCopyParamType & CopyParam, int SynchronizeParams)
  1458. {
  1459. TSynchronizeParamType Params;
  1460. Params.LocalDirectory = LocalDirectory;
  1461. Params.RemoteDirectory = RemoteDirectory;
  1462. Params.Params = SynchronizeParams;
  1463. Params.Options = soRecurse;
  1464. FSynchronizeController.StartStop(Application, true, Params,
  1465. CopyParam, NULL, SynchronizeControllerAbort, NULL,
  1466. SynchronizeControllerLog);
  1467. try
  1468. {
  1469. FSynchronizeAborted = false;
  1470. while (!FSynchronizeAborted && !Aborted(false))
  1471. {
  1472. Application->HandleMessage();
  1473. FScript->Terminal->Idle();
  1474. }
  1475. }
  1476. __finally
  1477. {
  1478. FSynchronizeController.StartStop(Application, false, Params,
  1479. CopyParam, NULL, SynchronizeControllerAbort, NULL,
  1480. SynchronizeControllerLog);
  1481. }
  1482. }
  1483. //---------------------------------------------------------------------------
  1484. void __fastcall TConsoleRunner::ScriptProgress(TScript * /*Script*/, const TScriptProgress & Progress)
  1485. {
  1486. FConsole->Progress(Progress);
  1487. }
  1488. //---------------------------------------------------------------------------
  1489. void __fastcall TConsoleRunner::SynchronizeControllerLog(
  1490. TSynchronizeController * /*Controller*/, TSynchronizeLogEntry /*Entry*/,
  1491. const UnicodeString Message)
  1492. {
  1493. PrintMessage(Message);
  1494. }
  1495. //---------------------------------------------------------------------------
  1496. void __fastcall TConsoleRunner::SynchronizeControllerAbort(TObject * /*Sender*/,
  1497. bool /*Close*/)
  1498. {
  1499. FSynchronizeAborted = true;
  1500. }
  1501. //---------------------------------------------------------------------------
  1502. void __fastcall TConsoleRunner::SynchronizeControllerSynchronize(
  1503. TSynchronizeController * /*Sender*/, const UnicodeString LocalDirectory,
  1504. const UnicodeString RemoteDirectory, const TCopyParamType & CopyParam,
  1505. const TSynchronizeParamType & Params, TSynchronizeChecklist ** Checklist,
  1506. TSynchronizeOptions * /*Options*/, bool Full)
  1507. {
  1508. if (!Full)
  1509. {
  1510. FScript->Synchronize(LocalDirectory, RemoteDirectory, CopyParam,
  1511. Params.Params, Checklist);
  1512. }
  1513. }
  1514. //---------------------------------------------------------------------------
  1515. void __fastcall TConsoleRunner::SynchronizeControllerSynchronizeInvalid(
  1516. TSynchronizeController * /*Sender*/, const UnicodeString Directory, const UnicodeString ErrorStr)
  1517. {
  1518. if (!Directory.IsEmpty())
  1519. {
  1520. PrintMessage(FMTLOAD(WATCH_ERROR_DIRECTORY, (Directory)));
  1521. }
  1522. else
  1523. {
  1524. PrintMessage(LoadStr(WATCH_ERROR_GENERAL));
  1525. }
  1526. if (!ErrorStr.IsEmpty())
  1527. {
  1528. PrintMessage(ErrorStr);
  1529. }
  1530. }
  1531. //---------------------------------------------------------------------------
  1532. void __fastcall TConsoleRunner::SynchronizeControllerTooManyDirectories(
  1533. TSynchronizeController * /*Sender*/, int & MaxDirectories)
  1534. {
  1535. if (Aborted())
  1536. {
  1537. Abort();
  1538. }
  1539. if (MaxDirectories < GUIConfiguration->MaxWatchDirectories)
  1540. {
  1541. MaxDirectories = GUIConfiguration->MaxWatchDirectories;
  1542. }
  1543. else
  1544. {
  1545. MaxDirectories *= 2;
  1546. }
  1547. }
  1548. //---------------------------------------------------------------------------
  1549. void __fastcall TConsoleRunner::ShowException(Exception * E)
  1550. {
  1551. DoShowException(NULL, E);
  1552. }
  1553. //---------------------------------------------------------------------------
  1554. void __fastcall TConsoleRunner::DoShowException(TTerminal * Terminal, Exception * E)
  1555. {
  1556. if ((Terminal == NULL) && (FScript != NULL))
  1557. {
  1558. Terminal = FScript->Terminal;
  1559. }
  1560. UnicodeString Message;
  1561. if (ExceptionMessage(E, Message))
  1562. {
  1563. FCommandError = true;
  1564. PrintMessage(Message);
  1565. ExtException * EE = dynamic_cast<ExtException *>(E);
  1566. if ((EE != NULL) && (EE->MoreMessages != NULL))
  1567. {
  1568. PrintMessage(EE->MoreMessages->Text);
  1569. }
  1570. }
  1571. TTerminal * LoggingTerminal = Terminal;
  1572. TSecondaryTerminal * SecondaryTerminal = dynamic_cast<TSecondaryTerminal *>(LoggingTerminal);
  1573. if (SecondaryTerminal != NULL)
  1574. {
  1575. LoggingTerminal = SecondaryTerminal->MainTerminal;
  1576. }
  1577. if (LoggingTerminal != NULL)
  1578. {
  1579. LoggingTerminal->ActionLog->AddFailure(E);
  1580. }
  1581. }
  1582. //---------------------------------------------------------------------------
  1583. bool __fastcall TConsoleRunner::DoInput(UnicodeString & Str, bool Echo,
  1584. unsigned int Timeout, bool Interactive)
  1585. {
  1586. bool Result;
  1587. if (Interactive && FConsole->NoInteractiveInput())
  1588. {
  1589. Result = false;
  1590. }
  1591. else
  1592. {
  1593. Result = FConsole->Input(Str, Echo, Timeout);
  1594. }
  1595. if (Result)
  1596. {
  1597. while (!Str.IsEmpty() &&
  1598. ((Str[Str.Length()] == L'\n') || (Str[Str.Length()] == L'\r')))
  1599. {
  1600. Str.SetLength(Str.Length() - 1);
  1601. }
  1602. }
  1603. else
  1604. {
  1605. NotifyAbort();
  1606. }
  1607. return Result;
  1608. }
  1609. //---------------------------------------------------------------------------
  1610. void __fastcall TConsoleRunner::MasterPasswordPrompt()
  1611. {
  1612. bool Retry;
  1613. do
  1614. {
  1615. UnicodeString Password;
  1616. Input(LoadStr(CONSOLE_MASTER_PASSWORD_PROMPT), Password, false, true);
  1617. Retry = !WinConfiguration->ValidateMasterPassword(Password);
  1618. if (Retry)
  1619. {
  1620. PrintLine(LoadStr(MASTER_PASSWORD_INCORRECT));
  1621. }
  1622. else
  1623. {
  1624. WinConfiguration->SetMasterPassword(Password);
  1625. }
  1626. }
  1627. while (Retry);
  1628. }
  1629. //---------------------------------------------------------------------------
  1630. UnicodeString TConsoleRunner::ExpandCommand(UnicodeString Command, TStrings * ScriptParameters)
  1631. {
  1632. assert(ScriptParameters != NULL);
  1633. for (int Index = 0; Index < ScriptParameters->Count; Index++)
  1634. {
  1635. Command = ReplaceStr(Command, FORMAT(L"%%%d%%", (Index+1)),
  1636. ScriptParameters->Strings[Index]);
  1637. }
  1638. Command = ExpandEnvironmentVariables(Command);
  1639. return Command;
  1640. }
  1641. //---------------------------------------------------------------------------
  1642. void __fastcall TConsoleRunner::Failed(bool & AnyError)
  1643. {
  1644. if (FScript != NULL)
  1645. {
  1646. FScript->Log(llMessage, L"Failed");
  1647. }
  1648. AnyError = true;
  1649. }
  1650. //---------------------------------------------------------------------------
  1651. int __fastcall TConsoleRunner::Run(const UnicodeString Session, TOptions * Options,
  1652. TStrings * ScriptCommands, TStrings * ScriptParameters)
  1653. {
  1654. int ExitCode;
  1655. try
  1656. {
  1657. bool AnyError = false;
  1658. try
  1659. {
  1660. FScript = new TManagementScript(StoredSessions, FConsole->LimitedOutput());
  1661. FScript->CopyParam = GUIConfiguration->DefaultCopyParam;
  1662. FScript->SynchronizeParams = GUIConfiguration->SynchronizeParams;
  1663. FScript->WantsProgress = FConsole->WantsProgress();
  1664. FScript->OnPrint = ScriptPrint;
  1665. FScript->OnPrintProgress = ScriptPrintProgress;
  1666. FScript->OnInput = ScriptInput;
  1667. FScript->OnTerminalPromptUser = ScriptTerminalPromptUser;
  1668. FScript->OnShowExtendedException = ScriptShowExtendedException;
  1669. FScript->OnTerminalQueryUser = ScriptTerminalQueryUser;
  1670. FScript->OnQueryCancel = ScriptQueryCancel;
  1671. FScript->OnSynchronizeStartStop = ScriptSynchronizeStartStop;
  1672. FScript->OnProgress = ScriptProgress;
  1673. UpdateTitle();
  1674. // everything until the first manually entered command is "batch"
  1675. // (including opening session from command line and script file)
  1676. FBatchScript = true;
  1677. if (!Session.IsEmpty())
  1678. {
  1679. PrintMessage(LoadStr(SCRIPT_CMDLINE_SESSION));
  1680. FScript->Connect(Session, Options, false);
  1681. if (FCommandError)
  1682. {
  1683. Failed(AnyError);
  1684. }
  1685. }
  1686. FScript->Groups = Options->SwitchValue(L"xmlgroups", true, false);
  1687. int ScriptPos = 0;
  1688. bool Result;
  1689. do
  1690. {
  1691. UpdateTitle();
  1692. UnicodeString Command;
  1693. if ((ScriptCommands != NULL) && (ScriptPos < ScriptCommands->Count))
  1694. {
  1695. Result = true;
  1696. Command = ScriptCommands->Strings[ScriptPos];
  1697. ScriptPos++;
  1698. }
  1699. else
  1700. {
  1701. // no longer batch
  1702. FBatchScript = false;
  1703. Print(L"winscp> ");
  1704. Result = DoInput(Command, true, 0, false);
  1705. }
  1706. if (Result)
  1707. {
  1708. FCommandError = false;
  1709. FScript->Command(ExpandCommand(Command, ScriptParameters));
  1710. if (FCommandError)
  1711. {
  1712. Failed(AnyError);
  1713. if (FScript->Batch == TScript::BatchAbort)
  1714. {
  1715. Result = false;
  1716. }
  1717. }
  1718. if (FScript->Terminal != NULL)
  1719. {
  1720. FScript->Terminal->Idle();
  1721. }
  1722. }
  1723. }
  1724. while (Result && FScript->Continue && !Aborted());
  1725. }
  1726. catch(Exception & E)
  1727. {
  1728. Failed(AnyError);
  1729. ShowException(&E);
  1730. }
  1731. ExitCode = AnyError ? RESULT_ANY_ERROR : RESULT_SUCCESS;
  1732. if (FScript != NULL)
  1733. {
  1734. FScript->Log(llMessage, FORMAT(L"Exit code: %d", (ExitCode)));
  1735. }
  1736. }
  1737. __finally
  1738. {
  1739. delete FScript;
  1740. FScript = NULL;
  1741. }
  1742. return ExitCode;
  1743. }
  1744. //---------------------------------------------------------------------------
  1745. void __fastcall TConsoleRunner::UpdateTitle()
  1746. {
  1747. UnicodeString NewTitle;
  1748. if (FScript->Terminal != NULL)
  1749. {
  1750. NewTitle = FMTLOAD(APP_CAPTION, (FScript->Terminal->SessionData->SessionName, AppName));
  1751. }
  1752. else
  1753. {
  1754. NewTitle = AppName;
  1755. }
  1756. FConsole->SetTitle(NewTitle);
  1757. }
  1758. //---------------------------------------------------------------------------
  1759. void __fastcall TConsoleRunner::ConfigurationChange(TObject * /*Sender*/)
  1760. {
  1761. if (FScript != NULL)
  1762. {
  1763. FScript->ReflectSettings();
  1764. }
  1765. }
  1766. //---------------------------------------------------------------------------
  1767. void __fastcall LoadScriptFromFile(UnicodeString FileName, TStrings * Lines)
  1768. {
  1769. Lines->LoadFromFile(FileName);
  1770. }
  1771. //---------------------------------------------------------------------------
  1772. void __fastcall Usage(TConsole * Console)
  1773. {
  1774. UnicodeString Usage = LoadStr(USAGE8, 10240);
  1775. UnicodeString ExeBaseName = ExtractFileBaseName(Application->ExeName);
  1776. Usage = ReplaceText(Usage, L"%APP%", ExeBaseName);
  1777. UnicodeString Copyright =
  1778. ReplaceText(LoadStr(WINSCP_COPYRIGHT), L"©", L"(c)");
  1779. Usage = FORMAT(Usage, (Configuration->VersionStr, Copyright));
  1780. TStrings * Lines = new TStringList();
  1781. try
  1782. {
  1783. Lines->Text = Usage;
  1784. for (int Index = 0; Index < Lines->Count; Index++)
  1785. {
  1786. bool Print = true;
  1787. UnicodeString Line = Lines->Strings[Index];
  1788. if ((Line.Length() >= 2) && (Line[2] == L':'))
  1789. {
  1790. switch (Line[1])
  1791. {
  1792. case L'G':
  1793. Print = !Console->CommandLineOnly();
  1794. break;
  1795. case L'C':
  1796. Print = Console->CommandLineOnly();
  1797. break;
  1798. case L'B':
  1799. Print = true;
  1800. break;
  1801. default:
  1802. assert(false);
  1803. break;
  1804. }
  1805. Line.Delete(1, 2);
  1806. }
  1807. if (Print)
  1808. {
  1809. Console->Print(Line + L"\n");
  1810. }
  1811. }
  1812. }
  1813. __finally
  1814. {
  1815. delete Lines;
  1816. }
  1817. Console->WaitBeforeExit();
  1818. }
  1819. //---------------------------------------------------------------------------
  1820. int __fastcall Console(bool Help)
  1821. {
  1822. TProgramParams * Params = TProgramParams::Instance();
  1823. int Result = 0;
  1824. TConsole * Console = NULL;
  1825. TConsoleRunner * Runner = NULL;
  1826. TStrings * ScriptCommands = new TStringList();
  1827. TStrings * ScriptParameters = new TStringList();
  1828. try
  1829. {
  1830. UnicodeString ConsoleInstance;
  1831. if (Params->FindSwitch(L"consoleinstance", ConsoleInstance))
  1832. {
  1833. Configuration->Usage->Inc(L"ConsoleExternal");
  1834. Console = new TExternalConsole(ConsoleInstance, Params->FindSwitch(L"nointeractiveinput"));
  1835. }
  1836. else if (Params->FindSwitch(L"Console") || Help)
  1837. {
  1838. Configuration->Usage->Inc(L"ConsoleOwn");
  1839. Console = TOwnConsole::Instance();
  1840. }
  1841. else
  1842. {
  1843. Configuration->Usage->Inc(L"ConsoleNull");
  1844. Console = new TNullConsole();
  1845. }
  1846. if (Help)
  1847. {
  1848. Usage(Console);
  1849. }
  1850. else
  1851. {
  1852. Runner = new TConsoleRunner(Console);
  1853. try
  1854. {
  1855. UnicodeString Value;
  1856. if (Params->FindSwitch(L"script", Value) && !Value.IsEmpty())
  1857. {
  1858. Configuration->Usage->Inc(L"ScriptFile");
  1859. LoadScriptFromFile(Value, ScriptCommands);
  1860. }
  1861. Params->FindSwitch(L"command", ScriptCommands);
  1862. if (ScriptCommands->Count > 0)
  1863. {
  1864. Configuration->Usage->Inc(L"ScriptCommands");
  1865. }
  1866. Params->FindSwitch(L"parameter", ScriptParameters);
  1867. if (ScriptParameters->Count > 0)
  1868. {
  1869. Configuration->Usage->Inc(L"ScriptParameters");
  1870. }
  1871. bool Url = false;
  1872. UnicodeString Session;
  1873. if (Params->ParamCount >= 1)
  1874. {
  1875. Session = Params->Param[1];
  1876. }
  1877. bool DefaultsOnly;
  1878. delete StoredSessions->ParseUrl(Session, Params, DefaultsOnly,
  1879. NULL, &Url);
  1880. if (Url || Params->FindSwitch(L"Unsafe"))
  1881. {
  1882. // prevent any automatic action when URL is provided on
  1883. // command-line (the check is duplicated in Execute())
  1884. if ((ScriptCommands->Count > 0) || Params->FindSwitch(L"Log") || Params->FindSwitch(L"XmlLog"))
  1885. {
  1886. Console->Print(LoadStr(UNSAFE_ACTIONS_DISABLED) + L"\n");
  1887. }
  1888. ScriptCommands->Clear();
  1889. }
  1890. else
  1891. {
  1892. UnicodeString LogFile;
  1893. if (Params->FindSwitch(L"Log", LogFile))
  1894. {
  1895. Configuration->Usage->Inc(L"ScriptLog");
  1896. Configuration->TemporaryLogging(LogFile);
  1897. }
  1898. if (Params->FindSwitch(L"XmlLog", LogFile))
  1899. {
  1900. Configuration->Usage->Inc(L"ScriptXmlLog");
  1901. Configuration->TemporaryActionsLogging(LogFile);
  1902. }
  1903. }
  1904. Result = Runner->Run(Session, Params,
  1905. (ScriptCommands->Count > 0 ? ScriptCommands : NULL),
  1906. ScriptParameters);
  1907. }
  1908. catch(Exception & E)
  1909. {
  1910. Runner->ShowException(&E);
  1911. Result = RESULT_ANY_ERROR;
  1912. }
  1913. }
  1914. }
  1915. __finally
  1916. {
  1917. delete Runner;
  1918. delete Console;
  1919. delete ScriptCommands;
  1920. delete ScriptParameters;
  1921. }
  1922. return Result;
  1923. }