flnews_post_proc 1.40

Sign up to get free protection for your applications and to get access to all the features.
data/lib/body.rb ADDED
@@ -0,0 +1,270 @@
1
+ #encoding: UTF-8
2
+
3
+ =begin
4
+ /***************************************************************************
5
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
6
+ * This program is free software; you can redistribute it and/or modify *
7
+ * it under the terms of the WTFPL 2.0 or later, see *
8
+ * http://www.wtfpl.net/about/ *
9
+ * *
10
+ * This program is distributed in the hope that it will be useful, *
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
13
+ * *
14
+ ***************************************************************************/
15
+ =end
16
+
17
+ require_relative 'basic_logging'
18
+ require_relative 'configuration'
19
+
20
+ # an object of this class represents the body of a news-article.
21
+ class Body
22
+ # a class-level configuration instance.
23
+ @@config = Configuration.instance
24
+ include BasicLogging
25
+
26
+ # reads the body text of the article
27
+ def initialize(article_text)
28
+ debug 'body intialize'
29
+ # for simplicity.
30
+ line = nil
31
+ # transform the article into an array.
32
+ line_array = article_text.split($LN)
33
+ # keep only from the first after an empty line ''
34
+ start_index = line_array.index('')
35
+
36
+ # ... to the end of the current array (all that follows '').
37
+ @lines = line_array.slice(start_index + 1, line_array.size)
38
+ debug('initialize(): body lines are ' << @lines.inspect)
39
+ end
40
+
41
+ # If so configured, replace an eventual followup-intro by the one
42
+ # configured for a group. This may depend on other conditions and
43
+ # must be triggered explicitly.
44
+ def set_intro(intro)
45
+ return if !intro || intro.empty? || @@config.no_intro
46
+
47
+ # name of the previous poster
48
+ fup_name = nil
49
+ # the current newsgroup
50
+ fup_group = nil
51
+
52
+ debug('FUP_NAME is ' << @@config.FUP_NAME)
53
+ debug('FUP_GROUP is ' << @@config.FUP_GROUP)
54
+ # The expressions which allow the identification of both
55
+ # in the current article.
56
+ fn = @@config.FUP_NAME
57
+ fg = @@config.FUP_GROUP
58
+
59
+ # Okay, this is called parsing, when it is well done.
60
+ # I just try and am happy when it works.
61
+ @lines.each_with_index do |line, i|
62
+ # find the name in the intro-line
63
+ if !fn.strip.empty? && !line.strip.empty? && !fup_name
64
+ # match a name
65
+ fup_name = line.match(Regexp.new(fn) ) do |md|
66
+ # debug("\tmatch: " << md.to_s)
67
+ md.length == 2 ? md[1] : md[0]
68
+ end
69
+ debug("\tfup_name: " << fup_name.to_s)
70
+
71
+ if !fg.strip.empty? && !fup_group
72
+ # match a group
73
+ fup_group = line.match(Regexp.new(fg) ) do |md|
74
+ debug("\tmatch: " << md.to_s)
75
+ md.length == 2 ? md[1] : nil
76
+ end
77
+ end
78
+ debug "group is " << fup_group.to_s
79
+
80
+ # All that follows depends on the presence of a name
81
+ # in the intro-string.
82
+ if fup_name && !fup_name.strip.empty?
83
+ # keep the current intro for later
84
+ ointro = line
85
+ line = ''
86
+ while line.strip.empty?
87
+ i = i.next
88
+ line = @lines[i]
89
+ end
90
+ # check if there is a citation, at all
91
+ if(line.start_with?('>'))
92
+ debug("\tfound intro " << ointro)
93
+ # variables are part of the $intro.
94
+ # Do substitutions.
95
+ intro.sub!('%fup_name%', fup_name) if fup_name
96
+ intro.sub!('%fup_group%', fup_group) if fup_group
97
+ debug("\tsetting intro " << intro.to_s)
98
+
99
+ # exchange original intro-line against the new one
100
+ @lines[@lines.index(ointro)] = intro.strip
101
+ # looked complicated because it is.
102
+ end
103
+ end
104
+ end # fn.strip.empty?
105
+ end # lines.each_with_index
106
+ end
107
+
108
+ def set_signature(signature)
109
+ # unless no changes requested.
110
+ if signature && !signature.empty?
111
+ # remove any signature(s) from
112
+ # the current article
113
+ sigpos = @lines.index('-- ')
114
+ debug('found signature at position ' << sigpos) if sigpos
115
+ @lines = @lines.slice(0, sigpos ) if sigpos
116
+ debug('setting signature ' << signature) if signature
117
+ @lines << "-- " << signature if signature
118
+ end
119
+ end
120
+
121
+ def join
122
+ return @lines.join("\r\n")
123
+ end
124
+
125
+ # Verify and possibly correct links in the post.
126
+ # Simple.
127
+ def handle_urls()
128
+ # Determine here or elsewhere if URLs shall be verified.
129
+ # Default is != no. nil or '' do qualify as default.
130
+ if @@config.VFY_URLS
131
+ @lines.each_with_index do | l, i |
132
+ # leave cited lines as they are.
133
+ if !l.start_with?( '>')
134
+ # IMPORTANT --------------------------------->
135
+ # IT IS HENCEFORTH PROHIBITED TO WRITE AN EMAIL-ADDRESS
136
+ # IN THE BODY OF A NEWS-POST AND TO NOT PREPEND IT WITH
137
+ # mailto:
138
+ # ... Because I do not know what to do in these cases,
139
+ # and I am the boss!
140
+ # <----------------------------
141
+
142
+ # BUGFIX : Urls with @
143
+ new_line = handle_news(l)
144
+ # http(s)
145
+ if l.include?('http')
146
+ new_line = handle_http(l)
147
+ end #if http
148
+ @lines[i] = new_line if new_line
149
+ end # if '>'
150
+ end # @lines.each_with_index
151
+ end # if verify
152
+ end
153
+
154
+ # extract URL or other stuff, if configured for footnotes,
155
+ def handle_references()
156
+ # a symbol or string to mark the beginning an ending of a future footnote.
157
+ ref_delim = @@config.REFERENCES_DELIMITER
158
+ debug('references delimiter is ' << ref_delim)
159
+ references = Array.new
160
+ body = @lines.join($LN)
161
+ if ref_delim && !ref_delim.strip.empty?
162
+ unless ref_delim == ref_delim.reverse
163
+ ref_delim.strip!
164
+ ref_rx = Regexp.new(ref_delim.dup << ".*?" << ref_delim.reverse, Regexp::MULTILINE)
165
+ debug('ref_rx is ' << ref_rx.to_s)
166
+ index = 0
167
+ # I cannot work with an array, here, and apply the pattern
168
+ # to the whole body, over multiple lines, if need be.
169
+ begin
170
+ ref = body.match(ref_rx )
171
+ debug("found reference " << ref.to_s << " (length: " << (ref ? ref.to_s.size.to_s : '0') << ")")
172
+ if ref
173
+ debug('ref is ' << ref.to_s)
174
+ # ... This is some presentation thing and I think
175
+ # it works, too.
176
+ r = ref[0].gsub(/[ \t]+/, ' ').strip
177
+ r.gsub!("\n", "\n ")
178
+ references << r
179
+ index += 1
180
+ body.gsub!(ref[0], format(@@config.REFERENCE_FORMAT, index.to_s ))
181
+ end
182
+ end until ref == nil
183
+ debug("all references found:\n" << references.join('\n'))
184
+ else
185
+ msg = 'The References Delimiter is the same in its reversed form.'
186
+ msg << "#{$LN}Cannot handle references or footnotes!"
187
+ error(msg)
188
+ end
189
+
190
+ if(references && !references.empty?)
191
+ # a line, separating the footnotes from the body of the article
192
+ body << $LN << @@config.REFERENCES_SEPARATOR << $LN
193
+ references.each_with_index do |r, i|
194
+ r = r.gsub(ref_delim, '').gsub(ref_delim.reverse,'')
195
+ body << (i + 1 ).to_s << ") " << r.strip << $LN
196
+ end
197
+ end
198
+ end
199
+ @lines = body.split($LN)
200
+ end
201
+ private
202
+
203
+ # handle <news:..@...>
204
+ def handle_news(l)
205
+ # ... “Do not trust nobody!”
206
+ if l.include?('@') && ! l.include?('http:')
207
+ # And I forgot how this works. It does.
208
+ l.split.collect do |ele|
209
+ # angular brackets are there alright. Hurra.
210
+ url = ele.match(/\<(.*)\>/)
211
+ unless !url
212
+ debug(' with angular brackets: ' << url.inspect)
213
+ l = l.sub(url[1], 'news:' << url[1]) if !url[1].include?('mailto')
214
+ else
215
+ url = ele.match(/\S+@(\.|[^\p{P}]+)\S+/)
216
+ if url
217
+ debug(' withOUT angular brackets: ' << url.inspect)
218
+ l = l.sub(url[0], '<news:' << url[0] << '>') if !url[0].include?('mailto')
219
+ end
220
+ end
221
+ end # url_strs = collect
222
+ debug(' news: line is now ' << l)
223
+ return l
224
+ end
225
+ end
226
+
227
+ def handle_http(l)
228
+ debug('handle http')
229
+ l_array = l.split
230
+ url_strs = l_array.collect{|ele| ele.strip.include?('http') ? ele.strip : nil}.compact
231
+ debug('url_strs: ' << url_strs.to_s)
232
+ url_strs.each do |str|
233
+ # keep str for final replacement
234
+ nstr = str.dup
235
+ debug( 'try to match http-link')
236
+ begin
237
+ # missing slashes ?
238
+ match = str.match(/(https?):(\/{2})/)
239
+ if (!match || match.length != 3 || match[2] != '//')
240
+ debug 'cannot find slashes in the URL'
241
+ if str.match(/https:/)
242
+ nstr = str.sub(/https:/, "https://")
243
+ elsif str.match(/http:/)
244
+ nstr = str.sub(/http:/, "http://")
245
+ end
246
+ debug ' with slashes: ' << nstr
247
+ end
248
+ # missing angular brackets
249
+ nstr.sub!(/http/, "<http") if !nstr.include?( "<http")
250
+ nstr << '>' if !nstr.end_with? '>'
251
+ debug ' matching nstr ' << nstr
252
+ match = nstr.match(/\<?(https?:\/\/[^\>]*)\>?/)
253
+ # get the darned URL already!
254
+ url = match[1] if match
255
+ # nstr = with_angular_brackets(nstr, url)
256
+ return l.sub(str, nstr)
257
+ rescue Exception => ex
258
+ # this had looked intelligent, once.
259
+ line = __LINE__
260
+ msg = "Line #{line}: Cannot match the url " << str << ' against the Regexp (' << ex.message << ')'
261
+ msg << "\nAborting. Bye"
262
+ fatal msg
263
+ STDERR.puts msg
264
+ exit false
265
+ end
266
+ end
267
+ end
268
+ end
269
+ # Ω
270
+
@@ -0,0 +1,65 @@
1
+ #encoding: UTF-8
2
+ =begin
3
+ /***************************************************************************
4
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
5
+ * This program is free software; you can redistribute it and/or modify *
6
+ * it under the terms of the WTFPL 2.0 or later, see *
7
+ * http://www.wtfpl.net/about/ *
8
+ * *
9
+ * This program is distributed in the hope that it will be useful, *
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
12
+ * *
13
+ ***************************************************************************/
14
+ =end
15
+
16
+ # Functions to apply colors to terminal output.
17
+ # This is stolen from the Internet and I have lost track of my own additions
18
+ # and modifications.
19
+
20
+ COLORS = {:default => 9, :black => 0, :red => 1, :green => 2, :yellow => 3, :blue => 4, :purple => 5, :cyan => 6, :white => 7 }
21
+
22
+ BG = 4
23
+ FG = 3
24
+ REGULAR = 0
25
+ BOLD = 1
26
+ UNDERLINE = 4
27
+ BLINK = 5
28
+ SWAP = 7
29
+ NEUTRAL = 0
30
+
31
+ STYLES = {:regular => REGULAR, :bold => BOLD, :underline => UNDERLINE, :blink => BLINK, :swap => SWAP, :neutral => NEUTRAL}
32
+
33
+ # Colorizes the given text. Color-code is either an escape-sequence or one of
34
+ # the symbols representing color-names in the COLORS hash.
35
+ def colorize(text, color_code)
36
+ if (COLORS.keys.include?(color_code) )
37
+ "\033[3#{COLORS[color_code]}m#{text}\033[0m"
38
+ else
39
+ "#{color_code}#{text}\033[0m"
40
+ end
41
+ end
42
+
43
+ def style(text, style_code)
44
+ "#{style_code}#{text}\033[0m"
45
+ end
46
+ # a function which allows to manipulate every known aspect of the ansi-output.
47
+ def colored_output(output_text, fg_color = :default, bg_color = :default, style = :regular , mode = :neutral )
48
+ "\033[%i;%i;%i%i;%i%im%s\033[0m" %[STYLES[mode.to_sym], STYLES[style.to_sym], FG, COLORS[fg_color.to_sym], BG, COLORS[bg_color.to_sym], output_text]
49
+ end
50
+
51
+ # convenience functions
52
+ def red(text); colorize(text, "\033[31m"); end
53
+ def green(text); colorize(text, "\033[32m"); end
54
+ def yellow(text); colorize(text, "\033[33m"); end
55
+ def purple(text); colorize(text, "\033[35m"); end
56
+ def cyan(text); colorize(text, "\033[36m"); end
57
+ def blue(text); colorize(text, "\033[34m"); end
58
+ def white(text); colorize(text, "\033[37m"); end
59
+
60
+ def black_on_white(text); colorize(colorize(text, "\033[30m"), "\033[47m");end
61
+ def white_on_black(text); colorize(colorize(text, "\033[37m"), "\033[40m");end
62
+
63
+ def bold(text); style(text, "\033[01m");end
64
+ def underline(text); style(text, "\033[04m");end
65
+
@@ -0,0 +1,136 @@
1
+ #encoding: UTF-8
2
+ =begin
3
+ /***************************************************************************
4
+ * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
5
+ * This program is free software; you can redistribute it and/or modify *
6
+ * it under the terms of the WTFPL 2.0 or later, see *
7
+ * http://www.wtfpl.net/about/ *
8
+ * *
9
+ * This program is distributed in the hope that it will be useful, *
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
12
+ * *
13
+ ***************************************************************************/
14
+ =end
15
+
16
+ require_relative 'basic_logging'
17
+ require_relative 'version'
18
+ require 'yaml'
19
+ require 'singleton'
20
+
21
+ # An object of this class represents the configuration of the program.
22
+ # The parameters are set in the configuration-file
23
+
24
+ class Configuration
25
+ include Singleton
26
+ include BasicLogging
27
+
28
+ self.extend(BasicLogging)
29
+
30
+ def initialize()
31
+ debug 'Configuration::initialize()'
32
+ confname = PROGNAME << '.conf'
33
+ # try to open user-configuration
34
+ @config_file = ENV['HOME'].dup << File::Separator << '.' << confname
35
+ # if user-configuration does not exist, copy installed version.
36
+ installed_config = File::dirname(File::absolute_path(__FILE__)) << File::Separator << confname
37
+ debug 'installed configuration is in ' << installed_config
38
+ if !File.exist?(@config_file)
39
+ begin
40
+ File.open(@config_file, 'w') do |cf|
41
+ cf.write(File.read(installed_config ) )
42
+ end
43
+ rescue => ex
44
+ STDERR.puts('Cannot write user-configuration to ' << @config_file << '! (' << ex.message << ')' )
45
+ end
46
+ end
47
+ # read the config-file.
48
+ read_config
49
+ # updates the user-configuration *ONLY*
50
+ # if necessary
51
+ update_config(installed_config)
52
+ end
53
+
54
+ # What this object does not have, may still be in the configuration.
55
+ def method_missing(method, args = nil)
56
+ if @conf
57
+ v = @conf[method]
58
+ debug('method_missing returns value for ' << method.to_s << ': |' << v.to_s << '|')
59
+ return v
60
+ else
61
+ error("user-version of the configuration (#{@conf}) is not accessible")
62
+ end
63
+ end
64
+
65
+ # set a configuration option.
66
+ def set(key, value)
67
+ if @conf
68
+ @conf[key] = value
69
+ if key == 'DEBUG_LOG'
70
+ set_target value
71
+ end
72
+ end
73
+ end
74
+
75
+ attr_reader :log_target, :log_level
76
+ private
77
+
78
+ # updates the configuration file, if need be.
79
+ def update_config(i_config)
80
+ if @conf
81
+ conf_version = @conf[:CONFIG_VERSION]
82
+ if !conf_version || conf_version < PROGVERSION.to_f
83
+ info "configuration has an older version number, looking for changes"
84
+ i_conf = YAML::load_file(i_config)
85
+ i_conf.transform_keys!{|k| k.to_sym}
86
+ i_keys = i_conf.keys
87
+ i_keys.each do |k|
88
+ if !@conf[k]
89
+ info('new configuration option: ' << k.to_s)
90
+ @conf[k] = ''
91
+ end
92
+ end
93
+ @conf[:CONFIG_VERSION] = PROGVERSION.to_f
94
+ bak_conf = @config_file.dup << '_' << conf_version.to_s
95
+ info('Old configuration is saved to ' << bak_conf )
96
+ info('New configuration is saved to ' << @config_file )
97
+ begin
98
+ File::write(bak_conf, File.read(@config_file))
99
+ File::write(@config_file, @conf.to_yaml)
100
+ rescue Exception => ex
101
+ msg = ex.message
102
+ error "Cannot write altered configuration to " << @config_file << "!"
103
+ error msg
104
+ exit false
105
+ end
106
+ end
107
+ else
108
+ error "User-version of the configuration (#{@conf}) inaccessibble."
109
+ exit false
110
+ end
111
+ end
112
+
113
+ # reads the configuration and sets some properties.
114
+ def read_config
115
+ if File::exist?(@config_file) && File::readable?(@config_file)
116
+ begin
117
+ @conf = YAML::load_file(@config_file)
118
+ @conf.transform_keys!{|k| k.to_sym}
119
+ @keys = @conf.keys
120
+ clevel = @conf[:LOG_LEVEL]
121
+ set_level clevel
122
+ set_target @conf[:DEBUG_LOG]
123
+ debug('log target and -level set: ' << @@log_level.to_s << ', ' << @@target.path)
124
+ rescue Exception => ex
125
+ msg = ex.message
126
+ error msg
127
+ exit false
128
+ end
129
+ else
130
+ STDERR.puts("cannot read the configuration-file (" << @config_file << ")")
131
+ exit false
132
+ end
133
+ end
134
+ end
135
+ # EOF
136
+
@@ -0,0 +1,172 @@
1
+ #/***************************************************************************
2
+ # * 2023-2024, Michael Uplawski <michael.uplawski@uplawski.eu> *
3
+ # * *
4
+ # * This program is free software; you can redistribute it and/or modify *
5
+ # * it under the terms of the DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE *
6
+ # * *
7
+ # * This program is distributed in the hope that it will be useful, *
8
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of *
9
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
10
+ # * *
11
+ # ***************************************************************************/
12
+ # This is a YAML file. Keep intact these three dashes.
13
+ ---
14
+ # CONFIG_VERSION – DO NOT MODIFY
15
+ # If this version is smaller than that of the current program, this file
16
+ # may be updated automatically. Your settings should not be altered in
17
+ # the process, if they are still valid.
18
+ # ATTN! COMMENTS WILL BE REMOVED but a copy of your previous configuration
19
+ # will be saved in a file with a version suffix.
20
+ CONFIG_VERSION: 1.35
21
+
22
+ # FUP_NAME
23
+ # A Regular Expression, describing the string which contains the name of
24
+ # previous poster who is the author of a quoted post. This string is
25
+ # recognized in the original article and may be used with the fitting element
26
+ # from GROUP_INTRO, below. The Regexp-format is that of the Regexp class in
27
+ # Ruby, noted as a String. Beware to mask a backslash '\' by another one,
28
+ # like in the example. A capture-group '()' serves to extract the name from the
29
+ # match result.
30
+ # Leave this field empty to keep the default from the FLNews configuration
31
+ # intact.
32
+ # CONTENT: A String equivalent of a regular expression.
33
+ # DEFAULT: EMPTY
34
+ # EXAMPLE1: "Am \\d+.\\d+.\\d{2,4} um \\d+:\\d+ schrieb (.*):"
35
+ # EXAMPLE2: "(.*) wrote:"
36
+ FUP_NAME: "(.*) wrote in"
37
+
38
+ #FUP_GROUP
39
+ # A Regular Expression, describing the string which contains the newsgroup
40
+ # where the previous post, that you are referring to in the followup, had been
41
+ # published.
42
+ # Leave this field empty to ignore the precise group.
43
+ # CONTENT: A String equivalent of a regular expression.
44
+ # DEFAULT: EMPTY
45
+ # EXAMPLE: "wrote in (.*):"
46
+ FUP_GROUP: 'wrote in (.*)'
47
+
48
+ # GROUP_INTROS:
49
+ # Introductory strings, referring to the previous poster who is the author of a
50
+ # quoted post. If you match the newsgroup of the post (see FUP_GROUP), you can
51
+ # use these variables in the result.
52
+ # Currently only %fup_name% and %fup_group% are reproduced in the resulting
53
+ # introductory string.
54
+ # CONTENT: A newsgroup or regexp, followed by a colon, a space and a String.
55
+ # DEFAULT: As configured in FLNews
56
+ # EXAMPLE: alt.test: "Thus spoke #{fup_name} on that baleful #{fup_date}:"
57
+ GROUP_INTROS:
58
+ .*fr.test: "(%fup_name%) a écris :"
59
+ .*de.test: "%fup_name% hat in %fup_group% getestet:"
60
+ de\.*: "%fup_name% hat geschrieben:"
61
+ uk\.*: "%fup_name% wrote:"
62
+ fr\.*: "%fup_name% a écrit:"
63
+
64
+ # GROUP_SIGS
65
+ # A signature line per Newsgroup.
66
+ # ATTN! In multi line signatures, use \r\n for line breaks!
67
+ # CONTENT: A newsgroup or regexp, followed by a colon, a space and a String.
68
+ # DEFAULT: As configured in flnews
69
+ # EXAMPLE: alt.test: "Signature for alt.test\r\nsecond line"
70
+ GROUP_SIGS:
71
+ .*de.test: "newsgroup_hook .*de.test\r\nMit Zeilenumbruch"
72
+ .*fr.test: "newsgroup_hook .*fr.test\r\n2ème ligne, « guillemets »"
73
+ de.*: Geh Kaffee kochen
74
+ fr.*: "« Ton ordinateur de rêve ? » – « Le bleue. »\r\n "
75
+
76
+ # CUSTOM_HEADERS
77
+ # Additional headers for the outgoing article.
78
+ # If you wish to insert an "X-Post-Processor" header, the current version
79
+ # of the program will be appended.
80
+ # CONTENT: A dash and space, then a String, comprising the name of the header, ending in a
81
+ # colon and the value of the header
82
+ # DEFAULT: undefined
83
+ # EXAMPLE (two headers):
84
+ # - 'X-My-Header: nothing fancy'
85
+ # - 'X-Post-Processor: flnews_post_proc'
86
+ CUSTOM_HEADERS:
87
+ - "X-Post-Processor: flnews_post_proc"
88
+
89
+ # XNAY_GROUPS:
90
+ # The newsgroups, where a header X-No-Archive: YES shall be set.
91
+ # CONTENT: a dash and space, then a String, containing the name of the group
92
+ # or a regexp.
93
+ # DEFAULT: empty
94
+ # EXAMPLE: - "alt.test"
95
+ XNAY_GROUPS:
96
+ - ".*.test"
97
+
98
+ # DEBUG_LOG:
99
+ # The name of a file, where debug messages are written. Setting this
100
+ # variable will enable the log. Leave empty to disable logging.
101
+ # CONTENT: The name of a writable file, which will be overwritten.
102
+ # DEFAULT: empty
103
+ # EXAMPLE: '/tmp/a_log-file.txt'
104
+ DEBUG_LOG: '/tmp/flnews_post_proc.log'
105
+
106
+ # LOG LEVEL
107
+ # One of debug, fatal, error, info, warn
108
+ LOG_LEVEL: 'info'
109
+
110
+ # REFERENCES_SEPARATOR
111
+ # A symbol or sequence of symbols marking the end of the message-body and the
112
+ # beginning of a list of “references” or “footnotes”. It will only appear, if
113
+ # the original message-body contains text marked for use as such a footnote. See
114
+ # REFERENCES_DELIMITER.
115
+ # If the option is not defined or empty, the list of footnotes will appear
116
+ # below the last line of the message body and no separator will be inserted.
117
+ # CONTENT: A quoted symbol or sequence of symbols.
118
+ # DEFAULT: empty
119
+ # EXAMPLE: '---------'
120
+ # REFERENCES_SEPARATOR: "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁"
121
+ REFERENCES_SEPARATOR: "──────────────────"
122
+
123
+ # REFERENCE_DELIMITER
124
+ # A symbol or sequence of symbols marking the beginning of a text which will
125
+ # serve as footnote (or reference). The reversed sequence musst be used to mark
126
+ # the end of the text. The presence of this sequence or symbol in the origimal
127
+ # message body will cause the enclosed text to be moved below the message body.
128
+ # The REFERENCES_SEPARATOR, if defined, will separate the message from the list
129
+ # of footnotes.
130
+ # If this option is not defined or empty, footnotes are not created.
131
+ # CONTENT a quoted symbol or sequence of symbols.
132
+ # DEFAULT: none/empty
133
+ # EXAMPLE: '%?'
134
+ REFERENCES_DELIMITER: "%="
135
+
136
+ # REFERENCE_FORMAT
137
+ # A format-string, using %s for a number, replacing the reference-
138
+ # text in the message body.
139
+ # DEFAULT: " %s]" -> becomes 1) ... 2) ... 3)
140
+ # EXAMPLE: "(%s)" -> becomes (1) ... (2) ... (3)
141
+ REFERENCE_FORMAT: " ➤%s"
142
+
143
+ # VFY_URLS
144
+ # A Boolean constant. It determines if the program shall verify and possibly
145
+ # try to correct URLs. Even if URLs are identified as such, only a few
146
+ # manipulations are attempted :
147
+ # * Angular brackets '<' and '>' are added, if missing
148
+ # * Article-references are prepended with "news:", if missing
149
+ # * Slashes are added, if they are found missing after "http(s):"
150
+ # ATTN! The program is unable to discern "mailto:" and "news:" references. If
151
+ # neither is given, but '@' is present, "news:" is automatically prepended.
152
+ #
153
+ # If the variable is not set, a value 'yes' is assumed.
154
+ # CONTENT: One of YES, yes, NO, no, and other variations of case.
155
+ # DEFAULT: yes
156
+ # Example: ... I let you guess.
157
+ VFY_URLS: YES
158
+
159
+ # OVERRIDE_CONFIG
160
+ # A Boolean constant. You can choose to override the following
161
+ # configuration options before an article is posted:
162
+ # GROUP_SIGS, XNAY_GROUPS, CUSTOM_HEADERS and DEBUG_LOG.
163
+ # A dialog may be displayed which allows you to disable any of these
164
+ # four options, so that the defaults from flnews prevail.
165
+ # A fifth option (PP) can be used to disable post-processing completely.
166
+ #
167
+ # Set this option to no, NO or similar to disable the dialog.
168
+ # DEFAULT: yes
169
+ OVERRIDE_CONFIG: YES
170
+
171
+ # EOF
172
+