Source code of Windows XP (NT5)
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.

1539 lines
36 KiB

  1. /*++
  2. // Copyright (c) 1999-2001 Microsoft Corporation, All Rights Reserved
  3. Module Name:
  4. OBJPATH.CPP
  5. Abstract:
  6. Object path parser.
  7. History:
  8. --*/
  9. #include "precomp.h"
  10. #include <genlex.h>
  11. #include <opathlex.h>
  12. #include <objpath.h>
  13. inline LPWSTR Macro_CloneLPWSTR(LPCWSTR x)
  14. {
  15. if (x)
  16. {
  17. LPWSTR szTemp = new wchar_t[wcslen(x) + 1];
  18. if (szTemp)
  19. wcscpy(szTemp, x);
  20. return szTemp;
  21. }
  22. else
  23. return NULL;
  24. }
  25. bool inline CloneFailed(LPCWSTR p1, LPCWSTR p2)
  26. {
  27. if (0 == p1 && 0 == p2 ) return false;
  28. if (p1 && p2) return false;
  29. return true;
  30. }
  31. ParsedObjectPath::ParsedObjectPath()
  32. {
  33. m_pServer = 0; // NULL if no server
  34. m_dwNumNamespaces = 0; // 0 if no namespaces
  35. m_dwAllocNamespaces = 2;
  36. m_paNamespaces = new LPWSTR[m_dwAllocNamespaces];
  37. if(m_paNamespaces)
  38. for (unsigned i = 0; i < m_dwAllocNamespaces; i++)
  39. m_paNamespaces[i] = 0;
  40. m_pClass = 0; // Class name
  41. m_dwNumKeys = 0; // 0 if no keys (just a class name)
  42. m_bSingletonObj = FALSE;
  43. m_dwAllocKeys = 2;
  44. m_paKeys = new KeyRef *[m_dwAllocKeys];
  45. }
  46. ParsedObjectPath::~ParsedObjectPath()
  47. {
  48. if (m_pServer)
  49. delete m_pServer;
  50. if (m_paNamespaces)
  51. {
  52. for (DWORD dwIx = 0; dwIx < m_dwNumNamespaces; dwIx++)
  53. delete m_paNamespaces[dwIx];
  54. delete m_paNamespaces;
  55. }
  56. if (m_pClass)
  57. delete m_pClass;
  58. if (m_paKeys)
  59. {
  60. for (DWORD dwIx = 0; dwIx < m_dwNumKeys; dwIx++)
  61. delete m_paKeys[dwIx];
  62. delete m_paKeys;
  63. }
  64. }
  65. BOOL ParsedObjectPath::SetClassName(LPCWSTR wszClassName)
  66. {
  67. if (m_pClass)
  68. delete [] m_pClass;
  69. if(wszClassName == NULL)
  70. {
  71. m_pClass = NULL;
  72. }
  73. else
  74. {
  75. m_pClass = Macro_CloneLPWSTR(wszClassName);
  76. if (CloneFailed(m_pClass,wszClassName))
  77. return FALSE;
  78. }
  79. return TRUE;
  80. }
  81. BOOL ParsedObjectPath::IsClass()
  82. {
  83. if(!IsObject())
  84. return FALSE;
  85. return (m_dwNumKeys == 0 && !m_bSingletonObj);
  86. }
  87. BOOL ParsedObjectPath::IsInstance()
  88. {
  89. return IsObject() && !IsClass();
  90. }
  91. BOOL ParsedObjectPath::IsObject()
  92. {
  93. if(m_pClass == NULL)
  94. return FALSE;
  95. if(m_pServer)
  96. {
  97. return (m_dwNumNamespaces > 0);
  98. }
  99. else
  100. {
  101. return (m_dwNumNamespaces == 0);
  102. }
  103. }
  104. BOOL ParsedObjectPath::AddNamespace(LPCWSTR wszNamespace)
  105. {
  106. if(m_dwNumNamespaces == m_dwAllocNamespaces)
  107. {
  108. DWORD dwNewAllocNamespaces = m_dwAllocNamespaces * 2;
  109. LPWSTR* paNewNamespaces = new LPWSTR[dwNewAllocNamespaces];
  110. if(paNewNamespaces == NULL) return FALSE;
  111. memcpy(paNewNamespaces, m_paNamespaces,
  112. sizeof(LPWSTR) * m_dwAllocNamespaces);
  113. delete [] m_paNamespaces;
  114. m_paNamespaces = paNewNamespaces;
  115. m_dwAllocNamespaces = dwNewAllocNamespaces;
  116. }
  117. LPWSTR p = Macro_CloneLPWSTR(wszNamespace);
  118. if (p)
  119. {
  120. m_paNamespaces[m_dwNumNamespaces++] = p;
  121. return TRUE;
  122. }
  123. else
  124. {
  125. return FALSE;
  126. }
  127. }
  128. BOOL ParsedObjectPath::AddKeyRefEx(LPCWSTR wszKeyName, const VARIANT* pvValue )
  129. {
  130. BOOL bStatus = TRUE ;
  131. BOOL bFound = FALSE ;
  132. BOOL bUnNamed = FALSE ;
  133. for ( ULONG dwIndex = 0 ; dwIndex < m_dwNumKeys ; dwIndex ++ )
  134. {
  135. if ( ( m_paKeys [ dwIndex ]->m_pName ) && wszKeyName )
  136. {
  137. if ( _wcsicmp ( m_paKeys [ dwIndex ]->m_pName , wszKeyName )
  138. == 0 )
  139. {
  140. bFound = TRUE ;
  141. break ;
  142. }
  143. }
  144. else
  145. {
  146. if ( ( ( m_paKeys [ dwIndex ]->m_pName ) == 0 ) )
  147. {
  148. bUnNamed = TRUE ;
  149. if ( ( wszKeyName == 0 ) )
  150. {
  151. bFound = TRUE ;
  152. break ;
  153. }
  154. }
  155. }
  156. }
  157. if ( ! wszKeyName )
  158. {
  159. /* Remove all existing keys */
  160. for ( ULONG dwDeleteIndex = 0 ; dwDeleteIndex < m_dwNumKeys ;
  161. dwDeleteIndex ++ )
  162. {
  163. delete ( m_paKeys [ dwDeleteIndex ]->m_pName ) ;
  164. m_paKeys [ dwDeleteIndex ]->m_pName = NULL ;
  165. VariantClear ( & ( m_paKeys [ dwDeleteIndex ]->m_vValue ) ) ;
  166. }
  167. if (S_OK == VariantCopy ( & ( m_paKeys [ 0 ]->m_vValue ) , ( VARIANT * ) pvValue ))
  168. {
  169. m_dwNumKeys = 1 ;
  170. }
  171. else
  172. {
  173. bStatus = FALSE;
  174. }
  175. }
  176. else
  177. {
  178. if ( bFound )
  179. {
  180. /*
  181. * If key already exists then just replace the value
  182. */
  183. if ( wszKeyName )
  184. {
  185. m_paKeys[dwIndex]->m_pName =
  186. new wchar_t [ wcslen ( wszKeyName ) + 1 ] ;
  187. if (m_paKeys[dwIndex]->m_pName)
  188. {
  189. wcscpy ( m_paKeys [ dwIndex ]->m_pName , wszKeyName ) ;
  190. }
  191. else
  192. {
  193. bStatus = FALSE;
  194. goto error;
  195. }
  196. }
  197. VariantClear ( & ( m_paKeys [ dwIndex ]->m_vValue ) ) ;
  198. if (S_OK != VariantCopy ( & ( m_paKeys [ dwIndex ]->m_vValue ) ,
  199. ( VARIANT * ) pvValue ))
  200. {
  201. bStatus = FALSE;
  202. }
  203. }
  204. else
  205. {
  206. if ( bUnNamed )
  207. {
  208. /* Add an un named key */
  209. for ( ULONG dwDeleteIndex = 0 ; dwDeleteIndex < m_dwNumKeys ;
  210. dwDeleteIndex ++ )
  211. {
  212. delete ( m_paKeys [ dwDeleteIndex ]->m_pName ) ;
  213. m_paKeys [ dwDeleteIndex ]->m_pName = NULL ;
  214. VariantClear (& ( m_paKeys [ dwDeleteIndex ]->m_vValue ) );
  215. }
  216. m_paKeys[0]->m_pName =
  217. new wchar_t [ wcslen ( wszKeyName ) + 1 ] ;
  218. if (m_paKeys[0]->m_pName)
  219. {
  220. wcscpy ( m_paKeys [ 0 ]->m_pName , wszKeyName ) ;
  221. if (S_OK == VariantCopy ( & ( m_paKeys [ 0 ]->m_vValue ) ,
  222. ( VARIANT * ) pvValue ))
  223. {
  224. m_dwNumKeys = 1;
  225. }
  226. else
  227. {
  228. bStatus = FALSE;
  229. }
  230. }
  231. else
  232. {
  233. bStatus = FALSE;
  234. }
  235. }
  236. else
  237. {
  238. /* Add a Named Key */
  239. bStatus = AddKeyRef(wszKeyName, pvValue);
  240. }
  241. }
  242. }
  243. error:
  244. return bStatus;
  245. }
  246. void ParsedObjectPath::ClearKeys ()
  247. {
  248. for ( ULONG dwDeleteIndex = 0 ; dwDeleteIndex < m_dwNumKeys ;
  249. dwDeleteIndex ++ )
  250. {
  251. delete m_paKeys [ dwDeleteIndex ] ;
  252. m_paKeys [ dwDeleteIndex ] = NULL ;
  253. }
  254. delete [] m_paKeys ;
  255. m_paKeys = NULL ;
  256. m_dwNumKeys = 0; // 0 if no keys (just a class name)
  257. m_paKeys = new KeyRef *[2];
  258. if (m_paKeys)
  259. m_dwAllocKeys = 2;
  260. }
  261. BOOL ParsedObjectPath::AddKeyRef(LPCWSTR wszKeyName, const VARIANT* pvValue)
  262. {
  263. if(m_dwNumKeys == m_dwAllocKeys)
  264. {
  265. DWORD dwNewAllocKeys = m_dwAllocKeys * 2;
  266. KeyRef** paNewKeys = new KeyRef*[dwNewAllocKeys];
  267. if (!paNewKeys)
  268. return FALSE;
  269. memcpy(paNewKeys, m_paKeys, sizeof(KeyRef*) * m_dwAllocKeys);
  270. delete [] m_paKeys;
  271. m_paKeys = paNewKeys;
  272. m_dwAllocKeys = dwNewAllocKeys;
  273. }
  274. if(m_paKeys == NULL)
  275. return FALSE;
  276. m_paKeys[m_dwNumKeys] = new KeyRef(wszKeyName, pvValue);
  277. if (!m_paKeys[m_dwNumKeys] || !m_paKeys[m_dwNumKeys]->IsValid())
  278. return FALSE;
  279. m_dwNumKeys++;
  280. return TRUE;
  281. }
  282. BOOL ParsedObjectPath::AddKeyRef(KeyRef* pAcquireRef)
  283. {
  284. if(m_dwNumKeys == m_dwAllocKeys)
  285. {
  286. DWORD dwNewAllocKeys = m_dwAllocKeys * 2;
  287. KeyRef** paNewKeys = new KeyRef*[dwNewAllocKeys];
  288. if(paNewKeys == NULL)return FALSE;
  289. memcpy(paNewKeys, m_paKeys, sizeof(KeyRef*) * m_dwAllocKeys);
  290. delete [] m_paKeys;
  291. m_paKeys = paNewKeys;
  292. m_dwAllocKeys = dwNewAllocKeys;
  293. }
  294. m_paKeys[m_dwNumKeys] = pAcquireRef;
  295. m_dwNumKeys++;
  296. return TRUE;
  297. }
  298. KeyRef::KeyRef()
  299. {
  300. m_pName = 0;
  301. VariantInit(&m_vValue);
  302. m_hRes = S_OK;
  303. }
  304. KeyRef::KeyRef(LPCWSTR wszKeyName, const VARIANT* pvValue)
  305. {
  306. m_pName = Macro_CloneLPWSTR(wszKeyName);
  307. VariantInit(&m_vValue);
  308. m_hRes = VariantCopy(&m_vValue, (VARIANT*)pvValue);
  309. }
  310. KeyRef::~KeyRef()
  311. {
  312. delete m_pName;
  313. VariantClear(&m_vValue);
  314. }
  315. int WINAPI CObjectPathParser::Unparse(
  316. ParsedObjectPath* pInput,
  317. DELETE_ME LPWSTR* pwszPath)
  318. {
  319. if(pInput->m_pClass == NULL)
  320. {
  321. return CObjectPathParser::InvalidParameter;
  322. }
  323. // Allocate enough space
  324. // =====================
  325. int nSpace = wcslen(pInput->m_pClass);
  326. nSpace += 10;
  327. DWORD dwIx;
  328. for (dwIx = 0; dwIx < pInput->m_dwNumKeys; dwIx++)
  329. {
  330. KeyRef* pKey = pInput->m_paKeys[dwIx];
  331. if(pKey->m_pName)
  332. nSpace += wcslen(pKey->m_pName);
  333. if(V_VT(&pKey->m_vValue) == VT_BSTR)
  334. {
  335. nSpace += wcslen(V_BSTR(&pKey->m_vValue))*2 + 10;
  336. }
  337. else if( V_VT(&pKey->m_vValue) == VT_I4
  338. || V_VT(&pKey->m_vValue) == VT_UI4 )
  339. {
  340. nSpace += 30;
  341. }
  342. else if ( V_VT(&pKey->m_vValue) == VT_I2
  343. || V_VT(&pKey->m_vValue) == VT_UI2 )
  344. {
  345. nSpace += 15;
  346. }
  347. else if ( V_VT(&pKey->m_vValue) == VT_I1
  348. || V_VT(&pKey->m_vValue) == VT_UI1 )
  349. {
  350. nSpace += 8;
  351. }
  352. }
  353. if(pInput->m_bSingletonObj)
  354. nSpace +=2;
  355. WCHAR wszTemp[30];
  356. LPWSTR wszPath = new WCHAR[nSpace];
  357. if (!wszPath)
  358. return OutOfMemory;
  359. wcscpy(wszPath, pInput->m_pClass);
  360. for (dwIx = 0; dwIx < pInput->m_dwNumKeys; dwIx++)
  361. {
  362. KeyRef* pKey = pInput->m_paKeys[dwIx];
  363. // We dont want to put a '.' if there isnt a key name,
  364. // for example, Myclass="value"
  365. if(dwIx == 0)
  366. {
  367. if((pKey->m_pName && (0 < wcslen(pKey->m_pName))) || pInput->m_dwNumKeys > 1)
  368. wcscat(wszPath, L".");
  369. }
  370. else
  371. {
  372. wcscat(wszPath, L",");
  373. }
  374. if(pKey->m_pName)
  375. wcscat(wszPath, pKey->m_pName);
  376. wcscat(wszPath, L"=");
  377. if(V_VT(&pKey->m_vValue) == VT_BSTR)
  378. {
  379. wcscat(wszPath, L"\"");
  380. WCHAR* pwc = V_BSTR(&pKey->m_vValue);
  381. WCHAR str[2];
  382. str[1] = 0;
  383. while(*pwc)
  384. {
  385. if(*pwc == '\\' || *pwc == '"')
  386. {
  387. wcscat(wszPath, L"\\");
  388. }
  389. str[0] = *pwc;
  390. wcscat(wszPath, str);
  391. pwc++;
  392. }
  393. wcscat(wszPath, L"\"");
  394. }
  395. else if( V_VT(&pKey->m_vValue) == VT_I4 )
  396. {
  397. swprintf(wszTemp, L"%d", V_I4(&pKey->m_vValue));
  398. wcscat(wszPath, wszTemp);
  399. }
  400. else if( V_VT(&pKey->m_vValue) == VT_UI4 )
  401. {
  402. swprintf(wszTemp, L"%u", V_UI4(&pKey->m_vValue));
  403. wcscat(wszPath, wszTemp);
  404. }
  405. else if( V_VT(&pKey->m_vValue) == VT_I2 )
  406. {
  407. swprintf(wszTemp, L"%hd", V_I2(&pKey->m_vValue));
  408. wcscat(wszPath, wszTemp);
  409. }
  410. else if( V_VT(&pKey->m_vValue) == VT_UI2 )
  411. {
  412. swprintf(wszTemp, L"%hu", V_UI2(&pKey->m_vValue));
  413. wcscat(wszPath, wszTemp);
  414. }
  415. else if( V_VT(&pKey->m_vValue) == VT_I1 )
  416. {
  417. swprintf(wszTemp, L"%d", V_I1(&pKey->m_vValue));
  418. wcscat(wszPath, wszTemp);
  419. }
  420. else if( V_VT(&pKey->m_vValue) == VT_UI1 )
  421. {
  422. swprintf(wszTemp, L"%u", V_UI1(&pKey->m_vValue));
  423. wcscat(wszPath, wszTemp);
  424. }
  425. }
  426. // Take care of the singleton case. This is a path of the form
  427. // MyClass=@ and represents a single instance of a class with no
  428. // keys.
  429. if(pInput->m_bSingletonObj && pInput->m_dwNumKeys == 0)
  430. wcscat(wszPath, L"=@");
  431. *pwszPath = wszPath;
  432. return NoError;
  433. }
  434. LPWSTR WINAPI CObjectPathParser::GetRelativePath(LPWSTR wszFullPath)
  435. {
  436. LPWSTR wsz = wcschr(wszFullPath, L':');
  437. if(wsz)
  438. return wsz + 1;
  439. else
  440. return NULL;
  441. }
  442. void CObjectPathParser::Zero()
  443. {
  444. m_nCurrentToken = 0;
  445. m_pLexer = 0;
  446. m_pInitialIdent = 0;
  447. m_pOutput = 0;
  448. m_pTmpKeyRef = 0;
  449. }
  450. CObjectPathParser::CObjectPathParser(ObjectParserFlags eFlags)
  451. : m_eFlags(eFlags)
  452. {
  453. Zero();
  454. }
  455. void CObjectPathParser::Empty()
  456. {
  457. delete m_pLexer;
  458. delete m_pInitialIdent;
  459. delete m_pTmpKeyRef;
  460. // m_pOutput is intentionally left alone,
  461. // since all code paths delete this already on error, or
  462. // else the user acquired the pointer.
  463. }
  464. CObjectPathParser::~CObjectPathParser()
  465. {
  466. Empty();
  467. }
  468. int CObjectPathParser::Parse(
  469. LPCWSTR pRawPath,
  470. ParsedObjectPath **pOutput
  471. )
  472. {
  473. if (pOutput == 0 || pRawPath == 0 || wcslen(pRawPath) == 0)
  474. return CObjectPathParser::InvalidParameter;
  475. // Check for leading / trailing ws.
  476. // ================================
  477. if (iswspace(pRawPath[wcslen(pRawPath)-1]) || iswspace(pRawPath[0]))
  478. return InvalidParameter;
  479. // These are required for multiple calls to Parse().
  480. // ==================================================
  481. Empty();
  482. Zero();
  483. // Set default return to NULL initially until we have some output.
  484. // ===============================================================
  485. *pOutput = 0;
  486. m_pOutput = new ParsedObjectPath;
  487. if (!m_pOutput || !m_pOutput->IsValid())
  488. return OutOfMemory;
  489. // Parse the server name (if there is one) manually
  490. // ================================================
  491. if ( (pRawPath[0] == '\\' && pRawPath[1] == '\\') ||
  492. (pRawPath[0] == '/' && pRawPath[1] == '/'))
  493. {
  494. const WCHAR* pwcStart = pRawPath + 2;
  495. // Find the next backslash --- it's the end of the server name
  496. // ===========================================================
  497. const WCHAR* pwcEnd = pwcStart;
  498. while (*pwcEnd != L'\0' && *pwcEnd != L'\\' && *pwcEnd != L'/')
  499. {
  500. pwcEnd++;
  501. }
  502. if (*pwcEnd == L'\0')
  503. {
  504. // If we have already exhausted the object path string,
  505. // a lone server name was all there was.
  506. // ====================================================
  507. if (m_eFlags != e_ParserAcceptAll)
  508. {
  509. delete m_pOutput;
  510. return SyntaxError;
  511. }
  512. else // A lone server name is legal.
  513. {
  514. m_pOutput->m_pServer = new WCHAR[wcslen(pwcStart)+1];
  515. if (!m_pOutput->m_pServer)
  516. {
  517. delete m_pOutput;
  518. return OutOfMemory;
  519. }
  520. wcscpy(m_pOutput->m_pServer, pwcStart);
  521. *pOutput = m_pOutput;
  522. m_pOutput = 0;
  523. return NoError;
  524. }
  525. }
  526. if (pwcEnd == pwcStart)
  527. {
  528. // No name at all.
  529. // ===============
  530. delete m_pOutput;
  531. return SyntaxError;
  532. }
  533. m_pOutput->m_pServer = new WCHAR[pwcEnd-pwcStart+1];
  534. if (!m_pOutput->m_pServer)
  535. {
  536. delete m_pOutput;
  537. return OutOfMemory;
  538. }
  539. wcsncpy(m_pOutput->m_pServer, pwcStart, pwcEnd-pwcStart);
  540. m_pOutput->m_pServer[pwcEnd-pwcStart] = 0;
  541. pRawPath = pwcEnd;
  542. }
  543. // Point the lexer at the source.
  544. // ==============================
  545. CTextLexSource src(pRawPath);
  546. m_pLexer = new CGenLexer(OPath_LexTable, &src);
  547. if (!m_pLexer)
  548. {
  549. delete m_pOutput;
  550. return OutOfMemory;
  551. }
  552. // Go.
  553. // ===
  554. int nRes = begin_parse();
  555. if (nRes)
  556. {
  557. delete m_pOutput;
  558. return nRes;
  559. }
  560. if (m_nCurrentToken != OPATH_TOK_EOF)
  561. {
  562. delete m_pOutput;
  563. return SyntaxError;
  564. }
  565. if (m_pOutput->m_dwNumNamespaces > 0 && m_pOutput->m_pServer == NULL)
  566. {
  567. if (m_eFlags != e_ParserAcceptRelativeNamespace && m_eFlags != e_ParserAcceptAll)
  568. {
  569. delete m_pOutput;
  570. return SyntaxError;
  571. }
  572. else
  573. {
  574. // Local namespace --- set server to "."
  575. // =====================================
  576. m_pOutput->m_pServer = new WCHAR[2];
  577. if (!m_pOutput->m_pServer)
  578. {
  579. delete m_pOutput;
  580. return OutOfMemory;
  581. }
  582. wcscpy(m_pOutput->m_pServer, L".");
  583. }
  584. }
  585. // Sort the key refs lexically. If there is only
  586. // one key, there is nothing to sort anyway.
  587. // =============================================
  588. if (m_pOutput->m_dwNumKeys > 1)
  589. {
  590. BOOL bChanges = TRUE;
  591. while (bChanges)
  592. {
  593. bChanges = FALSE;
  594. for (DWORD dwIx = 0; dwIx < m_pOutput->m_dwNumKeys - 1; dwIx++)
  595. {
  596. if (_wcsicmp(m_pOutput->m_paKeys[dwIx]->m_pName,
  597. m_pOutput->m_paKeys[dwIx+1]->m_pName) > 0)
  598. {
  599. KeyRef *pTmp = m_pOutput->m_paKeys[dwIx];
  600. m_pOutput->m_paKeys[dwIx] = m_pOutput->m_paKeys[dwIx + 1];
  601. m_pOutput->m_paKeys[dwIx + 1] = pTmp;
  602. bChanges = TRUE;
  603. }
  604. }
  605. }
  606. }
  607. // Add in key refs.
  608. // ================
  609. *pOutput = m_pOutput;
  610. m_pOutput = 0;
  611. return NoError;
  612. }
  613. BOOL CObjectPathParser::NextToken()
  614. {
  615. m_nCurrentToken = m_pLexer->NextToken();
  616. if (m_nCurrentToken == OPATH_TOK_ERROR)
  617. return FALSE;
  618. return TRUE;
  619. }
  620. void CObjectPathParser::Free(ParsedObjectPath *pOutput)
  621. {
  622. delete pOutput;
  623. }
  624. void CObjectPathParser::Free( LPWSTR wszUnparsedPath )
  625. {
  626. delete wszUnparsedPath;
  627. }
  628. //
  629. // <Parse> ::= BACKSLASH <ns_or_server>;
  630. // <Parse> ::= IDENT <ns_or_class>;
  631. // <Parse> ::= COLON <objref>;
  632. //
  633. int CObjectPathParser::begin_parse()
  634. {
  635. if (!NextToken())
  636. return SyntaxError;
  637. if (m_nCurrentToken == OPATH_TOK_BACKSLASH)
  638. {
  639. if (!NextToken())
  640. return SyntaxError;
  641. return ns_or_server();
  642. }
  643. else if (m_nCurrentToken == OPATH_TOK_IDENT)
  644. {
  645. m_pInitialIdent = Macro_CloneLPWSTR(m_pLexer->GetTokenText());
  646. if (CloneFailed(m_pInitialIdent,m_pLexer->GetTokenText()))
  647. return OutOfMemory;
  648. if (!NextToken())
  649. return SyntaxError;
  650. // Copy the token and put it in a temporary holding place
  651. // until we figure out whether it is a namespace or a class name.
  652. // ==============================================================
  653. return ns_or_class();
  654. }
  655. else if (m_nCurrentToken == OPATH_TOK_COLON)
  656. {
  657. if (!NextToken())
  658. return SyntaxError;
  659. return objref();
  660. }
  661. // If here, we had a bad starter token.
  662. // ====================================
  663. return SyntaxError;
  664. }
  665. //
  666. // <ns_or_server> ::= BACKSLASH <dot_or_ident> BACKSLASH <ns_list> <optional_objref>;
  667. // <ns_or_server> ::= <ns_list> <optional_objref>;
  668. //
  669. // <dot_or_ident> is embedded.
  670. //
  671. int CObjectPathParser::ns_or_server()
  672. {
  673. if (m_nCurrentToken == OPATH_TOK_BACKSLASH)
  674. {
  675. // Actually, server names have been take care of, so this is a failure
  676. // ===================================================================
  677. return SyntaxError;
  678. }
  679. else if (m_nCurrentToken == OPATH_TOK_IDENT)
  680. {
  681. int nRes = ns_list();
  682. if (nRes)
  683. return nRes;
  684. return optional_objref();
  685. }
  686. else
  687. if (m_nCurrentToken == OPATH_TOK_EOF)
  688. return NoError;
  689. return SyntaxError;
  690. }
  691. //
  692. // <optional_objref> ::= COLON <objref>;
  693. // <optional_objref> ::= <>;
  694. //
  695. int CObjectPathParser::optional_objref()
  696. {
  697. if (m_nCurrentToken == OPATH_TOK_EOF)
  698. return NoError;
  699. if (m_nCurrentToken != OPATH_TOK_COLON)
  700. return SyntaxError;
  701. if (!NextToken())
  702. return SyntaxError;
  703. return objref();
  704. }
  705. //
  706. // <ns_or_class> ::= COLON <ident_becomes_ns> <objref>;
  707. // <ns_or_class> ::= BACKSLASH <ident_becomes_ns> <ns_list> COLON <objref>;
  708. // <ns_or_class> ::= BACKSLASH <ident_becomes_ns> <ns_list>;
  709. //
  710. int CObjectPathParser::ns_or_class()
  711. {
  712. if (m_nCurrentToken == OPATH_TOK_COLON)
  713. {
  714. ident_becomes_ns();
  715. if (!NextToken())
  716. return SyntaxError;
  717. return objref();
  718. }
  719. else if (m_nCurrentToken == OPATH_TOK_BACKSLASH)
  720. {
  721. ident_becomes_ns();
  722. if (!NextToken())
  723. return SyntaxError;
  724. int nRes = ns_list();
  725. if (nRes)
  726. return nRes;
  727. if (m_nCurrentToken == OPATH_TOK_EOF) // ns only
  728. return NoError;
  729. if (m_nCurrentToken != OPATH_TOK_COLON)
  730. return SyntaxError;
  731. if (!NextToken())
  732. return SyntaxError;
  733. return objref();
  734. }
  735. // Else
  736. // ====
  737. ident_becomes_class();
  738. return objref_rest();
  739. }
  740. //
  741. // <objref> ::= IDENT <objref_rest>; // IDENT is classname
  742. //
  743. int CObjectPathParser::objref()
  744. {
  745. if (m_nCurrentToken != OPATH_TOK_IDENT)
  746. return SyntaxError;
  747. m_pOutput->m_pClass = Macro_CloneLPWSTR(m_pLexer->GetTokenText());
  748. if (CloneFailed(m_pOutput->m_pClass,m_pLexer->GetTokenText()))
  749. return OutOfMemory;
  750. if (!NextToken())
  751. return SyntaxError;
  752. return objref_rest();
  753. }
  754. //
  755. // <ns_list> ::= IDENT <ns_list_rest>;
  756. //
  757. int CObjectPathParser::ns_list()
  758. {
  759. if (m_nCurrentToken == OPATH_TOK_IDENT)
  760. {
  761. m_pOutput->AddNamespace(m_pLexer->GetTokenText());
  762. if (!NextToken())
  763. return SyntaxError;
  764. return ns_list_rest();
  765. }
  766. return SyntaxError;
  767. }
  768. //
  769. // <ident_becomes_ns> ::= <>; // <initial_ident> becomes a namespace
  770. //
  771. int CObjectPathParser::ident_becomes_ns()
  772. {
  773. m_pOutput->AddNamespace(m_pInitialIdent);
  774. delete m_pInitialIdent;
  775. m_pInitialIdent = 0;
  776. return NoError;
  777. }
  778. //
  779. // <ident_becomes_class> ::= <>; // <initial_ident> becomes the class
  780. //
  781. int CObjectPathParser::ident_becomes_class()
  782. {
  783. m_pOutput->m_pClass = Macro_CloneLPWSTR(m_pInitialIdent);
  784. if (CloneFailed(m_pOutput->m_pClass,m_pInitialIdent))
  785. return OutOfMemory;
  786. delete m_pInitialIdent;
  787. m_pInitialIdent = 0;
  788. return NoError;
  789. }
  790. //
  791. // <objref_rest> ::= EQUALS <key_const>;
  792. // <objref_rest> ::= EQUALS *;
  793. // <objref_rest> ::= DOT <keyref_list>;
  794. // <objref_rest> ::= <>;
  795. //
  796. int CObjectPathParser::objref_rest()
  797. {
  798. if (m_nCurrentToken == OPATH_TOK_EQ)
  799. {
  800. if (!NextToken())
  801. return SyntaxError;
  802. // Take care of the singleton case. This is a path of the form
  803. // MyClass=@ and represents a singleton instance of a class with no
  804. // keys.
  805. if(m_nCurrentToken == OPATH_TOK_SINGLETON_SYM)
  806. {
  807. if(NextToken() && m_nCurrentToken != OPATH_TOK_EOF)
  808. return SyntaxError;
  809. m_pOutput->m_bSingletonObj = TRUE;
  810. return NoError;
  811. }
  812. m_pTmpKeyRef = new KeyRef; // no IsValid here
  813. if (!m_pTmpKeyRef)
  814. return OutOfMemory;
  815. int nRes = key_const();
  816. if (nRes)
  817. {
  818. delete m_pTmpKeyRef;
  819. m_pTmpKeyRef = 0;
  820. return nRes;
  821. }
  822. if (!m_pOutput->AddKeyRef(m_pTmpKeyRef))
  823. {
  824. delete m_pTmpKeyRef;
  825. m_pTmpKeyRef = 0;
  826. return OutOfMemory;
  827. }
  828. m_pTmpKeyRef = 0;
  829. }
  830. else if (m_nCurrentToken == OPATH_TOK_DOT)
  831. {
  832. if (!NextToken())
  833. return SyntaxError;
  834. return keyref_list();
  835. }
  836. return NoError;
  837. }
  838. //
  839. // <ns_list_rest> ::= BACKSLASH <ns_list>;
  840. // <ns_list_rest> ::= <>;
  841. //
  842. int CObjectPathParser::ns_list_rest()
  843. {
  844. if (m_nCurrentToken == OPATH_TOK_BACKSLASH)
  845. {
  846. if (!NextToken())
  847. return SyntaxError;
  848. return ns_list();
  849. }
  850. return NoError;
  851. }
  852. //
  853. // <key_const> ::= STRING_CONST;
  854. // <key_const> ::= INTEGRAL_CONST;
  855. // <key_const> ::= REAL_CONST;
  856. // <key_const> ::= IDENT; // Where IDENT is "OBJECT" for singleton classes
  857. //
  858. int CObjectPathParser::key_const()
  859. {
  860. // If here, we have a key constant.
  861. // We may or may not have the property name
  862. // associated with it.
  863. // ========================================
  864. if (m_nCurrentToken == OPATH_TOK_QSTRING)
  865. {
  866. BSTR bStr = SysAllocString(m_pLexer->GetTokenText());
  867. if (bStr)
  868. {
  869. V_VT(&m_pTmpKeyRef->m_vValue) = VT_BSTR;
  870. V_BSTR(&m_pTmpKeyRef->m_vValue) = bStr;
  871. }
  872. else
  873. return OutOfMemory;
  874. }
  875. else if (m_nCurrentToken == OPATH_TOK_INT)
  876. {
  877. V_VT(&m_pTmpKeyRef->m_vValue) = VT_I4;
  878. char buf[32];
  879. if(m_pLexer->GetTokenText() == NULL || wcslen(m_pLexer->GetTokenText()) > 31)
  880. return SyntaxError;
  881. sprintf(buf, "%S", m_pLexer->GetTokenText());
  882. V_I4(&m_pTmpKeyRef->m_vValue) = atol(buf);
  883. }
  884. else if (m_nCurrentToken == OPATH_TOK_HEXINT)
  885. {
  886. V_VT(&m_pTmpKeyRef->m_vValue) = VT_I4;
  887. char buf[32];
  888. if(m_pLexer->GetTokenText() == NULL || wcslen(m_pLexer->GetTokenText()) > 31)
  889. return SyntaxError;
  890. sprintf(buf, "%S", m_pLexer->GetTokenText());
  891. long l;
  892. sscanf(buf, "%x", &l);
  893. V_I4(&m_pTmpKeyRef->m_vValue) = l;
  894. }
  895. else if (m_nCurrentToken == OPATH_TOK_IDENT)
  896. {
  897. if (_wcsicmp(m_pLexer->GetTokenText(), L"TRUE") == 0)
  898. {
  899. V_VT(&m_pTmpKeyRef->m_vValue) = VT_I4;
  900. V_I4(&m_pTmpKeyRef->m_vValue) = 1;
  901. }
  902. else if (_wcsicmp(m_pLexer->GetTokenText(), L"FALSE") == 0)
  903. {
  904. V_VT(&m_pTmpKeyRef->m_vValue) = VT_I4;
  905. V_I4(&m_pTmpKeyRef->m_vValue) = 0;
  906. }
  907. else
  908. return SyntaxError;
  909. }
  910. else return SyntaxError;
  911. if (!NextToken())
  912. return SyntaxError;
  913. return NoError;
  914. }
  915. //
  916. // <keyref_list> ::= <keyref> <keyref_term>;
  917. //
  918. int CObjectPathParser::keyref_list()
  919. {
  920. int nRes = keyref();
  921. if (nRes)
  922. return nRes;
  923. return keyref_term();
  924. }
  925. //
  926. // <keyref> ::= <propname> EQUALS <key_const>;
  927. //
  928. int CObjectPathParser::keyref()
  929. {
  930. m_pTmpKeyRef = new KeyRef; // no IsValid here
  931. if (!m_pTmpKeyRef)
  932. return OutOfMemory;
  933. int nRes = propname();
  934. if (nRes)
  935. {
  936. delete m_pTmpKeyRef;
  937. m_pTmpKeyRef = 0;
  938. return nRes;
  939. }
  940. if (m_nCurrentToken != OPATH_TOK_EQ)
  941. {
  942. delete m_pTmpKeyRef;
  943. m_pTmpKeyRef = 0;
  944. return SyntaxError;
  945. }
  946. if (!NextToken())
  947. {
  948. delete m_pTmpKeyRef;
  949. m_pTmpKeyRef = 0;
  950. return SyntaxError;
  951. }
  952. nRes = key_const();
  953. if (nRes)
  954. {
  955. delete m_pTmpKeyRef;
  956. m_pTmpKeyRef = 0;
  957. return nRes;
  958. }
  959. if (!m_pOutput->AddKeyRef(m_pTmpKeyRef))
  960. {
  961. delete m_pTmpKeyRef;
  962. m_pTmpKeyRef = 0;
  963. return OutOfMemory;
  964. }
  965. m_pTmpKeyRef = 0;
  966. return NoError;
  967. }
  968. //
  969. // <keyref_term> ::= COMMA <keyref_list>; // Used for compound keys
  970. // <keyref_term> ::= <>;
  971. //
  972. int CObjectPathParser::keyref_term()
  973. {
  974. if (m_nCurrentToken == OPATH_TOK_COMMA)
  975. {
  976. if (!NextToken())
  977. return SyntaxError;
  978. return keyref_list();
  979. }
  980. return NoError;
  981. }
  982. //
  983. // <propname> ::= IDENT;
  984. //
  985. int CObjectPathParser::propname()
  986. {
  987. if (m_nCurrentToken != OPATH_TOK_IDENT)
  988. return SyntaxError;
  989. m_pTmpKeyRef->m_pName = Macro_CloneLPWSTR(m_pLexer->GetTokenText());
  990. if (CloneFailed(m_pTmpKeyRef->m_pName,m_pLexer->GetTokenText()))
  991. return OutOfMemory;
  992. if (!NextToken())
  993. {
  994. delete m_pTmpKeyRef;
  995. m_pTmpKeyRef = 0;
  996. return SyntaxError;
  997. }
  998. return NoError;
  999. }
  1000. //***************************************************************************
  1001. //
  1002. // ParsedObjectPath::GetKeyString
  1003. //
  1004. // Returns the db-engine compatible key string for the object.
  1005. // The format will likely change after the Alpha PDK Release.
  1006. //
  1007. // Return value:
  1008. // NULL on error or for pure classes. Otherwise returns a pointer to
  1009. // a newly allocated string which must be deallocated with operator
  1010. // delete.
  1011. //
  1012. //***************************************************************************
  1013. LPWSTR ParsedObjectPath::GetKeyString()
  1014. {
  1015. if (m_dwNumKeys == 0 && !m_bSingletonObj)
  1016. {
  1017. if (m_pClass == 0 || wcslen(m_pClass) == 0)
  1018. return 0;
  1019. LPWSTR pTmp = new wchar_t[wcslen(m_pClass) + 1];
  1020. if (pTmp)
  1021. wcscpy(pTmp, m_pClass);
  1022. return pTmp;
  1023. }
  1024. // Allocate enough space
  1025. // =====================
  1026. int nSpace = 10;
  1027. DWORD dwIx;
  1028. for (dwIx = 0; dwIx < m_dwNumKeys; dwIx++)
  1029. {
  1030. KeyRef* pKey = m_paKeys[dwIx];
  1031. nSpace += 2; // for the |
  1032. if(V_VT(&pKey->m_vValue) == VT_BSTR)
  1033. {
  1034. nSpace += wcslen(V_BSTR(&pKey->m_vValue))*2 + 10;
  1035. }
  1036. else if(V_VT(&pKey->m_vValue) == VT_I4)
  1037. {
  1038. nSpace += 30;
  1039. }
  1040. }
  1041. if(m_bSingletonObj)
  1042. nSpace +=20;
  1043. LPWSTR pRetVal = new wchar_t[nSpace];
  1044. wchar_t Tmp[32];
  1045. long nVal;
  1046. if (!pRetVal)
  1047. return NULL;
  1048. *pRetVal = 0;
  1049. BOOL bFirst = TRUE;
  1050. // The key are already sorted lexically.
  1051. // =====================================
  1052. WCHAR wszSeparator[2];
  1053. wszSeparator[0] = 0xFFFF;
  1054. wszSeparator[1] = 0;
  1055. for (DWORD i = 0; i < m_dwNumKeys; i++)
  1056. {
  1057. if (!bFirst)
  1058. wcscat(pRetVal, wszSeparator);
  1059. bFirst = FALSE;
  1060. KeyRef *pKeyRef = m_paKeys[i];
  1061. VARIANT *pv = &pKeyRef->m_vValue;
  1062. int nType = V_VT(pv);
  1063. switch (nType)
  1064. {
  1065. case VT_LPWSTR:
  1066. case VT_BSTR:
  1067. wcscat(pRetVal, V_BSTR(pv));
  1068. break;
  1069. case VT_I4:
  1070. nVal = V_I4(pv);
  1071. swprintf(Tmp, L"%d", nVal);
  1072. wcscat(pRetVal, Tmp);
  1073. break;
  1074. case VT_I2:
  1075. nVal = V_I2(pv);
  1076. swprintf(Tmp, L"%d", nVal);
  1077. wcscat(pRetVal, Tmp);
  1078. break;
  1079. case VT_UI1:
  1080. nVal = V_UI1(pv);
  1081. swprintf(Tmp, L"%d", nVal);
  1082. wcscat(pRetVal, Tmp);
  1083. break;
  1084. case VT_BOOL:
  1085. nVal = V_BOOL(pv);
  1086. swprintf(Tmp, L"%d", (nVal?1:0));
  1087. wcscat(pRetVal, Tmp);
  1088. break;
  1089. default:
  1090. wcscat(pRetVal, L"NULL");
  1091. }
  1092. }
  1093. if (wcslen(pRetVal) == 0)
  1094. {
  1095. if(m_bSingletonObj)
  1096. {
  1097. wcscpy(pRetVal, L"@");
  1098. }
  1099. }
  1100. return pRetVal; // This may not be NULL
  1101. }
  1102. LPWSTR ParsedObjectPath::GetNamespacePart()
  1103. {
  1104. if (m_dwNumNamespaces == 0)
  1105. return NULL;
  1106. // Compute necessary space
  1107. // =======================
  1108. int nSpace = 0;
  1109. for(DWORD i = 0; i < m_dwNumNamespaces; i++)
  1110. nSpace += 1 + wcslen(m_paNamespaces[i]);
  1111. nSpace--;
  1112. // Allocate buffer
  1113. // ===============
  1114. LPWSTR wszOut = new wchar_t[nSpace + 1];
  1115. if (!wszOut)
  1116. return NULL;
  1117. *wszOut = 0;
  1118. // Output
  1119. // ======
  1120. for(i = 0; i < m_dwNumNamespaces; i++)
  1121. {
  1122. if(i != 0) wcscat(wszOut, L"\\");
  1123. wcscat(wszOut, m_paNamespaces[i]);
  1124. }
  1125. return wszOut;
  1126. }
  1127. LPWSTR ParsedObjectPath::GetParentNamespacePart()
  1128. {
  1129. if(m_dwNumNamespaces < 2)
  1130. return NULL;
  1131. // Compute necessary space
  1132. // =======================
  1133. int nSpace = 0;
  1134. for(DWORD i = 0; i < m_dwNumNamespaces - 1; i++)
  1135. nSpace += 1 + wcslen(m_paNamespaces[i]);
  1136. nSpace--;
  1137. // Allocate buffer
  1138. // ===============
  1139. LPWSTR wszOut = new wchar_t[nSpace + 1];
  1140. if(wszOut == NULL)return NULL;
  1141. *wszOut = 0;
  1142. // Output
  1143. // ======
  1144. for(i = 0; i < m_dwNumNamespaces - 1; i++)
  1145. {
  1146. if(i != 0) wcscat(wszOut, L"\\");
  1147. wcscat(wszOut, m_paNamespaces[i]);
  1148. }
  1149. return wszOut;
  1150. }
  1151. BOOL ParsedObjectPath::IsRelative(LPCWSTR wszMachine, LPCWSTR wszNamespace)
  1152. {
  1153. if(!IsLocal(wszMachine))
  1154. return FALSE;
  1155. if(m_dwNumNamespaces == 0)
  1156. return TRUE;
  1157. LPWSTR wszCopy = new wchar_t[wcslen(wszNamespace) + 1];
  1158. if(wszCopy == NULL)return FALSE;
  1159. wcscpy(wszCopy, wszNamespace);
  1160. LPWSTR wszLeft = wszCopy;
  1161. BOOL bFailed = FALSE;
  1162. for(DWORD i = 0; i < m_dwNumNamespaces; i++)
  1163. {
  1164. unsigned int nLen = wcslen(m_paNamespaces[i]);
  1165. if(nLen > wcslen(wszLeft))
  1166. {
  1167. bFailed = TRUE;
  1168. break;
  1169. }
  1170. if(i == m_dwNumNamespaces - 1 && wszLeft[nLen] != 0)
  1171. {
  1172. bFailed = TRUE;
  1173. break;
  1174. }
  1175. if(i != m_dwNumNamespaces - 1 && wszLeft[nLen] != L'\\')
  1176. {
  1177. bFailed = TRUE;
  1178. break;
  1179. }
  1180. wszLeft[nLen] = 0;
  1181. if(_wcsicmp(wszLeft, m_paNamespaces[i]))
  1182. {
  1183. bFailed = TRUE;
  1184. break;
  1185. }
  1186. wszLeft += nLen+1;
  1187. }
  1188. delete [] wszCopy;
  1189. return !bFailed;
  1190. }
  1191. BOOL ParsedObjectPath::IsLocal(LPCWSTR wszMachine)
  1192. {
  1193. return (m_pServer == NULL || !_wcsicmp(m_pServer, L".") ||
  1194. !_wcsicmp(m_pServer, wszMachine));
  1195. }
  1196. ////////////////////////////////////////////////////////
  1197. //
  1198. // Test object path parser by parsing all objects
  1199. // in the input file (one object path per line).
  1200. //
  1201. ////////////////////////////////////////////////////////
  1202. #ifdef TEST
  1203. void xmain(int argc, char **argv)
  1204. {
  1205. printf("Object Path Test\n");
  1206. if (argc < 2 || strchr(argv[1], '?') != NULL)
  1207. {
  1208. printf("Usage: objpath input-file\n");
  1209. return;
  1210. }
  1211. int nLine = 1;
  1212. char buf[2048];
  1213. FILE *f = fopen(argv[1], "rt");
  1214. if (f == NULL)
  1215. {
  1216. printf("Usage: objpath input-file\nError: cannot open file %s!\n", argv[1]);
  1217. return;
  1218. }
  1219. while (fgets(buf, 2048, f) != NULL)
  1220. {
  1221. // Get rid of newline and trailing spaces.
  1222. // =======================================
  1223. char* ptr = strchr(buf, '\n');
  1224. if (ptr != NULL)
  1225. {
  1226. *ptr = ' ';
  1227. while (ptr >= buf && *ptr == ' ')
  1228. {
  1229. *ptr = '\0';
  1230. ptr--;
  1231. }
  1232. }
  1233. // Get rid of leading spaces.
  1234. // ==========================
  1235. ptr = buf;
  1236. while (*ptr == ' ')
  1237. {
  1238. ptr++;
  1239. }
  1240. // Convert to wide char and parse. Ignore blank lines.
  1241. // ====================================================
  1242. if (*ptr != '\0')
  1243. {
  1244. wchar_t buf2[2048];
  1245. MultiByteToWideChar(CP_ACP, 0, ptr, -1, buf2, 2048);
  1246. printf("----Object path----\n");
  1247. printf("%S\n", buf2);
  1248. ParsedObjectPath* pOutput = 0;
  1249. CObjectPathParser p(e_ParserAcceptAll);
  1250. int nStatus = p.Parse(buf2, &pOutput);
  1251. if (nStatus != 0)
  1252. {
  1253. printf("ERROR: return code is %d\n", nStatus);
  1254. continue;
  1255. }
  1256. printf("No errors.\n");
  1257. printf("------Output------\n");
  1258. LPWSTR pKey = pOutput->GetKeyString();
  1259. printf("Key String = <%S>\n", pKey);
  1260. delete pKey;
  1261. printf("Server = %S\n", pOutput->m_pServer);
  1262. printf("Namespace Part = %S\n", pOutput->GetNamespacePart());
  1263. printf("Parent Part = %S\n", pOutput->GetParentNamespacePart());
  1264. for (DWORD dwIx = 0; dwIx < pOutput->m_dwNumNamespaces; dwIx++)
  1265. {
  1266. printf("Namespace = <%S>\n", pOutput->m_paNamespaces[dwIx]);
  1267. }
  1268. printf("Class = <%S>\n", pOutput->m_pClass);
  1269. // If here, the key ref is complete.
  1270. // =================================
  1271. for (dwIx = 0; dwIx < pOutput->m_dwNumKeys; dwIx++)
  1272. {
  1273. KeyRef *pTmp = pOutput->m_paKeys[dwIx];
  1274. printf("*** KeyRef contents:\n");
  1275. printf(" Name = %S Value=", pTmp->m_pName);
  1276. switch (V_VT(&pTmp->m_vValue))
  1277. {
  1278. case VT_I4: printf("%d", V_I4(&pTmp->m_vValue)); break;
  1279. case VT_R8: printf("%f", V_R8(&pTmp->m_vValue)); break;
  1280. case VT_BSTR: printf("<%S>", V_BSTR(&pTmp->m_vValue)); break;
  1281. default:
  1282. printf("BAD KEY REF\n");
  1283. }
  1284. printf("\n");
  1285. }
  1286. p.Free(pOutput);
  1287. }
  1288. }
  1289. }
  1290. void main(int argc, char **argv)
  1291. {
  1292. xmain(argc, argv);
  1293. }
  1294. #endif