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.

869 lines
19 KiB

  1. @rem = ('
  2. @perl "%~fpd0" %*
  3. @goto :eof
  4. ');
  5. #
  6. # This script manages SD source branches.
  7. # It knows how to construct client view specs from branch view specs.
  8. # It knows how to insert them into the client view, and to remove them.
  9. # It will also check to make sure that the user doesn't have any files open.
  10. #
  11. # Written and maintained by Arlie Davis ([email protected]).
  12. #
  13. $PGM = $0;
  14. $PGM =~ s/.*\\//;
  15. $PGM =~ s/\.cmd$//i;
  16. $PGM =~ tr/a-z/A-Z/;
  17. $FakeClientWrite = 0;
  18. %Actions = (
  19. 'add' => [ \&Action_AddBranch, "\tadd <branch>\tAdd a branch to your client view\n" ],
  20. 'list' => [ \&Action_ListViews, "\tlist\t\tList your current client view\n" ],
  21. 'remove' => [ \&Action_RemoveBranch, "\tremove <branch>\tRemove a branch from your client view\n" ],
  22. 'verifyopen' => [ \&Action_VerifyOpen, "\tverifyopen\tVerify that all open (checked out) files
  23. \t\t\tare checked out on a private branch\n" ],
  24. );
  25. @ClientView = (); # contains references to viewspec arrays [depot path, client path, flags]
  26. $ClientName = ""; # client name of this client workspace
  27. $clientRoot = ""; # local directory for this client workspace
  28. $ClientViewHead = ""; # contains all information before the client view
  29. @ClientFieldOrder = (); # order of the fields in client definition
  30. %ClientFieldData = (); # contains fields
  31. $ClientRootDepotPath = ""; # root of this enlistment (depot path), e.g. "//depot/Lab02_n/net"
  32. $ClientDescriptionSignature = "[Managed by br.pl]";
  33. %ClientParseFunc = (
  34. 'Root' => \&ParseClient_Root,
  35. 'View' => \&ParseClient_View,
  36. 'Client' => \&ParseClient_Client,
  37. );
  38. @argv = @ARGV;
  39. while ($argv[0] =~ /^-/)
  40. {
  41. $_ = $argv[0];
  42. if (/^-force$/) { $ArgForce = 1; }
  43. else { &Usage; }
  44. shift @argv;
  45. }
  46. #shift @argv while ($argv[0] =~ /^-/);
  47. $action = shift @argv;
  48. if ($Actions{$action}) {
  49. $routine = $Actions{$action}[0];
  50. &$routine (@argv);
  51. exit 0;
  52. }
  53. else {
  54. print "Invalid action: $action\n" if $action;
  55. &Usage;
  56. }
  57. # --------------------------------------------------------------------------
  58. # subroutines
  59. sub strcmpi
  60. {
  61. # isn't there an easier way?????
  62. local ($a, $b) = @_;
  63. $a =~ tr/a-z/A-Z/;
  64. $b =~ tr/a-z/A-Z/;
  65. return $a eq $b;
  66. }
  67. sub ParseClient_Root
  68. {
  69. &DieBadInstall ("The client definition contained an empty 'Root' field.\n")
  70. unless $_[0];
  71. $ClientRoot = $_[0];
  72. }
  73. sub ParseClient_Client
  74. {
  75. &DieBadInstall ("The client definition contained an empty 'Client' field.\n")
  76. unless $_[0];
  77. $ClientName = $_[0];
  78. }
  79. sub ParseClient_View
  80. {
  81. local (@ViewSpec);
  82. # the client definition specifies the client name (such as "arlied0").
  83. # client view spec paths always begin with //client/ (//arlied0/foo/bar/...)
  84. # in order for us to determine how to automatically set up branch rules,
  85. # we have to find the rule that maps the root of the client enlistment.
  86. # in other words, the client viewspec that has (x //client/...).
  87. # this will tell us the depot path of the client's root.
  88. # this will be used later to build client view specs from branch specs.
  89. for (@_) {
  90. @ViewSpec = &ParseViewSpec ($_);
  91. push @ClientView, [@ViewSpec] if @ViewSpec;
  92. }
  93. }
  94. sub DetermineRootDepotPath
  95. {
  96. local ($ClientRootViewSpec);
  97. local ($ArrayRef);
  98. die "Expected \$ClientName by now.\n" unless $ClientName;
  99. $ClientRootViewSpec = "//$ClientName/...";
  100. $ClientRootDepotPath = "";
  101. for (@ClientView) {
  102. # print "comparing ($ClientRootViewSpec) and $$_[1]\n";
  103. if (strcmpi ($ClientRootViewSpec, $$_[1])) {
  104. # print "found depot root: ($$_[0])\n";
  105. $ClientRootDepotPath = $$_[0];
  106. last;
  107. }
  108. }
  109. if (!$ClientRootDepotPath) {
  110. print "
  111. Your client workspace has no root view spec.
  112. While this is a legal configuration, it makes it impossible
  113. for this script to determine the root depot path.
  114. If you wish to use this script, please insure that a client
  115. workspace mapping of this form:
  116. //depot/LabFOO/project/... //$ClientName/...
  117. is present in your client workspace view. You can use
  118. the 'sd client -o' command to view your client workspace view.
  119. ";
  120. exit 1;
  121. }
  122. if (!($ClientRootDepotPath =~ s/\/\.\.\.$//)) {
  123. print "
  124. Your root view spec is not valid. It does not end in a
  125. wildcard mapping (/...). This script cannot continue.
  126. ";
  127. exit 1;
  128. }
  129. # print "client root depot path ($ClientRootDepotPath)\n";
  130. }
  131. # LoadClientView loads and parses the client view definition.
  132. # It parses the output of "sd client -o".
  133. # It determines the client name (stored in $ClientName on return).
  134. # It determines the root of the client enlistment (stored in $ClientRoot).
  135. # It loads the set of client view spcs (stored in @ClientView).
  136. # no arguments
  137. sub LoadClientView
  138. {
  139. local ($CurrentField);
  140. local ($LineCount) = 0;
  141. local ($ArrayRef);
  142. print "\nReading client configuration...\n";
  143. open IN, "sd client -o |" or die "Failed to query SD client view: $!\n"
  144. ."Please verify your SD installation.\n";
  145. $ClientViewHead = "";
  146. @ClientFieldOrder = ();
  147. %ClientFieldData = ();
  148. @ClientView = ();
  149. $ClientName = "";
  150. $ClientRoot = "";
  151. $LastField = "";
  152. while (<IN>) {
  153. $LineCount++;
  154. if (/^([a-zA-Z]+):[ \t]*(.*)$/) {
  155. $CurrentField = $1;
  156. push @ClientFieldOrder, $CurrentField;
  157. if ($2) {
  158. # insert initial data
  159. @ClientFieldData{$CurrentField} = [$2];
  160. }
  161. else {
  162. # no data, but insert empty array
  163. @ClientFieldData{$CurrentField} = [];
  164. }
  165. }
  166. else {
  167. if ($CurrentField) {
  168. # we are in the form body
  169. # so we expect data to be reasonable now
  170. if (/^[ \t]+(.*)$/) {
  171. # continuation of previous field
  172. $ArrayRef = $ClientFieldData{$CurrentField};
  173. push @$ArrayRef, $1;
  174. }
  175. elsif (/^[ \t]*$/) {} # just an empty line
  176. else {
  177. # bogus line
  178. die "Invalid line ($LineCount) in client definition file:\n$_\n";
  179. }
  180. }
  181. else {
  182. # not processing any field yet
  183. # just take lines onto the client view head
  184. $ClientViewHead .= $_;
  185. }
  186. }
  187. }
  188. close IN;
  189. # client definition has now been loaded.
  190. # now make something out of the data.
  191. for (@ClientFieldOrder) {
  192. local ($ParseFunc) = $ClientParseFunc{$_};
  193. if ($ParseFunc) {
  194. $ArrayRef = $ClientFieldData{$_};
  195. # print "field ($_): parse function exists, calling: (", join (':', @$ArrayRef, ), ")\n";
  196. &$ParseFunc (@$ArrayRef);
  197. }
  198. # else, no parsing function exists, and we don't care about this field
  199. }
  200. &DieBadInstall ("Client definition does not include 'Client' field, which is mandatory.\n")
  201. unless $ClientName;
  202. &DieBadInstall ("Client definition does not include 'Root' field, which is mandatory.\n")
  203. unless $ClientRoot;
  204. die "The client definition did not contain the client name.\n"
  205. ."Please verify your SD installation.\n"
  206. unless $ClientName;
  207. &DetermineRootDepotPath;
  208. print "\n";
  209. print "Client name: $ClientName\n";
  210. print "Client root: $ClientRoot\n";
  211. print "Depot root: $ClientRootDepotPath\n";
  212. }
  213. sub SaveClientView
  214. {
  215. local ($ArrayRef);
  216. local ($x);
  217. local (@ClientViewLines);
  218. # build the View: entry of the form from @ClientView.
  219. for (@ClientView) {
  220. push @ClientViewLines, &BuildViewSpec (@$_);
  221. }
  222. $ClientFieldData{'View'} = \@ClientViewLines;
  223. $x = 0;
  224. for (@ClientFieldOrder) { $x = 1 if (/^View$/i); }
  225. push @ClientFieldOrder, "View" unless $x;
  226. # clue in the user
  227. print "\nNew client view:\n";
  228. &PrintView (@ClientView);
  229. print "\nSaving client definition...\n";
  230. if ($FakeClientWrite) {
  231. open OUT, ">con:";
  232. }
  233. else {
  234. open OUT, "|sd client -i" || die "Failed to execute 'sd client -i'.\n"
  235. ."Please verify your SD installation.\n";
  236. }
  237. print OUT $ClientViewHead;
  238. for (@ClientFieldOrder) {
  239. $ArrayRef = $ClientFieldData{$_};
  240. if ($ArrayRef) {
  241. if (length (@$ArrayRef) == 0) {
  242. print OUT "$_:\n";
  243. }
  244. elsif (length (@$ArrayRef) == 1 && $_ ne 'View' && $_ ne 'Description') {
  245. print OUT "$_: ", $$ArrayRef[0], "\n";
  246. }
  247. else {
  248. print OUT "$_:\n";
  249. for $x (@$ArrayRef) { print OUT "\t$x\n"; }
  250. }
  251. print OUT "\n";
  252. }
  253. else {
  254. # well, this shouldn't happen, but we cope
  255. # emit a blank field
  256. print STDERR "Warning: Field '$_' contained no data (not even an empty array!)\n";
  257. print OUT "$_:\n";
  258. }
  259. }
  260. close OUT;
  261. print "\n*** Remember to run 'sd sync' to refresh your enlistment! ***\n";
  262. }
  263. sub Action_ListViews
  264. {
  265. &LoadClientView;
  266. print "\nYour client view:\n";
  267. for (@ClientView) {
  268. print "\t", &BuildViewSpec (@$_), "\n";
  269. }
  270. }
  271. sub Usage
  272. {
  273. print STDERR "\nUsage: $PGM <command> ...\n\n",
  274. "Valid commands:\n";
  275. print ($Actions{$_}[1]) for (keys %Actions);
  276. exit;
  277. }
  278. # 0 = string1
  279. # 1 = string2
  280. # returns ($common string, $string1 remainder, $string2 remainder)
  281. #sub FindCommonString
  282. #{
  283. #}
  284. # arg 0 = client view spec (array reference)
  285. # arg 1 = client view spec (array reference)
  286. # returns true if both are equal (ignoring case)
  287. sub IsEqualViewSpec
  288. {
  289. # print "IsEqualViewSpec: comparing [$_[0][0] $_[0][1]] and [$_[1][0] $_[1][1]]\n";
  290. return &strcmpi ($_[0][0], $_[1][0])
  291. && strcmpi ($_[1][0], $_[1][0])
  292. && strcmpi ($_[2][0], $_[2][0]);
  293. }
  294. # AddBranch changes your client view to include a specific branch.
  295. # parameters come from command line
  296. # arg 0 = branch name
  297. sub Action_AddBranch
  298. {
  299. local ($BranchName) = $_[0];
  300. local (@BranchClientView);
  301. local ($x);
  302. local (@DupViewSet);
  303. local (@AddViewSet);
  304. &Usage unless $_[0];
  305. &CheckOpenFiles;
  306. &LoadClientView;
  307. @BranchClientView = &TransformBranch ($BranchName);
  308. if (!@BranchClientView) {
  309. print "The branch views could not be mapped, or an error occurred.\n";
  310. exit 0;
  311. }
  312. # for each client view spec generated from the branch view spec,
  313. # check to see if the mapping is already present.
  314. # if it is, complain.
  315. # if not, add it.
  316. @AddViewSet = ();
  317. @DupViewSet = ();
  318. for (@BranchClientView) {
  319. # print "\tbranch view: ($$_[0]) ($$_[1])\n";
  320. $Found = 0;
  321. for $x (@ClientView) {
  322. # print "checking client view ($$x[0]) ($$x[1])\n";
  323. # compare the client view constructed from the branch view
  324. # with the existing client view. compare both the depot path
  325. # and the client path.
  326. if (&IsEqualViewSpec ($_, $x)) {
  327. # view spec is already in client view.
  328. # note this fact.
  329. push @DupViewSet, $x;
  330. $Found = 1;
  331. last;
  332. }
  333. }
  334. if (!$Found) {
  335. # view spec is not in client view.
  336. # add it.
  337. push @AddViewSet, $_;
  338. }
  339. }
  340. print "\n";
  341. # if all were dups (none were added), then the branch is already part of the view.
  342. if (!@AddViewSet) {
  343. print "The source branch '$BranchName' is already part of your client view.\n";
  344. exit 0;
  345. }
  346. if (@DupViewSet) {
  347. print "WARNING: You have requested that the source branch '$BranchName' be added\n";
  348. print "to your client view. Some (but not all) of the view specifications for \n";
  349. print "this branch are already included in your client view.\n";
  350. print "\n";
  351. print "The following branch views are already part of your client view:\n";
  352. &PrintView (@DupViewSet);
  353. print "\n";
  354. print "The following branch views will be added to your client view:\n";
  355. &PrintView (@AddViewSet);
  356. }
  357. else {
  358. # no parts of the branch view are part of the client view.
  359. # this is the normal case.
  360. print "The source branch '$BranchName' will be added to your client view.\n";
  361. # print "The following lines will be added to your client view:\n";
  362. # &PrintView (@AddViewSet);
  363. }
  364. # add the new lines to the client view
  365. push @ClientView, @AddViewSet;
  366. &SaveClientView;
  367. }
  368. # RemoveBranch changes your client view to remove a specific branch.
  369. # parameters come from command line
  370. # arg 0 = branch name
  371. sub Action_RemoveBranch
  372. {
  373. local ($BranchName) = $_[0];
  374. local (@BranchClientView);
  375. local ($x);
  376. local (@MissingViewSet);
  377. local (@NewClientView);
  378. local ($RemoveCount);
  379. &Usage unless $_[0];
  380. &CheckOpenFiles;
  381. &LoadClientView;
  382. @BranchClientView = &TransformBranch ($BranchName);
  383. if (!@BranchClientView) {
  384. print "The branch views could not be mapped, or an error occurred.\n";
  385. exit 0;
  386. }
  387. # for each client view spec generated from the branch view spec,
  388. # check to see if the mapping is already present.
  389. # if it is, complain.
  390. # if not, add it.
  391. @MissingViewSet = ();
  392. @NewClientView = ();
  393. @SearchClientView = @ClientView;
  394. $RemoveCount = 0;
  395. for (@ClientView) {
  396. # search the branch
  397. $Found = 0;
  398. for $x (@BranchClientView) {
  399. # compare the client view constructed from the branch view
  400. # with the existing client view. compare both the depot path
  401. # and the client path.
  402. if (&IsEqualViewSpec ($_, $x)) {
  403. $Found = 1;
  404. last;
  405. }
  406. }
  407. if ($Found) {
  408. # do not add to the new view set
  409. # do not add to the missing view set
  410. # print "- removing view (", &BuildViewSpec (@$_), ")\n";
  411. $RemoveCount++;
  412. }
  413. else {
  414. # print "- no match (", &BuildViewSpec (@$_), ")\n";
  415. # not found
  416. # carry from old list
  417. push @NewClientView, $_;
  418. }
  419. }
  420. print "\n";
  421. if (!$RemoveCount) {
  422. print "The source branch '$BranchName' was not part of your client view.\n";
  423. exit 0;
  424. }
  425. if (@MissingViewSet) {
  426. print "WARNING: You have requested that the source branch '$BranchName'\n";
  427. print "be removed from your client view. Some (but not all) of the branch\n";
  428. print "view specifications were present in your client view.\n";
  429. print "\n";
  430. print "The following branch views will be removed:\n";
  431. &PrintView (@BranchClientView);
  432. print "\n";
  433. print "The following branch views were missing from your client view:\n";
  434. &PrintView (&MissingViewSet);
  435. }
  436. else {
  437. # the normal case
  438. print "The source branch '$BranchName' will be removed from your client view.\n";
  439. }
  440. @ClientView = @NewClientView;
  441. &SaveClientView;
  442. }
  443. # args are array of view spec
  444. sub PrintView
  445. {
  446. for (@_) {
  447. print "\t", &BuildViewSpec (@$_), "\n";
  448. }
  449. }
  450. # arg 0 = branch name
  451. # return = array of client view specs, transformed from branch specs
  452. # returns empty on error
  453. sub TransformBranch
  454. {
  455. local (@BranchView); # from sd branch -o
  456. local (@BranchClientView); # branch views mapped to client views
  457. local ($DepotPath);
  458. local ($BranchPath);
  459. local ($ClientDepotPathLength);
  460. local ($ClientPath);
  461. return () unless $_[0];
  462. @BranchView = &LoadBranch ($_[0]);
  463. if (!@BranchView) {
  464. print "\nThe name '$_[0]' does not identify a valid, existing branch.\n\n",
  465. "You can use the 'sd branches' command to enumerate valid branches.\n",
  466. "Please make sure you are in the correct SD depot (root, net, etc.), as well.\n";
  467. }
  468. # for each of the elememnts in the branch specification,
  469. # build a new client view that implements the branch view.
  470. @BranchClientView = ();
  471. die "Expected \$ClientRootDepotPath by now\n" unless $ClientRootDepotPath; #assert
  472. # for (@ClientView) {
  473. # print "client: depot path ($$_[0]) view ($$_[1])\n";
  474. # }
  475. $ClientDepotPathLength = length ($ClientRootDepotPath);
  476. for (@BranchView) {
  477. $DepotPath = $$_[0];
  478. $BranchPath = $$_[1];
  479. # print "branch: depot path ($$_[0]) view ($$_[1])\n";
  480. if (&strcmpi (substr ($DepotPath, 0, $ClientDepotPathLength), $ClientRootDepotPath)) {
  481. $ClientPath = "//$ClientName" . substr ($DepotPath, $ClientDepotPathLength);
  482. # copy the flags, though i'm not sure if this is correct.
  483. push @BranchClientView, [$BranchPath, $ClientPath, $$_[2]];
  484. }
  485. else {
  486. print "Warning: Branch spec target ($DepotPath) does not match your root. Ignoring.\n";
  487. }
  488. }
  489. return @BranchClientView;
  490. }
  491. # LoadBranch queries SD for the branch definition of a named branch.
  492. # It parses the output of "sd branch -o <branch>".
  493. # arg 0 = branch name
  494. # returns array of branch views (or empty on error)
  495. sub LoadBranch
  496. {
  497. local (@BranchView); # array of branch view specs
  498. local (@BranchViewSpec);
  499. open BRANCH, "sd branch -o $_[0] |" || die "Failed to query branch: $!\n";
  500. while (<BRANCH>) {
  501. last if (/^View:/i);
  502. }
  503. while (<BRANCH>) {
  504. chop;
  505. @BranchViewSpec = &ParseViewSpec ($_);
  506. if (@BranchViewSpec) {
  507. push @BranchView, [@BranchViewSpec];
  508. }
  509. else {
  510. print "bogus view spec: $_\n" if /[^ \t]/;
  511. }
  512. }
  513. close BRANCH;
  514. # check to see if the user specified a non-existent branch.
  515. # sd is kind of stupid this way -- if you do "sd branch -o badbranch",
  516. # it will cheerfully output a nice, helpful image of something that
  517. # *looks* like a real branch, when it's really trying to tell you
  518. # "no such branch". the only reliable indication is that the only
  519. # branch view spec is a single line: "//depot/... //depot/...".
  520. # we now use this to detect this condition.
  521. if ($BranchView[0][0] eq '//depot/...' && $BranchView[0][1] eq '//depot/...') {
  522. @BranchView = ();
  523. }
  524. return @BranchView;
  525. }
  526. sub ParseViewSpec
  527. # 0 = viewspec
  528. {
  529. if ($_[0] =~ /^[ \t]*(-*)(\/\/[^ \t]+)[ \t]+(\/\/[^ \t]+)[ \t]*$/) {
  530. # $1 is flags (empty or "-")
  531. # $2 is depot path
  532. # $3 is client view or branch view path
  533. return ($2, $3, $1);
  534. }
  535. else {
  536. print "bogus view spec ($_[0])" if $_[0] =~ /[^ \t]/;
  537. return ();
  538. }
  539. }
  540. sub BuildViewSpec
  541. # 0 = depot path
  542. # 1 = client/branch view path
  543. # 3 = flags (- or empty)
  544. {
  545. return "$_[2]$_[0] $_[1]";
  546. }
  547. sub DieBadInstall
  548. {
  549. print @_;
  550. print "Please verify your SD installation.\n";
  551. exit 1;
  552. }
  553. sub TestAction
  554. {
  555. &LoadClientView;
  556. &SaveClientView;
  557. }
  558. # no args
  559. # return true if any files are still opened
  560. sub CheckOpenFiles
  561. {
  562. local ($Result);
  563. if (!open IN, "sd opened |") {
  564. print STDERR "\nWarning: Failed to query the list of open files.\n",
  565. "Assuming no files are open.\n";
  566. return 0;
  567. }
  568. $Result = 0;
  569. while (<IN>) {
  570. if (/^File\(s\) not opened/) {
  571. $Result = 0;
  572. last;
  573. }
  574. elsif (/^[ \t]*$/) {
  575. # ignore blank line
  576. }
  577. elsif (/^\/\//) {
  578. # assume file
  579. $Result = 1;
  580. last;
  581. }
  582. else {
  583. # ????? unexpected output
  584. chomp;
  585. print "Warning: Unexpected output from 'sd opened': $_\n";
  586. }
  587. }
  588. return unless $Result;
  589. if ($ArgForce) {
  590. print "
  591. Warning: You have files open (checked out) in this depot.
  592. ";
  593. }
  594. else {
  595. # user has not elected to override this check
  596. print "
  597. Warning: You have files open (checked out) in this depot.
  598. You should not change your client view until you have
  599. checked in (or reverted) all checked out files.
  600. To see what files you have open:
  601. sd opened
  602. To revert all files (close them without submitting them):
  603. sd revert ...
  604. To override this check, use the '-force' flag.
  605. However, this is NOT RECOMMENDED.
  606. ";
  607. exit 1;
  608. }
  609. }
  610. # VerifyOpen will read your client view, read your list of open files,
  611. # and verify that all open files are opened on a private depot
  612. # (depot path begins with '//depot/private/'.
  613. sub Action_VerifyOpen
  614. {
  615. local (@PrivateSet);
  616. local (@NonPrivateSet);
  617. &LoadClientView;
  618. &DieBadInstall ("Failed to execute 'sd opened'.\n")
  619. unless open IN, "sd opened |";
  620. while (<IN>) {
  621. if (/^File\(s\) not opened on this client/i) {
  622. print;
  623. return;
  624. }
  625. elsif (/^\/\/depot\/private\//io) {
  626. push @PrivateSet, $_;
  627. }
  628. elsif (/^\/\/depot\//io) {
  629. push @NonPrivateSet, $_;
  630. }
  631. elsif (/[^ \t]/) {
  632. print "Warning: 'sd opened' generated this (unrecognized) line:\n$_";
  633. }
  634. }
  635. close IN;
  636. print "\n";
  637. if (@NonPrivateSet) {
  638. print "WARNING: The following files are checked out against a PUBLIC depot:\n\n";
  639. print @NonPrivateSet;
  640. print "\nPLEASE MAKE SURE THIS IS CORRECT!!!\n",
  641. "If you wish to revert your files (cancel your check-in),\n",
  642. "use the 'sd revert' command.\n\n";
  643. }
  644. else {
  645. print "The following files are checked out against a private branch:\n\n";
  646. print @PrivateSet;
  647. }
  648. }