flnews_post_proc 1.40

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.
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
+