Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

initial

  • Loading branch information...
commit 45e2188065c1a998863f6804e83b496fb1b4d993 0 parents
joe armstrong authored February 27, 2012

Showing 70 changed files with 15,986 additions and 0 deletions. Show diff stats Hide diff stats

  1. 76  src/Makefile
  2. 177  src/best.notes
  3. 35  src/book.book
  4. 0  src/chap2html.xsl
  5. 457  src/chap2pdf.xsl
  6. 154  src/design.chap1
  7. 33  src/edit.html
  8. 44  src/elib1_best.erl
  9. 43  src/elib1_blob_store.erl
  10. 356  src/elib1_chunks.erl
  11. 111  src/elib1_content_edit.erl
  12. 110  src/elib1_diff.erl
  13. 60  src/elib1_doc.erl
  14. 452  src/elib1_docmaker.erl
  15. 31  src/elib1_ensure_copyrighted.erl
  16. 191  src/elib1_expand.erl
  17. 129  src/elib1_fast_read.erl
  18. 83  src/elib1_fast_write.erl
  19. 138  src/elib1_file_finder.erl
  20. 134  src/elib1_find.erl
  21. 128  src/elib1_find_reent.erl
  22. 251  src/elib1_gamma.erl
  23. 30  src/elib1_geom.erl
  24. 143  src/elib1_guid_store.erl
  25. 409  src/elib1_html_tokenise.erl
  26. 258  src/elib1_http_driver.erl
  27. 643  src/elib1_indexer.erl
  28. 117  src/elib1_indexer_plugin_erl.erl
  29. 42  src/elib1_indexer_results.erl
  30. 2,538  src/elib1_misc.erl
  31. 370  src/elib1_ml9.erl
  32. 103  src/elib1_ml9_2_html.erl
  33. 38  src/elib1_ml9_parse_header.yrl
  34. 585  src/elib1_mysql.erl
  35. 549  src/elib1_new_webkit.erl
  36. 78  src/elib1_org2latex.erl
  37. 397  src/elib1_parse_dtd.yrl
  38. 378  src/elib1_porter.erl
  39. 429  src/elib1_rfc4627.erl
  40. 185  src/elib1_screen.erl
  41. 26  src/elib1_search.erl
  42. 207  src/elib1_seq_web_server1.erl
  43. 103  src/elib1_sha1.erl
  44. 203  src/elib1_similar.erl
  45. 67  src/elib1_simple_kv_db.erl
  46. 137  src/elib1_spy.erl
  47. 137  src/elib1_spy.erl~
  48. 432  src/elib1_store.erl
  49. 641  src/elib1_tagger.erl
  50. 130  src/elib1_telnet.erl
  51. 89  src/elib1_txt2xml.erl
  52. 576  src/elib1_webkit.erl
  53. 25  src/elib1_webquery.erl
  54. 1,045  src/elib1_xml.erl
  55. 460  src/ezxml.chap1
  56. 119  src/gen_component.erl
  57. 22  src/log
  58. 572  src/log.log
  59. 59  src/lorem.chap
  60. 5  src/mkdoc
  61. 12  src/myapp.app.src
  62. 16  src/myapp_app.erl
  63. 28  src/myapp_sup.erl
  64. 2  src/notes
  65. 12  src/password.erl
  66. 246  src/readme.html
  67. 33  src/slides.ehtml
  68. 3  src/todo
  69. 50  src/tryxform.erl
  70. 44  src/wiki.chap1
76  src/Makefile
... ...
@@ -0,0 +1,76 @@
  1
+.SUFFIXES: .erl .beam .yrl
  2
+
  3
+MODS := $(wildcard *.erl)
  4
+YRL := $(wildcard *.yrl)
  5
+CHAPS := $(wildcard *.chap)
  6
+BOOKS := $(wildcard *.book)
  7
+LOGS := $(wildcard *.log)
  8
+
  9
+CWD := $(shell pwd)
  10
+
  11
+../ebin/%.beam: %.erl
  12
+	## erlc +warn_missing_spec -o ../ebin -W $< 
  13
+	##  grep --silent --invert-match "_test"
  14
+	erlc -o ../ebin -W $< 
  15
+
  16
+../ebin/%.beam: ../tmp/%.erl
  17
+	erlc -o ../ebin -W $<
  18
+
  19
+../doc/%.html: %.chap
  20
+	@erl -noshell -pa ../ebin -s elib1_docmaker batch $< -s init stop
  21
+
  22
+../doc/%.html: %.log
  23
+	@erl -noshell -pa ../ebin -s elib1_chunks batch $< -s init stop
  24
+
  25
+../doc/%.html: %.erl
  26
+	./mkdoc $<
  27
+
  28
+../tmp/%.erl: %.yrl
  29
+	erlc -o ../tmp -W $<
  30
+
  31
+../../pdf/%.pdf: %.chap
  32
+	erl -s elib1_doc batch $<
  33
+	fop -xml ../tmp/$*.xml -xsl chap2pdf.xsl -pdf ../../pdf/$*.pdf
  34
+
  35
+# ../../html/%.html: %.chap
  36
+# 	erl -s elib1_doc batch $<
  37
+# 	fop -xml ../tmp/$*.xml -xsl chap2html.xsl -txt  ../../html/$*.html
  38
+
  39
+all: yecc beam html #chapHTML
  40
+
  41
+test:
  42
+	dialyzer -Wno_return --src -c "." 
  43
+
  44
+utest:  beam
  45
+	erl -noshell -eval "eunit:test(elib1_misc, [verbose])" -s init stop	
  46
+
  47
+edoc:
  48
+	erl -noshell -eval "edoc:application(lib, \".\", [{dir,\"../doc\"}])" \
  49
+	             -s init stop
  50
+
  51
+html: ${MODS:%.erl=../doc/%.html}
  52
+
  53
+beam: ${MODS:%.erl=../ebin/%.beam}
  54
+
  55
+yecc: ${YRL:%.yrl=../tmp/%.erl} ${YRL:%.yrl=../ebin/%.beam}
  56
+
  57
+chapPDF: ${CHAPS:%.chap=../../pdf/%.pdf}
  58
+
  59
+# chapHTML: ${CHAPS:%.chap=../../html/%.html}	
  60
+
  61
+books: ${BOOKS:%.book=../doc/%.html}
  62
+
  63
+logs: ${LOGS:%.log=../doc/%.html}
  64
+
  65
+clean:
  66
