mk_semi_lattice 0.4.5 → 0.4.6
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/lib/abbrev_checker/abbrev_yamls/command_tree.yaml +5 -1
- data/lib/abbrev_checker/abbrev_yamls/initialism_tree.yaml +1 -0
- data/lib/mk_semi_lattice/version.rb +1 -1
- data/lib/mk_stack/docs/mk_stack/mk_stack.001.png +0 -0
- data/lib/mk_stack/docs/mk_stack.key +0 -0
- data/lib/mk_stack/mk_stack.rb +187 -156
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab93185225f98ff9decc294bcd22bcb730910405bce57f9e5b8abd9b98f0942e
|
|
4
|
+
data.tar.gz: 7a7ca43878ef13f9e32495b6a813da62ea551c423dd581b76aa9492ad0ab3be0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3260b0bc9e85d536d0ffc53231ffa20cb26cceaf790dceae083f2ff4c1cb2f5279473823eef61c69d92bc641e9a3df2d3416cde82faeef600951540611822cbf
|
|
7
|
+
data.tar.gz: 80b233d58003d4b7b39915bb7220852a5ebfe5fc267c0a6d2bfaaf1b66d8c4fe28c9eb049a8fe6f7f04e3ab54fc0fdc125993313efb77c779a24c2ce54cffa35
|
|
@@ -18,8 +18,12 @@
|
|
|
18
18
|
ssh=secure shell=安全なremote shell接続:
|
|
19
19
|
#path=path=パス,小径:
|
|
20
20
|
full_path=full path=フルパス:
|
|
21
|
-
|
|
21
|
+
abs_path=abosolute path=絶対(absolute)パス:
|
|
22
22
|
rel_path=relative path=相対パス:
|
|
23
|
+
# etc:
|
|
24
|
+
fish=friendly interactive shell:
|
|
25
|
+
apt=advanced package tool:
|
|
26
|
+
sudo=super user do:
|
|
23
27
|
#c-keys=controll-keys=fishコマンド編集keyバインド:
|
|
24
28
|
c-a/ahead=行頭へ移動:
|
|
25
29
|
c-e/end=行末へ移動:
|
|
Binary file
|
|
Binary file
|
data/lib/mk_stack/mk_stack.rb
CHANGED
|
@@ -4,12 +4,13 @@ require 'fileutils'
|
|
|
4
4
|
require 'optparse'
|
|
5
5
|
require 'date'
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
module StackOperations
|
|
8
9
|
class OptionParserWrapper
|
|
9
10
|
attr_reader :options, :args
|
|
10
11
|
|
|
11
12
|
def initialize(argv)
|
|
12
|
-
@options = { flatten: false, nest: false, dryrun: true, reverse: false }
|
|
13
|
+
@options = { flatten: false, nest: false, dryrun: true, reverse: false, empty: false, no_dir_move: false }
|
|
13
14
|
@args = []
|
|
14
15
|
OptionParser.new do |opts|
|
|
15
16
|
opts.banner = "Usage: hc stack [options]"
|
|
@@ -27,13 +28,50 @@ module StackOperations
|
|
|
27
28
|
end
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
class
|
|
31
|
+
class MkTree
|
|
32
|
+
def mk_dir_tree(dir, current_level = 1, max_level = 2, dirs_only: false)
|
|
33
|
+
tree = {}
|
|
34
|
+
return tree unless File.directory?(dir)
|
|
35
|
+
|
|
36
|
+
Dir.children(dir).sort.each do |entry|
|
|
37
|
+
path = File.join(dir, entry)
|
|
38
|
+
is_dir = File.directory?(path)
|
|
39
|
+
next if dirs_only && !is_dir
|
|
40
|
+
|
|
41
|
+
if is_dir
|
|
42
|
+
tree[entry] = current_level < max_level ? mk_dir_tree(path, current_level + 1, max_level, dirs_only: dirs_only) : {}
|
|
43
|
+
else
|
|
44
|
+
tree[entry] = nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
tree
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def print_tree(tree, is_last_arr = [])
|
|
51
|
+
entries = tree.keys
|
|
52
|
+
entries.each_with_index do |entry, idx|
|
|
53
|
+
is_last = idx == entries.size - 1
|
|
54
|
+
prefix = ""
|
|
55
|
+
is_last_arr.each { |last| prefix += last ? " " : "│ " }
|
|
56
|
+
prefix += is_last ? "└── " : "├── "
|
|
57
|
+
|
|
58
|
+
is_dir = tree[entry].is_a?(Hash)
|
|
59
|
+
name = is_dir ? "#{entry}/" : entry
|
|
60
|
+
puts "#{prefix}#{name}"
|
|
61
|
+
|
|
62
|
+
if is_dir
|
|
63
|
+
print_tree(tree[entry], is_last_arr + [is_last])
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class MkStack < MkTree
|
|
31
70
|
def initialize(options, args)
|
|
32
71
|
@options = options
|
|
33
72
|
@root_name = args[0]
|
|
34
73
|
@date = args[1] || Date.today.strftime('%y%m%d')
|
|
35
74
|
end
|
|
36
|
-
|
|
37
75
|
def ordinal(n)
|
|
38
76
|
abs_n = n.to_i.abs
|
|
39
77
|
if (11..13).include?(abs_n % 100)
|
|
@@ -47,7 +85,6 @@ module StackOperations
|
|
|
47
85
|
end
|
|
48
86
|
end
|
|
49
87
|
end
|
|
50
|
-
|
|
51
88
|
def pull_root_name(dir_name)
|
|
52
89
|
if dir_name =~ /^_stack_(.+)_(\d{6})$/
|
|
53
90
|
$1
|
|
@@ -55,7 +92,6 @@ module StackOperations
|
|
|
55
92
|
dir_name
|
|
56
93
|
end
|
|
57
94
|
end
|
|
58
|
-
|
|
59
95
|
def next_available_dir(root_name, date)
|
|
60
96
|
n = 1
|
|
61
97
|
if root_name =~ /^(\d+)(st|nd|rd|th)$/
|
|
@@ -82,218 +118,213 @@ module StackOperations
|
|
|
82
118
|
dir = "_stack_#{base_name}_#{date}"
|
|
83
119
|
[base_name, dir]
|
|
84
120
|
end
|
|
85
|
-
|
|
86
121
|
def find_max_ordinal
|
|
87
122
|
candidates = Dir.glob("_stack_*_*").select { |d| d =~ /^_stack_(\d+)(st|nd|rd|th)_(\d{6})$/ }
|
|
88
|
-
ordinals = candidates.map
|
|
89
|
-
if d =~ /^_stack_(\d+)(st|nd|rd|th)_(\d{6})$/
|
|
90
|
-
$1.to_i
|
|
91
|
-
else
|
|
92
|
-
nil
|
|
93
|
-
end
|
|
94
|
-
end.compact
|
|
123
|
+
ordinals = candidates.map { |d| d[/^_stack_(\d+)/, 1].to_i }.compact
|
|
95
124
|
ordinals.any? ? ordinals.max : 0
|
|
96
125
|
end
|
|
97
|
-
|
|
98
126
|
def ensure_root_name
|
|
99
|
-
if @root_name.nil? || @root_name.
|
|
127
|
+
if @root_name.nil? || @root_name.empty?
|
|
100
128
|
max_num = find_max_ordinal
|
|
101
|
-
@root_name = max_num > 0 ?
|
|
129
|
+
@root_name = max_num > 0 ? "#{max_num}th" : "1st"
|
|
102
130
|
end
|
|
103
131
|
end
|
|
132
|
+
def run
|
|
133
|
+
ensure_root_name
|
|
134
|
+
@root_name, dir = next_available_dir(@root_name, @date)
|
|
104
135
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
# 1. 現在のツリーを取得
|
|
137
|
+
current_tree = mk_dir_tree(".", 1, 1)
|
|
138
|
+
|
|
139
|
+
# 2. 移動対象の決定
|
|
140
|
+
exclude = ['.', '..', dir, '.vscode', 'project.code-workspace', '.git']
|
|
141
|
+
all_entries = Dir.glob('*', File::FNM_DOTMATCH) - exclude
|
|
142
|
+
moved_entries = []
|
|
143
|
+
|
|
144
|
+
unless @options[:empty]
|
|
145
|
+
all_entries.each do |entry|
|
|
146
|
+
next if @options[:no_dir_move] && File.directory?(entry)
|
|
147
|
+
moved_entries << entry
|
|
148
|
+
end
|
|
111
149
|
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def move_entries(dir)
|
|
115
|
-
exclude = ['.', '..', dir, '.vscode', 'project.code-workspace']
|
|
116
|
-
entries = Dir.glob('*', File::FNM_DOTMATCH) - exclude
|
|
117
150
|
|
|
118
|
-
|
|
119
|
-
|
|
151
|
+
# 3. 仮想ツリーの構築 (Hashの操作としてmvをシミュレート)
|
|
152
|
+
virtual_tree = {}
|
|
153
|
+
current_tree.each do |k, v|
|
|
154
|
+
virtual_tree[k] = v unless moved_entries.include?(k)
|
|
120
155
|
end
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
156
|
+
|
|
157
|
+
moved_tree = {}
|
|
158
|
+
moved_entries.each do |entry|
|
|
159
|
+
moved_tree[entry] = current_tree[entry]
|
|
124
160
|
end
|
|
161
|
+
virtual_tree[dir] = moved_tree
|
|
162
|
+
virtual_tree = virtual_tree.sort.to_h
|
|
125
163
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
164
|
+
# 4. Before表示
|
|
165
|
+
puts "Before stack:"
|
|
166
|
+
puts "."
|
|
167
|
+
print_tree(current_tree)
|
|
168
|
+
|
|
169
|
+
# 5. 操作の表示 (Dry Run or Execute)
|
|
170
|
+
puts "\nOperations:"
|
|
171
|
+
prefix = @options[:dryrun] ? "[Dry Run] " : ""
|
|
172
|
+
puts "#{prefix}mkdir #{dir}"
|
|
173
|
+
if moved_entries.empty?
|
|
174
|
+
puts "#{prefix}mv nothing (to) #{dir}"
|
|
175
|
+
else
|
|
176
|
+
moved_entries.each do |entry|
|
|
177
|
+
puts "#{prefix}mv #{entry} (to) #{dir}"
|
|
132
178
|
end
|
|
133
179
|
end
|
|
134
180
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
end
|
|
140
|
-
end
|
|
181
|
+
# 6. After表示
|
|
182
|
+
puts "\nAfter stack:"
|
|
183
|
+
puts "."
|
|
184
|
+
print_tree(virtual_tree)
|
|
141
185
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
186
|
+
# 7. 実行 (-e 指定時)
|
|
187
|
+
unless @options[:dryrun]
|
|
188
|
+
Dir.mkdir(dir) unless Dir.exist?(dir)
|
|
189
|
+
moved_entries.each do |entry|
|
|
190
|
+
FileUtils.mv(entry, dir)
|
|
191
|
+
end
|
|
192
|
+
puts "\nExecution completed."
|
|
193
|
+
end
|
|
147
194
|
end
|
|
148
195
|
end
|
|
149
196
|
|
|
150
|
-
class MkFlatten
|
|
197
|
+
class MkFlatten < MkTree
|
|
151
198
|
def initialize(options, args)
|
|
152
199
|
@options = options
|
|
153
200
|
@target_dir = args[0] || "."
|
|
154
201
|
@target_dir = @target_dir.chomp("/")
|
|
155
202
|
end
|
|
156
|
-
|
|
157
|
-
# 指定ディレクトリ以下のtree構造を表示(ディレクトリのみ)
|
|
158
|
-
def print_tree(dir, prefix = "", is_last_arr = [])
|
|
159
|
-
puts "#{prefix}#{File.basename(dir)}"
|
|
160
|
-
if File.directory?(dir)
|
|
161
|
-
entries = Dir.children(dir).sort.select { |entry| File.directory?(File.join(dir, entry)) }
|
|
162
|
-
entries.each_with_index do |entry, idx|
|
|
163
|
-
path = File.join(dir, entry)
|
|
164
|
-
is_last = idx == entries.size - 1
|
|
165
|
-
# インデント生成
|
|
166
|
-
new_prefix = ""
|
|
167
|
-
is_last_arr.each { |last| new_prefix += last ? " " : "│ " }
|
|
168
|
-
new_prefix += is_last ? "└── " : "├── "
|
|
169
|
-
print_tree(path, new_prefix, is_last_arr + [is_last])
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# flatten後のtree構造を表示(@target_dir直下に全ての_stack_*_*ディレクトリを並べる)
|
|
175
|
-
def print_flattened_tree
|
|
176
|
-
stacks = []
|
|
203
|
+
def build_virtual_tree
|
|
177
204
|
search_dirs = Dir.glob(File.join(@target_dir, "**/_stack_*_*")).select { |d| File.directory?(d) }
|
|
178
|
-
# ルート直下の_stack_*_*も含める
|
|
179
205
|
root_stacks = Dir.glob(File.join(@target_dir, "_stack_*_*")).select { |d| File.directory?(d) }
|
|
180
206
|
all_stacks = (root_stacks + search_dirs).uniq
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
207
|
+
|
|
208
|
+
tree = {}
|
|
209
|
+
all_stacks.map { |d| File.basename(d) }.sort.each do |d|
|
|
210
|
+
tree[d] = {}
|
|
184
211
|
end
|
|
212
|
+
tree
|
|
185
213
|
end
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
214
|
+
def flatten_moves
|
|
215
|
+
search_dirs = Dir.glob(File.join(@target_dir, "**/_stack_*_*")).select { |d| File.directory?(d) }
|
|
216
|
+
root_stacks = Dir.glob(File.join(@target_dir, "_stack_*_*")).select { |d| File.directory?(d) }
|
|
217
|
+
stacks = (root_stacks + search_dirs).uniq
|
|
218
|
+
|
|
219
|
+
# 深い階層から順に移動させるため、パスの深さで降順ソート
|
|
220
|
+
stacks.sort_by! { |d| -d.count('/') }
|
|
221
|
+
|
|
222
|
+
moves = []
|
|
223
|
+
stacks.each do |src|
|
|
224
|
+
dest = File.join(@target_dir, File.basename(src))
|
|
225
|
+
moves << [src, dest] unless src == dest
|
|
191
226
|
end
|
|
227
|
+
moves
|
|
192
228
|
end
|
|
229
|
+
def run
|
|
230
|
+
puts "Before flatten:"
|
|
231
|
+
puts @target_dir == "." ? "." : "#{File.basename(@target_dir)}/"
|
|
232
|
+
print_tree(mk_dir_tree(@target_dir, 1, 999, dirs_only: true))
|
|
193
233
|
|
|
194
|
-
|
|
195
|
-
stacks = Dir.glob(File.join(@target_dir, "**/_stack_*_*")).select { |d| File.directory?(d) }
|
|
196
|
-
stacks.sort_by! { |d| -d.count(File::SEPARATOR) }
|
|
197
|
-
stacks.map { |src| [src, File.join(".", File.basename(src))] }
|
|
198
|
-
end
|
|
234
|
+
moves = flatten_moves
|
|
199
235
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
236
|
+
puts "\nOperations:"
|
|
237
|
+
prefix = @options[:dryrun] ? "[Dry Run] " : ""
|
|
238
|
+
if moves.empty?
|
|
239
|
+
puts "#{prefix}nothing to flatten"
|
|
240
|
+
else
|
|
241
|
+
moves.each do |src, dest|
|
|
242
|
+
puts "#{prefix}mv #{src} (to) #{dest}"
|
|
206
243
|
end
|
|
207
|
-
puts "mv '#{src}' '#{dest}'"
|
|
208
|
-
FileUtils.mv(src, dest)
|
|
209
244
|
end
|
|
210
|
-
end
|
|
211
245
|
|
|
212
|
-
def run
|
|
213
|
-
puts "Before flatten:"
|
|
214
|
-
puts "."
|
|
215
|
-
print_tree(@target_dir, "", [])
|
|
216
246
|
puts "\nAfter flatten:"
|
|
217
247
|
puts "."
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
248
|
+
print_tree(build_virtual_tree)
|
|
249
|
+
|
|
250
|
+
unless @options[:dryrun]
|
|
251
|
+
moves.each do |src, dest|
|
|
252
|
+
FileUtils.mv(src, dest)
|
|
253
|
+
end
|
|
254
|
+
puts "\nExecution completed."
|
|
224
255
|
end
|
|
225
256
|
end
|
|
226
257
|
end
|
|
227
258
|
|
|
228
|
-
class MkNest
|
|
259
|
+
class MkNest < MkTree
|
|
229
260
|
def initialize(options, args)
|
|
230
261
|
@options = options
|
|
231
262
|
@target_dir = args[0] || "."
|
|
232
263
|
end
|
|
233
|
-
|
|
234
|
-
# yymmdd順に並べ替え(reverseオプションで順序切替)
|
|
235
264
|
def sorted_stacks
|
|
236
|
-
stacks = Dir.glob(File.join(@target_dir, "_stack_*_*"))
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
#.sort_by { |d| d[/^_stack_(\d+)(st|nd|rd|th)_/, 1].to_i }
|
|
265
|
+
stacks = Dir.glob(File.join(@target_dir, "_stack_*_*")).select { |d| File.directory?(d) }
|
|
266
|
+
stacks = stacks.sort_by { |d| d[/_stack_(\d+)(st|nd|rd|th)_/, 1].to_i }
|
|
267
|
+
# デフォルトはFILO(新しいものがルート: 7th, 6th, 5th...)
|
|
268
|
+
# -r オプションでFIFO(古いものがルート: 1st, 2nd, 3rd...)
|
|
241
269
|
@options[:reverse] ? stacks : stacks.reverse
|
|
242
270
|
end
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
new_prefix = ""
|
|
254
|
-
is_last_arr.each { |last| new_prefix += last ? " " : "│ " }
|
|
255
|
-
new_prefix += is_last ? "└── " : "├── "
|
|
256
|
-
print_tree(path, new_prefix, is_last_arr + [is_last])
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# nest後のtree構造を表示
|
|
262
|
-
def print_nested_tree
|
|
263
|
-
p stacks = sorted_stacks
|
|
264
|
-
return if stacks.empty?
|
|
265
|
-
puts "."
|
|
266
|
-
stacks.each_with_index do |dir, idx|
|
|
267
|
-
indent = " " * idx + "└── "
|
|
268
|
-
puts "#{indent}#{File.basename(dir)}"
|
|
271
|
+
def build_virtual_tree
|
|
272
|
+
stacks = sorted_stacks
|
|
273
|
+
tree = {}
|
|
274
|
+
return tree if stacks.empty?
|
|
275
|
+
|
|
276
|
+
current = tree
|
|
277
|
+
stacks.each do |dir|
|
|
278
|
+
name = File.basename(dir)
|
|
279
|
+
current[name] = {}
|
|
280
|
+
current = current[name]
|
|
269
281
|
end
|
|
282
|
+
tree
|
|
270
283
|
end
|
|
271
|
-
|
|
272
|
-
# nestのmvコマンド生成&実行/表示
|
|
273
284
|
def nest_moves
|
|
274
285
|
stacks = sorted_stacks
|
|
275
|
-
|
|
276
|
-
moves
|
|
277
|
-
|
|
286
|
+
moves = []
|
|
287
|
+
return moves if stacks.size < 2
|
|
288
|
+
|
|
289
|
+
# ENOENTエラーを防ぐため、一番奥(深い階層)に入るものから順に親へ移動する
|
|
290
|
+
# 例: stacks = [7th, 6th, 5th] の場合
|
|
291
|
+
# 1. 5th を 6th へ移動
|
|
292
|
+
# 2. 6th を 7th へ移動
|
|
293
|
+
(0...stacks.size - 1).to_a.reverse.each do |i|
|
|
294
|
+
parent = stacks[i]
|
|
295
|
+
child = stacks[i + 1]
|
|
278
296
|
dest = File.join(parent, File.basename(child))
|
|
279
|
-
|
|
280
|
-
puts "[Dry Run] mv '#{child}' '#{dest}'"
|
|
281
|
-
else
|
|
282
|
-
puts "mv '#{child}' '#{dest}'"
|
|
283
|
-
FileUtils.mkdir_p(parent) unless Dir.exist?(parent)
|
|
284
|
-
FileUtils.mv(child, dest)
|
|
285
|
-
end
|
|
297
|
+
moves << [child, dest]
|
|
286
298
|
end
|
|
299
|
+
moves
|
|
287
300
|
end
|
|
288
|
-
|
|
289
301
|
def run
|
|
290
302
|
puts "Before nest:"
|
|
291
|
-
puts "."
|
|
292
|
-
print_tree(@target_dir,
|
|
303
|
+
puts @target_dir == "." ? "." : "#{File.basename(@target_dir)}/"
|
|
304
|
+
print_tree(mk_dir_tree(@target_dir, 1, 999, dirs_only: true))
|
|
305
|
+
|
|
306
|
+
moves = nest_moves
|
|
307
|
+
|
|
308
|
+
puts "\nOperations:"
|
|
309
|
+
prefix = @options[:dryrun] ? "[Dry Run] " : ""
|
|
310
|
+
if moves.empty?
|
|
311
|
+
puts "#{prefix}nothing to nest"
|
|
312
|
+
else
|
|
313
|
+
moves.each do |child, dest|
|
|
314
|
+
puts "#{prefix}mv #{child} (to) #{dest}"
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
293
318
|
puts "\nAfter nest:"
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
319
|
+
puts "."
|
|
320
|
+
print_tree(build_virtual_tree)
|
|
321
|
+
|
|
322
|
+
unless @options[:dryrun]
|
|
323
|
+
moves.each do |child, dest|
|
|
324
|
+
FileUtils.mv(child, dest)
|
|
325
|
+
end
|
|
326
|
+
puts "\nExecution completed."
|
|
327
|
+
end
|
|
297
328
|
end
|
|
298
329
|
end
|
|
299
330
|
end
|