flnews_post_proc 1.94 → 1.97
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.
- checksums.yaml +4 -4
- data/doc/fr/html/flnews_post_proc.html +137 -79
- data/doc/fr/man/flnews_post_proc.1.gz +0 -0
- data/doc/fr/pdf/flnews_post_proc.pdf +0 -0
- data/doc/fr/rst/flnews_post_proc.rst +75 -13
- data/doc/html/flnews_post_proc.html +394 -304
- data/doc/man/flnews_post_proc.1.gz +0 -0
- data/doc/pdf/flnews_post_proc.pdf +0 -0
- data/doc/rst/flnews_post_proc.rst +289 -237
- data/lib/bak_basic_logging.rb +202 -0
- data/lib/basic_logging.rb +62 -14
- data/lib/body.rb +1 -0
- data/lib/color_output.rb +41 -4
- data/lib/configuration.rb +1 -0
- data/lib/flnews_post_proc.conf +15 -1
- data/lib/headers.rb +80 -5
- data/lib/version.rb +2 -2
- metadata +5 -4
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/bin/env ruby
|
|
2
|
+
#encoding: UTF-8
|
|
3
|
+
=begin
|
|
4
|
+
/***************************************************************************
|
|
5
|
+
* 2023-2025, 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
|
+
#
|
|
18
|
+
# Simplified logging.
|
|
19
|
+
# See example code at the bottom of this file.
|
|
20
|
+
# Execute this file to see the output.
|
|
21
|
+
module BasicLogging
|
|
22
|
+
|
|
23
|
+
DEBUG = 0
|
|
24
|
+
INFO = 1
|
|
25
|
+
WARN = 2
|
|
26
|
+
ERROR = 3
|
|
27
|
+
FATAL = 4
|
|
28
|
+
UNKNOWN = nil
|
|
29
|
+
|
|
30
|
+
# this is mainly for the translation of method calls into log levels
|
|
31
|
+
Levels = {:debug => DEBUG, :info => INFO, :warn => WARN, :error => ERROR,
|
|
32
|
+
:fatal => FATAL, :unknown => UNKNOWN}
|
|
33
|
+
|
|
34
|
+
@@log_level = UNKNOWN
|
|
35
|
+
@@target = STDOUT
|
|
36
|
+
@@muted = []
|
|
37
|
+
|
|
38
|
+
# do not log, if caller is obj (class or instance)
|
|
39
|
+
def self.mute(obj)
|
|
40
|
+
name = obj.class == Class ? obj.name.dup : obj.class.name
|
|
41
|
+
@@muted << name
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# return true if obj is muted and will not log.
|
|
45
|
+
def self.is_muted?(obj)
|
|
46
|
+
name = obj.class == Class ? obj.name.dup : obj.class.name
|
|
47
|
+
@@muted.include?(name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# set the log level
|
|
51
|
+
def set_level(lv)
|
|
52
|
+
if lv.respond_to?(:to_str) && Levels.keys.include?(lv.strip.to_sym)
|
|
53
|
+
lv = Levels[lv.to_sym]
|
|
54
|
+
elsif lv.respond_to?(:to_sym) && Levels.keys.include?(lv)
|
|
55
|
+
lv = Levels[lv]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if(!lv || (lv.respond_to?(:to_int) && lv >= DEBUG && lv <= FATAL) )
|
|
59
|
+
@@log_level = lv
|
|
60
|
+
else
|
|
61
|
+
msg = __FILE__.dup << ": ERROR : invalid log level \"" << lv.to_s << "\""
|
|
62
|
+
msg << "\n" << "Keepinng old log level " << Levels.keys.detect {| k| Levels[k] == @@log_level}.to_s
|
|
63
|
+
STDERR.puts msg
|
|
64
|
+
puts msg
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# set the log target
|
|
69
|
+
def set_target(tg)
|
|
70
|
+
if tg.respond_to?(:to_io)
|
|
71
|
+
@@target = tg
|
|
72
|
+
elsif(!File::exist?(tg) || ( File.file?(tg) && File.writable?(tg) ) )
|
|
73
|
+
@@target = File.open(tg, 'w+')
|
|
74
|
+
elsif !tg || tg.respond_to?(:to_str) && tg.strip.empty?
|
|
75
|
+
@@target = nil
|
|
76
|
+
else
|
|
77
|
+
STDERR.puts __FILE__.dup << ': ERROR : target ' << tg << ' cannot be set'
|
|
78
|
+
STDERR.puts "Keeping old target " << @@target.inspect
|
|
79
|
+
return
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Output log messages, depending on the log level set for the calling class
|
|
84
|
+
# and the name of the alias method which is actually called.
|
|
85
|
+
def log(message)
|
|
86
|
+
if !BasicLogging.is_muted?(self)
|
|
87
|
+
# how has this method been called?
|
|
88
|
+
mlevel = __callee__
|
|
89
|
+
if Levels.has_key?(mlevel) && Levels[mlevel] <= FATAL
|
|
90
|
+
# output only for levels equal or above the value that corresponds to
|
|
91
|
+
# the calling alias.
|
|
92
|
+
format_log( message, mlevel) if @@log_level && Levels[mlevel] >= @@log_level
|
|
93
|
+
else
|
|
94
|
+
STDERR.puts __FILE__.dup << ": ERROR : invalid log level \"" << mlevel.to_s << "\""
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def target
|
|
100
|
+
@@target.path if @@target
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def level
|
|
104
|
+
@@level.to_s if @@level
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Clear the log (-file)
|
|
108
|
+
def clear_log
|
|
109
|
+
if @@target && @@target.respond_to?(:truncate)
|
|
110
|
+
lock_target{ @@target.truncate(0) }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
alias :debug :log
|
|
115
|
+
alias :info :log
|
|
116
|
+
alias :warn :log
|
|
117
|
+
alias :error :log
|
|
118
|
+
alias :fatal :log
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def lock_target(&block)
|
|
123
|
+
begin
|
|
124
|
+
if @@target.respond_to?(:flock)
|
|
125
|
+
@@target.flock(File::LOCK_EX)
|
|
126
|
+
block.call
|
|
127
|
+
@@target.flock(File::LOCK_UN)
|
|
128
|
+
elsif @@target.respond_to?(:to_io)
|
|
129
|
+
block.call
|
|
130
|
+
end
|
|
131
|
+
rescue => ex
|
|
132
|
+
STDERR.puts __FILE__.dup << ": ERROR : cannot lock target (" << ex.message << ")"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# 1 format_log for all loggers.
|
|
137
|
+
def format_log(message, mlevel)
|
|
138
|
+
if @@target
|
|
139
|
+
# indicate if a registered class or the registered object of a class is calling.
|
|
140
|
+
name = self.class == Class ? self.name.dup << ' [class]' : (self.class.name == 'Object' ? 'Top Level' : self.class.name)
|
|
141
|
+
lock_target{@@target.puts '' << name << ' ' << mlevel.to_s << ' ' << Time.now.strftime("%H:%M:%S:%6N") << ': ' << message.gsub("\n", "\n |")}
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
#---------test: execute file----------
|
|
146
|
+
if $0 == __FILE__
|
|
147
|
+
Array.extend(BasicLogging)
|
|
148
|
+
Array.set_level(BasicLogging::INFO)
|
|
149
|
+
Array.info('TEST')
|
|
150
|
+
ar = Array.new
|
|
151
|
+
ar.extend(BasicLogging)
|
|
152
|
+
# --- no output :
|
|
153
|
+
l = __LINE__
|
|
154
|
+
ar.debug(l.next.to_s << ': debug-test 0')
|
|
155
|
+
# output
|
|
156
|
+
ar.set_level(BasicLogging::DEBUG)
|
|
157
|
+
l = __LINE__
|
|
158
|
+
ar.debug(l.next.to_s << ': debug-test 1')
|
|
159
|
+
|
|
160
|
+
obj = Object.new
|
|
161
|
+
obj.extend(BasicLogging)
|
|
162
|
+
obj.set_level(BasicLogging::DEBUG)
|
|
163
|
+
puts "--------debug-----------"
|
|
164
|
+
obj.debug('debug')
|
|
165
|
+
obj.info('info')
|
|
166
|
+
obj.warn('warn')
|
|
167
|
+
obj.error('error')
|
|
168
|
+
obj.fatal('fatal')
|
|
169
|
+
puts "--------info-----------"
|
|
170
|
+
obj.set_level("info")
|
|
171
|
+
obj.debug('debug')
|
|
172
|
+
obj.info('info')
|
|
173
|
+
obj.warn('warn')
|
|
174
|
+
obj.error('error')
|
|
175
|
+
obj.fatal('fatal')
|
|
176
|
+
puts "--------fatal-----------"
|
|
177
|
+
obj.set_level("fatal")
|
|
178
|
+
obj.debug('debug')
|
|
179
|
+
obj.info('info')
|
|
180
|
+
obj.warn('warn')
|
|
181
|
+
obj.error('error')
|
|
182
|
+
obj.fatal('fatal')
|
|
183
|
+
puts "--------UNKNOWN-----------"
|
|
184
|
+
obj.set_level(nil)
|
|
185
|
+
obj.debug('debug')
|
|
186
|
+
obj.info('info')
|
|
187
|
+
obj.warn('warn')
|
|
188
|
+
obj.error('error')
|
|
189
|
+
obj.fatal('fatal')
|
|
190
|
+
puts " ------ Output into file ----"
|
|
191
|
+
obj.set_target "/tmp/test_log.log"
|
|
192
|
+
puts " ------ INFO -----------"
|
|
193
|
+
obj.set_level BasicLogging::INFO
|
|
194
|
+
obj.info('info output')
|
|
195
|
+
|
|
196
|
+
obj.info('info output 2')
|
|
197
|
+
puts "---------- invalid -------"
|
|
198
|
+
obj.set_target "/dev/sr0"
|
|
199
|
+
obj.set_level "power"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# EOF
|
data/lib/basic_logging.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
#encoding: UTF-8
|
|
3
3
|
=begin
|
|
4
4
|
/***************************************************************************
|
|
5
|
-
* 2023-
|
|
5
|
+
* 2023-2026, Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
6
6
|
* This program is free software; you can redistribute it and/or modify *
|
|
7
7
|
* it under the terms of the WTFPL 2.0 or later, see *
|
|
8
8
|
* http://www.wtfpl.net/about/ *
|
|
@@ -34,7 +34,7 @@ module BasicLogging
|
|
|
34
34
|
@@log_level = UNKNOWN
|
|
35
35
|
@@target = STDOUT
|
|
36
36
|
@@muted = []
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
# do not log, if caller is obj (class or instance)
|
|
39
39
|
def self.mute(obj)
|
|
40
40
|
name = obj.class == Class ? obj.name.dup : obj.class.name
|
|
@@ -57,7 +57,7 @@ module BasicLogging
|
|
|
57
57
|
if(!lv || (lv.respond_to?(:to_int) && lv >= DEBUG && lv <= FATAL) )
|
|
58
58
|
@@log_level = lv
|
|
59
59
|
else
|
|
60
|
-
msg =
|
|
60
|
+
msg = calling_method << ": ERROR : invalid log level \"" << lv.to_s << "\""
|
|
61
61
|
msg << "\n" << "Keepinng old log level " << Levels.keys.detect {| k| Levels[k] == @@log_level}.to_s
|
|
62
62
|
STDERR.puts msg
|
|
63
63
|
puts msg
|
|
@@ -73,14 +73,14 @@ module BasicLogging
|
|
|
73
73
|
elsif !tg || tg.respond_to?(:to_str) && tg.strip.empty?
|
|
74
74
|
@@target = nil
|
|
75
75
|
else
|
|
76
|
-
STDERR.puts
|
|
76
|
+
STDERR.puts calling_method << ': ERROR : target ' << tg.to_str << ' cannot be set'
|
|
77
77
|
STDERR.puts "Keeping old target " << @@target.inspect
|
|
78
78
|
return
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
# Output of log messages, depending on the log level
|
|
83
|
-
#
|
|
82
|
+
# Output of log messages, depending on the set log level and the name of the
|
|
83
|
+
# alias method which is actually called.
|
|
84
84
|
def log(message)
|
|
85
85
|
if !BasicLogging.is_muted?(self)
|
|
86
86
|
# how has this method been called?
|
|
@@ -90,7 +90,7 @@ module BasicLogging
|
|
|
90
90
|
# the calling alias.
|
|
91
91
|
format_log( message, mlevel) if @@log_level && Levels[mlevel] >= @@log_level
|
|
92
92
|
else
|
|
93
|
-
STDERR.puts
|
|
93
|
+
STDERR.puts calling_method << ": ERROR : invalid log level \"" << mlevel.to_s << "\""
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
end
|
|
@@ -110,6 +110,21 @@ module BasicLogging
|
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
+
# find the name of the calling object's class or the file name in case of the
|
|
114
|
+
# top level object... or any dumb instance of Object
|
|
115
|
+
def log_name()
|
|
116
|
+
if !@log_name
|
|
117
|
+
clname = self.class.name
|
|
118
|
+
# limit analysis of the call stack to 'Object'.
|
|
119
|
+
if clname == 'Object'
|
|
120
|
+
@log_name = File::basename(caller.last.split(':')[0])
|
|
121
|
+
else
|
|
122
|
+
@log_name = self.class == Class ? self.name.dup << ' [class]' : clname
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
@log_name
|
|
126
|
+
end
|
|
127
|
+
|
|
113
128
|
alias :debug :log
|
|
114
129
|
alias :info :log
|
|
115
130
|
alias :warn :log
|
|
@@ -118,6 +133,31 @@ module BasicLogging
|
|
|
118
133
|
|
|
119
134
|
private
|
|
120
135
|
|
|
136
|
+
# Find the method that called, or 'main' in case of a top level Object.
|
|
137
|
+
# This needs a bunch of memory and slows down the process.
|
|
138
|
+
# Use of this method should be limited.
|
|
139
|
+
def calling_method()
|
|
140
|
+
# find instances other than the current module.
|
|
141
|
+
stack_item = caller.detect do |item|
|
|
142
|
+
item.split(':')[0] != __FILE__
|
|
143
|
+
end
|
|
144
|
+
if stack_item
|
|
145
|
+
if stack_item.include?('#')
|
|
146
|
+
m = stack_item.split(':').last.split("#")[1]
|
|
147
|
+
m.gsub!("'", '') if m && m.include?("'")
|
|
148
|
+
# looks cooler with a class name in front.
|
|
149
|
+
return '#' << m << '()'
|
|
150
|
+
else
|
|
151
|
+
# top level Object
|
|
152
|
+
return stack_item.split(':').last
|
|
153
|
+
end
|
|
154
|
+
else
|
|
155
|
+
# This is dumb but won't hurt a lot.
|
|
156
|
+
return 'from main'
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# could be useful
|
|
121
161
|
def lock_target(&block)
|
|
122
162
|
begin
|
|
123
163
|
if @@target.respond_to?(:flock)
|
|
@@ -128,19 +168,27 @@ module BasicLogging
|
|
|
128
168
|
block.call
|
|
129
169
|
end
|
|
130
170
|
rescue => ex
|
|
131
|
-
STDERR.puts
|
|
171
|
+
STDERR.puts calling_method << ": ERROR : cannot lock target (" << ex.message << ")"
|
|
132
172
|
end
|
|
133
173
|
end
|
|
134
174
|
|
|
135
175
|
# 1 format_log for all loggers.
|
|
136
176
|
def format_log(message, mlevel)
|
|
137
177
|
if @@target
|
|
178
|
+
location = log_name().dup << ' '
|
|
179
|
+
# Limit the memory-hungry analysis of the call stack
|
|
180
|
+
# This also takes about 2.5 times as long.
|
|
181
|
+
if mlevel != :info
|
|
182
|
+
location = log_name().dup << ' ' << calling_method << ' '
|
|
183
|
+
end
|
|
138
184
|
# indicate if a registered class or the registered object of a class is calling.
|
|
139
|
-
|
|
140
|
-
lock_target{@@target.puts '' << name << ' ' << mlevel.to_s << ' ' << Time.now.strftime("%H:%M:%S:%6N") << ': ' << message.gsub("\n", "\n |")}
|
|
185
|
+
lock_target{@@target.puts '' << location << mlevel.to_s << ' ' << Time.now.strftime("%H:%M:%S:%6N") << ': ' << message.gsub("\n", "\n |")}
|
|
141
186
|
end
|
|
142
187
|
end
|
|
143
|
-
end
|
|
188
|
+
end # Module end
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
144
192
|
#---------test: execute file----------
|
|
145
193
|
if $0 == __FILE__
|
|
146
194
|
Array.extend(BasicLogging)
|
|
@@ -186,14 +234,14 @@ if $0 == __FILE__
|
|
|
186
234
|
obj.warn('warn')
|
|
187
235
|
obj.error('error')
|
|
188
236
|
obj.fatal('fatal')
|
|
189
|
-
|
|
237
|
+
# ---- into file ---
|
|
190
238
|
obj.set_target "/tmp/test_log.log"
|
|
191
|
-
puts "
|
|
239
|
+
puts "------ INFO into #{obj.target}-----------"
|
|
192
240
|
obj.set_level BasicLogging::INFO
|
|
193
241
|
obj.info('info output')
|
|
194
242
|
|
|
195
243
|
obj.info('info output 2')
|
|
196
|
-
puts "---------- invalid -------"
|
|
244
|
+
puts "---------- invalid (fails) -------"
|
|
197
245
|
obj.set_target "/dev/sr0"
|
|
198
246
|
obj.set_level "power"
|
|
199
247
|
end
|
data/lib/body.rb
CHANGED
data/lib/color_output.rb
CHANGED
|
@@ -33,22 +33,48 @@ STYLES = {:regular => REGULAR, :bold => BOLD, :underline => UNDERLINE, :blink =>
|
|
|
33
33
|
# Colorizes the given text. Color-code is either an escape-sequence or one of
|
|
34
34
|
# the symbols representing color-names in the COLORS hash.
|
|
35
35
|
def colorize(text, color_code)
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
# ensure that colors are supported
|
|
37
|
+
if $stdout.tty? && ENV['TERM'] != 'dumb'
|
|
38
|
+
if (COLORS.keys.include?(color_code) )
|
|
39
|
+
"\033[3#{COLORS[color_code]}m#{text}\033[0m"
|
|
40
|
+
else
|
|
41
|
+
"#{color_code}#{text}\033[0m"
|
|
42
|
+
end
|
|
43
|
+
# if Terminal does not support colors
|
|
38
44
|
else
|
|
39
|
-
|
|
45
|
+
text
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
|
|
49
|
+
# rgb foreground
|
|
50
|
+
def colorize_RGB(text, r, g, b)
|
|
51
|
+
code = "%s;%s;%s" %[r, g, b]
|
|
52
|
+
"\033[38;2;#{code}m#{text}\033[0m"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# rgb background
|
|
56
|
+
def background_RGB(text, r, g, b)
|
|
57
|
+
code = "%s;%s;%s" %[r, g, b]
|
|
58
|
+
"\033[48;2;#{code}m#{text}\033[0m"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# duplicate code without much intelligence
|
|
43
62
|
def style(text, style_code)
|
|
44
63
|
"#{style_code}#{text}\033[0m"
|
|
45
64
|
end
|
|
65
|
+
|
|
46
66
|
# a function which allows to manipulate every known aspect of the ansi-output.
|
|
47
67
|
def colored_output(output_text, fg_color = :default, bg_color = :default, style = :regular , mode = :neutral )
|
|
48
|
-
|
|
68
|
+
if $stdout.tty? && ENV['TERM'] != 'dumb'
|
|
69
|
+
"\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]
|
|
70
|
+
else
|
|
71
|
+
# fallback for terminals that do not support colors.
|
|
72
|
+
output_text
|
|
73
|
+
end
|
|
49
74
|
end
|
|
50
75
|
|
|
51
76
|
# convenience functions
|
|
77
|
+
def black(text); colorize(text, "\033[30m"); end
|
|
52
78
|
def red(text); colorize(text, "\033[31m"); end
|
|
53
79
|
def green(text); colorize(text, "\033[32m"); end
|
|
54
80
|
def yellow(text); colorize(text, "\033[33m"); end
|
|
@@ -57,9 +83,20 @@ def cyan(text); colorize(text, "\033[36m"); end
|
|
|
57
83
|
def blue(text); colorize(text, "\033[34m"); end
|
|
58
84
|
def white(text); colorize(text, "\033[37m"); end
|
|
59
85
|
|
|
86
|
+
# RGB
|
|
87
|
+
# TODO: Fallbacks from the 256 color palette
|
|
88
|
+
def orange(text); colorize(text, "\033[38;2;255;165;0m"); end
|
|
89
|
+
def pink(text); colorize(text, "\033[38;2;255;0;165m"); end
|
|
90
|
+
def lightred(text); colorize(text, "\033[38;2;255;100;100m"); end
|
|
91
|
+
def lightgreen(text); colorize(text, "\033[38;2;100;255;100m"); end
|
|
92
|
+
def brown(text); colorize(text, "\033[38;2;153;76;0m"); end
|
|
93
|
+
def crimson(text); colorize(text, "\033[38;2;220;20;60m"); end
|
|
94
|
+
|
|
95
|
+
# quick contrast
|
|
60
96
|
def black_on_white(text); colorize(colorize(text, "\033[30m"), "\033[47m");end
|
|
61
97
|
def white_on_black(text); colorize(colorize(text, "\033[37m"), "\033[40m");end
|
|
62
98
|
|
|
99
|
+
# styles
|
|
63
100
|
def bold(text); style(text, "\033[01m");end
|
|
64
101
|
def underline(text); style(text, "\033[04m");end
|
|
65
102
|
|
data/lib/configuration.rb
CHANGED
|
@@ -78,6 +78,7 @@ class Configuration
|
|
|
78
78
|
def update_config(i_config)
|
|
79
79
|
if @conf
|
|
80
80
|
conf_version = @conf[:CONFIG_VERSION]
|
|
81
|
+
debug 'conf_version is ' << conf_version.to_s
|
|
81
82
|
if !conf_version || conf_version < PROGVERSION.to_f
|
|
82
83
|
info "configuration has an older version number, looking for changes"
|
|
83
84
|
i_conf = YAML::load_file(i_config)
|
data/lib/flnews_post_proc.conf
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
# the process, if they are still valid.
|
|
18
18
|
# ATTN! COMMENTS WILL BE REMOVED but a copy of your previous configuration
|
|
19
19
|
# will be saved in a file with a version suffix.
|
|
20
|
-
CONFIG_VERSION: 1.
|
|
20
|
+
CONFIG_VERSION: 1.95
|
|
21
21
|
|
|
22
22
|
# FUP_NAME
|
|
23
23
|
# A Regular Expression, describing the string which contains the name of
|
|
@@ -169,5 +169,19 @@ VFY_URLS: No
|
|
|
169
169
|
# DEFAULT: yes
|
|
170
170
|
OVERRIDE_CONFIG: YES
|
|
171
171
|
|
|
172
|
+
# USE_OLD
|
|
173
|
+
# A Boolean constant. The post processor can handle subject changes in the way, that
|
|
174
|
+
# old subjects which are preceded by variations of the string '(Was:' are automatically
|
|
175
|
+
# removed from the header. This imposes, that our own subject changes must be ignored!
|
|
176
|
+
# This behavior is activated here and necessitates that our own subject changes
|
|
177
|
+
# are indicated with a prefix "(Old:". It will be replaced by "(Was:" in the
|
|
178
|
+
# posted article.
|
|
179
|
+
#
|
|
180
|
+
# Set this option to true, when you want to use the prefix
|
|
181
|
+
# "(Old:", or to false, no, NO or similar if you do not want subject changes
|
|
182
|
+
# handled by the program.
|
|
183
|
+
# DEFAULT: No
|
|
184
|
+
USE_OLD: false
|
|
185
|
+
|
|
172
186
|
# EOF
|
|
173
187
|
|
data/lib/headers.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#encoding: UTF-8
|
|
2
2
|
=begin
|
|
3
3
|
/***************************************************************************
|
|
4
|
-
* 2023-
|
|
4
|
+
* 2023-2026, Michael Uplawski <michael.uplawski@uplawski.eu> *
|
|
5
5
|
* This program is free software; you can redistribute it and/or modify *
|
|
6
6
|
* it under the terms of the WTFPL 2.0 or later, see *
|
|
7
7
|
* http://www.wtfpl.net/about/ *
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
***************************************************************************/
|
|
14
14
|
=end
|
|
15
15
|
|
|
16
|
+
require 'rfc_2047'
|
|
16
17
|
require_relative 'basic_logging'
|
|
17
18
|
require_relative 'configuration'
|
|
18
19
|
require_relative 'newsgroups'
|
|
@@ -81,10 +82,9 @@ class Headers
|
|
|
81
82
|
headers[cur_header] = val
|
|
82
83
|
end
|
|
83
84
|
end
|
|
84
|
-
|
|
85
|
+
|
|
85
86
|
return headers
|
|
86
87
|
end
|
|
87
|
-
|
|
88
88
|
def self::supersedes?(article_text)
|
|
89
89
|
headers(article_text).keys.include?(:Supersedes)
|
|
90
90
|
end
|
|
@@ -124,6 +124,10 @@ class Headers
|
|
|
124
124
|
@headers["X-No-Archive".to_sym] = no_archive
|
|
125
125
|
@headers["Archive".to_sym] = 'no'
|
|
126
126
|
end
|
|
127
|
+
# ------->
|
|
128
|
+
# check for altered Subject and impose the newer value
|
|
129
|
+
alterSubject()
|
|
130
|
+
# <----
|
|
127
131
|
if @config.CUSTOM_HEADERS
|
|
128
132
|
ch = @config.CUSTOM_HEADERS
|
|
129
133
|
debug('setting custom headers : ' << ch.inspect)
|
|
@@ -131,15 +135,18 @@ class Headers
|
|
|
131
135
|
ch = pair.split(':')
|
|
132
136
|
hn = ch[0].strip
|
|
133
137
|
hv = ch[1].strip
|
|
134
|
-
# Ensure header is
|
|
138
|
+
# Ensure header is ascii only
|
|
135
139
|
if hv.ascii_only? && hn.ascii_only?
|
|
136
140
|
# <---------- special treatment Post-Processor ---------->
|
|
137
141
|
hv << ' ' << PROGVERSION.to_s if hn == 'X-Post-Processor' && hv == 'flnews_post_proc'
|
|
138
142
|
# >----------<
|
|
139
143
|
@headers[hn.to_sym] = hv
|
|
140
144
|
else
|
|
141
|
-
warn "Custom header [#{hn}:#{hv}] should be ASCII only!
|
|
145
|
+
warn "Custom header [#{hn}:#{hv}] should be ASCII only! But I try to encode it..."
|
|
146
|
+
hv = hv.split.collect{|w| (w.ascii_only? ? w : Rfc2047.encode(w, encoding: :Q)) }.join(' ')
|
|
147
|
+
debug ' custom header encoded'
|
|
142
148
|
end
|
|
149
|
+
@headers[hn.to_sym] = hv
|
|
143
150
|
end
|
|
144
151
|
@headers.compact!
|
|
145
152
|
end
|
|
@@ -173,6 +180,74 @@ class Headers
|
|
|
173
180
|
|
|
174
181
|
attr_reader :lines, :newsgroups
|
|
175
182
|
|
|
183
|
+
private
|
|
184
|
+
|
|
185
|
+
# Check for was, war, était and clip the old subject header.
|
|
186
|
+
# Currently UNUSED
|
|
187
|
+
def alterSubject
|
|
188
|
+
if @config.USE_OLD
|
|
189
|
+
refs = @headers[:References]
|
|
190
|
+
# only if older articles exist in the thread
|
|
191
|
+
if refs && !refs.empty?
|
|
192
|
+
hv = @headers[:Subject]
|
|
193
|
+
if hv
|
|
194
|
+
debug 'checking for altered Subject'
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# Our own very first article with an altered subject line, uses:
|
|
198
|
+
# (old:
|
|
199
|
+
# because this is what we do.
|
|
200
|
+
|
|
201
|
+
old = /\(Old:/i
|
|
202
|
+
if hv.match(old)
|
|
203
|
+
debug "\tfound old"
|
|
204
|
+
if hv.split(old).length == 2
|
|
205
|
+
@headers[:Subject]= hv.sub(old, '(Was:')
|
|
206
|
+
debug 'Subject is now: ' << @headers[:Subject]
|
|
207
|
+
return
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
# Handle previous subject changes.
|
|
211
|
+
# Create some regexps
|
|
212
|
+
olds = Array.new
|
|
213
|
+
olds << /\s*\(was:.*/i
|
|
214
|
+
olds << /\s*\(war:.*/i
|
|
215
|
+
olds << /\s*\(était\s?:.*/i
|
|
216
|
+
olds << /\s*\(etait\s?:.*/i
|
|
217
|
+
|
|
218
|
+
#decode subject from header value
|
|
219
|
+
subj = Rfc2047.decode(hv)
|
|
220
|
+
debug 'subject encoding: ' << hv.encoding.to_s
|
|
221
|
+
debug 'decoded subject: ' << subj
|
|
222
|
+
# check for each of those regexps
|
|
223
|
+
olds.each do |old|
|
|
224
|
+
m = subj.match(old)
|
|
225
|
+
debug "matches prefix #{old}?" << (m ? m.to_s : ' NO!')
|
|
226
|
+
# works too, but contains empty strings, if not
|
|
227
|
+
# parts = subj.partition(old) if m
|
|
228
|
+
# simpler:
|
|
229
|
+
parts = subj.split(old) if m
|
|
230
|
+
debug 'partitioned subject ' << parts.inspect
|
|
231
|
+
if parts
|
|
232
|
+
# ---> do not understand why this is needed:
|
|
233
|
+
new_subject = parts[0].strip('(')
|
|
234
|
+
# <--- but it is
|
|
235
|
+
# encode if you must.
|
|
236
|
+
new_subject = new_subject.split.collect{|w| (w.ascii_only? ? w : Rfc2047.encode(w, encoding: :Q)) }.join(' ')
|
|
237
|
+
# encode everything.
|
|
238
|
+
#new_subject = new_subject.ascii_only? ? new_subject : Rfc2047.encode(new_subject, encoding: :Q)
|
|
239
|
+
# probably unneeded
|
|
240
|
+
@headers[:Subject] = new_subject.force_encoding('UTF-8')
|
|
241
|
+
debug 'subject is now: ' << @headers[:Subject]
|
|
242
|
+
return
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
else
|
|
248
|
+
info('subject changes are ignored')
|
|
249
|
+
end
|
|
250
|
+
end
|
|
176
251
|
end
|
|
177
252
|
# EOF
|
|
178
253
|
|
data/lib/version.rb
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
=end
|
|
15
15
|
|
|
16
16
|
PROGNAME = 'flnews_post_proc'
|
|
17
|
-
PROGVERSION = "1.
|
|
17
|
+
PROGVERSION = "1.97"
|
|
18
18
|
AUTHORS = "Michael Uplawski"
|
|
19
19
|
EMAIL = "michael.uplawski@uplawski.eu"
|
|
20
20
|
YEARS = "2023 - 2026"
|
|
21
|
-
SUMMARY = "
|
|
21
|
+
SUMMARY = "Custom headers are rfc2047 encoded, if need be"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flnews_post_proc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.97'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Uplawski
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-06-21 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: diffy
|
|
@@ -68,6 +68,7 @@ files:
|
|
|
68
68
|
- doc/pdf/flnews_post_proc.pdf
|
|
69
69
|
- doc/rst/flnews_post_proc.rst
|
|
70
70
|
- lib/_quoting_style_regexp
|
|
71
|
+
- lib/bak_basic_logging.rb
|
|
71
72
|
- lib/basic_logging.rb
|
|
72
73
|
- lib/body.rb
|
|
73
74
|
- lib/color_output.rb
|
|
@@ -101,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
101
102
|
- !ruby/object:Gem::Version
|
|
102
103
|
version: '0'
|
|
103
104
|
requirements: []
|
|
104
|
-
rubygems_version: 4.0.
|
|
105
|
+
rubygems_version: 4.0.13
|
|
105
106
|
specification_version: 4
|
|
106
|
-
summary:
|
|
107
|
+
summary: Custom headers are rfc2047 encoded, if need be
|
|
107
108
|
test_files: []
|