SHIT 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. # Go find perl if we are running this as a shell script.
  2. eval 'exec perl -Ssw $0 "$@"'
  3. if 0;
  4. # Mimic the BSD tool, sccs, for RCS.
  5. # $Id$
  6. #
  7. # Note - this reflects a lot of my personal taste. I'll try and list the
  8. # important differences here:
  9. #
  10. # A bunch of unused commands are not implemented. It is easy to add them,
  11. # mail me if you want me to add something. Please include a spec of what
  12. # you want the command to do. Mail lm@engr.sgi.com.
  13. #
  14. # I look at RCS file internals and know about certain fields as of revision
  15. # 5.x.
  16. #
  17. # This interface does not require a list of files/directories for most
  18. # commands; the implied list is *,v and/or RCS/*,v. Destructive commands,
  19. # such as clean -f, unedit, unget, do *not* have an implied list. In
  20. # other words,
  21. # rccs diffs is the same as rccs diffs RCS
  22. # but
  23. # rccs unedit is not the same as rccs unedit RCS
  24. #
  25. # If you add (potentially) destructive commands, please check for
  26. # them in main() and make sure that the autoexpand does not happen.
  27. #
  28. # TODO:
  29. # Make it so that you can pass a list of files/dirs via stdin.
  30. #
  31. # It might be nice to have all the "system" args printed out in
  32. # verbose and/or learn mode. Depends on whether you want people
  33. # to learn RCS or not.
  34. &init;
  35. &main;
  36. sub init
  37. {
  38. $0 =~ s|.*/||;
  39. # Add commands here so that -w shuts up.
  40. $lint = 0;
  41. &clean() && &create() && &example() && &get() && &edit() &&
  42. &unedit() && &unget() && &diffs() && &delta() && &help() &&
  43. &prs() && &prt() && &deledit() && &delget() && &enter() &&
  44. &info() && &ci() && &co() && &fix() && &print()
  45. if $lint;
  46. }
  47. sub help
  48. {
  49. if ($#_ == -1) {
  50. &usage;
  51. }
  52. # Handle all the aliases.
  53. if ($_[0] eq "unedit" || $_[0] eq "unget") {
  54. &help("clean");
  55. } elsif ($_[0] eq "clean") {
  56. }
  57. warn "Extended help on @_ not available yet.\n";
  58. }
  59. sub usage
  60. {
  61. print <<EOF;
  62. usage: $0 [$0 opts] command [args] [file and/or directory list]
  63. $0 options are:
  64. -debug for debugging of $0 itself
  65. -verbose for more information about what $0 is doing
  66. More information may be had by saying "$0 help subcommand".
  67. Most commands take "-s" to mean do the work silently.
  68. Command Effect
  69. ------- ------
  70. clean - remove unedited (ro) working files
  71. -e remove unmodified edited (rw) & unedited (ro) files
  72. -f (force) remove modified working files as well
  73. create - add a set of files to RCS control and get (co) the working files
  74. -g do not do the get (co) of the working files
  75. -y<msg> use <msg> as the description message (aka -d<msg>)
  76. delta - check in a revision
  77. -y<msg> use <msg> as the log message (aka -d<msg>)
  78. -s
  79. diffs - diff the working file against the RCS file
  80. fix - redit the last revision
  81. get - get the working file[s] (possibly for editing)
  82. history - print history of the files
  83. print - print the history and the latest contents
  84. Alias Real command Effect
  85. ----- ------------ ------
  86. ci - delta check in a revision
  87. co - get check out a revision
  88. enter - create -g initialize a file without a get afterward
  89. unedit - clean -f remove working file even if modified
  90. unget - clean -f remove working file even if modified
  91. edit - get -e check out the file for editing
  92. prs - history print change log history
  93. prt - history print change log history
  94. An implied list of *,v and/or RCS/*,v is implied for most commands.
  95. The exceptions are commands that are potentially destructive, such as
  96. unedit.
  97. EOF
  98. exit 0;
  99. }
  100. sub main
  101. {
  102. local($cmd);
  103. local(@args);
  104. local(@comma_v);
  105. $cmd = "oops";
  106. $cmd = shift(@ARGV) if $#ARGV > -1;
  107. &help(@ARGV) if $cmd eq "help" || $cmd eq "oops";
  108. $dir_specified = $file_specified = 0;
  109. foreach $_ (@ARGV) {
  110. # If it is an option, just pass it through.
  111. if (/^-/) {
  112. push(@args, $_);
  113. }
  114. # If they specified an RCS directory, explode it into ,v files.
  115. elsif (-d $_) {
  116. $dir_specified = 1;
  117. warn "Exploding $_\n" if $debug;
  118. push(@args, grep(/,v$/, &filelist($_)));
  119. push(@args, grep(/,v$/, &filelist("$_/RCS")));
  120. }
  121. # If it is a file, make it be the ,v file.
  122. else {
  123. if (!/,v$/) {
  124. # XXX - what if both ./xxx,v and ./RCS/xxx,v?
  125. if (-f "$_,v") {
  126. $_ .= ",v";
  127. } else {
  128. if (m|/|) {
  129. m|(.*)/(.*)|;
  130. $f = "$1/RCS/$2,v";
  131. } else {
  132. $f = "RCS/$_,v";
  133. }
  134. if (-f $f) {
  135. $_ = $f;
  136. }
  137. }
  138. }
  139. if (-f $_) {
  140. $file_specified = 1;
  141. warn "Adding $_\n" if $debug;
  142. push(@args, $_);
  143. } else {
  144. warn "$0: skipping $_, no RCS file.\n";
  145. }
  146. }
  147. }
  148. # Figure out if it is a potentially destructive command. These
  149. # commands do not automagically expand *,v and RCS/*,v.
  150. $destructive = ($cmd eq "clean" && $args[0] eq "-f") ||
  151. $cmd eq "unedit" || $cmd eq "unget";
  152. # If they didn't specify a file or a directory, generate a list
  153. # of all ./*,v and ./RCS/*,v files.
  154. unless ($destructive || $dir_specified || $file_specified) {
  155. warn "Exploding . && ./RCS\n" if $debug;
  156. push(@args, grep(/,v$/, &filelist(".")));
  157. push(@args, grep(/,v$/, &filelist("RCS")));
  158. }
  159. unless ($cmd =~ /^create$/) {
  160. @comma_v = grep(/,v$/, @args);
  161. if ($#comma_v == -1) {
  162. ($s = "$cmd @ARGV") =~ s/\s+$//;
  163. die "$0 $s: No RCS files specified.\n";
  164. }
  165. }
  166. # Exit codes:
  167. # 0 - it worked
  168. # 1 - unspecified error
  169. # 2 - command unknown
  170. $exit = 2;
  171. warn "Trying &$cmd(@args)\n" if $debug;
  172. eval(&$cmd(@args));
  173. if ($exit == 2) {
  174. warn "Possible unknown/unimplemented command: $cmd\n";
  175. &usage;
  176. } else {
  177. exit $exit;
  178. }
  179. }
  180. # Read the directory and return a list of files.
  181. # XXX - isn't there a builtin that does this?
  182. sub filelist
  183. {
  184. local(@entries) = ();
  185. local($ent);
  186. opendir(DFD, $_[0]) || return ();
  187. foreach $ent (readdir(DFD)) {
  188. $ent = "$_[0]/$ent";
  189. next unless -f $ent;
  190. push(@entries, $ent);
  191. }
  192. warn "filelist($_[0]): @entries\n" if $debug;
  193. @entries;
  194. }
  195. # Take a list of ,v files and return a list of associated working files.
  196. sub working
  197. {
  198. local(@working, $working) = ();
  199. foreach $comma_v (@_) {
  200. # Strip the ,v.
  201. # Strip the RCS specification.
  202. ($working = $comma_v) =~ s|,v$||;
  203. $working =~ s|RCS/||;
  204. push(@working, $working);
  205. }
  206. @working;
  207. }
  208. # Same as "clean -f" - throw away all changes
  209. sub unedit { &clean("-f", @_); }
  210. sub unget { &clean("-f", @_); }
  211. # Get rid of everything that isn't edited and has an associated RCS file.
  212. # -e remove edited files that have not been changed.
  213. # -f remove files that are edited with changes (CAREFUL!)
  214. # This implies the -e opt.
  215. # -d<m> Check in files that have been modified. If no message, prompt
  216. # on each file. This implies -e.
  217. # -y<m> Like -d for people that are used to SCCS.
  218. # -m<m> Like -d for people that are used to RCS.
  219. #
  220. # Note: this does not use rcsclean; I don't know when that showed up. And
  221. # the 5.x release of RCS I have does not install it.
  222. sub clean
  223. {
  224. local(@working);
  225. local($e_opt, $f_opt, $d_opt, $s_opt) = (0,0,0,0);
  226. local($msg);
  227. local(@checkins) = ();
  228. while ($_[0] =~ /^-/) {
  229. if ($_[0] eq "-s") {
  230. $s_opt = 1;
  231. shift(@_);
  232. } elsif ($_[0] eq "-e") {
  233. $e_opt = 1;
  234. shift(@_);
  235. } elsif ($_[0] eq "-f") {
  236. $f_opt = $e_opt = 1;
  237. shift(@_);
  238. } elsif ($_[0] =~ /^-[dym]/) {
  239. $d_opt = $e_opt = 1;
  240. if ($_[0] =~ /^-[dym]$/) {
  241. $msg = $_[0];
  242. } else {
  243. ($msg = $_[0]) =~ s/-[ydm]//;
  244. $msg = "-m'" . $msg . "'";
  245. }
  246. shift(@_);
  247. } else {
  248. die "$0 clean: unknown option: $_[0]\n";
  249. }
  250. }
  251. @working = &working(@_);
  252. for ($i = 0; $i <= $#_; ++$i) {
  253. # No working file?
  254. if (!-f $working[$i]) {
  255. warn "No working file $working[$i] for $_[$i]\n"
  256. if $debug;
  257. next;
  258. }
  259. # Read only? Unlink.
  260. if (!-w $working[$i]) {
  261. warn "rm $working[$i]\n" unless $s_opt;
  262. # Make sure there is an RCS file
  263. if (-f $_[$i]) {
  264. # XXX - what if ro and edited?
  265. unlink($working[$i]) unless $n;
  266. } else {
  267. warn "clean: no RCS file for $working[$i]\n";
  268. }
  269. next;
  270. }
  271. # If they just want to know about it, tell them.
  272. if ($e_opt == 0) {
  273. open(RCS, $_[$i]);
  274. while ($r = <RCS>) {
  275. last if $r =~ /locks/;
  276. }
  277. @locks = ();
  278. while ($r = <RCS>) {
  279. # XXX - I use "comment" a delimiter.
  280. last if $r =~ /comment/;
  281. $r =~ s/^\s+//;
  282. chop($r);
  283. push(@locks, $r);
  284. }
  285. close(RCS);
  286. if ($#locks > -1) {
  287. warn "$working[$i]: being edited: @locks\n";
  288. } else {
  289. warn "$working[$i]: " .
  290. "writeable but not edited?!?\n";
  291. }
  292. next;
  293. }
  294. # See if there have actually been any changes.
  295. # Notice that this is cmp(1) in about 10 lines of perl!
  296. open(RCS, "co -q -p -kkvl $_[$i] |");
  297. open(WORK, $working[$i]);
  298. $diff = 0;
  299. while ($r = <RCS>) {
  300. unless (($w = <WORK>) && ($r eq $w)) {
  301. $diff = 1;
  302. last;
  303. }
  304. }
  305. if ($w = <WORK>) {
  306. $diff = 1;
  307. }
  308. close(RCS); close(WORK);
  309. if ($diff) {
  310. if ($f_opt) {
  311. warn "Clean modified $working[$i]\n"
  312. unless $s_opt;
  313. unless ($n) {
  314. unlink($working[$i]);
  315. system "rcs -q -u $_[$i]";
  316. }
  317. } elsif ($d_opt) {
  318. push(@checkins, $_[$i]);
  319. } else {
  320. warn "Can't clean modified $working[$i]\n";
  321. }
  322. next;
  323. } else {
  324. warn "rm $working[$i]\n" unless $s_opt;
  325. unless ($n) {
  326. unlink($working[$i]);
  327. system "rcs -q -u $_[$i]";
  328. }
  329. }
  330. }
  331. # Handle files that needed deltas.
  332. if ($#checkins > -1) {
  333. warn "ci -q $msg @checkins\n" if $verbose;
  334. system "ci -q $msg @checkins";
  335. }
  336. $exit = 0;
  337. }
  338. # Create - initialize the RCS file
  339. # -y<c> - use <c> as the description message for all files.
  340. # -d<c> - use <c> as the description message for all files.
  341. # -g - don't do the get
  342. #
  343. # Differs from sccs in that it does not preserve the original
  344. # files (I never found that very useful).
  345. sub create
  346. {
  347. local($arg, $noget, $description, $cmd) = ("", "", "");
  348. foreach $arg (@_) {
  349. # Options...
  350. if ($arg =~ /^-[yd]/) {
  351. ($description = $arg) =~ s/^-[yd]//;
  352. $arg = "";
  353. warn "Desc: $description\n" if $debug;
  354. next;
  355. }
  356. if ($arg eq "-g") {
  357. $noget = "yes";
  358. $arg = "";
  359. next;
  360. }
  361. next if ($arg =~ /^-/);
  362. # If no RCS subdir, make one.
  363. if ($arg =~ m|/|) { # full path
  364. ($dir = $arg) =~ s|/[^/]+$||;
  365. mkdir("$dir/RCS", 0775);
  366. } else { # in $CWD
  367. mkdir("RCS", 0775);
  368. }
  369. }
  370. $exit = 0;
  371. if ($description ne "") {
  372. $cmd = "ci -t-'$description' @_";
  373. } else {
  374. $cmd = "ci @_";
  375. }
  376. warn "$cmd\n" if $verbose;
  377. system "$cmd";
  378. system "co @_" unless $noget;
  379. }
  380. # Like create without the get.
  381. sub enter { &create("-g", @_); }
  382. # Edit - get the working file editable
  383. sub edit { &get("-e", @_); }
  384. # co - normal RCS
  385. sub co { &get(@_); }
  386. # Get - get the working file
  387. # -e Retrieve a version for editing.
  388. # Same as co -l.
  389. # -p Print the file to stdout.
  390. # -k Suppress expansion of ID keywords.
  391. # Like co -kk.
  392. # -s Suppress all output.
  393. #
  394. # Note that all other options are passed to co(1).
  395. sub get
  396. {
  397. local($arg, $working, $f, $p);
  398. $f = $p = 0;
  399. foreach $arg (@_) {
  400. # Options...
  401. $arg = "-l" if ($arg eq "-e");
  402. $arg = "-kk" if ($arg eq "-k");
  403. $arg = "-q" if ($arg eq "-s");
  404. $f = 1 if ($arg eq "-f");
  405. $p = 1 if ($arg eq "-p"); # XXX - what if -sp?
  406. next if $arg =~ /^-/ || $p;
  407. # Check for writable files and skip them unless someone asked
  408. # for co's -f option.
  409. ($working = $arg) =~ s|,v$||;
  410. $working =~ s|RCS/||;
  411. if ((-w $working) && $f == 0) {
  412. warn "ERROR [$arg]: writable `$working' exists.\n";
  413. $arg = "";
  414. }
  415. }
  416. @files = grep(/,v/, @_);
  417. if ($#files == -1) {
  418. warn "$0 $cmd: no files to get. @_\n";
  419. $exit = 1;
  420. } else {
  421. system "co @_";
  422. $exit = 0;
  423. }
  424. }
  425. # Aliases for history.
  426. sub prt { &history(@_); }
  427. sub prs { &history(@_); }
  428. # History - change history sub command
  429. sub history
  430. {
  431. local(@history);
  432. open(RL, "rlog @_|");
  433. # Read the whole history
  434. while ($r = <RL>) {
  435. # Read the history for one file.
  436. if ($r !~ /^[=]+$/) {
  437. push(@history, $r);
  438. next;
  439. }
  440. &print_history(@history);
  441. @history = ();
  442. }
  443. close(RL);
  444. print "+-----------------------------------\n";
  445. $exit = 0;
  446. }
  447. sub print_history
  448. {
  449. for ($i = 0; $i <= $#_; ++$i) {
  450. # Get the one time stuff
  451. if ($_[$i] =~ /^RCS file:/) {
  452. $_[$i] =~ s/RCS file:\s*//;
  453. chop($_[$i]);
  454. print "+------ $_[$i] -------\n|\n";
  455. }
  456. # Get the history
  457. if ($_[$i] =~ /^----------------------------/) {
  458. local($rev, $date, $author, $lines) = ("", "", "", "");
  459. $i++;
  460. die "Bad format\n" unless $_[$i] =~ /revision/;
  461. $_[$i] =~ s/revision\s+//;
  462. chop($_[$i]);
  463. $rev = $_[$i];
  464. $i++;
  465. die "Bad format\n" unless $_[$i] =~ /date/;
  466. @parts = split(/[\s\n;]+/, $_[$i]);
  467. for ($j = 0; $j <= $#parts; $j++) {
  468. if ($parts[$j] =~ /date/) {
  469. $j++;
  470. $date = "$parts[$j] ";
  471. $j++;
  472. $date .= "$parts[$j]";
  473. }
  474. if ($parts[$j] =~ /author/) {
  475. $j++;
  476. $author = $parts[$j];
  477. }
  478. if ($parts[$j] =~ /lines/) {
  479. $j++;
  480. $lines = "$parts[$j] ";
  481. $j++;
  482. $lines .= "$parts[$j]";
  483. }
  484. }
  485. print "| $rev $date $author $lines\n";
  486. while ($_[++$i] &&
  487. $_[$i] !~ /^----------------------------/) {
  488. print "| $_[$i]"; ### unless $rev =~ /^1\.1$/;
  489. }
  490. print "|\n";
  491. $i--;
  492. }
  493. }
  494. }
  495. # Show changes between working file and RCS file
  496. #
  497. # -C -> -c for compat with sccs (not sure if this is needed...).
  498. sub diffs
  499. {
  500. local(@working);
  501. local($diff) = "diff";
  502. local($rev) = "";
  503. while ($_[0] =~ /^-/) {
  504. if ($_[0] eq "-C") {
  505. $diff .= " -c";
  506. shift(@_);
  507. } elsif ($_[0] =~ /^-r/) {
  508. $rev = $_[0];
  509. shift(@_);
  510. } elsif ($_[0] eq "-sdiff") {
  511. # XXX - screen size
  512. $diff = "sdiff -w80";
  513. shift(@_);
  514. } else {
  515. $diff .= " $_[0]";
  516. shift(@_);
  517. }
  518. }
  519. @working = &working(@_);
  520. for ($i = 0; $i <= $#_; ++$i) {
  521. # No working file?
  522. if (!-f $working[$i]) {
  523. warn "No working file $working[$i] for $_[$i]\n"
  524. if $debug;
  525. next;
  526. }
  527. # Read only? Skip.
  528. next unless (-w $working[$i]);
  529. # Show the changes
  530. print "\n------ $working[$i]$rev ------\n";
  531. fflush(stdout);
  532. # XXX - flush stdout.
  533. if ($diff =~ /^sdiff/) {
  534. system "co -q -p -kkvl $rev $_[$i] > /tmp/sdiff.$$" .
  535. "&& $diff /tmp/sdiff.$$ $working[$i]";
  536. # XXX - interrupts?
  537. unlink("/tmp/sdiff.$$");
  538. } else {
  539. system "co -q -p -kkvl $rev $_[$i] |" .
  540. " $diff - $working[$i]";
  541. }
  542. }
  543. $exit = 0;
  544. }
  545. # delta - check in the files
  546. sub delta
  547. {
  548. local($description) = ("");
  549. local($i, @working);
  550. @working = &working(@_);
  551. for ($i = 0; $i <= $#_; ++$i) {
  552. # Options...
  553. if ($_[$i] =~ /^-[yd]/) {
  554. ($description = $_[$i]) =~ s/^-[yd]/-m/;
  555. $description = "'" . $description . "'";
  556. $_[$i] = "";
  557. next;
  558. }
  559. $_[$i] = "-q" if $_[$i] eq "-s";
  560. $_[$i] = "" unless -f $working[$i];
  561. }
  562. $exit = 0;
  563. warn "ci $description @_\n" if $verbose;
  564. system "ci $description @_";
  565. }
  566. # Allow RCS interface ci
  567. sub ci
  568. {
  569. &delta(@_);
  570. }
  571. # delget
  572. sub delget
  573. {
  574. &delta(@_);
  575. &get(@_); # If there was a description, delta nuked it...
  576. }
  577. # deledit
  578. sub deledit
  579. {
  580. &delta(@_);
  581. &get("-e", @_); # If there was a description, delta nuked it...
  582. }
  583. # info - who is editing what
  584. sub info
  585. {
  586. local(@working);
  587. @working = &working(@_);
  588. for ($i = 0; $i <= $#_; $i++) {
  589. open(RCS, $_[$i]);
  590. while ($r = <RCS>) {
  591. last if $r =~ /locks/;
  592. }
  593. @locks = ();
  594. while ($r = <RCS>) {
  595. # XXX - I use "comment" a delimter.
  596. last if $r =~ /comment/;
  597. $r =~ s/^\s+//;
  598. chop($r);
  599. push(@locks, $r);
  600. }
  601. close(RCS);
  602. if ($#locks > -1) {
  603. warn "$working[$i]: being edited: @locks\n";
  604. }
  605. }
  606. $exit = 0;
  607. }
  608. # Fix - fix the last change to a file
  609. sub fix
  610. {
  611. foreach $f (@_) {
  612. next unless -f $f;
  613. open(F, $f); while (<F>) { last if /head\s\d/; } close(F);
  614. unless ($_ && /head/) {
  615. warn "$0 $cmd: No head node found in $f\n";
  616. next;
  617. }
  618. s/head\s+//; chop; chop; $rev = $_;
  619. ($working = $f) =~ s/,v//;
  620. $working =~ s|RCS/||;
  621. system "co -q $f && rcs -o$rev $f && rcs -l $f && chmod +w $working";
  622. }
  623. $exit = 0;
  624. }
  625. # print - print the history and the latest revision of the file
  626. sub print
  627. {
  628. local($file);
  629. foreach $file (@_) {
  630. &history($file);
  631. &get("-s", "-p", $file);
  632. }
  633. $exit = 0;
  634. }
  635. # Example - example sub command
  636. # -Q change this option to -q just to show how.
  637. sub example
  638. {
  639. local($arg, $working);
  640. foreach $arg (@_) {
  641. # Options...
  642. $arg = "-Q" if ($arg eq "-q");
  643. }
  644. warn "rlog @_\n" if $verbose;
  645. system "rlog @_";
  646. $exit = 0;
  647. }
  648. RCS bghtml html-list man2html