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.

997 lines
27 KiB

  1. # =========================================================================
  2. # Name: UpdFX.pm
  3. # Owner: RickKr
  4. # Purpose: This module contains global variable assignments and support routines for UpdFX.pl
  5. # History:
  6. # 02/23/01, RickKr: Created.
  7. # 03/08/01, RickKr: Don't show sd cmdline if return context is list.
  8. # 03/30/01, RickKr: Moved sd functions to end of file.
  9. # ============================================================================================
  10. # Module definition
  11. # Define package namespace for the module
  12. package UpdFX;
  13. # Export code required to execute at module load
  14. BEGIN
  15. {
  16. # Use standard Exporter module functionality
  17. use Exporter;
  18. @ISA = qw(Exporter);
  19. # List of all default exported variables and procedures
  20. @EXPORT = qw
  21. (
  22. $TRUE
  23. $FALSE
  24. $DEFAULT
  25. _Assert
  26. _CopyFile
  27. _DoesHelpArgExist
  28. _EnsurePathExists
  29. _Error
  30. _GetDirList
  31. _ParseArgs
  32. _RequireArgument
  33. _RequireReference
  34. _SdExec
  35. _SplitPath
  36. _Warning
  37. );
  38. # Global constant declarations
  39. $TRUE = (0 == 0);
  40. $FALSE = (0 != 0);
  41. $DEFAULT = undef;
  42. }
  43. # enum RefTypes
  44. {
  45. my $nEnum = 0;
  46. $keRefNoRef = $nEnum++;
  47. $keRefReference = $nEnum++;
  48. $keRefScalar = $nEnum++;
  49. $keRefArray = $nEnum++;
  50. $keRefHash = $nEnum++;
  51. $keRefCode = $nEnum++;
  52. $keRefGlob = $nEnum++;
  53. $keRefOLE = $nEnum++;
  54. }
  55. my %mhcRefTypes =
  56. (
  57. $keRefNoRef => "no reference",
  58. $keRefReference => "REF",
  59. $keRefScalar => "SCALAR",
  60. $keRefArray => "ARRAY",
  61. $keRefHash => "HASH",
  62. $keRefCode => "CODE",
  63. $keRefGlob => "GLOB",
  64. $keRefOLE => "OLE",
  65. );
  66. use File::Copy;
  67. # Module has successfully been initialized.
  68. return ($TRUE);
  69. # =========================================================================
  70. # _Assert()
  71. #
  72. # Purpose:
  73. # Print a standardized message and halt the system if an expression does not evaluate to true.
  74. # Inputs:
  75. # $bExpressionResult The boolean espression to evaluate.
  76. # $sMsg A brief, informative message describing the test or failure (optional).
  77. # Outputs:
  78. # None.
  79. # Dependencies:
  80. # None.
  81. # Notes:
  82. # =========================================================================
  83. sub _Assert
  84. {
  85. my ($bExpressionResult, $sMsg) = @_;
  86. if (! $bExpressionResult)
  87. {
  88. UpdFX_Message("Assertion Failure", $sMsg);
  89. exit(1);
  90. }
  91. }
  92. # =========================================================================
  93. # _Error()
  94. #
  95. # Purpose:
  96. # Print a standardized error message.
  97. # Inputs:
  98. # $sMsg A brief, informative message describing failure (optional).
  99. # Outputs:
  100. # Returns $FALSE.
  101. # Dependencies:
  102. # None
  103. # Notes:
  104. # This routine should be called to signal errors that are serious, but do not prevent a script
  105. # from continuing execution.
  106. # ===========================================================================
  107. sub _Error
  108. {
  109. my ($sMsg) = @_;
  110. UpdFX_Message("Error", $sMsg);
  111. return ($FALSE);
  112. }
  113. # =========================================================================
  114. # _Warning()
  115. #
  116. # Purpose:
  117. # Print a standardized warning message.
  118. # Inputs:
  119. # $sMsg A brief, informative message describing the warning (optional).
  120. # Outputs:
  121. # Returns $FALSE.
  122. # Dependencies:
  123. # None
  124. # Notes:
  125. # None.
  126. # ===========================================================================
  127. sub _Warning
  128. {
  129. my ($sMsg) = @_;
  130. UpdFX_Message("Warning", $sMsg);
  131. return ($FALSE);
  132. }
  133. # =========================================================================
  134. # _GetCallStack()
  135. #
  136. # Purpose:
  137. # Get the current call stack.
  138. # Inputs:
  139. # None.
  140. # Outputs:
  141. # The call stack as an array.
  142. # Dependencies:
  143. # None.
  144. # Notes:
  145. # None.
  146. # =========================================================================
  147. sub _GetCallStack
  148. {
  149. my $bContinue = $TRUE;
  150. my $i = 0;
  151. my $nIndex;
  152. my $sPackage;
  153. my $sFile;
  154. my $nLine;
  155. my $sSubName;
  156. my $bHasArguments;
  157. my $bWantArray;
  158. my $sEvalText;
  159. my $bIsRequire;
  160. my $sNextFile;
  161. my $nNextLine;
  162. my @CallStack = ();
  163. my $nStackIndex;
  164. $nStackIndex = 0;
  165. ($sPackage, $sFile, $nLine, $sSubName, $bHasArguments, $bWantArray, $sEvalText, $bIsRequire) = caller($i++);
  166. while ($bContinue)
  167. {
  168. $bContinue = ($sPackage, $sNextFile, $nNextLine, $sSubName, $bHasArguments, $bWantArray, $sEvalText, $bIsRequire) = caller($i++);
  169. $CallStack[$nStackIndex] = $sFile;
  170. if (defined($sSubName) && "(eval)" ne $sSubName)
  171. {
  172. $nIndex = index($sSubName, "::");
  173. if (-1 != $nIndex)
  174. {
  175. $sSubName = substr($sSubName, $nIndex + 2);
  176. }
  177. if (0 != length($sSubName))
  178. {
  179. $CallStack[$nStackIndex] .= ":" . $sSubName;
  180. }
  181. }
  182. $CallStack[$nStackIndex] .= "(" . $nLine . ")";
  183. if (! $bContinue)
  184. {
  185. last;
  186. }
  187. $sFile = $sNextFile;
  188. $nLine = $nNextLine;
  189. $nStackIndex ++;
  190. }
  191. return (@CallStack);
  192. }
  193. # =========================================================================
  194. # _DoesHelpArgExist()
  195. #
  196. # Purpose:
  197. # Determine if the help arg is present in an arg list.
  198. # Inputs:
  199. # $rsaArgs List of args.
  200. # Outputs:
  201. # Returns $TRUE if help arg is present, else $FALSE.
  202. # Dependencies:
  203. # None.
  204. # Notes:
  205. # None.
  206. # =========================================================================
  207. sub _DoesHelpArgExist
  208. {
  209. my ($rsaArgs) = @_;
  210. _RequireReference($rsaArgs, "\$rsaArgs", $keRefArray);
  211. return (0 < grep(/^[\/-]?(\?|h|help)$/i, @$rsaArgs));
  212. }
  213. # =========================================================================
  214. # _ParseArgs()
  215. #
  216. # Purpose:
  217. # Use a list of valid args to parse a list of actual args into a hash.
  218. # Inputs:
  219. # $rhParsedArgs Reference to a hash that will receive the data.
  220. # $rsaActualArgs Reference to an array containing the actual args.
  221. # $rsaValidArgs Reference to an array containing valid args.
  222. # $rsaRepeatedArgs Optional reference to an array listing the args
  223. # (from valid args) that can be repeated.
  224. # Outputs:
  225. # Returns $TRUE for success, else $FALSE.
  226. # Dependencies:
  227. # None.
  228. # Notes:
  229. # None.
  230. # =========================================================================
  231. sub _ParseArgs
  232. {
  233. my ($rhParsedArgs, $rsaActualArgs, $rsaValidArgs, $rsaRepeatedArgs) = @_;
  234. _RequireReference($rhParsedArgs, "\$rhParsedArgs", $keRefHash);
  235. _RequireReference($rsaActualArgs, "\$rsaActualArgs", $keRefArray);
  236. _RequireReference($rsaValidArgs, "\$rsaValidArgs", $keRefArray);
  237. if (! defined($rsaRepeatedArgs))
  238. {
  239. $rsaRepeatedArgs = [];
  240. }
  241. foreach my $sArg (@$rsaActualArgs)
  242. {
  243. my $nStart = ($sArg =~ /^[\/-]/ ? 1 : 0);
  244. my ($sArgName, $sArgValue) = split(/:/, lc(substr($sArg, $nStart)));
  245. my $sFoundName;
  246. my $sValidName;
  247. # Check to see if the argument matches exactly an entry in the argument list
  248. #
  249. if (0 < grep(/^$sArgName$/i, @$rsaValidArgs))
  250. {
  251. $sFoundName = $sArgName;
  252. }
  253. # If the argument does not exactly match a valid arg in the list, then we check to see if
  254. # we can match it to a portion of one (and only one) of the valid args.
  255. #
  256. else
  257. {
  258. foreach $sValidName (@$rsaValidArgs)
  259. {
  260. if ($sValidName =~ /^$sArgName/i)
  261. {
  262. if (defined($sFoundName))
  263. {
  264. return (_Error("Argument (" . $sArg . ") matches 2 possible args " .
  265. "(/" . $sFoundName . ", /" . $sValidName . ")"));
  266. }
  267. $sFoundName = $sValidName;
  268. }
  269. }
  270. }
  271. # If we didn't find a match, return a nonfatal error
  272. #
  273. if (! defined($sFoundName))
  274. {
  275. return (_Error("Unknown argument specified (" . $sArg . ")"));
  276. }
  277. # If we did find a match, see if it can be repeated. If it can, add it to the array
  278. # for this arg
  279. #
  280. if (grep(/$sFoundName/i, @$rsaRepeatedArgs))
  281. {
  282. push(@{$$rhParsedArgs{$sFoundName}}, $sArgValue);
  283. }
  284. # If it can't be repeated and doesn't already exist in the parsed args hash, add it
  285. #
  286. elsif (! exists($$rhParsedArgs{$sFoundName}))
  287. {
  288. $$rhParsedArgs{$sFoundName} = $sArgValue;
  289. }
  290. # We've already got the arg in parsed args, so we'll return a nonfatal error
  291. # describing the problem
  292. #
  293. else
  294. {
  295. my $sFoundArgument = $$rhParsedArgs{$sFoundName};
  296. if (lc($sFoundArgument) eq lc($sArgValue))
  297. {
  298. return (_Error("Duplicate argument detected (" . $sArg . ")"));
  299. }
  300. else
  301. {
  302. return (_Error("Redefined argument detected (/" . $sFoundName .
  303. ":" . $sFoundArgument . ", " . $sArg . ")"));
  304. }
  305. }
  306. }
  307. return ($TRUE);
  308. }
  309. # =========================================================================
  310. # _IsReference()
  311. #
  312. # Purpose:
  313. # Check to see is a variable is a reference.
  314. # Inputs:
  315. # $eRefType Reference type.
  316. # $rVariable The variable to check.
  317. # Outputs:
  318. # Returns $TRUE if the passed variable is a reference of the indicated type, $FALSE otherwise.
  319. # Dependencies:
  320. # None.
  321. # Notes:
  322. # None.
  323. # =========================================================================
  324. sub _IsReference
  325. {
  326. my ($eRefType, $rVariable) = @_;
  327. my $sRefType;
  328. # Note: It is alright if $rVariable is undefined. Under that special case, the return value for
  329. # ref will still be "", which is what we want to happen.
  330. $sRefType = ref($rVariable);
  331. if (defined($eRefType))
  332. {
  333. _Assert(defined($mhcRefTypes{$eRefType}), "Invalid Reference Type (\$eRefType) Passed.");
  334. return ($mhcRefTypes{$eRefType} eq $sRefType);
  335. }
  336. # if $eRefType was passed as $DEFAULT, then we simply want to know if $rVariable is a reference
  337. # but don't care what it references.
  338. return ("" ne $sRefType);
  339. }
  340. # =========================================================================
  341. # _RequireArgDefined()
  342. #
  343. # Purpose:
  344. # Assert the existence of a required argument passed to an subroutine. Print a standardized
  345. # text message if undefined.
  346. # Inputs:
  347. # $uArgument The argument to check.
  348. # $sTextToDisplay The name of the argument to display to the user.
  349. # Outputs:
  350. # None.
  351. # Dependencies:
  352. # None.
  353. # Notes:
  354. # This routine is not exported to other modules.
  355. # =========================================================================
  356. sub _RequireArgDefined
  357. {
  358. my ($uArgument, $sTextToDisplay) = @_;
  359. my @scExpectedRoutines = ("_RequireArgument", "_RequireReference");
  360. if (defined($sTextToDisplay))
  361. {
  362. $sTextToDisplay .= " ";
  363. }
  364. else
  365. {
  366. $sTextToDisplay = "";
  367. }
  368. #
  369. # Only process stack if there is going to be an error
  370. #
  371. if ( ! defined($uArgument))
  372. {
  373. my ($sPackage, $sFile, $sLine, $sSubName) = caller(1);
  374. # If this sub is called from one of the expected arg handling routines, then we want to
  375. # return information about the subroutine that called the expected routine and not information
  376. # about the expected routine.
  377. if (0 == grep(/$sSubName/, @scExpectedRoutines))
  378. {
  379. ($sPackage, $sFile, $sLine, $sSubName) = caller(2);
  380. }
  381. #
  382. # Use standard assert functionality
  383. #
  384. _Assert($FALSE, "Required argument " . $sTextToDisplay . "not passed to " . $sSubName .
  385. "() in " . $sFile . " line " . $sLine . ".");
  386. }
  387. }
  388. # =========================================================================
  389. # _RequireArgument()
  390. #
  391. # Purpose:
  392. # Assert the existence of a required argument passed to an subroutine.
  393. # Inputs:
  394. # $uArgument The argument to check.
  395. # $sTextToDisplay The name of the argument to display to the user.
  396. # Outputs:
  397. # None.
  398. # Dependencies:
  399. # None.
  400. # Notes:
  401. # None.
  402. # =========================================================================
  403. sub _RequireArgument
  404. {
  405. my ($uArgument, $sTextToDisplay) = @_;
  406. _RequireArgDefined($uArgument, $sTextToDisplay);
  407. #
  408. # Ensure that a reference was not passed
  409. #
  410. if (_IsReference($DEFAULT, $uArgument))
  411. {
  412. my ($sPackage, $sFile, $sLine, $sSubName) = caller(1);
  413. if (defined($sTextToDisplay))
  414. {
  415. $sTextToDisplay .= " ";
  416. }
  417. else
  418. {
  419. $sTextToDisplay = "";
  420. }
  421. #
  422. # Use standard assert functionality
  423. #
  424. _Assert($FALSE, "Variable " . $sTextToDisplay . "passed to " . $sSubName .
  425. "() in " . $sFile . " line " . $sLine . " is unexpected reference.");
  426. }
  427. }
  428. # =========================================================================
  429. # _RequireReference()
  430. #
  431. # Purpose:
  432. # Assert that a variable is a reference.
  433. # Inputs:
  434. # $rVariable The variable to check.
  435. # $sTextToDisplay The name of the variable to display to the user.
  436. # $eRefType Identifier .
  437. # Outputs:
  438. # None.
  439. # Dependencies:
  440. # None.
  441. # Notes:
  442. # None.
  443. # =========================================================================
  444. sub _RequireReference
  445. {
  446. my ($rVariable, $sTextToDisplay, $eRefType) = @_;
  447. my $sRefType = "";
  448. _RequireArgDefined($rVariable, $sTextToDisplay);
  449. if ( ! _IsReference($eRefType, $rVariable))
  450. {
  451. my ($sPackage, $sFile, $sLine, $sSubName) = caller(1);
  452. if (defined($eRefType))
  453. {
  454. $sRefType = $mhcRefTypes{$eRefType} . " ";
  455. }
  456. if (defined($sTextToDisplay))
  457. {
  458. $sTextToDisplay .= " ";
  459. }
  460. else
  461. {
  462. $sTextToDisplay = "";
  463. }
  464. #
  465. # Use standard assert functionality
  466. #
  467. _Assert($FALSE, "Variable " . $sTextToDisplay . "passed to " . $sSubName .
  468. "() in " . $sFile . " line " . $sLine . " is not a " . $sRefType . "reference");
  469. }
  470. }
  471. # =========================================================================
  472. # UpdFX_Message()
  473. #
  474. # Purpose:
  475. # Print a message in a standard format
  476. # Inputs:
  477. # $sHeader The type of message
  478. # $sMsg A brief, informative message describing the event.
  479. # Outputs:
  480. # None.
  481. # Dependencies:
  482. # None.
  483. # Notes:
  484. # This subroutine is not exported.
  485. # =========================================================================
  486. sub UpdFX_Message
  487. {
  488. my ($sHeader, $sMsg) = @_;
  489. _RequireArgument($sHeader, "\$sHeader");
  490. my $sPrefix = "*** " . $sHeader;
  491. if (defined($sMsg))
  492. {
  493. $sPrefix .= ": ";
  494. }
  495. else
  496. {
  497. $sMsg = "";
  498. }
  499. my $nStart;
  500. my $nLength;
  501. my @sMessage;
  502. foreach my $sMsgText (split(/\n/, $sMsg))
  503. {
  504. push(@sMessage, $sPrefix . $sMsgText);
  505. if (-1 != ($nStart = index($sPrefix, $sHeader)))
  506. {
  507. $nLength = length($sPrefix) - $nStart;
  508. substr($sPrefix, $nStart, $nLength, " " x $nLength);
  509. }
  510. }
  511. my @scShortMessages = ("Warning", "Error");
  512. if (0 == grep(/$sHeader/, @scShortMessages))
  513. {
  514. push(@sMessage, ("CALL STACK...", _GetCallStack()));
  515. }
  516. print(join("\n", ("", @sMessage, "", "")));
  517. }
  518. # ===========================================================================
  519. # _GetDirList()
  520. #
  521. # Purpose:
  522. # Return a list of filenames and directories in directory.
  523. # Inputs:
  524. # $sDirectory Directory name
  525. # $bDirectoriesOnly TRUE if only subdirectories are to be returned.
  526. # Outputs:
  527. # List of directory entries.
  528. # Dependencies:
  529. # None
  530. # Notes:
  531. # ===========================================================================
  532. sub _GetDirList
  533. {
  534. my ($sDirectory, $bDirectoriesOnly) = @_;
  535. _RequireArgument($sDirectory, "Directory");
  536. if ( ! defined($bDirectoriesOnly))
  537. {
  538. $bDirectoriesOnly = $FALSE;
  539. }
  540. my @sDirList;
  541. if (! -d $sDirectory)
  542. {
  543. _Error("Directory not found (" . $sDirectory . ")");
  544. }
  545. else
  546. {
  547. if (! opendir(hDirectory, $sDirectory))
  548. {
  549. _Error("Cannot open directory (" . $sDirectory . ")");
  550. }
  551. else
  552. {
  553. # Strip out the . and .. directories
  554. # ! / # do not match
  555. # ^ # start of string
  556. # \. # single period
  557. # \.? # followed by optional period
  558. # $ # end of string
  559. # /
  560. @sDirList = grep (!/^\.\.?$/, readdir(hDirectory));
  561. closedir(hDirectory);
  562. if ($bDirectoriesOnly)
  563. {
  564. @sDirList = grep(-d $sDirectory . "\\" . $_, @sDirList);
  565. }
  566. }
  567. }
  568. return (@sDirList);
  569. }
  570. # =========================================================================
  571. # _SplitPath()
  572. #
  573. # Purpose:
  574. # Separate path and drive from a fully or partly qualified path name
  575. # Inputs:
  576. # $sPath input path name
  577. # Outputs:
  578. # Returns an array:
  579. # - drive (C:) or "" if not present
  580. # - path (\foo\bar) or "" if not present
  581. # - filename (blech.c) or "" if not present
  582. # Notes:
  583. # Example: "c:\directory\subdir\file.ext" will get split into
  584. # ("c:", "\directory\subdir\", "file.ext")
  585. #
  586. # UNC paths are treated as the path part. I.E. "\\server\share\foo\bar.c"
  587. # will get split into ("", "\\server\share\foo\", "bar.c")
  588. #
  589. # =========================================================================
  590. sub _SplitPath
  591. {
  592. my ($sPath) = @_;
  593. my $sDrivePart = "";
  594. my $sPathPart = "";
  595. my $sFilePart = "";
  596. _RequireArgument($sPath, "\$sPath");
  597. #
  598. # /^ Start of string
  599. # (.) Drive letter, assign to $1
  600. # : Followed by a colon
  601. # (.*) Rest of string, assign to $2
  602. # /
  603. if ($sPath =~ /^(.):(.*)/)
  604. {
  605. $sDrivePart = $1 . ":";
  606. $sPath = $2;
  607. }
  608. #
  609. # /^ Start of string
  610. # (.+) any characters, as many as possible, assign to $1
  611. # \\ Followed by backslash
  612. # (.*) Rest of string, assign to $2
  613. # /
  614. if ($sPath =~ /^(.+)\\(.*)/)
  615. {
  616. $sPathPart = $1 . "\\";
  617. $sPath = $2;
  618. }
  619. # what remains must be filename.
  620. $sFilePart = $sPath;
  621. return (($sDrivePart, $sPathPart, $sFilePart));
  622. }
  623. # =========================================================================
  624. # _EnsurePathExists()
  625. #
  626. # Purpose:
  627. # Make sure a full path (from the root) exists.
  628. # Inputs:
  629. # $sPath - The path you want to make sure exists
  630. # Outputs:
  631. # $TRUE if it exists, $FALSE if it can't create the path.
  632. # Dependencies:
  633. # None
  634. # Notes:
  635. # If the supplied path is simply a share (\\server\share) or a drive (c:),the
  636. # function will return $FALSE.
  637. # ===========================================================================
  638. sub _EnsurePathExists
  639. {
  640. my($sPath) = @_;
  641. my @sDirectoryList;
  642. my $sDir;
  643. @sDirectoryList = split /\\/, $sPath;
  644. if ((1 < length($sDirectoryList[0])) && (":" eq substr($sDirectoryList[0], 1, 1)))
  645. {
  646. $sPath = $sDirectoryList[0];
  647. shift(@sDirectoryList);
  648. }
  649. elsif ("\\\\" eq substr($sPath, 0, 2))
  650. {
  651. shift(@sDirectoryList);
  652. shift(@sDirectoryList);
  653. $sPath = "\\\\" . $sDirectoryList[0] . "\\" . $sDirectoryList[1];
  654. shift(@sDirectoryList);
  655. shift(@sDirectoryList);
  656. }
  657. else
  658. {
  659. $sPath = "";
  660. }
  661. #
  662. # determine if an invalid path (x:, \\server\share) was passed.
  663. #
  664. if (! @sDirectoryList)
  665. {
  666. return ($FALSE);
  667. }
  668. foreach my $sDir (@sDirectoryList)
  669. {
  670. $sPath .= "\\$sDir";
  671. if (! -d $sPath)
  672. {
  673. if (! mkdir($sPath, umask()))
  674. {
  675. # If we couldn't create the dir, it's possible that someone else either beat us to it or is in
  676. # the process of creating that same dir. So we'll sleep for 10 seconds (to allow the other process
  677. # to complete) and check for it's existence again
  678. #
  679. sleep(10);
  680. if (! -d $sPath)
  681. {
  682. return (_Error("Cannot create required directory (" . $sPath . ")"));
  683. }
  684. }
  685. }
  686. }
  687. return ($TRUE);
  688. }
  689. # =========================================================================
  690. # _CopyFile()
  691. #
  692. # Purpose:
  693. # Copy a file, creating the destination path if necessary
  694. # Inputs:
  695. # $sSrcFileSpec Filespec of the source file
  696. # $sDestFileSpec Filespec of the destination file
  697. # Outputs:
  698. # Returns $TRUE for success, $FALSE for failure
  699. # Dependencies:
  700. # None
  701. # Notes:
  702. # =========================================================================
  703. sub _CopyFile
  704. {
  705. my ($sSrcFileSpec, $sDestFileSpec) = @_;
  706. _RequireArgument($sSrcFileSpec, "\$sSrcFileSpec");
  707. _RequireArgument($sDestFileSpec, "\$sDestFileSpec");
  708. my ($sDestDrive, $sDestPath, $sDestName) = _SplitPath($sDestFileSpec);
  709. my $sDestPathSpec = $sDestDrive . $sDestPath;
  710. if (_EnsurePathExists($sDestPathSpec)) # else error already output
  711. {
  712. if (0 == copy($sSrcFileSpec, $sDestFileSpec))
  713. {
  714. return (_Error("Cannot copy file (" . $sSrcFileSpec . ")--" . $!));
  715. }
  716. return ($TRUE);
  717. }
  718. return ($FALSE);
  719. }
  720. # =========================================================================
  721. # _SdExec()
  722. #
  723. # Purpose:
  724. # Change the current dir and run an SD command
  725. # Inputs:
  726. # $sCmd SD command to run (e.g. sync, edit, ...)
  727. # $sFileSpec Filespec to run command on
  728. # $bShowOnly (Optional) If $TRUE, only show command
  729. # Outputs:
  730. # - Scalar context: Returns $TRUE for success, $FALSE for failure
  731. # - List context: Returns the output from the command
  732. # Dependencies:
  733. # - Sd.exe must be on the path
  734. # Notes:
  735. # =========================================================================
  736. sub _SdExec
  737. {
  738. my ($sCmd, $sFileSpec, $bShowOnly) = @_;
  739. _RequireArgument($sCmd, "\$sCmd");
  740. _RequireArgument($sFileSpec, "\$sFileSpec");
  741. my ($sFileDrive, $sFilePath, $sFileName) = _SplitPath($sFileSpec);
  742. my $sPathSpec = $sFileDrive . $sFilePath;
  743. if (! _EnsurePathExists($sPathSpec))
  744. {
  745. return (_Error("Cannot create path (" . $sPathSpec . ")"));
  746. }
  747. if (! chdir($sPathSpec))
  748. {
  749. return (_Error("Cannot set path (" . $sPathSpec . ")"));
  750. }
  751. my @sSdArgs = (lc($sCmd));
  752. my %hcSdArgs = ("opened" => "-l");
  753. if ($hcSdArgs{$sSdArgs[0]})
  754. {
  755. push(@sSdArgs, $hcSdArgs{$sSdArgs[0]});
  756. }
  757. my $sSdNum = UpdFX_GetSdChangeListNumber($sCmd, $sPathSpec);
  758. if (defined($sSdNum))
  759. {
  760. push(@sSdArgs, "-c " . $sSdNum);
  761. }
  762. my $sSdArgs = join(" ", @sSdArgs);
  763. my $sSdCmd = "sd.exe";
  764. if ($bShowOnly)
  765. {
  766. $sSdCmd = "echo " . $sSdCmd;
  767. }
  768. elsif (! wantarray())
  769. {
  770. print("sd " . $sSdArgs . " " . $sFileSpec . "\n");
  771. }
  772. if ("submit" ne $sSdArgs[0])
  773. {
  774. $sSdArgs .= " " . $sFileName;
  775. }
  776. if (wantarray())
  777. {
  778. return (`$sSdCmd $sSdArgs`);
  779. }
  780. my $bSucceeded = (0 == system($sSdCmd . " " . $sSdArgs));
  781. if (($bSucceeded) && ("revert" eq $sSdArgs[0]))
  782. {
  783. system($sSdCmd . " change -d " . $sSdNum);
  784. }
  785. return ($bSucceeded);
  786. }
  787. # =========================================================================
  788. # UpdFX_GetSdChangeListNumber()
  789. #
  790. # Purpose:
  791. # Get the changelist number associated with an SD command
  792. # Inputs:
  793. # $sCmd SD command to run (e.g. sync, edit, ...)
  794. # $sPathSpec Path where command will be invoked
  795. # Outputs:
  796. # Returns a changelist number if needed for the cmd, else undefined
  797. # Dependencies:
  798. # - Sd.exe must be on the path
  799. # Notes:
  800. # - This routine is not exported--it is intended solely as a helper
  801. # function for _SdExec()
  802. # =========================================================================
  803. {
  804. my %hChangeListNumber = ();
  805. sub UpdFX_GetSdChangeListNumber
  806. {
  807. my ($sCmd, $sPathSpec) = @_;
  808. _RequireArgument($sCmd, "\$sCmd");
  809. _RequireArgument($sPathSpec, "\$sPathSpec");
  810. my @scChangeListCmds = ("add", "edit", "delete", "opened", "revert", "submit");
  811. if (0 == grep(/$sCmd/i, @scChangeListCmds))
  812. {
  813. return (undef);
  814. }
  815. if (! defined($hChangeListNumber{$sPathSpec}))
  816. {
  817. my $sSdInfo = `sd.exe info`;
  818. my ($sClientRoot) = ($sSdInfo =~ /Client root:\s*(.+)\s+/);
  819. if (! defined($hChangeListNumber{$sClientRoot}))
  820. {
  821. my $scDescription = "NetFX Component Update";
  822. my ($sUserName) = ($sSdInfo =~ /User name:\s*(.+)\s+/);
  823. my ($sClientName) = ($sSdInfo =~ /Client name:\s*(.+)\s+/);
  824. my $sPendingChangesCmd = "sd.exe changes -s pending -u " . $sUserName;
  825. # new change will be created if there is no pending change on this client
  826. my ($sPendingChange) = grep (/\@$sClientName .+$scDescription/, `$sPendingChangesCmd`);
  827. if (! defined($sPendingChange))
  828. {
  829. my @sChangeListText = ();
  830. foreach my $sLine (`sd.exe change -o`)
  831. {
  832. if ($sLine =~ /<enter description here>/)
  833. {
  834. push(@sChangeListText, "\t" . $scDescription);
  835. last;
  836. }
  837. push(@sChangeListText, $sLine);
  838. }
  839. if (open(hProcess, "| sd.exe change -i"))
  840. {
  841. print(hProcess @sChangeListText);
  842. close(hProcess);
  843. }
  844. ($sPendingChange) = grep(/$scDescription/, `$sPendingChangesCmd`);
  845. _Assert(defined($sPendingChange), "Cannot create changelist");
  846. }
  847. my ($sChangeNumber) = ($sPendingChange =~ /Change (\d+)/);
  848. _Assert(defined($sChangeNumber), "Cannot find changelist number");
  849. $hChangeListNumber{$sClientRoot} = $sChangeNumber;
  850. }
  851. $hChangeListNumber{$sPathSpec} = $hChangeListNumber{$sClientRoot};
  852. }
  853. return ($hChangeListNumber{$sPathSpec});
  854. }
  855. }