fto 0.1.0

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 (43) hide show
  1. data/CONTRIBUTORS.txt +5 -0
  2. data/LICENCE.txt +202 -0
  3. data/README.txt +75 -0
  4. data/doc/classes/FormatText.html +120 -0
  5. data/doc/classes/FormatText/Context.html +240 -0
  6. data/doc/classes/FormatText/Context.src/M000001.html +26 -0
  7. data/doc/classes/FormatText/Context.src/M000004.html +26 -0
  8. data/doc/classes/FormatText/Context.src/M000007.html +26 -0
  9. data/doc/classes/FormatText/Context.src/M000008.html +26 -0
  10. data/doc/classes/FormatText/Context.src/M000009.html +26 -0
  11. data/doc/classes/FormatText/Effector.html +411 -0
  12. data/doc/classes/FormatText/Effector.src/M000004.html +38 -0
  13. data/doc/classes/FormatText/Effector.src/M000005.html +39 -0
  14. data/doc/classes/FormatText/Effector.src/M000007.html +38 -0
  15. data/doc/classes/FormatText/Effector.src/M000008.html +42 -0
  16. data/doc/classes/FormatText/Effector.src/M000009.html +43 -0
  17. data/doc/classes/FormatText/Effector.src/M000010.html +19 -0
  18. data/doc/classes/FormatText/Effector.src/M000011.html +18 -0
  19. data/doc/classes/FormatText/Effector.src/M000012.html +19 -0
  20. data/doc/classes/FormatText/Effector.src/M000013.html +18 -0
  21. data/doc/classes/FormatText/Effector.src/M000014.html +18 -0
  22. data/doc/classes/FormatText/FTO.html +305 -0
  23. data/doc/classes/FormatText/FTO.src/M000001.html +19 -0
  24. data/doc/classes/FormatText/FTO.src/M000002.html +34 -0
  25. data/doc/classes/FormatText/FTO.src/M000003.html +22 -0
  26. data/doc/classes/FormatText/FTO.src/M000004.html +19 -0
  27. data/doc/classes/FormatText/FTO.src/M000005.html +22 -0
  28. data/doc/classes/FormatText/FTO.src/M000006.html +20 -0
  29. data/doc/classes/FormatText/FTO.src/M000007.html +63 -0
  30. data/doc/classes/FormatText/FTO.src/M000008.html +63 -0
  31. data/doc/created.rid +1 -0
  32. data/doc/files/fto_rb.html +141 -0
  33. data/doc/files/lib/fto_rb.html +150 -0
  34. data/doc/fr_class_index.html +30 -0
  35. data/doc/fr_file_index.html +27 -0
  36. data/doc/fr_method_index.html +39 -0
  37. data/doc/index.html +24 -0
  38. data/doc/rdoc-style.css +208 -0
  39. data/lib/fto.rb +1349 -0
  40. data/test/test_fto_api.rb +143 -0
  41. data/test/test_fto_effectors.rb +440 -0
  42. data/test/test_helper.rb +3 -0
  43. metadata +115 -0
