changeset 679:80928ea6e7ae

Add the ability to include text files and have them XML-mangled.
author Bryan O'Sullivan <bos@serpentine.com>
date Tue, 17 Mar 2009 21:47:12 -0700
parents cd978765da64
children 8366882f67f2
files en/ch02-tour-basic.xml en/ch03-tour-merge.xml en/ch06-collab.xml en/examples/auto-snippets.xml en/examples/ch06/apache-config.lst en/examples/run-example
diffstat 6 files changed, 138 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/en/ch02-tour-basic.xml	Tue Mar 10 22:40:47 2009 -0700
+++ b/en/ch02-tour-basic.xml	Tue Mar 17 21:47:12 2009 -0700
@@ -30,21 +30,17 @@
 
       <itemizedlist>
 	<listitem><para>Debian:</para>
-	  <programlisting>apt-get install
-	    mercurial</programlisting></listitem>
+	  <programlisting>apt-get install mercurial</programlisting></listitem>
 	<listitem><para>Fedora Core:</para>
-	  <programlisting>yum install
-	    mercurial</programlisting></listitem>
+	  <programlisting>yum install mercurial</programlisting></listitem>
 	<listitem><para>Gentoo:</para>
 	  <programlisting>emerge mercurial</programlisting></listitem>
 	<listitem><para>OpenSUSE:</para>
-	  <programlisting>yum install
-	    mercurial</programlisting></listitem>
+	  <programlisting>yum install mercurial</programlisting></listitem>
 	<listitem><para>Ubuntu: Ubuntu's Mercurial package is based on
 	    Debian's.  To install it, run the following
 	    command.</para>
-	  <programlisting>apt-get install
-	    mercurial</programlisting></listitem>
+	  <programlisting>apt-get install mercurial</programlisting></listitem>
       </itemizedlist>
 
     </sect2>
@@ -554,7 +550,8 @@
 	    <filename role="special">.hgrc</filename> should look like
 	    this.</para>
 	<programlisting># This is a Mercurial configuration file.
-[ui] username = Firstname Lastname
+[ui]
+username = Firstname Lastname
 &lt;email.address@domain.net&gt;</programlisting>
 
 	<para>The <quote><literal>[ui]</literal></quote> line begins a
@@ -626,8 +623,8 @@
 
       <programlisting>
 changeset:   73:584af0e231be
-user: Censored Person &lt;censored.person@example.org&gt;
-date: Tue Sep 26 21:37:07 2006 -0700
+user:        Censored Person &lt;censored.person@example.org&gt;
+date:        Tue Sep 26 21:37:07 2006 -0700
 summary:     include buildmeister/commondefs. Add exports.</programlisting>
 
       <para>As far as the remainder of the contents of the
--- a/en/ch03-tour-merge.xml	Tue Mar 10 22:40:47 2009 -0700
+++ b/en/ch03-tour-merge.xml	Tue Mar 17 21:47:12 2009 -0700
@@ -337,9 +337,9 @@
     <para>The process of merging changes as outlined above is
       straightforward, but requires running three commands in
       sequence.</para>
-    <programlisting>
-      hg pull hg merge hg commit -m 'Merged remote changes'
-    </programlisting>
+    <programlisting>hg pull
+hg merge
+hg commit -m 'Merged remote changes'</programlisting>
     <para>In the case of the final commit, you also need to enter a
       commit message, which is almost always going to be a piece of
       uninteresting <quote>boilerplate</quote> text.</para>
@@ -376,9 +376,8 @@
       <literal role="rc-extensions">extensions</literal> section. Then
       add a line that simply reads <quote><literal>fetch
 	</literal></quote>.</para>
