1 contributor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
#!/bin/bash
set -e
declare -A data=(
[/sim/description]=text
[/sim/long-description]=text
[/sim/author]=text
[/sim/flight-model]=text
[/sim/type]=text
[/sim/model/path]=text
)
fgaddon_url=https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Aircraft
fgaddon_svn=https://svn.code.sf.net/p/flightgear/fgaddon/trunk/Aircraft
fgaddon_path=$HOME/.fgfs/flightgear-fgaddon/Aircraft
database=${DB:-$0.db}
test -r "$0.conf" && source $0.conf && echo config red
aircrafts=$(mktemp --dry-run /dev/shm/Aircraft-XXXXXXXXX)
aircraft=$(mktemp --dry-run /dev/shm/aircraft-XXXXXXX)
setxml=$(mktemp --dry-run /dev/shm/setxml-XXXXXXXX)
in_ram_database=$(mktemp --dry-run /dev/shm/XXXXXXX)
function xmlgetnext () {
local IFS='>'
read -d '<' TAG VALUE
if test -z "$TAG"; then
test ${xmlgetnext_firstentry:-1} -eq 1 && xmlgetnext_firstentry=0 || return 1;
fi
local _TAG=$(printf '%q' $TAG)
if test ${_TAG:0:1} = '$'; then
TAG=$(tr '\n' ' ' <<< $TAG | sed 's/ */ /g; s/ *$//')
fi
}
function sqlite_request () {
if ! sqlite3 "$in_ram_database" <<< "$1"; then
register_state
fi
}
function xmlremovecomments () {
sed -ri 's/<(!--|script>)/\n&/;s/(<\/script|--)>/&\n/' $setxml
sed -ri '/<(script>|!--).*(<\/script|--)>/d;/<(script>|!--)/,/(<\/script|--)>/d' $setxml
}
function trap_break () {
trap '' INT
echo "stop requested"
register_state
}
function trap_exit () {
trapped_rc=$?
trap '' INT
rm -f $aircrafts $aircraft $setxml
if test ! -e $in_ram_database; then
exit
fi
test $trapped_rc -ne 0 && register_state
echo "updating installation status"
for ac in $(sqlite_request 'select printf("%i:%s/%s", aircrafts.id, aircrafts.name, setxml.file)
from aircrafts inner join setxml
where aircrafts.id = setxml.variantof and setxml.installed != 0;'); do
ac_path=${ac#*:}
if test ! -e $fgaddon_path/$ac_path-set.xml; then
sqlite_request "update setxml set installed = 0 where file = '${ac_path#*/}' and variantof = ${ac%:*}"
fi
done
for ac in $fgaddon_path/*/*-set.xml; do
ac=${ac/$fgaddon_path}
sx=${ac##*/}
ac=${ac%/*}
if test -d $fgaddon_path/$ac/.svn; then
install_type=1
elif test -d $fgaddon_path/$ac/.git; then
install_type=2
else
install_type=3
fi
sqlite_request "update setxml set installed = $install_type
where exists (
select 1
from aircrafts
where name = '${ac/\/}' and setxml.variantof = id
)"
done
missing_setxml=$(sqlite_request "select name from aircrafts where id not in (select variantof from setxml)")
if test -n "$missing_setxml"; then
echo "missing setxml config: $missing_setxml"
fi
if test -r "$database" && md5sum $in_ram_database | sed "s,$in_ram_database,$database," | md5sum --status -c -; then
rm -f $in_ram_database
echo "no changes in $database"
elif test -w "$database"; then
sqlite_request "vacuum"
mv -f $in_ram_database "$database"
echo "database $database updated"
elif ! test -e "$database"; then
mv $in_ram_database "$database"
echo "database $database created"
else
rm -f $in_ram_database
echo "nothing can be done with $database !"
fi
}
function register_state () {
set +x
sqlite_request "drop table if exists recover_rev"
sqlite_request "create table recover_rev (
revkey text,
revision integer,
revauthor text,
revdate integer
)"
for revkey in ${!revision[@]}; do
sqlite_request "insert into recover_rev values (
'$revkey',
${revision[$revkey]:-0},
'${revauthor[$revkey]}',
${revdate[$revkey]:-0}
)"
done
sqlite_request "drop table if exists recover_setxmlmodified"
sqlite_request "create table if not exists recover_setxmlmodified (
sx text
)"
for sx in ${!setxmlmodified[@]}; do
sqlite_request "insert into recover_setxmlmodified values (
'$sx'
)"
done
set +x
exit
}
function update_database () {
echo "[ ${#revision[@]} ] ${ac:1}"
dbupdate=$(sqlite_request "select revision from aircrafts where name is '${ac:1}'")
if test -z "$dbupdate"; then
sqlite_request "insert into aircrafts (name, revision, date, author)
values ('${ac:1}', ${revision[$ac]}, ${revdate[$ac]}, '${revauthor[$ac]}')"
elif test $dbupdate -lt ${revision[$ac]}; then
sqlite_request "update aircrafts set
revision = ${revision[$ac]},
author = '${revauthor[$ac]}',
date = ${revdate[$ac]}
where name is '${ac:1}'"
fi
id=$(sqlite_request "select id from aircrafts where name is '${ac:1}'")
for sx in ${!setxmlmodified[@]}; do
unset include include_rootpath
[[ "$sx" =~ ^"${ac:1}/" ]] || continue
for col in ${!data[@]}; do
data[$col]=
done
sx=${sx#*/}
echo " -> $sx"
if ! svn export --quiet --force $fgaddon_svn/${ac:1}/$sx-set.xml $setxml; then
register_state
fi
xmlremovecomments
unset xmlgetnext_firstentry property
while xmlgetnext; do
case "${TAG:0:1}" in
''|'?'|'!')
continue;;
/)
property=${property%/*};;
*)
if test "${TAG: -1}" != '/'; then
property+=/${TAG%% *}
fi;;
esac
if [[ "$TAG" =~ ^"PropertyList include=" ]]; then
include_rootpath=${include%/*}
test $include = $include_rootpath && unset include_rootpath
eval $(echo ${TAG#* })
[[ "$include" =~ ^Aircraft/Generic/ ]] && unset include include_rootpath && continue
if [[ "$include" =~ ^'../' ]]; then
if test -n "$include_rootpath"; then
if [[ "$include_rootpath" =~ '/' ]]; then
include_rootpath=${include_rootpath%/*}
else
unset include_rootpath
fi
else
ac_save=$ac
unset ac
fi
include=${include/\.\.\/}
fi
if ! svn cat $fgaddon_svn/${ac:1}/${include_rootpath:+$include_rootpath/}$include >> $setxml; then
register_state
fi
xmlremovecomments
fi
if [[ "$property" = /PropertyList@($data_pattern) ]]; then
eval "data[${property/\/PropertyList}]=\"${VALUE//\"/\\\"}\""
data[${property/\/PropertyList}]=$(tr '\n' ' ' <<< ${data[${property/\/PropertyList}]} | sed -r 's/^\s*//;s/\s+/ /g;s/\s*$//')
fi
if test -n "$_TAG"; then
property=${property/%\/${_TAG%% *}}
unset _TAG
fi
for col in ${!data[@]}; do
test -z "${data[$col]}" && continue 2
done
break
done < $setxml
if eval "test -z \"$data_test_null\""; then
echo "WARNING: no info found, skipping"
continue
fi
known=$(sqlite_request "select variantof from setxml where file is '$sx'")
if test -n "$known"; then
for col in ${!data[@]}; do
dbvalue=$(sqlite_request "select '$col'
from setxml
where file is '$sx' and variantof = $known")
if test "$dbvalue" != "${data[$col]}" -a -n "${data[$col]}"; then
sqlite_request "update setxml
set '$col' = '${data[$col]//\'/\'\'}'
where file is '$sx' and variantof = $known"
fi
done
else
values="'$sx', $id, "
for col in ${!data[@]}; do
values+="'${data[$col]//\'/\'\'}', "
done
values+=0
sqlite_request "insert into setxml values ($values)"
fi
test -n "$ac_save" && ac=$ac_save
unset setxmlmodified[${ac:1}/$sx]
done
unset revision[$ac]
}
function apply_revision () {
for ac in "${!revision[@]}"; do
update_database
if test -d $fgaddon_path/${ac:1}/.svn \
&& test "$(svn info --show-item=url $fgaddon_path/${ac:1})" != "$fgaddon_svn/${ac:1}" \
|| test -d $fgaddon_path/${ac:1} -a ! -d $fgaddon_path/${ac:1}/.svn; then
echo "INFO: local ${ac:1} installed out from repo" >&2
fi
done
}
trap trap_break INT
trap trap_exit EXIT
stty -echoctl
declare -A revision revauthor revdate setxmlmodified files revpath
data_pattern=$(printf "%s|" ${!data[@]})
data_pattern=${data_pattern:0:-1}
data_test_null=$(printf '${data[%s]}' ${!data[@]})
if test -e $database; then
cp $database $in_ram_database
sql_cols=$(sqlite_request "pragma table_info(setxml)" | awk -F'|' '{printf("%s %s ", $2, $3)}')
script_cols="file text variantof integer "
for col in ${!data[@]}; do
script_cols+="$col ${data["$col"]} "
done
script_cols+="installed integer "
if test "$sql_cols" != "$script_cols"; then
echo "ALERT: datbase version mismatch !"
exit 1
fi
if sqlite_request '.tables' | grep -q 'recover_' && test -z "$1"; then
echo "recovering from previous saved state"
eval $(sqlite_request "select printf('revision[%s]=%u;revauthor[%s]=%s;revdate[%s]=%u;',
revkey, revision,
revkey, revauthor,
revkey, revdate)
from recover_rev")
eval $(sqlite_request "select printf('setxmlmodified[%s]=1;', sx)
from recover_setxmlmodified")
sqlite_request "drop table recover_rev"
sqlite_request "drop table recover_setxmlmodified"
apply_revision
exit
fi
fi
sqlite_request "create table if not exists aircrafts (
id integer primary key,
name text,
revision integer,
date integer,
author text)"
sqlite_request "create table if not exists setxml (
file text,
variantof integer,
$(for col in ${!data[@]}; do printf "'%s' %s, " $col ${data[$col]}; done)
installed integer)"
latest_revision=$(sqlite_request "select max(revision) from aircrafts")
if test -n "$2"; then
ac=_${1%/*}
revision[$ac]=1
revdate[$ac]=0
revauthor[$ac]=foobar
setxmlmodified[${ac:1}/${1#*/}]=1
set -x
update_database
set +x
exit
elif test -n "$1"; then
ac=_${1%/*}
eval $(sqlite_request "select printf('revision[_%s]=%s;revdate[_%s]=%i;revauthor[_%s]=%s;',
name, revision,
name, date,
name, author)
from aircrafts
where name = '${ac:1}'")
setxmlmodified[${ac:1}/${1#*/}]=1
if test -z "${revision[$ac]}"; then
echo "aircraft ${ac:1} not found"
rm $in_ram_database
exit
fi
update_database
exit
fi
echo "downloading FGADDON history from revision ${latest_revision:-0}"
svn log --revision ${latest_revision:-0}:HEAD --xml --verbose $fgaddon_svn > $aircrafts
total=$(grep -c '<logentry' $aircrafts)
progress=0
echo parsing history
while xmlgetnext; do
case "$TAG" in
'logentry revision='*)
eval $(echo ${TAG#* })
for action in ${!revpath[@]}; do
unset revpath[$action]
done
;;
'author')
revauthor=${VALUE//\'/\'\'}
;;
'date')
revdate=$(date +%s -d "$VALUE")
;;
'path '*)
TAG=${TAG#* }
TAG=${TAG// /;}
TAG=${TAG//-/_}
eval $(echo ${TAG// /;})
path=(${VALUE//\// })
if test $kind = 'file' -a ${#path[@]} -gt 3; then
revpath[$action]+="$VALUE "
elif test $kind = 'dir' -a ${#path[@]} -eq 3 -a $action = 'D'; then
files[_${path[2]}]=0
unset revision[_${path[2]}] revauthor[_${path[2]}] revdate[_${path[2]}]
for sx in ${!setxmlmodified[@]}; do
[[ "$sx" =~ "${path[2]}/" ]] && unset setxmlmodified[$sx]
done
fi
;;
'/logentry')
for item in ${revpath[D]}; do
path=(${item//\// })
[[ "${path[3]}" =~ "-set.xml" ]] && unset setxmlmodified[${path[2]}/${path[3]/-set.xml}]
files[_${path[2]}]=$(( --files[_${path[2]}] ))
if test ${files[_${path[2]}]} -le 0; then
unset revision[_${path[2]}] revauthor[_${path[2]}] revdate[_${path[2]}]
fi
done
for action in A M R; do
for item in ${revpath[$action]}; do
path=(${item//\// })
revision[_${path[2]}]=$revision
revauthor[_${path[2]}]=$revauthor
revdate[_${path[2]}]=$revdate
[[ "${path[3]}" =~ "-set.xml" ]] && setxmlmodified[${path[2]}/${path[3]/-set.xml}]=1
test $action = 'A' && files[_${path[2]}]=$(( ++files[_${path[2]}] ))
done
done
newprogress=$((++logentry * 100 / $total))
if test $(( $newprogress - $progress )) -ge ${progress_granularity:-1}; then
progress=$newprogress
echo "$progress% (${#revision[@]})"
fi
;;
'/log')
apply_revision
break
;;
esac
done < $aircrafts