From: Eric S. Raymond Date: Fri, 13 Jul 2007 11:51:55 +0000 (+0000) Subject: Python translation isn't finished, but it's far enough along to check in. X-Git-Tag: 1.4~12 X-Git-Url: http://thyrsus.com/gitweb/?a=commitdiff_plain;h=b5db3fd0741e8520e4b4f7f33762b97efb3aa5fe;p=sccs2rcs.git Python translation isn't finished, but it's far enough along to check in. --- diff --git a/sccs2rcs b/sccs2rcs index fd8a09c..adb60a0 100755 --- a/sccs2rcs +++ b/sccs2rcs @@ -1,19 +1,37 @@ -#!/bin/csh -f +#!/usr/bin/env python # -# Sccs2rcs is a script to convert an existing SCCS -# history into an RCS history without losing any of -# the information contained therein. +# sccs2rcs is a script to convert an existing SCCS history into an RCS +# history without losing any of the information contained therein. # -# Things to note: -# + The -b (batch) option can be used to tell this script to run -# non-interactively. In this mode, it assumes that you do not -# want to enter descriptions on initial checkins, nor change -# the default keyword substitution +# This tool has a complicated history for its size. It began life as a +# script in C-shell written by a hacker named Ken Cox in 1991. +# +# In 1993 I applied some minor fixes for newer RCS versions and it +# shipped as part of one of Rich Morin's Prime Time Freeware releases. +# +# At some undetermined time after 1993, Brian Berliner hacked Ken +# Cox's original in a slightly different direction than mine, adding +# more SCCS keyword translations and an improvement in dealing with +# branches. +# +# In 2002 I fixed it not to require the Berkeley sccs(1) front end. +# In 2003 I folded in a minor bug fix from Roger Pilkey. +# In 2007 I merged in the missing features from Brian Berliner's version +# and sent it to the GNU RCS project hoping they'd take it off my hands. +# +# Then it started to bother me that I was casting the thing loose in a +# dying language, so I decided to port it to Python and clean it up. +# I added error checking, added the comments-file processing, added +# auto-checkout of RCS workfiles replacing SCCS ones, and removed +# no-longer-needed options. Finally I made it able to parse tag data from +# snapshots made by Emacs VC mode. # -# + The -u option will cause a copy of the file to be checked out -# after the conversion, so if you started with a workfile in place, -# you'll end up with one there. +# Ken said: +# "This file may be copied, processed, hacked, mutilated, and +# even destroyed as long as you don't tell anyone you wrote it." +# Those were more innocent days. This version is released under MIT terms. # +# Things to note: # + It will NOT delete or alter your ./SCCS history under any circumstances. # # + Run in a directory where ./SCCS exists and where you can create ./RCS @@ -32,16 +50,6 @@ # the date of a delta to be in sync, then run "sccs admin -z" # on the thing. # -# + After everything finishes, ./SCCS will be moved to ./old-SCCS. -# -# This file may be copied, processed, hacked, mutilated, and -# even destroyed as long as you don't tell anyone you wrote it. -# -# Ken Cox -# Viewlogic Systems, Inc. -# kenstir@viewlogic.com -# ...!harvard!cg-atla!viewlog!kenstir -# # 1.1: I added some portability fixes and the -u option. More importantly, # I removed the dependencies on the BSD/SV `sccs' utility; this script # will now work on any system that has the base SCCS/RCS tools available. @@ -54,267 +62,237 @@ # of this script, the one distributed with CVS. That and 4 SCCS # keyword substitutions were we only thing it did that this didn't. # +# 1.4: Python rewrite describe aboved. Interface cleanup. Auto-checkout +# of RCS workfiles corresponding to SCCS workfiles present when started. +# # Eric S. Raymond # # $Id$ +import os, sys, getopt, re, commands -# Note: newer versions of RCS require century digits on the -d option -# of ci. Losing SCCS doesn't supply them. Kluge around this. -# -set century = 19 - -############################################################ -# Option handling -# -set nodesc = 0 -set nohack = 0 -set regen = 0 -while ($#argv) +# Here are the translations for SCCS % keywords to RCS-style $ keywords. +# The quotes surround the dollar signs are to fool RCS and later version- +# control systems when the script is checked in. In the unlikely event +# that you need more keyword translations, add them to the end of this list. +# The ones this misses are %[ABCDFHLMPQRSTYZ]%; the script will issue +# warnings if it sees them after translation. +conversions = [ + ('%W%[ \t]*%G%', "$""Id""$"), + ('%W%[ \t]*%E%', "$""Id""$"), + ('%W%', "$""Id""$"), + ('%Z%%M%[ \t]*%I%[ \t]*%G%', "$""SunId""$"), + ('%Z%%M%[ \t]*%I%[ \t]*%E%', "$""SunId""$"), + ('%M%[ \t]*%I%[ \t]*%G%', "$""Id""$"), + ('%M%[ \t]*%I%[ \t]*%E%', "$""Id""$"), + ('%M%', "$""RCSfile""$"), + ('%I%', "$""Revision""$"), + ('%G%', "$""Date""$"), + ('%E%', "$""Date""$"), + ('%U%', ""), + ] +conversions = map(lambda (s, t): (re.compile(s), t), conversions) - if ($argv[1] == "-b") then - set nodesc = 1 - set nohack = 1 - endif +months = ("Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec") - if ($argv[1] == "-u") then - set regen = 1 - endif +def writeable(f): + return os.access(f, os.W_OK) - shift -end +def sccsfile(f): + return os.path.join("SCCS", "s." + f) -############################################################ -# Error checking -# -if (! -w .) then - echo "Error: ./ not writeable by you." - exit 1 -endif -if (! -d SCCS) then - echo "Error: ./SCCS directory not found." - exit 1 -endif -foreach f (*) - if (-f SCCS/s.$f && -w $f) then - echo "Error: $f is out for edit...clean up before converting." - exit 1 - endif -end -if (-d RCS) then - echo "Warning: RCS directory exists" - if (`ls -a RCS | wc -l` > 2) then - echo "Error: RCS directory not empty" - exit 1 - endif -else - mkdir RCS -endif +def progress(msg): + global logtext + logtext += "sccs2rcs: " + msg + "\n" + print msg -set months = (Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) +def get_output(command): + global logtext + logtext += "$ " + command + (st, out) = commands.getstatusoutput(command) + logtext += out + "\n" + return out + "\n" -# Remove all files for which there exist SCCS masters -foreach f (*) - if (-f SCCS/s.$f) then - rm $f - endif -end +def die(msg): + print >>sys.stderr, "sccs2rcs: fatal error, ", msg + for filename in os.listdir("."): + if os.path.exists(sccsfile(filename)) and writeable(filename): + get_output("unget " + sccsfile(filename)) + print >>sys.stderr, """\ +Danger! Danger! +Incomplete history in ./RCS -- remove it. +Original unchanged history in ./SCCS. +""" + if logtext: + print "Log follows:" + print logtext + print "Log ends here." + sys.exit(1) -set logfile = /tmp/sccs2rcs_$$_log -rm -f $logfile -set tmpfile = /tmp/sccs2rcs_$$_tmp -rm -f $tmpfile -set initialfile = /tmp/sccs2rcs_$$_init -echo "Initial revision" > $initialfile -set sedfile = /tmp/sccs2rcs_$$_sed -rm -f $sedfile +def warn(msg): + print >>sys.stderr, "sccs2rcs: warning,", msg -# the quotes surround the dollar signs are to fool RCS when I check in this script -set sccs_keywords = (\ - '%W%[ ]*%G%'\ - '%W%[ ]*%E%'\ - '%W%'\ - '%Z%%M%[ ]*%I%[ ]*%G%'\ - '%Z%%M%[ ]*%I%[ ]*%E%'\ - '%M%[ ]*%I%[ ]*%G%'\ - '%M%[ ]*%I%[ ]*%E%'\ - '%M%'\ - '%I%'\ - '%G%'\ - '%E%'\ - '%U%') -set rcs_keywords = (\ - '$'Id'$'\ - '$'Id'$'\ - '$'Id'$'\ - '$'SunId'$'\ - '$'SunId'$'\ - '$'Id'$'\ - '$'Id'$'\ - '$'RCSfile'$'\ - '$'Revision'$'\ - '$'Date'$'\ - '$'Date'$'\ - '') +def get_output_or_die(command): + global logtext + logtext += "$ " + command + (st, out) = commands.getstatusoutput(command) + if st != 0: + die("'%s' failed" % command) + logtext += out + "\n" + return out + "\n" +if __name__ == '__main__': + comments = None + (options, arguments) = getopt.getopt(sys.argv[1:], "c:") + for (switch, val) in options: + if switch == '-c': + comments = val -############################################################ -# Get some answers from user -# -if ($nodesc == 0) then - echo "" - echo "Do you want to be prompted for a description of each" - echo "file as it is checked in to RCS initially?" - echo -n "(y = prompt for description, n = null description) [y]? " - set ans = $< - if ((_$ans == _n) || (_$ans == _N)) then - set nodesc = 1 - endif -endif + logtext = "" -if ($nohack == 0) then - echo "" - echo "The default keyword substitutions are as follows and are" - echo "applied in the order specified:" - set i = 1 - while ($i <= $#sccs_keywords) -# echo ' '\"$sccs_keywords[$i]\"' ==> '\"$rcs_keywords[$i]\" - echo " $sccs_keywords[$i] ==> $rcs_keywords[$i]" - @ i = $i + 1 - end - echo "" - echo -n "Do you want to change them [n]? " - set ans = $< - if ((_$ans != _) && (_$ans != _n) && (_$ans != _N)) then - echo "You can't always get what you want." - echo "Edit this script file and change the variables:" - echo ' $sccs_keywords' - echo ' $rcs_keywords' - else - echo "Good idea." - endif -endif + # Sanity checks on the directory + if not writeable("."): + die("./ is not writeable by you.") + elif not os.path.isdir("SCCS"): + die("no SCCS directory to convert.") + for filename in os.listdir("SCCS"): + if filename.startswith("s.") and writeable(filename): + die(filename + " is locked for edit...unlock before converting.") + if os.path.isdir("RCS"): + warn("RCS directory already exists.") + #if len(os.listdir("RCS")) > 2: # Allow . and .. + # die("RCS directory is nonempty.") + else: + try: + os.mkdir("RCS") + except OSError: + die("failed while attempting to create RCS directory.") -# create the sed script -set i = 1 -while ($i <= $#sccs_keywords) - echo "s,$sccs_keywords[$i],$rcs_keywords[$i],g" >> $sedfile - @ i = $i + 1 -end + # User may have supplied a comments file + initstrings = {} + if comments: + for line in open(comments): + try: + line = line.strip() + if not line: # ignore blank lines + continue + (filename, initstring) = line.split(":") + except ValueError: + die("ill-formed line '%s' in comments file" % line) + if filename in initstrings: + die("duplicate filename in initstrings") + initstrings[filename] = initstrings -mkdir old-SCCS + # If the CSSC package is installed, the SCCS commands might be here. + # This assumption works under Debian/Ubuntu and will be harmless elsewhere. + os.environ["PATH"] += ":/usr/lib/cssc" -############################################################ -# Loop over every s-file in SCCS dir -# -foreach sfile (SCCS/s.*) - # get rid of the "s." at the beginning of the name - set file = `echo $sfile:t | sed -e "s/^..//"` + # Loop over every s-file in SCCS dir + for sfile in filter(lambda f: f.startswith("s."), os.listdir("SCCS")): + file = sfile[2:] + firsttime = True - # work on each rev of that file in ascending order - set firsttime = 1 - foreach rev (`prs SCCS/s.$file | grep "^D " | awk '{print $2}' | tac`) - if ($status != 0) goto ERROR + # Extract a list of revisions from the master + revisions = [] + try: + for line in os.popen("prs " + sccsfile(file), "r"): + if line.startswith("D "): + revisions.append(line.split()[1]) + except OSError: + die("open of master file %s failed" % sfile) + except IndexError: + die("garbled revision line in master file " + sfile) + revisions.reverse() - # get file into current dir and get stats - set date = (`prs -d":Dd: :Dm: :Dy: :T: :P:" -r$rev SCCS/s.$file`) - set day = $date[1] - set month = $date[2] - if ($date[3] < 10) then - set next_century = (`expr $century + 1`) - set year = ${next_century}${date[3]} - else - set year = ${century}${date[3]} - endif - set time = $date[4] - set author = $date[5] - set date = "$day $months[$month] $year $time" + # Now use prs to run through the revision list extracting data + progress("%s revisions: %s\n" % (file, ", ".join(revisions))) + for rev in revisions: + cmd = 'prs -d":Dd: :Dm: :Dy: :T: :P:" -r%s SCCS/s.%s' % (rev, file) + date = get_output_or_die(cmd) + try: + (day, month, year, tod, author) = date.split() + except ValueError: + die("prs output %s from '%s'was ill-formed" % (`date`, cmd)) - echo "" - echo "==> file $file, rev=$rev, date=$date, author=$author" - get -e -r$rev SCCS/s.$file >>& $logfile - if ($status != 0) goto ERROR - echo checked out of SCCS + # We assume here that no SCCS archives were made before 1970. + # If you're converting an SCCS archive made after 2070, you lose. + if int(year) < 70: + year = "20" + year + else: + year = "19" + year - # add RCS keywords in place of SCCS keywords - sed -f $sedfile $file > $tmpfile - if ($status != 0) goto ERROR - echo performed keyword substitutions - cp $tmpfile $file + rcsdate = day + " " + months[int(month)] + " " + year + " " + tod + progress("==> file %s, rev=%s, date=%s, author=%s" \ + % (file, rev, rcsdate, author)) + get_output_or_die("get -e -r%s SCCS/s.%s" % (rev, file)) + print "checked out of SCCS" - # check file into RCS - if ($firsttime) then - set firsttime = 0 - if ($nodesc == 1) then - ci -f -r$rev -d"$date" -w$author -t/dev/null $file <$initialfile >>& $logfile - if ($status != 0) goto ERROR - echo initial rev checked into RCS without description - else - echo "" - echo Enter a brief description of the file $file \(end w/ Ctrl-D\): - cat > $tmpfile - ci -f -r$rev -d"$date" -w$author -t$tmpfile $file <$initialfile >>& $logfile - if ($status != 0) goto ERROR - echo initial rev checked into RCS - endif - else - # get RCS lock - set lckrev = `echo $rev | sed -e 's/\.[0-9]*$//'` - if ("$lckrev" =~ [0-9]*.*) then - # need to lock the brach -- it is OK if the lock fails - rcs -l$lckrev $file >>& $logfile - else - # need to lock the trunk -- must succeed - rcs -l $file >>& $logfile - if ($status != 0) goto ERROR - endif - echo got lock - prs -r$rev SCCS/s.$file | grep "." > $tmpfile - # it's OK if grep fails here and gives status == 1 - # put the delta message in $tmpfile - ed $tmpfile >>& $logfile <>& $logfile - if ($status != 0) goto ERROR - echo checked into RCS - endif - unget SCCS/s.$file >>& $logfile - if ($status != 0) goto ERROR - end - rm -f $file - mv $sfile old-SCCS - if ($regen == 1) then - co $file - endif -end + # replace SCCS keywords with RCS keywords + try: + rfp = open(file, "r") + filetext = rfp.read() + rfp.close() + for (regexp, rcskey) in conversions: + filetext = regexp.sub(rcskey, filetext) + wfp = open(file, "w") + wfp.write(filetext) + wfp.close() + except OSError: + die("keyword substitution failed") + progress("performed keyword substitutions") + # check file into RCS + if firsttime: + firsttime = False + if file in initstrings: + cmd = 'ci -f -r%s -d"%s" -w%s -t-%s %s' \ + % (rev, date, author, initstrings[file], file) + else: + if initstrings: + warn("no description for %s in comments file" % file) + cmd = 'ci -f -r%s -d"%s" -w%s -t/dev/null %s' \ + % (rev, date, author, file) + cmd = "echo 'Initial revision.' | " + cmd + get_output_or_die(cmd) + else: + # get RCS lock + revparts = rev.split(".") + lckrev = ".".join(revparts[:-1]) + if re.match(r"[0-9]+\."): + # need to lock the branch -- it is OK if the lock fails + get_output("rcs -l%s %s" % (lckrev, file)) + progress("got branch lock") + else: + # need to lock the trunk -- must succeed + get_output_or_die("rcs -l %s" % (file,)) + progress("got trunk lock") + # Extract the delta message for this revision + deltamsg = get_output_or_die("prs -r$rev SCCS/s.$file") + deltalines = deltamsg.split("\n") + while deltalines: + deltalines.pop(0) + if deltalines[0].startswith("COMMENTS"): + break + deltamsg = deltalines.join("\n") + cmd = 'ci -f -r%s -d"%s" -w%s %s' % (rev, date, author, file) + # FIXME: output of this command isn't logged + progress("$ " + cmd) + try: + cfp = os.popen(cmd, "w") + cfp.write(deltamsg) + cfp.close() + except OSError: + die("RCS checkin of delta failed") + # We're done + logtext += get_output_or_die("unget " + sccsfile(file)) -############################################################ -# Clean up -# -echo cleaning up... -rm -f $tmpfile $initialfile $sedfile -echo =================================================== -echo "Conversion of `pwd` OK" -echo =================================================== -exit 0 + # Replace all files for which there exist SCCS masters, + progress("replacing workfiles") + for filename in os.listdir("."): + if os.exists(sccsfile(filename)): + os.remove(filename) + os.system("co " + file) + print progress("done.") -ERROR: -foreach f (*) - if (-f SCCS/s.$f && -w $f) then - unget SCCS/s.$f - endif -end -echo "" -echo "" -echo Danger\! Danger\! -echo Some command exited with a non-zero exit status. -echo Log file exists in $logfile. -echo "" -echo Incomplete history in ./RCS -- remove it -echo Original unchanged history in ./SCCS -exit 1 +# sccs2rcs ends here