sqlpostgres 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +22 -0
  3. data/LICENSE.md +23 -0
  4. data/README.rdoc +59 -0
  5. data/Rakefile +32 -0
  6. data/VERSION +1 -0
  7. data/doc/BUGS +2 -0
  8. data/doc/examples/README +6 -0
  9. data/doc/examples/connection.rb +16 -0
  10. data/doc/examples/connection_auto.rb +22 -0
  11. data/doc/examples/connection_ctor.rb +18 -0
  12. data/doc/examples/connection_default.rb +15 -0
  13. data/doc/examples/connection_exec.rb +18 -0
  14. data/doc/examples/connection_manual.rb +12 -0
  15. data/doc/examples/connection_wrapped_new.rb +13 -0
  16. data/doc/examples/connection_wrapped_open.rb +13 -0
  17. data/doc/examples/cursor.rb +38 -0
  18. data/doc/examples/include_module.rb +9 -0
  19. data/doc/examples/include_module2.rb +12 -0
  20. data/doc/examples/insert.rb +30 -0
  21. data/doc/examples/insert2.rb +36 -0
  22. data/doc/examples/insert_bytea.rb +16 -0
  23. data/doc/examples/insert_bytea_array.rb +17 -0
  24. data/doc/examples/insert_default_values.rb +16 -0
  25. data/doc/examples/insert_insert.rb +16 -0
  26. data/doc/examples/insert_insert_default.rb +16 -0
  27. data/doc/examples/insert_insert_select.rb +20 -0
  28. data/doc/examples/insert_select.rb +20 -0
  29. data/doc/examples/interval.rb +17 -0
  30. data/doc/examples/savepoint.rb +38 -0
  31. data/doc/examples/select.rb +33 -0
  32. data/doc/examples/select2.rb +36 -0
  33. data/doc/examples/select_cross_join.rb +18 -0
  34. data/doc/examples/select_distinct.rb +18 -0
  35. data/doc/examples/select_distinct_on +19 -0
  36. data/doc/examples/select_for_update.rb +18 -0
  37. data/doc/examples/select_from.rb +17 -0
  38. data/doc/examples/select_from_subselect.rb +20 -0
  39. data/doc/examples/select_group_by.rb +19 -0
  40. data/doc/examples/select_having.rb +20 -0
  41. data/doc/examples/select_join_on.rb +18 -0
  42. data/doc/examples/select_join_using.rb +18 -0
  43. data/doc/examples/select_limit.rb +19 -0
  44. data/doc/examples/select_natural_join.rb +18 -0
  45. data/doc/examples/select_offset.rb +19 -0
  46. data/doc/examples/select_order_by.rb +20 -0
  47. data/doc/examples/select_select.rb +30 -0
  48. data/doc/examples/select_select_alias.rb +30 -0
  49. data/doc/examples/select_select_expression.rb +31 -0
  50. data/doc/examples/select_select_literal.rb +24 -0
  51. data/doc/examples/select_union.rb +21 -0
  52. data/doc/examples/select_where_array.rb +18 -0
  53. data/doc/examples/select_where_in.rb +18 -0
  54. data/doc/examples/select_where_string.rb +18 -0
  55. data/doc/examples/simple.rb +34 -0
  56. data/doc/examples/transaction.rb +30 -0
  57. data/doc/examples/transaction_abort.rb +30 -0
  58. data/doc/examples/transaction_commit.rb +34 -0
  59. data/doc/examples/translate_substitute_values.rb +17 -0
  60. data/doc/examples/update.rb +32 -0
  61. data/doc/examples/update2.rb +44 -0
  62. data/doc/examples/update_only.rb +17 -0
  63. data/doc/examples/update_set.rb +17 -0
  64. data/doc/examples/update_set_array.rb +16 -0
  65. data/doc/examples/update_set_bytea.rb +16 -0
  66. data/doc/examples/update_set_expression.rb +16 -0
  67. data/doc/examples/update_set_subselect.rb +20 -0
  68. data/doc/examples/update_where.rb +17 -0
  69. data/doc/examples/use_prefix.rb +8 -0
  70. data/doc/examples/use_prefix2.rb +11 -0
  71. data/doc/index.html +31 -0
  72. data/doc/insertexamples.rb +9 -0
  73. data/doc/makemanual +4 -0
  74. data/doc/makerdoc +5 -0
  75. data/doc/manual.dbk +622 -0
  76. data/lib/sqlpostgres/Connection.rb +198 -0
  77. data/lib/sqlpostgres/Cursor.rb +157 -0
  78. data/lib/sqlpostgres/Delete.rb +67 -0
  79. data/lib/sqlpostgres/Exceptions.rb +15 -0
  80. data/lib/sqlpostgres/Insert.rb +279 -0
  81. data/lib/sqlpostgres/NullConnection.rb +22 -0
  82. data/lib/sqlpostgres/PgBit.rb +73 -0
  83. data/lib/sqlpostgres/PgBox.rb +37 -0
  84. data/lib/sqlpostgres/PgCidr.rb +21 -0
  85. data/lib/sqlpostgres/PgCircle.rb +75 -0
  86. data/lib/sqlpostgres/PgInet.rb +21 -0
  87. data/lib/sqlpostgres/PgInterval.rb +208 -0
  88. data/lib/sqlpostgres/PgLineSegment.rb +37 -0
  89. data/lib/sqlpostgres/PgMacAddr.rb +21 -0
  90. data/lib/sqlpostgres/PgPath.rb +64 -0
  91. data/lib/sqlpostgres/PgPoint.rb +65 -0
  92. data/lib/sqlpostgres/PgPolygon.rb +56 -0
  93. data/lib/sqlpostgres/PgTime.rb +77 -0
  94. data/lib/sqlpostgres/PgTimeWithTimeZone.rb +98 -0
  95. data/lib/sqlpostgres/PgTimestamp.rb +93 -0
  96. data/lib/sqlpostgres/PgTwoPoints.rb +54 -0
  97. data/lib/sqlpostgres/PgType.rb +34 -0
  98. data/lib/sqlpostgres/PgWrapper.rb +41 -0
  99. data/lib/sqlpostgres/Savepoint.rb +98 -0
  100. data/lib/sqlpostgres/Select.rb +855 -0
  101. data/lib/sqlpostgres/Transaction.rb +120 -0
  102. data/lib/sqlpostgres/Translate.rb +436 -0
  103. data/lib/sqlpostgres/Update.rb +188 -0
  104. data/lib/sqlpostgres.rb +67 -0
  105. data/test/Assert.rb +72 -0
  106. data/test/Connection.test.rb +246 -0
  107. data/test/Cursor.test.rb +190 -0
  108. data/test/Delete.test.rb +68 -0
  109. data/test/Insert.test.rb +123 -0
  110. data/test/MockPGconn.rb +62 -0
  111. data/test/NullConnection.test.rb +32 -0
  112. data/test/PgBit.test.rb +98 -0
  113. data/test/PgBox.test.rb +108 -0
  114. data/test/PgCidr.test.rb +61 -0
  115. data/test/PgCircle.test.rb +107 -0
  116. data/test/PgInet.test.rb +61 -0
  117. data/test/PgInterval.test.rb +180 -0
  118. data/test/PgLineSegment.test.rb +108 -0
  119. data/test/PgMacAddr.test.rb +61 -0
  120. data/test/PgPath.test.rb +106 -0
  121. data/test/PgPoint.test.rb +100 -0
  122. data/test/PgPolygon.test.rb +95 -0
  123. data/test/PgTime.test.rb +120 -0
  124. data/test/PgTimeWithTimeZone.test.rb +117 -0
  125. data/test/PgTimestamp.test.rb +134 -0
  126. data/test/RandomThings.rb +25 -0
  127. data/test/Savepoint.test.rb +286 -0
  128. data/test/Select.test.rb +930 -0
  129. data/test/Test.rb +62 -0
  130. data/test/TestConfig.rb +21 -0
  131. data/test/TestSetup.rb +13 -0
  132. data/test/TestUtil.rb +92 -0
  133. data/test/Transaction.test.rb +275 -0
  134. data/test/Translate.test.rb +354 -0
  135. data/test/Update.test.rb +227 -0
  136. data/test/roundtrip.test.rb +565 -0
  137. data/test/test +34 -0
  138. data/tools/exampleinserter/ExampleInserter.rb +177 -0
  139. data/tools/rdoc/ChangeLog +796 -0
  140. data/tools/rdoc/EXAMPLE.rb +48 -0
  141. data/tools/rdoc/MANIFEST +58 -0
  142. data/tools/rdoc/Makefile +27 -0
  143. data/tools/rdoc/NEW_FEATURES +226 -0
  144. data/tools/rdoc/README +390 -0
  145. data/tools/rdoc/ToDo +6 -0
  146. data/tools/rdoc/contrib/Index +6 -0
  147. data/tools/rdoc/contrib/xslfo/ChangeLog +181 -0
  148. data/tools/rdoc/contrib/xslfo/README +106 -0
  149. data/tools/rdoc/contrib/xslfo/TODO +10 -0
  150. data/tools/rdoc/contrib/xslfo/convert.xsl +151 -0
  151. data/tools/rdoc/contrib/xslfo/demo/README +21 -0
  152. data/tools/rdoc/contrib/xslfo/demo/rdocfo +99 -0
  153. data/tools/rdoc/contrib/xslfo/fcm.xsl +54 -0
  154. data/tools/rdoc/contrib/xslfo/files.xsl +62 -0
  155. data/tools/rdoc/contrib/xslfo/labeled-lists.xsl +66 -0
  156. data/tools/rdoc/contrib/xslfo/lists.xsl +44 -0
  157. data/tools/rdoc/contrib/xslfo/modules.xsl +152 -0
  158. data/tools/rdoc/contrib/xslfo/rdoc.xsl +75 -0
  159. data/tools/rdoc/contrib/xslfo/source.xsl +66 -0
  160. data/tools/rdoc/contrib/xslfo/styles.xsl +69 -0
  161. data/tools/rdoc/contrib/xslfo/tables.xsl +67 -0
  162. data/tools/rdoc/contrib/xslfo/utils.xsl +21 -0
  163. data/tools/rdoc/debian/changelog +33 -0
  164. data/tools/rdoc/debian/compat +1 -0
  165. data/tools/rdoc/debian/control +20 -0
  166. data/tools/rdoc/debian/copyright +10 -0
  167. data/tools/rdoc/debian/dirs +2 -0
  168. data/tools/rdoc/debian/docs +2 -0
  169. data/tools/rdoc/debian/rdoc.1 +252 -0
  170. data/tools/rdoc/debian/rdoc.manpages +1 -0
  171. data/tools/rdoc/debian/rdoc.pod +149 -0
  172. data/tools/rdoc/debian/rules +9 -0
  173. data/tools/rdoc/dot/dot.rb +255 -0
  174. data/tools/rdoc/etc/rdoc.dtd +203 -0
  175. data/tools/rdoc/install.rb +137 -0
  176. data/tools/rdoc/markup/install.rb +43 -0
  177. data/tools/rdoc/markup/sample/sample.rb +42 -0
  178. data/tools/rdoc/markup/simple_markup/fragments.rb +323 -0
  179. data/tools/rdoc/markup/simple_markup/inline.rb +348 -0
  180. data/tools/rdoc/markup/simple_markup/lines.rb +147 -0
  181. data/tools/rdoc/markup/simple_markup/preprocess.rb +68 -0
  182. data/tools/rdoc/markup/simple_markup/to_html.rb +281 -0
  183. data/tools/rdoc/markup/simple_markup.rb +474 -0
  184. data/tools/rdoc/markup/test/AllTests.rb +2 -0
  185. data/tools/rdoc/markup/test/TestInline.rb +151 -0
  186. data/tools/rdoc/markup/test/TestParse.rb +411 -0
  187. data/tools/rdoc/rdoc/code_objects.rb +536 -0
  188. data/tools/rdoc/rdoc/diagram.rb +331 -0
  189. data/tools/rdoc/rdoc/generators/chm_generator.rb +112 -0
  190. data/tools/rdoc/rdoc/generators/html_generator.rb +1268 -0
  191. data/tools/rdoc/rdoc/generators/template/chm/chm.rb +86 -0
  192. data/tools/rdoc/rdoc/generators/template/html/html.rb +705 -0
  193. data/tools/rdoc/rdoc/generators/template/html/kilmer.rb +377 -0
  194. data/tools/rdoc/rdoc/generators/template/xml/rdf.rb +110 -0
  195. data/tools/rdoc/rdoc/generators/template/xml/xml.rb +110 -0
  196. data/tools/rdoc/rdoc/generators/xml_generator.rb +130 -0
  197. data/tools/rdoc/rdoc/options.rb +451 -0
  198. data/tools/rdoc/rdoc/parsers/parse_c.rb +287 -0
  199. data/tools/rdoc/rdoc/parsers/parse_f95.rb +118 -0
  200. data/tools/rdoc/rdoc/parsers/parse_rb.rb +2311 -0
  201. data/tools/rdoc/rdoc/parsers/parse_simple.rb +37 -0
  202. data/tools/rdoc/rdoc/parsers/parserfactory.rb +75 -0
  203. data/tools/rdoc/rdoc/rdoc.rb +219 -0
  204. data/tools/rdoc/rdoc/template.rb +234 -0
  205. data/tools/rdoc/rdoc/tokenstream.rb +25 -0
  206. data/tools/rdoc/rdoc.rb +9 -0
  207. metadata +291 -0
