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.

1006 lines
22 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. devcon.cpp
  5. Abstract:
  6. Device Console
  7. command-line interface for managing devices
  8. @@BEGIN_DDKSPLIT
  9. Author:
  10. Jamie Hunter (JamieHun) Nov-30-2000
  11. Revision History:
  12. @@END_DDKSPLIT
  13. --*/
  14. #include "devcon.h"
  15. struct IdEntry {
  16. LPCTSTR String; // string looking for
  17. LPCTSTR Wild; // first wild character if any
  18. BOOL InstanceId;
  19. };
  20. void FormatToStream(FILE * stream,DWORD fmt,...)
  21. /*++
  22. Routine Description:
  23. Format text to stream using a particular msg-id fmt
  24. Used for displaying localizable messages
  25. Arguments:
  26. stream - file stream to output to, stdout or stderr
  27. fmt - message id
  28. ... - parameters %1...
  29. Return Value:
  30. none
  31. --*/
  32. {
  33. va_list arglist;
  34. LPTSTR locbuffer = NULL;
  35. DWORD count;
  36. va_start(arglist, fmt);
  37. count = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  38. NULL,
  39. fmt,
  40. 0, // LANGID
  41. (LPTSTR) &locbuffer,
  42. 0, // minimum size of buffer
  43. &arglist);
  44. if(locbuffer) {
  45. if(count) {
  46. _fputts(locbuffer,stream);
  47. }
  48. LocalFree(locbuffer);
  49. }
  50. }
  51. void Padding(int pad)
  52. /*++
  53. Routine Description:
  54. Insert padding into line before text
  55. Arguments:
  56. pad - number of padding tabs to insert
  57. Return Value:
  58. none
  59. --*/
  60. {
  61. int c;
  62. for(c=0;c<pad;c++) {
  63. fputs(" ",stdout);
  64. }
  65. }
  66. void Usage(LPCTSTR BaseName)
  67. /*++
  68. Routine Description:
  69. Display simple usage text
  70. Arguments:
  71. BaseName - name of executable
  72. Return Value:
  73. none
  74. --*/
  75. {
  76. FormatToStream(stderr,MSG_USAGE,BaseName);
  77. }
  78. void CommandUsage(LPCTSTR BaseName,LPCTSTR Cmd)
  79. /*++
  80. Routine Description:
  81. Invalid command usage
  82. Display how to get help on command
  83. Arguments:
  84. BaseName - name of executable
  85. Return Value:
  86. none
  87. --*/
  88. {
  89. FormatToStream(stderr,MSG_COMMAND_USAGE,BaseName,Cmd);
  90. }
  91. void Failure(LPCTSTR BaseName,LPCTSTR Cmd)
  92. /*++
  93. Routine Description:
  94. Display simple error text for general failure
  95. Arguments:
  96. BaseName - name of executable
  97. Return Value:
  98. none
  99. --*/
  100. {
  101. FormatToStream(stderr,MSG_FAILURE,BaseName,Cmd);
  102. }
  103. BOOL Reboot()
  104. /*++
  105. Routine Description:
  106. Attempt to reboot computer
  107. Arguments:
  108. none
  109. Return Value:
  110. TRUE if API suceeded
  111. --*/
  112. {
  113. HANDLE Token;
  114. BOOL b;
  115. TOKEN_PRIVILEGES NewPrivileges;
  116. LUID Luid;
  117. //
  118. // we need to "turn on" reboot privilege
  119. // if any of this fails, try reboot anyway
  120. //
  121. if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) {
  122. goto final;
  123. }
  124. if(!LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&Luid)) {
  125. CloseHandle(Token);
  126. goto final;
  127. }
  128. NewPrivileges.PrivilegeCount = 1;
  129. NewPrivileges.Privileges[0].Luid = Luid;
  130. NewPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  131. AdjustTokenPrivileges(
  132. Token,
  133. FALSE,
  134. &NewPrivileges,
  135. 0,
  136. NULL,
  137. NULL
  138. );
  139. CloseHandle(Token);
  140. final:
  141. //
  142. // attempt reboot - inform system that this is planned hardware install
  143. //
  144. return ExitWindowsEx(EWX_REBOOT, REASON_PLANNED_FLAG|REASON_HWINSTALL);
  145. }
  146. LPTSTR GetDeviceStringProperty(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop)
  147. /*++
  148. Routine Description:
  149. Return a string property for a device, otherwise NULL
  150. Arguments:
  151. Devs )_ uniquely identify device
  152. DevInfo )
  153. Prop - string property to obtain
  154. Return Value:
  155. string containing description
  156. --*/
  157. {
  158. LPTSTR buffer;
  159. DWORD size;
  160. DWORD reqSize;
  161. DWORD dataType;
  162. DWORD szChars;
  163. size = 1024; // initial guess
  164. buffer = new TCHAR[(size/sizeof(TCHAR))+1];
  165. if(!buffer) {
  166. return NULL;
  167. }
  168. while(!SetupDiGetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) {
  169. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  170. goto failed;
  171. }
  172. if(dataType != REG_SZ) {
  173. goto failed;
  174. }
  175. size = reqSize;
  176. delete [] buffer;
  177. buffer = new TCHAR[(size/sizeof(TCHAR))+1];
  178. if(!buffer) {
  179. goto failed;
  180. }
  181. }
  182. szChars = reqSize/sizeof(TCHAR);
  183. buffer[szChars] = TEXT('\0');
  184. return buffer;
  185. failed:
  186. if(buffer) {
  187. delete [] buffer;
  188. }
  189. return NULL;
  190. }
  191. LPTSTR GetDeviceDescription(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo)
  192. /*++
  193. Routine Description:
  194. Return a string containing a description of the device, otherwise NULL
  195. Always try friendly name first
  196. Arguments:
  197. Devs )_ uniquely identify device
  198. DevInfo )
  199. Return Value:
  200. string containing description
  201. --*/
  202. {
  203. LPTSTR desc;
  204. desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_FRIENDLYNAME);
  205. if(!desc) {
  206. desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_DEVICEDESC);
  207. }
  208. return desc;
  209. }
  210. IdEntry GetIdType(LPCTSTR Id)
  211. /*++
  212. Routine Description:
  213. Determine if this is instance id or hardware id and if there's any wildcards
  214. instance ID is prefixed by '@'
  215. wildcards are '*'
  216. Arguments:
  217. Id - ptr to string to check
  218. Return Value:
  219. IdEntry
  220. --*/
  221. {
  222. IdEntry Entry;
  223. Entry.InstanceId = FALSE;
  224. Entry.Wild = NULL;
  225. Entry.String = Id;
  226. if(Entry.String[0] == INSTANCEID_PREFIX_CHAR) {
  227. Entry.InstanceId = TRUE;
  228. Entry.String = CharNext(Entry.String);
  229. }
  230. if(Entry.String[0] == QUOTE_PREFIX_CHAR) {
  231. //
  232. // prefix to treat rest of string literally
  233. //
  234. Entry.String = CharNext(Entry.String);
  235. } else {
  236. //
  237. // see if any wild characters exist
  238. //
  239. Entry.Wild = _tcschr(Entry.String,WILD_CHAR);
  240. }
  241. return Entry;
  242. }
  243. LPTSTR * GetMultiSzIndexArray(LPTSTR MultiSz)
  244. /*++
  245. Routine Description:
  246. Get an index array pointing to the MultiSz passed in
  247. Arguments:
  248. MultiSz - well formed multi-sz string
  249. Return Value:
  250. array of strings. last entry+1 of array contains NULL
  251. returns NULL on failure
  252. --*/
  253. {
  254. LPTSTR scan;
  255. LPTSTR * array;
  256. int elements;
  257. for(scan = MultiSz, elements = 0; scan[0] ;elements++) {
  258. scan += lstrlen(scan)+1;
  259. }
  260. array = new LPTSTR[elements+2];
  261. if(!array) {
  262. return NULL;
  263. }
  264. array[0] = MultiSz;
  265. array++;
  266. if(elements) {
  267. for(scan = MultiSz, elements = 0; scan[0]; elements++) {
  268. array[elements] = scan;
  269. scan += lstrlen(scan)+1;
  270. }
  271. }
  272. array[elements] = NULL;
  273. return array;
  274. }
  275. void DelMultiSz(LPTSTR * Array)
  276. /*++
  277. Routine Description:
  278. Deletes the string array allocated by GetDevMultiSz/GetRegMultiSz/GetMultiSzIndexArray
  279. Arguments:
  280. Array - pointer returned by GetMultiSzIndexArray
  281. Return Value:
  282. None
  283. --*/
  284. {
  285. if(Array) {
  286. Array--;
  287. if(Array[0]) {
  288. delete [] Array[0];
  289. }
  290. delete [] Array;
  291. }
  292. }
  293. LPTSTR * GetDevMultiSz(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop)
  294. /*++
  295. Routine Description:
  296. Get a multi-sz device property
  297. and return as an array of strings
  298. Arguments:
  299. Devs - HDEVINFO containing DevInfo
  300. DevInfo - Specific device
  301. Prop - SPDRP_HARDWAREID or SPDRP_COMPATIBLEIDS
  302. Return Value:
  303. array of strings. last entry+1 of array contains NULL
  304. returns NULL on failure
  305. --*/
  306. {
  307. LPTSTR buffer;
  308. DWORD size;
  309. DWORD reqSize;
  310. DWORD dataType;
  311. LPTSTR * array;
  312. DWORD szChars;
  313. size = 8192; // initial guess, nothing magic about this
  314. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  315. if(!buffer) {
  316. return NULL;
  317. }
  318. while(!SetupDiGetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) {
  319. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  320. goto failed;
  321. }
  322. if(dataType != REG_MULTI_SZ) {
  323. goto failed;
  324. }
  325. size = reqSize;
  326. delete [] buffer;
  327. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  328. if(!buffer) {
  329. goto failed;
  330. }
  331. }
  332. szChars = reqSize/sizeof(TCHAR);
  333. buffer[szChars] = TEXT('\0');
  334. buffer[szChars+1] = TEXT('\0');
  335. array = GetMultiSzIndexArray(buffer);
  336. if(array) {
  337. return array;
  338. }
  339. failed:
  340. if(buffer) {
  341. delete [] buffer;
  342. }
  343. return NULL;
  344. }
  345. LPTSTR * GetRegMultiSz(HKEY hKey,LPCTSTR Val)
  346. /*++
  347. Routine Description:
  348. Get a multi-sz from registry
  349. and return as an array of strings
  350. Arguments:
  351. hKey - Registry Key
  352. Val - Value to query
  353. Return Value:
  354. array of strings. last entry+1 of array contains NULL
  355. returns NULL on failure
  356. --*/
  357. {
  358. LPTSTR buffer;
  359. DWORD size;
  360. DWORD reqSize;
  361. DWORD dataType;
  362. LPTSTR * array;
  363. DWORD szChars;
  364. LONG regErr;
  365. size = 8192; // initial guess, nothing magic about this
  366. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  367. if(!buffer) {
  368. return NULL;
  369. }
  370. reqSize = size;
  371. while((regErr = RegQueryValueEx(hKey,Val,NULL,&dataType,(PBYTE)buffer,&reqSize) != NO_ERROR)) {
  372. if(GetLastError() != ERROR_MORE_DATA) {
  373. goto failed;
  374. }
  375. if(dataType != REG_MULTI_SZ) {
  376. goto failed;
  377. }
  378. size = reqSize;
  379. delete [] buffer;
  380. buffer = new TCHAR[(size/sizeof(TCHAR))+2];
  381. if(!buffer) {
  382. goto failed;
  383. }
  384. }
  385. szChars = reqSize/sizeof(TCHAR);
  386. buffer[szChars] = TEXT('\0');
  387. buffer[szChars+1] = TEXT('\0');
  388. array = GetMultiSzIndexArray(buffer);
  389. if(array) {
  390. return array;
  391. }
  392. failed:
  393. if(buffer) {
  394. delete [] buffer;
  395. }
  396. return NULL;
  397. }
  398. BOOL WildCardMatch(LPCTSTR Item,const IdEntry & MatchEntry)
  399. /*++
  400. Routine Description:
  401. Compare a single item against wildcard
  402. I'm sure there's better ways of implementing this
  403. Other than a command-line management tools
  404. it's a bad idea to use wildcards as it implies
  405. assumptions about the hardware/instance ID
  406. eg, it might be tempting to enumerate root\* to
  407. find all root devices, however there is a CfgMgr
  408. API to query status and determine if a device is
  409. root enumerated, which doesn't rely on implementation
  410. details.
  411. Arguments:
  412. Item - item to find match for eg a\abcd\c
  413. MatchEntry - eg *\*bc*\*
  414. Return Value:
  415. TRUE if any match, otherwise FALSE
  416. --*/
  417. {
  418. LPCTSTR scanItem;
  419. LPCTSTR wildMark;
  420. LPCTSTR nextWild;
  421. size_t matchlen;
  422. //
  423. // before attempting anything else
  424. // try and compare everything up to first wild
  425. //
  426. if(!MatchEntry.Wild) {
  427. return _tcsicmp(Item,MatchEntry.String) ? FALSE : TRUE;
  428. }
  429. if(_tcsnicmp(Item,MatchEntry.String,MatchEntry.Wild-MatchEntry.String) != 0) {
  430. return FALSE;
  431. }
  432. wildMark = MatchEntry.Wild;
  433. scanItem = Item + (MatchEntry.Wild-MatchEntry.String);
  434. for(;wildMark[0];) {
  435. //
  436. // if we get here, we're either at or past a wildcard
  437. //
  438. if(wildMark[0] == WILD_CHAR) {
  439. //
  440. // so skip wild chars
  441. //
  442. wildMark = CharNext(wildMark);
  443. continue;
  444. }
  445. //
  446. // find next wild-card
  447. //
  448. nextWild = _tcschr(wildMark,WILD_CHAR);
  449. if(nextWild) {
  450. //
  451. // substring
  452. //
  453. matchlen = nextWild-wildMark;
  454. } else {
  455. //
  456. // last portion of match
  457. //
  458. size_t scanlen = lstrlen(scanItem);
  459. matchlen = lstrlen(wildMark);
  460. if(scanlen < matchlen) {
  461. return FALSE;
  462. }
  463. return _tcsicmp(scanItem+scanlen-matchlen,wildMark) ? FALSE : TRUE;
  464. }
  465. if(_istalpha(wildMark[0])) {
  466. //
  467. // scan for either lower or uppercase version of first character
  468. //
  469. TCHAR u = _totupper(wildMark[0]);
  470. TCHAR l = _totlower(wildMark[0]);
  471. while(scanItem[0] && scanItem[0]!=u && scanItem[0]!=l) {
  472. scanItem = CharNext(scanItem);
  473. }
  474. if(!scanItem[0]) {
  475. //
  476. // ran out of string
  477. //
  478. return FALSE;
  479. }
  480. } else {
  481. //
  482. // scan for first character (no case)
  483. //
  484. scanItem = _tcschr(scanItem,wildMark[0]);
  485. if(!scanItem) {
  486. //
  487. // ran out of string
  488. //
  489. return FALSE;
  490. }
  491. }
  492. //
  493. // try and match the sub-string at wildMark against scanItem
  494. //
  495. if(_tcsnicmp(scanItem,wildMark,matchlen)!=0) {
  496. //
  497. // nope, try again
  498. //
  499. scanItem = CharNext(scanItem);
  500. continue;
  501. }
  502. //
  503. // substring matched
  504. //
  505. scanItem += matchlen;
  506. wildMark += matchlen;
  507. }
  508. return (wildMark[0] ? FALSE : TRUE);
  509. }
  510. BOOL WildCompareHwIds(LPTSTR * Array,const IdEntry & MatchEntry)
  511. /*++
  512. Routine Description:
  513. Compares all strings in Array against Id
  514. Use WildCardMatch to do real compare
  515. Arguments:
  516. Array - pointer returned by GetDevMultiSz
  517. MatchEntry - string to compare against
  518. Return Value:
  519. TRUE if any match, otherwise FALSE
  520. --*/
  521. {
  522. if(Array) {
  523. while(Array[0]) {
  524. if(WildCardMatch(Array[0],MatchEntry)) {
  525. return TRUE;
  526. }
  527. Array++;
  528. }
  529. }
  530. return FALSE;
  531. }
  532. int EnumerateDevices(LPCTSTR BaseName,LPCTSTR Machine,DWORD Flags,int argc,LPTSTR argv[],CallbackFunc Callback,LPVOID Context)
  533. /*++
  534. Routine Description:
  535. Generic enumerator for devices that will be passed the following arguments:
  536. <id> [<id>...]
  537. =<class> [<id>...]
  538. where <id> can either be @instance-id, or hardware-id and may contain wildcards
  539. <class> is a class name
  540. Arguments:
  541. BaseName - name of executable
  542. Machine - name of machine to enumerate
  543. Flags - extra enumeration flags (eg DIGCF_PRESENT)
  544. argc/argv - remaining arguments on command line
  545. Callback - function to call for each hit
  546. Context - data to pass function for each hit
  547. Return Value:
  548. EXIT_xxxx
  549. --*/
  550. {
  551. HDEVINFO devs = INVALID_HANDLE_VALUE;
  552. IdEntry * templ = NULL;
  553. DWORD err;
  554. int failcode = EXIT_FAIL;
  555. int retcode;
  556. int argIndex;
  557. DWORD devIndex;
  558. SP_DEVINFO_DATA devInfo;
  559. SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
  560. BOOL doSearch = FALSE;
  561. BOOL match;
  562. BOOL all = FALSE;
  563. GUID cls;
  564. DWORD numClass = 0;
  565. int skip = 0;
  566. if(!argc) {
  567. return EXIT_USAGE;
  568. }
  569. templ = new IdEntry[argc];
  570. if(!templ) {
  571. goto final;
  572. }
  573. //
  574. // determine if a class is specified
  575. //
  576. if(argc>skip && argv[skip][0]==CLASS_PREFIX_CHAR && argv[skip][1]) {
  577. if(!SetupDiClassGuidsFromNameEx(argv[skip]+1,&cls,1,&numClass,Machine,NULL) &&
  578. GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  579. goto final;
  580. }
  581. if(!numClass) {
  582. failcode = EXIT_OK;
  583. goto final;
  584. }
  585. skip++;
  586. }
  587. if(argc>skip && argv[skip][0]==WILD_CHAR && !argv[skip][1]) {
  588. //
  589. // catch convinient case of specifying a single argument '*'
  590. //
  591. all = TRUE;
  592. skip++;
  593. } else if(argc<=skip) {
  594. //
  595. // at least one parameter, but no <id>'s
  596. //
  597. all = TRUE;
  598. }
  599. //
  600. // determine if any instance id's were specified
  601. //
  602. // note, if =<class> was specified with no id's
  603. // we'll mark it as not doSearch
  604. // but will go ahead and add them all
  605. //
  606. for(argIndex=skip;argIndex<argc;argIndex++) {
  607. templ[argIndex] = GetIdType(argv[argIndex]);
  608. if(templ[argIndex].Wild || !templ[argIndex].InstanceId) {
  609. //
  610. // anything other than simple InstanceId's require a search
  611. //
  612. doSearch = TRUE;
  613. }
  614. }
  615. if(doSearch || all) {
  616. //
  617. // add all id's to list
  618. // if there's a class, filter on specified class
  619. //
  620. devs = SetupDiGetClassDevsEx(numClass ? &cls : NULL,
  621. NULL,
  622. NULL,
  623. (numClass ? 0 : DIGCF_ALLCLASSES) | Flags,
  624. NULL,
  625. Machine,
  626. NULL);
  627. } else {
  628. //
  629. // blank list, we'll add instance id's by hand
  630. //
  631. devs = SetupDiCreateDeviceInfoListEx(numClass ? &cls : NULL,
  632. NULL,
  633. Machine,
  634. NULL);
  635. }
  636. if(devs == INVALID_HANDLE_VALUE) {
  637. goto final;
  638. }
  639. for(argIndex=skip;argIndex<argc;argIndex++) {
  640. //
  641. // add explicit instances to list (even if enumerated all,
  642. // this gets around DIGCF_PRESENT)
  643. // do this even if wildcards appear to be detected since they
  644. // might actually be part of the instance ID of a non-present device
  645. //
  646. if(templ[argIndex].InstanceId) {
  647. SetupDiOpenDeviceInfo(devs,templ[argIndex].String,NULL,0,NULL);
  648. }
  649. }
  650. devInfoListDetail.cbSize = sizeof(devInfoListDetail);
  651. if(!SetupDiGetDeviceInfoListDetail(devs,&devInfoListDetail)) {
  652. goto final;
  653. }
  654. //
  655. // now enumerate them
  656. //
  657. if(all) {
  658. doSearch = FALSE;
  659. }
  660. devInfo.cbSize = sizeof(devInfo);
  661. for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
  662. if(doSearch) {
  663. for(argIndex=skip,match=FALSE;(argIndex<argc) && !match;argIndex++) {
  664. TCHAR devID[MAX_DEVICE_ID_LEN];
  665. LPTSTR *hwIds = NULL;
  666. LPTSTR *compatIds = NULL;
  667. //
  668. // determine instance ID
  669. //
  670. if(CM_Get_Device_ID_Ex(devInfo.DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS) {
  671. devID[0] = TEXT('\0');
  672. }
  673. if(templ[argIndex].InstanceId) {
  674. //
  675. // match on the instance ID
  676. //
  677. if(WildCardMatch(devID,templ[argIndex])) {
  678. match = TRUE;
  679. }
  680. } else {
  681. //
  682. // determine hardware ID's
  683. // and search for matches
  684. //
  685. hwIds = GetDevMultiSz(devs,&devInfo,SPDRP_HARDWAREID);
  686. compatIds = GetDevMultiSz(devs,&devInfo,SPDRP_COMPATIBLEIDS);
  687. if(WildCompareHwIds(hwIds,templ[argIndex]) ||
  688. WildCompareHwIds(compatIds,templ[argIndex])) {
  689. match = TRUE;
  690. }
  691. }
  692. DelMultiSz(hwIds);
  693. DelMultiSz(compatIds);
  694. }
  695. } else {
  696. match = TRUE;
  697. }
  698. if(match) {
  699. retcode = Callback(devs,&devInfo,devIndex,Context);
  700. if(retcode) {
  701. failcode = retcode;
  702. goto final;
  703. }
  704. }
  705. }
  706. failcode = EXIT_OK;
  707. final:
  708. if(templ) {
  709. delete [] templ;
  710. }
  711. if(devs != INVALID_HANDLE_VALUE) {
  712. SetupDiDestroyDeviceInfoList(devs);
  713. }
  714. return failcode;
  715. }
  716. int
  717. __cdecl
  718. _tmain(int argc, LPTSTR argv[])
  719. /*++
  720. Routine Description:
  721. Main entry point
  722. interpret -m:<machine>
  723. and hand off execution to command
  724. Arguments:
  725. argc/argv - parameters passed to executable
  726. Return Value:
  727. EXIT_xxxx
  728. --*/
  729. {
  730. LPCTSTR cmd;
  731. LPCTSTR baseName;
  732. LPCTSTR machine = NULL;
  733. int dispIndex;
  734. int firstArg = 1;
  735. int retval = EXIT_USAGE;
  736. BOOL autoReboot = FALSE;
  737. //
  738. // syntax:
  739. //
  740. // [options] [-]command [<arg> [<arg>]]
  741. //
  742. // options:
  743. // -m:<machine> - remote
  744. // -r - auto reboot
  745. //
  746. baseName = _tcsrchr(argv[0],TEXT('\\'));
  747. if(!baseName) {
  748. baseName = argv[0];
  749. } else {
  750. baseName = CharNext(baseName);
  751. }
  752. while((argc > firstArg) && ((argv[firstArg][0] == TEXT('-')) || (argv[firstArg][0] == TEXT('/')))) {
  753. if((argv[firstArg][1]==TEXT('m')) || (argv[firstArg][1]==TEXT('M'))) {
  754. if((argv[firstArg][2]!=TEXT(':')) || (argv[firstArg][3]==TEXT('\0'))) {
  755. //
  756. // don't recognize this switch
  757. //
  758. break;
  759. }
  760. machine = argv[firstArg]+3;
  761. } else if((argv[firstArg][1]==TEXT('r')) || (argv[firstArg][1]==TEXT('R'))) {
  762. if((argv[firstArg][2]!=TEXT('\0')) ) {
  763. //
  764. // don't recognize this switch
  765. //
  766. break;
  767. } else {
  768. autoReboot = TRUE;
  769. }
  770. } else {
  771. //
  772. // don't recognize this switch
  773. //
  774. break;
  775. }
  776. firstArg++;
  777. }
  778. if((argc-firstArg) < 1) {
  779. //
  780. // after switches, must at least be command
  781. //
  782. Usage(baseName);
  783. return EXIT_USAGE;
  784. }
  785. cmd = argv[firstArg];
  786. if((cmd[0]==TEXT('-')) || (cmd[0]==TEXT('/'))) {
  787. //
  788. // command may begin '-' or '/'
  789. // eg, people might do devcon -help
  790. //
  791. cmd = CharNext(cmd);
  792. }
  793. firstArg++;
  794. for(dispIndex = 0;DispatchTable[dispIndex].cmd;dispIndex++) {
  795. if(lstrcmpi(cmd,DispatchTable[dispIndex].cmd)==0) {
  796. retval = DispatchTable[dispIndex].func(baseName,machine,argc-firstArg,argv+firstArg);
  797. switch(retval) {
  798. case EXIT_USAGE:
  799. CommandUsage(baseName,DispatchTable[dispIndex].cmd);
  800. break;
  801. case EXIT_REBOOT:
  802. if(autoReboot) {
  803. Reboot();
  804. }
  805. break;
  806. case EXIT_OK:
  807. break;
  808. default:
  809. Failure(baseName,DispatchTable[dispIndex].cmd);
  810. break;
  811. }
  812. return retval;
  813. }
  814. }
  815. Usage(baseName);
  816. return EXIT_USAGE;
  817. }