-    <programlisting>
-      [extensions] fetch =
-    </programlisting>
+    <programlisting>[extensions]
+fetch =</programlisting>
     <para>(Normally, on the right-hand side of the
       <quote><literal>=</literal></quote> would appear the location of
       the extension, but since the <literal
--- a/en/ch06-collab.xml	Tue Mar 10 22:40:47 2009 -0700
+++ b/en/ch06-collab.xml	Tue Mar 17 21:47:12 2009 -0700
@@ -536,8 +536,8 @@
 	</listitem>
 	<listitem><para>The <quote>:22</quote> identifies the port
 	    number to connect to the server on.  The default port is
-	    22, so you only need to specify this part if you're
-	    <emphasis>not</emphasis> using port 22.</para>
+	    22, so you only need to specify a colon and port number if
+	    you're <emphasis>not</emphasis> using port 22.</para>
 	</listitem>
 	<listitem><para>The remainder of the URL is the local path to
 	    the repository on the server.</para>
@@ -597,8 +597,8 @@
 	example, if you're using PuTTY, you'll need to use the
 	<command>plink</command> command as a command-line ssh
 	client.</para>
-      <programlisting>[ui] ssh = C:/path/to/plink.exe -ssh -i
-	"C:/path/to/my/private/key"</programlisting>
+      <programlisting>[ui]
+ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key"</programlisting>
 
       <note>
 	<para>  The path to <command>plink</command> shouldn't contain
@@ -840,15 +840,17 @@
 	turns on compression.  You can easily edit your <filename
 	  role="special"> /.hgrc</filename>\ to enable compression for
 	all of Mercurial's uses of the ssh protocol.</para>
-      <programlisting>[ui] ssh = ssh -C</programlisting>
+      <programlisting>[ui]
+ssh = ssh -C</programlisting>
 
       <para>If you use <command>ssh</command>, you can configure it to
 	always use compression when talking to your server.  To do
 	this, edit your <filename
 	  role="special">.ssh/config</filename> file (which may not
 	yet exist), as follows.</para>
-      <programlisting>Host hg Compression yes HostName
-	hg.example.com</programlisting>
+      <programlisting>Host hg
+  Compression yes
+  HostName hg.example.com</programlisting>
       <para>This defines an alias, <literal>hg</literal>.  When you
 	use it on the <command>ssh</command> command line or in a
 	Mercurial <literal>ssh</literal>-protocol URL, it will cause
@@ -936,8 +938,8 @@
       <para>You'll need to copy this script into your <filename
 	  class="directory">public_html</filename> directory, and
 	ensure that it's executable.</para>
-      <programlisting>cp .../hgweb.cgi ~/public_html chmod 755
-	~/public_html/hgweb.cgi</programlisting>
+      <programlisting>cp .../hgweb.cgi ~/public_html
+chmod 755 ~/public_html/hgweb.cgi</programlisting>
       <para>The <literal>755</literal> argument to
 	<command>chmod</command> is a little more general than just
 	making the script executable: it ensures that the script is
@@ -986,9 +988,9 @@
 	    class="directory">public_html</filename> directory, and
 	  read files under the latter too.  Here's a quick recipe to
 	  help you to make your permissions more appropriate.</para>
-	<programlisting>chmod 755 ~ find ~/public_html -type d -print0
-	  | xargs -0r chmod 755 find ~/public_html -type f -print0 |
-	  xargs -0r chmod 644</programlisting>
+	<programlisting>chmod 755 ~
+find ~/public_html -type d -print0 | xargs -0r chmod 755
+find ~/public_html -type f -print0 | xargs -0r chmod 644</programlisting>
 
 	<para>The other possibility with permissions is that you might
 	  get a completely empty window when you try to load the
@@ -1001,6 +1003,9 @@
 	  of CGI programs in your per-user web directory.  Here's
 	  Apache's default per-user configuration from my Fedora
 	  system.</para>
+
+	&ch06-apache-config.lst;
+
 	<programlisting>&lt;Directory /home/*/public_html&gt;
 	  AllowOverride FileInfo AuthConfig Limit Options MultiViews
 	  Indexes SymLinksIfOwnerMatch IncludesNoExec &lt;Limit GET
--- a/en/examples/auto-snippets.xml	Tue Mar 10 22:40:47 2009 -0700
+++ b/en/examples/auto-snippets.xml	Tue Mar 17 21:47:12 2009 -0700
@@ -1,3 +1,4 @@
+<!ENTITY ch06-apache-config.lst SYSTEM "results/ch06-apache-config.lst.out">
 <!ENTITY interaction.backout.init SYSTEM "results/backout.init.out">
 <!ENTITY interaction.backout.manual.backout SYSTEM "results/backout.manual.backout.out">
 <!ENTITY interaction.backout.manual.cat SYSTEM "results/backout.manual.cat.out">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/en/examples/ch06/apache-config.lst	Tue Mar 17 21:47:12 2009 -0700
@@ -0,0 +1,11 @@
+<Directory /home/*/public_html>
+  AllowOverride FileInfo AuthConfig Limit
+  Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
+  <Limit GET POST OPTIONS>
+    Order allow,deny
+    Allow from all
+  </Limit>
+  <LimitExcept GET POST OPTIONS>
+    Order deny,allow Deny from all
+  </LimitExcept>
+</Directory>
--- a/en/examples/run-example	Tue Mar 10 22:40:47 2009 -0700
+++ b/en/examples/run-example	Tue Mar 17 21:47:12 2009 -0700
@@ -54,10 +54,83 @@
     return None
         
 def result_name(name):
-    dirname, basename = os.path.split(name)
-    return os.path.join(dirname, 'results', basename)
+    return os.path.join('results', name.replace(os.sep, '-'))
 
 class example:
+    entities = dict.fromkeys(l.rstrip() for l in open('auto-snippets.xml'))
+
+    def __init__(self, name, verbose, keep_change):
+        self.name = name
+        self.verbose = verbose
+        self.keep_change = keep_change
+
+    def rename_output(self, base, ignore=[]):
+        mangle_re = re.compile('(?:' + '|'.join(ignore) + ')')
+        def mangle(s):
+            return mangle_re.sub('', s)
+        def matchfp(fp1, fp2):
+            while True:
+                s1 = mangle(fp1.readline())
+                s2 = mangle(fp2.readline())
+                if cmp(s1, s2):
+                    break
+                if not s1:
+                    return True
+            return False
+
+        oldname = result_name(base + '.out')
+        tmpname = result_name(base + '.tmp')
+        errname = result_name(base + '.err')
+        errfp = open(errname, 'w+')
+        for line in open(tmpname):
+            errfp.write(mangle_re.sub('', line))
+        os.rename(tmpname, result_name(base + '.lxo'))
+        errfp.seek(0)
+        try:
+            oldfp = open(oldname)
+        except IOError, err:
+            if err.errno != errno.ENOENT:
+                raise
+            os.rename(errname, oldname)
+            return False
+        if matchfp(oldfp, errfp):
+            os.unlink(errname)
+            return False
+        else:
+            print >> sys.stderr, '\nOutput of %s has changed!' % baseq
+            if self.keep_change:
+                os.rename(errname, oldname)
+                return False
+            else:
+                os.system('diff -u %s %s 1>&2' % (oldname, errname))
+            return True
+
+def wopen(name):
+    path = os.path.dirname(name)
+    if path:
+        try:
+            os.makedirs(path)
+        except OSError, err:
+            if err.errno != errno.EEXIST:
+                raise
+    return open(name, 'w')
+
+class static_example(example):
+    def run(self):
+        s = open(self.name).read().rstrip()
+        s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
+        ofp = wopen(result_name(self.name + '.tmp'))
+        ofp.write('<programlisting>')
+        ofp.write(s)
+        ofp.write('</programlisting>\n')
+        ofp.close()
+        self.rename_output(self.name)
+        norm = self.name.replace(os.sep, '-')
+        example.entities[
+            '<!ENTITY %s SYSTEM "results/%s.out">' % (norm, norm)] = 1
+
+
+class shell_example(example):
     shell = '/usr/bin/env bash'
     ps1 = '__run_example_ps1__ '
     ps2 = '__run_example_ps2__ '
@@ -65,12 +138,8 @@
     
     timeout = 10
 
-    entities = dict.fromkeys(l.rstrip() for l in open('auto-snippets.xml'))
-
     def __init__(self, name, verbose, keep_change):
-        self.name = name
-        self.verbose = verbose
-        self.keep_change = keep_change
+        example.__init__(self, name, verbose, keep_change)
         self.poll = select.poll()
 
     def parse(self):
@@ -153,12 +222,12 @@
         maybe_unlink(self.name + '.run')
 
         rcfile = os.path.join(tmpdir, '.hgrc')
-        rcfp = open(rcfile, 'w')
+        rcfp = wopen(rcfile)
         print >> rcfp, '[ui]'
         print >> rcfp, "username = Bryan O'Sullivan <bos@serpentine.com>"
         
         rcfile = os.path.join(tmpdir, '.bashrc')
-        rcfp = open(rcfile, 'w')
+        rcfp = wopen(rcfile)
         print >> rcfp, 'PS1="%s"' % self.ps1
         print >> rcfp, 'PS2="%s"' % self.ps2
         print >> rcfp, 'unset HISTFILE'
@@ -248,8 +317,7 @@
                                     'SYSTEM "results/%s.out">'
                                     % (norm, norm)] = 1
                                 read_hint = ofp_basename + ' '
