ConsoleRunner.cpp 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086
  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. // need to do at least one iteration, even when Str is empty (new line)
  581. do
  582. {
  583. TConsoleCommStruct * CommStruct = GetCommStruct();
  584. try
  585. {
  586. size_t MaxLen = LENOF(CommStruct->PrintEvent.Message) - 1;
  587. UnicodeString Piece = Str.SubString(1, MaxLen);
  588. Str.Delete(1, MaxLen);
  589. CommStruct->Event = TConsoleCommStruct::PRINT;
  590. wcscpy(CommStruct->PrintEvent.Message, Piece.c_str());
  591. CommStruct->PrintEvent.FromBeginning = FromBeginning;
  592. // In the next iteration we need to append never overwrite.
  593. // Note that this won't work properly for disk/pipe outputs,
  594. // when the next line is also FromBeginning,
  595. // as !FromBeginning print effectively commits previous FromBeginning print.
  596. // On the other hand, FromBeginning print is always initiated by us,
  597. // and it's not likely we ever issue print over 10 KiB.
  598. FromBeginning = false;
  599. }
  600. __finally
  601. {
  602. FreeCommStruct(CommStruct);
  603. }
  604. SendEvent(PrintTimeout);
  605. }
  606. while (!Str.IsEmpty());
  607. }
  608. //---------------------------------------------------------------------------
  609. bool __fastcall TExternalConsole::Input(UnicodeString & Str, bool Echo, unsigned int Timer)
  610. {
  611. TConsoleCommStruct * CommStruct = GetCommStruct();
  612. try
  613. {
  614. CommStruct->Event = TConsoleCommStruct::INPUT;
  615. CommStruct->InputEvent.Echo = Echo;
  616. CommStruct->InputEvent.Result = false;
  617. CommStruct->InputEvent.Str[0] = L'\0';
  618. CommStruct->InputEvent.Timer = Timer;
  619. }
  620. __finally
  621. {
  622. FreeCommStruct(CommStruct);
  623. }
  624. SendEvent(INFINITE);
  625. bool Result;
  626. CommStruct = GetCommStruct();
  627. try
  628. {
  629. Result = CommStruct->InputEvent.Result;
  630. Str = CommStruct->InputEvent.Str;
  631. }
  632. __finally
  633. {
  634. FreeCommStruct(CommStruct);
  635. }
  636. return Result;
  637. }
  638. //---------------------------------------------------------------------------
  639. int __fastcall TExternalConsole::Choice(UnicodeString Options, int Cancel, int Break,
  640. int Timeouted, bool Timeouting, unsigned int Timer)
  641. {
  642. TConsoleCommStruct * CommStruct = GetCommStruct();
  643. try
  644. {
  645. CommStruct->Event = TConsoleCommStruct::CHOICE;
  646. assert(Options.Length() < static_cast<int>(LENOF(CommStruct->ChoiceEvent.Options)));
  647. wcscpy(CommStruct->ChoiceEvent.Options, Options.c_str());
  648. CommStruct->ChoiceEvent.Cancel = Cancel;
  649. CommStruct->ChoiceEvent.Break = Break;
  650. CommStruct->ChoiceEvent.Result = Break;
  651. CommStruct->ChoiceEvent.Timeouted = Timeouted;
  652. CommStruct->ChoiceEvent.Timer = Timer;
  653. CommStruct->ChoiceEvent.Timeouting = Timeouting;
  654. }
  655. __finally
  656. {
  657. FreeCommStruct(CommStruct);
  658. }
  659. SendEvent(INFINITE);
  660. int Result;
  661. CommStruct = GetCommStruct();
  662. try
  663. {
  664. Result = CommStruct->ChoiceEvent.Result;
  665. }
  666. __finally
  667. {
  668. FreeCommStruct(CommStruct);
  669. }
  670. return Result;
  671. }
  672. //---------------------------------------------------------------------------
  673. bool __fastcall TExternalConsole::PendingAbort()
  674. {
  675. return (WaitForSingleObject(FCancelEvent, 0) == WAIT_OBJECT_0);
  676. }
  677. //---------------------------------------------------------------------------
  678. void __fastcall TExternalConsole::SetTitle(UnicodeString Title)
  679. {
  680. TConsoleCommStruct * CommStruct = GetCommStruct();
  681. try
  682. {
  683. // Truncate to maximum allowed. Title over 10 KiB won't fir to screen anyway
  684. Title = Title.SubString(1, LENOF(CommStruct->TitleEvent.Title) - 1);
  685. CommStruct->Event = TConsoleCommStruct::TITLE;
  686. wcscpy(CommStruct->TitleEvent.Title, Title.c_str());
  687. }
  688. __finally
  689. {
  690. FreeCommStruct(CommStruct);
  691. }
  692. SendEvent(INFINITE);
  693. }
  694. //---------------------------------------------------------------------------
  695. void __fastcall TExternalConsole::Init()
  696. {
  697. TConsoleCommStruct * CommStruct = GetCommStruct();
  698. try
  699. {
  700. CommStruct->Event = TConsoleCommStruct::INIT;
  701. CommStruct->InitEvent.WantsProgress = false;
  702. }
  703. __finally
  704. {
  705. FreeCommStruct(CommStruct);
  706. }
  707. SendEvent(INFINITE);
  708. CommStruct = GetCommStruct();
  709. try
  710. {
  711. FLimitedOutput = (CommStruct->InitEvent.OutputType == FILE_TYPE_CHAR);
  712. FLiveOutput =
  713. (CommStruct->InitEvent.OutputType != FILE_TYPE_DISK) &&
  714. (CommStruct->InitEvent.OutputType != FILE_TYPE_PIPE);
  715. FPipeOutput = (CommStruct->InitEvent.OutputType != FILE_TYPE_PIPE);
  716. FWantsProgress = CommStruct->InitEvent.WantsProgress;
  717. }
  718. __finally
  719. {
  720. FreeCommStruct(CommStruct);
  721. }
  722. }
  723. //---------------------------------------------------------------------------
  724. bool __fastcall TExternalConsole::LimitedOutput()
  725. {
  726. return FLimitedOutput;
  727. }
  728. //---------------------------------------------------------------------------
  729. bool __fastcall TExternalConsole::LiveOutput()
  730. {
  731. return FLiveOutput;
  732. }
  733. //---------------------------------------------------------------------------
  734. bool __fastcall TExternalConsole::NoInteractiveInput()
  735. {
  736. return FNoInteractiveInput;
  737. }
  738. //---------------------------------------------------------------------------
  739. void __fastcall TExternalConsole::WaitBeforeExit()
  740. {
  741. // noop
  742. }
  743. //---------------------------------------------------------------------------
  744. bool __fastcall TExternalConsole::CommandLineOnly()
  745. {
  746. return true;
  747. }
  748. //---------------------------------------------------------------------------
  749. bool __fastcall TExternalConsole::WantsProgress()
  750. {
  751. return FWantsProgress;
  752. }
  753. //---------------------------------------------------------------------------
  754. void __fastcall TExternalConsole::Progress(const TScriptProgress & Progress)
  755. {
  756. TConsoleCommStruct * CommStruct = GetCommStruct();
  757. try
  758. {
  759. CommStruct->Event = TConsoleCommStruct::PROGRESS;
  760. typedef TConsoleCommStruct::TProgressEvent TProgressEvent;
  761. TProgressEvent & ProgressEvent = CommStruct->ProgressEvent;
  762. switch (Progress.Operation)
  763. {
  764. case foCopy:
  765. case foMove:
  766. ProgressEvent.Operation = TProgressEvent::COPY;
  767. break;
  768. default:
  769. FAIL;
  770. }
  771. switch (Progress.Side)
  772. {
  773. case osLocal:
  774. ProgressEvent.Side = TProgressEvent::LOCAL;
  775. break;
  776. case osRemote:
  777. ProgressEvent.Side = TProgressEvent::REMOTE;
  778. break;
  779. default:
  780. FAIL;
  781. }
  782. wcsncpy(ProgressEvent.FileName, Progress.FileName.c_str(), LENOF(ProgressEvent.FileName));
  783. NULL_TERMINATE(ProgressEvent.FileName);
  784. wcsncpy(ProgressEvent.Directory, Progress.Directory.c_str(), LENOF(ProgressEvent.Directory));
  785. NULL_TERMINATE(ProgressEvent.Directory);
  786. ProgressEvent.OverallProgress = Progress.OverallProgress;
  787. ProgressEvent.FileProgress = Progress.FileProgress;
  788. ProgressEvent.CPS = Progress.CPS;
  789. }
  790. __finally
  791. {
  792. FreeCommStruct(CommStruct);
  793. }
  794. SendEvent(INFINITE);
  795. // nothing to read from response, just wait for it
  796. FreeCommStruct(GetCommStruct());
  797. }
  798. //---------------------------------------------------------------------------
  799. class TNullConsole : public TConsole
  800. {
  801. public:
  802. __fastcall TNullConsole();
  803. virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false);
  804. virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer);
  805. virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
  806. int Timeouted, bool Timeouting, unsigned int Timer);
  807. virtual bool __fastcall PendingAbort();
  808. virtual void __fastcall SetTitle(UnicodeString Title);
  809. virtual bool __fastcall LimitedOutput();
  810. virtual bool __fastcall LiveOutput();
  811. virtual bool __fastcall NoInteractiveInput();
  812. virtual void __fastcall WaitBeforeExit();
  813. virtual bool __fastcall CommandLineOnly();
  814. virtual bool __fastcall WantsProgress();
  815. virtual void __fastcall Progress(const TScriptProgress & Progress);
  816. };
  817. //---------------------------------------------------------------------------
  818. __fastcall TNullConsole::TNullConsole()
  819. {
  820. }
  821. //---------------------------------------------------------------------------
  822. void __fastcall TNullConsole::Print(UnicodeString /*Str*/, bool /*FromBeginning*/)
  823. {
  824. // noop
  825. }
  826. //---------------------------------------------------------------------------
  827. bool __fastcall TNullConsole::Input(UnicodeString & /*Str*/, bool /*Echo*/,
  828. unsigned int /*Timer*/)
  829. {
  830. return false;
  831. }
  832. //---------------------------------------------------------------------------
  833. int __fastcall TNullConsole::Choice(UnicodeString /*Options*/, int /*Cancel*/,
  834. int Break, int Timeouted, bool Timeouting, unsigned int Timer)
  835. {
  836. int Result;
  837. if (Timeouting)
  838. {
  839. Sleep(Timer);
  840. Result = Timeouted;
  841. }
  842. else
  843. {
  844. Result = Break;
  845. }
  846. return Result;
  847. }
  848. //---------------------------------------------------------------------------
  849. bool __fastcall TNullConsole::PendingAbort()
  850. {
  851. return false;
  852. }
  853. //---------------------------------------------------------------------------
  854. void __fastcall TNullConsole::SetTitle(UnicodeString /*Title*/)
  855. {
  856. // noop
  857. }
  858. //---------------------------------------------------------------------------
  859. bool __fastcall TNullConsole::LimitedOutput()
  860. {
  861. return false;
  862. }
  863. //---------------------------------------------------------------------------
  864. bool __fastcall TNullConsole::LiveOutput()
  865. {
  866. return false;
  867. }
  868. //---------------------------------------------------------------------------
  869. bool __fastcall TNullConsole::NoInteractiveInput()
  870. {
  871. // do not matter, even if we return false,
  872. // it fails immediatelly afterwards in TNullConsole::Input
  873. return true;
  874. }
  875. //---------------------------------------------------------------------------
  876. void __fastcall TNullConsole::WaitBeforeExit()
  877. {
  878. assert(false);
  879. // noop
  880. }
  881. //---------------------------------------------------------------------------
  882. bool __fastcall TNullConsole::CommandLineOnly()
  883. {
  884. assert(false);
  885. return false;
  886. }
  887. //---------------------------------------------------------------------------
  888. bool __fastcall TNullConsole::WantsProgress()
  889. {
  890. return false;
  891. }
  892. //---------------------------------------------------------------------------
  893. void __fastcall TNullConsole::Progress(const TScriptProgress & /*Progress*/)
  894. {
  895. FAIL;
  896. }
  897. //---------------------------------------------------------------------------
  898. class TConsoleRunner
  899. {
  900. public:
  901. TConsoleRunner(TConsole * Console);
  902. ~TConsoleRunner();
  903. int __fastcall Run(const UnicodeString Session, TOptions * Options,
  904. TStrings * ScriptCommands, TStrings * ScriptParameters);
  905. void __fastcall ShowException(Exception * E);
  906. protected:
  907. bool __fastcall DoInput(UnicodeString & Str, bool Echo, unsigned int Timer,
  908. bool Interactive);
  909. void __fastcall Input(const UnicodeString Prompt, UnicodeString & Str,
  910. bool Echo, bool Interactive);
  911. inline void __fastcall Print(const UnicodeString & Str, bool FromBeginning = false);
  912. inline void __fastcall PrintLine(const UnicodeString & Str);
  913. inline void __fastcall PrintMessage(const UnicodeString & Str);
  914. void __fastcall UpdateTitle();
  915. inline void __fastcall NotifyAbort();
  916. inline bool __fastcall Aborted(bool AllowCompleteAbort = true);
  917. void __fastcall MasterPasswordPrompt();
  918. void __fastcall DoShowException(TTerminal * Terminal, Exception * E);
  919. private:
  920. TManagementScript * FScript;
  921. TConsole * FConsole;
  922. TSynchronizeController FSynchronizeController;
  923. int FLastProgressLen;
  924. bool FSynchronizeAborted;
  925. bool FCommandError;
  926. bool FBatchScript;
  927. bool FAborted;
  928. TTimer * Timer;
  929. void __fastcall ScriptPrint(TScript * Script, const UnicodeString Str);
  930. void __fastcall ScriptPrintProgress(TScript * Script, bool First, const UnicodeString Str);
  931. void __fastcall ScriptInput(TScript * Script, const UnicodeString Prompt, UnicodeString & Str);
  932. void __fastcall ScriptTerminalPromptUser(TTerminal * Terminal,
  933. TPromptKind Kind, UnicodeString Name, UnicodeString Instructions, TStrings * Prompts,
  934. TStrings * Results, bool & Result, void * Arg);
  935. void __fastcall ScriptShowExtendedException(TTerminal * Terminal,
  936. Exception * E, void * Arg);
  937. void __fastcall ScriptTerminalQueryUser(TObject * Sender, const UnicodeString Query,
  938. TStrings * MoreMessages, unsigned int Answers, const TQueryParams * Params, unsigned int & Answer,
  939. TQueryType QueryType, void * Arg);
  940. void __fastcall ScriptQueryCancel(TScript * Script, bool & Cancel);
  941. void __fastcall SynchronizeControllerAbort(TObject * Sender, bool Close);
  942. void __fastcall SynchronizeControllerLog(TSynchronizeController * Controller,
  943. TSynchronizeLogEntry Entry, const UnicodeString Message);
  944. void __fastcall ScriptSynchronizeStartStop(TScript * Script,
  945. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  946. const TCopyParamType & CopyParam, int SynchronizeParams);
  947. void __fastcall SynchronizeControllerSynchronize(TSynchronizeController * Sender,
  948. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  949. const TCopyParamType & CopyParam, const TSynchronizeParamType & Params,
  950. TSynchronizeChecklist ** Checklist, TSynchronizeOptions * Options, bool Full);
  951. void __fastcall SynchronizeControllerSynchronizeInvalid(TSynchronizeController * Sender,
  952. const UnicodeString Directory, const UnicodeString ErrorStr);
  953. void __fastcall SynchronizeControllerTooManyDirectories(TSynchronizeController * Sender,
  954. int & MaxDirectories);
  955. unsigned int InputTimeout();
  956. void __fastcall TimerTimer(TObject * Sender);
  957. UnicodeString ExpandCommand(UnicodeString Command, TStrings * ScriptParameters);
  958. void __fastcall Failed(bool & AnyError);
  959. void __fastcall ScriptProgress(TScript * Script, const TScriptProgress & Progress);
  960. void __fastcall ConfigurationChange(TObject * Sender);
  961. };
  962. //---------------------------------------------------------------------------
  963. TConsoleRunner::TConsoleRunner(TConsole * Console) :
  964. FSynchronizeController(&SynchronizeControllerSynchronize,
  965. &SynchronizeControllerSynchronizeInvalid,
  966. &SynchronizeControllerTooManyDirectories)
  967. {
  968. FConsole = Console;
  969. FLastProgressLen = 0;
  970. FScript = NULL;
  971. FAborted = false;
  972. FBatchScript = false;
  973. Timer = new TTimer(Application);
  974. Timer->OnTimer = TimerTimer;
  975. Timer->Interval = MSecsPerSec;
  976. Timer->Enabled = true;
  977. assert(WinConfiguration->OnMasterPasswordPrompt == NULL);
  978. WinConfiguration->OnMasterPasswordPrompt = MasterPasswordPrompt;
  979. assert(Configuration->OnChange == NULL);
  980. Configuration->OnChange = ConfigurationChange;
  981. }
  982. //---------------------------------------------------------------------------
  983. TConsoleRunner::~TConsoleRunner()
  984. {
  985. assert(WinConfiguration->OnMasterPasswordPrompt == MasterPasswordPrompt);
  986. WinConfiguration->OnMasterPasswordPrompt = NULL;
  987. assert(Configuration->OnChange == ConfigurationChange);
  988. Configuration->OnChange = NULL;
  989. delete Timer;
  990. }
  991. //---------------------------------------------------------------------------
  992. void __fastcall TConsoleRunner::TimerTimer(TObject * /*Sender*/)
  993. {
  994. // sole presence of timer causes message to be dispatched,
  995. // hence breaks the loops
  996. }
  997. //---------------------------------------------------------------------------
  998. unsigned int TConsoleRunner::InputTimeout()
  999. {
  1000. return ((FScript != NULL) && (FScript->Batch != TScript::BatchOff) ? BATCH_INPUT_TIMEOUT : 0);
  1001. }
  1002. //---------------------------------------------------------------------------
  1003. void __fastcall TConsoleRunner::Input(
  1004. const UnicodeString Prompt, UnicodeString & Str, bool Echo, bool Interactive)
  1005. {
  1006. Print(Prompt);
  1007. if (!DoInput(Str, Echo, InputTimeout(), Interactive))
  1008. {
  1009. Abort();
  1010. }
  1011. }
  1012. //---------------------------------------------------------------------------
  1013. void __fastcall TConsoleRunner::ScriptInput(TScript * /*Script*/,
  1014. const UnicodeString Prompt, UnicodeString & Str)
  1015. {
  1016. Input(Prompt, Str, true, true);
  1017. }
  1018. //---------------------------------------------------------------------------
  1019. void __fastcall TConsoleRunner::Print(const UnicodeString & Str, bool FromBeginning)
  1020. {
  1021. if (FLastProgressLen > 0)
  1022. {
  1023. FConsole->Print(L"\n" + Str, FromBeginning);
  1024. FLastProgressLen = 0;
  1025. }
  1026. else
  1027. {
  1028. FConsole->Print(Str, FromBeginning);
  1029. }
  1030. }
  1031. //---------------------------------------------------------------------------
  1032. void __fastcall TConsoleRunner::PrintLine(const UnicodeString & Str)
  1033. {
  1034. Print(Str + L"\n");
  1035. }
  1036. //---------------------------------------------------------------------------
  1037. void __fastcall TConsoleRunner::PrintMessage(const UnicodeString & Str)
  1038. {
  1039. UnicodeString Line =
  1040. ReplaceStr(ReplaceStr(Str.TrimRight(), L"\n\n", L"\n"),
  1041. L"\n \n", L"\n");
  1042. if (FScript != NULL)
  1043. {
  1044. // this also logs the message
  1045. FScript->PrintLine(Line);
  1046. }
  1047. else
  1048. {
  1049. PrintLine(Line);
  1050. }
  1051. }
  1052. //---------------------------------------------------------------------------
  1053. void __fastcall TConsoleRunner::NotifyAbort()
  1054. {
  1055. if (FBatchScript)
  1056. {
  1057. FAborted = true;
  1058. }
  1059. }
  1060. //---------------------------------------------------------------------------
  1061. bool __fastcall TConsoleRunner::Aborted(bool AllowCompleteAbort)
  1062. {
  1063. bool Result;
  1064. if (FAborted)
  1065. {
  1066. Result = true;
  1067. }
  1068. else
  1069. {
  1070. Result = FConsole->PendingAbort();
  1071. if (Result)
  1072. {
  1073. PrintMessage(LoadStr(USER_TERMINATED));
  1074. if (AllowCompleteAbort)
  1075. {
  1076. NotifyAbort();
  1077. }
  1078. }
  1079. }
  1080. return Result;
  1081. }
  1082. //---------------------------------------------------------------------------
  1083. void __fastcall TConsoleRunner::ScriptPrint(TScript * /*Script*/,
  1084. const UnicodeString Str)
  1085. {
  1086. Print(Str);
  1087. }
  1088. //---------------------------------------------------------------------------
  1089. void __fastcall TConsoleRunner::ScriptPrintProgress(TScript * /*Script*/,
  1090. bool First, const UnicodeString Str)
  1091. {
  1092. UnicodeString S = Str;
  1093. if (First && (FLastProgressLen > 0))
  1094. {
  1095. S = L"\n" + S;
  1096. }
  1097. else if (S.Length() < FLastProgressLen)
  1098. {
  1099. int Padding = FLastProgressLen - S.Length();
  1100. S += UnicodeString::StringOfChar(L' ', Padding) +
  1101. UnicodeString::StringOfChar(L'\b', Padding);
  1102. }
  1103. FConsole->Print(S, true);
  1104. FLastProgressLen = Str.Length();
  1105. }
  1106. //---------------------------------------------------------------------------
  1107. void __fastcall TConsoleRunner::ScriptTerminalPromptUser(TTerminal * /*Terminal*/,
  1108. TPromptKind /*Kind*/, UnicodeString Name, UnicodeString Instructions, TStrings * Prompts,
  1109. TStrings * Results, bool & Result, void * /*Arg*/)
  1110. {
  1111. if (!Instructions.IsEmpty())
  1112. {
  1113. PrintMessage(Instructions);
  1114. }
  1115. // if there are no prompts, success is default
  1116. Result = true;
  1117. for (int Index = 0; Index < Prompts->Count; Index++)
  1118. {
  1119. UnicodeString Prompt = Prompts->Strings[Index];
  1120. if (!Prompt.IsEmpty() && (Prompt[Prompt.Length()] != L' '))
  1121. {
  1122. Prompt += L' ';
  1123. }
  1124. int P = Prompt.Pos(L'&');
  1125. if (P > 0)
  1126. {
  1127. Prompt.Delete(P, 1);
  1128. }
  1129. Print(Prompt);
  1130. UnicodeString AResult = Results->Strings[Index]; // useless
  1131. bool Echo = FLAGSET(int(Prompts->Objects[Index]), pupEcho);
  1132. Result = DoInput(AResult, Echo, InputTimeout(), true);
  1133. Results->Strings[Index] = AResult;
  1134. }
  1135. }
  1136. //---------------------------------------------------------------------------
  1137. void __fastcall TConsoleRunner::ScriptShowExtendedException(
  1138. TTerminal * Terminal, Exception * E, void * /*Arg*/)
  1139. {
  1140. DoShowException(Terminal, E);
  1141. }
  1142. //---------------------------------------------------------------------------
  1143. void __fastcall TConsoleRunner::ScriptTerminalQueryUser(TObject * /*Sender*/,
  1144. const UnicodeString Query, TStrings * MoreMessages, unsigned int Answers,
  1145. const TQueryParams * Params, unsigned int & Answer, TQueryType /*QueryType*/,
  1146. void * /*Arg*/)
  1147. {
  1148. UnicodeString AQuery = Query;
  1149. unsigned int Timer = 0;
  1150. unsigned int Timeout = 0;
  1151. unsigned int TimeoutA = 0;
  1152. unsigned int NoBatchA = 0;
  1153. if (Params != NULL)
  1154. {
  1155. if (Params->Timeout > 0)
  1156. {
  1157. Timeout = Params->Timeout;
  1158. TimeoutA = Params->TimeoutAnswer;
  1159. }
  1160. if (Params->Timer > 0)
  1161. {
  1162. Timer = Params->Timer;
  1163. if (Params->TimerAnswers > 0)
  1164. {
  1165. Answers = Params->TimerAnswers;
  1166. }
  1167. // not considering TimerQueryType as we do not use QueryType anyway
  1168. if (!Params->TimerMessage.IsEmpty())
  1169. {
  1170. AQuery = Params->TimerMessage;
  1171. }
  1172. }
  1173. if (FLAGSET(Params->Params, qpFatalAbort))
  1174. {
  1175. AQuery = FMTLOAD(WARN_FATAL_ERROR, (AQuery));
  1176. }
  1177. NoBatchA = Params->NoBatchAnswers;
  1178. }
  1179. AQuery = UnformatMessage(AQuery);
  1180. ApplyTabs(AQuery, L' ', NULL, NULL);
  1181. unsigned int AAnswers = Answers;
  1182. PrintMessage(AQuery);
  1183. if ((MoreMessages != NULL) && (MoreMessages->Count > 0))
  1184. {
  1185. PrintMessage(MoreMessages->Text);
  1186. }
  1187. std::vector<unsigned int> Buttons;
  1188. std::vector<UnicodeString> Captions;
  1189. std::vector<TNotifyEvent> OnClicks;
  1190. for (unsigned int Answer = qaFirst; Answer <= qaLast; Answer = Answer << 1)
  1191. {
  1192. if (FLAGSET(Answers, Answer))
  1193. {
  1194. UnicodeString Name; // unused
  1195. UnicodeString Caption;
  1196. AnswerNameAndCaption(Answer, Name, Caption);
  1197. Captions.push_back(Caption);
  1198. Buttons.push_back(Answer);
  1199. OnClicks.push_back(NULL);
  1200. AAnswers -= Answer;
  1201. }
  1202. }
  1203. USEDPARAM(AAnswers);
  1204. assert(AAnswers == 0);
  1205. assert(!Buttons.empty());
  1206. if ((Params != NULL) && (Params->Aliases != NULL))
  1207. {
  1208. for (unsigned int bi = 0; bi < Buttons.size(); bi++)
  1209. {
  1210. for (unsigned int ai = 0; ai < Params->AliasesCount; ai++)
  1211. {
  1212. if (Params->Aliases[ai].Button == Buttons[bi])
  1213. {
  1214. if (!Params->Aliases[ai].Alias.IsEmpty())
  1215. {
  1216. Captions[bi] = Params->Aliases[ai].Alias;
  1217. }
  1218. OnClicks[bi] = Params->Aliases[ai].OnClick;
  1219. break;
  1220. }
  1221. }
  1222. }
  1223. }
  1224. UnicodeString Accels;
  1225. for (unsigned int Index = 0; Index < Buttons.size(); Index++)
  1226. {
  1227. UnicodeString & Caption = Captions[Index];
  1228. int P = Caption.Pos(L'&');
  1229. if ((P > 0) && (P < Caption.Length()))
  1230. {
  1231. wchar_t Accel = AnsiUpperCase(Caption)[P + 1];
  1232. if (Accels.Pos(Accel) > 0)
  1233. {
  1234. Caption.Delete(P, 1);
  1235. Accels += L' ';
  1236. }
  1237. else
  1238. {
  1239. Accels += Accel;
  1240. }
  1241. }
  1242. else
  1243. {
  1244. Accels += L' ';
  1245. }
  1246. }
  1247. assert(Accels.Length() == static_cast<int>(Buttons.size()));
  1248. int NumberAccel = 0;
  1249. unsigned int CancelA = CancelAnswer(Answers);
  1250. int CancelIndex;
  1251. unsigned int AbortA = AbortAnswer(Answers & ~NoBatchA);
  1252. int AbortIndex;
  1253. unsigned int ContinueA = ContinueAnswer(Answers & ~NoBatchA);
  1254. int ContinueIndex;
  1255. int TimeoutIndex = 0;
  1256. for (unsigned int Index = 0; Index < Buttons.size(); Index++)
  1257. {
  1258. UnicodeString & Caption = Captions[Index];
  1259. if (Accels[Index + 1] == L' ')
  1260. {
  1261. for (int Index2 = 1; Index2 <= Caption.Length(); Index2++)
  1262. {
  1263. wchar_t C = AnsiUpperCase(Caption)[Index2];
  1264. if (IsLetter(C) && (Accels.Pos(C) == 0))
  1265. {
  1266. Caption.Insert(L"&", Index2);
  1267. Accels[Index + 1] = C;
  1268. break;
  1269. }
  1270. }
  1271. }
  1272. if (Accels[Index + 1] == L' ')
  1273. {
  1274. for (int Index2 = 1; Index2 <= Caption.Length(); Index2++)
  1275. {
  1276. wchar_t C = AnsiUpperCase(Caption)[Index2];
  1277. if ((C != L' ') && (Accels.Pos(C) == 0))
  1278. {
  1279. Caption.Insert(L"&", Index2);
  1280. Accels[Index + 1] = C;
  1281. break;
  1282. }
  1283. }
  1284. }
  1285. if (Accels[Index + 1] == L' ')
  1286. {
  1287. NumberAccel++;
  1288. assert(NumberAccel <= 9);
  1289. Caption = FORMAT(L"&%d%s", (NumberAccel, Caption));
  1290. Accels[Index + 1] = Caption[2];
  1291. }
  1292. if (Buttons[Index] == CancelA)
  1293. {
  1294. CancelIndex = Index + 1;
  1295. }
  1296. if (Buttons[Index] == AbortA)
  1297. {
  1298. AbortIndex = Index + 1;
  1299. }
  1300. if (Buttons[Index] == ContinueA)
  1301. {
  1302. ContinueIndex = Index + 1;
  1303. }
  1304. if (Buttons[Index] == TimeoutA)
  1305. {
  1306. TimeoutIndex = Index + 1;
  1307. }
  1308. }
  1309. assert(Accels.Pos(L' ') == 0);
  1310. bool Timeouting = (Timeout > 0);
  1311. bool FirstOutput = true;
  1312. do
  1313. {
  1314. Answer = 0;
  1315. int AnswerIndex;
  1316. bool Retry;
  1317. do
  1318. {
  1319. Retry = false;
  1320. if (FirstOutput || FConsole->LiveOutput())
  1321. {
  1322. UnicodeString Output;
  1323. for (unsigned int i = 0; i < Buttons.size(); i++)
  1324. {
  1325. if (i > 0)
  1326. {
  1327. Output += L", ";
  1328. }
  1329. UnicodeString Caption = Captions[i];
  1330. int P = Caption.Pos(L'&');
  1331. if (ALWAYS_TRUE(P >= 0))
  1332. {
  1333. Caption[P] = L'(';
  1334. Caption.Insert(L")", P + 2);
  1335. }
  1336. if (i + 1 == static_cast<unsigned int>(TimeoutIndex))
  1337. {
  1338. assert(Timeouting);
  1339. Caption = FMTLOAD(TIMEOUT_BUTTON, (Caption, int(Timeout / MSecsPerSec)));
  1340. }
  1341. Output += Caption;
  1342. }
  1343. Output += L": ";
  1344. // note that length of string may decrease over time due to number of
  1345. // seconds length, but supposing it decreases by one character at time
  1346. // at most, we do not mind as the prompt is terminated with space
  1347. // If output is not live (file or pipe), do not use 'from beginning'
  1348. // as it means that the output is not actually stored until new line
  1349. // is sent (and we will not [because we cannot] rewrite the output anyway)
  1350. Print(Output, !FirstOutput);
  1351. FirstOutput = false;
  1352. }
  1353. if (!Timeouting && (FScript->Batch == TScript::BatchContinue))
  1354. {
  1355. AnswerIndex = ContinueIndex;
  1356. }
  1357. else if (!Timeouting && (FScript->Batch != TScript::BatchOff))
  1358. {
  1359. AnswerIndex = AbortIndex;
  1360. }
  1361. else if (Timeouting && (Timeout < MSecsPerSec))
  1362. {
  1363. AnswerIndex = TimeoutIndex;
  1364. }
  1365. else
  1366. {
  1367. unsigned int ActualTimer =
  1368. (Timeouting && ((Timer == 0) || (Timer > MSecsPerSec)) ? MSecsPerSec : Timer);
  1369. AnswerIndex = FConsole->Choice(Accels, CancelIndex, -1, -2,
  1370. Timeouting, ActualTimer);
  1371. if (AnswerIndex == -1)
  1372. {
  1373. NotifyAbort();
  1374. AnswerIndex = AbortIndex;
  1375. }
  1376. else if (AnswerIndex == -2)
  1377. {
  1378. if (Timeouting)
  1379. {
  1380. assert(Timeout >= MSecsPerSec);
  1381. Timeout -= ActualTimer;
  1382. Retry = true;
  1383. }
  1384. // this does not take Timer into account,
  1385. // but as of now Timer is used for TSecureShell timeout prompt only,
  1386. // where Timer is less than MSecsPerSec
  1387. if (Timer > 0)
  1388. {
  1389. assert((Params != NULL) && (Params->TimerEvent != NULL));
  1390. if ((Params != NULL) && (Params->TimerEvent != NULL))
  1391. {
  1392. unsigned int AAnswer = 0;
  1393. Params->TimerEvent(AAnswer);
  1394. if (AAnswer != 0)
  1395. {
  1396. Answer = AAnswer;
  1397. Retry = false;
  1398. }
  1399. else
  1400. {
  1401. Retry = true;
  1402. }
  1403. }
  1404. }
  1405. }
  1406. }
  1407. }
  1408. while (Retry);
  1409. if (Answer == 0)
  1410. {
  1411. assert((AnswerIndex >= 1) && (AnswerIndex <= Accels.Length()));
  1412. UnicodeString AnswerCaption = Captions[AnswerIndex - 1];
  1413. int P = AnswerCaption.Pos(L"&");
  1414. assert(P >= 0);
  1415. AnswerCaption.Delete(P, 1);
  1416. PrintLine(AnswerCaption);
  1417. FirstOutput = true;
  1418. if (OnClicks[AnswerIndex - 1] != NULL)
  1419. {
  1420. OnClicks[AnswerIndex - 1](NULL);
  1421. }
  1422. else
  1423. {
  1424. Answer = Buttons[AnswerIndex - 1];
  1425. }
  1426. }
  1427. else
  1428. {
  1429. PrintLine(L"");
  1430. }
  1431. }
  1432. while (Answer == 0);
  1433. if ((Answer == AbortA) && FLAGCLEAR(Params->Params, qpIgnoreAbort))
  1434. {
  1435. if (FScript->Terminal != NULL)
  1436. {
  1437. TStrings * Messages = new TStringList();
  1438. try
  1439. {
  1440. Messages->Add(Query);
  1441. if (MoreMessages != NULL)
  1442. {
  1443. Messages->AddStrings(MoreMessages);
  1444. }
  1445. FScript->Terminal->ActionLog->AddFailure(Messages);
  1446. }
  1447. __finally
  1448. {
  1449. delete Messages;
  1450. }
  1451. }
  1452. FCommandError = true;
  1453. }
  1454. }
  1455. //---------------------------------------------------------------------------
  1456. void __fastcall TConsoleRunner::ScriptQueryCancel(TScript * /*Script*/, bool & Cancel)
  1457. {
  1458. if (Aborted())
  1459. {
  1460. Cancel = true;
  1461. }
  1462. }
  1463. //---------------------------------------------------------------------------
  1464. void __fastcall TConsoleRunner::ScriptSynchronizeStartStop(TScript * /*Script*/,
  1465. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  1466. const TCopyParamType & CopyParam, int SynchronizeParams)
  1467. {
  1468. TSynchronizeParamType Params;
  1469. Params.LocalDirectory = LocalDirectory;
  1470. Params.RemoteDirectory = RemoteDirectory;
  1471. Params.Params = SynchronizeParams;
  1472. Params.Options = soRecurse;
  1473. FSynchronizeController.StartStop(Application, true, Params,
  1474. CopyParam, NULL, SynchronizeControllerAbort, NULL,
  1475. SynchronizeControllerLog);
  1476. try
  1477. {
  1478. FSynchronizeAborted = false;
  1479. while (!FSynchronizeAborted && !Aborted(false))
  1480. {
  1481. Application->HandleMessage();
  1482. FScript->Terminal->Idle();
  1483. }
  1484. }
  1485. __finally
  1486. {
  1487. FSynchronizeController.StartStop(Application, false, Params,
  1488. CopyParam, NULL, SynchronizeControllerAbort, NULL,
  1489. SynchronizeControllerLog);
  1490. }
  1491. }
  1492. //---------------------------------------------------------------------------
  1493. void __fastcall TConsoleRunner::ScriptProgress(TScript * /*Script*/, const TScriptProgress & Progress)
  1494. {
  1495. FConsole->Progress(Progress);
  1496. }
  1497. //---------------------------------------------------------------------------
  1498. void __fastcall TConsoleRunner::SynchronizeControllerLog(
  1499. TSynchronizeController * /*Controller*/, TSynchronizeLogEntry /*Entry*/,
  1500. const UnicodeString Message)
  1501. {
  1502. PrintMessage(Message);
  1503. }
  1504. //---------------------------------------------------------------------------
  1505. void __fastcall TConsoleRunner::SynchronizeControllerAbort(TObject * /*Sender*/,
  1506. bool /*Close*/)
  1507. {
  1508. FSynchronizeAborted = true;
  1509. }
  1510. //---------------------------------------------------------------------------
  1511. void __fastcall TConsoleRunner::SynchronizeControllerSynchronize(
  1512. TSynchronizeController * /*Sender*/, const UnicodeString LocalDirectory,
  1513. const UnicodeString RemoteDirectory, const TCopyParamType & CopyParam,
  1514. const TSynchronizeParamType & Params, TSynchronizeChecklist ** Checklist,
  1515. TSynchronizeOptions * /*Options*/, bool Full)
  1516. {
  1517. if (!Full)
  1518. {
  1519. FScript->Synchronize(LocalDirectory, RemoteDirectory, CopyParam,
  1520. Params.Params, Checklist);
  1521. }
  1522. }
  1523. //---------------------------------------------------------------------------
  1524. void __fastcall TConsoleRunner::SynchronizeControllerSynchronizeInvalid(
  1525. TSynchronizeController * /*Sender*/, const UnicodeString Directory, const UnicodeString ErrorStr)
  1526. {
  1527. if (!Directory.IsEmpty())
  1528. {
  1529. PrintMessage(FMTLOAD(WATCH_ERROR_DIRECTORY, (Directory)));
  1530. }
  1531. else
  1532. {
  1533. PrintMessage(LoadStr(WATCH_ERROR_GENERAL));
  1534. }
  1535. if (!ErrorStr.IsEmpty())
  1536. {
  1537. PrintMessage(ErrorStr);
  1538. }
  1539. }
  1540. //---------------------------------------------------------------------------
  1541. void __fastcall TConsoleRunner::SynchronizeControllerTooManyDirectories(
  1542. TSynchronizeController * /*Sender*/, int & MaxDirectories)
  1543. {
  1544. if (Aborted())
  1545. {
  1546. Abort();
  1547. }
  1548. if (MaxDirectories < GUIConfiguration->MaxWatchDirectories)
  1549. {
  1550. MaxDirectories = GUIConfiguration->MaxWatchDirectories;
  1551. }
  1552. else
  1553. {
  1554. MaxDirectories *= 2;
  1555. }
  1556. }
  1557. //---------------------------------------------------------------------------
  1558. void __fastcall TConsoleRunner::ShowException(Exception * E)
  1559. {
  1560. DoShowException(NULL, E);
  1561. }
  1562. //---------------------------------------------------------------------------
  1563. void __fastcall TConsoleRunner::DoShowException(TTerminal * Terminal, Exception * E)
  1564. {
  1565. if ((Terminal == NULL) && (FScript != NULL))
  1566. {
  1567. Terminal = FScript->Terminal;
  1568. }
  1569. UnicodeString Message;
  1570. if (ExceptionMessage(E, Message))
  1571. {
  1572. FCommandError = true;
  1573. PrintMessage(Message);
  1574. ExtException * EE = dynamic_cast<ExtException *>(E);
  1575. if ((EE != NULL) && (EE->MoreMessages != NULL))
  1576. {
  1577. PrintMessage(EE->MoreMessages->Text);
  1578. }
  1579. }
  1580. TTerminal * LoggingTerminal = Terminal;
  1581. TSecondaryTerminal * SecondaryTerminal = dynamic_cast<TSecondaryTerminal *>(LoggingTerminal);
  1582. if (SecondaryTerminal != NULL)
  1583. {
  1584. LoggingTerminal = SecondaryTerminal->MainTerminal;
  1585. }
  1586. if (LoggingTerminal != NULL)
  1587. {
  1588. LoggingTerminal->ActionLog->AddFailure(E);
  1589. }
  1590. }
  1591. //---------------------------------------------------------------------------
  1592. bool __fastcall TConsoleRunner::DoInput(UnicodeString & Str, bool Echo,
  1593. unsigned int Timeout, bool Interactive)
  1594. {
  1595. bool Result;
  1596. if (Interactive && FConsole->NoInteractiveInput())
  1597. {
  1598. Result = false;
  1599. }
  1600. else
  1601. {
  1602. Result = FConsole->Input(Str, Echo, Timeout);
  1603. }
  1604. if (Result)
  1605. {
  1606. while (!Str.IsEmpty() &&
  1607. ((Str[Str.Length()] == L'\n') || (Str[Str.Length()] == L'\r')))
  1608. {
  1609. Str.SetLength(Str.Length() - 1);
  1610. }
  1611. }
  1612. else
  1613. {
  1614. NotifyAbort();
  1615. }
  1616. return Result;
  1617. }
  1618. //---------------------------------------------------------------------------
  1619. void __fastcall TConsoleRunner::MasterPasswordPrompt()
  1620. {
  1621. bool Retry;
  1622. do
  1623. {
  1624. UnicodeString Password;
  1625. Input(LoadStr(CONSOLE_MASTER_PASSWORD_PROMPT), Password, false, true);
  1626. Retry = !WinConfiguration->ValidateMasterPassword(Password);
  1627. if (Retry)
  1628. {
  1629. PrintLine(LoadStr(MASTER_PASSWORD_INCORRECT));
  1630. }
  1631. else
  1632. {
  1633. WinConfiguration->SetMasterPassword(Password);
  1634. }
  1635. }
  1636. while (Retry);
  1637. }
  1638. //---------------------------------------------------------------------------
  1639. UnicodeString TConsoleRunner::ExpandCommand(UnicodeString Command, TStrings * ScriptParameters)
  1640. {
  1641. assert(ScriptParameters != NULL);
  1642. for (int Index = 0; Index < ScriptParameters->Count; Index++)
  1643. {
  1644. Command = ReplaceStr(Command, FORMAT(L"%%%d%%", (Index+1)),
  1645. ScriptParameters->Strings[Index]);
  1646. }
  1647. Command = ExpandEnvironmentVariables(Command);
  1648. return Command;
  1649. }
  1650. //---------------------------------------------------------------------------
  1651. void __fastcall TConsoleRunner::Failed(bool & AnyError)
  1652. {
  1653. if (FScript != NULL)
  1654. {
  1655. FScript->Log(llMessage, L"Failed");
  1656. }
  1657. AnyError = true;
  1658. }
  1659. //---------------------------------------------------------------------------
  1660. int __fastcall TConsoleRunner::Run(const UnicodeString Session, TOptions * Options,
  1661. TStrings * ScriptCommands, TStrings * ScriptParameters)
  1662. {
  1663. int ExitCode;
  1664. try
  1665. {
  1666. bool AnyError = false;
  1667. try
  1668. {
  1669. FScript = new TManagementScript(StoredSessions, FConsole->LimitedOutput());
  1670. FScript->CopyParam = GUIConfiguration->DefaultCopyParam;
  1671. FScript->SynchronizeParams = GUIConfiguration->SynchronizeParams;
  1672. FScript->WantsProgress = FConsole->WantsProgress();
  1673. FScript->OnPrint = ScriptPrint;
  1674. FScript->OnPrintProgress = ScriptPrintProgress;
  1675. FScript->OnInput = ScriptInput;
  1676. FScript->OnTerminalPromptUser = ScriptTerminalPromptUser;
  1677. FScript->OnShowExtendedException = ScriptShowExtendedException;
  1678. FScript->OnTerminalQueryUser = ScriptTerminalQueryUser;
  1679. FScript->OnQueryCancel = ScriptQueryCancel;
  1680. FScript->OnSynchronizeStartStop = ScriptSynchronizeStartStop;
  1681. FScript->OnProgress = ScriptProgress;
  1682. UpdateTitle();
  1683. // everything until the first manually entered command is "batch"
  1684. // (including opening session from command line and script file)
  1685. FBatchScript = true;
  1686. if (!Session.IsEmpty())
  1687. {
  1688. PrintMessage(LoadStr(SCRIPT_CMDLINE_SESSION));
  1689. FScript->Connect(Session, Options, false);
  1690. if (FCommandError)
  1691. {
  1692. Failed(AnyError);
  1693. }
  1694. }
  1695. FScript->Groups = Options->SwitchValue(L"xmlgroups", true, false);
  1696. int ScriptPos = 0;
  1697. bool Result;
  1698. do
  1699. {
  1700. UpdateTitle();
  1701. UnicodeString Command;
  1702. if ((ScriptCommands != NULL) && (ScriptPos < ScriptCommands->Count))
  1703. {
  1704. Result = true;
  1705. Command = ScriptCommands->Strings[ScriptPos];
  1706. ScriptPos++;
  1707. }
  1708. else
  1709. {
  1710. // no longer batch
  1711. FBatchScript = false;
  1712. Print(L"winscp> ");
  1713. Result = DoInput(Command, true, 0, false);
  1714. }
  1715. if (Result)
  1716. {
  1717. FCommandError = false;
  1718. FScript->Command(ExpandCommand(Command, ScriptParameters));
  1719. if (FCommandError)
  1720. {
  1721. Failed(AnyError);
  1722. if (FScript->Batch == TScript::BatchAbort)
  1723. {
  1724. Result = false;
  1725. }
  1726. }
  1727. if (FScript->Terminal != NULL)
  1728. {
  1729. FScript->Terminal->Idle();
  1730. }
  1731. }
  1732. }
  1733. while (Result && FScript->Continue && !Aborted());
  1734. }
  1735. catch(Exception & E)
  1736. {
  1737. Failed(AnyError);
  1738. ShowException(&E);
  1739. }
  1740. ExitCode = AnyError ? RESULT_ANY_ERROR : RESULT_SUCCESS;
  1741. if (FScript != NULL)
  1742. {
  1743. FScript->Log(llMessage, FORMAT(L"Exit code: %d", (ExitCode)));
  1744. }
  1745. }
  1746. __finally
  1747. {
  1748. delete FScript;
  1749. FScript = NULL;
  1750. }
  1751. return ExitCode;
  1752. }
  1753. //---------------------------------------------------------------------------
  1754. void __fastcall TConsoleRunner::UpdateTitle()
  1755. {
  1756. UnicodeString NewTitle;
  1757. if (FScript->Terminal != NULL)
  1758. {
  1759. NewTitle = FMTLOAD(APP_CAPTION, (FScript->Terminal->SessionData->SessionName, AppName));
  1760. }
  1761. else
  1762. {
  1763. NewTitle = AppName;
  1764. }
  1765. FConsole->SetTitle(NewTitle);
  1766. }
  1767. //---------------------------------------------------------------------------
  1768. void __fastcall TConsoleRunner::ConfigurationChange(TObject * /*Sender*/)
  1769. {
  1770. if (FScript != NULL)
  1771. {
  1772. FScript->ReflectSettings();
  1773. }
  1774. }
  1775. //---------------------------------------------------------------------------
  1776. void __fastcall LoadScriptFromFile(UnicodeString FileName, TStrings * Lines)
  1777. {
  1778. Lines->LoadFromFile(FileName);
  1779. }
  1780. //---------------------------------------------------------------------------
  1781. void __fastcall Usage(TConsole * Console)
  1782. {
  1783. UnicodeString Usage = LoadStr(USAGE8, 10240);
  1784. UnicodeString ExeBaseName = ExtractFileBaseName(Application->ExeName);
  1785. Usage = ReplaceText(Usage, L"%APP%", ExeBaseName);
  1786. UnicodeString Copyright =
  1787. ReplaceText(LoadStr(WINSCP_COPYRIGHT), L"©", L"(c)");
  1788. Usage = FORMAT(Usage, (Configuration->VersionStr, Copyright));
  1789. TStrings * Lines = new TStringList();
  1790. try
  1791. {
  1792. Lines->Text = Usage;
  1793. for (int Index = 0; Index < Lines->Count; Index++)
  1794. {
  1795. bool Print = true;
  1796. UnicodeString Line = Lines->Strings[Index];
  1797. if ((Line.Length() >= 2) && (Line[2] == L':'))
  1798. {
  1799. switch (Line[1])
  1800. {
  1801. case L'G':
  1802. Print = !Console->CommandLineOnly();
  1803. break;
  1804. case L'C':
  1805. Print = Console->CommandLineOnly();
  1806. break;
  1807. case L'B':
  1808. Print = true;
  1809. break;
  1810. default:
  1811. assert(false);
  1812. break;
  1813. }
  1814. Line.Delete(1, 2);
  1815. }
  1816. if (Print)
  1817. {
  1818. Console->Print(Line + L"\n");
  1819. }
  1820. }
  1821. }
  1822. __finally
  1823. {
  1824. delete Lines;
  1825. }
  1826. Console->WaitBeforeExit();
  1827. }
  1828. //---------------------------------------------------------------------------
  1829. int __fastcall Console(bool Help)
  1830. {
  1831. TProgramParams * Params = TProgramParams::Instance();
  1832. int Result = 0;
  1833. TConsole * Console = NULL;
  1834. TConsoleRunner * Runner = NULL;
  1835. TStrings * ScriptCommands = new TStringList();
  1836. TStrings * ScriptParameters = new TStringList();
  1837. try
  1838. {
  1839. UnicodeString ConsoleInstance;
  1840. if (Params->FindSwitch(L"consoleinstance", ConsoleInstance))
  1841. {
  1842. Configuration->Usage->Inc(L"ConsoleExternal");
  1843. Console = new TExternalConsole(ConsoleInstance, Params->FindSwitch(L"nointeractiveinput"));
  1844. }
  1845. else if (Params->FindSwitch(L"Console") || Help)
  1846. {
  1847. Configuration->Usage->Inc(L"ConsoleOwn");
  1848. Console = TOwnConsole::Instance();
  1849. }
  1850. else
  1851. {
  1852. Configuration->Usage->Inc(L"ConsoleNull");
  1853. Console = new TNullConsole();
  1854. }
  1855. if (Help)
  1856. {
  1857. Usage(Console);
  1858. }
  1859. else
  1860. {
  1861. Runner = new TConsoleRunner(Console);
  1862. try
  1863. {
  1864. UnicodeString Value;
  1865. if (Params->FindSwitch(L"script", Value) && !Value.IsEmpty())
  1866. {
  1867. Configuration->Usage->Inc(L"ScriptFile");
  1868. LoadScriptFromFile(Value, ScriptCommands);
  1869. }
  1870. Params->FindSwitch(L"command", ScriptCommands);
  1871. if (ScriptCommands->Count > 0)
  1872. {
  1873. Configuration->Usage->Inc(L"ScriptCommands");
  1874. }
  1875. Params->FindSwitch(L"parameter", ScriptParameters);
  1876. if (ScriptParameters->Count > 0)
  1877. {
  1878. Configuration->Usage->Inc(L"ScriptParameters");
  1879. }
  1880. bool Url = false;
  1881. UnicodeString Session;
  1882. if (Params->ParamCount >= 1)
  1883. {
  1884. Session = Params->Param[1];
  1885. }
  1886. bool DefaultsOnly;
  1887. delete StoredSessions->ParseUrl(Session, Params, DefaultsOnly,
  1888. NULL, &Url);
  1889. if (Url || Params->FindSwitch(L"Unsafe"))
  1890. {
  1891. // prevent any automatic action when URL is provided on
  1892. // command-line (the check is duplicated in Execute())
  1893. if ((ScriptCommands->Count > 0) || Params->FindSwitch(L"Log") || Params->FindSwitch(L"XmlLog"))
  1894. {
  1895. Console->Print(LoadStr(UNSAFE_ACTIONS_DISABLED) + L"\n");
  1896. }
  1897. ScriptCommands->Clear();
  1898. }
  1899. else
  1900. {
  1901. UnicodeString LogFile;
  1902. if (Params->FindSwitch(L"Log", LogFile))
  1903. {
  1904. Configuration->Usage->Inc(L"ScriptLog");
  1905. Configuration->TemporaryLogging(LogFile);
  1906. }
  1907. if (Params->FindSwitch(L"XmlLog", LogFile))
  1908. {
  1909. Configuration->Usage->Inc(L"ScriptXmlLog");
  1910. Configuration->TemporaryActionsLogging(LogFile);
  1911. }
  1912. }
  1913. Result = Runner->Run(Session, Params,
  1914. (ScriptCommands->Count > 0 ? ScriptCommands : NULL),
  1915. ScriptParameters);
  1916. }
  1917. catch(Exception & E)
  1918. {
  1919. Runner->ShowException(&E);
  1920. Result = RESULT_ANY_ERROR;
  1921. }
  1922. }
  1923. }
  1924. __finally
  1925. {
  1926. delete Runner;
  1927. delete Console;
  1928. delete ScriptCommands;
  1929. delete ScriptParameters;
  1930. }
  1931. return Result;
  1932. }