flnews_post_proc 1.40
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +374 -0
- data/bin/flnews_post_proc +84 -0
- data/doc/html/flnews_post_proc.html +654 -0
- data/doc/html/flnews_post_proc_fr.html +701 -0
- data/doc/license.txt +13 -0
- data/doc/man/flnews_post_proc.1.gz +0 -0
- data/doc/man/flnews_post_proc_fr.1.gz +0 -0
- data/doc/pdf/flnews_post_proc.pdf +0 -0
- data/doc/pdf/flnews_post_proc_fr.pdf +0 -0
- data/doc/rst/flnews_post_proc.rst +334 -0
- data/doc/rst/flnews_post_proc_fr.rst +377 -0
- data/lib/basic_logging.rb +170 -0
- data/lib/body.rb +270 -0
- data/lib/color_output.rb +65 -0
- data/lib/configuration.rb +136 -0
- data/lib/flnews_post_proc.conf +172 -0
- data/lib/flnews_post_proc.rb +71 -0
- data/lib/headers.rb +148 -0
- data/lib/newsgroups.rb +134 -0
- data/lib/override.rb +199 -0
- data/lib/ruby_dlg +136 -0
- data/lib/version.rb +21 -0
- data/lib/whiptail_dlg +79 -0
- data/lib/yad_dlg +52 -0
- data/lib/zenity_dlg +59 -0
- metadata +88 -0
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
|
+
|
data/lib/color_output.rb
ADDED
@@ -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
|
+
|