-                                ofp = open(result_name(ofp_basename + '.tmp'),
-                                           'w')
+                                ofp = wopen(result_name(ofp_basename + '.tmp'))
                                 ofp.write('<screen>')
                             else:
                                 ofp = None
@@ -297,52 +365,11 @@
                     elif os.WIFSIGNALED(rc):
                         print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc)
                 else:
-                    open(result_name(self.name + '.run'), 'w')
+                    wopen(result_name(self.name + '.run'))
                 return err
         finally:
             shutil.rmtree(tmpdir)
 
-    def rename_output(self, base, ignore):
-        mangle_re = re.compile('(?:' + '|'.join(ignore) + ')')
-        def mangle(s):
-            return mangle_re.sub('', s)
-        def matchfp(fp1, fp2):
-            while True:
-                s1 = mangle(fp1.readline())
-                s2 = mangle(fp2.readline())
-                if cmp(s1, s2):
-                    break
-                if not s1:
-                    return True
-            return False
-
-        oldname = result_name(base + '.out')
-        tmpname = result_name(base + '.tmp')
-        errname = result_name(base + '.err')
-        errfp = open(errname, 'w+')
-        for line in open(tmpname):
-            errfp.write(mangle_re.sub('', line))
-        os.rename(tmpname, result_name(base + '.lxo'))
-        errfp.seek(0)
-        try:
-            oldfp = open(oldname)
-        except IOError, err:
-            if err.errno != errno.ENOENT:
-                raise
-            os.rename(errname, oldname)
-            return False
-        if matchfp(oldfp, errfp):
-            os.unlink(errname)
-            return False
-        else:
-            print >> sys.stderr, '\nOutput of %s has changed!' % base
-            if self.keep_change:
-                os.rename(errname, oldname)
-                return False
-            else:
-                os.system('diff -u %s %s 1>&2' % (oldname, errname))
-            return True
-
 def print_help(exit, msg=None):
     if msg:
         print >> sys.stderr, 'Error:', msg