@@ -0,0 +1,30 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Classes
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Classes</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Classes</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/FormatText.html">FormatText</a><br />
24
+ <a href="classes/FormatText/Context.html">FormatText::Context</a><br />
25
+ <a href="classes/FormatText/Effector.html">FormatText::Effector</a><br />
26
+ <a href="classes/FormatText/FTO.html">FormatText::FTO</a><br />
27
+ </div>
28
+ </div>
29
+ </body>
30
+ </html>
@@ -0,0 +1,27 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Files
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Files</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Files</h1>
22
+ <div id="index-entries">
23
+ <a href="files/lib/fto_rb.html">lib/fto.rb</a><br />
24
+ </div>
25
+ </div>
26
+ </body>
27
+ </html>
@@ -0,0 +1,39 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Methods
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Methods</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Methods</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/FormatText/FTO.html#M000004">destroyEffector (FormatText::FTO)</a><br />
24
+ <a href="classes/FormatText/Effector.html#M000010">disable (FormatText::Effector)</a><br />
25
+ <a href="classes/FormatText/FTO.html#M000005">disableEffector (FormatText::FTO)</a><br />
26
+ <a href="classes/FormatText/Effector.html#M000011">disabled? (FormatText::Effector)</a><br />
27
+ <a href="classes/FormatText/Effector.html#M000012">enable (FormatText::Effector)</a><br />
28
+ <a href="classes/FormatText/FTO.html#M000003">enableEffector (FormatText::FTO)</a><br />
29
+ <a href="classes/FormatText/Effector.html#M000013">enabled? (FormatText::Effector)</a><br />
30
+ <a href="classes/FormatText/FTO.html#M000006">findEffectors (FormatText::FTO)</a><br />
31
+ <a href="classes/FormatText/FTO.html#M000007">format (FormatText::FTO)</a><br />
32
+ <a href="classes/FormatText/Effector.html#M000009">new (FormatText::Effector)</a><br />
33
+ <a href="classes/FormatText/Context.html#M000008">new (FormatText::Context)</a><br />
34
+ <a href="classes/FormatText/FTO.html#M000001">new (FormatText::FTO)</a><br />
35
+ <a href="classes/FormatText/FTO.html#M000002">registerEffector (FormatText::FTO)</a><br />
36
+ </div>
37
+ </div>
38
+ </body>
39
+ </html>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
5
+
6
+ <!--
7
+
8
+ RDoc Documentation
9
+
10
+ -->
11
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
12
+ <head>
13
+ <title>RDoc Documentation</title>
14
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
15
+ </head>
16
+ <frameset rows="20%, 80%">
17
+ <frameset cols="25%,35%,45%">
18
+ <frame src="fr_file_index.html" title="Files" name="Files" />
19
+ <frame src="fr_class_index.html" name="Classes" />
20
+ <frame src="fr_method_index.html" name="Methods" />
21
+ </frameset>
22
+ <frame src="files/lib/fto_rb.html" name="docwin" />
23
+ </frameset>
24
+ </html>
@@ -0,0 +1,208 @@
1
+
2
+ body {
3
+ font-family: Verdana,Arial,Helvetica,sans-serif;
4
+ font-size: 90%;
5
+ margin: 0;
6
+ margin-left: 40px;
7
+ padding: 0;
8
+ background: white;
9
+ }
10
+
11
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
12
+ h1 { font-size: 150%; }
13
+ h2,h3,h4 { margin-top: 1em; }
14
+
15
+ a { background: #eef; color: #039; text-decoration: none; }
16
+ a:hover { background: #039; color: #eef; }
17
+
18
+ /* Override the base stylesheet's Anchor inside a table cell */
19
+ td > a {
20
+ background: transparent;
21
+ color: #039;
22
+ text-decoration: none;
23
+ }
24
+
25
+ /* and inside a section title */
26
+ .section-title > a {
27
+ background: transparent;
28
+ color: #eee;
29
+ text-decoration: none;
30
+ }
31
+
32
+ /* === Structural elements =================================== */
33
+
34
+ div#index {
35
+ margin: 0;
36
+ margin-left: -40px;
37
+ padding: 0;
38
+ font-size: 90%;
39
+ }
40
+
41
+
42
+ div#index a {
43
+ margin-left: 0.7em;
44
+ }
45
+
46
+ div#index .section-bar {
47
+ margin-left: 0px;
48
+ padding-left: 0.7em;
49
+ background: #ccc;
50
+ font-size: small;
51
+ }
52
+
53
+
54
+ div#classHeader, div#fileHeader {
55
+ width: auto;
56
+ color: white;
57
+ padding: 0.5em 1.5em 0.5em 1.5em;
58
+ margin: 0;
59
+ margin-left: -40px;
60
+ border-bottom: 3px solid #006;
61
+ }
62
+
63
+ div#classHeader a, div#fileHeader a {
64
+ background: inherit;
65
+ color: white;
66
+ }
67
+
68
+ div#classHeader td, div#fileHeader td {
69
+ background: inherit;
70
+ color: white;
71
+ }
72
+
73
+
74
+ div#fileHeader {
75
+ background: #057;
76
+ }
77
+
78
+ div#classHeader {
79
+ background: #048;
80
+ }
81
+
82
+
83
+ .class-name-in-header {
84
+ font-size: 180%;
85
+ font-weight: bold;
86
+ }
87
+
88
+
89
+ div#bodyContent {
90
+ padding: 0 1.5em 0 1.5em;
91
+ }
92
+
93
+ div#description {
94
+ padding: 0.5em 1.5em;
95
+ background: #efefef;
96
+ border: 1px dotted #999;
97
+ }
98
+
99
+ div#description h1,h2,h3,h4,h5,h6 {
100
+ color: #125;;
101
+ background: transparent;
102
+ }
103
+
104
+ div#validator-badges {
105
+ text-align: center;
106
+ }
107
+ div#validator-badges img { border: 0; }
108
+
109
+ div#copyright {
110
+ color: #333;
111
+ background: #efefef;
112
+ font: 0.75em sans-serif;
113
+ margin-top: 5em;
114
+ margin-bottom: 0;
115
+ padding: 0.5em 2em;
116
+ }
117
+
118
+
119
+ /* === Classes =================================== */
120
+
121
+ table.header-table {
122
+ color: white;
123
+ font-size: small;
124
+ }
125
+
126
+ .type-note {
127
+ font-size: small;
128
+ color: #DEDEDE;
129
+ }
130
+
131
+ .xxsection-bar {
132
+ background: #eee;
133
+ color: #333;
134
+ padding: 3px;
135
+ }
136
+
137
+ .section-bar {
138
+ color: #333;
139
+ border-bottom: 1px solid #999;
140
+ margin-left: -20px;
141
+ }
142
+
143
+
144
+ .section-title {
145
+ background: #79a;
146
+ color: #eee;
147
+ padding: 3px;
148
+ margin-top: 2em;
149
+ margin-left: -30px;
150
+ border: 1px solid #999;
151
+ }
152
+
153
+ .top-aligned-row { vertical-align: top }
154
+ .bottom-aligned-row { vertical-align: bottom }
155
+
156
+ /* --- Context section classes ----------------------- */
157
+
158
+ .context-row { }
159
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
160
+ .context-item-value { font-size: small; color: #448; }
161
+ .context-item-desc { color: #333; padding-left: 2em; }
162
+
163
+ /* --- Method classes -------------------------- */
164
+ .method-detail {
165
+ background: #efefef;
166
+ padding: 0;
167
+ margin-top: 0.5em;
168
+ margin-bottom: 1em;
169
+ border: 1px dotted #ccc;
170
+ }
171
+ .method-heading {
172
+ color: black;
173
+ background: #ccc;
174
+ border-bottom: 1px solid #666;
175
+ padding: 0.2em 0.5em 0 0.5em;
176
+ }
177
+ .method-signature { color: black; background: inherit; }
178
+ .method-name { font-weight: bold; }
179
+ .method-args { font-style: italic; }
180
+ .method-description { padding: 0 0.5em 0 0.5em; }
181
+
182
+ /* --- Source code sections -------------------- */
183
+
184
+ a.source-toggle { font-size: 90%; }
185
+ div.method-source-code {
186
+ background: #262626;
187
+ color: #ffdead;
188
+ margin: 1em;
189
+ padding: 0.5em;
190
+ border: 1px dashed #999;
191
+ overflow: hidden;
192
+ }
193
+
194
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
195
+
196
+ /* --- Ruby keyword styles --------------------- */
197
+
198
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
199
+
200
+ .ruby-constant { color: #7fffd4; background: transparent; }
201
+ .ruby-keyword { color: #00ffff; background: transparent; }
202
+ .ruby-ivar { color: #eedd82; background: transparent; }
203
+ .ruby-operator { color: #00ffee; background: transparent; }
204
+ .ruby-identifier { color: #ffdead; background: transparent; }
205
+ .ruby-node { color: #ffa07a; background: transparent; }
206
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
207
+ .ruby-regexp { color: #ffa07a; background: transparent; }
208
+ .ruby-value { color: #7fffd4; background: transparent; }
@@ -0,0 +1,1349 @@
1
+ #
2
+ # = fto.rb - Formatted Text Output
3
+ #
4
+ # Author:: Ken Coar
5
+ # Copyright:: Copyright © 2009 Ken Coar
6
+ # License:: Apache Licence 2.0
7
+ #
8
+ # == Synopsis
9
+ #
10
+ # require 'fto'
11
+ # include FormatText
12
+ # formatString = FTO.new("This will include a string: !AS", "string1")
13
+ # puts formatString.format
14
+ # puts formatString.format("string2")
15
+ #
16
+ # == Description
17
+ #
18
+ # FTO is a Ruby library for formatting text strings. In function it
19
+ # is similar to <tt>printf(3)</tt>; however, the syntax of the format
20
+ # effectors (sometimes called 'format descriptors') and the selection
21
+ # of effectors bundled with the package are based on the SYS$FAO
22
+ # user-mode system service found on OpenVMS.
23
+ #
24
+ # The FTO class is an extension of String, enhancing the constructor,
25
+ # adding the <i>format()</i> instance method, and the
26
+ # <i>registerEffector()</i> class method.
27
+ #
28
+ # In addition to the included list of effectors, FTO provides easy
29
+ # extensibility by allowing the developer to write her own effector
30
+ # handlers and registering them with <i>FTO.registerEffector()</i>.
31
+ #
32
+ #--
33
+ # Copyright 2009 Ken Coar
34
+ #
35
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
36
+ # may not use this file except in compliance with the License. You may
37
+ # obtain a copy of the License at
38
+ #
39
+ # http://www.apache.org/licenses/LICENSE-2.0
40
+ #
41
+ # Unless required by applicable law or agreed to in writing, software
42
+ # distributed under the License is distributed on an "AS IS" BASIS,
43
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
44
+ # implied. See the License for the specific language governing
45
+ # permissions and limitations under the License.
46
+ #++
47
+
48
+
49
+ #--
50
+ # Notes about markup (docco being hard to find). This will be deleted
51
+ # ere long.
52
+ #
53
+ # \:arg:, \:args:
54
+ #
55
+ # \:call-seq:
56
+ # Alternative(s) to default invocation built by rdoc
57
+ #
58
+ # \:doc:
59
+ #
60
+ # \:enddoc:
61
+ # Stop documenting the current tree of items
62
+ #
63
+ # \:include:
64
+ # Pull in another file.
65
+ #
66
+ # \:main:
67
+ #
68
+ # \:nodoc: all
69
+ #
70
+ # \:nodoc:
71
+ # Temporarily turn off rdoc interpretation
72
+ #
73
+ # \:notnew:, \:not_new:, \:not-new:
74
+ # Document constructor as 'initialize' rather than as 'new'
75
+ #
76
+ # \:section:
77
+ #
78
+ # \:title:
79
+ # Duh.
80
+ #
81
+ # \:yield:, \:yields:
82
+ # Display an alternative to the automatic 'yields' rdoc makes up
83
+ #
84
+ # Stuff from SYS$FAO that isn't yet implemented (and may not be
85
+ # reasonable outside of OpenVMS).
86
+ #
87
+ # @TODO: !%T Inserts the system time. It takes one parameter: the
88
+ # address of a quadword time value to be converted to
89
+ # ASCII. If you specify 0, the current system time is
90
+ # inserted.
91
+ # @TODO: !%D Inserts the system date and time. It takes one
92
+ # parameter: the address of a quadword time value to be
93
+ # converted to ASCII. If you specify 0, the current system
94
+ # date and time is inserted.
95
+ # @TODO: !n%C Inserts a character string when the most recently
96
+ # evaluated argument has the value n. (Recommended for use
97
+ # with multilingual products.)
98
+ # @TODO: !%E Inserts a character string when the value of the most
99
+ # recently evaluated argument does not match any preceding
100
+ # !n%C directives. (Recommended for use with multilingual
101
+ # products.)
102
+ # @TODO: !%F Makes the end of a plurals statement.
103
+ #
104
+ #++
105
+
106
+ require 'rubygems'
107
+ require 'gettext'
108
+
109
+ module FormatText
110
+
111
+ include GetText
112
+
113
+ #
114
+ # Each time an effector's pattern is matched, its _code_ attribute
115
+ # is called with this structure, which is used to communicate
116
+ # details to the code block, and by the code block to pass back some
117
+ # behaviour modification notes. Most of the attributes should be
118
+ # treated as read-only, with exceptions noted below in the attribute
119
+ # descriptions.
120
+ #
121
+ # This class is used internally by the +fto+ library and isn't
122
+ # really intended for external consumption.
123
+ #
124
+ class Context
125
+
126
+ #
127
+ # <i>FormatText::Effector object</i>. The _Effector_ object
128
+ # involved. Read-only.
129
+ #
130
+ attr_accessor :effectorObj
131
+
132
+ #
133
+ # <i>FTO object</i>. The _FTO_ object being processed.
134
+ # Read-only.
135
+ #
136
+ attr_accessor :ftoObj
137
+
138
+ #
139
+ # _String_. The string that matched the effector and triggered
140
+ # its processing. Read-only.
141
+ #
142
+ attr_accessor :sMatched
143
+
144
+ #
145
+ # _Array_. Arguments remaining to be processed. Usually
146
+ # read-only, but see the description of the _usedArgs_ attribute
147
+ # for exceptions.
148
+ #
149
+ attr_accessor :usedArgs
150
+
151
+ #
152
+ # _Array_. List of arguments already processed. Usually
153
+ # read-only. The effector function is responsible for using the
154
+ # values in this array to perform its task.
155
+ #
156
+ # The only time this should be considered read/write is when the
157
+ # effector is intended to modify the list of arguments being used
158
+ # to build the final string. The _argList_ attribute should be
159
+ # modified in conjunction with _usedArgs_ to maintain continuity.
160
+ # By default, after the effector function returns, the caller (the
161
+ # <i>FTO#format</i> method) will take the element from the front
162
+ # of the _argList_ array and push it onto the end of the
163
+ # _usedArgs_ array.
164
+ #
165
+ attr_accessor :argList
166
+
167
+ #
168
+ # _Boolean_. The effector function sets this to +true+ to inhibit
169
+ # the <i>FTO#format()</i> method from modifying the argument list
170
+ # after the function returns. See the descriptions under the
171
+ # _argList_ and _usedArgs_ attributes.
172
+ #
173
+ attr_accessor :reuseArg
174
+
175
+ #
176
+ # Create a new <i>FormatText::Context</i> object. There may only
177
+ # be a single argument to the constructor, and it must be a hash,
178
+ # using symbols for the names of the attributes to set:
179
+ #
180
+ # x = FormatText::Context.new({ :sMatched => 'string', :reuseArg => false })
181
+ #
182
+ # This class is used internally by the +fto+ library and isn't
183
+ # really intended for external consumption.
184
+ #
185
+ # :call-seq:
186
+ # new<i>(Hash)</i> => <i>FormatText::Context object</i>
187
+ #
188
+ def initialize(*argsp)
189
+ (@effectorObj, @ftoObj) = nil
190
+ @sMatched = ''
191
+ @usedArgs = []
192
+ @argList = []
193
+ @reuseArg = false
194
+ if (argsp[0].class != Hash)
195
+ raise RuntimeError, self.class.name + _(' requires a Hash to create')
196
+ end
197
+ argsp[0].each { |key,val| eval("self.#{key.to_s} = val") }
198
+ end # def initialize
199
+
200
+ end # class Context
201
+
202
+ #
203
+ # = Description
204
+ #
205
+ # The _FTO_ class is the user interface; all others are for
206
+ # developers modifying or extending the +fto+ library.
207
+ #
208
+ # _FTO_ is a subclass of _String_, so all _String_ methods work on
209
+ # an _FTO_ object. _FTO_ provides the additional <i>format()</i>
210
+ # method.
211
+ #
212
+ # In addition to string text, the constructor (<i>FTO.new</i>) can
213
+ # take more than a single argument. Additional arguments will be
214
+ # stored as part of the object and will be available to the
215
+ # <i>FTO#format()</i> method at runtime.
216
+ #
217
+ # An _FTO_ object can be created as just a formatting string, or the
218
+ # constructor invocation can also include values to be applied by
219
+ # the <i>FTO#format()</i> method. At runtime the <i>format()</i> method
220
+ # can override any argument list provided at instantiation, but the
221
+ # latter is not lost.
222
+ #
223
+ class FTO < String
224
+
225
+ #
226
+ # Class variables
227
+ #
228
+
229
+ #
230
+ # Hash of all registered effector objects, keyed by their IDs.
231
+ #
232
+ @@RegisteredEffectors = {} unless (defined?(@@RegisteredEffectors))
233
+
234
+ #
235
+ # Hash of all currently enabled effectors, keyed by their sort
236
+ # key.
237
+ #
238
+ @@EnabledEffectors = {} unless (defined?(@@EnabledEffectors))
239
+
240
+ #
241
+ # Ordered array of effector keys (used to index
242
+ # @@EnabledEffectors)
243
+ #
244
+ @@EffectorKeys = [] unless (defined?(@@EffectorKeys))
245
+
246
+ # :stopdoc:
247
+ #
248
+ # We do this in order to call super on String, but since our
249
+ # argument list is different, we need to finesse it a little.
250
+ # Nobody's business but ours.
251
+ #
252
+ alias_method(:String_initialize, :initialize) # :nodoc:
253
+ # :startdoc:
254
+
255
+ #
256
+ # Debugging class method to access list of registered effectors
257
+ #
258
+ def self.effectors() # :nodoc:
259
+ @@RegisteredEffectors
260
+ end # def self.effectors()
261
+
262
+ #
263
+ # Debugging class method to access list of effector keys.
264
+ #
265
+ def self.eKeys() # :nodoc:
266
+ @@EffectorKeys
267
+ end # def self.eKeys()
268
+
269
+ #
270
+ # Debugging class method to access regular expression used to find
271
+ # effectors.
272
+ #
273
+ def self.regex() # :nodoc:
274
+ @@regex
275
+ end # def self.regex()
276
+
277
+ #
278
+ # Any argument list is supplied at object instantiation can be
279
+ # temporarily overridden when the <i>FTO#format()</i> method is
280
+ # invoked.
281
+ #
282
+ # :call-seq:
283
+ # new<i>()</i> => <i>FTO object</i>
284
+ # new<i>(String)</i> => <i>FTO object</i>
285
+ # new<i>(String, arg [, ...])</i> => <i>FTO object</i>
286
+ #
287
+ def initialize(text=nil, *args)
288
+ String_initialize(text)
289
+ @args = args
290
+ end # def initialize
291
+
292
+ #
293
+ # Add an effector description to the list of those which will be
294
+ # processed by the <i>FTO#format()</i> method.
295
+ #
296
+ # :call-seq:
297
+ # FTO.registerEffector<i>(FormatText::Effector)</i> => <i>nil</i>
298
+ # FTO.registerEffector<i>({ :symattr => value [, ...] })</i> => <i>nil</i>
299
+ #
300
+ def self.registerEffector(*args)
301
+ if ((args.length == 1) && (args[0].class.name.match(/Effector$/))
302
+ newE = args[0]
303
+ else
304
+ newE = Effector.new('placeholder')
305
+ if ((args.length == 1) && (args[0].class == Hash))
306
+ args[0].each do |key,val|
307
+ eval("newE.#{key.to_s} = val")
308
+ end
309
+ else
310
+ newE = Effector.new(args)
311
+ end
312
+ end
313
+ @@RegisteredEffectors[newE.id] = newE
314
+ key = sprintf('%06d-%s', newE.priority, newE.name)
315
+ newE.sortKey = key
316
+ self.rebuildEffectorList()
317
+ nil
318
+ end # def self.registerEffector()
319
+
320
+ # :stopdoc:
321
+ #
322
+ # This class method rebuilds the regular expression and the hash
323
+ # of enabled effectors. It needs to be invoked any time an
324
+ # effector is added, destroyed, enabled, or disabled. It's for
325
+ # internal use only.
326
+ #
327
+ def self.rebuildEffectorList()
328
+ enabled = @@RegisteredEffectors.select { |id,e| e.enabled? }
329
+ @@EffectorKeys = enabled.collect { |k,e| e.sortKey }.sort
330
+ @@EnabledEffectors = {}
331
+ enabled.each { |k,e| @@EnabledEffectors[e.sortKey] = e }
332
+ @@regex = Regexp.new("(#{@@EffectorKeys.collect {|k| @@EnabledEffectors[k].reMatch}.join(')|(')})")
333
+ nil
334
+ end # def self.rebuildEffectorList()
335
+ # :startdoc:
336
+
337
+ #
338
+ # Enables the effector with the specified ID (found in the
339
+ # effector's _id_ attribute). This is a no-op if the effector is
340
+ # already enabled.
341
+ #
342
+ # :call-seq:
343
+ # FTO.enableEffector<i>(Fixnum)</i> => <i>nil</i>
344
+ #
345
+ def self.enableEffector(id)
346
+ if ((e = @@RegisteredEffectors[id]).nil?)
347
+ raise RuntimeError, _('No such effector ') + "ID\##{id}"
348
+ end
349
+ e.enabled = true
350
+ self.rebuildEffectorList()
351
+ end # def self.enableEffector()
352
+
353
+ #
354
+ # Completely removes the effector with the specified ID from the
355
+ # FTO system. <strong>THIS IS NOT REVERSIBLE!</strong>
356
+ #
357
+ # :call-seq:
358
+ # FTO.destroyEffector<i>(Fixnum)</i> => <i>nil</i>
359
+ #
360
+ def self.destroyEffector(id)
361
+ @@RegisteredEffectors.delete(id)
362
+ self.rebuildEffectorList()
363
+ end # def self.destroyEffector()
364
+
365
+ #
366
+ # Disables the effector with the specified ID (such as from
367
+ # <i>FTO.findEffectors()</i>). This is a no-op if the effector is
368
+ # already disabled.
369
+ #
370
+ # :call-seq:
371
+ # FTO.disableEffector<i>(Fixnum)</i> => <i>nil</i>
372
+ #
373
+ def self.disableEffector(id)
374
+ if ((e = @@RegisteredEffectors[id]).nil?)
375
+ raise RuntimeError, _('No such effector ') + "ID\##{id}"
376
+ end
377
+ e.disable
378
+ nil
379
+ end # def self.disableEffector()
380
+
381
+ #
382
+ # Returns an array of registered effectors whose names (_name_
383
+ # attribute) match the specified pattern.
384
+ #
385
+ # :call-seq:
386
+ # FTO.findEffectors<i>(String)</i> => <i>Array</i>
387
+ # FTO.findEffectors<i>(Regexp)</i> => <i>Array</i>
388
+ #
389
+ def self.findEffectors(pattern)
390
+ pattern = Regexp.new(pattern) unless (pattern.class == Regexp)
391
+ matches = @@RegisteredEffectors.select { |id,e| e.name.match(pattern) }
392
+ matches.collect { |id,e| e }
393
+ end # def self.findEffector()
394
+
395
+ #
396
+ # Process the formatting string, optionally with a runtime
397
+ # argument list. The argument list can either be a list of
398
+ # values, an array of values, or an <i>FormatText::Context</i>
399
+ # object. (The latter is intended only for internal use with
400
+ # recursion.)
401
+ #
402
+ # :call-seq:
403
+ # format<i>()</i> => <i>String</i>
404
+ # format<i>(arg [, ...])</i> => <i>String</i>
405
+ # format<i>(Array)</i> => <i>String</i>
406
+ # format<i>(FormatText::Context)</i> => <i>String</i> (<u>internal use only</u>)
407
+ #
408
+ def format(*argListp)
409
+ argList = argListp.empty? ? @args.clone : argListp
410
+ if ((argList.length == 1) && (argList[0].class == Array)) &&
411
+ argList = argList[0]
412
+ end
413
+ #
414
+ # It's possible we were passed a Context object so we can
415
+ # recurse. If so, use its values for some of these.
416
+ #
417
+ if ((argList.length == 1) && (argList[0].class == FormatText::Context))
418
+ eContext = argList[0]
419
+ usedArgs = eContext.usedArgs
420
+ argList = eContext.argList
421
+ else
422
+ usedArgs = []
423
+ eContext = Context.new({
424
+ :ftoObj => self,
425
+ :usedArgs => usedArgs,
426
+ :argList => argList
427
+ })
428
+ end
429
+ result = self.to_s
430
+ effector = sMatched = nil
431
+ while (m = result.match(@@regex))
432
+ #
433
+ # Find out which effector was matched. The index in .captures
434
+ # will be the same as the index in @effectors.
435
+ #
436
+ m.captures.length.times do |i|
437
+ next if (m.captures[i].nil?)
438
+ eContext.effectorObj = effector = @@EnabledEffectors[@@EffectorKeys[i]]
439
+ eContext.sMatched = sMatched = m.captures[i]
440
+ eContext.reuseArg = false
441
+ break
442
+ end
443
+ #
444
+ # Call the workhorse for this descriptor
445
+ #
446
+ replacement = effector.code.call(eContext)
447
+ result.sub!(sMatched, replacement)
448
+ #
449
+ # Mark the item at the front of the argument list as having
450
+ # been used, if the effector agrees.
451
+ #
452
+ usedArgs.push(argList.shift) unless (eContext.reuseArg)
453
+ end
454
+ result
455
+ end # def format()
456
+
457
+ end # class FTO
458
+
459
+ #
460
+ # Class used to describe a format effector. The
461
+ # <i>FormatText::Effector</i> class is basically a container rather
462
+ # than an active class. It holds information about how an effector
463
+ # operates and what it needs.
464
+ #
465
+ # Typically <i>FormatText::Effector</i> objects aren't created
466
+ # directly, but by calling <i>FTO.registerEffector()</i>.
467
+ #
468
+ class Effector
469
+
470
+ @@NEXTID = 1 unless (defined?(@@NEXTID))
471
+
472
+ #
473
+ # _Fixnum_. ID number assigned to the effector, unique within a
474
+ # usage environment. Used by <i>FTO.enableEffector()</i>,
475
+ # <i>FTO.disableEffector()</i>, and <i>FTO.destroyEffector()</i>.
476
+ #
477
+ attr_reader :id
478
+
479
+ #
480
+ # _Boolean_. Whether this effector should be processed or
481
+ # ignored.
482
+ #
483
+ attr_accessor :enabled
484
+
485
+ #
486
+ # _String_. Human-readable name for what the effector does. Used
487
+ # for sorting.
488
+ #
489
+ attr_accessor :name
490
+
491
+ #
492
+ # _String_. Human-readable brief description.
493
+ #
494
+ attr_accessor :description
495
+
496
+ #
497
+ # _Fixnum_. Numeric priority value for sorting purposes.
498
+ #
499
+ attr_accessor :priority
500
+
501
+ #
502
+ # _String_. Key used to sort effectors, built from the name and
503
+ # priority.
504
+ #
505
+ attr_accessor :sortKey
506
+
507
+ #
508
+ # _String_. Regular expression used to recognise the effector in
509
+ # the format string.
510
+ #
511
+ attr_accessor :reMatch
512
+
513
+ #
514
+ # _String_. Optional regex pattern used to extract additional info
515
+ # (such as a field width).
516
+ #
517
+ attr_accessor :reExtra
518
+
519
+ #
520
+ # _Integer_. [<i>Numeric conversion effectors only</i>] If the
521
+ # effector is used to represent a number, this is a mask of the
522
+ # bits to be included. For instance, if the effector only
523
+ # displays 8-bit values, this value would be <tt>0xFF</tt>.
524
+ #
525
+ attr_accessor :mask
526
+
527
+ #
528
+ # _Integer_. [<i>Numeric conversion effectors only</i>] If the
529
+ # value is to be interpreted as signed, this is a mask for the
530
+ # sign bit. (For a byte, <tt>:mask</tt> would be <tt>0xFF</tt>
531
+ # and <tt>:signbit</tt> would be <tt>0x80</tt>.)
532
+ #
533
+ attr_accessor :signbit
534
+
535
+ #
536
+ # _Fixnum_. Default width for the effector result (used when
537
+ # filling).
538
+ #
539
+ attr_accessor :dWidth
540
+
541
+ #
542
+ # _Any_. Default value (used if _argList_ has been exhausted).
543
+ #
544
+ attr_accessor :dValue
545
+
546
+ #
547
+ # _String_. Character used to fill values shorter than the field
548
+ # widths.
549
+ #
550
+ attr_accessor :fill
551
+
552
+ #
553
+ # _Symbol_. Symbol value <tt>:left</tt> or <tt>:right</tt>,
554
+ # indicating against which edge of a too-wide field the value
555
+ # should abut.
556
+ #
557
+ attr_accessor :justify
558
+
559
+ #
560
+ # _Symbol_. Symbol value <tt>:left</tt> or <tt>:right</tt>
561
+ # indicating on which side too-wide results should be truncated to
562
+ # fit within the field width.
563
+ #
564
+ attr_accessor :truncate
565
+
566
+ #
567
+ # _Array_. Array of additional info for the function
568
+ # (effector-specific).
569
+ #
570
+ attr_accessor :data
571
+
572
+ #
573
+ # _Proc_. Code block (<i>e.g.</i>, a +lambda+ function) that
574
+ # actually interprets the effector. (See the
575
+ # <i>FormatText::Context</i> class for a description, or the
576
+ # various +Convert+ constants in the source.)
577
+ #
578
+ attr_accessor :code
579
+
580
+ #
581
+ # Creates a new <i>FormatText::Effector</i> object. The argument
582
+ # list can be either an order-dependent list of attribute values,
583
+ # or a hash using the symbolised attribute names as the keys.
584
+ #
585
+ # call-seq:
586
+ # new<i>(Hash)</i> => <i>FormatText::Effector object</i>
587
+ # new<i>(name, description, enabled, priority, code, reMatch, reExtra, mask, signbit, dWidth, fill, justify, truncate, data)</i> => <i>FormatText::Effector object</i>
588
+ #
589
+ def initialize(*argsp)
590
+ @id = @@NEXTID
591
+ @@NEXTID += 1
592
+ #
593
+ # Set up defaults
594
+ #
595
+ (@name, @description, @code, @reMatch, @reExtra, @mask,
596
+ @signbit, @dWidth, @dValue, @truncate) = nil
597
+ @enabled = true
598
+ @priority = 1000
599
+ @data = []
600
+ @fill = ' '
601
+ @justify = :left
602
+ #
603
+ # We can either handle an order-dependent list of arguments, or
604
+ # a hash representing keyword arguments.
605
+ #
606
+ args = (argsp.length == 1) ? argsp[0] : argsp
607
+ case args.class
608
+ when Array
609
+ (@name, @description, @enabled, @priority, @code, @reMatch,
610
+ @reExtra, @mask, @signbit, @dWidth, @fill, @justify,
611
+ @truncate, @data) = args
612
+ when Hash
613
+ args.each { |key,val| eval("@{key.to_s} = val") }
614
+ end
615
+ @data = [@data] unless (@data.nil? || (@data.class == Array))
616
+ end # def initialize
617
+
618
+ #
619
+ # Disables the effector, removing it from consideration when the
620
+ # format string is being scanned. It can be subsequently
621
+ # re-enabled with the <i>Effector#enable()</i> method. This is a
622
+ # no-op if the effector is already disabled.
623
+ #
624
+ # :call-seq:
625
+ # disable<i>()</i> => <i>nil</i>
626
+ #
627
+ def disable()
628
+ @enabled = false
629
+ FTO.rebuildEffectorList()
630
+ end # def disable()
631
+
632
+ #
633
+ # Returns +true+ if the effector is disabled (<i>i.e.</i>,
634
+ # inactive and not considered when scanning the format string);
635
+ # otherwise returns +false+.
636
+ #
637
+ # :call-seq:
638
+ # disabled? => <i>Boolean</i>
639
+ #
640
+ def disabled?()
641
+ ! @enabled
642
+ end # def disabled?()
643
+
644
+ #
645
+ # Enables the effector, making certain that it is considered when
646
+ # the format string is being scanned. This is a no-op if the
647
+ # effector is already active.
648
+ #
649
+ # :call-seq:
650
+ # enable<i>()</i> => <i>nil</i>
651
+ #
652
+ def enable()
653
+ @enabled = true
654
+ FTO.rebuildEffectorList()
655
+ end # def enable()
656
+
657
+ #
658
+ # Returns +true+ if the effector is enabled (<i>i.e.</i>,
659
+ # active and considered when scanning the format string);
660
+ # otherwise returns +false+.
661
+ #
662
+ # :call-seq:
663
+ # enabled? => <i>Boolean</i>
664
+ #
665
+ def enabled?()
666
+ @enabled
667
+ end # def enabled?()
668
+
669
+ end # class Effector
670
+
671
+ # :stopdoc:
672
+ #
673
+ # Lambda functions (for no particular reason) used by effectors.
674
+ #
675
+
676
+ #
677
+ # Handle filling, justifying, and truncating.
678
+ #
679
+ FillAndJustify = lambda {
680
+ | eObj, rText, width, overflowChar |
681
+ overflowChar = '*' if (overflowChar.nil?)
682
+ if ((! width.nil?) && (width.to_s.length != 0))
683
+ width = width.to_i
684
+ else
685
+ width = eObj.dWidth
686
+ end
687
+ return rText if (width.nil?)
688
+ width = width.to_i
689
+ if (rText.length > width)
690
+ case eObj.truncate
691
+ when :right
692
+ rText = rText[0,width]
693
+ when :left
694
+ rText = rText[rText.length - width,width]
695
+ else
696
+ rText = overflowChar * width
697
+ end
698
+ else
699
+ #
700
+ # The result is shorter than the width. How do we make up the
701
+ # difference?
702
+ #
703
+ fill = eObj.fill * (width - rText.length)
704
+ case eObj.justify
705
+ when :left then rText += fill
706
+ when :right then rText = fill + rText
707
+ end
708
+ end
709
+ rText
710
+ }
711
+
712
+ #
713
+ # Get the next argument, use the default, or raise an exception.
714
+ #
715
+ GetArgument = lambda {
716
+ | eContext |
717
+ if (! eContext.argList.empty?)
718
+ arg = eContext.argList.first
719
+ elsif ((arg = eContext.effectorObj.dValue).nil?)
720
+ raise RuntimeError,
721
+ _('Insufficient arguments to format ') + "'#{eContext.ftoObj}'"
722
+ end
723
+ arg
724
+ }
725
+
726
+ #
727
+ # Predefined conversion functions. The function is passed a Context
728
+ # object (defined above).
729
+ #
730
+ # The function is responsible for getting its argument from
731
+ # argList.first. It can modify usedArgs, argList, and reuseArg, but
732
+ # this should be done with caution.
733
+ #
734
+ # The function's return value is the formatted quantity.
735
+ #
736
+
737
+ #
738
+ # Placeholder effector function.
739
+ #
740
+ ConvertTBD = lambda {
741
+ | eContext |
742
+ eContext.reuseArg = true
743
+ return 'TBD'
744
+ }
745
+
746
+ ConvertFixedWindow = lambda {
747
+ | eContext |
748
+ eObj = eContext.effectorObj
749
+ parcels = eContext.sMatched.match(Regexp.new(eObj.reExtra))
750
+ width = parcels.captures[0].sub(/</, '').to_i
751
+ #
752
+ # Clone the Context because we want to retain the arglist array
753
+ # identities, but don't want to mess up any of the other aspects
754
+ # needed by our caller.
755
+ #
756
+ f = FTO.new(parcels.captures[1], eContext.clone)
757
+ sNext = f.format
758
+ if ((diff = width - sNext.length) <= 0)
759
+ sNext = sNext[diff.abs,width]
760
+ else
761
+ sNext = (' ' * diff) + sNext
762
+ end
763
+ eContext.reuseArg = true
764
+ sNext
765
+ }
766
+
767
+ #
768
+ # Convert a numeric argument of some sort.
769
+ #
770
+ ConvertNumeric = lambda {
771
+ | eContext |
772
+ eObj = eContext.effectorObj
773
+ #
774
+ # This bit of funk is so we can correctly handle a string like
775
+ # '0xABC'.
776
+ #
777
+ thisArg = eval("#{GetArgument.call(eContext)}").to_i & eObj.mask
778
+ #
779
+ # Sign-extend the value if appropriate.
780
+ #
781
+ if ((! eObj.signbit.nil?) && ((thisArg & eObj.signbit) != 0))
782
+ thisArg |= ~ eObj.mask
783
+ end
784
+ #
785
+ # See if we need to convert to a non-decimal base.
786
+ #
787
+ case true
788
+ when eObj.data.include?(:octal) then replacement = thisArg.to_s(8)
789
+ when eObj.data.include?(:hex) then replacement = thisArg.to_s(16).upcase
790
+ else replacement = thisArg.to_s
791
+ end
792
+ width = eContext.sMatched.match(Regexp.new(eObj.reExtra)).captures[0]
793
+ FillAndJustify.call(eObj, replacement, width, '*')
794
+ }
795
+
796
+ #
797
+ # Insert a string
798
+ #
799
+ ConvertString = lambda {
800
+ | eContext |
801
+ eObj = eContext.effectorObj
802
+ if (eObj.data.include?(:lengthAsArg))
803
+ strLen = GetArgument.call(eContext)
804
+ eContext.usedArgs.push(eContext.argList.shift)
805
+ end
806
+ replacement = GetArgument.call(eContext)
807
+ unless (strLen.nil?)
808
+ if (strLen <= replacement.length)
809
+ replacement = replacement[0,strLen]
810
+ else
811
+ replacement += "\000" * (strLen - replacement.length)
812
+ end
813
+ end
814
+ replacement.gsub!(/[^[:print:]]/, '.') if (eObj.data.include?(:printable))
815
+ eObj = eContext.effectorObj
816
+ width = eContext.sMatched.match(Regexp.new(eObj.reExtra)).captures[0]
817
+ FillAndJustify.call(eObj, replacement, width, '*')
818
+ }
819
+
820
+ #
821
+ # Insert a repeating character.
822
+ #
823
+ ConvertRepeatChar = lambda {
824
+ | eContext |
825
+ eContext.reuseArg = true
826
+ eObj = eContext.effectorObj
827
+ exInfo = eContext.sMatched.match(Regexp.new(eObj.reExtra))
828
+ width = exInfo.captures[0].to_i
829
+ replacement = exInfo.captures[1] * width
830
+ return replacement
831
+ }
832
+
833
+ ConvertIsAre = lambda {
834
+ | eContext |
835
+ #
836
+ # We don't actually use the argument list, so we need to make sure
837
+ # the argument pointer doesn't get advanced.
838
+ #
839
+ eContext.reuseArg = true
840
+ result = (eContext.usedArgs.last == 1) ? _('is') : _('are')
841
+ result.upcase! if (eContext.sMatched == '!%IS')
842
+ return result
843
+ }
844
+
845
+ ConvertPlural = lambda {
846
+ | eContext |
847
+ #
848
+ # We don't actually use the argument list, so we need to make sure
849
+ # the argument pointer doesn't get advanced.
850
+ #
851
+ eContext.reuseArg = true
852
+ m2 = eContext.sMatched.match(Regexp.new(eContext.effectorObj.reExtra))
853
+ return '' if (m2.captures[0].nil?)
854
+ return m2.captures[0] if (eContext.usedArgs.last == 1)
855
+ result = (m2.captures[0].upcase == 'S') ? 'es' : 's'
856
+ result.upcase! if (m2.captures[0].match(/[A-Z]/))
857
+ return m2.captures[0] + result
858
+ }
859
+
860
+ # :startdoc:
861
+
862
+ #
863
+ # Start registering the standard effectors.
864
+ #
865
+
866
+ #
867
+ # The simple replacements ('insert a TAB', etc.)
868
+ #
869
+ FTO.registerEffector({
870
+ :name => 'TAB',
871
+ :description => _('Insert a TAB character'),
872
+ :reMatch => '!_',
873
+ :code => lambda {
874
+ | eContext |
875
+ eContext.reuseArg = true
876
+ return "\011"
877
+ }
878
+ })
879
+ FTO.registerEffector({
880
+ :name => 'Bang',
881
+ :description => _('Insert an exclamation mark ' +
882
+ '(!) character'),
883
+ :reMatch => '!!',
884
+ :code => lambda {
885
+ | eContext |
886
+ eContext.reuseArg = true
887
+ return '!'
888
+ }
889
+ })
890
+ FTO.registerEffector({
891
+ :name => 'Formfeed',
892
+ :description => _('Insert a form-feed control ' +
893
+ 'character'),
894
+ :reMatch => '!\^',
895
+ :code => lambda {
896
+ | eContext |
897
+ eContext.reuseArg = true
898
+ return "\014"
899
+ }
900
+ })
901
+ FTO.registerEffector({
902
+ :name => 'CR',
903
+ :description => _('Insert a carriage-return ' +
904
+ 'character'),
905
+ :reMatch => '!=',
906
+ :code => lambda {
907
+ | eContext |
908
+ eContext.reuseArg = true
909
+ return "\015"
910
+ }
911
+ })
912
+ FTO.registerEffector({
913
+ :name => 'LF',
914
+ :description => _('Insert a line-feed control ' +
915
+ 'character'),
916
+ :reMatch => '!,',
917
+ :code => lambda {
918
+ | eContext |
919
+ eContext.reuseArg = true
920
+ return "\012"
921
+ }
922
+ })
923
+ FTO.registerEffector({
924
+ :name => 'CRLF',
925
+ :description => _('Insert a carriage-return and ' +
926
+ 'a line-feed'),
927
+ :reMatch => '!/',
928
+ :code => lambda {
929
+ | eContext |
930
+ eContext.reuseArg = true
931
+ return "\015\012"
932
+ }
933
+ })
934
+
935
+ #
936
+ # Effectors that frob the argument list.
937
+ #
938
+ FTO.registerEffector({
939
+ :name => 'Reuse',
940
+ :description => _('Back the argument list up ' +
941
+ 'so the last argument gets re-used'),
942
+ :reMatch => '!-',
943
+ :code => lambda {
944
+ | eContext |
945
+ eContext.reuseArg = true
946
+ eContext.argList.unshift(eContext.usedArgs.pop) unless (eContext.usedArgs.empty?)
947
+ return ''
948
+ }
949
+ })
950
+ FTO.registerEffector({
951
+ :name => 'Skip',
952
+ :description => _('Skip over the next item in ' +
953
+ 'the argument list'),
954
+ :reMatch => '!\+',
955
+ :code => lambda {
956
+ | eContext |
957
+ eContext.usedArgs.push(eContext.argList.unshift) unless (eContext.argList.empty?)
958
+ return ''
959
+ }
960
+ })
961
+ FTO.registerEffector({
962
+ :name => 'ArgWidth',
963
+ :description => _('Specify a field width using ' +
964
+ 'an argument rather than a ' +
965
+ 'hard-coded value'),
966
+ :reMatch => '!#',
967
+ :code => lambda {
968
+ | eContext |
969
+ n = GetArgument.call(eContext)
970
+ return "!#{n.to_i.to_s}"
971
+ }
972
+ })
973
+ FTO.registerEffector({
974
+ :name => 'RepeatingChar',
975
+ :description => _('Insert n occurrences of ' +
976
+ 'a character'),
977
+ :reMatch => '!\d+\*.',
978
+ :reExtra => '!(\d+)\*(.)',
979
+ :code => ConvertRepeatChar,
980
+ })
981
+ FTO.registerEffector({
982
+ :name => 'Pluralise-English',
983
+ :description => _('Add an "s" or "es" suffix if ' +
984
+ 'the last argument used was not 1'),
985
+ :reMatch => '.?!%S',
986
+ :reExtra => '(.)?!%S',
987
+ :code => ConvertPlural,
988
+ })
989
+ FTO.registerEffector({
990
+ :name => 'is/are-English',
991
+ :description => _('Insert "is" or "are" depending ' +
992
+ 'on whether the last argument ' +
993
+ 'used was 1'),
994
+ :reMatch => '!%is',
995
+ :code => ConvertIsAre,
996
+ })
997
+ FTO.registerEffector({
998
+ :name => 'IS/ARE-English',
999
+ :description => _('Insert n occurrences of a ' +
1000
+ 'character'),
1001
+ :reMatch => '!%IS',
1002
+ :code => ConvertIsAre,
1003
+ })
1004
+
1005
+ FTO.registerEffector({
1006
+ :name => 'Fixed Window',
1007
+ :description => _('Force a right-justified field ' +
1008
+ 'width on the contents between ' +
1009
+ 'the effector delimiters'),
1010
+ :priority => 10,
1011
+ :reMatch => '!\d+<.*!>',
1012
+ :reExtra => '!(\d+<)(.*)(!>)',
1013
+ :fill => ' ',
1014
+ :truncate => :right,
1015
+ :justify => :right,
1016
+ :code => ConvertFixedWindow,
1017
+ })
1018
+ #
1019
+ # Strings
1020
+ #
1021
+ FTO.registerEffector({
1022
+ :name => 'String AS',
1023
+ :description => _('Insert a simple string'),
1024
+ :reMatch => '!\d*AS',
1025
+ :reExtra => '!(\d*)AS',
1026
+ :fill => ' ',
1027
+ :truncate => :right,
1028
+ :data => [:AS],
1029
+ :code => ConvertString,
1030
+ })
1031
+ FTO.registerEffector({
1032
+ :name => 'String AN',
1033
+ :description => _('Insert a simple string, ' +
1034
+ 'replacing non-printing ' +
1035
+ 'characters with "."'),
1036
+ :reMatch => '!\d*AN',
1037
+ :reExtra => '!(\d*)AN',
1038
+ :fill => ' ',
1039
+ :truncate => :right,
1040
+ :data => [:AS, :printable],
1041
+ :code => ConvertString,
1042
+ })
1043
+ FTO.registerEffector({
1044
+ :name => 'String AD',
1045
+ :description => _('Insert the first n characters ' +
1046
+ 'of a string, drawing n from the ' +
1047
+ 'argument list'),
1048
+ :reMatch => '!\d*AD',
1049
+ :reExtra => '!(\d*)AD',
1050
+ :fill => ' ',
1051
+ :truncate => :right,
1052
+ :data => [:lengthAsArg],
1053
+ :code => ConvertString,
1054
+ })
1055
+ FTO.registerEffector({
1056
+ :name => 'String AF',
1057
+ :description => _('Insert the first n characters ' +
1058
+ 'of a string, drawing n from the ' +
1059
+ 'argument list and replacing ' +
1060
+ 'non-printing characters with "."'),
1061
+ :reMatch => '!\d*AF',
1062
+ :reExtra => '!(\d*)AF',
1063
+ :fill => ' ',
1064
+ :truncate => :right,
1065
+ :data => [:lengthAsArg, :printable],
1066
+ :code => ConvertString,
1067
+ })
1068
+ FTO.registerEffector({
1069
+ :name => 'Byte: unsigned',
1070
+ :description => _('Insert the decimal ' +
1071
+ 'representation of an unsigned ' +
1072
+ '8-bit value, left space-filled ' +
1073
+ 'to the field width'),
1074
+ :reMatch => '!\d*UB',
1075
+ :reExtra => '!(\d*)UB',
1076
+ :mask => 0xFF,
1077
+ :code => ConvertNumeric,
1078
+ })
1079
+ FTO.registerEffector({
1080
+ :name => 'Byte: unsigned, zero-filled',
1081
+ :description => _('Insert the decimal ' +
1082
+ 'representation of an unsigned ' +
1083
+ '8-bit value, left zero-filled ' +
1084
+ 'to the field width'),
1085
+ :reMatch => '!\d*ZB',
1086
+ :reExtra => '!(\d*)ZB',
1087
+ :mask => 0xFF,
1088
+ :fill => '0',
1089
+ :justify => :right,
1090
+ :code => ConvertNumeric,
1091
+ })
1092
+ FTO.registerEffector({
1093
+ :name => 'Byte: signed',
1094
+ :description => _('Insert the decimal ' +
1095
+ 'representation of a signed ' +
1096
+ '8-bit value, left space-filled ' +
1097
+ 'to the field width'),
1098
+ :reMatch => '!\d*SB',
1099
+ :reExtra => '!(\d*)SB',
1100
+ :mask => 0xFF,
1101
+ :signbit => 0x80,
1102
+ :code => ConvertNumeric,
1103
+ })
1104
+ FTO.registerEffector({
1105
+ :name => 'Byte: octal',
1106
+ :description => _('Insert the octal ' +
1107
+ 'representation of an unsigned ' +
1108
+ '8-bit value, left zero-filled ' +
1109
+ 'to the field width (default ' +
1110
+ '3)'),
1111
+ :reMatch => '!\d*OB',
1112
+ :reExtra => '!(\d*)OB',
1113
+ :mask => 0xFF,
1114
+ :dWidth => 3,
1115
+ :fill => '0',
1116
+ :justify => :right,
1117
+ :truncate => :left,
1118
+ :data => [:octal],
1119
+ :code => ConvertNumeric,
1120
+ })
1121
+ FTO.registerEffector({
1122
+ :name => 'Byte: hex',
1123
+ :description => _('Insert the hexadecimal ' +
1124
+ 'representation of an unsigned ' +
1125
+ '8-bit value, left zero-filled ' +
1126
+ 'to the field width (default ' +
1127
+ '2)'),
1128
+ :reMatch => '!\d*XB',
1129
+ :reExtra => '!(\d*)XB',
1130
+ :mask => 0xFF,
1131
+ :dWidth => 2,
1132
+ :fill => '0',
1133
+ :justify => :right,
1134
+ :truncate => :left,
1135
+ :data => [:hex],
1136
+ :code => ConvertNumeric,
1137
+ })
1138
+ FTO.registerEffector({
1139
+ :name => 'Word: unsigned',
1140
+ :description => _('Insert the decimal ' +
1141
+ 'representation of an unsigned ' +
1142
+ '8-bit value, left space-filled ' +
1143
+ 'to the field width'),
1144
+ :reMatch => '!\d*UW',
1145
+ :reExtra => '!(\d*)UW',
1146
+ :mask => 0xFFFF,
1147
+ :code => ConvertNumeric,
1148
+ })
1149
+ FTO.registerEffector({
1150
+ :name => 'Word: unsigned, zero-filled',
1151
+ :description => _('Insert the decimal ' +
1152
+ 'representation of an unsigned ' +
1153
+ '8-bit value, left zero-filled ' +
1154
+ 'to the field width'),
1155
+ :reMatch => '!\d*ZW',
1156
+ :reExtra => '!(\d*)ZW',
1157
+ :mask => 0xFFFF,
1158
+ :fill => '0',
1159
+ :justify => :right,
1160
+ :code => ConvertNumeric,
1161
+ })
1162
+ FTO.registerEffector({
1163
+ :name => 'Word: signed',
1164
+ :description => _('Insert the decimal ' +
1165
+ 'representation of a signed ' +
1166
+ '16-bit value, left space-filled ' +
1167
+ 'to the field width'),
1168
+ :reMatch => '!\d*SW',
1169
+ :reExtra => '!(\d*)SW',
1170
+ :mask => 0xFFFF,
1171
+ :signbit => 0x8000,
1172
+ :code => ConvertNumeric,
1173
+ })
1174
+ FTO.registerEffector({
1175
+ :name => 'Word: octal',
1176
+ :description => _('Insert the octal ' +
1177
+ 'representation of an unsigned ' +
1178
+ '16-bit value, left zero-filled ' +
1179
+ 'to the field width (default ' +
1180
+ '6)'),
1181
+ :reMatch => '!\d*OW',
1182
+ :reExtra => '!(\d*)OW',
1183
+ :mask => 0xFFFF,
1184
+ :dWidth => 6,
1185
+ :fill => '0',
1186
+ :justify => :right,
1187
+ :truncate => :left,
1188
+ :data => [:octal],
1189
+ :code => ConvertNumeric,
1190
+ })
1191
+ FTO.registerEffector({
1192
+ :name => 'Word: hex',
1193
+ :description => _('Insert the hexadecimal ' +
1194
+ 'representation of an unsigned ' +
1195
+ '16-bit value, left zero-filled ' +
1196
+ 'to the field width (default ' +
1197
+ '4)'),
1198
+ :reMatch => '!\d*XW',
1199
+ :reExtra => '!(\d*)XW',
1200
+ :mask => 0xFFFF,
1201
+ :dWidth => 4,
1202
+ :fill => '0',
1203
+ :justify => :right,
1204
+ :truncate => :left,
1205
+ :data => [:hex],
1206
+ :code => ConvertNumeric,
1207
+ })
1208
+ FTO.registerEffector({
1209
+ :name => 'Long: unsigned',
1210
+ :description => _('Insert the decimal ' +
1211
+ 'representation of an unsigned ' +
1212
+ '8-bit value, left space-filled ' +
1213
+ 'to the field width'),
1214
+ :reMatch => '!\d*UL',
1215
+ :reExtra => '!(\d*)UL',
1216
+ :mask => 0xFFFFFFFF,
1217
+ :code => ConvertNumeric,
1218
+ })
1219
+ FTO.registerEffector({
1220
+ :name => 'Long: unsigned, zero-filled',
1221
+ :description => _('Insert the decimal ' +
1222
+ 'representation of an unsigned ' +
1223
+ '8-bit value, left zero-filled ' +
1224
+ 'to the field width'),
1225
+ :reMatch => '!\d*ZL',
1226
+ :reExtra => '!(\d*)ZL',
1227
+ :mask => 0xFFFFFFFF,
1228
+ :fill => '0',
1229
+ :justify => :right,
1230
+ :code => ConvertNumeric,
1231
+ })
1232
+ FTO.registerEffector({
1233
+ :name => 'Long: signed',
1234
+ :description => _('Insert the decimal ' +
1235
+ 'representation of a signed ' +
1236
+ '32-bit value, left space-filled ' +
1237
+ 'to the field width'),
1238
+ :reMatch => '!\d*SL',
1239
+ :reExtra => '!(\d*)SL',
1240
+ :mask => 0xFFFFFFFF,
1241
+ :signbit => 0x80000000,
1242
+ :code => ConvertNumeric,
1243
+ })
1244
+ FTO.registerEffector({
1245
+ :name => 'Long: octal',
1246
+ :description => _('Insert the octal ' +
1247
+ 'representation of an unsigned ' +
1248
+ '32-bit value, left zero-filled ' +
1249
+ 'to the field width (default ' +
1250
+ '11)'),
1251
+ :reMatch => '!\d*OL',
1252
+ :reExtra => '!(\d*)OL',
1253
+ :mask => 0xFFFFFFFF,
1254
+ :dWidth => 11,
1255
+ :fill => '0',
1256
+ :justify => :right,
1257
+ :truncate => :left,
1258
+ :data => [:octal],
1259
+ :code => ConvertNumeric,
1260
+ })
1261
+ FTO.registerEffector({
1262
+ :name => 'Long: hex',
1263
+ :description => _('Insert the hexadecimal ' +
1264
+ 'representation of an unsigned ' +
1265
+ '32-bit value, left zero-filled ' +
1266
+ 'to the field width (default ' +
1267
+ '8)'),
1268
+ :reMatch => '!\d*XL',
1269
+ :reExtra => '!(\d*)XL',
1270
+ :mask => 0xFFFFFFFF,
1271
+ :dWidth => 8,
1272
+ :fill => '0',
1273
+ :justify => :right,
1274
+ :truncate => :left,
1275
+ :data => [:hex],
1276
+ :code => ConvertNumeric,
1277
+ })
1278
+ FTO.registerEffector({
1279
+ :name => 'Quadword: unsigned',
1280
+ :description => _('Insert the decimal ' +
1281
+ 'representation of an unsigned ' +
1282
+ '8-bit value, left space-filled ' +
1283
+ 'to the field width'),
1284
+ :reMatch => '!\d*UQ',
1285
+ :reExtra => '!(\d*)UQ',
1286
+ :mask => 0xFFFFFFFFFFFFFFFF,
1287
+ :code => ConvertNumeric,
1288
+ })
1289
+ FTO.registerEffector({
1290
+ :name => 'Quadword: unsigned, zero-filled',
1291
+ :description => _('Insert the decimal ' +
1292
+ 'representation of an unsigned ' +
1293
+ '8-bit value, left zero-filled ' +
1294
+ 'to the field width'),
1295
+ :reMatch => '!\d*ZQ',
1296
+ :reExtra => '!(\d*)ZQ',
1297
+ :mask => 0xFFFFFFFFFFFFFFFF,
1298
+ :fill => '0',
1299
+ :justify => :right,
1300
+ :code => ConvertNumeric,
1301
+ })
1302
+ FTO.registerEffector({
1303
+ :name => 'Quadword: signed',
1304
+ :description => _('Insert the decimal ' +
1305
+ 'representation of a signed ' +
1306
+ '64-bit value, left space-filled ' +
1307
+ 'to the field width'),
1308
+ :reMatch => '!\d*SQ',
1309
+ :reExtra => '!(\d*)SQ',
1310
+ :mask => 0xFFFFFFFFFFFFFFFF,
1311
+ :signbit => 0x8000000000000000,
1312
+ :code => ConvertNumeric,
1313
+ })
1314
+ FTO.registerEffector({
1315
+ :name => 'Quadword: octal',
1316
+ :description => _('Insert the octal ' +
1317
+ 'representation of an unsigned ' +
1318
+ '64-bit value, left zero-filled ' +
1319
+ 'to the field width (default ' +
1320
+ '22)'),
1321
+ :reMatch => '!\d*OQ',
1322
+ :reExtra => '!(\d*)OQ',
1323
+ :mask => 0xFFFFFFFFFFFFFFFF,
1324
+ :dWidth => 22,
1325
+ :fill => '0',
1326
+ :justify => :right,
1327
+ :truncate => :left,
1328
+ :data => [:octal],
1329
+ :code => ConvertNumeric,
1330
+ })
1331
+ FTO.registerEffector({
1332
+ :name => 'Quadword: hex',
1333
+ :description => _('Insert the hexadecimal ' +
1334
+ 'representation of an unsigned ' +
1335
+ '64-bit value, left zero-filled ' +
1336
+ 'to the field width (default ' +
1337
+ '16)'),
1338
+ :reMatch => '!\d*XQ',
1339
+ :reExtra => '!(\d*)XQ',
1340
+ :mask => 0xFFFFFFFFFFFFFFFF,
1341
+ :dWidth => 16,
1342
+ :fill => '0',
1343
+ :justify => :right,
1344
+ :truncate => :left,
1345
+ :data => [:hex],
1346
+ :code => ConvertNumeric,
1347
+ })
1348
+
1349
+ end # module FTO