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.

1815 lines
53 KiB

  1. @rem --*-Perl-*--
  2. @if "%overbose%" == "" if "%_echo%"=="" echo off
  3. if not exist "%~dp0oenvtest.bat" (perl -x "%~dpnx0" %* & goto :eof)
  4. setlocal
  5. call %~dp0oenvtest.bat
  6. "%~dp0%PROCESSOR_ARCHITECTURE%\perl%OPERLOPT%" -wx "%~dpnx0" %*
  7. goto :eof
  8. #!perl
  9. require 5.004;
  10. BEGIN {
  11. # set library path for OTOOLS environment
  12. if (defined $ENV{"OTOOLS"}) {
  13. require "$ENV{'OTOOLS'}\\lib\\perl\\otools.pm"; import otools;
  14. }
  15. # Convert "use strict 'subs'" to the eval below so we don't
  16. # barf if the user's @INC is set up wrong. You'd be surprised
  17. # how often this happens.
  18. eval { require strict; import strict 'subs' };
  19. }
  20. sub Usage {
  21. my $usage;
  22. for $usage (split(/\n/, <<'EOM')) {
  23. NAME
  24. $name - create a buddy build package
  25. SYNOPSIS
  26. $name -?
  27. $name [-c changelist] [-d] [-f] -o outputfile
  28. [-q] [-v-] [-x filename] [-FO] [-FN] [filelist]
  29. DESCRIPTION
  30. Combines up all files in a changelist into a self-contained
  31. package which can be used later to replicate the changelist
  32. on another (or the same) machine.
  33. If neither a changelist nor a filelist is specified on the
  34. command line, all files in the default changelist are used.
  35. OPTIONS
  36. -?
  37. Displays this help file.
  38. -c changelist
  39. Collect files from the specified changelist. As a special
  40. case, "-c all" requests all changelists, overriding the
  41. default of "-c default". Note that when a package is created
  42. from files from multiple changelists, they will still unpack
  43. into a single changelist.
  44. -d
  45. Turns on debugging spew to stdout. To avoid mixing debugging
  46. output from normal output, send the normal output to a file
  47. via the -o switch.
  48. -f
  49. Overwrite the output file if it already exists.
  50. -o outputfile
  51. -o -
  52. Generate the output to the specified file (or stdout if "-"
  53. is given as the filename).
  54. O|
  55. O| If no extension is provided, the ".bpk" extension will be
  56. O| assumed.
  57. -q
  58. Run quietly. Diagnostics are suppressed; only warnings
  59. and errors are displayed.
  60. -v-
  61. Disable autoverify. By default, $name verifies the package
  62. after building it if the output is to a file. (Output to
  63. stdout cannot be verified. Sorry.)
  64. -x filename
  65. Read the filelist from the specified file (or stdin if "-"
  66. is given as the filename).
  67. -FO
  68. -FN
  69. Set the $name flavor. O = Office project, N = NT project.
  70. See additional remarks below for a discussion of flavors.
  71. filelist
  72. Optional list of files to be included in the package.
  73. If no filelist is specified, then all files in the default
  74. changelist (or the changelist named by the -c option)
  75. are included in the package.
  76. sd wildcards are permitted here (such as "..." to package
  77. all files in the current directory and below).
  78. OUTPUT
  79. Output is a batch file which can be run on the same or another
  80. enlistment (into the same branch) to replicate the changelist
  81. on the target machine.
  82. See below (under "outputfile") for usage instructions for the
  83. output file.
  84. FLAVORS
  85. If the OTOOLS environment variable is defined, possibly by a
  86. successful, implicit call to oenvtest.bat, $name assumes
  87. the Office flavor; otherwise, it assumes the NT flavor. You
  88. can override this decision by using the -F command line option.
  89. The Office flavor differs from the NT flavor in the following
  90. aspects:
  91. Office flavor registers $name as the handler for
  92. the .bpk file extension. NT flavor does not.
  93. Office flavor appends the ".bpk" extension to the output
  94. file name if no extension is provided. NT flavor does not
  95. assume an extension.
  96. EXAMPLE
  97. Suppose you want to send your default changelist to Bob for
  98. a buddy build before you check the files in.
  99. N| $name -o buddybuild.cmd
  100. O| $name -o buddybuild
  101. N| You then copy buddybuild.cmd to a convenient location
  102. O| You then copy buddybuild.bpk to a convenient location
  103. or send it via email to Bob.
  104. Bob types
  105. N| buddybuild.cmd -u
  106. O| buddybuild.bpk -u
  107. The batch file first determines whether it is safe to unpack
  108. itself. If so, it regurgitates its contents into the
  109. default changelist.
  110. Bob can then do whatever he likes with the changelist. He can
  111. perform a code review with "sd diff". He can launch a buddy
  112. build. He can even submit it on your behalf. Or he can revert
  113. the entire changelist, thereby undoing the effect of running
  114. N| the buddybuild.cmd batch file.
  115. O| the buddybuild.bpk batch file.
  116. EXAMPLE
  117. Suppose you're working on a change, but you get tagged to fix a
  118. BVT break that requires changing a file you are already working on.
  119. You don't want to create a branch just for this one-off fix.
  120. Create a package that consists of all the files you were
  121. working on.
  122. N| $name -o %INIT%\hold.cmd
  123. O| $name -o %INIT%\hold
  124. N| (Notice that the file was output to your developer directory
  125. N| so it won't get scorched.)
  126. O| (This assumes that you have set the INIT environment variable
  127. O| to some safe directory.)
  128. Revert the changelist that you just packaged up.
  129. sd revert -c default ...
  130. Check in your BVT fix. (sd edit, build, sd submit)
  131. Restore the package you saved away.
  132. N| %INIT%\hold.cmd -s -u
  133. O| %INIT%\hold.bpk -s -u
  134. Continue your work.
  135. EXAMPLE
  136. Suppose you're working on a change and you've reached a stage
  137. where you've made a lot of progress but you're about to embark
  138. on some serious rewriting and you don't want to lose what you've
  139. done so far in case your rewrite turns out to be a bad idea.
  140. Create a package that consists of all the files you were
  141. working on.
  142. N| $name -o %INIT%\before_rewrite.cmd
  143. O| $name -o %INIT%\before_rewrite
  144. Do your rewrite. If you decide that your rewrite was a bad idea,
  145. you can back up to the package that you saved.
  146. sd revert -c default ...
  147. N| %INIT%\before_rewrite.cmd -u
  148. O| %INIT%\before_rewrite.bpk -u
  149. Paranoid people like me do this periodically and save the packages
  150. on another machine.
  151. LIMITATIONS
  152. The files in the package must be text or binary files with history.
  153. Unrecoverable files cannot be packaged.
  154. WARNINGS
  155. O| warning: cannot register .bpk file extension; continuing
  156. O|
  157. O| $name couldn't write to the registry to enable
  158. O| double-clicking of files with the .bpk extension. Your
  159. O| perl installation may be incomplete. $name will continue
  160. O| creating your package anyway.
  161. O|
  162. //depotpath: unrecoverable; skipping
  163. Unrecoverable files cannot be packaged
  164. by $name. They will be omitted from the resulting package.
  165. //depotpath: cannot package cmd; skipping
  166. The type of change is not one of the types supported by
  167. $name (add, delete, edit). The file will be omitted from
  168. the resulting package.
  169. //depotpath: will treat integrate as "edit"
  170. //depotpath: will treat branch as "add"
  171. The changelist contains "integrate" or "branch" records.
  172. $name does not know how to regenerate these changes, so it
  173. will treat them as if they were edits/adds instead.
  174. ERRORS
  175. error: Can't tell who you are, sorry
  176. $name was unable to connect to the Source Depot server to
  177. determine your identity. Make sure the server is up and you
  178. are running $name from the correct directory.
  179. error: You need to sd resolve before you can run $name
  180. There are changes that have not yet been resolved.
  181. $name cannot re-create an unresolved edit.
  182. error: outputfile exists; use -f -o to force overwrite
  183. By default, $name refuses to overwrite an existing file.
  184. Use the -f switch to force an overwrite.
  185. internal error: Cannot run sd diff
  186. The Source Depot "sd diff" command failed for some reason.
  187. internal error: filename in sd diff output but not in changelist
  188. The Source Depot "sd diff" command generated a diff entry for
  189. a file that wasn't listed in the output of "sd opened".
  190. Make sure you aren't running a Source Depot command in another
  191. window at the same time you are running $name.
  192. internal error: filename#rev in sd diff output; expected filename#rev2
  193. The Source Depot "sd diff" command generated a diff entry for
  194. a version of the file different from the one listed in the output
  195. of "sd opened".
  196. Make sure you aren't running a Source Depot command in another
  197. window at the same time you are running $name.
  198. internal error: filename in sd diff output twice?
  199. The Source Depot "sd diff" command generated two diff entries
  200. for the same file. $name can't tell which one to trust.
  201. internal error: parsing sd diff output (expecting header)
  202. internal error: parsing sd diff output (expecting header or a/d)
  203. error: Could not parse output of sd diff
  204. $name had trouble parsing the output of the "sd diff" command,
  205. perhaps because one of the files participating in the diff
  206. does not end in a newline. Files must end in a newline in order
  207. for the output of "sd diff" to be parse-able.
  208. In environments running pre-2.0 versions of Source Depot, a
  209. potential reason is that you've asked $name to do Source Depot
  210. operations requiring the server to handle more than MaxResults
  211. records. Specify lists of individual files to work around this
  212. limit.
  213. error: cannot open filename for reading (reason)
  214. error: cannot open filename for writing (reason)
  215. The specified error occurred attempting to open the indicated
  216. file.
  217. error: writing (reason)
  218. The specified error occurred attempting to write to
  219. the output file (usually out of disk space).
  220. REMARKS
  221. 4NT users need to type
  222. perl -Sx $name.cmd
  223. instead of just $name. You can create a 4NT alias
  224. alias $name=perl -Sx $name.cmd
  225. if you use this script a lot.
  226. ENVIRONMENT
  227. Since $name runs sd internally, all the SD environment variables
  228. also apply.
  229. BUGS
  230. Barfs on text files with no trailing newline.
  231. VERSION
  232. O| This is version $packver (Office flavor) of $name.
  233. N| This is version $packver (NT flavor) of $name.
  234. AUTHOR
  235. raymondc. Office flavor by smueller.
  236. ----------------------- HELP ON HOW TO UNPACK ---------------------------
  237. EOM
  238. $usage =~ s/\$name/$main::name/g;
  239. $usage =~ s/\$packver/$main::packver/g;
  240. $usage =~ s/^$main::F\|/ /;
  241. next if $usage =~ /^.\|/;
  242. print $usage, "\n";
  243. }
  244. # Now get the usage string from the output.
  245. 0 while <DATA> ne " my \$usage = <<'EOM';\n";
  246. while (($usage = <DATA>) ne "EOM\n") {
  247. $usage =~ s/\$name/outputfile/g;
  248. $usage =~ s/\$packver/$main::packver/g;
  249. $usage =~ s/\$pack/$main::name/g;
  250. print $usage;
  251. }
  252. }
  253. sub dprint {
  254. print "# ", @_, "\n" if $main::d;
  255. }
  256. sub vprint {
  257. print @_ unless $main::q;
  258. }
  259. sub Emit {
  260. print O @_ or die "error: writing ($!)\n";
  261. }
  262. sub SpewBinaryFile {
  263. local($/);
  264. my $file = shift;
  265. open(B, $file) or die "error: cannot open $file for reading ($!)\n";
  266. binmode(B);
  267. Emit pack("u", scalar <B>), "\n";
  268. close(B);
  269. }
  270. @main::warnings = ();
  271. sub Warning {
  272. warn $_[0];
  273. push(@main::warnings, $_[0]);
  274. }
  275. sub RepeatWarnings {
  276. if (@main::warnings)
  277. {
  278. warn "---- WARNING SUMMARY ----\n";
  279. for my $warning (@main::warnings) {
  280. warn $warning;
  281. }
  282. }
  283. }
  284. sub QuoteSpaces {
  285. wantarray ? map { / / ? "\"$_\"" : $_ } @_
  286. : $_[0] =~ / / ? "\"$_[0]\"" : $_[0];
  287. }
  288. sub CreateTempFile {
  289. my $TEMP = $ENV{"TEMP"} || $ENV{"TMP"};
  290. die "error: no TEMP directory" unless $TEMP;
  291. $TEMP =~ s/\\$//; # avoid the \\ problem
  292. $tempfile = "$TEMP\\bbpack.$$";
  293. open(T, ">$tempfile") || die "error: Cannot create $tempfile\n";
  294. my $success = print T @_;
  295. $success = close(T) && $success;
  296. unlink $tempfile, die "error: writing $tempfile ($!)\n" unless $success;
  297. $tempfile;
  298. }
  299. #
  300. # A "ChangeEntry" is a single line in a change list.
  301. # It is a hash of the form
  302. #
  303. # depotpath => //depot/blahblah
  304. # localpath => C:\nt\blahblah
  305. # rev => revision
  306. # cmd => "edit", "add" or "delete"
  307. # type => "text" or whatever
  308. #
  309. package ChangeEntry;
  310. sub dprint { main::dprint @_ } # For debugging
  311. # Constructs from a line in the "sd opened" output
  312. sub new {
  313. my ($class, $line) = @_;
  314. $line =~ m|^(//.*?)#(\d+) - (\S+) .* \((.*?)\)| || return undef;
  315. my $self = {
  316. depotpath => $1,
  317. rev => $2,
  318. cmd => $3,
  319. type => $4,
  320. };
  321. bless $self, $class;
  322. }
  323. sub Format {
  324. my $self = shift;
  325. "$self->{depotpath}#$self->{rev} $self->{cmd} $self->{type}";
  326. }
  327. sub dump {
  328. my ($self, $caller) = @_;
  329. dprint "$caller: ", $self->Format, " = $self->{localpath}\n";
  330. }
  331. #
  332. # A ChangeList is a list of files to be packaged.
  333. # It is a hash of the form
  334. #
  335. # list => a hash, keyed by depot path, of ChangeEntry's
  336. # skipped => number of files skipped
  337. # add => number of files added
  338. # del => number of files deleted
  339. # edit => number of files edited
  340. #
  341. # We break from generality and do ChangeList pruning in situ.
  342. #
  343. package ChangeList;
  344. sub dprint { main::dprint @_ } # For debugging
  345. sub Warning { main::Warning @_ }
  346. sub new {
  347. my ($class, $change) = @_;
  348. my $list = { };
  349. my $self = {
  350. list => $list,
  351. skipped => 0,
  352. add => 0,
  353. delete => 0,
  354. edit => 0,
  355. };
  356. bless $self, $class;
  357. my @help = (); # Files we need help locating
  358. dprint "sd opened $change";
  359. foreach $line (`sd opened $change 2>&1`) {
  360. my $entry = new ChangeEntry($line);
  361. $entry or die "error: $line";
  362. #dprint $entry->{depotpath};
  363. if ($entry->{type} !~ /(text|binary|unicode)/) {
  364. Warning "$entry->{depotpath}: is unknown type; skipping\n";
  365. $self->{skipped}++;
  366. next;
  367. } elsif ($entry->{type} =~ /S/) {
  368. Warning "$entry->{depotpath}: unrecoverable; skipping\n";
  369. $self->{skipped}++;
  370. next;
  371. } elsif ($entry->{cmd} =~ /^(add|delete)$/) {
  372. push(@help, $entry->{depotpath});
  373. } elsif ($entry->{cmd} eq "integrate") {
  374. Warning "$entry->{depotpath}: will treat $entry->{cmd} as \"edit\"\n";
  375. $entry->{cmd} = "edit";
  376. } elsif ($entry->{cmd} eq "branch") {
  377. Warning "$entry->{depotpath}: will treat $entry->{cmd} as \"add\"\n";
  378. $entry->{cmd} = "add";
  379. push(@help, $entry->{depotpath});
  380. } elsif ($entry->{cmd} ne "edit") {
  381. Warning "$entry->{depotpath}: cannot package $entry->{cmd}; skipping\n";
  382. $self->{skipped}++;
  383. next;
  384. }
  385. $self->{$entry->{cmd}}++;
  386. $list->{lc $entry->{depotpath}} = $entry;
  387. dprint "$entry->{depotpath}#$entry->{rev}";
  388. }
  389. # Now add local paths to all the add/delete's in the ChangeList.
  390. if (@help) {
  391. my $tempfile = main::CreateTempFile(join("\n", @help), "\n");
  392. local($/) = ""; # "sd where -T" emits paragraphs
  393. dprint "sd -x \"$tempfile\" where";
  394. foreach $line (`sd -x "$tempfile" where -T _ 2>&1`) {
  395. my($depotFile) = $line =~ m|^\.\.\. depotFile (.+)|m;
  396. next unless $depotFile;
  397. my $entry = $self->GetEntry($depotFile);
  398. next unless $entry;
  399. my($path) = $line =~ m|^\.\.\. path (.+)|m;
  400. next unless $path;
  401. if ($line =~ m|^\n\n\n unmap|m) {
  402. delete $entry->{localpath};
  403. } else {
  404. $entry->{localpath} = $path;
  405. }
  406. dprint "$depotFile -> $path";
  407. }
  408. unlink $tempfile;
  409. }
  410. # All done.
  411. $self;
  412. }
  413. sub GetEntry {
  414. my ($self, $depotpath) = @_;
  415. $self->{list}->{lc $depotpath};
  416. }
  417. sub GetAllEntries {
  418. my $self = shift;
  419. values %{$self->{list}};
  420. }
  421. sub dump {
  422. my ($self, $caller) = @_;
  423. for my $entry ($self->GetAllEntries()) {
  424. $entry->dump($caller);
  425. }
  426. dprint "$caller: .";
  427. }
  428. package Register;
  429. sub Warning { main::Warning @_ }
  430. #
  431. # RegBpk - Register .bpk file extension and create file association.
  432. # Note that RegBpk is called early; can't assume much.
  433. #
  434. sub RegBpk {
  435. eval { require Win32::Registry; import Win32::Registry };
  436. if ($@) {
  437. Warning "warning: cannot register .bpk file extension; continuing\n";
  438. return;
  439. }
  440. # assoc .bpk=BBPackage
  441. my $hkey = $main::HKEY_LOCAL_MACHINE;
  442. if ($hkey->Create('SOFTWARE\\Classes\\.bpk', $hkey)) {
  443. $hkey->SetValueEx('', 0, &REG_SZ, 'BBPackage');
  444. $hkey->Close();
  445. }
  446. my $binarch = "$ENV{OTOOLS}\\bin\\$ENV{PROCESSOR_ARCHITECTURE}";
  447. my $libperl = "$ENV{OTOOLS}\\lib\\perl";
  448. my $perl = qq/"$binarch\\perl" -I "$libperl" -x/;
  449. my $setup = "set OTOOLS=$ENV{OTOOLS}& set PATH=$binarch;%PATH%";
  450. my $diffcmd = qq!cmd.exe /c ($setup& $perl "%1" -w %*)!;
  451. my $listcmd = qq!cmd.exe /c ($setup& $perl "%1" -l %*& pause)!;
  452. # ftype BBPackage=cmd /c (set OTOOLS/PATH & perl -I LIB -x "%1" -w %*)
  453. # (i.e., shell context menu Open command)
  454. $hkey = $main::HKEY_LOCAL_MACHINE;
  455. if ($hkey->Create(
  456. 'SOFTWARE\\Classes\\BBPackage\\Shell\\Open\\Command', $hkey)) {
  457. $hkey->SetValueEx('', 0, &REG_EXPAND_SZ, $diffcmd);
  458. $hkey->Close();
  459. }
  460. # default is usually Open, but let's be explicit
  461. $hkey = $main::HKEY_LOCAL_MACHINE;
  462. if ($hkey->Create(
  463. 'SOFTWARE\\Classes\\BBPackage\\Shell', $hkey)) {
  464. $hkey->SetValueEx('', 0, &REG_SZ, 'Open');
  465. $hkey->Close();
  466. }
  467. # shell context menu Log command
  468. $hkey = $main::HKEY_LOCAL_MACHINE;
  469. if ($hkey->Create(
  470. 'SOFTWARE\\Classes\\BBPackage\\Shell\\Log\\Command', $hkey)) {
  471. $hkey->SetValueEx('', 0, &REG_EXPAND_SZ, $listcmd);
  472. $hkey->Close();
  473. }
  474. }
  475. package main;
  476. #
  477. # Okay, now initialize our globals.
  478. #
  479. $main::name = $0;
  480. $main::name =~ s/.*[\/\\:]//;
  481. $main::name =~ s/\.(bat|cmd)$//;
  482. $main::userid = $ENV{"USERNAME"} || getlogin || "userid";
  483. ($main::packver) = '$Id: bbpack.cmd#70 2002/09/25 09:23:56 REDMOND\\raymondc $' =~ /#(\d+)/;
  484. $main::c = undef;
  485. $main::d = 0;
  486. $main::f = 0;
  487. $main::o = undef;
  488. $main::q = 0;
  489. $main::v = 1;
  490. @main::x = ();
  491. $main::F = defined $ENV{"OTOOLS"} ? "O" : "N"; # Set default flavor
  492. $main::oCleanup = undef;
  493. # Allow "bbpack /?" to be an alias for "bbpack -?"
  494. while ($#ARGV >= 0 && ($ARGV[0] =~ /^-/ || $ARGV[0] eq '/?')) {
  495. my $switch = shift;
  496. if ($switch eq '-c') {
  497. $main::c = shift;
  498. } elsif ($switch eq '-d') {
  499. $main::userid = "userid";
  500. $main::d++;
  501. } elsif ($switch eq '-f') {
  502. $main::f++;
  503. } elsif ($switch eq '-o') {
  504. $main::o = shift;
  505. } elsif ($switch eq '-q') {
  506. $main::q++;
  507. } elsif ($switch eq '-v-') {
  508. $main::v = 0;
  509. } elsif ($switch eq '-x') {
  510. push(@main::x, shift);
  511. } elsif ($switch eq '-FN') {
  512. $main::F = 'N';
  513. } elsif ($switch eq '-FO') {
  514. $main::F = 'O';
  515. } elsif ($switch eq '-?' || $switch eq '/?') {
  516. if ($main::F eq 'O') {
  517. Register::RegBpk(); # Office flavor creates association
  518. }
  519. Usage(); exit 1;
  520. } else {
  521. die "Invalid command line switch; type $name -? for help\n";
  522. }
  523. }
  524. if ($main::F eq 'O') {
  525. Register::RegBpk(); # Office flavor creates association
  526. }
  527. die "Mandatory -o parameter missing; type $name -? for help\n"
  528. unless defined $main::o; # Output file should be specified
  529. #
  530. # Get some preliminary information.
  531. #
  532. my %ClientProperties;
  533. @RequiredProperties = ("Client name", "User name", "Server address");
  534. {
  535. # Intentionally let errors through to stderr
  536. foreach my $line (`sd info`) {
  537. $ClientProperties{$1} = $2 if $line =~ /^(.*?): (.*)$/;
  538. }
  539. foreach my $prop (@RequiredProperties) {
  540. die "error: Can't tell who you are, sorry\n"
  541. unless $ClientProperties{$prop};
  542. }
  543. }
  544. #
  545. # Global filehandles:
  546. #
  547. # O = output file
  548. # SD = sd command
  549. if ($main::o eq '-') {
  550. open(O, ">&STDOUT");
  551. } else {
  552. # Office flavor appends default extension
  553. $main::o .= '.bpk' if $main::F eq "O" && $main::o !~ /\./;
  554. die "error: $main::o exists; use -f -o to force overwrite\n"
  555. if !$main::f && -e $main::o;
  556. open(O, ">$main::o") or die "error: $main::o: $!\n";
  557. $main::oCleanup = $main::o;
  558. }
  559. dprint ">$main::o";
  560. #
  561. # Dump the header.
  562. #
  563. {
  564. my $line;
  565. while ($line = <DATA>) {
  566. $line =~ s/\$packver/$main::packver/;
  567. Emit $line;
  568. }
  569. }
  570. #
  571. # Dump out some meta-data.
  572. #
  573. {
  574. Emit "Packager: $main::name\n";
  575. foreach my $prop (@RequiredProperties) {
  576. Emit "$prop: $ClientProperties{$prop}\n";
  577. }
  578. my @today = localtime(time);
  579. printf O "Date: %04d/%02d/%02d %02d:%02d:%02d\n",
  580. 1900+$today[5], 1+$today[4], $today[3],
  581. $today[2], $today[1], $today[0];
  582. }
  583. Emit "\n";
  584. #
  585. # Gather up the files that belong to change $main::c and perhaps
  586. # also the files remaining on the command line.
  587. #
  588. # If no changelist or file list provided, then use -c default.
  589. $main::c = "default" if $#ARGV < 0 && !$main::c && !@main::x;
  590. # "-c all" means "all changelists"
  591. $main::c = "" if $main::c && $main::c eq "all";
  592. my $ChangeSpec = $main::c ? "-c $main::c" : "";
  593. @ARGV = QuoteSpaces(@ARGV);
  594. $ChangeSpec .= " @ARGV" if $#ARGV >= 0;
  595. #
  596. # Now add in the stuff from all the -x files.
  597. #
  598. foreach (@main::x) {
  599. open(I, $_) or die "error: cannot open $_ for reading ($!)\n";
  600. while (<I>) {
  601. chomp;
  602. $_ = "\"$_\"" if / / && !/"/;
  603. $ChangeSpec .= " $_";
  604. }
  605. close(I);
  606. }
  607. {
  608. my $line = `sd resolve -n @ARGV 2>&1`;
  609. die "error: You need to sd resolve before you can run $main::name\n"
  610. unless $line =~ /[Nn]o file\(s\) to resolve\.$/;
  611. }
  612. vprint "Collecting files from \"sd opened $ChangeSpec\"\n";
  613. my $ChangeList = new ChangeList($ChangeSpec);
  614. vprint "Collecting files done (",
  615. join(", ", map { "$ChangeList->{$_} $_" } qw(edit add delete skipped)),
  616. ")\n";
  617. #
  618. # Emit the file list, terminated by a blank line.
  619. #
  620. foreach my $entry ($ChangeList->GetAllEntries()) {
  621. Emit $entry->Format, "\n";
  622. }
  623. Emit "\n";
  624. #
  625. # Run a giant "sd diff" to collect the bulk of the information
  626. # The end of each diff is marked with a "q".
  627. if ($ChangeList->{edit}) {
  628. my $copy = 0; # number of lines to copy blindly to output
  629. my $files = 0; # number of files processed
  630. my $entry; # file being processed
  631. my $possibleBad = ""; # file that might be missing a newline
  632. my $line;
  633. my $tempfile;
  634. my $fUnicodeFile = 0;
  635. #
  636. # If the user has overridden SDDIFF in their sd config, we'll have
  637. # to temporarily reconfigure them. (Same goes for SDUDIFF.)
  638. #
  639. # First, try it the easy way: Remove SDDIFF from the environment.
  640. #
  641. delete $ENV{"SDDIFF"};
  642. delete $ENV{"SDUDIFF"};
  643. # Secret environment variable that also messes up sd...
  644. # Delete it while we still can.
  645. delete $ENV{"DIFF"};
  646. #
  647. # Next, see if the user has overridden SDDIFF by "sd set SDDIFF=..."
  648. #
  649. if (`sd set SDDIFF SDUDIFF` =~ /^(SDDIFF|SDUDIFF)=/im) {
  650. #
  651. # Darn, we have to unset it by creating a temporary INI file
  652. # that explicitly clears SDDIFF and SDUDIFF.
  653. #
  654. $tempfile = CreateTempFile("SDDIFF=\nSDUDIFF=\n");
  655. $ENV{"SDPORT"} = $ClientProperties{"Server address"};
  656. $ENV{"SDCLIENT"} = $ClientProperties{"Client name"};
  657. $ENV{"SDCONFIG"} = $tempfile;
  658. dprint "Force SDCONFIG=$ENV{'SDCONFIG'}";
  659. dprint "Force SDPORT=$ENV{'SDPORT'}";
  660. dprint "Force SDCLIENT=$ENV{'SDCLIENT'}";
  661. }
  662. # Okay, we're ready to do the diff thing.
  663. dprint "sd diff -dn $ChangeSpec";
  664. open(SD, "sd diff -dn $ChangeSpec 2>nul|") or die "internal error: Cannot run sd diff\n";
  665. while ($line = <SD>) {
  666. # Unlink the temp file the moment we get output, in case we die
  667. unlink($tempfile), $tempfile = undef if $tempfile;
  668. # Reset the Unicode flag if we hit a new file in the output
  669. $fUnicodeFile = 0 if $line =~ m,==== //.*?#\d+ - .+ ====,;
  670. next if $fUnicodeFile;
  671. next if substr($line, -1) eq "";
  672. die "error: Could not parse output of sd diff\n".
  673. "file $entry->{localpath} does not end in newline\n"
  674. unless substr($line, -1);
  675. if ($copy > 0) {
  676. $copy--;
  677. Emit $line; # Just copy the line to the output
  678. $possibleBad = "-- it might be $entry->{localpath}\n"
  679. if $line =~ m,==== //.*?#\d+ - .+ ====,;
  680. } elsif ($line =~ /^==== (.*?)#(\d+) - (.+) ====(.*)$/) { # New file starting?
  681. #
  682. # $1 = depotpath
  683. # $2 = rev
  684. # $3 = localpath
  685. # $4 = isbinary
  686. Emit "q\n" if $entry; # Finish the previous diff
  687. $entry = $ChangeList->GetEntry($1) or die "internal error: $1 in sd diff output but not in changelist\n";
  688. $entry->{rev} == $2 or die "internal error: $1#$2 in sd diff output; expected #$entry->{rev}\n";
  689. $entry->{localpath} and die "internal error: $1 in sd diff output twice?\n";
  690. $entry->{localpath} = $3;
  691. vprint "edit $3\n";
  692. $files++;
  693. Emit $entry->Format, "\n";
  694. if ($4) {
  695. SpewBinaryFile($3);
  696. $fUnicodeFile = 1 if $4 =~ /unicode/i;
  697. undef $entry; # finished with binary files
  698. }
  699. } elsif (!$entry) { # Expected file header
  700. die "internal error: parsing sd diff output (expecting header)\n".
  701. "-- perhaps a file does not end in a newline\n$possibleBad"
  702. unless $line eq "(... files differ ...)\n";
  703. } elsif ($line =~ /^d/) { # Lines to delete
  704. Emit $line; # Copy to output
  705. } elsif ($line =~ /^a\d+ (\d+)/) { # lines to add
  706. Emit $line;
  707. $copy = $1; # Number of lines to copy blindly
  708. } else {
  709. dprint "barf: $line";
  710. die "internal error: parsing sd diff output (expecting header or a/d)\n";
  711. }
  712. }
  713. Emit "q\n" if $entry; # Finish that last diff (if any)
  714. close(SD);
  715. # Unlink the temp file again, in case the output was null
  716. unlink($tempfile), $tempfile = undef if $tempfile;
  717. die "error: Could not parse output of sd diff\n".
  718. "-- perhaps a file does not end in a newline\n$possibleBad".
  719. "-- or you've hit MaxResults -- try specifying files individually\n"
  720. unless $copy == 0 && $files == $ChangeList->{edit};
  721. }
  722. #
  723. # Emit the added files.
  724. #
  725. foreach my $entry ($ChangeList->GetAllEntries()) {
  726. if ($entry->{cmd} eq 'add') {
  727. vprint "add $entry->{localpath}\n";
  728. Emit $entry->Format, "\n";
  729. if ($entry->{type} =~ /binary|unicode/) {
  730. SpewBinaryFile($entry->{localpath});
  731. } else {
  732. open(I, $entry->{localpath})
  733. or die "error: cannot open $entry->{localpath} for reading ($!)\n";
  734. my @slurp = <I>;
  735. close(I);
  736. die "error: $entry->{localpath} does not end in newline\n" if
  737. scalar(@slurp) && substr($slurp[$#slurp], -1) ne "\n";
  738. Emit "a1 ", scalar(@slurp), "\n", @slurp, "q\n";
  739. }
  740. } elsif ($entry->{cmd} eq 'delete') {
  741. vprint "delete $entry->{localpath}\n";
  742. Emit $entry->Format, "\n";
  743. }
  744. }
  745. close(O) or die "error: writing ($!)\n";
  746. $main::oCleanup = undef;
  747. if ($main::v && $main::o ne "-") {
  748. vprint "verifying package $main::o...\n";
  749. system $^X, "-Sx", "\"$main::o\"", "-v";
  750. }
  751. RepeatWarnings();
  752. END {
  753. if ($main::oCleanup) {
  754. close(O);
  755. warn "Deleting failed package $main::oCleanup\n";
  756. unlink $main::oCleanup;
  757. }
  758. }
  759. __END__
  760. @rem --*-Perl-*--
  761. @if "%overbose%" == "" if "%_echo%"=="" echo off
  762. setlocal
  763. for %%i in (oenvtest.bat) do call %%~$PATH:i
  764. perl -x "%~dpnx0" %*
  765. goto :eof
  766. #!perl
  767. BEGIN {
  768. # augment library path for OTOOLS environment
  769. if (defined $ENV{"OTOOLS"}) {
  770. require "$ENV{'OTOOLS'}\\lib\\perl\\otools.pm"; import otools;
  771. }
  772. # Convert "use strict 'subs'" to the eval below so we don't
  773. # barf if the user's @INC is set up wrong. You'd be surprised
  774. # how often this happens.
  775. eval { require strict; import strict 'subs' };
  776. }
  777. require 5.004;
  778. sub Usage {
  779. my $usage = <<'EOM';
  780. NAME
  781. $name - unpack a buddy build package
  782. SYNOPSIS
  783. $name -?
  784. $name [-d] [-c changelist] [-f] [-l] [-m from to] [-n] [-s] [-u] [-v] [-w] [-x]
  785. DESCRIPTION
  786. Unpack the buddy build generated by a previous $pack.
  787. OPTIONS
  788. -?
  789. Displays this help file.
  790. -d
  791. Turns on debugging spew.
  792. -c changelist
  793. Unpack the package onto the given changelist. If this option
  794. is omitted, the default changelist will be used.
  795. -f
  796. Unpack even if the changelist is nonempty.
  797. -l
  798. List contents of package.
  799. -m from to
  800. Unpack (merge) the package into a depot different from the one
  801. it was built from. "from" and "to" indicate the relationship
  802. between the source and target depots. For example, if the
  803. original package was built from //depot/branch1/... and
  804. you want to unpack to //depot/branch2/... you would specify
  805. -m //depot/branch1/ //depot/branch2/
  806. Note the trailing slashes. The source depot can even be on
  807. a different server.
  808. May not be combined with the -s or -w switches.
  809. -n
  810. Display what would have happened without actually doing
  811. it.
  812. -s
  813. Synchronize to the versions of the files that are
  814. the bases for the changes contained in the package,
  815. but do not unpack them.
  816. This is a convenient step to perform separately
  817. from unpacking because it allows you to perform a
  818. pre-build to ensure that the build was not broken
  819. before you unpacked the files in the package.
  820. -u
  821. Perform the unpack. This switch can be combined with
  822. the -s switch to synchronize and unpack in one step.
  823. The unpack will fail if the changelist is nonempty.
  824. Use the "sd change" command to move files in the default
  825. changelist to a new changelist. This allows you to use
  826. "sd revert -c default ..." to undo the unpack.
  827. To force the unpack even if the changelist is empty,
  828. pass the -f flag. Note that doing so will result in the
  829. unpacked files being added to your changelist,
  830. which in turn makes reverting the unpack a much more
  831. cumbersome operation.
  832. -v
  833. Verify that the package will produce results
  834. identical to what's on the machine right now.
  835. Use this immediately after generating a package as a
  836. double-check.
  837. -w
  838. View contents of packages using windiff (or whatever your
  839. BBDIFF environment variable refers to).
  840. -x
  841. Unpack the files as UNIX-style (LF only) rather than
  842. Win32-style (CRLF).
  843. WARNINGS
  844. warning: filename merge cancelled by user; skipped
  845. A file in the package needed to be merged, but you abandoned
  846. the merge operation ("s" or "q"). The file was left in its original
  847. state; the changes were not merged in.
  848. warning: //depot/.../filename not affected by branch mapping; skipped
  849. The indicated file in the package is not affected by the
  850. from/to mapping, so it was omitted from the merge.
  851. ERRORS
  852. error: sd failed; unpack abandoned
  853. One of the sd commands necessary to complete the unpack failed.
  854. The sd error message should have been displayed immediately
  855. before this message.
  856. error: cannot find local copy of //depot/.../filename
  857. The indicated file in the package could not be found on your
  858. enlistment. Perhaps you have not included it in your view.
  859. internal error: cannot parse output of 'sd have'
  860. internal error: Cannot parse output of 'sd opened'
  861. There was a problem parsing the output of an sd command.
  862. error: changelist is not empty; use -f -u to unpack anyway
  863. The changelist is not empty, so the unpack
  864. was abandoned. To force unpacking into a nonempty
  865. changelist, use the -f switch.
  866. error: filename is already open on client
  867. The specified file is already open. It must be submitted or
  868. reverted before the package can be unpacked.
  869. error: adds in this package already exist on client
  870. The package contains an "add" operation, but the file already
  871. exists. It must be ghosted or deleted before the package can
  872. be unpacked.
  873. error: files to be edited/deleted do not exist on client
  874. The package contains an "edit" or "delete" operation, but the
  875. file does not exist on the client. Perhaps you have not
  876. included it in your view.
  877. error: wrong version of filename on client
  878. The base version of the file in the package does not match the
  879. base version on the client. Use the -s option to synchronize
  880. to the version in the package.
  881. error: filename does not match copy in package
  882. The verification process (-v) failed.
  883. error: corrupted package
  884. An internal consistency check on the package has failed. Either
  885. it has been corrupted, or there is a bug in the program.
  886. error: cannot open filename for writing (reason)
  887. The specified error occurred attempting to open the indicated
  888. file for writing.
  889. error: filename: errorstring
  890. The specified error occurred attempting to open the indicated
  891. file.
  892. error: no TEMP directory
  893. Neither the environment variable TEMP nor TMP could be found.
  894. error: Too many TEMP### directories
  895. Unable to create a temporary directory for windiff because there
  896. are too many already. Normally, temporary directories are cleaned
  897. up automatically when the script terminates, but if the script
  898. terminates abnormally, temporary directories may be left behind
  899. and need to be cleaned up manually.
  900. REMARKS
  901. 4NT users need to type
  902. perl -Sx $name.cmd
  903. instead of just $name.
  904. ENVIRONMENT
  905. BBDIFF
  906. The name of the diff program to use. If not defined, the
  907. SDDIFF variable is used to obtain the name of the file difference
  908. program. If neither is defined, then "windiff" is used.
  909. BBUNPACKDEFCMD
  910. The default command to execute if no command line options are
  911. specified. If not defined, then an error message is displayed.
  912. For example, you might set BBUNPACKDEFCMD=-w to make the default
  913. action when running a package to be to view the contents via
  914. windiff.
  915. Since $name runs sd internally, all the SD environment variables
  916. also apply.
  917. BUGS
  918. Several error messages leak out when you unpack an sd add.
  919. (This is happening while verifying that the file about to be
  920. added hasn't already been added.)
  921. If the package contains an "add" command and the file exists
  922. on the client but is not under source control, the file is overwritten
  923. without warning.
  924. There are almost certainly other bugs in this script somewhere.
  925. VERSION
  926. The package was generated by version $packver of $pack.
  927. EOM
  928. $usage =~ s/\$name/$main::name/g;
  929. $usage =~ s/\$pack/$main::pack/g;
  930. print $usage;
  931. }
  932. sub dprint {
  933. print STDERR "# ", @_, "\n" if $main::d;
  934. }
  935. #
  936. # $action is optional prefix for printing.
  937. # $sharp says whether or not revisions should be kept.
  938. # $ary is a ref to an array of [ $file, $rev ].
  939. #
  940. # Returns a ref to an array of strings to pass to -x.
  941. sub sdarg {
  942. my ($action, $sharp, $ary) = @_;
  943. my @out = ();
  944. my $rc = "";
  945. for my $file (@$ary) {
  946. my $arg = $file->[0];
  947. $arg .= "#" . $file->[1] if $sharp;
  948. $arg .= "\n";
  949. push(@out, $arg);
  950. print "$action $arg" if $action;
  951. }
  952. \@out;
  953. }
  954. #
  955. # $action is a command ("sync#", "edit", etc.)
  956. #
  957. # The revision number is stripped off the file specification
  958. # unless the action itself ends in a # (namely, sync#).
  959. #
  960. # $ary is a ref to an array of [ $file, $rev ].
  961. sub sdaction {
  962. my ($action, $ary) = @_;
  963. my $sharp = $action =~ s/#$//;
  964. if ($#$ary >= 0) {
  965. my $args = sdarg($action, $sharp, $ary);
  966. unless ($main::n) {
  967. my $error = 0;
  968. my $tempfile = CreateTempFile(@$args);
  969. if (open(SD, "sd -x $tempfile -s $action |"))
  970. {
  971. my $line;
  972. while ($line = <SD>) {
  973. if ($line =~ /^(\S+): /) {
  974. $error = 1 if $1 eq 'error';
  975. print $' unless $1 eq 'exit';
  976. }
  977. }
  978. close(SD);
  979. }
  980. unlink $tempfile;
  981. die "error: sd failed; unpack abandoned\n" if $error && !$bang;
  982. }
  983. }
  984. }
  985. sub slurpfile {
  986. my ($file, $type) = @_;
  987. my @file;
  988. if ($type =~ /binary|unicode/) {
  989. open(B, $file) or die "error: cannot open $file for reading ($!)\n";
  990. binmode(B);
  991. local($/);
  992. push(@file, <B>);
  993. close(B);
  994. } else {
  995. open(I, $file) or die "error: cannot open $file for reading ($!)\n";
  996. @file = <I>;
  997. close(I);
  998. }
  999. @file;
  1000. }
  1001. sub spewfile {
  1002. my ($file, $ary, $type) = @_;
  1003. if (!open(O, ">$file")) {
  1004. # Maybe the parent directory hasn't been created yet
  1005. my $dir = $file;
  1006. $dir =~ s/\//\\/g;
  1007. if ($dir =~ s/[^\\\/]+$//) {
  1008. system "md \"$dir\"" unless -e $dir; # let cmd.exe do the hard work
  1009. }
  1010. open(O, ">$file") or die "error: cannot open $file for writing ($!)\n";
  1011. }
  1012. binmode(O) if $main::x || $type =~ /binary|unicode/;
  1013. print O @$ary;
  1014. close(O);
  1015. }
  1016. sub GetUniqueName {
  1017. my $name = shift;
  1018. $name =~ s,^[/\\]*,,; # clean out leading slashes
  1019. $name = substr($name, length($main::CommonPrefix));
  1020. $name =~ s,^[/\\]*,,; # clean out leading slashes again
  1021. if (defined($main::UniqueNames{lc $name}))
  1022. {
  1023. my $i = 1;
  1024. $i++ while $main::UniqueNames{lc "$name$i"};
  1025. $name .= $i;
  1026. }
  1027. $main::UniqueNames{lc $name} = 1;
  1028. $name;
  1029. }
  1030. sub CreateTempFile {
  1031. my $TEMP = $ENV{"TEMP"} || $ENV{"TMP"};
  1032. die "error: no TEMP directory" unless $TEMP;
  1033. $TEMP =~ s/\\$//; # avoid the \\ problem
  1034. $tempfile = "$TEMP\\bbpack.$$";
  1035. open(T, ">$tempfile") || die "error: Cannot create $tempfile\n";
  1036. my $success = print T @_;
  1037. $success = close(T) && $success;
  1038. unlink $tempfile, die "error: writing $tempfile ($!)\n" unless $success;
  1039. $tempfile;
  1040. }
  1041. sub Remap {
  1042. my $path = shift;
  1043. if ($path =~ m#^\Q$main::fromDepot\E#i) {
  1044. substr($path, $[, length($main::fromDepot)) = $main::toDepot;
  1045. }
  1046. $path;
  1047. }
  1048. #
  1049. # $depotpath, $rev is the file to be edited/added.
  1050. # $cmd is "edit" or "add" (indicates where basefile comes from)
  1051. #
  1052. sub ApplyEdit {
  1053. my ($depotpath, $rev, $cmd, $type) = @_;
  1054. my $destpath = $depotpath;
  1055. my $destfile;
  1056. my $where, $file;
  1057. if ($main::w) {
  1058. $file = $depotpath; # for the purpose of GetUniqueName
  1059. } else {
  1060. $destpath = Remap($depotpath) if $main::m;
  1061. dprint "$depotpath -> $destpath" if $main::m;
  1062. local($/) = ""; # "sd where -T" uses paragraphs
  1063. foreach $line (`sd where -T _ \"$destpath\" 2>&1`) {
  1064. undef $where, next if $line =~ m|^\.\.\. unmap|m;
  1065. $where = $1 if $line =~ m|^\.\.\. path (.+)|m;
  1066. }
  1067. die "error: cannot find local copy of $destpath\n" unless $where;
  1068. $destfile = $file = $where;
  1069. }
  1070. my @file;
  1071. my $bias = -1; # perl uses zero-based arrays but diff uses 1-based line numbers
  1072. if ($cmd eq 'add') {
  1073. @file = ();
  1074. $file = $destfile if $main::m;
  1075. } elsif ($cmd eq 'edit') {
  1076. my $src = $file;
  1077. if ($main::v || $main::w || $main::m) {
  1078. dprint "sd$main::ExtraFlags print -q \"$depotpath\"#$rev";
  1079. $src = "sd$main::ExtraFlags print -q \"$depotpath\"#$rev|";
  1080. }
  1081. @file = slurpfile($src, $type);
  1082. } elsif ($cmd eq 'delete') {
  1083. if ($main::w) {
  1084. dprint "sd$main::ExtraFlags print -q \"$depotpath\"#$rev";
  1085. @file = slurpfile("sd$main::ExtraFlags print -q \"$depotpath\"#$rev|", $type);
  1086. } else {
  1087. @file = ();
  1088. }
  1089. }
  1090. my $unique;
  1091. if ($main::w || ($main::m && $cmd eq "edit")) { # Write the original, set up for new
  1092. $unique = GetUniqueName($file);
  1093. spewfile("$main::BeforeDir\\$unique", \@file, $type) unless $cmd eq 'add';
  1094. $file = "$main::AfterDir\\$unique";
  1095. }
  1096. if ($cmd ne 'delete') {
  1097. # now read from <DATA> and apply the edits.
  1098. if ($type =~ /binary|unicode/) {
  1099. local($/) = "";
  1100. @file = (unpack("u", scalar(<DATA>)));
  1101. } else {
  1102. while (($line = <DATA>) ne "q\n") {
  1103. if ($line =~ /^a(\d+) (\d+)/) {
  1104. my @added = ();
  1105. my $count = $2;
  1106. while ($count--) {
  1107. push(@added, scalar(<DATA>));
  1108. }
  1109. splice(@file, $1 + $bias + 1, 0, @added); # +1 because it's "add", not "insert"
  1110. $bias += $2;
  1111. } elsif ($line =~ /^d(\d+) (\d+)/) {
  1112. splice(@file, $1 + $bias, $2);
  1113. $bias -= $2;
  1114. } else {
  1115. die "error: corrupted package\n";
  1116. }
  1117. }
  1118. }
  1119. if ($main::v) {
  1120. my @file2 = slurpfile($file, $type);
  1121. join("", @file) eq join("", @file2) or
  1122. die "error: $file does not match copy in package\n";
  1123. print "$file is okay\n";
  1124. } else {
  1125. spewfile($file, \@file, $type);
  1126. }
  1127. if ($cmd eq "edit" && $main::m) {
  1128. dprint "sd resolve3 \"$main::BeforeDir\\$unique\" \"$main::AfterDir\\$unique\" \"$destfile\" \"$destfile.out\"";
  1129. system("sd resolve3 \"$main::BeforeDir\\$unique\" \"$main::AfterDir\\$unique\" \"$destfile\" \"$destfile.out\"");
  1130. if (-e "$destfile.out") {
  1131. unlink $destfile;
  1132. rename "$destfile.out", $destfile;
  1133. chmod 0666, $destfile;
  1134. } else {
  1135. warn "warning: $destfile merge cancelled by user; skipped\n";
  1136. }
  1137. unlink "$main::BeforeDir\\$unique";
  1138. unlink "$main::AfterDir\\$unique";
  1139. }
  1140. }
  1141. }
  1142. sub IsDirectoryEmpty {
  1143. my $dir = shift;
  1144. my $empty = 1;
  1145. if (opendir(D, $dir)) {
  1146. while ($file = readdir(D)) {
  1147. $empty = 0, last if $file ne '.' && $file ne '..';
  1148. }
  1149. closedir(D);
  1150. } else {
  1151. $empty = 0; # Wacky directory, pretend nonempty so we skip it
  1152. }
  1153. $empty;
  1154. }
  1155. $main::NextUniqueDir = 0;
  1156. sub GetNewTempDir {
  1157. my $TEMP = $ENV{"TEMP"} || $ENV{"TMP"};
  1158. die "error: no TEMP directory" unless $TEMP;
  1159. $TEMP =~ s/\\$//; # avoid the \\ problem
  1160. # Look for suitable "before" and "after" directories; we'll
  1161. # call them "bbtmp###".
  1162. $TEMP .= "\\bbtmp";
  1163. while ($main::NextUniqueDir++ < 1000) {
  1164. my $try = "$TEMP$main::NextUniqueDir";
  1165. if (!-e $try && mkdir($try, 0777)) {
  1166. return $try;
  1167. }
  1168. if (-d _ && IsDirectoryEmpty($try)) {
  1169. return $try;
  1170. }
  1171. }
  1172. die "error: Too many ${TEMP}### directories\n";
  1173. }
  1174. sub CleanDir {
  1175. my $dir = shift;
  1176. if (defined($dir) && -e $dir) {
  1177. system "rd /q /s $dir";
  1178. }
  1179. }
  1180. sub AccumulateCommonPrefix {
  1181. my $file = "/" . lc shift;
  1182. # Remove filename component
  1183. while ($file =~ s,[/\\][^/\\]*$,,) {
  1184. last unless defined $main::CommonPrefix;
  1185. last if substr($main::CommonPrefix, 0, length($file)) eq $file;
  1186. }
  1187. $main::CommonPrefix = $file;
  1188. }
  1189. #
  1190. # Okay, now initialize our globals.
  1191. #
  1192. $main::name = $0;
  1193. $main::name =~ s/.*[\/\\:]//;
  1194. $main::name =~ s/\.(bat|cmd)$//;
  1195. $main::c = "default";
  1196. $main::d = 0;
  1197. $main::f = 0;
  1198. $main::l = 0;
  1199. $main::m = 0;
  1200. $main::n = 0;
  1201. $main::s = 0;
  1202. $main::u = 0;
  1203. $main::v = 0;
  1204. $main::w = 0;
  1205. $main::x = 0;
  1206. $main::anyChanges = 0;
  1207. $main::BeforeDir = undef;
  1208. $main::AfterDir = undef;
  1209. %main::UniqueNames = ("" => 1); # preinit to avoid blank name
  1210. $main::ExtraFlags = "";
  1211. $main::fromDepot = undef;
  1212. $main::toDepot = undef;
  1213. $main::CommonPrefix = undef;
  1214. my %PackerProperties;
  1215. {
  1216. my $line;
  1217. while (($line = <DATA>) =~ /(.*?): (.*)/) {
  1218. $PackerProperties{$1} = $2;
  1219. }
  1220. $main::pack = delete $PackerProperties{Packager};
  1221. die "error: corrupted package\n" unless $line eq "\n" && $main::pack;
  1222. }
  1223. # If there is no command line and there is a BBUNPACKDEFCMD, use that
  1224. # variable instead.
  1225. if ($#ARGV < 0 && defined $ENV{"BBUNPACKDEFCMD"}) {
  1226. my $cmd = $ENV{"BBUNPACKDEFCMD"};
  1227. $cmd =~ s/^\s+//;
  1228. while ($cmd =~ s/^\s*(?:"([^"]*)"|([^"]\S*))\s*//) {
  1229. push(@ARGV, $+);
  1230. }
  1231. }
  1232. while ($#ARGV >= 0 && $ARGV[0] =~ /^-/) {
  1233. my $switch = shift;
  1234. if ($switch eq '-d') {
  1235. $main::d++;
  1236. } elsif ($switch eq '-f') {
  1237. $main::f++;
  1238. } elsif ($switch eq '-l') {
  1239. $main::l++;
  1240. } elsif ($switch eq '-m') {
  1241. $main::m++;
  1242. $main::fromDepot = shift;
  1243. $main::toDepot = shift;
  1244. if ($main::fromDepot !~ m#^//# ||
  1245. $main::toDepot !~ m#^//#) {
  1246. die "-m must be followed by two depot prefixes; type $name -? for help\n";
  1247. }
  1248. } elsif ($switch eq '-c') {
  1249. $main::c = shift;
  1250. if ($main::c !~ m#^[0-9]#) {
  1251. die "-c must be followed by a changelist number; type $name -? for help\n";
  1252. }
  1253. } elsif ($switch eq '-n') {
  1254. $main::n++;
  1255. } elsif ($switch eq '-s') {
  1256. $main::s++;
  1257. } elsif ($switch eq '-u') {
  1258. $main::u++;
  1259. } elsif ($switch eq '-v') {
  1260. $main::v++;
  1261. } elsif ($switch eq '-w') {
  1262. $main::w++;
  1263. } elsif ($switch eq '-x') {
  1264. $main::x++;
  1265. } elsif ($switch eq '-?') {
  1266. Usage(); exit 1;
  1267. } else {
  1268. die "Invalid command line switch; type $name -? for help\n";
  1269. }
  1270. }
  1271. # Should be no command line options
  1272. die "Invalid command line; type $main::name -? for help\n" if $#ARGV >= 0;
  1273. die "Must specify an action; type -? for help\n"
  1274. unless $main::l || $main::s || $main::u || $main::v || $main::w;
  1275. # suppress -w (presumably from registered .bpk extension)
  1276. # if other actions found
  1277. $main::w = 0
  1278. if $main::l || $main::s || $main::u || $main::v;
  1279. die "Cannot combine -m with -s\n" if $main::m && $main::s;
  1280. die "Cannot combine -m with -w\n" if $main::m && $main::w;
  1281. #
  1282. # -l wants some meta-information about the package.
  1283. #
  1284. if ($main::l) {
  1285. my $key;
  1286. foreach $key (split(/,/, "Client name,User name,Date")) {
  1287. print "$key: $PackerProperties{$key}\n";
  1288. }
  1289. print "\n";
  1290. }
  1291. #
  1292. # See which files are open on the client. This also establishes whether
  1293. # the server is up and the user has proper permissions.
  1294. #
  1295. my %OpenedFiles;
  1296. if ($main::s || $main::u) {
  1297. # Intentionally let errors through to stderr
  1298. # Use -s to suppress stderr if no files are opened
  1299. foreach my $line (`sd -s opened -c $main::c`) {
  1300. next if $line =~ m|^exit: |;
  1301. next if $line =~ m!^(error|warning): File\(s\) not opened !;
  1302. $line =~ m,^info: (//.*?)#(\d+|none),
  1303. or die "error: Cannot parse output of 'sd opened -c $main::c'\n";
  1304. $OpenedFiles{$1} = 1;
  1305. dprint "opened $1#$2";
  1306. $main::anyChanges = 1 if $' =~ /$main::c/;
  1307. }
  1308. }
  1309. die "error: changelist $main::c is not empty; use -f -u to unpack anyway\n"
  1310. if $main::anyChanges && $main::u && !$main::f;
  1311. #
  1312. # The -w and -m options require us to set up some directories for unpacking.
  1313. #
  1314. if ($main::w || $main::m)
  1315. {
  1316. $main::BeforeDir = GetNewTempDir();
  1317. $main::AfterDir = GetNewTempDir();
  1318. $main::ExtraFlags = " -p $PackerProperties{'Server address'}";
  1319. }
  1320. #
  1321. # Go through each file in the package and perform an appropriate
  1322. # action on it.
  1323. #
  1324. {
  1325. my @sync, @edit, @add, @delete;
  1326. my $line;
  1327. while (($line = <DATA>) =~ m|^(//.*?)#(\d+) (\S+) (\S+)|) {
  1328. # $1 = depot path
  1329. # $2 = rev
  1330. # $3 = action
  1331. # $4 = filetype (not currently used)
  1332. if ($main::l) {
  1333. print $line;
  1334. }
  1335. # If sync'ing or unpacking, then the file had better not be open
  1336. # since we're the ones who are going to open it.
  1337. die "error: $1 is already open on client\n"
  1338. if defined $OpenedFiles{$1} && ($main::s || ($main::u && !$main::m));
  1339. # If sync'ing, add to list of files that need to be sync'd.
  1340. #
  1341. # If unpacking, then add to the appropriate list so we know
  1342. # how to prepare the file for action.
  1343. if ($main::s) {
  1344. push(@sync, [ $1, $3 eq 'add' ? 'none' : $2 ]);
  1345. }
  1346. if ($main::u) {
  1347. my $path = $1;
  1348. if ($main::m) {
  1349. $path = Remap($1);
  1350. }
  1351. if ($path) {
  1352. if ($3 eq 'edit') {
  1353. push(@edit, [ $path, $2 ]);
  1354. } elsif ($3 eq 'add') {
  1355. push(@add, [ $path, $2 ]);
  1356. } elsif ($3 eq 'delete') {
  1357. push(@delete, [ $path, $2 ]);
  1358. } else {
  1359. die "error: corrupted package\n";
  1360. }
  1361. }
  1362. }
  1363. AccumulateCommonPrefix($1);
  1364. }
  1365. die "error: corrupted package\n" unless $line eq "\n";
  1366. $main::CommonPrefix =~ s,^[/\\]+,,; # clean off leading slashes
  1367. if ($main::s || $main::u) {
  1368. #
  1369. # Make sure that no files being added currently exist.
  1370. #
  1371. if ($#add >= 0) {
  1372. my $args = sdarg(undef, undef, \@add);
  1373. my $tempfile = CreateTempFile(@$args);
  1374. if (`sd -x $tempfile have 2>nul`) {
  1375. unlink $tempfile;
  1376. die "error: adds in this package already exist on client\n";
  1377. }
  1378. unlink $tempfile;
  1379. }
  1380. #
  1381. # Make sure that files being edited are the correct versions.
  1382. #
  1383. if (($#edit >= 0 || $#delete >= 0) && !$main::s && !$main::m) {
  1384. my @have = (@edit, @delete);
  1385. my %have;
  1386. my $file;
  1387. my $args = sdarg(undef, undef, \@have);
  1388. my $tempfile = CreateTempFile(@$args);
  1389. dprint "sd have @$args";
  1390. for $file (`sd -x $tempfile have`) {
  1391. $file =~ m|(//.*?)#(\d+)| or die "error: parsing output of 'sd have'\n";
  1392. dprint "have $1#$2" if $main::d;
  1393. $have{lc $1} = $2;
  1394. }
  1395. unlink $tempfile;
  1396. die "error: files to be edited/deleted do not exist on client\n" if $?;
  1397. for $file (@have) {
  1398. die "error: wrong version of $file->[0] on client\n"
  1399. if $have{lc $file->[0]} ne $file->[1];
  1400. }
  1401. }
  1402. sdaction("sync#", \@sync);
  1403. sdaction("edit -c $main::c", \@edit);
  1404. # Do not do the adds yet; wait until after the edits have been applied
  1405. sdaction("delete -c $main::c", \@delete);
  1406. }
  1407. #
  1408. # Now go extract the actual files.
  1409. #
  1410. if (!$main::n && ($main::u || $main::v || $main::w)) {
  1411. my $line;
  1412. while (($line = <DATA>) =~ m|^(//.*?)#(\d+) (\S+) (\S+)|) {
  1413. ApplyEdit($1, $2, $3, $4);
  1414. }
  1415. }
  1416. # Okay, now do the adds now that the output files have been created
  1417. sdaction("add -c $main::c", \@add);
  1418. }
  1419. if ($main::w) {
  1420. my $windiff = $ENV{"BBDIFF"} || $ENV{"SDDIFF"} || "windiff";
  1421. system("$windiff \"$main::BeforeDir\" \"$main::AfterDir\"");
  1422. }
  1423. CleanDir($main::BeforeDir);
  1424. CleanDir($main::AfterDir);
  1425. __END__