@@ -383,18 +410,20 @@
                 print >> sys.stderr, '%s: %s' % (a, err.strerror)
                 errs += 1
                 continue
-            if stat.S_ISREG(st.st_mode) and st.st_mode & 0111:
-                if example(a, verbose, keep_change).run():
-                    errs += 1
+            if stat.S_ISREG(st.st_mode):
+                if st.st_mode & 0111:
+                    if shell_example(a, verbose, keep_change).run():
+                        errs += 1
+                elif a.endswith('.lst'):
+                    static_example(a, verbose, keep_change).run()
             else:
                 print >> sys.stderr, '%s: not a file, or not executable' % a
                 errs += 1
     elif run_all:
-        names = os.listdir(path)
+        names = glob.glob("*") + glob.glob("app*/*") + glob.glob("ch*/*")
         names.sort()
         for name in names:
-            if name == 'run-example' or name.startswith('.'): continue
-            if name.endswith('~'): continue
+            if name == 'run-example' or name.endswith('~'): continue
             pathname = os.path.join(path, name)
             try:
                 st = os.lstat(pathname)
@@ -403,14 +432,17 @@
                 if err.errno != errno.ENOENT:
                     raise
                 continue
-            if stat.S_ISREG(st.st_mode) and st.st_mode & 0111:
-                if example(pathname, verbose, keep_change).run():
-                    errs += 1
-        print >> open(os.path.join(path, '.run'), 'w'), time.asctime()
+            if stat.S_ISREG(st.st_mode):
+                if st.st_mode & 0111:
+                    if shell_example(pathname, verbose, keep_change).run():
+                        errs += 1
+                elif pathname.endswith('.lst'):
+                    static_example(pathname, verbose, keep_change).run()
+        print >> wopen(os.path.join(path, '.run')), time.asctime()
     else:
         print_help(1, msg='no test names given, and --all not provided')
 
-    fp = open('auto-snippets.xml', 'w')
+    fp = wopen('auto-snippets.xml')
     for key in sorted(example.entities.iterkeys()):
         print >> fp, key
     fp.close()