@@ -0,0 +1,348 @@
1
+ module SM
2
+
3
+ # We manage a set of attributes. Each attribute has a symbol name
4
+ # and a bit value
5
+
6
+ class Attribute
7
+ SPECIAL = 1
8
+
9
+ @@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
10
+ @@next_bitmap = 2
11
+
12
+ def Attribute.bitmap_for(name)
13
+ bitmap = @@name_to_bitmap[name]
14
+ if !bitmap
15
+ bitmap = @@next_bitmap
16
+ @@next_bitmap <<= 1
17
+ @@name_to_bitmap[name] = bitmap
18
+ end
19
+ bitmap
20
+ end
21
+
22
+ def Attribute.as_string(bitmap)
23
+ return "none" if bitmap.zero?
24
+ res = []
25
+ @@name_to_bitmap.each do |name, bit|
26
+ res << name if (bitmap & bit) != 0
27
+ end
28
+ res.join(",")
29
+ end
30
+
31
+ def Attribute.each_name_of(bitmap)
32
+ @@name_to_bitmap.each do |name, bit|
33
+ next if bit == SPECIAL
34
+ yield name.to_s if (bitmap & bit) != 0
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ # An AttrChanger records a change in attributes. It contains
41
+ # a bitmap of the attributes to turn on, and a bitmap of those to
42
+ # turn off
43
+
44
+ AttrChanger = Struct.new(:turn_on, :turn_off)
45
+ class AttrChanger
46
+ def to_s
47
+ "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
48
+ end
49
+ end
50
+
51
+ # An array of attributes which parallels the characters in a string
52
+ class AttrSpan
53
+ def initialize(length)
54
+ @attrs = Array.new(length, 0)
55
+ end
56
+
57
+ def set_attrs(start, length, bits)
58
+ for i in start ... (start+length)
59
+ @attrs[i] |= bits
60
+ end
61
+ end
62
+
63
+ def [](n)
64
+ @attrs[n]
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Hold details of a special sequence
70
+
71
+ class Special
72
+ attr_reader :type
73
+ attr_accessor :text
74
+
75
+ def initialize(type, text)
76
+ @type, @text = type, text
77
+ end
78
+
79
+ def ==(o)
80
+ self.text == o.text && self.type == o.type
81
+ end
82
+
83
+ def to_s
84
+ "Special: type=#{type}, text=#{text.dump}"
85
+ end
86
+ end
87
+
88
+ class AttributeManager
89
+
90
+ NULL = "\000".freeze
91
+
92
+ ##
93
+ # We work by substituting non-printing characters in to the
94
+ # text. For now I'm assuming that I can substitute
95
+ # a character in the range 0..8 for a 7 bit character
96
+ # without damaging the encoded string, but this might
97
+ # be optimistic
98
+ #
99
+
100
+ =begin
101
+ ATTR_FLAG = 001
102
+ A_START = 002
103
+ A_END = 003
104
+ A_SPECIAL_START = 005
105
+ A_SPECIAL_END = 006
106
+
107
+ START_ATTR = ATTR_FLAG.chr + A_START.chr
108
+ END_ATTR = ATTR_FLAG.chr + A_END.chr
109
+
110
+ START_SPECIAL = ATTR_FLAG.chr + A_SPECIAL_START.chr
111
+ END_SPECIAL = ATTR_FLAG.chr + A_SPECIAL_END.chr
112
+
113
+ =end
114
+ A_PROTECT = 004
115
+ PROTECT_ATTR = A_PROTECT.chr
116
+
117
+ # This maps delimiters that occur around words (such as
118
+ # *bold* or +tt+) where the start and end delimiters
119
+ # and the same. This lets us optimize the regexp
120
+ MATCHING_WORD_PAIRS = {}
121
+
122
+ # And this is used when the delimiters aren't the same. In this
123
+ # case the hash maps a pattern to the attribute character
124
+ WORD_PAIR_MAP = {}
125
+
126
+ # This maps HTML tags to the corresponding attribute char
127
+ HTML_TAGS = {}
128
+
129
+ # And this maps _special_ sequences to a name. A special sequence
130
+ # is something like a WikiWord
131
+ SPECIAL = {}
132
+
133
+ # Return an attribute object with the given turn_on
134
+ # and turn_off bits set
135
+
136
+ def attribute(turn_on, turn_off)
137
+ AttrChanger.new(turn_on, turn_off)
138
+ end
139
+
140
+
141
+ def change_attribute(current, new)
142
+ diff = current ^ new
143
+ attribute(new & diff, current & diff)
144
+ end
145
+
146
+ def changed_attribute_by_name(current_set, new_set)
147
+ current = new = 0
148
+ current_set.each {|name| current |= Attribute.bitmap_for(name) }
149
+ new_set.each {|name| new |= Attribute.bitmap_for(name) }
150
+ change_attribute(current, new)
151
+ end
152
+
153
+ def copy_string(start_pos, end_pos)
154
+ res = @str[start_pos...end_pos]
155
+ res.gsub!(/\000/, '')
156
+ res
157
+ end
158
+
159
+ # Map attributes like <b>text</b>to the sequence \001\002<char>\001\003<char>,
160
+ # where <char> is a per-attribute specific character
161
+
162
+ def convert_attrs(str, attrs)
163
+ # first do matching ones
164
+ tags = MATCHING_WORD_PAIRS.keys.join("")
165
+ re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
166
+ # re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
167
+ 1 while str.gsub!(Regexp.new(re)) {
168
+ attr = MATCHING_WORD_PAIRS[$2];
169
+ attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
170
+ $1 + NULL*$2.length + $3 + NULL*$2.length + $4
171
+ }
172
+
173
+ # then non-matching
174
+ unless WORD_PAIR_MAP.empty?
175
+ WORD_PAIR_MAP.each do |regexp, attr|
176
+ str.gsub!(regexp) {
177
+ attrs.set_attrs($`.length + $1.length, $2.length, attr)
178
+ NULL*$1.length + $2 + NULL*$3.length
179
+ }
180
+ end
181
+ end
182
+ end
183
+
184
+ def convert_html(str, attrs)
185
+ tags = HTML_TAGS.keys.join("|")
186
+ re = "<(#{tags})>(.*?)</\\1>"
187
+ 1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
188
+ attr = HTML_TAGS[$1.downcase]
189
+ html_length = $1.length + 2
190
+ seq = NULL * html_length
191
+ attrs.set_attrs($`.length + html_length, $2.length, attr)
192
+ seq + $2 + seq + NULL
193
+ }
194
+ end
195
+
196
+ def convert_specials(str, attrs)
197
+ unless SPECIAL.empty?
198
+ SPECIAL.each do |regexp, attr|
199
+ str.scan(regexp) do
200
+ attrs.set_attrs($`.length, $1.length, attr | Attribute::SPECIAL)
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ # A \ in front of a character that would normally be
207
+ # processed turns off processing. We do this by turning
208
+ # \< into <#{PROTECT}
209
+
210
+ PROTECTABLE = [ "<" << "\\" ] #"
211
+
212
+
213
+ def mask_protected_sequences
214
+ protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
215
+ @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
216
+ end
217
+
218
+ def unmask_protected_sequences
219
+ @str.gsub!(/(.)#{PROTECT_ATTR}/, '\1')
220
+ end
221
+
222
+ def initialize
223
+ add_word_pair("*", "*", :BOLD)
224
+ add_word_pair("_", "_", :EM)
225
+ add_word_pair("+", "+", :TT)
226
+
227
+ add_html("em", :EM)
228
+ add_html("i", :EM)
229
+ add_html("b", :BOLD)
230
+ add_html("tt", :TT)
231
+ end
232
+
233
+ def add_word_pair(start, stop, name)
234
+ raise "Word flags may not start '<'" if start[0] == ?<
235
+ bitmap = Attribute.bitmap_for(name)
236
+ if start == stop
237
+ MATCHING_WORD_PAIRS[start] = bitmap
238
+ else
239
+ pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
240
+ # "([A-Za-z]+)" +
241
+ "(\\S+)" +
242
+ "(" + Regexp.escape(stop) +")")
243
+ WORD_PAIR_MAP[pattern] = bitmap
244
+ end
245
+ PROTECTABLE << start[0,1]
246
+ PROTECTABLE.uniq!
247
+ end
248
+
249
+ def add_html(tag, name)
250
+ HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
251
+ end
252
+
253
+ def add_special(pattern, name)
254
+ SPECIAL[pattern] = Attribute.bitmap_for(name)
255
+ end
256
+
257
+ def flow(str)
258
+ @str = str
259
+ @attrs = AttrSpan.new(str.length)
260
+
261
+ puts("Before flow, str='#{@str.dump}'") if $DEBUG
262
+ mask_protected_sequences
263
+ convert_attrs(@str, @attrs)
264
+ convert_html(@str, @attrs)
265
+ convert_specials(str, @attrs)
266
+ unmask_protected_sequences
267
+ puts("After flow, str='#{@str.dump}'") if $DEBUG
268
+ return split_into_flow
269
+ end
270
+
271
+ def display_attributes
272
+ puts
273
+ puts @str.tr(NULL, "!")
274
+ bit = 1
275
+ 16.times do |bno|
276
+ line = ""
277
+ @str.length.times do |i|
278
+ if (@attrs[i] & bit) == 0
279
+ line << " "
280
+ else
281
+ if bno.zero?
282
+ line << "S"
283
+ else
284
+ line << ("%d" % (bno+1))
285
+ end
286
+ end
287
+ end
288
+ puts(line) unless line =~ /^ *$/
289
+ bit <<= 1
290
+ end
291
+ end
292
+
293
+ def split_into_flow
294
+
295
+ display_attributes if $DEBUG
296
+
297
+ res = []
298
+ current_attr = 0
299
+ str = ""
300
+
301
+
302
+ str_len = @str.length
303
+
304
+ # skip leading invisible text
305
+ i = 0
306
+ i += 1 while i < str_len and @str[i].zero?
307
+ start_pos = i
308
+
309
+ # then scan the string, chunking it on attribute changes
310
+ while i < str_len
311
+ new_attr = @attrs[i]
312
+ if new_attr != current_attr
313
+ if i > start_pos
314
+ res << copy_string(start_pos, i)
315
+ start_pos = i
316
+ end
317
+
318
+ res << change_attribute(current_attr, new_attr)
319
+ current_attr = new_attr
320
+
321
+ if (current_attr & Attribute::SPECIAL) != 0
322
+ i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
323
+ res << Special.new(current_attr, copy_string(start_pos, i))
324
+ start_pos = i
325
+ next
326
+ end
327
+ end
328
+
329
+ # move on, skipping any invisible characters
330
+ begin
331
+ i += 1
332
+ end while i < str_len and @str[i].zero?
333
+ end
334
+
335
+ # tidy up trailing text
336
+ if start_pos < str_len
337
+ res << copy_string(start_pos, str_len)
338
+ end
339
+
340
+ # and reset to all attributes off
341
+ res << change_attribute(current_attr, 0) if current_attr != 0
342
+
343
+ return res
344
+ end
345
+
346
+ end
347
+
348
+ end
@@ -0,0 +1,147 @@
1
+ ##########################################################################
2
+ #
3
+ # We store the lines we're working on as objects of class Line.
4
+ # These contain the text of the line, along with a flag indicating the
5
+ # line type, and an indentation level
6
+
7
+ module SM
8
+
9
+ class Line
10
+ INFINITY = 9999
11
+
12
+ BLANK = :BLANK
13
+ HEADING = :HEADING
14
+ LIST = :LIST
15
+ RULE = :RULE
16
+ PARAGRAPH = :PARAGRAPH
17
+ VERBATIM = :VERBATIM
18
+
19
+ # line type
20
+ attr_accessor :type
21
+
22
+ # The indentation nesting level
23
+ attr_accessor :level
24
+
25
+ # The contents
26
+ attr_accessor :text
27
+
28
+ # A prefix or parameter. For LIST lines, this is
29
+ # the text that introduced the list item (the label)
30
+ attr_accessor :param
31
+
32
+ # A flag. For list lines, this is the type of the list
33
+ attr_accessor :flag
34
+
35
+ # the number of leading spaces
36
+ attr_accessor :leading_spaces
37
+
38
+ # true if this line has been deleted from the list of lines
39
+ attr_accessor :deleted
40
+
41
+
42
+ def initialize(text)
43
+ @text = text.dup
44
+ @deleted = false
45
+
46
+ # expand tabs
47
+ 1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
48
+
49
+ # Strip trailing whitespace
50
+ @text.sub!(/\s+$/, '')
51
+
52
+ # and look for leading whitespace
53
+ if @text.length > 0
54
+ @text =~ /^(\s*)/
55
+ @leading_spaces = $1.length
56
+ else
57
+ @leading_spaces = INFINITY
58
+ end
59
+ end
60
+
61
+ # Return true if this line is blank
62
+ def isBlank?
63
+ @text.length.zero?
64
+ end
65
+
66
+ # stamp a line with a type, a level, a prefix, and a flag
67
+ def stamp(type, level, param="", flag=nil)
68
+ @type, @level, @param, @flag = type, level, param, flag
69
+ end
70
+
71
+ ##
72
+ # Strip off the leading margin
73
+ #
74
+
75
+ def strip_leading(size)
76
+ @text[0,size] = ""
77
+ end
78
+
79
+ def to_s
80
+ "#@type#@level: #@text"
81
+ end
82
+ end
83
+
84
+ ###############################################################################
85
+ #
86
+ # A container for all the lines
87
+ #
88
+
89
+ class Lines
90
+ include Enumerable
91
+
92
+ attr_reader :lines # for debugging
93
+
94
+ def initialize(lines)
95
+ @lines = lines
96
+ rewind
97
+ end
98
+
99
+ def empty?
100
+ @lines.size.zero?
101
+ end
102
+
103
+ def each
104
+ @lines.each do |line|
105
+ yield line unless line.deleted
106
+ end
107
+ end
108
+
109
+ # def [](index)
110
+ # @lines[index]
111
+ # end
112
+
113
+ def rewind
114
+ @nextline = 0
115
+ end
116
+
117
+ def next
118
+ begin
119
+ res = @lines[@nextline]
120
+ @nextline += 1 if @nextline < @lines.size
121
+ end while res and res.deleted and @nextline < @lines.size
122
+ res
123
+ end
124
+
125
+ def unget
126
+ @nextline -= 1
127
+ end
128
+
129
+ def delete(a_line)
130
+ a_line.deleted = true
131
+ end
132
+
133
+ def normalize
134
+ margin = @lines.collect{|l| l.leading_spaces}.min
135
+ margin = 0 if margin == Line::INFINITY
136
+ @lines.each {|line| line.strip_leading(margin) } if margin > 0
137
+ end
138
+
139
+ def as_text
140
+ @lines.map {|l| l.text}.join("\n")
141
+ end
142
+
143
+ def line_types
144
+ @lines.map {|l| l.type }
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,68 @@
1
+ module SM
2
+
3
+ ##
4
+ # Handle common directives that can occur in a block of text:
5
+ #
6
+ # : include : filename
7
+ #
8
+
9
+ class PreProcess
10
+
11
+ def initialize(input_file_name, include_path)
12
+ @input_file_name = input_file_name
13
+ @include_path = include_path
14
+ end
15
+
16
+ # Look for common options in a chunk of text. Options that
17
+ # we don't handle are passed back to our caller
18
+ # as |directive, param|
19
+
20
+ def handle(text)
21
+ text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
22
+
23
+ directive = $2.downcase
24
+ param = $3
25
+
26
+ case directive
27
+
28
+ when "include"
29
+ include_file($3, $1)
30
+
31
+ else
32
+ yield(directive, param)
33
+ end
34
+ end
35
+ end
36
+
37
+ #######
38
+ private
39
+ #######
40
+
41
+ # Include a file, indenting it correctly
42
+
43
+ def include_file(name, indent)
44
+ if (full_name = find_include_file(name))
45
+ content = File.open(full_name) {|f| f.read}
46
+ res = content.gsub(/^#?/, indent)
47
+ else
48
+ $stderr.puts "Couldn't find file to include: '#{name}'"
49
+ ''
50
+ end
51
+ end
52
+
53
+ # Look for the given file in the directory containing the current
54
+ # file, and then in each of the directories specified in the
55
+ # RDOC_INCLUDE path
56
+
57
+ def find_include_file(name)
58
+ to_search = [ File.dirname(@input_file_name) ].concat @include_path
59
+ to_search.each do |dir|
60
+ full_name = File.join(dir, name)
61
+ stat = File.stat(full_name) rescue next
62
+ return full_name if stat.readable?
63
+ end
64
+ nil
65
+ end
66
+
67
+ end
68
+ end