From a49c35dbcb2863cab766d240855dcc2b9f64d6bd Mon Sep 17 00:00:00 2001 From: shenmo Date: Wed, 8 Jan 2025 08:23:03 +0000 Subject: [PATCH 01/22] update tool/ssinstall. Signed-off-by: shenmo --- tool/ssinstall | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tool/ssinstall b/tool/ssinstall index 20d6f90..9ef3382 100755 --- a/tool/ssinstall +++ b/tool/ssinstall @@ -1,5 +1,6 @@ #!/bin/bash - +SPARK_DOWNLOAD_SERVER_URL="https://d.spark-app.store/" +SPARK_DOWNLOAD_SERVER_URL_NO_PROTOCOL="d.spark-app.store" source /opt/durapps/spark-store/bin/bashimport/transhell.amber load_transhell_debug export DEBIAN_FRONTEND=noninteractive @@ -60,7 +61,7 @@ function zenity() { } function hash_check() { - if [ ! -e "/var/lib/aptss/lists/d.spark-app.store_${STORE_URL}_Packages" ] && \ +if [ ! -e "/var/lib/aptss/lists/${SPARK_DOWNLOAD_SERVER_URL_NO_PROTOCOL}_${STORE_URL}_Packages" ] && \ [ ! -e "/var/lib/aptss/lists/d.store.deepinos.org.cn_${STORE_URL}_Packages" ] && \ [ ! -e "/var/lib/aptss/lists/mirrors.sdu.edu.cn_spark-store_${STORE_URL}_Packages" ]; then echo "接收星火仓库软件信息中..." From 4c5976208647db7224247a9ac149f324e25fee73 Mon Sep 17 00:00:00 2001 From: shenmo Date: Thu, 9 Jan 2025 07:25:35 +0000 Subject: [PATCH 02/22] update tool/ssinstall. Signed-off-by: shenmo --- tool/ssinstall | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tool/ssinstall b/tool/ssinstall index 9ef3382..7b821c9 100755 --- a/tool/ssinstall +++ b/tool/ssinstall @@ -1,4 +1,8 @@ #!/bin/bash +if [[ "$UID" != "0" ]];then +pkexec "$0" "$@" +exit +fi SPARK_DOWNLOAD_SERVER_URL="https://d.spark-app.store/" SPARK_DOWNLOAD_SERVER_URL_NO_PROTOCOL="d.spark-app.store" source /opt/durapps/spark-store/bin/bashimport/transhell.amber From 90ed903197131b876d5be1340d0c38e45a490c5a Mon Sep 17 00:00:00 2001 From: shenmo Date: Wed, 15 Jan 2025 06:33:13 +0000 Subject: [PATCH 03/22] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E6=97=A0=E9=99=90?= =?UTF-8?q?=E7=AD=89=E5=BE=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: shenmo --- tool/apt-fast/ss-apt-fast | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tool/apt-fast/ss-apt-fast b/tool/apt-fast/ss-apt-fast index 88c8838..9f8217a 100755 --- a/tool/apt-fast/ss-apt-fast +++ b/tool/apt-fast/ss-apt-fast @@ -231,17 +231,25 @@ _create_lock() { eval "exec $LCK_FD>\"$LCK_FILE.lock\"" -# trap "cleanup_aptfast; exit_cleanup_state" EXIT -# This will hide the exit code + # 设置 trap 来清理资源 trap "cleanup_aptfast" EXIT trap "cleanup_aptfast; exit 1" INT TERM - timer=0 - until $(flock -xn $LCK_FD);do - msg_already_running - sleep 1 - let timer+=1 - done - unset timer + + timer=0 + max_wait=180 # 最大等待时间为180秒(3分钟) + + until $(flock -xn $LCK_FD); do + msg_already_running + sleep 1 + let timer+=1 + + if [ $timer -ge $max_wait ]; then + echo "timeout" + exit 1 + fi + done + + unset timer } # unlock and remove the lock file From b153edf8dc2e8dd875338ae972d5f8ef90fd9e04 Mon Sep 17 00:00:00 2001 From: shenmo Date: Sun, 26 Jan 2025 02:48:46 +0000 Subject: [PATCH 04/22] fix: Can not trigger aptss download when firstly installed Signed-off-by: shenmo --- tool/ssinstall | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/ssinstall b/tool/ssinstall index 7b821c9..ada3d37 100755 --- a/tool/ssinstall +++ b/tool/ssinstall @@ -123,6 +123,7 @@ if [ ! -f "$1" ]; then PACKAGE_NAME=$(echo "$FILENAME" | sed -r 's/^([^_]+)_.*$/\1/') VERSION=$(echo "$FILENAME" | sed -r 's/^[^_]+_([^_]+)_.*$/\1/') pushd ${FILEPATH} + aptss ssupdate aptss download ${PACKAGE_NAME}=${VERSION} popd if [ ! -f "$1" ]; then From beaa19e9e5410cb4b8134a54a2be7210d57bab24 Mon Sep 17 00:00:00 2001 From: shenmo Date: Sun, 26 Jan 2025 04:44:54 +0000 Subject: [PATCH 05/22] fix: Can't recognize UOS spec debs Signed-off-by: shenmo --- tool/store-helper/ss-launcher | 45 +++++------------------------------ 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/tool/store-helper/ss-launcher b/tool/store-helper/ss-launcher index a347a3d..3d3ad7b 100755 --- a/tool/store-helper/ss-launcher +++ b/tool/store-helper/ss-launcher @@ -1,52 +1,19 @@ #!/bin/bash -# ===== Log ===== -# log.info xxx -# log.warn xxx -# log.info xxx -# log.debug xxx -# 带颜色的echo -function log.color_output() { - local color=$1 - shift 1 - - echo >&2 -e "\033[${color}m$@\033[0m" - return 0 -} - -# Log is named without prefix "utils." for convenience -# Usage: log.log ...content -function log.log() { - if [[ $# < 2 ]]; then - return -1 - fi - - local level=$1 - shift 1 - - case $level in - error) log.color_output "0;31" "[ERROR] $@" ;; - warn) log.color_output "1;33" "[WARN] $@" ;; - info) log.color_output "1;37" "[INFO] $@" ;; - debug) log.color_output "1;30" "[DEBUG] $@" ;; - esac - - return 0 -} - -function log.error() { log.log "error" "$@"; } -function log.warn() { log.log "warn" $@; } -function log.info() { log.log "info" $@; } -function log.debug() { log.log "debug" $@; } +log.warn() { echo -e "[\e[33mWARN\e[0m]: \e[1m$*\e[0m"; } +log.error() { echo -e "[\e[31mERROR\e[0m]: \e[1m$*\e[0m"; } +log.info() { echo -e "[\e[96mINFO\e[0m]: \e[1m$*\e[0m"; } +log.debug() { echo -e "[\e[32mDEBUG\e[0m]: \e[1m$*\e[0m"; } function scan_desktop_file_log(){ unset desktop_file_path +package_name=$1 for desktop_file_path in $(dpkg -L "$1" |grep /usr/share/applications/ | awk '/\.desktop$/ {print}'); do if [ "$(cat $desktop_file_path | grep NoDisplay=true)" = "" ];then log.info "$desktop_file_path is found." fi done - for desktop_file_path in $(dpkg -L "$1" |grep /opt/apps/$package_name/entries/applications | awk '/\.desktop$/ {print}'); do + for desktop_file_path in $(dpkg -L "$1" |grep /opt/apps/$package_name/entries/applications/ | awk '/\.desktop$/ {print}'); do if [ "$(cat $desktop_file_path | grep NoDisplay=true)" = "" ];then log.info "$desktop_file_path is found." fi From 97755980bccdbf632878e914bc525b8777cc172b Mon Sep 17 00:00:00 2001 From: shenmo Date: Sun, 26 Jan 2025 04:48:51 +0000 Subject: [PATCH 06/22] fix: Can't launch some apps with space in Exec Signed-off-by: shenmo --- tool/store-helper/ss-launcher | 44 +++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/tool/store-helper/ss-launcher b/tool/store-helper/ss-launcher index 3d3ad7b..563744b 100755 --- a/tool/store-helper/ss-launcher +++ b/tool/store-helper/ss-launcher @@ -1,8 +1,42 @@ #!/bin/bash -log.warn() { echo -e "[\e[33mWARN\e[0m]: \e[1m$*\e[0m"; } -log.error() { echo -e "[\e[31mERROR\e[0m]: \e[1m$*\e[0m"; } -log.info() { echo -e "[\e[96mINFO\e[0m]: \e[1m$*\e[0m"; } -log.debug() { echo -e "[\e[32mDEBUG\e[0m]: \e[1m$*\e[0m"; } +# ===== Log ===== +# log.info xxx +# log.warn xxx +# log.info xxx +# log.debug xxx +# 带颜色的echo +function log.color_output() { + local color=$1 + shift 1 + + echo >&2 -e "\033[${color}m$@\033[0m" + return 0 +} + +# Log is named without prefix "utils." for convenience +# Usage: log.log ...content +function log.log() { + if [[ $# < 2 ]]; then + return -1 + fi + + local level=$1 + shift 1 + + case $level in + error) log.color_output "0;31" "[ERROR] $@" ;; + warn) log.color_output "1;33" "[WARN] $@" ;; + info) log.color_output "1;37" "[INFO] $@" ;; + debug) log.color_output "1;30" "[DEBUG] $@" ;; + esac + + return 0 +} + +function log.error() { log.log "error" "$@"; } +function log.warn() { log.log "warn" $@; } +function log.info() { log.log "info" $@; } +function log.debug() { log.log "debug" $@; } function scan_desktop_file_log(){ @@ -67,7 +101,7 @@ function launch_app(){ log.info "Command is $exec_command" # 在默认终端执行命令 - eval "$exec_command" + bash -c $exec_command } if [ "$#" -lt 2 ];then From 761f67f02ca3a2b0673d0810368a0b179e2685be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=90=E5=85=89?= <3285778581@qq.com> Date: Sun, 26 Jan 2025 04:49:16 +0000 Subject: [PATCH 07/22] =?UTF-8?q?!316=20=E5=B0=86=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E6=94=B9=E4=B8=BA=E5=90=AF=E5=8A=A8=E8=BD=AF?= =?UTF-8?q?=E4=BB=B6=20*=20=E5=B0=86=E5=8F=AF=E5=90=AF=E5=8A=A8=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E9=87=8D=E6=96=B0=E5=AE=89=E8=A3=85=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=90=AF=E5=8A=A8=E5=BA=94=E7=94=A8=20*=20=E5=B0=86?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=AE=89=E8=A3=85=E6=94=B9=E4=B8=BA=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E8=BD=AF=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/appintopage.cpp | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/pages/appintopage.cpp b/src/pages/appintopage.cpp index 63b0235..9ef148c 100644 --- a/src/pages/appintopage.cpp +++ b/src/pages/appintopage.cpp @@ -180,7 +180,19 @@ void AppIntoPage::openUrl(const QUrl &url) { if (isUpdated) { - ui->downloadButton->setText(tr("Reinstall")); + QProcess process; + QStringList arguments; + arguments << "check" << info["Pkgname"].toString(); + process.start("/opt/durapps/spark-store/bin/store-helper/ss-launcher", arguments); + if (process.waitForFinished()) { + exitCode = process.exitCode(); + exitStatus = process.exitStatus(); + if (exitCode != 0){ + ui->downloadButton->setText(tr("Reinstall")); + }else{ + ui->downloadButton->setText(tr("Launch")); + } + } ui->downloadButton->setEnabled(true); ui->downloadButton->show(); ui->pushButton_3->show(); @@ -357,14 +369,25 @@ void AppIntoPage::isDownloading(const QUrl &url) int exitCode = process.exitCode(); QProcess::ExitStatus exitStatus = process.exitStatus(); - process.close(); if (exitCode == 0 && exitStatus == QProcess::NormalExit) { + QStringList arguments; + arguments << "check" << info["Pkgname"].toString(); + process.start("/opt/durapps/spark-store/bin/store-helper/ss-launcher", arguments); + if (process.waitForFinished()) { + exitCode = process.exitCode(); + exitStatus = process.exitStatus(); + if (exitCode != 0){ + ui->downloadButton->setText(tr("Reinstall")); + }else{ + ui->downloadButton->setText(tr("Launch")); + } + } ui->downloadButton->setEnabled(true); - ui->downloadButton->setText(tr("Reinstall")); ui->downloadButton->show(); ui->pushButton_3->show(); + process.close(); } else { @@ -498,6 +521,15 @@ void AppIntoPage::on_downloadButton_clicked() return; } + else if (ui->downloadButton->text() == tr("Launch")) + { + QString scriptPath = "/opt/durapps/spark-store/bin/store-helper/ss-launcher"; + QStringList arguments; + arguments << "launch" << info["Pkgname"].toString(); + QProcess process; + process.startDetached(scriptPath, arguments); + return; + } emit clickedDownloadBtn(); @@ -506,7 +538,6 @@ void AppIntoPage::on_downloadButton_clicked() { return; } - if (ui->downloadButton->text() == tr("Reinstall")) { item->reinstall = true; From 5e55607dbf18211c80d6f58e1a259809cfa66a8f Mon Sep 17 00:00:00 2001 From: shenmo Date: Sun, 26 Jan 2025 05:16:09 +0000 Subject: [PATCH 08/22] update debian/changelog. Signed-off-by: shenmo --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 6692452..d0eaf3b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +spark-store (4.4.0) UNRELEASED; urgency=medium + + * 支持从商店中直接启动 + + -- shenmo Tue, 24 Sep 2024 11:27:08 +0800 + spark-store (4.3.3.2) UNRELEASED; urgency=medium * 提升升级工具体验,不再反复弹窗 From 11d70739051673ea17c57347a22aaf19ce1a2581 Mon Sep 17 00:00:00 2001 From: shenmo Date: Fri, 7 Feb 2025 13:32:47 +0000 Subject: [PATCH 09/22] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E6=97=B6=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E6=8C=87=E5=AE=9A=E7=89=88=E6=9C=AC=E4=BB=A5=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E8=BD=AC=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: shenmo --- tool/ssinstall | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tool/ssinstall b/tool/ssinstall index ada3d37..45fcc1e 100755 --- a/tool/ssinstall +++ b/tool/ssinstall @@ -1,10 +1,5 @@ #!/bin/bash -if [[ "$UID" != "0" ]];then -pkexec "$0" "$@" -exit -fi -SPARK_DOWNLOAD_SERVER_URL="https://d.spark-app.store/" -SPARK_DOWNLOAD_SERVER_URL_NO_PROTOCOL="d.spark-app.store" + source /opt/durapps/spark-store/bin/bashimport/transhell.amber load_transhell_debug export DEBIAN_FRONTEND=noninteractive @@ -65,7 +60,7 @@ function zenity() { } function hash_check() { -if [ ! -e "/var/lib/aptss/lists/${SPARK_DOWNLOAD_SERVER_URL_NO_PROTOCOL}_${STORE_URL}_Packages" ] && \ + if [ ! -e "/var/lib/aptss/lists/d.spark-app.store_${STORE_URL}_Packages" ] && \ [ ! -e "/var/lib/aptss/lists/d.store.deepinos.org.cn_${STORE_URL}_Packages" ] && \ [ ! -e "/var/lib/aptss/lists/mirrors.sdu.edu.cn_spark-store_${STORE_URL}_Packages" ]; then echo "接收星火仓库软件信息中..." @@ -123,8 +118,7 @@ if [ ! -f "$1" ]; then PACKAGE_NAME=$(echo "$FILENAME" | sed -r 's/^([^_]+)_.*$/\1/') VERSION=$(echo "$FILENAME" | sed -r 's/^[^_]+_([^_]+)_.*$/\1/') pushd ${FILEPATH} - aptss ssupdate - aptss download ${PACKAGE_NAME}=${VERSION} + aptss download ${PACKAGE_NAME} popd if [ ! -f "$1" ]; then echo "OMG-IT-GOES-WRONG" From c8cbaa9c8b6cd830f9c5af71423162df42be733c Mon Sep 17 00:00:00 2001 From: shenmo Date: Sat, 8 Feb 2025 14:47:30 +0000 Subject: [PATCH 10/22] Upload: Spark Store Logo XCF Signed-off-by: shenmo --- src/assets/icon/logo.xcf | Bin 0 -> 306760 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/assets/icon/logo.xcf diff --git a/src/assets/icon/logo.xcf b/src/assets/icon/logo.xcf new file mode 100644 index 0000000000000000000000000000000000000000..7a02e0c232ead42ab7d21a0157f66189e2d76efd GIT binary patch literal 306760 zcmeFa2YeOP*8e>-r=|A>0TPlBO6a{PQLNYy5fw$fC{+-YA}H#K6(Nv73K$z8DhdLk z*sx&50*VTPf+!s!^qS57w;FNhpkYH=4I4XRFf2{tUu`yS{Jo=wq>mdjYQ*5?Va(?^WH zefW@Za>tmVL&psnpKhhE6}E$Q6HA$E{nIb8iRpc7F@r(JPrTkh2zkfkJx|Wv`_SUu zD@*p4yu7>g@!c!uq_=L}T9O)a;h&qIRG;G4Si-q^T&ZGy!k279gJD3+s1IWgM%*_9cDoK8J9O*{_dnM9Ic?-2J^uiI%Y*-z z_rTGAM8T4YQw)BiEN8CPk%(yW_$EOb%bm@>0 z>HT|G$F-93r+4TQOlpyK-@a?aq#>hZAPgEke#D?rBLuy~;c4*h3OZ(0p+jnZ$rERyr zjV0svjtS$&j~IGy`lum8$IAo9Olo!Kn88ER2j4q-(48Z053;NgTvPDjBPcmT-YT~A zuUh|{o6it6LsG^Q|6C%b_%~g~n_#OTb_?P~Hp;c;Ucw>SHb3+^8^i9fG5lp4quSdT zE#rxQG3VPDJH^Jh%WO>OX=9>kW71ss3M4I&@;H=u=$H=SqWExm2Lb*_zrI@ox74;zQ{vT<1O`6EQ;^6!r0HjWC` z!>BPq+H2#TM{T@Y5qf>eGsK^;KSgPpci}!ydQi9ybpX1oCwYX$Aiy_%?VRxDxCJegw7$SA#j=H((~X z9&7+^1{1+;U=+9)^neFJJycU+*_t}eNURLid^X1FxfG+dc`bQo)3#KXt~pK7S7?VuC-C-6$>)0AufwfHBG$l|4n9u;dGZpcX1dFRcbo$jIyH5@bq6W>!MGd72jKih_Dfs~Z9-oGJp=6Q zJTni7ObQ#R`ICMa9n(Xrx=X23bGvk-Wt}SU-SEgxnrh)uf#XlM>%jYlslZo*!rEx2 zu_Hp6Kk2PCb!nth6WY*|U>uBSt(ozwyemH>^aE1gq`bJ691P=MBXWMG(eHc~gFj~Wy6IJ)+ zJOkpc9uBP|nDpngfUC!qU9}2F=ZWv0R27XnXg;I;xE04-O3iD>U9TMurBPZj zus^0dy$POA&5NM;1o{i`0_clC5fq<+eV}`_)l6wT^)ZY=FnYpp?}D)s#yv1v!-zWx zV?GRIo>^6&=50mH6c``EI1~=!8W?ww)b%?_HxM(`&P-*43!o{Lc`79qJ4W+Jbule-$6W_0JD>b)0c^%`G${jT9t2FSr;9tOeVv@{I zr8d+3zk<<}hzG#2V0-Wb66kxmwJNIKANt*STDnrchJWt5P`&6i$~Aw+nlVW|w94Da z@y!8YU6J@s2wkRBtusXu#_+H8Wbbz3=|rT)n^@kx`3s?4tI|E}%o z41VWFT8{P?8ypH_i_Q!XaU9O2a6~bF1MEgjpfLfNXC4E`!PqWI6RY5y4@Y$0x$#=V z$!aN~2xIIT0@ze10KrO z61}Ha6!RGPcC=DAwxm*Gl({QB2f-Yx)Z;BQvx%SC($A|@WHr~+i%c_5^SC#fX=YBq zt<3r|&0W}(>4qG&M=9kxqlsp=GZE%m6iSb}6>0fqh|)DRir8JDsLB_HLYl5$iM=la z#p?S|WmtNvc_^x}rUr*Wnhwz>C#P#>U~o)N%AIfC$dm5_4Ujz<_Fig(ruM;C~EDEh6?PTmc@f3cX8(%9?j?c^@Ho( zS(+*gQ~v!p=G;wHhwP^2CA_zqU^Mko_&VLfeV*2Avg(_tHB)Ng-*@%0py7XhWCvFd z%}~D6pUvpssEZh&qdRL<>rZY0)vJp}?Z41fL%TVWBES@e5Hi;q-MGyRbnFhO(jJObb)^QJqnT<29sOv7b2T-bj;?$4TrI7ss@F(ML$UbJu65qdgMG54lfvfsQ&;M4 zck^|U3!!mL>;EUQd0kz?2-W=QgBA=yAfyao@$U#JN37BxNP8cQo-pE0z*qzWGu-r{a+-~$EDtra707j7mW(jH-d3@s=|D{K zzYDg|yevW0{ET@T=2+8^KX+ZH(!ECSI?adq{6BRQ*3aD3MDs6s`|h0X*y0Z5-#zcf zq|VrWZsjw-eWI7R1-et;C(oy8-Xrjn1N~I&i2j&;jvwHBD;n!4IG4hSKSafUO4=u3 zoC~8v0LBsmZb^G8jkJ#vh3RQ_WKJ=E3&HmEY=Ct&QBQ#H5_NHaz`Xd$^))kfe+Dz8 z>HI8HCdz49DuqeT%u>mk+4e*el|+hN*(y;p!@q5+66k!(n*pk%xx&nv+Cs%4+#>0i z{aa~j0@J|*;3#k*cwdwWqeP_e-)vN4l`llf!Wf5t_|7< zZwJ)S5EvckOPlS0>W6U&LwzOKUQ_4M!h$Oe<=Lcep;Q+*R5$w3S^+h{sk+kRw+g7y zE=9v99~4lF+=|iF$D`;J(YpmyS%~7aSo{!g!7?R1?E$q?V~!lU&XZIj~ExXwU>*?)Vx)+f~}iy98q6KcLo!+Jj*133ut5h2xlv>vo2HPoWoLA?xhhfAZ;R-tIEgZjau zu0+rBR53j3IZ;B)%?4>ZQ8l*;sqP@{2&k8wqMIBDhkO=^QEoUyNg1D^sUMIR9UUku z=I+qOni=MYo-DK)L2P#hWF~7PXN5|k#G(?%X8D;Q!sC&e+GQT$fQp)h~-jR>Uhm0`d*i%_|a=zeL-aNU$_P z1rCRH&DPY`M4ks`Cn@y_cu%t8DkEZJm?C8loOqXP36G6#4~m(mMnLLN%xM9zt(5e)9-99kQuIQ)4_zG|dB~U5i2*ao7zl zPDORMi-F}v2fy#!I=nWDx6)Ra+jpJ4UVW)8<^eAhPZdt6>#wF zsgIe^vatd!&CasL<`L6_v?s*tC!OR$@LCu<#pmqW0_O%e6TpwSx!y5u_O*nw08Rm% z?k1eQQE&?2+{i@PV$KQ1;oF_GK+L#b8Am75x@ZBT&j*Z)F|=Z{2AaQR*(&jwmiE+e zPNV*-N6*y)>QKZvVib&(tGUZ9#)cX$S95Q8cX4MtC|C1%Qa2forE)cWO}fg+eXFaC z?i;$unBNy?iMeMxi|9DN6IXLfSVxgD&vq1{bW;be=J6)&kzeMw=tOk75+t+q+@ZY3 za5|3f65d|_fY7nlgc{Rt&7JibvIu1J+eow=eFdS_5{jp5nX97+b@K&7YCH}9MHHP$ zUruOE3ZaPa2yJggsKFlT7QG2|I7!GqDyvSvsCIaVX@RcuPE(b#Fk<DUR6;O9YA}jFplJqUeF;TpOMZE)c2UK5+S_w5Yia8QTH%UK$ z928COvZz&1H$z=%QJ+EG5Y0?{*eOZZLS7Zav}#ezLFz)N-WK%>RCk8H>!3rDis{@Y z7LSERl|wawy2zqrX)HAsRk7TVqzn!fiq8z( z&;q3v)dFfa)Oi-w9qI=t#{o%s8PqD2+Id3U2=OY!)k2JfD1!()1u+@ou_PLia6aL& zga;9Rn((cJ69`M2T!Aw2DcBAC6>Nefr&kh)p%*8D$B1YPt_QCG-voz&bHIned%8KzeE09gV{X|>^ z;-pvif-}M8;9x1^0Rp1LHWvvLNni+!-%{)J{uHDl+;}2C9|>zF%d%bh3E?Lfe_2N$&fXZ%CKZ)i!Yf->9dz6i;NiubthDTMQwnR z_FJDUBBvA7Ua0y>LKQ>32z854<~|4Qa2wUyY^7WEpYj;wN+r$ZA&j#b7I{R7eGCx|>7426S7J!DaNESjTP8AXzXa7Hk*->m|K`okFyKF8#*Z&s@b zE`W7DxE|I;=x^M3$QjQ(FLmleXYemovZkxfC83Cew&7Y;13x=4cSAf1^A+$Gse%GG6n|+gfs2D59;UDQwfpEUQLx zLpZb6WpgYqAk=H+&Dy zBw}Ru270)8IE*|x!6Sv3dOLVqk; zhzMd*m3su#BOMr2W>q}_wZKwbI(C!+`l;wLs$VCudbT+P)Hurndm_g2z?O&y*7Rr8 za>SY~El2EfOJnLP?pXDqrAl3BIb+vYI#yrHCA(3#T(UQ@_@I6?#4+0s&1{{1uBI;c zY9DGU0{85|o|?9zwgZ;_@!u>|@<#uAi;$!0Jbg6O6vl)wvb?TNWiL=G-7?BO-Q}8YUAtM=e%5uAb$!gbK4)Fu zv98}+*F)V!z@+q$>jgY|5mJNPp`0uhw2Lt{bLy7l<+Z&%a{tMP*t~!Yu6ciyaI{n;Tz+14>GO(pB#rBS_#O zy7jOlNZ?Fr@Rln`xHWU|QFoBQN0qix99ohA>#VJhhn^wyx2p+~)jL$3%!PaKOxQHm zhzCsWcWbH(%Cyg>Ja~6e%XjvuCe0@Jq|D_BSyti@&Ht1-iPu){4ABA^UpH-Pg+p1w zY-WZ2H5L2Uo)TS0{qpL0>Tb4zp34q_eN#R4HUFp$1D&Z_Rn{Kt)HW$v!6 zQ^Rd2xs{U)jEq}QM+3i4j3Su)GoF=C($H^j_lrLHXkr|i+Yk6wj@}%LVu%tmG8WYk z#mrn3OLMcluBO2W{TQFfj&pJ9xn+^@MIBz$;wTmHKja8O8AA&+-*;h93_FD{W3J8`)&>Y&=_o z*++2OX&k!~apkqyLaM+}TszThTriLKLA#w8;H9^<#=U>>LF(kw#7-JJPL=tryNg(T z{=2Q!?jx6W7cbW4W<4~&S{@VKJ;#^CDX;6hqjNBM{%K@t@58jekTr06M|+Q!t72%3tK*vG+G zugx~<_J!(9GZ8FbW6iAgRC5@RVM}?#-;%1!f!%7%5O+$;>Vh`aeB({!?~TN^9?WXl zt-jIKZ-s`}vigPp(QWvz%CAu!>CM}0VV?P)Y)cW3hot`%yc^sG$`a>JP<(gd1Q`YX z4&DJ`(W{~0L2wW#9)z1faYS4PR)BpmS#_FI_I`zd=YX;t-41LBW`o&aeXtD}4R!^^ zmng293i1(G&_PffVLQM(z>T2z62!+SKCh+Ve6S2$0X_7h^!K&s66n`z(PHrx)}o~~Mpx5# zsnU2tuUhn#(DAisu~&D*2A@ZIu`XY)MgIi-a5c@89xC3AYtAq%r$$CC8oSIq%FJ!Q z0a8q?tWV;CG@5{AX%(Ges4f)gwpuh^AJw)N{XRJ!G^!bOwH3y72a=b>FB8bzNTi_0+Z%l(aHtu+u>-oA64`N?Z2Hnie5?E0bVu9@vu7_woe)l#QQ zS*J1^Xn}KIGaIqKP>)SfH>_z)h!vT*@jEgJC13+yu&|;D!8k3DI{DXTR!B{20U~Jf znp=s~WZ@$^XE(Lbbi9^L023hsVP=}_LpEET!Z7hIXvri!lwy|Wr*&d#_yp(nr#E@J zV)^VZ$~lK-_J&+mr}$W;j+rR@qtN^#-y~2>pyBsyE?(}yCw{^2gx>X_WEgn?f~!K= zPn_~04x-N^2wk<0(3jD;=Hp+8Q|8;@gs%CW&0~w+ys#|>Wx!v^iW%zMR~!rD zMGucKI!bH!X0yW&4RH|*;pmMYcIu99`ROIRXb*4bsyZ)MHxzHd%o z41tqF2;9SqERMm~^#Fk#Y>-nAP_*S~)W)ztEK3BFTvSKj(=dO$fKbmPyub(Dse_ik zBq;N(j=Xv7TNqQz+OnI;c-Tx-fsgw;(ck(UO;Ukf_a~#=rGK2P{FN_W=*S?+go+g9 z-(PyJj;7e^`})eitDvjF=y2zqPF4N`uMAE@gk-$Yhz0!Fgs#}yP$`r6kjY0<*^b>< z3&cKtB86QSS?r#gS;3<>NAl1#TCi(H-80y2_@AYl*dc)Kf#sR;&fs7$jwxuDR?2?Fo5d!a@|Q7ofgp zeWM;bK&^G&4sTMuhr+OsbJw#*kqo?b$cP?!e&bXp(T9tlxrK(8nf!Nqv zL|tdq_wtST5T1&p~rjohIcWGpM?^Xpi*Ml-!ilwoBm51ySA1ldMUZ zj`eW7aL^ge-5iNC<3(A%70zrp-x_dQOB0+2M>Z}j1N#$G)x=$WC@$>0IuGWh+Hu}T10Qg_XNqp242QsdS~ zu!a=t4dH`Fb&g|GNe>`p@U<(p=&D>sD%%?^d^m`$1lf`IQ!5)E31SODd_y0#wz0n; z<9n~m4ycFPz(9EVQtg0xm1fSRSDWqZfLcuZiqoens(gNvjxx6cuR{T+|~5W4OHOADGfq2$9nZH@mWc#>d5!YAHB9whUUB1`_Lngxu%?W#5;N7 zT|;l~cTtD*2yaMHo%XaH&VS9X`)|v4DCWwm>%7T7!p~XlFU4~1-^Qbyk|*IljL5$g zFAiO!a=m&>&DXc$_`$u%)X%JsfShlL$obwcht`4)+)qSuQakZR?u%1lJ8#P3#7HDn?^&0iF0P0yK)EcO# zaroD-#7Cf+Y0Pk{h>c`Z4Q746o_fs&hTMIe$w)kAsHz>mY+C>QI^)~Z-+jM+)0S;} zkNDXXJAge{#VG5IaldC)WhnJCjF(}IgpqiZ@$rTO-9BOZ=`?mdhEb~Y6^sJ&>1dqq zeRrm)z}}}?WbkE$7*!ECym{3NdDmrUYx+(4jmHLDf9*9_Uw-lVJ=!&G7^8bbme#3h zy!akjk(Ir=mhXhQ?0h7^@LKdE&`oO5;wjp}dt2$5^WuFl=AL25!6tQ6E&2=Sdet<; zKMVRtEDb9I=sf76TJ$f_ea_HKDaS%ui2pxYtJD}dBeqpGa1gYL#E(y?ZmC>S5jidR3rDGC05>=a2Q`p29jKqyvQLi?s z^4TU`{a*9&sUD82X2fW|tM{X>(($ejy-D@qO9l;A?+jrZh^w79WOAKm>G&u85?*7P z>bPF9gY9q2Da_*y`RL4vD?AndRxSmP?seQ!r-B%K;U)fbHh7xdwq$U|Dd2w0pDBl& zT7737a=PlYb;xO;&pPCEtJzG3=7m+wMd#1KSUT$ee*W~yBX7L=rr{H&yt<*niAHaF z)^k)~^@N_`5n7d_#htGm5jDR6-@<`H%xKeb3BA3C8KVMKkA*hZ)EC%wU$kU~?MI;0 zK*3ti{5T7RswY^40<{o-{s!6S9#M(vWOj$u2c4=66>1L1XBXAa4F63me}x1Hcww|9 zq6attOan*Boo)j6kzhZ%))V0G;53QAfK?BYG6Nh3o(KK~oCNj&e*zOJO?U8j=mp@* z&=*sb;aIA>V<}xjv00ZoDEJ4w$zm68ax)g{iVzG_=TC&bZO&U>jmdXvB!SPdNfLoTf!(b9z&-LI?WllXO8R z)-tX*g$!GL1IJy=ZU-Bx!0!8#8fs>XZOr!-^Z3+^8S^5V*=PM(Mm4WLgi^5cUP7sF zHpZ`GUa}#L0}KJj(BB(!3L+3bW_v0}DFV)$f2hZyjle~3nn|ju7RW9+k)SYBW5#WZ zQ#qu+=93uJS_`zFPm8qG0?B#%!t1z)&_-WpGZ<>?a*lFx^yB1s4qj5#-*s-I(VV_< zt;LbBB&wxG#ca;W5%0pGt}VpZa2%r@JH4V07W+c6;sckZDC$x zzU`hDcfGjj3xl{Ui0guQL=agj%LIaLg4idBV{9}F9VqN1FgLO#P?bx){MoDGIECa9 zIQ?7>J*_;NjW=d7o-h`-CbRGZf1eRLMlX1F?(K89vQ|5aqOlalx zIS*wz9IwT458{;P)Ycxn?hV@EvvDI|nJ!`MYK`)1@3h)bH`N!2G-p4L9-aRy6ltu(}rGhQJ7}t z1==xrv{9-EDWITO3rI*kWxq`U0yi&n=#+lVhEz7oO+nm&cP_ zazf+FY+givnSNL7!gm$$-_Pe_%#g1b=M@Q}X`AI2Fc?#IAPOcq#jW*qLzW8LR?x$@ zIUKUE{)&etDI)$V2mY>n9ba2LHV~+nSV$a61I{4nF%LUelTN_B9Jw+Jd=?b5aVsLt zafm=Fj2FS{z#G6~B4Rkjp+2LBd(8lb(d=g99dV4 zf9Aa}H|{#-KeqRmH7`GQTWcLpaKt53zNn;@4lc{o{F#%!@Fk06NSm-G6@lT`FMT7> zkOK&T$Z@}MW`T3+ zsgp;(c;%_-4^El4a{WOMDby?R%IEw1;fv~Xj-=INoL=OvwA1vkQ;YgWaBC+HR%1n4k1!?rO3LwdiJA(0rpi9XXw+>t7-SUT=vI=*ky;%xzcU zEL)(e9uLFonEGaOuj6^m?`(3#s7_j?yWK61E%~v^sN4HJOy0v|*@vPkU%gmH91>!L z*k$IY=gGK(e87-xtgj}@=z}aWtV2D0vTgDuL$<$~6J$gZyV8&@S7HrI$aMy?QRT}Z z1$P?sP<5w_RHzD}%)mJ^UXh#;nmAXtMsP??$aGCkQ$87_Itpb5a%4mk$#*en^EZMN zUzw!ib(BF)+}&CXRIyF@AJ7$y z@{Js^ZW2s@q*3kduvTZ-h%_eSf0DcJe=pxy?O5EkPrmoVKHtgm&2P`@8-+i>deJD& zA2Q@C+^7NHOC6$uOuh{kJDJ4>wW*rJrm5@p#W5#WGuRhb|22g1$Z7^I{d1V2*MXS>XzI|)K!et+CWt*IO$c_qZ(gs8-|K}6h>dYiz=RLml z@|uW!ENPg_yHiSs&BH5;;Q6VGj8+EbCYBxpW|4e?#U>f`mHlMYTanBnCod6^U{T16 zqvwjeuqYgn=H)1W)s%WbB#RY=oOxZZp^#uZqEQ*`ILh5mBobR#HgQmQB?@OST-DSL z^Qvm9C=8se-K3q0w}ZUp?A8{OP!l`!xAwU|xT3wD!J?TW0v<*24gCg5vINJ4rKx`# zMqnbGy$y)#jY4jo?o25A2+0Q!i2jMdLj;UBNwrw+n?hi%+;=^J-KoU2J;{BiIuc4m z+cQl*32eGP=0?7Hpk{+FgF{LEbtp0Ca2nm*RgX}^eS}W7CltC??(0V~nK&nd6Tp|4 zYA=;CeaY!qv&msXJ5mTmZ6b7{Eg|C#xvwwjA0z#}V1IA{_qCBSt!7cmOxQ+fdom&C zDniFwld6P3;3@)xNq!H>yQ3Scc?42_C+agdsYBKf`Yn+}3kV!*)ufJi!OMd=hD*KM z77oo>#+bcA`JGb^^Ivt^#W8+Vf47|Dzm?;SXuB6ySts~&j#?-9JvjeSS2^K|O5B63 zhv=qXh+ACsv-HvRk*W=LRg_$@s!$@tq*5lG;orleq9E8)l&c{}k}2c|*7at&VnbQ) zaCM}-)#vhg0@Yo7{c<`PkAKKo%j4g0pP1|Ph!Clzfj*Da*%hbUQiI+290Jxri)~K% zY(mV74q3mr{*dKUOMO$mVRZ?g->uUo&Bc(3*K)*=iPc(KhD@~9S`3*eO;przksA8x zlBjU4y%;iKS_d&?LYcGwXdI`AX%m7P5HyZ`mT@dHu#X4T!8VWmj!S|zk4H34Ok~lq zY}*H4t(eLz#|KTIP*FKc`K~~XB@`-$24`V52JIpAXH~>{XV54@P;YQyRs}7i2x<`H z^gpwX9EF;?xTl6?D<__pI;^wKi>(hUVbNOWo4;*tooqfeQqDAE%F1bGF=XW=keIRU zMQ>SoNn2DBF<@I^@QL**rkYA`}RZNGIgCcVoVGda^C7=>e-vnh_ znINAUv+(jD-V(&SgDB2GNjE!)vTiA~9PkqSHi+AT$oCS|Tt=}Q?L#mnh&e$#H;7jU zkxwdE=_UkGzHT7*Jtdf-9Zl_V`HdqcO_}r3o1d-Ud5rJcaDoiqY5RW;CWL3IA&8kJ zNqlY_pW@|&=Q6nePhfi8Zs6Xb9ctF^-g%#I+^!Nc(1>i*HS|n_n5qt|2qxXDqv&*JB}Nz7ZnZ zh@OFdG8{wrYBq_pM&AeP_ry&YRcT+j6iQn1evI8C-LQj0-*&=K^Pms5#|(BabI6{? zw({+Ws$3jVp3e>00{1?e^k8Zr@mIyC^VH>#%h>nO)1p2l@yRqCT=V3_W_#i$VG$=D zf|yU#CO66vD_mw$C6nj{=tg`Fg){j|eZ|c12{?@TG+E~FQ_;+#!{NL@%v)eDVsx3$ zx9h~TfHRMn+rW{ad>ErMC`v&kQ+OR$hi4>e5kq+dV_WcnAQlDjB`V@Ew$oUVD?JFr zjm?8r-#U+2`NyvhT75e`BBKlREIVxl^)VPVZ@xp+Lk4k#Q?yppz$I?cAK|3)wk=Mg zG`{Yz4L;QoMK)}MEPn(Uy|JjCBk5?{=<{RO4M4O8F1GDFKbDkGzty(u%=JHrX&)oDSvm%&jeJ-{Y$bL)y=%Q z3^kj+@;Pr|CL$p))6754lpuW$y>UOH!Qm!*FvyML)Cbn0wsjYnv^Ikhm>0H-q_%)nYn7#B*4YA49*h?3M@c zu54}2-#{TzQ7E3}c2zXV$MM!Df-3uEiVq*gT({-II*A+F@+p<_B}{enT(pb&q6x3! z^W+#Xm^#lu{gy!UJiy3q>8Ed=XiekDc~AL-%^38#27LP4+$s+E$KhNDC*%N=(N|0Z zH^ImS`AoRlh@KaIiogRf`V%4F@@7YeTFdOU2OI~T2EQ37gW!~W8Lo(kH6%dQNnAFwO2if z3lA@5MNxEPG!b)G?{pLq^BIqP;?ev(QPdIhS3VAd7~jpXLHF2NzrL2qcDS&5)abGD zdBd;jnCgtmKKI(uud$a-JyTD1Cl>T{aE>hG@+XhbJU7uF#=2v*K;ly(K9AK$cC{1n zhA=aAS=8VwcGic2u}ec4;57V&y3+2WFXkbrCtxfLu%U3eFN(SG@`q38j7= zmOd^Kqb@ zA0G_93SJAYj;&kQ^1=98`*W?Z@Pa?Sx#PKYT_}2apyz804?efk^eNViy0L)pIIFrS z<8hB1l?CnCsb1IBEX1OFCTxDF2OHf!S89fQ>TEIMwanMZ=^CL6c+d1q9bbZLQ7IIabR}- zvblUgu=kfuWyS5%FPn%v^}Od#$_MEKk+*%C&dQ8GbIj{U8}RumpQG#K_x7@|cJlM0 zzS-LT=*~Akw&=Ytf824{S9xU5*6&u#o^)d?hnMNHy0!%o+V6x0W&9g@P{xl9%J|9- zBG>7^TP%@IClmj9sYq}tE&ENDVk+v3bdD53)(2%mO}9w|UBlJneIni#y$C{Asrb=t z61n{poqT6CWg6l_Z(>VgR0h|NObEkl5;1W;)GIYYTNeBF^O=&M3~xqcbDKh2*~rMa zpoS9r{Q4TIGt{UWim6KF*%X?sd@`$~hWZWaAZkS0 z+TC5W5(xP#4<9Fs$^!8wUbD(Pr|lzCD{*~))?9QVqWBO~;FnuuZV>8owg&%tn^UwY zsIU3(h;M0c!}fgn)>W~VAa!&BKAnp$0{vaL+;>jRZ3D#YWt35=} z6(gwPQgTO$l8=U}U%={hl}*r}LH`QA3+@4zfQP{6!IL1C1^Fw%UC`2(HiI0_ zRNsS?LVW?sy3JB%LV0c>C~DB%;6Sh+C@VmstKy?J%_yb@=PB-Bd0Vw1M%JFi#dH_A z2t0=baTO#z?&e+OBcqZxE;)5ObZh#HS&u*=jIT%J_hgM8a9I`8T2PKzw>Tn>4aF*mTT737 zQhXQYdri=bns21b?TPlt7VxTB@yvSzPar9-2@z+(k;mB3Vm5o1e)vMDI7TX;?`2@E zw4GbU0_P85;zjZAEx9HkR14%x{DQU4!&zahX<+`mQ~Kzn2lAi&oJ|O3Rqt@EEz?*3 z2=T}S1|oQ`KGKp7%Qv~q%yld-9_WeO^-T3Qr5|PR$Sxb%p=i$J!$$$nq+`tFTYF-g zr9A6r{ruoPp(uEnWfiz1jy#GI5UBSc-#HAd9u&q$YxUfc%~7Zg?~P|q`jPaTr>@wz z=Y;?8_6_gN8s0P1%dYU+ovW=@720Fev+Hn1v8^lq%4w@{R5sIm&U2@HZ^p91?vEI_ z=tv?JF-C|(D)^@f$~*uSoF{h?9~$zX`2t@(*a17#9O{c#fv@jksaD;~w0Cr7HbZSF z>ih3^MdAp$8XYKbY;ri$V(KB9^6iHBgY=bj4c{nw!mR`j_o9zD858D-Zgi5rNT=*g z50e$28DJKS-F#11O@c9s?)@>N^>icwS)y4ArV}w1ybQbm90iDoj9kIThSsCaek zhtfgqQo}VPChgo&PyZAS+YH-MCXy?;?hCu*OlfuEWJsP5L#*6t+%Plcu%` z+4&D>>bhM9;r&V;gSRGQ`5xE^?YuLr3c3apvDWwu%g>+Filn$MIdu`d)6|AgEj!LI zQ1?EqL&>3xU}ECr!@6gvp>?QB>QGJ3P+sGqIzksoA^O2TX_eO8besJE(+o(!TA=tR zUg;U{2OqdU`k(u1|EwxgoBr5DG_`EAyB>YyV)?9bF;h&I%s&CPfSHj$Vq2`2bqaks zlI%VlZ;vqdpKgMgb}gs1)Q2c0txvF6JP21s{I5vjgGu;ooFB5<`w|*xQ3K52bWJu+ z4HIv~w(vC3BslOdyAAjhG)t$G*eZcT!Sr`%B%3du#T_+(ndDnm12Bc(cj1xzki_E~ z%D#<_F}OOKRgwH;;|47Fn)@2_3R8%Gw}EW)INcQ`KTHJWO{uaNV)n=AV4=#~A73BZ z`Z_7jDK)P?dJy^|XJ5o5Vflig_P&dW>H!k}5}G33!;R6*L>^HZr=*BpwXZRA%q7J8 zTFX};&C^}j4zn2gE6jv|GWqJD+2AqIU7+S!>5q2{G@(2y`Q=DHAv?OqAGgli`FR0%>75 zSHW3D;y1wCh-r-SYaYUwSNFqt36AGy0-}OkgnF_qoB-cyeS(c@esr+I{A$!twqgNTdal5Xq;krDXv5=;u66fV69ko{xU?C zDtmwFmNm*tzjSht%7;6^TAjolzcvDiv!bfkwp~7yzH&FS+r^3F8vH0Ba zs-PZMc3__#juloKx5%->*r}GCPmzN*KBcy;d&(WO>j~KwJxZWu%QHZ2E1rODzoQFU zmOIsN+w3qbYaNDVr-PP-4qCQ3Xj$d7t!0mcmL(2aHaI92)03cOchl0g#m$YDtqodM zHuyAsqRK_h+LkpO%cce`Ynr0ib~L56EodpfZ9CH{w$&^F+g|2vZA%$H|5Mo<+^n{g z#kns|AyjPJT9zf)TCO3a)5=f=c(rfHXZePZRXb8b5hF+b=o@0&;)%9lvS94H%CcE1 zud!^F)0aoen(}V8nY0osI+_J%v7ci!jF-=v$MH9D#Ac4yvMp;l5qG0yCnr}si2PzH zH>~jz`NT`a*Z2Oan<%4BSBm;SvKB|rxSSsHx2zrZCfz*J_XJ-C+ktO@O+c|F>Va#) z2oPJ|)J4`M!Pp9l^L06RANUx!1{{EhX+;0RpjI>Knr;zGRoQS}MGAc=w)eG6U`w6! zv`~y@^UX$x@t98dS6;-ZIOR(P1%jM zl0z#4Fbr7_>&*K%yh02?-CR2+}B`N=wk}D(F%^7*?yQlDZA6tG$MhoqbtIL?uw>IuRTv_@1 zrqxRxy0$6q!@7SUQ)j7LUZ0ngkvkh~3nKSbUr-J+iofAPa1z)Nd>G6G+1IM#LGfif z!1?5Jf@);oDnB?AzDx~|gHo-NLD|SJ_M7Zoy$NIzQQg76fFb}MrP_6Ih49B()JB;V zWF}%%E15UOf;WQQKxrV47>Uwyt6?-HLZ*)=z~;2qUMGPcVO$AHnP20MW+r+1vf#V} z=Uvo}3(x|3qkUh8=Hc0lrmnMh$=nl#{*fn(n}dmCw?!bkLRN~z(kTm8Be6$+Kacj0VKoU4<6Cqb z_8c>bcm2&R%eN_Vh=2VBe6TzD8UD&k7`DPGtorw_(D9mN&E!LxtOZ{aU@iF9f?oW| zDSy+uKV%nB1ofJvPWm=3p7qGY2dB?neKZ>Hv>MwM6WJURXf3Aib*Ed2UE5}h_^8dO zS98Qgeak-9r2{-Jc20n`>Tf!uaI{-uV#IE`RgO|Mf4&MU_S^pSzCgmrFXaSPK|4BH z)}2cZuozzT*`vK2&9sAh%j+Ll@WoHNkNQvT+xp$|{2@Krejias1YuQPN~K>>;}wmE zy3?kp37HTIYN+p_meo)nNjuk2%b+&aP|rbauc72GFO=6%_d}hkq3+~)LANu{yN9@{ znmAGP4%bjs8lq47alKP-nJ;XvpmE00Mn;pNjkfRx*Y1}8tP@JOq&u{GEPGEv+WzEbB-bM@fwQt zKGngd=+{!3ZMK^jO0s#Znxc#Q8k`~2ONcEB?UIehU8=)8=o9fH3m>J~?0kk$FN$AT zB3Pq2opy#$lf}<05$NBaqlViA+V`;xaXd>5i{iJ6?^(he?9Lb0vxHH>U*!OTO`w0T zMkTTdR*61|5;wFYL)m`I+Ndc6GWSK7_@sqEt*#oK9V7y3dzYrcu=!*xa)31d(Ti+z z{-|~Gto>M@V-P$tx10~Q0#`&Xq)2^aDMyX8u9K|mBi40>b$!yh&bO{lS=U1A`j~YU z@7VR^b{9AiyaAj8b_L%7Q^21n6RW3Yh5U7_O)%<%(nD_m?*?B0xxqY$Ot!mV4>+QJ zO#vr^>%nHUr4$?)t3ClAf$kL)km^$y@4^tNe;K`DLl|n9{xh5x;D|&V3B!_wm0g(? zMF{R}NZ?%pdpa;ZW}^+8C;OsW4oBbxIMbt6AlH^CgR;>@0&`cu4jDnrVDSk?nsjzKzh4g>$@itS>t6K4z^iCQUhFtuIDR z*lDdV8s|TCs3-fV{hXUV~48JfV)}^^pTITi} zTChg`D%jE_%w6SU`*RQLZzq0{sNsmbB0+1)7iLB{60|1x-wR`sv@A4}rYW*){r<)E zS<((1D`qhpDgNWg$VRg8{af*Qd;#di)Cj?ZbT9q@Fy`JorT{AuUms;VQq zx2#<{W7K6SURG&p`?{_0A6I`lM&bP{zrQx1{@wV0t^EII`NllH^Ks^7CqKoHzc8gabMv@r zS_nVkTQ>y5fR<4!+@MxCtX4R^Rye#?m~$~!o`4ovD;!N&o>%>k?q{Ww{F?;B5|<@m z#Xm5!RyaYz{0x4z{>`}N3{g`7l8%2f3M_CgO$nmpO)g9EkXm?-h1R9!UZ|o*HovH~ zjq|^?acMUjmz{6pYn+W6)RHh#noIsWOZ zyW99xnT=~E+IXZIr&zQR7sSMTn{MzI8#C6~m}%FO(d0Fo&iUTPjwfwA$F6_le7pR{ z&}y{b-*_U3`BUxmvx4-jQk$N=*v4n~?gxd)j>I`uuZ5F0(P=M;lZ7*qB{xW2;0PTQ9V+Up2mM(-VR?={=jCw8qBC8*I$G z%*IFU{^EGTeoseXSDRlXb+Ga6YP`Uve}2NoU+jM5_(g;e>9>~Hxczq~e|C?{a+*-?Fjmt2TDC$CZ=MT*yCX z_ux3bJk_R$HV9(NAlmKWyw@(jb4surPc*aRr}naOn%zFm{CjMAx;=iJbAr!*^@Pn| za&-{@62!ZLm>0z0eM{yA>DPkzQ4qfi;|xzb$eBRV^K>&E<%n)y0^K-bu4mvn_6568D&ifJFOIkSFLiC>3Nyt z>GC=k32i_J78Mm`poE`EC0w=0C^Efs(`!XkvwhyVa|;R@1!Ih0MrIy&R9I1EWo&1) zGnCkatIEpq@_35Z%Y8)VmzLJA6@O}Ib^KhTpu&otI<-hV@KPknL7r?BRR(W2N_|s{ zik>J-wo(N8FCxWWD-Rw)(Y$sRlB96TZ28MdPOqIx^3jV9)D&atvv!(-l9B|HIjqO& z-knx~B{QqIrKNVd*#!k9(~FAYQ3$D79;#c}{eC7{QIR~6RAoi^GiSzHS=vvQq`MYT z19pzp89qaX`EzEL78b{lYxVPg4L)BUPi0jnmTaxG#RWytR`N4>>qR>$hLw71(G!J* zQix#A1w}Jv&5E?jV$e;ttfDcjccY&2N^7O27-a=>ib`jd6c$HV`EQ_Zz255DkkU{W zXR?=aOYWpEoL$7SJa4exx$Y{ZiWhvAl+LQH znbYKTW=$_Fp3PlyQ^Ar=yJbfW{y1t*FZ;jjG^br4(^C^)S1*sGw}qK4-c2?65yO?9UGSv%~)EY#*Ff&(8K| zXM6AeS7&?Ahnmi}vYmPt&7*hF!u7UVgsvN{2|4)ySGexMw-=_n4TmEe%4Ikmx|2UB zWO1DA>JEeFa5$ZYE1j^Dc!$&B(%p@;P%E{A7{jf58bI5zLK_}EBvlKsIEIzN@E9RF zC!B@nAP<8)Y#yKDY z)P_^Hvau5#vCANd;^N{H5)-3po>n{AAxXy5ZMBJwDiUyRzd6{OXl0Gg8VT#rLag0qg?JsMD(v_AS*lvv(5h&=QIb=vYT#kkJGIDZf8S=O^12stNG*kZAhGO& zZ|IdB^3A+Xmdp~oq5WePDVD{eyhi`bMJ^TY)%!;*YUI^bC?U_H2(Lc0ewhbr)3qqn z8(Wr;CzhXck;fZa=E-v}N+=QvyMC3c%$es{6j~JOjh?Gx&#P5VIBJo*$nZMnHY{*> zon=OzzQ_S>%=HxL-pI1(e5cr@@Gx2Cx(XC&Lh@B!N~t2P*!4PT4btjmu6%c?YpVO% z*dj&x0M@fXdZWyd?d(50oKJ+5MHl+Tur>;eG9%w0f=zlw`V&T(tI#neVs>~z z6_%>Ej7Ysy=Psit@I+aeqfkvTX7l{V!%L#4>%mw>EaAeyl-aW#1&$Ku^w1(_e$<>( z`OjN+HrAL?sLwVE0*{xJ7}I%9zA?u=)0mbZSr?fSt%%l%)Qc36#!P*hQR*xVD~>Lx z5@W8A=!%l*Sj$+z#>}c|rKN?1Wk#{Hz*Xd)5mQ<&Haa$#Sm|@-%$zCF#l=Q}qe#u* z?PfWqyGvpV_Y`29TQ;;LC@U^5C@3nLQCd1{)^wwUcguGcdkUSi!;8wTeC$M+&!m|( zi=r1+z$kIe(hJhueP5^YDh&M( zf8E=yJn->vd%aqq{Or&&#z8%z357#>AE-h7WXy{sk z-pq*B6O8(fP(8}X#&ildq8yE!x*lqTIwG76Tpq*i2zAD~65I~MeB`)9vuMLOh|-VbS4Ek~%z&5Lc)xEF`>Mgn~9aPV6K{ zm@C|kj?Tt0QWzG;5za#+T~U$PEz05hsOobP=%|*HKL4YM~oxZ z6&Dd7ouFG>1Fjxz#2B%TI7hrQAv7^QNo+MeQn&betPy9#8wrj?XOcTPIi;Gf;Op@9 zL`Ra5>`Zai3#%WUS|t`i3{lF8(up!sjCw|WXR50~a>FVyoZ^U5iAIv1tf%Pp^!i4s zqk*HL(I_k}HeHW9WZ6|1Rw-C4m_n%y8W;`rMvgQ`x~p+SMtG*4aL}@SBwAt{VgRM3 z8RS*R{9?~Kp_ z7&%8;w!Yw&d#22OamA-U{P^R}ox78d?y*b|v2`$ZFhg48~`bKK&6om5qeoR*i*+R#k}uA05&9HmUJ zwhB`tPxESdil3L?qFiroS#Dl#QSRK_g53PvQbL7+dxx%<7w zd1ze!4mq61TYRMR1_nPzt~1w_>(2G$hUA9khUZ4)M&-uj#^omDCgrB&rsg)xP0!8D z&CYF>o0Ho*_b6|1wjR#b!`c3Dc3k|I@}VKydS%0WhBunc_N}}T%BdwT3hkfZ4O7h) z8U3TYPL=76Sfuw4_wx4$%JiWTUJsPpTQ4uREY#~-#Et`Ry(0G_Pae(~wn-$+4fTc= zv6&*zU4~0$QAUAtuHkj#FmdKN%L1#s-h}+fGTrMbz^UTJU$Mx@bCfkKRe2#eVwBfa zpwH#+J;2xVl8W5V;%+H*<+C5a>#$;sqJ+YbGUpSnA{-}E9i@(ZgS+sx%#A6CnC+P2 zDrDEf6Zj^G#XCW)UXU<7yhJ_jDllfNDUL$DOg({9eSRyV;atsIeI=Gqv<-C;7OTn6#B{}KA|A2*jZ?l;!hz>Aiu~;V-)yi zPr-qbSn8gEKd8VcuENJsT1ui5b4XJ*eR|2`1qHK{3f(1+>BcOhlv5RWC-h>yP%k|- zZRSi#MWUD@=WOi0VwLYGG)l^+&zeahXQd0B5jRVfItrXcRkOwQkx$`E z^jRk);T#f5y5wRtJ!B^A88}T!@p2R$o-Lk~!jclQoFN&O#g`ZbAw}d=Xpq{NX_k=q zXelln%T-b`T@ohDbmR7M&N50Q6SI`G`pjb`xJpWkipY{2XEoR#_+Z29IbB8e+5wxWvKl^@%`XWPb=vr2?HeP&7L z^3(fwZCyQE5_K>4?ce?LyCpr#)n2czS(EhH?t8ZTp6$M8rSDnk`>&V2=V{IOGE6%? zQtQgc=QE5{U1_b1SUt^=V1(&8jwlwfGM!P5a6{>hT@lVu?D%@_SXU_4zG2k!M7cx% zAA4T{A4hTJJ<~Iz`?f4!@_kFbz<`axF^9l}E06@TIo3%;0_0fOWJ$s%*~ke?IBYbs z4jcKv!GXoG!|fO_hXEN3_yBCcmPWEXx<)fSqtR&YneM9Z|EhaNl5Mj2{l5La-F#pF zep2`As#jI7UcIWWem(tvwoI$lG9@cJGbvZbt|lA>Z;k+slL zfQSA(xCC;{*`=j)*6QB@&4dj%M&lqPd zvy7EW@#tRy7eJ92IXu5~QqDx{1nYQdoTbc+TsQ!bYcAr2<&$mY)=APtJnJLZtmm3b zcyZaZoT<_j%VbMAlr+(dd_4Fg-&~5I@-y>h*rr>iS*Dt&;3>b{EI8m600$qNJqOS7 zv#c|v8F;pzW(JR0=Rqj5@#DtN%bIJMBhAJ${Y>)=^K`S$k}RJzY2t*k%B%|Oe9Js( zE*|M;n`fE96P-S7+SI92CQmLeU6j4hy1-H?Rp4QMo*6vm*^yZ@XU>>GEK|w@gMHoI zpMHA0WzUlvANcv2D;F)0D$U>x&6_)S&Yan^W)WuvmygWD=OyCn3`+7S5*R7_cPqcc}?b<__gIF_!=DnmTL$j zKBLBlFQfP^SvW5?;m5kzZ`$xt6<*?DzezN3*08&@;L3&)b4+;6hbzj#hjC&@N!K=9 zaMleW8&w#qJ$Nx2m@NXeR~bwC5bGHV73CiiZx>|$x=DkTBZwG5Jd)W?693Q zZ^wpHl*9`@y-EwP4-xXO$qWv?0=8?dR$hXb5C^iWgNx^>OXwYA%8YU=CJ zeseP#NihK_!!FbIQs=|(Ah&C5)L4pZtjSzEK- zil%F*>-F_(1*cgTuEf-heJ7Y3b>?jkrNumxx5(tQWB*CpRBZ0B@gylwwI+ndjhCjV z8H&X>x*cLCN(E5AUj1l(ybJ|DekP0$dmY-I6fHm?Aw0=W;^_4YmEq^adi{B(6LxG+ zv5}?yZrvuWkyM#=q-YWeTCUM$lJK?hN$fCF#ZpZP(L_rqrKyMn(;AZS)$%7||B7<7 ztu5+Lmgz(6Zc&al$c;kEvy0(%a7{|YUAv$)`s(^9w#rnL zPNKz4TK7bQ(1;ixl+-#`Q==EmFdaMfrYMvuyS@pk5t`A8YPuAn?t)sVgEUT7lUY9E z>U_IVYqp7+UB97Tw5oTrdALz335Dv7T1-~?Ls!RcA@U4E`peAVuNj+v$wtTGL@gy4D2o<3{(MuYB zbn*c)9yIi%9bHf=X2Mr}#%eRXJ{YTwvDz4`jj`Godz;ig$k^K$dz-)Cz0GAN2~lD* z%**ln>nvK>#xcxJ-mH+~3>)afR? zkH^oVDSfI5@8ZRk4Of#*ct*nxXOy9zQ)ih=O~Jx~g8clvyxiQJ9K6BL z!tbav%<23RlUY2H<88dC0l%;o98^2ikZCS9nZ={|P^uoPCJUlbGbyG*leu(YOi4*` zaZw@h>CI7V8k;2QDlnPLddHTQjv1qKLNHCPtgOt;3{fF*X22cdoG^aexU#WhOR3tT zBE43vagEmu2`!K#*VHj-;zW%db*HL^48zoGncBpoe!12J3Q!BwOHFp@f~2APNgDhL+H`Uz9{$NafHIIuYlJkDDx!5(6N%ce znNA2#07{`y`J@R{iX;>LCS^h}y^z)o{65|EDZB(IMxKV@wnmqtfzpMe-ryI11=(1uJO08Li>7M~xFy?pzEN=;Uw9noG{ znKp95I)y%kyG-=ZYR={-oDW`v#z;3*i29cv2CXSgEi`V_Ig5GJm(zu7@lnM}v2fIw z@uP!L|NBiqm4hlS#rK();Gcp~F3_VZe(q*z4 zADALM7_}79%F@@Zi@|TM38#v~gR(eXBu+Kp%il#JB_s+_CHPi%p(qv{t_NS%F4PN# zYi>uun%WwCY`Y*`Swys7+q~nUdP+3c1X6{mzNY%-N}V-0TzuPu_KI``A-w{8C2ZfQ zB@dErnl*_~!vnSRb?yK@EY?Lp9kq>=Zmt;^(F0LSWAi+{z@SiGjV8$hwd>ab4+@I% zX(dPt>zYJO^>uUgBK}kn^r(4bWAmmaz)7KuS1%(f+lKFd*ROlnK1USt3VGL(22ltP zk2E&m1}7{?uAvXDspHfpI?+(S!M<^( zDBGWwRt%li5tOY(1T0b5qqmzXjJ|BZH_S6cY1fF-TFdAUm2TAX&AZ$}w@}l_w(B?4 z%`kNfePG!2;T5VUbu>aR_EGsl%R*g3bQGspTSC14w0J20`{qr0^Y+H+rmm3bSma|!?xuO0e87!xVn@OvZtco;k zZ|s%jDPlOxyL5GEmPZY3!nXvFb)N*5w8n-T(u_e9HpSAX$wCd$MA1}Y$FOX0Npg+Z zSQ~{^7;9tNVw;S$F+K5(wb57`jeX;rO`Abe8R9Sl3T?4oGD%HPL6}^V3BmsiOG>l$a_U)e9FC z&eK_gBZ?Om&4WUmq@NIfg@m#??NM9R4NYQ*jVR6aaGh~BKS1F7+=_rpOr2%II?x*Owh8tBjlxC zptcE<07$3H=1-NPuAtP!@|mcS2FI6bD<#B)Nt0)!%MGNGWf3_{6772Q zc1IPSIU9I-dTDu7%X;ZKseJda*igFO_RQIH;lb9+dq%ciD$388HD~_W4BDtcms6&p zf{LlSfcKhbvu0zBXQ@fthpWRX#aIZv-5e zK{Pk?!Pcu-86gf$js03HVqir_wL0qX3QrTHFWn4DDil3!#;m#X7xmCvK4`m#tV){% zFe)<938J&}Dt?T(cIk#(XEy|eRlYsU)2p>a;tscj9$&3;1OU_q7J3_QN*fr_=SE3q z=}SwK>JrpKL$jbAz=6W`cZ|G6v=Hu7=7{{Zg- zupEGMN05K1Mt~Qvp%Hz0+F2-*^Hkb3l3jobYBHf}>V17vBfK$NIy^-(!d3`hX*;Ud zvHrlX$s@Fdih-d=x;oNdB5Ta%=1##Z&;%cZ*Rc_*1@r;|?xAi-@iTR!zd?Cc~=sJrHszTp!z;CKffNK!2>D^+q$d@RD zgFc&UA6XAi8f4qvSs*iSw3zp3))-GqGqHdt;KPOc6&Im}JV_qkW1?AH zpv4`KfhQMnMJtbZI$Wu%f6kLlk}c+{z4kO1;!8NIe-%UkVWt|pk?B>=<9t6-!sjC@ zHzlz1IW8khIWByRPCR-6&!P)D@yLZdli29Q!xzy}y@+SXd}tXR6U&S{ig8CV?kL(S zYTQwbJBo2fG43d|#W$W&w0*bnj6%D8vERl+PwH95ct$avQH*Dl|7*`E9KYZt7k>)T zwIhw_3gp<*Qo_^bu)7Hw3^c_+DaheZRE-Up~=Hk4lV;S_#acl2vRGlGRQGQaX4y;HgK zT$Mn-@asaoHHm55Oi0l%II_1?6+)?yWZ||Myaz(i=#zeg5PGkXebIrLT)<32fKjuO8?|(v&y|IIlEq6iEzmHsytDH-y~0vqh5a?6pqH?NlD@Ac*Ijr3=D;13Y*Of&Bfnb4*RwodKJMvyLo0;u zr0KhEd|{pX_E>zYPG5p+Uo7JrY5aVh7s{V_y6NlcI(#5aSV{OQn?4|?ubjo#-09Di z#b?#9VO}D)cJHgLZ>Yof+~Nyo`cjcTa2B6M4}VTgUqwqbrE=Tp-RJ^p76(zi_~aYa z@6f)X#&_`h!ECM>E4O(%wotE|P_>YMv@8&xKAH!!Qp2MDQu(CkXrmBiBeg&}(j}oU z^XW_LRBt7Wb;vhHZtLAk(un$-@ELg>ebN0u^Mk~XpVFuJ5l1%WFOeOdwk@K1sD_3B z9Tl3{*0f{YFgF4wCx=Spj{a9`g&v1RNokQjF&C0#lNjD2`IM)lnJO0DB%MHP`mP*e zqndWJy}y%QbNYSj1;CRhm+$=%NF z;6TGdUk_{Dr*;JwA_vC@3gsTxK8%7co!09IgmA<(hO>ybK<@P%)#SoJ>6%INlu}y- za-XLaK;+03EHFh~TF$+4Mg$dm@_m=gYEtA9ARxBXXN2A%u1)A7Ec^*NOpXd}LGe!<(8( z{o5v!RC%(`a{%2(wU7!ki1Hqsf$pfDEYpKO`l(l6YjoMU*I+!Q>J?~RXqAPZc z=hltW(9%$zJm@)y450+rTmAal8Jvgn_Z1OA!}7N5(OkBVT|tTB-R!79A5=@GUctG~LdOUl!6eC-poBQ8V!a=dvFh`LSvFQ$W2H4#T4SX(R$61F zHTJZ|p4QmY8hhHm`#mlHbm$LP4}X16xC9yq0%0wj{hdFY;pTk)nezN~TA7(MrV|DN z;1W@SD`8bS(qwBJBw!;cLsl|iw~i6=~MkAnWpnD_QaA3(y}g()<10s!7-Ef ze0((&t4l&=0k!HV4q2JAc<O)GqPv%KIH+?{*1G8=kosDi_!8dQrM`H+4H#j z-DM(6^h~r#Qs?H(=blf$KsgW>eICvM@@Mz8dOt>FjXj?S9V?J9bH>yuB>PexI=obxPo@3luyKk z`lHJEq^l|A@Lq>*q-vmoWjuCznN|iq>%CNb#UcOu-%G zyAo`=?{lFksCm7RcsqXNO@b~k4QYvaZNzUv~lnO1*VIL&-W zA3&ZUf0xz(>3L)YBs>_%o!1B_TUw=%kj75f02rM004Z%)PmVn?h^Z=|9nt@3re1Q( zk(kX7(~yw?r0bwgPBWc84p*BdC}~j}0P1L}lBuR1_fB%vk*uP#dq8Xx9oYTUazN#l&06C^@yYafQ5Xz3obqK{)ujr;`CulRWun) zJ)XCy>mwy1lhWn~)r3!ZWG6a5tU#e*&3N>h@X?Um^=?WcO+OFeV*;v2?>w>eIyHgN zGjh?=K#fviIe46&eW*u_rWmU;g)Rq1Y?m{op{7l+M|~(Xk)fb+lTGe>L9c~I99^gJ zq_sp>^CYlzwWAho%rzCR)r?y2lVEXg1F8sq-mYnIl)z|ck|{&(J6_)))YmL#@ zdlSLZCA5bD#RSmCO4E$q&_rO@c2TF%4(6_~98AuXebR_e083wIEjfKtmUCu9g2fGY zbrab+W}47Tx*?Kfg2}i?46l^NJ;Jz0(1M^Zs>VG+TPTftgmI5Bo+I=A9DL|o#4`8geRQeKAoWeNi{O}b4jhG7uwha)}3G(u4&SonvhHIN;@{-$gbW2*PnI>8IYDHV4 zI&Lw;r32^ZsOE<2oMG~iYgE*l>X@z^iy7V=_(sVGe0IH70EfG$!Og1ItSd$Dp#{19 zW(^P3OPc!w;B*&|845!tQEL&;8nG$MtWyUKHcy21@Q5)oI7K3d0hMkG!1x%PCn!XLZZfYAFZQ>K1Z0r}TM_YRZ8r z8@saNT=nSENak{QoV)%+PVRI?uuN^5q=$SGn0i!rzeh_XOe$T|B>1hnUJ>qcU4dt5 z*kYbYOzft#OuB}&no`}Lh(KVyaLVg4(OUu-K!vTICHDkiaCh%a zX^E_+n5`bG+^^SBm^cL$n< zpDDD(JRZ@_`tH)zDBfR;(lxymB$n=X^uDJJeRNZThiQEu(NxYz{fswd^1h8iWZmA+ zYHA#@I2V${7_gLf(oK;xU5kX7R#%?fPfX5gDNiQze%;(%fSK}4UErep=)h>%s|&}7wEQnEgzYprV}i_rZp zQtZY?g6v+e4d7WU!`%VEOl_TggJ+n9x*^l?1j@NsMvkC;L-$A)p%r0n zu!Hmn_ML-8!~53Ft!aaM6bpe8=~@w!gMQY(!INUqCdaP5!z=(bCzjf5p*Zu1>9a$? zu@mmNm_Co5i{lBhC423I_J^U`ZRh0J4w!h-EI4YPI-3K!7aY1RJ$p_Lf||GM9DB26 z#25$9xulZvPPsy1YGkh4v zHtuioU{O2HTJWRjzaFw-dy3`kkPQ^lK~{9-0<%V>mbj z;Xci)_=Xift=1w{P;7{dOoH6f!d(D8O2md}9?c@ayeLuypief{YAQlpLlq>|4 z1wbkhcH;`Bv7EEhTD*(O-`Vif>a;8rs6xmB)GPwcf-LhZUmeAwA)^h;LY-ddB^Hv! zb!I4wlq@%7oXJ6Ga$0Z>QvbZl-)8XfXGXaH(LaU!V3 z+=}IlShR6Q&=k=hE$#+^fQbS6b|xPOQO@YjNMRq;ILpfJ}k2G zXIZ>V*S*=Q$B9V|k8UU%&SpuEpl&Enor^__i=r!K!OO!ex>8c6pH|8T=U|~8lSOKC zY(d*776R)_D@9|eSRkiZwAnF|Mc3QHM2cnDXm!=5jCOIhu9kCf2&1jI;6LN2TyZvs zW^I_Jn&Dq|P7VxDn&awobBL*~)$zr%I7TiyG`;=`hd#oe&ykvce~m*}^j~QuW%K`O z9Sg4g+`gphFgQ}PN?S(^ef%Bk!zS~A-*Y{!8f?6vvo2DgcO-z4+o*_R}FqhX@_wbV$;H0Xfo#&vE_u z58!_Y{~`Q`@gLRyV>r`){J-blP}JY3ctcTtqoN4>@<090f00|54N#x+xx^&3P?^ci z%&d-6rf`+1>SSetIL}bVit`+$Sez@A0${rm zBCY_F(pBNi2f%1Z?po-~;c7C_f1WcFsE4#TLER)by?~7}17+)@-E&twsyP|NqRLYa?op&s>&X$UJ zGW7C2=UL`UXieU6Yr#S(hbx^AUt(P%Wpc&cbg5;jBs7`7mKP~j)xu`;az#?jY%-sz zm~kG*=PRnpSP5T%s8*hmL&wnWMAEGnAOg!-=5W-|IZpdhF0UMJRT|IsM zZrSG_9E!x1S$vW+-sAEN3`LXWe1hMIF+vc!FT~*;#7LP zVW5jIMT&bUnaP(q)xN=`m9KCn-9bbXyws_7dBQ>`fz~&7)<5v;pWOZp>rLTX|M9k; z)I73v_vu(fPDc`Zb;e$?shj#q(=KLz9y5VpVm#k~w!{5xSQf~a+YXK;w_b>O~ zl3T^EJ|CmL^X{v%s-P9$-rrnjtAbYI2Oqp%s)ANR`+j{5v@$5P60;3(M4{rvdD2k9 zma%scbQE^;jQZ zkB>+Q} zkAptcokV)5^+0xu`ff)Qbi#Qsr-gkAkZ4tL9R~Ta4b>y|E2OM#bgNH%B+o>!P_}j7^N!p2YaA)_S_vHZ))g*uG{Q4g1-AL z(q-(epMzerlZK=Hp=+|M)EgW7K@U8BGw7>+eFAi3&v%IbAn99q^LEfbc#~*$km!Hh zqY(WT@qF`{grL_58o}7qn~#)$e+dAGBWu`Wm|r8a?*iyejqjEn$pHZ0l;!w>*!< zhjrYUkBawURZ^2XugR}sKcZ1+yS<=_{jxKasIImTVvyQyE2vVxvWG3;YJAI81y%e` zupa$-K^3YG-k)CuwY_muZWRX1^Upa|OVRsnV&bw7zm{2r5gYtfM%6O(eb;Jh6^88C z*DO^SvcX@7sjP0hstQAP@FomduNX4y1k)2NXD#Q!FL3ohr%IEI9SLIp2}A6{Ue(&d zK8j*1+rkb4Gc~h?Ij{<3!SG@i@NC%E;8ILJcA)Qk4kLD8Bw&=ED2q94?`Rx&u4IIV zB82k#8K{F;eYg|EI|JyQ534pi-~|=_fG7{B_(w$bgHlfxv~U-2V8PGb&_X|^0hng< z8WOk-0$;O*R)s3|y^|0aWAUU)R>%IS7XnwYFTDkUtC&3rfuZ_-2wcT(IR=5N*y`sY zF!C_`7?xj#A#fGD!w2cU2jZJu5IC|H)Xm3fR{h~BFkN>DLcRATP}j78>ij0C)o+0E z{TS5MdqE|B59+EHL9xe)+6Sp$f;q6OUx(D^*q&fa24ox}bWmklZD~^BpAn?^|6VpV zm_=Y18wnNS+_(|&on^^=r}2_P;5i5ck6}Cqf}i0Ui+hW}b9e-J4rc&IXe`-s*ZV;4 z09wW{aD+hA&_Org)dCCfM0DVYVJHj&p`j?L94y=mfn6H}RtPXE2n=JSG&)ltTXwyw zp;Qoj$L|StV+6c}8EzgPMQe^)4G$xYWRZgS5ZEPPQo+=N-?R(pjfV;1hR``i!d;Y^ z823Xa&&E{u&m|Lag&tHcBcDyG8$?O=Dt^3c$(Qh@?O`MoBdm zKk*h~xzC}hVSNfV$kn|YQ9JEGaS>xj5ZD4TN7uUGib1fWFaeAo45e9s%A~mn*`}15 zREEXZM>(ZSM(!&KCjZ+61^g8{UOuy=6*p!-XJIK`1b5gKH zpxj3_u3CLkf@`k^A4NkTxM&D=q-OxF5~N%L-l?e>RtGGtdl!b_VX`z@OPW8A>xEjK ztFC$L_H%M!IJ^HsBOq2C!8N=Pq~)SPe|ZF#Hk`U#WVIzUObKx9r*#lNqHiYTB|?8n zN&h)q!-JWgz-QJ~KXS-FhpU0${Qp{AZ(}wIgdpOgHR_yly`3@(vYx-jRVV5)xHi}g zO$fV(x{KwlhQ-Ys%q0 z_g8B8aic{^wNpwguLJl@L)RnhL4si$#x%2-1G*adyCYa4IuhK7 zF;3&{QA7E>_i|ma0)O#L)f0#7TcSI{3J4=Fg(IkShdA8#4tHnd;7~j%Nwm__sN17v zCnGv6;1i2cNzZP*$D>Ut)nmc(y67=s-;Er=Q7lLPs!~KPrxs`#coAbY7=1l%YALKd3}c<%)xY<7g=$Rc%)i#6OpnqVVUJAz|X zTD70YaTTdntOld3ZMY8Cbt5-H!aA@nIHgXGa%P}-IY^VRP=%dDb z*(k8jGtlyeA!Cehjg4=OUo!9obx>7fHW;(Pm<@l&*pbG44xriw@R@TjPFf+;2rR7~@IYcoH88{BG>FjNR7X z>26EIB20tsrJ(E#_%{u%($I&%sx<6F;J_N-e}Bi|f5u8TX6!g)T{qVC`Tvt(U;n!h zE{%49WAry-HyV8Z=X522r*b*xDuGM^Jyul};n%LLqMcBN6S!s3t_&w|=MvqS&I^DN)|=%dFgjN@PTZ5@L~8@N zPP7)va{_-Y)|KZ3s8+Hk-zi!Qqs2%ST8w>BLW_wnNfJ=clv^YT7-;Nv3r_r3Rtas^ z*`#Hh{fE^iq0xPr84?=(q$E>9qkYq|Bs3blAX@^wnsR*(PW-N139bGiPeQBjWamj} z)j2+2gT+LgWzIA`Y8ph%Otq2P)J$Djnwm5P0alC^DhrWYq*UTq0`yVNN|kw_$0~C` zmnodhqDo2$D|5EU6$FyTP!gCxaCI)SL;?uZgPgfktOVsz8^c*e-D)xA=m|_q5E*HP zGYe=1sRb&fpQ%NvML(C~irN=~r+89lA)U@kb)4&ZL|s~)^EtIp!AY7*UJMl#0r9Yq z@Z$)X4OW&1d&`C8k=?Px2iZtcl1H2ES;$w}@hy@)6J;?to}E^E21tV7+h1q1+XT%{ zZqBq@K?2Y%_ENUpf)0W_wK>OL13WIK1_=IhpbNIi5?%|;lEq~k8D!F%2@_P6fvzl= zpQ;Xa<-qb(HUwl)0K7$d3!DqUiP zNbFA_F^ELg6#$iVBh8%z749x}az6;ggQRW%sj>)?m`fpR8K5>`Ni2cZxKCt)06&Ky zJcu0C4W2G)JxLPv#6iWnLG>j;g}Xqx$QFkncs~(?)EBZ2A8PP|hshxPM0iq!Td$mn z%EM?*Yh^x1b=`n=g@iqcN{;^YBo^>OoRSN9fs^$DOferP*hL3p6| zoB&8v2eOD-f~CiQ%8{wjEYK-AGN=JqNvzxIuu;!2gsLY4GzVxS3!ch!Se<+brlIy{ zJ1meKWaw0mLs-ecI#@~PkMpsbsGoj2L&8d;e)JWq6>Erk>RZ+ftRAZSuB zK@JuRHNIh7o`kAjm{lObP&#fXl3*y2nw(+@hVsJ9F%opsal=??HjM0%yi$o(G{91h zFE5qU%lEOF04M!jR*9rqt~&q=`RD?W)*C(mbM)u7Vo9~F`3y#KXhDg@zIc2Nn17Q| zEV1)mMWvryi2U-+SXWf#q3j}nss39S$!(W{Va4&eu))m*NX>hQjKO&&Qdc`+1G~o{ z(Q+@@h3f_+j(Zh}&yNGOtP6!AcUy>a2-%-jpw-*S5S}kCk@&sHP2Nkn8Jm&jzY4hp zFN5l+1U2z6sJF{N%|8X|DfEe5Ww@;pv+f2Rl`}!}2AB;CWV%oa3h3uMMf{G6kQ|sVfu}Ms^<1 zlrtG&C!1M5pDWH|_;hie0IX%gR-Veo(<(lTi+jR6ERB>}iDeO~i?Jf&bUx3*igqEl z(Zw<@(P;(79eB%`7xGvv;OTL8wzbXYL&?G3_G5<+zWv&3dv`t7kZGR|Y-QPTXy2~w zjbrVTcp}mF@oRtHI@w+ZfMxlkmmaU1VJ`u2aR2+yHqEsca%Jd~zig_s=W#V~^iPZJ z*+;P4=14G~T^o4dSSNmRWSY|iFx;p-}!mh>+Z@tvcH_oxg8y|h_u`L_x7u#c- zpLzX*lYOCh7LUb!r;i8bkZ_T;7)-a8c<#}jJ*N3T3yH{PCXcp50X(*WJv_VUIO zyRvEDDVQ2p3PHZnrB?DhyHfY`XIK()?CSQz;qy2eXJPj;rvzdAJr_Gw9`0Z4jCJ+7 zy#s@RfHlZ_E1ij6w?7zD^7tH$jH@MlnlsUZ)VMkx;LbpC9*h76e4JD1^M#T#`4~cR z4q=jD(RB^Rp?Ih24&wflgXSotHXz z#~eqzb1yXy^HE)gLAGyHy~JpQoY9n${|X%*Jg1w4bI-6M*&; zv51-(Z7(atxfphfHK+h1hEpy!sW@fPc3;gv5#U(+#!`|+na<;^byvgFA4SKACxux& z7TN#H@BHRZpCpUHJ%>lxiKgl`cWih&bTN+(?7U;mn(sdJdT=>mGv6hxQK~l_PU8NU z*msv@jrzkU`#>l6-(_8+-uFH>$ZCAgouKdD69FCiQ#I(@|LDV(yuaSIhE;FH2CVhp zY->;(+kb~;4L0-sCTY#3Ts?8`8tnBGJ8P_$@kE=`=?Ww-<~Dn*VcS#Bz5Y=b5Wn;7 z$wzm;@!8MAc*oZh1E_l~2(Yk#auWu5LaQ zo5*L{)u#Qy@qD&jt$!_8#^=&GQp)Gs)s2UeCA`wk9y?Xc7uwnLfkM8>t~MOP%Du$S zp7OyYocp&?(qSa2y{~Nq*VEwwm`ul>dK7FvT?FkM-d*IcN#r{k8HEES0sCt?CbmAs`@&lR;8^sh}(+F{2i87wcUe74tw1WYpdEd1o(B8`5d4- zd_c@s6}b(x!wtZ17VovSvbKH1gfW&_m=G8M*!5V0I{O3I=$2u@k)yNu1c%z`3D4l=IQtS)`BbqWVIUpq zsR0!O=}h1tfF9JU(EaYlOu~T6TqcU*1(!mGBC`xSe7Rzq_yyZ21&~n(;5kB_LE4#No zc-NO#OtwyuFpiOU$0zV_45}47*0>amCKi3@=wnUVmO0;yk1(ato+4W2@GNUPuE+P z19u#cJ8I(D>#fVx+h519snI`Hfxcl!5OknvCFm<^-5AcUdu_|v%74djezeB6oQ&hy zD=o`0p7O7yjg3XP{b=E40Ju@kUN7*DaXVLWjTDHu<&!eKmd9cCC$x9SO4;W`hdG z)6G1Xc}v`EAc^se4|(+zH8JD?k6)$nj178FOpaqbF`FvgJdE+gK8-d`m!@`6c!EUu zo=nqH9@rasE`~c;?y;A{Ks&e-)Q0_h|mr4o!9w~RXk4Q;wMoRUY zsgyCCz4C3OJeJJTOCjZ8HB#;$LJHg$q$Bq6ZAkeS*NBu8w|~f0Vt3{gGwjx>y;9r>h^}`Y~}`q>Sgx z@qJux6W4`^G{ZiwPUjaW6Cvfdv)Aw^vS4A8IDa;04Sz8U+x>FRw}O5X)xzgij>n`2 zQS7_N={i@Lj4j_ixW1!{n$5uumUZ2Z>z^XnAbIVka&{QivVT|TItw>@{uF9^G7FCm z(@@v%aB+~T%EYao`KSkX$aI~d%;4-pDtf<4MQsT6!5;_r^I3S_n28DhbL4%uop`N? zkHQ?^!Sy{+y0$2=?A_qqM!Jy{wR-?tVr-CIK?*A1q-&t_!+8*ff6=MpHUmEok_Qom z0sHJOANKd~1IX|Lz&Ri%D>ytTUidb~QYJPx<8bz?m{{H8@oYbY=Vdn31JV^EGS~~! zOF{*E$5H+)kq^=p7i4#t@T7o-XQvFwSc$M}?ivf{zz`-MZU%4xAwyQnu(9pOV#4D6 z$n8yF{IitkkK|&9f9u_%; zG*=R7p)R;jxx>2a?(&S`zQaq>WIDBZQQpQbbQ;(e~_CAju2=6Xs)# zF1@()mpCyU#ibjUzG5e4qqywHrCfvFC9!50^ zqABRf#tx4MbI^apE+ut2F(o~EV@gjZE*W#;8WR}RXV8u=iHuLv*wpb5vb)d| zx6O%3>Bbd_ru0~G$yg`JLh~?Ybz+7@(bg%N!h;swRAdq7Yvr_e$dIsrh_itfTwLW z-c&vg9;~C7l9AeRW$=&G&{Q2-TTq7NPcS{b_hyfkSk4Aa&o12CJHBbf<*kF5pO@U< zkEwQeC9dZl$NXHbu6$6=#gzY#ayZuVcVdDrS1;QbM$+IN79`z~zzkikUbB`Rl8 za+wQHH!CuhV_ij(_e)4Be-AV5SIV+ z57|~=&VF7F^#`B4O6HUz5F@)rz_C zyR+B zSL92WyjydY^T%=7UX~|e^8Pz6U%+Kcb}lYi^2aEC>HC-=+j9{+jRn4f>njnOivJ=< z!sK;OdpG$}y?S%DG!m z-&*iA!8d{beq^gxO4=(a3W=kB0UE+fJ~K8rw2Q^O;CSHyhakNH!Hyiq@`8h2=1FXS-9 zfp;l3j|>NCkfK=~7yuPL1nQjtsQ6J(2N7h4B|il9ehidy4AhY%DD^m~W7ttM_0zl- zesT%ykciVMLI$B8N8qe?Lqr_|bqG#=rhEwMI7Qn^97c?{&hxNbegMJ)B1D0`-VK5B zBPbX6@iMO;d7t3DTnEm5$YGx#=N;lnBA^#Lf~`FZy_3_z+ri`llNaZc$T=7zo)17B zgF3h$S>03*5)Q!s$P~)@h_Ym4^&;z2B)m(Zt|DliA45tTauCdl#n1pd7zghuq#&df zOCk;z_adPS-FY7`H<*fuVI1CR?nTNwsD>k&nBa{dei-*7!HJwBFnD@j$5iTmAFQdL z-mncK>tGTtXQyXA=dH-h9P{0|tg zm?A{iPx##N$LUq*}pgO(;>iP~) zhY^vCeJg;$-3$I-k~uYj{W)Z*_af`sQ=o$10(BeNWBYYERlFXSwFgY^;Cw%FZgLaP zH$eS_toi6w;CTvpjYzqMCP)}gfUDWLwzl@yC#I04ZPW1%YU4g16^d5QmZQIC8$Hz*b># zEI}%G0k*m-f~$`qAiFtW!!yE-*xd66kl6%lmH6$*_1}!#)m^YwiPs?aFUY+Q)++IO z*eAOixmR^qaLsJbAfXc-;J<;vVw3PTY_I|BCt$F6m(>X)4Bv|Ee}%zHY)KGi_!7dm z9fQG2>}J>%V|O7S+)rS#690E_w_T0eKg1QYy$0qwT-{CYiAqm{cjzmq>IhyQU4q93 z{uin@SK_}yj{3cvD!f~=zJc`ja;osQY3cyTpJi9&OZ;nKXh+nx0*UWJ;{8OivVIt7 z&+5#oLczcv%&aPs_%k9gqe}dEBjaDV((=#_-wK}Sy?oWG)i2;z4bNIvu?G0&c%y9< zZ^HTEj8*&@`~u;@j8*K4%}DGws$7Mw4~`A4~{aJ)|(w~FIm#PO&&_S2E6RXM9r>o>&l z9&vnF9Jh<(9&vn696Q7@kh2Qntj?z6H^uR8ar})qt{2B}xI35$nZgC9dR70FQv+4M++0{IR(huR> zhVw?q_D&paJ`SSgG>F~V)oRN@n!Y92)}>JWYs6GCX=5HuCcKyYekDwrwr1QZp@l-U4w100bGco1jAf8ue- zg4j)XwuK1E9GQo4R&!7#_6fP*!=*15F*9-hq2rJ^hQ%=|jtOy8b7kx<@DC-^aZntC z;usdks5r*O5eq+;&>+= z*_F{{95ePKaeP7?@1-NVf#RC3;-|%N&qch3#5)b~>L6Zq7sRXH3h}DH?}K2~d>0+r zkD?H%nj@Gkjt|q3-R6a0)qIaQ{wu_*=J=Hhj(f#%JssH{aR^h*KNZJa;&>k&+4te< z#{@blj(fy$10C62BwjV|6UVo8@sbb^amMxN<3rej%CrT=F+@k^MJ#i~H76kv`y9Jc zWJ4|(kvI-OI+^2r4vrCV6cN)=46?u`#Ss<@+K zl&{}jYhb7w80vqg80to36eBW$k8j>LyFiWDePg;XQbC=c>-6%vDoeK_T@hpZZF%?%uuf&z683jQg4H6_Q z@MQ~f#z=#L9gC%|=Mj}~a`3>Rah!W`j)12R5g3`;Lt*+7Nb({pq-7C{meWT@;!UwcwOSbK1%Rmx zq5KpR4>rjXXo^RW(vR~1&O^wd!hm8&z`QPmsp39l4dNU}mWvFgpK_8Q!pI4rtE?9( zeUvqblo(P}l;#64i2>yFz#dt$3n>w#cnMAw=LB-P$>N|p9>h70b04%w*HMju%eeLe zi?s`(TX+%~2&Bh-1kMWUWJ(tRTM=cC`zfj)ED*TqNC_b&h;ttXrjH<9aUIpKJ^FQ0 zzeY(OhB*xupfJLK@;;c6dJ5wlK~4}C9tSQwh!@xc`%vTUj(lnuoP)5eUI#Au5OTX< zO-jh&aN`;X@7xDlb2_?N9y*4a637iWP9>4+M{YkZ+3EHOs_w;Q2sQg10bIpURX3Z2 z-~p#R!ob2y3lQ6dv9A^4#2+c5=zlGlMG{o%o?MI|fK0u*SmKvsynl&!brbqv94}cB zvM%E-P_FUFTSu03Ju+{k*q!S^O!pz_NEWzqj(`ePfNLvi06;eX4$gbQvoMUD!71R$ z{}f4Uko5g>Xw^FrIhAC1-vp6+48+S?Ei60}h@-c{8h(zPa z0^3NsVOGqY3DDQy!Rq{U$!n3DC&Q3pZg&9Ji;??7Se?&3Kvs7cHGP8Is9RQ%dk=Ci zBdhbe6R3+#$Mt`p>ad%P@LkkZ7KPa{dB`Gw4hioY{fobrXP2NR*0U|&4kU0a4QjHb zuE5R|S|!{&itKcAV}~~s+hH%!=Zo#^nI3@c_H8S)t938Bk$AMe0OTIBx#;sC8(zn< z$NC@3x2ui&gBY)mHssmWO$VbO)%_rw50YvlyL0X8qel_5lgZle~wHaiC+f z{!O4>On`C|wI4vm%(E%e&Nh4m%vC`(b*8ATKm^s&9tSWf3m^(CdlHDZEc!U8-M~|2 z$(^8HR6wy8LG1zPIe!DhUx+xSr!ZH2hL*&_TZlpsAg1mH^$f-NeH+BnNyJj?LBv_M zGXW~L71R?kz^Lv8u^r(}_z4i30~ez4N3C|Y(F3T}mq0Z1fH)1Jt{pL!l8;&K{6nCz zw&464LM_P~EOz!R&^8&{3F=9pUGgI!wgizm)F|0mlME2ozksMa1)|qt=N|#x^$E$& z-(JCy{0xxa&35*O*d<)GZ}eZv!MGBY)R^}Z{VSX50#B!H=9qY(HnWn8w~h z2XjU+=rL!JgZn}F+lw8r2c@qVL9qzBIoes|K(rO4`P;$mqT8R+SAb|+9;zZnwIL7} zDjsalLu@V|wl|nEf)w|Ho?N8*DKc;TG@=P2iWsaV)RE(0GVB8Iun-{%17YrLRO%zZ zZPu5K#273@=?3K{?DwQI%fUK^U=8t3P)RmYcNH10KQgB z>m*|kxlV+VWeMWzBC}AOpn8CAi<=avJ`6o~fp8JwNf7~^Kq!(7EsrUk84m7(EwCOW z^%9b!UX+a3bZ2>@OM53!<4F_=d`h<1d^<6)bFhsYLA zTQMFm1uxDXSVFwr;^4g)|85J_ha5kMPK4lN$qotC0O6Ipz(a_?QJev-%DW^72Rtl} zy_aw^l!izFs-puTdYA(twYguIh{SFnyI@0kZZnoVTBs9V1R#_OKfwUAV4<5?UI3N! z6}kDZa6bu`xH(X~5m&Alk+u?`65b-ju|RYytZMAeJbufFHT07T;|$6$&Q#D?T=5eQ@E&Oo5$G^S~>Av z(^&gFu14OhFR}AYxXT7!t}Ddds0)TGKc8=B^(PSq+Ovo5d7BXvLJhqAC~g<8;?@vT zyWaZqV^8estI2tXdK7cF&!@EmJyHfb=V2Zv(IXb@o{NQM-tkdZ>Hqh`r1wAwC83DDm$mF*i|!7HbzSSO>kb;g z0YMbIK|xd$#V(@Q6$^rG#fGASLoXtrNN6Fn5Yi^eB$>?2ea`c~u1Q$^?S5a+^ZfCA zzq{YP|H$XMKBwL1zRz{eT_&&d;e_I~sJhx3PAFEr<$%qc4z}NBPN%)9FOsvSZCCMT zPA81mE2m>Z6+Jg|I>knGKwb6RtU2CX1X*r2a6XNkr44p+gBnlo?H|@em1X z&L!Tw?;vqG7pHmOff9F|3+@2Iv6z5u8c@wcR=lZyHD?kJ^{wViqV>hqoJpj%cQqU9 zx7PHm=0r>wDOXN}x~xVuoJhQ}Yqdo>)x<(MM7`WnVj&icnkeTHZ)QTWPMl)f+nXC| z>zbl+8WL$V8M_k&v$g3+sUQfjQIybbLxRaMaYTXb%Lyek<3LR7STcu;)D+5wHb~Z^ z_^5FpZVg$bVK-x^u-KI$RyP#; zWu~8}qE`CLa`uAarVKx;)@|v6?=sK_H|c$PVqy<#fqC0Ln<$O zo!@`Db;=ZZGrkMFFkL=B=9MXd^C{G4!QC7~c%V0GyXCd4>f zMSY7n;TeTkXe4hJv48-y6;UtQCts>>m3{R0NJSz+o}Hq|kC`oK0!h%Sq*o1o1*094 zoXw)>vYAJC)$gkAASN!lVw<9j{&pfkrM>X$h=jP*+7u%ao z#ny~+#(!J7V2xs1dO72dDjkT@9T-U~XZ$Nt1#1-BQp*|mEh&O~6hn$pQIfS4$%562 z&5H44q-E_k5ty}m6zdgR6rH4ULZHGUi25v8tJtX6su)vMTC{Hof$fUB7556-nl9yp zK=e;YE-fJh_7r+NEC*{u!s+sMV&E;0hZvYl_AA7|r4ZA60huMch=D!biGfea8%GR0 zFY7@mD4q}>okw~;Uhi{y-H7D23YSU*)s>nrSfa>ft^e?fO^Aa-`9muP6F zzeK}*ifc2x#KC3hg3l`cobDw8E=v=fuDCJHOB`IDDmYzngP?JSi0-T{Q~XG=D#c46 zT%IgARdJ!9X&NZfwDvN^dlcVPTrFtAr+T~u!4--V6kk^STydRZt;*V-?GSJ z9a5w}g0reNsAc*I`>3Oaqij-V6=7y5dW;3gkb3;~JMrX;F8530|nmL%?JqNS);s*g?fTM<8{y85#d)h%D)$a(OW4BeqLY0oJ} z6xUXyA=InhSBdKCKbP<=TEmpNqqDj&GqjbNN7pRE(y7|f{r(ryn5NE1G@$;3&c=FE z-V7IoE~(DbOq$>B>ugNf_lcgV&*OkJtbRvx>UT6J{{F^#+jKumWkqYO6FE!d`Bj?7TV$odN@QP)A=D7OyVAp$aD58 zYa=g)5~Q;_QR1XFmkCaV!*mOA z-sK=X5!o2FQB=#X+HY>N<46m5}qgj7Hry0y~~?d3KCl}@xNlalqY24y0QC>^U}GL&11_!w7# zIEOWox0AfEGMRyLO=^X4o2RIpz;32Qxow5DNJSA`vYHe%f;>yh6{ts=Worp4#3zk7?6lOL_q{gB9m=onz#6zWbyOcG+_L9dWAWvir z>!cZF%qiOhYbN~5tvtR+qxH&U@;I=)%Fs-=yJ&N#vMN|LdB`ABuM$=dYg4uf)(mSV z?#pddh8xzPEZos%jWT(7k~a4$(>A5o%A`%{CtBLHB5lga^SYFtXj3j}gz4ikeUS&Y z52yP096pZev-_MBT$6$nseLHL*A1C3hgjV@DA`xwv9FVQTe8m%B)xB#*k7LPGpmxj zdF&(#nc+!3V~3NMvx~gXl6($7X^=UL%=SNee9WQy$xYcv?z2Ko>j7jBg;sieOrqB% z_h$rA$NHegIhow6p%%pjn#4=`6v>z!&GQY}5`5e0r z>t{M0OGZwZX|sm_oJ`^@{5SA7_jPe2~8oy+5v=|=WUrFT-8 zv9o?*=65`hqm=5g2Qj6*b`9)(26GS7#%o`KZGpY7Y!vM{ITXD`s5wKX-zm_6%qg#3 z2D_GnrnfS0ygat>0}WNbQ$|($1#F5ibCzh{?Sx*$Eb`i)$&~IDE1Ls5gS*O<_M9*?P@V+8fsSF~&~;c1>}+MTDB6og3zbcR{hqukrjgfP0~-tbT9`RY z=H?5~>C6tV-340&yHi;(l~>0k+PO;U8`SIvJ6)N!3hS#(S{=a!NN#6dbu>GLhPE-W zyrM#B=fD>9(wAr+{t%l3mntq&{8n+E;wOr86rWdoP;s2%Uo#MvGi_&!;Sp2t1H}h3 zOLz>8UnIt^R4uy4vr6Q{-XFx+&$9(bW|#1Ba8r)pLpde9_=g9Ju^;6M269XIoY;Jp znznN^ZL;FpT&XftE54;zks}RWrp?}`_@UxPK@<6%cEPVWN%3XH&lT4z)@4cm&(Z#0 zt$3s21B%Zp&QbhEakb(WMFje&8&%!BA$n=9R2-o=R`CwSNs5msKBM?jMhSv&>p3BG z?vjF*Ut;W9^(yezh-a3`Z}}@TN_iNl!CRu_dVWA z8KtRchVsHORXuqL9k1Rhz81F+&nQhJn{{Jzrg+=&zE*EF-b2!yeB4@0+{0wD( z%96x&cr(Q_i$rTQc4JnlT~2!Kp)7ORrFIMc7W@bB8}L^h!t$9@YMjN82rKl<5c?xU zz7VyJ6t)Y-moI+jgS0?aL+;NlWkp^Ai}6LNWth@U(0XY5NTt}NyOiyPZGk0lgV#=O_T>#_vc1nIs zy%tz~masLj9WWFsVf$cfU?^24(YFhz_zg6YRg=se0yY=M6^7jpn+-$45;g<&8kem$ zQ`oz(=U|9h!sfxAh9PbVTLya)#@N9&!XAZ%mF`oZ50~-aq^$>bS#94a-Y#fXTgN=iYg*7O<4R$?@I3%wO z7JxM>y9YKLMzoT5AM9EfDx0v09ZX_6QBU$(5_+0xD9EaK<<)}btcH_qdWl`6I79IU zK{JS-%QH&sPl|ICCo2vWG-tP+Xfw%H{7~@`#SwzW$JeK<61z$96U8SL$0%NiczKDf zKSA&ZMPxwnpH{p@@d`n6Ha{Tdl-Qk$%N0LRoUS-tu~hLQL3397aW=Qa?omWI8vh%` z*^1K@A5gqqairo^iWdl)v$@^omDonb9g5s+Nm;G9MDZKN&lNvZoTd1>;tPt?6rcGA zeEL`s>3l^+I?DvjuqG~gsa>NuM{$ZEGWJ~VQoB|0OT`xz?-n$}5q?Gc7Rgoc8^zZY zCkgT$hs%w!sN#2uZz?{dIF?~3wP;#{8x_A*d|UBR#oGkUaDKGSDYbhQd04^!TJc@Q zXA~zYjukY+qFl;Si{?DIS#gEpJjHhvS**!@LQ#gK)ZVE$M$inCVJNlDihC8eDsE8x zL2-rRB1IXJQu~GC$BOSM%D|M`cNE|LHKIh?%mCEb`co`&y8K`Uwkw7eLyEO0TLf)o zC{Q$2Y*MU0$)dH(&#YifQKkiLDmEmfbXn|AYYf-x-h} z!a-E;!H^)!Y7`sJ=)?)v4fU+((l>KM&%b&OmcBUi`BeHXRvTIjBYuI{<3d+vUh z>*B73_quDLyB4}@p}Q8kYvI%GS~&fG+gj)YlE;r>pMis(ZTXp02v5`^4rxvAIuduA-f*Xy ziSAnHu7&Pe=&prNx@)1k7Czywh5ycLp{xGts=vDGude#5tNxl{-L=qN3*EKwzjrOP z?XL2vdky|`V?g$V%3{~NgqWza{M~pSnxlQtftl5k$NqVVbhD=+KT$}wxspw>*qfbg zA0?X9d8ppyAsk)B99HY$XApUa@c0p$9tb+7l{6o z)J)(hGHmrIZ#MKn)oG_;utfYbhRzY9q%d?iMWoZTMHxCN*vt^@&X)b$*^x6!0$YyR zMr;eVnv9+7K4zR9+3asmN((ti1F_R2`-d|URJCkZ#ul0E5{_+#?Qvb~9%pwdhh%tnVw>TH0um~5copeUYy-9p+k$Pu z5;V3sg^R|}H)3nC^;l6RtHp{^Sq)Zn4fkOsMD`|Ap0KFGR%7>I>#)19qGYxc+lbwP zZ6+}EXavU1IcyP5V1(;UE}MAv&t_BbEfN~_P9ED_lY17S5sl}w0Vd=H#_dqbq}LJ} zKa+3?36Ejl$Nrv-ZfrVd8q=~pb_6N2$@vC5oSY(&COn9Yo3Pu+Sc-j_jDuv#|Amb4 zY%^_&ao8HtZonSP*uI3Fj2)gu&$i+mo#nBoAx_tjjPpDRUyzf*-g@?45-w->&&STi z7H4`)PZ7DRCgCO$C}~$?AIkLDqZ#%qu~V_fWS}9tMfcynCD?%gh$9o-h;D;1W|ZdhrJd1E%pQKd)PhL=Tq2~oxz`s zy&5|X`xN$C?5Ehvu^X}HCDUL8=j>#UJ%A`U1^YYfQP|@vegHimi&xij7@E6FnuYJEtKnyM09v ziJZFiNx@T);;34g5AmP)nCM!q`M>pmKnFZC$a)W3HwXe?)9>q@m z%en?pC~`Iz1lhK`{fF*B)Q%dL7qZE)*}JGmkX>MR^a{%U!JB&rMZ{x6QBZaT#@Nr0 znlV2Y1hZ_Yd4Yklqb-SsKa|UDS*=MUc48R1I*g`MZJUOy-~>lWZ+tt z;Lp1$B~V1HuEt+DAr;?lr1Z43KyOwm+W+MGv_LO}fTZGglhOk{>F_E_ype%xw^8Do zn==CF&owO~_4&*IqIAw`lE1xKd|O!@v@d4`x^oSdkhuXvk_gXZheIf1TB#U@9;@etM-^~;6 zXS}EK0(ta>N@n4W;@cWhKh76#1K!O10P9_QxhL6BlGcwCZU^G0G<2#1bFE7pYiMrGs`930J(WPB|qGI-Bfse?jxuLs{$XI_x zp^wP;d`J%;5?XH#>ggjioF{wx2#wgiMLt3!HnGTO&m}bClZt%i4_;I^N|P8=vD{s9gaU}n8wCrX+dM-eGYKm zD2aM-5#nT)Qw?=p*bz&{7Wlm!&8dHwb#)@OYqI+oC z3PYyVAes_s>={Cpm7Tl8MIqA|A7J}noC9oMk`b2b7qV@Fr1i7?L$1nI3OB0 z141^|$Mz1jHMK_CBdtxLraqPin`5bRY!quaPGfr}@q!3VcbY_Ju|OI-!gj+Tw$&|U zQ5o+_%SYS%kf_Xq(PL~L?Xy)Z$2!*LhUDoF`q%-% z_@Wp*=ikerW4%j|iHjp#pm(0tx%N*`wL%zbj}K)#uw| z$ZE^8$Fe(qo;@yT_vYG+V0iAU&pt9?;yn*cn>kO`#pNc)rUc{P&6>)Z=Zt%GL9d{# z%dtp4&3pRh{D66KK@WEN&#_&C&X-S)E(kayUt83j7IUog-3w!S2F!%73xakzYs|!= zfEo8mx1e2?WBUZmO`mmTd;J{SH(M52Mt1zqp^NC2=LGD_L<@vR`|N;yg>ZpX zixE*ha3fI@u(COJz%C?QI95cAolU&ZMAK#kz*Fjmn_@$G-s6B)@+VpPOg$2I&pmB5Ae&0;{bCEB8?t^Pl zqw4Q-hQ76r*zv^&-?bo0?D(8ZUu-0He9p!1ImC|7+_0P2@tKR)5IZcF^SjxjSSr3D zcwoy39`g1QJe1^7`Z%Bcg6Q#u&%12IjW^wL%k6iUjXs~H^-qjn3SIca@G+Tz__!zT zVr7@{N9}0HpGR{sr{3Me#}l>f;&aZqYLtY}QFRE*=o&s_FYM|=ctyfz+~9n6yw~u#4)0S9pX-O@(!*vApRu_<`(7e^F39$oyXY|S zb90u@jA_>Rxje&X?rf3xY3q!io73pgOw;uj@iT2)iqG7*^%wE8{#K9Ax%^YpeSZMO zSH8at#oB-Q0AlgxIrd`}FrBZsMDYj3a>Y%G8x=Pwu2)>Am?*hI{Vx^YQ+!$Ram9&> zH~2+dHLN14KP!Hv_^RUl9#LS8jSyouswnG+iVr4ea+Xim|Iy z@pXENNWr#^5M$@1q8e+=tzzsd6_1^qCepH7R9bdQx(Ls%%kazd_dR0ll1x9(-z{Us z@GR%&>E-94S$}xrGp0`)z`gz3N7s&M^F&>?m=jVCGJe!ZP?{@VcW$icJ z{K#M}y{4O=XPZUW6!>|zS#nKxKexWmh8Bu6Z0w~TemjgCVAJHD=pyob^V5wz{pQ~G z0o=G|4ejZ7M(-1m#VvRD^2e|JDUL9r6P`J=x8Hu{K~>wSQ>D=c#U{nDV(UL(i>4$n zQ6_ ziLqf-TdhhiXROR-F}&XkBGM|aN1Mf5#2 zC}*V5$7Q6Ptzzunta9FtTC>X;C{rQER_By6P))hz3{-S;ZaD*GD)TUQPkuS$RMWMb zaSC-S7jcw=a)ePDyO$${(o|S}7`^Es+O1}9&vHg6zN2Tk-O32HZ0(82UZuR4)buKM zHf^tDfZDeADvxifsI26%GhSUpNlzu?^O_2OzNa`(agpK=it7~DD6UdmuDDb&QSxi` zXDdFhI9c%)#cLJM_lhWKql%BNR-B_aMe#b1D3?ah7Gu|_nCZ)kqmo4NwCxNrcBzV> zK9G!1s)_o<*oCT;dQXZ-sJ5If#?DIRUEDaAim?k+baiB!NV5K@lB{FWMX+^ghN!nX zelhloOfMtUe1RDIV3wD;^!>mrFGJLRMK;DhlH+BFzRvYBM7w;sUWTas+B~n!rMvSn z_Svpp#^}RtUgpx5r*`u)mp(hSz{^~kcWQSpbLq`f3%!g}^no6z$1+k4BYS#nA4Y2B zg+0CI_$201blRysz0SaN4@0&7uf4qSQ}a9?=2GM1r}p;RVvk22ToJ}xo64)RC~hK0 z=C_}#u4cl2`4>JSp`y2`Wo|>zOoc^-nPDP?*g?^^R=GjZSM6nCO~1+$#-TJvG}``| z&+}^{oz4U}ke0!}j<^?NQMa1;@>Yh-C3XUC*n(W z)weYPo--xO@{mQ_l`yA!yg$A(+Zcq2OkskJZgpVfS*Jg}jV+8DK1WfH=hRq)Cs5KB z$DKrp6dQL!p>PkHF}Fv0X(`+8;ccQ&dOCDnZ_y8I@yL5ecj?qiy=j-ErX*f$x?9IT zu9+0=TQaXbe6R4YD3Ur+XcM0-9w|yxXh{jdcEhxADz7R9wj(bBbCgNoH>FZi+lY}) z8*%{B!>|@%_;Frx+PjO2rc4gejtvul<*fX2&G^8pY%f<(gh@lncEUs}wqDsb*j`wz zGEwFfY1e&Bq;ftf^Hl@3cRv#$)IIxI=vPdHT>qZe1lz48rO(X?M$a~L1kqt74<<(! zMm?CQgi1+{2Sc*kN-OjQR^7qsl!<<+9IruHGfb3T8oiXkD_dcWs)@RldUEawg;Y`9lp`>AQBQ4v zAzjM|3rh$ka}G?tavqV0AxegnRl%g!t;(8U(lchTNWeWl~fG zYMYsm34vOEI)O)p^Gidb>@MbLLK0Vow}!b_Ze^BAknlnetA~jwYg8HX>4|1`F#{PV zGDV{m6+)uq}L1+!G{aFNNl%25bWf z8ziBHB4$Dg@0a}i<(Q>G+TJZ~Z=}=&T51fE#J6iC@kn`@l!{=mmuNs!sDjteulW|W zvP%#N&m|Q49tqF4h-I&R8`cPWMwy6fi!|yaW!J+FhTWv>GT5oGp~B3WG6nwty@(0v zwT-arU}q?6fZYW`Y}h!=8oxQQ1`3ldxgR-h@2_yF=Lm*e$R}mC3?!4eULYIvpr_$|9@#gHU@Z z@u`ZrEPqhZo9&H)a33abLQ=MwqGu~xNX50V5z0iS_#@aOqRVP8q@JApSIW2{sXQ<8 z#^lX`9Y~u8sJbh?F*OMJ+CMUTy=I8a*AJ+9zfdb8>^H(bRrWRP1lS&?HX62Js_vAk z{h7^Py`eux?$yfVUU@F;1?IBX_*g$oT{?WDaQhg!)6skFg@`n?9C{j=S1^IS_AjtC zM8~trX2FKR)+s|78WBRN$qgF4-m|5@kC0u;4E9<-*|7tm?<*Za<{Jh4CfB6&+EfDi z7;?{1t>52En7-egFl0`9?Izf7VOxbc)8Agfj1Iz&W`=v6bGn}bzXV>!4EIKRc)qn; zQlRViPjPRo5LL&ar2Xa>Q`{RnKH1|rxX@!C=K!5k+-viDA-cSWr2TWe*6TgEx5o}4 z9y{iEe1X@SLeg8moa9e=3wooI+)UNZN$x+k$b)*{FDChJUnWVbemTki<|E~F3UyBM z36k*cuO@l?4?fx+z_PV}lCSfTWT#WFbCSQ{^U?R-trY5<^aH}(Y1u%NA{d024!u*k=;so720)Hw-N?MluJc(^b=JeUE9IFQYMP0lJ~VT z(MA>ajk47pY=JV-R27X=1bb76Hkjz229+&^i8ktDWs6{bIKL3i}a8U&s@+*A1}Glr_M3x3(WEtA%ZWp_@v{YFGvAU1hsqJ7BLXtAOo-O;@%F zRs};nn0g|7TLZgS*$=P~47FGCR>H!t(aILX+IdlQh9O_fQwzek{0LziGKGl{?GlP^ zRVE!>30tX5irvQ;p?zFLHeIf5L%nd3EPiI#6W zM~}kFm4(P#O34Mvs$e2gJ4@MCm|V0cmHi0Y3AQKqmc`*;o zXS2lc9&c_EUqyen=8Gq-f23Xn@7{cokzS3rud8_5@IF9d)DWbToh~mZ$W^quqo1BX%hw?0W(#yZX(5Qup((O$!^lM>nz$U}cwuMPgq|fNy z!fu0o1Zz+>6t)<9AxNYOTBb742Y!pfe7$t7!4 zhLo^<8-~s=_2l$|w1Nh37Xo__v;x{LlvhH2Yn4gcqI5o)*GW6do`*dOLv@&AGwNF2Dib&!WNPx@zNmV70AQn7?DmcgM2ELAS!>nL+T)oSwRDr z>dFO?Q=24J(7v34{{emk|F8IRahmc_fi@S(GEgV3y%#T*FWx73E$UU{HROw!^l-d* zSMg@xwW{~MA6(=b^ zsrZWG$BLr&T4E~|`QN>q5_=K9=$6GK;xffb zuD&P`i?Mfc$xH1sitj0|R;*_bO6@okHl}Q7<&&FzSO>|xKMF7SHIMZ zlR+qz|5pP=qPodc5epfN3$p0K8Cd{P<`Cn8ptk|yT zFa#Wt%TBH$R~^T`RqzQyOPhY z^DHPxS5+z57J(%AC3~rxRV7Q&;A6s=F4tYoWUq{_C%Wu8gHCW9iCR{`bmQ zx^vE*bMBmT=bS5}ox!GJXS#pxHTaK>0ofC(%P*8-MK5;XFaD6hFse2C--ex;4t zT$EGWJ8UGSk`r!AAS$UlV67dtrNf3ht(FQaqSQ)B(QB2~ere?>l+mNNbl6sGjC78p zy_2JN_ELH+y^qqK=(WbMqUS1dzqLt~cBe?mV5?Csadxn!dMno;RJj8IP7{ewO#9Lr z&emhUpq&=%GHiW^1ZWj@7koRm8e5KSz^=nKV^?D3_=~X8?{885WE*Y!1vV4=H*6Mm zHkQ53?My72b=z05Y@uymO<{9s`$|XcmpWQ|5gVqpm$7x&x3GJ$?_qahKf!LrevSPZ zy9&DwyA!(#<&|hM&99^39@s5d5iZ?}Elu*+9$b#svFCC*YO*{Y`!Sbef+y|4)7Zy- z$qu<565$hb4zV6{noVX4N@|)E>~C!vh;)ld$DU*}!4qv3yRot7B}#TEP@`>^OX_hp z4?Nc9gW0rou#MG!G;?7q$E=&|DKnEd(B@0YWoG<$sKQjHmDvR}J14!&E)gA=2hzz| zOS9WE%glrww7f8@%sj~Ea`x@)GV`?PBR`r`W?pmH6FGKwZkhdn{gC(Nm)Y+)n)IGO zwmo*bg}|s{M6p#dtk@)25JJbtQH(3b6x$UeifxLmiY?`rE9%Yf((}I^GSbRr#}Fl~|1^BEZU7H>o6Ro1&9160YG+Ay=+a)8A!Y zQHNFi*NC7oNWyYmk%#3nH>qH3G)t6Z!6-7xXptL?LvVdfV})sP{A8 zo$687e0RON%c(rCAdp47i*P>@*S1k|mU@kNuc=pu_ndnB@Saj{58h+y?Zlg+o*eIf z^|s+nP_F`S{QnD2>fWv8q`iC9+l%*rdbN0oV}$S?*R(KRqF17hN>8~8ay@3Nw;JzL z^?s5w9;Iizg@Sjew;S(4_2e{Xs29ikKribmsw^C(YML8KoS>dmnW0__-dvU9T#k3E z%5zG&Y3fO@KM~JF7vWA+ozL}n@2FSRm+st=EBdf8$$wepV>gic?TtC29~FxP;??&trU1vJQJ5=eK|HI(2JdFNqsp*qFQ zpQt-T2H3;~ON%?9l^1Xx7pgZL?_Bln z7G>A#^xU7J;I-;~i1!!u)=M+unO4zwy+FhATCJlYIGKFEdLQ7S3(Ejh;GHL)F<0Q0 zYBWBGcZGVNSf- z5(lA7VymLEth z@r)URd$We+)^jLuVInN2lUk}_+4^Tv&l#y?p9CNw%p zQd?QtpGy|6jivsl6!9Xm_K#PWJz4GIR7s4YvK&Y4FyY$eX`S9A_1HRAln_IT@tFJ5_YOtK%j+;)mN zDe+ia)y8FCz?V@IP9j9l9zBgQ_*9lRd;FiT5}m72BfjtAx~^WJ!FVkZMU0Z$933^%fIRjj58hjEFLbGUBe1 z$B?v6uTs6m#1R6Gu#Jac25L8{Fv=ln&dagTcM|k3OHjI8dS59VBUjXcEGv*T?DWW2spRQj}T!2=hn}V06Q4X<~dbF zSiqUO3@x}L0`ajkmbdo%{r5@SzzybLcfW|j<8SAwi| zNRp&VkWEMyPl9Y^ig@!`x16ihl_>i>RT3r2;wVevN|en{>-2`JCsDROUD71V#;Ye$ zw)Zr3CCXmPkZg&vE7X%H+mK0GBFbLKl>SPTU85dRWqHaNu-OU}qE=-nA;FJyWW zfoArlX5c0w?XnC{N2DzgFA-_O(>)!L)_80hZX(k59iQsyh_w34Qal}z_EK&Vu0~pH zcPVm2ouRh+cb<+=TlB9HYVuOo2aU41ql*0YjU+_moVHI#7yHe_?MZQ`eS2#mi`W~1 zK7LcyoE(ph@?SUK>o+&oro@x{&csFSnKst8cJ`#9{Sl7bnHo>=H(xvH zwYdwHFaP%Aw;sJ^=z)GSVpCfDB)_@#`}FvU2qP}Yz}Sy7;BYv>o8U9JEXZ#?)xo>Ch_yFj~v7&f?zccjteSPBn{ASX|esRv} zwq*yzi~P3Q^6!Vkk3NN)(fWCQ%Fg(C*!Bv z6Y(?c3320?EPFgjIjnI<3(aS7cMd0;hjZs3Nhy+)J~VAuOS%{loD}~Ee7+pSlO2y4 z%EzQLc*GB#X5*$M6hdJ(*5u*U(U{g`d6A4ZrG!{F;^8#jK#kLq!3(O1w`8#sy@`f% zc>Q(at&Mr1Bd~3}Bp*UM(blHIP#^0!bz4M1H>r}KaO$@dmzyoyS-4FE6>=8M&3zCi zOsR}-Dc_8QofB)=(N9XGR3?=>m5nBor72ufc>qF#+fyr3%EOx~tLs8dO`*DdyS5!f zYDHS*NeC8Jq+_@rRi22dV`k-VIAT`i2{fHuc|4d?c^sHqd91XPTX~F_yvm~y^6Of8 zl#s5KN6KNlRUSdj?v;nrR$=9#lGCH|5Sr{&DF@r$yYfKti={tXxA&=(u2%G`?1R~M zKxGk%f)(2bRQ8h8gDZPT>YIH)u= zu?%|xRok-cahMh&Vc*e}rW&nt(_VX6rL(Ic)?Kda&H+N89s(0O9d- znJ3_7lD~wo+D z>yKW0a@-{Y4@878Kh1Lz&Gbn3oai+J`(|M5!I_@lcWkAj!5q6InrwmO6ul0!fShX_Z;RmC*>D<4)vO& z(t3Cf@!FzZo`a~I)!Q?GLlqZ$`Xg6-RC*szKRS@t&%^1QRd|4>$m^WlFKK|Mm)AM7 zchW(g9$s@w!J(e+UNa#3a8JHBe&T@qq$539-q?vp9?(7gSWlN?+sD&C+NY>T*X-2a zpx}3g#~ii&bdOU~Z%^~YFKH!aJZ2orhu34$o*`&Frr%go{>uc7$6Q)T z(0I%-UlKGP=g4;)-V!h5Q1RqJb7Fbc`N5xZsK@DZ=XN4z^KCr_c;W}%_%-Ur@yN&i zu0@`Bs(13*{x%j{{J=Sd-8`{F{`^F+h2W|D;<-Eg7o2+X2?w8e@}R5loHln&{r}&= zBX=Ir%SGs}Mc&Z5!|>z0gYdN7^Y1y0g|-LFVn<$+GL^HXWOJb?^GaTf*AZf_FrLQa zW5O4$Uvi?no{%F#z@kUkl%S$>Owl&pRA>@LQR6g18@Z&hNUH5h8YNnIMKKvR77vF) zPKwP>)QidMN;*%BNc|#~FLwx-&=L<8-4@_LdaOERx>Xi4X4I^Xzs zr)fhtshy8~d;_#OG)*&nPIbbqOs^clyy_rcI|BOd4s*-Xi1EV|E$RyxzA2Ircn(L#xKaw6C;Hr9^}(?`aWl$bb8IBhsaoUk}F zOQB-P>rbIhC(?)4EfQ#l7sDoA45b_r($Z3Y-We@xZy$~~fY(SHIgocs8#zecFOjnA zMeZT;=GhoKRNg}yc}3|i@1cj=UT6RljL-5wyOl30VNbAULb%oJ97$T3wC$3nddxFO zxuL7It3AOU2-p%+6;fydg<6v&iSV?K^YHr!DLa#b=-Jkh@)M7_kCF0yQm{LX6RpNf zChaM%eRDEt5|ws2PrTDe*_s?gdl$iH6TOS1seH6F%aViLxEIN^nad;a#9ZF6Qi5H% zk)hgZ>TV$I)-3J=8@~QYl6eP3p_S*~MkdJase!<}lwdxh#iW+=D7-d}&j+834g_xc zE+v>JJ)#lkK62j2+3^4>x{s!kfu4= zOso$mG?zj@C0Tx2tIp&lYBYtOpio1SO`;nz{T`?JQS&IaI@x*{n>cAJsrfv`_9ojd zXatjF+U}s-*!i5nGAEfk_HLPyb13&3<#rGWyP4oz97>H}D2jfu6efyH)yF9GF@?4j zpfrg8OgdL8!WX=@+bMkG*JN!XYZh5=XP{?D&NgZ0zJ=s$fW;=kMt?!^4o}4L*VD)^xj!cyH7Q)t2?iI@YEfaM};{5A=yoyV)^%UDIm2O!?RufM`)wjY% z&WHU3tGkn0voaA1Y|GZnCzk6bKu0ae3fgK|)t&tV_Cv@z zBL9~DyqGVRR$B8c{ymGx*p<(Vy1Dt=tf2XUhjETN`b+#p=rt2iBR|fjNH<>X&BzaM z*B9X0+1Xs~_5ykRANhVx(AIGi=51cw3U}Xi_@Yh!IJ??ld3z7&Jm#d%@@BYY;7u6d1l8lAtH*?SSCF7!OmskE)FngC+mdA4)0he zuBOyItP>t{F0T!Vh2kC*7T2&)c+79O$tMRiX+@H>;_I;fz z#n>yzn%c2aj3;YQMdwm+_h7On>|ZK|XCS}Wu~a;Se{<(jasPR+75kTp>tQ!`EEQ8T zIoXb-;*lX_+_rzIxQ;AY91=^#qp*8AmI^a{PzExHyF1s4DVLJ@^8U4AY^Kj#(XxN7 zn40CY^E=iGGh+~$H}eFetHr|?z|P;LtHt=MC^tt}i*eUw`OFw6v0B`TKcphDTHK%Q zv$GSc#e>;CbB7V%PQksjF0ouZjz4IrtQXFxX@e*}T^0;y^b7cATufFBXN+W+Ki4Og zjK^~Lz2xV_k})~gXTRvBKgl1-|1=bo9Kk~VXEAn7cfp6dmmJQDd!88kRiWV6!ji+d z>z*aXKHH<@Q0{sciLqby6dc>LlQ4=;CDJ)_ z)_R<^VT#*C7ztt5R* z*87}d`<0~JYDjql=b~DYYKHXUSc|v{lX`P5*rjCM6elZuIa!y~lJq#v@7En@&APLD zmDrDPqC;_>;X1xV*6ndpw*0;)$8EuD8;W;HHJOj#oVN*Q0nXsHjKS{ndY~lv9bRlC z-dXb)o#({)5Q%thkB9d zxGKr}uzI!RR}CvH?GiLg`LJzg^}w}TVR(yp9=31v zDz!h6(lQFCo`lbPms-Q=Ik)#Ng|03VoKjTE^{FftoKalLRjVH%#=cWrYVT$cTCT@g zz{q`4Tx!Nd`g8noq`XhccchGJCdDZu%LYcE;O2M8XQ}1`-~SFnYKM!N|=x<>b6bryeEY2C2K1gdhn|k}yTWsA5>} zQgdBZqKy3*yMe5UF$tpwBx@&0D~n3)d`>aS6)=xU7|q2Q8}S?ctxF}`c1 zia2jcjb1fou*83JuNr%g9s*}M39Y?Rki^#at^wPN1VcqNT$@<2U{i4oSJmkw7%oPy zQm%h3P8);PT3lmljYLNsDJ`V5X$np~31MtR5}bdQ(9lt$y|aWQv{F(M>Lpr2Bt)=L zYy-y^E@36SNqA95+jP~okbaOX9Y@pzZfi{WzLR5@>jDMpnu|J(^JqYKXCOGpn+l7U0uNXV-bisM2Tc_mQ zw$rUCdHM`%&fa;3wRfILQ1fRdW==%j)lQ-VrS@Y*{>3n*)ZR%4SPB$3bNo_s6aRXb zTx#E-8>RL~j?cOh6Jy_?8>MzD-9S2)ZXm(S@tGBMV(e79QEHdajneBQbfffbx-o1o z-FS*F{ItIt3?3a|C>8mC&lCcY4sg{K`Jc*UBvUy)QmTq_6_AVN@T4fl=mfRs1nnp~ z^nt;r4_trxP*ZE@Lrs`I)WqpSBgcDqOBEPQ4?|+)euPd+WiZQujOs*J{E5_ss zkhlUQt^mouz5t0UCG1KGyHdigl&~u$>`Do{Qo`=rtNZrqzP-9{ukPEc`}XR-y}EC& z(_AUxe`_gWSL)G~dUT~8U8zS`>M_H*YoWUqx@+No?^-@8($dUHhTg>$CKshR#QJO?A*rU=&pXAS) z9`vyDPx^a=l|SkKkyifXd=P)sGwbFY4eyGwrTkfw?}&`%v3Plo$Z8&km#Z?H$K&Ow z>}EC+obqQ)mLr0kC*ox~2soMy=VU8?BH4M0O-Dk?ZpasLO)lb+u4QK84vs#r8;YED zbm+Z;GV^F7`)|J5z05q`OxLFsmYHd7oXz7s%FJ`^oZurp%gie=&iv6{W#$dpc0L&T-cl?VG-hhAGJ5;1plP|c zXBp@4k)Wv^-=mB(dsA_Hk1|g8xkAB>g=JiW>D>i??q0@)nO-3Hb3qwbYkD`qZQaTk zo>#kqX3zNiGDhfKK~p~=uZ%(ZM9{Q8kXyz$EfqA*lR0G!)s~zx#Ejm^22JgRtTIOH zOFTcSEY^a`4?dBkFLRnMSPHeR+L zZClnO*rm)&X~Az^QW*4<*_ni9-JJXN(3$E*IYCj)C9dlhXXBa)yYU;BW(70K%wrM! z@XE|!W|@754RxbyGlE$h8P{x1$2Ip`3%fRt9m+J=>5Yi){?DnoO!0gw$=i6iOp8aA=G~1qlH)G*0 zlWtEgb0)vLE!BEirPw=qV3`dnP7^faTZox5`=jFD6d%*rS%m)M`PXUeJk-x;{~(@e zzN(+kxm-hM{a_0GNkZp^{yuvpp%We1-$&@|x~QLz&{@#WN9a7+7c}kH_3;rpdoL~a z5hCB8Q{*FL<`nrD;wimB(>|t`k3kGw)6>U5?Y^{!kC7?ApwP#~Uv^G+A3G&~F{r@D z^`ASan~#h9VK*Pk&+M)~)}6QWeJnM98I-zO4I0mXDQX+g~z$ zC@F=;W%yWBVvnW!PO;{Lb5ebX>8!dm#fS7#?QO}vfynNBFvR0ScxR*E=VMt3Pxi5* zET5QquWr$J3?d&9_NhNU6l)AeqNsJ89BOY2w??9IRAA0PJEExs$%->W#cUo> zr-wWou<_JT5(`$-z)&&)*X#|YvP}IplqOjxhtiof;gdp{6hASW>CgTT>r)A2rdIqR_#JKtx*_dxQ=XrxmH@qijr^#$1$;qw#24OEVJ3r`R?VkF`hI z+FDyDrY*2X2V$e2npc5j$nk;LjZc5NwvO`-w2pi9&Ci!_sdrG)3OFNg9$yl$&z>1D z!D(j%*ns$jk*5XNr+C(wQv+;gJolD?fnN2=om)b0&@K7w8{!K700-K7j$j_;>Hze|_)3 z!NK@9Z%iK3BXC$Sw&0y7?ikrEa8xk%)tissJvujVT(JGSxo1E&Pr=Fgq| z`U}&Zc=(ZtUH&fwE6xl$PQ{|rgI$;l-=7vl?A+{JGLQ-7M7J*bZ7`K#+k8rp5p7<7 zQjmSy8`l3Om`xm&FFG!m%WZ1wl4FATEEhXh9u+LGPV?5KhX)H0-P-r#qCEe<4(gi*4+b9|7qCq=jCuU>n+jXAMM zTXRyIiJFO?=z+r)& zKJ$l>3>B?ja&Vy7XD%MjK>3^thsr?toO7=j!9Y>2o444nEz- zAZ@(vG#`T$pLNkdAA=N{HsrUyRO?hfG3aC;qf~zDNj^qt)os7=F-lvf3^~rnDD9no z;W0i&sreoMQ9ed#)3cWx?qif9U*9?S5FffsHSgXq=s+LCR5$mo^JUSCwXd3S-5^=r zV&PRY%dY78f4BD~@NpDpzTG{C?)$K;!?G>QI&Ez5%^_eNHiQkEWY^2f?#m|6uoyzZ zh9${+I|fE%!Imv)MjB}(2J9#?fv_@HNC0zVj3L~I0pHlxX{jY!vTn_E*ZY4}-7_QG zko@-j-d<_2dTOe^yQ+JtznT7jxPDEH?0f zpAOPY(QalQ<`}2oY@E8&dT4556g73yaRzF^=}9^ti-tlYqhn*vad}K0)ke+?yKO%h z(#N!mheBf^##W;qYRJY`(u)I^BYi9s!!AS_)Nx#$mJDgAGnT*>V>Fn|D)4p!!1tvz z9L!+FR2_8++l+Z=j7|h+qec!cY!g$c+%sz^I5IXF#LhpBx2CakkHf`oWNcz8h+Vic zJ~c5pH67G+cg#4P2&(V=`tF-@@F6ih`uUsf^|uwEp76wY-{E&&d~Dn1+ln>>@Acky z|AY2lP5k2i`y1--*?8McZd>IBb_-oR>YurIL#X!YJ$qK+&6X!^dsgsQ$DU;X+V`N` z%7q)WA2#iZ+p}bY_P_6cIbqMD4WXOvedM*IJryX=+5Bwkp3)7WA8g(ETKb-1)Z^Ur z%a^nE%-azBLAC$U*K+pcZV~QByBpg6^_ka7 z_QY+Nyw$bYx4r$D*B0$j>ZZK@Z9CeZeq|ZJ(=XYheQ<`YL4A^5)cM+@?LCHQuh^r# zehB5^mhaK`jmEHx@G^oDUwFaw`vO#`8A)Q5pgA~}!piq(ZyudYW2LA`(;Y(4A>+VM zHY?hrz1umR!{%cA$Z#Gjz}dWi0Hup^(fG74G?&fV6ME|~K|5!lXJ6-N2}{`%-23tI z)1h*fuqXKLfv#bFF>~yhe(U4I-9w>^*z|#8-2)>bH?~**_6hkuznXsy6Y`U5*RG`r zx&2z(TAGmCPy+-La?`DU5wjK(^8FjHj$4Zf`QhJQm9X}L4eZ+FwU~{Yuls81+EToJ z=j$13F&Q^~=bKk$t;J;Ae8b;-{VO?ZF&RI&^}FA^21T+l8Q=fOk8l3#ug+hKVnH|G z{?mW_+pA00#%-9o`TIA0_ph$KdhuH2+f&!A`_?zU{gV+5| zXKdA4?V1O$ds%_2#MXXH##;TSM==?AIe^JP0f&#V@^L9~AHZy^)xNqJ1!v0FYJchP z$84WgH9EhQ9W*y+O92h6-kqNWF(GTGulsSs;~$SPDkxGD=D(VF!T{)F`!l1~;||M6i^h z^x4!!43(YFgqEQ!wmyz~69?V}S28=jTg4Oz46SCNCx(Wn*09jCS4`j*M|<{)Dr{t4 zzOt$cH%qTxsZ_0|7h4rJJG=kPUWKjC^H(~mun~IV3TG8=vL1@9!X8O)iL1hnN!uD< zh5gfnE1?Rzso`4^tFX7~{+~%z*lB(Cm&sMQ(c67hN)?rPe&~vnD)t?0&8F^2t$@DyAESn^e(Zmt^|hxfu>1RSw0_cuz9AP{*F1&3_x}p5|J@Ik>A)39C>HTNI*;6d z&i|v5oWPanT(=vYpZt9ibo)?ZTN`U4(px`A@5g_I-fujsV{%*nz!enc(rzu{}p-z(0LT;{b_p|m`@im z8wPwYKgU*p!JXw$(E+S)JT*JHo;<^cBiQ@(`t*aGgGLVILW z;(7;Ebz#%r_s>Zvg`s~mj`q=iOr}~Fd%H`a_NEl7Yq4*l1d)H{`jqwB#^3j&fBJ#c z_1XiIbeA?*or0PPSVH>YAE8x)MWDZWWAb|Kh9_y&P5&I78;_&2?=G|+pxW#2-;xY) z`HyG`J$yZyo*{I0--XsKd$2>mMA$svEr_0g5`Y z>OP8JuiyPCR^7hX_1e83(W=v*+jJY|<%hpGLaT0g$XIoE-HCcK_wiMC@2%K--L222 zWw-Tj(Y|$(FT2`n(EiS8T6Wsa_u>XgU)ROg-K{@O!7lA6Uw8lfcW7U`pRc>>uc3X- zhhp9R5Iec6-{R};C)c5U%{yY<{nJfV-EH4Y z$L--Utf`3&w|*C!!0T~uADU2axeDzc;#NePRMpJ2dNtK0m{e5s`jM(+lQtDXu6`bO z_mg%NivDIlH61Dp`(ZydohtnB2GN{|QBi8=>n~%)jmN6kjb48O&Cxg&`_mfA)s4if z*s*@=b*wRcI6=iG_QxuK0S8ocoRC6grXVxk7)4)2a{0?L%Zg2)MnKu2U4gA z#!s;N*m$a{om%%I7MXqq%`^H4n%e0U6k4EVG(Cvc5I&aKL@KcV(W8%`=Ts1_5K*1M z^&a(MlToT+ibhA#H;fDsM$tEbI_^-17Vp!B&Aw^agFf8vj#Aeo$b!Vc$#m))oT9!{ zXpNEz-ZvCN-}GP-MkjH5JcP!GP8rlha0ao)rs7dNVj62~Di)0?R3;xE8dOwmU<4%$ zWR2DreO$#c{Vfz8LY*4iq4!)%t;f(B{Vuh3qNV*awI0Px+>gc;PvaDS_3})#kg?GZ zUWL|GucM`|L+hH4&>FoSt#2MiYx0L^{f|zxv^&xI9;Ssgpm8g*BB*v#rMst4g|SM% zAIY=yD*eje;PVotC*H^BWtINz6O{~~8@HTZ#FDG@n(4(X5k-HV#3yEz{#T#i6B8df zgBP;6D!ndv5sQHt&n`!)3;l18u0VYYedEYVl&;X$?xc^-D*Zatu#l_tJ4Y^N`c=QZ zl(8f3ST{Mp52bYU>tBYW>aEmz5v}n%sP#NrdLy-7fb)-{al;xP*{TQtu97Jop8m-$-pfy#4)=y8OrTNji^Avpk6&f|;XgrBV%QT!{uRlu8L*grN z9zyoR`Stq$>4Ed>^-cJ&!ZqjT*l;AGRL#q9e!YJC5jYPyLvVh*{!45LV_?Q!I1j!~ zIKN(RLWK+4dhNy+$az?S${DDZ;YYm+{ifY;zEe2Qre>j32;2;z=nESse@{^Z?hYHT zC=A0U@O~1n^kEQ0O&NV)io6=6)-+oBX|x7$uHu|S>r@C1o}mW5p`da!`VdY*HipI- zoN}3{wo%41AQldj1UO7ga2S;M5+E6+HZEXEAb`6h81B*t+{FlXM|6_FTRQdNEtWg- zx;Au5w62AXUh7(D zT??&i;eU;5p;ZOJs)AruL9nVISXB`I)T$s@SAOft{~v$lw`y@%Xr>mL>3@4P)93yW zZgcaOQ8YIBnw!3aBGB5{>bvj!6x%so@)uJC{6rCG^+sgmNHU8$D~Y0Q8>jI48k;;% z$EkmS;)!!9c5MGun+GyHjoz(|b)NR~QTV)`I#ARQg>Us3wZqnG}tEv4%68BoHQP_8CI_jlIz%uLu$Wz3QiV z@c@3Y`y3Ka1d)(t&)e8}J`!Vce2ulf`XtNf>Kut zkL#K|4c^ASn6MCoggo}PH2b!45_sGbO+v=Dg@dvM>j*((e0cwC#mj6z(7zL?^Rw;Sdq^NT604T;!YSsRi%YeQmw zI?vcWSsRk5yOYR1n_sr9st{IH2&*c@f5EB{U%Z(x?kBAc$&T$-h9oLm6HXZL3>Q8W z>2uLzWk@I`WMxQ7C@(WCg9-H0na5`gR;|(o2xdnyu7nGDQnv6ASfXKF!=O?K=IfKLzmXeWWNGYp4 z5FwH6{emPhnc|w0n_tM+>Ko^f#3cxlS-JT%ePxx;oR7qq9ZphO@zA*>VtO!v`C^Le zV;Pd@vZBbgK6r#GFPtJh6H`oKDXqBeTnaNRoT8+B9Z}e1*_06z7YzP7 zN@lk3xTLHSFu0}=gM>UzjSk9(5fUSAX9uOCJ%;6Qcf!}au*1fh{uh&ChQnelafi$Q zjwpmXe3B6o(_v#BEYqCPB=83$FcynCJcCEiCW#Fji`erM@Np-bJ=53(e)5D*ukwxO zP?)DijuHE>62%u9J8uCZ+v|uXF_#-f)>uoW6;C@@E-axLJUg=Hd3=W;Q511@L>3Cp zbi@s3$E83Z&yBK$NWy^=>$>72JK(1N6BYEBge~+pfxmJ5jp1(;e`oMVDDtQAHwX-Q z6&Uim8l=M%9meT!h7P9zF0axb3tmFU34R>m$5VitSDAJ(9Vht_=u&780)Sp+p*3_I z=f@#{(Svfe6&))&|K_z54P9lnML`3mJ2fhBK&1|_#&jxtKz}F_fYr%#Q5XS8=enk0 zQR^t1K&2Cy?oND_0UNaEPN!=nW6#o2pRRYffFAn!M!O5L9;c%|vDxN=xKC^@hO-p0qN*ins6LX4dL`+26_m+ZM!21%Wui_ANlwtR_)*ZrYuU!TRMely zF=%46*-|eG?-xJTz&@OHmhvdM<(ns%t@g(eIK_aRNYr zsJUV@YRwRSpjmxo3ED~8#V*#ZFdUzzV`y^_K=3Z@O@7=w%`SE6uhQYJzRO(93t(;+ z+lbchE_3O@#!Foq>$>w27iRS}I_mn4i(Np>P zLBGFgzAF!}AJois6#$>}#XIM?3UT@VIn`M~_hu@kqD?%takeWB7hz!RAM@n6lG$YF zxvef&xIf+VoQ!lU(Tl zGxfxyRKzG~j%-PEWuSD+{phG6riouCxH5sa_h>wnzow(-z3MnuHqaXH0?Dr?(J|nU zb)hgo=T>xhV6PV1wb4mY8EZq&-TPqIks1fZM|=4W(!C3d8iRI(Nf=}V?)?*hk{j+;W z{BNfKCOq^2I_r)u4J&t3VvFmUj_urOL@T;QR+pMc!|OCUSX7S zNyOq_f>V-~|I|6%$D_NQ=w9)^f ziZV*BknhEAoob1MYM%IXYCg({fM$1pAuDu+JWn49<+1s$(A^KceL9BZ;}@d zxoi9G9$ZKk;RN3K;V4QxU;>(+J}?=_mWf&-K?f>_Ot}NQjsR^QC-~1E+zUr>a&M%Q z_~X0y>HFzMI(^xT{3H(Dvxd&)P|X@w=+M5sez$x{WR7C0F+U3Mshx-z&4`T4PcnLF zE@Zt7g{u_ILLrV6e_V`NF^(`A$61(*KlFzpQ>+x1(K66yh70J}fP%k5v!gVQTdCIE zF_#187t3VYRlWViBRu56JN>)%Pb_D#aJ%K{PluMWBv-KJ!B>tauP*{eJdUZz@D|KmizA&1>%3VSCgKw$$gjwvG`q{SMzHv|u(d>-f zQM>(#7vKEkSpPV~h4oI9J&J@maS)$+RQqTpUg;EA$`sVV0tPHjjZ|G`@wl2}wsG2D zfKRYR7)fQ7IHs|395amYm7tx?L`9=zx(&^@7s6Kab&2g^T@${AH2PH_Y2QHx}$aL zov5?Z5KLqjp!Ur`5?s@_^e1B(;+mJjDsUpahL6W`l)@QEMyYkBmqY@d;S>MxIcypzf9w@ z8UVTy@O{DhdIdk%^)&^2O@ElGwb2*&W2jpHSgHW*+I}9prT`na%hOcv^ES468v@&i zDsaw2(xB>m+SAl%Qnf`>x!sIqNVu*}g6cP(y2h>EMi0pX)sC>NFx3R8+Id4V0a8$xef;i<33ZnDO?&E73{^ zL3W}U6Hq&I%A?8Xwl_}gI3i}i=jAIy%+pz9A&~70Pn6G?PB(DjUTJS%CjtU#H0y65 zY%M&x0VBEw8G!_3NT3?WOzq@RtM~Y64k)-DAA!t?sAUq>2ps4`)I3df6g5s(3$jSm zI2nbbmxrSc7oS>zP1GJ8TuwFsvJRY0J}kDG=ZP7$O#C{W!Oz~q5kL9j7E29e{+CO6?gm}&%WK5~wm?#SZEB8{hDSbuodqudNfBTK^0AMx+tr+Jk5 zGrS}!3jcm#Jx&z5WSn(s5)tYc^u8EA0|K7SHD{ffL_}8g0o%jh#FwlYe8WYW?`(;J z)zN9j6i_IbNdL@r4I)f-hhY;g1d+=apGLlNKr{rKo{HGC=`5QVGWihGE3Ssot0BrD zB>OFNbqig+_M8uyX)G>$C|fu+e@Zwtf6V5}yy|{Aij%bkv$kN7JqO>AT3fIgJ2h(y zCbq5C7R=g$xvebNd9nf477VwJe`*CatSuP7A-A$%B8z8b!FaOF%7Rg{&&q;{Wd8q$ zESQD#W+A=($0NO2tMGiQaN&Y^h55N(hQ9fSC@RVX#jLE1w3Ot;gmbl2lrAiqHzz;$ z%PHp0$)Ej)DDwUoicHv%bRIjdAv@;IfsD*_qKH4+((bQRlop#5sWT~TGU0P}UpA8> zAxefpvAW!lk)4?i8Rwx`R$fv(e{MnE?DJD3u`X>fkrWo>aMS09gJ_!dUJ0mUCT+uV6JcdPo-X+c5At}9^BJS|@IAf)7itl2j@O6Oo7`57j zI~h^}>(W=9<4-KD12Kb~q$6Uiu~gvCOp>S0d;UdB>2;rF?2MB*6Q=b3MO5t1Ie9Xr zc-Ez!8(CURhOwOFIWufnZdj3td|ii^8z-4*$;{v@a+Vawdh{zJwwxVWLDKb!xF|)P z9pYRUfyDM$FEV1# zZ!8RfZC6*jRp>9`(oaUro5Ai|U~sF5pjf{`Ifpj%F5t|4V5X<~X&wcP3C(p~Q?bE2 z5Z26*5ClqKF=y>Ro0Tt<#)SswV3?DpmZI+QFXW7B*kC&A0xbXqL_BCraSsr7o(L7q zmYDDoE^@$NBS=Z00Ai{kvxIZW?WZ|c*b7WyDc3b9m;_!QJ);jpXoME6I1C}lDmVJi%#>ty%kEJQfhq7HTPh4{**(UWG`z=zRC1-m zLMdOz2ucn0l1AA*ZiqlQ78#vm?i*>!_+?-bex@S3M@*af8eGijMkB5VYA*=On=<5G zz!}HZ@v8xyVV)(NX58#?dzSK!2~&34Qm_c4Lgc`O7`DO2AdkvLoOsIMUdG=|3)W?v zFUa3456f6#urv2cPOO1-B_Gw-wXfn0wr(|lU4x_Sz8FX5z68fh!EhNCMU`Y-#-o8K z1=^@}8Ru6O>oRU##>IZhx{TwpZC%D2`>fq~G;XoA8|P5beD`VX#?8AIYd0>tt=+i$ z-?ST#0?G)(heRWC`0On23lTX4PRX|f$ytaT0)(UhFfxGtIAP;b2;F4{lnF_s$8+^WD#!n_N7Cq7~G z=ZBdGXD|ya9U3Lxfrw_BB?BWT%z7%CmBaANDKa<*XK;$yE0XL6+Rf=GMuFe6fb$Iy z2$V4ftSK?;Vu(O;hA>0`QNj~gQ*3BE9p*BoAg4I2YnY=-8ScZ7g$B>i3?5jqFw7!5 zM)_Gliw3Y-B?gCVA0gWaQ;0&DQ33{v(wUi(O2g!%LN#AGX{83WY#$>xXt_X2S(MTV zp%gBZheeD>M3fr>#)LY4mR3ZuPZ{jgM`p?PNt3xU+%;u%RZ0%bg<_lJ!eZj$7I6~A zHf1=oILsnDG)P_y1|c;>=3jte8&{&??Gl4IM9fR0IG2XGnB&55`yzvx*_RoN%Z#z* z{FT{O@Yj_%%E~GnnX($kHK4v2p+SxX=L{Ri8KCr%UB#F(GhPhVyqu;$MD)g9U9;{o{Lu*004AP2O2>j zDENLuGsMqFKbI zDb3v6NmPs26q2nCzNh+$u98iG9og^$pd1KQut~o2^S1P0^mGNAs7LM?f?go%(Q-CH zsc}D84ky5nGB$1`ZoREw&(Fe5>HilX%$aEwB>w7Oj)ra(jY?RWf z^uQm?`W&K$7qSsbiPqQE!Z`o-9`ubAvon;_#J-%{@Y_CgjxAur%>j`x<`UoNNAE-t z8%8SA6bfDY5)tM3Y)GU zk`SUC){9&y#%o|Ryy4k%KK>NO^&uc8^x(!vq>VX6z~irL;uJf3KruL-#bh4|*#QSA zN!;oWv|?^r$b(;XCqqdl>k>Iv^1>j&^m+NDv~?z{`srXgldHRsOQ-K=Od=QqUx~8$ za6bu3V{)LAGVb`E$D|^Yi3;o4^B$=5)6*&JxRJMq-BEP3iZF#<-)sFTsy?D8Av1cy z%n=jeW|&_Kfgq&0Zt}Hvf^cvG=m_rP(S)!(fHeUkI7!j=92@}UsmXXIw|`DKW=flf zwNv0hhq!t0(4ptezEp%Z7|RYDDPJO{lr|i1X~rb`v8aTtO}@uE(y&5;PIgdyw$X)3K;nl0KKvEJ9j^FSzyy|GHVR19(u(^pVWE=p zmB0}WA+x&mViqheDZAj%8a55s_VVxPtJyRqIV+xB&8Fs2g7C`GRcwlqiKP`atJoyZ z`IRkxXC<3Jevo+nd~5}q;6TnG*|dU#1Fu>sr4oLVmQWit$8Cl`3vWnZ0 zxfn81doy1X^lk8ji#v(Q-(sf~prR6I>t7HQr zQI<6;qWjY06-+gft*}~n0h|83r<}OwRPxo)Xr}Q?kghacg-r`Ph{Q7qT9nEGO&E8eH>1F_U8iYceJI5Tpp}@;wV! z7m{Hl8J0$dg+WW6C}Og+3$iJL9yKmpDXXXh(yi?1q(mJh5znHam~S{UmmN1Uwv-`^ zkkE9~w3Pp4M_-LZ42QU-+;K=<@rzIfqx2_!%56}G-6y}e4H3!TdOw{}CZ4E|knBjXOgktH+~m^o z%G*z*GIt74F3$k9T*iI;Xo!HuwNsN(iwQ60Iihu#ei0p^>W~W$^doF!*L#9jukx*;LVDjtySQdsuApEJP@#L z07P~tTn7?+V;i>dIol>EnAJzK5`dr}dL>J=%0q4Rvz1&AT*-p2n#VpKTLZ*qsJSd`vO7g%T4;ReoL4MvEM2M!0DONv1b}~i zj1H$%tm`S2j>D>YF%ZZUI*uwTWa|U;!p3YW5YhEPdST;s6U+^_823>+00y z15kOq6{vfVpiUiY29Q{bw;Zw()XAPUF!`G5%3ywiI??wmhh%H^mm)j~>iEFhK4SGY z`byA|sE+Rklo|cF($}<*cZ{8YI3Nbr)qA|fyl1rY0U@#;2xAKnqC|D1=K&$MzO~8U zS_I9B>Y3i1z9uk{D1WPOKKxHqhx>L#Iss1xkZFA=Q61`g$uy%0sAYAYLQLAJMD=tZ zylf_OAc*=~fLc90kf@&O+f4&4+^h#`>wrTC(Dd;nb+8Yy!vIk1JYJyP5kMWlu(jbN zb$|+6pK|pb_RGwx^O*wf8r~Mt`2b)>c9T1pXuN+Q*XB{;r4kSZ%YvH2_Z# zt{R_z2Y|VQr<2vbj%^tAcx&szqX7cg1CaOlK(c!BXfwBw4hA!Zkpd>@Z?O0D??Zff zn7F}^zL^LBy$ASkeI!Ng?t25iV+IgNvO9{R4WQ!_gDGlP7eY<5d>#@XK*Fa_rvUZt zD7j8965-a`0E{*?0lqH@<_x8(C;Hy2t(Fqq~Oc3`^6wHh%aZH8>6Jxe{*w?Duw=3-kZR>BH{ z6!?l{hSDy%tt*jrC9h z;o>snLnp2&FU3m@p6fZfeg#ZkK*~ovsL^_BE;0YJ9k#{aK zMC1W}U@1Fuc#SCr@3WS$;oh~84q!SU+1Yz_xCbx+ndyt!>62eGx=1Rz(iX8(>b0Ve zYsvvo!3H{s-7re@rX-a!^^?npSl9|gbikba* zKx6jVNY4WB-T%@z9xL0KWk0M10%= z*4_CH9zBYy!DtS~7O}3E5G{)4c}8RAv5t_p(y$aW0kxTiT7e`nm!0VTwor|DfMC{H z1kOTs{3(D?K*M~RzT6zdmPdhG$AKgY*yn?{RX_{3BAOF^9l_| zFiAXYVIq4j`@H|hLKLav1|d)&BXx!>#h$~Ch8|qSrIF)Yr7)b+#R;8?o!Mrw!yo>& zA&@-e0gQHlTW7Z`wk&q2_wGf}G7$`+Tg*f(z#jlW08&W-Socu^|2M(b2^icR4c*T1 zyk7@|I(~|F7A!b`);TQrEGbs)If>gC0h`aU`sqHELw*y=B16eckbG_`+KSt8j;wE@ zOPM4cdpIOL6%RNIf1G3V=SVSX55Ud|ug(CZp9zW1Q0@{WVUMUM<%vB1BnCJ?6`HMUq_zb zr92`z#Nz@`e2$%8v!yy5I#QG;*>NKe6n^}kj4Cuxf`QQ6O&ETP9no|gdYRcQ2WG5n4c zc_54NTYvQgDjHDqsCENYp5V*MSP+6nR*&~TYsf)40IY~`h)v8|Kt8C)v1-J8hu6f6 z@IYIq+W*-31VyZ#BOOz*DCpK7c>;57&6G1o>N*)fwuF);$Z-gJcnFPR0gM6n;UjFq zkP@RH8!J>uG0%lUshe_W5JFQ$%iOHeKu%Ev0sDZniJx#t`-srYlrdN8q3jpM&WJBx zdJ%U3*}zu=Wyg@~I48Jlp41Bsl*bawg@D}JurR462A)l)aaxnD!cVF_fa(>raIp}` z#`)4oEH)C)4dgg*oLv-6m@jpi!ZB-Np;55}<9bje_3>*3Vu*wkEmfL$;lIp^q;69( zt^r}s4BiD&zpz>eb|Y^>xe%dufutg;5wXH0F;*<8O(HYLYgF(I5e(w)7E1$2G~o;- zTPe-MZRJA?rIUP`kXVA9;Nmf&70ODa!P>fQlv3l-6Ap5w66qAB04ZG~47|}$R4NS^ zTFB?HEQOUyrzsg`=r98wViV!3y;$qk=a=|!vOd48&o8kBzzpT$M>+_3t z9R|8DT^#5Z&AR`hyAbRC3!fLX1GetJtotwP{)^waTK8XHaR0@+TB31OIeKX{bSy!W zc3l^SbUh0UnM2fOOR=n{XHgiHcLwY$Vf!jFCwahBl!L4|hyRTW!@dfVu5WS(RKhZj zRwTey6?v8v!+H(~WLN`4cqW>$LE;H3GI53}4;zuXaM5~>RvOyh_R|q*Av^d&k1Jag=x*su+W(jf_V64Yo16=j!;Z;6GbPRR-?TfAh_;0#nQ@#c zDGxBFXDGUcQpe5snt{HJ zp>KxH=U0K$jjuE4J1Frj5Z1dfsisp%eDbfG5WjGcqb;$^1yT=vPs6`75)nR21ah9j zPQGsVG|iMSN9r}d-=eRK=nEizDwvqtg;Fm}-g6|kr*Kec6os`C{!8lN8}Yt zr|2sj{pY0-`jbhC9$Z zYvhP$T;+7jBnCZpdAUT#6%z5TI0y0?zdo1DKSKBbimD70RT;mfml&>E^t8I{ zK5RU#&L6h-(ZlofEI*#MHy&=U^ZK?O0Yrh!IXY@)fD62you30z17GFAw0%)b{84(KK>&tf6P~=s zv-E@ofJg8Bc$nVr*?Fu#R<(1kk2%*XgeYv-x19qHkglyjGr$x6wIjBRW#9ti4+vB ztG=<0!UAQ9oQDv^OijlLtXSB`{Sr(FrLPea2VKdW1sIiBP2`c{>)>mHyJWH;iew%p z&D)f6j*d=>0Su!c2#x^O207FUS5iT5$HbZzb)QAOjjvhZe;SW2TSupZ0<4a9T0BBF zW)!k{u(QT zDM50zO@W+gtP7~a7pEW~V{ldk^y+rSH%>CzVa^W!obV{pEUzdqV~h(BegS@q^V;Bu zP4LQY4P!vR;#$c=!Nwq0cefQ`Q)qYF!`&LB74tr}j-<$32Y{ZK12)-UlZM4o4hjdj zY;J%qF(+Egy%;-CCl3!0l30v;Ie6>CYXKu{ueAa&NzrlWwU{~oe^TXlC%n1Vk&(QOy?eS#1`BZvY z`O+620s=A|6XO9^&UuvE%u8JeSTN<6l@>Rifzf#8jN&*VpbruaTmn2PPdf0pJ|4oy^ZH;}(=r^(WRBj6>;ddLG}A6a z4IbPl!wjaCn@>2I0w!b&xHGczkZD^|3dd-GCOpCqE<9#$a4q_W-8ybFXRz%T1`6N= z9yMX1!AD+|S6thsGG?E_C&wHzVnK)t#0Y)ln#s7Z6c7W8|KYa|$q6g*53X@52{^z| zCxULUaqo&LCgl~*8BB7_Q7|FyJc@>qCKq>)*kU*j#gI7^nkvDQWZFjo5e60-Jh5R8 zGEI(=V>?XSgi9t%{v1pg!WbvT@Q9Ny%&Ek5@DOlkOa??RtR&$a`Q2eR1U;<>GK#{M z2x>c~Qv`~v>77AM3n6=!@F9sCV#CxUF$Bwd$q*wzkjz8GMbA!&lBzg+DJmu<%^N65 zZ1${FLt;d`94E&QQOps*Fl9Oy$8%Fg$dtHYnomp(?LS9 z(PH*8DFH3TR)Nso9NuN1SqYm7i`sULP8qmzcrb+}O73?N9a}Io8pwblG~d zdb*Lq0Zm=Ax0a|qf}9NU`V}AZ*qR-Uc28Q9x|Om$C|w7^oR$+SjdHWn!0MDHyO;Ts zeQ};nBv-cL{*Af)Q@yd$)Opga(#g`nD2r9t27hsFS$@UYexUrN2*e;QevC{kE}VALugmbWT? zhc~B5u9f}r79?PVK$565%XKVZ^U1Zg)|dwS))fD^8_6f0y8-2NO@MSOEsBS2vNhX5 zo8(KXWjw>9)J{{1XUlfzsZj!oSKe%Eae#1ka|lTw&;WNMpwuc*DsP7l5IMc}%~>t1 z+UCe) zae>(C*!EOk40;^xvFOPRq*lu;&aJGAX-{&?H|7K!ECKJ5+VkBqzLQNxdw#nUt!#HD z4uDYFZ%oX zU5A4etQ&ViG(z;oQ00EfTp9ND_$wMkr-UxxKQe0x#Mf{bE0 zeJCR{GmEHjhecF*O1@m6%&`^P=Q`(Q&6iV$`Mph;C`Xws=PG%&d|QESPHJJ?+!4A> zqB|knaW+0;OR^=~Q=F-}Y0POuzYYC1a5$2j$$2TvVMDJC)b@Bsf-^BADGo^z8}GHp z+T)z@aS5{$38T>Nw8uDb`&bY!+Z=#Ei6x&h|q8ytnWy+ z1!$-qnjRlH4Fte~kQ|h!C#NRH$3})v54z>Q2Kr;y8a3E=;^PNAE7XbJs@m7nd2HXFwnmTH*VyRs<@VTmm9CENj$=pnfAaQoZO9r? zq6zt%hUWUlMkJ$MYvR_C&A)S?jA&1BAV=anT zZ>mLtrCDhl1v?-0`nNX3wxl7O1XayY)u{XH@hcvW+$4LaFxXn_^Cf%Zn;mtwM#azS zZN7n459A`F+B-a&4=t=bVNbjSPfyP#@kfW3m$QG1pzwmr5wF2HW& zzY@W4-CNgt>w5pEalLmdcLAdJ6@amI?)WabLB#L&Qw8ueqJun z#)@MW*o$oFM}DIad5!`(f071f=j6=Fo$s7ypKIgY$ZzDyx$aYgDQDyq#N<2jlw4#pcrUUW=#|suw1Qm6YpHtB-rDT>xi|*BI`SG!Fj#zt)-Dz{o#q%A`7zdupQ0)1!@iFu)o)buASuttOc(fe0EN2!G7Y~f(OdBayLj10r%jM5Cid9Sf-!~?592Z9w1+c3E0#4f_aOd zydh0B+oRy80iMHubeI0(Tj?J1)eZRkZ zUmM*=G$R;>LNZg}Qo}b>BgH$yssNws{qBQ1xlYas2VtvVt=oj%F^9jVsS5Em&k*vLaZ;{jo3fB=`m>hR-vD2$OlA|^zIOvI|flp9=zcf>_( z0RUB*QH_%h;GT-J2>V)nxX~cLNRw&dS*qftL&U*7;?b{b-u4A7N;{sXURDMgg9;{m)GV#n=Dh3y+KZ+_%(=9JQHLf_l7?L#D!%^vQOWRTPgfV zARv2mlA}|AFwcg*S&{~0l-g0UyUmb+8@5*{Cc>oI60uK25aj?=9NoX+o{#t7=?#FV*#Zu{!`J?- z7>ejQ5GUFt5^>4vs9h%Ek(_lgXjMvV9q!uz*G2sog2u^~OHM#&Da`GB$-2T?S6J%` zYh7WjE39>ewf3;IZ?^WZ)*jZ{!`gr+{a^azf67{!9aPNcB?qd&i_f>7fDoliLKB%J`- z3GbbwWe7+M1Ezw>N$j(c`z|34fbGMchz5e~ND?^m9QDFrk>vz-pkvD_vWZ+EqX^&L z@Ih!1RymPqAutR@j$@yGaQosg3xMO1rh;MQcWO#X(%F{T;JcIU8+`B^lzQW)5!yVM zg!iU96s?GH_-(&zV;^a6-oAv`79s{nvk5j*W<Pv+-&PQ%R zoiH-Z2wy}WslpFk9oA5lB;Ic{ki6vqlqR3;PmMWG7=MiuyLKB{@kPh%n zBy@z8h;?Kv3vQd?6RC-j9nvB4pBrTaE}cSfUIsfooFT~t$4NS;ro}LWT)2VB!BZ9_ zKUV56XvuiYCe4aiqvUgJ3`Fusk?eR$&N;!|jELpSS&$lr3&9DJoZXp~$q9wM{7gKH zlB6a{a^@K_(6U`>2A#IYg-v84OmI#+r-_&IB>YAUy799x7>c`poI5tOEsX~lkz&Vh!O&VD*&!eSKtBvWEK2|gj}t#%s{o2kBG7OW Date: Sun, 9 Feb 2025 19:21:16 +0800 Subject: [PATCH 11/22] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86fish=E7=9A=84?= =?UTF-8?q?=E8=A1=A5=E5=85=A8=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/usr/share/fish/completions/aptss.fish | 216 ++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 pkg/usr/share/fish/completions/aptss.fish diff --git a/pkg/usr/share/fish/completions/aptss.fish b/pkg/usr/share/fish/completions/aptss.fish new file mode 100644 index 0000000..1c00f22 --- /dev/null +++ b/pkg/usr/share/fish/completions/aptss.fish @@ -0,0 +1,216 @@ +# 清除已有的 aptss 补全(如果有的话) +complete -c aptss -e + +# 禁用默认的文件补全 +complete -c aptss -f + +######################################################################## +# aptss Fish 补全脚本(中文说明版) +# +# 该脚本参考了 Debian 的 aptss bash 补全和 apt 的 Fish 补全, +# 并将子命令的说明翻译为中文。 +######################################################################## + +### 辅助函数 + +# 输出所有可用的包名(调用 apt-cache 并指定 aptss 的缓存目录) +function __fish_aptss_print_packages + apt-cache --no-generate pkgnames -o Dir::Cache="/var/lib/aptss/" 2>/dev/null +end + +# 输出所有可用的源包(从 apt-cache dumpavail 中提取 Source 字段) +function __fish_aptss_print_sources + apt-cache dumpavail -o Dir::Cache="/var/lib/aptss/" 2>/dev/null | \ + grep "^Source:" | cut -d' ' -f2 | sort -u +end + +# 输出 target-release 备选项(从 apt-cache policy 中提取) +function __fish_aptss_target_release + apt-cache policy -o Dir::Cache="/var/lib/aptss/" | \ + grep -oE 'a=[^,]*|n=[^,]*' | cut -d= -f2 | sort -u +end + +# 翻译子命令为中文说明 +function __fish_translate_aptss_cmd + switch $argv[1] + case ssupdate + echo "更新软件源" + case list + echo "列出软件包" + case search + echo "搜索软件包" + case show + echo "显示软件包信息" + case showsrc + echo "显示源包信息" + case install + echo "安装软件包" + case remove + echo "移除软件包" + case purge + echo "彻底移除软件包" + case autoremove + echo "自动移除不必要的软件包" + case update + echo "更新软件包列表" + case upgrade + echo "升级软件包" + case full-upgrade + echo "完全升级(可能移除其他软件包)" + case dist-upgrade + echo "发行版升级" + case edit-sources + echo "编辑软件源列表" + case help + echo "显示帮助信息" + case source + echo "下载源代码包" + case build-dep + echo "安装构建依赖" + case clean + echo "清除软件包缓存" + case autoclean + echo "自动清理旧缓存" + case download + echo "下载软件包" + case changelog + echo "显示更新日志" + case moo + echo "彩蛋" + case depends + echo "显示软件包依赖" + case rdepends + echo "显示软件包逆向依赖" + case policy + echo "显示软件包策略" + case '*' + echo $argv[1] + end +end + +### 定义各类子命令组 + +# 所有子命令列表 +set -g __aptss_commands ssupdate list search show showsrc install remove purge autoremove update upgrade full-upgrade dist-upgrade edit-sources help source build-dep clean autoclean download changelog moo depends rdepends policy + +# 需要补全包名的子命令(例如安装、显示、搜索等) +set -l __aptss_pkg_subcmds install show search download changelog depends rdepends + +# 需要补全“已安装”包的子命令(如 remove、purge、autoremove) +set -l __aptss_installed_pkg_subcmds remove purge autoremove + +# 需要补全源包的子命令(结合 apt-cache dumpavail) +set -l __aptss_src_pkg_subcmds source build-dep showsrc policy + +### 子命令补全 +# 当未输入子命令时,显示所有候选子命令,并使用中文说明 +for cmd in $__aptss_commands + set desc (__fish_translate_aptss_cmd $cmd) + complete -c aptss -a $cmd -d "$desc" -n "not __fish_seen_subcommand_from $__aptss_commands" +end + +### 公共选项(适用于一组子命令) +set -l group1 "install remove purge upgrade dist-upgrade full-upgrade autoremove" + +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l show-progress -d '显示进度' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l fix-broken -d '修复损坏的依赖' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l purge -d '清除配置文件' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l verbose-versions -d '显示详细版本' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l auto-remove -d '自动移除依赖' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -s s -l simulate -d '模拟/试运行' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l download -d '下载软件包' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l fix-missing -d '修复丢失文件' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l fix-policy -d '修复策略' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l ignore-hold -d '忽略锁定' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l force-yes -d '强制确认' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l trivial-only -d '仅处理简单情况' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l reinstall -d '重新安装' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l solver -d '使用求解器' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -s t -l target-release -d '目标版本' + +# 附加的 GENERIC 选项 +complete -c aptss -n "__fish_seen_subcommand_from $group1" -s d -l download-only -d '仅下载' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -s y -l assume-yes -d '默认确认' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -l assume-no -d '默认否定' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -s u -l show-upgraded -d '显示升级情况' +complete -c aptss -n "__fish_seen_subcommand_from $group1" -s m -l ignore-missing -d '忽略缺失' + +### 针对各个子命令的专用选项 + +# update 命令 +complete -c aptss -n "__fish_seen_subcommand_from update" -l list-cleanup -d '清理列表' +complete -c aptss -n "__fish_seen_subcommand_from update" -l print-uris -d '显示 URI' +complete -c aptss -n "__fish_seen_subcommand_from update" -l allow-insecure-repositories -d '允许不安全的仓库' + +# list 命令 +complete -c aptss -n "__fish_seen_subcommand_from list" -l installed -d '已安装的软件包' +complete -c aptss -n "__fish_seen_subcommand_from list" -l upgradable -d '可升级的软件包' +complete -c aptss -n "__fish_seen_subcommand_from list" -l manual-installed -d '手动安装的软件包' +complete -c aptss -n "__fish_seen_subcommand_from list" -s v -l verbose -d '详细模式' +complete -c aptss -n "__fish_seen_subcommand_from list" -s a -l all-versions -d '显示所有版本' +complete -c aptss -n "__fish_seen_subcommand_from list" -s t -l target-release -d '目标版本' + +# show 命令 +complete -c aptss -n "__fish_seen_subcommand_from show" -s a -l all-versions -d '显示所有版本' + +# depends 和 rdepends 命令(逐项添加各选项) +for opt in i important installed pre-depends depends recommends suggests replaces breaks conflicts enhances recurse implicit + complete -c aptss -n "__fish_seen_subcommand_from depends rdepends" -l $opt -d $opt +end +complete -c aptss -n "__fish_seen_subcommand_from depends rdepends" -s i -d '选项 -i' + +# search 命令 +complete -c aptss -n "__fish_seen_subcommand_from search" -s n -l names-only -d '仅匹配名称' +complete -c aptss -n "__fish_seen_subcommand_from search" -s f -l full -d '全文搜索' + +# showsrc 命令 +complete -c aptss -n "__fish_seen_subcommand_from showsrc" -l only-source -d '仅显示源代码' + +# source 命令 +complete -c aptss -n "__fish_seen_subcommand_from source" -s s -l simulate -d '模拟' +complete -c aptss -n "__fish_seen_subcommand_from source" -s b -l compile -d '编译/构建' +complete -c aptss -n "__fish_seen_subcommand_from source" -s P -l build-profiles -d '构建配置' +complete -c aptss -n "__fish_seen_subcommand_from source" -l diff-only -d '仅显示差异' +complete -c aptss -n "__fish_seen_subcommand_from source" -l debian-only -d '仅限 Debian' +complete -c aptss -n "__fish_seen_subcommand_from source" -l tar-only -d '仅打包 tar' +complete -c aptss -n "__fish_seen_subcommand_from source" -l dsc-only -d '仅下载 DSC' +complete -c aptss -n "__fish_seen_subcommand_from source" -s t -l target-release -d '目标版本' + +# build-dep 命令 +complete -c aptss -n "__fish_seen_subcommand_from build-dep" -s a -l host-architecture -d '主机架构' +complete -c aptss -n "__fish_seen_subcommand_from build-dep" -s s -l simulate -d '模拟' +complete -c aptss -n "__fish_seen_subcommand_from build-dep" -s P -l build-profiles -d '构建配置' +complete -c aptss -n "__fish_seen_subcommand_from build-dep" -s t -l target-release -d '目标版本' +complete -c aptss -n "__fish_seen_subcommand_from build-dep" -l purge -d '清除' +complete -c aptss -n "__fish_seen_subcommand_from build-dep" -l solver -d '求解依赖' + +# moo 命令 +complete -c aptss -n "__fish_seen_subcommand_from moo" -l color -d '彩蛋模式' + +# clean 和 autoclean 命令 +complete -c aptss -n "__fish_seen_subcommand_from clean autoclean" -s s -l simulate -d '模拟' + +### 针对 -t/--target-release 的特殊补全 +complete -c aptss -n ' + begin + set -l prev (commandline -poc | string trim) + test "$prev" = "-t" -o "$prev" = "--target-release" + end +' -a '(__fish_aptss_target_release)' -d '目标版本' + +### 包名补全 +# 对于需要包名参数的子命令,调用 __fish_aptss_print_packages +complete -c aptss -n "__fish_seen_subcommand_from $__aptss_pkg_subcmds" -a '(__fish_aptss_print_packages)' -d '软件包名称' + +# 对于 remove、purge、autoremove 命令,补全“已安装”包 +complete -c aptss -n "__fish_seen_subcommand_from $__aptss_installed_pkg_subcmds" -a '(__fish_aptss_print_packages)' -d '已安装软件包' + +# 对于 source、build-dep、showsrc、policy 命令,组合补全包名及源包名称 +complete -c aptss -n "__fish_seen_subcommand_from $__aptss_src_pkg_subcmds" -a '(__fish_aptss_print_packages; __fish_aptss_print_sources)' -d '源代码包' + +### 文件补全 +# 对于 install 命令:若参数看似为文件路径,则仅补全 .deb 文件 +complete -c aptss -n '__fish_seen_subcommand_from install; and test (string match -q "/*" (commandline -ct))' -a "(_filedir -X '*.deb')" -d 'Deb 安装包' + +# 对于 edit-sources 命令:补全 /etc/apt/sources.list 与 /etc/apt/sources.list.d 下的 .list 文件 +complete -c aptss -n "__fish_seen_subcommand_from edit-sources" -a "(__fish_complete_path /etc/apt/sources.list /etc/apt/sources.list.d/*.list)" -d '软件源文件' From eb88fc85c5c50b72848e0105207dfdf9b96f358a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B0=E6=AF=9B=E5=AE=9D=E8=B4=9D?= <530060699@qq.com> Date: Sun, 9 Feb 2025 12:26:36 +0000 Subject: [PATCH 12/22] =?UTF-8?q?update=20pkg/usr/share/fish/completions/a?= =?UTF-8?q?ptss.fish.=20=E6=B7=BB=E5=8A=A0=E4=BA=86=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E8=BD=AF=E4=BB=B6=E5=8C=85=E4=BF=A1=E6=81=AF=EF=BC=88=E8=99=BD?= =?UTF-8?q?=E7=84=B6=E5=8D=A1=E5=8D=A1=E7=9A=84=EF=BC=8C=E9=97=AE=E4=BA=86?= =?UTF-8?q?shenmo=E4=B9=9F=E6=B2=A1=E6=9C=89=E5=BC=80=E9=94=80=E6=AF=94?= =?UTF-8?q?=E8=BE=83=E5=B0=8F=E7=9A=84=E8=8E=B7=E5=8F=96=E8=BD=AF=E4=BB=B6?= =?UTF-8?q?=E5=8C=85=E7=AE=80=E4=BB=8B=E7=9A=84=E6=96=B9=E5=BC=8F=E5=96=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 新毛宝贝 <530060699@qq.com> --- pkg/usr/share/fish/completions/aptss.fish | 92 +++++++++++++++-------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/pkg/usr/share/fish/completions/aptss.fish b/pkg/usr/share/fish/completions/aptss.fish index 1c00f22..0bb08fa 100644 --- a/pkg/usr/share/fish/completions/aptss.fish +++ b/pkg/usr/share/fish/completions/aptss.fish @@ -1,36 +1,66 @@ # 清除已有的 aptss 补全(如果有的话) complete -c aptss -e -# 禁用默认的文件补全 +# 禁用默认的文件补全(避免显示当前目录文件) complete -c aptss -f ######################################################################## -# aptss Fish 补全脚本(中文说明版) +# aptss Fish 补全脚本(中文说明版,软件包补全显示简介) # -# 该脚本参考了 Debian 的 aptss bash 补全和 apt 的 Fish 补全, -# 并将子命令的说明翻译为中文。 +# 说明: +# 1. 子命令和选项的说明采用中文显示。 +# 2. 软件包补全部分不再调用 apt-cache,而是解析 aptss 自有的软件源索引文件, +# 从 /var/lib/aptss/lists/*Packages(或 *Sources)中提取软件包名称及简介信息。 +# +# 注意:如果你的 aptss 软件源索引文件位置或格式有变化,请相应修改下面的 awk 命令。 ######################################################################## ### 辅助函数 -# 输出所有可用的包名(调用 apt-cache 并指定 aptss 的缓存目录) +# 解析 /var/lib/aptss/lists/*Packages 文件,输出符合当前输入前缀的“软件包简介” function __fish_aptss_print_packages - apt-cache --no-generate pkgnames -o Dir::Cache="/var/lib/aptss/" 2>/dev/null + set cur (commandline -ct) + # 将所有匹配的 Packages 文件拼接后,用 awk 分段解析(RS="" 表示以空行为分段) + awk -v cur="$cur" ' + BEGIN { RS=""; FS="\n" } + { + pkg = ""; desc = ""; + for(i=1; i<=NF; i++){ + if($i ~ /^Package: /) { pkg = substr($i, 10) } # “Package: ”共9个字符 + else if($i ~ /^Description: /) { desc = substr($i, 14) } # “Description: ”共13个字符 + } + if(pkg != "" && (cur == "" || pkg ~ ("^" cur))) { + print pkg "\t" desc + } + } + ' /var/lib/aptss/lists/*Packages 2>/dev/null end -# 输出所有可用的源包(从 apt-cache dumpavail 中提取 Source 字段) -function __fish_aptss_print_sources - apt-cache dumpavail -o Dir::Cache="/var/lib/aptss/" 2>/dev/null | \ - grep "^Source:" | cut -d' ' -f2 | sort -u +# 解析已安装软件包(这里仍使用 dpkg-query,如果需要使用 aptss 数据,可另外构造) +function __fish_aptss_print_installed_packages + set cur (commandline -ct) + dpkg-query -W -f='${Package}\t${Description}\n' 2>/dev/null | grep -i "^$cur" end -# 输出 target-release 备选项(从 apt-cache policy 中提取) -function __fish_aptss_target_release - apt-cache policy -o Dir::Cache="/var/lib/aptss/" | \ - grep -oE 'a=[^,]*|n=[^,]*' | cut -d= -f2 | sort -u +# 解析 /var/lib/aptss/lists/*Sources 文件,输出源代码包信息(如果存在) +function __fish_aptss_print_source_packages + set cur (commandline -ct) + awk -v cur="$cur" ' + BEGIN { RS=""; FS="\n" } + { + pkg = ""; desc = ""; + for(i=1; i<=NF; i++){ + if($i ~ /^Package: /) { pkg = substr($i, 10) } + else if($i ~ /^Description: /) { desc = substr($i, 14) } + } + if(pkg != "" && (cur == "" || pkg ~ ("^" cur))) { + print pkg "\t" desc + } + } + ' /var/lib/aptss/lists/*Sources 2>/dev/null end -# 翻译子命令为中文说明 +# 翻译子命令为中文说明(用于补全时显示在括号内) function __fish_translate_aptss_cmd switch $argv[1] case ssupdate @@ -93,17 +123,17 @@ end # 所有子命令列表 set -g __aptss_commands ssupdate list search show showsrc install remove purge autoremove update upgrade full-upgrade dist-upgrade edit-sources help source build-dep clean autoclean download changelog moo depends rdepends policy -# 需要补全包名的子命令(例如安装、显示、搜索等) +# 需要补全二进制软件包名称的子命令(例如 install、show、search、download、changelog、depends、rdepends) set -l __aptss_pkg_subcmds install show search download changelog depends rdepends -# 需要补全“已安装”包的子命令(如 remove、purge、autoremove) +# 需要补全已安装软件包的子命令(例如 remove、purge、autoremove) set -l __aptss_installed_pkg_subcmds remove purge autoremove -# 需要补全源包的子命令(结合 apt-cache dumpavail) +# 需要补全源代码包的子命令(例如 source、build-dep、showsrc、policy) set -l __aptss_src_pkg_subcmds source build-dep showsrc policy ### 子命令补全 -# 当未输入子命令时,显示所有候选子命令,并使用中文说明 +# 未输入子命令时,显示所有候选子命令,并在括号中显示中文说明 for cmd in $__aptss_commands set desc (__fish_translate_aptss_cmd $cmd) complete -c aptss -a $cmd -d "$desc" -n "not __fish_seen_subcommand_from $__aptss_commands" @@ -198,19 +228,15 @@ complete -c aptss -n ' end ' -a '(__fish_aptss_target_release)' -d '目标版本' -### 包名补全 -# 对于需要包名参数的子命令,调用 __fish_aptss_print_packages -complete -c aptss -n "__fish_seen_subcommand_from $__aptss_pkg_subcmds" -a '(__fish_aptss_print_packages)' -d '软件包名称' +### 软件包补全 +# 对于需要二进制软件包名称的子命令,调用 __fish_aptss_print_packages, +# 输出的每一行格式为 "包名简介",Fish 会将 TAB 后内容显示为注释。 +complete -c aptss -n "__fish_seen_subcommand_from $__aptss_pkg_subcmds" -a '(__fish_aptss_print_packages)' -# 对于 remove、purge、autoremove 命令,补全“已安装”包 -complete -c aptss -n "__fish_seen_subcommand_from $__aptss_installed_pkg_subcmds" -a '(__fish_aptss_print_packages)' -d '已安装软件包' +# 对于 remove、purge、autoremove 命令,补全已安装的软件包(使用 dpkg-query 输出) +complete -c aptss -n "__fish_seen_subcommand_from $__aptss_installed_pkg_subcmds" -a '(__fish_aptss_print_installed_packages)' -d '已安装软件包' -# 对于 source、build-dep、showsrc、policy 命令,组合补全包名及源包名称 -complete -c aptss -n "__fish_seen_subcommand_from $__aptss_src_pkg_subcmds" -a '(__fish_aptss_print_packages; __fish_aptss_print_sources)' -d '源代码包' - -### 文件补全 -# 对于 install 命令:若参数看似为文件路径,则仅补全 .deb 文件 -complete -c aptss -n '__fish_seen_subcommand_from install; and test (string match -q "/*" (commandline -ct))' -a "(_filedir -X '*.deb')" -d 'Deb 安装包' - -# 对于 edit-sources 命令:补全 /etc/apt/sources.list 与 /etc/apt/sources.list.d 下的 .list 文件 -complete -c aptss -n "__fish_seen_subcommand_from edit-sources" -a "(__fish_complete_path /etc/apt/sources.list /etc/apt/sources.list.d/*.list)" -d '软件源文件' +# 对于 source、build-dep、showsrc、policy 命令,补全源代码包, +# 如果存在对应的 Sources 索引文件,则调用 __fish_aptss_print_source_packages, +# 否则可考虑默认使用二进制包的索引。 +complete -c aptss -n "__fish_seen_subcommand_from $__aptss_src_pkg_subcmds" -a '(__fish_aptss_print_source_packages)' -d '源代码包' From a3355516cfe7af85ad60b6b2758bb244ee7c41d4 Mon Sep 17 00:00:00 2001 From: shenmo Date: Sun, 9 Feb 2025 13:35:32 +0000 Subject: [PATCH 13/22] add: "autopurge" Signed-off-by: shenmo --- pkg/usr/share/bash-completion/completions/aptss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/usr/share/bash-completion/completions/aptss b/pkg/usr/share/bash-completion/completions/aptss index 087c51e..91e12ef 100644 --- a/pkg/usr/share/bash-completion/completions/aptss +++ b/pkg/usr/share/bash-completion/completions/aptss @@ -40,7 +40,7 @@ _aptss() "list" "search" "show" "showsrc" - "install" "remove" "purge" "autoremove" + "install" "remove" "purge" "autoremove" "autopurge" "update" "upgrade" "full-upgrade" "dist-upgrade" "edit-sources" From f03e032a1b8370be5599122229df01177ff02b31 Mon Sep 17 00:00:00 2001 From: shenmo Date: Thu, 13 Feb 2025 07:20:36 +0000 Subject: [PATCH 14/22] update debian/changelog. Signed-off-by: shenmo --- debian/changelog | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index d0eaf3b..3298903 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ spark-store (4.4.0) UNRELEASED; urgency=medium - * 支持从商店中直接启动 + * 支持从商店中直接启动应用 + * ssinstall 修复安装时不再指定版本号以避免出现问题 + * aptss支持fish补全 -- shenmo Tue, 24 Sep 2024 11:27:08 +0800 From 5a248859ecfebe39905e12df0daa7c360c3d722b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=E7=82=B3?= <15163222+cbing2002@user.noreply.gitee.com> Date: Sun, 16 Feb 2025 19:38:53 +0800 Subject: [PATCH 15/22] fix: ssinstall hash check failed in container --- tool/apt-fast-conf/aptss-apt.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tool/apt-fast-conf/aptss-apt.conf b/tool/apt-fast-conf/aptss-apt.conf index c7e4d07..a6e0d63 100644 --- a/tool/apt-fast-conf/aptss-apt.conf +++ b/tool/apt-fast-conf/aptss-apt.conf @@ -7,6 +7,8 @@ Dir::State::lists "/var/lib/aptss/lists/"; APT::Get::Fix-Broken true; APT::Get::List-Cleanup="0"; +Acquire::GzipIndexes "false"; + #clear APT::Update::Post-Invoke-Success; #clear DPkg::Post-Invoke; From f0830822c7790e456883a9b294591acc94c4f0b2 Mon Sep 17 00:00:00 2001 From: shenmo Date: Sun, 16 Feb 2025 17:49:57 +0000 Subject: [PATCH 16/22] update debian/changelog. Signed-off-by: shenmo --- debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/changelog b/debian/changelog index 3298903..89fffde 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,7 @@ spark-store (4.4.0) UNRELEASED; urgency=medium * 支持从商店中直接启动应用 * ssinstall 修复安装时不再指定版本号以避免出现问题 * aptss支持fish补全 + * 修复: distrobox下无法正常校验应用hash -- shenmo Tue, 24 Sep 2024 11:27:08 +0800 From 6fa0e11927675ae470a1a8c91e4b2814957d8f98 Mon Sep 17 00:00:00 2001 From: zty199 <46324746+zty199@users.noreply.github.com> Date: Mon, 17 Feb 2025 14:18:49 +0800 Subject: [PATCH 17/22] fix: wrong app name displayed when checking app upgrade grep Name[$locale] from .desktop directly might return from other sections like [Desktop Action xxx] instead of [Desktop entry] section only Log: modify awk params in ss-do-upgrade.sh and format the script --- tool/update-upgrade/ss-do-upgrade.sh | 112 +++++++++++++-------------- 1 file changed, 52 insertions(+), 60 deletions(-) diff --git a/tool/update-upgrade/ss-do-upgrade.sh b/tool/update-upgrade/ss-do-upgrade.sh index 80e6c90..dd78410 100755 --- a/tool/update-upgrade/ss-do-upgrade.sh +++ b/tool/update-upgrade/ss-do-upgrade.sh @@ -1,16 +1,16 @@ #!/bin/bash + if [ "$(id -u)" != "0" ] ; then - if [ "$IS_ACE_ENV" = "1" ];then - /opt/durapps/spark-store/bin/store-helper/pass-auth.sh "$0" "$@" - else - xhost + - pkexec "$0" "$@" - exit - fi + if [ "$IS_ACE_ENV" = "1" ] ; then + /opt/durapps/spark-store/bin/store-helper/pass-auth.sh "$0" "$@" + else + xhost + + pkexec "$0" "$@" + exit + fi fi - -trap "rm -f /tmp/spark-store/upgradeStatus.txt" EXIT +trap "rm -f /tmp/spark-store/upgradeStatus.txt" EXIT source /opt/durapps/spark-store/bin/bashimport/transhell.amber load_transhell_debug @@ -20,40 +20,41 @@ function get_name_from_desktop_file() { local name_i18n local package_name package_name=$1 - for desktop_file_path in $(dpkg -L "$package_name" |grep /usr/share/applications/ | awk '/\.desktop$/ {print}'); do -if [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "true" ] || [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "True" ];then - continue - else - name_orig=$(grep -m 1 '^Name=' "$desktop_file_path" | cut -d '=' -f 2) - name_i18n=$(grep -m 1 "^Name\[${LANGUAGE}\]\=" "$desktop_file_path" | cut -d '=' -f 2) - if [ -z "$name_i18n" ] ;then - app_name_in_desktop=$name_orig + for desktop_file_path in $(dpkg -L "$package_name" |grep /usr/share/applications/ | awk '/\.desktop$/ {print}') ; do + if [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "true" ] || [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "True" ] ; then + continue else - app_name_in_desktop=$name_i18n + name_orig=$(awk -F= '/^\[Desktop Entry\]$/ {found=1} found && /^Name=/ {print $2; exit} /^\[.*\]$/ && !/\[Desktop Entry\]/ {exit}' "$desktop_file_path") + name_i18n=$(awk -v lang="Name[$LANGUAGE]" -F= '/^\[Desktop Entry\]$/ {found=1} found && /^Name\[/ && $1 == lang {print $2; exit} /^\[.*\]$/ && !/\[Desktop Entry\]/ {exit}' "$desktop_file_path") + if [ -z "$name_i18n" ] ; then + app_name_in_desktop=$name_orig + else + app_name_in_desktop=$name_i18n + fi fi - - fi done - for desktop_file_path in $(dpkg -L "$package_name" |grep /opt/apps/$package_name/entries/applications | awk '/\.desktop$/ {print}'); do - if [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "true" ] || [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "True" ];then - continue - else - name_orig=$(grep -m 1 '^Name=' "$desktop_file_path" | cut -d '=' -f 2) - name_i18n=$(grep -m 1 "^Name\[${LANGUAGE}\]\=" "$desktop_file_path" | cut -d '=' -f 2) - if [ -z "$name_i18n" ] ;then - app_name_in_desktop=$name_orig - else - app_name_in_desktop=$name_i18n - fi - - fi - done -if [ -z "$app_name_in_desktop" ] ;then -app_name_in_desktop=${package_name} -fi -echo ${app_name_in_desktop} + for desktop_file_path in $(dpkg -L "$package_name" |grep /opt/apps/$package_name/entries/applications | awk '/\.desktop$/ {print}') ; do + if [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "true" ] || [ "$(grep -m 1 '^NoDisplay=' "$desktop_file_path" | cut -d '=' -f 2)" = "True" ] ; then + continue + else + name_orig=$(awk -F= '/^\[Desktop Entry\]$/ {found=1} found && /^Name=/ {print $2; exit} /^\[.*\]$/ && !/\[Desktop Entry\]/ {exit}' "$desktop_file_path") + name_i18n=$(awk -v lang="Name[$LANGUAGE]" -F= '/^\[Desktop Entry\]$/ {found=1} found && /^Name\[/ && $1 == lang {print $2; exit} /^\[.*\]$/ && !/\[Desktop Entry\]/ {exit}' "$desktop_file_path") + if [ -z "$name_i18n" ] ; then + app_name_in_desktop=$name_orig + else + app_name_in_desktop=$name_i18n + fi + fi + done + + if [ -z "$app_name_in_desktop" ] ; then + app_name_in_desktop=${package_name} + fi + + echo ${app_name_in_desktop} } + touch /tmp/spark-store/upgradeStatus.txt # 执行 apt update @@ -118,32 +119,23 @@ done) ## 如果没有选择,则直接退出 if [ -z "$PKG_UPGRADE_LIST" ] ; then zenity --info --text "${TRANSHELL_CONTENT_NO_APP_IS_CHOSEN}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg - else - ### 更新用户选择的应用 - - -(for PKG_UPGRADE in $PKG_UPGRADE_LIST; do - APP_UPGRADE="$(get_name_from_desktop_file $PKG_UPGRADE)" - update_transhell - - # 启动升级任务 - (pkexec /opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade-worker.sh upgrade-app $PKG_UPGRADE -y 2>&1 > /dev/null ) & - cmd_pid=$! - # 动态修改zenity的文本 - echo "# ${TRANSHELL_CONTENT_UPGRADING_PLEASE_WAIT}" - wait -done) | zenity --progress --auto-close --no-cancel --pulsate --text="Preparing..." --height 70 --width 400 --title="${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg - - - - + else ### 更新用户选择的应用 + (for PKG_UPGRADE in $PKG_UPGRADE_LIST ; do + APP_UPGRADE="$(get_name_from_desktop_file $PKG_UPGRADE)" + update_transhell + # 启动升级任务 + (pkexec /opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade-worker.sh upgrade-app $PKG_UPGRADE -y 2>&1 > /dev/null ) & + cmd_pid=$! + # 动态修改zenity的文本 + echo "# ${TRANSHELL_CONTENT_UPGRADING_PLEASE_WAIT}" + wait + done) | zenity --progress --auto-close --no-cancel --pulsate --text="Preparing..." --height 70 --width 400 --title="${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg #### 更新成功 if [ -z "`cat /tmp/spark-store-app-upgrade-status.txt`" ] ; then zenity --info --text "${TRANSHELL_CONTENT_CHOSEN_APP_UPGRADE_FINISHED}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg - else - #### 更新异常 + else #### 更新异常 zenity --error --text "${TRANSHELL_CONTENT_APP_UGRADE_PROCESS_ERROR_PRESS_CONFIRM_TO_CHECK}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 200 --width 350 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg zenity --text-info --filename=/tmp/spark-store-app-upgrade-log.txt --checkbox="${TRANSHELL_CONTENT_I_ALREDY_COPIED_THE_LOG_HERE_AND_WILL_USE_IT_TO_FEEDBACK}" --title="${TRANSHELL_CONTENT_FEEDBACK_CAN_BE_FOUND_IN_THE_SETTINGS}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg fi @@ -151,5 +143,5 @@ done) | zenity --progress --auto-close --no-cancel --pulsate --text="Preparing.. fi fi -rm -f /tmp/spark-store/upgradeStatus.txt +rm -f /tmp/spark-store/upgradeStatus.txt # 从最开头 From f02c279c8a42c2a48c78f340113d2447417385e8 Mon Sep 17 00:00:00 2001 From: shenmo Date: Mon, 24 Feb 2025 11:47:44 +0000 Subject: [PATCH 18/22] Sync aptss 4.5.0 Signed-off-by: shenmo --- tool/apt-fast/ss-apt-fast | 334 ++++++++++++++++++++++++++------------ 1 file changed, 234 insertions(+), 100 deletions(-) diff --git a/tool/apt-fast/ss-apt-fast b/tool/apt-fast/ss-apt-fast index 9f8217a..1734187 100755 --- a/tool/apt-fast/ss-apt-fast +++ b/tool/apt-fast/ss-apt-fast @@ -1,21 +1,25 @@ #!/bin/bash -# -# apt-fast v1.9 +# +# apt-fast v1.10.0 # Use this just like aptitude or apt-get for faster package downloading. # # Copyright: 2008-2012 Matt Parnell, http://www.mattparnell.com -# Improvements, maintenance, revisions - 2012, 2017-2018 Dominique Lasserre +# Improvements, maintenance, revisions - 2012, 2017-2019 Dominique Lasserre # # You may distribute this file under the terms of the GNU General # Public License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # + +shopt -s nullglob [ -n "$DEBUG" ] && set -xv # Print colored messages. # Usage: msg "message text" "message type" "optional: err" # Message types are 'normal', 'hint' or 'warning'. Warnings and messages with a # third argument are piped to stderr. + +THREADS=$(nproc 2>/dev/null || echo 4) msg(){ msg_options=() case "$2" in @@ -34,7 +38,7 @@ msg(){ } # Search for known options and decide if root privileges are needed. -root=1 # default value: we need root privileges +root=$# option= for argument in "$@"; do case "$argument" in @@ -70,7 +74,6 @@ TMP__APTMGR="${_APTMGR-${TMP_RANDOM}}" TMP_APTCACHE="${APTCACHE-${TMP_RANDOM}}" TMP_DLDIR="${DLDIR-${TMP_RANDOM}}" TMP_DLLIST="${DLLIST-${TMP_RANDOM}}" -TMP_LISTDIR="${LISTDIR-${TMP_RANDOM}}" TMP__MAXNUM="${MAXNUM-${TMP_RANDOM}}" TMP__MAXCONPERSRV="${MAXCONPERSRV-${TMP_RANDOM}}" TMP__SPLITCON="${SPLITCON-${TMP_RANDOM}}" @@ -78,6 +81,7 @@ TMP__MINSPLITSZ=${MINSPLITSZ-${TMP_RANDOM}} TMP__PIECEALGO=${PIECEALGO-${TMP_RANDOM}} TMP_aptfast_prefix="${aptfast_prefix-${TMP_RANDOM}}" TMP_APT_FAST_TIMEOUT="${APT_FAST_TIMEOUT-${TMP_RANDOM}}" +TMP_APT_FAST_APT_AUTH="${APT_FAST_APT_AUTH-${TMP_RANDOM}}" TMP_VERBOSE_OUTPUT="${VERBOSE_OUTPUT-${TMP_RANDOM}}" TMP_ftp_proxy="${ftp_proxy-${TMP_RANDOM}}" TMP_http_proxy="${http_proxy-${TMP_RANDOM}}" @@ -85,7 +89,7 @@ TMP_https_proxy="${https_proxy-${TMP_RANDOM}}" # Check for proper privileges. # Call explicitly with environment variables to get them into root conext. -if [ "$root" = 1 ] && [ "$UID" != 0 ]; then +if [ "$root" -ne 0 ] && [ "$UID" != 0 ]; then exec sudo DEBUG="$DEBUG" \ LCK_FILE="$TMP_LCK_FILE" \ DOWNLOADBEFORE="$TMP_DOWNLOADBEFORE" \ @@ -93,7 +97,6 @@ if [ "$root" = 1 ] && [ "$UID" != 0 ]; then APTCACHE="$TMP_APTCACHE" \ DLDIR="$TMP_DLDIR" \ DLLIST="$TMP_DLLIST" \ - LISTDIR="$TMP_LISTDIR" \ _MAXNUM="$TMP__MAXNUM" \ _MAXCONPERSRV="$TMP__MAXCONPERSRV" \ _SPLITCON="$TMP__SPLITCON" \ @@ -101,6 +104,7 @@ if [ "$root" = 1 ] && [ "$UID" != 0 ]; then _PIECEALGO="$TMP__PIECEALGO" \ aptfast_prefix="$TMP_aptfast_prefix" \ APT_FAST_TIMEOUT="$TMP_APT_FAST_TIMEOUT" \ + APT_FAST_APT_AUTH="$TMP_APT_FAST_APT_AUTH" \ VERBOSE_OUTPUT="$TMP_VERBOSE_OUTPUT" \ ftp_proxy="$TMP_ftp_proxy" \ http_proxy="$TMP_http_proxy" \ @@ -108,7 +112,6 @@ if [ "$root" = 1 ] && [ "$UID" != 0 ]; then "$0" "$@" fi - # Define lockfile. # Use /tmp as directory because everybody (not only root) has to have write # permissions. @@ -123,27 +126,39 @@ LCK_FD=99 # Set default package manager, APT cache, temporary download dir, # temporary download list file, and maximal parallel downloads -_APTMGR=apt-get +_APTMGR=apt-get eval "$(apt-config shell APTCACHE Dir::Cache::archives/d)" # Check if APT config option Dir::Cache::archives::apt-fast-partial is set. eval "$(apt-config shell apt_fast_partial Dir::Cache::archives::apt-fast-partial/d)" if [ -z "$apt_fast_partial" ]; then - eval "$(apt-config -o Dir::Cache::archives::apt-fast-partial=apt-fast shell DLDIR Dir::Cache::archives::apt-fast-partial/d)" + DLDIR="$(realpath "${APTCACHE}/../apt-fast")" else - eval "$(apt-config shell DLDIR Dir::Cache::archives::apt-fast-partial/d)" + DLDIR="${apt_fast_partial}" fi -# Currently not needed. -eval "$(apt-config shell LISTDIR Dir::State::lists/d)" + +# Check for apt auth files +eval "$(apt-config shell NETRC Dir::Etc::netrc/f)" +eval "$(apt-config shell NETRCDIR Dir::Etc::netrcparts/d)" +APTAUTHFILES=() +if [ -f "$NETRC" ]; then + APTAUTHFILES=("$NETRC") +fi +APTAUTHFILES+=("$NETRCDIR"*) + if [ "$IS_ACE_ENV" != "" ];then DLLIST="/tmp/apt-fast-in-container.list" else DLLIST="/tmp/apt-fast.list" fi + + + _MAXNUM=5 _MAXCONPERSRV=10 _SPLITCON=8 _MINSPLITSZ="1M" _PIECEALGO="default" +MIRRORS=() # Prefix in front of apt-fast output: aptfast_prefix= @@ -162,6 +177,9 @@ APT_FAST_TIMEOUT=60 # Ask for download confirmation if unset DOWNLOADBEFORE= +# Enable APT authentication support +APT_FAST_APT_AUTH=1 + # Formatted package list in download confirmation if unset VERBOSE_OUTPUT= @@ -188,7 +206,6 @@ https_proxy= [ "$TMP_APTCACHE" = "$TMP_RANDOM" ] || APTCACHE="$TMP_APTCACHE" [ "$TMP_DLDIR" = "$TMP_RANDOM" ] || DLDIR="$TMP_DLDIR" [ "$TMP_DLLIST" = "$TMP_RANDOM" ] || DLLIST="$TMP_DLLIST" -[ "$TMP_LISTDIR" = "$TMP_RANDOM" ] || LISTDIR="$TMP_LISTDIR" [ "$TMP__MAXNUM" = "$TMP_RANDOM" ] || _MAXNUM="$TMP__MAXNUM" [ "$TMP__MAXCONPERSRV" = "$TMP_RANDOM" ] || _MAXCONPERSRV="$TMP__MAXCONPERSRV" [ "$TMP__SPLITCON" = "$TMP_RANDOM" ] || _SPLITCON="$TMP__SPLITCON" @@ -196,6 +213,7 @@ https_proxy= [ "$TMP__PIECEALGO" = "$TMP_RANDOM" ] || _PIECEALGO="$TMP__PIECEALGO" [ "$TMP_aptfast_prefix" = "$TMP_RANDOM" ] || aptfast_prefix="$TMP_aptfast_prefix" [ "$TMP_APT_FAST_TIMEOUT" = "$TMP_RANDOM" ] || APT_FAST_TIMEOUT="$TMP_APT_FAST_TIMEOUT" +[ "$TMP_APT_FAST_APT_AUTH" = "$TMP_RANDOM" ] || APT_FAST_APT_AUTH="$TMP_APT_FAST_APT_AUTH" [ "$TMP_VERBOSE_OUTPUT" = "$TMP_RANDOM" ] || VERBOSE_OUTPUT="$TMP_VERBOSE_OUTPUT" [ "$TMP_ftp_proxy" = "$TMP_RANDOM" ] || ftp_proxy="$TMP_ftp_proxy" [ "$TMP_http_proxy" = "$TMP_RANDOM" ] || http_proxy="$TMP_http_proxy" @@ -279,7 +297,8 @@ cleanup_dllist() cleanup_aptfast() { - [ "$CLEANUP_STATE" -eq 0 ] && CLEANUP_STATE=$? + local last_exit_code=$? + [ "$CLEANUP_STATE" -eq 0 ] && CLEANUP_STATE=$last_exit_code cleanup_dllist _remove_lock } @@ -308,7 +327,7 @@ get_mirrors(){ for mirror in "${mirrors[@]}"; do # Real expension. if [[ "$1" == "$mirror"* ]]; then - filepath=${1#${mirror}} + filepath="${1#"${mirror}"}" # Build list for aria download list. list="${mirrors[*]:1}" echo -e "${list// /${filepath}\\t}$filepath\n" @@ -319,67 +338,128 @@ get_mirrors(){ # No other mirrors found. echo "$1" } + ##########SPARK ADJUST: END + +AUTH_INFO_PARSED=() +# Parse apt authentication files. +# Undefined behavior on whitespaces in host, username or password. +prepare_auth(){ + if [ "$APT_FAST_APT_AUTH" -eq 0 ]; then + return + fi + for auth_file in "${APTAUTHFILES[@]}"; do + # auth files have netrc syntax, possible multiline entries starting with "machine" + auth_info="$(tr '\n' ' ' < "$auth_file" | sed 's/\(\\)/\n\1/g' | sed '1d')" + while IFS= read -r auth; do + machine="$(echo "$auth" | sed 's/.*\[ \t]\+\([^ \t]\+\).*/\1/')" + login="$(echo "$auth" | sed 's/.*\[ \t]\+\([^ \t]\+\).*/\1/')" + password="$(echo "$auth" | sed 's/.*\[ \t]\+\([^ \t]\+\).*/\1/')" + # if machine does not have protocol, try https:// + if ! [[ "$machine" =~ ^.*:// ]]; then + machine="https://$machine" + fi + if [ -z "$machine" ] || [ -z "$login" ] || [ -z "$password" ]; then + msg "Could not parse apt authentication (skipping): $auth ($auth_file)" "warning" + continue + fi + # use space separated string to convert back to array later + AUTH_INFO_PARSED+=("$machine $login $password") + done <<< "$auth_info" + done +} + +# Gets URI as parameter and tries to add basic http credentials. Will fail on +# credentials that contain characters that need URL-encoding. +get_auth(){ + if [ "$APT_FAST_APT_AUTH" -eq 0 ]; then + echo "$1" + return + fi + for auth_info in "${AUTH_INFO_PARSED[@]}"; do + # convert to array, don't escape variable here + auth_info_arr=($auth_info) + machine="${auth_info_arr[0]}" + # takes first match + if [[ "$1" == "$machine"* ]]; then + login="${auth_info_arr[1]}" + password="${auth_info_arr[2]}" + uri="$(echo "$1" | sed "s|^\([^:]\+://\)|\1$login:$password@|")" + echo "$uri" + return + fi + done + echo "$1" +} + # Globals to save package name, version, size and overall size. DOWNLOAD_DISPLAY= DOWNLOAD_SIZE=0 +# 获取包的URI # Get the package URLs. get_uris(){ - if [ ! -d "$(dirname "$DLLIST")" ] then if ! mkdir -p -- "$(dirname "$DLLIST")" then msg "Could not create download file directory." "warning" - msg "无法创建下载文件夹" "warning" - exit 1 + msg "无法创建下载目录" "warning" + CLEANUP_STATE=1 + exit fi elif [ -f "$DLLIST" ]; then if ! rm -f -- "$DLLIST" 2>/dev/null && ! touch -- "$DLLIST" 2>/dev/null then - msg "Unable to write to download file. Try restarting with root permissions or run 'aptss clean' first." "warning" + msg "Unable to write to download file. Try restarting with root permissions or run 'apt-fast clean' first." "warning" msg "无法下载文件。尝试使用root权限,或者运行 'aptss clean'" "warning" - exit 1 + CLEANUP_STATE=1 + exit fi fi # Add header to overwrite file. echo "# apt-fast mirror list: $(date)" > "$DLLIST" - #NOTE: aptitude doesn't have this functionality, so we use apt-get to get - # package URIs. -# case "$_APTMGR" in -# apt|apt-get) uri_mgr=$_APTMGR;; -# *) uri_mgr=apt-get;; -# esac - # NOTE:apt可能出现变动,不建议在脚本中使用,因此在此统一改用apt-get -uri_mgr=apt-get + # NOTE: "aptitude" doesn't have this functionality + # so we use "${_APTMGR}" to get package URI's + case "$(basename "${_APTMGR}")" in + 'apt'|'apt-get') uri_mgr="${_APTMGR}";; + *) uri_mgr='apt-get';; + esac uris_full="$("$uri_mgr" "${APT_SCRIPT_WARNING[@]}" -y --print-uris "$@")" - uris_full_ret="$?" - if [ "$uris_full_ret" -ne 0 ] + CLEANUP_STATE="$?" + if [ "$CLEANUP_STATE" -ne 0 ] then msg "Package manager quit with exit code.Here is the log" "warning" msg "包管理器以错误代码退出.日志如下" "warning" msg "${uris_full}" - exit "$uris_full_ret" + exit"$CLEANUP_STATE" fi - while read -r pkg_uri_info - do - [ -z "$pkg_uri_info" ] && continue - ## --print-uris format is: - # 'fileurl' filename filesize checksum_hint:filechecksum - uri="$(echo "$pkg_uri_info" | cut -d' ' -f1 | tr -d "'")" - filename="$(echo "$pkg_uri_info" | cut -d' ' -f2)" - filesize="$(echo "$pkg_uri_info" | cut -d' ' -f3)" - checksum_string="$(echo "$pkg_uri_info" | cut -d' ' -f4)" - hash_algo="$(echo "$checksum_string" | cut -d':' -f1)" - checksum="$(echo "$checksum_string" | cut -d':' -f2)" + prepare_auth + local tmpdir=$(mktemp -d) || { + msg "Failed to create tmp dir" "warning" + msg "无法创建临时目录" "warning" + exit 1 + } + ## --print-uris format is: + # 'fileurl' filename filesize checksum_hint:filechecksum + process_package() { + local pkg_uri_info="$@" + + local display_line="" # 添加局部变量并初始化为空 + + IFS=' ' read -r uri filename filesize checksum_string _ <<<"$pkg_uri_info" + [ -z "$uri" ] && continue + uri="$(get_auth "${uri//"'"/}")" + IFS=':' read -r hash_algo checksum _ <<<"$checksum_string" filename_decoded="$(urldecode "$filename")" - DOWNLOAD_DISPLAY="${DOWNLOAD_DISPLAY}$(echo "$filename_decoded" | cut -d'_' -f1)" - DOWNLOAD_DISPLAY="${DOWNLOAD_DISPLAY} $(echo "$filename_decoded" | cut -d'_' -f2)" - DOWNLOAD_DISPLAY="${DOWNLOAD_DISPLAY} $(echo "$filesize" | numfmt --to=iec-i --suffix=B)\n" - DOWNLOAD_SIZE=$((DOWNLOAD_SIZE + filesize)) + IFS='_' read -r pkg_name_decoded pkg_version_decoded _ <<<"$filename_decoded" + + + + display_line="${display_line}$pkg_name_decoded $pkg_version_decoded" + display_line="${display_line} $(echo "$filesize" | numfmt --to=iec-i --suffix=B)\n" ## whole uri comes encoded (urlencoded). Filename must NOT be decoded because # plain aptitude do not decode it when download and install it. Therefore, we @@ -397,40 +477,44 @@ uri_mgr=apt-get *) hash_algo= esac - # Using apt-cache show package=version to ensure recover single and + + # Using apt-cache show package=version to ensure recover single and # correct package version. # Warning: assuming that package naming uses '_' as field separator. - # Therefore, this code expects package-name_version_arch.deb Otherways + # Therefore, this code expects package-name_version_arch.deb Otherwise # below code will fail resoundingly if [ -z "$hash_algo" ]; then - pkg_name="$(echo "$filename" | cut -d'_' -f1)" - pkg_version="$(echo "$filename" | cut -d'_' -f2)" - pkg_version="$(urldecode "$pkg_version")" - package_info="$(apt-cache show "$pkg_name=$pkg_version")" + IFS='_' read -r pkg_name _ <<<"$filename" + pkg_version="$pkg_version_decoded" + # Transform multi-line field output from apt-cache to single line and sort checksums, strongest first + package_info="$(apt-cache show "$pkg_name=$pkg_version" | sed ':r;$!{N;br};s/\n / /g' | sort -r)" - patch_checksum= - if [ -n "$SHA512_SUPPORTED" ]; then - patch_checksum="$(echo "$package_info" | grep SHA512 | head -n 1)" - [ -n "$patch_checksum" ] && hash_algo="sha-512" - fi - if [ -z "$patch_checksum" ] && [ -n "$SHA256_SUPPORTED" ]; then - patch_checksum="$(echo "$package_info" | grep SHA256 | head -n 1)" - [ -n "$patch_checksum" ] && hash_algo="sha-256" - fi - if [ -z "$patch_checksum" ] && [ -n "$SHA1_SUPPORTED" ]; then - patch_checksum="$(echo "$package_info" | grep SHA1 | head -n 1)" - [ -n "$patch_checksum" ] && hash_algo="sha-1" - fi - if [ -z "$patch_checksum" ] && [ -n "$MD5sum_SUPPORTED" ]; then - patch_checksum="$(echo "$package_info" | grep MD5sum | head -n 1)" - [ -n "$patch_checksum" ] && hash_algo="md5" - fi + while IFS=': ' read -r field checksum _ + do + case "$field" in + SHA512) + [ -n "$SHA512_SUPPORTED" ] || continue + hash_algo="sha-512" + break ;; + SHA256) + [ -n "$SHA256_SUPPORTED" ] || continue + hash_algo="sha-256" + break ;; + SHA1) + [ -n "$SHA1_SUPPORTED" ] || continue + hash_algo="sha-1" + break ;; + MD5sum) + [ -n "$MD5sum_SUPPORTED" ] || continue + hash_algo="md5" + break ;; + esac + done <<<"$package_info" - if [ -n "$patch_checksum" ]; then - checksum="$(echo "$patch_checksum" | cut -d' ' -f2)" - else + if [ -z "$hash_algo" ]; then + checksum= msg "Couldn't get supported checksum for $pkg_name ($pkg_version)." "warning" - msg "无法获得 $pkg_name ($pkg_version) 的受支持的散列验证值" "warning" + msg "无法获得 $pkg_name ($pkg_version) 版本受到支持的散列验证值" "warning" REMOVE_WORKING_MESSAGE= fi fi @@ -438,21 +522,64 @@ uri_mgr=apt-get hash_algo= fi - { - get_mirrors "$uri" - #echo " dir=$DLDIR" - if [ -n "$hash_algo" ]; then - echo " checksum=$hash_algo=$checksum" - fi - echo " out=$filename" - } >> "$DLLIST" - done <<<"$(echo "$uris_full" | grep -E "^'(http(s|)|(s|)ftp)://")" + # 使用文件锁安全写入下载列表 + ( + flock -x 200 # 获取排他锁 + { + get_mirrors "$uri" + [ -n "$hash_algo" ] && echo " checksum=$hash_algo=$checksum" + echo " out=$filename" + } >> "$DLLIST" + ) 200>>"$DLLIST" # 使用文件描述符200关联锁文件 + + # 将显示信息和文件大小存入临时文件 + echo "$display_line" >> "$tmpdir/display" + echo "$filesize" >> "$tmpdir/sizes" + } + + # 主并行处理逻辑(新增线程控制) + mapfile -t pkg_uri_list < <(echo "$uris_full" | grep -E "^'(http(s|)|(s|)ftp)://") + total_pkgs=${#pkg_uri_list[@]} + threads=${THREADS:-4} # 默认4线程 + per_thread=$(( (total_pkgs + threads - 1) / threads )) # 向上取整 + + # 分割任务到不同线程 + for ((i=0; i/dev/null || exit 1 + cd "$DLDIR" &>/dev/null || { msg; msg "Not able to change into download directory." "warning"; CLEANUP_STATE=1; exit; } eval "${_DOWNLOADER}" # execute downloadhelper command if [ "$(find "$DLDIR" -printf . | wc -c)" -gt 1 ]; then @@ -624,22 +753,26 @@ if [ "$option" == "install" ]; then cd - &>/dev/null || msg "Failed to change back directory" "warning" fi else - exit 1 + CLEANUP_STATE=1 + exit fi else [ -t 1 ] && tput el fi - if [ -z "$DOWNLOAD_ONLY" ] || [ "$_APTMGR" == "aptitude" ]; then + # different problem resolving for aptitude + if [ -z "$DOWNLOAD_ONLY" ] || [ "$(basename "${_APTMGR}")" == 'aptitude' ]; then "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" fi elif [ "$option" == "clean" ]; then "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" && { - find "$DLDIR" -maxdepth 1 -type f -delete - CLEANUP_STATE="$?" - [ -f "$DLLIST" ] && rm -f -- "$DLLIST"* || true + if [ -d "$DLDIR" ]; then + find "$DLDIR" -maxdepth 1 -type f -delete + CLEANUP_STATE="$?" + [ -f "$DLLIST" ] && rm -f -- "$DLLIST"* || true + fi } elif [ "$option" == "download" ]; then @@ -657,7 +790,8 @@ elif [ "$option" == "download" ]; then eval "${_DOWNLOADER}" fi - if [ "$_APTMGR" == "aptitude" ]; then + # different problem resolving for aptitude + if [ "$(basename "${_APTMGR}")" == 'aptitude' ]; then "${_APTMGR}" "$@" fi From d2844b8b8a6e5f7cb956e4311e76302ca9d2c5a6 Mon Sep 17 00:00:00 2001 From: shenmo Date: Mon, 24 Feb 2025 11:48:50 +0000 Subject: [PATCH 19/22] update debian/changelog. Signed-off-by: shenmo --- debian/changelog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 89fffde..7b181e4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,10 @@ -spark-store (4.4.0) UNRELEASED; urgency=medium +spark-store (4.5.0) UNRELEASED; urgency=medium * 支持从商店中直接启动应用 * ssinstall 修复安装时不再指定版本号以避免出现问题 * aptss支持fish补全 * 修复: distrobox下无法正常校验应用hash + * aptss 4.5.0 -- shenmo Tue, 24 Sep 2024 11:27:08 +0800 From 19bfd4031c3d6080500d003580c0ee7bb5e7adae Mon Sep 17 00:00:00 2001 From: shenmo Date: Mon, 24 Feb 2025 19:51:48 +0800 Subject: [PATCH 20/22] update: Translate --- translations/spark-store_en.ts | 53 +++++++++++++++++-------------- translations/spark-store_es.ts | 53 +++++++++++++++++-------------- translations/spark-store_fr.ts | 53 +++++++++++++++++-------------- translations/spark-store_zh_CN.ts | 53 +++++++++++++++++-------------- translations/spark-store_zh_TW.ts | 53 +++++++++++++++++-------------- 5 files changed, 150 insertions(+), 115 deletions(-) diff --git a/translations/spark-store_en.ts b/translations/spark-store_en.ts index 85a0c7a..82075d7 100644 --- a/translations/spark-store_en.ts +++ b/translations/spark-store_en.ts @@ -126,10 +126,10 @@ - - - - + + + + Download and Install @@ -186,69 +186,76 @@ - - - + + + Reinstall - + + + + Launch + + + + Upgrade - - + + Install - + Installing - - - - + + + + Warning - + The current application does not support or tested on deepin, there may be problems - + The current application does not support or tested on UOS, there may be problems - + The current application does not support or tested on Ubuntu, there may be problems - + The current application does not support or tested on current platform, there may be problems - - + + Spark Store - + Uninstall succeeded - + The URL has been copied to the clipboard diff --git a/translations/spark-store_es.ts b/translations/spark-store_es.ts index ddebf5d..ea5688a 100644 --- a/translations/spark-store_es.ts +++ b/translations/spark-store_es.ts @@ -126,10 +126,10 @@ - - - - + + + + Download and Install Descargar e instalar @@ -186,69 +186,76 @@ Se ha desactivado el modo desarrollador - - - + + + Reinstall Reinstalación - + + + + Launch + + + + Upgrade Actualización - - + + Install Instalación - + Installing Se está instalando - - - - + + + + Warning Aviso - + The current application does not support or tested on deepin, there may be problems - + The current application does not support or tested on UOS, there may be problems - + The current application does not support or tested on Ubuntu, there may be problems - + The current application does not support or tested on current platform, there may be problems - - + + Spark Store SPARK Store - + Uninstall succeeded Desinstalación exitosa - + The URL has been copied to the clipboard La URL ha sido copiada al portapapeles diff --git a/translations/spark-store_fr.ts b/translations/spark-store_fr.ts index 1118bc7..f5b6d83 100644 --- a/translations/spark-store_fr.ts +++ b/translations/spark-store_fr.ts @@ -126,10 +126,10 @@ - - - - + + + + Download and Install Télécharger et installer @@ -186,69 +186,76 @@ Mode développeur désactivé - - - + + + Reinstall Réinstaller - + + + + Launch + + + + Upgrade Mise à niveau - - + + Install Installation - + Installing Installation en cours - - - - + + + + Warning Avertissement - + The current application does not support or tested on deepin, there may be problems - + The current application does not support or tested on UOS, there may be problems - + The current application does not support or tested on Ubuntu, there may be problems - + The current application does not support or tested on current platform, there may be problems - - + + Spark Store Le Spark store - + Uninstall succeeded Désinstallation réussie - + The URL has been copied to the clipboard L'URL a été copiée dans le presse - papiers diff --git a/translations/spark-store_zh_CN.ts b/translations/spark-store_zh_CN.ts index a63709a..bf7b0be 100644 --- a/translations/spark-store_zh_CN.ts +++ b/translations/spark-store_zh_CN.ts @@ -121,10 +121,10 @@ - - - - + + + + Download and Install 下载并安装 @@ -186,69 +186,76 @@ 开发者模式未开启 - - - + + + Reinstall 重新安装 - + + + + Launch + 启动应用 + + + Upgrade 升级 - - + + Install 安装 - + Installing 正在安装 - - - - + + + + Warning 警告 - + The current application does not support or tested on deepin, there may be problems 当前应用不支持或未在deepin上测试过,安装后可能会出现问题 - + The current application does not support or tested on UOS, there may be problems 当前应用不支持或未在UOS上测试过,安装后可能会出现问题 - + The current application does not support or tested on Ubuntu, there may be problems 当前应用不支持或未在Ubuntu上测试过,安装后可能会出现问题 - + The current application does not support or tested on current platform, there may be problems 当前应用不支持或未在您的平台上测试过,安装后可能会出现问题 - - + + Spark Store 星火应用商店 - + Uninstall succeeded 卸载成功 - + The URL has been copied to the clipboard 链接已复制到剪贴板 diff --git a/translations/spark-store_zh_TW.ts b/translations/spark-store_zh_TW.ts index 8a1d890..1490517 100644 --- a/translations/spark-store_zh_TW.ts +++ b/translations/spark-store_zh_TW.ts @@ -121,10 +121,10 @@ - - - - + + + + Download and Install 下載並安裝 @@ -186,69 +186,76 @@ 开发者模式未开启 - - - + + + Reinstall 重新安裝 - + + + + Launch + + + + Upgrade 升级 - - + + Install 安装 - + Installing 正在安装 - - - - + + + + Warning - + The current application does not support or tested on deepin, there may be problems - + The current application does not support or tested on UOS, there may be problems - + The current application does not support or tested on Ubuntu, there may be problems - + The current application does not support or tested on current platform, there may be problems - - + + Spark Store 星火应用商店 - + Uninstall succeeded 卸载成功 - + The URL has been copied to the clipboard 链接已复制到剪贴板 From b3eb13ada578af766b08308b25b553de9809f8cc Mon Sep 17 00:00:00 2001 From: shenmo Date: Mon, 24 Feb 2025 12:09:21 +0000 Subject: [PATCH 21/22] update debian/changelog. Signed-off-by: shenmo --- debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/changelog b/debian/changelog index 7b181e4..521ab0b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ spark-store (4.5.0) UNRELEASED; urgency=medium * aptss支持fish补全 * 修复: distrobox下无法正常校验应用hash * aptss 4.5.0 + * 应用更新新增进度条 -- shenmo Tue, 24 Sep 2024 11:27:08 +0800 From 0566d546df80bb7d5af64a85f1f10f82fe3b0a30 Mon Sep 17 00:00:00 2001 From: shenmo Date: Mon, 24 Feb 2025 20:08:48 +0800 Subject: [PATCH 22/22] =?UTF-8?q?update:=20=E6=96=B0=E5=A2=9E=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E8=BF=9B=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tool/update-upgrade/ss-do-upgrade.sh | 69 +++++++++++++++++++--------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/tool/update-upgrade/ss-do-upgrade.sh b/tool/update-upgrade/ss-do-upgrade.sh index dd78410..474a777 100755 --- a/tool/update-upgrade/ss-do-upgrade.sh +++ b/tool/update-upgrade/ss-do-upgrade.sh @@ -9,7 +9,7 @@ if [ "$(id -u)" != "0" ] ; then exit fi fi - +HERE=$(dirname $0) trap "rm -f /tmp/spark-store/upgradeStatus.txt" EXIT source /opt/durapps/spark-store/bin/bashimport/transhell.amber load_transhell_debug @@ -58,20 +58,20 @@ function get_name_from_desktop_file() { touch /tmp/spark-store/upgradeStatus.txt # 执行 apt update -pkexec /opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade-worker.sh ssupdate 2>&1 > /dev/null | zenity --progress --auto-close --pulsate --no-cancel --text="${TRANSHELL_CONTENT_UPDATE_CHEKING_PLEASE_WAIT}" --height 70 --width 400 --title="${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg +pkexec ${HERE}/ss-do-upgrade-worker.sh ssupdate 2>&1 > /dev/null | zenity --progress --auto-close --pulsate --no-cancel --text="${TRANSHELL_CONTENT_UPDATE_CHEKING_PLEASE_WAIT}" --height 70 --width 400 --title="${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg if [ -z `cat /tmp/spark-store-app-ssupdate-status.txt` ] ; then - /opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade-worker.sh clean-log + ${HERE}/ss-do-upgrade-worker.sh clean-log else zenity --error --text "${TRANSHELL_CONTENT_CHECK_UPDATE_PROCESS_ERROR_PRESS_CONFIRM_TO_CHECK}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 200 --width 350 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg zenity --text-info --filename=/tmp/spark-store-app-ssupdate-log.txt --checkbox="${TRANSHELL_CONTENT_I_ALREDY_COPIED_THE_LOG_HERE_AND_WILL_USE_IT_TO_FEEDBACK}" --title="${TRANSHELL_CONTENT_FEEDBACK_CAN_BE_FOUND_IN_THE_SETTINGS}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg - /opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade-worker.sh clean-log + ${HERE}/ss-do-upgrade-worker.sh clean-log rm -f /tmp/spark-store/upgradeStatus.txt exit fi # 获取可更新应用列表 -PKG_LIST="$(/opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade-worker.sh upgradable-list)" +PKG_LIST="$(${HERE}/ss-do-upgrade-worker.sh upgradable-list)" ## 如果没更新,就弹出不需要更新 if [ -z "$PKG_LIST" ] ; then zenity --info --text "${TRANSHELL_CONTENT_NO_NEED_TO_UPGRADE}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg @@ -113,24 +113,51 @@ done) ## 如果没有应用需要更新,则直接退出 if [ -z "$PKG_UPGRADE_LIST" ] ; then - zenity --info --text "${TRANSHELL_CONTENT_NO_NEED_TO_UPGRADE}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg - else - PKG_UPGRADE_LIST=$(echo "$PKG_UPGRADE_LIST" | zenity --list --text="${TRANSHELL_CONTENT_CHOOSE_APP_TO_UPGRADE}" --column="${TRANSHELL_CONTENT_CHOOSE}" --column="${TRANSHELL_CONTENT_APP_NAME}" --column="${TRANSHELL_CONTENT_NEW_VERSION}" --column="${TRANSHELL_CONTENT_UPGRADE_FROM}" --column="${TRANSHELL_CONTENT_PKG_NAME}" --separator=" " --checklist --multiple --print-column=5 --height 350 --width 650 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg) + zenity --info --text "${TRANSHELL_CONTENT_NO_NEED_TO_UPGRADE}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 + exit 0 + fi + + + while true;do + PKG_UPGRADE_LIST=$(echo "$PKG_UPGRADE_LIST" | zenity --list --text="${TRANSHELL_CONTENT_CHOOSE_APP_TO_UPGRADE}" --column="${TRANSHELL_CONTENT_CHOOSE}" --column="${TRANSHELL_CONTENT_APP_NAME}" --column="${TRANSHELL_CONTENT_NEW_VERSION}" --column="${TRANSHELL_CONTENT_UPGRADE_FROM}" --column="${TRANSHELL_CONTENT_PKG_NAME}" --separator=" " --checklist --multiple --print-column=5 --height 350 --width 650 ) ## 如果没有选择,则直接退出 if [ -z "$PKG_UPGRADE_LIST" ] ; then - zenity --info --text "${TRANSHELL_CONTENT_NO_APP_IS_CHOSEN}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg - else ### 更新用户选择的应用 - (for PKG_UPGRADE in $PKG_UPGRADE_LIST ; do - APP_UPGRADE="$(get_name_from_desktop_file $PKG_UPGRADE)" - update_transhell + zenity --info --text "${TRANSHELL_CONTENT_NO_APP_IS_CHOSEN}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 + exit 0 + fi + if [[ "$PKG_UPGRADE_LIST" == *"(null)"* ]]; then + zenity --error --text "${TRANSHELL_CONTENT_LIST_NOT_LOADED_PLEASE_WAIT}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 150 --width 300 + else + break + fi +done + ### 更新用户选择的应用 +# for PKG_UPGRADE in $PKG_UPGRADE_LIST;do +# APP_UPGRADE="$(get_name_from_desktop_file $PKG_UPGRADE)" +# update_transhell + +( +total=$(echo "$PKG_UPGRADE_LIST" | wc -w) +count=0 + +for PKG_UPGRADE in $PKG_UPGRADE_LIST; do + count=$((count + 1)) + APP_UPGRADE="$(get_name_from_desktop_file $PKG_UPGRADE)" + update_transhell + + # 启动升级任务 + (yes | pkexec ${HERE}/ss-do-upgrade-worker.sh upgrade-app $PKG_UPGRADE -y 2>&1 > /dev/null ) & + + # 计算进度百分比 + progress=$(( count * 100 / total - 1)) + + # 动态修改zenity的文本 + echo "# ${TRANSHELL_CONTENT_UPGRADING_PLEASE_WAIT}" + echo "$progress" + wait +done +) | zenity --progress --auto-close --no-cancel --text="Preparing..." --height 70 --width 400 --title="${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg - # 启动升级任务 - (pkexec /opt/durapps/spark-store/bin/update-upgrade/ss-do-upgrade-worker.sh upgrade-app $PKG_UPGRADE -y 2>&1 > /dev/null ) & - cmd_pid=$! - # 动态修改zenity的文本 - echo "# ${TRANSHELL_CONTENT_UPGRADING_PLEASE_WAIT}" - wait - done) | zenity --progress --auto-close --no-cancel --pulsate --text="Preparing..." --height 70 --width 400 --title="${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg #### 更新成功 if [ -z "`cat /tmp/spark-store-app-upgrade-status.txt`" ] ; then @@ -139,8 +166,6 @@ done) zenity --error --text "${TRANSHELL_CONTENT_APP_UGRADE_PROCESS_ERROR_PRESS_CONFIRM_TO_CHECK}" --title "${TRANSHELL_CONTENT_SPARK_STORE_UPGRADE_MODEL}" --height 200 --width 350 --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg zenity --text-info --filename=/tmp/spark-store-app-upgrade-log.txt --checkbox="${TRANSHELL_CONTENT_I_ALREDY_COPIED_THE_LOG_HERE_AND_WILL_USE_IT_TO_FEEDBACK}" --title="${TRANSHELL_CONTENT_FEEDBACK_CAN_BE_FOUND_IN_THE_SETTINGS}" --window-icon=/usr/share/icons/hicolor/scalable/apps/spark-store.svg fi - fi - fi fi rm -f /tmp/spark-store/upgradeStatus.txt