+	rm ../ebin/*.beam
  67
+	rm -rf *.aux *.beam 
  68
+	rm -rf *.log *.tmp erl_crash.dump 
  69
+
  70
+veryclean:
  71
+	rm ../bin/* ../doc/* ../tmp/*
  72
+
  73
+
  74
+
  75
+
  76
+
177  src/best.notes
... ...
@@ -0,0 +1,177 @@
  1
+Best practice for writing documenting and testing code
  2
+
  3
+I'd like to try and define "best practice" for writing documenting and
  4
+testing Erlang code. I want to use:
  5
+
  6
+    - only the tools supplied in the OTP release
  7
+
  8
+So I use:
  9
+
  10
+    - eunit for unit testing
  11
+    - the dialyzer for checking my code
  12
+    - edoc for documenting things
  13
+    - type specifications for specifying types
  14
+
  15
+These tools do not completely "play together" in a satisfactory manner,
  16
+so I'd like to define what I thing is "best practice" and hope that by doing
  17
+so the tools will converge.
  18
+
  19
+Let's suppose I want to define the good 'ol factorial. Here's a module
  20
+called elib1_best.erl. I've written it in such a way that it can be
  21
+processed by erlc,eunit,edoc and the dialyzer - read the footnotes
  22
+in brackets for an explanation.
  23
+
  24
+       -module(elib1_best). %% [1]
  25
+
  26
+       %% elib1_best: Best practice template for library modules [2]
  27
+       %% Time-stamp: <2009-12-02 09:43:12 ejoearm> [3]
  28
+
  29
+       %%----------------------------------------------------------------------
  30
+       %% Copyright (c) 2009 Joe Armstrong <erlang@gmail.com> [4]
  31
+       %% Copyright (c) 2009 Whoomph Software AB 
  32
+       %%
  33
+       %% Permission is hereby granted, free of charge, to any person
  34
+       %% obtaining a copy of this software and associated documentation
  35
+       %% files (the "Software"), to deal in the Software without
  36
+       %% restriction, including without limitation the rights to use, copy,
  37
+       %% modify, merge, publish, distribute, sublicense, and/or sell copies
  38
+       %% of the Software, and to permit persons to whom the Software is
  39
+       %% furnished to do so, subject to the following conditions:
  40
+       %%
  41
+       %% The above copyright notice and this permission notice shall be
  42
+       %% included in all copies or substantial portions of the Software.
  43
+       %%
  44
+       %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  45
+       %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  46
+       %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  47
+       %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  48
+       %% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  49
+       %% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  50
+       %% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  51
+       %% SOFTWARE.
  52
+       %%-----------------------------------------------------------------------
  53
+
  54
+       -include_lib("eunit/include/eunit.hrl"). %% [5]
  55
+
  56
+       -export([fac/1]).  %% [6]
  57
+
  58
+
  59
+       %% @doc fac(N) computes factorial(N) using a fast 
  60
+       %% iterative algorithm. [7]
  61
+
  62
+       -spec fac(integer()) -> integer(). [8]
  63
+
  64
+       fac(N) when is_integer(N), N >= 0 -> fac1(N, 1).
  65
+
  66
+       fac1(0, K) -> K;
  67
+       fac1(N, K) -> fac1(N-1, K*N).
  68
+
  69
+       fac_test() ->  %% [9]
  70
+           6  = fac(3),
  71
+	       24 = fac(4).
  72
+
  73
+	       %% Notes:
  74
+	       %% [1] - module on line 1
  75
+	       %% [2] - module comment
  76
+	       %% [3] - Time stamp auto generated by emacs. 
  77
+               %%       Must be near start of file
  78
+	       %% [4] - Copyright (I always forget this, but adding a 
  79
+               %%       copyright reduces the pain later
  80
+	       %% [5] - Needed for eunit
  81
+	       %% [6] - use export and NOT compile(export_all)
  82
+	       %% [7] - @doc comes first
  83
+	       %% [8] - -spec comes immediately *before* the function
  84
+	       %% [9] - test cases come immediately after the function
  85
+
  86
+	       %% end of module
  87
+...
  88
+
  89
+Now let's see what happens:
  90
+
  91
+    1) Compiling
  92
+
  93
+    erlc  +warn_missing_spec -o ../ebin -W elib1_best.erl
  94
+
  95
+    ./elib1_best.erl:0: Warning: missing specification for function test/0
  96
+    ./elib1_best.erl:44: Warning: missing specification for function fac_test/0
  97
+
  98
+Best practice is to support type specifications for all exported
  99
+functions.  But eunit magically adds a function test/0 and I really
  100
+don't want to have to add manual exports and type specs for
  101
+fac_test/0.
  102
+
  103
+    [A fix is needed to erlc here, OR eunit can add type specs,
  104
+     I think the latter is better - erlc should not need to know about eunit]
  105
+
  106
+   2) Dialyzing
  107
+
  108
+dialyzer --src elib1_best.erl
  109
+  Checking whether the PLT /home/ejoearm/.dialyzer_plt is up-to-date... yes
  110
+  Proceeding with analysis...
  111
+Unknown functions:
  112
+  eunit:test/1
  113
+ done in 0m0.32s
  114
+done (passed successfully)
  115
+
  116
+    This is ok - I could add eunit to my plt if I wanted ...
  117
+    the dialyzer warns for missing functions so I don't need to run
  118
+    xref
  119
+
  120
+   3) Documentation
  121
+
  122
+    I'll run edoc on everything in the current directory putting the 
  123
+results in ../doc
  124
+
  125
+      > erl -noshell -eval "edoc:application(lib, \".\", [{dir,\"../doc\"}])" \
  126
+  	             -s init stop
  127
+
  128
+    This works fine and ../doc/elib1_best.html has the documentation
  129
+    but now edoc has not found my nice -spec declaration and thinks that
  130
+    fac has type: fac(N) -> any()
  131
+
  132
+    Why: because edoc and erlc don't use the same type parser.
  133
+    
  134
+    Current best practice is to use -spec (in code) and 
  135
+    not @spec (in edoc comments) 
  136
+
  137
+     [A fix is needed here to edoc, to understand -spec's]
  138
+
  139
+4) Testing
  140
+
  141
+   1> eunit:test(elib1_best, [verbose]).
  142
+   ======================== EUnit ========================
  143
+   elib1_best: fac_test (module 'elib1_best')...ok
  144
+   =======================================================
  145
+   Test passed.
  146
+   ok
  147
+
  148
+   Great ...
  149
+
  150
+Now for questions.
  151
+
  152
+    1) Does this represent best practice? Is this the best way to
  153
+       write code? - can anybody improve on this?
  154
+
  155
+       [And yes I know about quickcheck, but I'm only concerned
  156
+        with SW in the OTP release]
  157
+
  158
+    2) If I write like this can I assume that one day edoc
  159
+       and eunit and erlc will converge so that I get correctly displayed
  160
+       types in edoc and no warnings in erlc etc?
  161
+
  162
+    3) Does anything else have to be fixed?
  163
+
  164
+    4) Improvements..
  165
+
  166
+       I can think of one. I have some code to convert .erl to
  167
+       .html with correctly colored code and hyperlinks etc.
  168
+       So I can "surf" the code. It would be nice to have hooks
  169
+       into edoc so I can call this code
  170
+
  171
+That's all for now ...
  172
+
  173
+/Joe
  174
+
  175
+
  176
+    
  177
+     
35  src/book.book
... ...
@@ -0,0 +1,35 @@
  1
+= An Erlang Library
  2
+
  3
+This book describes an Erlang library called ''elib1''.
  4
+
  5
+== Todo (formatting program)
  6
+
  7
+<ul>
  8
++ Add a diagram language (inline svg)
  9
++ ~~Make the Book version~~
  10
++ ~~Make compact lists, so I can leave out the blank line in paragraphs like
  11
+this~~
  12
++ ~~add processing of ''footnotes''~~
  13
++ Make commands for /bin
  14
++ check that all list entries do not end with a dot.
  15
++ Check that all list entries start with a big letter.
  16
++ punctuation check.
  17
++ ~~add "typographic quotes."~~
  18
++ Nice to make paragraphs "clickable-editable" in the browser.
  19
++ Add some nive paragraph hover effect that colors paras as we move over them. Makes text easier to read.
  20
+</ul>
  21
+
  22
+== To do (programs)
  23
+
  24
+<ul>
  25
++ mysql full text searching and metadata
  26
++ go through all old library adding good stuff (example topological sort)
  27
++ javascript slide show from wiki markup
  28
++ move over to GIT
  29
++ xref
  30
+</ul>
  31
+
  32
+<include chapter="design" />
  33
+<include chapter="ezxml" />
  34
+<include chapter="lorum" />
  35
+
0  src/chap2html.xsl
No changes.
457  src/chap2pdf.xsl
... ...
@@ -0,0 +1,457 @@
  1
+<?xml version="1.0" encoding="utf-8"?>
  2
+<xsl:stylesheet 
  3
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  4
+  xmlns:fo="http://www.w3.org/1999/XSL/Format"
  5
+  version="1.0">
  6
+  <xsl:output method="xml"/>
  7
+
  8
+  <xsl:template match="/">
  9
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
  10
+      <fo:layout-master-set>                  
  11
+	<fo:simple-page-master  
  12
+	   master-name="simple"                 
  13
+           page-height  ="29.7cm"   
  14
+           page-width   ="21cm"
  15
+           margin-left  ="2.5cm"
  16
+	   margin-right ="2.5cm">
  17
+
  18
+	  <fo:region-body
  19
+	     region-name="Content"
  20
+	     margin-top="1.0cm" margin-bottom="1in"
  21
+	     />
  22
+    
  23
+	  <fo:region-before extent=".8cm"/>
  24
+	  <fo:region-after extent=".5in" background-color="silver"/>
  25
+    	</fo:simple-page-master>
  26
+      </fo:layout-master-set> 
  27
+      <fo:page-sequence 
  28
+	 master-reference="simple">  
  29
+	
  30
+	<fo:static-content flow-name="xsl-region-before">
  31
+	  <fo:block>
  32
+	    <fo:inline keep-together.within-line="always">
  33
+              <fo:leader leader-pattern="rule" 
  34
+			 leader-length="2.95cm"/>
  35
+	      <fo:inline vertical-align="sub">
  36
+		Chapter NNNNN
  37
+	      </fo:inline>
  38
+	      <fo:leader leader-pattern="rule"
  39
+			 leader-length="2.95cm" /></fo:inline>
  40
+	  </fo:block>
  41
+	</fo:static-content>
  42
+	
  43
+	<fo:flow flow-name="Content">  
  44
+	  <xsl:apply-templates/>              
  45
+	</fo:flow>
  46
+      </fo:page-sequence>
  47
+    </fo:root>
  48
+  </xsl:template>
  49
+  
  50
+  <xsl:template match="chap">          
  51
+    <xsl:apply-templates/>
  52
+  </xsl:template>
  53
+  
  54
+  <xsl:template match="h2">        
  55
+    <fo:block  font-size="14pt"
  56
+	       font-weight="bold"
  57
+	       space-before="1em"
  58
+               space-after="0.5em"	
  59
+               space-after.conditionality = 'retain'
  60
+	       >
  61
+      <xsl:apply-templates/>
  62
+    </fo:block>
  63
+  </xsl:template>
  64
+
  65
+  <!-- this is my h1 -->
  66
+  
  67
+  <xsl:template match="title">        
  68
+    <fo:block  
  69
+       margin-top="4mm"
  70
+       margin-bottom="4mm"
  71
+       padding-before="0.4em"
  72
+       padding-after="0.4em"
  73
+       text-align="center"
  74
+       background-color="black" 
  75
+       color="white" 
  76
+       font-family="Times" 
  77
+       font-size="30pt"
  78
+       span = "all"
  79
+       font-weight="bold"
  80
+       space-after="1cm">
  81
+      <xsl:apply-templates/>
  82
+    </fo:block>
  83
+  </xsl:template>
  84
+
  85
+  <xsl:template match="strike">
  86
+    <fo:inline text-decoration="line-through">
  87
+      <xsl:apply-templates select="*|text()"/>
  88
+    </fo:inline>
  89
+  </xsl:template>
  90
+  
  91
+  <xsl:template match="note"> 
  92
+    <fo:block border-style="solid"
  93
+	      margin="0.2cm"
  94
+	      border-width="1px"
  95
+	      padding="4mm"
  96
+	      text-align="justify"  
  97
+	      font-family="Helvetica"> 
  98
+      <xsl:apply-templates/>
  99
+    </fo:block>
  100
+  </xsl:template>
  101
+  
  102
+  
  103
+  <xsl:template match="warn"> 
  104
+    <fo:block margin="0px" padding="8px" background-color="#aabbcc"> 
  105
+      <xsl:apply-templates/>
  106
+    </fo:block>
  107
+  </xsl:template>
  108
+  
  109
+  <xsl:template match="box"> 
  110
+    <fo:block border-style="solid"
  111
+	      margin="0.2cm"
  112
+	      border-width="5px"
  113
+	      border-color="red"
  114
+	      padding="4mm"
  115
+	      text-align="justify"  
  116
+	      font-family="Helvetica"> 
  117
+      <xsl:apply-templates/>
  118
+    </fo:block>
  119
+  </xsl:template>
  120
+  
  121
+  <xsl:template match="p">
  122
+    <fo:block text-align="justify"  font-family="Times"> 
  123
+      <xsl:apply-templates/>
  124
+    </fo:block>
  125
+  </xsl:template>
  126
+
  127
+
  128
+  <!-- ============================================
  129
+    We handle an ordered list with two complications:
  130
+    If the list appears inside another list (either 
  131
+    an <ol> or <ul>), we don't put any vertical space
  132
+    after it.  The other issue is that we indent the
  133
+    list according to how deeply nested the list is. 
  134
+    =============================================== -->
  135
+
  136
+  <xsl:template match="ol">
  137
+    <fo:list-block provisional-distance-between-starts="1cm"
  138
+      provisional-label-separation="0.5cm">
  139
+      <xsl:attribute name="space-after">
  140
+        <xsl:choose>
  141
+          <xsl:when test="ancestor::ul or ancestor::ol">
  142
+            <xsl:text>0pt</xsl:text>
  143
+          </xsl:when>
  144
+          <xsl:otherwise>
  145
+            <xsl:text>12pt</xsl:text>
  146
+          </xsl:otherwise>
  147
+        </xsl:choose>
  148
+      </xsl:attribute>
  149
+      <xsl:attribute name="start-indent">
  150
+        <xsl:variable name="ancestors">
  151
+          <xsl:choose>
  152
+            <xsl:when test="count(ancestor::ol) or count(ancestor::ul)">
  153
+              <xsl:value-of select="1 + 
  154
+                                    (count(ancestor::ol) + 
  155
+                                     count(ancestor::ul)) * 
  156
+                                    1.25"/>
  157
+            </xsl:when>
  158
+            <xsl:otherwise>
  159
+              <xsl:text>1</xsl:text>
  160
+            </xsl:otherwise>
  161
+          </xsl:choose>
  162
+        </xsl:variable>
  163
+        <xsl:value-of select="concat($ancestors, 'cm')"/>
  164
+      </xsl:attribute>
  165
+      <xsl:apply-templates select="*"/>
  166
+    </fo:list-block>
  167
+  </xsl:template>
  168
+
  169
+  <!-- ============================================
  170
+    When we handle items in an ordered list, we need
  171
+    to check if the list has a start attribute.  If
  172
+    it does, we change the starting number accordingly.
  173
+    Once we've figured out where to start counting,
  174
+    we check the type attribute to see what format
  175
+    the numbers should use.  
  176
+    =============================================== -->
  177
+
  178
+  <xsl:template match="ol/li">
  179
+    <fo:list-item>
  180
+      <fo:list-item-label end-indent="label-end()">
  181
+        <fo:block>
  182
+          <xsl:variable name="value-attr">
  183
+            <xsl:choose>
  184
+              <xsl:when test="../@start">
  185
+                <xsl:number value="position() + ../@start - 1"/>
  186
+              </xsl:when>
  187
+              <xsl:otherwise>
  188
+                <xsl:number value="position()"/>
  189
+              </xsl:otherwise>
  190
+            </xsl:choose>
  191
+          </xsl:variable>
  192
+          <xsl:choose>
  193
+            <xsl:when test="../@type='i'">
  194
+              <xsl:number value="$value-attr" format="i. "/>
  195
+            </xsl:when>
  196
+            <xsl:when test="../@type='I'">
  197
+              <xsl:number value="$value-attr" format="I. "/>
  198
+            </xsl:when>
  199
+            <xsl:when test="../@type='a'">
  200
+              <xsl:number value="$value-attr" format="a. "/>
  201
+            </xsl:when>
  202
+            <xsl:when test="../@type='A'">
  203
+              <xsl:number value="$value-attr" format="A. "/>
  204
+            </xsl:when>
  205
+            <xsl:otherwise>
  206
+              <xsl:number value="$value-attr" format="1. "/>
  207
+            </xsl:otherwise>
  208
+          </xsl:choose>
  209
+        </fo:block>
  210
+      </fo:list-item-label>
  211
+      <fo:list-item-body start-indent="body-start()">
  212
+        <fo:block>
  213
+          <xsl:apply-templates select="*|text()"/>
  214
+        </fo:block>
  215
+      </fo:list-item-body>
  216
+    </fo:list-item>
  217
+  </xsl:template>
  218
+
  219
+  <!-- ============================================
  220
+    The unordered list is pretty straightforward; 
  221
+    the only complication is calculating the space-
  222
+    after and start-indent properties.  If this 
  223
+    list is inside another list, we don't put any 
  224
+    space after this one, and we calculate the 
  225
+    indentation based on the nesting level of this 
  226
+    list. 
  227
+    =============================================== -->
  228
+
  229
+  <xsl:template match="ul">
  230
+    <fo:list-block provisional-distance-between-starts="1cm"
  231
+      provisional-label-separation="0.5cm">
  232
+      <xsl:attribute name="space-after">
  233
+        <xsl:choose>
  234
+          <xsl:when test="ancestor::ul or ancestor::ol">
  235
+            <xsl:text>0pt</xsl:text>
  236
+          </xsl:when>
  237
+          <xsl:otherwise>
  238
+            <xsl:text>12pt</xsl:text>
  239
+          </xsl:otherwise>
  240
+        </xsl:choose>
  241
+      </xsl:attribute>
  242
+      <xsl:attribute name="start-indent">
  243
+        <xsl:variable name="ancestors">
  244
+          <xsl:choose>
  245
+            <xsl:when test="count(ancestor::ol) or count(ancestor::ul)">
  246
+              <xsl:value-of select="1 + 
  247
+                                    (count(ancestor::ol) + 
  248
+                                     count(ancestor::ul)) * 
  249
+                                    1.25"/>
  250
+            </xsl:when>
  251
+            <xsl:otherwise>
  252
+              <xsl:text>1</xsl:text>
  253
+            </xsl:otherwise>
  254
+          </xsl:choose>
  255
+        </xsl:variable>
  256
+        <xsl:value-of select="concat($ancestors, 'cm')"/>
  257
+      </xsl:attribute>
  258
+      <xsl:apply-templates select="*"/>
  259
+    </fo:list-block>
  260
+  </xsl:template>
  261
+
  262
+
  263
+  <!-- ============================================
  264
+    Preformatted text is rendered in a monospaced
  265
+    font.  We also have to set the wrap-option
  266
+    and white-space-collapse properties.  
  267
+    =============================================== -->
  268
+
  269
+  <xsl:template match="pre">
  270
+    <fo:block font-family="monospace"
  271
+	      linefeed-treatment="preserve"
  272
+	      white-space-collapse="false" 
  273
+	      white-space-treatment="preserve" 
  274
+	      >
  275
+      <xsl:apply-templates select="*|text()"/>
  276
+    </fo:block>
  277
+  </xsl:template>
  278
+
  279
+  <!-- ============================================
  280
+    We don't do anything with the <dl> element, we
  281
+    just handle the elements it contains.  Notice
  282
+    that we're ignoring any text that appears 
  283
+    in the <dl> itself; I'm not sure if that's
  284
+    the right call.
  285
+    =============================================== -->
  286
+
  287
+  <xsl:template match="dl">
  288
+    <xsl:apply-templates select="*"/>
  289
+  </xsl:template>
  290
+
  291
+  <!-- ============================================
  292
+    A definition term is rendered in bold.  We 
  293
+    specify keep-with-next here, although it doesn't
  294
+    always work with FOP.
  295
+    =============================================== -->
  296
+
  297
+  <xsl:template match="dt">
  298
+    <fo:block font-weight="bold" space-after="2pt"
  299
+      keep-with-next="always">
  300
+      <xsl:apply-templates select="*|text()"/>
  301
+    </fo:block>
  302
+  </xsl:template>
  303
+
  304
+  <!-- ============================================
  305
+    We handle each <dd> element as an indented block
  306
+    beneath the defined term.  If the following 
  307
+    element is another <dd>, that means it's another
  308
+    definition for the same term.  In that case, 
  309
+    we don't put as much vertical space after the 
  310
+    block. 
  311
+    =============================================== -->
  312
+
  313
+  <xsl:template match="dd">
  314
+    <fo:block start-indent="1cm">
  315
+      <xsl:attribute name="space-after">
  316
+        <xsl:choose>
  317
+          <xsl:when test="name(following::*[1]) = 'dd'">
  318
+            <xsl:text>3pt</xsl:text>
  319
+          </xsl:when>
  320
+          <xsl:otherwise>
  321
+            <xsl:text>12pt</xsl:text>
  322
+          </xsl:otherwise>
  323
+        </xsl:choose>
  324
+      </xsl:attribute>
  325
+      <xsl:apply-templates select="*|text()"/>
  326
+    </fo:block>
  327
+  </xsl:template>
  328
+
  329
+  <!-- ============================================
  330
+    List items inside unordered lists are easy; we
  331
+    just have to use the correct Unicode character
  332
+    for the bullet.  
  333
+    =============================================== -->
  334
+
  335
+  <xsl:template match="ul/li">
  336
+    <fo:list-item>
  337
+      <fo:list-item-label end-indent="label-end()">
  338
+        <fo:block>&#x2022;</fo:block>
  339
+      </fo:list-item-label>
  340
+      <fo:list-item-body start-indent="body-start()">
  341
+        <fo:block>
  342
+          <xsl:apply-templates select="*|text()"/>
  343
+        </fo:block>
  344
+      </fo:list-item-body>
  345
+    </fo:list-item>
  346
+  </xsl:template>
  347
+
  348
+
  349
+  <!-- ============================================
  350
+    Teletype text is rendered in a monospaced font.
  351
+    =============================================== -->
  352
+
  353
+  <xsl:template match="c">
  354
+    <fo:inline font-family="monospace">
  355
+      <xsl:apply-templates select="*|text()"/>
  356
+    </fo:inline>
  357
+  </xsl:template>
  358
+ 
  359
+
  360
+  <!-- ============================================
  361
+    For bold elements, we just change the font-weight.
  362
+    =============================================== -->
  363
+
  364
+  <xsl:template match="b">
  365
+    <fo:inline font-weight="bold">
  366
+      <xsl:apply-templates select="*|text()"/>
  367
+    </fo:inline>
  368
+  </xsl:template>
  369
+
  370
+
  371
+ <!-- ============================================
  372
+    Italics.  You can't get much simpler than that.
  373
+    =============================================== -->
  374
+
  375
+  <xsl:template match="i">
  376
+    <fo:inline font-style="italic">
  377
+      <xsl:apply-templates select="*|text()"/>
  378
+    </fo:inline>
  379
+  </xsl:template>
  380
+  
  381
+  <xsl:template match="by">
  382
+    <fo:inline
  383
+       padding='.3mm'
  384
+       font-style="bold"
  385
+       background-color="orange">
  386
+      <xsl:apply-templates select="*|text()"/>
  387
+    </fo:inline>
  388
+  </xsl:template>
  389
+
  390
+ 
  391
+  <!-- ============================================
  392
+    For underlined text, we use the text-decoration
  393
+    property.
  394
+    =============================================== -->
  395
+
  396
+  <xsl:template match="u">
  397
+    <fo:inline text-decoration="underline">
  398
+      <xsl:apply-templates select="*|text()"/>
  399
+    </fo:inline>
  400
+  </xsl:template>
  401
+
  402
+
  403
+    
  404
+  <!-- ============================================
  405
+    For the <img> element, we use the src attribute
  406
+    as it comes from HTML.  We also check for any 
  407
+    width and height attributes.  If those attributes
  408
+    are there, we try to use them; height="300px" is
  409
+    used as-is, while height="300" is converted to 
  410
+    the value "300px".
  411
+    =============================================== -->
  412
+
  413
+  <xsl:template match="img">
  414
+    <fo:block  text-align="center" space-after="12pt">
  415
+      <fo:external-graphic src="{@src}">
  416
+        <xsl:if test="@width">
  417
+          <xsl:attribute name="content-width">
  418
+            <xsl:choose>
  419
+              <xsl:when test="contains(@width, 'px')">
  420
+                <xsl:value-of select="@width"/>
  421
+              </xsl:when>
  422
+              <xsl:otherwise>
  423
+                <xsl:value-of select="concat(@width, 'px')"/>
  424
+              </xsl:otherwise>
  425
+            </xsl:choose>
  426
+          </xsl:attribute>
  427
+        </xsl:if>
  428
+        <xsl:if test="@height">
  429
+          <xsl:attribute name="content-height">
  430
+            <xsl:choose>
  431
+              <xsl:when test="contains(@height, 'px')">
  432
+                <xsl:value-of select="@height"/>
  433
+              </xsl:when>
  434
+              <xsl:otherwise>
  435
+                <xsl:value-of select="concat(@height, 'px')"/>
  436
+              </xsl:otherwise>
  437
+            </xsl:choose>
  438
+          </xsl:attribute>
  439
+        </xsl:if>
  440
+      </fo:external-graphic>
  441
+    </fo:block>
  442
+  </xsl:template>
  443
+
  444
+  <xsl:template match="em">             
  445
+    <fo:inline font-style="italic">
  446
+      <xsl:apply-templates/>
  447
+    </fo:inline>
  448
+  </xsl:template>
  449
+ 
  450
+  <xsl:template match="*">          
  451
+    <fo:block background-color="red">
  452
+      <xsl:apply-templates/>
  453
+    </fo:block>
  454
+  </xsl:template>
  455
+
  456
+</xsl:stylesheet>
  457
+
154  src/design.chap1
... ...
@@ -0,0 +1,154 @@
  1
+= Projects
  2
+
  3
+Before you can do ''anything at all'' you need to impose a little order
  4
+on your masterwork. Any project that is destined to be ''the next great hack''
  5
+must have ''some structure.'' The temptation to ''just start hacking'' is
  6
+enormous, but there is really bit of infrastructure that is needed before
  7
+you start hacking. Having said this, I must admit it is difficult
  8
+to follow the rules, you really want to start hacking ''immediately''
  9
+but it's a good ides to have some basic infrastructure in-place before you start.
  10
+The larger the project  becomes, the more important this is.
  11
+
  12
+Here's the order I've decided upon:
  13
+
  14
+<ol>
  15
++ I'll use and must define  a fixed and defined directory structure.
  16
++ I need a documentation tool to produce ~~nice~~ beautiful documentation
  17
++ I need a search tool and meta-data in my code
  18
++ I need Xref - types etc to check my code
  19
+</ol>
  20
+
  21
+Why do I want these things in-place before I start hacking?
  22
+
  23
+<dl> 
  24
+[directory structures] Should I put everything in one directory
  25
+or define a directory structure and force my files into this
  26
+structure?  This is a difficult question. If I had a very small
  27
+project involving only a few files I would obviously stick everything
  28
+into a single directory---anything else would be overkill. For large
  29
+projects this doesn't work, so we need a directory structure. It is
  30
+best to think about and design this directory structure ''before''
  31
+writing the code. 
  32
+
  33
+This is especially important in a multi-person
  34
+project. The folks in the project need to know in which directories
  35
+they should put their code.
  36
+
  37
+[documentation] We need a way to write documentation. ~~Preferably this
  38
+ill result in ''beautiful'' documentation.~~ This ''must'' produce
  39
+''beautiful'' documentation (so that people will want to read it)---the
  40
+documentation should be a "pleasure" to read. The documentation system
  41
+needs to be in-place ''before'' we write the project code, so we need
  42
+pre-phase to set this up (or use an existing documentation system).
  43
+The documentation system should be tied to the code base so that
  44
+relations between the code and the documentation can be automated.
  45
+
  46
+[finding stuff] As projects grow the difficulty in finding stuff
  47
+increases.  When you have ten thousand files you begin to wish that
  48
+every document had been tagged with meta data and index in a search
  49
+engine. Now is too late.  Retrofitting meta data to large projects
  50
+never works (or if it does it requires massive effort). So right from
  51
+the beginning we should be thinking about the meta data we will need in
  52
+our files and how we will store and find this data.
  53
+
  54
+[finding errors at compile time] There are many tools that can help us
  55
+find errors at compile time. Cross reference tools, type inference
  56
+tools, unit test frameworks. These tools are being continually
  57
+improved, but how they are used and how they are customised to just
  58
+''your'' project needs thinking about. It's better to set this stuff
  59
+up ''before'' you get started.  When you are in the ,middle of your
  60
+project you certainly won't have time for this.
  61
+</dl>
  62
+
  63
+== Layout
  64
+
  65
+Let's have some <<code>> here, and ''italics''.
  66
+We can ~~strike~~ some text.
  67
+
  68
+We have the following sub-directories:
  69
+  
  70
+<dl>
  71
+[src] Source code. This is restricted to the following file types:
  72
+  <<.erl>>, <<.chap>> <<.book>>, <<.hrl>>. The content of this file
  73
+  are not actually intended to be read ''directly'', by this we
  74
+  mean we will always read content that is automatically generated
  75
+  from this directory. Files generated in the compilation process, or,
  76
+  for example while generating documentation are not in this
  77
+  directory.  
  78
+
  79
+[ebin] <<.beam>> files. These are put in one directory so that we can
  80
+  point search paths to this directory. We might add additional
  81
+  executables to this directory. At any time the entire contents of
  82
+  this directory can be deleted without problems.
  83
+
  84
+[doc] Generated documentation. This will contain (for example)
  85
+  <<.pdf>> and <<.html>> files. These files should not be hand-edited.
  86
+  The contents of this directory can be deleted.
  87
+
  88
+[save] The contents of this directory must not be deleted. This might
  89
+  for example have a database that records all edits to the system
  90
+  etc.
  91
+
  92
+[tmp] Temporary files. There may be a large number of files here don't
  93
+  worry, the entire contents can safely be deleted.
  94
+
  95
+[bin] Programs, scripts that can be run. These aren't in ebin these
  96
+  are the top-level commands that the user can execute.  
  97
+</dl>
  98
+
  99
+== Programs in bin
  100
+
  101
+<dl> 
  102
+[bin/publish X] Takes a file <<X.lit>> file in the current directory
  103
+  and produces two beautiful files <<X.html>> and <<X.pdf>> in the
  104
+  <<../doc>> directory. Temporary files are put in <<../tmp>>. This
  105
+  command should leave no crud in the current directory. If there are
  106
+  syntax errors then they will be written somewhere.
  107
+</dl>
  108
+
  109
+ 
  110
+== Design
  111
+
  112
+The documentation system uses two commands <<bin/mkchap>> and <<bin/mkbook>>.
  113
+
  114
+<dl>
  115
+[mkchap X.chap] is run in the <<src>> directory. If there are no
  116
+errors in <<X.cap>> then the following files
  117
+are created <<../tmp/X.inc>>, <<../tmp/X.tex>>, <<../doc/X.html>>,
  118
+and <<../doc/X.pdf>>. If anything goes wrong <warn>more here</warn>.
  119
+
  120
+[mkbook X.book] is run in the <<src>> directory. If there are no
  121
+errors in <<X.book>> then the following files
  122
+are created <<../tmp/X.inc>>, <<../tmp/X.tex>>, <<../doc/X.html>>,
  123
+and <<../doc/X.pdf>>. If anything goes wrong <warn>more here</warn>.
  124
+</dl>
  125
+
  126
+The workhorse of the system is <<mkcap>> this converts ''wiki text''
  127
+into PDF and HTML. In writing <<mkchap>> I have adopted a
  128
+''minimalistic approach'', namely:
  129
+
  130
+<ul>
  131
++ Reuse as much code as possible.
  132
++ Write as little as possible
  133
++ Only implement what I need for the documents I have written
  134
++ Make the output look nice
  135
+</ul>
  136
+
  137
+<include file="elib1_docmaker.erl" tag="tag1" />
  138
+
  139
+=== Parse tree of DL
  140
+
  141
+
  142
+<pre>
  143
+[aaa]p1
  144
+[bbb]p1
  145
+p2
  146
+...
  147
+</pre>
  148
+
  149
+is:
  150
+
  151
+<pre>
  152
+{dl, [{tag,"aaa",[{p,p1},{p,P2}]},
  153
+      {tag, "bbb",[{p,p1}]}}
  154
+</pre>
33  src/edit.html
... ...
@@ -0,0 +1,33 @@
  1
+<title>edit</title>
  2
+<link rel="stylesheet" href="./me.css" type="text/css" media="screen"/>
  3
+
  4
+<div class="box1">
  5
+<a id="commentForm"></a>
  6
+
  7
+<form method="post" action="http://meAuto-Detach" accept-charset="utf-8"
  8
+  id="comments_form" onsubmit="if (this.bakecookie.checked) rememberMe(this)">
  9
+  <input name="parent" type="hidden" value="3009"/>
  10
+  <input name="title" type="hidden" value="Auto Detach"/>
  11
+
  12
+  <table>
  13
+    <tr>
  14
+      <td><label for="name">Title:</label></td>
  15
+      <td><input id="name" name="name" size="40" type="text" value=""/></td>
  16
+    </tr>
  17
+    <tr>
  18
+      <td><label for="name">Keywords:</label></td>
  19
+      <td><input id="name" name="name"  size="40" type="text" value=""/></td>
  20
+    </tr>
  21
+    <tr>
  22
+      <td colspan="2">
  23
+	<textarea cols="72" id="comment" name="comment" rows="15"></textarea>
  24
+      </td>
  25
+    </tr>
  26
+    <tr>
  27
+      <td colspan="2">
  28
+	<input name="preview" type="submit" value="Preview"/>
  29
+      </td>
  30
+    </tr>
  31
+  </table>
  32
+</form>
  33
+</div>
44  src/elib1_best.erl
... ...
@@ -0,0 +1,44 @@
  1
+%% Copyright (c) 2006-2009 Joe Armstrong
  2
+%% See MIT-LICENSE for licensing information.
  3
+
  4
+%% ^ [4]
  5
+
  6
+-module(elib1_best). %% [1]
  7
+
  8
+%% elib1_best: Best practice template for library modules [2]
  9
+%% Time-stamp: <2009-12-02 12:28:04 ejoearm> [3]
  10
+
  11
+-include_lib("eunit/include/eunit.hrl"). %% [5]
  12
+
  13
+-export([fac/1]).  %% [6]
  14
+
  15
+
  16
+%% @doc fac(N) computes factorial(N) using a fast iterative algorithm. [7]
  17
+
  18
+-spec fac(non_neg_integer()) -> non_neg_integer(). %% [8]
  19
+
  20
+fac(N) when is_integer(N), N >= 0 -> fac1(N, 1).
  21
+
  22
+fac1(0, K) -> K;
  23
+fac1(N, K) -> fac1(N-1, K*N).
  24
+
  25
+fac_test() ->  %% [9]
  26
+    6  = fac(3),
  27
+    24 = fac(4).
  28
+
  29
+%% Notes:
  30
+%% [1] - module on line 1
  31
+%% [2] - module comment
  32
+%% [3] - Time stamp auto genertaed by emacs. Must be near start of file
  33
+%% [4] - Copyright (I always forget this, but adding a ciopyright reduces
  34
+%%       the pain later
  35
+%% [5] - Needed for eunit
  36
+%% [6] - use export and NOT compile(export_all)
  37
+%% [7] - @doc comes first
  38
+%% [8] - -spec comes immediately *before* the function
  39
+%% [9] - test cases come immediatly after the function
  40
+
  41
+%% end of module
  42
+
  43
+    
  44
+    
43  src/elib1_blob_store.erl
... ...
@@ -0,0 +1,43 @@
  1
+%% Copyright (c) 2006-2009 Joe Armstrong
  2
+%% See MIT-LICENSE for licensing information.
  3
+
  4
+-module(elib1_blob_store).
  5
+
  6
+%% The guid store is a two level store
  7
+%%
  8
+%% M:init(File)
  9
+%% M:store(Key, Blob)
  10
+%% M:fetch(Key) -> Blob raises eNoKey
  11
+%% M:keys() -> [Key]
  12
+
  13
+-export([open/1, close/0, fetch/1, store/2, keys/0]).
  14
+
  15
+open(File) ->
  16
+    %% io:format("dets opened:~p~n", [File]),
  17
+    case dets:open_file(?MODULE, [{file, File}]) of
  18
+	{ok, ?MODULE} ->
  19
+	    true;
  20
+	{error,_Reason} ->
  21
+	    io:format("cannot open dets table~n"),
  22
+	    exit(eDetsOpen)
  23
+    end.
  24
+
  25
+close() -> dets:close(?MODULE).
  26
+
  27
+store(Key, Blob) when is_binary(Blob) ->
  28
+    %% io:format("storing blob key=~p size=~p~n",[Key,size(Blob)]),
  29
+    ok = dets:insert(?MODULE, [{Key,Blob}]).
  30
+
  31
+fetch(Key) ->
  32
+    case dets:lookup(?MODULE, Key) of
  33
+	[]         -> error;
  34
+	[{_,Blob}] -> {ok, Blob}
  35
+    end.
  36
+
  37
+keys() ->
  38
+    %% I guess there is a better way of doing this ...
  39
+    dets:foldl(fun({K,_},A) -> [K|A] end,[],?MODULE).
  40
+
  41
+		       
  42
+    
  43
+			   
356  src/elib1_chunks.erl
... ...
@@ -0,0 +1,356 @@
  1
+%% Copyright (c) 2006-2009 Joe Armstrong
  2
+%% See MIT-LICENSE for licensing information.
  3
+
  4
+-module(elib1_chunks).
  5
+-compile(export_all).
  6
+-import(lists, [filter/2, flatten/1, reverse/1]).
  7
+-import(elib1_misc, [dump/2]).
  8
+
  9
+%% convert .log file to .html
  10
+%% by converting each chunk in the log file
  11
+
  12
+batch(X) ->
  13
+    try
  14
+	begin 
  15
+	    [A] = X,
  16
+	    File = atom_to_list(A),
  17
+	    convert(File)
  18
+	end
  19
+    catch
  20
+	P:Q ->
  21
+	    io:format("Some error ocurred ~p:~p ~p~n",
  22
+		      [P,Q,erlang:get_stacktrace()])
  23
+    end.
  24
+
  25
+file2chunks(File) ->
  26
+    Str = elib1_misc:file2string(File),
  27
+    str2chunks(Str).
  28
+
  29
+convert(File) ->
  30
+    Root = filename:rootname(File),
  31
+    Chunks = file2chunks(File),
  32
+    %% dump("logtmp", Chunks),
  33
+    H1 = [ chunk2html(C) || C <- Chunks],
  34
+    OutFile = "../doc/" ++ Root ++ ".html",
  35
+    elib1_misc:expand_file_template("me.template", 
  36
+				    [{"content", H1}], 
  37
+				    OutFile),
  38
+    io:format("created ~s~n",[OutFile]).
  39
+
  40
+chunk2html({chunk,Headers,Content}) ->
  41
+    div_box([headers2html(Headers), content2html(Content)]).
  42
+
  43
+content2html(Str) ->
  44
+    wikiA2html(parse_wiki_str(Str)).
  45
+
  46
+headers2html(L) ->
  47
+    [h2([atom_to_list(Key),": ",Val]) || {Key,Val} <- L].
  48
+
  49
+h2(X) ->
  50
+    ["<h2>",X,"</h2>\n"].
  51
+
  52
+div_box(X) ->
  53
+    ["<div class='box'>\n", X, "\n</div>"].
  54
+
  55
+
  56
+test1() ->
  57
+    str2chunks("@chunk\n@tag: abc\n\nabc\n\n+123\n+234\n\ndef").
  58
+
  59
+
  60
+str2chunks(Str) ->
  61
+    Str1 = elib1_misc:dos2unix(Str),
  62
+    parse_chunks(Str1).
  63
+
  64
+parse_chunks("@chunk" ++ _ = T) ->
  65
+    parse_chunks(T, 1, []);
  66
+parse_chunks(Str) ->
  67
+    io:format("**** string does not begin with @chunk~n"
  68
+	      " starts:~s ...~n"
  69
+	      " skipping some data~n",[string:sub_string(Str,1,10)]),
  70
+    {_, Ln1, Str1} = collect_chunk(Str, 1, []),
  71
+    parse_chunks(Str1, Ln1, []).
  72
+
  73
+parse_chunks([], _, L) ->
  74
+    reverse(L);
  75
+parse_chunks("@chunk" ++ Str, Ln, L) ->
  76
+    {C, Ln1, Str1} = collect_chunk(Str, Ln, []),
  77
+    {Header, Body} = parse_chunk_headers(C, Ln, []),
  78
+    parse_chunks(Str1, Ln1, [{chunk,Header,Body}|L]).
  79
+
  80
+parse_chunk_headers("\n\n" ++ T, _Ln, L) ->
  81
+    {reverse(L), T};
  82
+parse_chunk_headers("\n@" ++ T, Ln, L) ->
  83
+    %% io:format("isolate tag:~p~n",[T]),
  84
+    {Tag, T1} = isolate_meta_tag(T, Ln, []),
  85
+    %% io:format("Tag=~p T1=~s~n",[Tag, string:sub_string(T1,1,10)]),
  86
+    {Body, Ln1, T2} = collect_tag_body(T1, Ln+1, []),
  87
+    parse_chunk_headers(T2, Ln1, [{Tag,trim(Body)}|L]).
  88
+
  89
+trim(X) ->
  90
+    elib1_misc:remove_leading_and_trailing_whitespace(X).
  91
+
  92
+-define(IN(A,X,B), A =< X, X =< B).
  93
+
  94
+isolate_meta_tag([H|T], Ln, L) when ?IN($a,H,$z) ; ?IN($A,H,$Z) ->
  95
+    isolate_meta_tag(T, Ln, [H|L]);
  96
+isolate_meta_tag([$:|T], _, L) ->
  97
+    {list_to_atom(reverse(L)), T};
  98
+isolate_meta_tag(Str, Ln, L) ->
  99
+    exit({ebadTag,Ln,string:sub_string(Str,1,10),reverse(L)}).
  100
+
  101
+collect_tag_body("\n@" ++ _ = T, Ln, L) -> {reverse(L), Ln, T};
  102
+collect_tag_body("\n\s" ++ T, Ln, L)    ->
  103
+    collect_tag_body(T, Ln+1, L);
  104
+collect_tag_body("\n\n" ++ _ = T, Ln, L) ->
  105
+    {reverse(L), Ln, T};
  106
+collect_tag_body("\n" ++ _T, Ln, L) ->
  107
+    exit({ebadTag, Ln, reverse(L)});
  108
+collect_tag_body([H|T], Ln, L) ->
  109
+    collect_tag_body(T, Ln, [H|L]);
  110
+collect_tag_body([], Ln, L) ->
  111
+    {reverse(L), Ln, []}.
  112
+
  113
+collect_chunk("\n@chunk" ++ _ = T, Ln, L) ->
  114
+    {reverse(L), Ln+1, tl(T)};
  115
+collect_chunk([$\n|T], Ln, L) ->
  116
+    collect_chunk(T, Ln+1, [$\n|L]);
  117
+collect_chunk([H|T], Ln, L) ->
  118
+    collect_chunk(T, Ln, [H|L]);
  119
+collect_chunk([], Ln, L) ->
  120
+    {reverse(L), Ln, []}.
  121
+
  122
+parse_wiki_str(Str) ->
  123
+    parse_str0(Str, 0).
  124
+    
  125
+parse_str0(Str, Ln) ->
  126
+    Pass1 = parse(Str, Ln, []),
  127
+    %% dump("t1", Pass1),
  128
+    Pass2 = flatten([pass2(I) || I <- Pass1]),
  129
+    [pass3(I) || I <- Pass2].
  130
+
  131
+pass2({str,_Ln,L})    -> split_into_paras(L);
  132
+pass2({dl,_Ln,L})     -> parse_dl(L);
  133
+pass2({pre,_Ln,L})    -> {pre,L};
  134
+pass2(X)              -> exit({pass2, X}).
  135
+
  136
+pass3({para,[]})         -> drop;
  137
+pass3({para,"+" ++ _ =T}) -> parse_list1(ol, "\n\\+", [$\n|T]);
  138
+pass3({para,"*" ++ _ =T}) -> parse_list1(ul, "\n\\*", [$\n|T]);
  139
+pass3({para,"=" ++ S}) -> parse_header(S, 1);
  140
+pass3({para,S})        -> parse_para(S);
  141
+pass3(X)               -> X.
  142
+
  143
+parse_list1(Tag, Re, Str) ->
  144
+    L = re:split(Str, Re, [{return,list}]),
  145
+    L1 = remove_blank_lines(L),
  146
+    L2 = [parse_para(I) || I <- L1],
  147
+    {Tag, L2}.
  148
+
  149
+parse_dl(S) ->
  150