Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1804 lines
44 KiB

  1. #ifndef _WINDOWS_H
  2. #include "windows.h"
  3. #endif
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <io.h>
  7. #include <fcntl.h>
  8. #include <assert.h>
  9. #include <typeinfo.h>
  10. #include <time.h>
  11. #include <limits.h>
  12. #include <strsafe.h>
  13. #include "initguid.h"
  14. #include "sdapi.h"
  15. #ifndef DIMA
  16. #define DIMAT(Array, EltType) (sizeof(Array) / sizeof(EltType))
  17. #define DIMA(Array) DIMAT(Array, (Array)[0])
  18. #endif
  19. static const char usage[] = "sdapitest [-?] command [args]";
  20. static const char long_usage[] =
  21. "Usage:\n"
  22. "\n"
  23. " sdapitest [options] command [args]\n"
  24. "\n"
  25. " Roughly emulates the SD.EXE client, using the SDAPI.\n"
  26. "\n"
  27. " Options:\n"
  28. " -? Print this message.\n"
  29. " -! Break into debugger.\n"
  30. "\n"
  31. " -d Debug/diagnostic/informational output mode.\n"
  32. " -v Verbose mode (show type of output).\n"
  33. "\n"
  34. " -c client Set client name.\n"
  35. " -H host Set host name.\n"
  36. " -p port Set server port.\n"
  37. " -P password Set user's password.\n"
  38. " -u user Set username.\n"
  39. "\n"
  40. " -i file Read settings from file (same format as SD.INI).\n"
  41. " If file is a directory name, walk up the directory\n"
  42. " parent chain searching for an SD.INI file to read.\n"
  43. " -I file Same as -i, but clears the current settings first.\n"
  44. "\n"
  45. " -x file Read commands from 'file'. To read commands from\n"
  46. " stdin, use - as the file name. This can even be used\n"
  47. " as a simplistic interactive SD shell. Each command\n"
  48. " can be optionally preceded by an integer and a comma.\n"
  49. " The integer indicates the number of seconds to pause\n"
  50. " before running the command.\n"
  51. "\n"
  52. " -T Use the SDAPI structured mode.\n"
  53. #if 0
  54. " -C Use CreateSDAPIObject() instead of CoCreateInstance();\n"
  55. " (note, must come before any other options).\n"
  56. #endif
  57. "\n"
  58. " Special Commands:\n"
  59. " demo Uses structured mode to run 'sd changes' and\n"
  60. " format the output specially.\n"
  61. " detect [-s] file Uses ISDClientUtilities::DetectType to detect\n"
  62. " file's type the same way 'sd add file' does.\n"
  63. " set [-S service] var=[value]\n"
  64. " Uses ISDClientUtilities::Set to set variables\n"
  65. " similar to how 'sd set' does.\n"
  66. " ** DOES NOT UPDATE THE SDAPI OBJECT, therefore\n"
  67. " the 'query' command (below) cannot report the\n"
  68. " new value.\n"
  69. " query [-S service] [var]\n"
  70. " Uses ISDClientUtilities::QuerySettings to\n"
  71. " report the current settings similar to how\n"
  72. " 'sd set' does.\n"
  73. " ** QUERIES THE SDAPI OBJECT, therefore cannot\n"
  74. " report a new value from 'set' (above).\n"
  75. ;
  76. static BOOL s_fVerbose = FALSE;
  77. BOOL
  78. wcs2ansi(
  79. const WCHAR *pwsz,
  80. char *psz,
  81. DWORD pszlen
  82. )
  83. {
  84. BOOL rc;
  85. int len;
  86. assert(psz && pwsz);
  87. len = wcslen(pwsz);
  88. if (!len) {
  89. *psz = 0;
  90. return TRUE;
  91. }
  92. rc = WideCharToMultiByte(CP_ACP,
  93. WC_SEPCHARS | WC_COMPOSITECHECK,
  94. pwsz,
  95. len,
  96. psz,
  97. pszlen,
  98. NULL,
  99. NULL);
  100. if (!rc)
  101. return FALSE;
  102. psz[len] = 0;
  103. return TRUE;
  104. }
  105. BOOL
  106. ansi2wcs(
  107. const char *psz,
  108. WCHAR *pwsz,
  109. DWORD pwszlen
  110. )
  111. {
  112. BOOL rc;
  113. int len;
  114. assert(psz && pwsz);
  115. len = strlen(psz);
  116. if (!len) {
  117. *pwsz = 0L;
  118. return TRUE;
  119. }
  120. rc = MultiByteToWideChar(CP_ACP,
  121. MB_COMPOSITE,
  122. psz,
  123. len,
  124. pwsz,
  125. pwszlen);
  126. if (!rc)
  127. return FALSE;
  128. pwsz[len] = 0;
  129. return TRUE;
  130. }
  131. ///////////////////////////////////////////////////////////////////////////
  132. // Debugging Aids
  133. // compile-time assert
  134. #define CASSERT(expr) extern int cassert##__LINE__[(expr) ? 1 : 0]
  135. // run-time assert
  136. #ifdef DEBUG
  137. #define AssertHelper \
  138. do { \
  139. switch (MessageBox(NULL, "Assertion failed.", "SDAPITEST", MB_ABORTRETRYIGNORE)) { \
  140. case IDABORT: exit(2); break; \
  141. case IDRETRY: DebugBreak(); break; \
  142. } \
  143. } while (0)
  144. #define Assert(expr) \
  145. do { \
  146. if (!(expr)) { \
  147. printf("%s\n", #expr); \
  148. AssertHelper; \
  149. } \
  150. } while (0)
  151. #define Assert1(expr, fmt, arg1) \
  152. do { \
  153. if (!(expr)) { \
  154. printf(#fmt "\n", arg1); \
  155. AssertHelper; \
  156. } \
  157. } while (0)
  158. #define IfDebug(x) x
  159. #else
  160. #define Assert(expr) do {} while (0)
  161. #define Assert1(expr, fmt, arg1) do {} while (0)
  162. #define IfDebug(x)
  163. #endif
  164. #define Panic0(s) Assert1(FALSE, "%s", s)
  165. #define PanicSz(s) Panic0(s)
  166. ///////////////////////////////////////////////////////////////////////////
  167. // Embedded Interface Macros
  168. #define OffsetOf(s,m) (size_t)( (char *)&(((s *)0)->m) - (char *)0 )
  169. #define EmbeddorOf(C,m,p) ((C *)(((char *)p) - OffsetOf(C,m)))
  170. #define DeclareEmbeddedInterface(interface) \
  171. class E##interface : public interface \
  172. { \
  173. public: \
  174. STDMETHOD_(ULONG, AddRef)(); \
  175. STDMETHOD_(ULONG, Release)(); \
  176. STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj); \
  177. Declare##interface##Members(IMPL) \
  178. } m_##interface; \
  179. friend class E##interface;
  180. #define ImplementEmbeddedUnknown(embeddor, interface) \
  181. STDMETHODIMP embeddor::E##interface::QueryInterface(REFIID iid,void **ppv)\
  182. { \
  183. return EmbeddorOf(embeddor,m_##interface,this)->QueryInterface(iid,ppv);\
  184. } \
  185. STDMETHODIMP_(ULONG) embeddor::E##interface::AddRef() \
  186. { \
  187. return EmbeddorOf(embeddor, m_##interface, this)->AddRef(); \
  188. } \
  189. STDMETHODIMP_(ULONG) embeddor::E##interface::Release() \
  190. { \
  191. return EmbeddorOf(embeddor, m_##interface, this)->Release(); \
  192. }
  193. #define EMBEDDEDTHIS(embeddor, interface) \
  194. embeddor *pThis = EmbeddorOf(embeddor,m_##interface,this)
  195. ///////////////////////////////////////////////////////////////////////////
  196. // dbgPrintF
  197. static BOOL s_fDbg = FALSE;
  198. static int s_cIndent = 0;
  199. void dbgPrintF(const char *pszFmt, ...)
  200. {
  201. if (s_fDbg)
  202. {
  203. va_list args;
  204. va_start(args, pszFmt);
  205. for (int c = s_cIndent; c--;)
  206. printf("... ");
  207. vprintf(pszFmt, args);
  208. //printf("\n");
  209. va_end(args);
  210. }
  211. }
  212. class DbgIndent
  213. {
  214. public:
  215. DbgIndent() { s_cIndent++; }
  216. ~DbgIndent() { s_cIndent--; }
  217. };
  218. #define DBGINDENT DbgIndent dbgindent;
  219. class Ender
  220. {
  221. public:
  222. ~Ender() { dbgPrintF(""); dbgPrintF("---- end ----"); }
  223. };
  224. ///////////////////////////////////////////////////////////////////////////
  225. // VARIANT Helpers
  226. inline int SzToWz(UINT CodePage, const char* pszFrom, int cchFrom, WCHAR* pwzTo, int cchMax)
  227. {
  228. return MultiByteToWideChar(CodePage, 0, pszFrom, cchFrom, pwzTo, cchMax);
  229. }
  230. BSTR BstrFromSz(const char *psz, int cch = 0)
  231. {
  232. BSTR bstr;
  233. int cchActual;
  234. if (!cch)
  235. cch = strlen(psz);
  236. bstr = (BSTR)malloc((cch + 1) * sizeof(WCHAR));
  237. if (bstr)
  238. {
  239. ansi2wcs(psz, bstr, cch + 1);
  240. cchActual = SzToWz(CP_OEMCP, psz, cch, bstr, cch);
  241. bstr[cchActual] = 0;
  242. }
  243. return bstr;
  244. }
  245. HRESULT VariantSet(VARIANT *pvar, const char *psz, int cch = 0)
  246. {
  247. if (pvar->vt != VT_EMPTY || !psz)
  248. return E_INVALIDARG;
  249. V_BSTR(pvar) = BstrFromSz(psz, cch);
  250. V_VT(pvar) = VT_BSTR;
  251. if (!V_VT(pvar))
  252. return E_OUTOFMEMORY;
  253. return S_OK;
  254. }
  255. ///////////////////////////////////////////////////////////////////////////
  256. // Smart Interface Pointer
  257. void SetI(IUnknown * volatile *ppunkL, IUnknown *punkR)
  258. {
  259. // addref right side first, in case punkR and *ppunkL are on the same
  260. // object (weak refs) or are the same variable.
  261. if (punkR)
  262. punkR->AddRef();
  263. if (*ppunkL)
  264. {
  265. IUnknown *punkRel = *ppunkL;
  266. *ppunkL = 0;
  267. punkRel->Release();
  268. }
  269. *ppunkL = punkR;
  270. }
  271. #ifdef DEBUG
  272. void ReleaseI(IUnknown *punk)
  273. {
  274. if (punk)
  275. {
  276. if (IsBadReadPtr(punk,sizeof(void *)))
  277. {
  278. Panic0("Bad Punk");
  279. return;
  280. }
  281. if (IsBadReadPtr(*((void**) punk),sizeof(void *) * 3))
  282. {
  283. Panic0("Bad Vtable");
  284. return;
  285. }
  286. punk->Release();
  287. }
  288. }
  289. #else
  290. inline void ReleaseI(IUnknown *punk)
  291. {
  292. if (punk)
  293. punk->Release();
  294. }
  295. #endif
  296. template <class IFace> class PrivateRelease : public IFace
  297. {
  298. private:
  299. // force Release to be private to prevent "spfoo->Release()"!!!
  300. STDMETHODIMP_(ULONG) Release();
  301. };
  302. template <class IFace, const GUID *piid>
  303. class SPI
  304. {
  305. public:
  306. SPI() { m_p = 0; }
  307. //SPI(IFace *p) { m_p = p; if (m_p) m_p->AddRef(); }
  308. ~SPI() { ReleaseI(m_p); }
  309. operator IFace*() const { return m_p; }
  310. PrivateRelease<IFace> *operator->() const
  311. { return (PrivateRelease<IFace>*)m_p; }
  312. IFace **operator &() { Assert1(!m_p, "Non-empty %s as out param.", typeid(SPI<IFace, piid>).name()); return &m_p; }
  313. IFace *operator=(IFace *p) { Assert1(!m_p, "Non-empty %s in assignment.", typeid(SPI<IFace, piid>).name()); return m_p = p; }
  314. IFace *Transfer() { IFace *p = m_p; m_p = 0; return p; }
  315. IFace *Copy() { if (m_p) m_p->AddRef(); return m_p; }
  316. void Release() { SetI((IUnknown **)&m_p, 0); }
  317. void Set(IFace *p) { SetI((IUnknown **)&m_p, p); }
  318. bool operator!() { return (m_p == NULL); }
  319. BOOL FQuery(IUnknown *punk) { return FHrSucceeded(HrQuery(punk)); }
  320. HRESULT HrQuery(IUnknown *punk) { Assert1(!m_p, "Non-empty %s in HrQuery().", typeid(SPI<IFace, piid>).name()); return HrQueryInterface(punk, *piid, (void**)&m_p); }
  321. protected:
  322. IFace *m_p;
  323. private:
  324. // disallow these methods from being called
  325. SPI<IFace, piid> &operator=(const SPI<IFace, piid>& sp)
  326. { SetI((IUnknown **)&m_p, sp.m_p); return *this; }
  327. };
  328. #define DeclareSPI(TAG, IFace)\
  329. EXTERN_C const GUID CDECL IID_##IFace;\
  330. typedef SPI<IFace, &IID_##IFace> SP##TAG;
  331. DeclareSPI(API, ISDClientApi)
  332. ///////////////////////////////////////////////////////////////////////////
  333. // ClientUser
  334. #define DeclareIUnknownMembers(IPURE) \
  335. STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) IPURE; \
  336. STDMETHOD_(ULONG,AddRef) (THIS) IPURE; \
  337. STDMETHOD_(ULONG,Release) (THIS) IPURE; \
  338. class ClientUser : public ISDClientUser
  339. {
  340. public:
  341. ClientUser() : m_cRef(1), m_fFresh(TRUE), m_fDemo(FALSE) {}
  342. virtual ~ClientUser() {}
  343. DeclareIUnknownMembers(IMPL);
  344. DeclareISDClientUserMembers(IMPL);
  345. DeclareEmbeddedInterface(ISDActionUser);
  346. DeclareEmbeddedInterface(ISDInputUser);
  347. void SetDemo(BOOL fDemo) { m_fDemo = fDemo; m_fFresh = TRUE; }
  348. private:
  349. ULONG m_cRef;
  350. BOOL m_fFresh;
  351. BOOL m_fDemo;
  352. };
  353. STDMETHODIMP_(ULONG) ClientUser::AddRef()
  354. {
  355. return ++m_cRef;
  356. }
  357. STDMETHODIMP_(ULONG) ClientUser::Release()
  358. {
  359. if (--m_cRef > 0)
  360. return m_cRef;
  361. delete this;
  362. return 0;
  363. }
  364. STDMETHODIMP ClientUser::QueryInterface(REFIID iid, void** ppvObj)
  365. {
  366. HRESULT hr = S_OK;
  367. if (iid == IID_IUnknown || iid == IID_ISDClientUser)
  368. *ppvObj = (ISDClientUser*)this;
  369. else if (iid == IID_ISDActionUser)
  370. *ppvObj = &m_ISDActionUser;
  371. else if (iid == IID_ISDInputUser)
  372. *ppvObj = &m_ISDInputUser;
  373. else
  374. {
  375. *ppvObj = 0;
  376. return E_NOINTERFACE;
  377. }
  378. ((IUnknown*)*ppvObj)->AddRef();
  379. return hr;
  380. }
  381. // ---- ISDClientUser -----------------------------------------------------
  382. /*----------------------------------------------------------------------------
  383. ISDClientUser::OutputText
  384. Called for text data, generally the result of 'print textfile' or
  385. 'spec-command -o' (where spec-command is branch, change, client,
  386. label, protect, user, etc).
  387. IMPORTANT NOTE:
  388. The implementation of this method must translate '\n' in the pszText
  389. string to '\r\n' on Windows platforms to ensure correct line
  390. termination. This is particularly important when using 'print' to
  391. download the contents of a file.
  392. Args:
  393. pszText - [in] text string (not null terminated, and may
  394. contain embedded null characters that are part of
  395. the data itself).
  396. cchText - [in] number of bytes in pszText.
  397. Rets:
  398. The return value is ignored. For future compatibility, the method
  399. should return E_NOTIMPL if it is not implemented, or S_OK for success.
  400. ----------------------------------------------------------------------------*/
  401. STDMETHODIMP ClientUser::OutputText( const char *pszText,
  402. int cchText )
  403. {
  404. fwrite(pszText, cchText, 1, stdout);
  405. return S_OK;
  406. }
  407. /*----------------------------------------------------------------------------
  408. ISDClientUser::OutputBinary
  409. Called for binary data, generally the result of 'print nontextfile' or
  410. 'print unicodefile'.
  411. Args:
  412. pbData - [in] stream of bytes.
  413. cbData - [in] number of bytes in pbData.
  414. Rets:
  415. The return value is ignored. For future compatibility, the method
  416. should return E_NOTIMPL if it is not implemented, or S_OK for success.
  417. ----------------------------------------------------------------------------*/
  418. STDMETHODIMP ClientUser::OutputBinary( const unsigned char *pbData,
  419. int cbData )
  420. {
  421. static BOOL s_fBinary = FALSE;
  422. // we rely on a trailing zero length buffer to
  423. // tell us to turn off binary output for stdout.
  424. if (s_fBinary == !cbData)
  425. {
  426. // toggle
  427. s_fBinary = !!cbData;
  428. fflush(stdout);
  429. _setmode(_fileno(stdout), s_fBinary ? O_BINARY : O_TEXT);
  430. }
  431. fwrite(pbData, cbData, 1, stdout);
  432. return S_OK;
  433. }
  434. /*----------------------------------------------------------------------------
  435. ISDClientUser::OutputInfo
  436. Called for tabular data, usually the results of commands that affect
  437. sets of files.
  438. Some commands also support structured output; see ISDClientApi::Init
  439. and ISDClientUser::OutputStructured for more information.
  440. Args:
  441. cIndent - [in] indentation levels 0 - 2 (loosely implies
  442. hierarchical relationship). The SD.EXE client
  443. program normally handles 1 by prepending "... " to
  444. the string, and handles 2 by prepending "... ... ".
  445. pszInfo - [in] informational message string.
  446. Rets:
  447. The return value is ignored. For future compatibility, the method
  448. should return E_NOTIMPL if it is not implemented, or S_OK for success.
  449. ----------------------------------------------------------------------------*/
  450. STDMETHODIMP ClientUser::OutputInfo( int cIndent,
  451. const char *pszInfo )
  452. {
  453. if (s_fVerbose)
  454. printf(cIndent ? "info%d:\t" : "info:\t", cIndent);
  455. while (cIndent--)
  456. printf("");
  457. printf("%s\n", pszInfo);
  458. return S_OK;
  459. }
  460. /*----------------------------------------------------------------------------
  461. ISDClientUser::OutputWarning
  462. Called for warning messages (any text normally displayed in yellow by
  463. the SD.EXE client program).
  464. As of this writing, there is no list of the possible warning messages.
  465. Args:
  466. cIndent - [in] indentation levels 0 - 2 (loosely implies
  467. hierarchical relationship). The SD.EXE client
  468. program normally handles 1 by prepending "... " to
  469. the string, and handles 2 by prepending "... ... ".
  470. pszWarning - [in] warning message string.
  471. fEmptyReason - [in] the message is an "empty reason" message.
  472. Rets:
  473. The return value is ignored. For future compatibility, the method
  474. should return E_NOTIMPL if it is not implemented, or S_OK for success.
  475. ----------------------------------------------------------------------------*/
  476. STDMETHODIMP ClientUser::OutputWarning( int cIndent,
  477. const char *pszWarning,
  478. BOOL fEmptyReason )
  479. {
  480. if (s_fVerbose)
  481. printf(cIndent ? "%s%d:\t" : "%s:\t",
  482. fEmptyReason ? "empty" : "warn", cIndent);
  483. while (cIndent--)
  484. printf("");
  485. printf("%s\n", pszWarning);
  486. return S_OK;
  487. }
  488. /*----------------------------------------------------------------------------
  489. ISDClientUser::OutputError
  490. Called for error messages, failed commands (any text normally
  491. displayed in red by the SD.EXE client program).
  492. As of this writing, there is no list of the possible error messages.
  493. Args:
  494. pszError - [in] error message string.
  495. Rets:
  496. The return value is ignored. For future compatibility, the method
  497. should return E_NOTIMPL if it is not implemented, or S_OK for success.
  498. ----------------------------------------------------------------------------*/
  499. STDMETHODIMP ClientUser::OutputError( const char *pszError )
  500. {
  501. if (s_fVerbose)
  502. fprintf(stderr, "error:\t");
  503. fprintf(stderr, "%s", pszError);
  504. return S_OK;
  505. }
  506. /*----------------------------------------------------------------------------
  507. ISDClientUser::OutputStructured
  508. Called for tabular data if the ISDClientApi::Init call requested
  509. structured output and the command being run supports structured
  510. output.
  511. See the ISDVars interface in SDAPI.H for more information.
  512. Args:
  513. pVars - [in] pointer to object containing the data; use the
  514. provided accessor methods to retrieve the data.
  515. Rets:
  516. The return value is ignored. For future compatibility, the method
  517. should return E_NOTIMPL if it is not implemented, or S_OK for success.
  518. ----------------------------------------------------------------------------*/
  519. STDMETHODIMP ClientUser::OutputStructured( ISDVars *pVars )
  520. {
  521. // your code here
  522. if (m_fDemo)
  523. {
  524. // sample implementation -- illustrates how to use structured mode.
  525. const char *pszChange;
  526. const char *pszTime;
  527. const char *pszUser;
  528. const char *pszDesc;
  529. //const char *pszClient;
  530. //const char *pszStatus;
  531. int nChange;
  532. time_t ttTime;
  533. tm tmTime;
  534. char szDesc[32];
  535. if (m_fFresh)
  536. {
  537. printf("CHANGE DATE---- TIME---- "
  538. "USER---------------- DESC------------------------\n");
  539. m_fFresh = FALSE;
  540. }
  541. pVars->GetVar("change", &pszChange, 0, 0);
  542. pVars->GetVar("time", &pszTime, 0, 0);
  543. pVars->GetVar("user", &pszUser, 0, 0);
  544. pVars->GetVar("desc", &pszDesc, 0, 0);
  545. //pVars->GetVar("client", &pszClient, 0, 0);
  546. //pVars->GetVar("status", &pszStatus, 0, 0);
  547. nChange = atoi(pszChange);
  548. ttTime = atoi(pszTime);
  549. tmTime = *gmtime(&ttTime);
  550. StringCchCopy(szDesc, DIMA(szDesc), pszDesc);
  551. szDesc[sizeof(szDesc) - 1] = 0;
  552. for (char *psz = szDesc; *psz; ++psz)
  553. if (*psz == '\r' || *psz == '\n')
  554. *psz = ' ';
  555. printf("%6d %2d/%02d/%02d %2d:%02d:%02d %-20s %.28s\n",
  556. nChange,
  557. tmTime.tm_mon, tmTime.tm_mday, tmTime.tm_year % 100,
  558. tmTime.tm_hour, tmTime.tm_min, tmTime.tm_sec,
  559. pszUser,
  560. szDesc);
  561. }
  562. else
  563. {
  564. // sample implementation -- merely dumps the variables; useful only
  565. // for inspecting the output and learning the possible variables.
  566. HRESULT hr;
  567. const char *pszVar;
  568. const char *pszValue;
  569. BOOL fUnicode;
  570. int ii;
  571. for (ii = 0; 1; ii++)
  572. {
  573. hr = pVars->GetVarByIndex(ii, &pszVar, &pszValue, 0, &fUnicode);
  574. if (hr != S_OK)
  575. break;
  576. // output the variable name and value
  577. printf(fUnicode ? "%s[unicode]=%S\n" : "%s=%s\n", pszVar, pszValue);
  578. }
  579. }
  580. return S_OK;
  581. }
  582. /*----------------------------------------------------------------------------
  583. ISDClientUser::Finished
  584. Called by ISDClientUser::Run when a command has finished. The command
  585. may or may not have completed successfully.
  586. For example, this is where SD.EXE displays the auto-summary (see the
  587. -Y option in 'sd -?' for more information).
  588. Rets:
  589. The return value is ignored. For future compatibility, the method
  590. should return E_NOTIMPL if it is not implemented, or S_OK for success.
  591. ----------------------------------------------------------------------------*/
  592. STDMETHODIMP ClientUser::Finished()
  593. {
  594. // your code here
  595. return S_OK;
  596. }
  597. // ---- ISDInputUser ------------------------------------------------------
  598. ImplementEmbeddedUnknown(ClientUser, ISDInputUser)
  599. /*----------------------------------------------------------------------------
  600. ISDClientUser::InputData
  601. Called to provide data to 'spec-command -i', where spec-command is
  602. branch, change, client, label, protect, user, etc.
  603. Args:
  604. pvarInput - [in] pointer to VARIANT to contain input data.
  605. NOTE: SD will convert the BSTR from codepage 1200
  606. (Unicode) to CP_OEMCP (the OEM codepage).
  607. Rets:
  608. HRESULT - return S_OK to indicate strInput contains the data.
  609. return an error HRESULT code to indicate an error
  610. has occurred.
  611. ----------------------------------------------------------------------------*/
  612. STDMETHODIMP ClientUser::EISDInputUser::InputData( VARIANT* pvarInput )
  613. {
  614. return E_NOTIMPL;
  615. }
  616. /*----------------------------------------------------------------------------
  617. ISDInputUser::Prompt
  618. Called to prompt the user for a response. Called by 'resolve', and
  619. also when prompting the user to enter a password.
  620. Args:
  621. pszPrompt - [in] prompt string.
  622. pvarResponse - [in] pointer to VARIANT to contain user's response.
  623. NOTE: SD will convert the BSTR from codepage 1200
  624. (Unicode) to CP_OEMCP (the OEM codepage).
  625. fPassword - [in] prompting for a password (hide the input text).
  626. Rets:
  627. HRESULT - return S_OK to indicate pvarResponse contains the
  628. user's response. return an error HRESULT code to
  629. indicate an error has occurred.
  630. ----------------------------------------------------------------------------*/
  631. STDMETHODIMP ClientUser::EISDInputUser::Prompt( const char* pszPrompt, VARIANT* pvarResponse, BOOL fPassword )
  632. {
  633. char sz[1024];
  634. if (fPassword)
  635. return E_NOTIMPL;
  636. if (s_fVerbose)
  637. printf("prompt:\t");
  638. printf("%s", pszPrompt);
  639. fflush(stdout);
  640. fflush(stdin);
  641. fgets(sz, sizeof(sz), stdin);
  642. return VariantSet(pvarResponse, sz);
  643. }
  644. /*----------------------------------------------------------------------------
  645. ISDInputUser::PromptYesNo
  646. Called to prompt the user for a yes/no response.
  647. Currently only called by 'resolve'.
  648. Args:
  649. pszPrompt - [in] prompt string.
  650. Rets:
  651. HRESULT - return S_OK for Yes. return S_FALSE for No. return
  652. E_NOTIMPL to allow the SDAPI to perform the default
  653. behavior, which is to call ISDClientUser::Prompt and
  654. loop until the user responds y/Y/n/N or an error
  655. occurs. return other error HRESULT codes to
  656. indicate an error has occurred.
  657. ----------------------------------------------------------------------------*/
  658. STDMETHODIMP ClientUser::EISDInputUser::PromptYesNo( const char* pszPrompt )
  659. {
  660. return E_NOTIMPL;
  661. }
  662. /*----------------------------------------------------------------------------
  663. ISDInputUser::ErrorPause
  664. Called to display an error message and wait for the user before
  665. continuing.
  666. Args:
  667. pszError - [in] message string.
  668. Rets:
  669. HRESULT - return S_OK to continue. return an error HRESULT
  670. code to indicate an error has occurred.
  671. ----------------------------------------------------------------------------*/
  672. STDMETHODIMP ClientUser::EISDInputUser::ErrorPause( const char* pszError )
  673. {
  674. EMBEDDEDTHIS(ClientUser, ISDInputUser);
  675. char sz[1024];
  676. pThis->OutputError(pszError);
  677. printf("prompt:\tHit return to continue...");
  678. fgets(sz, sizeof(sz), stdin);
  679. return S_OK;
  680. }
  681. // ---- ISDActionUser -----------------------------------------------------
  682. ImplementEmbeddedUnknown(ClientUser, ISDActionUser)
  683. /*----------------------------------------------------------------------------
  684. ISDActionUser::Diff
  685. Called by 'resolve' when the user selects any of the 'd' (diff)
  686. actions. Also called by 'diff'.
  687. In particular, this is not called by 'diff2' because the server
  688. computes the diff and sends the computed diff to the client.
  689. Args:
  690. pszDiffCmd - [in] may be NULL. user-defined command to launch
  691. external diff engine, as defined by the SDDIFF or
  692. SDUDIFF variables; see 'sd help variables' for more
  693. information.
  694. pszLeft - [in] name of Left file for the diff.
  695. pszRight - [in] name of Right file for the diff.
  696. eTextual - [in] indicates the lowest common denominator file
  697. type for the 2 input files (non-textual, text, or
  698. Unicode).
  699. pszFlags - [in] flags for the diff engine (per the -d<flags>
  700. option).
  701. pszPaginateCmd - [in] may be NULL. user-defined command to pipe the
  702. diff output through, as defined by the SDPAGER
  703. variable; see 'sd help variables' for more info.
  704. For example, "more.exe".
  705. Rets:
  706. HRESULT - return S_OK to indicate the diff has been performed
  707. successfully. return E_NOTIMPL to allow the SDAPI
  708. to perform the default behavior, which is to launch
  709. an external diff engine (if defined) or use use the
  710. internal SD diff engine. return other error HRESULT
  711. codes to indicate an error has occurred.
  712. ----------------------------------------------------------------------------*/
  713. STDMETHODIMP ClientUser::EISDActionUser::Diff( const char *pszDiffCmd,
  714. const char *pszLeft,
  715. const char *pszRight,
  716. DWORD eTextual,
  717. const char *pszFlags,
  718. const char *pszPaginateCmd )
  719. {
  720. return E_NOTIMPL;
  721. }
  722. /*----------------------------------------------------------------------------
  723. ISDActionUser::EditForm
  724. Called by all commands that launch a user form (e.g. 'branch',
  725. 'change', 'client', etc).
  726. IMPORTANT NOTE:
  727. This command is synchronous in nature; if your implementation launches
  728. an editor, your code must not return until the user has finished
  729. editing the file.
  730. Args:
  731. pszEditCmd - [in] may by NULL. user-defined command to launch
  732. external editor, as defined by the SDFORMEDITOR
  733. variable; see 'sd help variables' for more
  734. information.
  735. pszFile - [in] name of file to edit.
  736. Rets:
  737. HRESULT - return S_OK to indicate the user has finished
  738. editing the file. return E_NOTIMPL to allow the
  739. SDAPI to perform the default behavior, which is to
  740. launch an external editor engine (if defined) or to
  741. launch notepad.exe. return other error HRESULT
  742. codes to indicate an error has occurred.
  743. ----------------------------------------------------------------------------*/
  744. STDMETHODIMP ClientUser::EISDActionUser::EditForm( const char *pszEditCmd,
  745. const char *pszFile )
  746. {
  747. return E_NOTIMPL;
  748. }
  749. /*----------------------------------------------------------------------------
  750. ISDActionUser::EditFile
  751. Called by 'resolve' when the user selects any of the 'e' actions.
  752. IMPORTANT NOTE:
  753. This command is synchronous in nature; if your implementation launches
  754. an editor, your code must not return until the user has finished
  755. editing the file.
  756. Args:
  757. pszEditCmd - [in] may by NULL. user-defined command to launch
  758. external editor, as defined by the SDEDITOR, or
  759. SDUEDITOR variables; see 'sd help variables' for
  760. more information.
  761. pszFile - [in] name of file to edit.
  762. eTextual - [in] indicates the file type (non-textual, text, or
  763. Unicode).
  764. Rets:
  765. HRESULT - return S_OK to indicate the user has finished
  766. editing the file. return E_NOTIMPL to allow the
  767. SDAPI to perform the default behavior, which is to
  768. launch an external editor engine (if defined) or to
  769. launch notepad.exe. return other error HRESULT
  770. codes to indicate an error has occurred.
  771. ----------------------------------------------------------------------------*/
  772. STDMETHODIMP ClientUser::EISDActionUser::EditFile( const char *pszEditCmd,
  773. const char *pszFile,
  774. DWORD eTextual )
  775. {
  776. return E_NOTIMPL;
  777. }
  778. /*----------------------------------------------------------------------------
  779. ISDActionUser::Merge
  780. Called by the 'resolve' command when the user selects the 'm' action
  781. to invoke an external merge engine.
  782. Args:
  783. pszMergeCmd - [in] may be NULL. user-defined command to launch
  784. external merge engine, as defined by the SDMERGE
  785. variable; see 'sd help variables' for more info.
  786. pszBase - [in] name of Base file for the 3-way merge.
  787. pszTheirs - [in] name of Theirs file for the 3-way merge.
  788. pszYours - [in] name of Yours file for the 3-way merge.
  789. pszResult - [in] name of file where the resulting merged file
  790. must be written.
  791. eTextual - [in] indicates the lowest common denominator file
  792. type for the 3 input files (non-textual, text, or
  793. Unicode).
  794. Rets:
  795. HRESULT - return S_OK to indicate the merge has been performed
  796. successfully. return E_NOTIMPL to allow the SDAPI
  797. to perform the default behavior, which is to launch
  798. the external merge engine (if defined). return
  799. other error HRESULT codes to indicate an error has
  800. occurred.
  801. ----------------------------------------------------------------------------*/
  802. STDMETHODIMP ClientUser::EISDActionUser::Merge( const char *pszMergeCmd,
  803. const char *pszBase,
  804. const char *pszTheirs,
  805. const char *pszYours,
  806. const char *pszResult,
  807. DWORD eTextual )
  808. {
  809. return E_NOTIMPL;
  810. }
  811. ///////////////////////////////////////////////////////////////////////////
  812. // Console Mode
  813. HANDLE g_hRestoreConsole = INVALID_HANDLE_VALUE;
  814. DWORD g_dwResetConsoleMode;
  815. void RestoreConsole_SetMode(DWORD dw)
  816. {
  817. g_hRestoreConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  818. g_dwResetConsoleMode = SetConsoleMode(g_hRestoreConsole, dw);
  819. }
  820. BOOL WINAPI RestoreConsole_BreakHandler(DWORD dwCtrlType)
  821. {
  822. if (g_hRestoreConsole != INVALID_HANDLE_VALUE)
  823. SetConsoleMode(g_hRestoreConsole, g_dwResetConsoleMode);
  824. #if 0
  825. if (g_hRestoreConsole != INVALID_HANDLE_VALUE)
  826. SetConsoleTextAttribute(g_hRestoreConsole, g_wRestoreAttr);
  827. #endif
  828. return FALSE;
  829. }
  830. ///////////////////////////////////////////////////////////////////////////
  831. // Options (a dumbed-down option parsing class)
  832. enum { c_cMaxOptions = 20 };
  833. enum OptFlag
  834. {
  835. // bitwise selectors
  836. OPT_ONE = 0x01, // exactly one
  837. OPT_TWO = 0x02, // exactly two
  838. OPT_THREE = 0x04, // exactly three
  839. OPT_MORE = 0x10, // more than three
  840. OPT_NONE = 0x20, // require none
  841. // combos of the above
  842. OPT_OPT = OPT_NONE|OPT_ONE,
  843. OPT_ANY = OPT_NONE|OPT_ONE|OPT_TWO|OPT_THREE|OPT_MORE,
  844. OPT_SOME = OPT_ONE|OPT_TWO|OPT_THREE|OPT_MORE,
  845. };
  846. class Options
  847. {
  848. public:
  849. Options() { m_cOpts = 0; m_pszError = 0; }
  850. ~Options() { delete m_pszError; }
  851. BOOL Parse(int &argc, const char **&argv, const char *pszOpts,
  852. int flag, const char *pszUsage);
  853. const char* GetErrorString() const { Assert(m_pszError); return m_pszError; }
  854. const char* GetValue(char chOpt, int iSubOpt) const;
  855. const char* operator[](char chOpt) const { return GetValue(chOpt, 0); }
  856. protected:
  857. void ClearError() { delete m_pszError; m_pszError = 0; }
  858. void SetError(const char *pszUsage, const char *pszFormat, ...);
  859. private:
  860. int m_cOpts;
  861. char m_rgchFlags[c_cMaxOptions];
  862. const char* m_rgpszOpts[c_cMaxOptions];
  863. char* m_pszError;
  864. };
  865. static const char *GetArg(const char *psz, int &argc, const char **&argv)
  866. {
  867. psz++;
  868. if (*psz)
  869. return psz;
  870. if (!argc)
  871. return 0;
  872. argc--;
  873. argv++;
  874. return argv[0];
  875. }
  876. BOOL Options::Parse(int &argc, const char **&argv, const char *pszOpts,
  877. int flag, const char *pszUsage)
  878. {
  879. BOOL fSlash; // allow both - and /
  880. const char *psz;
  881. const char *pszArg;
  882. Assert(pszOpts);
  883. Assert(pszUsage);
  884. ClearError();
  885. fSlash = (*pszOpts == '/');
  886. if (fSlash)
  887. pszOpts++;
  888. // parse flags
  889. while (argc)
  890. {
  891. if (argv[0][0] != '-' && (!fSlash || argv[0][0] != '/'))
  892. break; // not a flag, so done parsing
  893. if (argv[0][1] == '-')
  894. {
  895. // '--' is special and means that subsequent arguments should
  896. // not be treated as flags even if they being with '-'.
  897. argc--;
  898. argv++;
  899. break;
  900. }
  901. pszArg = argv[0];
  902. while (TRUE)
  903. {
  904. pszArg++; // skip the '-' or option character
  905. if (!*pszArg)
  906. break;
  907. #ifdef DEBUG
  908. if (*pszArg == '!')
  909. {
  910. DebugBreak();
  911. continue;
  912. }
  913. #endif
  914. psz = pszOpts;
  915. while (*psz && *psz != *pszArg)
  916. psz++;
  917. if (!*psz)
  918. {
  919. SetError(pszUsage, "Invalid option: '%c'.", *pszArg);
  920. return FALSE;
  921. }
  922. if (m_cOpts >= c_cMaxOptions)
  923. {
  924. SetError(pszUsage, "Too many options.");
  925. return FALSE;
  926. }
  927. m_rgchFlags[m_cOpts] = *pszArg;
  928. m_rgpszOpts[m_cOpts] = "true";
  929. if (psz[1] == '.')
  930. {
  931. m_rgpszOpts[m_cOpts++] = pszArg + 1;
  932. break;
  933. }
  934. else if (psz[1] == ':')
  935. {
  936. psz = GetArg(pszArg, argc, argv);
  937. if (!psz)
  938. {
  939. SetError(pszUsage, "Option '%c' missing required argument.", *pszArg);
  940. return FALSE;
  941. }
  942. m_rgpszOpts[m_cOpts++] = psz;
  943. break;
  944. }
  945. m_cOpts++;
  946. }
  947. argc--;
  948. argv++;
  949. }
  950. // check number of arguments
  951. if (!((argc == 0 && (flag & OPT_NONE)) ||
  952. (argc == 1 && (flag & OPT_ONE)) ||
  953. (argc == 2 && (flag & OPT_TWO)) ||
  954. (argc == 3 && (flag & OPT_THREE)) ||
  955. (argc > 3 && (flag & OPT_MORE))))
  956. {
  957. SetError(pszUsage, "Missing/wrong number of arguments.");
  958. return FALSE;
  959. }
  960. return TRUE;
  961. }
  962. void Options::SetError(const char *pszUsage, const char *pszFormat, ...)
  963. {
  964. int cch;
  965. va_list args;
  966. va_start(args, pszFormat);
  967. ClearError();
  968. m_pszError = new char[1024]; //$ todo: (chrisant) BUFFER OVERRUN
  969. StringCchPrintf(m_pszError, 1024, "Usage: %s\n", pszUsage);
  970. cch = strlen(m_pszError);
  971. StringCchVPrintfEx(m_pszError + cch,
  972. 1024 - cch,
  973. NULL,
  974. NULL,
  975. 0,
  976. pszFormat,
  977. args);
  978. va_end(args);
  979. }
  980. const char *Options::GetValue(char chOpt, int iSubOpt) const
  981. {
  982. for (int ii = m_cOpts; ii--;)
  983. if (chOpt == m_rgchFlags[ii])
  984. if (iSubOpt-- == 0)
  985. return m_rgpszOpts[ii];
  986. return 0;
  987. }
  988. ///////////////////////////////////////////////////////////////////////////
  989. // RunCmd
  990. static void PrintError(HRESULT hr)
  991. {
  992. char sz[1024];
  993. int cch;
  994. cch = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
  995. 0, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  996. sz, sizeof(sz), NULL);
  997. sz[cch] = 0;
  998. fprintf(stderr, "error: (0x%08.8x)\n%s", hr, sz);
  999. }
  1000. static BOOL FStrPrefixCut(const char *pszPrefix, const char **ppsz)
  1001. {
  1002. int cch = strlen(pszPrefix);
  1003. BOOL fPrefix = (strncmp(*ppsz, pszPrefix, cch) == 0 && (!(*ppsz)[cch] || isspace((*ppsz)[cch])));
  1004. if (fPrefix)
  1005. {
  1006. *ppsz += cch;
  1007. while (isspace(**ppsz))
  1008. (*ppsz)++;
  1009. }
  1010. return fPrefix;
  1011. }
  1012. HRESULT Cmd_Detect(ISDClientApi *papi, const char *psz)
  1013. {
  1014. HRESULT hr = S_OK;
  1015. ISDClientUtilities *putil;
  1016. BOOL fServer = FALSE;
  1017. // check for -s flag, to detect based on the server's capabilities
  1018. if (FStrPrefixCut("-s", &psz))
  1019. fServer = TRUE;
  1020. // check for file argument
  1021. if (*psz && *psz != '-')
  1022. {
  1023. hr = papi->QueryInterface(IID_ISDClientUtilities, (void**)&putil);
  1024. if (SUCCEEDED(hr))
  1025. {
  1026. DWORD tt;
  1027. const char *pszType;
  1028. // detect file type
  1029. hr = putil->DetectType(psz, &tt, &pszType, fServer);
  1030. if (SUCCEEDED(hr))
  1031. {
  1032. if (pszType)
  1033. {
  1034. const char *pszTT;
  1035. switch (tt)
  1036. {
  1037. default:
  1038. case SDTT_NONTEXT: pszTT = "SDT_NONTEXT"; break;
  1039. case SDTT_TEXT: pszTT = "SDT_TEXT"; break;
  1040. case SDTT_UNICODE: pszTT = "SDT_UNICODE"; break;
  1041. }
  1042. printf("%s - %s (%s)\n", psz, pszType, pszTT);
  1043. }
  1044. else
  1045. {
  1046. printf("%s - unable to determine file type.\n", psz);
  1047. }
  1048. }
  1049. else
  1050. {
  1051. PrintError(hr);
  1052. }
  1053. putil->Release();
  1054. }
  1055. }
  1056. else
  1057. {
  1058. fprintf(stderr, "Usage: detect [-s] file\n\n"
  1059. "The -s flag set the fServer parameter to TRUE in the DetectType call.\n"
  1060. "Please refer to the SDAPI documentation for more information.\n");
  1061. }
  1062. return hr;
  1063. }
  1064. HRESULT Cmd_Set(ISDClientApi *papi, const char *psz)
  1065. {
  1066. HRESULT hr = S_OK;
  1067. ISDClientUtilities *putil;
  1068. char szVar[64];
  1069. char szService[64];
  1070. const char *pszValue;
  1071. szVar[0] = 0;
  1072. szService[0] = 0;
  1073. // check for the "-S servicename" optional flag
  1074. if (FStrPrefixCut("-S", &psz))
  1075. {
  1076. pszValue = psz;
  1077. while (*pszValue && !isspace(*pszValue))
  1078. pszValue++;
  1079. lstrcpyn(szService, psz, min(pszValue - psz + 1, sizeof(szService)));
  1080. psz = pszValue;
  1081. while (isspace(*psz))
  1082. psz++;
  1083. }
  1084. // find the end of the variable name
  1085. pszValue = strpbrk(psz, "= \t");
  1086. if (*psz && *psz != '-' && pszValue && *pszValue == '=')
  1087. {
  1088. // copy the variable name
  1089. lstrcpyn(szVar, psz, min(pszValue - psz + 1, sizeof(szVar)));
  1090. pszValue++;
  1091. hr = papi->QueryInterface(IID_ISDClientUtilities, (void**)&putil);
  1092. if (SUCCEEDED(hr))
  1093. {
  1094. // set the variable and value
  1095. hr = putil->Set(szVar, pszValue, FALSE, szService);
  1096. if (FAILED(hr))
  1097. {
  1098. PrintError(hr);
  1099. }
  1100. putil->Release();
  1101. }
  1102. }
  1103. else
  1104. {
  1105. fprintf(stderr, "Usage: set [-S service] var=[value]\n");
  1106. }
  1107. return hr;
  1108. }
  1109. HRESULT Cmd_Query(ISDClientApi *papi, const char *psz)
  1110. {
  1111. HRESULT hr = S_OK;
  1112. ISDClientUtilities *putil;
  1113. char szService[64];
  1114. const char *pszValue;
  1115. szService[0] = 0;
  1116. // check for the "-S servicename" optional flag
  1117. if (FStrPrefixCut("-S", &psz))
  1118. {
  1119. pszValue = psz;
  1120. while (*pszValue && !isspace(*pszValue))
  1121. pszValue++;
  1122. lstrcpyn(szService, psz, min(pszValue - psz + 1, sizeof(szService)));
  1123. psz = pszValue;
  1124. while (isspace(*psz))
  1125. psz++;
  1126. }
  1127. // find the end of the (optional) variable name
  1128. pszValue = strpbrk(psz, "= \t");
  1129. if (*psz == '-' || pszValue)
  1130. {
  1131. fprintf(stderr, "Usage: query [-S service] [var]\n");
  1132. return S_OK;
  1133. }
  1134. hr = papi->QueryInterface(IID_ISDClientUtilities, (void**)&putil);
  1135. if (SUCCEEDED(hr))
  1136. {
  1137. ISDVars *pVars;
  1138. hr = putil->QuerySettings(psz, szService, &pVars);
  1139. if (SUCCEEDED(hr))
  1140. {
  1141. int ii;
  1142. for (ii = 0; 1; ii++)
  1143. {
  1144. const char *pszVar;
  1145. const char *pszValue;
  1146. const char *pszHow;
  1147. const char *pszType;
  1148. if (pVars->GetVarX("var", ii, &pszVar, 0, 0) != S_OK)
  1149. break;
  1150. pVars->GetVarX("value", ii, &pszValue, 0, 0);
  1151. pVars->GetVarX("how", ii, &pszHow, 0, 0);
  1152. pVars->GetVarX("type", ii, &pszType, 0, 0);
  1153. printf("%s=%s (%s)", pszVar, pszValue, pszHow);
  1154. if (strcmp(pszType, "env") != 0)
  1155. printf(" (%s)", pszType);
  1156. printf("\n");
  1157. }
  1158. pVars->Release();
  1159. }
  1160. else
  1161. {
  1162. PrintError(hr);
  1163. }
  1164. putil->Release();
  1165. }
  1166. return hr;
  1167. }
  1168. HRESULT RunCmd(ISDClientApi *papi, const char *psz, int argc, const char **argv, ClientUser *pui, BOOL fStructured)
  1169. {
  1170. BOOL fDemo = FALSE;
  1171. DWORD dwTicks;
  1172. HRESULT hr = S_OK;
  1173. char *pszFree = 0;
  1174. dbgPrintF("\nRUN:\t[%s]\n", psz);
  1175. dwTicks = GetTickCount();
  1176. if (FStrPrefixCut("detect", &psz))
  1177. {
  1178. hr = Cmd_Detect(papi, psz);
  1179. }
  1180. else if (FStrPrefixCut("set", &psz))
  1181. {
  1182. hr = Cmd_Set(papi, psz);
  1183. }
  1184. else if (FStrPrefixCut("query", &psz))
  1185. {
  1186. hr = Cmd_Query(papi, psz);
  1187. }
  1188. else
  1189. {
  1190. if (FStrPrefixCut("demo", &psz))
  1191. {
  1192. // demo mode
  1193. fDemo = TRUE;
  1194. fStructured = TRUE;
  1195. // alloc string (length of command string, plus "changes ")
  1196. pszFree = (char*)malloc(lstrlen(psz) + 8 + 1);
  1197. // format string
  1198. StringCchPrintf(pszFree, lstrlen(psz) + 8 + 1, "changes %s", psz);
  1199. // use the formatted string
  1200. psz = pszFree;
  1201. }
  1202. pui->SetDemo(fDemo);
  1203. if (argc && argv)
  1204. papi->SetArgv(argc, argv);
  1205. hr = papi->Run(psz, pui, fStructured);
  1206. }
  1207. dwTicks = GetTickCount() - dwTicks;
  1208. dbgPrintF("[took %dms to run command]\n", dwTicks);
  1209. free(pszFree);
  1210. return hr;
  1211. }
  1212. ///////////////////////////////////////////////////////////////////////////
  1213. // main
  1214. int __cdecl main(int argc, const char **argv)
  1215. {
  1216. ClientUser *pui = 0;
  1217. SPAPI spapi;
  1218. const char *pszFile = 0;
  1219. #if 0
  1220. BOOL fCreate = FALSE;
  1221. #endif
  1222. BOOL fDemo = FALSE;
  1223. BOOL fStructured = FALSE;
  1224. BOOL fStdin = FALSE;
  1225. int nRet = 0;
  1226. DWORD dwTicks;
  1227. HRESULT hr;
  1228. SetConsoleCtrlHandler(RestoreConsole_BreakHandler, TRUE);
  1229. if (argc)
  1230. {
  1231. // skip app name
  1232. argc--;
  1233. argv++;
  1234. if (argc && !strcmp(argv[0], "-!"))
  1235. {
  1236. argc--;
  1237. argv++;
  1238. DebugBreak();
  1239. }
  1240. #if 0
  1241. if (argc && !strcmp(argv[0], "-C"))
  1242. {
  1243. argc--;
  1244. argv++;
  1245. fCreate = TRUE;
  1246. }
  1247. #endif
  1248. }
  1249. #ifdef DEBUG
  1250. {
  1251. int n = argc;
  1252. const char **pp = argv;
  1253. printf("argc = %d\n", n);
  1254. for (int i = 0; i < n; i++)
  1255. {
  1256. printf("%d:\t[%s]\n", i, pp[0]);
  1257. pp++;
  1258. }
  1259. }
  1260. #endif
  1261. // parse options
  1262. Options opts;
  1263. const char *s;
  1264. if (!opts.Parse(argc, argv, "?p:u:P:c:H:i:I:x:dvT", OPT_OPT, usage))
  1265. {
  1266. fprintf(stderr, "%s", opts.GetErrorString());
  1267. return 1;
  1268. }
  1269. if (opts['?'])
  1270. {
  1271. // full usage text
  1272. printf("%s", long_usage);
  1273. return 0;
  1274. }
  1275. if (opts['d']) s_fDbg = TRUE;
  1276. if (opts['v']) s_fVerbose = TRUE;
  1277. if (opts['T']) fStructured = TRUE;
  1278. if (pszFile = opts['x'])
  1279. {
  1280. fStdin = FALSE;
  1281. if (strcmp(pszFile, "-") == 0)
  1282. {
  1283. pszFile = 0;
  1284. fStdin = TRUE;
  1285. }
  1286. }
  1287. // create SDAPI object
  1288. #if 1
  1289. hr = CreateSDAPIObject(CLSID_SDAPI, (void**)&spapi);
  1290. #else
  1291. hr = CoInitialize(0);
  1292. if (SUCCEEDED(hr))
  1293. {
  1294. hr = CoCreateInstance(CLSID_SDAPI, NULL, CLSCTX_INPROC_SERVER,
  1295. IID_ISDClientApi, (void**)&spapi);
  1296. }
  1297. #endif
  1298. if (FAILED(hr))
  1299. {
  1300. fprintf(stderr, "ERROR:\tunable to create SDAPI object (0x%08x).\n", hr);
  1301. return 1;
  1302. }
  1303. // initialize the SDAPI object based on the options
  1304. if (s = opts['I']) spapi->LoadIniFile(s, TRUE);
  1305. if (s = opts['i']) spapi->LoadIniFile(s, FALSE);
  1306. if (s = opts['p']) spapi->SetPort(s);
  1307. if (s = opts['u']) spapi->SetUser(s);
  1308. if (s = opts['P']) spapi->SetPassword(s);
  1309. if (s = opts['c']) spapi->SetClient(s);
  1310. if (s = opts['H']) spapi->SetHost(s);
  1311. pui = new ClientUser;
  1312. if (!pui)
  1313. {
  1314. fprintf(stderr, "ERROR:\tunable to allocate ClientUser.\n");
  1315. return 1;
  1316. }
  1317. // connect to server
  1318. dbgPrintF("\nINIT:\tconnect to server\n");
  1319. dwTicks = GetTickCount();
  1320. hr = spapi->Init(pui);
  1321. dwTicks = GetTickCount() - dwTicks;
  1322. dbgPrintF("[took %dms to connect and authenticate]\n\n", dwTicks);
  1323. if (FAILED(hr))
  1324. goto LFatal;
  1325. // detect server version
  1326. SDVERINFO ver;
  1327. ver.dwSize = sizeof(ver);
  1328. if (spapi->GetVersion(&ver) == S_OK)
  1329. {
  1330. dbgPrintF("SDAPI:\t[%d.%d.%d.%d]\n",
  1331. ver.nApiMajor, ver.nApiMinor, ver.nApiBuild, ver.nApiDot);
  1332. if (ver.nSrvMajor || ver.nSrvMinor || ver.nSrvBuild || ver.nSrvDot)
  1333. {
  1334. dbgPrintF("SERVER:\t[%d.%d.%d.%d]\n",
  1335. ver.nSrvMajor, ver.nSrvMinor, ver.nSrvBuild, ver.nSrvDot);
  1336. }
  1337. }
  1338. else
  1339. {
  1340. dbgPrintF("SDAPI:\t[unknown build]\n");
  1341. dbgPrintF("SERVER:\t[unknown build]\n");
  1342. }
  1343. // run commands from file
  1344. if (pszFile || fStdin)
  1345. {
  1346. FILE *pfile = 0;
  1347. FILE *pfileClose = 0;
  1348. char sz[4096];
  1349. if (pszFile)
  1350. {
  1351. pfileClose = fopen(pszFile, "rt");
  1352. pfile = pfileClose;
  1353. }
  1354. else
  1355. {
  1356. pfile = stdin;
  1357. RestoreConsole_SetMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT);
  1358. }
  1359. if (pfile)
  1360. {
  1361. while (fgets(sz, sizeof(sz), pfile))
  1362. {
  1363. int cch = strlen(sz);
  1364. if (!cch)
  1365. continue;
  1366. // trim linefeeds
  1367. cch--;
  1368. while (sz[cch] == '\r' || sz[cch] == '\n')
  1369. {
  1370. sz[cch] = 0;
  1371. cch--;
  1372. }
  1373. cch++;
  1374. if (!cch)
  1375. continue;
  1376. // sleep
  1377. int cSleep = atoi(sz);
  1378. if (cSleep >= 0)
  1379. Sleep(cSleep * 1000);
  1380. // get command line
  1381. const char *psz = strchr(sz, ',');
  1382. if (psz)
  1383. psz++;
  1384. else
  1385. psz = sz;
  1386. // run command
  1387. hr = RunCmd(spapi, psz, 0, 0, pui, fStructured);
  1388. if (FAILED(hr))
  1389. {
  1390. const char *pszError = 0;
  1391. if (SUCCEEDED(spapi->GetErrorString(&pszError)) && pszError)
  1392. fprintf(stderr, "error:\n%s\n", pszError);
  1393. }
  1394. }
  1395. }
  1396. if (pfileClose)
  1397. fclose(pfileClose);
  1398. }
  1399. // run command from command line
  1400. if (argc)
  1401. {
  1402. hr = RunCmd(spapi, argv[0], argc - 1, argv + 1, pui, fStructured);
  1403. if (FAILED(hr))
  1404. goto LFatal;
  1405. }
  1406. // final
  1407. LOut:
  1408. pui->Release();
  1409. if (spapi)
  1410. nRet = FAILED(spapi->Final()) || nRet;
  1411. return nRet;
  1412. LFatal:
  1413. if (spapi)
  1414. {
  1415. const char *pszError = 0;
  1416. if (SUCCEEDED(spapi->GetErrorString(&pszError)) && pszError)
  1417. fprintf(stderr, "error:\n%s\n", pszError);
  1418. }
  1419. nRet = 1;
  1420. goto LOut;
  1421. }