hgbook

changeset 579:80928ea6e7ae

Add the ability to include text files and have them XML-mangled.
author Bryan O'Sullivan <bos@serpentine.com>
date Tue Mar 17 21:47:12 2009 -0700 (2009-03-17)
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
line diff
     1.1 --- a/en/ch02-tour-basic.xml	Tue Mar 10 22:40:47 2009 -0700
     1.2 +++ b/en/ch02-tour-basic.xml	Tue Mar 17 21:47:12 2009 -0700
     1.3 @@ -30,21 +30,17 @@
     1.4  
     1.5        <itemizedlist>
     1.6  	<listitem><para>Debian:</para>
     1.7 -	  <programlisting>apt-get install
     1.8 -	    mercurial</programlisting></listitem>
     1.9 +	  <programlisting>apt-get install mercurial</programlisting></listitem>
    1.10  	<listitem><para>Fedora Core:</para>
    1.11 -	  <programlisting>yum install
    1.12 -	    mercurial</programlisting></listitem>
    1.13 +	  <programlisting>yum install mercurial</programlisting></listitem>
    1.14  	<listitem><para>Gentoo:</para>
    1.15  	  <programlisting>emerge mercurial</programlisting></listitem>
    1.16  	<listitem><para>OpenSUSE:</para>
    1.17 -	  <programlisting>yum install
    1.18 -	    mercurial</programlisting></listitem>
    1.19 +	  <programlisting>yum install mercurial</programlisting></listitem>
    1.20  	<listitem><para>Ubuntu: Ubuntu's Mercurial package is based on
    1.21  	    Debian's.  To install it, run the following
    1.22  	    command.</para>
    1.23 -	  <programlisting>apt-get install
    1.24 -	    mercurial</programlisting></listitem>
    1.25 +	  <programlisting>apt-get install mercurial</programlisting></listitem>
    1.26        </itemizedlist>
    1.27  
    1.28      </sect2>
    1.29 @@ -554,7 +550,8 @@
    1.30  	    <filename role="special">.hgrc</filename> should look like
    1.31  	    this.</para>
    1.32  	<programlisting># This is a Mercurial configuration file.
    1.33 -[ui] username = Firstname Lastname
    1.34 +[ui]
    1.35 +username = Firstname Lastname
    1.36  &lt;email.address@domain.net&gt;</programlisting>
    1.37  
    1.38  	<para>The <quote><literal>[ui]</literal></quote> line begins a
    1.39 @@ -626,8 +623,8 @@
    1.40  
    1.41        <programlisting>
    1.42  changeset:   73:584af0e231be
    1.43 -user: Censored Person &lt;censored.person@example.org&gt;
    1.44 -date: Tue Sep 26 21:37:07 2006 -0700
    1.45 +user:        Censored Person &lt;censored.person@example.org&gt;
    1.46 +date:        Tue Sep 26 21:37:07 2006 -0700
    1.47  summary:     include buildmeister/commondefs. Add exports.</programlisting>
    1.48  
    1.49        <para>As far as the remainder of the contents of the
     2.1 --- a/en/ch03-tour-merge.xml	Tue Mar 10 22:40:47 2009 -0700
     2.2 +++ b/en/ch03-tour-merge.xml	Tue Mar 17 21:47:12 2009 -0700
     2.3 @@ -337,9 +337,9 @@
     2.4      <para>The process of merging changes as outlined above is
     2.5        straightforward, but requires running three commands in
     2.6        sequence.</para>
     2.7 -    <programlisting>
     2.8 -      hg pull hg merge hg commit -m 'Merged remote changes'
     2.9 -    </programlisting>
    2.10 +    <programlisting>hg pull
    2.11 +hg merge
    2.12 +hg commit -m 'Merged remote changes'</programlisting>
    2.13      <para>In the case of the final commit, you also need to enter a
    2.14        commit message, which is almost always going to be a piece of
    2.15        uninteresting <quote>boilerplate</quote> text.</para>
    2.16 @@ -376,9 +376,8 @@
    2.17        <literal role="rc-extensions">extensions</literal> section. Then
    2.18        add a line that simply reads <quote><literal>fetch
    2.19  	</literal></quote>.</para>
    2.20 -    <programlisting>
    2.21 -      [extensions] fetch =
    2.22 -    </programlisting>
    2.23 +    <programlisting>[extensions]
    2.24 +fetch =</programlisting>
    2.25      <para>(Normally, on the right-hand side of the
    2.26        <quote><literal>=</literal></quote> would appear the location of
    2.27        the extension, but since the <literal
     3.1 --- a/en/ch06-collab.xml	Tue Mar 10 22:40:47 2009 -0700
     3.2 +++ b/en/ch06-collab.xml	Tue Mar 17 21:47:12 2009 -0700
     3.3 @@ -536,8 +536,8 @@
     3.4  	</listitem>
     3.5  	<listitem><para>The <quote>:22</quote> identifies the port
     3.6  	    number to connect to the server on.  The default port is
     3.7 -	    22, so you only need to specify this part if you're
     3.8 -	    <emphasis>not</emphasis> using port 22.</para>
     3.9 +	    22, so you only need to specify a colon and port number if
    3.10 +	    you're <emphasis>not</emphasis> using port 22.</para>
    3.11  	</listitem>
    3.12  	<listitem><para>The remainder of the URL is the local path to
    3.13  	    the repository on the server.</para>
    3.14 @@ -597,8 +597,8 @@
    3.15  	example, if you're using PuTTY, you'll need to use the
    3.16  	<command>plink</command> command as a command-line ssh
    3.17  	client.</para>
    3.18 -      <programlisting>[ui] ssh = C:/path/to/plink.exe -ssh -i
    3.19 -	"C:/path/to/my/private/key"</programlisting>
    3.20 +      <programlisting>[ui]
    3.21 +ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key"</programlisting>
    3.22  
    3.23        <note>
    3.24  	<para>  The path to <command>plink</command> shouldn't contain
    3.25 @@ -840,15 +840,17 @@
    3.26  	turns on compression.  You can easily edit your <filename
    3.27  	  role="special"> /.hgrc</filename>\ to enable compression for
    3.28  	all of Mercurial's uses of the ssh protocol.</para>
    3.29 -      <programlisting>[ui] ssh = ssh -C</programlisting>
    3.30 +      <programlisting>[ui]
    3.31 +ssh = ssh -C</programlisting>
    3.32  
    3.33        <para>If you use <command>ssh</command>, you can configure it to
    3.34  	always use compression when talking to your server.  To do
    3.35  	this, edit your <filename
    3.36  	  role="special">.ssh/config</filename> file (which may not
    3.37  	yet exist), as follows.</para>
    3.38 -      <programlisting>Host hg Compression yes HostName
    3.39 -	hg.example.com</programlisting>
    3.40 +      <programlisting>Host hg
    3.41 +  Compression yes
    3.42 +  HostName hg.example.com</programlisting>
    3.43        <para>This defines an alias, <literal>hg</literal>.  When you
    3.44  	use it on the <command>ssh</command> command line or in a
    3.45  	Mercurial <literal>ssh</literal>-protocol URL, it will cause
    3.46 @@ -936,8 +938,8 @@
    3.47        <para>You'll need to copy this script into your <filename
    3.48  	  class="directory">public_html</filename> directory, and
    3.49  	ensure that it's executable.</para>
    3.50 -      <programlisting>cp .../hgweb.cgi ~/public_html chmod 755
    3.51 -	~/public_html/hgweb.cgi</programlisting>
    3.52 +      <programlisting>cp .../hgweb.cgi ~/public_html
    3.53 +chmod 755 ~/public_html/hgweb.cgi</programlisting>
    3.54        <para>The <literal>755</literal> argument to
    3.55  	<command>chmod</command> is a little more general than just
    3.56  	making the script executable: it ensures that the script is
    3.57 @@ -986,9 +988,9 @@
    3.58  	    class="directory">public_html</filename> directory, and
    3.59  	  read files under the latter too.  Here's a quick recipe to
    3.60  	  help you to make your permissions more appropriate.</para>
    3.61 -	<programlisting>chmod 755 ~ find ~/public_html -type d -print0
    3.62 -	  | xargs -0r chmod 755 find ~/public_html -type f -print0 |
    3.63 -	  xargs -0r chmod 644</programlisting>
    3.64 +	<programlisting>chmod 755 ~
    3.65 +find ~/public_html -type d -print0 | xargs -0r chmod 755
    3.66 +find ~/public_html -type f -print0 | xargs -0r chmod 644</programlisting>
    3.67  
    3.68  	<para>The other possibility with permissions is that you might
    3.69  	  get a completely empty window when you try to load the
    3.70 @@ -1001,6 +1003,9 @@
    3.71  	  of CGI programs in your per-user web directory.  Here's
    3.72  	  Apache's default per-user configuration from my Fedora
    3.73  	  system.</para>
    3.74 +
    3.75 +	&ch06-apache-config.lst;
    3.76 +
    3.77  	<programlisting>&lt;Directory /home/*/public_html&gt;
    3.78  	  AllowOverride FileInfo AuthConfig Limit Options MultiViews
    3.79  	  Indexes SymLinksIfOwnerMatch IncludesNoExec &lt;Limit GET
     4.1 --- a/en/examples/auto-snippets.xml	Tue Mar 10 22:40:47 2009 -0700
     4.2 +++ b/en/examples/auto-snippets.xml	Tue Mar 17 21:47:12 2009 -0700
     4.3 @@ -1,3 +1,4 @@
     4.4 +<!ENTITY ch06-apache-config.lst SYSTEM "results/ch06-apache-config.lst.out">
     4.5  <!ENTITY interaction.backout.init SYSTEM "results/backout.init.out">
     4.6  <!ENTITY interaction.backout.manual.backout SYSTEM "results/backout.manual.backout.out">
     4.7  <!ENTITY interaction.backout.manual.cat SYSTEM "results/backout.manual.cat.out">
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/en/examples/ch06/apache-config.lst	Tue Mar 17 21:47:12 2009 -0700
     5.3 @@ -0,0 +1,11 @@
     5.4 +<Directory /home/*/public_html>
     5.5 +  AllowOverride FileInfo AuthConfig Limit
     5.6 +  Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
     5.7 +  <Limit GET POST OPTIONS>
     5.8 +    Order allow,deny
     5.9 +    Allow from all
    5.10 +  </Limit>
    5.11 +  <LimitExcept GET POST OPTIONS>
    5.12 +    Order deny,allow Deny from all
    5.13 +  </LimitExcept>
    5.14 +</Directory>
     6.1 --- a/en/examples/run-example	Tue Mar 10 22:40:47 2009 -0700
     6.2 +++ b/en/examples/run-example	Tue Mar 17 21:47:12 2009 -0700
     6.3 @@ -54,10 +54,83 @@
     6.4      return None
     6.5          
     6.6  def result_name(name):
     6.7 -    dirname, basename = os.path.split(name)
     6.8 -    return os.path.join(dirname, 'results', basename)
     6.9 +    return os.path.join('results', name.replace(os.sep, '-'))
    6.10  
    6.11  class example:
    6.12 +    entities = dict.fromkeys(l.rstrip() for l in open('auto-snippets.xml'))
    6.13 +
    6.14 +    def __init__(self, name, verbose, keep_change):
    6.15 +        self.name = name
    6.16 +        self.verbose = verbose
    6.17 +        self.keep_change = keep_change
    6.18 +
    6.19 +    def rename_output(self, base, ignore=[]):
    6.20 +        mangle_re = re.compile('(?:' + '|'.join(ignore) + ')')
    6.21 +        def mangle(s):
    6.22 +            return mangle_re.sub('', s)
    6.23 +        def matchfp(fp1, fp2):
    6.24 +            while True:
    6.25 +                s1 = mangle(fp1.readline())
    6.26 +                s2 = mangle(fp2.readline())
    6.27 +                if cmp(s1, s2):
    6.28 +                    break
    6.29 +                if not s1:
    6.30 +                    return True
    6.31 +            return False
    6.32 +
    6.33 +        oldname = result_name(base + '.out')
    6.34 +        tmpname = result_name(base + '.tmp')
    6.35 +        errname = result_name(base + '.err')
    6.36 +        errfp = open(errname, 'w+')
    6.37 +        for line in open(tmpname):
    6.38 +            errfp.write(mangle_re.sub('', line))
    6.39 +        os.rename(tmpname, result_name(base + '.lxo'))
    6.40 +        errfp.seek(0)
    6.41 +        try:
    6.42 +            oldfp = open(oldname)
    6.43 +        except IOError, err:
    6.44 +            if err.errno != errno.ENOENT:
    6.45 +                raise
    6.46 +            os.rename(errname, oldname)
    6.47 +            return False
    6.48 +        if matchfp(oldfp, errfp):
    6.49 +            os.unlink(errname)
    6.50 +            return False
    6.51 +        else:
    6.52 +            print >> sys.stderr, '\nOutput of %s has changed!' % baseq
    6.53 +            if self.keep_change:
    6.54 +                os.rename(errname, oldname)
    6.55 +                return False
    6.56 +            else:
    6.57 +                os.system('diff -u %s %s 1>&2' % (oldname, errname))
    6.58 +            return True
    6.59 +
    6.60 +def wopen(name):
    6.61 +    path = os.path.dirname(name)
    6.62 +    if path:
    6.63 +        try:
    6.64 +            os.makedirs(path)
    6.65 +        except OSError, err:
    6.66 +            if err.errno != errno.EEXIST:
    6.67 +                raise
    6.68 +    return open(name, 'w')
    6.69 +
    6.70 +class static_example(example):
    6.71 +    def run(self):
    6.72 +        s = open(self.name).read().rstrip()
    6.73 +        s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
    6.74 +        ofp = wopen(result_name(self.name + '.tmp'))
    6.75 +        ofp.write('<programlisting>')
    6.76 +        ofp.write(s)
    6.77 +        ofp.write('</programlisting>\n')
    6.78 +        ofp.close()
    6.79 +        self.rename_output(self.name)
    6.80 +        norm = self.name.replace(os.sep, '-')
    6.81 +        example.entities[
    6.82 +            '<!ENTITY %s SYSTEM "results/%s.out">' % (norm, norm)] = 1
    6.83 +
    6.84 +
    6.85 +class shell_example(example):
    6.86      shell = '/usr/bin/env bash'
    6.87      ps1 = '__run_example_ps1__ '
    6.88      ps2 = '__run_example_ps2__ '
    6.89 @@ -65,12 +138,8 @@
    6.90      
    6.91      timeout = 10
    6.92  
    6.93 -    entities = dict.fromkeys(l.rstrip() for l in open('auto-snippets.xml'))
    6.94 -
    6.95      def __init__(self, name, verbose, keep_change):
    6.96 -        self.name = name
    6.97 -        self.verbose = verbose
    6.98 -        self.keep_change = keep_change
    6.99 +        example.__init__(self, name, verbose, keep_change)
   6.100          self.poll = select.poll()
   6.101  
   6.102      def parse(self):
   6.103 @@ -153,12 +222,12 @@
   6.104          maybe_unlink(self.name + '.run')
   6.105  
   6.106          rcfile = os.path.join(tmpdir, '.hgrc')
   6.107 -        rcfp = open(rcfile, 'w')
   6.108 +        rcfp = wopen(rcfile)
   6.109          print >> rcfp, '[ui]'
   6.110          print >> rcfp, "username = Bryan O'Sullivan <bos@serpentine.com>"
   6.111          
   6.112          rcfile = os.path.join(tmpdir, '.bashrc')
   6.113 -        rcfp = open(rcfile, 'w')
   6.114 +        rcfp = wopen(rcfile)
   6.115          print >> rcfp, 'PS1="%s"' % self.ps1
   6.116          print >> rcfp, 'PS2="%s"' % self.ps2
   6.117          print >> rcfp, 'unset HISTFILE'
   6.118 @@ -248,8 +317,7 @@
   6.119                                      'SYSTEM "results/%s.out">'
   6.120                                      % (norm, norm)] = 1
   6.121                                  read_hint = ofp_basename + ' '
   6.122 -                                ofp = open(result_name(ofp_basename + '.tmp'),
   6.123 -                                           'w')
   6.124 +                                ofp = wopen(result_name(ofp_basename + '.tmp'))
   6.125                                  ofp.write('<screen>')
   6.126                              else:
   6.127                                  ofp = None
   6.128 @@ -297,52 +365,11 @@
   6.129                      elif os.WIFSIGNALED(rc):
   6.130                          print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc)
   6.131                  else:
   6.132 -                    open(result_name(self.name + '.run'), 'w')
   6.133 +                    wopen(result_name(self.name + '.run'))
   6.134                  return err
   6.135          finally:
   6.136              shutil.rmtree(tmpdir)
   6.137  
   6.138 -    def rename_output(self, base, ignore):
   6.139 -        mangle_re = re.compile('(?:' + '|'.join(ignore) + ')')
   6.140 -        def mangle(s):
   6.141 -            return mangle_re.sub('', s)
   6.142 -        def matchfp(fp1, fp2):
   6.143 -            while True:
   6.144 -                s1 = mangle(fp1.readline())
   6.145 -                s2 = mangle(fp2.readline())
   6.146 -                if cmp(s1, s2):
   6.147 -                    break
   6.148 -                if not s1:
   6.149 -                    return True
   6.150 -            return False
   6.151 -
   6.152 -        oldname = result_name(base + '.out')
   6.153 -        tmpname = result_name(base + '.tmp')
   6.154 -        errname = result_name(base + '.err')
   6.155 -        errfp = open(errname, 'w+')
   6.156 -        for line in open(tmpname):
   6.157 -            errfp.write(mangle_re.sub('', line))
   6.158 -        os.rename(tmpname, result_name(base + '.lxo'))
   6.159 -        errfp.seek(0)
   6.160 -        try:
   6.161 -            oldfp = open(oldname)
   6.162 -        except IOError, err:
   6.163 -            if err.errno != errno.ENOENT:
   6.164 -                raise
   6.165 -            os.rename(errname, oldname)
   6.166 -            return False
   6.167 -        if matchfp(oldfp, errfp):
   6.168 -            os.unlink(errname)
   6.169 -            return False
   6.170 -        else:
   6.171 -            print >> sys.stderr, '\nOutput of %s has changed!' % base
   6.172 -            if self.keep_change:
   6.173 -                os.rename(errname, oldname)
   6.174 -                return False
   6.175 -            else:
   6.176 -                os.system('diff -u %s %s 1>&2' % (oldname, errname))
   6.177 -            return True
   6.178 -
   6.179  def print_help(exit, msg=None):
   6.180      if msg:
   6.181          print >> sys.stderr, 'Error:', msg
   6.182 @@ -383,18 +410,20 @@
   6.183                  print >> sys.stderr, '%s: %s' % (a, err.strerror)
   6.184                  errs += 1
   6.185                  continue
   6.186 -            if stat.S_ISREG(st.st_mode) and st.st_mode & 0111:
   6.187 -                if example(a, verbose, keep_change).run():
   6.188 -                    errs += 1
   6.189 +            if stat.S_ISREG(st.st_mode):
   6.190 +                if st.st_mode & 0111:
   6.191 +                    if shell_example(a, verbose, keep_change).run():
   6.192 +                        errs += 1
   6.193 +                elif a.endswith('.lst'):
   6.194 +                    static_example(a, verbose, keep_change).run()
   6.195              else:
   6.196                  print >> sys.stderr, '%s: not a file, or not executable' % a
   6.197                  errs += 1
   6.198      elif run_all:
   6.199 -        names = os.listdir(path)
   6.200 +        names = glob.glob("*") + glob.glob("app*/*") + glob.glob("ch*/*")
   6.201          names.sort()
   6.202          for name in names:
   6.203 -            if name == 'run-example' or name.startswith('.'): continue
   6.204 -            if name.endswith('~'): continue
   6.205 +            if name == 'run-example' or name.endswith('~'): continue
   6.206              pathname = os.path.join(path, name)
   6.207              try:
   6.208                  st = os.lstat(pathname)
   6.209 @@ -403,14 +432,17 @@
   6.210                  if err.errno != errno.ENOENT:
   6.211                      raise
   6.212                  continue
   6.213 -            if stat.S_ISREG(st.st_mode) and st.st_mode & 0111:
   6.214 -                if example(pathname, verbose, keep_change).run():
   6.215 -                    errs += 1
   6.216 -        print >> open(os.path.join(path, '.run'), 'w'), time.asctime()
   6.217 +            if stat.S_ISREG(st.st_mode):
   6.218 +                if st.st_mode & 0111:
   6.219 +                    if shell_example(pathname, verbose, keep_change).run():
   6.220 +                        errs += 1
   6.221 +                elif pathname.endswith('.lst'):
   6.222 +                    static_example(pathname, verbose, keep_change).run()
   6.223 +        print >> wopen(os.path.join(path, '.run')), time.asctime()
   6.224      else:
   6.225          print_help(1, msg='no test names given, and --all not provided')
   6.226  
   6.227 -    fp = open('auto-snippets.xml', 'w')
   6.228 +    fp = wopen('auto-snippets.xml')
   6.229      for key in sorted(example.entities.iterkeys()):
   6.230          print >> fp, key
   6.231      fp.close()