[PATCH] plan9: initial support for plan 9 from bell labs

Steven Stallion sstallion at gmail.com
Fri Apr 6 16:42:45 CDT 2012


# HG changeset patch
# User Steven Stallion <sstallion at gmail.com>
# Date 1333748387 25200
# Node ID 70fcfb0bf1f677bd6c0f47dd60e498fbf65123db
# Parent  329887a7074c8e49e73fa76712d8d45aee0d0fd7
plan9: initial support for plan 9 from bell labs

This patch contains support for Plan 9 from Bell Labs. A README is
provided in contrib/plan9 which describes the port in greater detail.
A new extension is also provided named factotum which permits the
factotum(4) authentication agent to provide credentials for HTTP
repositories. This extension is also applicable to other POSIX
platforms which make use of Plan 9 from User Space (aka plan9ports).

diff -r 329887a7074c -r 70fcfb0bf1f6 contrib/plan9/README
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/plan9/README	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,46 @@
+Mercurial for Plan 9 from Bell Labs
+===================================
+
+This directory contains support for Mercurial on Plan 9 from Bell Labs
+platforms. It is assumed that the version of Python running on these
+systems supports the ANSI/POSIX Environment (APE). At the time of this
+writing, the bichued/python port is the most commonly installed version
+of Python on these platforms. If a native port of Python is ever made,
+some minor modification will need to be made to support some of the more
+esoteric requirements of the platform rather than those currently made
+(cf. posix.py).
+
+By default, installations will have the factotum extension enabled; this
+extension permits factotum(4) to act as an authentication agent for
+HTTP repositories. Additionally, an extdiff command named 9diff is
+enabled which generates diff(1) compatible output suitable for use with
+the plumber(4).
+
+Commit messages are plumbed using E if no editor is defined; users must
+update the plumbed file to continue, otherwise the hg process must be
+interrupted.
+
+idiff(1) may be used to resolve merge conflicts, however it is not
+enabled by default. Users should use the following configuration snippet
+to enable this behavior:
+
+  [ui]
+  merge = idiff
+
+Some work remains with regard to documentation. Section 5 manual page
+references for hgignore and hgrc need to be re-numbered to section 6 (file
+formats) and a new man page writer should be written to support the
+Plan 9 man macro set. Until these issues can be resolved, manual pages
+are elided from the installation.
+
+Basic install:
+
+  % mk install      # do a system-wide install
+  % hg debuginstall # sanity-check setup
+  % hg              # see help
+
+A proto(2) file is included in this directory as an example of how a
+binary distribution could be packaged, ostensibly with contrib(1).
+
+See http://mercurial.selenic.com/ for detailed installation
+instructions, platform-specific notes, and Mercurial user information.
diff -r 329887a7074c -r 70fcfb0bf1f6 contrib/plan9/hgdiff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/plan9/hgdiff	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,42 @@
+#!/bin/rc
+
+rfork e
+
+fn getfiles{
+	cd $1
+	for(f in `{du -as | awk '{print $2}'})
+		test -f $f && echo `{cleanname $f}
+}
+
+fn usage{
+	echo >[1=2] usage: hgdiff [diff options] parent child root
+	exit usage
+}
+
+opts=()
+while(~ $1 -*){
+	opts=($opts $1)
+	shift
+}
+if(! ~ $#* 3)
+	usage
+
+# extdiff will set the parent and child to a single file if
+# there is only one change.  If there are multiple changes,
+# directories will be set.  diff(1) does not cope particularly
+# with directories; instead we do the recursion ourselves and
+# diff each file individually.
+if(test -f $1)
+	diff $opts $1 $2
+if not{
+	# extdiff will create a snapshot of the working copy to
+	# prevent conflicts during the diff.  We circumvent this
+	# behavior by diffing against the repository root to
+	# produce plumbable output.  This is antisocial.
+	for(f in `{sort -u <{getfiles $1} <{getfiles $2}}){
+		file1=$1/$f; test -f $file1 || file1=/dev/null
+		file2=$3/$f; test -f $file2 || file2=/dev/null
+		diff $opts $file1 $file2
+	}
+}
+exit ''
diff -r 329887a7074c -r 70fcfb0bf1f6 contrib/plan9/hgrc.d/9diff.rc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/plan9/hgrc.d/9diff.rc	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,5 @@
+[extensions]
+extdiff =
+
+[extdiff]
+9diff = hgdiff -cm $parent $child $root
diff -r 329887a7074c -r 70fcfb0bf1f6 contrib/plan9/hgrc.d/factotum.rc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/plan9/hgrc.d/factotum.rc	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,2 @@
+[extensions]
+factotum =
diff -r 329887a7074c -r 70fcfb0bf1f6 contrib/plan9/hgrc.d/idiff.rc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/plan9/hgrc.d/idiff.rc	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,4 @@
+[merge-tools]
+idiff.executable = /bin/idiff
+idiff.args = $local $other > $output
+idiff.premerge = True
diff -r 329887a7074c -r 70fcfb0bf1f6 contrib/plan9/mkfile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/plan9/mkfile	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,37 @@
+APE=/sys/src/ape
+<$APE/config
+
+PYTHON=python
+PYTHONBIN=/rc/bin
+SH=ape/psh
+
+PURE=--pure
+ROOT=../..
+
+# This is slightly underhanded; Plan 9 does not support GNU gettext nor
+# does it support dynamically loaded extension modules. We work around
+# this by calling build_py and build_scripts directly; this avoids
+# additional platform hacks in setup.py.
+build:VQ:
+	@{
+		cd $ROOT
+		$SH -c '$PYTHON setup.py $PURE build_py build_scripts'
+	}
+
+clean:VQ:
+	@{
+		cd $ROOT
+		$SH -c '$PYTHON setup.py $PURE clean --all'
+	}
+
+install:VQ:	build
+	@{
+		cd $ROOT
+		$SH -c '$PYTHON setup.py $PURE install \
+			--install-scripts $PYTHONBIN \
+			--skip-build' \
+			--force
+	}
+	mkdir -p /lib/mercurial/hgrc.d
+	dircp hgrc.d /lib/mercurial/hgrc.d
+	cp hgdiff /rc/bin
diff -r 329887a7074c -r 70fcfb0bf1f6 contrib/plan9/proto
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/plan9/proto	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,21 @@
+lib	- sys sys
+	mercurial	- sys sys
+		hgrc.d	- sys sys
+			9diff.rc	- sys sys
+			factotum.rc	- sys sys
+			idiff.rc	- sys sys
+rc	- sys sys
+	bin	- sys sys
+		hg	- sys sys
+		hgdiff	- sys sys
+sys	- sys sys
+	lib	- sys sys
+		python	- sys sys
+			lib	- sys sys
+				python2.5	- sys sys
+					site-packages	- sys sys
+						hgext	- sys sys
+							+	- sys sys
+						mercurial	- sys sys
+							+	- sys sys
+						mercurial-2.1.1-py2.5.egg-info	- sys sys
diff -r 329887a7074c -r 70fcfb0bf1f6 hgext/factotum.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/factotum.py	Fri Apr 06 14:39:47 2012 -0700
@@ -0,0 +1,119 @@
+# factotum.py - Plan 9 factotum integration for Mercurial
+#
+# Copyright (C) 2012 Steven Stallion <sstallion at gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+'''http authentication with factotum
+
+This extension allows the factotum facility on Plan 9 from Bell Labs platforms
+to provide authentication information for HTTP access. Configuration entries
+specified in the auth section as well as authentication information provided
+in the repository URL are fully supported.  If no prefix is specified, a value
+of ``*`` will be assumed.
+
+By default, keys are specified as::
+
+  proto=pass service=hg prefix=<prefix> user=<username> !password=<password>
+
+If the factotum extension is unable to read the required key, one will be
+requested interactively.
+
+A configuration section is available to customize runtime behavior. By
+default, these entries are::
+
+  [factotum]
+  mount = /mnt/factotum
+  path = /bin/auth/factotum
+  service = hg
+
+The mount entry defines the mount point for the factotum file service.  The
+path entry defines the full path to the factotum binary.  Lastly, the service
+entry controls the service name used when reading keys.
+
+'''
+
+from mercurial.i18n import _
+from mercurial.url import passwordmgr
+from mercurial import httpconnection, urllib2, util
+import os
+
+ERRMAX = 128
+
+def auth_getkey(self, params):
+    if not self.ui.interactive():
+        raise util.Abort(_('factotum not interactive'))
+    if 'user=' not in params:
+        params = '%s user?' % params
+    params = '%s !password?' % params
+    os.system("%s -g '%s'" % (_path, params))
+
+def auth_getuserpasswd(self, getkey, params):
+    params = 'proto=pass %s' % params
+    while True:
+        fd = os.open('%s/rpc' % _mount, os.O_RDWR)
+        try:
+            os.write(fd, 'start %s' % params)
+            l = os.read(fd, ERRMAX).split()
+            if l[0] == 'ok':
+                os.write(fd, 'read')
+                l = os.read(fd, ERRMAX).split()
+                if l[0] == 'ok':
+                    return l[1:]
+        except:
+            raise util.Abort(_('factotum not responding'))
+        finally:
+            os.close(fd)
+        getkey(self, params)
+
+def monkeypatch_method(cls):
+    def decorator(func):
+        setattr(cls, func.__name__, func)
+        return func
+    return decorator
+
+ at monkeypatch_method(passwordmgr)
+def find_user_password(self, realm, authuri):
+    user, passwd = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
+        self, realm, authuri)
+    if user and passwd:
+        self._writedebug(user, passwd)
+        return (user, passwd)
+
+    prefix = ''
+    res = httpconnection.readauthforuri(self.ui, authuri, user)
+    if res:
+        _, auth = res
+        prefix = auth.get('prefix')
+        user, passwd = auth.get('username'), auth.get('password')
+    if not user or not passwd:
+        if not prefix:
+            prefix = '*'
+        params = 'service=%s prefix=%s' % (_service, prefix)
+        if user:
+            params = '%s user=%s' % (params, user)
+        user, passwd = auth_getuserpasswd(self, auth_getkey, params)
+
+    self.add_password(realm, authuri, user, passwd)
+    self._writedebug(user, passwd)
+    return (user, passwd)
+
+def uisetup(ui):
+    global _mount
+    _mount = ui.config('factotum', 'mount', '/mnt/factotum')
+    global _path
+    _path = ui.config('factotum', 'path', '/bin/auth/factotum')
+    global _service
+    _service = ui.config('factotum', 'service', 'hg')
diff -r 329887a7074c -r 70fcfb0bf1f6 mercurial/help/config.txt
--- a/mercurial/help/config.txt	Fri Apr 06 15:18:14 2012 -0500
+++ b/mercurial/help/config.txt	Fri Apr 06 14:39:47 2012 -0700
@@ -28,16 +28,17 @@
 paths are given below, settings from earlier paths override later
 ones.
 
-| (Unix, Windows) ``<repo>/.hg/hgrc``
+| (Plan 9, Unix, Windows) ``<repo>/.hg/hgrc``
 
     Per-repository configuration options that only apply in a
     particular repository. This file is not version-controlled, and
     will not get transferred during a "clone" operation. Options in
     this file override options in all other configuration files. On
-    Unix, most of this file will be ignored if it doesn't belong to a
-    trusted user or to a trusted group. See the documentation for the
-    ``[trusted]`` section below for more details.
+    Plan 9 and Unix, most of this file will be ignored if it doesn't
+    belong to a trusted user or to a trusted group. See the documentation
+    for the ``[trusted]`` section below for more details.
 
+| (Plan 9) ``$home/lib/hgrc``
 | (Unix) ``$HOME/.hgrc``
 | (Windows) ``%USERPROFILE%\.hgrc``
 | (Windows) ``%USERPROFILE%\Mercurial.ini``
@@ -50,6 +51,8 @@
     directory. Options in these files override per-system and per-installation
     options.
 
+| (Plan 9) ``/lib/mercurial/hgrc``
+| (Plan 9) ``/lib/mercurial/hgrc.d/*.rc``
 | (Unix) ``/etc/mercurial/hgrc``
 | (Unix) ``/etc/mercurial/hgrc.d/*.rc``
 
@@ -58,6 +61,8 @@
     executed by any user in any directory. Options in these files
     override per-installation options.
 
+| (Plan 9) ``<install-root>/lib/mercurial/hgrc``
+| (Plan 9) ``<install-root>/lib/mercurial/hgrc.d/*.rc``
 | (Unix) ``<install-root>/etc/mercurial/hgrc``
 | (Unix) ``<install-root>/etc/mercurial/hgrc.d/*.rc``
 
diff -r 329887a7074c -r 70fcfb0bf1f6 mercurial/posix.py
--- a/mercurial/posix.py	Fri Apr 06 15:18:14 2012 -0500
+++ b/mercurial/posix.py	Fri Apr 06 14:39:47 2012 -0700
@@ -333,6 +333,9 @@
     if os.sep in command:
         return findexisting(command)
 
+    if sys.platform == 'plan9':
+        return findexisting(os.path.join('/bin', command))
+
     for path in os.environ.get('PATH', '').split(os.pathsep):
         executable = findexisting(os.path.join(path, command))
         if executable is not None:
diff -r 329887a7074c -r 70fcfb0bf1f6 mercurial/scmutil.py
--- a/mercurial/scmutil.py	Fri Apr 06 15:18:14 2012 -0500
+++ b/mercurial/scmutil.py	Fri Apr 06 14:39:47 2012 -0700
@@ -436,15 +436,22 @@
 
     def systemrcpath():
         path = []
+        if sys.platform == 'plan9':
+            root = '/lib/mercurial'
+        else:
+            root = '/etc/mercurial'
         # old mod_python does not set sys.argv
         if len(getattr(sys, 'argv', [])) > 0:
             p = os.path.dirname(os.path.dirname(sys.argv[0]))
-            path.extend(rcfiles(os.path.join(p, 'etc/mercurial')))
-        path.extend(rcfiles('/etc/mercurial'))
+            path.extend(rcfiles(os.path.join(p, root)))
+        path.extend(rcfiles(root))
         return path
 
     def userrcpath():
-        return [os.path.expanduser('~/.hgrc')]
+        if sys.platform == 'plan9':
+            return [os.environ['home'] + '/lib/hgrc']
+        else:
+            return [os.path.expanduser('~/.hgrc')]
 
 else:
 
diff -r 329887a7074c -r 70fcfb0bf1f6 mercurial/ui.py
--- a/mercurial/ui.py	Fri Apr 06 15:18:14 2012 -0500
+++ b/mercurial/ui.py	Fri Apr 06 14:39:47 2012 -0700
@@ -687,10 +687,17 @@
 
     def geteditor(self):
         '''return editor to use'''
+        if sys.platform == 'plan9':
+            # vi is the MIPS instruction simulator on Plan 9. We
+            # instead default to E to plumb commit messages to
+            # avoid confusion.
+            editor = 'E'
+        else:
+            editor = 'vi'
         return (os.environ.get("HGEDITOR") or
                 self.config("ui", "editor") or
                 os.environ.get("VISUAL") or
-                os.environ.get("EDITOR", "vi"))
+                os.environ.get("EDITOR", editor))
 
     def progress(self, topic, pos, item="", unit="", total=None):
         '''show a progress message
diff -r 329887a7074c -r 70fcfb0bf1f6 mercurial/util.py
--- a/mercurial/util.py	Fri Apr 06 15:18:14 2012 -0500
+++ b/mercurial/util.py	Fri Apr 06 14:39:47 2012 -0700
@@ -422,22 +422,29 @@
         return str(val)
     origcmd = cmd
     cmd = quotecommand(cmd)
-    env = dict(os.environ)
-    env.update((k, py2shell(v)) for k, v in environ.iteritems())
-    env['HG'] = hgexecutable()
-    if out is None or out == sys.__stdout__:
-        rc = subprocess.call(cmd, shell=True, close_fds=closefds,
-                             env=env, cwd=cwd)
+    if sys.platform == 'plan9':
+        # subprocess kludge to work around issues in half-baked Python
+        # ports, notably bichued/python:
+        if not cwd is None:
+            os.chdir(cwd)
+        rc = os.system(cmd)
     else:
-        proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
-                                env=env, cwd=cwd, stdout=subprocess.PIPE,
-                                stderr=subprocess.STDOUT)
-        for line in proc.stdout:
-            out.write(line)
-        proc.wait()
-        rc = proc.returncode
-    if sys.platform == 'OpenVMS' and rc & 1:
-        rc = 0
+        env = dict(os.environ)
+        env.update((k, py2shell(v)) for k, v in environ.iteritems())
+        env['HG'] = hgexecutable()
+        if out is None or out == sys.__stdout__:
+            rc = subprocess.call(cmd, shell=True, close_fds=closefds,
+                                 env=env, cwd=cwd)
+        else:
+            proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
+                                    env=env, cwd=cwd, stdout=subprocess.PIPE,
+                                    stderr=subprocess.STDOUT)
+            for line in proc.stdout:
+                out.write(line)
+            proc.wait()
+            rc = proc.returncode
+        if sys.platform == 'OpenVMS' and rc & 1:
+            rc = 0
     if rc and onerr:
         errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
                             explainexit(rc)[0])
diff -r 329887a7074c -r 70fcfb0bf1f6 setup.py
--- a/setup.py	Fri Apr 06 15:18:14 2012 -0500
+++ b/setup.py	Fri Apr 06 14:39:47 2012 -0700
@@ -127,10 +127,16 @@
     py2exeloaded = False
 
 def runcmd(cmd, env):
-    p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                         stderr=subprocess.PIPE, env=env)
-    out, err = p.communicate()
-    return out, err
+    if sys.platform == 'plan9':
+        # subprocess kludge to work around issues in half-baked Python
+        # ports, notably bichued/python:
+        _, out, err = os.popen3(cmd)
+        return str(out), str(err)
+    else:
+        p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE, env=env)
+        out, err = p.communicate()
+        return out, err
 
 def runhg(cmd, env):
     out, err = runcmd(cmd, env)


More information about the Mercurial-devel mailing list